diff --git a/Build.PL b/Build.PL index 16e37986a7..98920731c6 100644 --- a/Build.PL +++ b/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"; diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e3d8ad4df..520b913c18 100644 --- a/CMakeLists.txt +++ b/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) diff --git a/README.md b/README.md index 40c22b98af..f6c0677c48 100644 --- a/README.md +++ b/README.md @@ -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. @@ -385,4 +385,4 @@ If you want to change a preset file, just do If you want to slice a file overriding an option contained in your preset file: - slic3r.pl --load config.ini --layer-height 0.25 file.stl \ No newline at end of file + slic3r.pl --load config.ini --layer-height 0.25 file.stl diff --git a/cmake/modules/FindCURL.cmake b/cmake/modules/FindCURL.cmake index b8724858c8..e0deafa45c 100644 --- a/cmake/modules/FindCURL.cmake +++ b/cmake/modules/FindCURL.cmake @@ -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() diff --git a/cmake/modules/FindPackageHandleStandardArgs_SLIC3R.cmake b/cmake/modules/FindPackageHandleStandardArgs_SLIC3R.cmake new file mode 100644 index 0000000000..eddfd001e4 --- /dev/null +++ b/cmake/modules/FindPackageHandleStandardArgs_SLIC3R.cmake @@ -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()` calls. It handles the +``REQUIRED``, ``QUIET`` and version-related arguments of ``find_package``. +It also sets the ``_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( + (DEFAULT_MSG|) + ... + ) + + find_package_handle_standard_args( + [FOUND_VAR ] + [REQUIRED_VARS ...] + [VERSION_VAR ] + [HANDLE_COMPONENTS] + [CONFIG_MODE] + [FAIL_MESSAGE ] + ) + + The ``_FOUND`` variable will be set to ``TRUE`` if all + the variables ``...`` 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|)`` + 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 `` + Obsolete. Specifies either ``_FOUND`` or + ``_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 ...`` + 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 `` + 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 ``_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( NO_MODULE)``. This implies + a ``VERSION_VAR`` value of ``_VERSION``. The command + will automatically check whether the package configuration file + was found. + + ``FAIL_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 ````. 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) + # _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 _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() diff --git a/cmake/modules/FindPackageMessage_SLIC3R.cmake b/cmake/modules/FindPackageMessage_SLIC3R.cmake new file mode 100644 index 0000000000..a7a5eb98fa --- /dev/null +++ b/cmake/modules/FindPackageMessage_SLIC3R.cmake @@ -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( "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() diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake index 8b498d3ab1..e5115ab443 100644 --- a/cmake/modules/FindTBB.cmake +++ b/cmake/modules/FindTBB.cmake @@ -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 "$<$,$>: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}") diff --git a/cmake/modules/PrecompiledHeader.cmake b/cmake/modules/PrecompiledHeader.cmake new file mode 100644 index 0000000000..2880da9f2e --- /dev/null +++ b/cmake/modules/PrecompiledHeader.cmake @@ -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 +# +# 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 "$") + set(_compile_definitions "$") + set(_compile_flags "$") + set(_compile_options "$") + set(_include_directories "$<$:-I$\n>") + set(_compile_definitions "$<$:-D$\n>") + set(_compile_flags "$<$:$\n>") + set(_compile_options "$<$:$\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() diff --git a/cmake/modules/SelectLibraryConfigurations_SLIC3R.cmake b/cmake/modules/SelectLibraryConfigurations_SLIC3R.cmake new file mode 100644 index 0000000000..d25ac86408 --- /dev/null +++ b/cmake/modules/SelectLibraryConfigurations_SLIC3R.cmake @@ -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 . + +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() diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt new file mode 100644 index 0000000000..eca86a7c5e --- /dev/null +++ b/deps/CMakeLists.txt @@ -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). diff --git a/deps/deps-unix-static.cmake b/deps/deps-unix-static.cmake new file mode 100644 index 0000000000..cbba0f14df --- /dev/null +++ b/deps/deps-unix-static.cmake @@ -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 +) diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake new file mode 100644 index 0000000000..a47a29be11 --- /dev/null +++ b/deps/deps-windows.cmake @@ -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 () diff --git a/deps/wxwidgets-pngprefix.h b/deps/wxwidgets-pngprefix.h new file mode 100644 index 0000000000..de62e310bd --- /dev/null +++ b/deps/wxwidgets-pngprefix.h @@ -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 + + + + diff --git a/doc/deps-build/unix-static/Makefile b/doc/deps-build/unix-static/Makefile index 21740bc532..90d5083bba 100644 --- a/doc/deps-build/unix-static/Makefile +++ b/doc/deps-build/unix-static/Makefile @@ -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 diff --git a/doc/deps-build/windows/slic3r-makedeps.ps1 b/doc/deps-build/windows/slic3r-makedeps.ps1 index e256d61e42..228244d0b4 100644 --- a/doc/deps-build/windows/slic3r-makedeps.ps1 +++ b/doc/deps-build/windows/slic3r-makedeps.ps1 @@ -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!" diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 46627311ff..44100db8cf 100644 --- a/lib/Slic3r.pm +++ b/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; diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 27aa1a59b4..6adb650c21 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -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, @_); diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 7510d22afb..31f614ba92 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -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 diff --git a/lib/Slic3r/GUI/2DBed.pm b/lib/Slic3r/GUI/2DBed.pm deleted file mode 100644 index ebbc70b6be..0000000000 --- a/lib/Slic3r/GUI/2DBed.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/Controller.pm b/lib/Slic3r/GUI/Controller.pm deleted file mode 100644 index f7d90c7962..0000000000 --- a/lib/Slic3r/GUI/Controller.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm deleted file mode 100644 index de0565255b..0000000000 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/Controller/PrinterPanel.pm b/lib/Slic3r/GUI/Controller/PrinterPanel.pm deleted file mode 100644 index 794ea4e4ea..0000000000 --- a/lib/Slic3r/GUI/Controller/PrinterPanel.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 9d2fd19cea..c1975cd5d3 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -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,13 +26,25 @@ 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) = @_; my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE); Slic3r::GUI::set_main_frame($self); - + $appController = Slic3r::AppController->new(); if ($^O eq 'MSWin32') { @@ -45,13 +57,11 @@ 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}; $self->{preferences_event} = $params{preferences_event}; - + # initialize tabpanel and menubar $self->_init_tabpanel; $self->_init_menubar; @@ -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,25 +190,76 @@ 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 { @@ -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 { diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm deleted file mode 100644 index 962e6ffc0d..0000000000 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm deleted file mode 100644 index 1a53daeb5f..0000000000 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 48b82237ff..d2a7a23b11 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -8,7 +8,6 @@ use utf8; use File::Basename qw(basename dirname); use List::Util qw(sum first max); use Slic3r::Geometry qw(X Y Z scale unscale deg2rad rad2deg); -use threads::shared qw(shared_clone); use Wx qw(:button :colour :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc :panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap); use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED @@ -35,28 +34,31 @@ use constant TB_LAYER_EDITING => &Wx::NewId; use Wx::Locale gettext => 'L'; -# package variables to avoid passing lexicals to threads -our $PROGRESS_BAR_EVENT : shared = Wx::NewEventType; -our $ERROR_EVENT : shared = Wx::NewEventType; # Emitted from the worker thread when the G-code export is finished. -our $EXPORT_COMPLETED_EVENT : shared = Wx::NewEventType; -our $PROCESS_COMPLETED_EVENT : shared = Wx::NewEventType; - -use constant FILAMENT_CHOOSERS_SPACING => 0; -use constant PROCESS_DELAY => 0.5 * 1000; # milliseconds +our $SLICING_COMPLETED_EVENT = Wx::NewEventType; +our $PROCESS_COMPLETED_EVENT = Wx::NewEventType; my $PreventListEvents = 0; our $appController; +# XXX: VK: done, except callback handling and timer sub new { - my ($class, $parent) = @_; + my ($class, $parent, %params) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + Slic3r::GUI::set_plater($self); $self->{config} = Slic3r::Config::new_from_defaults_keys([qw( bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height serial_port serial_speed host_type print_host printhost_apikey printhost_cafile nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_rotation_angle extruder_colour filament_colour max_print_height printer_model )]); + + # store input params + $self->{event_object_selection_changed} = $params{event_object_selection_changed}; + $self->{event_object_settings_changed} = $params{event_object_settings_changed}; + $self->{event_remove_object} = $params{event_remove_object}; + $self->{event_update_scene} = $params{event_update_scene}; + # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm $self->{model} = Slic3r::Model->new; # C++ Slic3r::Print with Perl extensions in Slic3r/Print.pm @@ -64,26 +66,27 @@ sub new { # List of Perl objects Slic3r::GUI::Plater::Object, representing a 2D preview of the platter. $self->{objects} = []; $self->{gcode_preview_data} = Slic3r::GCode::PreviewData->new; - - $self->{print}->set_status_cb(sub { - my ($percent, $message) = @_; - my $event = Wx::CommandEvent->new($PROGRESS_BAR_EVENT); - $event->SetString($message); - $event->SetInt($percent); - Wx::PostEvent($self, $event); - }); + $self->{background_slicing_process} = Slic3r::GUI::BackgroundSlicingProcess->new; + $self->{background_slicing_process}->set_print($self->{print}); + $self->{background_slicing_process}->set_gcode_preview_data($self->{gcode_preview_data}); + $self->{background_slicing_process}->set_sliced_event($SLICING_COMPLETED_EVENT); + $self->{background_slicing_process}->set_finished_event($PROCESS_COMPLETED_EVENT); + + # The C++ slicing core will post a wxCommand message to the main window. + Slic3r::GUI::set_print_callback_event($self->{print}, $Slic3r::GUI::MainFrame::PROGRESS_BAR_EVENT); # Initialize preview notebook - $self->{preview_notebook} = Wx::Notebook->new($self, -1, wxDefaultPosition, [335,335], wxNB_BOTTOM); + $self->{preview_notebook} = Wx::Notebook->new($self, -1, wxDefaultPosition, [-1,335], wxNB_BOTTOM); # Initialize handlers for canvases my $on_select_object = sub { - my ($obj_idx) = @_; - # Ignore the special objects (the wipe tower proxy and such). - $self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef); - }; - my $on_double_click = sub { - $self->object_settings_dialog if $self->selected_object; + my ($obj_idx, $vol_idx) = @_; + + if (($obj_idx != -1) && ($vol_idx == -1)) { + # Ignore the special objects (the wipe tower proxy and such). + $self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef); + $self->item_changed_selection($obj_idx) if (defined($obj_idx)); + } }; my $on_right_click = sub { my ($canvas, $click_pos_x, $click_pos_y) = @_; @@ -102,10 +105,11 @@ sub new { # callback to enable/disable action buttons my $enable_action_buttons = sub { my ($enable) = @_; - $self->{btn_export_gcode}->Enable($enable); - $self->{btn_reslice}->Enable($enable); - $self->{btn_print}->Enable($enable); - $self->{btn_send_gcode}->Enable($enable); + Slic3r::GUI::enable_action_buttons($enable); +# $self->{btn_export_gcode}->Enable($enable); +# $self->{btn_reslice}->Enable($enable); +# $self->{btn_print}->Enable($enable); +# $self->{btn_send_gcode}->Enable($enable); }; # callback to react to gizmo scale @@ -128,7 +132,9 @@ sub new { } $_->set_scaling_factor($scale) for @{ $model_object->instances }; - $self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%"); + # Set object scale on c++ side +# Slic3r::GUI::set_object_scale($obj_idx, $model_object->instances->[0]->scaling_factor * 100); + # $object->transform_thumbnail($self->{model}, $obj_idx); #update print and start background processing @@ -139,19 +145,104 @@ sub new { $self->schedule_background_process; }; + # callback to react to gizmo scale + my $on_gizmo_scale_3D = sub { + my ($scale_x, $scale_y, $scale_z) = @_; + + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_instance = $model_object->instances->[0]; + + $self->stop_background_process; + + #FIXME Scale the layer height profile? +# my $variation = $scale / $model_instance->scaling_factor; +# foreach my $range (@{ $model_object->layer_height_ranges }) { +# $range->[0] *= $variation; +# $range->[1] *= $variation; +# } + + my $scale = Slic3r::Pointf3->new($scale_x, $scale_y, $scale_z); + foreach my $inst (@{ $model_object->instances }) { + $inst->set_scaling_factors($scale); + } + Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D}); + + #update print and start background processing + $self->{print}->add_model_object($model_object, $obj_idx); + + $self->selection_changed(1); # refresh info (size, volume etc.) + $self->update; + $self->schedule_background_process; + + }; + # callback to react to gizmo rotate - # omitting last three parameters means rotation around Z - # otherwise they are the components of the rotation axis vector my $on_gizmo_rotate = sub { + my ($angle) = @_; + $self->rotate(rad2deg($angle), Z, 'absolute'); + }; + + # callback to react to gizmo rotate + my $on_gizmo_rotate_3D = sub { + my ($angle_x, $angle_y, $angle_z) = @_; + + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_instance = $model_object->instances->[0]; + + $self->stop_background_process; + + my $rotation = Slic3r::Pointf3->new($angle_x, $angle_y, $angle_z); + foreach my $inst (@{ $model_object->instances }) { + $inst->set_rotations($rotation); + } + Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D}); + + # update print and start background processing + $self->{print}->add_model_object($model_object, $obj_idx); + + $self->selection_changed; # refresh info (size etc.) + $self->update; + $self->schedule_background_process; + }; + + # callback to react to gizmo flatten + my $on_gizmo_flatten = sub { my ($angle, $axis_x, $axis_y, $axis_z) = @_; - if (!defined $axis_x) { - $self->rotate(rad2deg($angle), Z, 'absolute'); - } - else { - $self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0; - } + $self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0; }; + # callback to react to gizmo flatten + my $on_gizmo_flatten_3D = sub { + my ($angle_x, $angle_y, $angle_z) = @_; + + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_instance = $model_object->instances->[0]; + + $self->stop_background_process; + + my $rotation = Slic3r::Pointf3->new($angle_x, $angle_y, $angle_z); + foreach my $inst (@{ $model_object->instances }) { + $inst->set_rotations($rotation); + } + Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D}); + + # update print and start background processing + $self->{print}->add_model_object($model_object, $obj_idx); + + $self->selection_changed; # refresh info (size etc.) + $self->update; + $self->schedule_background_process; + }; + # callback to update object's geometry info while using gizmos my $on_update_geometry_info = sub { my ($size_x, $size_y, $size_z, $scale_factor) = @_; @@ -166,13 +257,100 @@ sub new { } } }; + + # callback to update object's geometry info while using gizmos + my $on_update_geometry_3D_info = sub { + my ($size_x, $size_y, $size_z, $scale_x, $scale_y, $scale_z) = @_; + my ($obj_idx, $object) = $self->selected_object; + + if ((defined $obj_idx) && ($self->{object_info_size})) { # have we already loaded the info pane? + $self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", $size_x, $size_y, $size_z)); + my $model_object = $self->{model}->objects->[$obj_idx]; + if (my $stats = $model_object->mesh_stats) { + $self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * $scale_x * $scale_y * $scale_z)); + } + } + }; + + # callbacks for toolbar + my $on_action_add = sub { + $self->add; + }; + + my $on_action_delete = sub { + $self->remove(); + }; + + my $on_action_deleteall = sub { + $self->reset; + }; + + my $on_action_arrange = sub { + $self->arrange; + }; + + my $on_action_more = sub { + $self->increase; + }; + + my $on_action_fewer = sub { + $self->decrease; + }; + + my $on_action_split = sub { + $self->split_object; + }; + + my $on_action_cut = sub { + $self->object_cut_dialog; + }; + + my $on_action_settings = sub { + $self->object_settings_dialog; + }; + + my $on_action_layersediting = sub { + my $state = Slic3r::GUI::_3DScene::is_toolbar_item_pressed($self->{canvas3D}, "layersediting"); + $self->on_layer_editing_toggled($state); + }; + + my $on_action_selectbyparts = sub { + my $curr = Slic3r::GUI::_3DScene::get_select_by($self->{canvas3D}); + if ($curr eq 'volume') { + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object'); + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + } + elsif ($curr eq 'object') { + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'volume'); + my $selections = []; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + + my ($obj_idx, $object) = $self->selected_object; + if (defined $obj_idx) { + my $vol_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx); + #Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx) if ($vol_idx != -1); + my $inst_cnt = $self->{model}->objects->[$obj_idx]->instances_count; + for (0..$inst_cnt-1){ + Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $_ + $vol_idx) if ($vol_idx != -1); + } + + my $volume_idx = Slic3r::GUI::_3DScene::get_in_object_volume_id($self->{canvas3D}, $vol_idx); + Slic3r::GUI::select_current_volume($obj_idx, $volume_idx) if ($volume_idx != -1); + } + } + }; + # Initialize 3D plater if ($Slic3r::GUI::have_OpenGL) { $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config}); $self->{preview_notebook}->AddPage($self->{canvas3D}, L('3D')); Slic3r::GUI::_3DScene::register_on_select_object_callback($self->{canvas3D}, $on_select_object); - Slic3r::GUI::_3DScene::register_on_double_click_callback($self->{canvas3D}, $on_double_click); +# Slic3r::GUI::_3DScene::register_on_double_click_callback($self->{canvas3D}, $on_double_click); Slic3r::GUI::_3DScene::register_on_right_click_callback($self->{canvas3D}, sub { $on_right_click->($self->{canvas3D}, @_); }); Slic3r::GUI::_3DScene::register_on_arrange_callback($self->{canvas3D}, sub { $self->arrange }); Slic3r::GUI::_3DScene::register_on_rotate_object_left_callback($self->{canvas3D}, sub { $self->rotate(-45, Z, 'relative') }); @@ -184,9 +362,26 @@ sub new { Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved); Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly); + Slic3r::GUI::_3DScene::register_on_gizmo_scale_3D_callback($self->{canvas3D}, $on_gizmo_scale_3D); Slic3r::GUI::_3DScene::register_on_gizmo_rotate_callback($self->{canvas3D}, $on_gizmo_rotate); + Slic3r::GUI::_3DScene::register_on_gizmo_rotate_3D_callback($self->{canvas3D}, $on_gizmo_rotate_3D); + Slic3r::GUI::_3DScene::register_on_gizmo_flatten_callback($self->{canvas3D}, $on_gizmo_flatten); + Slic3r::GUI::_3DScene::register_on_gizmo_flatten_3D_callback($self->{canvas3D}, $on_gizmo_flatten_3D); Slic3r::GUI::_3DScene::register_on_update_geometry_info_callback($self->{canvas3D}, $on_update_geometry_info); + Slic3r::GUI::_3DScene::register_on_update_geometry_3D_info_callback($self->{canvas3D}, $on_update_geometry_3D_info); + Slic3r::GUI::_3DScene::register_action_add_callback($self->{canvas3D}, $on_action_add); + Slic3r::GUI::_3DScene::register_action_delete_callback($self->{canvas3D}, $on_action_delete); + Slic3r::GUI::_3DScene::register_action_deleteall_callback($self->{canvas3D}, $on_action_deleteall); + Slic3r::GUI::_3DScene::register_action_arrange_callback($self->{canvas3D}, $on_action_arrange); + Slic3r::GUI::_3DScene::register_action_more_callback($self->{canvas3D}, $on_action_more); + Slic3r::GUI::_3DScene::register_action_fewer_callback($self->{canvas3D}, $on_action_fewer); + Slic3r::GUI::_3DScene::register_action_split_callback($self->{canvas3D}, $on_action_split); + Slic3r::GUI::_3DScene::register_action_cut_callback($self->{canvas3D}, $on_action_cut); + Slic3r::GUI::_3DScene::register_action_settings_callback($self->{canvas3D}, $on_action_settings); + Slic3r::GUI::_3DScene::register_action_layersediting_callback($self->{canvas3D}, $on_action_layersediting); + Slic3r::GUI::_3DScene::register_action_selectbyparts_callback($self->{canvas3D}, $on_action_selectbyparts); Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); + Slic3r::GUI::_3DScene::enable_toolbar($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); @@ -207,43 +402,42 @@ sub new { } }); - Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); + Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, + sub { + $self->{preview_iface}->set_viewport_from_scene($self->{canvas3D}); + }); +# Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); } Slic3r::GUI::register_on_request_update_callback(sub { $self->schedule_background_process; }); -# # Initialize 2D preview canvas -# $self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config}); -# $self->{preview_notebook}->AddPage($self->{canvas}, L('2D')); -# $self->{canvas}->on_select_object($on_select_object); -# $self->{canvas}->on_double_click($on_double_click); -# $self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); }); -# $self->{canvas}->on_instances_moved($on_instances_moved); - # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { - $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); - Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1); - Slic3r::GUI::_3DScene::enable_dynamic_background($self->{preview3D}->canvas, 1); - Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); }); - $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview')); + $self->{preview_iface} = Slic3r::GUI::create_preview_iface($self->{preview_notebook}, $self->{config}, $self->{print}, $self->{gcode_preview_data}); + $self->{preview_page_idx} = $self->{preview_notebook}->GetPageCount-1; + $self->{preview_iface}->register_on_viewport_changed_callback(sub { $self->{preview_iface}->set_viewport_into_scene($self->{canvas3D}); }); +# $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); +# Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1); +# Slic3r::GUI::_3DScene::enable_dynamic_background($self->{preview3D}->canvas, 1); +# Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); }); +# $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview')); $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; } - # Initialize toolpaths preview - if ($Slic3r::GUI::have_OpenGL) { - $self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print}); - $self->{preview_notebook}->AddPage($self->{toolpaths2D}, L('Layers')); - } - EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { my $preview = $self->{preview_notebook}->GetCurrentPage; - if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) { + my $page_id = $self->{preview_notebook}->GetSelection; + if (($preview != $self->{canvas3D}) && ($page_id != $self->{preview_page_idx})) { +# if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) { $preview->OnActivate if $preview->can('OnActivate'); - } elsif ($preview == $self->{preview3D}) { - $self->{preview3D}->reload_print; + } elsif ($page_id == $self->{preview_page_idx}) { + $self->{preview_iface}->reload_print; # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) - Slic3r::GUI::_3DScene::set_as_dirty($self->{preview3D}->canvas); + $self->{preview_iface}->set_canvas_as_dirty; +# } elsif ($preview == $self->{preview3D}) { +# $self->{preview3D}->reload_print; +# # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) +# Slic3r::GUI::_3DScene::set_as_dirty($self->{preview3D}->canvas); } elsif ($preview == $self->{canvas3D}) { if (Slic3r::GUI::_3DScene::is_reload_delayed($self->{canvas3D})) { my $selections = $self->collect_selections; @@ -255,111 +449,82 @@ sub new { } }); - # toolbar for object manipulation - if (!&Wx::wxMSW) { - Wx::ToolTip::Enable(1); - $self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL); - $self->{htoolbar}->AddTool(TB_ADD, L("Add…"), Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_REMOVE, L("Delete"), Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_RESET, L("Delete All"), Wx::Bitmap->new(Slic3r::var("cross.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_ARRANGE, L("Arrange"), Wx::Bitmap->new(Slic3r::var("bricks.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddSeparator; - $self->{htoolbar}->AddTool(TB_MORE, L("More"), Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_FEWER, L("Fewer"), Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddSeparator; - $self->{htoolbar}->AddTool(TB_45CCW, L("45° ccw"), Wx::Bitmap->new(Slic3r::var("arrow_rotate_anticlockwise.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_45CW, L("45° cw"), Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_SCALE, L("Scale…"), Wx::Bitmap->new(Slic3r::var("arrow_out.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_SPLIT, L("Split"), Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_CUT, L("Cut…"), Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddSeparator; - $self->{htoolbar}->AddTool(TB_SETTINGS, L("Settings…"), Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_LAYER_EDITING, L('Layer Editing'), Wx::Bitmap->new(Slic3r::var("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing'); - } else { - my %tbar_buttons = ( - add => L("Add…"), - remove => L("Delete"), - reset => L("Delete All"), - arrange => L("Arrange"), - increase => "", - decrease => "", - rotate45ccw => "", - rotate45cw => "", - changescale => L("Scale…"), - split => L("Split"), - cut => L("Cut…"), - settings => L("Settings…"), - layer_editing => L("Layer editing"), - ); - $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL); - for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) { - $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - $self->{btoolbar}->Add($self->{"btn_$_"}); - } - $self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - $self->{btoolbar}->Add($self->{"btn_layer_editing"}); - } +# # toolbar for object manipulation +# if (!&Wx::wxMSW) { +# Wx::ToolTip::Enable(1); +# $self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL); +# $self->{htoolbar}->AddTool(TB_ADD, L("Add…"), Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddTool(TB_REMOVE, L("Delete"), Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddTool(TB_RESET, L("Delete All"), Wx::Bitmap->new(Slic3r::var("cross.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddTool(TB_ARRANGE, L("Arrange"), Wx::Bitmap->new(Slic3r::var("bricks.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddSeparator; +# $self->{htoolbar}->AddTool(TB_MORE, L("More"), Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddTool(TB_FEWER, L("Fewer"), Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddSeparator; +# $self->{htoolbar}->AddTool(TB_45CCW, L("45° ccw"), Wx::Bitmap->new(Slic3r::var("arrow_rotate_anticlockwise.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddTool(TB_45CW, L("45° cw"), Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddTool(TB_SCALE, L("Scale…"), Wx::Bitmap->new(Slic3r::var("arrow_out.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddTool(TB_SPLIT, L("Split"), Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddTool(TB_CUT, L("Cut…"), Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddSeparator; +# $self->{htoolbar}->AddTool(TB_SETTINGS, L("Settings…"), Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG), ''); +# $self->{htoolbar}->AddTool(TB_LAYER_EDITING, L('Layer Editing'), Wx::Bitmap->new(Slic3r::var("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing'); +# } else { +# my %tbar_buttons = ( +# add => L("Add…"), +# remove => L("Delete"), +# reset => L("Delete All"), +# arrange => L("Arrange"), +# increase => "", +# decrease => "", +# rotate45ccw => "", +# rotate45cw => "", +# changescale => L("Scale…"), +# split => L("Split"), +# cut => L("Cut…"), +# settings => L("Settings…"), +# layer_editing => L("Layer editing"), +# ); +# $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL); +# for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) { +# $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); +# $self->{btoolbar}->Add($self->{"btn_$_"}); +# } +# $self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); +# $self->{btoolbar}->Add($self->{"btn_layer_editing"}); +# } ### Panel for right column $self->{right_panel} = Wx::Panel->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - - ### Scrolled Window for info boxes +# $self->{right_panel} = Wx::ScrolledWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); +# $self->{right_panel}->SetScrollbars(0, 1, 1, 1); + + ### Scrolled Window for panel without "Export G-code" and "Slice now" buttons my $scrolled_window_sizer = $self->{scrolled_window_sizer} = Wx::BoxSizer->new(wxVERTICAL); - $scrolled_window_sizer->SetMinSize([310, -1]); + $scrolled_window_sizer->SetMinSize([320, -1]); my $scrolled_window_panel = $self->{scrolled_window_panel} = Wx::ScrolledWindow->new($self->{right_panel}, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); $scrolled_window_panel->SetSizer($scrolled_window_sizer); - $scrolled_window_panel->SetScrollbars(1, 1, 1, 1); - - $self->{list} = Wx::ListView->new($scrolled_window_panel, -1, wxDefaultPosition, wxDefaultSize, - wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN | wxTAB_TRAVERSAL | wxWANTS_CHARS ); - $self->{list}->InsertColumn(0, L("Name"), wxLIST_FORMAT_LEFT, 145); - $self->{list}->InsertColumn(1, L("Copies"), wxLIST_FORMAT_CENTER, 45); - $self->{list}->InsertColumn(2, L("Scale"), wxLIST_FORMAT_CENTER, wxLIST_AUTOSIZE_USEHEADER); - EVT_LIST_ITEM_SELECTED($self, $self->{list}, \&list_item_selected); - EVT_LIST_ITEM_DESELECTED($self, $self->{list}, \&list_item_deselected); - EVT_LIST_ITEM_ACTIVATED($self, $self->{list}, \&list_item_activated); - EVT_KEY_DOWN($self->{list}, sub { - my ($list, $event) = @_; - if ($event->GetKeyCode == WXK_TAB) { - $list->Navigate($event->ShiftDown ? &Wx::wxNavigateBackward : &Wx::wxNavigateForward); - } elsif ($event->GetKeyCode == WXK_DELETE || - ($event->GetKeyCode == WXK_BACK && &Wx::wxMAC) ) { - $self->remove; - } else { - $event->Skip; - } - }); + $scrolled_window_panel->SetScrollbars(0, 1, 1, 1); # right pane buttons - $self->{btn_export_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); - $self->{btn_reslice} = Wx::Button->new($self->{right_panel}, -1, L("Slice now"), wxDefaultPosition, [-1, 30], wxBU_LEFT); - $self->{btn_print} = Wx::Button->new($self->{right_panel}, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); - $self->{btn_send_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT); - $self->{btn_export_stl} = Wx::Button->new($self->{right_panel}, -1, L("Export STL…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_export_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30],);# wxNO_BORDER);#, wxBU_LEFT); + $self->{btn_reslice} = Wx::Button->new($self->{right_panel}, -1, L("Slice now"), wxDefaultPosition, [-1, 30]);#, wxNO_BORDER);#, wxBU_LEFT); +# $self->{btn_print} = Wx::Button->new($self->{right_panel}, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); +# $self->{btn_send_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_print} = Wx::Button->new($scrolled_window_panel, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_send_gcode} = Wx::Button->new($scrolled_window_panel, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT); + #$self->{btn_export_stl} = Wx::Button->new($self->{right_panel}, -1, L("Export STL…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); #$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font); #$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font); $self->{btn_print}->Hide; $self->{btn_send_gcode}->Hide; +# export_gcode cog_go.png +#! reslice reslice.png my %icons = qw( - add brick_add.png - remove brick_delete.png - reset cross.png - arrange bricks.png - export_gcode cog_go.png print arrow_up.png send_gcode arrow_up.png - reslice reslice.png export_stl brick_go.png - - increase add.png - decrease delete.png - rotate45cw arrow_rotate_clockwise.png - rotate45ccw arrow_rotate_anticlockwise.png - changescale arrow_out.png - split shape_ungroup.png - cut package.png - settings cog.png ); for (grep $self->{"btn_$_"}, keys %icons) { $self->{"btn_$_"}->SetBitmap(Wx::Bitmap->new(Slic3r::var($icons{$_}), wxBITMAP_TYPE_PNG)); @@ -378,58 +543,50 @@ sub new { EVT_BUTTON($self, $self->{btn_reslice}, \&reslice); EVT_BUTTON($self, $self->{btn_export_stl}, \&export_stl); - if ($self->{htoolbar}) { - EVT_TOOL($self, TB_ADD, sub { $self->add; }); - EVT_TOOL($self, TB_REMOVE, sub { $self->remove() }); # explicitly pass no argument to remove - EVT_TOOL($self, TB_RESET, sub { $self->reset; }); - EVT_TOOL($self, TB_ARRANGE, sub { $self->arrange; }); - EVT_TOOL($self, TB_MORE, sub { $self->increase; }); - EVT_TOOL($self, TB_FEWER, sub { $self->decrease; }); - EVT_TOOL($self, TB_45CW, sub { $_[0]->rotate(-45, Z, 'relative') }); - EVT_TOOL($self, TB_45CCW, sub { $_[0]->rotate(45, Z, 'relative') }); - EVT_TOOL($self, TB_SCALE, sub { $self->changescale(undef); }); - EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; }); - EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog }); - EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog }); - EVT_TOOL($self, TB_LAYER_EDITING, sub { - my $state = Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D}); - $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state); - $self->on_layer_editing_toggled(! $state); - }); - } else { - EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; }); - EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove - EVT_BUTTON($self, $self->{btn_reset}, sub { $self->reset; }); - EVT_BUTTON($self, $self->{btn_arrange}, sub { $self->arrange; }); - EVT_BUTTON($self, $self->{btn_increase}, sub { $self->increase; }); - EVT_BUTTON($self, $self->{btn_decrease}, sub { $self->decrease; }); - EVT_BUTTON($self, $self->{btn_rotate45cw}, sub { $_[0]->rotate(-45, Z, 'relative') }); - EVT_BUTTON($self, $self->{btn_rotate45ccw}, sub { $_[0]->rotate(45, Z, 'relative') }); - EVT_BUTTON($self, $self->{btn_changescale}, sub { $self->changescale(undef); }); - EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; }); - EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog }); - EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog }); - EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); }); - } +# if ($self->{htoolbar}) { +# EVT_TOOL($self, TB_ADD, sub { $self->add; }); +# EVT_TOOL($self, TB_REMOVE, sub { $self->remove() }); # explicitly pass no argument to remove +# EVT_TOOL($self, TB_RESET, sub { $self->reset; }); +# EVT_TOOL($self, TB_ARRANGE, sub { $self->arrange; }); +# EVT_TOOL($self, TB_MORE, sub { $self->increase; }); +# EVT_TOOL($self, TB_FEWER, sub { $self->decrease; }); +# EVT_TOOL($self, TB_45CW, sub { $_[0]->rotate(-45, Z, 'relative') }); +# EVT_TOOL($self, TB_45CCW, sub { $_[0]->rotate(45, Z, 'relative') }); +# EVT_TOOL($self, TB_SCALE, sub { $self->changescale(undef); }); +# EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; }); +# EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog }); +# EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog }); +# EVT_TOOL($self, TB_LAYER_EDITING, sub { +# my $state = Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D}); +# $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state); +# $self->on_layer_editing_toggled(! $state); +# }); +# } else { +# EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; }); +# EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove +# EVT_BUTTON($self, $self->{btn_remove}, sub { Slic3r::GUI::remove_obj() }); # explicitly pass no argument to remove +# EVT_BUTTON($self, $self->{btn_reset}, sub { $self->reset; }); +# EVT_BUTTON($self, $self->{btn_arrange}, sub { $self->arrange; }); +# EVT_BUTTON($self, $self->{btn_increase}, sub { $self->increase; }); +# EVT_BUTTON($self, $self->{btn_decrease}, sub { $self->decrease; }); +# EVT_BUTTON($self, $self->{btn_rotate45cw}, sub { $_[0]->rotate(-45, Z, 'relative') }); +# EVT_BUTTON($self, $self->{btn_rotate45ccw}, sub { $_[0]->rotate(45, Z, 'relative') }); +# EVT_BUTTON($self, $self->{btn_changescale}, sub { $self->changescale(undef); }); +# EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; }); +# EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog }); +# EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog }); +# EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); }); +# } $_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self)) for grep defined($_), - $self, $self->{canvas3D}, $self->{preview3D}, $self->{list}; -# $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}, $self->{list}; + $self, $self->{canvas3D}, $self->{preview_iface}, $self->{list}; +# $self, $self->{canvas3D}, $self->{preview3D}, $self->{list}; +# $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}; - EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub { + EVT_COMMAND($self, -1, $SLICING_COMPLETED_EVENT, sub { my ($self, $event) = @_; - $self->on_progress_event($event->GetInt, $event->GetString); - }); - - EVT_COMMAND($self, -1, $ERROR_EVENT, sub { - my ($self, $event) = @_; - Slic3r::GUI::show_error($self, $event->GetString); - }); - - EVT_COMMAND($self, -1, $EXPORT_COMPLETED_EVENT, sub { - my ($self, $event) = @_; - $self->on_export_completed($event->GetInt); + $self->on_update_print_preview; }); EVT_COMMAND($self, -1, $PROCESS_COMPLETED_EVENT, sub { @@ -437,6 +594,7 @@ sub new { $self->on_process_completed($event->GetInt ? undef : $event->GetString); }); +# XXX: not done { my $timer_id = Wx::NewId(); $self->{apply_config_timer} = Wx::Timer->new($self, $timer_id); @@ -451,31 +609,35 @@ sub new { Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape); Slic3r::GUI::_3DScene::zoom_to_bed($self->{canvas3D}); } - if ($self->{preview3D}) { - Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape); - } + $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); +# if ($self->{preview3D}) { +# Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape); +# } $self->update; { my $presets; { - $presets = $self->{presets_sizer} = Wx::FlexGridSizer->new(3, 2, 1, 2); + $presets = $self->{presets_sizer} = Wx::FlexGridSizer->new(4, 2, 1, 2); $presets->AddGrowableCol(1, 1); $presets->SetFlexibleDirection(wxHORIZONTAL); my %group_labels = ( print => L('Print settings'), filament => L('Filament'), + sla_material=> L('SLA material'), printer => L('Printer'), ); - # UI Combo boxes for a print, multiple filaments, and a printer. + # UI Combo boxes for a print, multiple filaments, SLA material and a printer. # Initially a single filament combo box is created, but the number of combo boxes for the filament selection may increase, # once a printer preset with multiple extruders is activated. # $self->{preset_choosers}{$group}[$idx] $self->{preset_choosers} = {}; - for my $group (qw(print filament printer)) { - my $text = Wx::StaticText->new($self->{right_panel}, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); + for my $group (qw(print filament sla_material printer)) { +# my $text = Wx::StaticText->new($self->{right_panel}, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); + my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); $text->SetFont($Slic3r::GUI::small_font); - my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY); +# my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY); + my $choice = Wx::BitmapComboBox->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY); if ($group eq 'filament') { EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } ); } @@ -493,15 +655,19 @@ sub new { $presets->Layout; } - my $frequently_changed_parameters_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - Slic3r::GUI::add_frequently_changed_parameters($self->{right_panel}, $frequently_changed_parameters_sizer, $presets); + my $frequently_changed_parameters_sizer = $self->{frequently_changed_parameters_sizer} = Wx::BoxSizer->new(wxVERTICAL); +#! Slic3r::GUI::add_frequently_changed_parameters($self->{right_panel}, $frequently_changed_parameters_sizer, $presets); + Slic3r::GUI::add_frequently_changed_parameters($self->{scrolled_window_panel}, $frequently_changed_parameters_sizer, $presets); my $object_info_sizer; { my $box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Info")); +# my $box = Wx::StaticBox->new($self->{right_panel}, -1, L("Info")); + $box->SetFont($Slic3r::GUI::small_bold_font); $object_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL); $object_info_sizer->SetMinSize([300,-1]); - my $grid_sizer = Wx::FlexGridSizer->new(3, 4, 5, 5); + #!my $grid_sizer = Wx::FlexGridSizer->new(3, 4, 5, 5); + my $grid_sizer = Wx::FlexGridSizer->new(2, 4, 5, 5); $grid_sizer->SetFlexibleDirection(wxHORIZONTAL); $grid_sizer->AddGrowableCol(1, 1); $grid_sizer->AddGrowableCol(3, 1); @@ -517,64 +683,122 @@ sub new { while (my $field = shift @info) { my $label = shift @info; my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); +# my $text = Wx::StaticText->new($self->{right_panel}, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); $text->SetFont($Slic3r::GUI::small_font); - $grid_sizer->Add($text, 0); + #!$grid_sizer->Add($text, 0); $self->{"object_info_$field"} = Wx::StaticText->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); +# $self->{"object_info_$field"} = Wx::StaticText->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); $self->{"object_info_$field"}->SetFont($Slic3r::GUI::small_font); if ($field eq 'manifold') { $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($scrolled_window_panel, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG)); - $self->{object_info_manifold_warning_icon}->Hide; +# $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self->{right_panel}, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG)); + #$self->{object_info_manifold_warning_icon}->Hide; + $self->{"object_info_manifold_warning_icon_show"} = sub { + if ($self->{object_info_manifold_warning_icon}->IsShown() != $_[0]) { + # this fuction show/hide info_manifold_warning_icon on the c++ side now + Slic3r::GUI::set_show_manifold_warning_icon($_[0]); + #my $mode = wxTheApp->{app_config}->get("view_mode"); + #return if ($mode eq "" || $mode eq "simple"); + #$self->{object_info_manifold_warning_icon}->Show($_[0]); + #$self->Layout + } + }; + $self->{"object_info_manifold_warning_icon_show"}->(0); my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $h_sizer->Add($self->{object_info_manifold_warning_icon}, 0); - $h_sizer->Add($self->{"object_info_$field"}, 0); - $grid_sizer->Add($h_sizer, 0, wxEXPAND); + $h_sizer->Add($text, 0); + $h_sizer->Add($self->{object_info_manifold_warning_icon}, 0, wxLEFT, 2); + $h_sizer->Add($self->{"object_info_$field"}, 0, wxLEFT, 2); + #!$grid_sizer->Add($h_sizer, 0, wxEXPAND); + $object_info_sizer->Add($h_sizer, 0, wxEXPAND|wxTOP, 4); } else { + $grid_sizer->Add($text, 0); $grid_sizer->Add($self->{"object_info_$field"}, 0); } } } - my $print_info_sizer = $self->{print_info_sizer} = Wx::StaticBoxSizer->new( - Wx::StaticBox->new($scrolled_window_panel, -1, L("Sliced Info")), wxVERTICAL); + my $print_info_box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Sliced Info")); + $print_info_box->SetFont($Slic3r::GUI::small_bold_font); + my $print_info_sizer = $self->{print_info_sizer} = Wx::StaticBoxSizer->new($print_info_box, wxVERTICAL); +# Wx::StaticBox->new($self->{right_panel}, -1, L("Sliced Info")), wxVERTICAL); $print_info_sizer->SetMinSize([300,-1]); my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL); $self->{buttons_sizer} = $buttons_sizer; $buttons_sizer->AddStretchSpacer(1); - $buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0); - $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0); - $buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT, 0); - $buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0); - $buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0); +# $buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0); +#! $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0); + $buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT | wxBOTTOM | wxTOP, 5); + $buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT | wxBOTTOM | wxTOP, 5); - $scrolled_window_sizer->Add($self->{list}, 1, wxEXPAND, 5); - $scrolled_window_sizer->Add($object_info_sizer, 0, wxEXPAND, 0); - $scrolled_window_sizer->Add($print_info_sizer, 0, wxEXPAND, 0); +# $scrolled_window_sizer->Add($self->{list}, 1, wxEXPAND, 5); +# $scrolled_window_sizer->Add($object_info_sizer, 0, wxEXPAND, 0); +# $scrolled_window_sizer->Add($print_info_sizer, 0, wxEXPAND, 0); + #$buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0); - my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); - $right_sizer->SetMinSize([320,-1]); - $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets; - $right_sizer->Add($frequently_changed_parameters_sizer, 0, wxEXPAND | wxTOP, 0) if defined $frequently_changed_parameters_sizer; - $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM, 5); - $right_sizer->Add($scrolled_window_panel, 1, wxEXPAND | wxALL, 1); + ### Sizer for info boxes + my $info_sizer = $self->{info_sizer} = Wx::BoxSizer->new(wxVERTICAL); + $info_sizer->SetMinSize([318, -1]); + $info_sizer->Add($object_info_sizer, 0, wxEXPAND | wxTOP, 20); + $info_sizer->Add($print_info_sizer, 0, wxEXPAND | wxTOP, 20); + + $scrolled_window_sizer->Add($presets, 0, wxEXPAND | wxLEFT, 2) if defined $presets; + $scrolled_window_sizer->Add($frequently_changed_parameters_sizer, 1, wxEXPAND | wxLEFT, 0) if defined $frequently_changed_parameters_sizer; + $scrolled_window_sizer->Add($buttons_sizer, 0, wxEXPAND, 0); + $scrolled_window_sizer->Add($info_sizer, 0, wxEXPAND | wxLEFT, 20); # Show the box initially, let it be shown after the slicing is finished. $self->print_info_box_show(0); + ### Sizer for "Export G-code" & "Slice now" buttons + my $btns_sizer = Wx::BoxSizer->new(wxVERTICAL); + $btns_sizer->SetMinSize([318, -1]); + $btns_sizer->Add($self->{btn_reslice}, 0, wxEXPAND, 0); + $btns_sizer->Add($self->{btn_export_gcode}, 0, wxEXPAND | wxTOP, 5); + + my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $self->{right_panel}->SetSizer($right_sizer); + $right_sizer->SetMinSize([320, -1]); +#! $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets; +#! $right_sizer->Add($frequently_changed_parameters_sizer, 1, wxEXPAND | wxTOP, 0) if defined $frequently_changed_parameters_sizer; +#! $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM | wxTOP, 10); +#! $right_sizer->Add($info_sizer, 0, wxEXPAND | wxLEFT, 20); + # Show the box initially, let it be shown after the slicing is finished. +#! $self->print_info_box_show(0); + $right_sizer->Add($scrolled_window_panel, 1, wxEXPAND | wxTOP, 5); +# $right_sizer->Add($self->{btn_reslice}, 0, wxEXPAND | wxLEFT | wxTOP, 20); +# $right_sizer->Add($self->{btn_export_gcode}, 0, wxEXPAND | wxLEFT | wxTOP, 20); + $right_sizer->Add($btns_sizer, 0, wxEXPAND | wxLEFT | wxTOP, 20); my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); $hsizer->Add($self->{preview_notebook}, 1, wxEXPAND | wxTOP, 1); - $hsizer->Add($self->{right_panel}, 0, wxEXPAND | wxLEFT | wxRIGHT, 3); - + $hsizer->Add($self->{right_panel}, 0, wxEXPAND | wxLEFT | wxRIGHT, 0);#3); + my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($self->{htoolbar}, 0, wxEXPAND, 0) if $self->{htoolbar}; - $sizer->Add($self->{btoolbar}, 0, wxEXPAND, 0) if $self->{btoolbar}; +# $sizer->Add($self->{htoolbar}, 0, wxEXPAND, 0) if $self->{htoolbar}; +# $sizer->Add($self->{btoolbar}, 0, wxEXPAND, 0) if $self->{btoolbar}; $sizer->Add($hsizer, 1, wxEXPAND, 0); $sizer->SetSizeHints($self); $self->SetSizer($sizer); + + # Send sizers/buttons to C++ + Slic3r::GUI::set_objects_from_perl( $self->{scrolled_window_panel}, + $frequently_changed_parameters_sizer, + $info_sizer, + $self->{btn_export_gcode}, + # $self->{btn_export_stl}, + $self->{btn_reslice}, + $self->{btn_print}, + $self->{btn_send_gcode}, + $self->{object_info_manifold_warning_icon} ); + + Slic3r::GUI::set_model_events_from_perl( $self->{model}, + $self->{event_object_selection_changed}, + $self->{event_object_settings_changed}, + $self->{event_remove_object}, + $self->{event_update_scene}); } # Last correct selected item for each preset @@ -585,16 +809,19 @@ sub new { } $self->update_ui_from_settings(); + $self->Layout; return $self; } +# XXX: VK: WIP # sets the callback sub on_select_preset { my ($self, $cb) = @_; $self->{on_select_preset} = $cb; } +# XXX: merged with on_select_preset # Called from the platter combo boxes selecting the active print, filament or printer. sub _on_select_preset { my ($self, $group, $choice, $idx) = @_; @@ -631,28 +858,32 @@ sub _on_select_preset { $self->on_config_change(wxTheApp->{preset_bundle}->full_config); } +# XXX: VK: done sub on_layer_editing_toggled { my ($self, $new_state) = @_; Slic3r::GUI::_3DScene::enable_layers_editing($self->{canvas3D}, $new_state); if ($new_state && ! Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D})) { # Initialization of the OpenGL shaders failed. Disable the tool. - if ($self->{htoolbar}) { - $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0); - $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, 0); - } else { - $self->{"btn_layer_editing"}->Disable; - $self->{"btn_layer_editing"}->SetValue(0); - } +# if ($self->{htoolbar}) { +# $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0); +# $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, 0); +# } else { +# $self->{"btn_layer_editing"}->Disable; +# $self->{"btn_layer_editing"}->SetValue(0); +# } + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", 0); } $self->{canvas3D}->Refresh; $self->{canvas3D}->Update; } +# XXX: VK: done (Plater::priv::main_frame) sub GetFrame { my ($self) = @_; return &Wx::GetTopLevelParent($self); } +# XXX: not done # Called after the Preferences dialog is closed and the program settings are saved. # Update the UI based on the current preferences. sub update_ui_from_settings @@ -664,16 +895,18 @@ sub update_ui_from_settings } } -# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs. +# XXX: VK: done +# Update preset combo boxes (Print settings, Filament, Material, Printer) from their respective tabs. # Called by # Slic3r::GUI::Tab::Print::_on_presets_changed # Slic3r::GUI::Tab::Filament::_on_presets_changed +# Slic3r::GUI::Tab::Material::_on_presets_changed # Slic3r::GUI::Tab::Printer::_on_presets_changed # when the presets are loaded or the user selects another preset. # For Print settings and Printer, synchronize the selection index with their tabs. # For Filament, synchronize the selection index for a single extruder printer only, otherwise keep the selection. sub update_presets { - # $group: one of qw(print filament printer) + # $group: one of qw(print filament sla_material printer) # $presets: PresetCollection my ($self, $group, $presets) = @_; my @choosers = @{$self->{preset_choosers}{$group}}; @@ -689,6 +922,8 @@ sub update_presets { } } elsif ($group eq 'print') { wxTheApp->{preset_bundle}->print->update_platter_ui($choosers[0]); + } elsif ($group eq 'sla_material') { + wxTheApp->{preset_bundle}->sla_material->update_platter_ui($choosers[0]); } elsif ($group eq 'printer') { # Update the print choosers to only contain the compatible presets, update the dirty flags. wxTheApp->{preset_bundle}->print->update_platter_ui($self->{preset_choosers}{print}->[0]); @@ -706,12 +941,14 @@ sub update_presets { wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config}); } +# XXX: VK: done, in on_action_add() sub add { my ($self) = @_; my @input_files = wxTheApp->open_model($self); $self->load_files(\@input_files); } +# XXX: VK: done sub load_files { my ($self, $input_files) = @_; @@ -733,7 +970,7 @@ sub load_files { # Object indices for the UI. my @obj_idx = (); # Collected file names to display the final message on the status bar. - my @loaded_files = (); + my @loaded_files = (); # XXX: used??? # For all input files. for (my $i = 0; $i < @$input_files; $i += 1) { my $input_file = $input_files->[$i]; @@ -769,7 +1006,7 @@ sub load_files { # objects imported from 3mf require a call to center_around_origin to have gizmos working properly and this call # need to be done after looks_like_multipart_object detection - if ($input_file =~ /.3[mM][fF]$/) + if ($input_file =~ /[.]3[mM][fF]$/) { foreach my $model_object (@{$model->objects}) { $model_object->center_around_origin; # also aligns object to Z = 0 @@ -802,6 +1039,7 @@ sub load_files { return @obj_idx; } +# XXX: VK: done, except a few todos sub load_model_objects { my ($self, @model_objects) = @_; @@ -864,12 +1102,10 @@ sub load_model_objects { foreach my $obj_idx (@obj_idx) { my $object = $self->{objects}[$obj_idx]; my $model_object = $self->{model}->objects->[$obj_idx]; - $self->{list}->InsertStringItem($obj_idx, $object->name); - $self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)) - if $self->{list}->can('SetItemFont'); # legacy code for wxPerl < 0.9918 not supporting SetItemFont() + + # Add object to list on c++ side + Slic3r::GUI::add_object_to_list($object->name, $model_object); - $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count); - $self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%"); # $self->reset_thumbnail($obj_idx); } @@ -879,8 +1115,6 @@ sub load_model_objects { # zoom to objects Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; - $self->{list}->Update; - $self->{list}->Select($obj_idx[-1], 1); $self->object_list_changed; $self->schedule_background_process; @@ -888,6 +1122,7 @@ sub load_model_objects { return @obj_idx; } +# XXX: Removed, replaced with bed_shape_bb() sub bed_centerf { my ($self) = @_; @@ -896,17 +1131,17 @@ sub bed_centerf { return Slic3r::Pointf->new(unscale($bed_center->x), unscale($bed_center->y)); #) } +# XXX: VK: done sub remove { - my $self = shift; - my ($obj_idx) = @_; + my ($self, $obj_idx) = @_; $self->stop_background_process; # Prevent toolpaths preview from rendering while we modify the Print object - $self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D}; - $self->{preview3D}->enabled(0) if $self->{preview3D}; + $self->{preview_iface}->set_enabled(0) if $self->{preview_iface}; +# $self->{preview3D}->enabled(0) if $self->{preview3D}; - # if no object index is supplied, remove the selected one + # If no object index is supplied, remove the selected one. if (! defined $obj_idx) { ($obj_idx, undef) = $self->selected_object; return if ! defined $obj_idx; @@ -915,33 +1150,36 @@ sub remove { splice @{$self->{objects}}, $obj_idx, 1; $self->{model}->delete_object($obj_idx); $self->{print}->delete_object($obj_idx); - $self->{list}->DeleteItem($obj_idx); + # Delete object from list on c++ side + Slic3r::GUI::delete_object_from_list(); $self->object_list_changed; $self->select_object(undef); $self->update; - $self->schedule_background_process; } +# XXX: VK: done sub reset { - my $self = shift; + my ($self) = @_; $self->stop_background_process; # Prevent toolpaths preview from rendering while we modify the Print object - $self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D}; - $self->{preview3D}->enabled(0) if $self->{preview3D}; + $self->{preview_iface}->set_enabled(0) if $self->{preview_iface}; +# $self->{preview3D}->enabled(0) if $self->{preview3D}; @{$self->{objects}} = (); $self->{model}->clear_objects; $self->{print}->clear_objects; - $self->{list}->DeleteAllItems; + # Delete all objects from list on c++ side + Slic3r::GUI::delete_all_objects_from_list(); $self->object_list_changed; $self->select_object(undef); $self->update; } +# XXX: VK: done sub increase { my ($self, $copies) = @_; $copies //= 1; @@ -949,6 +1187,7 @@ sub increase { return if ! defined $obj_idx; my $model_object = $self->{model}->objects->[$obj_idx]; my $instance = $model_object->instances->[-1]; + $self->stop_background_process; for my $i (1..$copies) { $instance = $model_object->add_instance( offset => Slic3r::Pointf->new(map 10+$_, @{$instance->offset}), @@ -957,7 +1196,8 @@ sub increase { ); $self->{print}->objects->[$obj_idx]->add_copy($instance->offset); } - $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count); + # Set count of object on c++ side + Slic3r::GUI::set_object_count($obj_idx, $model_object->instances_count); # only autoarrange if user has autocentering enabled $self->stop_background_process; @@ -966,57 +1206,51 @@ sub increase { } else { $self->update; } + + $self->selection_changed; # refresh info (size, volume etc.) $self->schedule_background_process; } +# XXX: VK: done sub decrease { my ($self, $copies_asked) = @_; my $copies = $copies_asked // 1; my ($obj_idx, $object) = $self->selected_object; return if ! defined $obj_idx; - $self->stop_background_process; - my $model_object = $self->{model}->objects->[$obj_idx]; if ($model_object->instances_count > $copies) { + $self->stop_background_process; for my $i (1..$copies) { $model_object->delete_last_instance; $self->{print}->objects->[$obj_idx]->delete_last_copy; } - $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count); + # Set conut of object on c++ side + Slic3r::GUI::set_object_count($obj_idx, $model_object->instances_count); } elsif (defined $copies_asked) { # The "decrease" came from the "set number of copies" dialog. + $self->stop_background_process; $self->remove; } else { # The "decrease" came from the "-" button. Don't allow the object to disappear. - $self->resume_background_process; return; } - - if ($self->{objects}[$obj_idx]) { - $self->{list}->Select($obj_idx, 0); - $self->{list}->Select($obj_idx, 1); - } + $self->update; - $self->schedule_background_process; } +# XXX: VK: done sub set_number_of_copies { my ($self) = @_; - - $self->pause_background_process; - # get current number of copies my ($obj_idx, $object) = $self->selected_object; - my $model_object = $self->{model}->objects->[$obj_idx]; - + my $model_object = $self->{model}->objects->[$obj_idx]; # prompt user my $copies = -1; $copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self); my $diff = $copies - $model_object->instances_count; if ($diff == 0 || $copies == -1) { # no variation - $self->resume_background_process; } elsif ($diff > 0) { $self->increase($diff); } elsif ($diff < 0) { @@ -1024,6 +1258,7 @@ sub set_number_of_copies { } } +# XXX: VK: removed sub _get_number_from_user { my ($self, $title, $prompt_message, $error_message, $default, $only_positive) = @_; for (;;) { @@ -1046,6 +1281,7 @@ sub _get_number_from_user { } } +# XXX: not done sub rotate { my ($self, $angle, $axis, $relative_key, $axis_x, $axis_y, $axis_z) = @_; $relative_key //= 'absolute'; # relative or absolute coordinates @@ -1068,13 +1304,12 @@ sub rotate { } # Let's calculate vector of rotation axis (if we don't have it already) - # The minus is there so that the direction is the same as was established if (defined $axis) { if ($axis == X) { - $axis_x = -1; + $axis_x = 1; } if ($axis == Y) { - $axis_y = -1; + $axis_y = 1; } } @@ -1109,15 +1344,15 @@ sub rotate { # $model_object->center_around_origin; # $self->reset_thumbnail($obj_idx); } - + # update print and start background processing $self->{print}->add_model_object($model_object, $obj_idx); $self->selection_changed; # refresh info (size etc.) $self->update; - $self->schedule_background_process; } +# XXX: not done sub mirror { my ($self, $axis) = @_; @@ -1145,9 +1380,9 @@ sub mirror { $self->selection_changed; # refresh info (size etc.) $self->update; - $self->schedule_background_process; } +# XXX: not done, renamed as Plater::priv::scale() sub changescale { my ($self, $axis, $tosize) = @_; @@ -1199,8 +1434,9 @@ sub changescale { $scale = $self->_get_number_from_user(L('Enter the scale % for the selected object:'), L('Scale'), L('Invalid scaling value entered'), $model_instance->scaling_factor*100, 1); return if ! defined($scale) || $scale eq ''; } - - $self->{list}->SetItem($obj_idx, 2, "$scale%"); + + # Set object scale on c++ side +# Slic3r::GUI::set_object_scale($obj_idx, $scale); $scale /= 100; # turn percent into factor my $variation = $scale / $model_instance->scaling_factor; @@ -1219,14 +1455,13 @@ sub changescale { $self->selection_changed(1); # refresh info (size, volume etc.) $self->update; - $self->schedule_background_process; } +# XXX: VK: WIP sub arrange { - my $self = shift; - - $self->pause_background_process; + my ($self) = @_; + $self->stop_background_process; # my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($self->{config}->bed_shape); # my $success = $self->{model}->arrange_objects(wxTheApp->{preset_bundle}->full_config->min_object_distance, $bb); @@ -1240,6 +1475,7 @@ sub arrange { $self->update(0); } +# XXX: not done sub split_object { my $self = shift; @@ -1255,90 +1491,71 @@ sub split_object { return; } - $self->pause_background_process; + $self->stop_background_process; my @model_objects = @{$current_model_object->split_object}; if (@model_objects == 1) { - $self->resume_background_process; Slic3r::GUI::warning_catcher($self)->(L("The selected object couldn't be split because it contains only one part.")); - $self->resume_background_process; - return; + $self->schedule_background_process; + } else { + $_->center_around_origin for (@model_objects); + $self->remove($obj_idx); + $current_object = $obj_idx = undef; + # load all model objects at once, otherwise the plate would be rearranged after each one + # causing original positions not to be kept + $self->load_model_objects(@model_objects); } - - $_->center_around_origin for (@model_objects); - - $self->remove($obj_idx); - $current_object = $obj_idx = undef; - - # load all model objects at once, otherwise the plate would be rearranged after each one - # causing original positions not to be kept - $self->load_model_objects(@model_objects); } +# XXX: not done +# Trigger $self->async_apply_config() after 500ms. +# The call is delayed to avoid restarting the background processing during typing into an edit field. sub schedule_background_process { my ($self) = @_; - - if (defined $self->{apply_config_timer}) { - $self->{apply_config_timer}->Start(PROCESS_DELAY, 1); # 1 = one shot - } + $self->{apply_config_timer}->Start(0.5 * 1000, 1); # 1 = one shot, every half a second. } +# XXX: not done # Executed asynchronously by a timer every PROCESS_DELAY (0.5 second). # The timer is started by schedule_background_process(), sub async_apply_config { my ($self) = @_; - - # pause process thread before applying new config - # since we don't want to touch data that is being used by the threads - $self->pause_background_process; - - # apply new config - my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config); - - # Just redraw the 3D canvas without reloading the scene. + # Apply new config to the possibly running background task. + my $was_running = $self->{background_slicing_process}->running; + my $invalidated = $self->{background_slicing_process}->apply_config(wxTheApp->{preset_bundle}->full_config); + # Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. $self->{canvas3D}->Refresh if Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D}); - - # Hide the slicing results if the current slicing status is no more valid. - $self->print_info_box_show(0) if $invalidated; - - if (wxTheApp->{app_config}->get("background_processing")) { - if ($invalidated) { - # kill current thread if any - $self->stop_background_process; - } else { - $self->resume_background_process; - } - # schedule a new process thread in case it wasn't running - $self->start_background_process; - } - - # Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. - # Otherwise they will be just refreshed. + # If the apply_config caused the calculation to stop, or it was not running yet: if ($invalidated) { - $self->{gcode_preview_data}->reset; - $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; - $self->{preview3D}->reload_print if $self->{preview3D}; - - # We also need to reload 3D scene because of the wipe tower preview box - if ($self->{config}->wipe_tower) { - my $selections = $self->collect_selections; - Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); - Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1) if $self->{canvas3D} + if ($was_running) { + # Hide the slicing results if the current slicing status is no more valid. + $self->print_info_box_show(0) + } + if (wxTheApp->{app_config}->get("background_processing")) { + $self->{background_slicing_process}->start; + } + if ($was_running) { + # Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. + # Otherwise they will be just refreshed. + $self->{gcode_preview_data}->reset; + $self->{preview_iface}->reload_print if $self->{preview_iface}; +# $self->{preview3D}->reload_print if $self->{preview3D}; + # We also need to reload 3D scene because of the wipe tower preview box + if ($self->{config}->wipe_tower) { + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1) if $self->{canvas3D} + } } } } +# XXX: not done +# Background processing is started either by the "Slice now" button, by the "Export G-code button" or by async_apply_config(). sub start_background_process { my ($self) = @_; - - return if !@{$self->{objects}}; - return if $self->{process_thread}; - - # It looks like declaring a local $SIG{__WARN__} prevents the ugly - # "Attempt to free unreferenced scalar" warning... - local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); - - # don't start process thread if config is not valid + return if ! @{$self->{objects}} || $self->{background_slicing_process}->running; + # Don't start process thread if config is not valid. eval { # this will throw errors if config is not valid wxTheApp->{preset_bundle}->full_config->validate; @@ -1347,79 +1564,24 @@ sub start_background_process { if ($@) { $self->statusbar->SetStatusText($@); return; - } - + } # Copy the names of active presets into the placeholder parser. wxTheApp->{preset_bundle}->export_selections_pp($self->{print}->placeholder_parser); - - # start thread - @_ = (); - $self->{process_thread} = Slic3r::spawn_thread(sub { - eval { - $self->{print}->process; - }; - my $event = Wx::CommandEvent->new($PROCESS_COMPLETED_EVENT); - if ($@) { - Slic3r::debugf "Background process error: $@\n"; - $event->SetInt(0); - $event->SetString($@); - } else { - $event->SetInt(1); - } - Wx::PostEvent($self, $event); - Slic3r::thread_cleanup(); - }); - Slic3r::debugf "Background processing started.\n"; + # Start the background process. + $self->{background_slicing_process}->start; } +# XXX: not done +# Stop the background processing sub stop_background_process { my ($self) = @_; - - $self->{apply_config_timer}->Stop if defined $self->{apply_config_timer}; - $self->statusbar->SetCancelCallback(undef); - $self->statusbar->StopBusy; - $self->statusbar->SetStatusText(""); - $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; - $self->{preview3D}->reload_print if $self->{preview3D}; - - if ($self->{process_thread}) { - Slic3r::debugf "Killing background process.\n"; - Slic3r::kill_all_threads(); - $self->{process_thread} = undef; - } else { - Slic3r::debugf "No background process running.\n"; - } - - # if there's an export process, kill that one as well - if ($self->{export_thread}) { - Slic3r::debugf "Killing background export process.\n"; - Slic3r::kill_all_threads(); - $self->{export_thread} = undef; - } -} - -sub pause_background_process { - my ($self) = @_; - - if ($self->{process_thread} || $self->{export_thread}) { - Slic3r::pause_all_threads(); - return 1; - } elsif (defined $self->{apply_config_timer} && $self->{apply_config_timer}->IsRunning) { - $self->{apply_config_timer}->Stop; - return 1; - } - - return 0; -} - -sub resume_background_process { - my ($self) = @_; - - if ($self->{process_thread} || $self->{export_thread}) { - Slic3r::resume_all_threads(); - } + $self->{background_slicing_process}->stop(); + $self->{preview_iface}->reload_print if $self->{preview_iface}; +# $self->{preview3D}->reload_print if $self->{preview3D}; } +# XXX: not done +# Called by the "Slice now" button, which is visible only if the background processing is disabled. sub reslice { # explicitly cancel a previous thread and start a new one. my ($self) = @_; @@ -1439,6 +1601,7 @@ sub reslice { } } +# XXX: VK: done sub export_gcode { my ($self, $output_file) = @_; @@ -1464,6 +1627,7 @@ sub export_gcode { eval { # this will throw errors if config is not valid $config->validate; + #FIXME it shall use the background processing! $self->{print}->apply_config($config); $self->{print}->validate; }; @@ -1505,6 +1669,8 @@ sub export_gcode { # this updates buttons status $self->object_list_changed; }); + + $self->{background_slicing_process}->set_output_path($self->{export_gcode_output_file}); # start background process, whose completion event handler # will detect $self->{export_gcode_output_file} and proceed with export @@ -1516,88 +1682,51 @@ sub export_gcode { return $self->{export_gcode_output_file}; } -# This gets called only if we have threads. -sub on_process_completed { - my ($self, $error) = @_; - - $self->statusbar->SetCancelCallback(undef); - $self->statusbar->StopBusy; - $self->statusbar->SetStatusText($error // ""); - - Slic3r::debugf "Background processing completed.\n"; - $self->{process_thread}->detach if $self->{process_thread}; - $self->{process_thread} = undef; - - # if we're supposed to perform an explicit export let's display the error in a dialog - if ($error && $self->{export_gcode_output_file}) { - $self->{export_gcode_output_file} = undef; - Slic3r::GUI::show_error($self, $error); - } - - return if $error; - $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; - $self->{preview3D}->reload_print if $self->{preview3D}; +# XXX: not done +# This message should be called by the background process synchronously. +sub on_update_print_preview { + my ($self) = @_; + $self->{preview_iface}->reload_print if $self->{preview_iface}; +# $self->{preview3D}->reload_print if $self->{preview3D}; # in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: my $selections = $self->collect_selections; Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); - - # if we have an export filename, start a new thread for exporting G-code - if ($self->{export_gcode_output_file}) { - @_ = (); - - # workaround for "Attempt to free un referenced scalar..." - our $_thread_self = $self; - - $self->{export_thread} = Slic3r::spawn_thread(sub { - eval { - $_thread_self->{print}->export_gcode(output_file => $_thread_self->{export_gcode_output_file}, gcode_preview_data => $_thread_self->{gcode_preview_data}); - }; - my $export_completed_event = Wx::CommandEvent->new($EXPORT_COMPLETED_EVENT); - if ($@) { - { - my $error_event = Wx::CommandEvent->new($ERROR_EVENT); - $error_event->SetString($@); - Wx::PostEvent($_thread_self, $error_event); - } - $export_completed_event->SetInt(0); - $export_completed_event->SetString($@); - } else { - $export_completed_event->SetInt(1); - } - Wx::PostEvent($_thread_self, $export_completed_event); - Slic3r::thread_cleanup(); - }); - Slic3r::debugf "Background G-code export started.\n"; - } } +# XXX: not done # This gets called also if we have no threads. sub on_progress_event { my ($self, $percent, $message) = @_; $self->statusbar->SetProgress($percent); - $self->statusbar->SetStatusText("$message…"); + # TODO: three dot character is not properly translated into C++ + # $self->statusbar->SetStatusText("$message…"); + $self->statusbar->SetStatusText("$message..."); } +# XXX: not done # Called when the G-code export finishes, either successfully or with an error. # This gets called also if we don't have threads. -sub on_export_completed { +sub on_process_completed { my ($self, $result) = @_; - - $self->statusbar->SetCancelCallback(undef); + + # Stop the background task, wait until the thread goes into the "Idle" state. + # At this point of time the thread should be either finished or canceled, + # so the following call just confirms, that the produced data were consumed. + $self->{background_slicing_process}->stop; + $self->statusbar->ResetCancelCallback(); $self->statusbar->StopBusy; $self->statusbar->SetStatusText(""); - Slic3r::debugf "Background export process completed.\n"; - $self->{export_thread}->detach if $self->{export_thread}; - $self->{export_thread} = undef; - my $message; my $send_gcode = 0; my $do_print = 0; - if ($result) { +# print "Process completed, message: ", $message, "\n"; + if (defined($result)) { + $message = L("Export failed"); + } else { # G-code file exported successfully. if ($self->{print_file}) { $message = L("File added to print queue"); @@ -1605,14 +1734,13 @@ sub on_export_completed { } elsif ($self->{send_gcode_file}) { $message = L("Sending G-code file to the Printer Host ..."); $send_gcode = 1; - } else { + } elsif (defined $self->{export_gcode_output_file}) { $message = L("G-code file exported to ") . $self->{export_gcode_output_file}; + } else { + $message = L("Slicing complete"); } - } else { - $message = L("Export failed"); } $self->{export_gcode_output_file} = undef; - $self->statusbar->SetStatusText($message); wxTheApp->notify($message); $self->do_print if $do_print; @@ -1620,14 +1748,20 @@ sub on_export_completed { # Send $self->{send_gcode_file} to OctoPrint. if ($send_gcode) { my $host = Slic3r::PrintHost::get_print_host($self->{config}); - if ($host->send_gcode($self->{send_gcode_file})) { - $self->statusbar->SetStatusText(L("Upload to host finished.")); + $message = L("Upload to host finished."); } else { - $self->statusbar->SetStatusText(""); + $message = ""; } } + # As of now, the BackgroundProcessing thread posts status bar update messages to a queue on the MainFrame.pm, + # but the "Processing finished" message is posted to this window. + # Delay the following status bar update, so it will be called later than what is received by MainFrame.pm. + wxTheApp->CallAfter(sub { + $self->statusbar->SetStatusText($message); + }); + $self->{print_file} = undef; $self->{send_gcode_file} = undef; $self->print_info_box_show(1); @@ -1636,18 +1770,27 @@ sub on_export_completed { $self->object_list_changed; # refresh preview - $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; - $self->{preview3D}->reload_print if $self->{preview3D}; + $self->{preview_iface}->reload_print if $self->{preview_iface}; +# $self->{preview3D}->reload_print if $self->{preview3D}; } +# XXX: partially done in the Sidebar # Fill in the "Sliced info" box with the result of the G-code generator. sub print_info_box_show { my ($self, $show) = @_; - my $scrolled_window_panel = $self->{scrolled_window_panel}; - my $scrolled_window_sizer = $self->{scrolled_window_sizer}; - return if (!$show && ($scrolled_window_sizer->IsShown(2) == $show)); +# my $scrolled_window_panel = $self->{scrolled_window_panel}; +# my $scrolled_window_sizer = $self->{scrolled_window_sizer}; +# return if (!$show && ($scrolled_window_sizer->IsShown(2) == $show)); + my $panel = $self->{scrolled_window_panel};#$self->{right_panel}; + my $sizer = $self->{info_sizer}; +# return if (!$sizer || !$show && ($sizer->IsShown(1) == $show)); + return if (!$sizer); - if ($show) { + Slic3r::GUI::set_show_print_info($show); +# return if (wxTheApp->{app_config}->get("view_mode") eq "simple"); + +# if ($show) + { my $print_info_sizer = $self->{print_info_sizer}; $print_info_sizer->Clear(1); my $grid_sizer = Wx::FlexGridSizer->new(2, 2, 5, 5); @@ -1684,25 +1827,31 @@ sub print_info_box_show { => $self->{print}->estimated_silent_print_time ); # if there is a wipe tower, insert number of toolchanges info into the array: - splice (@info, 8, 0, L("Number of tool changes") => sprintf("%.d", $self->{print}->m_wipe_tower_number_of_toolchanges)) if ($is_wipe_tower); + splice (@info, 8, 0, L("Number of tool changes") => sprintf("%.d", $self->{print}->wipe_tower_number_of_toolchanges)) if ($is_wipe_tower); while ( my $label = shift @info) { my $value = shift @info; next if $value eq "N/A"; - my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); +# my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); + my $text = Wx::StaticText->new($panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); $text->SetFont($Slic3r::GUI::small_font); $grid_sizer->Add($text, 0); - my $field = Wx::StaticText->new($scrolled_window_panel, -1, $value, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); +# my $field = Wx::StaticText->new($scrolled_window_panel, -1, $value, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + my $field = Wx::StaticText->new($panel, -1, $value, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); $field->SetFont($Slic3r::GUI::small_font); $grid_sizer->Add($field, 0); } } - $scrolled_window_sizer->Show(2, $show); - $scrolled_window_panel->Layout; +# $scrolled_window_sizer->Show(2, $show); +# $scrolled_window_panel->Layout; + $sizer->Show(1, $show && wxTheApp->{app_config}->get("view_mode") ne "simple"); + $self->Layout; + $panel->Refresh; } +# XXX: not done - to be removed sub do_print { my ($self) = @_; @@ -1716,6 +1865,7 @@ sub do_print { $printer_panel->load_print_job($self->{print_file}, $filament_stats); } +# XXX: VK: done sub export_stl { my ($self) = @_; return if !@{$self->{objects}}; @@ -1726,6 +1876,7 @@ sub export_stl { $self->statusbar->SetStatusText(L("STL file exported to ").$output_file); } +# XXX: not done sub reload_from_disk { my ($self) = @_; @@ -1757,6 +1908,7 @@ sub reload_from_disk { $self->remove($obj_idx); } +# XXX: VK: done sub export_object_stl { my ($self) = @_; my ($obj_idx, $object) = $self->selected_object; @@ -1768,6 +1920,7 @@ sub export_object_stl { $self->statusbar->SetStatusText(L("STL file exported to ").$output_file); } +# XXX: not done sub fix_through_netfabb { my ($self) = @_; my ($obj_idx, $object) = $self->selected_object; @@ -1796,6 +1949,7 @@ sub fix_through_netfabb { $self->remove($obj_idx); } +# XXX: VK: done sub export_amf { my ($self) = @_; return if !@{$self->{objects}}; @@ -1812,6 +1966,7 @@ sub export_amf { } } +# XXX: VK: done sub export_3mf { my ($self) = @_; return if !@{$self->{objects}}; @@ -1828,6 +1983,7 @@ sub export_3mf { } } +# XXX: VK: done # Ask user to select an output file for a given file format (STl, AMF, 3MF). # Propose a default file name based on the 'output_filename_format' configuration value. sub _get_export_file { @@ -1877,38 +2033,50 @@ sub _get_export_file { # $self->{objects}[$obj_idx]->thumbnail(undef); #} +# XXX: VK: done # this method gets called whenever print center is changed or the objects' bounding box changes # (i.e. when an object is added/removed/moved/rotated/scaled) sub update { my ($self, $force_autocenter) = @_; - + $self->Freeze; if (wxTheApp->{app_config}->get("autocenter") || $force_autocenter) { $self->{model}->center_instances_around_point($self->bed_centerf); } - - my $running = $self->pause_background_process; - my $invalidated = $self->{print}->reload_model_instances(); - - # The mere fact that no steps were invalidated when reloading model instances - # doesn't mean that all steps were done: for example, validation might have - # failed upon previous instance move, so we have no running thread and no steps - # are invalidated on this move, thus we need to schedule a new run. - if ($invalidated || !$running) { - $self->schedule_background_process; - } else { - $self->resume_background_process; - } - - $self->print_info_box_show(0); - + $self->stop_background_process; + $self->{print}->reload_model_instances(); + $self->{canvas}->reload_scene if $self->{canvas}; # $self->{canvas}->reload_scene if $self->{canvas}; my $selections = $self->collect_selections; Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0); - $self->{preview3D}->reset_gcode_preview_data if $self->{preview3D}; - $self->{preview3D}->reload_print if $self->{preview3D}; + $self->{preview_iface}->reset_gcode_preview_data if $self->{preview_iface}; + $self->{preview_iface}->reload_print if $self->{preview_iface}; +# $self->{preview3D}->reset_gcode_preview_data if $self->{preview3D}; +# $self->{preview3D}->reload_print if $self->{preview3D}; + $self->schedule_background_process; + $self->Thaw; } +# XXX: YS: done +# When a printer technology is changed, the UI needs to be updated to show/hide needed preset combo boxes. +sub show_preset_comboboxes{ + my ($self, $showSLA) = @_; #if showSLA is oposite value to "ptFFF" + + my $choices = $self->{preset_choosers}{filament}; + my $print_filament_ctrls_cnt = 2 + 2 * ($#$choices+1); + + foreach (0..$print_filament_ctrls_cnt-1){ + $self->{presets_sizer}->Show($_, !$showSLA); + } + $self->{presets_sizer}->Show($print_filament_ctrls_cnt , $showSLA); + $self->{presets_sizer}->Show($print_filament_ctrls_cnt+1, $showSLA); + + $self->{frequently_changed_parameters_sizer}->Show(0,!$showSLA); + + $self->Layout; +} + +# XXX: YS: done # When a number of extruders changes, the UI needs to be updated to show a single filament selection combo box per extruder. # Also the wxTheApp->{preset_bundle}->filament_presets needs to be resized accordingly # and some reasonable default has to be selected for the additional extruders. @@ -1921,7 +2089,8 @@ sub on_extruders_change { my @presets = $choices->[0]->GetStrings; # initialize new choice - my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY); +# my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY); + my $choice = Wx::BitmapComboBox->new($self->{scrolled_window_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY); my $extruder_idx = scalar @$choices; EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down($extruder_idx, @_); } ); push @$choices, $choice; @@ -1929,7 +2098,7 @@ sub on_extruders_change { $choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets; # insert new choice into sizer $self->{presets_sizer}->Insert(4 + ($#$choices-1)*2, 0, 0); - $self->{presets_sizer}->Insert(5 + ($#$choices-1)*2, $choice, 0, wxEXPAND | wxBOTTOM, FILAMENT_CHOOSERS_SPACING); + $self->{presets_sizer}->Insert(5 + ($#$choices-1)*2, $choice, 0, wxEXPAND | wxBOTTOM, 0); # setup the listener EVT_COMBOBOX($choice, $choice, sub { my ($choice) = @_; @@ -1948,9 +2117,11 @@ sub on_extruders_change { $choices->[-1]->Destroy; pop @$choices; } + $self->{right_panel}->Layout; $self->Layout; } +# XXX: not done sub on_config_change { my ($self, $config) = @_; @@ -1960,7 +2131,8 @@ sub on_config_change { if ($opt_key eq 'bed_shape') { # $self->{canvas}->update_bed_size; Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D}; - Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; + $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); +# Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; $update_scheduled = 1; } elsif ($opt_key =~ '^wipe_tower' || $opt_key eq 'single_extruder_multi_material') { $update_scheduled = 1; @@ -1972,34 +2144,38 @@ sub on_config_change { $self->Layout; } elsif ($opt_key eq 'variable_layer_height') { if ($config->get('variable_layer_height') != 1) { - if ($self->{htoolbar}) { - $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0); - $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, 0); - } else { - $self->{"btn_layer_editing"}->Disable; - $self->{"btn_layer_editing"}->SetValue(0); - } +# if ($self->{htoolbar}) { +# $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0); +# $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, 0); +# } else { +# $self->{"btn_layer_editing"}->Disable; +# $self->{"btn_layer_editing"}->SetValue(0); +# } + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", 0); Slic3r::GUI::_3DScene::enable_layers_editing($self->{canvas3D}, 0); $self->{canvas3D}->Refresh; $self->{canvas3D}->Update; } elsif (Slic3r::GUI::_3DScene::is_layers_editing_allowed($self->{canvas3D})) { # Want to allow the layer editing, but do it only if the OpenGL supports it. - if ($self->{htoolbar}) { - $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 1); - } else { - $self->{"btn_layer_editing"}->Enable; - } +# if ($self->{htoolbar}) { +# $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 1); +# } else { +# $self->{"btn_layer_editing"}->Enable; +# } + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", 1); } } elsif ($opt_key eq 'extruder_colour') { $update_scheduled = 1; my $extruder_colors = $config->get('extruder_colour'); - $self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors})); + $self->{preview_iface}->set_number_extruders(scalar(@{$extruder_colors})); +# $self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors})); } elsif ($opt_key eq 'max_print_height') { $update_scheduled = 1; } elsif ($opt_key eq 'printer_model') { # update to force bed selection (for texturing) Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D}; - Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; + $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); +# Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; $update_scheduled = 1; } } @@ -2012,34 +2188,23 @@ sub on_config_change { $self->schedule_background_process; } -sub list_item_deselected { - my ($self, $event) = @_; - return if $PreventListEvents; - $self->{_lecursor} = Wx::BusyCursor->new(); - if ($self->{list}->GetFirstSelected == -1) { - $self->select_object(undef); -# $self->{canvas}->Refresh; - Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}) if $self->{canvas3D}; - Slic3r::GUI::_3DScene::render($self->{canvas3D}) if $self->{canvas3D}; +# XXX: YS: WIP +sub item_changed_selection { + my ($self, $obj_idx) = @_; + + if (($obj_idx >= 0) && ($obj_idx < 1000)) { # skip if wipe tower selected + if ($self->{canvas3D}) { + Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}); + if ($obj_idx >= 0) { + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections); + } +# Slic3r::GUI::_3DScene::render($self->{canvas3D}); + } } - undef $self->{_lecursor}; -} - -sub list_item_selected { - my ($self, $event) = @_; - return if $PreventListEvents; - $self->{_lecursor} = Wx::BusyCursor->new(); - my $obj_idx = $event->GetIndex; - $self->select_object($obj_idx); -# $self->{canvas}->Refresh; - if ($self->{canvas3D}) { - my $selections = $self->collect_selections; - Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections); - Slic3r::GUI::_3DScene::render($self->{canvas3D}); - } - undef $self->{_lecursor}; } +# XXX: VK: done sub collect_selections { my ($self) = @_; my $selections = []; @@ -2049,13 +2214,7 @@ sub collect_selections { return $selections; } -sub list_item_activated { - my ($self, $event, $obj_idx) = @_; - - $obj_idx //= $event->GetIndex; - $self->object_settings_dialog($obj_idx); -} - +# XXX: YS: done, lambda on LEFT_DOWN # Called when clicked on the filament preset combo box. # When clicked on the icon, show the color picker. sub filament_color_box_lmouse_down @@ -2083,72 +2242,59 @@ sub filament_color_box_lmouse_down } } -sub object_cut_dialog { - my ($self, $obj_idx) = @_; - - if (!defined $obj_idx) { - ($obj_idx, undef) = $self->selected_object; - } - - if (!$Slic3r::GUI::have_OpenGL) { - Slic3r::GUI::show_error($self, L("Please install the OpenGL modules to use this feature (see build instructions).")); - return; - } - - my $dlg = Slic3r::GUI::Plater::ObjectCutDialog->new($self, - object => $self->{objects}[$obj_idx], - model_object => $self->{model}->objects->[$obj_idx], - ); - return unless $dlg->ShowModal == wxID_OK; - - if (my @new_objects = $dlg->NewModelObjects) { - $self->remove($obj_idx); - $self->load_model_objects(grep defined($_), @new_objects); - $self->arrange; - Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; - } -} - -sub object_settings_dialog { - my ($self, $obj_idx) = @_; - ($obj_idx, undef) = $self->selected_object if !defined $obj_idx; - my $model_object = $self->{model}->objects->[$obj_idx]; - - # validate config before opening the settings dialog because - # that dialog can't be closed if validation fails, but user - # can't fix any error which is outside that dialog - eval { wxTheApp->{preset_bundle}->full_config->validate; }; - return if Slic3r::GUI::catch_error($_[0]); - - my $dlg = Slic3r::GUI::Plater::ObjectSettingsDialog->new($self, - object => $self->{objects}[$obj_idx], - model_object => $model_object, - config => wxTheApp->{preset_bundle}->full_config, - ); - $self->pause_background_process; - $dlg->ShowModal; - -# # update thumbnail since parts may have changed -# if ($dlg->PartsChanged) { -# # recenter and re-align to Z = 0 -# $model_object->center_around_origin; -# $self->reset_thumbnail($obj_idx); +#sub object_cut_dialog { +# my ($self, $obj_idx) = @_; +# +# if (!defined $obj_idx) { +# ($obj_idx, undef) = $self->selected_object; # } - - # update print - if ($dlg->PartsChanged || $dlg->PartSettingsChanged) { - $self->stop_background_process; +# +# if (!$Slic3r::GUI::have_OpenGL) { +# Slic3r::GUI::show_error($self, L("Please install the OpenGL modules to use this feature (see build instructions).")); +# return; +# } +# +# my $dlg = Slic3r::GUI::Plater::ObjectCutDialog->new($self, +# object => $self->{objects}[$obj_idx], +# model_object => $self->{model}->objects->[$obj_idx], +# ); +# return unless $dlg->ShowModal == wxID_OK; +# +# if (my @new_objects = $dlg->NewModelObjects) { +# $self->remove($obj_idx); +# $self->load_model_objects(grep defined($_), @new_objects); +# $self->arrange; +# Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; +# } +#} + +# XXX: YS: done +sub changed_object_settings { + my ($self, $obj_idx, $parts_changed, $part_settings_changed) = @_; + + # update thumbnail since parts may have changed + if ($parts_changed) { + # recenter and re-align to Z = 0 + my $model_object = $self->{model}->objects->[$obj_idx]; + $model_object->center_around_origin; +# $self->reset_thumbnail($obj_idx); + } + + # update print + if ($parts_changed || $part_settings_changed) { + $self->stop_background_process; $self->{print}->reload_object($obj_idx); $self->schedule_background_process; -# $self->{canvas}->reload_scene if $self->{canvas}; + $self->{canvas}->reload_scene if $self->{canvas}; my $selections = $self->collect_selections; Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0); } else { - $self->resume_background_process; + $self->schedule_background_process; } } +# XXX: VK: done # Called to update various buttons depending on whether there are any objects or # whether background processing (export of a G-code, sending to Octoprint, forced background re-slicing) is active. sub object_list_changed { @@ -2156,20 +2302,21 @@ sub object_list_changed { # Enable/disable buttons depending on whether there are any objects on the platter. my $have_objects = @{$self->{objects}} ? 1 : 0; - my $variable_layer_height_allowed = $self->{config}->variable_layer_height && Slic3r::GUI::_3DScene::is_layers_editing_allowed($self->{canvas3D}); - if ($self->{htoolbar}) { - # On OSX or Linux - $self->{htoolbar}->EnableTool($_, $have_objects) - for (TB_RESET, TB_ARRANGE, TB_LAYER_EDITING); - $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0) if (! $variable_layer_height_allowed); - } else { - # On MSW - my $method = $have_objects ? 'Enable' : 'Disable'; - $self->{"btn_$_"}->$method - for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode layer_editing); - $self->{"btn_layer_editing"}->Disable if (! $variable_layer_height_allowed); - } +# if ($self->{htoolbar}) { +# # On OSX or Linux +# $self->{htoolbar}->EnableTool($_, $have_objects) +# for (TB_RESET, TB_ARRANGE); +# } else { +# # On MSW +# my $method = $have_objects ? 'Enable' : 'Disable'; +# $self->{"btn_$_"}->$method +# for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode); +# } + for my $toolbar_item (qw(deleteall arrange)) { + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, $toolbar_item, $have_objects); + } + my $export_in_progress = $self->{export_gcode_output_file} || $self->{send_gcode_file}; my $model_fits = $self->{canvas3D} ? Slic3r::GUI::_3DScene::check_volumes_outside_state($self->{canvas3D}, $self->{config}) : 1; # $model_fits == 1 -> ModelInstance::PVS_Partly_Outside @@ -2178,24 +2325,76 @@ sub object_list_changed { for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode); } +# XXX: VK: WIP # Selection of an active 3D object changed. sub selection_changed { my ($self) = @_; my ($obj_idx, $object) = $self->selected_object; my $have_sel = defined $obj_idx; + my $layers_height_allowed = $self->{config}->variable_layer_height && Slic3r::GUI::_3DScene::is_layers_editing_allowed($self->{canvas3D}) && $have_sel; $self->{right_panel}->Freeze; - if ($self->{htoolbar}) { - # On OSX or Linux - $self->{htoolbar}->EnableTool($_, $have_sel) - for (TB_REMOVE, TB_MORE, TB_FEWER, TB_45CW, TB_45CCW, TB_SCALE, TB_SPLIT, TB_CUT, TB_SETTINGS); - } else { - # On MSW - my $method = $have_sel ? 'Enable' : 'Disable'; - $self->{"btn_$_"}->$method - for grep $self->{"btn_$_"}, qw(remove increase decrease rotate45cw rotate45ccw changescale split cut settings); +# if ($self->{htoolbar}) { +# # On OSX or Linux +# $self->{htoolbar}->EnableTool($_, $have_sel) +# for (TB_REMOVE, TB_MORE, TB_45CW, TB_45CCW, TB_SCALE, TB_SPLIT, TB_CUT, TB_SETTINGS); +# +# $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, $layers_height_allowed); +# +# if ($have_sel) { +# my $model_object = $self->{model}->objects->[$obj_idx]; +# $self->{htoolbar}->EnableTool(TB_FEWER, $model_object->instances_count > 1); +# } else { +# $self->{htoolbar}->EnableTool(TB_FEWER, 0); +# } +# +# } else { +# # On MSW +# my $method = $have_sel ? 'Enable' : 'Disable'; +# $self->{"btn_$_"}->$method +# for grep $self->{"btn_$_"}, qw(remove increase rotate45cw rotate45ccw changescale split cut settings); +# +# if ($layers_height_allowed) { +# $self->{"btn_layer_editing"}->Enable; +# } else { +# $self->{"btn_layer_editing"}->Disable; +# } +# +# if ($have_sel) { +# my $model_object = $self->{model}->objects->[$obj_idx]; +# if ($model_object->instances_count > 1) { +# $self->{"btn_decrease"}->Enable; +# } else { +# $self->{"btn_decrease"}->Disable; +# } +# } else { +# $self->{"btn_decrease"}->Disable; +# } +# } + + for my $toolbar_item (qw(delete more fewer split cut settings)) { + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, $toolbar_item, $have_sel); } + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", $layers_height_allowed); + + my $can_select_by_parts = 0; + + if ($have_sel) { + my $model_object = $self->{model}->objects->[$obj_idx]; + $can_select_by_parts = ($obj_idx >= 0) && ($obj_idx < 1000) && ($model_object->volumes_count > 1); + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "fewer", $model_object->instances_count > 1); + } + + if ($can_select_by_parts) { + # first disable to let the item in the toolbar to switch to the unpressed state + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 0); + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 1); + } else { + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 0); + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object'); + } + if ($self->{object_info_size}) { # have we already loaded the info pane? if ($have_sel) { my $model_object = $self->{model}->objects->[$obj_idx]; @@ -2210,7 +2409,8 @@ sub selection_changed { $self->{object_info_facets}->SetLabel(sprintf(L('%d (%d shells)'), $model_object->facets_count, $stats->{number_of_parts})); if (my $errors = sum(@$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)})) { $self->{object_info_manifold}->SetLabel(sprintf(L("Auto-repaired (%d errors)"), $errors)); - $self->{object_info_manifold_warning_icon}->Show; + #$self->{object_info_manifold_warning_icon}->Show; + $self->{"object_info_manifold_warning_icon_show"}->(1); # we don't show normals_fixed because we never provide normals # to admesh, so it generates normals for all facets @@ -2220,7 +2420,8 @@ sub selection_changed { $self->{object_info_manifold_warning_icon}->SetToolTipString($message); } else { $self->{object_info_manifold}->SetLabel(L("Yes")); - $self->{object_info_manifold_warning_icon}->Hide; + #$self->{object_info_manifold_warning_icon}->Hide; + $self->{"object_info_manifold_warning_icon_show"}->(0); $self->{object_info_manifold}->SetToolTipString(""); $self->{object_info_manifold_warning_icon}->SetToolTipString(""); } @@ -2229,7 +2430,8 @@ sub selection_changed { } } else { $self->{"object_info_$_"}->SetLabel("") for qw(size volume facets materials manifold); - $self->{object_info_manifold_warning_icon}->Hide; + #$self->{object_info_manifold_warning_icon}->Hide; + $self->{"object_info_manifold_warning_icon_show"}->(0); $self->{object_info_manifold}->SetToolTipString(""); $self->{object_info_manifold_warning_icon}->SetToolTipString(""); } @@ -2241,41 +2443,85 @@ sub selection_changed { $self->{right_panel}->Thaw; } +# XXX: VK: done sub select_object { - my ($self, $obj_idx) = @_; + my ($self, $obj_idx, $child) = @_; # remove current selection foreach my $o (0..$#{$self->{objects}}) { - $PreventListEvents = 1; $self->{objects}->[$o]->selected(0); - $self->{list}->Select($o, 0); - $PreventListEvents = 0; } - + if (defined $obj_idx) { $self->{objects}->[$obj_idx]->selected(1); - # We use this flag to avoid circular event handling - # Select() happens to fire a wxEVT_LIST_ITEM_SELECTED on Windows, - # whose event handler calls this method again and again and again - $PreventListEvents = 1; - $self->{list}->Select($obj_idx, 1); - $PreventListEvents = 0; + # Select current object in the list on c++ side, if item isn't child +# if (!defined $child){ +# Slic3r::GUI::select_current_object($obj_idx);} # all selections in the object list is on c++ side } else { - # TODO: deselect all in list + # Unselect all objects in the list on c++ side +# Slic3r::GUI::unselect_objects(); # all selections in the object list is on c++ side } $self->selection_changed(1); } +# XXX: YS: WIP +sub select_object_from_cpp { + my ($self, $obj_idx, $vol_idx) = @_; + + # remove current selection + foreach my $o (0..$#{$self->{objects}}) { + $self->{objects}->[$o]->selected(0); + } + + my $curr = Slic3r::GUI::_3DScene::get_select_by($self->{canvas3D}); + + if (defined $obj_idx) { + if ($vol_idx == -1){ + if ($curr eq 'object') { + $self->{objects}->[$obj_idx]->selected(1); + } + elsif ($curr eq 'volume') { + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object'); + } + + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + } + else { + if ($curr eq 'object') { + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'volume'); + } + + my $selections = []; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + my $volume_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx); + + my $inst_cnt = $self->{model}->objects->[$obj_idx]->instances_count; + for (0..$inst_cnt-1){ + Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx*$inst_cnt + $_ + $volume_idx) if ($volume_idx != -1); + } + } + } + + $self->selection_changed(1); +} + +# XXX: VK: done sub selected_object { my ($self) = @_; my $obj_idx = first { $self->{objects}[$_]->selected } 0..$#{ $self->{objects} }; return defined $obj_idx ? ($obj_idx, $self->{objects}[$obj_idx]) : undef; } +# XXX: VK: done sub statusbar { return $_[0]->GetFrame->{statusbar}; } +# XXX: not done, to be removed (?) sub object_menu { my ($self) = @_; @@ -2386,20 +2632,26 @@ sub object_menu { return $menu; } +# XXX: not done # Set a camera direction, zoom to all objects. sub select_view { my ($self, $direction) = @_; my $idx_page = $self->{preview_notebook}->GetSelection; my $page = ($idx_page == &Wx::wxNOT_FOUND) ? L('3D') : $self->{preview_notebook}->GetPageText($idx_page); if ($page eq L('Preview')) { - Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction); - Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); + $self->{preview_iface}->select_view($direction); + $self->{preview_iface}->set_viewport_into_scene($self->{canvas3D}); +# Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction); +# Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); } else { Slic3r::GUI::_3DScene::select_view($self->{canvas3D}, $direction); - Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); + $self->{preview_iface}->set_viewport_from_scene($self->{canvas3D}); +# Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); } } + +# XXX: VK: done, in PlaterDropTarget package Slic3r::GUI::Plater::DropTarget; use Wx::DND; use base 'Wx::FileDropTarget'; @@ -2426,48 +2678,6 @@ package Slic3r::GUI::Plater::Object; use Moo; has 'name' => (is => 'rw', required => 1); -#has 'thumbnail' => (is => 'rw'); # ExPolygon::Collection in scaled model units with no transforms -#has 'transformed_thumbnail' => (is => 'rw'); -#has 'instance_thumbnails' => (is => 'ro', default => sub { [] }); # array of ExPolygon::Collection objects, each one representing the actual placed thumbnail of each instance in pixel units has 'selected' => (is => 'rw', default => sub { 0 }); -#sub make_thumbnail { -# my ($self, $model, $obj_idx) = @_; -# # make method idempotent -# $self->thumbnail->clear; -# # raw_mesh is the non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes. -# my $mesh = $model->objects->[$obj_idx]->raw_mesh; -##FIXME The "correct" variant could be extremely slow. -## if ($mesh->facets_count <= 5000) { -## # remove polygons with area <= 1mm -## my $area_threshold = Slic3r::Geometry::scale 1; -## $self->thumbnail->append( -## grep $_->area >= $area_threshold, -## @{ $mesh->horizontal_projection }, # horizontal_projection returns scaled expolygons -## ); -## $self->thumbnail->simplify(0.5); -## } else { -# my $convex_hull = Slic3r::ExPolygon->new($mesh->convex_hull); -# $self->thumbnail->append($convex_hull); -## } -# return $self->thumbnail; -#} -# -#sub transform_thumbnail { -# my ($self, $model, $obj_idx) = @_; -# -# return unless defined $self->thumbnail; -# -# my $model_object = $model->objects->[$obj_idx]; -# my $model_instance = $model_object->instances->[0]; -# -# # the order of these transformations MUST be the same everywhere, including -# # in Slic3r::Print->add_model_object() -# my $t = $self->thumbnail->clone; -# $t->rotate($model_instance->rotation, Slic3r::Point->new(0,0)); -# $t->scale($model_instance->scaling_factor); -# -# $self->transformed_thumbnail($t); -#} - 1; diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm deleted file mode 100644 index 88a05c2921..0000000000 --- a/lib/Slic3r/GUI/Plater/2D.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/Plater/2DToolpaths.pm b/lib/Slic3r/GUI/Plater/2DToolpaths.pm deleted file mode 100644 index 382310f245..0000000000 --- a/lib/Slic3r/GUI/Plater/2DToolpaths.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 09c2f0b8cf..06d1a798b5 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -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")); @@ -102,26 +102,31 @@ sub new { .L("Wipe tower")."|" .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'; @@ -243,6 +250,12 @@ sub new { $self->gcode_preview_data->set_shells_visible($checkbox_shells->IsChecked()); $self->refresh_print; }); + + EVT_SIZE($self, sub { + my ($s, $event) = @_; + $event->Skip; + $self->Refresh; + }); $self->SetSizer($main_sizer); $self->SetMinSize($self->GetSize); @@ -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]); +# $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->Layout; } diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm deleted file mode 100644 index 0685b41006..0000000000 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm deleted file mode 100644 index 77efbb29b4..0000000000 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm deleted file mode 100644 index 13c1b69450..0000000000 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm deleted file mode 100644 index 3ccf1d7f8a..0000000000 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm deleted file mode 100644 index b085871f05..0000000000 --- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/ProgressStatusBar.pm b/lib/Slic3r/GUI/ProgressStatusBar.pm deleted file mode 100644 index 32fd526800..0000000000 --- a/lib/Slic3r/GUI/ProgressStatusBar.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/GUI/SystemInfo.pm b/lib/Slic3r/GUI/SystemInfo.pm deleted file mode 100644 index 717ff12e5e..0000000000 --- a/lib/Slic3r/GUI/SystemInfo.pm +++ /dev/null @@ -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 = - '' . - '' . - ($params{slic3r_info} // '') . - ($params{copyright_info} // '') . - ($params{system_info} // '') . - ($params{opengl_info} // '') . - '' . - ''; - 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; diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 180d0d028d..286a73e2de 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -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) = @_; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 22a76100d6..ec31f6c8a1 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -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}; diff --git a/lib/Slic3r/Point.pm b/lib/Slic3r/Point.pm index 535a97194b..1134138ea3 100644 --- a/lib/Slic3r/Point.pm +++ b/lib/Slic3r/Point.pm @@ -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; diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 9cc1424093..a42b5d1c4a 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -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; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm deleted file mode 100644 index a9763a8240..0000000000 --- a/lib/Slic3r/Print.pm +++ /dev/null @@ -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]); - - - - -EOF - - my $print_polygon = sub { - my ($polygon, $type) = @_; - printf $fh qq{ \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{ \n}, $layer_id; - } else { - printf $fh qq{ \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{ \n}, - map @$_, $support_point, $anchor_point; - } - } - print $fh qq{ \n}; - @previous_layer_slices = @current_layer_slices; - } - - print $fh "\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; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index e275fdde58..7370881ea3 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -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; diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index 4fe3eb820b..b5b749f12b 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -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; } diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm deleted file mode 100644 index 1aa7dd27d5..0000000000 --- a/lib/Slic3r/SVG.pm +++ /dev/null @@ -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; diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 6471ae1239..b767ca593d 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -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; { diff --git a/resources/icons/add_object.png b/resources/icons/add_object.png new file mode 100644 index 0000000000..ffc958edc4 Binary files /dev/null and b/resources/icons/add_object.png differ diff --git a/resources/icons/bed/mk2_top.png b/resources/icons/bed/mk2_top.png index cab4b966f8..e93430b098 100644 Binary files a/resources/icons/bed/mk2_top.png and b/resources/icons/bed/mk2_top.png differ diff --git a/resources/icons/bed/mk3_top.png b/resources/icons/bed/mk3_top.png index b44007ad44..9674484979 100644 Binary files a/resources/icons/bed/mk3_top.png and b/resources/icons/bed/mk3_top.png differ diff --git a/resources/icons/colorchange_add_off.png b/resources/icons/colorchange_add_off.png new file mode 100644 index 0000000000..6ddeccbe0a Binary files /dev/null and b/resources/icons/colorchange_add_off.png differ diff --git a/resources/icons/colorchange_add_on.png b/resources/icons/colorchange_add_on.png new file mode 100644 index 0000000000..cc800b81e8 Binary files /dev/null and b/resources/icons/colorchange_add_on.png differ diff --git a/resources/icons/colorchange_delete_off.png b/resources/icons/colorchange_delete_off.png new file mode 100644 index 0000000000..c16655271a Binary files /dev/null and b/resources/icons/colorchange_delete_off.png differ diff --git a/resources/icons/colorchange_delete_on.png b/resources/icons/colorchange_delete_on.png new file mode 100644 index 0000000000..8f27ce9fe6 Binary files /dev/null and b/resources/icons/colorchange_delete_on.png differ diff --git a/resources/icons/disclosure_triangle_close.png b/resources/icons/disclosure_triangle_close.png new file mode 100644 index 0000000000..0660422c7f Binary files /dev/null and b/resources/icons/disclosure_triangle_close.png differ diff --git a/resources/icons/disclosure_triangle_open.png b/resources/icons/disclosure_triangle_open.png new file mode 100644 index 0000000000..81112f2a26 Binary files /dev/null and b/resources/icons/disclosure_triangle_open.png differ diff --git a/resources/icons/down_half_circle.png b/resources/icons/down_half_circle.png new file mode 100644 index 0000000000..f86a2932c1 Binary files /dev/null and b/resources/icons/down_half_circle.png differ diff --git a/resources/icons/erase.png b/resources/icons/erase.png new file mode 100644 index 0000000000..4c4cfd755c Binary files /dev/null and b/resources/icons/erase.png differ diff --git a/resources/icons/exclamation_mark_.png b/resources/icons/exclamation_mark_.png new file mode 100644 index 0000000000..5fe7c13556 Binary files /dev/null and b/resources/icons/exclamation_mark_.png differ diff --git a/resources/icons/lambda.png b/resources/icons/lambda.png new file mode 100644 index 0000000000..3be73b1424 Binary files /dev/null and b/resources/icons/lambda.png differ diff --git a/resources/icons/lambda_.png b/resources/icons/lambda_.png new file mode 100644 index 0000000000..8e9d2b0ea2 Binary files /dev/null and b/resources/icons/lambda_.png differ diff --git a/resources/icons/left_half_circle.png b/resources/icons/left_half_circle.png new file mode 100644 index 0000000000..3bdc4c3eef Binary files /dev/null and b/resources/icons/left_half_circle.png differ diff --git a/resources/icons/mode_expert.png b/resources/icons/mode_expert.png new file mode 100644 index 0000000000..1716bab44b Binary files /dev/null and b/resources/icons/mode_expert.png differ diff --git a/resources/icons/mode_expert_.png b/resources/icons/mode_expert_.png new file mode 100644 index 0000000000..4d78bcccf5 Binary files /dev/null and b/resources/icons/mode_expert_.png differ diff --git a/resources/icons/mode_middle.png b/resources/icons/mode_middle.png new file mode 100644 index 0000000000..b08f73ca65 Binary files /dev/null and b/resources/icons/mode_middle.png differ diff --git a/resources/icons/mode_middle_.png b/resources/icons/mode_middle_.png new file mode 100644 index 0000000000..d98d8f7091 Binary files /dev/null and b/resources/icons/mode_middle_.png differ diff --git a/resources/icons/mode_simple.png b/resources/icons/mode_simple.png new file mode 100644 index 0000000000..8bc300228d Binary files /dev/null and b/resources/icons/mode_simple.png differ diff --git a/resources/icons/mode_simple_.png b/resources/icons/mode_simple_.png new file mode 100644 index 0000000000..aac2b61b04 Binary files /dev/null and b/resources/icons/mode_simple_.png differ diff --git a/resources/icons/object.png b/resources/icons/object.png new file mode 100644 index 0000000000..c85cbaf2fa Binary files /dev/null and b/resources/icons/object.png differ diff --git a/resources/icons/one_layer_lock_off.png b/resources/icons/one_layer_lock_off.png new file mode 100644 index 0000000000..f6e61d058c Binary files /dev/null and b/resources/icons/one_layer_lock_off.png differ diff --git a/resources/icons/one_layer_lock_on.png b/resources/icons/one_layer_lock_on.png new file mode 100644 index 0000000000..0110999721 Binary files /dev/null and b/resources/icons/one_layer_lock_on.png differ diff --git a/resources/icons/one_layer_unlock_off.png b/resources/icons/one_layer_unlock_off.png new file mode 100644 index 0000000000..46fabfb050 Binary files /dev/null and b/resources/icons/one_layer_unlock_off.png differ diff --git a/resources/icons/one_layer_unlock_on.png b/resources/icons/one_layer_unlock_on.png new file mode 100644 index 0000000000..265b926101 Binary files /dev/null and b/resources/icons/one_layer_unlock_on.png differ diff --git a/resources/icons/overlay/cut_hover.png b/resources/icons/overlay/cut_hover.png new file mode 100644 index 0000000000..99dc4cf8db Binary files /dev/null and b/resources/icons/overlay/cut_hover.png differ diff --git a/resources/icons/overlay/cut_off.png b/resources/icons/overlay/cut_off.png new file mode 100644 index 0000000000..cd4f130e1a Binary files /dev/null and b/resources/icons/overlay/cut_off.png differ diff --git a/resources/icons/overlay/cut_on.png b/resources/icons/overlay/cut_on.png new file mode 100644 index 0000000000..7e78e0e6a3 Binary files /dev/null and b/resources/icons/overlay/cut_on.png differ diff --git a/resources/icons/overlay/move_hover.png b/resources/icons/overlay/move_hover.png new file mode 100644 index 0000000000..99dc4cf8db Binary files /dev/null and b/resources/icons/overlay/move_hover.png differ diff --git a/resources/icons/overlay/move_off.png b/resources/icons/overlay/move_off.png new file mode 100644 index 0000000000..cd4f130e1a Binary files /dev/null and b/resources/icons/overlay/move_off.png differ diff --git a/resources/icons/overlay/move_on.png b/resources/icons/overlay/move_on.png new file mode 100644 index 0000000000..7e78e0e6a3 Binary files /dev/null and b/resources/icons/overlay/move_on.png differ diff --git a/resources/icons/overlay/sla_support_points_hover.png b/resources/icons/overlay/sla_support_points_hover.png new file mode 100644 index 0000000000..6303232b24 Binary files /dev/null and b/resources/icons/overlay/sla_support_points_hover.png differ diff --git a/resources/icons/overlay/sla_support_points_off.png b/resources/icons/overlay/sla_support_points_off.png new file mode 100644 index 0000000000..b654366d57 Binary files /dev/null and b/resources/icons/overlay/sla_support_points_off.png differ diff --git a/resources/icons/overlay/sla_support_points_on.png b/resources/icons/overlay/sla_support_points_on.png new file mode 100644 index 0000000000..8c1e695650 Binary files /dev/null and b/resources/icons/overlay/sla_support_points_on.png differ diff --git a/resources/icons/printers/PrusaResearch_SL1.png b/resources/icons/printers/PrusaResearch_SL1.png new file mode 100644 index 0000000000..281d7a6202 Binary files /dev/null and b/resources/icons/printers/PrusaResearch_SL1.png differ diff --git a/resources/icons/right_half_circle.png b/resources/icons/right_half_circle.png new file mode 100644 index 0000000000..ecc8980b32 Binary files /dev/null and b/resources/icons/right_half_circle.png differ diff --git a/resources/icons/shape_ungroup_o.png b/resources/icons/shape_ungroup_o.png new file mode 100644 index 0000000000..97746d32cb Binary files /dev/null and b/resources/icons/shape_ungroup_o.png differ diff --git a/resources/icons/shape_ungroup_p.png b/resources/icons/shape_ungroup_p.png new file mode 100644 index 0000000000..81c0198ff1 Binary files /dev/null and b/resources/icons/shape_ungroup_p.png differ diff --git a/resources/icons/split.png b/resources/icons/split.png new file mode 100644 index 0000000000..c5680ab916 Binary files /dev/null and b/resources/icons/split.png differ diff --git a/resources/icons/support_blocker_.png b/resources/icons/support_blocker_.png new file mode 100644 index 0000000000..fc658e4f63 Binary files /dev/null and b/resources/icons/support_blocker_.png differ diff --git a/resources/icons/support_enforcer_.png b/resources/icons/support_enforcer_.png new file mode 100644 index 0000000000..ac34b6cb05 Binary files /dev/null and b/resources/icons/support_enforcer_.png differ diff --git a/resources/icons/toolbar.png b/resources/icons/toolbar.png new file mode 100644 index 0000000000..660be1a5dd Binary files /dev/null and b/resources/icons/toolbar.png differ diff --git a/resources/icons/up_half_circle.png b/resources/icons/up_half_circle.png new file mode 100644 index 0000000000..aac6e32c3f Binary files /dev/null and b/resources/icons/up_half_circle.png differ diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index f6befe005a..e440b35df8 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -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 diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 95859a67fc..d5425dc12a 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -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,7 +1463,25 @@ 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" -filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm" \ No newline at end of file +filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm" diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt new file mode 100644 index 0000000000..5905c438e9 --- /dev/null +++ b/sandboxes/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(slabasebed) +add_subdirectory(slasupporttree) diff --git a/sandboxes/slabasebed/CMakeLists.txt b/sandboxes/slabasebed/CMakeLists.txt new file mode 100644 index 0000000000..bff5ca5887 --- /dev/null +++ b/sandboxes/slabasebed/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(slabasebed EXCLUDE_FROM_ALL slabasebed.cpp) +target_link_libraries(slabasebed libslic3r) \ No newline at end of file diff --git a/sandboxes/slabasebed/slabasebed.cpp b/sandboxes/slabasebed/slabasebed.cpp new file mode 100644 index 0000000000..255ce2cc34 --- /dev/null +++ b/sandboxes/slabasebed/slabasebed.cpp @@ -0,0 +1,43 @@ +#include +#include + +#include +#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; +} diff --git a/sandboxes/slasupporttree/CMakeLists.txt b/sandboxes/slasupporttree/CMakeLists.txt new file mode 100644 index 0000000000..a3323a83de --- /dev/null +++ b/sandboxes/slasupporttree/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(slasupporttree EXCLUDE_FROM_ALL slasupporttree.cpp) +target_link_libraries(slasupporttree libslic3r) \ No newline at end of file diff --git a/sandboxes/slasupporttree/slasupporttree.cpp b/sandboxes/slasupporttree/slasupporttree.cpp new file mode 100644 index 0000000000..0cfc60f7aa --- /dev/null +++ b/sandboxes/slasupporttree/slasupporttree.cpp @@ -0,0 +1,48 @@ +#include +#include + +#include +#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; +} diff --git a/slic3r.pl b/slic3r.pl index d9bed0ab66..95427443b3 100755 --- a/slic3r.pl +++ b/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. diff --git a/slic3r.sublime-project b/slic3r.sublime-project index 3f2fc36dc2..785dddba41 100644 --- a/slic3r.sublime-project +++ b/slic3r.sublime-project @@ -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" ] } ], diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000000..32d2c5b03f --- /dev/null +++ b/src/CMakeLists.txt @@ -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() diff --git a/src/Shiny/CMakeLists.txt b/src/Shiny/CMakeLists.txt new file mode 100644 index 0000000000..8be7592ae5 --- /dev/null +++ b/src/Shiny/CMakeLists.txt @@ -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 +) diff --git a/xs/src/Shiny/Shiny.h b/src/Shiny/Shiny.h similarity index 100% rename from xs/src/Shiny/Shiny.h rename to src/Shiny/Shiny.h diff --git a/xs/src/Shiny/ShinyConfig.h b/src/Shiny/ShinyConfig.h similarity index 100% rename from xs/src/Shiny/ShinyConfig.h rename to src/Shiny/ShinyConfig.h diff --git a/xs/src/Shiny/ShinyData.h b/src/Shiny/ShinyData.h similarity index 100% rename from xs/src/Shiny/ShinyData.h rename to src/Shiny/ShinyData.h diff --git a/xs/src/Shiny/ShinyMacros.h b/src/Shiny/ShinyMacros.h similarity index 100% rename from xs/src/Shiny/ShinyMacros.h rename to src/Shiny/ShinyMacros.h diff --git a/xs/src/Shiny/ShinyManager.c b/src/Shiny/ShinyManager.c similarity index 100% rename from xs/src/Shiny/ShinyManager.c rename to src/Shiny/ShinyManager.c diff --git a/xs/src/Shiny/ShinyManager.h b/src/Shiny/ShinyManager.h similarity index 100% rename from xs/src/Shiny/ShinyManager.h rename to src/Shiny/ShinyManager.h diff --git a/xs/src/Shiny/ShinyNode.c b/src/Shiny/ShinyNode.c similarity index 100% rename from xs/src/Shiny/ShinyNode.c rename to src/Shiny/ShinyNode.c diff --git a/xs/src/Shiny/ShinyNode.h b/src/Shiny/ShinyNode.h similarity index 100% rename from xs/src/Shiny/ShinyNode.h rename to src/Shiny/ShinyNode.h diff --git a/xs/src/Shiny/ShinyNodePool.c b/src/Shiny/ShinyNodePool.c similarity index 100% rename from xs/src/Shiny/ShinyNodePool.c rename to src/Shiny/ShinyNodePool.c diff --git a/xs/src/Shiny/ShinyNodePool.h b/src/Shiny/ShinyNodePool.h similarity index 100% rename from xs/src/Shiny/ShinyNodePool.h rename to src/Shiny/ShinyNodePool.h diff --git a/xs/src/Shiny/ShinyNodeState.c b/src/Shiny/ShinyNodeState.c similarity index 100% rename from xs/src/Shiny/ShinyNodeState.c rename to src/Shiny/ShinyNodeState.c diff --git a/xs/src/Shiny/ShinyNodeState.h b/src/Shiny/ShinyNodeState.h similarity index 100% rename from xs/src/Shiny/ShinyNodeState.h rename to src/Shiny/ShinyNodeState.h diff --git a/xs/src/Shiny/ShinyOutput.c b/src/Shiny/ShinyOutput.c similarity index 100% rename from xs/src/Shiny/ShinyOutput.c rename to src/Shiny/ShinyOutput.c diff --git a/xs/src/Shiny/ShinyOutput.h b/src/Shiny/ShinyOutput.h similarity index 100% rename from xs/src/Shiny/ShinyOutput.h rename to src/Shiny/ShinyOutput.h diff --git a/xs/src/Shiny/ShinyPrereqs.h b/src/Shiny/ShinyPrereqs.h similarity index 100% rename from xs/src/Shiny/ShinyPrereqs.h rename to src/Shiny/ShinyPrereqs.h diff --git a/xs/src/Shiny/ShinyTools.c b/src/Shiny/ShinyTools.c similarity index 100% rename from xs/src/Shiny/ShinyTools.c rename to src/Shiny/ShinyTools.c diff --git a/xs/src/Shiny/ShinyTools.h b/src/Shiny/ShinyTools.h similarity index 100% rename from xs/src/Shiny/ShinyTools.h rename to src/Shiny/ShinyTools.h diff --git a/xs/src/Shiny/ShinyVersion.h b/src/Shiny/ShinyVersion.h similarity index 100% rename from xs/src/Shiny/ShinyVersion.h rename to src/Shiny/ShinyVersion.h diff --git a/xs/src/Shiny/ShinyZone.c b/src/Shiny/ShinyZone.c similarity index 100% rename from xs/src/Shiny/ShinyZone.c rename to src/Shiny/ShinyZone.c diff --git a/xs/src/Shiny/ShinyZone.h b/src/Shiny/ShinyZone.h similarity index 100% rename from xs/src/Shiny/ShinyZone.h rename to src/Shiny/ShinyZone.h diff --git a/src/admesh/CMakeLists.txt b/src/admesh/CMakeLists.txt new file mode 100644 index 0000000000..44c97c3f1b --- /dev/null +++ b/src/admesh/CMakeLists.txt @@ -0,0 +1,12 @@ +project(admesh) +cmake_minimum_required(VERSION 2.6) + +add_library(admesh STATIC + connect.cpp + normals.cpp + shared.cpp + stl.h + stl_io.cpp + stlinit.cpp + util.cpp +) diff --git a/xs/src/admesh/connect.cpp b/src/admesh/connect.cpp similarity index 73% rename from xs/src/admesh/connect.cpp rename to src/admesh/connect.cpp index e9129d0079..166eec3302 100644 --- a/xs/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -25,11 +25,14 @@ #include #include +#include +#include + +#include + #include "stl.h" -static void stl_match_neighbors_exact(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b); static void stl_match_neighbors_nearby(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b); static void stl_record_neighbors(stl_file *stl, @@ -43,7 +46,6 @@ static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, void (*match_neighbors)(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b)); -static int stl_get_hash_for_edge(int M, stl_hash_edge *edge); static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b); static void stl_free_edges(stl_file *stl); static void stl_remove_facet(stl_file *stl, int facet_number); @@ -82,37 +84,20 @@ stl_check_facets_exact(stl_file *stl) { for(i = 0; i < stl->stats.number_of_facets; i++) { facet = stl->facet_start[i]; - // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. - // When using a memcmp on raw floats, those numbers report to be different. - // Unify all +0 and -0 to +0 to make the floats equal under memcmp. - { - uint32_t *f = (uint32_t*)&facet; - for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats - if (*f == 0x80000000) - // Negative zero, switch to positive zero. - *f = 0; - } - - /* If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet. */ - if( !memcmp(&facet.vertex[0], &facet.vertex[1], - sizeof(stl_vertex)) - || !memcmp(&facet.vertex[1], &facet.vertex[2], - sizeof(stl_vertex)) - || !memcmp(&facet.vertex[0], &facet.vertex[2], - sizeof(stl_vertex))) { + // If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet. + if (facet.vertex[0] == facet.vertex[1] || + facet.vertex[1] == facet.vertex[2] || + facet.vertex[0] == facet.vertex[2]) { stl->stats.degenerate_facets += 1; stl_remove_facet(stl, i); - i--; + -- i; continue; - } for(j = 0; j < 3; j++) { edge.facet_number = i; edge.which_edge = j; - stl_load_edge_exact(stl, &edge, &facet.vertex[j], - &facet.vertex[(j + 1) % 3]); - - insert_hash_edge(stl, edge, stl_match_neighbors_exact); + stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]); + insert_hash_edge(stl, edge, stl_record_neighbors); } } stl_free_edges(stl); @@ -131,28 +116,43 @@ stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, if (stl->error) return; { - float diff_x = ABS(a->x - b->x); - float diff_y = ABS(a->y - b->y); - float diff_z = ABS(a->z - b->z); - float max_diff = STL_MAX(diff_x, diff_y); - max_diff = STL_MAX(diff_z, max_diff); - stl->stats.shortest_edge = STL_MIN(max_diff, stl->stats.shortest_edge); + stl_vertex diff = (*a - *b).cwiseAbs(); + float max_diff = std::max(diff(0), std::max(diff(1), diff(2))); + stl->stats.shortest_edge = std::min(max_diff, stl->stats.shortest_edge); } // Ensure identical vertex ordering of equal edges. // This method is numerically robust. - if ((a->x != b->x) ? - (a->x < b->x) : - ((a->y != b->y) ? - (a->y < b->y) : - (a->z < b->z))) { - memcpy(&edge->key[0], a, sizeof(stl_vertex)); - memcpy(&edge->key[3], b, sizeof(stl_vertex)); + if (stl_vertex_lower(*a, *b)) { } else { - memcpy(&edge->key[0], b, sizeof(stl_vertex)); - memcpy(&edge->key[3], a, sizeof(stl_vertex)); + std::swap(a, b); edge->which_edge += 3; /* this edge is loaded backwards */ } + memcpy(&edge->key[0], a->data(), sizeof(stl_vertex)); + memcpy(&edge->key[3], b->data(), sizeof(stl_vertex)); + // Switch negative zeros to positive zeros, so memcmp will consider them to be equal. + for (size_t i = 0; i < 6; ++ i) { + unsigned char *p = (unsigned char*)(edge->key + i); +#ifdef BOOST_LITTLE_ENDIAN + if (p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0x80) + // Negative zero, switch to positive zero. + p[3] = 0; +#else /* BOOST_LITTLE_ENDIAN */ + if (p[0] == 0x80 && p[1] == 0 && p[2] == 0 && p[3] == 0) + // Negative zero, switch to positive zero. + p[0] = 0; +#endif /* BOOST_LITTLE_ENDIAN */ + } +} + +static inline size_t hash_size_from_nr_faces(const size_t nr_faces) +{ + // Good primes for addressing a cca. 30 bit space. + // https://planetmath.org/goodhashtableprimes + static std::vector primes{ 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 }; + // Find a prime number for 50% filling of the shared triangle edges in the mesh. + auto it = std::upper_bound(primes.begin(), primes.end(), nr_faces * 3 * 2 - 1); + return (it == primes.end()) ? primes.back() : *it; } static void @@ -165,10 +165,9 @@ stl_initialize_facet_check_exact(stl_file *stl) { stl->stats.freed = 0; stl->stats.collisions = 0; + stl->M = hash_size_from_nr_faces(stl->stats.number_of_facets); - stl->M = 81397; - - for(i = 0; i < stl->stats.number_of_facets ; i++) { + for (i = 0; i < stl->stats.number_of_facets ; i++) { /* initialize neighbors list to -1 to mark unconnected edges */ stl->neighbors_start[i].neighbor[0] = -1; stl->neighbors_start[i].neighbor[1] = -1; @@ -188,21 +187,17 @@ stl_initialize_facet_check_exact(stl_file *stl) { } } -static void -insert_hash_edge(stl_file *stl, stl_hash_edge edge, +static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, void (*match_neighbors)(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b)) { - stl_hash_edge *link; - stl_hash_edge *new_edge; - stl_hash_edge *temp; - int chain_number; - + stl_hash_edge *edge_a, stl_hash_edge *edge_b)) +{ if (stl->error) return; - chain_number = stl_get_hash_for_edge(stl->M, &edge); - - link = stl->heads[chain_number]; + int chain_number = edge.hash(stl->M); + stl_hash_edge *link = stl->heads[chain_number]; + stl_hash_edge *new_edge; + stl_hash_edge *temp; if(link == stl->tail) { /* This list doesn't have any edges currently in it. Add this one. */ new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); @@ -252,30 +247,17 @@ insert_hash_edge(stl_file *stl, stl_hash_edge edge, } } - -static int -stl_get_hash_for_edge(int M, stl_hash_edge *edge) { - return ((edge->key[0] / 23 + edge->key[1] / 19 + edge->key[2] / 17 - + edge->key[3] /13 + edge->key[4] / 11 + edge->key[5] / 7 ) % M); +// Return 1 if the edges are not matched. +static inline int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b) +{ + // Don't match edges of the same facet + return (edge_a->facet_number == edge_b->facet_number) || (*edge_a != *edge_b); } -static int -stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b) { - if(edge_a->facet_number == edge_b->facet_number) { - return 1; /* Don't match edges of the same facet */ - } else { - return memcmp(edge_a, edge_b, SIZEOF_EDGE_SORT); - } -} - -void -stl_check_facets_nearby(stl_file *stl, float tolerance) { - stl_hash_edge edge[3]; - stl_facet facet; - int i; - int j; - - if (stl->error) return; +void stl_check_facets_nearby(stl_file *stl, float tolerance) +{ + if (stl->error) + return; if( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets) && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets) @@ -286,27 +268,19 @@ stl_check_facets_nearby(stl_file *stl, float tolerance) { stl_initialize_facet_check_nearby(stl); - for(i = 0; i < stl->stats.number_of_facets; i++) { - facet = stl->facet_start[i]; - // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. - // When using a memcmp on raw floats, those numbers report to be different. - // Unify all +0 and -0 to +0 to make the floats equal under memcmp. - { - uint32_t *f = (uint32_t*)&facet; - for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats - if (*f == 0x80000000) - // Negative zero, switch to positive zero. - *f = 0; - } - for(j = 0; j < 3; j++) { + for (int i = 0; i < stl->stats.number_of_facets; ++ i) { + //FIXME is the copy necessary? + stl_facet facet = stl->facet_start[i]; + for (int j = 0; j < 3; j++) { if(stl->neighbors_start[i].neighbor[j] == -1) { - edge[j].facet_number = i; - edge[j].which_edge = j; - if(stl_load_edge_nearby(stl, &edge[j], &facet.vertex[j], + stl_hash_edge edge; + edge.facet_number = i; + edge.which_edge = j; + if(stl_load_edge_nearby(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3], tolerance)) { /* only insert edges that have different keys */ - insert_hash_edge(stl, edge[j], stl_match_neighbors_nearby); + insert_hash_edge(stl, edge, stl_match_neighbors_nearby); } } } @@ -315,27 +289,17 @@ stl_check_facets_nearby(stl_file *stl, float tolerance) { stl_free_edges(stl); } -static int -stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, - stl_vertex *a, stl_vertex *b, float tolerance) { +static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, stl_vertex *a, stl_vertex *b, float tolerance) +{ // Index of a grid cell spaced by tolerance. - uint32_t vertex1[3] = { - (uint32_t)((a->x - stl->stats.min.x) / tolerance), - (uint32_t)((a->y - stl->stats.min.y) / tolerance), - (uint32_t)((a->z - stl->stats.min.z) / tolerance) - }; - uint32_t vertex2[3] = { - (uint32_t)((b->x - stl->stats.min.x) / tolerance), - (uint32_t)((b->y - stl->stats.min.y) / tolerance), - (uint32_t)((b->z - stl->stats.min.z) / tolerance) - }; + typedef Eigen::Matrix Vec3i; + Vec3i vertex1 = (*a / tolerance).cast(); + Vec3i vertex2 = (*b / tolerance).cast(); + static_assert(sizeof(Vec3i) == 12, "size of Vec3i incorrect"); - if( (vertex1[0] == vertex2[0]) - && (vertex1[1] == vertex2[1]) - && (vertex1[2] == vertex2[2])) { - /* Both vertices hash to the same value */ + if (vertex1 == vertex2) + // Both vertices hash to the same value return 0; - } // Ensure identical vertex ordering of edges, which vertices land into equal grid cells. // This method is numerically robust. @@ -344,30 +308,27 @@ stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, ((vertex1[1] != vertex2[1]) ? (vertex1[1] < vertex2[1]) : (vertex1[2] < vertex2[2]))) { - memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); + memcpy(&edge->key[0], vertex1.data(), sizeof(stl_vertex)); + memcpy(&edge->key[3], vertex2.data(), sizeof(stl_vertex)); } else { - memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); + memcpy(&edge->key[0], vertex2.data(), sizeof(stl_vertex)); + memcpy(&edge->key[3], vertex1.data(), sizeof(stl_vertex)); edge->which_edge += 3; /* this edge is loaded backwards */ } return 1; } -static void -stl_free_edges(stl_file *stl) { - int i; - stl_hash_edge *temp; - - if (stl->error) return; +static void stl_free_edges(stl_file *stl) +{ + if (stl->error) + return; if(stl->stats.malloced != stl->stats.freed) { - for(i = 0; i < stl->M; i++) { - for(temp = stl->heads[i]; stl->heads[i] != stl->tail; - temp = stl->heads[i]) { + for (int i = 0; i < stl->M; i++) { + for (stl_hash_edge *temp = stl->heads[i]; stl->heads[i] != stl->tail; temp = stl->heads[i]) { stl->heads[i] = stl->heads[i]->next; free(temp); - stl->stats.freed++; + ++ stl->stats.freed; } } } @@ -375,8 +336,8 @@ stl_free_edges(stl_file *stl) { free(stl->tail); } -static void -stl_initialize_facet_check_nearby(stl_file *stl) { +static void stl_initialize_facet_check_nearby(stl_file *stl) +{ int i; if (stl->error) return; @@ -389,7 +350,7 @@ stl_initialize_facet_check_nearby(stl_file *stl) { /* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/ /* tolerance *= 0.5;*/ - stl->M = 81397; + stl->M = hash_size_from_nr_faces(stl->stats.number_of_facets); stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby"); @@ -467,16 +428,8 @@ stl_record_neighbors(stl_file *stl, } } -static void -stl_match_neighbors_exact(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b) { - if (stl->error) return; - stl_record_neighbors(stl, edge_a, edge_b); -} - -static void -stl_match_neighbors_nearby(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b) { +static void stl_match_neighbors_nearby(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b) +{ int facet1; int facet2; int vertex1; @@ -517,9 +470,7 @@ stl_match_neighbors_nearby(stl_file *stl, } -static void -stl_change_vertices(stl_file *stl, int facet_num, int vnot, - stl_vertex new_vertex) { +static void stl_change_vertices(stl_file *stl, int facet_num, int vnot, stl_vertex new_vertex) { int first_facet; int direction; int next_edge; @@ -551,30 +502,30 @@ stl_change_vertices(stl_file *stl, int facet_num, int vnot, } } #if 0 - if (stl->facet_start[facet_num].vertex[pivot_vertex].x == new_vertex.x && - stl->facet_start[facet_num].vertex[pivot_vertex].y == new_vertex.y && - stl->facet_start[facet_num].vertex[pivot_vertex].z == new_vertex.z) + if (stl->facet_start[facet_num].vertex[pivot_vertex](0) == new_vertex(0) && + stl->facet_start[facet_num].vertex[pivot_vertex](1) == new_vertex(1) && + stl->facet_start[facet_num].vertex[pivot_vertex](2) == new_vertex(2)) printf("Changing vertex %f,%f,%f: Same !!!\r\n", - new_vertex.x, new_vertex.y, new_vertex.z); + new_vertex(0), new_vertex(1), new_vertex(2)); else { - if (stl->facet_start[facet_num].vertex[pivot_vertex].x != new_vertex.x) + if (stl->facet_start[facet_num].vertex[pivot_vertex](0) != new_vertex(0)) printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex].x, - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].x), - new_vertex.x, - *reinterpret_cast(&new_vertex.x)); - if (stl->facet_start[facet_num].vertex[pivot_vertex].y != new_vertex.y) + stl->facet_start[facet_num].vertex[pivot_vertex](0), + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](0)), + new_vertex(0), + *reinterpret_cast(&new_vertex(0))); + if (stl->facet_start[facet_num].vertex[pivot_vertex](1) != new_vertex(1)) printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex].y, - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].y), - new_vertex.y, - *reinterpret_cast(&new_vertex.y)); - if (stl->facet_start[facet_num].vertex[pivot_vertex].z != new_vertex.z) + stl->facet_start[facet_num].vertex[pivot_vertex](1), + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](1)), + new_vertex(1), + *reinterpret_cast(&new_vertex(1))); + if (stl->facet_start[facet_num].vertex[pivot_vertex](2) != new_vertex(2)) printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex].z, - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].z), - new_vertex.z, - *reinterpret_cast(&new_vertex.z)); + stl->facet_start[facet_num].vertex[pivot_vertex](2), + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](2)), + new_vertex(2), + *reinterpret_cast(&new_vertex(2))); } #endif stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex; @@ -595,7 +546,6 @@ Try using a smaller tolerance or don't do a nearby check\n"); } } - static void stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b, int *facet1, int *vertex1, @@ -622,11 +572,10 @@ stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, v1b = (edge_b->which_edge + 1) % 3; } - /* Of the first pair, which vertex, if any, should be changed */ - if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v1a], - &stl->facet_start[edge_b->facet_number].vertex[v1b], - sizeof(stl_vertex))) { - /* These facets are already equal. No need to change. */ + // Of the first pair, which vertex, if any, should be changed + if(stl->facet_start[edge_a->facet_number].vertex[v1a] == + stl->facet_start[edge_b->facet_number].vertex[v1b]) { + // These facets are already equal. No need to change. *facet1 = -1; } else { if( (stl->neighbors_start[edge_a->facet_number].neighbor[v1a] == -1) @@ -644,10 +593,9 @@ stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, } /* Of the second pair, which vertex, if any, should be changed */ - if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v2a], - &stl->facet_start[edge_b->facet_number].vertex[v2b], - sizeof(stl_vertex))) { - /* These facets are already equal. No need to change. */ + if(stl->facet_start[edge_a->facet_number].vertex[v2a] == + stl->facet_start[edge_b->facet_number].vertex[v2b]) { + // These facets are already equal. No need to change. *facet2 = -1; } else { if( (stl->neighbors_start[edge_a->facet_number].neighbor[v2a] == -1) @@ -718,40 +666,35 @@ in stl_remove_facet: neighbor = %d numfacets = %d this is wrong\n", } } -void -stl_remove_unconnected_facets(stl_file *stl) { +void stl_remove_unconnected_facets(stl_file *stl) +{ /* A couple of things need to be done here. One is to remove any */ /* completely unconnected facets (0 edges connected) since these are */ /* useless and could be completely wrong. The second thing that needs to */ /* be done is to remove any degenerate facets that were created during */ /* stl_check_facets_nearby(). */ + if (stl->error) + return; - int i; - - if (stl->error) return; - - /* remove degenerate facets */ - for(i = 0; i < stl->stats.number_of_facets; i++) { - if( !memcmp(&stl->facet_start[i].vertex[0], - &stl->facet_start[i].vertex[1], sizeof(stl_vertex)) - || !memcmp(&stl->facet_start[i].vertex[1], - &stl->facet_start[i].vertex[2], sizeof(stl_vertex)) - || !memcmp(&stl->facet_start[i].vertex[0], - &stl->facet_start[i].vertex[2], sizeof(stl_vertex))) { + // remove degenerate facets + for (int i = 0; i < stl->stats.number_of_facets; ++ i) { + if(stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[1] || + stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[2] || + stl->facet_start[i].vertex[1] == stl->facet_start[i].vertex[2]) { stl_remove_degenerate(stl, i); i--; } } if(stl->stats.connected_facets_1_edge < stl->stats.number_of_facets) { - /* remove completely unconnected facets */ - for(i = 0; i < stl->stats.number_of_facets; i++) { - if( (stl->neighbors_start[i].neighbor[0] == -1) - && (stl->neighbors_start[i].neighbor[1] == -1) - && (stl->neighbors_start[i].neighbor[2] == -1)) { - /* This facet is completely unconnected. Remove it. */ + // remove completely unconnected facets + for (int i = 0; i < stl->stats.number_of_facets; i++) { + if (stl->neighbors_start[i].neighbor[0] == -1 && + stl->neighbors_start[i].neighbor[1] == -1 && + stl->neighbors_start[i].neighbor[2] == -1) { + // This facet is completely unconnected. Remove it. stl_remove_facet(stl, i); - i--; + -- i; } } } @@ -771,30 +714,24 @@ stl_remove_degenerate(stl_file *stl, int facet) { if (stl->error) return; - if( !memcmp(&stl->facet_start[facet].vertex[0], - &stl->facet_start[facet].vertex[1], sizeof(stl_vertex)) - && !memcmp(&stl->facet_start[facet].vertex[1], - &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) { + if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1] && + stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) { /* all 3 vertices are equal. Just remove the facet. I don't think*/ /* this is really possible, but just in case... */ printf("removing a facet in stl_remove_degenerate\n"); - stl_remove_facet(stl, facet); return; } - if(!memcmp(&stl->facet_start[facet].vertex[0], - &stl->facet_start[facet].vertex[1], sizeof(stl_vertex))) { + if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1]) { edge1 = 1; edge2 = 2; edge3 = 0; - } else if(!memcmp(&stl->facet_start[facet].vertex[1], - &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) { + } else if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) { edge1 = 0; edge2 = 2; edge3 = 1; - } else if(!memcmp(&stl->facet_start[facet].vertex[2], - &stl->facet_start[facet].vertex[0], sizeof(stl_vertex))) { + } else if (stl->facet_start[facet].vertex[2] == stl->facet_start[facet].vertex[0]) { edge1 = 0; edge2 = 1; edge3 = 2; @@ -883,7 +820,7 @@ stl_fill_holes(stl_file *stl) { stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]); - insert_hash_edge(stl, edge, stl_match_neighbors_exact); + insert_hash_edge(stl, edge, stl_record_neighbors); } } @@ -939,7 +876,7 @@ stl_fill_holes(stl_file *stl) { stl_load_edge_exact(stl, &edge, &new_facet.vertex[k], &new_facet.vertex[(k + 1) % 3]); - insert_hash_edge(stl, edge, stl_match_neighbors_exact); + insert_hash_edge(stl, edge, stl_record_neighbors); } break; } else { @@ -977,9 +914,7 @@ stl_add_facet(stl_file *stl, stl_facet *new_facet) { stl->facet_start[stl->stats.number_of_facets] = *new_facet; /* note that the normal vector is not set here, just initialized to 0 */ - stl->facet_start[stl->stats.number_of_facets].normal.x = 0.0; - stl->facet_start[stl->stats.number_of_facets].normal.y = 0.0; - stl->facet_start[stl->stats.number_of_facets].normal.z = 0.0; + stl->facet_start[stl->stats.number_of_facets].normal = stl_normal::Zero(); stl->neighbors_start[stl->stats.number_of_facets].neighbor[0] = -1; stl->neighbors_start[stl->stats.number_of_facets].neighbor[1] = -1; diff --git a/xs/src/admesh/normals.cpp b/src/admesh/normals.cpp similarity index 78% rename from xs/src/admesh/normals.cpp rename to src/admesh/normals.cpp index b7cf9a8ab7..a8faa44bd2 100644 --- a/xs/src/admesh/normals.cpp +++ b/src/admesh/normals.cpp @@ -27,12 +27,6 @@ #include "stl.h" -static void stl_reverse_vector(float v[]) { - v[0] *= -1; - v[1] *= -1; - v[2] *= -1; -} - static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag); static void @@ -228,102 +222,52 @@ static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_ /* Returns 2 if the normal is not within tolerance and backwards */ /* Returns 4 if the status is unknown. */ - float normal[3]; - float test_norm[3]; stl_facet *facet; facet = &stl->facet_start[facet_num]; + stl_normal normal; stl_calculate_normal(normal, facet); stl_normalize_vector(normal); + stl_normal normal_dif = (normal - facet->normal).cwiseAbs(); - if( (ABS(normal[0] - facet->normal.x) < 0.001) - && (ABS(normal[1] - facet->normal.y) < 0.001) - && (ABS(normal[2] - facet->normal.z) < 0.001)) { + const float eps = 0.001f; + if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { /* It is not really necessary to change the values here */ /* but just for consistency, I will. */ - facet->normal.x = normal[0]; - facet->normal.y = normal[1]; - facet->normal.z = normal[2]; + facet->normal = normal; return 0; } - test_norm[0] = facet->normal.x; - test_norm[1] = facet->normal.y; - test_norm[2] = facet->normal.z; - + stl_normal test_norm = facet->normal; stl_normalize_vector(test_norm); - if( (ABS(normal[0] - test_norm[0]) < 0.001) - && (ABS(normal[1] - test_norm[1]) < 0.001) - && (ABS(normal[2] - test_norm[2]) < 0.001)) { + normal_dif = (normal - test_norm).cwiseAbs(); + if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { if(normal_fix_flag) { - facet->normal.x = normal[0]; - facet->normal.y = normal[1]; - facet->normal.z = normal[2]; + facet->normal = normal; stl->stats.normals_fixed += 1; } return 1; } - stl_reverse_vector(test_norm); - if( (ABS(normal[0] - test_norm[0]) < 0.001) - && (ABS(normal[1] - test_norm[1]) < 0.001) - && (ABS(normal[2] - test_norm[2]) < 0.001)) { - /* Facet is backwards. */ + test_norm *= -1.f; + normal_dif = (normal - test_norm).cwiseAbs(); + if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { + // Facet is backwards. if(normal_fix_flag) { - facet->normal.x = normal[0]; - facet->normal.y = normal[1]; - facet->normal.z = normal[2]; + facet->normal = normal; stl->stats.normals_fixed += 1; } return 2; } if(normal_fix_flag) { - facet->normal.x = normal[0]; - facet->normal.y = normal[1]; - facet->normal.z = normal[2]; + facet->normal = normal; stl->stats.normals_fixed += 1; } return 4; } -void stl_calculate_normal(float normal[], stl_facet *facet) { - float v1[3] = { - facet->vertex[1].x - facet->vertex[0].x, - facet->vertex[1].y - facet->vertex[0].y, - facet->vertex[1].z - facet->vertex[0].z - }; - float v2[3] = { - facet->vertex[2].x - facet->vertex[0].x, - facet->vertex[2].y - facet->vertex[0].y, - facet->vertex[2].z - facet->vertex[0].z - }; - normal[0] = (float)((double)v1[1] * (double)v2[2]) - ((double)v1[2] * (double)v2[1]); - normal[1] = (float)((double)v1[2] * (double)v2[0]) - ((double)v1[0] * (double)v2[2]); - normal[2] = (float)((double)v1[0] * (double)v2[1]) - ((double)v1[1] * (double)v2[0]); -} - -void stl_normalize_vector(float v[]) { - double length; - double factor; - float min_normal_length; - - length = sqrt((double)v[0] * (double)v[0] + (double)v[1] * (double)v[1] + (double)v[2] * (double)v[2]); - min_normal_length = 0.000000000001; - if(length < min_normal_length) { - v[0] = 0.0; - v[1] = 0.0; - v[2] = 0.0; - return; - } - factor = 1.0 / length; - v[0] *= factor; - v[1] *= factor; - v[2] *= factor; -} - -void -stl_fix_normal_values(stl_file *stl) { +void stl_fix_normal_values(stl_file *stl) { int i; if (stl->error) return; @@ -333,20 +277,16 @@ stl_fix_normal_values(stl_file *stl) { } } -void -stl_reverse_all_facets(stl_file *stl) { - int i; - float normal[3]; +void stl_reverse_all_facets(stl_file *stl) +{ + if (stl->error) + return; - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { + stl_normal normal; + for(int i = 0; i < stl->stats.number_of_facets; i++) { stl_reverse_facet(stl, i); stl_calculate_normal(normal, &stl->facet_start[i]); stl_normalize_vector(normal); - stl->facet_start[i].normal.x = normal[0]; - stl->facet_start[i].normal.y = normal[1]; - stl->facet_start[i].normal.z = normal[2]; + stl->facet_start[i].normal = normal; } } - diff --git a/xs/src/admesh/shared.cpp b/src/admesh/shared.cpp similarity index 95% rename from xs/src/admesh/shared.cpp rename to src/admesh/shared.cpp index 8080f3574e..91bb82e006 100644 --- a/xs/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -169,7 +169,7 @@ stl_write_off(stl_file *stl, char *file) { for(i = 0; i < stl->stats.shared_vertices; i++) { fprintf(fp, "\t%f %f %f\n", - stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); + stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); } for(i = 0; i < stl->stats.number_of_facets; i++) { fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0], @@ -216,10 +216,10 @@ stl_write_vrml(stl_file *stl, char *file) { for(i = 0; i < (stl->stats.shared_vertices - 1); i++) { fprintf(fp, "\t\t\t\t%f %f %f,\n", - stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); + stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); } fprintf(fp, "\t\t\t\t%f %f %f]\n", - stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); + stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); fprintf(fp, "\t\t}\n"); fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n"); fprintf(fp, "\t\t\tcoordIndex [\n"); @@ -254,7 +254,7 @@ void stl_write_obj (stl_file *stl, char *file) { } for (i = 0; i < stl->stats.shared_vertices; i++) { - fprintf(fp, "v %f %f %f\n", stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); + fprintf(fp, "v %f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); } for (i = 0; i < stl->stats.number_of_facets; i++) { fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1); diff --git a/xs/src/admesh/stl.h b/src/admesh/stl.h similarity index 82% rename from xs/src/admesh/stl.h rename to src/admesh/stl.h index dc0f7ae195..9c71f00f66 100644 --- a/xs/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -27,9 +27,7 @@ #include #include -#define STL_MAX(A,B) ((A)>(B)? (A):(B)) -#define STL_MIN(A,B) ((A)<(B)? (A):(B)) -#define ABS(X) ((X) < 0 ? -(X) : (X)) +#include // Size of the binary STL header, free form. #define LABEL_SIZE 80 @@ -39,31 +37,16 @@ #define HEADER_SIZE 84 #define STL_MIN_FILE_SIZE 284 #define ASCII_LINES_PER_FACET 7 -// Comparing an edge by memcmp, 2x3x4 bytes = 24 -#define SIZEOF_EDGE_SORT 24 - -typedef struct { - float x; - float y; - float z; -} stl_vertex; +typedef Eigen::Matrix stl_vertex; +typedef Eigen::Matrix stl_normal; static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); - -typedef struct { - float x; - float y; - float z; -} stl_normal; - static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); -typedef char stl_extra[2]; - typedef struct { stl_normal normal; stl_vertex vertex[3]; - stl_extra extra; + char extra[2]; } stl_facet; #define SIZEOF_STL_FACET 50 @@ -81,8 +64,12 @@ typedef struct { } stl_edge; typedef struct stl_hash_edge { - // Key of a hash edge: 2x binary copy of a floating point vertex. + // Key of a hash edge: sorted vertices of the edge. uint32_t key[6]; + // Compare two keys. + bool operator==(const stl_hash_edge &rhs) { return memcmp(key, rhs.key, sizeof(key)) == 0; } + bool operator!=(const stl_hash_edge &rhs) { return ! (*this == rhs); } + int hash(int M) const { return ((key[0] / 11 + key[1] / 7 + key[2] / 3) ^ (key[3] / 11 + key[4] / 7 + key[5] / 3)) % M; } // Index of a facet owning this edge. int facet_number; // Index of this edge inside the facet with an index of facet_number. @@ -91,8 +78,6 @@ typedef struct stl_hash_edge { struct stl_hash_edge *next; } stl_hash_edge; -static_assert(offsetof(stl_hash_edge, facet_number) == SIZEOF_EDGE_SORT, "size of stl_hash_edge.key incorrect"); - typedef struct { // Index of a neighbor facet. int neighbor[3]; @@ -179,8 +164,8 @@ extern void stl_fix_normal_values(stl_file *stl); extern void stl_reverse_all_facets(stl_file *stl); extern void stl_translate(stl_file *stl, float x, float y, float z); extern void stl_translate_relative(stl_file *stl, float x, float y, float z); -extern void stl_scale_versor(stl_file *stl, float versor[3]); -extern void stl_scale(stl_file *stl, float factor); +extern void stl_scale_versor(stl_file *stl, const stl_vertex &versor); +inline void stl_scale(stl_file *stl, float factor) { stl_scale_versor(stl, stl_vertex(factor, factor, factor)); } extern void stl_rotate_x(stl_file *stl, float angle); extern void stl_rotate_y(stl_file *stl, float angle); extern void stl_rotate_z(stl_file *stl, float angle); @@ -188,6 +173,7 @@ extern void stl_mirror_xy(stl_file *stl); extern void stl_mirror_yz(stl_file *stl); extern void stl_mirror_xz(stl_file *stl); extern void stl_transform(stl_file *stl, float *trafo3x4); +extern void stl_transform(stl_file *stl, const Eigen::Transform& t); extern void stl_open_merge(stl_file *stl, char *file); extern void stl_invalidate_shared_vertices(stl_file *stl); extern void stl_generate_shared_vertices(stl_file *stl); @@ -195,8 +181,20 @@ extern void stl_write_obj(stl_file *stl, char *file); extern void stl_write_off(stl_file *stl, char *file); extern void stl_write_dxf(stl_file *stl, char *file, char *label); extern void stl_write_vrml(stl_file *stl, char *file); -extern void stl_calculate_normal(float normal[], stl_facet *facet); -extern void stl_normalize_vector(float v[]); +inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) { + normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]); +} +inline void stl_normalize_vector(stl_normal &normal) { + double length = normal.cast().norm(); + if (length < 0.000000000001) + normal = stl_normal::Zero(); + else + normal *= float(1.0 / length); +} +inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) { + return (a(0) != b(0)) ? (a(0) < b(0)) : + ((a(1) != b(1)) ? (a(1) < b(1)) : (a(2) < b(2))); +} extern void stl_calculate_volume(stl_file *stl); extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag); @@ -204,8 +202,8 @@ extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int toler extern void stl_initialize(stl_file *stl); extern void stl_count_facets(stl_file *stl, const char *file); extern void stl_allocate(stl_file *stl); -extern void stl_read(stl_file *stl, int first_facet, int first); -extern void stl_facet_stats(stl_file *stl, stl_facet facet, int first); +extern void stl_read(stl_file *stl, int first_facet, bool first); +extern void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first); extern void stl_reallocate(stl_file *stl); extern void stl_add_facet(stl_file *stl, stl_facet *new_facet); extern void stl_get_size(stl_file *stl); diff --git a/xs/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp similarity index 82% rename from xs/src/admesh/stl_io.cpp rename to src/admesh/stl_io.cpp index 1603981fcb..81e29d2860 100644 --- a/xs/src/admesh/stl_io.cpp +++ b/src/admesh/stl_io.cpp @@ -44,9 +44,9 @@ stl_print_edges(stl_file *stl, FILE *file) { for(i = 0; i < edges_allocated; i++) { fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n", stl->edge_start[i].facet_number, - stl->edge_start[i].p1.x, stl->edge_start[i].p1.y, - stl->edge_start[i].p1.z, stl->edge_start[i].p2.x, - stl->edge_start[i].p2.y, stl->edge_start[i].p2.z); + stl->edge_start[i].p1(0), stl->edge_start[i].p1(1), + stl->edge_start[i].p1(2), stl->edge_start[i].p2(0), + stl->edge_start[i].p2(1), stl->edge_start[i].p2(2)); } } @@ -75,11 +75,11 @@ File type : ASCII STL file\n"); Header : %s\n", stl->stats.header); fprintf(file, "============== Size ==============\n"); fprintf(file, "Min X = % f, Max X = % f\n", - stl->stats.min.x, stl->stats.max.x); + stl->stats.min(0), stl->stats.max(0)); fprintf(file, "Min Y = % f, Max Y = % f\n", - stl->stats.min.y, stl->stats.max.y); + stl->stats.min(1), stl->stats.max(1)); fprintf(file, "Min Z = % f, Max Z = % f\n", - stl->stats.min.z, stl->stats.max.z); + stl->stats.min(2), stl->stats.max(2)); fprintf(file, "\ ========= Facet Status ========== Original ============ Final ====\n"); @@ -149,18 +149,18 @@ stl_write_ascii(stl_file *stl, const char *file, const char *label) { for(i = 0; i < stl->stats.number_of_facets; i++) { fprintf(fp, " facet normal % .8E % .8E % .8E\n", - stl->facet_start[i].normal.x, stl->facet_start[i].normal.y, - stl->facet_start[i].normal.z); + stl->facet_start[i].normal(0), stl->facet_start[i].normal(1), + stl->facet_start[i].normal(2)); fprintf(fp, " outer loop\n"); fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0].y, - stl->facet_start[i].vertex[0].z); + stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), + stl->facet_start[i].vertex[0](2)); fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1].y, - stl->facet_start[i].vertex[1].z); + stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), + stl->facet_start[i].vertex[1](2)); fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, - stl->facet_start[i].vertex[2].z); + stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), + stl->facet_start[i].vertex[2](2)); fprintf(fp, " endloop\n"); fprintf(fp, " endfacet\n"); } @@ -264,9 +264,9 @@ void stl_write_vertex(stl_file *stl, int facet, int vertex) { if (stl->error) return; printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet, - stl->facet_start[facet].vertex[vertex].x, - stl->facet_start[facet].vertex[vertex].y, - stl->facet_start[facet].vertex[vertex].z); + stl->facet_start[facet].vertex[vertex](0), + stl->facet_start[facet].vertex[vertex](1), + stl->facet_start[facet].vertex[vertex](2)); } void @@ -309,10 +309,10 @@ stl_write_quad_object(stl_file *stl, char *file) { int i; int j; char *error_msg; - stl_vertex connect_color; - stl_vertex uncon_1_color; - stl_vertex uncon_2_color; - stl_vertex uncon_3_color; + stl_vertex connect_color = stl_vertex::Zero(); + stl_vertex uncon_1_color = stl_vertex::Zero(); + stl_vertex uncon_2_color = stl_vertex::Zero(); + stl_vertex uncon_3_color = stl_vertex::Zero(); stl_vertex color; if (stl->error) return; @@ -330,19 +330,6 @@ stl_write_quad_object(stl_file *stl, char *file) { return; } - connect_color.x = 0.0; - connect_color.y = 0.0; - connect_color.z = 1.0; - uncon_1_color.x = 0.0; - uncon_1_color.y = 1.0; - uncon_1_color.z = 0.0; - uncon_2_color.x = 1.0; - uncon_2_color.y = 1.0; - uncon_2_color.z = 1.0; - uncon_3_color.x = 1.0; - uncon_3_color.y = 0.0; - uncon_3_color.z = 0.0; - fprintf(fp, "CQUAD\n"); for(i = 0; i < stl->stats.number_of_facets; i++) { j = ((stl->neighbors_start[i].neighbor[0] == -1) + @@ -358,21 +345,21 @@ stl_write_quad_object(stl_file *stl, char *file) { color = uncon_3_color; } fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[0].x, - stl->facet_start[i].vertex[0].y, - stl->facet_start[i].vertex[0].z, color.x, color.y, color.z); + stl->facet_start[i].vertex[0](0), + stl->facet_start[i].vertex[0](1), + stl->facet_start[i].vertex[0](2), color(0), color(1), color(2)); fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[1].x, - stl->facet_start[i].vertex[1].y, - stl->facet_start[i].vertex[1].z, color.x, color.y, color.z); + stl->facet_start[i].vertex[1](0), + stl->facet_start[i].vertex[1](1), + stl->facet_start[i].vertex[1](2), color(0), color(1), color(2)); fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[2].x, - stl->facet_start[i].vertex[2].y, - stl->facet_start[i].vertex[2].z, color.x, color.y, color.z); + stl->facet_start[i].vertex[2](0), + stl->facet_start[i].vertex[2](1), + stl->facet_start[i].vertex[2](2), color(0), color(1), color(2)); fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[2].x, - stl->facet_start[i].vertex[2].y, - stl->facet_start[i].vertex[2].z, color.x, color.y, color.z); + stl->facet_start[i].vertex[2](0), + stl->facet_start[i].vertex[2](1), + stl->facet_start[i].vertex[2](2), color(0), color(1), color(2)); } fclose(fp); } @@ -409,17 +396,17 @@ stl_write_dxf(stl_file *stl, char *file, char *label) { for(i = 0; i < stl->stats.number_of_facets; i++) { fprintf(fp, "0\n3DFACE\n8\n0\n"); fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", - stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0].y, - stl->facet_start[i].vertex[0].z); + stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), + stl->facet_start[i].vertex[0](2)); fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n", - stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1].y, - stl->facet_start[i].vertex[1].z); + stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), + stl->facet_start[i].vertex[1](2)); fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n", - stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, - stl->facet_start[i].vertex[2].z); + stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), + stl->facet_start[i].vertex[2](2)); fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n", - stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, - stl->facet_start[i].vertex[2].z); + stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), + stl->facet_start[i].vertex[2](2)); } fprintf(fp, "0\nENDSEC\n0\nEOF\n"); diff --git a/xs/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp similarity index 72% rename from xs/src/admesh/stlinit.cpp rename to src/admesh/stlinit.cpp index ed8beb9d19..e2939b8af8 100644 --- a/xs/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -40,7 +40,7 @@ stl_open(stl_file *stl, const char *file) { stl_initialize(stl); stl_count_facets(stl, file); stl_allocate(stl); - stl_read(stl, 0, 1); + stl_read(stl, 0, true); if (!stl->error) fclose(stl->fp); } @@ -227,7 +227,7 @@ stl_open_merge(stl_file *stl, char *file_to_merge) { Start at num_facets_so_far, the index to the first unused facet. Also say that this isn't our first time so we should augment stats like min and max instead of erasing them. */ - stl_read(stl, num_facets_so_far, 0); + stl_read(stl, num_facets_so_far, false); /* Restore the stl information we overwrote (for stl_read) so that it still accurately reflects the subject part: */ @@ -255,8 +255,7 @@ stl_reallocate(stl_file *stl) { /* Reads the contents of the file pointed to by stl->fp into the stl structure, starting at facet first_facet. The second argument says if it's our first time running this for the stl and therefore we should reset our max and min stats. */ -void -stl_read(stl_file *stl, int first_facet, int first) { +void stl_read(stl_file *stl, int first_facet, bool first) { stl_facet facet; int i; @@ -294,11 +293,11 @@ stl_read(stl_file *stl, int first_facet, int first) { assert(res_normal == 3); int res_outer_loop = fscanf(stl->fp, " outer loop"); assert(res_outer_loop == 0); - int res_vertex1 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z); + int res_vertex1 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2)); assert(res_vertex1 == 3); - int res_vertex2 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z); + int res_vertex2 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2)); assert(res_vertex2 == 3); - int res_vertex3 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z); + int res_vertex3 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2)); assert(res_vertex3 == 3); int res_endloop = fscanf(stl->fp, " endloop"); assert(res_endloop == 0); @@ -311,9 +310,9 @@ stl_read(stl_file *stl, int first_facet, int first) { } // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. - if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 || - sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 || - sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) { + if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 || + sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 || + sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) { // Normal was mangled. Maybe denormals or "not a number" were stored? // Just reset the normal and silently ignore it. memset(&facet.normal, 0, sizeof(facet.normal)); @@ -326,104 +325,45 @@ stl_read(stl_file *stl, int first_facet, int first) { // It may be worth to round these numbers to zero during loading to reduce the number of errors reported // during the STL import. for (size_t j = 0; j < 3; ++ j) { - if (facet.vertex[j].x > -1e-12f && facet.vertex[j].x < 1e-12f) - printf("stl_read: facet %d.x = %e\r\n", j, facet.vertex[j].x); - if (facet.vertex[j].y > -1e-12f && facet.vertex[j].y < 1e-12f) - printf("stl_read: facet %d.y = %e\r\n", j, facet.vertex[j].y); - if (facet.vertex[j].z > -1e-12f && facet.vertex[j].z < 1e-12f) - printf("stl_read: facet %d.z = %e\r\n", j, facet.vertex[j].z); + if (facet.vertex[j](0) > -1e-12f && facet.vertex[j](0) < 1e-12f) + printf("stl_read: facet %d(0) = %e\r\n", j, facet.vertex[j](0)); + if (facet.vertex[j](1) > -1e-12f && facet.vertex[j](1) < 1e-12f) + printf("stl_read: facet %d(1) = %e\r\n", j, facet.vertex[j](1)); + if (facet.vertex[j](2) > -1e-12f && facet.vertex[j](2) < 1e-12f) + printf("stl_read: facet %d(2) = %e\r\n", j, facet.vertex[j](2)); } #endif -#if 1 - { - // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. - // When using a memcmp on raw floats, those numbers report to be different. - // Unify all +0 and -0 to +0 to make the floats equal under memcmp. - uint32_t *f = (uint32_t*)&facet; - for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats - if (*f == 0x80000000) - // Negative zero, switch to positive zero. - *f = 0; - } -#else - { - // Due to the nature of the floating point numbers, close to zero values may be represented with singificantly higher precision - // than the rest of the vertices. Round them to zero. - float *f = (float*)&facet; - for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats - if (*f > -1e-12f && *f < 1e-12f) - // Negative zero, switch to positive zero. - *f = 0; - } -#endif /* Write the facet into memory. */ - memcpy(stl->facet_start+i, &facet, SIZEOF_STL_FACET); + stl->facet_start[i] = facet; stl_facet_stats(stl, facet, first); - first = 0; } - stl->stats.size.x = stl->stats.max.x - stl->stats.min.x; - stl->stats.size.y = stl->stats.max.y - stl->stats.min.y; - stl->stats.size.z = stl->stats.max.z - stl->stats.min.z; - stl->stats.bounding_diameter = sqrt( - stl->stats.size.x * stl->stats.size.x + - stl->stats.size.y * stl->stats.size.y + - stl->stats.size.z * stl->stats.size.z - ); + stl->stats.size = stl->stats.max - stl->stats.min; + stl->stats.bounding_diameter = stl->stats.size.norm(); } -void -stl_facet_stats(stl_file *stl, stl_facet facet, int first) { - float diff_x; - float diff_y; - float diff_z; - float max_diff; +void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first) +{ + if (stl->error) + return; - if (stl->error) return; + // While we are going through all of the facets, let's find the + // maximum and minimum values for x, y, and z - /* while we are going through all of the facets, let's find the */ - /* maximum and minimum values for x, y, and z */ - - /* Initialize the max and min values the first time through*/ if (first) { - stl->stats.max.x = facet.vertex[0].x; - stl->stats.min.x = facet.vertex[0].x; - stl->stats.max.y = facet.vertex[0].y; - stl->stats.min.y = facet.vertex[0].y; - stl->stats.max.z = facet.vertex[0].z; - stl->stats.min.z = facet.vertex[0].z; - - diff_x = ABS(facet.vertex[0].x - facet.vertex[1].x); - diff_y = ABS(facet.vertex[0].y - facet.vertex[1].y); - diff_z = ABS(facet.vertex[0].z - facet.vertex[1].z); - max_diff = STL_MAX(diff_x, diff_y); - max_diff = STL_MAX(diff_z, max_diff); - stl->stats.shortest_edge = max_diff; - - first = 0; + // Initialize the max and min values the first time through + stl->stats.min = facet.vertex[0]; + stl->stats.max = facet.vertex[0]; + stl_vertex diff = (facet.vertex[1] - facet.vertex[0]).cwiseAbs(); + stl->stats.shortest_edge = std::max(diff(0), std::max(diff(1), diff(2))); + first = false; } - /* now find the max and min values */ - stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[0].x); - stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[0].x); - stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[0].y); - stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[0].y); - stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[0].z); - stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[0].z); - - stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[1].x); - stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[1].x); - stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[1].y); - stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[1].y); - stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[1].z); - stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[1].z); - - stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[2].x); - stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[2].x); - stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[2].y); - stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[2].y); - stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[2].z); - stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[2].z); + // Now find the max and min values. + for (size_t i = 0; i < 3; ++ i) { + stl->stats.min = stl->stats.min.cwiseMin(facet.vertex[i]); + stl->stats.max = stl->stats.max.cwiseMax(facet.vertex[i]); + } } void diff --git a/xs/src/admesh/util.cpp b/src/admesh/util.cpp similarity index 54% rename from xs/src/admesh/util.cpp rename to src/admesh/util.cpp index f3bf59b56e..7cb69bccdd 100644 --- a/xs/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -62,7 +62,7 @@ stl_verify_neighbors(stl_file *stl) { edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; } - if(memcmp(&edge_a, &edge_b, SIZEOF_EDGE_SORT) != 0) { + if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) { /* These edges should match but they don't. Print results. */ printf("edge %d of facet %d doesn't match edge %d of facet %d\n", j, i, vnot + 1, neighbor); @@ -73,114 +73,67 @@ stl_verify_neighbors(stl_file *stl) { } } -void -stl_translate(stl_file *stl, float x, float y, float z) { - int i; - int j; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].x -= (stl->stats.min.x - x); - stl->facet_start[i].vertex[j].y -= (stl->stats.min.y - y); - stl->facet_start[i].vertex[j].z -= (stl->stats.min.z - z); - } - } - stl->stats.max.x -= (stl->stats.min.x - x); - stl->stats.max.y -= (stl->stats.min.y - y); - stl->stats.max.z -= (stl->stats.min.z - z); - stl->stats.min.x = x; - stl->stats.min.y = y; - stl->stats.min.z = z; +void stl_translate(stl_file *stl, float x, float y, float z) +{ + if (stl->error) + return; + stl_vertex new_min(x, y, z); + stl_vertex shift = new_min - stl->stats.min; + for (int i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + stl->facet_start[i].vertex[j] += shift; + stl->stats.min = new_min; + stl->stats.max += shift; stl_invalidate_shared_vertices(stl); } /* Translates the stl by x,y,z, relatively from wherever it is currently */ -void -stl_translate_relative(stl_file *stl, float x, float y, float z) { - int i; - int j; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].x += x; - stl->facet_start[i].vertex[j].y += y; - stl->facet_start[i].vertex[j].z += z; - } - } - stl->stats.min.x += x; - stl->stats.min.y += y; - stl->stats.min.z += z; - stl->stats.max.x += x; - stl->stats.max.y += y; - stl->stats.max.z += z; +void stl_translate_relative(stl_file *stl, float x, float y, float z) +{ + if (stl->error) + return; + stl_vertex shift(x, y, z); + for (int i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + stl->facet_start[i].vertex[j] += shift; + stl->stats.min += shift; + stl->stats.max += shift; stl_invalidate_shared_vertices(stl); } -void -stl_scale_versor(stl_file *stl, float versor[3]) { - int i; - int j; - - if (stl->error) return; - - /* scale extents */ - stl->stats.min.x *= versor[0]; - stl->stats.min.y *= versor[1]; - stl->stats.min.z *= versor[2]; - stl->stats.max.x *= versor[0]; - stl->stats.max.y *= versor[1]; - stl->stats.max.z *= versor[2]; - - /* scale size */ - stl->stats.size.x *= versor[0]; - stl->stats.size.y *= versor[1]; - stl->stats.size.z *= versor[2]; - - /* scale volume */ - if (stl->stats.volume > 0.0) { - stl->stats.volume *= (versor[0] * versor[1] * versor[2]); - } - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].x *= versor[0]; - stl->facet_start[i].vertex[j].y *= versor[1]; - stl->facet_start[i].vertex[j].z *= versor[2]; - } - } +void stl_scale_versor(stl_file *stl, const stl_vertex &versor) +{ + if (stl->error) + return; + // Scale extents. + auto s = versor.array(); + stl->stats.min.array() *= s; + stl->stats.max.array() *= s; + // Scale size. + stl->stats.size.array() *= s; + // Scale volume. + if (stl->stats.volume > 0.0) + stl->stats.volume *= versor(0) * versor(1) * versor(2); + // Scale the mesh. + for (int i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + stl->facet_start[i].vertex[j].array() *= s; stl_invalidate_shared_vertices(stl); } -void -stl_scale(stl_file *stl, float factor) { - float versor[3]; - - if (stl->error) return; - - versor[0] = factor; - versor[1] = factor; - versor[2] = factor; - stl_scale_versor(stl, versor); -} - -static void calculate_normals(stl_file *stl) { - float normal[3]; - - if (stl->error) return; +static void calculate_normals(stl_file *stl) +{ + if (stl->error) + return; + stl_normal normal; for(uint32_t i = 0; i < stl->stats.number_of_facets; i++) { stl_calculate_normal(normal, &stl->facet_start[i]); stl_normalize_vector(normal); - stl->facet_start[i].normal.x = normal[0]; - stl->facet_start[i].normal.y = normal[1]; - stl->facet_start[i].normal.z = normal[2]; + stl->facet_start[i].normal = normal; } } @@ -193,15 +146,56 @@ void stl_transform(stl_file *stl, float *trafo3x4) { for (i_vertex = 0; i_vertex < 3; ++ i_vertex) { stl_vertex &v_dst = vertices[i_vertex]; stl_vertex v_src = v_dst; - v_dst.x = trafo3x4[0] * v_src.x + trafo3x4[1] * v_src.y + trafo3x4[2] * v_src.z + trafo3x4[3]; - v_dst.y = trafo3x4[4] * v_src.x + trafo3x4[5] * v_src.y + trafo3x4[6] * v_src.z + trafo3x4[7]; - v_dst.z = trafo3x4[8] * v_src.x + trafo3x4[9] * v_src.y + trafo3x4[10] * v_src.z + trafo3x4[11]; + v_dst(0) = trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]; + v_dst(1) = trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]; + v_dst(2) = trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]; } } stl_get_size(stl); calculate_normals(stl); } +void stl_transform(stl_file *stl, const Eigen::Transform& t) +{ + if (stl->error) + return; + + unsigned int vertices_count = 3 * (unsigned int)stl->stats.number_of_facets; + if (vertices_count == 0) + return; + + Eigen::MatrixXf src_vertices(3, vertices_count); + stl_facet* facet_ptr = stl->facet_start; + unsigned int v_id = 0; + while (facet_ptr < stl->facet_start + stl->stats.number_of_facets) + { + for (int i = 0; i < 3; ++i) + { + ::memcpy((void*)src_vertices.col(v_id).data(), (const void*)&facet_ptr->vertex[i], 3 * sizeof(float)); + ++v_id; + } + facet_ptr += 1; + } + + Eigen::MatrixXf dst_vertices(3, vertices_count); + dst_vertices = t.cast() * src_vertices.colwise().homogeneous(); + + facet_ptr = stl->facet_start; + v_id = 0; + while (facet_ptr < stl->facet_start + stl->stats.number_of_facets) + { + for (int i = 0; i < 3; ++i) + { + ::memcpy((void*)&facet_ptr->vertex[i], (const void*)dst_vertices.col(v_id).data(), 3 * sizeof(float)); + ++v_id; + } + facet_ptr += 1; + } + + stl_get_size(stl); + calculate_normals(stl); +} + void stl_rotate_x(stl_file *stl, float angle) { int i; @@ -214,8 +208,8 @@ stl_rotate_x(stl_file *stl, float angle) { for(i = 0; i < stl->stats.number_of_facets; i++) { for(j = 0; j < 3; j++) { - stl_rotate(&stl->facet_start[i].vertex[j].y, - &stl->facet_start[i].vertex[j].z, c, s); + stl_rotate(&stl->facet_start[i].vertex[j](1), + &stl->facet_start[i].vertex[j](2), c, s); } } stl_get_size(stl); @@ -234,8 +228,8 @@ stl_rotate_y(stl_file *stl, float angle) { for(i = 0; i < stl->stats.number_of_facets; i++) { for(j = 0; j < 3; j++) { - stl_rotate(&stl->facet_start[i].vertex[j].z, - &stl->facet_start[i].vertex[j].x, c, s); + stl_rotate(&stl->facet_start[i].vertex[j](2), + &stl->facet_start[i].vertex[j](0), c, s); } } stl_get_size(stl); @@ -254,8 +248,8 @@ stl_rotate_z(stl_file *stl, float angle) { for(i = 0; i < stl->stats.number_of_facets; i++) { for(j = 0; j < 3; j++) { - stl_rotate(&stl->facet_start[i].vertex[j].x, - &stl->facet_start[i].vertex[j].y, c, s); + stl_rotate(&stl->facet_start[i].vertex[j](0), + &stl->facet_start[i].vertex[j](1), c, s); } } stl_get_size(stl); @@ -272,142 +266,98 @@ stl_rotate(float *x, float *y, const double c, const double s) { *y = float(s * xold + c * yold); } -extern void -stl_get_size(stl_file *stl) { - int i; - int j; - - if (stl->error) return; - if (stl->stats.number_of_facets == 0) return; - - stl->stats.min.x = stl->facet_start[0].vertex[0].x; - stl->stats.min.y = stl->facet_start[0].vertex[0].y; - stl->stats.min.z = stl->facet_start[0].vertex[0].z; - stl->stats.max.x = stl->facet_start[0].vertex[0].x; - stl->stats.max.y = stl->facet_start[0].vertex[0].y; - stl->stats.max.z = stl->facet_start[0].vertex[0].z; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->stats.min.x = STL_MIN(stl->stats.min.x, - stl->facet_start[i].vertex[j].x); - stl->stats.min.y = STL_MIN(stl->stats.min.y, - stl->facet_start[i].vertex[j].y); - stl->stats.min.z = STL_MIN(stl->stats.min.z, - stl->facet_start[i].vertex[j].z); - stl->stats.max.x = STL_MAX(stl->stats.max.x, - stl->facet_start[i].vertex[j].x); - stl->stats.max.y = STL_MAX(stl->stats.max.y, - stl->facet_start[i].vertex[j].y); - stl->stats.max.z = STL_MAX(stl->stats.max.z, - stl->facet_start[i].vertex[j].z); +void stl_get_size(stl_file *stl) +{ + if (stl->error || stl->stats.number_of_facets == 0) + return; + stl->stats.min = stl->facet_start[0].vertex[0]; + stl->stats.max = stl->stats.min; + for (int i = 0; i < stl->stats.number_of_facets; ++ i) { + const stl_facet &face = stl->facet_start[i]; + for (int j = 0; j < 3; ++ j) { + stl->stats.min = stl->stats.min.cwiseMin(face.vertex[j]); + stl->stats.max = stl->stats.max.cwiseMax(face.vertex[j]); } } - stl->stats.size.x = stl->stats.max.x - stl->stats.min.x; - stl->stats.size.y = stl->stats.max.y - stl->stats.min.y; - stl->stats.size.z = stl->stats.max.z - stl->stats.min.z; - stl->stats.bounding_diameter = sqrt( - stl->stats.size.x * stl->stats.size.x + - stl->stats.size.y * stl->stats.size.y + - stl->stats.size.z * stl->stats.size.z - ); + stl->stats.size = stl->stats.max - stl->stats.min; + stl->stats.bounding_diameter = stl->stats.size.norm(); } -void -stl_mirror_xy(stl_file *stl) { - int i; - int j; - float temp_size; +void stl_mirror_xy(stl_file *stl) +{ + if (stl->error) + return; - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].z *= -1.0; + for(int i = 0; i < stl->stats.number_of_facets; i++) { + for(int j = 0; j < 3; j++) { + stl->facet_start[i].vertex[j](2) *= -1.0; } } - temp_size = stl->stats.min.z; - stl->stats.min.z = stl->stats.max.z; - stl->stats.max.z = temp_size; - stl->stats.min.z *= -1.0; - stl->stats.max.z *= -1.0; + float temp_size = stl->stats.min(2); + stl->stats.min(2) = stl->stats.max(2); + stl->stats.max(2) = temp_size; + stl->stats.min(2) *= -1.0; + stl->stats.max(2) *= -1.0; stl_reverse_all_facets(stl); stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ } -void -stl_mirror_yz(stl_file *stl) { - int i; - int j; - float temp_size; - +void stl_mirror_yz(stl_file *stl) +{ if (stl->error) return; - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].x *= -1.0; + for (int i = 0; i < stl->stats.number_of_facets; i++) { + for (int j = 0; j < 3; j++) { + stl->facet_start[i].vertex[j](0) *= -1.0; } } - temp_size = stl->stats.min.x; - stl->stats.min.x = stl->stats.max.x; - stl->stats.max.x = temp_size; - stl->stats.min.x *= -1.0; - stl->stats.max.x *= -1.0; + float temp_size = stl->stats.min(0); + stl->stats.min(0) = stl->stats.max(0); + stl->stats.max(0) = temp_size; + stl->stats.min(0) *= -1.0; + stl->stats.max(0) *= -1.0; stl_reverse_all_facets(stl); stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ } -void -stl_mirror_xz(stl_file *stl) { - int i; - int j; - float temp_size; +void stl_mirror_xz(stl_file *stl) +{ + if (stl->error) + return; - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].y *= -1.0; + for (int i = 0; i < stl->stats.number_of_facets; i++) { + for (int j = 0; j < 3; j++) { + stl->facet_start[i].vertex[j](1) *= -1.0; } } - temp_size = stl->stats.min.y; - stl->stats.min.y = stl->stats.max.y; - stl->stats.max.y = temp_size; - stl->stats.min.y *= -1.0; - stl->stats.max.y *= -1.0; + float temp_size = stl->stats.min(1); + stl->stats.min(1) = stl->stats.max(1); + stl->stats.max(1) = temp_size; + stl->stats.min(1) *= -1.0; + stl->stats.max(1) *= -1.0; stl_reverse_all_facets(stl); stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ } -static float get_volume(stl_file *stl) { - stl_vertex p0; - stl_vertex p; - stl_normal n; - float height; - float area; - float volume = 0.0; +static float get_volume(stl_file *stl) +{ + if (stl->error) + return 0; - if (stl->error) return 0; - - /* Choose a point, any point as the reference */ - p0.x = stl->facet_start[0].vertex[0].x; - p0.y = stl->facet_start[0].vertex[0].y; - p0.z = stl->facet_start[0].vertex[0].z; - - for(uint32_t i = 0; i < stl->stats.number_of_facets; i++) { - p.x = stl->facet_start[i].vertex[0].x - p0.x; - p.y = stl->facet_start[i].vertex[0].y - p0.y; - p.z = stl->facet_start[i].vertex[0].z - p0.z; - /* Do dot product to get distance from point to plane */ - n = stl->facet_start[i].normal; - height = (n.x * p.x) + (n.y * p.y) + (n.z * p.z); - area = get_area(&stl->facet_start[i]); + // Choose a point, any point as the reference. + stl_vertex p0 = stl->facet_start[0].vertex[0]; + float volume = 0.f; + for(uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + // Do dot product to get distance from point to plane. + float height = stl->facet_start[i].normal.dot(stl->facet_start[i].vertex[0] - p0); + float area = get_area(&stl->facet_start[i]); volume += (area * height) / 3.0f; } return volume; } -void stl_calculate_volume(stl_file *stl) { +void stl_calculate_volume(stl_file *stl) +{ if (stl->error) return; stl->stats.volume = get_volume(stl); if(stl->stats.volume < 0.0) { @@ -416,35 +366,32 @@ void stl_calculate_volume(stl_file *stl) { } } -static float get_area(stl_facet *facet) { - double cross[3][3]; - float sum[3]; - float n[3]; - float area; - int i; - +static float get_area(stl_facet *facet) +{ /* cast to double before calculating cross product because large coordinates can result in overflowing product (bad area is responsible for bad volume and bad facets reversal) */ - for(i = 0; i < 3; i++) { - cross[i][0]=(((double)facet->vertex[i].y * (double)facet->vertex[(i + 1) % 3].z) - - ((double)facet->vertex[i].z * (double)facet->vertex[(i + 1) % 3].y)); - cross[i][1]=(((double)facet->vertex[i].z * (double)facet->vertex[(i + 1) % 3].x) - - ((double)facet->vertex[i].x * (double)facet->vertex[(i + 1) % 3].z)); - cross[i][2]=(((double)facet->vertex[i].x * (double)facet->vertex[(i + 1) % 3].y) - - ((double)facet->vertex[i].y * (double)facet->vertex[(i + 1) % 3].x)); + double cross[3][3]; + for (int i = 0; i < 3; i++) { + cross[i][0]=(((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](2)) - + ((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](1))); + cross[i][1]=(((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](0)) - + ((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](2))); + cross[i][2]=(((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](1)) - + ((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](0))); } - sum[0] = cross[0][0] + cross[1][0] + cross[2][0]; - sum[1] = cross[0][1] + cross[1][1] + cross[2][1]; - sum[2] = cross[0][2] + cross[1][2] + cross[2][2]; + stl_normal sum; + sum(0) = cross[0][0] + cross[1][0] + cross[2][0]; + sum(1) = cross[0][1] + cross[1][1] + cross[2][1]; + sum(2) = cross[0][2] + cross[1][2] + cross[2][2]; - /* This should already be done. But just in case, let's do it again */ + // This should already be done. But just in case, let's do it again. + //FIXME this is questionable. the "sum" normal should be accurate, while the normal "n" may be calculated with a low accuracy. + stl_normal n; stl_calculate_normal(n, facet); stl_normalize_vector(n); - - area = 0.5 * (n[0] * sum[0] + n[1] * sum[1] + n[2] * sum[2]); - return area; + return 0.5f * n.dot(sum); } void stl_repair(stl_file *stl, diff --git a/src/agg/AUTHORS b/src/agg/AUTHORS new file mode 100644 index 0000000000..2bb6518ec0 --- /dev/null +++ b/src/agg/AUTHORS @@ -0,0 +1,2 @@ +Anti-Grain Geometry - Version 2.4 +Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) diff --git a/src/agg/VERSION b/src/agg/VERSION new file mode 100644 index 0000000000..c5de3e3b0c --- /dev/null +++ b/src/agg/VERSION @@ -0,0 +1,2 @@ +2.4 +svn revision 128 \ No newline at end of file diff --git a/src/agg/agg_array.h b/src/agg/agg_array.h new file mode 100644 index 0000000000..8d56683840 --- /dev/null +++ b/src/agg/agg_array.h @@ -0,0 +1,1119 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +#ifndef AGG_ARRAY_INCLUDED +#define AGG_ARRAY_INCLUDED + +#include +#include +#include "agg_basics.h" + +namespace agg +{ + + //-------------------------------------------------------pod_array_adaptor + template class pod_array_adaptor + { + public: + typedef T value_type; + pod_array_adaptor(T* array, unsigned size) : + m_array(array), m_size(size) {} + + unsigned size() const { return m_size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + private: + T* m_array; + unsigned m_size; + }; + + + //---------------------------------------------------------pod_auto_array + template class pod_auto_array + { + public: + typedef T value_type; + typedef pod_auto_array self_type; + + pod_auto_array() {} + explicit pod_auto_array(const T* c) + { + memcpy(m_array, c, sizeof(T) * Size); + } + + const self_type& operator = (const T* c) + { + memcpy(m_array, c, sizeof(T) * Size); + return *this; + } + + static unsigned size() { return Size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + private: + T m_array[Size]; + }; + + + //--------------------------------------------------------pod_auto_vector + template class pod_auto_vector + { + public: + typedef T value_type; + typedef pod_auto_vector self_type; + + pod_auto_vector() : m_size(0) {} + + void remove_all() { m_size = 0; } + void clear() { m_size = 0; } + void add(const T& v) { m_array[m_size++] = v; } + void push_back(const T& v) { m_array[m_size++] = v; } + void inc_size(unsigned size) { m_size += size; } + + unsigned size() const { return m_size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + private: + T m_array[Size]; + unsigned m_size; + }; + + + //---------------------------------------------------------------pod_array + template class pod_array + { + public: + typedef T value_type; + typedef pod_array self_type; + + ~pod_array() { pod_allocator::deallocate(m_array, m_size); } + pod_array() : m_array(0), m_size(0) {} + + pod_array(unsigned size) : + m_array(pod_allocator::allocate(size)), + m_size(size) + {} + + pod_array(const self_type& v) : + m_array(pod_allocator::allocate(v.m_size)), + m_size(v.m_size) + { + memcpy(m_array, v.m_array, sizeof(T) * m_size); + } + + void resize(unsigned size) + { + if(size != m_size) + { + pod_allocator::deallocate(m_array, m_size); + m_array = pod_allocator::allocate(m_size = size); + } + } + const self_type& operator = (const self_type& v) + { + resize(v.size()); + memcpy(m_array, v.m_array, sizeof(T) * m_size); + return *this; + } + + unsigned size() const { return m_size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + const T* data() const { return m_array; } + T* data() { return m_array; } + private: + T* m_array; + unsigned m_size; + }; + + + + //--------------------------------------------------------------pod_vector + // A simple class template to store Plain Old Data, a vector + // of a fixed size. The data is continous in memory + //------------------------------------------------------------------------ + template class pod_vector + { + public: + typedef T value_type; + + ~pod_vector() { pod_allocator::deallocate(m_array, m_capacity); } + pod_vector() : m_size(0), m_capacity(0), m_array(0) {} + pod_vector(unsigned cap, unsigned extra_tail=0); + + // Copying + pod_vector(const pod_vector&); + const pod_vector& operator = (const pod_vector&); + + // Set new capacity. All data is lost, size is set to zero. + void capacity(unsigned cap, unsigned extra_tail=0); + unsigned capacity() const { return m_capacity; } + + // Allocate n elements. All data is lost, + // but elements can be accessed in range 0...size-1. + void allocate(unsigned size, unsigned extra_tail=0); + + // Resize keeping the content. + void resize(unsigned new_size); + + void zero() + { + memset(m_array, 0, sizeof(T) * m_size); + } + + void add(const T& v) { m_array[m_size++] = v; } + void push_back(const T& v) { m_array[m_size++] = v; } + void insert_at(unsigned pos, const T& val); + void inc_size(unsigned size) { m_size += size; } + unsigned size() const { return m_size; } + unsigned byte_size() const { return m_size * sizeof(T); } + void serialize(int8u* ptr) const; + void deserialize(const int8u* data, unsigned byte_size); + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + const T* data() const { return m_array; } + T* data() { return m_array; } + + void remove_all() { m_size = 0; } + void clear() { m_size = 0; } + void cut_at(unsigned num) { if(num < m_size) m_size = num; } + + private: + unsigned m_size; + unsigned m_capacity; + T* m_array; + }; + + //------------------------------------------------------------------------ + template + void pod_vector::capacity(unsigned cap, unsigned extra_tail) + { + m_size = 0; + if(cap > m_capacity) + { + pod_allocator::deallocate(m_array, m_capacity); + m_capacity = cap + extra_tail; + m_array = m_capacity ? pod_allocator::allocate(m_capacity) : 0; + } + } + + //------------------------------------------------------------------------ + template + void pod_vector::allocate(unsigned size, unsigned extra_tail) + { + capacity(size, extra_tail); + m_size = size; + } + + + //------------------------------------------------------------------------ + template + void pod_vector::resize(unsigned new_size) + { + if(new_size > m_size) + { + if(new_size > m_capacity) + { + T* data = pod_allocator::allocate(new_size); + memcpy(data, m_array, m_size * sizeof(T)); + pod_allocator::deallocate(m_array, m_capacity); + m_array = data; + } + } + else + { + m_size = new_size; + } + } + + //------------------------------------------------------------------------ + template pod_vector::pod_vector(unsigned cap, unsigned extra_tail) : + m_size(0), + m_capacity(cap + extra_tail), + m_array(pod_allocator::allocate(m_capacity)) {} + + //------------------------------------------------------------------------ + template pod_vector::pod_vector(const pod_vector& v) : + m_size(v.m_size), + m_capacity(v.m_capacity), + m_array(v.m_capacity ? pod_allocator::allocate(v.m_capacity) : 0) + { + memcpy(m_array, v.m_array, sizeof(T) * v.m_size); + } + + //------------------------------------------------------------------------ + template const pod_vector& + pod_vector::operator = (const pod_vector&v) + { + allocate(v.m_size); + if(v.m_size) memcpy(m_array, v.m_array, sizeof(T) * v.m_size); + return *this; + } + + //------------------------------------------------------------------------ + template void pod_vector::serialize(int8u* ptr) const + { + if(m_size) memcpy(ptr, m_array, m_size * sizeof(T)); + } + + //------------------------------------------------------------------------ + template + void pod_vector::deserialize(const int8u* data, unsigned byte_size) + { + byte_size /= sizeof(T); + allocate(byte_size); + if(byte_size) memcpy(m_array, data, byte_size * sizeof(T)); + } + + //------------------------------------------------------------------------ + template + void pod_vector::insert_at(unsigned pos, const T& val) + { + if(pos >= m_size) + { + m_array[m_size] = val; + } + else + { + memmove(m_array + pos + 1, m_array + pos, (m_size - pos) * sizeof(T)); + m_array[pos] = val; + } + ++m_size; + } + + //---------------------------------------------------------------pod_bvector + // A simple class template to store Plain Old Data, similar to std::deque + // It doesn't reallocate memory but instead, uses blocks of data of size + // of (1 << S), that is, power of two. The data is NOT contiguous in memory, + // so the only valid access method is operator [] or curr(), prev(), next() + // + // There reallocs occure only when the pool of pointers to blocks needs + // to be extended (it happens very rarely). You can control the value + // of increment to reallocate the pointer buffer. See the second constructor. + // By default, the incremeent value equals (1 << S), i.e., the block size. + //------------------------------------------------------------------------ + template class pod_bvector + { + public: + enum block_scale_e + { + block_shift = S, + block_size = 1 << block_shift, + block_mask = block_size - 1 + }; + + typedef T value_type; + + ~pod_bvector(); + pod_bvector(); + pod_bvector(unsigned block_ptr_inc); + + // Copying + pod_bvector(const pod_bvector& v); + const pod_bvector& operator = (const pod_bvector& v); + + void remove_all() { m_size = 0; } + void clear() { m_size = 0; } + void free_all() { free_tail(0); } + void free_tail(unsigned size); + void add(const T& val); + void push_back(const T& val) { add(val); } + void modify_last(const T& val); + void remove_last(); + + int allocate_continuous_block(unsigned num_elements); + + void add_array(const T* ptr, unsigned num_elem) + { + while(num_elem--) + { + add(*ptr++); + } + } + + template void add_data(DataAccessor& data) + { + while(data.size()) + { + add(*data); + ++data; + } + } + + void cut_at(unsigned size) + { + if(size < m_size) m_size = size; + } + + unsigned size() const { return m_size; } + + const T& operator [] (unsigned i) const + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + T& operator [] (unsigned i) + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + const T& at(unsigned i) const + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + T& at(unsigned i) + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + T value_at(unsigned i) const + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + const T& curr(unsigned idx) const + { + return (*this)[idx]; + } + + T& curr(unsigned idx) + { + return (*this)[idx]; + } + + const T& prev(unsigned idx) const + { + return (*this)[(idx + m_size - 1) % m_size]; + } + + T& prev(unsigned idx) + { + return (*this)[(idx + m_size - 1) % m_size]; + } + + const T& next(unsigned idx) const + { + return (*this)[(idx + 1) % m_size]; + } + + T& next(unsigned idx) + { + return (*this)[(idx + 1) % m_size]; + } + + const T& last() const + { + return (*this)[m_size - 1]; + } + + T& last() + { + return (*this)[m_size - 1]; + } + + unsigned byte_size() const; + void serialize(int8u* ptr) const; + void deserialize(const int8u* data, unsigned byte_size); + void deserialize(unsigned start, const T& empty_val, + const int8u* data, unsigned byte_size); + + template + void deserialize(ByteAccessor data) + { + remove_all(); + unsigned elem_size = data.size() / sizeof(T); + + for(unsigned i = 0; i < elem_size; ++i) + { + int8u* ptr = (int8u*)data_ptr(); + for(unsigned j = 0; j < sizeof(T); ++j) + { + *ptr++ = *data; + ++data; + } + ++m_size; + } + } + + template + void deserialize(unsigned start, const T& empty_val, ByteAccessor data) + { + while(m_size < start) + { + add(empty_val); + } + + unsigned elem_size = data.size() / sizeof(T); + for(unsigned i = 0; i < elem_size; ++i) + { + int8u* ptr; + if(start + i < m_size) + { + ptr = (int8u*)(&((*this)[start + i])); + } + else + { + ptr = (int8u*)data_ptr(); + ++m_size; + } + for(unsigned j = 0; j < sizeof(T); ++j) + { + *ptr++ = *data; + ++data; + } + } + } + + const T* block(unsigned nb) const { return m_blocks[nb]; } + + private: + void allocate_block(unsigned nb); + T* data_ptr(); + + unsigned m_size; + unsigned m_num_blocks; + unsigned m_max_blocks; + T** m_blocks; + unsigned m_block_ptr_inc; + }; + + + //------------------------------------------------------------------------ + template pod_bvector::~pod_bvector() + { + if(m_num_blocks) + { + T** blk = m_blocks + m_num_blocks - 1; + while(m_num_blocks--) + { + pod_allocator::deallocate(*blk, block_size); + --blk; + } + } + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::free_tail(unsigned size) + { + if(size < m_size) + { + unsigned nb = (size + block_mask) >> block_shift; + while(m_num_blocks > nb) + { + pod_allocator::deallocate(m_blocks[--m_num_blocks], block_size); + } + if(m_num_blocks == 0) + { + pod_allocator::deallocate(m_blocks, m_max_blocks); + m_blocks = 0; + m_max_blocks = 0; + } + m_size = size; + } + } + + + //------------------------------------------------------------------------ + template pod_bvector::pod_bvector() : + m_size(0), + m_num_blocks(0), + m_max_blocks(0), + m_blocks(0), + m_block_ptr_inc(block_size) + { + } + + + //------------------------------------------------------------------------ + template + pod_bvector::pod_bvector(unsigned block_ptr_inc) : + m_size(0), + m_num_blocks(0), + m_max_blocks(0), + m_blocks(0), + m_block_ptr_inc(block_ptr_inc) + { + } + + + //------------------------------------------------------------------------ + template + pod_bvector::pod_bvector(const pod_bvector& v) : + m_size(v.m_size), + m_num_blocks(v.m_num_blocks), + m_max_blocks(v.m_max_blocks), + m_blocks(v.m_max_blocks ? + pod_allocator::allocate(v.m_max_blocks) : + 0), + m_block_ptr_inc(v.m_block_ptr_inc) + { + unsigned i; + for(i = 0; i < v.m_num_blocks; ++i) + { + m_blocks[i] = pod_allocator::allocate(block_size); + memcpy(m_blocks[i], v.m_blocks[i], block_size * sizeof(T)); + } + } + + + //------------------------------------------------------------------------ + template + const pod_bvector& + pod_bvector::operator = (const pod_bvector& v) + { + unsigned i; + for(i = m_num_blocks; i < v.m_num_blocks; ++i) + { + allocate_block(i); + } + for(i = 0; i < v.m_num_blocks; ++i) + { + memcpy(m_blocks[i], v.m_blocks[i], block_size * sizeof(T)); + } + m_size = v.m_size; + return *this; + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::allocate_block(unsigned nb) + { + if(nb >= m_max_blocks) + { + T** new_blocks = pod_allocator::allocate(m_max_blocks + m_block_ptr_inc); + + if(m_blocks) + { + memcpy(new_blocks, + m_blocks, + m_num_blocks * sizeof(T*)); + + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + m_blocks = new_blocks; + m_max_blocks += m_block_ptr_inc; + } + m_blocks[nb] = pod_allocator::allocate(block_size); + m_num_blocks++; + } + + + + //------------------------------------------------------------------------ + template + inline T* pod_bvector::data_ptr() + { + unsigned nb = m_size >> block_shift; + if(nb >= m_num_blocks) + { + allocate_block(nb); + } + return m_blocks[nb] + (m_size & block_mask); + } + + + + //------------------------------------------------------------------------ + template + inline void pod_bvector::add(const T& val) + { + *data_ptr() = val; + ++m_size; + } + + + //------------------------------------------------------------------------ + template + inline void pod_bvector::remove_last() + { + if(m_size) --m_size; + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::modify_last(const T& val) + { + remove_last(); + add(val); + } + + + //------------------------------------------------------------------------ + template + int pod_bvector::allocate_continuous_block(unsigned num_elements) + { + if(num_elements < block_size) + { + data_ptr(); // Allocate initial block if necessary + unsigned rest = block_size - (m_size & block_mask); + unsigned index; + if(num_elements <= rest) + { + // The rest of the block is good, we can use it + //----------------- + index = m_size; + m_size += num_elements; + return index; + } + + // New block + //--------------- + m_size += rest; + data_ptr(); + index = m_size; + m_size += num_elements; + return index; + } + return -1; // Impossible to allocate + } + + + //------------------------------------------------------------------------ + template + unsigned pod_bvector::byte_size() const + { + return m_size * sizeof(T); + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::serialize(int8u* ptr) const + { + unsigned i; + for(i = 0; i < m_size; i++) + { + memcpy(ptr, &(*this)[i], sizeof(T)); + ptr += sizeof(T); + } + } + + //------------------------------------------------------------------------ + template + void pod_bvector::deserialize(const int8u* data, unsigned byte_size) + { + remove_all(); + byte_size /= sizeof(T); + for(unsigned i = 0; i < byte_size; ++i) + { + T* ptr = data_ptr(); + memcpy(ptr, data, sizeof(T)); + ++m_size; + data += sizeof(T); + } + } + + + // Replace or add a number of elements starting from "start" position + //------------------------------------------------------------------------ + template + void pod_bvector::deserialize(unsigned start, const T& empty_val, + const int8u* data, unsigned byte_size) + { + while(m_size < start) + { + add(empty_val); + } + + byte_size /= sizeof(T); + for(unsigned i = 0; i < byte_size; ++i) + { + if(start + i < m_size) + { + memcpy(&((*this)[start + i]), data, sizeof(T)); + } + else + { + T* ptr = data_ptr(); + memcpy(ptr, data, sizeof(T)); + ++m_size; + } + data += sizeof(T); + } + } + + + //---------------------------------------------------------block_allocator + // Allocator for arbitrary POD data. Most usable in different cache + // systems for efficient memory allocations. + // Memory is allocated with blocks of fixed size ("block_size" in + // the constructor). If required size exceeds the block size the allocator + // creates a new block of the required size. However, the most efficient + // use is when the average reqired size is much less than the block size. + //------------------------------------------------------------------------ + class block_allocator + { + struct block_type + { + int8u* data; + unsigned size; + }; + + public: + void remove_all() + { + if(m_num_blocks) + { + block_type* blk = m_blocks + m_num_blocks - 1; + while(m_num_blocks--) + { + pod_allocator::deallocate(blk->data, blk->size); + --blk; + } + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + m_num_blocks = 0; + m_max_blocks = 0; + m_blocks = 0; + m_buf_ptr = 0; + m_rest = 0; + } + + ~block_allocator() + { + remove_all(); + } + + block_allocator(unsigned block_size, unsigned block_ptr_inc=256-8) : + m_block_size(block_size), + m_block_ptr_inc(block_ptr_inc), + m_num_blocks(0), + m_max_blocks(0), + m_blocks(0), + m_buf_ptr(0), + m_rest(0) + { + } + + + int8u* allocate(unsigned size, unsigned alignment=1) + { + if(size == 0) return 0; + if(size <= m_rest) + { + int8u* ptr = m_buf_ptr; + if(alignment > 1) + { + unsigned align = + (alignment - unsigned((size_t)ptr) % alignment) % alignment; + + size += align; + ptr += align; + if(size <= m_rest) + { + m_rest -= size; + m_buf_ptr += size; + return ptr; + } + allocate_block(size); + return allocate(size - align, alignment); + } + m_rest -= size; + m_buf_ptr += size; + return ptr; + } + allocate_block(size + alignment - 1); + return allocate(size, alignment); + } + + + private: + void allocate_block(unsigned size) + { + if(size < m_block_size) size = m_block_size; + if(m_num_blocks >= m_max_blocks) + { + block_type* new_blocks = + pod_allocator::allocate(m_max_blocks + m_block_ptr_inc); + + if(m_blocks) + { + memcpy(new_blocks, + m_blocks, + m_num_blocks * sizeof(block_type)); + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + m_blocks = new_blocks; + m_max_blocks += m_block_ptr_inc; + } + + m_blocks[m_num_blocks].size = size; + m_blocks[m_num_blocks].data = + m_buf_ptr = + pod_allocator::allocate(size); + + m_num_blocks++; + m_rest = size; + } + + unsigned m_block_size; + unsigned m_block_ptr_inc; + unsigned m_num_blocks; + unsigned m_max_blocks; + block_type* m_blocks; + int8u* m_buf_ptr; + unsigned m_rest; + }; + + + + + + + + + //------------------------------------------------------------------------ + enum quick_sort_threshold_e + { + quick_sort_threshold = 9 + }; + + + //-----------------------------------------------------------swap_elements + template inline void swap_elements(T& a, T& b) + { + T temp = a; + a = b; + b = temp; + } + + + //--------------------------------------------------------------quick_sort + template + void quick_sort(Array& arr, Less less) + { + if(arr.size() < 2) return; + + typename Array::value_type* e1; + typename Array::value_type* e2; + + int stack[80]; + int* top = stack; + int limit = arr.size(); + int base = 0; + + for(;;) + { + int len = limit - base; + + int i; + int j; + int pivot; + + if(len > quick_sort_threshold) + { + // we use base + len/2 as the pivot + pivot = base + len / 2; + swap_elements(arr[base], arr[pivot]); + + i = base + 1; + j = limit - 1; + + // now ensure that *i <= *base <= *j + e1 = &(arr[j]); + e2 = &(arr[i]); + if(less(*e1, *e2)) swap_elements(*e1, *e2); + + e1 = &(arr[base]); + e2 = &(arr[i]); + if(less(*e1, *e2)) swap_elements(*e1, *e2); + + e1 = &(arr[j]); + e2 = &(arr[base]); + if(less(*e1, *e2)) swap_elements(*e1, *e2); + + for(;;) + { + do i++; while( less(arr[i], arr[base]) ); + do j--; while( less(arr[base], arr[j]) ); + + if( i > j ) + { + break; + } + + swap_elements(arr[i], arr[j]); + } + + swap_elements(arr[base], arr[j]); + + // now, push the largest sub-array + if(j - base > limit - i) + { + top[0] = base; + top[1] = j; + base = i; + } + else + { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } + else + { + // the sub-array is small, perform insertion sort + j = base; + i = j + 1; + + for(; i < limit; j = i, i++) + { + for(; less(*(e1 = &(arr[j + 1])), *(e2 = &(arr[j]))); j--) + { + swap_elements(*e1, *e2); + if(j == base) + { + break; + } + } + } + if(top > stack) + { + top -= 2; + base = top[0]; + limit = top[1]; + } + else + { + break; + } + } + } + } + + + + + //------------------------------------------------------remove_duplicates + // Remove duplicates from a sorted array. It doesn't cut the + // tail of the array, it just returns the number of remaining elements. + //----------------------------------------------------------------------- + template + unsigned remove_duplicates(Array& arr, Equal equal) + { + if(arr.size() < 2) return arr.size(); + + unsigned i, j; + for(i = 1, j = 1; i < arr.size(); i++) + { + typename Array::value_type& e = arr[i]; + if(!equal(e, arr[i - 1])) + { + arr[j++] = e; + } + } + return j; + } + + //--------------------------------------------------------invert_container + template void invert_container(Array& arr) + { + int i = 0; + int j = arr.size() - 1; + while(i < j) + { + swap_elements(arr[i++], arr[j--]); + } + } + + //------------------------------------------------------binary_search_pos + template + unsigned binary_search_pos(const Array& arr, const Value& val, Less less) + { + if(arr.size() == 0) return 0; + + unsigned beg = 0; + unsigned end = arr.size() - 1; + + if(less(val, arr[0])) return 0; + if(less(arr[end], val)) return end + 1; + + while(end - beg > 1) + { + unsigned mid = (end + beg) >> 1; + if(less(val, arr[mid])) end = mid; + else beg = mid; + } + + //if(beg <= 0 && less(val, arr[0])) return 0; + //if(end >= arr.size() - 1 && less(arr[end], val)) ++end; + + return end; + } + + //----------------------------------------------------------range_adaptor + template class range_adaptor + { + public: + typedef typename Array::value_type value_type; + + range_adaptor(Array& array, unsigned start, unsigned size) : + m_array(array), m_start(start), m_size(size) + {} + + unsigned size() const { return m_size; } + const value_type& operator [] (unsigned i) const { return m_array[m_start + i]; } + value_type& operator [] (unsigned i) { return m_array[m_start + i]; } + const value_type& at(unsigned i) const { return m_array[m_start + i]; } + value_type& at(unsigned i) { return m_array[m_start + i]; } + value_type value_at(unsigned i) const { return m_array[m_start + i]; } + + private: + Array& m_array; + unsigned m_start; + unsigned m_size; + }; + + //---------------------------------------------------------------int_less + inline bool int_less(int a, int b) { return a < b; } + + //------------------------------------------------------------int_greater + inline bool int_greater(int a, int b) { return a > b; } + + //----------------------------------------------------------unsigned_less + inline bool unsigned_less(unsigned a, unsigned b) { return a < b; } + + //-------------------------------------------------------unsigned_greater + inline bool unsigned_greater(unsigned a, unsigned b) { return a > b; } +} + +#endif diff --git a/src/agg/agg_basics.h b/src/agg/agg_basics.h new file mode 100644 index 0000000000..273850ba1b --- /dev/null +++ b/src/agg/agg_basics.h @@ -0,0 +1,574 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_BASICS_INCLUDED +#define AGG_BASICS_INCLUDED + +#include +#include "agg_config.h" + +//---------------------------------------------------------AGG_CUSTOM_ALLOCATOR +#ifdef AGG_CUSTOM_ALLOCATOR +#include "agg_allocator.h" +#else +namespace agg +{ + // The policy of all AGG containers and memory allocation strategy + // in general is that no allocated data requires explicit construction. + // It means that the allocator can be really simple; you can even + // replace new/delete to malloc/free. The constructors and destructors + // won't be called in this case, however everything will remain working. + // The second argument of deallocate() is the size of the allocated + // block. You can use this information if you wish. + //------------------------------------------------------------pod_allocator + template struct pod_allocator + { + static T* allocate(unsigned num) { return new T [num]; } + static void deallocate(T* ptr, unsigned) { delete [] ptr; } + }; + + // Single object allocator. It's also can be replaced with your custom + // allocator. The difference is that it can only allocate a single + // object and the constructor and destructor must be called. + // In AGG there is no need to allocate an array of objects with + // calling their constructors (only single ones). So that, if you + // replace these new/delete to malloc/free make sure that the in-place + // new is called and take care of calling the destructor too. + //------------------------------------------------------------obj_allocator + template struct obj_allocator + { + static T* allocate() { return new T; } + static void deallocate(T* ptr) { delete ptr; } + }; +} +#endif + + +//-------------------------------------------------------- Default basic types +// +// If the compiler has different capacity of the basic types you can redefine +// them via the compiler command line or by generating agg_config.h that is +// empty by default. +// +#ifndef AGG_INT8 +#define AGG_INT8 signed char +#endif + +#ifndef AGG_INT8U +#define AGG_INT8U unsigned char +#endif + +#ifndef AGG_INT16 +#define AGG_INT16 short +#endif + +#ifndef AGG_INT16U +#define AGG_INT16U unsigned short +#endif + +#ifndef AGG_INT32 +#define AGG_INT32 int +#endif + +#ifndef AGG_INT32U +#define AGG_INT32U unsigned +#endif + +#ifndef AGG_INT64 +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define AGG_INT64 signed __int64 +#else +#define AGG_INT64 signed long long +#endif +#endif + +#ifndef AGG_INT64U +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define AGG_INT64U unsigned __int64 +#else +#define AGG_INT64U unsigned long long +#endif +#endif + +//------------------------------------------------ Some fixes for MS Visual C++ +#if defined(_MSC_VER) +#pragma warning(disable:4786) // Identifier was truncated... +#endif + +#if defined(_MSC_VER) +#define AGG_INLINE __forceinline +#else +#define AGG_INLINE inline +#endif + +namespace agg +{ + //------------------------------------------------------------------------- + typedef AGG_INT8 int8; //----int8 + typedef AGG_INT8U int8u; //----int8u + typedef AGG_INT16 int16; //----int16 + typedef AGG_INT16U int16u; //----int16u + typedef AGG_INT32 int32; //----int32 + typedef AGG_INT32U int32u; //----int32u + typedef AGG_INT64 int64; //----int64 + typedef AGG_INT64U int64u; //----int64u + +#if defined(AGG_FISTP) +#pragma warning(push) +#pragma warning(disable : 4035) //Disable warning "no return value" + AGG_INLINE int iround(double v) //-------iround + { + int t; + __asm fld qword ptr [v] + __asm fistp dword ptr [t] + __asm mov eax, dword ptr [t] + } + AGG_INLINE unsigned uround(double v) //-------uround + { + unsigned t; + __asm fld qword ptr [v] + __asm fistp dword ptr [t] + __asm mov eax, dword ptr [t] + } +#pragma warning(pop) + AGG_INLINE int ifloor(double v) + { + return int(floor(v)); + } + AGG_INLINE unsigned ufloor(double v) //-------ufloor + { + return unsigned(floor(v)); + } + AGG_INLINE int iceil(double v) + { + return int(ceil(v)); + } + AGG_INLINE unsigned uceil(double v) //--------uceil + { + return unsigned(ceil(v)); + } +#elif defined(AGG_QIFIST) + AGG_INLINE int iround(double v) + { + return int(v); + } + AGG_INLINE int uround(double v) + { + return unsigned(v); + } + AGG_INLINE int ifloor(double v) + { + return int(floor(v)); + } + AGG_INLINE unsigned ufloor(double v) + { + return unsigned(floor(v)); + } + AGG_INLINE int iceil(double v) + { + return int(ceil(v)); + } + AGG_INLINE unsigned uceil(double v) + { + return unsigned(ceil(v)); + } +#else + AGG_INLINE int iround(double v) + { + return int((v < 0.0) ? v - 0.5 : v + 0.5); + } + AGG_INLINE int uround(double v) + { + return unsigned(v + 0.5); + } + AGG_INLINE int ifloor(double v) + { + int i = int(v); + return i - (i > v); + } + AGG_INLINE unsigned ufloor(double v) + { + return unsigned(v); + } + AGG_INLINE int iceil(double v) + { + return int(ceil(v)); + } + AGG_INLINE unsigned uceil(double v) + { + return unsigned(ceil(v)); + } +#endif + + //---------------------------------------------------------------saturation + template struct saturation + { + AGG_INLINE static int iround(double v) + { + if(v < double(-Limit)) return -Limit; + if(v > double( Limit)) return Limit; + return agg::iround(v); + } + }; + + //------------------------------------------------------------------mul_one + template struct mul_one + { + AGG_INLINE static unsigned mul(unsigned a, unsigned b) + { + unsigned q = a * b + (1 << (Shift-1)); + return (q + (q >> Shift)) >> Shift; + } + }; + + //------------------------------------------------------------------------- + typedef unsigned char cover_type; //----cover_type + enum cover_scale_e + { + cover_shift = 8, //----cover_shift + cover_size = 1 << cover_shift, //----cover_size + cover_mask = cover_size - 1, //----cover_mask + cover_none = 0, //----cover_none + cover_full = cover_mask //----cover_full + }; + + //----------------------------------------------------poly_subpixel_scale_e + // These constants determine the subpixel accuracy, to be more precise, + // the number of bits of the fractional part of the coordinates. + // The possible coordinate capacity in bits can be calculated by formula: + // sizeof(int) * 8 - poly_subpixel_shift, i.e, for 32-bit integers and + // 8-bits fractional part the capacity is 24 bits. + enum poly_subpixel_scale_e + { + poly_subpixel_shift = 8, //----poly_subpixel_shift + poly_subpixel_scale = 1< struct rect_base + { + typedef T value_type; + typedef rect_base self_type; + T x1, y1, x2, y2; + + rect_base() {} + rect_base(T x1_, T y1_, T x2_, T y2_) : + x1(x1_), y1(y1_), x2(x2_), y2(y2_) {} + + void init(T x1_, T y1_, T x2_, T y2_) + { + x1 = x1_; y1 = y1_; x2 = x2_; y2 = y2_; + } + + const self_type& normalize() + { + T t; + if(x1 > x2) { t = x1; x1 = x2; x2 = t; } + if(y1 > y2) { t = y1; y1 = y2; y2 = t; } + return *this; + } + + bool clip(const self_type& r) + { + if(x2 > r.x2) x2 = r.x2; + if(y2 > r.y2) y2 = r.y2; + if(x1 < r.x1) x1 = r.x1; + if(y1 < r.y1) y1 = r.y1; + return x1 <= x2 && y1 <= y2; + } + + bool is_valid() const + { + return x1 <= x2 && y1 <= y2; + } + + bool hit_test(T x, T y) const + { + return (x >= x1 && x <= x2 && y >= y1 && y <= y2); + } + + bool overlaps(const self_type& r) const + { + return !(r.x1 > x2 || r.x2 < x1 + || r.y1 > y2 || r.y2 < y1); + } + }; + + //-----------------------------------------------------intersect_rectangles + template + inline Rect intersect_rectangles(const Rect& r1, const Rect& r2) + { + Rect r = r1; + + // First process x2,y2 because the other order + // results in Internal Compiler Error under + // Microsoft Visual C++ .NET 2003 69462-335-0000007-18038 in + // case of "Maximize Speed" optimization option. + //----------------- + if(r.x2 > r2.x2) r.x2 = r2.x2; + if(r.y2 > r2.y2) r.y2 = r2.y2; + if(r.x1 < r2.x1) r.x1 = r2.x1; + if(r.y1 < r2.y1) r.y1 = r2.y1; + return r; + } + + + //---------------------------------------------------------unite_rectangles + template + inline Rect unite_rectangles(const Rect& r1, const Rect& r2) + { + Rect r = r1; + if(r.x2 < r2.x2) r.x2 = r2.x2; + if(r.y2 < r2.y2) r.y2 = r2.y2; + if(r.x1 > r2.x1) r.x1 = r2.x1; + if(r.y1 > r2.y1) r.y1 = r2.y1; + return r; + } + + typedef rect_base rect_i; //----rect_i + typedef rect_base rect_f; //----rect_f + typedef rect_base rect_d; //----rect_d + + //---------------------------------------------------------path_commands_e + enum path_commands_e + { + path_cmd_stop = 0, //----path_cmd_stop + path_cmd_move_to = 1, //----path_cmd_move_to + path_cmd_line_to = 2, //----path_cmd_line_to + path_cmd_curve3 = 3, //----path_cmd_curve3 + path_cmd_curve4 = 4, //----path_cmd_curve4 + path_cmd_curveN = 5, //----path_cmd_curveN + path_cmd_catrom = 6, //----path_cmd_catrom + path_cmd_ubspline = 7, //----path_cmd_ubspline + path_cmd_end_poly = 0x0F, //----path_cmd_end_poly + path_cmd_mask = 0x0F //----path_cmd_mask + }; + + //------------------------------------------------------------path_flags_e + enum path_flags_e + { + path_flags_none = 0, //----path_flags_none + path_flags_ccw = 0x10, //----path_flags_ccw + path_flags_cw = 0x20, //----path_flags_cw + path_flags_close = 0x40, //----path_flags_close + path_flags_mask = 0xF0 //----path_flags_mask + }; + + //---------------------------------------------------------------is_vertex + inline bool is_vertex(unsigned c) + { + return c >= path_cmd_move_to && c < path_cmd_end_poly; + } + + //--------------------------------------------------------------is_drawing + inline bool is_drawing(unsigned c) + { + return c >= path_cmd_line_to && c < path_cmd_end_poly; + } + + //-----------------------------------------------------------------is_stop + inline bool is_stop(unsigned c) + { + return c == path_cmd_stop; + } + + //--------------------------------------------------------------is_move_to + inline bool is_move_to(unsigned c) + { + return c == path_cmd_move_to; + } + + //--------------------------------------------------------------is_line_to + inline bool is_line_to(unsigned c) + { + return c == path_cmd_line_to; + } + + //----------------------------------------------------------------is_curve + inline bool is_curve(unsigned c) + { + return c == path_cmd_curve3 || c == path_cmd_curve4; + } + + //---------------------------------------------------------------is_curve3 + inline bool is_curve3(unsigned c) + { + return c == path_cmd_curve3; + } + + //---------------------------------------------------------------is_curve4 + inline bool is_curve4(unsigned c) + { + return c == path_cmd_curve4; + } + + //-------------------------------------------------------------is_end_poly + inline bool is_end_poly(unsigned c) + { + return (c & path_cmd_mask) == path_cmd_end_poly; + } + + //----------------------------------------------------------------is_close + inline bool is_close(unsigned c) + { + return (c & ~(path_flags_cw | path_flags_ccw)) == + (path_cmd_end_poly | path_flags_close); + } + + //------------------------------------------------------------is_next_poly + inline bool is_next_poly(unsigned c) + { + return is_stop(c) || is_move_to(c) || is_end_poly(c); + } + + //-------------------------------------------------------------------is_cw + inline bool is_cw(unsigned c) + { + return (c & path_flags_cw) != 0; + } + + //------------------------------------------------------------------is_ccw + inline bool is_ccw(unsigned c) + { + return (c & path_flags_ccw) != 0; + } + + //-------------------------------------------------------------is_oriented + inline bool is_oriented(unsigned c) + { + return (c & (path_flags_cw | path_flags_ccw)) != 0; + } + + //---------------------------------------------------------------is_closed + inline bool is_closed(unsigned c) + { + return (c & path_flags_close) != 0; + } + + //----------------------------------------------------------get_close_flag + inline unsigned get_close_flag(unsigned c) + { + return c & path_flags_close; + } + + //-------------------------------------------------------clear_orientation + inline unsigned clear_orientation(unsigned c) + { + return c & ~(path_flags_cw | path_flags_ccw); + } + + //---------------------------------------------------------get_orientation + inline unsigned get_orientation(unsigned c) + { + return c & (path_flags_cw | path_flags_ccw); + } + + //---------------------------------------------------------set_orientation + inline unsigned set_orientation(unsigned c, unsigned o) + { + return clear_orientation(c) | o; + } + + //--------------------------------------------------------------point_base + template struct point_base + { + typedef T value_type; + T x,y; + point_base() {} + point_base(T x_, T y_) : x(x_), y(y_) {} + }; + typedef point_base point_i; //-----point_i + typedef point_base point_f; //-----point_f + typedef point_base point_d; //-----point_d + + //-------------------------------------------------------------vertex_base + template struct vertex_base + { + typedef T value_type; + T x,y; + unsigned cmd; + vertex_base() {} + vertex_base(T x_, T y_, unsigned cmd_) : x(x_), y(y_), cmd(cmd_) {} + }; + typedef vertex_base vertex_i; //-----vertex_i + typedef vertex_base vertex_f; //-----vertex_f + typedef vertex_base vertex_d; //-----vertex_d + + //----------------------------------------------------------------row_info + template struct row_info + { + int x1, x2; + T* ptr; + row_info() {} + row_info(int x1_, int x2_, T* ptr_) : x1(x1_), x2(x2_), ptr(ptr_) {} + }; + + //----------------------------------------------------------const_row_info + template struct const_row_info + { + int x1, x2; + const T* ptr; + const_row_info() {} + const_row_info(int x1_, int x2_, const T* ptr_) : + x1(x1_), x2(x2_), ptr(ptr_) {} + }; + + //------------------------------------------------------------is_equal_eps + template inline bool is_equal_eps(T v1, T v2, T epsilon) + { + bool neg1 = v1 < 0.0; + bool neg2 = v2 < 0.0; + + if (neg1 != neg2) + return std::fabs(v1) < epsilon && std::fabs(v2) < epsilon; + + int int1, int2; + std::frexp(v1, &int1); + std::frexp(v2, &int2); + int min12 = int1 < int2 ? int1 : int2; + + v1 = std::ldexp(v1, -min12); + v2 = std::ldexp(v2, -min12); + + return std::fabs(v1 - v2) < epsilon; + } +} + + +#endif + diff --git a/src/agg/agg_bezier_arc.h b/src/agg/agg_bezier_arc.h new file mode 100644 index 0000000000..cfd9308ea5 --- /dev/null +++ b/src/agg/agg_bezier_arc.h @@ -0,0 +1,159 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Arc generator. Produces at most 4 consecutive cubic bezier curves, i.e., +// 4, 7, 10, or 13 vertices. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_BEZIER_ARC_INCLUDED +#define AGG_BEZIER_ARC_INCLUDED + +#include "agg_conv_transform.h" + +namespace agg +{ + + //----------------------------------------------------------------------- + void arc_to_bezier(double cx, double cy, double rx, double ry, + double start_angle, double sweep_angle, + double* curve); + + + //==============================================================bezier_arc + // + // See implemantaion agg_bezier_arc.cpp + // + class bezier_arc + { + public: + //-------------------------------------------------------------------- + bezier_arc() : m_vertex(26), m_num_vertices(0), m_cmd(path_cmd_line_to) {} + bezier_arc(double x, double y, + double rx, double ry, + double start_angle, + double sweep_angle) + { + init(x, y, rx, ry, start_angle, sweep_angle); + } + + //-------------------------------------------------------------------- + void init(double x, double y, + double rx, double ry, + double start_angle, + double sweep_angle); + + //-------------------------------------------------------------------- + void rewind(unsigned) + { + m_vertex = 0; + } + + //-------------------------------------------------------------------- + unsigned vertex(double* x, double* y) + { + if(m_vertex >= m_num_vertices) return path_cmd_stop; + *x = m_vertices[m_vertex]; + *y = m_vertices[m_vertex + 1]; + m_vertex += 2; + return (m_vertex == 2) ? unsigned(path_cmd_move_to) : m_cmd; + } + + // Supplemantary functions. num_vertices() actually returns doubled + // number of vertices. That is, for 1 vertex it returns 2. + //-------------------------------------------------------------------- + unsigned num_vertices() const { return m_num_vertices; } + const double* vertices() const { return m_vertices; } + double* vertices() { return m_vertices; } + + private: + unsigned m_vertex; + unsigned m_num_vertices; + double m_vertices[26]; + unsigned m_cmd; + }; + + + + //==========================================================bezier_arc_svg + // Compute an SVG-style bezier arc. + // + // Computes an elliptical arc from (x1, y1) to (x2, y2). The size and + // orientation of the ellipse are defined by two radii (rx, ry) + // and an x-axis-rotation, which indicates how the ellipse as a whole + // is rotated relative to the current coordinate system. The center + // (cx, cy) of the ellipse is calculated automatically to satisfy the + // constraints imposed by the other parameters. + // large-arc-flag and sweep-flag contribute to the automatic calculations + // and help determine how the arc is drawn. + class bezier_arc_svg + { + public: + //-------------------------------------------------------------------- + bezier_arc_svg() : m_arc(), m_radii_ok(false) {} + + bezier_arc_svg(double x1, double y1, + double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x2, double y2) : + m_arc(), m_radii_ok(false) + { + init(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2); + } + + //-------------------------------------------------------------------- + void init(double x1, double y1, + double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x2, double y2); + + //-------------------------------------------------------------------- + bool radii_ok() const { return m_radii_ok; } + + //-------------------------------------------------------------------- + void rewind(unsigned) + { + m_arc.rewind(0); + } + + //-------------------------------------------------------------------- + unsigned vertex(double* x, double* y) + { + return m_arc.vertex(x, y); + } + + // Supplemantary functions. num_vertices() actually returns doubled + // number of vertices. That is, for 1 vertex it returns 2. + //-------------------------------------------------------------------- + unsigned num_vertices() const { return m_arc.num_vertices(); } + const double* vertices() const { return m_arc.vertices(); } + double* vertices() { return m_arc.vertices(); } + + private: + bezier_arc m_arc; + bool m_radii_ok; + }; + + + + +} + + +#endif diff --git a/src/agg/agg_clip_liang_barsky.h b/src/agg/agg_clip_liang_barsky.h new file mode 100644 index 0000000000..4b5fedbab5 --- /dev/null +++ b/src/agg/agg_clip_liang_barsky.h @@ -0,0 +1,333 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Liang-Barsky clipping +// +//---------------------------------------------------------------------------- +#ifndef AGG_CLIP_LIANG_BARSKY_INCLUDED +#define AGG_CLIP_LIANG_BARSKY_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + enum clipping_flags_e + { + clipping_flags_x1_clipped = 4, + clipping_flags_x2_clipped = 1, + clipping_flags_y1_clipped = 8, + clipping_flags_y2_clipped = 2, + clipping_flags_x_clipped = clipping_flags_x1_clipped | clipping_flags_x2_clipped, + clipping_flags_y_clipped = clipping_flags_y1_clipped | clipping_flags_y2_clipped + }; + + //----------------------------------------------------------clipping_flags + // Determine the clipping code of the vertex according to the + // Cyrus-Beck line clipping algorithm + // + // | | + // 0110 | 0010 | 0011 + // | | + // -------+--------+-------- clip_box.y2 + // | | + // 0100 | 0000 | 0001 + // | | + // -------+--------+-------- clip_box.y1 + // | | + // 1100 | 1000 | 1001 + // | | + // clip_box.x1 clip_box.x2 + // + // + template + inline unsigned clipping_flags(T x, T y, const rect_base& clip_box) + { + return (x > clip_box.x2) | + ((y > clip_box.y2) << 1) | + ((x < clip_box.x1) << 2) | + ((y < clip_box.y1) << 3); + } + + //--------------------------------------------------------clipping_flags_x + template + inline unsigned clipping_flags_x(T x, const rect_base& clip_box) + { + return (x > clip_box.x2) | ((x < clip_box.x1) << 2); + } + + + //--------------------------------------------------------clipping_flags_y + template + inline unsigned clipping_flags_y(T y, const rect_base& clip_box) + { + return ((y > clip_box.y2) << 1) | ((y < clip_box.y1) << 3); + } + + + //-------------------------------------------------------clip_liang_barsky + template + inline unsigned clip_liang_barsky(T x1, T y1, T x2, T y2, + const rect_base& clip_box, + T* x, T* y) + { + const double nearzero = 1e-30; + + double deltax = x2 - x1; + double deltay = y2 - y1; + double xin; + double xout; + double yin; + double yout; + double tinx; + double tiny; + double toutx; + double touty; + double tin1; + double tin2; + double tout1; + unsigned np = 0; + + if(deltax == 0.0) + { + // bump off of the vertical + deltax = (x1 > clip_box.x1) ? -nearzero : nearzero; + } + + if(deltay == 0.0) + { + // bump off of the horizontal + deltay = (y1 > clip_box.y1) ? -nearzero : nearzero; + } + + if(deltax > 0.0) + { + // points to right + xin = clip_box.x1; + xout = clip_box.x2; + } + else + { + xin = clip_box.x2; + xout = clip_box.x1; + } + + if(deltay > 0.0) + { + // points up + yin = clip_box.y1; + yout = clip_box.y2; + } + else + { + yin = clip_box.y2; + yout = clip_box.y1; + } + + tinx = (xin - x1) / deltax; + tiny = (yin - y1) / deltay; + + if (tinx < tiny) + { + // hits x first + tin1 = tinx; + tin2 = tiny; + } + else + { + // hits y first + tin1 = tiny; + tin2 = tinx; + } + + if(tin1 <= 1.0) + { + if(0.0 < tin1) + { + *x++ = (T)xin; + *y++ = (T)yin; + ++np; + } + + if(tin2 <= 1.0) + { + toutx = (xout - x1) / deltax; + touty = (yout - y1) / deltay; + + tout1 = (toutx < touty) ? toutx : touty; + + if(tin2 > 0.0 || tout1 > 0.0) + { + if(tin2 <= tout1) + { + if(tin2 > 0.0) + { + if(tinx > tiny) + { + *x++ = (T)xin; + *y++ = (T)(y1 + tinx * deltay); + } + else + { + *x++ = (T)(x1 + tiny * deltax); + *y++ = (T)yin; + } + ++np; + } + + if(tout1 < 1.0) + { + if(toutx < touty) + { + *x++ = (T)xout; + *y++ = (T)(y1 + toutx * deltay); + } + else + { + *x++ = (T)(x1 + touty * deltax); + *y++ = (T)yout; + } + } + else + { + *x++ = x2; + *y++ = y2; + } + ++np; + } + else + { + if(tinx > tiny) + { + *x++ = (T)xin; + *y++ = (T)yout; + } + else + { + *x++ = (T)xout; + *y++ = (T)yin; + } + ++np; + } + } + } + } + return np; + } + + + //---------------------------------------------------------------------------- + template + bool clip_move_point(T x1, T y1, T x2, T y2, + const rect_base& clip_box, + T* x, T* y, unsigned flags) + { + T bound; + + if(flags & clipping_flags_x_clipped) + { + if(x1 == x2) + { + return false; + } + bound = (flags & clipping_flags_x1_clipped) ? clip_box.x1 : clip_box.x2; + *y = (T)(double(bound - x1) * (y2 - y1) / (x2 - x1) + y1); + *x = bound; + } + + flags = clipping_flags_y(*y, clip_box); + if(flags & clipping_flags_y_clipped) + { + if(y1 == y2) + { + return false; + } + bound = (flags & clipping_flags_y1_clipped) ? clip_box.y1 : clip_box.y2; + *x = (T)(double(bound - y1) * (x2 - x1) / (y2 - y1) + x1); + *y = bound; + } + return true; + } + + //-------------------------------------------------------clip_line_segment + // Returns: ret >= 4 - Fully clipped + // (ret & 1) != 0 - First point has been moved + // (ret & 2) != 0 - Second point has been moved + // + template + unsigned clip_line_segment(T* x1, T* y1, T* x2, T* y2, + const rect_base& clip_box) + { + unsigned f1 = clipping_flags(*x1, *y1, clip_box); + unsigned f2 = clipping_flags(*x2, *y2, clip_box); + unsigned ret = 0; + + if((f2 | f1) == 0) + { + // Fully visible + return 0; + } + + if((f1 & clipping_flags_x_clipped) != 0 && + (f1 & clipping_flags_x_clipped) == (f2 & clipping_flags_x_clipped)) + { + // Fully clipped + return 4; + } + + if((f1 & clipping_flags_y_clipped) != 0 && + (f1 & clipping_flags_y_clipped) == (f2 & clipping_flags_y_clipped)) + { + // Fully clipped + return 4; + } + + T tx1 = *x1; + T ty1 = *y1; + T tx2 = *x2; + T ty2 = *y2; + if(f1) + { + if(!clip_move_point(tx1, ty1, tx2, ty2, clip_box, x1, y1, f1)) + { + return 4; + } + if(*x1 == *x2 && *y1 == *y2) + { + return 4; + } + ret |= 1; + } + if(f2) + { + if(!clip_move_point(tx1, ty1, tx2, ty2, clip_box, x2, y2, f2)) + { + return 4; + } + if(*x1 == *x2 && *y1 == *y2) + { + return 4; + } + ret |= 2; + } + return ret; + } + + +} + + +#endif diff --git a/src/agg/agg_color_gray.h b/src/agg/agg_color_gray.h new file mode 100644 index 0000000000..f66588c119 --- /dev/null +++ b/src/agg/agg_color_gray.h @@ -0,0 +1,1047 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +// +// color types gray8, gray16 +// +//---------------------------------------------------------------------------- + +#ifndef AGG_COLOR_GRAY_INCLUDED +#define AGG_COLOR_GRAY_INCLUDED + +#include "agg_basics.h" +#include "agg_color_rgba.h" + +namespace agg +{ + + //===================================================================gray8 + template + struct gray8T + { + typedef int8u value_type; + typedef int32u calc_type; + typedef int32 long_type; + enum base_scale_e + { + base_shift = 8, + base_scale = 1 << base_shift, + base_mask = base_scale - 1, + base_MSB = 1 << (base_shift - 1) + }; + typedef gray8T self_type; + + value_type v; + value_type a; + + static value_type luminance(const rgba& c) + { + // Calculate grayscale value as per ITU-R BT.709. + return value_type(uround((0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) * base_mask)); + } + + static value_type luminance(const rgba8& c) + { + // Calculate grayscale value as per ITU-R BT.709. + return value_type((55u * c.r + 184u * c.g + 18u * c.b) >> 8); + } + + static void convert(gray8T& dst, const gray8T& src) + { + dst.v = sRGB_conv::rgb_from_sRGB(src.v); + dst.a = src.a; + } + + static void convert(gray8T& dst, const gray8T& src) + { + dst.v = sRGB_conv::rgb_to_sRGB(src.v); + dst.a = src.a; + } + + static void convert(gray8T& dst, const rgba8& src) + { + dst.v = luminance(src); + dst.a = src.a; + } + + static void convert(gray8T& dst, const srgba8& src) + { + // The RGB weights are only valid for linear values. + convert(dst, rgba8(src)); + } + + static void convert(gray8T& dst, const rgba8& src) + { + dst.v = sRGB_conv::rgb_to_sRGB(luminance(src)); + dst.a = src.a; + } + + static void convert(gray8T& dst, const srgba8& src) + { + // The RGB weights are only valid for linear values. + convert(dst, rgba8(src)); + } + + //-------------------------------------------------------------------- + gray8T() {} + + //-------------------------------------------------------------------- + explicit gray8T(unsigned v_, unsigned a_ = base_mask) : + v(int8u(v_)), a(int8u(a_)) {} + + //-------------------------------------------------------------------- + gray8T(const self_type& c, unsigned a_) : + v(c.v), a(value_type(a_)) {} + + //-------------------------------------------------------------------- + gray8T(const rgba& c) : + v(luminance(c)), + a(value_type(uround(c.a * base_mask))) {} + + //-------------------------------------------------------------------- + template + gray8T(const gray8T& c) + { + convert(*this, c); + } + + //-------------------------------------------------------------------- + template + gray8T(const rgba8T& c) + { + convert(*this, c); + } + + //-------------------------------------------------------------------- + template + T convert_from_sRGB() const + { + typename T::value_type y = sRGB_conv::rgb_from_sRGB(v); + return T(y, y, y, sRGB_conv::alpha_from_sRGB(a)); + } + + template + T convert_to_sRGB() const + { + typename T::value_type y = sRGB_conv::rgb_to_sRGB(v); + return T(y, y, y, sRGB_conv::alpha_to_sRGB(a)); + } + + //-------------------------------------------------------------------- + rgba8 make_rgba8(const linear&) const + { + return rgba8(v, v, v, a); + } + + rgba8 make_rgba8(const sRGB&) const + { + return convert_from_sRGB(); + } + + operator rgba8() const + { + return make_rgba8(Colorspace()); + } + + //-------------------------------------------------------------------- + srgba8 make_srgba8(const linear&) const + { + return convert_to_sRGB(); + } + + srgba8 make_srgba8(const sRGB&) const + { + return srgba8(v, v, v, a); + } + + operator srgba8() const + { + return make_rgba8(Colorspace()); + } + + //-------------------------------------------------------------------- + rgba16 make_rgba16(const linear&) const + { + rgba16::value_type rgb = (v << 8) | v; + return rgba16(rgb, rgb, rgb, (a << 8) | a); + } + + rgba16 make_rgba16(const sRGB&) const + { + return convert_from_sRGB(); + } + + operator rgba16() const + { + return make_rgba16(Colorspace()); + } + + //-------------------------------------------------------------------- + rgba32 make_rgba32(const linear&) const + { + rgba32::value_type v32 = v / 255.0f; + return rgba32(v32, v32, v32, a / 255.0f); + } + + rgba32 make_rgba32(const sRGB&) const + { + return convert_from_sRGB(); + } + + operator rgba32() const + { + return make_rgba32(Colorspace()); + } + + //-------------------------------------------------------------------- + static AGG_INLINE double to_double(value_type a) + { + return double(a) / base_mask; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type from_double(double a) + { + return value_type(uround(a * base_mask)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type empty_value() + { + return 0; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type full_value() + { + return base_mask; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_transparent() const + { + return a == 0; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_opaque() const + { + return a == base_mask; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, exact over int8u. + static AGG_INLINE value_type multiply(value_type a, value_type b) + { + calc_type t = a * b + base_MSB; + return value_type(((t >> base_shift) + t) >> base_shift); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type demultiply(value_type a, value_type b) + { + if (a * b == 0) + { + return 0; + } + else if (a >= b) + { + return base_mask; + } + else return value_type((a * base_mask + (b >> 1)) / b); + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downscale(T a) + { + return a >> base_shift; + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downshift(T a, unsigned n) + { + return a >> n; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, exact over int8u. + // Specifically for multiplying a color component by a cover. + static AGG_INLINE value_type mult_cover(value_type a, value_type b) + { + return multiply(a, b); + } + + //-------------------------------------------------------------------- + static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) + { + return multiply(b, a); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a, assuming q is premultiplied by a. + static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) + { + return p + q - multiply(p, a); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a. + static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) + { + int t = (q - p) * a + base_MSB - (p > q); + return value_type(p + (((t >> base_shift) + t) >> base_shift)); + } + + //-------------------------------------------------------------------- + self_type& clear() + { + v = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& opacity(double a_) + { + if (a_ < 0) a = 0; + else if (a_ > 1) a = 1; + else a = (value_type)uround(a_ * double(base_mask)); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return double(a) / double(base_mask); + } + + //-------------------------------------------------------------------- + self_type& premultiply() + { + if (a < base_mask) + { + if (a == 0) v = 0; + else v = multiply(v, a); + } + return *this; + } + + //-------------------------------------------------------------------- + self_type& demultiply() + { + if (a < base_mask) + { + if (a == 0) + { + v = 0; + } + else + { + calc_type v_ = (calc_type(v) * base_mask) / a; + v = value_type((v_ > base_mask) ? (value_type)base_mask : v_); + } + } + return *this; + } + + //-------------------------------------------------------------------- + self_type gradient(self_type c, double k) const + { + self_type ret; + calc_type ik = uround(k * base_scale); + ret.v = lerp(v, c.v, ik); + ret.a = lerp(a, c.a, ik); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + calc_type cv, ca; + if (cover == cover_mask) + { + if (c.a == base_mask) + { + *this = c; + return; + } + else + { + cv = v + c.v; + ca = a + c.a; + } + } + else + { + cv = v + mult_cover(c.v, cover); + ca = a + mult_cover(c.a, cover); + } + v = (value_type)((cv > calc_type(base_mask)) ? calc_type(base_mask) : cv); + a = (value_type)((ca > calc_type(base_mask)) ? calc_type(base_mask) : ca); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0); } + }; + + typedef gray8T gray8; + typedef gray8T sgray8; + + + //==================================================================gray16 + struct gray16 + { + typedef int16u value_type; + typedef int32u calc_type; + typedef int64 long_type; + enum base_scale_e + { + base_shift = 16, + base_scale = 1 << base_shift, + base_mask = base_scale - 1, + base_MSB = 1 << (base_shift - 1) + }; + typedef gray16 self_type; + + value_type v; + value_type a; + + static value_type luminance(const rgba& c) + { + // Calculate grayscale value as per ITU-R BT.709. + return value_type(uround((0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) * base_mask)); + } + + static value_type luminance(const rgba16& c) + { + // Calculate grayscale value as per ITU-R BT.709. + return value_type((13933u * c.r + 46872u * c.g + 4732u * c.b) >> 16); + } + + static value_type luminance(const rgba8& c) + { + return luminance(rgba16(c)); + } + + static value_type luminance(const srgba8& c) + { + return luminance(rgba16(c)); + } + + static value_type luminance(const rgba32& c) + { + return luminance(rgba(c)); + } + + //-------------------------------------------------------------------- + gray16() {} + + //-------------------------------------------------------------------- + explicit gray16(unsigned v_, unsigned a_ = base_mask) : + v(int16u(v_)), a(int16u(a_)) {} + + //-------------------------------------------------------------------- + gray16(const self_type& c, unsigned a_) : + v(c.v), a(value_type(a_)) {} + + //-------------------------------------------------------------------- + gray16(const rgba& c) : + v(luminance(c)), + a((value_type)uround(c.a * double(base_mask))) {} + + //-------------------------------------------------------------------- + gray16(const rgba8& c) : + v(luminance(c)), + a((value_type(c.a) << 8) | c.a) {} + + //-------------------------------------------------------------------- + gray16(const srgba8& c) : + v(luminance(c)), + a((value_type(c.a) << 8) | c.a) {} + + //-------------------------------------------------------------------- + gray16(const rgba16& c) : + v(luminance(c)), + a(c.a) {} + + //-------------------------------------------------------------------- + gray16(const gray8& c) : + v((value_type(c.v) << 8) | c.v), + a((value_type(c.a) << 8) | c.a) {} + + //-------------------------------------------------------------------- + gray16(const sgray8& c) : + v(sRGB_conv::rgb_from_sRGB(c.v)), + a(sRGB_conv::alpha_from_sRGB(c.a)) {} + + //-------------------------------------------------------------------- + operator rgba8() const + { + return rgba8(v >> 8, v >> 8, v >> 8, a >> 8); + } + + //-------------------------------------------------------------------- + operator srgba8() const + { + value_type y = sRGB_conv::rgb_to_sRGB(v); + return srgba8(y, y, y, sRGB_conv::alpha_to_sRGB(a)); + } + + //-------------------------------------------------------------------- + operator rgba16() const + { + return rgba16(v, v, v, a); + } + + //-------------------------------------------------------------------- + operator rgba32() const + { + rgba32::value_type v32 = v / 65535.0f; + return rgba32(v32, v32, v32, a / 65535.0f); + } + + //-------------------------------------------------------------------- + operator gray8() const + { + return gray8(v >> 8, a >> 8); + } + + //-------------------------------------------------------------------- + operator sgray8() const + { + return sgray8( + sRGB_conv::rgb_to_sRGB(v), + sRGB_conv::alpha_to_sRGB(a)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE double to_double(value_type a) + { + return double(a) / base_mask; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type from_double(double a) + { + return value_type(uround(a * base_mask)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type empty_value() + { + return 0; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type full_value() + { + return base_mask; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_transparent() const + { + return a == 0; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_opaque() const + { + return a == base_mask; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, exact over int16u. + static AGG_INLINE value_type multiply(value_type a, value_type b) + { + calc_type t = a * b + base_MSB; + return value_type(((t >> base_shift) + t) >> base_shift); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type demultiply(value_type a, value_type b) + { + if (a * b == 0) + { + return 0; + } + else if (a >= b) + { + return base_mask; + } + else return value_type((a * base_mask + (b >> 1)) / b); + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downscale(T a) + { + return a >> base_shift; + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downshift(T a, unsigned n) + { + return a >> n; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, almost exact over int16u. + // Specifically for multiplying a color component by a cover. + static AGG_INLINE value_type mult_cover(value_type a, cover_type b) + { + return multiply(a, b << 8 | b); + } + + //-------------------------------------------------------------------- + static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) + { + return mult_cover(b, a) >> 8; + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a, assuming q is premultiplied by a. + static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) + { + return p + q - multiply(p, a); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a. + static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) + { + int t = (q - p) * a + base_MSB - (p > q); + return value_type(p + (((t >> base_shift) + t) >> base_shift)); + } + + //-------------------------------------------------------------------- + self_type& clear() + { + v = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& opacity(double a_) + { + if (a_ < 0) a = 0; + else if(a_ > 1) a = 1; + else a = (value_type)uround(a_ * double(base_mask)); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return double(a) / double(base_mask); + } + + + //-------------------------------------------------------------------- + self_type& premultiply() + { + if (a < base_mask) + { + if(a == 0) v = 0; + else v = multiply(v, a); + } + return *this; + } + + //-------------------------------------------------------------------- + self_type& demultiply() + { + if (a < base_mask) + { + if (a == 0) + { + v = 0; + } + else + { + calc_type v_ = (calc_type(v) * base_mask) / a; + v = (v_ > base_mask) ? value_type(base_mask) : value_type(v_); + } + } + return *this; + } + + //-------------------------------------------------------------------- + self_type gradient(self_type c, double k) const + { + self_type ret; + calc_type ik = uround(k * base_scale); + ret.v = lerp(v, c.v, ik); + ret.a = lerp(a, c.a, ik); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + calc_type cv, ca; + if (cover == cover_mask) + { + if (c.a == base_mask) + { + *this = c; + return; + } + else + { + cv = v + c.v; + ca = a + c.a; + } + } + else + { + cv = v + mult_cover(c.v, cover); + ca = a + mult_cover(c.a, cover); + } + v = (value_type)((cv > calc_type(base_mask)) ? calc_type(base_mask) : cv); + a = (value_type)((ca > calc_type(base_mask)) ? calc_type(base_mask) : ca); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0); } + }; + + + //===================================================================gray32 + struct gray32 + { + typedef float value_type; + typedef double calc_type; + typedef double long_type; + typedef gray32 self_type; + + value_type v; + value_type a; + + // Calculate grayscale value as per ITU-R BT.709. + static value_type luminance(double r, double g, double b) + { + return value_type(0.2126 * r + 0.7152 * g + 0.0722 * b); + } + + static value_type luminance(const rgba& c) + { + return luminance(c.r, c.g, c.b); + } + + static value_type luminance(const rgba32& c) + { + return luminance(c.r, c.g, c.b); + } + + static value_type luminance(const rgba8& c) + { + return luminance(c.r / 255.0, c.g / 255.0, c.g / 255.0); + } + + static value_type luminance(const rgba16& c) + { + return luminance(c.r / 65535.0, c.g / 65535.0, c.g / 65535.0); + } + + //-------------------------------------------------------------------- + gray32() {} + + //-------------------------------------------------------------------- + explicit gray32(value_type v_, value_type a_ = 1) : + v(v_), a(a_) {} + + //-------------------------------------------------------------------- + gray32(const self_type& c, value_type a_) : + v(c.v), a(a_) {} + + //-------------------------------------------------------------------- + gray32(const rgba& c) : + v(luminance(c)), + a(value_type(c.a)) {} + + //-------------------------------------------------------------------- + gray32(const rgba8& c) : + v(luminance(c)), + a(value_type(c.a / 255.0)) {} + + //-------------------------------------------------------------------- + gray32(const srgba8& c) : + v(luminance(rgba32(c))), + a(value_type(c.a / 255.0)) {} + + //-------------------------------------------------------------------- + gray32(const rgba16& c) : + v(luminance(c)), + a(value_type(c.a / 65535.0)) {} + + //-------------------------------------------------------------------- + gray32(const rgba32& c) : + v(luminance(c)), + a(value_type(c.a)) {} + + //-------------------------------------------------------------------- + gray32(const gray8& c) : + v(value_type(c.v / 255.0)), + a(value_type(c.a / 255.0)) {} + + //-------------------------------------------------------------------- + gray32(const sgray8& c) : + v(sRGB_conv::rgb_from_sRGB(c.v)), + a(sRGB_conv::alpha_from_sRGB(c.a)) {} + + //-------------------------------------------------------------------- + gray32(const gray16& c) : + v(value_type(c.v / 65535.0)), + a(value_type(c.a / 65535.0)) {} + + //-------------------------------------------------------------------- + operator rgba() const + { + return rgba(v, v, v, a); + } + + //-------------------------------------------------------------------- + operator gray8() const + { + return gray8(uround(v * 255.0), uround(a * 255.0)); + } + + //-------------------------------------------------------------------- + operator sgray8() const + { + // Return (non-premultiplied) sRGB values. + return sgray8( + sRGB_conv::rgb_to_sRGB(v), + sRGB_conv::alpha_to_sRGB(a)); + } + + //-------------------------------------------------------------------- + operator gray16() const + { + return gray16(uround(v * 65535.0), uround(a * 65535.0)); + } + + //-------------------------------------------------------------------- + operator rgba8() const + { + rgba8::value_type y = uround(v * 255.0); + return rgba8(y, y, y, uround(a * 255.0)); + } + + //-------------------------------------------------------------------- + operator srgba8() const + { + srgba8::value_type y = sRGB_conv::rgb_to_sRGB(v); + return srgba8(y, y, y, sRGB_conv::alpha_to_sRGB(a)); + } + + //-------------------------------------------------------------------- + operator rgba16() const + { + rgba16::value_type y = uround(v * 65535.0); + return rgba16(y, y, y, uround(a * 65535.0)); + } + + //-------------------------------------------------------------------- + operator rgba32() const + { + return rgba32(v, v, v, a); + } + + //-------------------------------------------------------------------- + static AGG_INLINE double to_double(value_type a) + { + return a; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type from_double(double a) + { + return value_type(a); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type empty_value() + { + return 0; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type full_value() + { + return 1; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_transparent() const + { + return a <= 0; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_opaque() const + { + return a >= 1; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type invert(value_type x) + { + return 1 - x; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type multiply(value_type a, value_type b) + { + return value_type(a * b); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type demultiply(value_type a, value_type b) + { + return (b == 0) ? 0 : value_type(a / b); + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downscale(T a) + { + return a; + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downshift(T a, unsigned n) + { + return n > 0 ? a / (1 << n) : a; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type mult_cover(value_type a, cover_type b) + { + return value_type(a * b / cover_mask); + } + + //-------------------------------------------------------------------- + static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) + { + return cover_type(uround(a * b)); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a, assuming q is premultiplied by a. + static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) + { + return (1 - a) * p + q; // more accurate than "p + q - p * a" + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a. + static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) + { + // The form "p + a * (q - p)" avoids a multiplication, but may produce an + // inaccurate result. For example, "p + (q - p)" may not be exactly equal + // to q. Therefore, stick to the basic expression, which at least produces + // the correct result at either extreme. + return (1 - a) * p + a * q; + } + + //-------------------------------------------------------------------- + self_type& clear() + { + v = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& opacity(double a_) + { + if (a_ < 0) a = 0; + else if (a_ > 1) a = 1; + else a = value_type(a_); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return a; + } + + + //-------------------------------------------------------------------- + self_type& premultiply() + { + if (a < 0) v = 0; + else if(a < 1) v *= a; + return *this; + } + + //-------------------------------------------------------------------- + self_type& demultiply() + { + if (a < 0) v = 0; + else if (a < 1) v /= a; + return *this; + } + + //-------------------------------------------------------------------- + self_type gradient(self_type c, double k) const + { + return self_type( + value_type(v + (c.v - v) * k), + value_type(a + (c.a - a) * k)); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0); } + }; +} + + + + +#endif diff --git a/src/agg/agg_color_rgba.h b/src/agg/agg_color_rgba.h new file mode 100644 index 0000000000..ff33a1179c --- /dev/null +++ b/src/agg/agg_color_rgba.h @@ -0,0 +1,1353 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_COLOR_RGBA_INCLUDED +#define AGG_COLOR_RGBA_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_gamma_lut.h" + +namespace agg +{ + // Supported component orders for RGB and RGBA pixel formats + //======================================================================= + struct order_rgb { enum rgb_e { R=0, G=1, B=2, N=3 }; }; + struct order_bgr { enum bgr_e { B=0, G=1, R=2, N=3 }; }; + struct order_rgba { enum rgba_e { R=0, G=1, B=2, A=3, N=4 }; }; + struct order_argb { enum argb_e { A=0, R=1, G=2, B=3, N=4 }; }; + struct order_abgr { enum abgr_e { A=0, B=1, G=2, R=3, N=4 }; }; + struct order_bgra { enum bgra_e { B=0, G=1, R=2, A=3, N=4 }; }; + + // Colorspace tag types. + struct linear {}; + struct sRGB {}; + + //====================================================================rgba + struct rgba + { + typedef double value_type; + + double r; + double g; + double b; + double a; + + //-------------------------------------------------------------------- + rgba() {} + + //-------------------------------------------------------------------- + rgba(double r_, double g_, double b_, double a_=1.0) : + r(r_), g(g_), b(b_), a(a_) {} + + //-------------------------------------------------------------------- + rgba(const rgba& c, double a_) : r(c.r), g(c.g), b(c.b), a(a_) {} + + //-------------------------------------------------------------------- + rgba& clear() + { + r = g = b = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + rgba& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + rgba& opacity(double a_) + { + if (a_ < 0) a = 0; + else if (a_ > 1) a = 1; + else a = a_; + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return a; + } + + //-------------------------------------------------------------------- + rgba& premultiply() + { + r *= a; + g *= a; + b *= a; + return *this; + } + + //-------------------------------------------------------------------- + rgba& premultiply(double a_) + { + if (a <= 0 || a_ <= 0) + { + r = g = b = a = 0; + } + else + { + a_ /= a; + r *= a_; + g *= a_; + b *= a_; + a = a_; + } + return *this; + } + + //-------------------------------------------------------------------- + rgba& demultiply() + { + if (a == 0) + { + r = g = b = 0; + } + else + { + double a_ = 1.0 / a; + r *= a_; + g *= a_; + b *= a_; + } + return *this; + } + + + //-------------------------------------------------------------------- + rgba gradient(rgba c, double k) const + { + rgba ret; + ret.r = r + (c.r - r) * k; + ret.g = g + (c.g - g) * k; + ret.b = b + (c.b - b) * k; + ret.a = a + (c.a - a) * k; + return ret; + } + + rgba& operator+=(const rgba& c) + { + r += c.r; + g += c.g; + b += c.b; + a += c.a; + return *this; + } + + rgba& operator*=(double k) + { + r *= k; + g *= k; + b *= k; + a *= k; + return *this; + } + + //-------------------------------------------------------------------- + static rgba no_color() { return rgba(0,0,0,0); } + + //-------------------------------------------------------------------- + static rgba from_wavelength(double wl, double gamma = 1.0); + + //-------------------------------------------------------------------- + explicit rgba(double wavelen, double gamma=1.0) + { + *this = from_wavelength(wavelen, gamma); + } + + }; + + inline rgba operator+(const rgba& a, const rgba& b) + { + return rgba(a) += b; + } + + inline rgba operator*(const rgba& a, double b) + { + return rgba(a) *= b; + } + + //------------------------------------------------------------------------ + inline rgba rgba::from_wavelength(double wl, double gamma) + { + rgba t(0.0, 0.0, 0.0); + + if (wl >= 380.0 && wl <= 440.0) + { + t.r = -1.0 * (wl - 440.0) / (440.0 - 380.0); + t.b = 1.0; + } + else if (wl >= 440.0 && wl <= 490.0) + { + t.g = (wl - 440.0) / (490.0 - 440.0); + t.b = 1.0; + } + else if (wl >= 490.0 && wl <= 510.0) + { + t.g = 1.0; + t.b = -1.0 * (wl - 510.0) / (510.0 - 490.0); + } + else if (wl >= 510.0 && wl <= 580.0) + { + t.r = (wl - 510.0) / (580.0 - 510.0); + t.g = 1.0; + } + else if (wl >= 580.0 && wl <= 645.0) + { + t.r = 1.0; + t.g = -1.0 * (wl - 645.0) / (645.0 - 580.0); + } + else if (wl >= 645.0 && wl <= 780.0) + { + t.r = 1.0; + } + + double s = 1.0; + if (wl > 700.0) s = 0.3 + 0.7 * (780.0 - wl) / (780.0 - 700.0); + else if (wl < 420.0) s = 0.3 + 0.7 * (wl - 380.0) / (420.0 - 380.0); + + t.r = pow(t.r * s, gamma); + t.g = pow(t.g * s, gamma); + t.b = pow(t.b * s, gamma); + return t; + } + + inline rgba rgba_pre(double r, double g, double b, double a) + { + return rgba(r, g, b, a).premultiply(); + } + + + //===================================================================rgba8 + template + struct rgba8T + { + typedef int8u value_type; + typedef int32u calc_type; + typedef int32 long_type; + enum base_scale_e + { + base_shift = 8, + base_scale = 1 << base_shift, + base_mask = base_scale - 1, + base_MSB = 1 << (base_shift - 1) + }; + typedef rgba8T self_type; + + + value_type r; + value_type g; + value_type b; + value_type a; + + static void convert(rgba8T& dst, const rgba8T& src) + { + dst.r = sRGB_conv::rgb_from_sRGB(src.r); + dst.g = sRGB_conv::rgb_from_sRGB(src.g); + dst.b = sRGB_conv::rgb_from_sRGB(src.b); + dst.a = src.a; + } + + static void convert(rgba8T& dst, const rgba8T& src) + { + dst.r = sRGB_conv::rgb_to_sRGB(src.r); + dst.g = sRGB_conv::rgb_to_sRGB(src.g); + dst.b = sRGB_conv::rgb_to_sRGB(src.b); + dst.a = src.a; + } + + static void convert(rgba8T& dst, const rgba& src) + { + dst.r = value_type(uround(src.r * base_mask)); + dst.g = value_type(uround(src.g * base_mask)); + dst.b = value_type(uround(src.b * base_mask)); + dst.a = value_type(uround(src.a * base_mask)); + } + + static void convert(rgba8T& dst, const rgba& src) + { + // Use the "float" table. + dst.r = sRGB_conv::rgb_to_sRGB(float(src.r)); + dst.g = sRGB_conv::rgb_to_sRGB(float(src.g)); + dst.b = sRGB_conv::rgb_to_sRGB(float(src.b)); + dst.a = sRGB_conv::alpha_to_sRGB(float(src.a)); + } + + static void convert(rgba& dst, const rgba8T& src) + { + dst.r = src.r / 255.0; + dst.g = src.g / 255.0; + dst.b = src.b / 255.0; + dst.a = src.a / 255.0; + } + + static void convert(rgba& dst, const rgba8T& src) + { + // Use the "float" table. + dst.r = sRGB_conv::rgb_from_sRGB(src.r); + dst.g = sRGB_conv::rgb_from_sRGB(src.g); + dst.b = sRGB_conv::rgb_from_sRGB(src.b); + dst.a = sRGB_conv::alpha_from_sRGB(src.a); + } + + //-------------------------------------------------------------------- + rgba8T() {} + + //-------------------------------------------------------------------- + rgba8T(unsigned r_, unsigned g_, unsigned b_, unsigned a_ = base_mask) : + r(value_type(r_)), + g(value_type(g_)), + b(value_type(b_)), + a(value_type(a_)) {} + + //-------------------------------------------------------------------- + rgba8T(const rgba& c) + { + convert(*this, c); + } + + //-------------------------------------------------------------------- + rgba8T(const self_type& c, unsigned a_) : + r(c.r), g(c.g), b(c.b), a(value_type(a_)) {} + + //-------------------------------------------------------------------- + template + rgba8T(const rgba8T& c) + { + convert(*this, c); + } + + //-------------------------------------------------------------------- + operator rgba() const + { + rgba c; + convert(c, *this); + return c; + } + + //-------------------------------------------------------------------- + static AGG_INLINE double to_double(value_type a) + { + return double(a) / base_mask; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type from_double(double a) + { + return value_type(uround(a * base_mask)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type empty_value() + { + return 0; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type full_value() + { + return base_mask; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_transparent() const + { + return a == 0; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_opaque() const + { + return a == base_mask; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type invert(value_type x) + { + return base_mask - x; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, exact over int8u. + static AGG_INLINE value_type multiply(value_type a, value_type b) + { + calc_type t = a * b + base_MSB; + return value_type(((t >> base_shift) + t) >> base_shift); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type demultiply(value_type a, value_type b) + { + if (a * b == 0) + { + return 0; + } + else if (a >= b) + { + return base_mask; + } + else return value_type((a * base_mask + (b >> 1)) / b); + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downscale(T a) + { + return a >> base_shift; + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downshift(T a, unsigned n) + { + return a >> n; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, exact over int8u. + // Specifically for multiplying a color component by a cover. + static AGG_INLINE value_type mult_cover(value_type a, cover_type b) + { + return multiply(a, b); + } + + //-------------------------------------------------------------------- + static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) + { + return multiply(b, a); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a, assuming q is premultiplied by a. + static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) + { + return p + q - multiply(p, a); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a. + static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) + { + int t = (q - p) * a + base_MSB - (p > q); + return value_type(p + (((t >> base_shift) + t) >> base_shift)); + } + + //-------------------------------------------------------------------- + self_type& clear() + { + r = g = b = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& opacity(double a_) + { + if (a_ < 0) a = 0; + else if (a_ > 1) a = 1; + else a = (value_type)uround(a_ * double(base_mask)); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return double(a) / double(base_mask); + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& premultiply() + { + if (a != base_mask) + { + if (a == 0) + { + r = g = b = 0; + } + else + { + r = multiply(r, a); + g = multiply(g, a); + b = multiply(b, a); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& premultiply(unsigned a_) + { + if (a != base_mask || a_ < base_mask) + { + if (a == 0 || a_ == 0) + { + r = g = b = a = 0; + } + else + { + calc_type r_ = (calc_type(r) * a_) / a; + calc_type g_ = (calc_type(g) * a_) / a; + calc_type b_ = (calc_type(b) * a_) / a; + r = value_type((r_ > a_) ? a_ : r_); + g = value_type((g_ > a_) ? a_ : g_); + b = value_type((b_ > a_) ? a_ : b_); + a = value_type(a_); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& demultiply() + { + if (a < base_mask) + { + if (a == 0) + { + r = g = b = 0; + } + else + { + calc_type r_ = (calc_type(r) * base_mask) / a; + calc_type g_ = (calc_type(g) * base_mask) / a; + calc_type b_ = (calc_type(b) * base_mask) / a; + r = value_type((r_ > calc_type(base_mask)) ? calc_type(base_mask) : r_); + g = value_type((g_ > calc_type(base_mask)) ? calc_type(base_mask) : g_); + b = value_type((b_ > calc_type(base_mask)) ? calc_type(base_mask) : b_); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type gradient(const self_type& c, double k) const + { + self_type ret; + calc_type ik = uround(k * base_mask); + ret.r = lerp(r, c.r, ik); + ret.g = lerp(g, c.g, ik); + ret.b = lerp(b, c.b, ik); + ret.a = lerp(a, c.a, ik); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + calc_type cr, cg, cb, ca; + if (cover == cover_mask) + { + if (c.a == base_mask) + { + *this = c; + return; + } + else + { + cr = r + c.r; + cg = g + c.g; + cb = b + c.b; + ca = a + c.a; + } + } + else + { + cr = r + mult_cover(c.r, cover); + cg = g + mult_cover(c.g, cover); + cb = b + mult_cover(c.b, cover); + ca = a + mult_cover(c.a, cover); + } + r = (value_type)((cr > calc_type(base_mask)) ? calc_type(base_mask) : cr); + g = (value_type)((cg > calc_type(base_mask)) ? calc_type(base_mask) : cg); + b = (value_type)((cb > calc_type(base_mask)) ? calc_type(base_mask) : cb); + a = (value_type)((ca > calc_type(base_mask)) ? calc_type(base_mask) : ca); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) + { + r = gamma.dir(r); + g = gamma.dir(g); + b = gamma.dir(b); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) + { + r = gamma.inv(r); + g = gamma.inv(g); + b = gamma.inv(b); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0,0,0); } + + //-------------------------------------------------------------------- + static self_type from_wavelength(double wl, double gamma = 1.0) + { + return self_type(rgba::from_wavelength(wl, gamma)); + } + }; + + typedef rgba8T rgba8; + typedef rgba8T srgba8; + + + //-------------------------------------------------------------rgb8_packed + inline rgba8 rgb8_packed(unsigned v) + { + return rgba8((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); + } + + //-------------------------------------------------------------bgr8_packed + inline rgba8 bgr8_packed(unsigned v) + { + return rgba8(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF); + } + + //------------------------------------------------------------argb8_packed + inline rgba8 argb8_packed(unsigned v) + { + return rgba8((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF, v >> 24); + } + + //---------------------------------------------------------rgba8_gamma_dir + template + rgba8 rgba8_gamma_dir(rgba8 c, const GammaLUT& gamma) + { + return rgba8(gamma.dir(c.r), gamma.dir(c.g), gamma.dir(c.b), c.a); + } + + //---------------------------------------------------------rgba8_gamma_inv + template + rgba8 rgba8_gamma_inv(rgba8 c, const GammaLUT& gamma) + { + return rgba8(gamma.inv(c.r), gamma.inv(c.g), gamma.inv(c.b), c.a); + } + + + + //==================================================================rgba16 + struct rgba16 + { + typedef int16u value_type; + typedef int32u calc_type; + typedef int64 long_type; + enum base_scale_e + { + base_shift = 16, + base_scale = 1 << base_shift, + base_mask = base_scale - 1, + base_MSB = 1 << (base_shift - 1) + }; + typedef rgba16 self_type; + + value_type r; + value_type g; + value_type b; + value_type a; + + //-------------------------------------------------------------------- + rgba16() {} + + //-------------------------------------------------------------------- + rgba16(unsigned r_, unsigned g_, unsigned b_, unsigned a_=base_mask) : + r(value_type(r_)), + g(value_type(g_)), + b(value_type(b_)), + a(value_type(a_)) {} + + //-------------------------------------------------------------------- + rgba16(const self_type& c, unsigned a_) : + r(c.r), g(c.g), b(c.b), a(value_type(a_)) {} + + //-------------------------------------------------------------------- + rgba16(const rgba& c) : + r((value_type)uround(c.r * double(base_mask))), + g((value_type)uround(c.g * double(base_mask))), + b((value_type)uround(c.b * double(base_mask))), + a((value_type)uround(c.a * double(base_mask))) {} + + //-------------------------------------------------------------------- + rgba16(const rgba8& c) : + r(value_type((value_type(c.r) << 8) | c.r)), + g(value_type((value_type(c.g) << 8) | c.g)), + b(value_type((value_type(c.b) << 8) | c.b)), + a(value_type((value_type(c.a) << 8) | c.a)) {} + + //-------------------------------------------------------------------- + rgba16(const srgba8& c) : + r(sRGB_conv::rgb_from_sRGB(c.r)), + g(sRGB_conv::rgb_from_sRGB(c.g)), + b(sRGB_conv::rgb_from_sRGB(c.b)), + a(sRGB_conv::alpha_from_sRGB(c.a)) {} + + //-------------------------------------------------------------------- + operator rgba() const + { + return rgba( + r / 65535.0, + g / 65535.0, + b / 65535.0, + a / 65535.0); + } + + //-------------------------------------------------------------------- + operator rgba8() const + { + return rgba8(r >> 8, g >> 8, b >> 8, a >> 8); + } + + //-------------------------------------------------------------------- + operator srgba8() const + { + // Return (non-premultiplied) sRGB values. + return srgba8( + sRGB_conv::rgb_to_sRGB(r), + sRGB_conv::rgb_to_sRGB(g), + sRGB_conv::rgb_to_sRGB(b), + sRGB_conv::alpha_to_sRGB(a)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE double to_double(value_type a) + { + return double(a) / base_mask; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type from_double(double a) + { + return value_type(uround(a * base_mask)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type empty_value() + { + return 0; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type full_value() + { + return base_mask; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_transparent() const + { + return a == 0; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_opaque() const + { + return a == base_mask; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type invert(value_type x) + { + return base_mask - x; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, exact over int16u. + static AGG_INLINE value_type multiply(value_type a, value_type b) + { + calc_type t = a * b + base_MSB; + return value_type(((t >> base_shift) + t) >> base_shift); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type demultiply(value_type a, value_type b) + { + if (a * b == 0) + { + return 0; + } + else if (a >= b) + { + return base_mask; + } + else return value_type((a * base_mask + (b >> 1)) / b); + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downscale(T a) + { + return a >> base_shift; + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downshift(T a, unsigned n) + { + return a >> n; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, almost exact over int16u. + // Specifically for multiplying a color component by a cover. + static AGG_INLINE value_type mult_cover(value_type a, cover_type b) + { + return multiply(a, (b << 8) | b); + } + + //-------------------------------------------------------------------- + static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) + { + return multiply((a << 8) | a, b) >> 8; + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a, assuming q is premultiplied by a. + static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) + { + return p + q - multiply(p, a); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a. + static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) + { + int t = (q - p) * a + base_MSB - (p > q); + return value_type(p + (((t >> base_shift) + t) >> base_shift)); + } + + //-------------------------------------------------------------------- + self_type& clear() + { + r = g = b = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& opacity(double a_) + { + if (a_ < 0) a = 0; + if (a_ > 1) a = 1; + a = value_type(uround(a_ * double(base_mask))); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return double(a) / double(base_mask); + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& premultiply() + { + if (a != base_mask) + { + if (a == 0) + { + r = g = b = 0; + } + else + { + r = multiply(r, a); + g = multiply(g, a); + b = multiply(b, a); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& premultiply(unsigned a_) + { + if (a < base_mask || a_ < base_mask) + { + if (a == 0 || a_ == 0) + { + r = g = b = a = 0; + } + else + { + calc_type r_ = (calc_type(r) * a_) / a; + calc_type g_ = (calc_type(g) * a_) / a; + calc_type b_ = (calc_type(b) * a_) / a; + r = value_type((r_ > a_) ? a_ : r_); + g = value_type((g_ > a_) ? a_ : g_); + b = value_type((b_ > a_) ? a_ : b_); + a = value_type(a_); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& demultiply() + { + if (a < base_mask) + { + if (a == 0) + { + r = g = b = 0; + } + else + { + calc_type r_ = (calc_type(r) * base_mask) / a; + calc_type g_ = (calc_type(g) * base_mask) / a; + calc_type b_ = (calc_type(b) * base_mask) / a; + r = value_type((r_ > calc_type(base_mask)) ? calc_type(base_mask) : r_); + g = value_type((g_ > calc_type(base_mask)) ? calc_type(base_mask) : g_); + b = value_type((b_ > calc_type(base_mask)) ? calc_type(base_mask) : b_); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type gradient(const self_type& c, double k) const + { + self_type ret; + calc_type ik = uround(k * base_mask); + ret.r = lerp(r, c.r, ik); + ret.g = lerp(g, c.g, ik); + ret.b = lerp(b, c.b, ik); + ret.a = lerp(a, c.a, ik); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + calc_type cr, cg, cb, ca; + if (cover == cover_mask) + { + if (c.a == base_mask) + { + *this = c; + return; + } + else + { + cr = r + c.r; + cg = g + c.g; + cb = b + c.b; + ca = a + c.a; + } + } + else + { + cr = r + mult_cover(c.r, cover); + cg = g + mult_cover(c.g, cover); + cb = b + mult_cover(c.b, cover); + ca = a + mult_cover(c.a, cover); + } + r = (value_type)((cr > calc_type(base_mask)) ? calc_type(base_mask) : cr); + g = (value_type)((cg > calc_type(base_mask)) ? calc_type(base_mask) : cg); + b = (value_type)((cb > calc_type(base_mask)) ? calc_type(base_mask) : cb); + a = (value_type)((ca > calc_type(base_mask)) ? calc_type(base_mask) : ca); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) + { + r = gamma.dir(r); + g = gamma.dir(g); + b = gamma.dir(b); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) + { + r = gamma.inv(r); + g = gamma.inv(g); + b = gamma.inv(b); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0,0,0); } + + //-------------------------------------------------------------------- + static self_type from_wavelength(double wl, double gamma = 1.0) + { + return self_type(rgba::from_wavelength(wl, gamma)); + } + }; + + + //------------------------------------------------------rgba16_gamma_dir + template + rgba16 rgba16_gamma_dir(rgba16 c, const GammaLUT& gamma) + { + return rgba16(gamma.dir(c.r), gamma.dir(c.g), gamma.dir(c.b), c.a); + } + + //------------------------------------------------------rgba16_gamma_inv + template + rgba16 rgba16_gamma_inv(rgba16 c, const GammaLUT& gamma) + { + return rgba16(gamma.inv(c.r), gamma.inv(c.g), gamma.inv(c.b), c.a); + } + + //====================================================================rgba32 + struct rgba32 + { + typedef float value_type; + typedef double calc_type; + typedef double long_type; + typedef rgba32 self_type; + + value_type r; + value_type g; + value_type b; + value_type a; + + //-------------------------------------------------------------------- + rgba32() {} + + //-------------------------------------------------------------------- + rgba32(value_type r_, value_type g_, value_type b_, value_type a_= 1) : + r(r_), g(g_), b(b_), a(a_) {} + + //-------------------------------------------------------------------- + rgba32(const self_type& c, float a_) : + r(c.r), g(c.g), b(c.b), a(a_) {} + + //-------------------------------------------------------------------- + rgba32(const rgba& c) : + r(value_type(c.r)), g(value_type(c.g)), b(value_type(c.b)), a(value_type(c.a)) {} + + //-------------------------------------------------------------------- + rgba32(const rgba8& c) : + r(value_type(c.r / 255.0)), + g(value_type(c.g / 255.0)), + b(value_type(c.b / 255.0)), + a(value_type(c.a / 255.0)) {} + + //-------------------------------------------------------------------- + rgba32(const srgba8& c) : + r(sRGB_conv::rgb_from_sRGB(c.r)), + g(sRGB_conv::rgb_from_sRGB(c.g)), + b(sRGB_conv::rgb_from_sRGB(c.b)), + a(sRGB_conv::alpha_from_sRGB(c.a)) {} + + //-------------------------------------------------------------------- + rgba32(const rgba16& c) : + r(value_type(c.r / 65535.0)), + g(value_type(c.g / 65535.0)), + b(value_type(c.b / 65535.0)), + a(value_type(c.a / 65535.0)) {} + + //-------------------------------------------------------------------- + operator rgba() const + { + return rgba(r, g, b, a); + } + + //-------------------------------------------------------------------- + operator rgba8() const + { + return rgba8( + uround(r * 255.0), + uround(g * 255.0), + uround(b * 255.0), + uround(a * 255.0)); + } + + //-------------------------------------------------------------------- + operator srgba8() const + { + return srgba8( + sRGB_conv::rgb_to_sRGB(r), + sRGB_conv::rgb_to_sRGB(g), + sRGB_conv::rgb_to_sRGB(b), + sRGB_conv::alpha_to_sRGB(a)); + } + + //-------------------------------------------------------------------- + operator rgba16() const + { + return rgba8( + uround(r * 65535.0), + uround(g * 65535.0), + uround(b * 65535.0), + uround(a * 65535.0)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE double to_double(value_type a) + { + return a; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type from_double(double a) + { + return value_type(a); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type empty_value() + { + return 0; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type full_value() + { + return 1; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_transparent() const + { + return a <= 0; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_opaque() const + { + return a >= 1; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type invert(value_type x) + { + return 1 - x; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type multiply(value_type a, value_type b) + { + return value_type(a * b); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type demultiply(value_type a, value_type b) + { + return (b == 0) ? 0 : value_type(a / b); + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downscale(T a) + { + return a; + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downshift(T a, unsigned n) + { + return n > 0 ? a / (1 << n) : a; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type mult_cover(value_type a, cover_type b) + { + return value_type(a * b / cover_mask); + } + + //-------------------------------------------------------------------- + static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) + { + return cover_type(uround(a * b)); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a, assuming q is premultiplied by a. + static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) + { + return (1 - a) * p + q; // more accurate than "p + q - p * a" + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a. + static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) + { + // The form "p + a * (q - p)" avoids a multiplication, but may produce an + // inaccurate result. For example, "p + (q - p)" may not be exactly equal + // to q. Therefore, stick to the basic expression, which at least produces + // the correct result at either extreme. + return (1 - a) * p + a * q; + } + + //-------------------------------------------------------------------- + self_type& clear() + { + r = g = b = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& opacity(double a_) + { + if (a_ < 0) a = 0; + else if (a_ > 1) a = 1; + else a = value_type(a_); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return a; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& premultiply() + { + if (a < 1) + { + if (a <= 0) + { + r = g = b = 0; + } + else + { + r *= a; + g *= a; + b *= a; + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& demultiply() + { + if (a < 1) + { + if (a <= 0) + { + r = g = b = 0; + } + else + { + r /= a; + g /= a; + b /= a; + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type gradient(const self_type& c, double k) const + { + self_type ret; + ret.r = value_type(r + (c.r - r) * k); + ret.g = value_type(g + (c.g - g) * k); + ret.b = value_type(b + (c.b - b) * k); + ret.a = value_type(a + (c.a - a) * k); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + if (cover == cover_mask) + { + if (c.is_opaque()) + { + *this = c; + return; + } + else + { + r += c.r; + g += c.g; + b += c.b; + a += c.a; + } + } + else + { + r += mult_cover(c.r, cover); + g += mult_cover(c.g, cover); + b += mult_cover(c.b, cover); + a += mult_cover(c.a, cover); + } + if (a > 1) a = 1; + if (r > a) r = a; + if (g > a) g = a; + if (b > a) b = a; + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) + { + r = gamma.dir(r); + g = gamma.dir(g); + b = gamma.dir(b); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) + { + r = gamma.inv(r); + g = gamma.inv(g); + b = gamma.inv(b); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0,0,0); } + + //-------------------------------------------------------------------- + static self_type from_wavelength(double wl, double gamma = 1) + { + return self_type(rgba::from_wavelength(wl, gamma)); + } + }; +} + + + +#endif diff --git a/src/agg/agg_config.h b/src/agg/agg_config.h new file mode 100644 index 0000000000..fa1dae2ba7 --- /dev/null +++ b/src/agg/agg_config.h @@ -0,0 +1,44 @@ +#ifndef AGG_CONFIG_INCLUDED +#define AGG_CONFIG_INCLUDED + +// This file can be used to redefine certain data types. + +//--------------------------------------- +// 1. Default basic types such as: +// +// AGG_INT8 +// AGG_INT8U +// AGG_INT16 +// AGG_INT16U +// AGG_INT32 +// AGG_INT32U +// AGG_INT64 +// AGG_INT64U +// +// Just replace this file with new defines if necessary. +// For example, if your compiler doesn't have a 64 bit integer type +// you can still use AGG if you define the follows: +// +// #define AGG_INT64 int +// #define AGG_INT64U unsigned +// +// It will result in overflow in 16 bit-per-component image/pattern resampling +// but it won't result any crash and the rest of the library will remain +// fully functional. + + +//--------------------------------------- +// 2. Default rendering_buffer type. Can be: +// +// Provides faster access for massive pixel operations, +// such as blur, image filtering: +// #define AGG_RENDERING_BUFFER row_ptr_cache +// +// Provides cheaper creation and destruction (no mem allocs): +// #define AGG_RENDERING_BUFFER row_accessor +// +// You can still use both of them simultaneously in your applications +// This #define is used only for default rendering_buffer type, +// in short hand typedefs like pixfmt_rgba32. + +#endif diff --git a/src/agg/agg_conv_transform.h b/src/agg/agg_conv_transform.h new file mode 100644 index 0000000000..0c88a245bd --- /dev/null +++ b/src/agg/agg_conv_transform.h @@ -0,0 +1,68 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// class conv_transform +// +//---------------------------------------------------------------------------- +#ifndef AGG_CONV_TRANSFORM_INCLUDED +#define AGG_CONV_TRANSFORM_INCLUDED + +#include "agg_basics.h" +#include "agg_trans_affine.h" + +namespace agg +{ + + //----------------------------------------------------------conv_transform + template class conv_transform + { + public: + conv_transform(VertexSource& source, Transformer& tr) : + m_source(&source), m_trans(&tr) {} + void attach(VertexSource& source) { m_source = &source; } + + void rewind(unsigned path_id) + { + m_source->rewind(path_id); + } + + unsigned vertex(double* x, double* y) + { + unsigned cmd = m_source->vertex(x, y); + if(is_vertex(cmd)) + { + m_trans->transform(x, y); + } + return cmd; + } + + void transformer(Transformer& tr) + { + m_trans = &tr; + } + + private: + conv_transform(const conv_transform&); + const conv_transform& + operator = (const conv_transform&); + + VertexSource* m_source; + Transformer* m_trans; + }; + + +} + +#endif diff --git a/src/agg/agg_gamma_functions.h b/src/agg/agg_gamma_functions.h new file mode 100644 index 0000000000..5d720daa9a --- /dev/null +++ b/src/agg/agg_gamma_functions.h @@ -0,0 +1,132 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_GAMMA_FUNCTIONS_INCLUDED +#define AGG_GAMMA_FUNCTIONS_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + //===============================================================gamma_none + struct gamma_none + { + double operator()(double x) const { return x; } + }; + + + //==============================================================gamma_power + class gamma_power + { + public: + gamma_power() : m_gamma(1.0) {} + gamma_power(double g) : m_gamma(g) {} + + void gamma(double g) { m_gamma = g; } + double gamma() const { return m_gamma; } + + double operator() (double x) const + { + return pow(x, m_gamma); + } + + private: + double m_gamma; + }; + + + //==========================================================gamma_threshold + class gamma_threshold + { + public: + gamma_threshold() : m_threshold(0.5) {} + gamma_threshold(double t) : m_threshold(t) {} + + void threshold(double t) { m_threshold = t; } + double threshold() const { return m_threshold; } + + double operator() (double x) const + { + return (x < m_threshold) ? 0.0 : 1.0; + } + + private: + double m_threshold; + }; + + + //============================================================gamma_linear + class gamma_linear + { + public: + gamma_linear() : m_start(0.0), m_end(1.0) {} + gamma_linear(double s, double e) : m_start(s), m_end(e) {} + + void set(double s, double e) { m_start = s; m_end = e; } + void start(double s) { m_start = s; } + void end(double e) { m_end = e; } + double start() const { return m_start; } + double end() const { return m_end; } + + double operator() (double x) const + { + if(x < m_start) return 0.0; + if(x > m_end) return 1.0; + return (x - m_start) / (m_end - m_start); + } + + private: + double m_start; + double m_end; + }; + + + //==========================================================gamma_multiply + class gamma_multiply + { + public: + gamma_multiply() : m_mul(1.0) {} + gamma_multiply(double v) : m_mul(v) {} + + void value(double v) { m_mul = v; } + double value() const { return m_mul; } + + double operator() (double x) const + { + double y = x * m_mul; + if(y > 1.0) y = 1.0; + return y; + } + + private: + double m_mul; + }; + + inline double sRGB_to_linear(double x) + { + return (x <= 0.04045) ? (x / 12.92) : pow((x + 0.055) / (1.055), 2.4); + } + + inline double linear_to_sRGB(double x) + { + return (x <= 0.0031308) ? (x * 12.92) : (1.055 * pow(x, 1 / 2.4) - 0.055); + } +} + +#endif + + + diff --git a/src/agg/agg_gamma_lut.h b/src/agg/agg_gamma_lut.h new file mode 100644 index 0000000000..e30873632a --- /dev/null +++ b/src/agg/agg_gamma_lut.h @@ -0,0 +1,300 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_GAMMA_LUT_INCLUDED +#define AGG_GAMMA_LUT_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_gamma_functions.h" + +namespace agg +{ + template class gamma_lut + { + public: + typedef gamma_lut self_type; + + enum gamma_scale_e + { + gamma_shift = GammaShift, + gamma_size = 1 << gamma_shift, + gamma_mask = gamma_size - 1 + }; + + enum hi_res_scale_e + { + hi_res_shift = HiResShift, + hi_res_size = 1 << hi_res_shift, + hi_res_mask = hi_res_size - 1 + }; + + ~gamma_lut() + { + pod_allocator::deallocate(m_inv_gamma, hi_res_size); + pod_allocator::deallocate(m_dir_gamma, gamma_size); + } + + gamma_lut() : + m_gamma(1.0), + m_dir_gamma(pod_allocator::allocate(gamma_size)), + m_inv_gamma(pod_allocator::allocate(hi_res_size)) + { + unsigned i; + for(i = 0; i < gamma_size; i++) + { + m_dir_gamma[i] = HiResT(i << (hi_res_shift - gamma_shift)); + } + + for(i = 0; i < hi_res_size; i++) + { + m_inv_gamma[i] = LoResT(i >> (hi_res_shift - gamma_shift)); + } + } + + gamma_lut(double g) : + m_gamma(1.0), + m_dir_gamma(pod_allocator::allocate(gamma_size)), + m_inv_gamma(pod_allocator::allocate(hi_res_size)) + { + gamma(g); + } + + void gamma(double g) + { + m_gamma = g; + + unsigned i; + for(i = 0; i < gamma_size; i++) + { + m_dir_gamma[i] = (HiResT) + uround(pow(i / double(gamma_mask), m_gamma) * double(hi_res_mask)); + } + + double inv_g = 1.0 / g; + for(i = 0; i < hi_res_size; i++) + { + m_inv_gamma[i] = (LoResT) + uround(pow(i / double(hi_res_mask), inv_g) * double(gamma_mask)); + } + } + + double gamma() const + { + return m_gamma; + } + + HiResT dir(LoResT v) const + { + return m_dir_gamma[unsigned(v)]; + } + + LoResT inv(HiResT v) const + { + return m_inv_gamma[unsigned(v)]; + } + + private: + gamma_lut(const self_type&); + const self_type& operator = (const self_type&); + + double m_gamma; + HiResT* m_dir_gamma; + LoResT* m_inv_gamma; + }; + + // + // sRGB support classes + // + + // sRGB_lut - implements sRGB conversion for the various types. + // Base template is undefined, specializations are provided below. + template + class sRGB_lut; + + template<> + class sRGB_lut + { + public: + sRGB_lut() + { + // Generate lookup tables. + for (int i = 0; i <= 255; ++i) + { + m_dir_table[i] = float(sRGB_to_linear(i / 255.0)); + } + for (int i = 0; i <= 65535; ++i) + { + m_inv_table[i] = uround(255.0 * linear_to_sRGB(i / 65535.0)); + } + } + + float dir(int8u v) const + { + return m_dir_table[v]; + } + + int8u inv(float v) const + { + return m_inv_table[int16u(0.5 + v * 65535)]; + } + + private: + float m_dir_table[256]; + int8u m_inv_table[65536]; + }; + + template<> + class sRGB_lut + { + public: + sRGB_lut() + { + // Generate lookup tables. + for (int i = 0; i <= 255; ++i) + { + m_dir_table[i] = uround(65535.0 * sRGB_to_linear(i / 255.0)); + } + for (int i = 0; i <= 65535; ++i) + { + m_inv_table[i] = uround(255.0 * linear_to_sRGB(i / 65535.0)); + } + } + + int16u dir(int8u v) const + { + return m_dir_table[v]; + } + + int8u inv(int16u v) const + { + return m_inv_table[v]; + } + + private: + int16u m_dir_table[256]; + int8u m_inv_table[65536]; + }; + + template<> + class sRGB_lut + { + public: + sRGB_lut() + { + // Generate lookup tables. + for (int i = 0; i <= 255; ++i) + { + m_dir_table[i] = uround(255.0 * sRGB_to_linear(i / 255.0)); + m_inv_table[i] = uround(255.0 * linear_to_sRGB(i / 255.0)); + } + } + + int8u dir(int8u v) const + { + return m_dir_table[v]; + } + + int8u inv(int8u v) const + { + return m_inv_table[v]; + } + + private: + int8u m_dir_table[256]; + int8u m_inv_table[256]; + }; + + // Common base class for sRGB_conv objects. Defines an internal + // sRGB_lut object so that users don't have to. + template + class sRGB_conv_base + { + public: + static T rgb_from_sRGB(int8u x) + { + return lut.dir(x); + } + + static int8u rgb_to_sRGB(T x) + { + return lut.inv(x); + } + + private: + static sRGB_lut lut; + }; + + // Definition of sRGB_conv_base::lut. Due to the fact that this a template, + // we don't need to place the definition in a cpp file. Hurrah. + template + sRGB_lut sRGB_conv_base::lut; + + // Wrapper for sRGB-linear conversion. + // Base template is undefined, specializations are provided below. + template + class sRGB_conv; + + template<> + class sRGB_conv : public sRGB_conv_base + { + public: + static float alpha_from_sRGB(int8u x) + { + static const double y = 1 / 255.0; + return float(x * y); + } + + static int8u alpha_to_sRGB(float x) + { + return int8u(0.5 + x * 255); + } + }; + + template<> + class sRGB_conv : public sRGB_conv_base + { + public: + static int16u alpha_from_sRGB(int8u x) + { + return (x << 8) | x; + } + + static int8u alpha_to_sRGB(int16u x) + { + return x >> 8; + } + }; + + template<> + class sRGB_conv : public sRGB_conv_base + { + public: + static int8u alpha_from_sRGB(int8u x) + { + return x; + } + + static int8u alpha_to_sRGB(int8u x) + { + return x; + } + }; +} + +#endif diff --git a/src/agg/agg_math.h b/src/agg/agg_math.h new file mode 100644 index 0000000000..2ec49cf3ff --- /dev/null +++ b/src/agg/agg_math.h @@ -0,0 +1,437 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// Bessel function (besj) was adapted for use in AGG library by Andy Wilk +// Contact: castor.vulgaris@gmail.com +//---------------------------------------------------------------------------- + +#ifndef AGG_MATH_INCLUDED +#define AGG_MATH_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + + //------------------------------------------------------vertex_dist_epsilon + // Coinciding points maximal distance (Epsilon) + const double vertex_dist_epsilon = 1e-14; + + //-----------------------------------------------------intersection_epsilon + // See calc_intersection + const double intersection_epsilon = 1.0e-30; + + //------------------------------------------------------------cross_product + AGG_INLINE double cross_product(double x1, double y1, + double x2, double y2, + double x, double y) + { + return (x - x2) * (y2 - y1) - (y - y2) * (x2 - x1); + } + + //--------------------------------------------------------point_in_triangle + AGG_INLINE bool point_in_triangle(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x, double y) + { + bool cp1 = cross_product(x1, y1, x2, y2, x, y) < 0.0; + bool cp2 = cross_product(x2, y2, x3, y3, x, y) < 0.0; + bool cp3 = cross_product(x3, y3, x1, y1, x, y) < 0.0; + return cp1 == cp2 && cp2 == cp3 && cp3 == cp1; + } + + //-----------------------------------------------------------calc_distance + AGG_INLINE double calc_distance(double x1, double y1, double x2, double y2) + { + double dx = x2-x1; + double dy = y2-y1; + return sqrt(dx * dx + dy * dy); + } + + //--------------------------------------------------------calc_sq_distance + AGG_INLINE double calc_sq_distance(double x1, double y1, double x2, double y2) + { + double dx = x2-x1; + double dy = y2-y1; + return dx * dx + dy * dy; + } + + //------------------------------------------------calc_line_point_distance + AGG_INLINE double calc_line_point_distance(double x1, double y1, + double x2, double y2, + double x, double y) + { + double dx = x2-x1; + double dy = y2-y1; + double d = sqrt(dx * dx + dy * dy); + if(d < vertex_dist_epsilon) + { + return calc_distance(x1, y1, x, y); + } + return ((x - x2) * dy - (y - y2) * dx) / d; + } + + //-------------------------------------------------------calc_line_point_u + AGG_INLINE double calc_segment_point_u(double x1, double y1, + double x2, double y2, + double x, double y) + { + double dx = x2 - x1; + double dy = y2 - y1; + + if(dx == 0 && dy == 0) + { + return 0; + } + + double pdx = x - x1; + double pdy = y - y1; + + return (pdx * dx + pdy * dy) / (dx * dx + dy * dy); + } + + //---------------------------------------------calc_line_point_sq_distance + AGG_INLINE double calc_segment_point_sq_distance(double x1, double y1, + double x2, double y2, + double x, double y, + double u) + { + if(u <= 0) + { + return calc_sq_distance(x, y, x1, y1); + } + else + if(u >= 1) + { + return calc_sq_distance(x, y, x2, y2); + } + return calc_sq_distance(x, y, x1 + u * (x2 - x1), y1 + u * (y2 - y1)); + } + + //---------------------------------------------calc_line_point_sq_distance + AGG_INLINE double calc_segment_point_sq_distance(double x1, double y1, + double x2, double y2, + double x, double y) + { + return + calc_segment_point_sq_distance( + x1, y1, x2, y2, x, y, + calc_segment_point_u(x1, y1, x2, y2, x, y)); + } + + //-------------------------------------------------------calc_intersection + AGG_INLINE bool calc_intersection(double ax, double ay, double bx, double by, + double cx, double cy, double dx, double dy, + double* x, double* y) + { + double num = (ay-cy) * (dx-cx) - (ax-cx) * (dy-cy); + double den = (bx-ax) * (dy-cy) - (by-ay) * (dx-cx); + if(fabs(den) < intersection_epsilon) return false; + double r = num / den; + *x = ax + r * (bx-ax); + *y = ay + r * (by-ay); + return true; + } + + //-----------------------------------------------------intersection_exists + AGG_INLINE bool intersection_exists(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4) + { + // It's less expensive but you can't control the + // boundary conditions: Less or LessEqual + double dx1 = x2 - x1; + double dy1 = y2 - y1; + double dx2 = x4 - x3; + double dy2 = y4 - y3; + return ((x3 - x2) * dy1 - (y3 - y2) * dx1 < 0.0) != + ((x4 - x2) * dy1 - (y4 - y2) * dx1 < 0.0) && + ((x1 - x4) * dy2 - (y1 - y4) * dx2 < 0.0) != + ((x2 - x4) * dy2 - (y2 - y4) * dx2 < 0.0); + + // It's is more expensive but more flexible + // in terms of boundary conditions. + //-------------------- + //double den = (x2-x1) * (y4-y3) - (y2-y1) * (x4-x3); + //if(fabs(den) < intersection_epsilon) return false; + //double nom1 = (x4-x3) * (y1-y3) - (y4-y3) * (x1-x3); + //double nom2 = (x2-x1) * (y1-y3) - (y2-y1) * (x1-x3); + //double ua = nom1 / den; + //double ub = nom2 / den; + //return ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0; + } + + //--------------------------------------------------------calc_orthogonal + AGG_INLINE void calc_orthogonal(double thickness, + double x1, double y1, + double x2, double y2, + double* x, double* y) + { + double dx = x2 - x1; + double dy = y2 - y1; + double d = sqrt(dx*dx + dy*dy); + *x = thickness * dy / d; + *y = -thickness * dx / d; + } + + //--------------------------------------------------------dilate_triangle + AGG_INLINE void dilate_triangle(double x1, double y1, + double x2, double y2, + double x3, double y3, + double *x, double* y, + double d) + { + double dx1=0.0; + double dy1=0.0; + double dx2=0.0; + double dy2=0.0; + double dx3=0.0; + double dy3=0.0; + double loc = cross_product(x1, y1, x2, y2, x3, y3); + if(fabs(loc) > intersection_epsilon) + { + if(cross_product(x1, y1, x2, y2, x3, y3) > 0.0) + { + d = -d; + } + calc_orthogonal(d, x1, y1, x2, y2, &dx1, &dy1); + calc_orthogonal(d, x2, y2, x3, y3, &dx2, &dy2); + calc_orthogonal(d, x3, y3, x1, y1, &dx3, &dy3); + } + *x++ = x1 + dx1; *y++ = y1 + dy1; + *x++ = x2 + dx1; *y++ = y2 + dy1; + *x++ = x2 + dx2; *y++ = y2 + dy2; + *x++ = x3 + dx2; *y++ = y3 + dy2; + *x++ = x3 + dx3; *y++ = y3 + dy3; + *x++ = x1 + dx3; *y++ = y1 + dy3; + } + + //------------------------------------------------------calc_triangle_area + AGG_INLINE double calc_triangle_area(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + return (x1*y2 - x2*y1 + x2*y3 - x3*y2 + x3*y1 - x1*y3) * 0.5; + } + + //-------------------------------------------------------calc_polygon_area + template double calc_polygon_area(const Storage& st) + { + unsigned i; + double sum = 0.0; + double x = st[0].x; + double y = st[0].y; + double xs = x; + double ys = y; + + for(i = 1; i < st.size(); i++) + { + const typename Storage::value_type& v = st[i]; + sum += x * v.y - y * v.x; + x = v.x; + y = v.y; + } + return (sum + x * ys - y * xs) * 0.5; + } + + //------------------------------------------------------------------------ + // Tables for fast sqrt + extern int16u g_sqrt_table[1024]; + extern int8 g_elder_bit_table[256]; + + + //---------------------------------------------------------------fast_sqrt + //Fast integer Sqrt - really fast: no cycles, divisions or multiplications + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 4035) //Disable warning "no return value" + #endif + AGG_INLINE unsigned fast_sqrt(unsigned val) + { + #if defined(_M_IX86) && defined(_MSC_VER) && !defined(AGG_NO_ASM) + //For Ix86 family processors this assembler code is used. + //The key command here is bsr - determination the number of the most + //significant bit of the value. For other processors + //(and maybe compilers) the pure C "#else" section is used. + __asm + { + mov ebx, val + mov edx, 11 + bsr ecx, ebx + sub ecx, 9 + jle less_than_9_bits + shr ecx, 1 + adc ecx, 0 + sub edx, ecx + shl ecx, 1 + shr ebx, cl + less_than_9_bits: + xor eax, eax + mov ax, g_sqrt_table[ebx*2] + mov ecx, edx + shr eax, cl + } + #else + + //This code is actually pure C and portable to most + //arcitectures including 64bit ones. + unsigned t = val; + int bit=0; + unsigned shift = 11; + + //The following piece of code is just an emulation of the + //Ix86 assembler command "bsr" (see above). However on old + //Intels (like Intel MMX 233MHz) this code is about twice + //faster (sic!) then just one "bsr". On PIII and PIV the + //bsr is optimized quite well. + bit = t >> 24; + if(bit) + { + bit = g_elder_bit_table[bit] + 24; + } + else + { + bit = (t >> 16) & 0xFF; + if(bit) + { + bit = g_elder_bit_table[bit] + 16; + } + else + { + bit = (t >> 8) & 0xFF; + if(bit) + { + bit = g_elder_bit_table[bit] + 8; + } + else + { + bit = g_elder_bit_table[t]; + } + } + } + + //This code calculates the sqrt. + bit -= 9; + if(bit > 0) + { + bit = (bit >> 1) + (bit & 1); + shift -= bit; + val >>= (bit << 1); + } + return g_sqrt_table[val] >> shift; + #endif + } + #if defined(_MSC_VER) + #pragma warning(pop) + #endif + + + + + //--------------------------------------------------------------------besj + // Function BESJ calculates Bessel function of first kind of order n + // Arguments: + // n - an integer (>=0), the order + // x - value at which the Bessel function is required + //-------------------- + // C++ Mathematical Library + // Convereted from equivalent FORTRAN library + // Converetd by Gareth Walker for use by course 392 computational project + // All functions tested and yield the same results as the corresponding + // FORTRAN versions. + // + // If you have any problems using these functions please report them to + // M.Muldoon@UMIST.ac.uk + // + // Documentation available on the web + // http://www.ma.umist.ac.uk/mrm/Teaching/392/libs/392.html + // Version 1.0 8/98 + // 29 October, 1999 + //-------------------- + // Adapted for use in AGG library by Andy Wilk (castor.vulgaris@gmail.com) + //------------------------------------------------------------------------ + inline double besj(double x, int n) + { + if(n < 0) + { + return 0; + } + double d = 1E-6; + double b = 0; + if(fabs(x) <= d) + { + if(n != 0) return 0; + return 1; + } + double b1 = 0; // b1 is the value from the previous iteration + // Set up a starting order for recurrence + int m1 = (int)fabs(x) + 6; + if(fabs(x) > 5) + { + m1 = (int)(fabs(1.4 * x + 60 / x)); + } + int m2 = (int)(n + 2 + fabs(x) / 4); + if (m1 > m2) + { + m2 = m1; + } + + // Apply recurrence down from curent max order + for(;;) + { + double c3 = 0; + double c2 = 1E-30; + double c4 = 0; + int m8 = 1; + if (m2 / 2 * 2 == m2) + { + m8 = -1; + } + int imax = m2 - 2; + for (int i = 1; i <= imax; i++) + { + double c6 = 2 * (m2 - i) * c2 / x - c3; + c3 = c2; + c2 = c6; + if(m2 - i - 1 == n) + { + b = c6; + } + m8 = -1 * m8; + if (m8 > 0) + { + c4 = c4 + 2 * c6; + } + } + double c6 = 2 * c2 / x - c3; + if(n == 0) + { + b = c6; + } + c4 += c6; + b /= c4; + if(fabs(b - b1) < d) + { + return b; + } + b1 = b; + m2 += 3; + } + } + +} + + +#endif diff --git a/src/agg/agg_path_storage.h b/src/agg/agg_path_storage.h new file mode 100644 index 0000000000..f55c89957b --- /dev/null +++ b/src/agg/agg_path_storage.h @@ -0,0 +1,1582 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_PATH_STORAGE_INCLUDED +#define AGG_PATH_STORAGE_INCLUDED + +#include +#include +#include "agg_math.h" +#include "agg_array.h" +#include "agg_bezier_arc.h" + +namespace agg +{ + + + //----------------------------------------------------vertex_block_storage + template + class vertex_block_storage + { + public: + // Allocation parameters + enum block_scale_e + { + block_shift = BlockShift, + block_size = 1 << block_shift, + block_mask = block_size - 1, + block_pool = BlockPool + }; + + typedef T value_type; + typedef vertex_block_storage self_type; + + ~vertex_block_storage(); + vertex_block_storage(); + vertex_block_storage(const self_type& v); + const self_type& operator = (const self_type& ps); + + void remove_all(); + void free_all(); + + void add_vertex(double x, double y, unsigned cmd); + void modify_vertex(unsigned idx, double x, double y); + void modify_vertex(unsigned idx, double x, double y, unsigned cmd); + void modify_command(unsigned idx, unsigned cmd); + void swap_vertices(unsigned v1, unsigned v2); + + unsigned last_command() const; + unsigned last_vertex(double* x, double* y) const; + unsigned prev_vertex(double* x, double* y) const; + + double last_x() const; + double last_y() const; + + unsigned total_vertices() const; + unsigned vertex(unsigned idx, double* x, double* y) const; + unsigned command(unsigned idx) const; + + private: + void allocate_block(unsigned nb); + int8u* storage_ptrs(T** xy_ptr); + + private: + unsigned m_total_vertices; + unsigned m_total_blocks; + unsigned m_max_blocks; + T** m_coord_blocks; + int8u** m_cmd_blocks; + }; + + + //------------------------------------------------------------------------ + template + void vertex_block_storage::free_all() + { + if(m_total_blocks) + { + T** coord_blk = m_coord_blocks + m_total_blocks - 1; + while(m_total_blocks--) + { + pod_allocator::deallocate( + *coord_blk, + block_size * 2 + + block_size / (sizeof(T) / sizeof(unsigned char))); + --coord_blk; + } + pod_allocator::deallocate(m_coord_blocks, m_max_blocks * 2); + m_total_blocks = 0; + m_max_blocks = 0; + m_coord_blocks = 0; + m_cmd_blocks = 0; + m_total_vertices = 0; + } + } + + //------------------------------------------------------------------------ + template + vertex_block_storage::~vertex_block_storage() + { + free_all(); + } + + //------------------------------------------------------------------------ + template + vertex_block_storage::vertex_block_storage() : + m_total_vertices(0), + m_total_blocks(0), + m_max_blocks(0), + m_coord_blocks(0), + m_cmd_blocks(0) + { + } + + //------------------------------------------------------------------------ + template + vertex_block_storage::vertex_block_storage(const vertex_block_storage& v) : + m_total_vertices(0), + m_total_blocks(0), + m_max_blocks(0), + m_coord_blocks(0), + m_cmd_blocks(0) + { + *this = v; + } + + //------------------------------------------------------------------------ + template + const vertex_block_storage& + vertex_block_storage::operator = (const vertex_block_storage& v) + { + remove_all(); + unsigned i; + for(i = 0; i < v.total_vertices(); i++) + { + double x, y; + unsigned cmd = v.vertex(i, &x, &y); + add_vertex(x, y, cmd); + } + return *this; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::remove_all() + { + m_total_vertices = 0; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::add_vertex(double x, double y, + unsigned cmd) + { + T* coord_ptr = 0; + *storage_ptrs(&coord_ptr) = (int8u)cmd; + coord_ptr[0] = T(x); + coord_ptr[1] = T(y); + m_total_vertices++; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::modify_vertex(unsigned idx, + double x, double y) + { + T* pv = m_coord_blocks[idx >> block_shift] + ((idx & block_mask) << 1); + pv[0] = T(x); + pv[1] = T(y); + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::modify_vertex(unsigned idx, + double x, double y, + unsigned cmd) + { + unsigned block = idx >> block_shift; + unsigned offset = idx & block_mask; + T* pv = m_coord_blocks[block] + (offset << 1); + pv[0] = T(x); + pv[1] = T(y); + m_cmd_blocks[block][offset] = (int8u)cmd; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::modify_command(unsigned idx, + unsigned cmd) + { + m_cmd_blocks[idx >> block_shift][idx & block_mask] = (int8u)cmd; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::swap_vertices(unsigned v1, unsigned v2) + { + unsigned b1 = v1 >> block_shift; + unsigned b2 = v2 >> block_shift; + unsigned o1 = v1 & block_mask; + unsigned o2 = v2 & block_mask; + T* pv1 = m_coord_blocks[b1] + (o1 << 1); + T* pv2 = m_coord_blocks[b2] + (o2 << 1); + T val; + val = pv1[0]; pv1[0] = pv2[0]; pv2[0] = val; + val = pv1[1]; pv1[1] = pv2[1]; pv2[1] = val; + int8u cmd = m_cmd_blocks[b1][o1]; + m_cmd_blocks[b1][o1] = m_cmd_blocks[b2][o2]; + m_cmd_blocks[b2][o2] = cmd; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::last_command() const + { + if(m_total_vertices) return command(m_total_vertices - 1); + return path_cmd_stop; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::last_vertex(double* x, double* y) const + { + if(m_total_vertices) return vertex(m_total_vertices - 1, x, y); + return path_cmd_stop; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::prev_vertex(double* x, double* y) const + { + if(m_total_vertices > 1) return vertex(m_total_vertices - 2, x, y); + return path_cmd_stop; + } + + //------------------------------------------------------------------------ + template + inline double vertex_block_storage::last_x() const + { + if(m_total_vertices) + { + unsigned idx = m_total_vertices - 1; + return m_coord_blocks[idx >> block_shift][(idx & block_mask) << 1]; + } + return 0.0; + } + + //------------------------------------------------------------------------ + template + inline double vertex_block_storage::last_y() const + { + if(m_total_vertices) + { + unsigned idx = m_total_vertices - 1; + return m_coord_blocks[idx >> block_shift][((idx & block_mask) << 1) + 1]; + } + return 0.0; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::total_vertices() const + { + return m_total_vertices; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::vertex(unsigned idx, + double* x, double* y) const + { + unsigned nb = idx >> block_shift; + const T* pv = m_coord_blocks[nb] + ((idx & block_mask) << 1); + *x = pv[0]; + *y = pv[1]; + return m_cmd_blocks[nb][idx & block_mask]; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::command(unsigned idx) const + { + return m_cmd_blocks[idx >> block_shift][idx & block_mask]; + } + + //------------------------------------------------------------------------ + template + void vertex_block_storage::allocate_block(unsigned nb) + { + if(nb >= m_max_blocks) + { + T** new_coords = + pod_allocator::allocate((m_max_blocks + block_pool) * 2); + + unsigned char** new_cmds = + (unsigned char**)(new_coords + m_max_blocks + block_pool); + + if(m_coord_blocks) + { + memcpy(new_coords, + m_coord_blocks, + m_max_blocks * sizeof(T*)); + + memcpy(new_cmds, + m_cmd_blocks, + m_max_blocks * sizeof(unsigned char*)); + + pod_allocator::deallocate(m_coord_blocks, m_max_blocks * 2); + } + m_coord_blocks = new_coords; + m_cmd_blocks = new_cmds; + m_max_blocks += block_pool; + } + m_coord_blocks[nb] = + pod_allocator::allocate(block_size * 2 + + block_size / (sizeof(T) / sizeof(unsigned char))); + + m_cmd_blocks[nb] = + (unsigned char*)(m_coord_blocks[nb] + block_size * 2); + + m_total_blocks++; + } + + //------------------------------------------------------------------------ + template + int8u* vertex_block_storage::storage_ptrs(T** xy_ptr) + { + unsigned nb = m_total_vertices >> block_shift; + if(nb >= m_total_blocks) + { + allocate_block(nb); + } + *xy_ptr = m_coord_blocks[nb] + ((m_total_vertices & block_mask) << 1); + return m_cmd_blocks[nb] + (m_total_vertices & block_mask); + } + + + + + //-----------------------------------------------------poly_plain_adaptor + template class poly_plain_adaptor + { + public: + typedef T value_type; + + poly_plain_adaptor() : + m_data(0), + m_ptr(0), + m_end(0), + m_closed(false), + m_stop(false) + {} + + poly_plain_adaptor(const T* data, unsigned num_points, bool closed) : + m_data(data), + m_ptr(data), + m_end(data + num_points * 2), + m_closed(closed), + m_stop(false) + {} + + void init(const T* data, unsigned num_points, bool closed) + { + m_data = data; + m_ptr = data; + m_end = data + num_points * 2; + m_closed = closed; + m_stop = false; + } + + void rewind(unsigned) + { + m_ptr = m_data; + m_stop = false; + } + + unsigned vertex(double* x, double* y) + { + if(m_ptr < m_end) + { + bool first = m_ptr == m_data; + *x = *m_ptr++; + *y = *m_ptr++; + return first ? path_cmd_move_to : path_cmd_line_to; + } + *x = *y = 0.0; + if(m_closed && !m_stop) + { + m_stop = true; + return path_cmd_end_poly | path_flags_close; + } + return path_cmd_stop; + } + + private: + const T* m_data; + const T* m_ptr; + const T* m_end; + bool m_closed; + bool m_stop; + }; + + + + + + //-------------------------------------------------poly_container_adaptor + template class poly_container_adaptor + { + public: + typedef typename Container::value_type vertex_type; + + poly_container_adaptor() : + m_container(0), + m_index(0), + m_closed(false), + m_stop(false) + {} + + poly_container_adaptor(const Container& data, bool closed) : + m_container(&data), + m_index(0), + m_closed(closed), + m_stop(false) + {} + + void init(const Container& data, bool closed) + { + m_container = &data; + m_index = 0; + m_closed = closed; + m_stop = false; + } + + void rewind(unsigned) + { + m_index = 0; + m_stop = false; + } + + unsigned vertex(double* x, double* y) + { + if(m_index < m_container->size()) + { + bool first = m_index == 0; + const vertex_type& v = (*m_container)[m_index++]; + *x = v.x; + *y = v.y; + return first ? path_cmd_move_to : path_cmd_line_to; + } + *x = *y = 0.0; + if(m_closed && !m_stop) + { + m_stop = true; + return path_cmd_end_poly | path_flags_close; + } + return path_cmd_stop; + } + + private: + const Container* m_container; + unsigned m_index; + bool m_closed; + bool m_stop; + }; + + + + //-----------------------------------------poly_container_reverse_adaptor + template class poly_container_reverse_adaptor + { + public: + typedef typename Container::value_type vertex_type; + + poly_container_reverse_adaptor() : + m_container(0), + m_index(-1), + m_closed(false), + m_stop(false) + {} + + poly_container_reverse_adaptor(Container& data, bool closed) : + m_container(&data), + m_index(-1), + m_closed(closed), + m_stop(false) + {} + + void init(Container& data, bool closed) + { + m_container = &data; + m_index = m_container->size() - 1; + m_closed = closed; + m_stop = false; + } + + void rewind(unsigned) + { + m_index = m_container->size() - 1; + m_stop = false; + } + + unsigned vertex(double* x, double* y) + { + if(m_index >= 0) + { + bool first = m_index == int(m_container->size() - 1); + const vertex_type& v = (*m_container)[m_index--]; + *x = v.x; + *y = v.y; + return first ? path_cmd_move_to : path_cmd_line_to; + } + *x = *y = 0.0; + if(m_closed && !m_stop) + { + m_stop = true; + return path_cmd_end_poly | path_flags_close; + } + return path_cmd_stop; + } + + private: + Container* m_container; + int m_index; + bool m_closed; + bool m_stop; + }; + + + + + + //--------------------------------------------------------line_adaptor + class line_adaptor + { + public: + typedef double value_type; + + line_adaptor() : m_line(m_coord, 2, false) {} + line_adaptor(double x1, double y1, double x2, double y2) : + m_line(m_coord, 2, false) + { + m_coord[0] = x1; + m_coord[1] = y1; + m_coord[2] = x2; + m_coord[3] = y2; + } + + void init(double x1, double y1, double x2, double y2) + { + m_coord[0] = x1; + m_coord[1] = y1; + m_coord[2] = x2; + m_coord[3] = y2; + m_line.rewind(0); + } + + void rewind(unsigned) + { + m_line.rewind(0); + } + + unsigned vertex(double* x, double* y) + { + return m_line.vertex(x, y); + } + + private: + double m_coord[4]; + poly_plain_adaptor m_line; + }; + + + + + + + + + + + + + + //---------------------------------------------------------------path_base + // A container to store vertices with their flags. + // A path consists of a number of contours separated with "move_to" + // commands. The path storage can keep and maintain more than one + // path. + // To navigate to the beginning of a particular path, use rewind(path_id); + // Where path_id is what start_new_path() returns. So, when you call + // start_new_path() you need to store its return value somewhere else + // to navigate to the path afterwards. + // + // See also: vertex_source concept + //------------------------------------------------------------------------ + template class path_base + { + public: + typedef VertexContainer container_type; + typedef path_base self_type; + + //-------------------------------------------------------------------- + path_base() : m_vertices(), m_iterator(0) {} + void remove_all() { m_vertices.remove_all(); m_iterator = 0; } + void free_all() { m_vertices.free_all(); m_iterator = 0; } + + // Make path functions + //-------------------------------------------------------------------- + unsigned start_new_path(); + + void move_to(double x, double y); + void move_rel(double dx, double dy); + + void line_to(double x, double y); + void line_rel(double dx, double dy); + + void hline_to(double x); + void hline_rel(double dx); + + void vline_to(double y); + void vline_rel(double dy); + + void arc_to(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x, double y); + + void arc_rel(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double dx, double dy); + + void curve3(double x_ctrl, double y_ctrl, + double x_to, double y_to); + + void curve3_rel(double dx_ctrl, double dy_ctrl, + double dx_to, double dy_to); + + void curve3(double x_to, double y_to); + + void curve3_rel(double dx_to, double dy_to); + + void curve4(double x_ctrl1, double y_ctrl1, + double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + + void curve4_rel(double dx_ctrl1, double dy_ctrl1, + double dx_ctrl2, double dy_ctrl2, + double dx_to, double dy_to); + + void curve4(double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + + void curve4_rel(double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + + + void end_poly(unsigned flags = path_flags_close); + void close_polygon(unsigned flags = path_flags_none); + + // Accessors + //-------------------------------------------------------------------- + const container_type& vertices() const { return m_vertices; } + container_type& vertices() { return m_vertices; } + + unsigned total_vertices() const; + + void rel_to_abs(double* x, double* y) const; + + unsigned last_vertex(double* x, double* y) const; + unsigned prev_vertex(double* x, double* y) const; + + double last_x() const; + double last_y() const; + + unsigned vertex(unsigned idx, double* x, double* y) const; + unsigned command(unsigned idx) const; + + void modify_vertex(unsigned idx, double x, double y); + void modify_vertex(unsigned idx, double x, double y, unsigned cmd); + void modify_command(unsigned idx, unsigned cmd); + + // VertexSource interface + //-------------------------------------------------------------------- + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + // Arrange the orientation of a polygon, all polygons in a path, + // or in all paths. After calling arrange_orientations() or + // arrange_orientations_all_paths(), all the polygons will have + // the same orientation, i.e. path_flags_cw or path_flags_ccw + //-------------------------------------------------------------------- + unsigned arrange_polygon_orientation(unsigned start, path_flags_e orientation); + unsigned arrange_orientations(unsigned path_id, path_flags_e orientation); + void arrange_orientations_all_paths(path_flags_e orientation); + void invert_polygon(unsigned start); + + // Flip all vertices horizontally or vertically, + // between x1 and x2, or between y1 and y2 respectively + //-------------------------------------------------------------------- + void flip_x(double x1, double x2); + void flip_y(double y1, double y2); + + // Concatenate path. The path is added as is. + //-------------------------------------------------------------------- + template + void concat_path(VertexSource& vs, unsigned path_id = 0) + { + double x, y; + unsigned cmd; + vs.rewind(path_id); + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + m_vertices.add_vertex(x, y, cmd); + } + } + + //-------------------------------------------------------------------- + // Join path. The path is joined with the existing one, that is, + // it behaves as if the pen of a plotter was always down (drawing) + template + void join_path(VertexSource& vs, unsigned path_id = 0) + { + double x, y; + unsigned cmd; + vs.rewind(path_id); + cmd = vs.vertex(&x, &y); + if(!is_stop(cmd)) + { + if(is_vertex(cmd)) + { + double x0, y0; + unsigned cmd0 = last_vertex(&x0, &y0); + if(is_vertex(cmd0)) + { + if(calc_distance(x, y, x0, y0) > vertex_dist_epsilon) + { + if(is_move_to(cmd)) cmd = path_cmd_line_to; + m_vertices.add_vertex(x, y, cmd); + } + } + else + { + if(is_stop(cmd0)) + { + cmd = path_cmd_move_to; + } + else + { + if(is_move_to(cmd)) cmd = path_cmd_line_to; + } + m_vertices.add_vertex(x, y, cmd); + } + } + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + m_vertices.add_vertex(x, y, is_move_to(cmd) ? + unsigned(path_cmd_line_to) : + cmd); + } + } + } + + // Concatenate polygon/polyline. + //-------------------------------------------------------------------- + template void concat_poly(const T* data, + unsigned num_points, + bool closed) + { + poly_plain_adaptor poly(data, num_points, closed); + concat_path(poly); + } + + // Join polygon/polyline continuously. + //-------------------------------------------------------------------- + template void join_poly(const T* data, + unsigned num_points, + bool closed) + { + poly_plain_adaptor poly(data, num_points, closed); + join_path(poly); + } + + //-------------------------------------------------------------------- + void translate(double dx, double dy, unsigned path_id=0); + void translate_all_paths(double dx, double dy); + + //-------------------------------------------------------------------- + template + void transform(const Trans& trans, unsigned path_id=0) + { + unsigned num_ver = m_vertices.total_vertices(); + for(; path_id < num_ver; path_id++) + { + double x, y; + unsigned cmd = m_vertices.vertex(path_id, &x, &y); + if(is_stop(cmd)) break; + if(is_vertex(cmd)) + { + trans.transform(&x, &y); + m_vertices.modify_vertex(path_id, x, y); + } + } + } + + //-------------------------------------------------------------------- + template + void transform_all_paths(const Trans& trans) + { + unsigned idx; + unsigned num_ver = m_vertices.total_vertices(); + for(idx = 0; idx < num_ver; idx++) + { + double x, y; + if(is_vertex(m_vertices.vertex(idx, &x, &y))) + { + trans.transform(&x, &y); + m_vertices.modify_vertex(idx, x, y); + } + } + } + + + //-------------------------------------------------------------------- + // If the end points of a path are very, very close then make them + // exactly equal so that the stroke converter is not confused. + //-------------------------------------------------------------------- + unsigned align_path(unsigned idx = 0) + { + if (idx >= total_vertices() || !is_move_to(command(idx))) + { + return total_vertices(); + } + + double start_x, start_y; + for (; idx < total_vertices() && is_move_to(command(idx)); ++idx) + { + vertex(idx, &start_x, &start_y); + } + while (idx < total_vertices() && is_drawing(command(idx))) + ++idx; + + double x, y; + if (is_drawing(vertex(idx - 1, &x, &y)) && + is_equal_eps(x, start_x, 1e-8) && + is_equal_eps(y, start_y, 1e-8)) + { + modify_vertex(idx - 1, start_x, start_y); + } + + while (idx < total_vertices() && !is_move_to(command(idx))) + ++idx; + return idx; + } + + void align_all_paths() + { + for (unsigned i = 0; i < total_vertices(); i = align_path(i)); + } + + + private: + unsigned perceive_polygon_orientation(unsigned start, unsigned end); + void invert_polygon(unsigned start, unsigned end); + + VertexContainer m_vertices; + unsigned m_iterator; + }; + + //------------------------------------------------------------------------ + template + unsigned path_base::start_new_path() + { + if(!is_stop(m_vertices.last_command())) + { + m_vertices.add_vertex(0.0, 0.0, path_cmd_stop); + } + return m_vertices.total_vertices(); + } + + + //------------------------------------------------------------------------ + template + inline void path_base::rel_to_abs(double* x, double* y) const + { + if(m_vertices.total_vertices()) + { + double x2; + double y2; + if(is_vertex(m_vertices.last_vertex(&x2, &y2))) + { + *x += x2; + *y += y2; + } + } + } + + //------------------------------------------------------------------------ + template + inline void path_base::move_to(double x, double y) + { + m_vertices.add_vertex(x, y, path_cmd_move_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::move_rel(double dx, double dy) + { + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_move_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::line_to(double x, double y) + { + m_vertices.add_vertex(x, y, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::line_rel(double dx, double dy) + { + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::hline_to(double x) + { + m_vertices.add_vertex(x, last_y(), path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::hline_rel(double dx) + { + double dy = 0; + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::vline_to(double y) + { + m_vertices.add_vertex(last_x(), y, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::vline_rel(double dy) + { + double dx = 0; + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + void path_base::arc_to(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x, double y) + { + if(m_vertices.total_vertices() && is_vertex(m_vertices.last_command())) + { + const double epsilon = 1e-30; + double x0 = 0.0; + double y0 = 0.0; + m_vertices.last_vertex(&x0, &y0); + + rx = fabs(rx); + ry = fabs(ry); + + // Ensure radii are valid + //------------------------- + if(rx < epsilon || ry < epsilon) + { + line_to(x, y); + return; + } + + if(calc_distance(x0, y0, x, y) < epsilon) + { + // If the endpoints (x, y) and (x0, y0) are identical, then this + // is equivalent to omitting the elliptical arc segment entirely. + return; + } + bezier_arc_svg a(x0, y0, rx, ry, angle, large_arc_flag, sweep_flag, x, y); + if(a.radii_ok()) + { + join_path(a); + } + else + { + line_to(x, y); + } + } + else + { + move_to(x, y); + } + } + + //------------------------------------------------------------------------ + template + void path_base::arc_rel(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double dx, double dy) + { + rel_to_abs(&dx, &dy); + arc_to(rx, ry, angle, large_arc_flag, sweep_flag, dx, dy); + } + + //------------------------------------------------------------------------ + template + void path_base::curve3(double x_ctrl, double y_ctrl, + double x_to, double y_to) + { + m_vertices.add_vertex(x_ctrl, y_ctrl, path_cmd_curve3); + m_vertices.add_vertex(x_to, y_to, path_cmd_curve3); + } + + //------------------------------------------------------------------------ + template + void path_base::curve3_rel(double dx_ctrl, double dy_ctrl, + double dx_to, double dy_to) + { + rel_to_abs(&dx_ctrl, &dy_ctrl); + rel_to_abs(&dx_to, &dy_to); + m_vertices.add_vertex(dx_ctrl, dy_ctrl, path_cmd_curve3); + m_vertices.add_vertex(dx_to, dy_to, path_cmd_curve3); + } + + //------------------------------------------------------------------------ + template + void path_base::curve3(double x_to, double y_to) + { + double x0; + double y0; + if(is_vertex(m_vertices.last_vertex(&x0, &y0))) + { + double x_ctrl; + double y_ctrl; + unsigned cmd = m_vertices.prev_vertex(&x_ctrl, &y_ctrl); + if(is_curve(cmd)) + { + x_ctrl = x0 + x0 - x_ctrl; + y_ctrl = y0 + y0 - y_ctrl; + } + else + { + x_ctrl = x0; + y_ctrl = y0; + } + curve3(x_ctrl, y_ctrl, x_to, y_to); + } + } + + //------------------------------------------------------------------------ + template + void path_base::curve3_rel(double dx_to, double dy_to) + { + rel_to_abs(&dx_to, &dy_to); + curve3(dx_to, dy_to); + } + + //------------------------------------------------------------------------ + template + void path_base::curve4(double x_ctrl1, double y_ctrl1, + double x_ctrl2, double y_ctrl2, + double x_to, double y_to) + { + m_vertices.add_vertex(x_ctrl1, y_ctrl1, path_cmd_curve4); + m_vertices.add_vertex(x_ctrl2, y_ctrl2, path_cmd_curve4); + m_vertices.add_vertex(x_to, y_to, path_cmd_curve4); + } + + //------------------------------------------------------------------------ + template + void path_base::curve4_rel(double dx_ctrl1, double dy_ctrl1, + double dx_ctrl2, double dy_ctrl2, + double dx_to, double dy_to) + { + rel_to_abs(&dx_ctrl1, &dy_ctrl1); + rel_to_abs(&dx_ctrl2, &dy_ctrl2); + rel_to_abs(&dx_to, &dy_to); + m_vertices.add_vertex(dx_ctrl1, dy_ctrl1, path_cmd_curve4); + m_vertices.add_vertex(dx_ctrl2, dy_ctrl2, path_cmd_curve4); + m_vertices.add_vertex(dx_to, dy_to, path_cmd_curve4); + } + + //------------------------------------------------------------------------ + template + void path_base::curve4(double x_ctrl2, double y_ctrl2, + double x_to, double y_to) + { + double x0; + double y0; + if(is_vertex(last_vertex(&x0, &y0))) + { + double x_ctrl1; + double y_ctrl1; + unsigned cmd = prev_vertex(&x_ctrl1, &y_ctrl1); + if(is_curve(cmd)) + { + x_ctrl1 = x0 + x0 - x_ctrl1; + y_ctrl1 = y0 + y0 - y_ctrl1; + } + else + { + x_ctrl1 = x0; + y_ctrl1 = y0; + } + curve4(x_ctrl1, y_ctrl1, x_ctrl2, y_ctrl2, x_to, y_to); + } + } + + //------------------------------------------------------------------------ + template + void path_base::curve4_rel(double dx_ctrl2, double dy_ctrl2, + double dx_to, double dy_to) + { + rel_to_abs(&dx_ctrl2, &dy_ctrl2); + rel_to_abs(&dx_to, &dy_to); + curve4(dx_ctrl2, dy_ctrl2, dx_to, dy_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::end_poly(unsigned flags) + { + if(is_vertex(m_vertices.last_command())) + { + m_vertices.add_vertex(0.0, 0.0, path_cmd_end_poly | flags); + } + } + + //------------------------------------------------------------------------ + template + inline void path_base::close_polygon(unsigned flags) + { + end_poly(path_flags_close | flags); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::total_vertices() const + { + return m_vertices.total_vertices(); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::last_vertex(double* x, double* y) const + { + return m_vertices.last_vertex(x, y); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::prev_vertex(double* x, double* y) const + { + return m_vertices.prev_vertex(x, y); + } + + //------------------------------------------------------------------------ + template + inline double path_base::last_x() const + { + return m_vertices.last_x(); + } + + //------------------------------------------------------------------------ + template + inline double path_base::last_y() const + { + return m_vertices.last_y(); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::vertex(unsigned idx, double* x, double* y) const + { + return m_vertices.vertex(idx, x, y); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::command(unsigned idx) const + { + return m_vertices.command(idx); + } + + //------------------------------------------------------------------------ + template + void path_base::modify_vertex(unsigned idx, double x, double y) + { + m_vertices.modify_vertex(idx, x, y); + } + + //------------------------------------------------------------------------ + template + void path_base::modify_vertex(unsigned idx, double x, double y, unsigned cmd) + { + m_vertices.modify_vertex(idx, x, y, cmd); + } + + //------------------------------------------------------------------------ + template + void path_base::modify_command(unsigned idx, unsigned cmd) + { + m_vertices.modify_command(idx, cmd); + } + + //------------------------------------------------------------------------ + template + inline void path_base::rewind(unsigned path_id) + { + m_iterator = path_id; + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::vertex(double* x, double* y) + { + if(m_iterator >= m_vertices.total_vertices()) return path_cmd_stop; + return m_vertices.vertex(m_iterator++, x, y); + } + + //------------------------------------------------------------------------ + template + unsigned path_base::perceive_polygon_orientation(unsigned start, + unsigned end) + { + // Calculate signed area (double area to be exact) + //--------------------- + unsigned np = end - start; + double area = 0.0; + unsigned i; + for(i = 0; i < np; i++) + { + double x1, y1, x2, y2; + m_vertices.vertex(start + i, &x1, &y1); + m_vertices.vertex(start + (i + 1) % np, &x2, &y2); + area += x1 * y2 - y1 * x2; + } + return (area < 0.0) ? path_flags_cw : path_flags_ccw; + } + + //------------------------------------------------------------------------ + template + void path_base::invert_polygon(unsigned start, unsigned end) + { + unsigned i; + unsigned tmp_cmd = m_vertices.command(start); + + --end; // Make "end" inclusive + + // Shift all commands to one position + for(i = start; i < end; i++) + { + m_vertices.modify_command(i, m_vertices.command(i + 1)); + } + + // Assign starting command to the ending command + m_vertices.modify_command(end, tmp_cmd); + + // Reverse the polygon + while(end > start) + { + m_vertices.swap_vertices(start++, end--); + } + } + + //------------------------------------------------------------------------ + template + void path_base::invert_polygon(unsigned start) + { + // Skip all non-vertices at the beginning + while(start < m_vertices.total_vertices() && + !is_vertex(m_vertices.command(start))) ++start; + + // Skip all insignificant move_to + while(start+1 < m_vertices.total_vertices() && + is_move_to(m_vertices.command(start)) && + is_move_to(m_vertices.command(start+1))) ++start; + + // Find the last vertex + unsigned end = start + 1; + while(end < m_vertices.total_vertices() && + !is_next_poly(m_vertices.command(end))) ++end; + + invert_polygon(start, end); + } + + //------------------------------------------------------------------------ + template + unsigned path_base::arrange_polygon_orientation(unsigned start, + path_flags_e orientation) + { + if(orientation == path_flags_none) return start; + + // Skip all non-vertices at the beginning + while(start < m_vertices.total_vertices() && + !is_vertex(m_vertices.command(start))) ++start; + + // Skip all insignificant move_to + while(start+1 < m_vertices.total_vertices() && + is_move_to(m_vertices.command(start)) && + is_move_to(m_vertices.command(start+1))) ++start; + + // Find the last vertex + unsigned end = start + 1; + while(end < m_vertices.total_vertices() && + !is_next_poly(m_vertices.command(end))) ++end; + + if(end - start > 2) + { + if(perceive_polygon_orientation(start, end) != unsigned(orientation)) + { + // Invert polygon, set orientation flag, and skip all end_poly + invert_polygon(start, end); + unsigned cmd; + while(end < m_vertices.total_vertices() && + is_end_poly(cmd = m_vertices.command(end))) + { + m_vertices.modify_command(end++, set_orientation(cmd, orientation)); + } + } + } + return end; + } + + //------------------------------------------------------------------------ + template + unsigned path_base::arrange_orientations(unsigned start, + path_flags_e orientation) + { + if(orientation != path_flags_none) + { + while(start < m_vertices.total_vertices()) + { + start = arrange_polygon_orientation(start, orientation); + if(is_stop(m_vertices.command(start))) + { + ++start; + break; + } + } + } + return start; + } + + //------------------------------------------------------------------------ + template + void path_base::arrange_orientations_all_paths(path_flags_e orientation) + { + if(orientation != path_flags_none) + { + unsigned start = 0; + while(start < m_vertices.total_vertices()) + { + start = arrange_orientations(start, orientation); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::flip_x(double x1, double x2) + { + unsigned i; + double x, y; + for(i = 0; i < m_vertices.total_vertices(); i++) + { + unsigned cmd = m_vertices.vertex(i, &x, &y); + if(is_vertex(cmd)) + { + m_vertices.modify_vertex(i, x2 - x + x1, y); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::flip_y(double y1, double y2) + { + unsigned i; + double x, y; + for(i = 0; i < m_vertices.total_vertices(); i++) + { + unsigned cmd = m_vertices.vertex(i, &x, &y); + if(is_vertex(cmd)) + { + m_vertices.modify_vertex(i, x, y2 - y + y1); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::translate(double dx, double dy, unsigned path_id) + { + unsigned num_ver = m_vertices.total_vertices(); + for(; path_id < num_ver; path_id++) + { + double x, y; + unsigned cmd = m_vertices.vertex(path_id, &x, &y); + if(is_stop(cmd)) break; + if(is_vertex(cmd)) + { + x += dx; + y += dy; + m_vertices.modify_vertex(path_id, x, y); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::translate_all_paths(double dx, double dy) + { + unsigned idx; + unsigned num_ver = m_vertices.total_vertices(); + for(idx = 0; idx < num_ver; idx++) + { + double x, y; + if(is_vertex(m_vertices.vertex(idx, &x, &y))) + { + x += dx; + y += dy; + m_vertices.modify_vertex(idx, x, y); + } + } + } + + //-----------------------------------------------------vertex_stl_storage + template class vertex_stl_storage + { + public: + typedef typename Container::value_type vertex_type; + typedef typename vertex_type::value_type value_type; + + void remove_all() { m_vertices.clear(); } + void free_all() { m_vertices.clear(); } + + void add_vertex(double x, double y, unsigned cmd) + { + m_vertices.push_back(vertex_type(value_type(x), + value_type(y), + int8u(cmd))); + } + + void modify_vertex(unsigned idx, double x, double y) + { + vertex_type& v = m_vertices[idx]; + v.x = value_type(x); + v.y = value_type(y); + } + + void modify_vertex(unsigned idx, double x, double y, unsigned cmd) + { + vertex_type& v = m_vertices[idx]; + v.x = value_type(x); + v.y = value_type(y); + v.cmd = int8u(cmd); + } + + void modify_command(unsigned idx, unsigned cmd) + { + m_vertices[idx].cmd = int8u(cmd); + } + + void swap_vertices(unsigned v1, unsigned v2) + { + vertex_type t = m_vertices[v1]; + m_vertices[v1] = m_vertices[v2]; + m_vertices[v2] = t; + } + + unsigned last_command() const + { + return m_vertices.size() ? + m_vertices[m_vertices.size() - 1].cmd : + path_cmd_stop; + } + + unsigned last_vertex(double* x, double* y) const + { + if(m_vertices.size() == 0) + { + *x = *y = 0.0; + return path_cmd_stop; + } + return vertex(m_vertices.size() - 1, x, y); + } + + unsigned prev_vertex(double* x, double* y) const + { + if(m_vertices.size() < 2) + { + *x = *y = 0.0; + return path_cmd_stop; + } + return vertex(m_vertices.size() - 2, x, y); + } + + double last_x() const + { + return m_vertices.size() ? m_vertices[m_vertices.size() - 1].x : 0.0; + } + + double last_y() const + { + return m_vertices.size() ? m_vertices[m_vertices.size() - 1].y : 0.0; + } + + unsigned total_vertices() const + { + return m_vertices.size(); + } + + unsigned vertex(unsigned idx, double* x, double* y) const + { + const vertex_type& v = m_vertices[idx]; + *x = v.x; + *y = v.y; + return v.cmd; + } + + unsigned command(unsigned idx) const + { + return m_vertices[idx].cmd; + } + + private: + Container m_vertices; + }; + + //-----------------------------------------------------------path_storage + typedef path_base > path_storage; + + // Example of declarations path_storage with pod_bvector as a container + //----------------------------------------------------------------------- + //typedef path_base > > path_storage; + +} + + + +// Example of declarations path_storage with std::vector as a container +//--------------------------------------------------------------------------- +//#include +//namespace agg +//{ +// typedef path_base > > stl_path_storage; +//} + + + + +#endif diff --git a/src/agg/agg_pixfmt_base.h b/src/agg/agg_pixfmt_base.h new file mode 100644 index 0000000000..57ae19cfe0 --- /dev/null +++ b/src/agg/agg_pixfmt_base.h @@ -0,0 +1,97 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_BASE_INCLUDED +#define AGG_PIXFMT_BASE_INCLUDED + +#include "agg_basics.h" +#include "agg_color_gray.h" +#include "agg_color_rgba.h" + +namespace agg +{ + struct pixfmt_gray_tag + { + }; + + struct pixfmt_rgb_tag + { + }; + + struct pixfmt_rgba_tag + { + }; + + //--------------------------------------------------------------blender_base + template + struct blender_base + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + + static rgba get(value_type r, value_type g, value_type b, value_type a, cover_type cover = cover_full) + { + if (cover > cover_none) + { + rgba c( + color_type::to_double(r), + color_type::to_double(g), + color_type::to_double(b), + color_type::to_double(a)); + + if (cover < cover_full) + { + double x = double(cover) / cover_full; + c.r *= x; + c.g *= x; + c.b *= x; + c.a *= x; + } + + return c; + } + else return rgba::no_color(); + } + + static rgba get(const value_type* p, cover_type cover = cover_full) + { + return get( + p[order_type::R], + p[order_type::G], + p[order_type::B], + p[order_type::A], + cover); + } + + static void set(value_type* p, value_type r, value_type g, value_type b, value_type a) + { + p[order_type::R] = r; + p[order_type::G] = g; + p[order_type::B] = b; + p[order_type::A] = a; + } + + static void set(value_type* p, const rgba& c) + { + p[order_type::R] = color_type::from_double(c.r); + p[order_type::G] = color_type::from_double(c.g); + p[order_type::B] = color_type::from_double(c.b); + p[order_type::A] = color_type::from_double(c.a); + } + }; +} + +#endif diff --git a/src/agg/agg_pixfmt_gray.h b/src/agg/agg_pixfmt_gray.h new file mode 100644 index 0000000000..d03dc86501 --- /dev/null +++ b/src/agg/agg_pixfmt_gray.h @@ -0,0 +1,738 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_GRAY_INCLUDED +#define AGG_PIXFMT_GRAY_INCLUDED + +#include +#include "agg_pixfmt_base.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + + //============================================================blender_gray + template struct blender_gray + { + typedef ColorT color_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + + // Blend pixels using the non-premultiplied form of Alvy-Ray Smith's + // compositing function. Since the render buffer is opaque we skip the + // initial premultiply and final demultiply. + + static AGG_INLINE void blend_pix(value_type* p, + value_type cv, value_type alpha, cover_type cover) + { + blend_pix(p, cv, color_type::mult_cover(alpha, cover)); + } + + static AGG_INLINE void blend_pix(value_type* p, + value_type cv, value_type alpha) + { + *p = color_type::lerp(*p, cv, alpha); + } + }; + + + //======================================================blender_gray_pre + template struct blender_gray_pre + { + typedef ColorT color_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + + // Blend pixels using the premultiplied form of Alvy-Ray Smith's + // compositing function. + + static AGG_INLINE void blend_pix(value_type* p, + value_type cv, value_type alpha, cover_type cover) + { + blend_pix(p, color_type::mult_cover(cv, cover), color_type::mult_cover(alpha, cover)); + } + + static AGG_INLINE void blend_pix(value_type* p, + value_type cv, value_type alpha) + { + *p = color_type::prelerp(*p, cv, alpha); + } + }; + + + + //=====================================================apply_gamma_dir_gray + template class apply_gamma_dir_gray + { + public: + typedef typename ColorT::value_type value_type; + + apply_gamma_dir_gray(const GammaLut& gamma) : m_gamma(gamma) {} + + AGG_INLINE void operator () (value_type* p) + { + *p = m_gamma.dir(*p); + } + + private: + const GammaLut& m_gamma; + }; + + + + //=====================================================apply_gamma_inv_gray + template class apply_gamma_inv_gray + { + public: + typedef typename ColorT::value_type value_type; + + apply_gamma_inv_gray(const GammaLut& gamma) : m_gamma(gamma) {} + + AGG_INLINE void operator () (value_type* p) + { + *p = m_gamma.inv(*p); + } + + private: + const GammaLut& m_gamma; + }; + + + + //=================================================pixfmt_alpha_blend_gray + template + class pixfmt_alpha_blend_gray + { + public: + typedef pixfmt_gray_tag pixfmt_category; + typedef RenBuf rbuf_type; + typedef typename rbuf_type::row_data row_data; + typedef Blender blender_type; + typedef typename blender_type::color_type color_type; + typedef int order_type; // A fake one + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum + { + num_components = 1, + pix_width = sizeof(value_type) * Step, + pix_step = Step, + pix_offset = Offset, + }; + struct pixel_type + { + value_type c[num_components]; + + void set(value_type v) + { + c[0] = v; + } + + void set(const color_type& color) + { + set(color.v); + } + + void get(value_type& v) const + { + v = c[0]; + } + + color_type get() const + { + return color_type(c[0]); + } + + pixel_type* next() + { + return (pixel_type*)(c + pix_step); + } + + const pixel_type* next() const + { + return (const pixel_type*)(c + pix_step); + } + + pixel_type* advance(int n) + { + return (pixel_type*)(c + n * pix_step); + } + + const pixel_type* advance(int n) const + { + return (const pixel_type*)(c + n * pix_step); + } + }; + + private: + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(pixel_type* p, + value_type v, value_type a, + unsigned cover) + { + blender_type::blend_pix(p->c, v, a, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(pixel_type* p, value_type v, value_type a) + { + blender_type::blend_pix(p->c, v, a); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(pixel_type* p, const color_type& c, unsigned cover) + { + blender_type::blend_pix(p->c, c.v, c.a, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(pixel_type* p, const color_type& c) + { + blender_type::blend_pix(p->c, c.v, c.a); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_or_blend_pix(pixel_type* p, const color_type& c, unsigned cover) + { + if (!c.is_transparent()) + { + if (c.is_opaque() && cover == cover_mask) + { + p->set(c); + } + else + { + blend_pix(p, c, cover); + } + } + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_or_blend_pix(pixel_type* p, const color_type& c) + { + if (!c.is_transparent()) + { + if (c.is_opaque()) + { + p->set(c); + } + else + { + blend_pix(p, c); + } + } + } + + public: + //-------------------------------------------------------------------- + explicit pixfmt_alpha_blend_gray(rbuf_type& rb) : + m_rbuf(&rb) + {} + void attach(rbuf_type& rb) { m_rbuf = &rb; } + //-------------------------------------------------------------------- + + template + bool attach(PixFmt& pixf, int x1, int y1, int x2, int y2) + { + rect_i r(x1, y1, x2, y2); + if (r.clip(rect_i(0, 0, pixf.width()-1, pixf.height()-1))) + { + int stride = pixf.stride(); + m_rbuf->attach(pixf.pix_ptr(r.x1, stride < 0 ? r.y2 : r.y1), + (r.x2 - r.x1) + 1, + (r.y2 - r.y1) + 1, + stride); + return true; + } + return false; + } + + //-------------------------------------------------------------------- + AGG_INLINE unsigned width() const { return m_rbuf->width(); } + AGG_INLINE unsigned height() const { return m_rbuf->height(); } + AGG_INLINE int stride() const { return m_rbuf->stride(); } + + //-------------------------------------------------------------------- + int8u* row_ptr(int y) { return m_rbuf->row_ptr(y); } + const int8u* row_ptr(int y) const { return m_rbuf->row_ptr(y); } + row_data row(int y) const { return m_rbuf->row(y); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* pix_ptr(int x, int y) + { + return m_rbuf->row_ptr(y) + sizeof(value_type) * (x * pix_step + pix_offset); + } + + AGG_INLINE const int8u* pix_ptr(int x, int y) const + { + return m_rbuf->row_ptr(y) + sizeof(value_type) * (x * pix_step + pix_offset); + } + + // Return pointer to pixel value, forcing row to be allocated. + AGG_INLINE pixel_type* pix_value_ptr(int x, int y, unsigned len) + { + return (pixel_type*)(m_rbuf->row_ptr(x, y, len) + sizeof(value_type) * (x * pix_step + pix_offset)); + } + + // Return pointer to pixel value, or null if row not allocated. + AGG_INLINE const pixel_type* pix_value_ptr(int x, int y) const + { + int8u* p = m_rbuf->row_ptr(y); + return p ? (pixel_type*)(p + sizeof(value_type) * (x * pix_step + pix_offset)) : 0; + } + + // Get pixel pointer from raw buffer pointer. + AGG_INLINE static pixel_type* pix_value_ptr(void* p) + { + return (pixel_type*)((value_type*)p + pix_offset); + } + + // Get pixel pointer from raw buffer pointer. + AGG_INLINE static const pixel_type* pix_value_ptr(const void* p) + { + return (const pixel_type*)((const value_type*)p + pix_offset); + } + + //-------------------------------------------------------------------- + AGG_INLINE static void write_plain_color(void* p, color_type c) + { + // Grayscale formats are implicitly premultiplied. + c.premultiply(); + pix_value_ptr(p)->set(c); + } + + //-------------------------------------------------------------------- + AGG_INLINE static color_type read_plain_color(const void* p) + { + return pix_value_ptr(p)->get(); + } + + //-------------------------------------------------------------------- + AGG_INLINE static void make_pix(int8u* p, const color_type& c) + { + ((pixel_type*)p)->set(c); + } + + //-------------------------------------------------------------------- + AGG_INLINE color_type pixel(int x, int y) const + { + if (const pixel_type* p = pix_value_ptr(x, y)) + { + return p->get(); + } + return color_type::no_color(); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_pixel(int x, int y, const color_type& c) + { + pix_value_ptr(x, y, 1)->set(c); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pixel(int x, int y, const color_type& c, int8u cover) + { + copy_or_blend_pix(pix_value_ptr(x, y, 1), c, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_hline(int x, int y, + unsigned len, + const color_type& c) + { + pixel_type* p = pix_value_ptr(x, y, len); + do + { + p->set(c); + p = p->next(); + } + while(--len); + } + + + //-------------------------------------------------------------------- + AGG_INLINE void copy_vline(int x, int y, + unsigned len, + const color_type& c) + { + do + { + pix_value_ptr(x, y++, 1)->set(c); + } + while (--len); + } + + + //-------------------------------------------------------------------- + void blend_hline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (!c.is_transparent()) + { + pixel_type* p = pix_value_ptr(x, y, len); + + if (c.is_opaque() && cover == cover_mask) + { + do + { + p->set(c); + p = p->next(); + } + while (--len); + } + else + { + do + { + blend_pix(p, c, cover); + p = p->next(); + } + while (--len); + } + } + } + + + //-------------------------------------------------------------------- + void blend_vline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (!c.is_transparent()) + { + if (c.is_opaque() && cover == cover_mask) + { + do + { + pix_value_ptr(x, y++, 1)->set(c); + } + while (--len); + } + else + { + do + { + blend_pix(pix_value_ptr(x, y++, 1), c, cover); + } + while (--len); + } + } + } + + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + if (!c.is_transparent()) + { + pixel_type* p = pix_value_ptr(x, y, len); + + do + { + if (c.is_opaque() && *covers == cover_mask) + { + p->set(c); + } + else + { + blend_pix(p, c, *covers); + } + p = p->next(); + ++covers; + } + while (--len); + } + } + + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + if (!c.is_transparent()) + { + do + { + pixel_type* p = pix_value_ptr(x, y++, 1); + + if (c.is_opaque() && *covers == cover_mask) + { + p->set(c); + } + else + { + blend_pix(p, c, *covers); + } + ++covers; + } + while (--len); + } + } + + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, + unsigned len, + const color_type* colors) + { + pixel_type* p = pix_value_ptr(x, y, len); + + do + { + p->set(*colors++); + p = p->next(); + } + while (--len); + } + + + //-------------------------------------------------------------------- + void copy_color_vspan(int x, int y, + unsigned len, + const color_type* colors) + { + do + { + pix_value_ptr(x, y++, 1)->set(*colors++); + } + while (--len); + } + + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + pixel_type* p = pix_value_ptr(x, y, len); + + if (covers) + { + do + { + copy_or_blend_pix(p, *colors++, *covers++); + p = p->next(); + } + while (--len); + } + else + { + if (cover == cover_mask) + { + do + { + copy_or_blend_pix(p, *colors++); + p = p->next(); + } + while (--len); + } + else + { + do + { + copy_or_blend_pix(p, *colors++, cover); + p = p->next(); + } + while (--len); + } + } + } + + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + if (covers) + { + do + { + copy_or_blend_pix(pix_value_ptr(x, y++, 1), *colors++, *covers++); + } + while (--len); + } + else + { + if (cover == cover_mask) + { + do + { + copy_or_blend_pix(pix_value_ptr(x, y++, 1), *colors++); + } + while (--len); + } + else + { + do + { + copy_or_blend_pix(pix_value_ptr(x, y++, 1), *colors++, cover); + } + while (--len); + } + } + } + + //-------------------------------------------------------------------- + template void for_each_pixel(Function f) + { + unsigned y; + for (y = 0; y < height(); ++y) + { + row_data r = m_rbuf->row(y); + if (r.ptr) + { + unsigned len = r.x2 - r.x1 + 1; + pixel_type* p = pix_value_ptr(r.x1, y, len); + do + { + f(p->c); + p = p->next(); + } + while (--len); + } + } + } + + //-------------------------------------------------------------------- + template void apply_gamma_dir(const GammaLut& g) + { + for_each_pixel(apply_gamma_dir_gray(g)); + } + + //-------------------------------------------------------------------- + template void apply_gamma_inv(const GammaLut& g) + { + for_each_pixel(apply_gamma_inv_gray(g)); + } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf2& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len) + { + if (const int8u* p = from.row_ptr(ysrc)) + { + memmove(m_rbuf->row_ptr(xdst, ydst, len) + xdst * pix_width, + p + xsrc * pix_width, + len * pix_width); + } + } + + //-------------------------------------------------------------------- + // Blend from single color, using grayscale surface as alpha channel. + template + void blend_from_color(const SrcPixelFormatRenderer& from, + const color_type& color, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::pixel_type src_pixel_type; + typedef typename SrcPixelFormatRenderer::color_type src_color_type; + + if (const src_pixel_type* psrc = from.pix_value_ptr(xsrc, ysrc)) + { + pixel_type* pdst = pix_value_ptr(xdst, ydst, len); + + do + { + copy_or_blend_pix(pdst, color, src_color_type::scale_cover(cover, psrc->c[0])); + psrc = psrc->next(); + pdst = pdst->next(); + } + while (--len); + } + } + + //-------------------------------------------------------------------- + // Blend from color table, using grayscale surface as indexes into table. + // Obviously, this only works for integer value types. + template + void blend_from_lut(const SrcPixelFormatRenderer& from, + const color_type* color_lut, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::pixel_type src_pixel_type; + + if (const src_pixel_type* psrc = from.pix_value_ptr(xsrc, ysrc)) + { + pixel_type* pdst = pix_value_ptr(xdst, ydst, len); + + do + { + copy_or_blend_pix(pdst, color_lut[psrc->c[0]], cover); + psrc = psrc->next(); + pdst = pdst->next(); + } + while (--len); + } + } + + private: + rbuf_type* m_rbuf; + }; + + typedef blender_gray blender_gray8; + typedef blender_gray blender_sgray8; + typedef blender_gray blender_gray16; + typedef blender_gray blender_gray32; + + typedef blender_gray_pre blender_gray8_pre; + typedef blender_gray_pre blender_sgray8_pre; + typedef blender_gray_pre blender_gray16_pre; + typedef blender_gray_pre blender_gray32_pre; + + typedef pixfmt_alpha_blend_gray pixfmt_gray8; + typedef pixfmt_alpha_blend_gray pixfmt_sgray8; + typedef pixfmt_alpha_blend_gray pixfmt_gray16; + typedef pixfmt_alpha_blend_gray pixfmt_gray32; + + typedef pixfmt_alpha_blend_gray pixfmt_gray8_pre; + typedef pixfmt_alpha_blend_gray pixfmt_sgray8_pre; + typedef pixfmt_alpha_blend_gray pixfmt_gray16_pre; + typedef pixfmt_alpha_blend_gray pixfmt_gray32_pre; +} + +#endif + diff --git a/src/agg/agg_pixfmt_rgb.h b/src/agg/agg_pixfmt_rgb.h new file mode 100644 index 0000000000..6fa8772ce0 --- /dev/null +++ b/src/agg/agg_pixfmt_rgb.h @@ -0,0 +1,995 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_RGB_INCLUDED +#define AGG_PIXFMT_RGB_INCLUDED + +#include +#include "agg_pixfmt_base.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + + //=====================================================apply_gamma_dir_rgb + template class apply_gamma_dir_rgb + { + public: + typedef typename ColorT::value_type value_type; + + apply_gamma_dir_rgb(const GammaLut& gamma) : m_gamma(gamma) {} + + AGG_INLINE void operator () (value_type* p) + { + p[Order::R] = m_gamma.dir(p[Order::R]); + p[Order::G] = m_gamma.dir(p[Order::G]); + p[Order::B] = m_gamma.dir(p[Order::B]); + } + + private: + const GammaLut& m_gamma; + }; + + + + //=====================================================apply_gamma_inv_rgb + template class apply_gamma_inv_rgb + { + public: + typedef typename ColorT::value_type value_type; + + apply_gamma_inv_rgb(const GammaLut& gamma) : m_gamma(gamma) {} + + AGG_INLINE void operator () (value_type* p) + { + p[Order::R] = m_gamma.inv(p[Order::R]); + p[Order::G] = m_gamma.inv(p[Order::G]); + p[Order::B] = m_gamma.inv(p[Order::B]); + } + + private: + const GammaLut& m_gamma; + }; + + + //=========================================================blender_rgb + template + struct blender_rgb + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + + // Blend pixels using the non-premultiplied form of Alvy-Ray Smith's + // compositing function. Since the render buffer is opaque we skip the + // initial premultiply and final demultiply. + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + value_type cr, value_type cg, value_type cb, value_type alpha, cover_type cover) + { + blend_pix(p, cr, cg, cb, color_type::mult_cover(alpha, cover)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + value_type cr, value_type cg, value_type cb, value_type alpha) + { + p[Order::R] = color_type::lerp(p[Order::R], cr, alpha); + p[Order::G] = color_type::lerp(p[Order::G], cg, alpha); + p[Order::B] = color_type::lerp(p[Order::B], cb, alpha); + } + }; + + //======================================================blender_rgb_pre + template + struct blender_rgb_pre + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + + // Blend pixels using the premultiplied form of Alvy-Ray Smith's + // compositing function. + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + value_type cr, value_type cg, value_type cb, value_type alpha, cover_type cover) + { + blend_pix(p, + color_type::mult_cover(cr, cover), + color_type::mult_cover(cg, cover), + color_type::mult_cover(cb, cover), + color_type::mult_cover(alpha, cover)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + value_type cr, value_type cg, value_type cb, value_type alpha) + { + p[Order::R] = color_type::prelerp(p[Order::R], cr, alpha); + p[Order::G] = color_type::prelerp(p[Order::G], cg, alpha); + p[Order::B] = color_type::prelerp(p[Order::B], cb, alpha); + } + }; + + //===================================================blender_rgb_gamma + template + class blender_rgb_gamma : public blender_base + { + public: + typedef ColorT color_type; + typedef Order order_type; + typedef Gamma gamma_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + + //-------------------------------------------------------------------- + blender_rgb_gamma() : m_gamma(0) {} + void gamma(const gamma_type& g) { m_gamma = &g; } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(value_type* p, + value_type cr, value_type cg, value_type cb, value_type alpha, cover_type cover) + { + blend_pix(p, cr, cg, cb, color_type::mult_cover(alpha, cover)); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(value_type* p, + value_type cr, value_type cg, value_type cb, value_type alpha) + { + calc_type r = m_gamma->dir(p[Order::R]); + calc_type g = m_gamma->dir(p[Order::G]); + calc_type b = m_gamma->dir(p[Order::B]); + p[Order::R] = m_gamma->inv(color_type::downscale((m_gamma->dir(cr) - r) * alpha) + r); + p[Order::G] = m_gamma->inv(color_type::downscale((m_gamma->dir(cg) - g) * alpha) + g); + p[Order::B] = m_gamma->inv(color_type::downscale((m_gamma->dir(cb) - b) * alpha) + b); + } + + private: + const gamma_type* m_gamma; + }; + + + //==================================================pixfmt_alpha_blend_rgb + template + class pixfmt_alpha_blend_rgb + { + public: + typedef pixfmt_rgb_tag pixfmt_category; + typedef RenBuf rbuf_type; + typedef Blender blender_type; + typedef typename rbuf_type::row_data row_data; + typedef typename blender_type::color_type color_type; + typedef typename blender_type::order_type order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum + { + num_components = 3, + pix_step = Step, + pix_offset = Offset, + pix_width = sizeof(value_type) * pix_step + }; + struct pixel_type + { + value_type c[num_components]; + + void set(value_type r, value_type g, value_type b) + { + c[order_type::R] = r; + c[order_type::G] = g; + c[order_type::B] = b; + } + + void set(const color_type& color) + { + set(color.r, color.g, color.b); + } + + void get(value_type& r, value_type& g, value_type& b) const + { + r = c[order_type::R]; + g = c[order_type::G]; + b = c[order_type::B]; + } + + color_type get() const + { + return color_type( + c[order_type::R], + c[order_type::G], + c[order_type::B]); + } + + pixel_type* next() + { + return (pixel_type*)(c + pix_step); + } + + const pixel_type* next() const + { + return (const pixel_type*)(c + pix_step); + } + + pixel_type* advance(int n) + { + return (pixel_type*)(c + n * pix_step); + } + + const pixel_type* advance(int n) const + { + return (const pixel_type*)(c + n * pix_step); + } + }; + + private: + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(pixel_type* p, + value_type r, value_type g, value_type b, value_type a, + unsigned cover) + { + m_blender.blend_pix(p->c, r, g, b, a, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(pixel_type* p, + value_type r, value_type g, value_type b, value_type a) + { + m_blender.blend_pix(p->c, r, g, b, a); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(pixel_type* p, const color_type& c, unsigned cover) + { + m_blender.blend_pix(p->c, c.r, c.g, c.b, c.a, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(pixel_type* p, const color_type& c) + { + m_blender.blend_pix(p->c, c.r, c.g, c.b, c.a); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_or_blend_pix(pixel_type* p, const color_type& c, unsigned cover) + { + if (!c.is_transparent()) + { + if (c.is_opaque() && cover == cover_mask) + { + p->set(c); + } + else + { + blend_pix(p, c, cover); + } + } + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_or_blend_pix(pixel_type* p, const color_type& c) + { + if (!c.is_transparent()) + { + if (c.is_opaque()) + { + p->set(c); + } + else + { + blend_pix(p, c); + } + } + } + + public: + //-------------------------------------------------------------------- + explicit pixfmt_alpha_blend_rgb(rbuf_type& rb) : + m_rbuf(&rb) + {} + void attach(rbuf_type& rb) { m_rbuf = &rb; } + + //-------------------------------------------------------------------- + template + bool attach(PixFmt& pixf, int x1, int y1, int x2, int y2) + { + rect_i r(x1, y1, x2, y2); + if (r.clip(rect_i(0, 0, pixf.width()-1, pixf.height()-1))) + { + int stride = pixf.stride(); + m_rbuf->attach(pixf.pix_ptr(r.x1, stride < 0 ? r.y2 : r.y1), + (r.x2 - r.x1) + 1, + (r.y2 - r.y1) + 1, + stride); + return true; + } + return false; + } + + //-------------------------------------------------------------------- + Blender& blender() { return m_blender; } + + //-------------------------------------------------------------------- + AGG_INLINE unsigned width() const { return m_rbuf->width(); } + AGG_INLINE unsigned height() const { return m_rbuf->height(); } + AGG_INLINE int stride() const { return m_rbuf->stride(); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* row_ptr(int y) { return m_rbuf->row_ptr(y); } + AGG_INLINE const int8u* row_ptr(int y) const { return m_rbuf->row_ptr(y); } + AGG_INLINE row_data row(int y) const { return m_rbuf->row(y); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* pix_ptr(int x, int y) + { + return m_rbuf->row_ptr(y) + sizeof(value_type) * (x * pix_step + pix_offset); + } + + AGG_INLINE const int8u* pix_ptr(int x, int y) const + { + return m_rbuf->row_ptr(y) + sizeof(value_type) * (x * pix_step + pix_offset); + } + + // Return pointer to pixel value, forcing row to be allocated. + AGG_INLINE pixel_type* pix_value_ptr(int x, int y, unsigned len) + { + return (pixel_type*)(m_rbuf->row_ptr(x, y, len) + sizeof(value_type) * (x * pix_step + pix_offset)); + } + + // Return pointer to pixel value, or null if row not allocated. + AGG_INLINE const pixel_type* pix_value_ptr(int x, int y) const + { + int8u* p = m_rbuf->row_ptr(y); + return p ? (pixel_type*)(p + sizeof(value_type) * (x * pix_step + pix_offset)) : 0; + } + + // Get pixel pointer from raw buffer pointer. + AGG_INLINE static pixel_type* pix_value_ptr(void* p) + { + return (pixel_type*)((value_type*)p + pix_offset); + } + + // Get pixel pointer from raw buffer pointer. + AGG_INLINE static const pixel_type* pix_value_ptr(const void* p) + { + return (const pixel_type*)((const value_type*)p + pix_offset); + } + + //-------------------------------------------------------------------- + AGG_INLINE static void write_plain_color(void* p, color_type c) + { + // RGB formats are implicitly premultiplied. + c.premultiply(); + pix_value_ptr(p)->set(c); + } + + //-------------------------------------------------------------------- + AGG_INLINE static color_type read_plain_color(const void* p) + { + return pix_value_ptr(p)->get(); + } + + //-------------------------------------------------------------------- + AGG_INLINE static void make_pix(int8u* p, const color_type& c) + { + ((pixel_type*)p)->set(c); + } + + //-------------------------------------------------------------------- + AGG_INLINE color_type pixel(int x, int y) const + { + if (const pixel_type* p = pix_value_ptr(x, y)) + { + return p->get(); + } + return color_type::no_color(); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_pixel(int x, int y, const color_type& c) + { + pix_value_ptr(x, y, 1)->set(c); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pixel(int x, int y, const color_type& c, int8u cover) + { + copy_or_blend_pix(pix_value_ptr(x, y, 1), c, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_hline(int x, int y, + unsigned len, + const color_type& c) + { + pixel_type* p = pix_value_ptr(x, y, len); + do + { + p->set(c); + p = p->next(); + } + while(--len); + } + + + //-------------------------------------------------------------------- + AGG_INLINE void copy_vline(int x, int y, + unsigned len, + const color_type& c) + { + do + { + pix_value_ptr(x, y++, 1)->set(c); + } + while (--len); + } + + //-------------------------------------------------------------------- + void blend_hline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (!c.is_transparent()) + { + pixel_type* p = pix_value_ptr(x, y, len); + + if (c.is_opaque() && cover == cover_mask) + { + do + { + p->set(c); + p = p->next(); + } + while (--len); + } + else + { + do + { + blend_pix(p, c, cover); + p = p->next(); + } + while (--len); + } + } + } + + + //-------------------------------------------------------------------- + void blend_vline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (!c.is_transparent()) + { + if (c.is_opaque() && cover == cover_mask) + { + do + { + pix_value_ptr(x, y++, 1)->set(c); + } + while (--len); + } + else + { + do + { + blend_pix(pix_value_ptr(x, y++, 1), c, cover); + } + while (--len); + } + } + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + if (!c.is_transparent()) + { + pixel_type* p = pix_value_ptr(x, y, len); + + do + { + if (c.is_opaque() && *covers == cover_mask) + { + p->set(c); + } + else + { + blend_pix(p, c, *covers); + } + p = p->next(); + ++covers; + } + while (--len); + } + } + + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + if (!c.is_transparent()) + { + do + { + pixel_type* p = pix_value_ptr(x, y++, 1); + + if (c.is_opaque() && *covers == cover_mask) + { + p->set(c); + } + else + { + blend_pix(p, c, *covers); + } + ++covers; + } + while (--len); + } + } + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, + unsigned len, + const color_type* colors) + { + pixel_type* p = pix_value_ptr(x, y, len); + + do + { + p->set(*colors++); + p = p->next(); + } + while (--len); + } + + + //-------------------------------------------------------------------- + void copy_color_vspan(int x, int y, + unsigned len, + const color_type* colors) + { + do + { + pix_value_ptr(x, y++, 1)->set(*colors++); + } + while (--len); + } + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + pixel_type* p = pix_value_ptr(x, y, len); + + if (covers) + { + do + { + copy_or_blend_pix(p, *colors++, *covers++); + p = p->next(); + } + while (--len); + } + else + { + if (cover == cover_mask) + { + do + { + copy_or_blend_pix(p, *colors++); + p = p->next(); + } + while (--len); + } + else + { + do + { + copy_or_blend_pix(p, *colors++, cover); + p = p->next(); + } + while (--len); + } + } + } + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + if (covers) + { + do + { + copy_or_blend_pix(pix_value_ptr(x, y++, 1), *colors++, *covers++); + } + while (--len); + } + else + { + if (cover == cover_mask) + { + do + { + copy_or_blend_pix(pix_value_ptr(x, y++, 1), *colors++); + } + while (--len); + } + else + { + do + { + copy_or_blend_pix(pix_value_ptr(x, y++, 1), *colors++, cover); + } + while (--len); + } + } + } + + //-------------------------------------------------------------------- + template void for_each_pixel(Function f) + { + for (unsigned y = 0; y < height(); ++y) + { + row_data r = m_rbuf->row(y); + if (r.ptr) + { + unsigned len = r.x2 - r.x1 + 1; + pixel_type* p = pix_value_ptr(r.x1, y, len); + do + { + f(p->c); + p = p->next(); + } + while (--len); + } + } + } + + //-------------------------------------------------------------------- + template void apply_gamma_dir(const GammaLut& g) + { + for_each_pixel(apply_gamma_dir_rgb(g)); + } + + //-------------------------------------------------------------------- + template void apply_gamma_inv(const GammaLut& g) + { + for_each_pixel(apply_gamma_inv_rgb(g)); + } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf2& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len) + { + if (const int8u* p = from.row_ptr(ysrc)) + { + memmove(m_rbuf->row_ptr(xdst, ydst, len) + xdst * pix_width, + p + xsrc * pix_width, + len * pix_width); + } + } + + //-------------------------------------------------------------------- + // Blend from an RGBA surface. + template + void blend_from(const SrcPixelFormatRenderer& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::pixel_type src_pixel_type; + typedef typename SrcPixelFormatRenderer::order_type src_order; + + if (const src_pixel_type* psrc = from.pix_value_ptr(xsrc, ysrc)) + { + pixel_type* pdst = pix_value_ptr(xdst, ydst, len); + + if (cover == cover_mask) + { + do + { + value_type alpha = psrc->c[src_order::A]; + if (alpha <= color_type::empty_value()) + { + if (alpha >= color_type::full_value()) + { + pdst->c[order_type::R] = psrc->c[src_order::R]; + pdst->c[order_type::G] = psrc->c[src_order::G]; + pdst->c[order_type::B] = psrc->c[src_order::B]; + } + else + { + blend_pix(pdst, + psrc->c[src_order::R], + psrc->c[src_order::G], + psrc->c[src_order::B], + alpha); + } + } + psrc = psrc->next(); + pdst = pdst->next(); + } + while(--len); + } + else + { + do + { + copy_or_blend_pix(pdst, psrc->get(), cover); + psrc = psrc->next(); + pdst = pdst->next(); + } + while (--len); + } + } + } + + //-------------------------------------------------------------------- + // Blend from single color, using grayscale surface as alpha channel. + template + void blend_from_color(const SrcPixelFormatRenderer& from, + const color_type& color, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::pixel_type src_pixel_type; + typedef typename SrcPixelFormatRenderer::color_type src_color_type; + + if (const src_pixel_type* psrc = from.pix_value_ptr(xsrc, ysrc)) + { + pixel_type* pdst = pix_value_ptr(xdst, ydst, len); + + do + { + copy_or_blend_pix(pdst, color, src_color_type::scale_cover(cover, psrc->c[0])); + psrc = psrc->next(); + pdst = pdst->next(); + } + while (--len); + } + } + + //-------------------------------------------------------------------- + // Blend from color table, using grayscale surface as indexes into table. + // Obviously, this only works for integer value types. + template + void blend_from_lut(const SrcPixelFormatRenderer& from, + const color_type* color_lut, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::pixel_type src_pixel_type; + + if (const src_pixel_type* psrc = from.pix_value_ptr(xsrc, ysrc)) + { + pixel_type* pdst = pix_value_ptr(xdst, ydst, len); + + if (cover == cover_mask) + { + do + { + const color_type& color = color_lut[psrc->c[0]]; + blend_pix(pdst, color); + psrc = psrc->next(); + pdst = pdst->next(); + } + while(--len); + } + else + { + do + { + copy_or_blend_pix(pdst, color_lut[psrc->c[0]], cover); + psrc = psrc->next(); + pdst = pdst->next(); + } + while(--len); + } + } + } + + private: + rbuf_type* m_rbuf; + Blender m_blender; + }; + + //----------------------------------------------------------------------- + typedef blender_rgb blender_rgb24; + typedef blender_rgb blender_bgr24; + typedef blender_rgb blender_srgb24; + typedef blender_rgb blender_sbgr24; + typedef blender_rgb blender_rgb48; + typedef blender_rgb blender_bgr48; + typedef blender_rgb blender_rgb96; + typedef blender_rgb blender_bgr96; + + typedef blender_rgb_pre blender_rgb24_pre; + typedef blender_rgb_pre blender_bgr24_pre; + typedef blender_rgb_pre blender_srgb24_pre; + typedef blender_rgb_pre blender_sbgr24_pre; + typedef blender_rgb_pre blender_rgb48_pre; + typedef blender_rgb_pre blender_bgr48_pre; + typedef blender_rgb_pre blender_rgb96_pre; + typedef blender_rgb_pre blender_bgr96_pre; + + typedef pixfmt_alpha_blend_rgb pixfmt_rgb24; + typedef pixfmt_alpha_blend_rgb pixfmt_bgr24; + typedef pixfmt_alpha_blend_rgb pixfmt_srgb24; + typedef pixfmt_alpha_blend_rgb pixfmt_sbgr24; + typedef pixfmt_alpha_blend_rgb pixfmt_rgb48; + typedef pixfmt_alpha_blend_rgb pixfmt_bgr48; + typedef pixfmt_alpha_blend_rgb pixfmt_rgb96; + typedef pixfmt_alpha_blend_rgb pixfmt_bgr96; + + typedef pixfmt_alpha_blend_rgb pixfmt_rgb24_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_bgr24_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_srgb24_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_sbgr24_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_rgb48_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_bgr48_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_rgb96_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_bgr96_pre; + + typedef pixfmt_alpha_blend_rgb pixfmt_rgbx32; + typedef pixfmt_alpha_blend_rgb pixfmt_xrgb32; + typedef pixfmt_alpha_blend_rgb pixfmt_xbgr32; + typedef pixfmt_alpha_blend_rgb pixfmt_bgrx32; + typedef pixfmt_alpha_blend_rgb pixfmt_srgbx32; + typedef pixfmt_alpha_blend_rgb pixfmt_sxrgb32; + typedef pixfmt_alpha_blend_rgb pixfmt_sxbgr32; + typedef pixfmt_alpha_blend_rgb pixfmt_sbgrx32; + typedef pixfmt_alpha_blend_rgb pixfmt_rgbx64; + typedef pixfmt_alpha_blend_rgb pixfmt_xrgb64; + typedef pixfmt_alpha_blend_rgb pixfmt_xbgr64; + typedef pixfmt_alpha_blend_rgb pixfmt_bgrx64; + typedef pixfmt_alpha_blend_rgb pixfmt_rgbx128; + typedef pixfmt_alpha_blend_rgb pixfmt_xrgb128; + typedef pixfmt_alpha_blend_rgb pixfmt_xbgr128; + typedef pixfmt_alpha_blend_rgb pixfmt_bgrx128; + + typedef pixfmt_alpha_blend_rgb pixfmt_rgbx32_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_xrgb32_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_xbgr32_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_bgrx32_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_srgbx32_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_sxrgb32_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_sxbgr32_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_sbgrx32_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_rgbx64_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_xrgb64_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_xbgr64_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_bgrx64_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_rgbx128_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_xrgb128_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_xbgr128_pre; + typedef pixfmt_alpha_blend_rgb pixfmt_bgrx128_pre; + + + //-----------------------------------------------------pixfmt_rgb24_gamma + template class pixfmt_rgb24_gamma : + public pixfmt_alpha_blend_rgb, rendering_buffer, 3> + { + public: + pixfmt_rgb24_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb, rendering_buffer, 3>(rb) + { + this->blender().gamma(g); + } + }; + + //-----------------------------------------------------pixfmt_srgb24_gamma + template class pixfmt_srgb24_gamma : + public pixfmt_alpha_blend_rgb, rendering_buffer, 3> + { + public: + pixfmt_srgb24_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb, rendering_buffer, 3>(rb) + { + this->blender().gamma(g); + } + }; + + //-----------------------------------------------------pixfmt_bgr24_gamma + template class pixfmt_bgr24_gamma : + public pixfmt_alpha_blend_rgb, rendering_buffer, 3> + { + public: + pixfmt_bgr24_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb, rendering_buffer, 3>(rb) + { + this->blender().gamma(g); + } + }; + + //-----------------------------------------------------pixfmt_sbgr24_gamma + template class pixfmt_sbgr24_gamma : + public pixfmt_alpha_blend_rgb, rendering_buffer, 3> + { + public: + pixfmt_sbgr24_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb, rendering_buffer, 3>(rb) + { + this->blender().gamma(g); + } + }; + + //-----------------------------------------------------pixfmt_rgb48_gamma + template class pixfmt_rgb48_gamma : + public pixfmt_alpha_blend_rgb, rendering_buffer, 3> + { + public: + pixfmt_rgb48_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb, rendering_buffer, 3>(rb) + { + this->blender().gamma(g); + } + }; + + //-----------------------------------------------------pixfmt_bgr48_gamma + template class pixfmt_bgr48_gamma : + public pixfmt_alpha_blend_rgb, rendering_buffer, 3> + { + public: + pixfmt_bgr48_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb, rendering_buffer, 3>(rb) + { + this->blender().gamma(g); + } + }; + +} + +#endif + diff --git a/src/agg/agg_rasterizer_cells_aa.h b/src/agg/agg_rasterizer_cells_aa.h new file mode 100644 index 0000000000..1147148fa7 --- /dev/null +++ b/src/agg/agg_rasterizer_cells_aa.h @@ -0,0 +1,741 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// +// The author gratefully acknowleges the support of David Turner, +// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType +// libray - in producing this work. See http://www.freetype.org for details. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +#ifndef AGG_RASTERIZER_CELLS_AA_INCLUDED +#define AGG_RASTERIZER_CELLS_AA_INCLUDED + +#include +#include +#include +#include "agg_math.h" +#include "agg_array.h" + + +namespace agg +{ + + //-----------------------------------------------------rasterizer_cells_aa + // An internal class that implements the main rasterization algorithm. + // Used in the rasterizer. Should not be used direcly. + template class rasterizer_cells_aa + { + enum cell_block_scale_e + { + cell_block_shift = 12, + cell_block_size = 1 << cell_block_shift, + cell_block_mask = cell_block_size - 1, + cell_block_pool = 256, + cell_block_limit = 1024 + }; + + struct sorted_y + { + unsigned start; + unsigned num; + }; + + public: + typedef Cell cell_type; + typedef rasterizer_cells_aa self_type; + + ~rasterizer_cells_aa(); + rasterizer_cells_aa(); + + void reset(); + void style(const cell_type& style_cell); + void line(int x1, int y1, int x2, int y2); + + int min_x() const { return m_min_x; } + int min_y() const { return m_min_y; } + int max_x() const { return m_max_x; } + int max_y() const { return m_max_y; } + + void sort_cells(); + + unsigned total_cells() const + { + return m_num_cells; + } + + unsigned scanline_num_cells(unsigned y) const + { + return m_sorted_y[y - m_min_y].num; + } + + const cell_type* const* scanline_cells(unsigned y) const + { + return m_sorted_cells.data() + m_sorted_y[y - m_min_y].start; + } + + bool sorted() const { return m_sorted; } + + private: + rasterizer_cells_aa(const self_type&); + const self_type& operator = (const self_type&); + + void set_curr_cell(int x, int y); + void add_curr_cell(); + void render_hline(int ey, int x1, int y1, int x2, int y2); + void allocate_block(); + + private: + unsigned m_num_blocks; + unsigned m_max_blocks; + unsigned m_curr_block; + unsigned m_num_cells; + cell_type** m_cells; + cell_type* m_curr_cell_ptr; + pod_vector m_sorted_cells; + pod_vector m_sorted_y; + cell_type m_curr_cell; + cell_type m_style_cell; + int m_min_x; + int m_min_y; + int m_max_x; + int m_max_y; + bool m_sorted; + }; + + + + + //------------------------------------------------------------------------ + template + rasterizer_cells_aa::~rasterizer_cells_aa() + { + if(m_num_blocks) + { + cell_type** ptr = m_cells + m_num_blocks - 1; + while(m_num_blocks--) + { + pod_allocator::deallocate(*ptr, cell_block_size); + ptr--; + } + pod_allocator::deallocate(m_cells, m_max_blocks); + } + } + + //------------------------------------------------------------------------ + template + rasterizer_cells_aa::rasterizer_cells_aa() : + m_num_blocks(0), + m_max_blocks(0), + m_curr_block(0), + m_num_cells(0), + m_cells(0), + m_curr_cell_ptr(0), + m_sorted_cells(), + m_sorted_y(), + m_min_x(std::numeric_limits::max()), + m_min_y(std::numeric_limits::max()), + m_max_x(std::numeric_limits::min()), + m_max_y(std::numeric_limits::min()), + m_sorted(false) + { + m_style_cell.initial(); + m_curr_cell.initial(); + } + + //------------------------------------------------------------------------ + template + void rasterizer_cells_aa::reset() + { + m_num_cells = 0; + m_curr_block = 0; + m_curr_cell.initial(); + m_style_cell.initial(); + m_sorted = false; + m_min_x = std::numeric_limits::max(); + m_min_y = std::numeric_limits::max(); + m_max_x = std::numeric_limits::min(); + m_max_y = std::numeric_limits::min(); + } + + //------------------------------------------------------------------------ + template + AGG_INLINE void rasterizer_cells_aa::add_curr_cell() + { + if(m_curr_cell.area | m_curr_cell.cover) + { + if((m_num_cells & cell_block_mask) == 0) + { + if(m_num_blocks >= cell_block_limit) return; + allocate_block(); + } + *m_curr_cell_ptr++ = m_curr_cell; + ++m_num_cells; + } + } + + //------------------------------------------------------------------------ + template + AGG_INLINE void rasterizer_cells_aa::set_curr_cell(int x, int y) + { + if(m_curr_cell.not_equal(x, y, m_style_cell)) + { + add_curr_cell(); + m_curr_cell.style(m_style_cell); + m_curr_cell.x = x; + m_curr_cell.y = y; + m_curr_cell.cover = 0; + m_curr_cell.area = 0; + } + } + + //------------------------------------------------------------------------ + template + AGG_INLINE void rasterizer_cells_aa::render_hline(int ey, + int x1, int y1, + int x2, int y2) + { + int ex1 = x1 >> poly_subpixel_shift; + int ex2 = x2 >> poly_subpixel_shift; + int fx1 = x1 & poly_subpixel_mask; + int fx2 = x2 & poly_subpixel_mask; + + int delta, p, first; + long long dx; + int incr, lift, mod, rem; + + //trivial case. Happens often + if(y1 == y2) + { + set_curr_cell(ex2, ey); + return; + } + + //everything is located in a single cell. That is easy! + if(ex1 == ex2) + { + delta = y2 - y1; + m_curr_cell.cover += delta; + m_curr_cell.area += (fx1 + fx2) * delta; + return; + } + + //ok, we'll have to render a run of adjacent cells on the same + //hline... + p = (poly_subpixel_scale - fx1) * (y2 - y1); + first = poly_subpixel_scale; + incr = 1; + + dx = (long long)x2 - (long long)x1; + + if(dx < 0) + { + p = fx1 * (y2 - y1); + first = 0; + incr = -1; + dx = -dx; + } + + delta = (int)(p / dx); + mod = (int)(p % dx); + + if(mod < 0) + { + delta--; + mod += static_cast(dx); + } + + m_curr_cell.cover += delta; + m_curr_cell.area += (fx1 + first) * delta; + + ex1 += incr; + set_curr_cell(ex1, ey); + y1 += delta; + + if(ex1 != ex2) + { + p = poly_subpixel_scale * (y2 - y1 + delta); + lift = (int)(p / dx); + rem = (int)(p % dx); + + if (rem < 0) + { + lift--; + rem += static_cast(dx); + } + + mod -= static_cast(dx); + + while (ex1 != ex2) + { + delta = lift; + mod += rem; + if(mod >= 0) + { + mod -= static_cast(dx); + delta++; + } + + m_curr_cell.cover += delta; + m_curr_cell.area += poly_subpixel_scale * delta; + y1 += delta; + ex1 += incr; + set_curr_cell(ex1, ey); + } + } + delta = y2 - y1; + m_curr_cell.cover += delta; + m_curr_cell.area += (fx2 + poly_subpixel_scale - first) * delta; + } + + //------------------------------------------------------------------------ + template + AGG_INLINE void rasterizer_cells_aa::style(const cell_type& style_cell) + { + m_style_cell.style(style_cell); + } + + //------------------------------------------------------------------------ + template + void rasterizer_cells_aa::line(int x1, int y1, int x2, int y2) + { + enum dx_limit_e { dx_limit = 16384 << poly_subpixel_shift }; + + long long dx = (long long)x2 - (long long)x1; + + if(dx >= dx_limit || dx <= -dx_limit) + { + int cx = (int)(((long long)x1 + (long long)x2) >> 1); + int cy = (int)(((long long)y1 + (long long)y2) >> 1); + line(x1, y1, cx, cy); + line(cx, cy, x2, y2); + } + + long long dy = (long long)y2 - (long long)y1; + int ex1 = x1 >> poly_subpixel_shift; + int ex2 = x2 >> poly_subpixel_shift; + int ey1 = y1 >> poly_subpixel_shift; + int ey2 = y2 >> poly_subpixel_shift; + int fy1 = y1 & poly_subpixel_mask; + int fy2 = y2 & poly_subpixel_mask; + + int x_from, x_to; + int rem, mod, lift, delta, first, incr; + long long p; + + if(ex1 < m_min_x) m_min_x = ex1; + if(ex1 > m_max_x) m_max_x = ex1; + if(ey1 < m_min_y) m_min_y = ey1; + if(ey1 > m_max_y) m_max_y = ey1; + if(ex2 < m_min_x) m_min_x = ex2; + if(ex2 > m_max_x) m_max_x = ex2; + if(ey2 < m_min_y) m_min_y = ey2; + if(ey2 > m_max_y) m_max_y = ey2; + + set_curr_cell(ex1, ey1); + + //everything is on a single hline + if(ey1 == ey2) + { + render_hline(ey1, x1, fy1, x2, fy2); + return; + } + + //Vertical line - we have to calculate start and end cells, + //and then - the common values of the area and coverage for + //all cells of the line. We know exactly there's only one + //cell, so, we don't have to call render_hline(). + incr = 1; + if(dx == 0) + { + int ex = x1 >> poly_subpixel_shift; + int two_fx = (x1 - (ex << poly_subpixel_shift)) << 1; + int area; + + first = poly_subpixel_scale; + if(dy < 0) + { + first = 0; + incr = -1; + } + + x_from = x1; + + //render_hline(ey1, x_from, fy1, x_from, first); + delta = first - fy1; + m_curr_cell.cover += delta; + m_curr_cell.area += two_fx * delta; + + ey1 += incr; + set_curr_cell(ex, ey1); + + delta = first + first - poly_subpixel_scale; + area = two_fx * delta; + while(ey1 != ey2) + { + //render_hline(ey1, x_from, poly_subpixel_scale - first, x_from, first); + m_curr_cell.cover = delta; + m_curr_cell.area = area; + ey1 += incr; + set_curr_cell(ex, ey1); + } + //render_hline(ey1, x_from, poly_subpixel_scale - first, x_from, fy2); + delta = fy2 - poly_subpixel_scale + first; + m_curr_cell.cover += delta; + m_curr_cell.area += two_fx * delta; + return; + } + + //ok, we have to render several hlines + p = (poly_subpixel_scale - fy1) * dx; + first = poly_subpixel_scale; + + if(dy < 0) + { + p = fy1 * dx; + first = 0; + incr = -1; + dy = -dy; + } + + delta = (int)(p / dy); + mod = (int)(p % dy); + + if(mod < 0) + { + delta--; + mod += static_cast(dy); + } + + x_from = x1 + delta; + render_hline(ey1, x1, fy1, x_from, first); + + ey1 += incr; + set_curr_cell(x_from >> poly_subpixel_shift, ey1); + + if(ey1 != ey2) + { + p = poly_subpixel_scale * dx; + lift = (int)(p / dy); + rem = (int)(p % dy); + + if(rem < 0) + { + lift--; + rem += static_cast(dy); + } + mod -= static_cast(dy); + + while(ey1 != ey2) + { + delta = lift; + mod += rem; + if (mod >= 0) + { + mod -= static_cast(dy); + delta++; + } + + x_to = x_from + delta; + render_hline(ey1, x_from, poly_subpixel_scale - first, x_to, first); + x_from = x_to; + + ey1 += incr; + set_curr_cell(x_from >> poly_subpixel_shift, ey1); + } + } + render_hline(ey1, x_from, poly_subpixel_scale - first, x2, fy2); + } + + //------------------------------------------------------------------------ + template + void rasterizer_cells_aa::allocate_block() + { + if(m_curr_block >= m_num_blocks) + { + if(m_num_blocks >= m_max_blocks) + { + cell_type** new_cells = + pod_allocator::allocate(m_max_blocks + + cell_block_pool); + + if(m_cells) + { + memcpy(new_cells, m_cells, m_max_blocks * sizeof(cell_type*)); + pod_allocator::deallocate(m_cells, m_max_blocks); + } + m_cells = new_cells; + m_max_blocks += cell_block_pool; + } + + m_cells[m_num_blocks++] = + pod_allocator::allocate(cell_block_size); + + } + m_curr_cell_ptr = m_cells[m_curr_block++]; + } + + + + //------------------------------------------------------------------------ + template static AGG_INLINE void swap_cells(T* a, T* b) + { + T temp = *a; + *a = *b; + *b = temp; + } + + + //------------------------------------------------------------------------ + enum + { + qsort_threshold = 9 + }; + + + //------------------------------------------------------------------------ + template + void qsort_cells(Cell** start, unsigned num) + { + Cell** stack[80]; + Cell*** top; + Cell** limit; + Cell** base; + + limit = start + num; + base = start; + top = stack; + + for (;;) + { + int len = int(limit - base); + + Cell** i; + Cell** j; + Cell** pivot; + + if(len > qsort_threshold) + { + // we use base + len/2 as the pivot + pivot = base + len / 2; + swap_cells(base, pivot); + + i = base + 1; + j = limit - 1; + + // now ensure that *i <= *base <= *j + if((*j)->x < (*i)->x) + { + swap_cells(i, j); + } + + if((*base)->x < (*i)->x) + { + swap_cells(base, i); + } + + if((*j)->x < (*base)->x) + { + swap_cells(base, j); + } + + for(;;) + { + int x = (*base)->x; + do i++; while( (*i)->x < x ); + do j--; while( x < (*j)->x ); + + if(i > j) + { + break; + } + + swap_cells(i, j); + } + + swap_cells(base, j); + + // now, push the largest sub-array + if(j - base > limit - i) + { + top[0] = base; + top[1] = j; + base = i; + } + else + { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } + else + { + // the sub-array is small, perform insertion sort + j = base; + i = j + 1; + + for(; i < limit; j = i, i++) + { + for(; j[1]->x < (*j)->x; j--) + { + swap_cells(j + 1, j); + if (j == base) + { + break; + } + } + } + + if(top > stack) + { + top -= 2; + base = top[0]; + limit = top[1]; + } + else + { + break; + } + } + } + } + + + //------------------------------------------------------------------------ + template + void rasterizer_cells_aa::sort_cells() + { + if(m_sorted) return; //Perform sort only the first time. + + add_curr_cell(); + m_curr_cell.x = std::numeric_limits::max(); + m_curr_cell.y = std::numeric_limits::max(); + m_curr_cell.cover = 0; + m_curr_cell.area = 0; + + if(m_num_cells == 0) return; + +// DBG: Check to see if min/max works well. +//for(unsigned nc = 0; nc < m_num_cells; nc++) +//{ +// cell_type* cell = m_cells[nc >> cell_block_shift] + (nc & cell_block_mask); +// if(cell->x < m_min_x || +// cell->y < m_min_y || +// cell->x > m_max_x || +// cell->y > m_max_y) +// { +// cell = cell; // Breakpoint here +// } +//} + // Allocate the array of cell pointers + m_sorted_cells.allocate(m_num_cells, 16); + + // Allocate and zero the Y array + m_sorted_y.allocate(m_max_y - m_min_y + 1, 16); + m_sorted_y.zero(); + + // Create the Y-histogram (count the numbers of cells for each Y) + cell_type** block_ptr = m_cells; + cell_type* cell_ptr; + unsigned nb = m_num_cells; + unsigned i; + while(nb) + { + cell_ptr = *block_ptr++; + i = (nb > cell_block_size) ? unsigned(cell_block_size) : nb; + nb -= i; + while(i--) + { + m_sorted_y[cell_ptr->y - m_min_y].start++; + ++cell_ptr; + } + } + + // Convert the Y-histogram into the array of starting indexes + unsigned start = 0; + for(i = 0; i < m_sorted_y.size(); i++) + { + unsigned v = m_sorted_y[i].start; + m_sorted_y[i].start = start; + start += v; + } + + // Fill the cell pointer array sorted by Y + block_ptr = m_cells; + nb = m_num_cells; + while(nb) + { + cell_ptr = *block_ptr++; + i = (nb > cell_block_size) ? unsigned(cell_block_size) : nb; + nb -= i; + while(i--) + { + sorted_y& curr_y = m_sorted_y[cell_ptr->y - m_min_y]; + m_sorted_cells[curr_y.start + curr_y.num] = cell_ptr; + ++curr_y.num; + ++cell_ptr; + } + } + + // Finally arrange the X-arrays + for(i = 0; i < m_sorted_y.size(); i++) + { + const sorted_y& curr_y = m_sorted_y[i]; + if(curr_y.num) + { + qsort_cells(m_sorted_cells.data() + curr_y.start, curr_y.num); + } + } + m_sorted = true; + } + + + + //------------------------------------------------------scanline_hit_test + class scanline_hit_test + { + public: + scanline_hit_test(int x) : m_x(x), m_hit(false) {} + + void reset_spans() {} + void finalize(int) {} + void add_cell(int x, int) + { + if(m_x == x) m_hit = true; + } + void add_span(int x, int len, int) + { + if(m_x >= x && m_x < x+len) m_hit = true; + } + unsigned num_spans() const { return 1; } + bool hit() const { return m_hit; } + + private: + int m_x; + bool m_hit; + }; + + +} + +#endif diff --git a/src/agg/agg_rasterizer_scanline_aa.h b/src/agg/agg_rasterizer_scanline_aa.h new file mode 100644 index 0000000000..ffc2ddf941 --- /dev/null +++ b/src/agg/agg_rasterizer_scanline_aa.h @@ -0,0 +1,481 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// +// The author gratefully acknowleges the support of David Turner, +// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType +// libray - in producing this work. See http://www.freetype.org for details. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +#ifndef AGG_RASTERIZER_SCANLINE_AA_INCLUDED +#define AGG_RASTERIZER_SCANLINE_AA_INCLUDED + +#include "agg_rasterizer_cells_aa.h" +#include "agg_rasterizer_sl_clip.h" +#include "agg_rasterizer_scanline_aa_nogamma.h" +#include "agg_gamma_functions.h" + + +namespace agg +{ + //==================================================rasterizer_scanline_aa + // Polygon rasterizer that is used to render filled polygons with + // high-quality Anti-Aliasing. Internally, by default, the class uses + // integer coordinates in format 24.8, i.e. 24 bits for integer part + // and 8 bits for fractional - see poly_subpixel_shift. This class can be + // used in the following way: + // + // 1. filling_rule(filling_rule_e ft) - optional. + // + // 2. gamma() - optional. + // + // 3. reset() + // + // 4. move_to(x, y) / line_to(x, y) - make the polygon. One can create + // more than one contour, but each contour must consist of at least 3 + // vertices, i.e. move_to(x1, y1); line_to(x2, y2); line_to(x3, y3); + // is the absolute minimum of vertices that define a triangle. + // The algorithm does not check either the number of vertices nor + // coincidence of their coordinates, but in the worst case it just + // won't draw anything. + // The orger of the vertices (clockwise or counterclockwise) + // is important when using the non-zero filling rule (fill_non_zero). + // In this case the vertex order of all the contours must be the same + // if you want your intersecting polygons to be without "holes". + // You actually can use different vertices order. If the contours do not + // intersect each other the order is not important anyway. If they do, + // contours with the same vertex order will be rendered without "holes" + // while the intersecting contours with different orders will have "holes". + // + // filling_rule() and gamma() can be called anytime before "sweeping". + //------------------------------------------------------------------------ + template class rasterizer_scanline_aa + { + enum status + { + status_initial, + status_move_to, + status_line_to, + status_closed + }; + + public: + typedef Clip clip_type; + typedef typename Clip::conv_type conv_type; + typedef typename Clip::coord_type coord_type; + + enum aa_scale_e + { + aa_shift = 8, + aa_scale = 1 << aa_shift, + aa_mask = aa_scale - 1, + aa_scale2 = aa_scale * 2, + aa_mask2 = aa_scale2 - 1 + }; + + //-------------------------------------------------------------------- + rasterizer_scanline_aa() : + m_outline(), + m_clipper(), + m_filling_rule(fill_non_zero), + m_auto_close(true), + m_start_x(0), + m_start_y(0), + m_status(status_initial) + { + int i; + for(i = 0; i < aa_scale; i++) m_gamma[i] = i; + } + + //-------------------------------------------------------------------- + template + rasterizer_scanline_aa(const GammaF& gamma_function) : + m_outline(), + m_clipper(m_outline), + m_filling_rule(fill_non_zero), + m_auto_close(true), + m_start_x(0), + m_start_y(0), + m_status(status_initial) + { + gamma(gamma_function); + } + + //-------------------------------------------------------------------- + void reset(); + void reset_clipping(); + void clip_box(double x1, double y1, double x2, double y2); + void filling_rule(filling_rule_e filling_rule); + void auto_close(bool flag) { m_auto_close = flag; } + + //-------------------------------------------------------------------- + template void gamma(const GammaF& gamma_function) + { + int i; + for(i = 0; i < aa_scale; i++) + { + m_gamma[i] = uround(gamma_function(double(i) / aa_mask) * aa_mask); + } + } + + //-------------------------------------------------------------------- + unsigned apply_gamma(unsigned cover) const + { + return m_gamma[cover]; + } + + //-------------------------------------------------------------------- + void move_to(int x, int y); + void line_to(int x, int y); + void move_to_d(double x, double y); + void line_to_d(double x, double y); + void close_polygon(); + void add_vertex(double x, double y, unsigned cmd); + + void edge(int x1, int y1, int x2, int y2); + void edge_d(double x1, double y1, double x2, double y2); + + //------------------------------------------------------------------- + template + void add_path(VertexSource& vs, unsigned path_id=0) + { + double x; + double y; + + unsigned cmd; + vs.rewind(path_id); + if(m_outline.sorted()) reset(); + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + add_vertex(x, y, cmd); + } + } + + //-------------------------------------------------------------------- + int min_x() const { return m_outline.min_x(); } + int min_y() const { return m_outline.min_y(); } + int max_x() const { return m_outline.max_x(); } + int max_y() const { return m_outline.max_y(); } + + //-------------------------------------------------------------------- + void sort(); + bool rewind_scanlines(); + bool navigate_scanline(int y); + + //-------------------------------------------------------------------- + AGG_INLINE unsigned calculate_alpha(int area) const + { + int cover = area >> (poly_subpixel_shift*2 + 1 - aa_shift); + + if(cover < 0) cover = -cover; + if(m_filling_rule == fill_even_odd) + { + cover &= aa_mask2; + if(cover > aa_scale) + { + cover = aa_scale2 - cover; + } + } + if(cover > aa_mask) cover = aa_mask; + return m_gamma[cover]; + } + + //-------------------------------------------------------------------- + template bool sweep_scanline(Scanline& sl) + { + for(;;) + { + if(m_scan_y > m_outline.max_y()) return false; + sl.reset_spans(); + unsigned num_cells = m_outline.scanline_num_cells(m_scan_y); + const cell_aa* const* cells = m_outline.scanline_cells(m_scan_y); + int cover = 0; + + while(num_cells) + { + const cell_aa* cur_cell = *cells; + int x = cur_cell->x; + int area = cur_cell->area; + unsigned alpha; + + cover += cur_cell->cover; + + //accumulate all cells with the same X + while(--num_cells) + { + cur_cell = *++cells; + if(cur_cell->x != x) break; + area += cur_cell->area; + cover += cur_cell->cover; + } + + if(area) + { + alpha = calculate_alpha((cover << (poly_subpixel_shift + 1)) - area); + if(alpha) + { + sl.add_cell(x, alpha); + } + x++; + } + + if(num_cells && cur_cell->x > x) + { + alpha = calculate_alpha(cover << (poly_subpixel_shift + 1)); + if(alpha) + { + sl.add_span(x, cur_cell->x - x, alpha); + } + } + } + + if(sl.num_spans()) break; + ++m_scan_y; + } + + sl.finalize(m_scan_y); + ++m_scan_y; + return true; + } + + //-------------------------------------------------------------------- + bool hit_test(int tx, int ty); + + + private: + //-------------------------------------------------------------------- + // Disable copying + rasterizer_scanline_aa(const rasterizer_scanline_aa&); + const rasterizer_scanline_aa& + operator = (const rasterizer_scanline_aa&); + + private: + rasterizer_cells_aa m_outline; + clip_type m_clipper; + int m_gamma[aa_scale]; + filling_rule_e m_filling_rule; + bool m_auto_close; + coord_type m_start_x; + coord_type m_start_y; + unsigned m_status; + int m_scan_y; + }; + + + + + + + + + + + + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::reset() + { + m_outline.reset(); + m_status = status_initial; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::filling_rule(filling_rule_e filling_rule) + { + m_filling_rule = filling_rule; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::clip_box(double x1, double y1, + double x2, double y2) + { + reset(); + m_clipper.clip_box(conv_type::upscale(x1), conv_type::upscale(y1), + conv_type::upscale(x2), conv_type::upscale(y2)); + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::reset_clipping() + { + reset(); + m_clipper.reset_clipping(); + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::close_polygon() + { + if(m_status == status_line_to) + { + m_clipper.line_to(m_outline, m_start_x, m_start_y); + m_status = status_closed; + } + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::move_to(int x, int y) + { + if(m_outline.sorted()) reset(); + if(m_auto_close) close_polygon(); + m_clipper.move_to(m_start_x = conv_type::downscale(x), + m_start_y = conv_type::downscale(y)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::line_to(int x, int y) + { + m_clipper.line_to(m_outline, + conv_type::downscale(x), + conv_type::downscale(y)); + m_status = status_line_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::move_to_d(double x, double y) + { + if(m_outline.sorted()) reset(); + if(m_auto_close) close_polygon(); + m_clipper.move_to(m_start_x = conv_type::upscale(x), + m_start_y = conv_type::upscale(y)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::line_to_d(double x, double y) + { + m_clipper.line_to(m_outline, + conv_type::upscale(x), + conv_type::upscale(y)); + m_status = status_line_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::add_vertex(double x, double y, unsigned cmd) + { + if(is_move_to(cmd)) + { + move_to_d(x, y); + } + else + if(is_vertex(cmd)) + { + line_to_d(x, y); + } + else + if(is_close(cmd)) + { + close_polygon(); + } + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::edge(int x1, int y1, int x2, int y2) + { + if(m_outline.sorted()) reset(); + m_clipper.move_to(conv_type::downscale(x1), conv_type::downscale(y1)); + m_clipper.line_to(m_outline, + conv_type::downscale(x2), + conv_type::downscale(y2)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::edge_d(double x1, double y1, + double x2, double y2) + { + if(m_outline.sorted()) reset(); + m_clipper.move_to(conv_type::upscale(x1), conv_type::upscale(y1)); + m_clipper.line_to(m_outline, + conv_type::upscale(x2), + conv_type::upscale(y2)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::sort() + { + if(m_auto_close) close_polygon(); + m_outline.sort_cells(); + } + + //------------------------------------------------------------------------ + template + AGG_INLINE bool rasterizer_scanline_aa::rewind_scanlines() + { + if(m_auto_close) close_polygon(); + m_outline.sort_cells(); + if(m_outline.total_cells() == 0) + { + return false; + } + m_scan_y = m_outline.min_y(); + return true; + } + + + //------------------------------------------------------------------------ + template + AGG_INLINE bool rasterizer_scanline_aa::navigate_scanline(int y) + { + if(m_auto_close) close_polygon(); + m_outline.sort_cells(); + if(m_outline.total_cells() == 0 || + y < m_outline.min_y() || + y > m_outline.max_y()) + { + return false; + } + m_scan_y = y; + return true; + } + + //------------------------------------------------------------------------ + template + bool rasterizer_scanline_aa::hit_test(int tx, int ty) + { + if(!navigate_scanline(ty)) return false; + scanline_hit_test sl(tx); + sweep_scanline(sl); + return sl.hit(); + } + + + +} + + + +#endif + diff --git a/src/agg/agg_rasterizer_scanline_aa_nogamma.h b/src/agg/agg_rasterizer_scanline_aa_nogamma.h new file mode 100644 index 0000000000..9a809aa5ae --- /dev/null +++ b/src/agg/agg_rasterizer_scanline_aa_nogamma.h @@ -0,0 +1,483 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// +// The author gratefully acknowleges the support of David Turner, +// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType +// libray - in producing this work. See http://www.freetype.org for details. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +#ifndef AGG_RASTERIZER_SCANLINE_AA_NOGAMMA_INCLUDED +#define AGG_RASTERIZER_SCANLINE_AA_NOGAMMA_INCLUDED + +#include +#include "agg_rasterizer_cells_aa.h" +#include "agg_rasterizer_sl_clip.h" + + +namespace agg +{ + + + //-----------------------------------------------------------------cell_aa + // A pixel cell. There're no constructors defined and it was done + // intentionally in order to avoid extra overhead when allocating an + // array of cells. + struct cell_aa + { + int x; + int y; + int cover; + int area; + + void initial() + { + x = std::numeric_limits::max(); + y = std::numeric_limits::max(); + cover = 0; + area = 0; + } + + void style(const cell_aa&) {} + + int not_equal(int ex, int ey, const cell_aa&) const + { + return ((unsigned)ex - (unsigned)x) | ((unsigned)ey - (unsigned)y); + } + }; + + + //==================================================rasterizer_scanline_aa_nogamma + // Polygon rasterizer that is used to render filled polygons with + // high-quality Anti-Aliasing. Internally, by default, the class uses + // integer coordinates in format 24.8, i.e. 24 bits for integer part + // and 8 bits for fractional - see poly_subpixel_shift. This class can be + // used in the following way: + // + // 1. filling_rule(filling_rule_e ft) - optional. + // + // 2. gamma() - optional. + // + // 3. reset() + // + // 4. move_to(x, y) / line_to(x, y) - make the polygon. One can create + // more than one contour, but each contour must consist of at least 3 + // vertices, i.e. move_to(x1, y1); line_to(x2, y2); line_to(x3, y3); + // is the absolute minimum of vertices that define a triangle. + // The algorithm does not check either the number of vertices nor + // coincidence of their coordinates, but in the worst case it just + // won't draw anything. + // The orger of the vertices (clockwise or counterclockwise) + // is important when using the non-zero filling rule (fill_non_zero). + // In this case the vertex order of all the contours must be the same + // if you want your intersecting polygons to be without "holes". + // You actually can use different vertices order. If the contours do not + // intersect each other the order is not important anyway. If they do, + // contours with the same vertex order will be rendered without "holes" + // while the intersecting contours with different orders will have "holes". + // + // filling_rule() and gamma() can be called anytime before "sweeping". + //------------------------------------------------------------------------ + template class rasterizer_scanline_aa_nogamma + { + enum status + { + status_initial, + status_move_to, + status_line_to, + status_closed + }; + + public: + typedef Clip clip_type; + typedef typename Clip::conv_type conv_type; + typedef typename Clip::coord_type coord_type; + + enum aa_scale_e + { + aa_shift = 8, + aa_scale = 1 << aa_shift, + aa_mask = aa_scale - 1, + aa_scale2 = aa_scale * 2, + aa_mask2 = aa_scale2 - 1 + }; + + //-------------------------------------------------------------------- + rasterizer_scanline_aa_nogamma() : + m_outline(), + m_clipper(), + m_filling_rule(fill_non_zero), + m_auto_close(true), + m_start_x(0), + m_start_y(0), + m_status(status_initial) + { + } + + //-------------------------------------------------------------------- + void reset(); + void reset_clipping(); + void clip_box(double x1, double y1, double x2, double y2); + void filling_rule(filling_rule_e filling_rule); + void auto_close(bool flag) { m_auto_close = flag; } + + //-------------------------------------------------------------------- + unsigned apply_gamma(unsigned cover) const + { + return cover; + } + + //-------------------------------------------------------------------- + void move_to(int x, int y); + void line_to(int x, int y); + void move_to_d(double x, double y); + void line_to_d(double x, double y); + void close_polygon(); + void add_vertex(double x, double y, unsigned cmd); + + void edge(int x1, int y1, int x2, int y2); + void edge_d(double x1, double y1, double x2, double y2); + + //------------------------------------------------------------------- + template + void add_path(VertexSource& vs, unsigned path_id=0) + { + double x; + double y; + + unsigned cmd; + vs.rewind(path_id); + if(m_outline.sorted()) reset(); + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + add_vertex(x, y, cmd); + } + } + + //-------------------------------------------------------------------- + int min_x() const { return m_outline.min_x(); } + int min_y() const { return m_outline.min_y(); } + int max_x() const { return m_outline.max_x(); } + int max_y() const { return m_outline.max_y(); } + + //-------------------------------------------------------------------- + void sort(); + bool rewind_scanlines(); + bool navigate_scanline(int y); + + //-------------------------------------------------------------------- + AGG_INLINE unsigned calculate_alpha(int area) const + { + int cover = area >> (poly_subpixel_shift*2 + 1 - aa_shift); + + if(cover < 0) cover = -cover; + if(m_filling_rule == fill_even_odd) + { + cover &= aa_mask2; + if(cover > aa_scale) + { + cover = aa_scale2 - cover; + } + } + if(cover > aa_mask) cover = aa_mask; + return cover; + } + + //-------------------------------------------------------------------- + template bool sweep_scanline(Scanline& sl) + { + for(;;) + { + if(m_scan_y > m_outline.max_y()) return false; + sl.reset_spans(); + unsigned num_cells = m_outline.scanline_num_cells(m_scan_y); + const cell_aa* const* cells = m_outline.scanline_cells(m_scan_y); + int cover = 0; + + while(num_cells) + { + const cell_aa* cur_cell = *cells; + int x = cur_cell->x; + int area = cur_cell->area; + unsigned alpha; + + cover += cur_cell->cover; + + //accumulate all cells with the same X + while(--num_cells) + { + cur_cell = *++cells; + if(cur_cell->x != x) break; + area += cur_cell->area; + cover += cur_cell->cover; + } + + if(area) + { + alpha = calculate_alpha((cover << (poly_subpixel_shift + 1)) - area); + if(alpha) + { + sl.add_cell(x, alpha); + } + x++; + } + + if(num_cells && cur_cell->x > x) + { + alpha = calculate_alpha(cover << (poly_subpixel_shift + 1)); + if(alpha) + { + sl.add_span(x, cur_cell->x - x, alpha); + } + } + } + + if(sl.num_spans()) break; + ++m_scan_y; + } + + sl.finalize(m_scan_y); + ++m_scan_y; + return true; + } + + //-------------------------------------------------------------------- + bool hit_test(int tx, int ty); + + + private: + //-------------------------------------------------------------------- + // Disable copying + rasterizer_scanline_aa_nogamma(const rasterizer_scanline_aa_nogamma&); + const rasterizer_scanline_aa_nogamma& + operator = (const rasterizer_scanline_aa_nogamma&); + + private: + rasterizer_cells_aa m_outline; + clip_type m_clipper; + filling_rule_e m_filling_rule; + bool m_auto_close; + coord_type m_start_x; + coord_type m_start_y; + unsigned m_status; + int m_scan_y; + }; + + + + + + + + + + + + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::reset() + { + m_outline.reset(); + m_status = status_initial; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::filling_rule(filling_rule_e filling_rule) + { + m_filling_rule = filling_rule; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::clip_box(double x1, double y1, + double x2, double y2) + { + reset(); + m_clipper.clip_box(conv_type::upscale(x1), conv_type::upscale(y1), + conv_type::upscale(x2), conv_type::upscale(y2)); + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::reset_clipping() + { + reset(); + m_clipper.reset_clipping(); + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::close_polygon() + { + if(m_status == status_line_to) + { + m_clipper.line_to(m_outline, m_start_x, m_start_y); + m_status = status_closed; + } + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::move_to(int x, int y) + { + if(m_outline.sorted()) reset(); + if(m_auto_close) close_polygon(); + m_clipper.move_to(m_start_x = conv_type::downscale(x), + m_start_y = conv_type::downscale(y)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::line_to(int x, int y) + { + m_clipper.line_to(m_outline, + conv_type::downscale(x), + conv_type::downscale(y)); + m_status = status_line_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::move_to_d(double x, double y) + { + if(m_outline.sorted()) reset(); + if(m_auto_close) close_polygon(); + m_clipper.move_to(m_start_x = conv_type::upscale(x), + m_start_y = conv_type::upscale(y)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::line_to_d(double x, double y) + { + m_clipper.line_to(m_outline, + conv_type::upscale(x), + conv_type::upscale(y)); + m_status = status_line_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::add_vertex(double x, double y, unsigned cmd) + { + if(is_move_to(cmd)) + { + move_to_d(x, y); + } + else + if(is_vertex(cmd)) + { + line_to_d(x, y); + } + else + if(is_close(cmd)) + { + close_polygon(); + } + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::edge(int x1, int y1, int x2, int y2) + { + if(m_outline.sorted()) reset(); + m_clipper.move_to(conv_type::downscale(x1), conv_type::downscale(y1)); + m_clipper.line_to(m_outline, + conv_type::downscale(x2), + conv_type::downscale(y2)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::edge_d(double x1, double y1, + double x2, double y2) + { + if(m_outline.sorted()) reset(); + m_clipper.move_to(conv_type::upscale(x1), conv_type::upscale(y1)); + m_clipper.line_to(m_outline, + conv_type::upscale(x2), + conv_type::upscale(y2)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa_nogamma::sort() + { + if(m_auto_close) close_polygon(); + m_outline.sort_cells(); + } + + //------------------------------------------------------------------------ + template + AGG_INLINE bool rasterizer_scanline_aa_nogamma::rewind_scanlines() + { + if(m_auto_close) close_polygon(); + m_outline.sort_cells(); + if(m_outline.total_cells() == 0) + { + return false; + } + m_scan_y = m_outline.min_y(); + return true; + } + + + //------------------------------------------------------------------------ + template + AGG_INLINE bool rasterizer_scanline_aa_nogamma::navigate_scanline(int y) + { + if(m_auto_close) close_polygon(); + m_outline.sort_cells(); + if(m_outline.total_cells() == 0 || + y < m_outline.min_y() || + y > m_outline.max_y()) + { + return false; + } + m_scan_y = y; + return true; + } + + //------------------------------------------------------------------------ + template + bool rasterizer_scanline_aa_nogamma::hit_test(int tx, int ty) + { + if(!navigate_scanline(ty)) return false; + scanline_hit_test sl(tx); + sweep_scanline(sl); + return sl.hit(); + } + + + +} + + + +#endif + diff --git a/src/agg/agg_rasterizer_sl_clip.h b/src/agg/agg_rasterizer_sl_clip.h new file mode 100644 index 0000000000..3a7f3a1033 --- /dev/null +++ b/src/agg/agg_rasterizer_sl_clip.h @@ -0,0 +1,351 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +#ifndef AGG_RASTERIZER_SL_CLIP_INCLUDED +#define AGG_RASTERIZER_SL_CLIP_INCLUDED + +#include "agg_clip_liang_barsky.h" + +namespace agg +{ + //--------------------------------------------------------poly_max_coord_e + enum poly_max_coord_e + { + poly_max_coord = (1 << 30) - 1 //----poly_max_coord + }; + + //------------------------------------------------------------ras_conv_int + struct ras_conv_int + { + typedef int coord_type; + static AGG_INLINE int mul_div(double a, double b, double c) + { + return iround(a * b / c); + } + static int xi(int v) { return v; } + static int yi(int v) { return v; } + static int upscale(double v) { return iround(v * poly_subpixel_scale); } + static int downscale(int v) { return v; } + }; + + //--------------------------------------------------------ras_conv_int_sat + struct ras_conv_int_sat + { + typedef int coord_type; + static AGG_INLINE int mul_div(double a, double b, double c) + { + return saturation::iround(a * b / c); + } + static int xi(int v) { return v; } + static int yi(int v) { return v; } + static int upscale(double v) + { + return saturation::iround(v * poly_subpixel_scale); + } + static int downscale(int v) { return v; } + }; + + //---------------------------------------------------------ras_conv_int_3x + struct ras_conv_int_3x + { + typedef int coord_type; + static AGG_INLINE int mul_div(double a, double b, double c) + { + return iround(a * b / c); + } + static int xi(int v) { return v * 3; } + static int yi(int v) { return v; } + static int upscale(double v) { return iround(v * poly_subpixel_scale); } + static int downscale(int v) { return v; } + }; + + //-----------------------------------------------------------ras_conv_dbl + struct ras_conv_dbl + { + typedef double coord_type; + static AGG_INLINE double mul_div(double a, double b, double c) + { + return a * b / c; + } + static int xi(double v) { return iround(v * poly_subpixel_scale); } + static int yi(double v) { return iround(v * poly_subpixel_scale); } + static double upscale(double v) { return v; } + static double downscale(int v) { return v / double(poly_subpixel_scale); } + }; + + //--------------------------------------------------------ras_conv_dbl_3x + struct ras_conv_dbl_3x + { + typedef double coord_type; + static AGG_INLINE double mul_div(double a, double b, double c) + { + return a * b / c; + } + static int xi(double v) { return iround(v * poly_subpixel_scale * 3); } + static int yi(double v) { return iround(v * poly_subpixel_scale); } + static double upscale(double v) { return v; } + static double downscale(int v) { return v / double(poly_subpixel_scale); } + }; + + + + + + //------------------------------------------------------rasterizer_sl_clip + template class rasterizer_sl_clip + { + public: + typedef Conv conv_type; + typedef typename Conv::coord_type coord_type; + typedef rect_base rect_type; + + //-------------------------------------------------------------------- + rasterizer_sl_clip() : + m_clip_box(0,0,0,0), + m_x1(0), + m_y1(0), + m_f1(0), + m_clipping(false) + {} + + //-------------------------------------------------------------------- + void reset_clipping() + { + m_clipping = false; + } + + //-------------------------------------------------------------------- + void clip_box(coord_type x1, coord_type y1, coord_type x2, coord_type y2) + { + m_clip_box = rect_type(x1, y1, x2, y2); + m_clip_box.normalize(); + m_clipping = true; + } + + //-------------------------------------------------------------------- + void move_to(coord_type x1, coord_type y1) + { + m_x1 = x1; + m_y1 = y1; + if(m_clipping) m_f1 = clipping_flags(x1, y1, m_clip_box); + } + + private: + //------------------------------------------------------------------------ + template + AGG_INLINE void line_clip_y(Rasterizer& ras, + coord_type x1, coord_type y1, + coord_type x2, coord_type y2, + unsigned f1, unsigned f2) const + { + f1 &= 10; + f2 &= 10; + if((f1 | f2) == 0) + { + // Fully visible + ras.line(Conv::xi(x1), Conv::yi(y1), Conv::xi(x2), Conv::yi(y2)); + } + else + { + if(f1 == f2) + { + // Invisible by Y + return; + } + + coord_type tx1 = x1; + coord_type ty1 = y1; + coord_type tx2 = x2; + coord_type ty2 = y2; + + if(f1 & 8) // y1 < clip.y1 + { + tx1 = x1 + Conv::mul_div(m_clip_box.y1-y1, x2-x1, y2-y1); + ty1 = m_clip_box.y1; + } + + if(f1 & 2) // y1 > clip.y2 + { + tx1 = x1 + Conv::mul_div(m_clip_box.y2-y1, x2-x1, y2-y1); + ty1 = m_clip_box.y2; + } + + if(f2 & 8) // y2 < clip.y1 + { + tx2 = x1 + Conv::mul_div(m_clip_box.y1-y1, x2-x1, y2-y1); + ty2 = m_clip_box.y1; + } + + if(f2 & 2) // y2 > clip.y2 + { + tx2 = x1 + Conv::mul_div(m_clip_box.y2-y1, x2-x1, y2-y1); + ty2 = m_clip_box.y2; + } + ras.line(Conv::xi(tx1), Conv::yi(ty1), + Conv::xi(tx2), Conv::yi(ty2)); + } + } + + + public: + //-------------------------------------------------------------------- + template + void line_to(Rasterizer& ras, coord_type x2, coord_type y2) + { + if(m_clipping) + { + unsigned f2 = clipping_flags(x2, y2, m_clip_box); + + if((m_f1 & 10) == (f2 & 10) && (m_f1 & 10) != 0) + { + // Invisible by Y + m_x1 = x2; + m_y1 = y2; + m_f1 = f2; + return; + } + + coord_type x1 = m_x1; + coord_type y1 = m_y1; + unsigned f1 = m_f1; + coord_type y3, y4; + unsigned f3, f4; + + switch(((f1 & 5) << 1) | (f2 & 5)) + { + case 0: // Visible by X + line_clip_y(ras, x1, y1, x2, y2, f1, f2); + break; + + case 1: // x2 > clip.x2 + y3 = y1 + Conv::mul_div(m_clip_box.x2-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + line_clip_y(ras, x1, y1, m_clip_box.x2, y3, f1, f3); + line_clip_y(ras, m_clip_box.x2, y3, m_clip_box.x2, y2, f3, f2); + break; + + case 2: // x1 > clip.x2 + y3 = y1 + Conv::mul_div(m_clip_box.x2-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + line_clip_y(ras, m_clip_box.x2, y1, m_clip_box.x2, y3, f1, f3); + line_clip_y(ras, m_clip_box.x2, y3, x2, y2, f3, f2); + break; + + case 3: // x1 > clip.x2 && x2 > clip.x2 + line_clip_y(ras, m_clip_box.x2, y1, m_clip_box.x2, y2, f1, f2); + break; + + case 4: // x2 < clip.x1 + y3 = y1 + Conv::mul_div(m_clip_box.x1-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + line_clip_y(ras, x1, y1, m_clip_box.x1, y3, f1, f3); + line_clip_y(ras, m_clip_box.x1, y3, m_clip_box.x1, y2, f3, f2); + break; + + case 6: // x1 > clip.x2 && x2 < clip.x1 + y3 = y1 + Conv::mul_div(m_clip_box.x2-x1, y2-y1, x2-x1); + y4 = y1 + Conv::mul_div(m_clip_box.x1-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + f4 = clipping_flags_y(y4, m_clip_box); + line_clip_y(ras, m_clip_box.x2, y1, m_clip_box.x2, y3, f1, f3); + line_clip_y(ras, m_clip_box.x2, y3, m_clip_box.x1, y4, f3, f4); + line_clip_y(ras, m_clip_box.x1, y4, m_clip_box.x1, y2, f4, f2); + break; + + case 8: // x1 < clip.x1 + y3 = y1 + Conv::mul_div(m_clip_box.x1-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + line_clip_y(ras, m_clip_box.x1, y1, m_clip_box.x1, y3, f1, f3); + line_clip_y(ras, m_clip_box.x1, y3, x2, y2, f3, f2); + break; + + case 9: // x1 < clip.x1 && x2 > clip.x2 + y3 = y1 + Conv::mul_div(m_clip_box.x1-x1, y2-y1, x2-x1); + y4 = y1 + Conv::mul_div(m_clip_box.x2-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + f4 = clipping_flags_y(y4, m_clip_box); + line_clip_y(ras, m_clip_box.x1, y1, m_clip_box.x1, y3, f1, f3); + line_clip_y(ras, m_clip_box.x1, y3, m_clip_box.x2, y4, f3, f4); + line_clip_y(ras, m_clip_box.x2, y4, m_clip_box.x2, y2, f4, f2); + break; + + case 12: // x1 < clip.x1 && x2 < clip.x1 + line_clip_y(ras, m_clip_box.x1, y1, m_clip_box.x1, y2, f1, f2); + break; + } + m_f1 = f2; + } + else + { + ras.line(Conv::xi(m_x1), Conv::yi(m_y1), + Conv::xi(x2), Conv::yi(y2)); + } + m_x1 = x2; + m_y1 = y2; + } + + + private: + rect_type m_clip_box; + coord_type m_x1; + coord_type m_y1; + unsigned m_f1; + bool m_clipping; + }; + + + + + //---------------------------------------------------rasterizer_sl_no_clip + class rasterizer_sl_no_clip + { + public: + typedef ras_conv_int conv_type; + typedef int coord_type; + + rasterizer_sl_no_clip() : m_x1(0), m_y1(0) {} + + void reset_clipping() {} + void clip_box(coord_type, coord_type, coord_type, coord_type) {} + void move_to(coord_type x1, coord_type y1) { m_x1 = x1; m_y1 = y1; } + + template + void line_to(Rasterizer& ras, coord_type x2, coord_type y2) + { + ras.line(m_x1, m_y1, x2, y2); + m_x1 = x2; + m_y1 = y2; + } + + private: + int m_x1, m_y1; + }; + + + // -----rasterizer_sl_clip_int + // -----rasterizer_sl_clip_int_sat + // -----rasterizer_sl_clip_int_3x + // -----rasterizer_sl_clip_dbl + // -----rasterizer_sl_clip_dbl_3x + //------------------------------------------------------------------------ + typedef rasterizer_sl_clip rasterizer_sl_clip_int; + typedef rasterizer_sl_clip rasterizer_sl_clip_int_sat; + typedef rasterizer_sl_clip rasterizer_sl_clip_int_3x; + typedef rasterizer_sl_clip rasterizer_sl_clip_dbl; + typedef rasterizer_sl_clip rasterizer_sl_clip_dbl_3x; + + +} + +#endif diff --git a/src/agg/agg_renderer_base.h b/src/agg/agg_renderer_base.h new file mode 100644 index 0000000000..527c62f789 --- /dev/null +++ b/src/agg/agg_renderer_base.h @@ -0,0 +1,731 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// class renderer_base +// +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERER_BASE_INCLUDED +#define AGG_RENDERER_BASE_INCLUDED + +#include "agg_basics.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + + //-----------------------------------------------------------renderer_base + template class renderer_base + { + public: + typedef PixelFormat pixfmt_type; + typedef typename pixfmt_type::color_type color_type; + typedef typename pixfmt_type::row_data row_data; + + //-------------------------------------------------------------------- + renderer_base() : m_ren(0), m_clip_box(1, 1, 0, 0) {} + explicit renderer_base(pixfmt_type& ren) : + m_ren(&ren), + m_clip_box(0, 0, ren.width() - 1, ren.height() - 1) + {} + void attach(pixfmt_type& ren) + { + m_ren = &ren; + m_clip_box = rect_i(0, 0, ren.width() - 1, ren.height() - 1); + } + + //-------------------------------------------------------------------- + const pixfmt_type& ren() const { return *m_ren; } + pixfmt_type& ren() { return *m_ren; } + + //-------------------------------------------------------------------- + unsigned width() const { return m_ren->width(); } + unsigned height() const { return m_ren->height(); } + + //-------------------------------------------------------------------- + bool clip_box(int x1, int y1, int x2, int y2) + { + rect_i cb(x1, y1, x2, y2); + cb.normalize(); + if(cb.clip(rect_i(0, 0, width() - 1, height() - 1))) + { + m_clip_box = cb; + return true; + } + m_clip_box.x1 = 1; + m_clip_box.y1 = 1; + m_clip_box.x2 = 0; + m_clip_box.y2 = 0; + return false; + } + + //-------------------------------------------------------------------- + void reset_clipping(bool visibility) + { + if(visibility) + { + m_clip_box.x1 = 0; + m_clip_box.y1 = 0; + m_clip_box.x2 = width() - 1; + m_clip_box.y2 = height() - 1; + } + else + { + m_clip_box.x1 = 1; + m_clip_box.y1 = 1; + m_clip_box.x2 = 0; + m_clip_box.y2 = 0; + } + } + + //-------------------------------------------------------------------- + void clip_box_naked(int x1, int y1, int x2, int y2) + { + m_clip_box.x1 = x1; + m_clip_box.y1 = y1; + m_clip_box.x2 = x2; + m_clip_box.y2 = y2; + } + + //-------------------------------------------------------------------- + bool inbox(int x, int y) const + { + return x >= m_clip_box.x1 && y >= m_clip_box.y1 && + x <= m_clip_box.x2 && y <= m_clip_box.y2; + } + + //-------------------------------------------------------------------- + const rect_i& clip_box() const { return m_clip_box; } + int xmin() const { return m_clip_box.x1; } + int ymin() const { return m_clip_box.y1; } + int xmax() const { return m_clip_box.x2; } + int ymax() const { return m_clip_box.y2; } + + //-------------------------------------------------------------------- + const rect_i& bounding_clip_box() const { return m_clip_box; } + int bounding_xmin() const { return m_clip_box.x1; } + int bounding_ymin() const { return m_clip_box.y1; } + int bounding_xmax() const { return m_clip_box.x2; } + int bounding_ymax() const { return m_clip_box.y2; } + + //-------------------------------------------------------------------- + void clear(const color_type& c) + { + unsigned y; + if(width()) + { + for(y = 0; y < height(); y++) + { + m_ren->copy_hline(0, y, width(), c); + } + } + } + + + //-------------------------------------------------------------------- + void fill(const color_type& c) + { + unsigned y; + if(width()) + { + for(y = 0; y < height(); y++) + { + m_ren->blend_hline(0, y, width(), c, cover_mask); + } + } + } + + //-------------------------------------------------------------------- + void copy_pixel(int x, int y, const color_type& c) + { + if(inbox(x, y)) + { + m_ren->copy_pixel(x, y, c); + } + } + + //-------------------------------------------------------------------- + void blend_pixel(int x, int y, const color_type& c, cover_type cover) + { + if(inbox(x, y)) + { + m_ren->blend_pixel(x, y, c, cover); + } + } + + //-------------------------------------------------------------------- + color_type pixel(int x, int y) const + { + return inbox(x, y) ? + m_ren->pixel(x, y) : + color_type::no_color(); + } + + //-------------------------------------------------------------------- + void copy_hline(int x1, int y, int x2, const color_type& c) + { + if(x1 > x2) { int t = x2; x2 = x1; x1 = t; } + if(y > ymax()) return; + if(y < ymin()) return; + if(x1 > xmax()) return; + if(x2 < xmin()) return; + + if(x1 < xmin()) x1 = xmin(); + if(x2 > xmax()) x2 = xmax(); + + m_ren->copy_hline(x1, y, x2 - x1 + 1, c); + } + + //-------------------------------------------------------------------- + void copy_vline(int x, int y1, int y2, const color_type& c) + { + if(y1 > y2) { int t = y2; y2 = y1; y1 = t; } + if(x > xmax()) return; + if(x < xmin()) return; + if(y1 > ymax()) return; + if(y2 < ymin()) return; + + if(y1 < ymin()) y1 = ymin(); + if(y2 > ymax()) y2 = ymax(); + + m_ren->copy_vline(x, y1, y2 - y1 + 1, c); + } + + //-------------------------------------------------------------------- + void blend_hline(int x1, int y, int x2, + const color_type& c, cover_type cover) + { + if(x1 > x2) { int t = x2; x2 = x1; x1 = t; } + if(y > ymax()) return; + if(y < ymin()) return; + if(x1 > xmax()) return; + if(x2 < xmin()) return; + + if(x1 < xmin()) x1 = xmin(); + if(x2 > xmax()) x2 = xmax(); + + m_ren->blend_hline(x1, y, x2 - x1 + 1, c, cover); + } + + //-------------------------------------------------------------------- + void blend_vline(int x, int y1, int y2, + const color_type& c, cover_type cover) + { + if(y1 > y2) { int t = y2; y2 = y1; y1 = t; } + if(x > xmax()) return; + if(x < xmin()) return; + if(y1 > ymax()) return; + if(y2 < ymin()) return; + + if(y1 < ymin()) y1 = ymin(); + if(y2 > ymax()) y2 = ymax(); + + m_ren->blend_vline(x, y1, y2 - y1 + 1, c, cover); + } + + + //-------------------------------------------------------------------- + void copy_bar(int x1, int y1, int x2, int y2, const color_type& c) + { + rect_i rc(x1, y1, x2, y2); + rc.normalize(); + if(rc.clip(clip_box())) + { + int y; + for(y = rc.y1; y <= rc.y2; y++) + { + m_ren->copy_hline(rc.x1, y, unsigned(rc.x2 - rc.x1 + 1), c); + } + } + } + + //-------------------------------------------------------------------- + void blend_bar(int x1, int y1, int x2, int y2, + const color_type& c, cover_type cover) + { + rect_i rc(x1, y1, x2, y2); + rc.normalize(); + if(rc.clip(clip_box())) + { + int y; + for(y = rc.y1; y <= rc.y2; y++) + { + m_ren->blend_hline(rc.x1, + y, + unsigned(rc.x2 - rc.x1 + 1), + c, + cover); + } + } + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, int len, + const color_type& c, + const cover_type* covers) + { + if(y > ymax()) return; + if(y < ymin()) return; + + if(x < xmin()) + { + len -= xmin() - x; + if(len <= 0) return; + covers += xmin() - x; + x = xmin(); + } + if(x + len > xmax()) + { + len = xmax() - x + 1; + if(len <= 0) return; + } + m_ren->blend_solid_hspan(x, y, len, c, covers); + } + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, int len, + const color_type& c, + const cover_type* covers) + { + if(x > xmax()) return; + if(x < xmin()) return; + + if(y < ymin()) + { + len -= ymin() - y; + if(len <= 0) return; + covers += ymin() - y; + y = ymin(); + } + if(y + len > ymax()) + { + len = ymax() - y + 1; + if(len <= 0) return; + } + m_ren->blend_solid_vspan(x, y, len, c, covers); + } + + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, int len, const color_type* colors) + { + if(y > ymax()) return; + if(y < ymin()) return; + + if(x < xmin()) + { + int d = xmin() - x; + len -= d; + if(len <= 0) return; + colors += d; + x = xmin(); + } + if(x + len > xmax()) + { + len = xmax() - x + 1; + if(len <= 0) return; + } + m_ren->copy_color_hspan(x, y, len, colors); + } + + + //-------------------------------------------------------------------- + void copy_color_vspan(int x, int y, int len, const color_type* colors) + { + if(x > xmax()) return; + if(x < xmin()) return; + + if(y < ymin()) + { + int d = ymin() - y; + len -= d; + if(len <= 0) return; + colors += d; + y = ymin(); + } + if(y + len > ymax()) + { + len = ymax() - y + 1; + if(len <= 0) return; + } + m_ren->copy_color_vspan(x, y, len, colors); + } + + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, int len, + const color_type* colors, + const cover_type* covers, + cover_type cover = agg::cover_full) + { + if(y > ymax()) return; + if(y < ymin()) return; + + if(x < xmin()) + { + int d = xmin() - x; + len -= d; + if(len <= 0) return; + if(covers) covers += d; + colors += d; + x = xmin(); + } + if(x + len > xmax()) + { + len = xmax() - x + 1; + if(len <= 0) return; + } + m_ren->blend_color_hspan(x, y, len, colors, covers, cover); + } + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, int len, + const color_type* colors, + const cover_type* covers, + cover_type cover = agg::cover_full) + { + if(x > xmax()) return; + if(x < xmin()) return; + + if(y < ymin()) + { + int d = ymin() - y; + len -= d; + if(len <= 0) return; + if(covers) covers += d; + colors += d; + y = ymin(); + } + if(y + len > ymax()) + { + len = ymax() - y + 1; + if(len <= 0) return; + } + m_ren->blend_color_vspan(x, y, len, colors, covers, cover); + } + + //-------------------------------------------------------------------- + rect_i clip_rect_area(rect_i& dst, rect_i& src, int wsrc, int hsrc) const + { + rect_i rc(0,0,0,0); + rect_i cb = clip_box(); + ++cb.x2; + ++cb.y2; + + if(src.x1 < 0) + { + dst.x1 -= src.x1; + src.x1 = 0; + } + if(src.y1 < 0) + { + dst.y1 -= src.y1; + src.y1 = 0; + } + + if(src.x2 > wsrc) src.x2 = wsrc; + if(src.y2 > hsrc) src.y2 = hsrc; + + if(dst.x1 < cb.x1) + { + src.x1 += cb.x1 - dst.x1; + dst.x1 = cb.x1; + } + if(dst.y1 < cb.y1) + { + src.y1 += cb.y1 - dst.y1; + dst.y1 = cb.y1; + } + + if(dst.x2 > cb.x2) dst.x2 = cb.x2; + if(dst.y2 > cb.y2) dst.y2 = cb.y2; + + rc.x2 = dst.x2 - dst.x1; + rc.y2 = dst.y2 - dst.y1; + + if(rc.x2 > src.x2 - src.x1) rc.x2 = src.x2 - src.x1; + if(rc.y2 > src.y2 - src.y1) rc.y2 = src.y2 - src.y1; + return rc; + } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf& src, + const rect_i* rect_src_ptr = 0, + int dx = 0, + int dy = 0) + { + rect_i rsrc(0, 0, src.width(), src.height()); + if(rect_src_ptr) + { + rsrc.x1 = rect_src_ptr->x1; + rsrc.y1 = rect_src_ptr->y1; + rsrc.x2 = rect_src_ptr->x2 + 1; + rsrc.y2 = rect_src_ptr->y2 + 1; + } + + // Version with xdst, ydst (absolute positioning) + //rect_i rdst(xdst, ydst, xdst + rsrc.x2 - rsrc.x1, ydst + rsrc.y2 - rsrc.y1); + + // Version with dx, dy (relative positioning) + rect_i rdst(rsrc.x1 + dx, rsrc.y1 + dy, rsrc.x2 + dx, rsrc.y2 + dy); + + rect_i rc = clip_rect_area(rdst, rsrc, src.width(), src.height()); + + if(rc.x2 > 0) + { + int incy = 1; + if(rdst.y1 > rsrc.y1) + { + rsrc.y1 += rc.y2 - 1; + rdst.y1 += rc.y2 - 1; + incy = -1; + } + while(rc.y2 > 0) + { + m_ren->copy_from(src, + rdst.x1, rdst.y1, + rsrc.x1, rsrc.y1, + rc.x2); + rdst.y1 += incy; + rsrc.y1 += incy; + --rc.y2; + } + } + } + + //-------------------------------------------------------------------- + template + void blend_from(const SrcPixelFormatRenderer& src, + const rect_i* rect_src_ptr = 0, + int dx = 0, + int dy = 0, + cover_type cover = agg::cover_full) + { + rect_i rsrc(0, 0, src.width(), src.height()); + if(rect_src_ptr) + { + rsrc.x1 = rect_src_ptr->x1; + rsrc.y1 = rect_src_ptr->y1; + rsrc.x2 = rect_src_ptr->x2 + 1; + rsrc.y2 = rect_src_ptr->y2 + 1; + } + + // Version with xdst, ydst (absolute positioning) + //rect_i rdst(xdst, ydst, xdst + rsrc.x2 - rsrc.x1, ydst + rsrc.y2 - rsrc.y1); + + // Version with dx, dy (relative positioning) + rect_i rdst(rsrc.x1 + dx, rsrc.y1 + dy, rsrc.x2 + dx, rsrc.y2 + dy); + rect_i rc = clip_rect_area(rdst, rsrc, src.width(), src.height()); + + if(rc.x2 > 0) + { + int incy = 1; + if(rdst.y1 > rsrc.y1) + { + rsrc.y1 += rc.y2 - 1; + rdst.y1 += rc.y2 - 1; + incy = -1; + } + while(rc.y2 > 0) + { + typename SrcPixelFormatRenderer::row_data rw = src.row(rsrc.y1); + if(rw.ptr) + { + int x1src = rsrc.x1; + int x1dst = rdst.x1; + int len = rc.x2; + if(rw.x1 > x1src) + { + x1dst += rw.x1 - x1src; + len -= rw.x1 - x1src; + x1src = rw.x1; + } + if(len > 0) + { + if(x1src + len-1 > rw.x2) + { + len -= x1src + len - rw.x2 - 1; + } + if(len > 0) + { + m_ren->blend_from(src, + x1dst, rdst.y1, + x1src, rsrc.y1, + len, + cover); + } + } + } + rdst.y1 += incy; + rsrc.y1 += incy; + --rc.y2; + } + } + } + + //-------------------------------------------------------------------- + template + void blend_from_color(const SrcPixelFormatRenderer& src, + const color_type& color, + const rect_i* rect_src_ptr = 0, + int dx = 0, + int dy = 0, + cover_type cover = agg::cover_full) + { + rect_i rsrc(0, 0, src.width(), src.height()); + if(rect_src_ptr) + { + rsrc.x1 = rect_src_ptr->x1; + rsrc.y1 = rect_src_ptr->y1; + rsrc.x2 = rect_src_ptr->x2 + 1; + rsrc.y2 = rect_src_ptr->y2 + 1; + } + + // Version with xdst, ydst (absolute positioning) + //rect_i rdst(xdst, ydst, xdst + rsrc.x2 - rsrc.x1, ydst + rsrc.y2 - rsrc.y1); + + // Version with dx, dy (relative positioning) + rect_i rdst(rsrc.x1 + dx, rsrc.y1 + dy, rsrc.x2 + dx, rsrc.y2 + dy); + rect_i rc = clip_rect_area(rdst, rsrc, src.width(), src.height()); + + if(rc.x2 > 0) + { + int incy = 1; + if(rdst.y1 > rsrc.y1) + { + rsrc.y1 += rc.y2 - 1; + rdst.y1 += rc.y2 - 1; + incy = -1; + } + while(rc.y2 > 0) + { + typename SrcPixelFormatRenderer::row_data rw = src.row(rsrc.y1); + if(rw.ptr) + { + int x1src = rsrc.x1; + int x1dst = rdst.x1; + int len = rc.x2; + if(rw.x1 > x1src) + { + x1dst += rw.x1 - x1src; + len -= rw.x1 - x1src; + x1src = rw.x1; + } + if(len > 0) + { + if(x1src + len-1 > rw.x2) + { + len -= x1src + len - rw.x2 - 1; + } + if(len > 0) + { + m_ren->blend_from_color(src, + color, + x1dst, rdst.y1, + x1src, rsrc.y1, + len, + cover); + } + } + } + rdst.y1 += incy; + rsrc.y1 += incy; + --rc.y2; + } + } + } + + //-------------------------------------------------------------------- + template + void blend_from_lut(const SrcPixelFormatRenderer& src, + const color_type* color_lut, + const rect_i* rect_src_ptr = 0, + int dx = 0, + int dy = 0, + cover_type cover = agg::cover_full) + { + rect_i rsrc(0, 0, src.width(), src.height()); + if(rect_src_ptr) + { + rsrc.x1 = rect_src_ptr->x1; + rsrc.y1 = rect_src_ptr->y1; + rsrc.x2 = rect_src_ptr->x2 + 1; + rsrc.y2 = rect_src_ptr->y2 + 1; + } + + // Version with xdst, ydst (absolute positioning) + //rect_i rdst(xdst, ydst, xdst + rsrc.x2 - rsrc.x1, ydst + rsrc.y2 - rsrc.y1); + + // Version with dx, dy (relative positioning) + rect_i rdst(rsrc.x1 + dx, rsrc.y1 + dy, rsrc.x2 + dx, rsrc.y2 + dy); + rect_i rc = clip_rect_area(rdst, rsrc, src.width(), src.height()); + + if(rc.x2 > 0) + { + int incy = 1; + if(rdst.y1 > rsrc.y1) + { + rsrc.y1 += rc.y2 - 1; + rdst.y1 += rc.y2 - 1; + incy = -1; + } + while(rc.y2 > 0) + { + typename SrcPixelFormatRenderer::row_data rw = src.row(rsrc.y1); + if(rw.ptr) + { + int x1src = rsrc.x1; + int x1dst = rdst.x1; + int len = rc.x2; + if(rw.x1 > x1src) + { + x1dst += rw.x1 - x1src; + len -= rw.x1 - x1src; + x1src = rw.x1; + } + if(len > 0) + { + if(x1src + len-1 > rw.x2) + { + len -= x1src + len - rw.x2 - 1; + } + if(len > 0) + { + m_ren->blend_from_lut(src, + color_lut, + x1dst, rdst.y1, + x1src, rsrc.y1, + len, + cover); + } + } + } + rdst.y1 += incy; + rsrc.y1 += incy; + --rc.y2; + } + } + } + + private: + pixfmt_type* m_ren; + rect_i m_clip_box; + }; + + +} + +#endif diff --git a/src/agg/agg_renderer_scanline.h b/src/agg/agg_renderer_scanline.h new file mode 100644 index 0000000000..311e9f739a --- /dev/null +++ b/src/agg/agg_renderer_scanline.h @@ -0,0 +1,854 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERER_SCANLINE_INCLUDED +#define AGG_RENDERER_SCANLINE_INCLUDED + +#include +#include +#include "agg_basics.h" +#include "agg_renderer_base.h" + +namespace agg +{ + + //================================================render_scanline_aa_solid + template + void render_scanline_aa_solid(const Scanline& sl, + BaseRenderer& ren, + const ColorT& color) + { + int y = sl.y(); + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + + for(;;) + { + int x = span->x; + if(span->len > 0) + { + ren.blend_solid_hspan(x, y, (unsigned)span->len, + color, + span->covers); + } + else + { + ren.blend_hline(x, y, (unsigned)(x - span->len - 1), + color, + *(span->covers)); + } + if(--num_spans == 0) break; + ++span; + } + } + + //===============================================render_scanlines_aa_solid + template + void render_scanlines_aa_solid(Rasterizer& ras, Scanline& sl, + BaseRenderer& ren, const ColorT& color) + { + if(ras.rewind_scanlines()) + { + // Explicitly convert "color" to the BaseRenderer color type. + // For example, it can be called with color type "rgba", while + // "rgba8" is needed. Otherwise it will be implicitly + // converted in the loop many times. + //---------------------- + typename BaseRenderer::color_type ren_color = color; + + sl.reset(ras.min_x(), ras.max_x()); + while(ras.sweep_scanline(sl)) + { + //render_scanline_aa_solid(sl, ren, ren_color); + + // This code is equivalent to the above call (copy/paste). + // It's just a "manual" optimization for old compilers, + // like Microsoft Visual C++ v6.0 + //------------------------------- + int y = sl.y(); + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + + for(;;) + { + int x = span->x; + if(span->len > 0) + { + ren.blend_solid_hspan(x, y, (unsigned)span->len, + ren_color, + span->covers); + } + else + { + ren.blend_hline(x, y, (unsigned)(x - span->len - 1), + ren_color, + *(span->covers)); + } + if(--num_spans == 0) break; + ++span; + } + } + } + } + + //==============================================renderer_scanline_aa_solid + template class renderer_scanline_aa_solid + { + public: + typedef BaseRenderer base_ren_type; + typedef typename base_ren_type::color_type color_type; + + //-------------------------------------------------------------------- + renderer_scanline_aa_solid() : m_ren(0) {} + explicit renderer_scanline_aa_solid(base_ren_type& ren) : m_ren(&ren) {} + void attach(base_ren_type& ren) + { + m_ren = &ren; + } + + //-------------------------------------------------------------------- + void color(const color_type& c) { m_color = c; } + const color_type& color() const { return m_color; } + + //-------------------------------------------------------------------- + void prepare() {} + + //-------------------------------------------------------------------- + template void render(const Scanline& sl) + { + render_scanline_aa_solid(sl, *m_ren, m_color); + } + + private: + base_ren_type* m_ren; + color_type m_color; + }; + + + + + + + + + + + + + + //======================================================render_scanline_aa + template + void render_scanline_aa(const Scanline& sl, BaseRenderer& ren, + SpanAllocator& alloc, SpanGenerator& span_gen) + { + int y = sl.y(); + + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + for(;;) + { + int x = span->x; + int len = span->len; + const typename Scanline::cover_type* covers = span->covers; + + if(len < 0) len = -len; + typename BaseRenderer::color_type* colors = alloc.allocate(len); + span_gen.generate(colors, x, y, len); + ren.blend_color_hspan(x, y, len, colors, + (span->len < 0) ? 0 : covers, *covers); + + if(--num_spans == 0) break; + ++span; + } + } + + //=====================================================render_scanlines_aa + template + void render_scanlines_aa(Rasterizer& ras, Scanline& sl, BaseRenderer& ren, + SpanAllocator& alloc, SpanGenerator& span_gen) + { + if(ras.rewind_scanlines()) + { + sl.reset(ras.min_x(), ras.max_x()); + span_gen.prepare(); + while(ras.sweep_scanline(sl)) + { + render_scanline_aa(sl, ren, alloc, span_gen); + } + } + } + + //====================================================renderer_scanline_aa + template + class renderer_scanline_aa + { + public: + typedef BaseRenderer base_ren_type; + typedef SpanAllocator alloc_type; + typedef SpanGenerator span_gen_type; + + //-------------------------------------------------------------------- + renderer_scanline_aa() : m_ren(0), m_alloc(0), m_span_gen(0) {} + renderer_scanline_aa(base_ren_type& ren, + alloc_type& alloc, + span_gen_type& span_gen) : + m_ren(&ren), + m_alloc(&alloc), + m_span_gen(&span_gen) + {} + void attach(base_ren_type& ren, + alloc_type& alloc, + span_gen_type& span_gen) + { + m_ren = &ren; + m_alloc = &alloc; + m_span_gen = &span_gen; + } + + //-------------------------------------------------------------------- + void prepare() { m_span_gen->prepare(); } + + //-------------------------------------------------------------------- + template void render(const Scanline& sl) + { + render_scanline_aa(sl, *m_ren, *m_alloc, *m_span_gen); + } + + private: + base_ren_type* m_ren; + alloc_type* m_alloc; + span_gen_type* m_span_gen; + }; + + + + + + + //===============================================render_scanline_bin_solid + template + void render_scanline_bin_solid(const Scanline& sl, + BaseRenderer& ren, + const ColorT& color) + { + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + for(;;) + { + ren.blend_hline(span->x, + sl.y(), + span->x - 1 + ((span->len < 0) ? + -span->len : + span->len), + color, + cover_full); + if(--num_spans == 0) break; + ++span; + } + } + + //==============================================render_scanlines_bin_solid + template + void render_scanlines_bin_solid(Rasterizer& ras, Scanline& sl, + BaseRenderer& ren, const ColorT& color) + { + if(ras.rewind_scanlines()) + { + // Explicitly convert "color" to the BaseRenderer color type. + // For example, it can be called with color type "rgba", while + // "rgba8" is needed. Otherwise it will be implicitly + // converted in the loop many times. + //---------------------- + typename BaseRenderer::color_type ren_color(color); + + sl.reset(ras.min_x(), ras.max_x()); + while(ras.sweep_scanline(sl)) + { + //render_scanline_bin_solid(sl, ren, ren_color); + + // This code is equivalent to the above call (copy/paste). + // It's just a "manual" optimization for old compilers, + // like Microsoft Visual C++ v6.0 + //------------------------------- + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + for(;;) + { + ren.blend_hline(span->x, + sl.y(), + span->x - 1 + ((span->len < 0) ? + -span->len : + span->len), + ren_color, + cover_full); + if(--num_spans == 0) break; + ++span; + } + } + } + } + + //=============================================renderer_scanline_bin_solid + template class renderer_scanline_bin_solid + { + public: + typedef BaseRenderer base_ren_type; + typedef typename base_ren_type::color_type color_type; + + //-------------------------------------------------------------------- + renderer_scanline_bin_solid() : m_ren(0) {} + explicit renderer_scanline_bin_solid(base_ren_type& ren) : m_ren(&ren) {} + void attach(base_ren_type& ren) + { + m_ren = &ren; + } + + //-------------------------------------------------------------------- + void color(const color_type& c) { m_color = c; } + const color_type& color() const { return m_color; } + + //-------------------------------------------------------------------- + void prepare() {} + + //-------------------------------------------------------------------- + template void render(const Scanline& sl) + { + render_scanline_bin_solid(sl, *m_ren, m_color); + } + + private: + base_ren_type* m_ren; + color_type m_color; + }; + + + + + + + + + //======================================================render_scanline_bin + template + void render_scanline_bin(const Scanline& sl, BaseRenderer& ren, + SpanAllocator& alloc, SpanGenerator& span_gen) + { + int y = sl.y(); + + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + for(;;) + { + int x = span->x; + int len = span->len; + if(len < 0) len = -len; + typename BaseRenderer::color_type* colors = alloc.allocate(len); + span_gen.generate(colors, x, y, len); + ren.blend_color_hspan(x, y, len, colors, 0, cover_full); + if(--num_spans == 0) break; + ++span; + } + } + + //=====================================================render_scanlines_bin + template + void render_scanlines_bin(Rasterizer& ras, Scanline& sl, BaseRenderer& ren, + SpanAllocator& alloc, SpanGenerator& span_gen) + { + if(ras.rewind_scanlines()) + { + sl.reset(ras.min_x(), ras.max_x()); + span_gen.prepare(); + while(ras.sweep_scanline(sl)) + { + render_scanline_bin(sl, ren, alloc, span_gen); + } + } + } + + //====================================================renderer_scanline_bin + template + class renderer_scanline_bin + { + public: + typedef BaseRenderer base_ren_type; + typedef SpanAllocator alloc_type; + typedef SpanGenerator span_gen_type; + + //-------------------------------------------------------------------- + renderer_scanline_bin() : m_ren(0), m_alloc(0), m_span_gen(0) {} + renderer_scanline_bin(base_ren_type& ren, + alloc_type& alloc, + span_gen_type& span_gen) : + m_ren(&ren), + m_alloc(&alloc), + m_span_gen(&span_gen) + {} + void attach(base_ren_type& ren, + alloc_type& alloc, + span_gen_type& span_gen) + { + m_ren = &ren; + m_alloc = &alloc; + m_span_gen = &span_gen; + } + + //-------------------------------------------------------------------- + void prepare() { m_span_gen->prepare(); } + + //-------------------------------------------------------------------- + template void render(const Scanline& sl) + { + render_scanline_bin(sl, *m_ren, *m_alloc, *m_span_gen); + } + + private: + base_ren_type* m_ren; + alloc_type* m_alloc; + span_gen_type* m_span_gen; + }; + + + + + + + + + + + //========================================================render_scanlines + template + void render_scanlines(Rasterizer& ras, Scanline& sl, Renderer& ren) + { + if(ras.rewind_scanlines()) + { + sl.reset(ras.min_x(), ras.max_x()); + ren.prepare(); + while(ras.sweep_scanline(sl)) + { + ren.render(sl); + } + } + } + + //========================================================render_all_paths + template + void render_all_paths(Rasterizer& ras, + Scanline& sl, + Renderer& r, + VertexSource& vs, + const ColorStorage& as, + const PathId& path_id, + unsigned num_paths) + { + for(unsigned i = 0; i < num_paths; i++) + { + ras.reset(); + ras.add_path(vs, path_id[i]); + r.color(as[i]); + render_scanlines(ras, sl, r); + } + } + + + + + + + //=============================================render_scanlines_compound + template + void render_scanlines_compound(Rasterizer& ras, + ScanlineAA& sl_aa, + ScanlineBin& sl_bin, + BaseRenderer& ren, + SpanAllocator& alloc, + StyleHandler& sh) + { + if(ras.rewind_scanlines()) + { + int min_x = ras.min_x(); + int len = ras.max_x() - min_x + 2; + sl_aa.reset(min_x, ras.max_x()); + sl_bin.reset(min_x, ras.max_x()); + + typedef typename BaseRenderer::color_type color_type; + color_type* color_span = alloc.allocate(len * 2); + color_type* mix_buffer = color_span + len; + unsigned num_spans; + + unsigned num_styles; + unsigned style; + bool solid; + while((num_styles = ras.sweep_styles()) > 0) + { + typename ScanlineAA::const_iterator span_aa; + if(num_styles == 1) + { + // Optimization for a single style. Happens often + //------------------------- + if(ras.sweep_scanline(sl_aa, 0)) + { + style = ras.style(0); + if(sh.is_solid(style)) + { + // Just solid fill + //----------------------- + render_scanline_aa_solid(sl_aa, ren, sh.color(style)); + } + else + { + // Arbitrary span generator + //----------------------- + span_aa = sl_aa.begin(); + num_spans = sl_aa.num_spans(); + for(;;) + { + len = span_aa->len; + sh.generate_span(color_span, + span_aa->x, + sl_aa.y(), + len, + style); + + ren.blend_color_hspan(span_aa->x, + sl_aa.y(), + span_aa->len, + color_span, + span_aa->covers); + if(--num_spans == 0) break; + ++span_aa; + } + } + } + } + else + { + if(ras.sweep_scanline(sl_bin, -1)) + { + // Clear the spans of the mix_buffer + //-------------------- + typename ScanlineBin::const_iterator span_bin = sl_bin.begin(); + num_spans = sl_bin.num_spans(); + for(;;) + { + memset(mix_buffer + span_bin->x - min_x, + 0, + span_bin->len * sizeof(color_type)); + + if(--num_spans == 0) break; + ++span_bin; + } + + unsigned i; + for(i = 0; i < num_styles; i++) + { + style = ras.style(i); + solid = sh.is_solid(style); + + if(ras.sweep_scanline(sl_aa, i)) + { + color_type* colors; + color_type* cspan; + typename ScanlineAA::cover_type* covers; + span_aa = sl_aa.begin(); + num_spans = sl_aa.num_spans(); + if(solid) + { + // Just solid fill + //----------------------- + for(;;) + { + color_type c = sh.color(style); + len = span_aa->len; + colors = mix_buffer + span_aa->x - min_x; + covers = span_aa->covers; + do + { + if(*covers == cover_full) + { + *colors = c; + } + else + { + colors->add(c, *covers); + } + ++colors; + ++covers; + } + while(--len); + if(--num_spans == 0) break; + ++span_aa; + } + } + else + { + // Arbitrary span generator + //----------------------- + for(;;) + { + len = span_aa->len; + colors = mix_buffer + span_aa->x - min_x; + cspan = color_span; + sh.generate_span(cspan, + span_aa->x, + sl_aa.y(), + len, + style); + covers = span_aa->covers; + do + { + if(*covers == cover_full) + { + *colors = *cspan; + } + else + { + colors->add(*cspan, *covers); + } + ++cspan; + ++colors; + ++covers; + } + while(--len); + if(--num_spans == 0) break; + ++span_aa; + } + } + } + } + + // Emit the blended result as a color hspan + //------------------------- + span_bin = sl_bin.begin(); + num_spans = sl_bin.num_spans(); + for(;;) + { + ren.blend_color_hspan(span_bin->x, + sl_bin.y(), + span_bin->len, + mix_buffer + span_bin->x - min_x, + 0, + cover_full); + if(--num_spans == 0) break; + ++span_bin; + } + } // if(ras.sweep_scanline(sl_bin, -1)) + } // if(num_styles == 1) ... else + } // while((num_styles = ras.sweep_styles()) > 0) + } // if(ras.rewind_scanlines()) + } + + //=======================================render_scanlines_compound_layered + template + void render_scanlines_compound_layered(Rasterizer& ras, + ScanlineAA& sl_aa, + BaseRenderer& ren, + SpanAllocator& alloc, + StyleHandler& sh) + { + if(ras.rewind_scanlines()) + { + int min_x = ras.min_x(); + int len = ras.max_x() - min_x + 2; + sl_aa.reset(min_x, ras.max_x()); + + typedef typename BaseRenderer::color_type color_type; + color_type* color_span = alloc.allocate(len * 2); + color_type* mix_buffer = color_span + len; + cover_type* cover_buffer = ras.allocate_cover_buffer(len); + unsigned num_spans; + + unsigned num_styles; + unsigned style; + bool solid; + while((num_styles = ras.sweep_styles()) > 0) + { + typename ScanlineAA::const_iterator span_aa; + if(num_styles == 1) + { + // Optimization for a single style. Happens often + //------------------------- + if(ras.sweep_scanline(sl_aa, 0)) + { + style = ras.style(0); + if(sh.is_solid(style)) + { + // Just solid fill + //----------------------- + render_scanline_aa_solid(sl_aa, ren, sh.color(style)); + } + else + { + // Arbitrary span generator + //----------------------- + span_aa = sl_aa.begin(); + num_spans = sl_aa.num_spans(); + for(;;) + { + len = span_aa->len; + sh.generate_span(color_span, + span_aa->x, + sl_aa.y(), + len, + style); + + ren.blend_color_hspan(span_aa->x, + sl_aa.y(), + span_aa->len, + color_span, + span_aa->covers); + if(--num_spans == 0) break; + ++span_aa; + } + } + } + } + else + { + int sl_start = ras.scanline_start(); + unsigned sl_len = ras.scanline_length(); + + if(sl_len) + { + memset(mix_buffer + sl_start - min_x, + 0, + sl_len * sizeof(color_type)); + + memset(cover_buffer + sl_start - min_x, + 0, + sl_len * sizeof(cover_type)); + + int sl_y = std::numeric_limits::max(); + unsigned i; + for(i = 0; i < num_styles; i++) + { + style = ras.style(i); + solid = sh.is_solid(style); + + if(ras.sweep_scanline(sl_aa, i)) + { + unsigned cover; + color_type* colors; + color_type* cspan; + cover_type* src_covers; + cover_type* dst_covers; + span_aa = sl_aa.begin(); + num_spans = sl_aa.num_spans(); + sl_y = sl_aa.y(); + if(solid) + { + // Just solid fill + //----------------------- + for(;;) + { + color_type c = sh.color(style); + len = span_aa->len; + colors = mix_buffer + span_aa->x - min_x; + src_covers = span_aa->covers; + dst_covers = cover_buffer + span_aa->x - min_x; + do + { + cover = *src_covers; + if(*dst_covers + cover > cover_full) + { + cover = cover_full - *dst_covers; + } + if(cover) + { + colors->add(c, cover); + *dst_covers += cover; + } + ++colors; + ++src_covers; + ++dst_covers; + } + while(--len); + if(--num_spans == 0) break; + ++span_aa; + } + } + else + { + // Arbitrary span generator + //----------------------- + for(;;) + { + len = span_aa->len; + colors = mix_buffer + span_aa->x - min_x; + cspan = color_span; + sh.generate_span(cspan, + span_aa->x, + sl_aa.y(), + len, + style); + src_covers = span_aa->covers; + dst_covers = cover_buffer + span_aa->x - min_x; + do + { + cover = *src_covers; + if(*dst_covers + cover > cover_full) + { + cover = cover_full - *dst_covers; + } + if(cover) + { + colors->add(*cspan, cover); + *dst_covers += cover; + } + ++cspan; + ++colors; + ++src_covers; + ++dst_covers; + } + while(--len); + if(--num_spans == 0) break; + ++span_aa; + } + } + } + } + ren.blend_color_hspan(sl_start, + sl_y, + sl_len, + mix_buffer + sl_start - min_x, + 0, + cover_full); + } //if(sl_len) + } //if(num_styles == 1) ... else + } //while((num_styles = ras.sweep_styles()) > 0) + } //if(ras.rewind_scanlines()) + } + + +} + +#endif diff --git a/src/agg/agg_rendering_buffer.h b/src/agg/agg_rendering_buffer.h new file mode 100644 index 0000000000..0eff6ff27d --- /dev/null +++ b/src/agg/agg_rendering_buffer.h @@ -0,0 +1,300 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// class rendering_buffer +// +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERING_BUFFER_INCLUDED +#define AGG_RENDERING_BUFFER_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + + //===========================================================row_accessor + template class row_accessor + { + public: + typedef const_row_info row_data; + + //------------------------------------------------------------------- + row_accessor() : + m_buf(0), + m_start(0), + m_width(0), + m_height(0), + m_stride(0) + { + } + + //-------------------------------------------------------------------- + row_accessor(T* buf, unsigned width, unsigned height, int stride) : + m_buf(0), + m_start(0), + m_width(0), + m_height(0), + m_stride(0) + { + attach(buf, width, height, stride); + } + + + //-------------------------------------------------------------------- + void attach(T* buf, unsigned width, unsigned height, int stride) + { + m_buf = m_start = buf; + m_width = width; + m_height = height; + m_stride = stride; + if(stride < 0) + { + m_start = m_buf - int(height - 1) * stride; + } + } + + //-------------------------------------------------------------------- + AGG_INLINE T* buf() { return m_buf; } + AGG_INLINE const T* buf() const { return m_buf; } + AGG_INLINE unsigned width() const { return m_width; } + AGG_INLINE unsigned height() const { return m_height; } + AGG_INLINE int stride() const { return m_stride; } + AGG_INLINE unsigned stride_abs() const + { + return (m_stride < 0) ? unsigned(-m_stride) : unsigned(m_stride); + } + + //-------------------------------------------------------------------- + AGG_INLINE T* row_ptr(int, int y, unsigned) + { + return m_start + y * m_stride; + } + AGG_INLINE T* row_ptr(int y) { return m_start + y * m_stride; } + AGG_INLINE const T* row_ptr(int y) const { return m_start + y * m_stride; } + AGG_INLINE row_data row (int y) const + { + return row_data(0, m_width-1, row_ptr(y)); + } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf& src) + { + unsigned h = height(); + if(src.height() < h) h = src.height(); + + unsigned l = stride_abs(); + if(src.stride_abs() < l) l = src.stride_abs(); + + l *= sizeof(T); + + unsigned y; + unsigned w = width(); + for (y = 0; y < h; y++) + { + memcpy(row_ptr(0, y, w), src.row_ptr(y), l); + } + } + + //-------------------------------------------------------------------- + void clear(T value) + { + unsigned y; + unsigned w = width(); + unsigned stride = stride_abs(); + for(y = 0; y < height(); y++) + { + T* p = row_ptr(0, y, w); + unsigned x; + for(x = 0; x < stride; x++) + { + *p++ = value; + } + } + } + + private: + //-------------------------------------------------------------------- + T* m_buf; // Pointer to renrdering buffer + T* m_start; // Pointer to first pixel depending on stride + unsigned m_width; // Width in pixels + unsigned m_height; // Height in pixels + int m_stride; // Number of bytes per row. Can be < 0 + }; + + + + + //==========================================================row_ptr_cache + template class row_ptr_cache + { + public: + typedef const_row_info row_data; + + //------------------------------------------------------------------- + row_ptr_cache() : + m_buf(0), + m_rows(), + m_width(0), + m_height(0), + m_stride(0) + { + } + + //-------------------------------------------------------------------- + row_ptr_cache(T* buf, unsigned width, unsigned height, int stride) : + m_buf(0), + m_rows(), + m_width(0), + m_height(0), + m_stride(0) + { + attach(buf, width, height, stride); + } + + //-------------------------------------------------------------------- + void attach(T* buf, unsigned width, unsigned height, int stride) + { + m_buf = buf; + m_width = width; + m_height = height; + m_stride = stride; + if(height > m_rows.size()) + { + m_rows.resize(height); + } + + T* row_ptr = m_buf; + + if(stride < 0) + { + row_ptr = m_buf - int(height - 1) * stride; + } + + T** rows = &m_rows[0]; + + while(height--) + { + *rows++ = row_ptr; + row_ptr += stride; + } + } + + //-------------------------------------------------------------------- + AGG_INLINE T* buf() { return m_buf; } + AGG_INLINE const T* buf() const { return m_buf; } + AGG_INLINE unsigned width() const { return m_width; } + AGG_INLINE unsigned height() const { return m_height; } + AGG_INLINE int stride() const { return m_stride; } + AGG_INLINE unsigned stride_abs() const + { + return (m_stride < 0) ? unsigned(-m_stride) : unsigned(m_stride); + } + + //-------------------------------------------------------------------- + AGG_INLINE T* row_ptr(int, int y, unsigned) + { + return m_rows[y]; + } + AGG_INLINE T* row_ptr(int y) { return m_rows[y]; } + AGG_INLINE const T* row_ptr(int y) const { return m_rows[y]; } + AGG_INLINE row_data row (int y) const + { + return row_data(0, m_width-1, m_rows[y]); + } + + //-------------------------------------------------------------------- + T const* const* rows() const { return &m_rows[0]; } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf& src) + { + unsigned h = height(); + if(src.height() < h) h = src.height(); + + unsigned l = stride_abs(); + if(src.stride_abs() < l) l = src.stride_abs(); + + l *= sizeof(T); + + unsigned y; + unsigned w = width(); + for (y = 0; y < h; y++) + { + memcpy(row_ptr(0, y, w), src.row_ptr(y), l); + } + } + + //-------------------------------------------------------------------- + void clear(T value) + { + unsigned y; + unsigned w = width(); + unsigned stride = stride_abs(); + for(y = 0; y < height(); y++) + { + T* p = row_ptr(0, y, w); + unsigned x; + for(x = 0; x < stride; x++) + { + *p++ = value; + } + } + } + + private: + //-------------------------------------------------------------------- + T* m_buf; // Pointer to renrdering buffer + pod_array m_rows; // Pointers to each row of the buffer + unsigned m_width; // Width in pixels + unsigned m_height; // Height in pixels + int m_stride; // Number of bytes per row. Can be < 0 + }; + + + + + //========================================================rendering_buffer + // + // The definition of the main type for accessing the rows in the frame + // buffer. It provides functionality to navigate to the rows in a + // rectangular matrix, from top to bottom or from bottom to top depending + // on stride. + // + // row_accessor is cheap to create/destroy, but performs one multiplication + // when calling row_ptr(). + // + // row_ptr_cache creates an array of pointers to rows, so, the access + // via row_ptr() may be faster. But it requires memory allocation + // when creating. For example, on typical Intel Pentium hardware + // row_ptr_cache speeds span_image_filter_rgb_nn up to 10% + // + // It's used only in short hand typedefs like pixfmt_rgba32 and can be + // redefined in agg_config.h + // In real applications you can use both, depending on your needs + //------------------------------------------------------------------------ +#ifdef AGG_RENDERING_BUFFER + typedef AGG_RENDERING_BUFFER rendering_buffer; +#else +// typedef row_ptr_cache rendering_buffer; + typedef row_accessor rendering_buffer; +#endif + +} + + +#endif diff --git a/src/agg/agg_scanline_p.h b/src/agg/agg_scanline_p.h new file mode 100644 index 0000000000..1d1cbe72f1 --- /dev/null +++ b/src/agg/agg_scanline_p.h @@ -0,0 +1,329 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Class scanline_p - a general purpose scanline container with packed spans. +// +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates (scanline32_p) has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +#ifndef AGG_SCANLINE_P_INCLUDED +#define AGG_SCANLINE_P_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + + //=============================================================scanline_p8 + // + // This is a general purpose scaline container which supports the interface + // used in the rasterizer::render(). See description of scanline_u8 + // for details. + // + //------------------------------------------------------------------------ + class scanline_p8 + { + public: + typedef scanline_p8 self_type; + typedef int8u cover_type; + typedef int16 coord_type; + + //-------------------------------------------------------------------- + struct span + { + coord_type x; + coord_type len; // If negative, it's a solid span, covers is valid + const cover_type* covers; + }; + + typedef span* iterator; + typedef const span* const_iterator; + + scanline_p8() : + m_last_x(0x7FFFFFF0), + m_covers(), + m_cover_ptr(0), + m_spans(), + m_cur_span(0) + { + } + + //-------------------------------------------------------------------- + void reset(int min_x, int max_x) + { + unsigned max_len = max_x - min_x + 3; + if(max_len > m_spans.size()) + { + m_spans.resize(max_len); + m_covers.resize(max_len); + } + m_last_x = 0x7FFFFFF0; + m_cover_ptr = &m_covers[0]; + m_cur_span = &m_spans[0]; + m_cur_span->len = 0; + } + + //-------------------------------------------------------------------- + void add_cell(int x, unsigned cover) + { + *m_cover_ptr = (cover_type)cover; + if(x == m_last_x+1 && m_cur_span->len > 0) + { + m_cur_span->len++; + } + else + { + m_cur_span++; + m_cur_span->covers = m_cover_ptr; + m_cur_span->x = (int16)x; + m_cur_span->len = 1; + } + m_last_x = x; + m_cover_ptr++; + } + + //-------------------------------------------------------------------- + void add_cells(int x, unsigned len, const cover_type* covers) + { + memcpy(m_cover_ptr, covers, len * sizeof(cover_type)); + if(x == m_last_x+1 && m_cur_span->len > 0) + { + m_cur_span->len += (int16)len; + } + else + { + m_cur_span++; + m_cur_span->covers = m_cover_ptr; + m_cur_span->x = (int16)x; + m_cur_span->len = (int16)len; + } + m_cover_ptr += len; + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void add_span(int x, unsigned len, unsigned cover) + { + if(x == m_last_x+1 && + m_cur_span->len < 0 && + cover == *m_cur_span->covers) + { + m_cur_span->len -= (int16)len; + } + else + { + *m_cover_ptr = (cover_type)cover; + m_cur_span++; + m_cur_span->covers = m_cover_ptr++; + m_cur_span->x = (int16)x; + m_cur_span->len = (int16)(-int(len)); + } + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void finalize(int y) + { + m_y = y; + } + + //-------------------------------------------------------------------- + void reset_spans() + { + m_last_x = 0x7FFFFFF0; + m_cover_ptr = &m_covers[0]; + m_cur_span = &m_spans[0]; + m_cur_span->len = 0; + } + + //-------------------------------------------------------------------- + int y() const { return m_y; } + unsigned num_spans() const { return unsigned(m_cur_span - &m_spans[0]); } + const_iterator begin() const { return &m_spans[1]; } + + private: + scanline_p8(const self_type&); + const self_type& operator = (const self_type&); + + int m_last_x; + int m_y; + pod_array m_covers; + cover_type* m_cover_ptr; + pod_array m_spans; + span* m_cur_span; + }; + + + + + + + + + //==========================================================scanline32_p8 + class scanline32_p8 + { + public: + typedef scanline32_p8 self_type; + typedef int8u cover_type; + typedef int32 coord_type; + + struct span + { + span() {} + span(coord_type x_, coord_type len_, const cover_type* covers_) : + x(x_), len(len_), covers(covers_) {} + + coord_type x; + coord_type len; // If negative, it's a solid span, covers is valid + const cover_type* covers; + }; + typedef pod_bvector span_array_type; + + + //-------------------------------------------------------------------- + class const_iterator + { + public: + const_iterator(const span_array_type& spans) : + m_spans(spans), + m_span_idx(0) + {} + + const span& operator*() const { return m_spans[m_span_idx]; } + const span* operator->() const { return &m_spans[m_span_idx]; } + + void operator ++ () { ++m_span_idx; } + + private: + const span_array_type& m_spans; + unsigned m_span_idx; + }; + + //-------------------------------------------------------------------- + scanline32_p8() : + m_max_len(0), + m_last_x(0x7FFFFFF0), + m_covers(), + m_cover_ptr(0) + { + } + + //-------------------------------------------------------------------- + void reset(int min_x, int max_x) + { + unsigned max_len = max_x - min_x + 3; + if(max_len > m_covers.size()) + { + m_covers.resize(max_len); + } + m_last_x = 0x7FFFFFF0; + m_cover_ptr = &m_covers[0]; + m_spans.remove_all(); + } + + //-------------------------------------------------------------------- + void add_cell(int x, unsigned cover) + { + *m_cover_ptr = cover_type(cover); + if(x == m_last_x+1 && m_spans.size() && m_spans.last().len > 0) + { + m_spans.last().len++; + } + else + { + m_spans.add(span(coord_type(x), 1, m_cover_ptr)); + } + m_last_x = x; + m_cover_ptr++; + } + + //-------------------------------------------------------------------- + void add_cells(int x, unsigned len, const cover_type* covers) + { + memcpy(m_cover_ptr, covers, len * sizeof(cover_type)); + if(x == m_last_x+1 && m_spans.size() && m_spans.last().len > 0) + { + m_spans.last().len += coord_type(len); + } + else + { + m_spans.add(span(coord_type(x), coord_type(len), m_cover_ptr)); + } + m_cover_ptr += len; + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void add_span(int x, unsigned len, unsigned cover) + { + if(x == m_last_x+1 && + m_spans.size() && + m_spans.last().len < 0 && + cover == *m_spans.last().covers) + { + m_spans.last().len -= coord_type(len); + } + else + { + *m_cover_ptr = cover_type(cover); + m_spans.add(span(coord_type(x), -coord_type(len), m_cover_ptr++)); + } + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void finalize(int y) + { + m_y = y; + } + + //-------------------------------------------------------------------- + void reset_spans() + { + m_last_x = 0x7FFFFFF0; + m_cover_ptr = &m_covers[0]; + m_spans.remove_all(); + } + + //-------------------------------------------------------------------- + int y() const { return m_y; } + unsigned num_spans() const { return m_spans.size(); } + const_iterator begin() const { return const_iterator(m_spans); } + + private: + scanline32_p8(const self_type&); + const self_type& operator = (const self_type&); + + unsigned m_max_len; + int m_last_x; + int m_y; + pod_array m_covers; + cover_type* m_cover_ptr; + span_array_type m_spans; + }; + + +} + + +#endif + diff --git a/src/agg/agg_trans_affine.h b/src/agg/agg_trans_affine.h new file mode 100644 index 0000000000..1a61163883 --- /dev/null +++ b/src/agg/agg_trans_affine.h @@ -0,0 +1,518 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Affine transformation classes. +// +//---------------------------------------------------------------------------- +#ifndef AGG_TRANS_AFFINE_INCLUDED +#define AGG_TRANS_AFFINE_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + const double affine_epsilon = 1e-14; + + //============================================================trans_affine + // + // See Implementation agg_trans_affine.cpp + // + // Affine transformation are linear transformations in Cartesian coordinates + // (strictly speaking not only in Cartesian, but for the beginning we will + // think so). They are rotation, scaling, translation and skewing. + // After any affine transformation a line segment remains a line segment + // and it will never become a curve. + // + // There will be no math about matrix calculations, since it has been + // described many times. Ask yourself a very simple question: + // "why do we need to understand and use some matrix stuff instead of just + // rotating, scaling and so on". The answers are: + // + // 1. Any combination of transformations can be done by only 4 multiplications + // and 4 additions in floating point. + // 2. One matrix transformation is equivalent to the number of consecutive + // discrete transformations, i.e. the matrix "accumulates" all transformations + // in the order of their settings. Suppose we have 4 transformations: + // * rotate by 30 degrees, + // * scale X to 2.0, + // * scale Y to 1.5, + // * move to (100, 100). + // The result will depend on the order of these transformations, + // and the advantage of matrix is that the sequence of discret calls: + // rotate(30), scaleX(2.0), scaleY(1.5), move(100,100) + // will have exactly the same result as the following matrix transformations: + // + // affine_matrix m; + // m *= rotate_matrix(30); + // m *= scaleX_matrix(2.0); + // m *= scaleY_matrix(1.5); + // m *= move_matrix(100,100); + // + // m.transform_my_point_at_last(x, y); + // + // What is the good of it? In real life we will set-up the matrix only once + // and then transform many points, let alone the convenience to set any + // combination of transformations. + // + // So, how to use it? Very easy - literally as it's shown above. Not quite, + // let us write a correct example: + // + // agg::trans_affine m; + // m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0); + // m *= agg::trans_affine_scaling(2.0, 1.5); + // m *= agg::trans_affine_translation(100.0, 100.0); + // m.transform(&x, &y); + // + // The affine matrix is all you need to perform any linear transformation, + // but all transformations have origin point (0,0). It means that we need to + // use 2 translations if we want to rotate someting around (100,100): + // + // m *= agg::trans_affine_translation(-100.0, -100.0); // move to (0,0) + // m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0); // rotate + // m *= agg::trans_affine_translation(100.0, 100.0); // move back to (100,100) + //---------------------------------------------------------------------- + struct trans_affine + { + double sx, shy, shx, sy, tx, ty; + + //------------------------------------------ Construction + // Identity matrix + trans_affine() : + sx(1.0), shy(0.0), shx(0.0), sy(1.0), tx(0.0), ty(0.0) + {} + + // Custom matrix. Usually used in derived classes + trans_affine(double v0, double v1, double v2, + double v3, double v4, double v5) : + sx(v0), shy(v1), shx(v2), sy(v3), tx(v4), ty(v5) + {} + + // Custom matrix from m[6] + explicit trans_affine(const double* m) : + sx(m[0]), shy(m[1]), shx(m[2]), sy(m[3]), tx(m[4]), ty(m[5]) + {} + + // Rectangle to a parallelogram. + trans_affine(double x1, double y1, double x2, double y2, + const double* parl) + { + rect_to_parl(x1, y1, x2, y2, parl); + } + + // Parallelogram to a rectangle. + trans_affine(const double* parl, + double x1, double y1, double x2, double y2) + { + parl_to_rect(parl, x1, y1, x2, y2); + } + + // Arbitrary parallelogram transformation. + trans_affine(const double* src, const double* dst) + { + parl_to_parl(src, dst); + } + + //---------------------------------- Parellelogram transformations + // transform a parallelogram to another one. Src and dst are + // pointers to arrays of three points (double[6], x1,y1,...) that + // identify three corners of the parallelograms assuming implicit + // fourth point. The arguments are arrays of double[6] mapped + // to x1,y1, x2,y2, x3,y3 where the coordinates are: + // *-----------------* + // / (x3,y3)/ + // / / + // /(x1,y1) (x2,y2)/ + // *-----------------* + const trans_affine& parl_to_parl(const double* src, + const double* dst); + + const trans_affine& rect_to_parl(double x1, double y1, + double x2, double y2, + const double* parl); + + const trans_affine& parl_to_rect(const double* parl, + double x1, double y1, + double x2, double y2); + + + //------------------------------------------ Operations + // Reset - load an identity matrix + const trans_affine& reset(); + + // Direct transformations operations + const trans_affine& translate(double x, double y); + const trans_affine& rotate(double a); + const trans_affine& scale(double s); + const trans_affine& scale(double x, double y); + + // Multiply matrix to another one + const trans_affine& multiply(const trans_affine& m); + + // Multiply "m" to "this" and assign the result to "this" + const trans_affine& premultiply(const trans_affine& m); + + // Multiply matrix to inverse of another one + const trans_affine& multiply_inv(const trans_affine& m); + + // Multiply inverse of "m" to "this" and assign the result to "this" + const trans_affine& premultiply_inv(const trans_affine& m); + + // Invert matrix. Do not try to invert degenerate matrices, + // there's no check for validity. If you set scale to 0 and + // then try to invert matrix, expect unpredictable result. + const trans_affine& invert(); + + // Mirroring around X + const trans_affine& flip_x(); + + // Mirroring around Y + const trans_affine& flip_y(); + + //------------------------------------------- Load/Store + // Store matrix to an array [6] of double + void store_to(double* m) const + { + *m++ = sx; *m++ = shy; *m++ = shx; *m++ = sy; *m++ = tx; *m++ = ty; + } + + // Load matrix from an array [6] of double + const trans_affine& load_from(const double* m) + { + sx = *m++; shy = *m++; shx = *m++; sy = *m++; tx = *m++; ty = *m++; + return *this; + } + + //------------------------------------------- Operators + + // Multiply the matrix by another one + const trans_affine& operator *= (const trans_affine& m) + { + return multiply(m); + } + + // Multiply the matrix by inverse of another one + const trans_affine& operator /= (const trans_affine& m) + { + return multiply_inv(m); + } + + // Multiply the matrix by another one and return + // the result in a separete matrix. + trans_affine operator * (const trans_affine& m) const + { + return trans_affine(*this).multiply(m); + } + + // Multiply the matrix by inverse of another one + // and return the result in a separete matrix. + trans_affine operator / (const trans_affine& m) const + { + return trans_affine(*this).multiply_inv(m); + } + + // Calculate and return the inverse matrix + trans_affine operator ~ () const + { + trans_affine ret = *this; + return ret.invert(); + } + + // Equal operator with default epsilon + bool operator == (const trans_affine& m) const + { + return is_equal(m, affine_epsilon); + } + + // Not Equal operator with default epsilon + bool operator != (const trans_affine& m) const + { + return !is_equal(m, affine_epsilon); + } + + //-------------------------------------------- Transformations + // Direct transformation of x and y + void transform(double* x, double* y) const; + + // Direct transformation of x and y, 2x2 matrix only, no translation + void transform_2x2(double* x, double* y) const; + + // Inverse transformation of x and y. It works slower than the + // direct transformation. For massive operations it's better to + // invert() the matrix and then use direct transformations. + void inverse_transform(double* x, double* y) const; + + //-------------------------------------------- Auxiliary + // Calculate the determinant of matrix + double determinant() const + { + return sx * sy - shy * shx; + } + + // Calculate the reciprocal of the determinant + double determinant_reciprocal() const + { + return 1.0 / (sx * sy - shy * shx); + } + + // Get the average scale (by X and Y). + // Basically used to calculate the approximation_scale when + // decomposinting curves into line segments. + double scale() const; + + // Check to see if the matrix is not degenerate + bool is_valid(double epsilon = affine_epsilon) const; + + // Check to see if it's an identity matrix + bool is_identity(double epsilon = affine_epsilon) const; + + // Check to see if two matrices are equal + bool is_equal(const trans_affine& m, double epsilon = affine_epsilon) const; + + // Determine the major parameters. Use with caution considering + // possible degenerate cases. + double rotation() const; + void translation(double* dx, double* dy) const; + void scaling(double* x, double* y) const; + void scaling_abs(double* x, double* y) const; + }; + + //------------------------------------------------------------------------ + inline void trans_affine::transform(double* x, double* y) const + { + double tmp = *x; + *x = tmp * sx + *y * shx + tx; + *y = tmp * shy + *y * sy + ty; + } + + //------------------------------------------------------------------------ + inline void trans_affine::transform_2x2(double* x, double* y) const + { + double tmp = *x; + *x = tmp * sx + *y * shx; + *y = tmp * shy + *y * sy; + } + + //------------------------------------------------------------------------ + inline void trans_affine::inverse_transform(double* x, double* y) const + { + double d = determinant_reciprocal(); + double a = (*x - tx) * d; + double b = (*y - ty) * d; + *x = a * sy - b * shx; + *y = b * sx - a * shy; + } + + //------------------------------------------------------------------------ + inline double trans_affine::scale() const + { + double x = 0.707106781 * sx + 0.707106781 * shx; + double y = 0.707106781 * shy + 0.707106781 * sy; + return sqrt(x*x + y*y); + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::translate(double x, double y) + { + tx += x; + ty += y; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::rotate(double a) + { + double ca = cos(a); + double sa = sin(a); + double t0 = sx * ca - shy * sa; + double t2 = shx * ca - sy * sa; + double t4 = tx * ca - ty * sa; + shy = sx * sa + shy * ca; + sy = shx * sa + sy * ca; + ty = tx * sa + ty * ca; + sx = t0; + shx = t2; + tx = t4; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::scale(double x, double y) + { + double mm0 = x; // Possible hint for the optimizer + double mm3 = y; + sx *= mm0; + shx *= mm0; + tx *= mm0; + shy *= mm3; + sy *= mm3; + ty *= mm3; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::scale(double s) + { + double m = s; // Possible hint for the optimizer + sx *= m; + shx *= m; + tx *= m; + shy *= m; + sy *= m; + ty *= m; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::premultiply(const trans_affine& m) + { + trans_affine t = m; + return *this = t.multiply(*this); + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::multiply_inv(const trans_affine& m) + { + trans_affine t = m; + t.invert(); + return multiply(t); + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::premultiply_inv(const trans_affine& m) + { + trans_affine t = m; + t.invert(); + return *this = t.multiply(*this); + } + + //------------------------------------------------------------------------ + inline void trans_affine::scaling_abs(double* x, double* y) const + { + // Used to calculate scaling coefficients in image resampling. + // When there is considerable shear this method gives us much + // better estimation than just sx, sy. + *x = sqrt(sx * sx + shx * shx); + *y = sqrt(shy * shy + sy * sy); + } + + //====================================================trans_affine_rotation + // Rotation matrix. sin() and cos() are calculated twice for the same angle. + // There's no harm because the performance of sin()/cos() is very good on all + // modern processors. Besides, this operation is not going to be invoked too + // often. + class trans_affine_rotation : public trans_affine + { + public: + trans_affine_rotation(double a) : + trans_affine(cos(a), sin(a), -sin(a), cos(a), 0.0, 0.0) + {} + }; + + //====================================================trans_affine_scaling + // Scaling matrix. x, y - scale coefficients by X and Y respectively + class trans_affine_scaling : public trans_affine + { + public: + trans_affine_scaling(double x, double y) : + trans_affine(x, 0.0, 0.0, y, 0.0, 0.0) + {} + + trans_affine_scaling(double s) : + trans_affine(s, 0.0, 0.0, s, 0.0, 0.0) + {} + }; + + //================================================trans_affine_translation + // Translation matrix + class trans_affine_translation : public trans_affine + { + public: + trans_affine_translation(double x, double y) : + trans_affine(1.0, 0.0, 0.0, 1.0, x, y) + {} + }; + + //====================================================trans_affine_skewing + // Sckewing (shear) matrix + class trans_affine_skewing : public trans_affine + { + public: + trans_affine_skewing(double x, double y) : + trans_affine(1.0, tan(y), tan(x), 1.0, 0.0, 0.0) + {} + }; + + + //===============================================trans_affine_line_segment + // Rotate, Scale and Translate, associating 0...dist with line segment + // x1,y1,x2,y2 + class trans_affine_line_segment : public trans_affine + { + public: + trans_affine_line_segment(double x1, double y1, double x2, double y2, + double dist) + { + double dx = x2 - x1; + double dy = y2 - y1; + if(dist > 0.0) + { + multiply(trans_affine_scaling(sqrt(dx * dx + dy * dy) / dist)); + } + multiply(trans_affine_rotation(atan2(dy, dx))); + multiply(trans_affine_translation(x1, y1)); + } + }; + + + //============================================trans_affine_reflection_unit + // Reflection matrix. Reflect coordinates across the line through + // the origin containing the unit vector (ux, uy). + // Contributed by John Horigan + class trans_affine_reflection_unit : public trans_affine + { + public: + trans_affine_reflection_unit(double ux, double uy) : + trans_affine(2.0 * ux * ux - 1.0, + 2.0 * ux * uy, + 2.0 * ux * uy, + 2.0 * uy * uy - 1.0, + 0.0, 0.0) + {} + }; + + + //=================================================trans_affine_reflection + // Reflection matrix. Reflect coordinates across the line through + // the origin at the angle a or containing the non-unit vector (x, y). + // Contributed by John Horigan + class trans_affine_reflection : public trans_affine_reflection_unit + { + public: + trans_affine_reflection(double a) : + trans_affine_reflection_unit(cos(a), sin(a)) + {} + + + trans_affine_reflection(double x, double y) : + trans_affine_reflection_unit(x / sqrt(x * x + y * y), y / sqrt(x * x + y * y)) + {} + }; + +} + + +#endif + diff --git a/src/agg/copying b/src/agg/copying new file mode 100644 index 0000000000..b6028e5195 --- /dev/null +++ b/src/agg/copying @@ -0,0 +1,65 @@ +The Anti-Grain Geometry Project +A high quality rendering engine for C++ +http://antigrain.com + +Anti-Grain Geometry has dual licensing model. The Modified BSD +License was first added in version v2.4 just for convenience. +It is a simple, permissive non-copyleft free software license, +compatible with the GNU GPL. It's well proven and recognizable. +See http://www.fsf.org/licensing/licenses/index_html#ModifiedBSD +for details. + +Note that the Modified BSD license DOES NOT restrict your rights +if you choose the Anti-Grain Geometry Public License. + + + + +Anti-Grain Geometry Public License +==================================================== + +Anti-Grain Geometry - Version 2.4 +Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) + +Permission to copy, use, modify, sell and distribute this software +is granted provided this copyright notice appears in all copies. +This software is provided "as is" without express or implied +warranty, and with no claim as to its suitability for any purpose. + + + + + +Modified BSD License +==================================================== +Anti-Grain Geometry - Version 2.4 +Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + diff --git a/xs/src/avrdude/AUTHORS b/src/avrdude/AUTHORS similarity index 100% rename from xs/src/avrdude/AUTHORS rename to src/avrdude/AUTHORS diff --git a/xs/src/avrdude/BUILD-FROM-SVN b/src/avrdude/BUILD-FROM-SVN similarity index 100% rename from xs/src/avrdude/BUILD-FROM-SVN rename to src/avrdude/BUILD-FROM-SVN diff --git a/xs/src/avrdude/CMakeLists.txt b/src/avrdude/CMakeLists.txt similarity index 100% rename from xs/src/avrdude/CMakeLists.txt rename to src/avrdude/CMakeLists.txt diff --git a/xs/src/avrdude/COPYING b/src/avrdude/COPYING similarity index 100% rename from xs/src/avrdude/COPYING rename to src/avrdude/COPYING diff --git a/xs/src/avrdude/ChangeLog b/src/avrdude/ChangeLog similarity index 100% rename from xs/src/avrdude/ChangeLog rename to src/avrdude/ChangeLog diff --git a/xs/src/avrdude/ChangeLog-2001 b/src/avrdude/ChangeLog-2001 similarity index 100% rename from xs/src/avrdude/ChangeLog-2001 rename to src/avrdude/ChangeLog-2001 diff --git a/xs/src/avrdude/ChangeLog-2002 b/src/avrdude/ChangeLog-2002 similarity index 100% rename from xs/src/avrdude/ChangeLog-2002 rename to src/avrdude/ChangeLog-2002 diff --git a/xs/src/avrdude/ChangeLog-2003 b/src/avrdude/ChangeLog-2003 similarity index 100% rename from xs/src/avrdude/ChangeLog-2003 rename to src/avrdude/ChangeLog-2003 diff --git a/xs/src/avrdude/ChangeLog-2004-2006 b/src/avrdude/ChangeLog-2004-2006 similarity index 100% rename from xs/src/avrdude/ChangeLog-2004-2006 rename to src/avrdude/ChangeLog-2004-2006 diff --git a/xs/src/avrdude/ChangeLog-2007 b/src/avrdude/ChangeLog-2007 similarity index 100% rename from xs/src/avrdude/ChangeLog-2007 rename to src/avrdude/ChangeLog-2007 diff --git a/xs/src/avrdude/ChangeLog-2008 b/src/avrdude/ChangeLog-2008 similarity index 100% rename from xs/src/avrdude/ChangeLog-2008 rename to src/avrdude/ChangeLog-2008 diff --git a/xs/src/avrdude/ChangeLog-2009 b/src/avrdude/ChangeLog-2009 similarity index 100% rename from xs/src/avrdude/ChangeLog-2009 rename to src/avrdude/ChangeLog-2009 diff --git a/xs/src/avrdude/ChangeLog-2010 b/src/avrdude/ChangeLog-2010 similarity index 100% rename from xs/src/avrdude/ChangeLog-2010 rename to src/avrdude/ChangeLog-2010 diff --git a/xs/src/avrdude/ChangeLog-2011 b/src/avrdude/ChangeLog-2011 similarity index 100% rename from xs/src/avrdude/ChangeLog-2011 rename to src/avrdude/ChangeLog-2011 diff --git a/xs/src/avrdude/ChangeLog-2012 b/src/avrdude/ChangeLog-2012 similarity index 100% rename from xs/src/avrdude/ChangeLog-2012 rename to src/avrdude/ChangeLog-2012 diff --git a/xs/src/avrdude/ChangeLog-2013 b/src/avrdude/ChangeLog-2013 similarity index 100% rename from xs/src/avrdude/ChangeLog-2013 rename to src/avrdude/ChangeLog-2013 diff --git a/xs/src/avrdude/ChangeLog-2014 b/src/avrdude/ChangeLog-2014 similarity index 100% rename from xs/src/avrdude/ChangeLog-2014 rename to src/avrdude/ChangeLog-2014 diff --git a/xs/src/avrdude/ChangeLog-2015 b/src/avrdude/ChangeLog-2015 similarity index 100% rename from xs/src/avrdude/ChangeLog-2015 rename to src/avrdude/ChangeLog-2015 diff --git a/xs/src/avrdude/Makefile.am b/src/avrdude/Makefile.am similarity index 100% rename from xs/src/avrdude/Makefile.am rename to src/avrdude/Makefile.am diff --git a/xs/src/avrdude/Makefile.standalone b/src/avrdude/Makefile.standalone similarity index 100% rename from xs/src/avrdude/Makefile.standalone rename to src/avrdude/Makefile.standalone diff --git a/xs/src/avrdude/NEWS b/src/avrdude/NEWS similarity index 100% rename from xs/src/avrdude/NEWS rename to src/avrdude/NEWS diff --git a/xs/src/avrdude/README b/src/avrdude/README similarity index 100% rename from xs/src/avrdude/README rename to src/avrdude/README diff --git a/xs/src/avrdude/ac_cfg.h b/src/avrdude/ac_cfg.h similarity index 100% rename from xs/src/avrdude/ac_cfg.h rename to src/avrdude/ac_cfg.h diff --git a/xs/src/avrdude/ac_cfg.h.in b/src/avrdude/ac_cfg.h.in similarity index 100% rename from xs/src/avrdude/ac_cfg.h.in rename to src/avrdude/ac_cfg.h.in diff --git a/xs/src/avrdude/arduino.c b/src/avrdude/arduino.c similarity index 100% rename from xs/src/avrdude/arduino.c rename to src/avrdude/arduino.c diff --git a/xs/src/avrdude/arduino.h b/src/avrdude/arduino.h similarity index 100% rename from xs/src/avrdude/arduino.h rename to src/avrdude/arduino.h diff --git a/xs/src/avrdude/atmel-docs/AVR109.pdf b/src/avrdude/atmel-docs/AVR109.pdf similarity index 100% rename from xs/src/avrdude/atmel-docs/AVR109.pdf rename to src/avrdude/atmel-docs/AVR109.pdf diff --git a/xs/src/avrdude/atmel-docs/AVR910.pdf b/src/avrdude/atmel-docs/AVR910.pdf similarity index 100% rename from xs/src/avrdude/atmel-docs/AVR910.pdf rename to src/avrdude/atmel-docs/AVR910.pdf diff --git a/xs/src/avrdude/atmel-docs/AVRISPmkII-AVR069.pdf b/src/avrdude/atmel-docs/AVRISPmkII-AVR069.pdf similarity index 100% rename from xs/src/avrdude/atmel-docs/AVRISPmkII-AVR069.pdf rename to src/avrdude/atmel-docs/AVRISPmkII-AVR069.pdf diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/browserDetect.js b/src/avrdude/atmel-docs/EDBG/common/browserDetect.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/browserDetect.js rename to src/avrdude/atmel-docs/EDBG/common/browserDetect.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/css/960.css b/src/avrdude/atmel-docs/EDBG/common/css/960.css similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/css/960.css rename to src/avrdude/atmel-docs/EDBG/common/css/960.css diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/css/docbook.css b/src/avrdude/atmel-docs/EDBG/common/css/docbook.css similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/css/docbook.css rename to src/avrdude/atmel-docs/EDBG/common/css/docbook.css diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/css/fluid_grid.css b/src/avrdude/atmel-docs/EDBG/common/css/fluid_grid.css similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/css/fluid_grid.css rename to src/avrdude/atmel-docs/EDBG/common/css/fluid_grid.css diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/css/index.css b/src/avrdude/atmel-docs/EDBG/common/css/index.css similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/css/index.css rename to src/avrdude/atmel-docs/EDBG/common/css/index.css diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/css/positioning.css b/src/avrdude/atmel-docs/EDBG/common/css/positioning.css similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/css/positioning.css rename to src/avrdude/atmel-docs/EDBG/common/css/positioning.css diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/css/print.css b/src/avrdude/atmel-docs/EDBG/common/css/print.css similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/css/print.css rename to src/avrdude/atmel-docs/EDBG/common/css/print.css diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/css/reset.css b/src/avrdude/atmel-docs/EDBG/common/css/reset.css similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/css/reset.css rename to src/avrdude/atmel-docs/EDBG/common/css/reset.css diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/css/text.css b/src/avrdude/atmel-docs/EDBG/common/css/text.css similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/css/text.css rename to src/avrdude/atmel-docs/EDBG/common/css/text.css diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/fonts/DroidSansMono.eot b/src/avrdude/atmel-docs/EDBG/common/fonts/DroidSansMono.eot similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/fonts/DroidSansMono.eot rename to src/avrdude/atmel-docs/EDBG/common/fonts/DroidSansMono.eot diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/fonts/DroidSansMono.ttf b/src/avrdude/atmel-docs/EDBG/common/fonts/DroidSansMono.ttf similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/fonts/DroidSansMono.ttf rename to src/avrdude/atmel-docs/EDBG/common/fonts/DroidSansMono.ttf diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/Book_Closed.png b/src/avrdude/atmel-docs/EDBG/common/images/Book_Closed.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/Book_Closed.png rename to src/avrdude/atmel-docs/EDBG/common/images/Book_Closed.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/Book_Open.png b/src/avrdude/atmel-docs/EDBG/common/images/Book_Open.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/Book_Open.png rename to src/avrdude/atmel-docs/EDBG/common/images/Book_Open.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/Document_Text.png b/src/avrdude/atmel-docs/EDBG/common/images/Document_Text.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/Document_Text.png rename to src/avrdude/atmel-docs/EDBG/common/images/Document_Text.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/Library.png b/src/avrdude/atmel-docs/EDBG/common/images/Library.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/Library.png rename to src/avrdude/atmel-docs/EDBG/common/images/Library.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/external_link.gif b/src/avrdude/atmel-docs/EDBG/common/images/external_link.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/external_link.gif rename to src/avrdude/atmel-docs/EDBG/common/images/external_link.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/loading.gif b/src/avrdude/atmel-docs/EDBG/common/images/loading.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/loading.gif rename to src/avrdude/atmel-docs/EDBG/common/images/loading.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/logo.png b/src/avrdude/atmel-docs/EDBG/common/images/logo.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/logo.png rename to src/avrdude/atmel-docs/EDBG/common/images/logo.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/minus.png b/src/avrdude/atmel-docs/EDBG/common/images/minus.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/minus.png rename to src/avrdude/atmel-docs/EDBG/common/images/minus.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/next-arrow.png b/src/avrdude/atmel-docs/EDBG/common/images/next-arrow.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/next-arrow.png rename to src/avrdude/atmel-docs/EDBG/common/images/next-arrow.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/plus.png b/src/avrdude/atmel-docs/EDBG/common/images/plus.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/plus.png rename to src/avrdude/atmel-docs/EDBG/common/images/plus.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/previous-arrow.png b/src/avrdude/atmel-docs/EDBG/common/images/previous-arrow.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/previous-arrow.png rename to src/avrdude/atmel-docs/EDBG/common/images/previous-arrow.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/search-icon.png b/src/avrdude/atmel-docs/EDBG/common/images/search-icon.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/search-icon.png rename to src/avrdude/atmel-docs/EDBG/common/images/search-icon.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/sidebar.png b/src/avrdude/atmel-docs/EDBG/common/images/sidebar.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/sidebar.png rename to src/avrdude/atmel-docs/EDBG/common/images/sidebar.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/starsSmall.png b/src/avrdude/atmel-docs/EDBG/common/images/starsSmall.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/starsSmall.png rename to src/avrdude/atmel-docs/EDBG/common/images/starsSmall.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/toc-icon.png b/src/avrdude/atmel-docs/EDBG/common/images/toc-icon.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/toc-icon.png rename to src/avrdude/atmel-docs/EDBG/common/images/toc-icon.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/images/ui-icons_217bc0_256x240.png b/src/avrdude/atmel-docs/EDBG/common/images/ui-icons_217bc0_256x240.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/images/ui-icons_217bc0_256x240.png rename to src/avrdude/atmel-docs/EDBG/common/images/ui-icons_217bc0_256x240.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/jquery-1.7.2.min.js b/src/avrdude/atmel-docs/EDBG/common/jquery/jquery-1.7.2.min.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/jquery-1.7.2.min.js rename to src/avrdude/atmel-docs/EDBG/common/jquery/jquery-1.7.2.min.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/jquery.cookie.js b/src/avrdude/atmel-docs/EDBG/common/jquery/jquery.cookie.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/jquery.cookie.js rename to src/avrdude/atmel-docs/EDBG/common/jquery/jquery.cookie.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/jquery.ui.all.js b/src/avrdude/atmel-docs/EDBG/common/jquery/jquery.ui.all.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/jquery.ui.all.js rename to src/avrdude/atmel-docs/EDBG/common/jquery/jquery.ui.all.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/layout/jquery.layout.js b/src/avrdude/atmel-docs/EDBG/common/jquery/layout/jquery.layout.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/layout/jquery.layout.js rename to src/avrdude/atmel-docs/EDBG/common/jquery/layout/jquery.layout.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-anim_basic_16x16.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-anim_basic_16x16.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-anim_basic_16x16.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-anim_basic_16x16.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_flat_0_aaaaaa_40x100.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_flat_0_aaaaaa_40x100.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_flat_0_aaaaaa_40x100.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_flat_0_aaaaaa_40x100.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_flat_55_fbec88_40x100.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_flat_55_fbec88_40x100.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_flat_55_fbec88_40x100.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_flat_55_fbec88_40x100.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_75_d0e5f5_1x400.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_75_d0e5f5_1x400.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_75_d0e5f5_1x400.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_75_d0e5f5_1x400.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_85_dfeffc_1x400.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_85_dfeffc_1x400.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_85_dfeffc_1x400.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_85_dfeffc_1x400.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_95_fef1ec_1x400.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_95_fef1ec_1x400.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_95_fef1ec_1x400.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_glass_95_fef1ec_1x400.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_217bc0_256x240.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_217bc0_256x240.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_217bc0_256x240.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_217bc0_256x240.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_2e83ff_256x240.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_2e83ff_256x240.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_2e83ff_256x240.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_2e83ff_256x240.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_469bdd_256x240.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_469bdd_256x240.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_469bdd_256x240.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_469bdd_256x240.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_6da8d5_256x240.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_6da8d5_256x240.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_6da8d5_256x240.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_6da8d5_256x240.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_cd0a0a_256x240.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_cd0a0a_256x240.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_cd0a0a_256x240.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_cd0a0a_256x240.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_d8e7f3_256x240.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_d8e7f3_256x240.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_d8e7f3_256x240.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_d8e7f3_256x240.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_f9bd01_256x240.png b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_f9bd01_256x240.png similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_f9bd01_256x240.png rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/images/ui-icons_f9bd01_256x240.png diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/jquery-ui-1.8.2.custom.css b/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/jquery-ui-1.8.2.custom.css similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/jquery-ui-1.8.2.custom.css rename to src/avrdude/atmel-docs/EDBG/common/jquery/theme-redmond/jquery-ui-1.8.2.custom.css diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/file.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/file.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/file.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/file.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/folder-closed.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/folder-closed.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/folder-closed.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/folder-closed.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/folder.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/folder.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/folder.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/folder.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-black-line.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-black-line.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-black-line.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-black-line.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-black.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-black.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-black.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-black.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-default-line.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-default-line.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-default-line.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-default-line.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-default.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-default.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-default.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-default.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-famfamfam-line.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-famfamfam-line.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-famfamfam-line.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-famfamfam-line.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-famfamfam.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-famfamfam.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-famfamfam.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-famfamfam.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-gray-line.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-gray-line.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-gray-line.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-gray-line.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-gray.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-gray.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-gray.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-gray.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-red-line.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-red-line.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-red-line.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-red-line.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-red.gif b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-red.gif similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-red.gif rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/images/treeview-red.gif diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/jquery.treeview.css b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/jquery.treeview.css similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/jquery.treeview.css rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/jquery.treeview.css diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/jquery.treeview.min.js b/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/jquery.treeview.min.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/jquery/treeview/jquery.treeview.min.js rename to src/avrdude/atmel-docs/EDBG/common/jquery/treeview/jquery.treeview.min.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/main.js b/src/avrdude/atmel-docs/EDBG/common/main.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/main.js rename to src/avrdude/atmel-docs/EDBG/common/main.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/common/splitterInit.js b/src/avrdude/atmel-docs/EDBG/common/splitterInit.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/common/splitterInit.js rename to src/avrdude/atmel-docs/EDBG/common/splitterInit.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch01s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch01s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch01s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch01s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch01s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch01s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch01s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch01s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s02s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s02s03s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s06.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s06.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s06.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s06.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s07.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s07.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s07.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s07.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s08.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s08.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s08.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s08.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s09.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s09.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s09.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s03s09.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch02s04s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s03s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s03s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s03s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s03s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s02s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s04s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s06s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s07s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s08s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s09.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s09.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s09.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s09.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s10.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s10.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s10.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch04s05s10.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s06.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s06.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s06.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s06.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s07.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s07.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s07.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s07.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s08.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s08.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s08.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s08.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s09.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s09.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s09.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s09.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s10.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s10.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s10.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s10.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s11.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s11.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s11.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s11.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s12.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s12.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s12.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s12.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s13.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s13.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s13.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s13.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s14.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s14.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s14.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s14.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s15.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s15.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s15.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s01s15.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s06.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s06.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s06.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s02s06.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s03s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch05s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s06.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s06.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s06.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s06.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s07.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s07.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s07.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s07.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s08.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s08.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s08.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s08.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s09.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s09.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s09.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s09.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s10.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s10.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s10.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s10.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s11.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s11.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s11.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s11.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s12.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s12.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s12.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s12.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s13.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s13.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s13.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s13.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s14.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s14.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s14.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s14.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s15.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s15.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s15.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s15.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s16.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s16.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s16.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s16.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s17.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s17.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s17.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s17.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s18.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s18.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s18.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s18.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s19.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s19.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s19.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s19.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s20.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s20.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s20.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s20.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s21.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s21.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s21.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s21.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s22.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s22.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s22.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s22.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s23.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s23.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s23.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s23.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s24.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s24.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s24.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s24.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s25.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s25.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s25.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s25.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s26.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s26.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s26.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s26.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s27.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s27.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s27.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s27.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s28.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s28.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s28.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s28.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s29.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s29.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s29.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s01s29.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s02s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s03s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s04s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s05s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s06.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s06.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s06.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch06s06.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s06.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s06.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s06.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s06.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s07.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s07.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s07.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s07.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s08.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s08.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s08.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s08.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s09.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s09.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s09.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s09.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s10.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s10.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s10.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s10.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s11.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s11.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s11.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s11.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s12.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s12.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s12.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s12.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s13.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s13.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s13.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s13.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s14.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s14.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s14.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s14.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s15.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s15.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s15.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s15.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s16.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s16.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s16.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s16.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s17.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s17.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s17.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s01s17.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch07s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s04.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s04.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s04.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s04.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s05.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s05.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s05.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s05.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s06.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s06.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s06.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s01s06.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s02.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s02.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s02.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s02.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s03.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s03.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s03.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/ch08s03.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/document.revisions.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/document.revisions.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/document.revisions.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/document.revisions.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/index.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/index.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/index.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/index.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/pr01.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/pr01.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/pr01.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/pr01.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.Introduction.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.Introduction.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.Introduction.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.Introduction.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avr32protocol.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avr32protocol.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avr32protocol.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avr32protocol.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avr8protocol.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avr8protocol.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avr8protocol.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avr8protocol.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avrispprotocol.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avrispprotocol.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avrispprotocol.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avrispprotocol.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avrprotocol.Overview.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avrprotocol.Overview.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avrprotocol.Overview.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.avrprotocol.Overview.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.cmsis_dap.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.cmsis_dap.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.cmsis_dap.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.cmsis_dap.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.edbg_ctrl_protocol.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.edbg_ctrl_protocol.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.edbg_ctrl_protocol.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.edbg_ctrl_protocol.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.tpiprotocol.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.tpiprotocol.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.tpiprotocol.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/protocoldocs.tpiprotocol.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/htmlFileInfoList.js b/src/avrdude/atmel-docs/EDBG/protocoldocs/search/htmlFileInfoList.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/htmlFileInfoList.js rename to src/avrdude/atmel-docs/EDBG/protocoldocs/search/htmlFileInfoList.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-1.js b/src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-1.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-1.js rename to src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-1.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-2.js b/src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-2.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-2.js rename to src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-2.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-3.js b/src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-3.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-3.js rename to src/avrdude/atmel-docs/EDBG/protocoldocs/search/index-3.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/l10n.js b/src/avrdude/atmel-docs/EDBG/protocoldocs/search/l10n.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/l10n.js rename to src/avrdude/atmel-docs/EDBG/protocoldocs/search/l10n.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/nwSearchFnt.js b/src/avrdude/atmel-docs/EDBG/protocoldocs/search/nwSearchFnt.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/nwSearchFnt.js rename to src/avrdude/atmel-docs/EDBG/protocoldocs/search/nwSearchFnt.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/stemmers/en_stemmer.js b/src/avrdude/atmel-docs/EDBG/protocoldocs/search/stemmers/en_stemmer.js similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/search/stemmers/en_stemmer.js rename to src/avrdude/atmel-docs/EDBG/protocoldocs/search/stemmers/en_stemmer.js diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr32_memtypes.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr32_memtypes.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr32_memtypes.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr32_memtypes.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr32_setget_params.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr32_setget_params.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr32_setget_params.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr32_setget_params.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_memtypes.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_memtypes.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_memtypes.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_memtypes.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_query_contexts.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_query_contexts.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_query_contexts.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_query_contexts.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_setget_params.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_setget_params.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_setget_params.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_avr8_setget_params.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_edbg_ctrl_setget_params.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_edbg_ctrl_setget_params.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_edbg_ctrl_setget_params.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_edbg_ctrl_setget_params.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_edbg_query_contexts.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_edbg_query_contexts.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_edbg_query_contexts.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_edbg_query_contexts.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_housekeeping_start_session.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_housekeeping_start_session.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_housekeeping_start_session.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_housekeeping_start_session.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_i5v_3yz_rl.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_i5v_3yz_rl.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_i5v_3yz_rl.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_i5v_3yz_rl.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_jdx_m11_sl.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_jdx_m11_sl.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_jdx_m11_sl.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_jdx_m11_sl.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_qhb_x1c_sl.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_qhb_x1c_sl.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_qhb_x1c_sl.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_qhb_x1c_sl.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_serial_trace.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_serial_trace.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_serial_trace.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_serial_trace.html diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_t1f_hb1_sl.html b/src/avrdude/atmel-docs/EDBG/protocoldocs/section_t1f_hb1_sl.html similarity index 100% rename from xs/src/avrdude/atmel-docs/EDBG/protocoldocs/section_t1f_hb1_sl.html rename to src/avrdude/atmel-docs/EDBG/protocoldocs/section_t1f_hb1_sl.html diff --git a/xs/src/avrdude/atmel-docs/JTAGICE-AVR060.pdf b/src/avrdude/atmel-docs/JTAGICE-AVR060.pdf similarity index 100% rename from xs/src/avrdude/atmel-docs/JTAGICE-AVR060.pdf rename to src/avrdude/atmel-docs/JTAGICE-AVR060.pdf diff --git a/xs/src/avrdude/atmel-docs/JTAGICEmkII-AVR067.pdf b/src/avrdude/atmel-docs/JTAGICEmkII-AVR067.pdf similarity index 100% rename from xs/src/avrdude/atmel-docs/JTAGICEmkII-AVR067.pdf rename to src/avrdude/atmel-docs/JTAGICEmkII-AVR067.pdf diff --git a/xs/src/avrdude/atmel-docs/STK500-AVR061.pdf b/src/avrdude/atmel-docs/STK500-AVR061.pdf similarity index 100% rename from xs/src/avrdude/atmel-docs/STK500-AVR061.pdf rename to src/avrdude/atmel-docs/STK500-AVR061.pdf diff --git a/xs/src/avrdude/atmel-docs/STK500v2-AVR068.pdf b/src/avrdude/atmel-docs/STK500v2-AVR068.pdf similarity index 100% rename from xs/src/avrdude/atmel-docs/STK500v2-AVR068.pdf rename to src/avrdude/atmel-docs/STK500v2-AVR068.pdf diff --git a/xs/src/avrdude/avr.c b/src/avrdude/avr.c similarity index 100% rename from xs/src/avrdude/avr.c rename to src/avrdude/avr.c diff --git a/xs/src/avrdude/avr910.c b/src/avrdude/avr910.c similarity index 100% rename from xs/src/avrdude/avr910.c rename to src/avrdude/avr910.c diff --git a/xs/src/avrdude/avr910.h b/src/avrdude/avr910.h similarity index 100% rename from xs/src/avrdude/avr910.h rename to src/avrdude/avr910.h diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/src/avrdude/avrdude-slic3r.cpp similarity index 100% rename from xs/src/avrdude/avrdude-slic3r.cpp rename to src/avrdude/avrdude-slic3r.cpp diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/src/avrdude/avrdude-slic3r.hpp similarity index 100% rename from xs/src/avrdude/avrdude-slic3r.hpp rename to src/avrdude/avrdude-slic3r.hpp diff --git a/xs/src/avrdude/avrdude.1 b/src/avrdude/avrdude.1 similarity index 100% rename from xs/src/avrdude/avrdude.1 rename to src/avrdude/avrdude.1 diff --git a/xs/src/avrdude/avrdude.conf b/src/avrdude/avrdude.conf similarity index 100% rename from xs/src/avrdude/avrdude.conf rename to src/avrdude/avrdude.conf diff --git a/xs/src/avrdude/avrdude.conf.in b/src/avrdude/avrdude.conf.in similarity index 100% rename from xs/src/avrdude/avrdude.conf.in rename to src/avrdude/avrdude.conf.in diff --git a/xs/src/avrdude/avrdude.conf.tmp b/src/avrdude/avrdude.conf.tmp similarity index 100% rename from xs/src/avrdude/avrdude.conf.tmp rename to src/avrdude/avrdude.conf.tmp diff --git a/xs/src/avrdude/avrdude.h b/src/avrdude/avrdude.h similarity index 100% rename from xs/src/avrdude/avrdude.h rename to src/avrdude/avrdude.h diff --git a/xs/src/avrdude/avrdude.spec.in b/src/avrdude/avrdude.spec.in similarity index 100% rename from xs/src/avrdude/avrdude.spec.in rename to src/avrdude/avrdude.spec.in diff --git a/xs/src/avrdude/avrftdi.c b/src/avrdude/avrftdi.c similarity index 100% rename from xs/src/avrdude/avrftdi.c rename to src/avrdude/avrftdi.c diff --git a/xs/src/avrdude/avrftdi.h b/src/avrdude/avrftdi.h similarity index 100% rename from xs/src/avrdude/avrftdi.h rename to src/avrdude/avrftdi.h diff --git a/xs/src/avrdude/avrftdi_private.h b/src/avrdude/avrftdi_private.h similarity index 100% rename from xs/src/avrdude/avrftdi_private.h rename to src/avrdude/avrftdi_private.h diff --git a/xs/src/avrdude/avrftdi_tpi.c b/src/avrdude/avrftdi_tpi.c similarity index 100% rename from xs/src/avrdude/avrftdi_tpi.c rename to src/avrdude/avrftdi_tpi.c diff --git a/xs/src/avrdude/avrftdi_tpi.h b/src/avrdude/avrftdi_tpi.h similarity index 100% rename from xs/src/avrdude/avrftdi_tpi.h rename to src/avrdude/avrftdi_tpi.h diff --git a/xs/src/avrdude/avrpart.c b/src/avrdude/avrpart.c similarity index 100% rename from xs/src/avrdude/avrpart.c rename to src/avrdude/avrpart.c diff --git a/xs/src/avrdude/bitbang.c b/src/avrdude/bitbang.c similarity index 100% rename from xs/src/avrdude/bitbang.c rename to src/avrdude/bitbang.c diff --git a/xs/src/avrdude/bitbang.h b/src/avrdude/bitbang.h similarity index 100% rename from xs/src/avrdude/bitbang.h rename to src/avrdude/bitbang.h diff --git a/xs/src/avrdude/bootstrap b/src/avrdude/bootstrap old mode 100755 new mode 100644 similarity index 100% rename from xs/src/avrdude/bootstrap rename to src/avrdude/bootstrap diff --git a/xs/src/avrdude/buspirate.c b/src/avrdude/buspirate.c similarity index 100% rename from xs/src/avrdude/buspirate.c rename to src/avrdude/buspirate.c diff --git a/xs/src/avrdude/buspirate.h b/src/avrdude/buspirate.h similarity index 100% rename from xs/src/avrdude/buspirate.h rename to src/avrdude/buspirate.h diff --git a/xs/src/avrdude/butterfly.c b/src/avrdude/butterfly.c similarity index 100% rename from xs/src/avrdude/butterfly.c rename to src/avrdude/butterfly.c diff --git a/xs/src/avrdude/butterfly.h b/src/avrdude/butterfly.h similarity index 100% rename from xs/src/avrdude/butterfly.h rename to src/avrdude/butterfly.h diff --git a/xs/src/avrdude/config.c b/src/avrdude/config.c similarity index 100% rename from xs/src/avrdude/config.c rename to src/avrdude/config.c diff --git a/xs/src/avrdude/config.h b/src/avrdude/config.h similarity index 100% rename from xs/src/avrdude/config.h rename to src/avrdude/config.h diff --git a/xs/src/avrdude/config_gram.c b/src/avrdude/config_gram.c similarity index 100% rename from xs/src/avrdude/config_gram.c rename to src/avrdude/config_gram.c diff --git a/xs/src/avrdude/config_gram.h b/src/avrdude/config_gram.h similarity index 100% rename from xs/src/avrdude/config_gram.h rename to src/avrdude/config_gram.h diff --git a/xs/src/avrdude/config_gram.y b/src/avrdude/config_gram.y similarity index 100% rename from xs/src/avrdude/config_gram.y rename to src/avrdude/config_gram.y diff --git a/xs/src/avrdude/configure.ac b/src/avrdude/configure.ac similarity index 100% rename from xs/src/avrdude/configure.ac rename to src/avrdude/configure.ac diff --git a/xs/src/avrdude/confwin.c b/src/avrdude/confwin.c similarity index 100% rename from xs/src/avrdude/confwin.c rename to src/avrdude/confwin.c diff --git a/xs/src/avrdude/crc16.c b/src/avrdude/crc16.c similarity index 100% rename from xs/src/avrdude/crc16.c rename to src/avrdude/crc16.c diff --git a/xs/src/avrdude/crc16.h b/src/avrdude/crc16.h similarity index 100% rename from xs/src/avrdude/crc16.h rename to src/avrdude/crc16.h diff --git a/xs/src/avrdude/dfu.c b/src/avrdude/dfu.c similarity index 100% rename from xs/src/avrdude/dfu.c rename to src/avrdude/dfu.c diff --git a/xs/src/avrdude/dfu.h b/src/avrdude/dfu.h similarity index 100% rename from xs/src/avrdude/dfu.h rename to src/avrdude/dfu.h diff --git a/xs/src/avrdude/doc/.cvsignore b/src/avrdude/doc/.cvsignore similarity index 100% rename from xs/src/avrdude/doc/.cvsignore rename to src/avrdude/doc/.cvsignore diff --git a/xs/src/avrdude/doc/Makefile.am b/src/avrdude/doc/Makefile.am similarity index 100% rename from xs/src/avrdude/doc/Makefile.am rename to src/avrdude/doc/Makefile.am diff --git a/xs/src/avrdude/doc/TODO b/src/avrdude/doc/TODO similarity index 100% rename from xs/src/avrdude/doc/TODO rename to src/avrdude/doc/TODO diff --git a/xs/src/avrdude/doc/avrdude.texi b/src/avrdude/doc/avrdude.texi similarity index 100% rename from xs/src/avrdude/doc/avrdude.texi rename to src/avrdude/doc/avrdude.texi diff --git a/xs/src/avrdude/doc/parts_comments.txt b/src/avrdude/doc/parts_comments.txt similarity index 100% rename from xs/src/avrdude/doc/parts_comments.txt rename to src/avrdude/doc/parts_comments.txt diff --git a/xs/src/avrdude/fileio.c b/src/avrdude/fileio.c similarity index 100% rename from xs/src/avrdude/fileio.c rename to src/avrdude/fileio.c diff --git a/xs/src/avrdude/flip1.c b/src/avrdude/flip1.c similarity index 100% rename from xs/src/avrdude/flip1.c rename to src/avrdude/flip1.c diff --git a/xs/src/avrdude/flip1.h b/src/avrdude/flip1.h similarity index 100% rename from xs/src/avrdude/flip1.h rename to src/avrdude/flip1.h diff --git a/xs/src/avrdude/flip2.c b/src/avrdude/flip2.c similarity index 100% rename from xs/src/avrdude/flip2.c rename to src/avrdude/flip2.c diff --git a/xs/src/avrdude/flip2.h b/src/avrdude/flip2.h similarity index 100% rename from xs/src/avrdude/flip2.h rename to src/avrdude/flip2.h diff --git a/xs/src/avrdude/freebsd_ppi.h b/src/avrdude/freebsd_ppi.h similarity index 100% rename from xs/src/avrdude/freebsd_ppi.h rename to src/avrdude/freebsd_ppi.h diff --git a/xs/src/avrdude/ft245r.c b/src/avrdude/ft245r.c similarity index 100% rename from xs/src/avrdude/ft245r.c rename to src/avrdude/ft245r.c diff --git a/xs/src/avrdude/ft245r.h b/src/avrdude/ft245r.h similarity index 100% rename from xs/src/avrdude/ft245r.h rename to src/avrdude/ft245r.h diff --git a/xs/src/avrdude/jtag3.c b/src/avrdude/jtag3.c similarity index 100% rename from xs/src/avrdude/jtag3.c rename to src/avrdude/jtag3.c diff --git a/xs/src/avrdude/jtag3.h b/src/avrdude/jtag3.h similarity index 100% rename from xs/src/avrdude/jtag3.h rename to src/avrdude/jtag3.h diff --git a/xs/src/avrdude/jtag3_private.h b/src/avrdude/jtag3_private.h similarity index 100% rename from xs/src/avrdude/jtag3_private.h rename to src/avrdude/jtag3_private.h diff --git a/xs/src/avrdude/jtagmkI.c b/src/avrdude/jtagmkI.c similarity index 100% rename from xs/src/avrdude/jtagmkI.c rename to src/avrdude/jtagmkI.c diff --git a/xs/src/avrdude/jtagmkI.h b/src/avrdude/jtagmkI.h similarity index 100% rename from xs/src/avrdude/jtagmkI.h rename to src/avrdude/jtagmkI.h diff --git a/xs/src/avrdude/jtagmkII.c b/src/avrdude/jtagmkII.c similarity index 100% rename from xs/src/avrdude/jtagmkII.c rename to src/avrdude/jtagmkII.c diff --git a/xs/src/avrdude/jtagmkII.h b/src/avrdude/jtagmkII.h similarity index 100% rename from xs/src/avrdude/jtagmkII.h rename to src/avrdude/jtagmkII.h diff --git a/xs/src/avrdude/jtagmkII_private.h b/src/avrdude/jtagmkII_private.h similarity index 100% rename from xs/src/avrdude/jtagmkII_private.h rename to src/avrdude/jtagmkII_private.h diff --git a/xs/src/avrdude/jtagmkI_private.h b/src/avrdude/jtagmkI_private.h similarity index 100% rename from xs/src/avrdude/jtagmkI_private.h rename to src/avrdude/jtagmkI_private.h diff --git a/xs/src/avrdude/lexer.c b/src/avrdude/lexer.c similarity index 100% rename from xs/src/avrdude/lexer.c rename to src/avrdude/lexer.c diff --git a/xs/src/avrdude/lexer.l b/src/avrdude/lexer.l similarity index 100% rename from xs/src/avrdude/lexer.l rename to src/avrdude/lexer.l diff --git a/xs/src/avrdude/libavrdude.h b/src/avrdude/libavrdude.h similarity index 100% rename from xs/src/avrdude/libavrdude.h rename to src/avrdude/libavrdude.h diff --git a/xs/src/avrdude/linux_ppdev.h b/src/avrdude/linux_ppdev.h similarity index 100% rename from xs/src/avrdude/linux_ppdev.h rename to src/avrdude/linux_ppdev.h diff --git a/xs/src/avrdude/linuxgpio.c b/src/avrdude/linuxgpio.c similarity index 100% rename from xs/src/avrdude/linuxgpio.c rename to src/avrdude/linuxgpio.c diff --git a/xs/src/avrdude/linuxgpio.h b/src/avrdude/linuxgpio.h similarity index 100% rename from xs/src/avrdude/linuxgpio.h rename to src/avrdude/linuxgpio.h diff --git a/xs/src/avrdude/lists.c b/src/avrdude/lists.c similarity index 100% rename from xs/src/avrdude/lists.c rename to src/avrdude/lists.c diff --git a/xs/src/avrdude/main-standalone.c b/src/avrdude/main-standalone.c similarity index 100% rename from xs/src/avrdude/main-standalone.c rename to src/avrdude/main-standalone.c diff --git a/xs/src/avrdude/main.c b/src/avrdude/main.c similarity index 100% rename from xs/src/avrdude/main.c rename to src/avrdude/main.c diff --git a/xs/src/avrdude/my_ddk_hidsdi.h b/src/avrdude/my_ddk_hidsdi.h similarity index 100% rename from xs/src/avrdude/my_ddk_hidsdi.h rename to src/avrdude/my_ddk_hidsdi.h diff --git a/xs/src/avrdude/par.c b/src/avrdude/par.c similarity index 100% rename from xs/src/avrdude/par.c rename to src/avrdude/par.c diff --git a/xs/src/avrdude/par.h b/src/avrdude/par.h similarity index 100% rename from xs/src/avrdude/par.h rename to src/avrdude/par.h diff --git a/xs/src/avrdude/pgm.c b/src/avrdude/pgm.c similarity index 100% rename from xs/src/avrdude/pgm.c rename to src/avrdude/pgm.c diff --git a/xs/src/avrdude/pgm_type.c b/src/avrdude/pgm_type.c similarity index 100% rename from xs/src/avrdude/pgm_type.c rename to src/avrdude/pgm_type.c diff --git a/xs/src/avrdude/pickit2.c b/src/avrdude/pickit2.c similarity index 100% rename from xs/src/avrdude/pickit2.c rename to src/avrdude/pickit2.c diff --git a/xs/src/avrdude/pickit2.h b/src/avrdude/pickit2.h similarity index 100% rename from xs/src/avrdude/pickit2.h rename to src/avrdude/pickit2.h diff --git a/xs/src/avrdude/pindefs.c b/src/avrdude/pindefs.c similarity index 100% rename from xs/src/avrdude/pindefs.c rename to src/avrdude/pindefs.c diff --git a/xs/src/avrdude/ppi.c b/src/avrdude/ppi.c similarity index 100% rename from xs/src/avrdude/ppi.c rename to src/avrdude/ppi.c diff --git a/xs/src/avrdude/ppi.h b/src/avrdude/ppi.h similarity index 100% rename from xs/src/avrdude/ppi.h rename to src/avrdude/ppi.h diff --git a/xs/src/avrdude/ppiwin.c b/src/avrdude/ppiwin.c similarity index 100% rename from xs/src/avrdude/ppiwin.c rename to src/avrdude/ppiwin.c diff --git a/xs/src/avrdude/safemode.c b/src/avrdude/safemode.c similarity index 100% rename from xs/src/avrdude/safemode.c rename to src/avrdude/safemode.c diff --git a/xs/src/avrdude/ser_avrdoper.c b/src/avrdude/ser_avrdoper.c similarity index 100% rename from xs/src/avrdude/ser_avrdoper.c rename to src/avrdude/ser_avrdoper.c diff --git a/xs/src/avrdude/ser_posix.c b/src/avrdude/ser_posix.c similarity index 100% rename from xs/src/avrdude/ser_posix.c rename to src/avrdude/ser_posix.c diff --git a/xs/src/avrdude/ser_win32.c b/src/avrdude/ser_win32.c similarity index 100% rename from xs/src/avrdude/ser_win32.c rename to src/avrdude/ser_win32.c diff --git a/xs/src/avrdude/serbb.h b/src/avrdude/serbb.h similarity index 100% rename from xs/src/avrdude/serbb.h rename to src/avrdude/serbb.h diff --git a/xs/src/avrdude/serbb_posix.c b/src/avrdude/serbb_posix.c similarity index 100% rename from xs/src/avrdude/serbb_posix.c rename to src/avrdude/serbb_posix.c diff --git a/xs/src/avrdude/serbb_win32.c b/src/avrdude/serbb_win32.c similarity index 100% rename from xs/src/avrdude/serbb_win32.c rename to src/avrdude/serbb_win32.c diff --git a/xs/src/avrdude/solaris_ecpp.h b/src/avrdude/solaris_ecpp.h similarity index 100% rename from xs/src/avrdude/solaris_ecpp.h rename to src/avrdude/solaris_ecpp.h diff --git a/xs/src/avrdude/stk500.c b/src/avrdude/stk500.c similarity index 100% rename from xs/src/avrdude/stk500.c rename to src/avrdude/stk500.c diff --git a/xs/src/avrdude/stk500.h b/src/avrdude/stk500.h similarity index 100% rename from xs/src/avrdude/stk500.h rename to src/avrdude/stk500.h diff --git a/xs/src/avrdude/stk500_private.h b/src/avrdude/stk500_private.h similarity index 100% rename from xs/src/avrdude/stk500_private.h rename to src/avrdude/stk500_private.h diff --git a/xs/src/avrdude/stk500generic.c b/src/avrdude/stk500generic.c similarity index 100% rename from xs/src/avrdude/stk500generic.c rename to src/avrdude/stk500generic.c diff --git a/xs/src/avrdude/stk500generic.h b/src/avrdude/stk500generic.h similarity index 100% rename from xs/src/avrdude/stk500generic.h rename to src/avrdude/stk500generic.h diff --git a/xs/src/avrdude/stk500v2.c b/src/avrdude/stk500v2.c similarity index 100% rename from xs/src/avrdude/stk500v2.c rename to src/avrdude/stk500v2.c diff --git a/xs/src/avrdude/stk500v2.h b/src/avrdude/stk500v2.h similarity index 100% rename from xs/src/avrdude/stk500v2.h rename to src/avrdude/stk500v2.h diff --git a/xs/src/avrdude/stk500v2_private.h b/src/avrdude/stk500v2_private.h similarity index 100% rename from xs/src/avrdude/stk500v2_private.h rename to src/avrdude/stk500v2_private.h diff --git a/xs/src/avrdude/term.c b/src/avrdude/term.c similarity index 100% rename from xs/src/avrdude/term.c rename to src/avrdude/term.c diff --git a/xs/src/avrdude/term.h b/src/avrdude/term.h similarity index 100% rename from xs/src/avrdude/term.h rename to src/avrdude/term.h diff --git a/xs/src/avrdude/tools/build-mingw32.sh b/src/avrdude/tools/build-mingw32.sh old mode 100755 new mode 100644 similarity index 100% rename from xs/src/avrdude/tools/build-mingw32.sh rename to src/avrdude/tools/build-mingw32.sh diff --git a/xs/src/avrdude/tools/get-dw-params.xsl b/src/avrdude/tools/get-dw-params.xsl similarity index 100% rename from xs/src/avrdude/tools/get-dw-params.xsl rename to src/avrdude/tools/get-dw-params.xsl diff --git a/xs/src/avrdude/tools/get-hv-params.xsl b/src/avrdude/tools/get-hv-params.xsl similarity index 100% rename from xs/src/avrdude/tools/get-hv-params.xsl rename to src/avrdude/tools/get-hv-params.xsl diff --git a/xs/src/avrdude/tools/get-stk600-cards.xsl b/src/avrdude/tools/get-stk600-cards.xsl similarity index 100% rename from xs/src/avrdude/tools/get-stk600-cards.xsl rename to src/avrdude/tools/get-stk600-cards.xsl diff --git a/xs/src/avrdude/tools/get-stk600-devices.xsl b/src/avrdude/tools/get-stk600-devices.xsl similarity index 100% rename from xs/src/avrdude/tools/get-stk600-devices.xsl rename to src/avrdude/tools/get-stk600-devices.xsl diff --git a/xs/src/avrdude/tpi.h b/src/avrdude/tpi.h similarity index 100% rename from xs/src/avrdude/tpi.h rename to src/avrdude/tpi.h diff --git a/xs/src/avrdude/update.c b/src/avrdude/update.c similarity index 100% rename from xs/src/avrdude/update.c rename to src/avrdude/update.c diff --git a/xs/src/avrdude/usb_hidapi.c b/src/avrdude/usb_hidapi.c similarity index 100% rename from xs/src/avrdude/usb_hidapi.c rename to src/avrdude/usb_hidapi.c diff --git a/xs/src/avrdude/usb_libusb.c b/src/avrdude/usb_libusb.c similarity index 100% rename from xs/src/avrdude/usb_libusb.c rename to src/avrdude/usb_libusb.c diff --git a/xs/src/avrdude/usbasp.c b/src/avrdude/usbasp.c similarity index 100% rename from xs/src/avrdude/usbasp.c rename to src/avrdude/usbasp.c diff --git a/xs/src/avrdude/usbasp.h b/src/avrdude/usbasp.h similarity index 100% rename from xs/src/avrdude/usbasp.h rename to src/avrdude/usbasp.h diff --git a/xs/src/avrdude/usbdevs.h b/src/avrdude/usbdevs.h similarity index 100% rename from xs/src/avrdude/usbdevs.h rename to src/avrdude/usbdevs.h diff --git a/xs/src/avrdude/usbtiny.c b/src/avrdude/usbtiny.c similarity index 100% rename from xs/src/avrdude/usbtiny.c rename to src/avrdude/usbtiny.c diff --git a/xs/src/avrdude/usbtiny.h b/src/avrdude/usbtiny.h similarity index 100% rename from xs/src/avrdude/usbtiny.h rename to src/avrdude/usbtiny.h diff --git a/xs/src/avrdude/windows/.cvsignore b/src/avrdude/windows/.cvsignore similarity index 100% rename from xs/src/avrdude/windows/.cvsignore rename to src/avrdude/windows/.cvsignore diff --git a/xs/src/avrdude/windows/Makefile.am b/src/avrdude/windows/Makefile.am similarity index 100% rename from xs/src/avrdude/windows/Makefile.am rename to src/avrdude/windows/Makefile.am diff --git a/xs/src/avrdude/windows/getopt.c b/src/avrdude/windows/getopt.c similarity index 100% rename from xs/src/avrdude/windows/getopt.c rename to src/avrdude/windows/getopt.c diff --git a/xs/src/avrdude/windows/getopt.h b/src/avrdude/windows/getopt.h similarity index 100% rename from xs/src/avrdude/windows/getopt.h rename to src/avrdude/windows/getopt.h diff --git a/xs/src/avrdude/windows/giveio.c b/src/avrdude/windows/giveio.c similarity index 100% rename from xs/src/avrdude/windows/giveio.c rename to src/avrdude/windows/giveio.c diff --git a/xs/src/avrdude/windows/giveio.sys b/src/avrdude/windows/giveio.sys similarity index 100% rename from xs/src/avrdude/windows/giveio.sys rename to src/avrdude/windows/giveio.sys diff --git a/xs/src/avrdude/windows/install_giveio.bat b/src/avrdude/windows/install_giveio.bat old mode 100755 new mode 100644 similarity index 100% rename from xs/src/avrdude/windows/install_giveio.bat rename to src/avrdude/windows/install_giveio.bat diff --git a/xs/src/avrdude/windows/loaddrv.c b/src/avrdude/windows/loaddrv.c similarity index 100% rename from xs/src/avrdude/windows/loaddrv.c rename to src/avrdude/windows/loaddrv.c diff --git a/xs/src/avrdude/windows/loaddrv.h b/src/avrdude/windows/loaddrv.h similarity index 100% rename from xs/src/avrdude/windows/loaddrv.h rename to src/avrdude/windows/loaddrv.h diff --git a/xs/src/avrdude/windows/remove_giveio.bat b/src/avrdude/windows/remove_giveio.bat old mode 100755 new mode 100644 similarity index 100% rename from xs/src/avrdude/windows/remove_giveio.bat rename to src/avrdude/windows/remove_giveio.bat diff --git a/xs/src/avrdude/windows/status_giveio.bat b/src/avrdude/windows/status_giveio.bat old mode 100755 new mode 100644 similarity index 100% rename from xs/src/avrdude/windows/status_giveio.bat rename to src/avrdude/windows/status_giveio.bat diff --git a/xs/src/avrdude/windows/unistd.cpp b/src/avrdude/windows/unistd.cpp similarity index 100% rename from xs/src/avrdude/windows/unistd.cpp rename to src/avrdude/windows/unistd.cpp diff --git a/xs/src/avrdude/windows/unistd.h b/src/avrdude/windows/unistd.h similarity index 100% rename from xs/src/avrdude/windows/unistd.h rename to src/avrdude/windows/unistd.h diff --git a/xs/src/avrdude/wiring.c b/src/avrdude/wiring.c similarity index 100% rename from xs/src/avrdude/wiring.c rename to src/avrdude/wiring.c diff --git a/xs/src/avrdude/wiring.h b/src/avrdude/wiring.h similarity index 100% rename from xs/src/avrdude/wiring.h rename to src/avrdude/wiring.h diff --git a/src/boost/CMakeLists.txt b/src/boost/CMakeLists.txt new file mode 100644 index 0000000000..2b23ec3b20 --- /dev/null +++ b/src/boost/CMakeLists.txt @@ -0,0 +1,20 @@ +project(nowide) +cmake_minimum_required(VERSION 2.6) + +add_library(nowide STATIC + nowide/args.hpp + nowide/cenv.hpp + nowide/config.hpp + nowide/convert.hpp + nowide/cstdio.hpp + nowide/cstdlib.hpp + nowide/filebuf.hpp + nowide/fstream.hpp + nowide/integration/filesystem.hpp + nowide/iostream.cpp + nowide/iostream.hpp + nowide/stackstring.hpp + nowide/system.hpp + nowide/utf8_codecvt.hpp + nowide/windows.hpp +) diff --git a/xs/src/boost/nowide/args.hpp b/src/boost/nowide/args.hpp similarity index 100% rename from xs/src/boost/nowide/args.hpp rename to src/boost/nowide/args.hpp diff --git a/xs/src/boost/nowide/cenv.hpp b/src/boost/nowide/cenv.hpp similarity index 100% rename from xs/src/boost/nowide/cenv.hpp rename to src/boost/nowide/cenv.hpp diff --git a/xs/src/boost/nowide/config.hpp b/src/boost/nowide/config.hpp similarity index 100% rename from xs/src/boost/nowide/config.hpp rename to src/boost/nowide/config.hpp diff --git a/xs/src/boost/nowide/convert.hpp b/src/boost/nowide/convert.hpp similarity index 100% rename from xs/src/boost/nowide/convert.hpp rename to src/boost/nowide/convert.hpp diff --git a/xs/src/boost/nowide/cstdio.hpp b/src/boost/nowide/cstdio.hpp similarity index 100% rename from xs/src/boost/nowide/cstdio.hpp rename to src/boost/nowide/cstdio.hpp diff --git a/xs/src/boost/nowide/cstdlib.hpp b/src/boost/nowide/cstdlib.hpp similarity index 100% rename from xs/src/boost/nowide/cstdlib.hpp rename to src/boost/nowide/cstdlib.hpp diff --git a/xs/src/boost/nowide/filebuf.hpp b/src/boost/nowide/filebuf.hpp similarity index 100% rename from xs/src/boost/nowide/filebuf.hpp rename to src/boost/nowide/filebuf.hpp diff --git a/xs/src/boost/nowide/fstream.hpp b/src/boost/nowide/fstream.hpp similarity index 100% rename from xs/src/boost/nowide/fstream.hpp rename to src/boost/nowide/fstream.hpp diff --git a/xs/src/boost/nowide/integration/filesystem.hpp b/src/boost/nowide/integration/filesystem.hpp similarity index 100% rename from xs/src/boost/nowide/integration/filesystem.hpp rename to src/boost/nowide/integration/filesystem.hpp diff --git a/xs/src/boost/nowide/iostream.cpp b/src/boost/nowide/iostream.cpp similarity index 100% rename from xs/src/boost/nowide/iostream.cpp rename to src/boost/nowide/iostream.cpp diff --git a/xs/src/boost/nowide/iostream.hpp b/src/boost/nowide/iostream.hpp similarity index 100% rename from xs/src/boost/nowide/iostream.hpp rename to src/boost/nowide/iostream.hpp diff --git a/xs/src/boost/nowide/stackstring.hpp b/src/boost/nowide/stackstring.hpp similarity index 100% rename from xs/src/boost/nowide/stackstring.hpp rename to src/boost/nowide/stackstring.hpp diff --git a/xs/src/boost/nowide/system.hpp b/src/boost/nowide/system.hpp similarity index 100% rename from xs/src/boost/nowide/system.hpp rename to src/boost/nowide/system.hpp diff --git a/xs/src/boost/nowide/utf8_codecvt.hpp b/src/boost/nowide/utf8_codecvt.hpp similarity index 100% rename from xs/src/boost/nowide/utf8_codecvt.hpp rename to src/boost/nowide/utf8_codecvt.hpp diff --git a/xs/src/boost/nowide/windows.hpp b/src/boost/nowide/windows.hpp similarity index 100% rename from xs/src/boost/nowide/windows.hpp rename to src/boost/nowide/windows.hpp diff --git a/src/clipper/CMakeLists.txt b/src/clipper/CMakeLists.txt new file mode 100644 index 0000000000..d6f3861dce --- /dev/null +++ b/src/clipper/CMakeLists.txt @@ -0,0 +1,7 @@ +project(clipper) +cmake_minimum_required(VERSION 2.6) + +add_library(clipper STATIC + clipper.cpp + clipper.hpp +) diff --git a/xs/src/clipper.cpp b/src/clipper/clipper.cpp similarity index 96% rename from xs/src/clipper.cpp rename to src/clipper/clipper.cpp index e865288fb1..228e0c6ef6 100644 --- a/xs/src/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -1,4180 +1,4180 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.2.9 * -* Date : 16 February 2015 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -/******************************************************************************* -* * -* This is a translation of the Delphi Clipper library and the naming style * -* used has retained a Delphi flavour. * -* * -*******************************************************************************/ - -#include "clipper.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ClipperLib { - -static double const pi = 3.141592653589793238; -static double const two_pi = pi *2; -static double const def_arc_tolerance = 0.25; - -enum Direction { dRightToLeft, dLeftToRight }; - -static int const Unassigned = -1; //edge not currently 'owning' a solution -static int const Skip = -2; //edge that would otherwise close a path - -#define HORIZONTAL (-1.0E+40) -#define TOLERANCE (1.0e-20) -#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) - -// Output polygon. -struct OutRec { - int Idx; - bool IsHole; - bool IsOpen; - //The 'FirstLeft' field points to another OutRec that contains or is the - //'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is - //parsed left from the current edge (owning OutRec) until the owner OutRec - //is found. This field simplifies sorting the polygons into a tree structure - //which reflects the parent/child relationships of all polygons. - //This field should be renamed Parent, and will be later. - OutRec *FirstLeft; - // Used only by void Clipper::BuildResult2(PolyTree& polytree) - PolyNode *PolyNd; - // Linked list of output points, dynamically allocated. - OutPt *Pts; - OutPt *BottomPt; -}; - -//------------------------------------------------------------------------------ - -inline cInt Round(double val) -{ - return static_cast((val < 0) ? (val - 0.5) : (val + 0.5)); -} - -//------------------------------------------------------------------------------ -// PolyTree methods ... -//------------------------------------------------------------------------------ - -int PolyTree::Total() const -{ - int result = (int)AllNodes.size(); - //with negative offsets, ignore the hidden outer polygon ... - if (result > 0 && Childs.front() != &AllNodes.front()) result--; - return result; -} - -//------------------------------------------------------------------------------ -// PolyNode methods ... -//------------------------------------------------------------------------------ - -void PolyNode::AddChild(PolyNode& child) -{ - unsigned cnt = (unsigned)Childs.size(); - Childs.push_back(&child); - child.Parent = this; - child.Index = cnt; -} -//------------------------------------------------------------------------------ - -// Edge delimits a hole if it has an odd number of parent loops. -bool PolyNode::IsHole() const -{ - bool result = true; - PolyNode* node = Parent; - while (node) - { - result = !result; - node = node->Parent; - } - return result; -} - -//------------------------------------------------------------------------------ -// Miscellaneous global functions -//------------------------------------------------------------------------------ - -double Area(const Path &poly) -{ - int size = (int)poly.size(); - if (size < 3) return 0; - - double a = 0; - for (int i = 0, j = size -1; i < size; ++i) - { - a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); - j = i; - } - return -a * 0.5; -} -//------------------------------------------------------------------------------ - -double Area(const OutRec &outRec) -{ - OutPt *op = outRec.Pts; - if (!op) return 0; - double a = 0; - do { - a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); - op = op->Next; - } while (op != outRec.Pts); - return a * 0.5; -} -//------------------------------------------------------------------------------ - -bool PointIsVertex(const IntPoint &Pt, OutPt *pp) -{ - OutPt *pp2 = pp; - do - { - if (pp2->Pt == Pt) return true; - pp2 = pp2->Next; - } - while (pp2 != pp); - return false; -} -//------------------------------------------------------------------------------ - -//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos -//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf -int PointInPolygon(const IntPoint &pt, const Path &path) -{ - //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - size_t cnt = path.size(); - if (cnt < 3) return 0; - IntPoint ip = path[0]; - for(size_t i = 1; i <= cnt; ++i) - { - IntPoint ipNext = (i == cnt ? path[0] : path[i]); - if (ipNext.Y == pt.Y && ((ipNext.X == pt.X) || (ip.Y == pt.Y && ((ipNext.X > pt.X) == (ip.X < pt.X))))) - return -1; - if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) - { - if (ip.X >= pt.X) - { - if (ipNext.X > pt.X) result = 1 - result; - else - { - double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; - } - } else - { - if (ipNext.X > pt.X) - { - double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; - } - } - } - ip = ipNext; - } - return result; -} -//------------------------------------------------------------------------------ - -// Called by Poly2ContainsPoly1() -int PointInPolygon (const IntPoint &pt, OutPt *op) -{ - //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - OutPt* startOp = op; - do - { - if (op->Next->Pt.Y == pt.Y) - { - if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && - ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; - } - if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) - { - if (op->Pt.X >= pt.X) - { - if (op->Next->Pt.X > pt.X) result = 1 - result; - else - { - double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; - } - } else - { - if (op->Next->Pt.X > pt.X) - { - double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; - } - } - } - op = op->Next; - } while (startOp != op); - return result; -} -//------------------------------------------------------------------------------ - -// This is potentially very expensive! O(n^2)! -bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) -{ - PROFILE_FUNC(); - OutPt* op = OutPt1; - do - { - //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon - int res = PointInPolygon(op->Pt, OutPt2); - if (res >= 0) return res > 0; - op = op->Next; - } - while (op != OutPt1); - return true; -} -//---------------------------------------------------------------------- - -inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cInt dy2, bool UseFullInt64Range) { - return (UseFullInt64Range) ? - // |dx1| < 2^63, |dx2| < 2^63 etc, - Int128::sign_determinant_2x2_filtered(dx1, dy1, dx2, dy2) == 0 : -// Int128::sign_determinant_2x2(dx1, dy1, dx2, dy2) == 0 : - // |dx1| < 2^31, |dx2| < 2^31 etc, - // therefore the following computation could be done with 64bit arithmetics. - dy1 * dx2 == dx1 * dy2; -} -inline bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) - { return SlopesEqual(e1.Delta.X, e1.Delta.Y, e2.Delta.X, e2.Delta.Y, UseFullInt64Range); } -inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, bool UseFullInt64Range) - { return SlopesEqual(pt1.X-pt2.X, pt1.Y-pt2.Y, pt2.X-pt3.X, pt2.Y-pt3.Y, UseFullInt64Range); } -inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, const IntPoint &pt4, bool UseFullInt64Range) - { return SlopesEqual(pt1.X-pt2.X, pt1.Y-pt2.Y, pt3.X-pt4.X, pt3.Y-pt4.Y, UseFullInt64Range); } - -//------------------------------------------------------------------------------ - -inline bool IsHorizontal(TEdge &e) -{ - return e.Delta.Y == 0; -} -//------------------------------------------------------------------------------ - -inline double GetDx(const IntPoint &pt1, const IntPoint &pt2) -{ - return (pt1.Y == pt2.Y) ? - HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); -} -//--------------------------------------------------------------------------- - -inline cInt TopX(TEdge &edge, const cInt currentY) -{ - return (currentY == edge.Top.Y) ? - edge.Top.X : - edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); -} -//------------------------------------------------------------------------------ - -void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) -{ -#ifdef use_xyz - ip.Z = 0; -#endif - - double b1, b2; - if (Edge1.Dx == Edge2.Dx) - { - ip.Y = Edge1.Curr.Y; - ip.X = TopX(Edge1, ip.Y); - return; - } - else if (Edge1.Delta.X == 0) - { - ip.X = Edge1.Bot.X; - if (IsHorizontal(Edge2)) - ip.Y = Edge2.Bot.Y; - else - { - b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); - ip.Y = Round(ip.X / Edge2.Dx + b2); - } - } - else if (Edge2.Delta.X == 0) - { - ip.X = Edge2.Bot.X; - if (IsHorizontal(Edge1)) - ip.Y = Edge1.Bot.Y; - else - { - b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); - ip.Y = Round(ip.X / Edge1.Dx + b1); - } - } - else - { - b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; - b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; - double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); - ip.Y = Round(q); - ip.X = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ? - Round(Edge1.Dx * q + b1) : - Round(Edge2.Dx * q + b2); - } - - if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) - { - if (Edge1.Top.Y > Edge2.Top.Y) - ip.Y = Edge1.Top.Y; - else - ip.Y = Edge2.Top.Y; - if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) - ip.X = TopX(Edge1, ip.Y); - else - ip.X = TopX(Edge2, ip.Y); - } - //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... - if (ip.Y > Edge1.Curr.Y) - { - ip.Y = Edge1.Curr.Y; - //use the more vertical edge to derive X ... - if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) - ip.X = TopX(Edge2, ip.Y); else - ip.X = TopX(Edge1, ip.Y); - } -} -//------------------------------------------------------------------------------ - -// Reverse a linked loop of points representing a closed polygon. -// This has a time complexity of O(n) -void ReversePolyPtLinks(OutPt *pp) -{ - if (!pp) return; - OutPt *pp1 = pp; - do { - OutPt *pp2 = pp1->Next; - pp1->Next = pp1->Prev; - pp1->Prev = pp2; - pp1 = pp2; - } while( pp1 != pp ); -} -//------------------------------------------------------------------------------ - -inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) -{ - std::memset(e, 0, sizeof(TEdge)); - e->Next = eNext; - e->Prev = ePrev; - e->Curr = Pt; - e->OutIdx = Unassigned; -} -//------------------------------------------------------------------------------ - -void InitEdge2(TEdge& e, PolyType Pt) -{ - if (e.Curr.Y >= e.Next->Curr.Y) - { - e.Bot = e.Curr; - e.Top = e.Next->Curr; - } else - { - e.Top = e.Curr; - e.Bot = e.Next->Curr; - } - - e.Delta.X = (e.Top.X - e.Bot.X); - e.Delta.Y = (e.Top.Y - e.Bot.Y); - - if (e.Delta.Y == 0) e.Dx = HORIZONTAL; - else e.Dx = (double)(e.Delta.X) / e.Delta.Y; - - e.PolyTyp = Pt; -} -//------------------------------------------------------------------------------ - -// Called from ClipperBase::AddPathInternal() to remove collinear and duplicate points. -inline TEdge* RemoveEdge(TEdge* e) -{ - //removes e from double_linked_list (but without removing from memory) - e->Prev->Next = e->Next; - e->Next->Prev = e->Prev; - TEdge* result = e->Next; - e->Prev = 0; //flag as removed (see ClipperBase.Clear) - return result; -} -//------------------------------------------------------------------------------ - -inline void ReverseHorizontal(TEdge &e) -{ - //swap horizontal edges' Top and Bottom x's so they follow the natural - //progression of the bounds - ie so their xbots will align with the - //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - std::swap(e.Top.X, e.Bot.X); -#ifdef use_xyz - std::swap(e.Top.Z, e.Bot.Z); -#endif -} -//------------------------------------------------------------------------------ - -bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, - IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) -{ - //precondition: segments are Collinear. - if (std::abs(pt1a.X - pt1b.X) > std::abs(pt1a.Y - pt1b.Y)) - { - if (pt1a.X > pt1b.X) std::swap(pt1a, pt1b); - if (pt2a.X > pt2b.X) std::swap(pt2a, pt2b); - if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; - if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; - return pt1.X < pt2.X; - } else - { - if (pt1a.Y < pt1b.Y) std::swap(pt1a, pt1b); - if (pt2a.Y < pt2b.Y) std::swap(pt2a, pt2b); - if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; - if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; - return pt1.Y > pt2.Y; - } -} -//------------------------------------------------------------------------------ - -bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) -{ - OutPt *p = btmPt1->Prev; - while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; - double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); - p = btmPt1->Next; - while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; - double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); - - p = btmPt2->Prev; - while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; - double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); - p = btmPt2->Next; - while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; - double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); - return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); -} -//------------------------------------------------------------------------------ - -// Called by GetLowermostRec() -OutPt* GetBottomPt(OutPt *pp) -{ - OutPt* dups = 0; - OutPt* p = pp->Next; - while (p != pp) - { - if (p->Pt.Y > pp->Pt.Y) - { - pp = p; - dups = 0; - } - else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) - { - if (p->Pt.X < pp->Pt.X) - { - dups = 0; - pp = p; - } else - { - if (p->Next != pp && p->Prev != pp) dups = p; - } - } - p = p->Next; - } - if (dups) - { - //there appears to be at least 2 vertices at BottomPt so ... - while (dups != p) - { - if (!FirstIsBottomPt(p, dups)) pp = dups; - dups = dups->Next; - while (dups->Pt != pp->Pt) dups = dups->Next; - } - } - return pp; -} -//------------------------------------------------------------------------------ - -bool Pt2IsBetweenPt1AndPt3(const IntPoint &pt1, - const IntPoint &pt2, const IntPoint &pt3) -{ - if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) - return false; - else if (pt1.X != pt3.X) - return (pt2.X > pt1.X) == (pt2.X < pt3.X); - else - return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); -} -//------------------------------------------------------------------------------ - -bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) -{ - if (seg1a > seg1b) std::swap(seg1a, seg1b); - if (seg2a > seg2b) std::swap(seg2a, seg2b); - return (seg1a < seg2b) && (seg2a < seg1b); -} - -//------------------------------------------------------------------------------ -// ClipperBase class methods ... -//------------------------------------------------------------------------------ - -// Called from ClipperBase::AddPath() to verify the scale of the input polygon coordinates. -inline void RangeTest(const IntPoint& Pt, bool& useFullRange) -{ - if (useFullRange) - { - if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) - throw clipperException("Coordinate outside allowed range"); - } - else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) - { - useFullRange = true; - RangeTest(Pt, useFullRange); - } -} -//------------------------------------------------------------------------------ - -// Called from ClipperBase::AddPath() to construct the Local Minima List. -// Find a local minimum edge on the path starting with E. -inline TEdge* FindNextLocMin(TEdge* E) -{ - for (;;) - { - while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; - if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; - while (IsHorizontal(*E->Prev)) E = E->Prev; - TEdge* E2 = E; - while (IsHorizontal(*E)) E = E->Next; - if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. - if (E2->Prev->Bot.X < E->Bot.X) E = E2; - break; - } - return E; -} -//------------------------------------------------------------------------------ - -// Called from ClipperBase::AddPath(). -TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) -{ - TEdge *Result = E; - TEdge *Horz = 0; - - if (E->OutIdx == Skip) - { - //if edges still remain in the current bound beyond the skip edge then - //create another LocMin and call ProcessBound once more - if (NextIsForward) - { - while (E->Top.Y == E->Next->Bot.Y) E = E->Next; - //don't include top horizontals when parsing a bound a second time, - //they will be contained in the opposite bound ... - while (E != Result && IsHorizontal(*E)) E = E->Prev; - } - else - { - while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; - while (E != Result && IsHorizontal(*E)) E = E->Next; - } - - if (E == Result) - { - if (NextIsForward) Result = E->Next; - else Result = E->Prev; - } - else - { - //there are more edges in the bound beyond result starting with E - if (NextIsForward) - E = Result->Next; - else - E = Result->Prev; - LocalMinimum locMin; - locMin.Y = E->Bot.Y; - locMin.LeftBound = 0; - locMin.RightBound = E; - E->WindDelta = 0; - Result = ProcessBound(E, NextIsForward); - m_MinimaList.push_back(locMin); - } - return Result; - } - - TEdge *EStart; - - if (IsHorizontal(*E)) - { - //We need to be careful with open paths because this may not be a - //true local minima (ie E may be following a skip edge). - //Also, consecutive horz. edges may start heading left before going right. - if (NextIsForward) - EStart = E->Prev; - else - EStart = E->Next; - if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge - { - if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) - ReverseHorizontal(*E); - } - else if (EStart->Bot.X != E->Bot.X) - ReverseHorizontal(*E); - } - - EStart = E; - if (NextIsForward) - { - while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) - Result = Result->Next; - if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) - { - //nb: at the top of a bound, horizontals are added to the bound - //only when the preceding edge attaches to the horizontal's left vertex - //unless a Skip edge is encountered when that becomes the top divide - Horz = Result; - while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; - if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; - } - while (E != Result) - { - E->NextInLML = E->Next; - if (IsHorizontal(*E) && E != EStart && - E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); - E = E->Next; - } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) - ReverseHorizontal(*E); - Result = Result->Next; //move to the edge just beyond current bound - } else - { - while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) - Result = Result->Prev; - if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) - { - Horz = Result; - while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; - if (Horz->Next->Top.X == Result->Prev->Top.X || - Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; - } - - while (E != Result) - { - E->NextInLML = E->Prev; - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) - ReverseHorizontal(*E); - E = E->Prev; - } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) - ReverseHorizontal(*E); - Result = Result->Prev; //move to the edge just beyond current bound - } - - return Result; -} -//------------------------------------------------------------------------------ - -bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) -{ - PROFILE_FUNC(); - // Remove duplicate end point from a closed input path. - // Remove duplicate points from the end of the input path. - int highI = (int)pg.size() -1; - if (Closed) - while (highI > 0 && (pg[highI] == pg[0])) - --highI; - while (highI > 0 && (pg[highI] == pg[highI -1])) - --highI; - if ((Closed && highI < 2) || (!Closed && highI < 1)) - return false; - - // Allocate a new edge array. - std::vector edges(highI + 1); - // Fill in the edge array. - bool result = AddPathInternal(pg, highI, PolyTyp, Closed, edges.data()); - if (result) - // Success, remember the edge array. - m_edges.emplace_back(std::move(edges)); - return result; -} - -bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) -{ - PROFILE_FUNC(); - std::vector num_edges(ppg.size(), 0); - int num_edges_total = 0; - for (size_t i = 0; i < ppg.size(); ++ i) { - const Path &pg = ppg[i]; - // Remove duplicate end point from a closed input path. - // Remove duplicate points from the end of the input path. - int highI = (int)pg.size() -1; - if (Closed) - while (highI > 0 && (pg[highI] == pg[0])) - --highI; - while (highI > 0 && (pg[highI] == pg[highI -1])) - --highI; - if ((Closed && highI < 2) || (!Closed && highI < 1)) - highI = -1; - num_edges[i] = highI + 1; - num_edges_total += highI + 1; - } - if (num_edges_total == 0) - return false; - - // Allocate a new edge array. - std::vector edges(num_edges_total); - // Fill in the edge array. - bool result = false; - TEdge *p_edge = edges.data(); - for (Paths::size_type i = 0; i < ppg.size(); ++i) - if (num_edges[i]) { - bool res = AddPathInternal(ppg[i], num_edges[i] - 1, PolyTyp, Closed, p_edge); - if (res) { - p_edge += num_edges[i]; - result = true; - } - } - if (result) - // At least some edges were generated. Remember the edge array. - m_edges.emplace_back(std::move(edges)); - return result; -} - -bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, bool Closed, TEdge* edges) -{ - PROFILE_FUNC(); -#ifdef use_lines - if (!Closed && PolyTyp == ptClip) - throw clipperException("AddPath: Open paths must be subject."); -#else - if (!Closed) - throw clipperException("AddPath: Open paths have been disabled."); -#endif - - assert(highI >= 0 && highI < pg.size()); - - //1. Basic (first) edge initialization ... - try - { - edges[1].Curr = pg[1]; - RangeTest(pg[0], m_UseFullRange); - RangeTest(pg[highI], m_UseFullRange); - InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); - InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); - for (int i = highI - 1; i >= 1; --i) - { - RangeTest(pg[i], m_UseFullRange); - InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); - } - } - catch(...) - { - throw; //range test fails - } - TEdge *eStart = &edges[0]; - - //2. Remove duplicate vertices, and (when closed) collinear edges ... - TEdge *E = eStart, *eLoopStop = eStart; - for (;;) - { - //nb: allows matching start and end points when not Closed ... - if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) - { - if (E == E->Next) break; - if (E == eStart) eStart = E->Next; - E = RemoveEdge(E); - eLoopStop = E; - continue; - } - if (E->Prev == E->Next) - break; //only two vertices - else if (Closed && - SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && - (!m_PreserveCollinear || - !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) - { - //Collinear edges are allowed for open paths but in closed paths - //the default is to merge adjacent collinear edges into a single edge. - //However, if the PreserveCollinear property is enabled, only overlapping - //collinear edges (ie spikes) will be removed from closed paths. - if (E == eStart) eStart = E->Next; - E = RemoveEdge(E); - E = E->Prev; - eLoopStop = E; - continue; - } - E = E->Next; - if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; - } - - if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) - { - return false; - } - - if (!Closed) - { - m_HasOpenPaths = true; - eStart->Prev->OutIdx = Skip; - } - - //3. Do second stage of edge initialization ... - // IsFlat means all vertices have the same Y coordinate. - bool IsFlat = true; - E = eStart; - do - { - InitEdge2(*E, PolyTyp); - E = E->Next; - if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; - } - while (E != eStart); - - //4. Finally, add edge bounds to LocalMinima list ... - - //Totally flat paths must be handled differently when adding them - //to LocalMinima list to avoid endless loops etc ... - if (IsFlat) - { - if (Closed) - { - return false; - } - E->Prev->OutIdx = Skip; - LocalMinimum locMin; - locMin.Y = E->Bot.Y; - locMin.LeftBound = 0; - locMin.RightBound = E; - locMin.RightBound->Side = esRight; - locMin.RightBound->WindDelta = 0; - for (;;) - { - if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); - if (E->Next->OutIdx == Skip) break; - E->NextInLML = E->Next; - E = E->Next; - } - m_MinimaList.push_back(locMin); - return true; - } - - bool leftBoundIsForward; - TEdge* EMin = 0; - - //workaround to avoid an endless loop in the while loop below when - //open paths have matching start and end points ... - if (E->Prev->Bot == E->Prev->Top) E = E->Next; - - // Find local minima and store them into a Local Minima List. - // Multiple Local Minima could be created for a single path. - for (;;) - { - E = FindNextLocMin(E); - if (E == EMin) break; - else if (!EMin) EMin = E; - - //E and E.Prev now share a local minima (left aligned if horizontal). - //Compare their slopes to find which starts which bound ... - LocalMinimum locMin; - locMin.Y = E->Bot.Y; - if (E->Dx < E->Prev->Dx) - { - locMin.LeftBound = E->Prev; - locMin.RightBound = E; - leftBoundIsForward = false; //Q.nextInLML = Q.prev - } else - { - locMin.LeftBound = E; - locMin.RightBound = E->Prev; - leftBoundIsForward = true; //Q.nextInLML = Q.next - } - locMin.LeftBound->Side = esLeft; - locMin.RightBound->Side = esRight; - - if (!Closed) locMin.LeftBound->WindDelta = 0; - else if (locMin.LeftBound->Next == locMin.RightBound) - locMin.LeftBound->WindDelta = -1; - else locMin.LeftBound->WindDelta = 1; - locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; - - E = ProcessBound(locMin.LeftBound, leftBoundIsForward); - if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); - - TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); - if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); - - if (locMin.LeftBound->OutIdx == Skip) - locMin.LeftBound = 0; - else if (locMin.RightBound->OutIdx == Skip) - locMin.RightBound = 0; - m_MinimaList.push_back(locMin); - if (!leftBoundIsForward) E = E2; - } - return true; -} -//------------------------------------------------------------------------------ - -void ClipperBase::Clear() -{ - PROFILE_FUNC(); - m_MinimaList.clear(); - m_edges.clear(); - m_UseFullRange = false; - m_HasOpenPaths = false; -} -//------------------------------------------------------------------------------ - -// Initialize the Local Minima List: -// Sort the LML entries, initialize the left / right bound edges of each Local Minima. -void ClipperBase::Reset() -{ - PROFILE_FUNC(); - if (m_MinimaList.empty()) return; //ie nothing to process - std::sort(m_MinimaList.begin(), m_MinimaList.end(), [](const LocalMinimum& lm1, const LocalMinimum& lm2){ return lm1.Y < lm2.Y; }); - - //reset all edges ... - for (LocalMinimum &lm : m_MinimaList) { - TEdge* e = lm.LeftBound; - if (e) - { - e->Curr = e->Bot; - e->Side = esLeft; - e->OutIdx = Unassigned; - } - - e = lm.RightBound; - if (e) - { - e->Curr = e->Bot; - e->Side = esRight; - e->OutIdx = Unassigned; - } - } -} -//------------------------------------------------------------------------------ - -// Get bounds of the edges referenced by the Local Minima List. -// Returns (0,0,0,0) for an empty rectangle. -IntRect ClipperBase::GetBounds() -{ - PROFILE_FUNC(); - IntRect result; - auto lm = m_MinimaList.begin(); - if (lm == m_MinimaList.end()) - { - result.left = result.top = result.right = result.bottom = 0; - return result; - } - result.left = lm->LeftBound->Bot.X; - result.top = lm->LeftBound->Bot.Y; - result.right = lm->LeftBound->Bot.X; - result.bottom = lm->LeftBound->Bot.Y; - while (lm != m_MinimaList.end()) - { - result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); - TEdge* e = lm->LeftBound; - for (;;) { - TEdge* bottomE = e; - while (e->NextInLML) - { - if (e->Bot.X < result.left) result.left = e->Bot.X; - if (e->Bot.X > result.right) result.right = e->Bot.X; - e = e->NextInLML; - } - result.left = std::min(result.left, e->Bot.X); - result.right = std::max(result.right, e->Bot.X); - result.left = std::min(result.left, e->Top.X); - result.right = std::max(result.right, e->Top.X); - result.top = std::min(result.top, e->Top.Y); - if (bottomE == lm->LeftBound) e = lm->RightBound; - else break; - } - ++lm; - } - return result; -} - -//------------------------------------------------------------------------------ -// TClipper methods ... -//------------------------------------------------------------------------------ - -Clipper::Clipper(int initOptions) : - ClipperBase(), - m_OutPtsFree(nullptr), - m_OutPtsChunkSize(32), - m_OutPtsChunkLast(32), - m_ActiveEdges(nullptr), - m_SortedEdges(nullptr) -{ - m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); - m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); - m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); - m_HasOpenPaths = false; -#ifdef use_xyz - m_ZFill = 0; -#endif -} -//------------------------------------------------------------------------------ - -void Clipper::Reset() -{ - PROFILE_FUNC(); - ClipperBase::Reset(); - m_Scanbeam = std::priority_queue(); - m_Maxima.clear(); - m_ActiveEdges = 0; - m_SortedEdges = 0; - for (auto lm = m_MinimaList.rbegin(); lm != m_MinimaList.rend(); ++lm) - m_Scanbeam.push(lm->Y); -} - -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, Paths &solution, - PolyFillType subjFillType, PolyFillType clipFillType) -{ - PROFILE_FUNC(); - if (m_HasOpenPaths) - throw clipperException("Error: PolyTree struct is needed for open path clipping."); - solution.resize(0); - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - m_UsingPolyTree = false; - bool succeeded = ExecuteInternal(); - if (succeeded) BuildResult(solution); - DisposeAllOutRecs(); - return succeeded; -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, PolyTree& polytree, - PolyFillType subjFillType, PolyFillType clipFillType) -{ - PROFILE_FUNC(); - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - m_UsingPolyTree = true; - bool succeeded = ExecuteInternal(); - if (succeeded) BuildResult2(polytree); - DisposeAllOutRecs(); - return succeeded; -} -//------------------------------------------------------------------------------ - -bool Clipper::ExecuteInternal() -{ - PROFILE_FUNC(); - bool succeeded = true; - try { - PROFILE_BLOCK(Clipper_ExecuteInternal_Process); - Reset(); - if (m_MinimaList.empty()) return true; - cInt botY = m_Scanbeam.top(); - do { m_Scanbeam.pop(); } while (! m_Scanbeam.empty() && botY == m_Scanbeam.top()); - do { - InsertLocalMinimaIntoAEL(botY); - ProcessHorizontals(); - m_GhostJoins.clear(); - if (m_Scanbeam.empty()) break; - cInt topY = m_Scanbeam.top(); - do { m_Scanbeam.pop(); } while (! m_Scanbeam.empty() && topY == m_Scanbeam.top()); - succeeded = ProcessIntersections(topY); - if (!succeeded) break; - ProcessEdgesAtTopOfScanbeam(topY); - botY = topY; - } while (!m_Scanbeam.empty() || !m_MinimaList.empty()); - } - catch(...) - { - succeeded = false; - } - - if (succeeded) - { - PROFILE_BLOCK(Clipper_ExecuteInternal_Fix); - - //fix orientations ... - //FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers? - //FIXME Vojtech: The area is calculated with floats, it may not be numerically stable! - { - PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_orientations); - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) - ReversePolyPtLinks(outRec->Pts); - } - - JoinCommonEdges(); - - //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() - { - PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_fixup); - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts) { - if (outRec->IsOpen) - // Removes duplicate points. - FixupOutPolyline(*outRec); - else - // Removes duplicate points and simplifies consecutive parallel edges by removing the middle vertex. - FixupOutPolygon(*outRec); - } - } - // For each polygon, search for exactly duplicate non-successive points. - // If such a point is found, the loop is split into two pieces. - // Search for the duplicate points is O(n^2)! - // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm - if (m_StrictSimple) DoSimplePolygons(); - } - - m_Joins.clear(); - m_GhostJoins.clear(); - return succeeded; -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::AllocateOutPt() -{ - OutPt *pt; - if (m_OutPtsFree) { - // Recycle some of the already released points. - pt = m_OutPtsFree; - m_OutPtsFree = pt->Next; - } else if (m_OutPtsChunkLast < m_OutPtsChunkSize) { - // Get a point from the last chunk. - pt = m_OutPts.back() + (m_OutPtsChunkLast ++); - } else { - // The last chunk is full. Allocate a new one. - m_OutPts.push_back(new OutPt[m_OutPtsChunkSize]); - m_OutPtsChunkLast = 1; - pt = m_OutPts.back(); - } - return pt; -} - -void Clipper::DisposeAllOutRecs() -{ - for (OutPt *pts : m_OutPts) - delete[] pts; - for (OutRec *rec : m_PolyOuts) - delete rec; - m_OutPts.clear(); - m_OutPtsFree = nullptr; - m_OutPtsChunkLast = m_OutPtsChunkSize; - m_PolyOuts.clear(); -} -//------------------------------------------------------------------------------ - -void Clipper::SetWindingCount(TEdge &edge) const -{ - TEdge *e = edge.PrevInAEL; - //find the edge of the same polytype that immediately preceeds 'edge' in AEL - while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; - if (!e) - { - edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); - edge.WindCnt2 = 0; - e = m_ActiveEdges; //ie get ready to calc WindCnt2 - } - else if (edge.WindDelta == 0 && m_ClipType != ctUnion) - { - edge.WindCnt = 1; - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - else if (IsEvenOddFillType(edge)) - { - //EvenOdd filling ... - if (edge.WindDelta == 0) - { - //are we inside a subj polygon ... - bool Inside = true; - TEdge *e2 = e->PrevInAEL; - while (e2) - { - if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) - Inside = !Inside; - e2 = e2->PrevInAEL; - } - edge.WindCnt = (Inside ? 0 : 1); - } - else - { - edge.WindCnt = edge.WindDelta; - } - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - else - { - //nonZero, Positive or Negative filling ... - if (e->WindCnt * e->WindDelta < 0) - { - //prev edge is 'decreasing' WindCount (WC) toward zero - //so we're outside the previous polygon ... - if (std::abs(e->WindCnt) > 1) - { - //outside prev poly but still inside another. - //when reversing direction of prev poly use the same WC - if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; - //otherwise continue to 'decrease' WC ... - else edge.WindCnt = e->WindCnt + edge.WindDelta; - } - else - //now outside all polys of same polytype so set own WC ... - edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); - } else - { - //prev edge is 'increasing' WindCount (WC) away from zero - //so we're inside the previous polygon ... - if (edge.WindDelta == 0) - edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); - //if wind direction is reversing prev then use same WC - else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; - //otherwise add to WC ... - else edge.WindCnt = e->WindCnt + edge.WindDelta; - } - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - - //update WindCnt2 ... - if (IsEvenOddAltFillType(edge)) - { - //EvenOdd filling ... - while (e != &edge) - { - if (e->WindDelta != 0) - edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); - e = e->NextInAEL; - } - } else - { - //nonZero, Positive or Negative filling ... - while ( e != &edge ) - { - edge.WindCnt2 += e->WindDelta; - e = e->NextInAEL; - } - } -} -//------------------------------------------------------------------------------ - -bool Clipper::IsContributing(const TEdge& edge) const -{ - PolyFillType pft, pft2; - if (edge.PolyTyp == ptSubject) - { - pft = m_SubjFillType; - pft2 = m_ClipFillType; - } else - { - pft = m_ClipFillType; - pft2 = m_SubjFillType; - } - - switch(pft) - { - case pftEvenOdd: - //return false if a subj line has been flagged as inside a subj polygon - if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; - break; - case pftNonZero: - if (std::abs(edge.WindCnt) != 1) return false; - break; - case pftPositive: - if (edge.WindCnt != 1) return false; - break; - default: //pftNegative - if (edge.WindCnt != -1) return false; - } - - switch(m_ClipType) - { - case ctIntersection: - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 != 0); - case pftPositive: - return (edge.WindCnt2 > 0); - default: - return (edge.WindCnt2 < 0); - } - break; - case ctUnion: - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - break; - case ctDifference: - if (edge.PolyTyp == ptSubject) - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - else - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 != 0); - case pftPositive: - return (edge.WindCnt2 > 0); - default: - return (edge.WindCnt2 < 0); - } - break; - case ctXor: - if (edge.WindDelta == 0) //XOr always contributing unless open - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - else - return true; - break; - default: - return true; - } -} -//------------------------------------------------------------------------------ - -// Called from Clipper::InsertLocalMinimaIntoAEL() and Clipper::IntersectEdges(). -OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) -{ - PROFILE_FUNC(); - OutPt* result; - TEdge *e, *prevE; - if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) - { - result = AddOutPt(e1, Pt); - e2->OutIdx = e1->OutIdx; - e1->Side = esLeft; - e2->Side = esRight; - e = e1; - if (e->PrevInAEL == e2) - prevE = e2->PrevInAEL; - else - prevE = e->PrevInAEL; - } else - { - result = AddOutPt(e2, Pt); - e1->OutIdx = e2->OutIdx; - e1->Side = esRight; - e2->Side = esLeft; - e = e2; - if (e->PrevInAEL == e1) - prevE = e1->PrevInAEL; - else - prevE = e->PrevInAEL; - } - - if (prevE && prevE->OutIdx >= 0 && - (TopX(*prevE, Pt.Y) == TopX(*e, Pt.Y)) && - SlopesEqual(*e, *prevE, m_UseFullRange) && - (e->WindDelta != 0) && (prevE->WindDelta != 0)) - { - OutPt* outPt = AddOutPt(prevE, Pt); - m_Joins.emplace_back(Join(result, outPt, e->Top)); - } - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) -{ - AddOutPt( e1, Pt ); - if (e2->WindDelta == 0) AddOutPt(e2, Pt); - if( e1->OutIdx == e2->OutIdx ) - { - e1->OutIdx = Unassigned; - e2->OutIdx = Unassigned; - } - else if (e1->OutIdx < e2->OutIdx) - AppendPolygon(e1, e2); - else - AppendPolygon(e2, e1); -} -//------------------------------------------------------------------------------ - -void Clipper::AddEdgeToSEL(TEdge *edge) -{ - //SEL pointers in PEdge are reused to build a list of horizontal edges. - //However, we don't need to worry about order with horizontal edge processing. - if( !m_SortedEdges ) - { - m_SortedEdges = edge; - edge->PrevInSEL = 0; - edge->NextInSEL = 0; - } - else - { - edge->NextInSEL = m_SortedEdges; - edge->PrevInSEL = 0; - m_SortedEdges->PrevInSEL = edge; - m_SortedEdges = edge; - } -} -//------------------------------------------------------------------------------ - -void Clipper::CopyAELToSEL() -{ - TEdge* e = m_ActiveEdges; - m_SortedEdges = e; - while ( e ) - { - e->PrevInSEL = e->PrevInAEL; - e->NextInSEL = e->NextInAEL; - e = e->NextInAEL; - } -} - -//------------------------------------------------------------------------------ - -// Called from Clipper::ExecuteInternal() -void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) -{ - PROFILE_FUNC(); - while (!m_MinimaList.empty() && m_MinimaList.back().Y == botY) - { - TEdge* lb = m_MinimaList.back().LeftBound; - TEdge* rb = m_MinimaList.back().RightBound; - m_MinimaList.pop_back(); - - OutPt *Op1 = 0; - if (!lb) - { - //nb: don't insert LB into either AEL or SEL - InsertEdgeIntoAEL(rb, 0); - SetWindingCount(*rb); - if (IsContributing(*rb)) - Op1 = AddOutPt(rb, rb->Bot); - } - else if (!rb) - { - InsertEdgeIntoAEL(lb, 0); - SetWindingCount(*lb); - if (IsContributing(*lb)) - Op1 = AddOutPt(lb, lb->Bot); - m_Scanbeam.push(lb->Top.Y); - } - else - { - InsertEdgeIntoAEL(lb, 0); - InsertEdgeIntoAEL(rb, lb); - SetWindingCount( *lb ); - rb->WindCnt = lb->WindCnt; - rb->WindCnt2 = lb->WindCnt2; - if (IsContributing(*lb)) - Op1 = AddLocalMinPoly(lb, rb, lb->Bot); - m_Scanbeam.push(lb->Top.Y); - } - - if (rb) - { - if(IsHorizontal(*rb)) AddEdgeToSEL(rb); - else m_Scanbeam.push(rb->Top.Y); - } - - if (!lb || !rb) continue; - - //if any output polygons share an edge, they'll need joining later ... - if (Op1 && IsHorizontal(*rb) && - m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) - { - for (Join &jr : m_GhostJoins) - //if the horizontal Rb and a 'ghost' horizontal overlap, then convert - //the 'ghost' join to a real join ready for later ... - if (HorzSegmentsOverlap(jr.OutPt1->Pt.X, jr.OffPt.X, rb->Bot.X, rb->Top.X)) - m_Joins.emplace_back(Join(jr.OutPt1, Op1, jr.OffPt)); - } - - if (lb->OutIdx >= 0 && lb->PrevInAEL && - lb->PrevInAEL->Curr.X == lb->Bot.X && - lb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(*lb->PrevInAEL, *lb, m_UseFullRange) && - (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) - { - OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); - m_Joins.emplace_back(Join(Op1, Op2, lb->Top)); - } - - if(lb->NextInAEL != rb) - { - - if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(*rb->PrevInAEL, *rb, m_UseFullRange) && - (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) - { - OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); - m_Joins.emplace_back(Join(Op1, Op2, rb->Top)); - } - - TEdge* e = lb->NextInAEL; - if (e) - { - while( e != rb ) - { - //nb: For calculating winding counts etc, IntersectEdges() assumes - //that param1 will be to the Right of param2 ABOVE the intersection ... - IntersectEdges(rb , e , lb->Curr); //order important here - e = e->NextInAEL; - } - } - } - - } -} -//------------------------------------------------------------------------------ - -void Clipper::DeleteFromAEL(TEdge *e) -{ - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted - if( AelPrev ) AelPrev->NextInAEL = AelNext; - else m_ActiveEdges = AelNext; - if( AelNext ) AelNext->PrevInAEL = AelPrev; - e->NextInAEL = 0; - e->PrevInAEL = 0; -} -//------------------------------------------------------------------------------ - -void Clipper::DeleteFromSEL(TEdge *e) -{ - TEdge* SelPrev = e->PrevInSEL; - TEdge* SelNext = e->NextInSEL; - if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted - if( SelPrev ) SelPrev->NextInSEL = SelNext; - else m_SortedEdges = SelNext; - if( SelNext ) SelNext->PrevInSEL = SelPrev; - e->NextInSEL = 0; - e->PrevInSEL = 0; -} -//------------------------------------------------------------------------------ - -#ifdef use_xyz -void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) -{ - if (pt.Z != 0 || !m_ZFill) return; - else if (pt == e1.Bot) pt.Z = e1.Bot.Z; - else if (pt == e1.Top) pt.Z = e1.Top.Z; - else if (pt == e2.Bot) pt.Z = e2.Bot.Z; - else if (pt == e2.Top) pt.Z = e2.Top.Z; - else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); -} -//------------------------------------------------------------------------------ -#endif - -void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) -{ - bool e1Contributing = ( e1->OutIdx >= 0 ); - bool e2Contributing = ( e2->OutIdx >= 0 ); - -#ifdef use_xyz - SetZ(Pt, *e1, *e2); -#endif - -#ifdef use_lines - //if either edge is on an OPEN path ... - if (e1->WindDelta == 0 || e2->WindDelta == 0) - { - //ignore subject-subject open path intersections UNLESS they - //are both open paths, AND they are both 'contributing maximas' ... - if (e1->WindDelta == 0 && e2->WindDelta == 0) return; - - //if intersecting a subj line with a subj poly ... - else if (e1->PolyTyp == e2->PolyTyp && - e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) - { - if (e1->WindDelta == 0) - { - if (e2Contributing) - { - AddOutPt(e1, Pt); - if (e1Contributing) e1->OutIdx = Unassigned; - } - } - else - { - if (e1Contributing) - { - AddOutPt(e2, Pt); - if (e2Contributing) e2->OutIdx = Unassigned; - } - } - } - else if (e1->PolyTyp != e2->PolyTyp) - { - //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... - if ((e1->WindDelta == 0) && std::abs(e2->WindCnt) == 1 && - (m_ClipType != ctUnion || e2->WindCnt2 == 0)) - { - AddOutPt(e1, Pt); - if (e1Contributing) e1->OutIdx = Unassigned; - } - else if ((e2->WindDelta == 0) && (std::abs(e1->WindCnt) == 1) && - (m_ClipType != ctUnion || e1->WindCnt2 == 0)) - { - AddOutPt(e2, Pt); - if (e2Contributing) e2->OutIdx = Unassigned; - } - } - return; - } -#endif - - //update winding counts... - //assumes that e1 will be to the Right of e2 ABOVE the intersection - if ( e1->PolyTyp == e2->PolyTyp ) - { - if ( IsEvenOddFillType( *e1) ) - { - int oldE1WindCnt = e1->WindCnt; - e1->WindCnt = e2->WindCnt; - e2->WindCnt = oldE1WindCnt; - } else - { - if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; - else e1->WindCnt += e2->WindDelta; - if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; - else e2->WindCnt -= e1->WindDelta; - } - } else - { - if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; - else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; - if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; - else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; - } - - PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; - if (e1->PolyTyp == ptSubject) - { - e1FillType = m_SubjFillType; - e1FillType2 = m_ClipFillType; - } else - { - e1FillType = m_ClipFillType; - e1FillType2 = m_SubjFillType; - } - if (e2->PolyTyp == ptSubject) - { - e2FillType = m_SubjFillType; - e2FillType2 = m_ClipFillType; - } else - { - e2FillType = m_ClipFillType; - e2FillType2 = m_SubjFillType; - } - - cInt e1Wc, e2Wc; - switch (e1FillType) - { - case pftPositive: e1Wc = e1->WindCnt; break; - case pftNegative: e1Wc = -e1->WindCnt; break; - default: e1Wc = std::abs(e1->WindCnt); - } - switch(e2FillType) - { - case pftPositive: e2Wc = e2->WindCnt; break; - case pftNegative: e2Wc = -e2->WindCnt; break; - default: e2Wc = std::abs(e2->WindCnt); - } - - if ( e1Contributing && e2Contributing ) - { - if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || - (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) - { - AddLocalMaxPoly(e1, e2, Pt); - } - else - { - AddOutPt(e1, Pt); - AddOutPt(e2, Pt); - std::swap(e1->Side, e2->Side); - std::swap(e1->OutIdx, e2->OutIdx); - } - } - else if ( e1Contributing ) - { - if (e2Wc == 0 || e2Wc == 1) - { - AddOutPt(e1, Pt); - std::swap(e1->Side, e2->Side); - std::swap(e1->OutIdx, e2->OutIdx); - } - } - else if ( e2Contributing ) - { - if (e1Wc == 0 || e1Wc == 1) - { - AddOutPt(e2, Pt); - std::swap(e1->Side, e2->Side); - std::swap(e1->OutIdx, e2->OutIdx); - } - } - else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) - { - //neither edge is currently contributing ... - - cInt e1Wc2, e2Wc2; - switch (e1FillType2) - { - case pftPositive: e1Wc2 = e1->WindCnt2; break; - case pftNegative : e1Wc2 = -e1->WindCnt2; break; - default: e1Wc2 = std::abs(e1->WindCnt2); - } - switch (e2FillType2) - { - case pftPositive: e2Wc2 = e2->WindCnt2; break; - case pftNegative: e2Wc2 = -e2->WindCnt2; break; - default: e2Wc2 = std::abs(e2->WindCnt2); - } - - if (e1->PolyTyp != e2->PolyTyp) - { - AddLocalMinPoly(e1, e2, Pt); - } - else if (e1Wc == 1 && e2Wc == 1) - switch( m_ClipType ) { - case ctIntersection: - if (e1Wc2 > 0 && e2Wc2 > 0) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctUnion: - if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctDifference: - if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || - ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctXor: - AddLocalMinPoly(e1, e2, Pt); - } - else - std::swap(e1->Side, e2->Side); - } -} -//------------------------------------------------------------------------------ - -void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const -{ - bool IsHole = false; - TEdge *e2 = e->PrevInAEL; - while (e2) - { - if (e2->OutIdx >= 0 && e2->WindDelta != 0) - { - IsHole = !IsHole; - if (! outrec->FirstLeft) - outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; - } - e2 = e2->PrevInAEL; - } - if (IsHole) outrec->IsHole = true; -} -//------------------------------------------------------------------------------ - -OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) -{ - //work out which polygon fragment has the correct hole state ... - if (!outRec1->BottomPt) - outRec1->BottomPt = GetBottomPt(outRec1->Pts); - if (!outRec2->BottomPt) - outRec2->BottomPt = GetBottomPt(outRec2->Pts); - OutPt *OutPt1 = outRec1->BottomPt; - OutPt *OutPt2 = outRec2->BottomPt; - if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; - else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; - else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; - else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; - else if (OutPt1->Next == OutPt1) return outRec2; - else if (OutPt2->Next == OutPt2) return outRec1; - else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; - else return outRec2; -} -//------------------------------------------------------------------------------ - -bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) -{ - do - { - outRec1 = outRec1->FirstLeft; - if (outRec1 == outRec2) return true; - } while (outRec1); - return false; -} -//------------------------------------------------------------------------------ - -OutRec* Clipper::GetOutRec(int Idx) -{ - OutRec* outrec = m_PolyOuts[Idx]; - while (outrec != m_PolyOuts[outrec->Idx]) - outrec = m_PolyOuts[outrec->Idx]; - return outrec; -} -//------------------------------------------------------------------------------ - -void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const -{ - //get the start and ends of both output polygons ... - OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; - OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; - - OutRec *holeStateRec; - if (Param1RightOfParam2(outRec1, outRec2)) - holeStateRec = outRec2; - else if (Param1RightOfParam2(outRec2, outRec1)) - holeStateRec = outRec1; - else - holeStateRec = GetLowermostRec(outRec1, outRec2); - - //get the start and ends of both output polygons and - //join e2 poly onto e1 poly and delete pointers to e2 ... - - OutPt* p1_lft = outRec1->Pts; - OutPt* p1_rt = p1_lft->Prev; - OutPt* p2_lft = outRec2->Pts; - OutPt* p2_rt = p2_lft->Prev; - - EdgeSide Side; - //join e2 poly onto e1 poly and delete pointers to e2 ... - if( e1->Side == esLeft ) - { - if( e2->Side == esLeft ) - { - //z y x a b c - ReversePolyPtLinks(p2_lft); - p2_lft->Next = p1_lft; - p1_lft->Prev = p2_lft; - p1_rt->Next = p2_rt; - p2_rt->Prev = p1_rt; - outRec1->Pts = p2_rt; - } else - { - //x y z a b c - p2_rt->Next = p1_lft; - p1_lft->Prev = p2_rt; - p2_lft->Prev = p1_rt; - p1_rt->Next = p2_lft; - outRec1->Pts = p2_lft; - } - Side = esLeft; - } else - { - if( e2->Side == esRight ) - { - //a b c z y x - ReversePolyPtLinks(p2_lft); - p1_rt->Next = p2_rt; - p2_rt->Prev = p1_rt; - p2_lft->Next = p1_lft; - p1_lft->Prev = p2_lft; - } else - { - //a b c x y z - p1_rt->Next = p2_lft; - p2_lft->Prev = p1_rt; - p1_lft->Prev = p2_rt; - p2_rt->Next = p1_lft; - } - Side = esRight; - } - - outRec1->BottomPt = 0; - if (holeStateRec == outRec2) - { - if (outRec2->FirstLeft != outRec1) - outRec1->FirstLeft = outRec2->FirstLeft; - outRec1->IsHole = outRec2->IsHole; - } - outRec2->Pts = 0; - outRec2->BottomPt = 0; - outRec2->FirstLeft = outRec1; - - int OKIdx = e1->OutIdx; - int ObsoleteIdx = e2->OutIdx; - - e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly - e2->OutIdx = Unassigned; - - TEdge* e = m_ActiveEdges; - while( e ) - { - if( e->OutIdx == ObsoleteIdx ) - { - e->OutIdx = OKIdx; - e->Side = Side; - break; - } - e = e->NextInAEL; - } - - outRec2->Idx = outRec1->Idx; -} -//------------------------------------------------------------------------------ - -OutRec* Clipper::CreateOutRec() -{ - OutRec* result = new OutRec; - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back(result); - result->Idx = (int)m_PolyOuts.size()-1; - return result; -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) -{ - if( e->OutIdx < 0 ) - { - OutRec *outRec = CreateOutRec(); - outRec->IsOpen = (e->WindDelta == 0); - OutPt* newOp = this->AllocateOutPt(); - outRec->Pts = newOp; - newOp->Idx = outRec->Idx; - newOp->Pt = pt; - newOp->Next = newOp; - newOp->Prev = newOp; - if (!outRec->IsOpen) - SetHoleState(e, outRec); - e->OutIdx = outRec->Idx; - return newOp; - } else - { - OutRec *outRec = m_PolyOuts[e->OutIdx]; - //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' - OutPt* op = outRec->Pts; - - bool ToFront = (e->Side == esLeft); - if (ToFront && (pt == op->Pt)) return op; - else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; - - OutPt* newOp = this->AllocateOutPt(); - newOp->Idx = outRec->Idx; - newOp->Pt = pt; - newOp->Next = op; - newOp->Prev = op->Prev; - newOp->Prev->Next = newOp; - op->Prev = newOp; - if (ToFront) outRec->Pts = newOp; - return newOp; - } -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::GetLastOutPt(TEdge *e) -{ - OutRec *outRec = m_PolyOuts[e->OutIdx]; - if (e->Side == esLeft) - return outRec->Pts; - else - return outRec->Pts->Prev; -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessHorizontals() -{ - PROFILE_FUNC(); - TEdge* horzEdge = m_SortedEdges; - while(horzEdge) - { - DeleteFromSEL(horzEdge); - ProcessHorizontal(horzEdge); - horzEdge = m_SortedEdges; - } -} -//------------------------------------------------------------------------------ - -inline bool IsMaxima(TEdge *e, const cInt Y) -{ - return e && e->Top.Y == Y && !e->NextInLML; -} -//------------------------------------------------------------------------------ - -inline bool IsIntermediate(TEdge *e, const cInt Y) -{ - return e->Top.Y == Y && e->NextInLML; -} -//------------------------------------------------------------------------------ - -inline TEdge *GetMaximaPair(TEdge *e) -{ - TEdge* result = 0; - if ((e->Next->Top == e->Top) && !e->Next->NextInLML) - result = e->Next; - else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) - result = e->Prev; - - if (result && (result->OutIdx == Skip || - //result is false if both NextInAEL & PrevInAEL are nil & not horizontal ... - (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) - return 0; - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) -{ - //check that one or other edge hasn't already been removed from AEL ... - if (Edge1->NextInAEL == Edge1->PrevInAEL || - Edge2->NextInAEL == Edge2->PrevInAEL) return; - - if( Edge1->NextInAEL == Edge2 ) - { - TEdge* Next = Edge2->NextInAEL; - if( Next ) Next->PrevInAEL = Edge1; - TEdge* Prev = Edge1->PrevInAEL; - if( Prev ) Prev->NextInAEL = Edge2; - Edge2->PrevInAEL = Prev; - Edge2->NextInAEL = Edge1; - Edge1->PrevInAEL = Edge2; - Edge1->NextInAEL = Next; - } - else if( Edge2->NextInAEL == Edge1 ) - { - TEdge* Next = Edge1->NextInAEL; - if( Next ) Next->PrevInAEL = Edge2; - TEdge* Prev = Edge2->PrevInAEL; - if( Prev ) Prev->NextInAEL = Edge1; - Edge1->PrevInAEL = Prev; - Edge1->NextInAEL = Edge2; - Edge2->PrevInAEL = Edge1; - Edge2->NextInAEL = Next; - } - else - { - TEdge* Next = Edge1->NextInAEL; - TEdge* Prev = Edge1->PrevInAEL; - Edge1->NextInAEL = Edge2->NextInAEL; - if( Edge1->NextInAEL ) Edge1->NextInAEL->PrevInAEL = Edge1; - Edge1->PrevInAEL = Edge2->PrevInAEL; - if( Edge1->PrevInAEL ) Edge1->PrevInAEL->NextInAEL = Edge1; - Edge2->NextInAEL = Next; - if( Edge2->NextInAEL ) Edge2->NextInAEL->PrevInAEL = Edge2; - Edge2->PrevInAEL = Prev; - if( Edge2->PrevInAEL ) Edge2->PrevInAEL->NextInAEL = Edge2; - } - - if( !Edge1->PrevInAEL ) m_ActiveEdges = Edge1; - else if( !Edge2->PrevInAEL ) m_ActiveEdges = Edge2; -} -//------------------------------------------------------------------------------ - -void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) -{ - if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; - if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; - - if( Edge1->NextInSEL == Edge2 ) - { - TEdge* Next = Edge2->NextInSEL; - if( Next ) Next->PrevInSEL = Edge1; - TEdge* Prev = Edge1->PrevInSEL; - if( Prev ) Prev->NextInSEL = Edge2; - Edge2->PrevInSEL = Prev; - Edge2->NextInSEL = Edge1; - Edge1->PrevInSEL = Edge2; - Edge1->NextInSEL = Next; - } - else if( Edge2->NextInSEL == Edge1 ) - { - TEdge* Next = Edge1->NextInSEL; - if( Next ) Next->PrevInSEL = Edge2; - TEdge* Prev = Edge2->PrevInSEL; - if( Prev ) Prev->NextInSEL = Edge1; - Edge1->PrevInSEL = Prev; - Edge1->NextInSEL = Edge2; - Edge2->PrevInSEL = Edge1; - Edge2->NextInSEL = Next; - } - else - { - TEdge* Next = Edge1->NextInSEL; - TEdge* Prev = Edge1->PrevInSEL; - Edge1->NextInSEL = Edge2->NextInSEL; - if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; - Edge1->PrevInSEL = Edge2->PrevInSEL; - if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; - Edge2->NextInSEL = Next; - if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; - Edge2->PrevInSEL = Prev; - if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; - } - - if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; - else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; -} -//------------------------------------------------------------------------------ - -inline void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) -{ - if (HorzEdge.Bot.X < HorzEdge.Top.X) - { - Left = HorzEdge.Bot.X; - Right = HorzEdge.Top.X; - Dir = dLeftToRight; - } else - { - Left = HorzEdge.Top.X; - Right = HorzEdge.Bot.X; - Dir = dRightToLeft; - } -} -//------------------------------------------------------------------------ - -/******************************************************************************* -* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * -* Bottom of a scanbeam) are processed as if layered. The order in which HEs * -* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * -* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * -* and with other non-horizontal edges [*]. Once these intersections are * -* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * -* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * -*******************************************************************************/ - -void Clipper::ProcessHorizontal(TEdge *horzEdge) -{ - Direction dir; - cInt horzLeft, horzRight; - bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen); - - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - - TEdge* eLastHorz = horzEdge, *eMaxPair = 0; - while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) - eLastHorz = eLastHorz->NextInLML; - if (!eLastHorz->NextInLML) - eMaxPair = GetMaximaPair(eLastHorz); - - std::vector::const_iterator maxIt; - std::vector::const_reverse_iterator maxRit; - if (!m_Maxima.empty()) - { - //get the first maxima in range (X) ... - if (dir == dLeftToRight) - { - maxIt = m_Maxima.begin(); - while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) ++maxIt; - if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) - maxIt = m_Maxima.end(); - } - else - { - maxRit = m_Maxima.rbegin(); - while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) ++maxRit; - if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) - maxRit = m_Maxima.rend(); - } - } - - OutPt* op1 = 0; - - for (;;) //loop through consec. horizontal edges - { - - bool IsLastHorz = (horzEdge == eLastHorz); - TEdge* e = (dir == dLeftToRight) ? horzEdge->NextInAEL : horzEdge->PrevInAEL; - while(e) - { - - //this code block inserts extra coords into horizontal edges (in output - //polygons) whereever maxima touch these horizontal edges. This helps - //'simplifying' polygons (ie if the Simplify property is set). - if (!m_Maxima.empty()) - { - if (dir == dLeftToRight) - { - while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) - { - if (horzEdge->OutIdx >= 0 && !IsOpen) - AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); - ++maxIt; - } - } - else - { - while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) - { - if (horzEdge->OutIdx >= 0 && !IsOpen) - AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); - ++maxRit; - } - } - }; - - if ((dir == dLeftToRight && e->Curr.X > horzRight) || - (dir == dRightToLeft && e->Curr.X < horzLeft)) break; - - //Also break if we've got to the end of an intermediate horizontal edge ... - //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && - e->Dx < horzEdge->NextInLML->Dx) break; - - if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times - { - op1 = AddOutPt(horzEdge, e->Curr); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = GetLastOutPt(eNextHorz); - m_Joins.emplace_back(Join(op2, op1, eNextHorz->Top)); - } - eNextHorz = eNextHorz->NextInSEL; - } - m_GhostJoins.emplace_back(Join(op1, 0, horzEdge->Bot)); - } - - //OK, so far we're still in range of the horizontal Edge but make sure - //we're at the last of consec. horizontals when matching with eMaxPair - if(e == eMaxPair && IsLastHorz) - { - if (horzEdge->OutIdx >= 0) - AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); - DeleteFromAEL(horzEdge); - DeleteFromAEL(eMaxPair); - return; - } - - if(dir == dLeftToRight) - { - IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges(horzEdge, e, Pt); - } - else - { - IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges( e, horzEdge, Pt); - } - TEdge* eNext = (dir == dLeftToRight) ? e->NextInAEL : e->PrevInAEL; - SwapPositionsInAEL( horzEdge, e ); - e = eNext; - } //end while(e) - - //Break out of loop if HorzEdge.NextInLML is not also horizontal ... - if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; - - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - - } //end for (;;) - - if (horzEdge->OutIdx >= 0 && !op1) - { - op1 = GetLastOutPt(horzEdge); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = GetLastOutPt(eNextHorz); - m_Joins.emplace_back(Join(op2, op1, eNextHorz->Top)); - } - eNextHorz = eNextHorz->NextInSEL; - } - m_GhostJoins.emplace_back(Join(op1, 0, horzEdge->Top)); - } - - if (horzEdge->NextInLML) - { - if(horzEdge->OutIdx >= 0) - { - op1 = AddOutPt( horzEdge, horzEdge->Top); - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->WindDelta == 0) return; - //nb: HorzEdge is no longer horizontal here - TEdge* ePrev = horzEdge->PrevInAEL; - TEdge* eNext = horzEdge->NextInAEL; - if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && - ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && - (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && - SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) - { - OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); - m_Joins.emplace_back(Join(op1, op2, horzEdge->Top)); - } - else if (eNext && eNext->Curr.X == horzEdge->Bot.X && - eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && - eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && - SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) - { - OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); - m_Joins.emplace_back(Join(op1, op2, horzEdge->Top)); - } - } - else - UpdateEdgeIntoAEL(horzEdge); - } - else - { - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); - DeleteFromAEL(horzEdge); - } -} -//------------------------------------------------------------------------------ - -void Clipper::UpdateEdgeIntoAEL(TEdge *&e) -{ - if( !e->NextInLML ) - throw clipperException("UpdateEdgeIntoAEL: invalid call"); - - e->NextInLML->OutIdx = e->OutIdx; - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if (AelPrev) AelPrev->NextInAEL = e->NextInLML; - else m_ActiveEdges = e->NextInLML; - if (AelNext) AelNext->PrevInAEL = e->NextInLML; - e->NextInLML->Side = e->Side; - e->NextInLML->WindDelta = e->WindDelta; - e->NextInLML->WindCnt = e->WindCnt; - e->NextInLML->WindCnt2 = e->WindCnt2; - e = e->NextInLML; - e->Curr = e->Bot; - e->PrevInAEL = AelPrev; - e->NextInAEL = AelNext; - if (!IsHorizontal(*e)) - m_Scanbeam.push(e->Top.Y); -} -//------------------------------------------------------------------------------ - -bool Clipper::ProcessIntersections(const cInt topY) -{ - PROFILE_FUNC(); - if( !m_ActiveEdges ) return true; - try { - BuildIntersectList(topY); - size_t IlSize = m_IntersectList.size(); - if (IlSize == 0) return true; - if (IlSize == 1 || FixupIntersectionOrder()) { - for (IntersectNode &iNode : m_IntersectList) { - IntersectEdges( iNode.Edge1, iNode.Edge2, iNode.Pt); - SwapPositionsInAEL( iNode.Edge1 , iNode.Edge2 ); - } - m_IntersectList.clear(); - } - else return false; - } - catch(...) - { - m_SortedEdges = 0; - m_IntersectList.clear(); - throw clipperException("ProcessIntersections error"); - } - m_SortedEdges = 0; - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::BuildIntersectList(const cInt topY) -{ - if ( !m_ActiveEdges ) return; - - //prepare for sorting ... - TEdge* e = m_ActiveEdges; - m_SortedEdges = e; - while( e ) - { - e->PrevInSEL = e->PrevInAEL; - e->NextInSEL = e->NextInAEL; - e->Curr.X = TopX( *e, topY ); - e = e->NextInAEL; - } - - //bubblesort ... - bool isModified; - do - { - isModified = false; - e = m_SortedEdges; - while( e->NextInSEL ) - { - TEdge *eNext = e->NextInSEL; - IntPoint Pt; - if(e->Curr.X > eNext->Curr.X) - { - IntersectPoint(*e, *eNext, Pt); - m_IntersectList.emplace_back(IntersectNode(e, eNext, Pt)); - SwapPositionsInSEL(e, eNext); - isModified = true; - } - else - e = eNext; - } - if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; - else break; - } - while ( isModified ); - m_SortedEdges = 0; //important -} -//------------------------------------------------------------------------------ - - -inline bool EdgesAdjacent(const IntersectNode &inode) -{ - return (inode.Edge1->NextInSEL == inode.Edge2) || - (inode.Edge1->PrevInSEL == inode.Edge2); -} -//------------------------------------------------------------------------------ - -bool Clipper::FixupIntersectionOrder() -{ - //pre-condition: intersections are sorted Bottom-most first. - //Now it's crucial that intersections are made only between adjacent edges, - //so to ensure this the order of intersections may need adjusting ... - CopyAELToSEL(); - std::sort(m_IntersectList.begin(), m_IntersectList.end(), [](const IntersectNode &node1, const IntersectNode &node2) { return node2.Pt.Y < node1.Pt.Y; }); - - size_t cnt = m_IntersectList.size(); - for (size_t i = 0; i < cnt; ++i) - { - if (!EdgesAdjacent(m_IntersectList[i])) - { - size_t j = i + 1; - while (j < cnt && !EdgesAdjacent(m_IntersectList[j])) j++; - if (j == cnt) return false; - std::swap(m_IntersectList[i], m_IntersectList[j]); - } - SwapPositionsInSEL(m_IntersectList[i].Edge1, m_IntersectList[i].Edge2); - } - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::DoMaxima(TEdge *e) -{ - TEdge* eMaxPair = GetMaximaPair(e); - if (!eMaxPair) - { - if (e->OutIdx >= 0) - AddOutPt(e, e->Top); - DeleteFromAEL(e); - return; - } - - TEdge* eNext = e->NextInAEL; - while(eNext && eNext != eMaxPair) - { - IntersectEdges(e, eNext, e->Top); - SwapPositionsInAEL(e, eNext); - eNext = e->NextInAEL; - } - - if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) - { - DeleteFromAEL(e); - DeleteFromAEL(eMaxPair); - } - else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) - { - if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); - DeleteFromAEL(e); - DeleteFromAEL(eMaxPair); - } -#ifdef use_lines - else if (e->WindDelta == 0) - { - if (e->OutIdx >= 0) - { - AddOutPt(e, e->Top); - e->OutIdx = Unassigned; - } - DeleteFromAEL(e); - - if (eMaxPair->OutIdx >= 0) - { - AddOutPt(eMaxPair, e->Top); - eMaxPair->OutIdx = Unassigned; - } - DeleteFromAEL(eMaxPair); - } -#endif - else throw clipperException("DoMaxima error"); -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) -{ - PROFILE_FUNC(); - TEdge* e = m_ActiveEdges; - while( e ) - { - //1. process maxima, treating them as if they're 'bent' horizontal edges, - // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - bool IsMaximaEdge = IsMaxima(e, topY); - - if(IsMaximaEdge) - { - TEdge* eMaxPair = GetMaximaPair(e); - IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); - } - - if(IsMaximaEdge) - { - if (m_StrictSimple) m_Maxima.push_back(e->Top.X); - TEdge* ePrev = e->PrevInAEL; - DoMaxima(e); - if( !ePrev ) e = m_ActiveEdges; - else e = ePrev->NextInAEL; - } - else - { - //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... - if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) - { - UpdateEdgeIntoAEL(e); - if (e->OutIdx >= 0) - AddOutPt(e, e->Bot); - AddEdgeToSEL(e); - } - else - { - e->Curr.X = TopX( *e, topY ); - e->Curr.Y = topY; - } - - //When StrictlySimple and 'e' is being touched by another edge, then - //make sure both edges have a vertex here ... - if (m_StrictSimple) - { - TEdge* ePrev = e->PrevInAEL; - if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && - (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) - { - IntPoint pt = e->Curr; -#ifdef use_xyz - SetZ(pt, *ePrev, *e); -#endif - OutPt* op = AddOutPt(ePrev, pt); - OutPt* op2 = AddOutPt(e, pt); - m_Joins.emplace_back(Join(op, op2, pt)); //StrictlySimple (type-3) join - } - } - - e = e->NextInAEL; - } - } - - //3. Process horizontals at the Top of the scanbeam ... - std::sort(m_Maxima.begin(), m_Maxima.end()); - ProcessHorizontals(); - m_Maxima.clear(); - - //4. Promote intermediate vertices ... - e = m_ActiveEdges; - while(e) - { - if(IsIntermediate(e, topY)) - { - OutPt* op = 0; - if( e->OutIdx >= 0 ) - op = AddOutPt(e, e->Top); - UpdateEdgeIntoAEL(e); - - //if output polygons share an edge, they'll need joining later ... - TEdge* ePrev = e->PrevInAEL; - TEdge* eNext = e->NextInAEL; - if (ePrev && ePrev->Curr.X == e->Bot.X && - ePrev->Curr.Y == e->Bot.Y && op && - ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && - SlopesEqual(*e, *ePrev, m_UseFullRange) && - (e->WindDelta != 0) && (ePrev->WindDelta != 0)) - { - OutPt* op2 = AddOutPt(ePrev, e->Bot); - m_Joins.emplace_back(Join(op, op2, e->Top)); - } - else if (eNext && eNext->Curr.X == e->Bot.X && - eNext->Curr.Y == e->Bot.Y && op && - eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && - SlopesEqual(*e, *eNext, m_UseFullRange) && - (e->WindDelta != 0) && (eNext->WindDelta != 0)) - { - OutPt* op2 = AddOutPt(eNext, e->Bot); - m_Joins.emplace_back(Join(op, op2, e->Top)); - } - } - e = e->NextInAEL; - } -} -//------------------------------------------------------------------------------ - -void Clipper::FixupOutPolyline(OutRec &outrec) -{ - OutPt *pp = outrec.Pts; - OutPt *lastPP = pp->Prev; - while (pp != lastPP) - { - pp = pp->Next; - if (pp->Pt == pp->Prev->Pt) - { - if (pp == lastPP) lastPP = pp->Prev; - OutPt *tmpPP = pp->Prev; - tmpPP->Next = pp->Next; - pp->Next->Prev = tmpPP; - this->DisposeOutPt(pp); - pp = tmpPP; - } - } - - if (pp == pp->Prev) - { - this->DisposeOutPts(pp); - outrec.Pts = 0; - return; - } -} -//------------------------------------------------------------------------------ - -void Clipper::FixupOutPolygon(OutRec &outrec) -{ - //FixupOutPolygon() - removes duplicate points and simplifies consecutive - //parallel edges by removing the middle vertex. - OutPt *lastOK = nullptr; - outrec.BottomPt = nullptr; - OutPt *pp = outrec.Pts; - bool preserveCol = m_PreserveCollinear || m_StrictSimple; - - for (;;) - { - if (pp->Prev == pp || pp->Prev == pp->Next) - { - // Empty loop or a stick. Release the polygon. - this->DisposeOutPts(pp); - outrec.Pts = nullptr; - return; - } - - //test for duplicate points and collinear edges ... - if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || - (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && - (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) - { - lastOK = nullptr; - OutPt *tmp = pp; - pp->Prev->Next = pp->Next; - pp->Next->Prev = pp->Prev; - pp = pp->Prev; - this->DisposeOutPt(tmp); - } - else if (pp == lastOK) break; - else - { - if (!lastOK) lastOK = pp; - pp = pp->Next; - } - } - outrec.Pts = pp; -} -//------------------------------------------------------------------------------ - -// Count the number of points in a closed linked loop starting with Pts. -int PointCount(OutPt *Pts) -{ - if (!Pts) return 0; - int result = 0; - OutPt* p = Pts; - do - { - result++; - p = p->Next; - } - while (p != Pts); - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::BuildResult(Paths &polys) -{ - polys.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) - { - assert(! outRec->IsOpen); - if (!outRec->Pts) continue; - Path pg; - OutPt* p = outRec->Pts->Prev; - int cnt = PointCount(p); - if (cnt < 2) continue; - pg.reserve(cnt); - for (int i = 0; i < cnt; ++i) - { - pg.emplace_back(p->Pt); - p = p->Prev; - } - polys.emplace_back(std::move(pg)); - } -} -//------------------------------------------------------------------------------ - -void Clipper::BuildResult2(PolyTree& polytree) -{ - polytree.Clear(); - polytree.AllNodes.reserve(m_PolyOuts.size()); - //add each output polygon/contour to polytree ... - for (OutRec* outRec : m_PolyOuts) - { - int cnt = PointCount(outRec->Pts); - if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) - // Ignore an invalid output loop or a polyline. - continue; - - //skip OutRecs that (a) contain outermost polygons or - //(b) already have the correct owner/child linkage ... - if (outRec->FirstLeft && - (outRec->IsHole == outRec->FirstLeft->IsHole || ! outRec->FirstLeft->Pts)) { - OutRec* orfl = outRec->FirstLeft; - while (orfl && ((orfl->IsHole == outRec->IsHole) || !orfl->Pts)) - orfl = orfl->FirstLeft; - outRec->FirstLeft = orfl; - } - - //nb: polytree takes ownership of all the PolyNodes - polytree.AllNodes.emplace_back(PolyNode()); - PolyNode* pn = &polytree.AllNodes.back(); - outRec->PolyNd = pn; - pn->Parent = 0; - pn->Index = 0; - pn->Contour.reserve(cnt); - OutPt *op = outRec->Pts->Prev; - for (int j = 0; j < cnt; j++) - { - pn->Contour.emplace_back(op->Pt); - op = op->Prev; - } - } - - //fixup PolyNode links etc ... - polytree.Childs.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) - { - if (!outRec->PolyNd) continue; - if (outRec->IsOpen) - { - outRec->PolyNd->m_IsOpen = true; - polytree.AddChild(*outRec->PolyNd); - } - else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) - outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); - else - polytree.AddChild(*outRec->PolyNd); - } -} -//------------------------------------------------------------------------------ - -inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) -{ - if (e2.Curr.X == e1.Curr.X) - { - if (e2.Top.Y > e1.Top.Y) - return e2.Top.X < TopX(e1, e2.Top.Y); - else return e1.Top.X > TopX(e2, e1.Top.Y); - } - else return e2.Curr.X < e1.Curr.X; -} -//------------------------------------------------------------------------------ - -bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, - cInt& Left, cInt& Right) -{ - if (a1 < a2) - { - if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} - else {Left = std::max(a1,b2); Right = std::min(a2,b1);} - } - else - { - if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} - else {Left = std::max(a2,b2); Right = std::min(a1,b1);} - } - return Left < Right; -} -//------------------------------------------------------------------------------ - -// Make all points of outrec point to outrec.Idx -inline void UpdateOutPtIdxs(OutRec& outrec) -{ - OutPt* op = outrec.Pts; - do - { - op->Idx = outrec.Idx; - op = op->Prev; - } - while(op != outrec.Pts); -} -//------------------------------------------------------------------------------ - -void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) -{ - if(!m_ActiveEdges) - { - edge->PrevInAEL = 0; - edge->NextInAEL = 0; - m_ActiveEdges = edge; - } - else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) - { - edge->PrevInAEL = 0; - edge->NextInAEL = m_ActiveEdges; - m_ActiveEdges->PrevInAEL = edge; - m_ActiveEdges = edge; - } - else - { - if(!startEdge) startEdge = m_ActiveEdges; - while(startEdge->NextInAEL && - !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) - startEdge = startEdge->NextInAEL; - edge->NextInAEL = startEdge->NextInAEL; - if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; - edge->PrevInAEL = startEdge; - startEdge->NextInAEL = edge; - } -} -//---------------------------------------------------------------------- - -OutPt* Clipper::DupOutPt(OutPt* outPt, bool InsertAfter) -{ - OutPt* result = this->AllocateOutPt(); - result->Pt = outPt->Pt; - result->Idx = outPt->Idx; - if (InsertAfter) - { - result->Next = outPt->Next; - result->Prev = outPt; - outPt->Next->Prev = result; - outPt->Next = result; - } - else - { - result->Prev = outPt->Prev; - result->Next = outPt; - outPt->Prev->Next = result; - outPt->Prev = result; - } - return result; -} -//------------------------------------------------------------------------------ - -bool Clipper::JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, - const IntPoint &Pt, bool DiscardLeft) -{ - Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); - Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); - if (Dir1 == Dir2) return false; - - //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we - //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) - //So, to facilitate this while inserting Op1b and Op2b ... - //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, - //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) - if (Dir1 == dLeftToRight) - { - while (op1->Next->Pt.X <= Pt.X && - op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) - op1 = op1->Next; - if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; - op1b = this->DupOutPt(op1, !DiscardLeft); - if (op1b->Pt != Pt) - { - op1 = op1b; - op1->Pt = Pt; - op1b = this->DupOutPt(op1, !DiscardLeft); - } - } - else - { - while (op1->Next->Pt.X >= Pt.X && - op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) - op1 = op1->Next; - if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; - op1b = this->DupOutPt(op1, DiscardLeft); - if (op1b->Pt != Pt) - { - op1 = op1b; - op1->Pt = Pt; - op1b = this->DupOutPt(op1, DiscardLeft); - } - } - - if (Dir2 == dLeftToRight) - { - while (op2->Next->Pt.X <= Pt.X && - op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) - op2 = op2->Next; - if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; - op2b = this->DupOutPt(op2, !DiscardLeft); - if (op2b->Pt != Pt) - { - op2 = op2b; - op2->Pt = Pt; - op2b = this->DupOutPt(op2, !DiscardLeft); - }; - } else - { - while (op2->Next->Pt.X >= Pt.X && - op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) - op2 = op2->Next; - if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; - op2b = this->DupOutPt(op2, DiscardLeft); - if (op2b->Pt != Pt) - { - op2 = op2b; - op2->Pt = Pt; - op2b = this->DupOutPt(op2, DiscardLeft); - }; - }; - - if ((Dir1 == dLeftToRight) == DiscardLeft) - { - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - } - else - { - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - } - return true; -} -//------------------------------------------------------------------------------ - -bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) -{ - OutPt *op1 = j->OutPt1, *op1b; - OutPt *op2 = j->OutPt2, *op2b; - - //There are 3 kinds of joins for output polygons ... - //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere - //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). - //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same - //location at the Bottom of the overlapping segment (& Join.OffPt is above). - //3. StrictSimple joins where edges touch but are not collinear and where - //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. - bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); - - if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && - (j->OffPt == j->OutPt2->Pt)) - { - //Strictly Simple join ... - if (outRec1 != outRec2) return false; - op1b = j->OutPt1->Next; - while (op1b != op1 && (op1b->Pt == j->OffPt)) - op1b = op1b->Next; - bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); - op2b = j->OutPt2->Next; - while (op2b != op2 && (op2b->Pt == j->OffPt)) - op2b = op2b->Next; - bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); - if (reverse1 == reverse2) return false; - if (reverse1) - { - op1b = this->DupOutPt(op1, false); - op2b = this->DupOutPt(op2, true); - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } else - { - op1b = this->DupOutPt(op1, true); - op2b = this->DupOutPt(op2, false); - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - } - else if (isHorizontal) - { - //treat horizontal joins differently to non-horizontal joins since with - //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt - //may be anywhere along the horizontal edge. - op1b = op1; - while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) - op1 = op1->Prev; - while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) - op1b = op1b->Next; - if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' - - op2b = op2; - while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) - op2 = op2->Prev; - while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) - op2b = op2b->Next; - if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' - - cInt Left, Right; - //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges - if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) - return false; - - //DiscardLeftSide: when overlapping edges are joined, a spike will created - //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up - //on the discard Side as either may still be needed for other joins ... - IntPoint Pt; - bool DiscardLeftSide; - if (op1->Pt.X >= Left && op1->Pt.X <= Right) - { - Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); - } - else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) - { - Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); - } - else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) - { - Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; - } - else - { - Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); - } - j->OutPt1 = op1; j->OutPt2 = op2; - return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); - } else - { - //nb: For non-horizontal joins ... - // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y - // 2. Jr.OutPt1.Pt > Jr.OffPt.Y - - //make sure the polygons are correctly oriented ... - op1b = op1->Next; - while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; - bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || - !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); - if (Reverse1) - { - op1b = op1->Prev; - while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; - if ((op1b->Pt.Y > op1->Pt.Y) || - !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; - }; - op2b = op2->Next; - while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; - bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || - !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); - if (Reverse2) - { - op2b = op2->Prev; - while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; - if ((op2b->Pt.Y > op2->Pt.Y) || - !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; - } - - if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || - ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; - - if (Reverse1) - { - op1b = this->DupOutPt(op1, false); - op2b = this->DupOutPt(op2, true); - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } else - { - op1b = this->DupOutPt(op1, true); - op2b = this->DupOutPt(op2, false); - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - } -} -//---------------------------------------------------------------------- - -// This is potentially very expensive! O(n^3)! -void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const -{ - PROFILE_FUNC(); - //tests if NewOutRec contains the polygon before reassigning FirstLeft - for (OutRec *outRec : m_PolyOuts) - { - if (!outRec->Pts || !outRec->FirstLeft) continue; - OutRec* firstLeft = outRec->FirstLeft; - // Skip empty polygons. - while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) - outRec->FirstLeft = NewOutRec; - } -} -//---------------------------------------------------------------------- - -void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const -{ - //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon - for (OutRec *outRec : m_PolyOuts) - if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; -} -//---------------------------------------------------------------------- - -void Clipper::JoinCommonEdges() -{ - PROFILE_FUNC(); - for (Join &join : m_Joins) - { - OutRec *outRec1 = GetOutRec(join.OutPt1->Idx); - OutRec *outRec2 = GetOutRec(join.OutPt2->Idx); - - if (!outRec1->Pts || !outRec2->Pts) continue; - if (outRec1->IsOpen || outRec2->IsOpen) continue; - - //get the polygon fragment with the correct hole state (FirstLeft) - //before calling JoinPoints() ... - OutRec *holeStateRec; - if (outRec1 == outRec2) holeStateRec = outRec1; - else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2; - else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; - else holeStateRec = GetLowermostRec(outRec1, outRec2); - - if (!JoinPoints(&join, outRec1, outRec2)) continue; - - if (outRec1 == outRec2) - { - //instead of joining two polygons, we've just created a new one by - //splitting one polygon into two. - outRec1->Pts = join.OutPt1; - outRec1->BottomPt = 0; - outRec2 = CreateOutRec(); - outRec2->Pts = join.OutPt2; - - //update all OutRec2.Pts Idx's ... - UpdateOutPtIdxs(*outRec2); - - //We now need to check every OutRec.FirstLeft pointer. If it points - //to OutRec1 it may need to point to OutRec2 instead ... - if (m_UsingPolyTree) - for (size_t j = 0; j < m_PolyOuts.size() - 1; j++) - { - OutRec* oRec = m_PolyOuts[j]; - OutRec* firstLeft = oRec->FirstLeft; - while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (!oRec->Pts || firstLeft != outRec1 || - oRec->IsHole == outRec1->IsHole) continue; - if (Poly2ContainsPoly1(oRec->Pts, join.OutPt2)) - oRec->FirstLeft = outRec2; - } - - if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) - { - //outRec2 is contained by outRec1 ... - outRec2->IsHole = !outRec1->IsHole; - outRec2->FirstLeft = outRec1; - - // For each m_PolyOuts, replace FirstLeft from outRec2 to outRec1. - if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); - - if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) - ReversePolyPtLinks(outRec2->Pts); - - } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) - { - //outRec1 is contained by outRec2 ... - outRec2->IsHole = outRec1->IsHole; - outRec1->IsHole = !outRec2->IsHole; - outRec2->FirstLeft = outRec1->FirstLeft; - outRec1->FirstLeft = outRec2; - - // For each m_PolyOuts, replace FirstLeft from outRec1 to outRec2. - if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); - - if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) - ReversePolyPtLinks(outRec1->Pts); - } - else - { - //the 2 polygons are completely separate ... - outRec2->IsHole = outRec1->IsHole; - outRec2->FirstLeft = outRec1->FirstLeft; - - //fixup FirstLeft pointers that may need reassigning to OutRec2 - // For each polygon of m_PolyOuts, replace FirstLeft from outRec1 to outRec2 if the polygon is inside outRec2. - //FIXME This is potentially very expensive! O(n^3)! - if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); - } - - } else - { - //joined 2 polygons together ... - - outRec2->Pts = 0; - outRec2->BottomPt = 0; - outRec2->Idx = outRec1->Idx; - - outRec1->IsHole = holeStateRec->IsHole; - if (holeStateRec == outRec2) - outRec1->FirstLeft = outRec2->FirstLeft; - outRec2->FirstLeft = outRec1; - - // For each m_PolyOuts, replace FirstLeft from outRec2 to outRec1. - if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); - } - } -} - -//------------------------------------------------------------------------------ -// ClipperOffset support functions ... -//------------------------------------------------------------------------------ - -DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) -{ - if(pt2.X == pt1.X && pt2.Y == pt1.Y) - return DoublePoint(0, 0); - - double Dx = (double)(pt2.X - pt1.X); - double dy = (double)(pt2.Y - pt1.Y); - double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); - Dx *= f; - dy *= f; - return DoublePoint(dy, -Dx); -} - -//------------------------------------------------------------------------------ -// ClipperOffset class -//------------------------------------------------------------------------------ - -void ClipperOffset::Clear() -{ - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - delete m_polyNodes.Childs[i]; - m_polyNodes.Childs.clear(); - m_lowest.X = -1; -} -//------------------------------------------------------------------------------ - -void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) -{ - int highI = (int)path.size() - 1; - if (highI < 0) return; - PolyNode* newNode = new PolyNode(); - newNode->m_jointype = joinType; - newNode->m_endtype = endType; - - //strip duplicate points from path and also get index to the lowest point ... - bool has_shortest_edge_length = ShortestEdgeLength > 0.; - double shortest_edge_length2 = has_shortest_edge_length ? ShortestEdgeLength * ShortestEdgeLength : 0.; - if (endType == etClosedLine || endType == etClosedPolygon) - for (; highI > 0; -- highI) { - bool same = false; - if (has_shortest_edge_length) { - double dx = double(path[highI].X - path[0].X); - double dy = double(path[highI].Y - path[0].Y); - same = dx*dx + dy*dy < shortest_edge_length2; - } else - same = path[0] == path[highI]; - if (! same) - break; - } - newNode->Contour.reserve(highI + 1); - newNode->Contour.push_back(path[0]); - int j = 0, k = 0; - for (int i = 1; i <= highI; i++) { - bool same = false; - if (has_shortest_edge_length) { - double dx = double(path[i].X - newNode->Contour[j].X); - double dy = double(path[i].Y - newNode->Contour[j].Y); - same = dx*dx + dy*dy < shortest_edge_length2; - } else - same = newNode->Contour[j] == path[i]; - if (same) - continue; - j++; - newNode->Contour.push_back(path[i]); - if (path[i].Y > newNode->Contour[k].Y || - (path[i].Y == newNode->Contour[k].Y && - path[i].X < newNode->Contour[k].X)) k = j; - } - if (endType == etClosedPolygon && j < 2) - { - delete newNode; - return; - } - m_polyNodes.AddChild(*newNode); - - //if this path's lowest pt is lower than all the others then update m_lowest - if (endType != etClosedPolygon) return; - if (m_lowest.X < 0) - m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); - else - { - IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; - if (newNode->Contour[k].Y > ip.Y || - (newNode->Contour[k].Y == ip.Y && - newNode->Contour[k].X < ip.X)) - m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) -{ - for (const Path &path : paths) - AddPath(path, joinType, endType); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::FixOrientations() -{ - //fixup orientations of all closed paths if the orientation of the - //closed path with the lowermost vertex is wrong ... - if (m_lowest.X >= 0 && - !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) - { - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedPolygon || - (node.m_endtype == etClosedLine && Orientation(node.Contour))) - ReversePath(node.Contour); - } - } else - { - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) - ReversePath(node.Contour); - } - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Execute(Paths& solution, double delta) -{ - solution.clear(); - FixOrientations(); - DoOffset(delta); - - //now clean up 'corners' ... - Clipper clpr; - clpr.AddPaths(m_destPolys, ptSubject, true); - if (delta > 0) - { - clpr.Execute(ctUnion, solution, pftPositive, pftPositive); - } - else - { - IntRect r = clpr.GetBounds(); - Path outer(4); - outer[0] = IntPoint(r.left - 10, r.bottom + 10); - outer[1] = IntPoint(r.right + 10, r.bottom + 10); - outer[2] = IntPoint(r.right + 10, r.top - 10); - outer[3] = IntPoint(r.left - 10, r.top - 10); - - clpr.AddPath(outer, ptSubject, true); - clpr.ReverseSolution(true); - clpr.Execute(ctUnion, solution, pftNegative, pftNegative); - if (solution.size() > 0) solution.erase(solution.begin()); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Execute(PolyTree& solution, double delta) -{ - solution.Clear(); - FixOrientations(); - DoOffset(delta); - - //now clean up 'corners' ... - Clipper clpr; - clpr.AddPaths(m_destPolys, ptSubject, true); - if (delta > 0) - { - clpr.Execute(ctUnion, solution, pftPositive, pftPositive); - } - else - { - IntRect r = clpr.GetBounds(); - Path outer(4); - outer[0] = IntPoint(r.left - 10, r.bottom + 10); - outer[1] = IntPoint(r.right + 10, r.bottom + 10); - outer[2] = IntPoint(r.right + 10, r.top - 10); - outer[3] = IntPoint(r.left - 10, r.top - 10); - - clpr.AddPath(outer, ptSubject, true); - clpr.ReverseSolution(true); - clpr.Execute(ctUnion, solution, pftNegative, pftNegative); - //remove the outer PolyNode rectangle ... - if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) - { - PolyNode* outerNode = solution.Childs[0]; - solution.Childs.reserve(outerNode->ChildCount()); - solution.Childs[0] = outerNode->Childs[0]; - solution.Childs[0]->Parent = outerNode->Parent; - for (int i = 1; i < outerNode->ChildCount(); ++i) - solution.AddChild(*outerNode->Childs[i]); - } - else - solution.Clear(); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoOffset(double delta) -{ - m_destPolys.clear(); - m_delta = delta; - - //if Zero offset, just copy any CLOSED polygons to m_p and return ... - if (NEAR_ZERO(delta)) - { - m_destPolys.reserve(m_polyNodes.ChildCount()); - for (int i = 0; i < m_polyNodes.ChildCount(); i++) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedPolygon) - m_destPolys.push_back(node.Contour); - } - return; - } - - //see offset_triginometry3.svg in the documentation folder ... - if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); - else m_miterLim = 0.5; - - double y; - if (ArcTolerance <= 0.0) y = def_arc_tolerance; - else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) - y = std::fabs(delta) * def_arc_tolerance; - else y = ArcTolerance; - //see offset_triginometry2.svg in the documentation folder ... - double steps = pi / std::acos(1 - y / std::fabs(delta)); - if (steps > std::fabs(delta) * pi) - steps = std::fabs(delta) * pi; //ie excessive precision check - m_sin = std::sin(two_pi / steps); - m_cos = std::cos(two_pi / steps); - m_StepsPerRad = steps / two_pi; - if (delta < 0.0) m_sin = -m_sin; - - m_destPolys.reserve(m_polyNodes.ChildCount() * 2); - for (int i = 0; i < m_polyNodes.ChildCount(); i++) - { - PolyNode& node = *m_polyNodes.Childs[i]; - m_srcPoly = node.Contour; - - int len = (int)m_srcPoly.size(); - if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) - continue; - - m_destPoly.clear(); - if (len == 1) - { - if (node.m_jointype == jtRound) - { - double X = 1.0, Y = 0.0; - for (cInt j = 1; j <= steps; j++) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[0].X + X * delta), - Round(m_srcPoly[0].Y + Y * delta))); - double X2 = X; - X = X * m_cos - m_sin * Y; - Y = X2 * m_sin + Y * m_cos; - } - } - else - { - double X = -1.0, Y = -1.0; - for (int j = 0; j < 4; ++j) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[0].X + X * delta), - Round(m_srcPoly[0].Y + Y * delta))); - if (X < 0) X = 1; - else if (Y < 0) Y = 1; - else X = -1; - } - } - m_destPolys.push_back(m_destPoly); - continue; - } - //build m_normals ... - m_normals.clear(); - m_normals.reserve(len); - for (int j = 0; j < len - 1; ++j) - m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); - if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) - m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); - else - m_normals.push_back(DoublePoint(m_normals[len - 2])); - - if (node.m_endtype == etClosedPolygon) - { - int k = len - 1; - for (int j = 0; j < len; ++j) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - } - else if (node.m_endtype == etClosedLine) - { - int k = len - 1; - for (int j = 0; j < len; ++j) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - m_destPoly.clear(); - //re-build m_normals ... - DoublePoint n = m_normals[len -1]; - for (int j = len - 1; j > 0; j--) - m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); - m_normals[0] = DoublePoint(-n.X, -n.Y); - k = 0; - for (int j = len - 1; j >= 0; j--) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - } - else - { - int k = 0; - for (int j = 1; j < len - 1; ++j) - OffsetPoint(j, k, node.m_jointype); - - IntPoint pt1; - if (node.m_endtype == etOpenButt) - { - int j = len - 1; - pt1 = IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * - delta), Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); - m_destPoly.push_back(pt1); - pt1 = IntPoint(Round(m_srcPoly[j].X - m_normals[j].X * - delta), Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); - m_destPoly.push_back(pt1); - } - else - { - int j = len - 1; - k = len - 2; - m_sinA = 0; - m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); - if (node.m_endtype == etOpenSquare) - DoSquare(j, k); - else - DoRound(j, k); - } - - //re-build m_normals ... - for (int j = len - 1; j > 0; j--) - m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); - m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); - - k = len - 1; - for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); - - if (node.m_endtype == etOpenButt) - { - pt1 = IntPoint(Round(m_srcPoly[0].X - m_normals[0].X * delta), - Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); - m_destPoly.push_back(pt1); - pt1 = IntPoint(Round(m_srcPoly[0].X + m_normals[0].X * delta), - Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); - m_destPoly.push_back(pt1); - } - else - { - k = 1; - m_sinA = 0; - if (node.m_endtype == etOpenSquare) - DoSquare(0, 1); - else - DoRound(0, 1); - } - m_destPolys.push_back(m_destPoly); - } - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) -{ - //cross product ... - m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); - if (std::fabs(m_sinA * m_delta) < 1.0) - { - //dot product ... - double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); - if (cosA > 0) // angle => 0 degrees - { - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); - return; - } - //else angle => 180 degrees - } - else if (m_sinA > 1.0) m_sinA = 1.0; - else if (m_sinA < -1.0) m_sinA = -1.0; - - if (m_sinA * m_delta < 0) - { - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); - m_destPoly.push_back(m_srcPoly[j]); - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); - } - else - switch (jointype) - { - case jtMiter: - { - double r = 1 + (m_normals[j].X * m_normals[k].X + - m_normals[j].Y * m_normals[k].Y); - if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); - break; - } - case jtSquare: DoSquare(j, k); break; - case jtRound: DoRound(j, k); break; - } - k = j; -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoSquare(int j, int k) -{ - double dx = std::tan(std::atan2(m_sinA, - m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), - Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), - Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoMiter(int j, int k, double r) -{ - double q = m_delta / r; - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), - Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoRound(int j, int k) -{ - double a = std::atan2(m_sinA, - m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); - int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); - - double X = m_normals[k].X, Y = m_normals[k].Y, X2; - for (int i = 0; i < steps; ++i) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + X * m_delta), - Round(m_srcPoly[j].Y + Y * m_delta))); - X2 = X; - X = X * m_cos - m_sin * Y; - Y = X2 * m_sin + Y * m_cos; - } - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_normals[j].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); -} - -//------------------------------------------------------------------------------ -// Miscellaneous public functions -//------------------------------------------------------------------------------ - -// Called by Clipper::ExecuteInternal() -// For each polygon, search for exactly duplicate non-successive points. -// If such a point is found, the loop is split into two pieces. -// Search for the duplicate points is O(n^2)! -// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm -void Clipper::DoSimplePolygons() -{ - PROFILE_FUNC(); - size_t i = 0; - while (i < m_PolyOuts.size()) - { - OutRec* outrec = m_PolyOuts[i++]; - OutPt* op = outrec->Pts; - if (!op || outrec->IsOpen) continue; - do //for each Pt in Polygon until duplicate found do ... - { - OutPt* op2 = op->Next; - while (op2 != outrec->Pts) - { - if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) - { - //split the polygon into two ... - OutPt* op3 = op->Prev; - OutPt* op4 = op2->Prev; - op->Prev = op4; - op4->Next = op; - op2->Prev = op3; - op3->Next = op2; - - outrec->Pts = op; - OutRec* outrec2 = CreateOutRec(); - outrec2->Pts = op2; - UpdateOutPtIdxs(*outrec2); - if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) - { - //OutRec2 is contained by OutRec1 ... - outrec2->IsHole = !outrec->IsHole; - outrec2->FirstLeft = outrec; - // For each m_PolyOuts, replace FirstLeft from outRec2 to outrec. - if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); - } - else - if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) - { - //OutRec1 is contained by OutRec2 ... - outrec2->IsHole = outrec->IsHole; - outrec->IsHole = !outrec2->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - outrec->FirstLeft = outrec2; - // For each m_PolyOuts, replace FirstLeft from outrec to outrec2. - if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); - } - else - { - //the 2 polygons are separate ... - outrec2->IsHole = outrec->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - // For each polygon of m_PolyOuts, replace FirstLeft from outrec to outrec2 if the polygon is inside outRec2. - //FIXME This is potentially very expensive! O(n^3)! - if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); - } - op2 = op; //ie get ready for the Next iteration - } - op2 = op2->Next; - } - op = op->Next; - } - while (op != outrec->Pts); - } -} -//------------------------------------------------------------------------------ - -void ReversePath(Path& p) -{ - std::reverse(p.begin(), p.end()); -} -//------------------------------------------------------------------------------ - -void ReversePaths(Paths& p) -{ - for (Paths::size_type i = 0; i < p.size(); ++i) - ReversePath(p[i]); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) -{ - Clipper c; - c.StrictlySimple(true); - c.AddPath(in_poly, ptSubject, true); - c.Execute(ctUnion, out_polys, fillType, fillType); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) -{ - Clipper c; - c.StrictlySimple(true); - c.AddPaths(in_polys, ptSubject, true); - c.Execute(ctUnion, out_polys, fillType, fillType); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygons(Paths &polys, PolyFillType fillType) -{ - SimplifyPolygons(polys, polys, fillType); -} -//------------------------------------------------------------------------------ - -inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) -{ - double Dx = ((double)pt1.X - pt2.X); - double dy = ((double)pt1.Y - pt2.Y); - return (Dx*Dx + dy*dy); -} -//------------------------------------------------------------------------------ - -double DistanceFromLineSqrd( - const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) -{ - //The equation of a line in general form (Ax + By + C = 0) - //given 2 points (x¹,y¹) & (x²,y²) is ... - //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 - //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ - //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) - //see http://en.wikipedia.org/wiki/Perpendicular_distance - double A = double(ln1.Y - ln2.Y); - double B = double(ln2.X - ln1.X); - double C = A * ln1.X + B * ln1.Y; - C = A * pt.X + B * pt.Y - C; - return (C * C) / (A * A + B * B); -} -//--------------------------------------------------------------------------- - -bool SlopesNearCollinear(const IntPoint& pt1, - const IntPoint& pt2, const IntPoint& pt3, double distSqrd) -{ - //this function is more accurate when the point that's geometrically - //between the other 2 points is the one that's tested for distance. - //ie makes it more likely to pick up 'spikes' ... - if (std::abs(pt1.X - pt2.X) > std::abs(pt1.Y - pt2.Y)) - { - if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - else - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } - else - { - if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - else - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } -} -//------------------------------------------------------------------------------ - -bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) -{ - double Dx = (double)pt1.X - pt2.X; - double dy = (double)pt1.Y - pt2.Y; - return ((Dx * Dx) + (dy * dy) <= distSqrd); -} -//------------------------------------------------------------------------------ - -OutPt* ExcludeOp(OutPt* op) -{ - OutPt* result = op->Prev; - result->Next = op->Next; - op->Next->Prev = result; - result->Idx = 0; - return result; -} -//------------------------------------------------------------------------------ - -// Simplify a polygon using a linked list of points. -void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) -{ - //distance = proximity in units/pixels below which vertices - //will be stripped. Default ~= sqrt(2). - - size_t size = in_poly.size(); - - if (size == 0) - { - out_poly.clear(); - return; - } - - std::vector outPts(size); - for (size_t i = 0; i < size; ++i) - { - outPts[i].Pt = in_poly[i]; - outPts[i].Next = &outPts[(i + 1) % size]; - outPts[i].Next->Prev = &outPts[i]; - outPts[i].Idx = 0; - } - - double distSqrd = distance * distance; - OutPt* op = &outPts[0]; - while (op->Idx == 0 && op->Next != op->Prev) - { - if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) - { - op = ExcludeOp(op); - size--; - } - else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) - { - ExcludeOp(op->Next); - op = ExcludeOp(op); - size -= 2; - } - else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) - { - op = ExcludeOp(op); - size--; - } - else - { - op->Idx = 1; - op = op->Next; - } - } - - if (size < 3) size = 0; - out_poly.resize(size); - for (size_t i = 0; i < size; ++i) - { - out_poly[i] = op->Pt; - op = op->Next; - } -} -//------------------------------------------------------------------------------ - -void CleanPolygon(Path& poly, double distance) -{ - CleanPolygon(poly, poly, distance); -} -//------------------------------------------------------------------------------ - -void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) -{ - for (Paths::size_type i = 0; i < in_polys.size(); ++i) - CleanPolygon(in_polys[i], out_polys[i], distance); -} -//------------------------------------------------------------------------------ - -void CleanPolygons(Paths& polys, double distance) -{ - CleanPolygons(polys, polys, distance); -} -//------------------------------------------------------------------------------ - -void Minkowski(const Path& poly, const Path& path, - Paths& solution, bool isSum, bool isClosed) -{ - int delta = (isClosed ? 1 : 0); - size_t polyCnt = poly.size(); - size_t pathCnt = path.size(); - Paths pp; - pp.reserve(pathCnt); - if (isSum) - for (size_t i = 0; i < pathCnt; ++i) - { - Path p; - p.reserve(polyCnt); - for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); - pp.push_back(p); - } - else - for (size_t i = 0; i < pathCnt; ++i) - { - Path p; - p.reserve(polyCnt); - for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); - pp.push_back(p); - } - - solution.clear(); - solution.reserve((pathCnt + delta) * (polyCnt + 1)); - for (size_t i = 0; i < pathCnt - 1 + delta; ++i) - for (size_t j = 0; j < polyCnt; ++j) - { - Path quad; - quad.reserve(4); - quad.push_back(pp[i % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); - quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); - if (!Orientation(quad)) ReversePath(quad); - solution.push_back(quad); - } -} -//------------------------------------------------------------------------------ - -void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) -{ - Minkowski(pattern, path, solution, true, pathIsClosed); - Clipper c; - c.AddPaths(solution, ptSubject, true); - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -void TranslatePath(const Path& input, Path& output, const IntPoint& delta) -{ - //precondition: input != output - output.resize(input.size()); - for (size_t i = 0; i < input.size(); ++i) - output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); -} -//------------------------------------------------------------------------------ - -void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) -{ - Clipper c; - for (size_t i = 0; i < paths.size(); ++i) - { - Paths tmp; - Minkowski(pattern, paths[i], tmp, true, pathIsClosed); - c.AddPaths(tmp, ptSubject, true); - if (pathIsClosed) - { - Path tmp2; - TranslatePath(paths[i], tmp2, pattern[0]); - c.AddPath(tmp2, ptClip, true); - } - } - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) -{ - Minkowski(poly1, poly2, solution, false, true); - Clipper c; - c.AddPaths(solution, ptSubject, true); - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -enum NodeType {ntAny, ntOpen, ntClosed}; - -void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) -{ - bool match = true; - if (nodetype == ntClosed) match = !polynode.IsOpen(); - else if (nodetype == ntOpen) return; - - if (!polynode.Contour.empty() && match) - paths.push_back(polynode.Contour); - for (int i = 0; i < polynode.ChildCount(); ++i) - AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); -} -//------------------------------------------------------------------------------ - -void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - AddPolyNodeToPaths(polytree, ntAny, paths); -} -//------------------------------------------------------------------------------ - -void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - AddPolyNodeToPaths(polytree, ntClosed, paths); -} -//------------------------------------------------------------------------------ - -void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - //Open paths are top level only, so ... - for (int i = 0; i < polytree.ChildCount(); ++i) - if (polytree.Childs[i]->IsOpen()) - paths.push_back(polytree.Childs[i]->Contour); -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const IntPoint &p) -{ - s << "(" << p.X << "," << p.Y << ")"; - return s; -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const Path &p) -{ - if (p.empty()) return s; - Path::size_type last = p.size() -1; - for (Path::size_type i = 0; i < last; i++) - s << "(" << p[i].X << "," << p[i].Y << "), "; - s << "(" << p[last].X << "," << p[last].Y << ")\n"; - return s; -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const Paths &p) -{ - for (Paths::size_type i = 0; i < p.size(); i++) - s << p[i]; - s << "\n"; - return s; -} -//------------------------------------------------------------------------------ - -} //ClipperLib namespace +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.2.9 * +* Date : 16 February 2015 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2015 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +/******************************************************************************* +* * +* This is a translation of the Delphi Clipper library and the naming style * +* used has retained a Delphi flavour. * +* * +*******************************************************************************/ + +#include "clipper.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +static double const pi = 3.141592653589793238; +static double const two_pi = pi *2; +static double const def_arc_tolerance = 0.25; + +enum Direction { dRightToLeft, dLeftToRight }; + +static int const Unassigned = -1; //edge not currently 'owning' a solution +static int const Skip = -2; //edge that would otherwise close a path + +#define HORIZONTAL (-1.0E+40) +#define TOLERANCE (1.0e-20) +#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) + +// Output polygon. +struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + //The 'FirstLeft' field points to another OutRec that contains or is the + //'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is + //parsed left from the current edge (owning OutRec) until the owner OutRec + //is found. This field simplifies sorting the polygons into a tree structure + //which reflects the parent/child relationships of all polygons. + //This field should be renamed Parent, and will be later. + OutRec *FirstLeft; + // Used only by void Clipper::BuildResult2(PolyTree& polytree) + PolyNode *PolyNd; + // Linked list of output points, dynamically allocated. + OutPt *Pts; + OutPt *BottomPt; +}; + +//------------------------------------------------------------------------------ + +inline cInt Round(double val) +{ + return static_cast((val < 0) ? (val - 0.5) : (val + 0.5)); +} + +//------------------------------------------------------------------------------ +// PolyTree methods ... +//------------------------------------------------------------------------------ + +int PolyTree::Total() const +{ + int result = (int)AllNodes.size(); + //with negative offsets, ignore the hidden outer polygon ... + if (result > 0 && Childs.front() != &AllNodes.front()) result--; + return result; +} + +//------------------------------------------------------------------------------ +// PolyNode methods ... +//------------------------------------------------------------------------------ + +void PolyNode::AddChild(PolyNode& child) +{ + unsigned cnt = (unsigned)Childs.size(); + Childs.push_back(&child); + child.Parent = this; + child.Index = cnt; +} +//------------------------------------------------------------------------------ + +// Edge delimits a hole if it has an odd number of parent loops. +bool PolyNode::IsHole() const +{ + bool result = true; + PolyNode* node = Parent; + while (node) + { + result = !result; + node = node->Parent; + } + return result; +} + +//------------------------------------------------------------------------------ +// Miscellaneous global functions +//------------------------------------------------------------------------------ + +double Area(const Path &poly) +{ + int size = (int)poly.size(); + if (size < 3) return 0; + + double a = 0; + for (int i = 0, j = size -1; i < size; ++i) + { + a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); + j = i; + } + return -a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutRec &outRec) +{ + OutPt *op = outRec.Pts; + if (!op) return 0; + double a = 0; + do { + a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); + op = op->Next; + } while (op != outRec.Pts); + return a * 0.5; +} +//------------------------------------------------------------------------------ + +bool PointIsVertex(const IntPoint &Pt, OutPt *pp) +{ + OutPt *pp2 = pp; + do + { + if (pp2->Pt == Pt) return true; + pp2 = pp2->Next; + } + while (pp2 != pp); + return false; +} +//------------------------------------------------------------------------------ + +//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos +//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf +int PointInPolygon(const IntPoint &pt, const Path &path) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + size_t cnt = path.size(); + if (cnt < 3) return 0; + IntPoint ip = path[0]; + for(size_t i = 1; i <= cnt; ++i) + { + IntPoint ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y && ((ipNext.X == pt.X) || (ip.Y == pt.Y && ((ipNext.X > pt.X) == (ip.X < pt.X))))) + return -1; + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) result = 1 - result; + else + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } else + { + if (ipNext.X > pt.X) + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + } + ip = ipNext; + } + return result; +} +//------------------------------------------------------------------------------ + +// Called by Poly2ContainsPoly1() +int PointInPolygon (const IntPoint &pt, OutPt *op) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + OutPt* startOp = op; + do + { + if (op->Next->Pt.Y == pt.Y) + { + if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && + ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; + } + if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) + { + if (op->Pt.X >= pt.X) + { + if (op->Next->Pt.X > pt.X) result = 1 - result; + else + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } else + { + if (op->Next->Pt.X > pt.X) + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } + } + op = op->Next; + } while (startOp != op); + return result; +} +//------------------------------------------------------------------------------ + +// This is potentially very expensive! O(n^2)! +bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) +{ + PROFILE_FUNC(); + OutPt* op = OutPt1; + do + { + //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon + int res = PointInPolygon(op->Pt, OutPt2); + if (res >= 0) return res > 0; + op = op->Next; + } + while (op != OutPt1); + return true; +} +//---------------------------------------------------------------------- + +inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cInt dy2, bool UseFullInt64Range) { + return (UseFullInt64Range) ? + // |dx1| < 2^63, |dx2| < 2^63 etc, + Int128::sign_determinant_2x2_filtered(dx1, dy1, dx2, dy2) == 0 : +// Int128::sign_determinant_2x2(dx1, dy1, dx2, dy2) == 0 : + // |dx1| < 2^31, |dx2| < 2^31 etc, + // therefore the following computation could be done with 64bit arithmetics. + dy1 * dx2 == dx1 * dy2; +} +inline bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) + { return SlopesEqual(e1.Delta.X, e1.Delta.Y, e2.Delta.X, e2.Delta.Y, UseFullInt64Range); } +inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, bool UseFullInt64Range) + { return SlopesEqual(pt1.X-pt2.X, pt1.Y-pt2.Y, pt2.X-pt3.X, pt2.Y-pt3.Y, UseFullInt64Range); } +inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, const IntPoint &pt4, bool UseFullInt64Range) + { return SlopesEqual(pt1.X-pt2.X, pt1.Y-pt2.Y, pt3.X-pt4.X, pt3.Y-pt4.Y, UseFullInt64Range); } + +//------------------------------------------------------------------------------ + +inline bool IsHorizontal(TEdge &e) +{ + return e.Delta.Y == 0; +} +//------------------------------------------------------------------------------ + +inline double GetDx(const IntPoint &pt1, const IntPoint &pt2) +{ + return (pt1.Y == pt2.Y) ? + HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); +} +//--------------------------------------------------------------------------- + +inline cInt TopX(TEdge &edge, const cInt currentY) +{ + return (currentY == edge.Top.Y) ? + edge.Top.X : + edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); +} +//------------------------------------------------------------------------------ + +void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) +{ +#ifdef use_xyz + ip.Z = 0; +#endif + + double b1, b2; + if (Edge1.Dx == Edge2.Dx) + { + ip.Y = Edge1.Curr.Y; + ip.X = TopX(Edge1, ip.Y); + return; + } + else if (Edge1.Delta.X == 0) + { + ip.X = Edge1.Bot.X; + if (IsHorizontal(Edge2)) + ip.Y = Edge2.Bot.Y; + else + { + b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); + ip.Y = Round(ip.X / Edge2.Dx + b2); + } + } + else if (Edge2.Delta.X == 0) + { + ip.X = Edge2.Bot.X; + if (IsHorizontal(Edge1)) + ip.Y = Edge1.Bot.Y; + else + { + b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); + ip.Y = Round(ip.X / Edge1.Dx + b1); + } + } + else + { + b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; + b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; + double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); + ip.Y = Round(q); + ip.X = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ? + Round(Edge1.Dx * q + b1) : + Round(Edge2.Dx * q + b2); + } + + if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) + { + if (Edge1.Top.Y > Edge2.Top.Y) + ip.Y = Edge1.Top.Y; + else + ip.Y = Edge2.Top.Y; + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = TopX(Edge1, ip.Y); + else + ip.X = TopX(Edge2, ip.Y); + } + //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... + if (ip.Y > Edge1.Curr.Y) + { + ip.Y = Edge1.Curr.Y; + //use the more vertical edge to derive X ... + if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) + ip.X = TopX(Edge2, ip.Y); else + ip.X = TopX(Edge1, ip.Y); + } +} +//------------------------------------------------------------------------------ + +// Reverse a linked loop of points representing a closed polygon. +// This has a time complexity of O(n) +void ReversePolyPtLinks(OutPt *pp) +{ + if (!pp) return; + OutPt *pp1 = pp; + do { + OutPt *pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; + pp1 = pp2; + } while( pp1 != pp ); +} +//------------------------------------------------------------------------------ + +inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) +{ + std::memset(e, 0, sizeof(TEdge)); + e->Next = eNext; + e->Prev = ePrev; + e->Curr = Pt; + e->OutIdx = Unassigned; +} +//------------------------------------------------------------------------------ + +void InitEdge2(TEdge& e, PolyType Pt) +{ + if (e.Curr.Y >= e.Next->Curr.Y) + { + e.Bot = e.Curr; + e.Top = e.Next->Curr; + } else + { + e.Top = e.Curr; + e.Bot = e.Next->Curr; + } + + e.Delta.X = (e.Top.X - e.Bot.X); + e.Delta.Y = (e.Top.Y - e.Bot.Y); + + if (e.Delta.Y == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Delta.X) / e.Delta.Y; + + e.PolyTyp = Pt; +} +//------------------------------------------------------------------------------ + +// Called from ClipperBase::AddPathInternal() to remove collinear and duplicate points. +inline TEdge* RemoveEdge(TEdge* e) +{ + //removes e from double_linked_list (but without removing from memory) + e->Prev->Next = e->Next; + e->Next->Prev = e->Prev; + TEdge* result = e->Next; + e->Prev = 0; //flag as removed (see ClipperBase.Clear) + return result; +} +//------------------------------------------------------------------------------ + +inline void ReverseHorizontal(TEdge &e) +{ + //swap horizontal edges' Top and Bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + std::swap(e.Top.X, e.Bot.X); +#ifdef use_xyz + std::swap(e.Top.Z, e.Bot.Z); +#endif +} +//------------------------------------------------------------------------------ + +bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, + IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) +{ + //precondition: segments are Collinear. + if (std::abs(pt1a.X - pt1b.X) > std::abs(pt1a.Y - pt1b.Y)) + { + if (pt1a.X > pt1b.X) std::swap(pt1a, pt1b); + if (pt2a.X > pt2b.X) std::swap(pt2a, pt2b); + if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; + return pt1.X < pt2.X; + } else + { + if (pt1a.Y < pt1b.Y) std::swap(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) std::swap(pt2a, pt2b); + if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; + return pt1.Y > pt2.Y; + } +} +//------------------------------------------------------------------------------ + +bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) +{ + OutPt *p = btmPt1->Prev; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; + double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + p = btmPt1->Next; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; + double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + + p = btmPt2->Prev; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; + double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + p = btmPt2->Next; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; + double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); +} +//------------------------------------------------------------------------------ + +// Called by GetLowermostRec() +OutPt* GetBottomPt(OutPt *pp) +{ + OutPt* dups = 0; + OutPt* p = pp->Next; + while (p != pp) + { + if (p->Pt.Y > pp->Pt.Y) + { + pp = p; + dups = 0; + } + else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) + { + if (p->Pt.X < pp->Pt.X) + { + dups = 0; + pp = p; + } else + { + if (p->Next != pp && p->Prev != pp) dups = p; + } + } + p = p->Next; + } + if (dups) + { + //there appears to be at least 2 vertices at BottomPt so ... + while (dups != p) + { + if (!FirstIsBottomPt(p, dups)) pp = dups; + dups = dups->Next; + while (dups->Pt != pp->Pt) dups = dups->Next; + } + } + return pp; +} +//------------------------------------------------------------------------------ + +bool Pt2IsBetweenPt1AndPt3(const IntPoint &pt1, + const IntPoint &pt2, const IntPoint &pt3) +{ + if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) + return false; + else if (pt1.X != pt3.X) + return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else + return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); +} +//------------------------------------------------------------------------------ + +bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) +{ + if (seg1a > seg1b) std::swap(seg1a, seg1b); + if (seg2a > seg2b) std::swap(seg2a, seg2b); + return (seg1a < seg2b) && (seg2a < seg1b); +} + +//------------------------------------------------------------------------------ +// ClipperBase class methods ... +//------------------------------------------------------------------------------ + +// Called from ClipperBase::AddPath() to verify the scale of the input polygon coordinates. +inline void RangeTest(const IntPoint& Pt, bool& useFullRange) +{ + if (useFullRange) + { + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + throw clipperException("Coordinate outside allowed range"); + } + else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) + { + useFullRange = true; + RangeTest(Pt, useFullRange); + } +} +//------------------------------------------------------------------------------ + +// Called from ClipperBase::AddPath() to construct the Local Minima List. +// Find a local minimum edge on the path starting with E. +inline TEdge* FindNextLocMin(TEdge* E) +{ + for (;;) + { + while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; + if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; + while (IsHorizontal(*E->Prev)) E = E->Prev; + TEdge* E2 = E; + while (IsHorizontal(*E)) E = E->Next; + if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. + if (E2->Prev->Bot.X < E->Bot.X) E = E2; + break; + } + return E; +} +//------------------------------------------------------------------------------ + +// Called from ClipperBase::AddPath(). +TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) +{ + TEdge *Result = E; + TEdge *Horz = 0; + + if (E->OutIdx == Skip) + { + //if edges still remain in the current bound beyond the skip edge then + //create another LocMin and call ProcessBound once more + if (NextIsForward) + { + while (E->Top.Y == E->Next->Bot.Y) E = E->Next; + //don't include top horizontals when parsing a bound a second time, + //they will be contained in the opposite bound ... + while (E != Result && IsHorizontal(*E)) E = E->Prev; + } + else + { + while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; + while (E != Result && IsHorizontal(*E)) E = E->Next; + } + + if (E == Result) + { + if (NextIsForward) Result = E->Next; + else Result = E->Prev; + } + else + { + //there are more edges in the bound beyond result starting with E + if (NextIsForward) + E = Result->Next; + else + E = Result->Prev; + LocalMinimum locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + E->WindDelta = 0; + Result = ProcessBound(E, NextIsForward); + m_MinimaList.push_back(locMin); + } + return Result; + } + + TEdge *EStart; + + if (IsHorizontal(*E)) + { + //We need to be careful with open paths because this may not be a + //true local minima (ie E may be following a skip edge). + //Also, consecutive horz. edges may start heading left before going right. + if (NextIsForward) + EStart = E->Prev; + else + EStart = E->Next; + if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge + { + if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) + ReverseHorizontal(*E); + } + else if (EStart->Bot.X != E->Bot.X) + ReverseHorizontal(*E); + } + + EStart = E; + if (NextIsForward) + { + while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) + Result = Result->Next; + if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; + if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + } + while (E != Result) + { + E->NextInLML = E->Next; + if (IsHorizontal(*E) && E != EStart && + E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + ReverseHorizontal(*E); + Result = Result->Next; //move to the edge just beyond current bound + } else + { + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + Result = Result->Prev; + if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) + { + Horz = Result; + while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X || + Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + } + + while (E != Result) + { + E->NextInLML = E->Prev; + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + E = E->Prev; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + Result = Result->Prev; //move to the edge just beyond current bound + } + + return Result; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) +{ + PROFILE_FUNC(); + // Remove duplicate end point from a closed input path. + // Remove duplicate points from the end of the input path. + int highI = (int)pg.size() -1; + if (Closed) + while (highI > 0 && (pg[highI] == pg[0])) + --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) + --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) + return false; + + // Allocate a new edge array. + std::vector edges(highI + 1); + // Fill in the edge array. + bool result = AddPathInternal(pg, highI, PolyTyp, Closed, edges.data()); + if (result) + // Success, remember the edge array. + m_edges.emplace_back(std::move(edges)); + return result; +} + +bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) +{ + PROFILE_FUNC(); + std::vector num_edges(ppg.size(), 0); + int num_edges_total = 0; + for (size_t i = 0; i < ppg.size(); ++ i) { + const Path &pg = ppg[i]; + // Remove duplicate end point from a closed input path. + // Remove duplicate points from the end of the input path. + int highI = (int)pg.size() -1; + if (Closed) + while (highI > 0 && (pg[highI] == pg[0])) + --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) + --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) + highI = -1; + num_edges[i] = highI + 1; + num_edges_total += highI + 1; + } + if (num_edges_total == 0) + return false; + + // Allocate a new edge array. + std::vector edges(num_edges_total); + // Fill in the edge array. + bool result = false; + TEdge *p_edge = edges.data(); + for (Paths::size_type i = 0; i < ppg.size(); ++i) + if (num_edges[i]) { + bool res = AddPathInternal(ppg[i], num_edges[i] - 1, PolyTyp, Closed, p_edge); + if (res) { + p_edge += num_edges[i]; + result = true; + } + } + if (result) + // At least some edges were generated. Remember the edge array. + m_edges.emplace_back(std::move(edges)); + return result; +} + +bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, bool Closed, TEdge* edges) +{ + PROFILE_FUNC(); +#ifdef use_lines + if (!Closed && PolyTyp == ptClip) + throw clipperException("AddPath: Open paths must be subject."); +#else + if (!Closed) + throw clipperException("AddPath: Open paths have been disabled."); +#endif + + assert(highI >= 0 && highI < pg.size()); + + //1. Basic (first) edge initialization ... + try + { + edges[1].Curr = pg[1]; + RangeTest(pg[0], m_UseFullRange); + RangeTest(pg[highI], m_UseFullRange); + InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); + InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); + for (int i = highI - 1; i >= 1; --i) + { + RangeTest(pg[i], m_UseFullRange); + InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); + } + } + catch(...) + { + throw; //range test fails + } + TEdge *eStart = &edges[0]; + + //2. Remove duplicate vertices, and (when closed) collinear edges ... + TEdge *E = eStart, *eLoopStop = eStart; + for (;;) + { + //nb: allows matching start and end points when not Closed ... + if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) + { + if (E == E->Next) break; + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E->Prev == E->Next) + break; //only two vertices + else if (Closed && + SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + E = E->Prev; + eLoopStop = E; + continue; + } + E = E->Next; + if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; + } + + if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) + { + return false; + } + + if (!Closed) + { + m_HasOpenPaths = true; + eStart->Prev->OutIdx = Skip; + } + + //3. Do second stage of edge initialization ... + // IsFlat means all vertices have the same Y coordinate. + bool IsFlat = true; + E = eStart; + do + { + InitEdge2(*E, PolyTyp); + E = E->Next; + if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; + } + while (E != eStart); + + //4. Finally, add edge bounds to LocalMinima list ... + + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) + { + return false; + } + E->Prev->OutIdx = Skip; + LocalMinimum locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + locMin.RightBound->Side = esRight; + locMin.RightBound->WindDelta = 0; + for (;;) + { + if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + if (E->Next->OutIdx == Skip) break; + E->NextInLML = E->Next; + E = E->Next; + } + m_MinimaList.push_back(locMin); + return true; + } + + bool leftBoundIsForward; + TEdge* EMin = 0; + + //workaround to avoid an endless loop in the while loop below when + //open paths have matching start and end points ... + if (E->Prev->Bot == E->Prev->Top) E = E->Next; + + // Find local minima and store them into a Local Minima List. + // Multiple Local Minima could be created for a single path. + for (;;) + { + E = FindNextLocMin(E); + if (E == EMin) break; + else if (!EMin) EMin = E; + + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + LocalMinimum locMin; + locMin.Y = E->Bot.Y; + if (E->Dx < E->Prev->Dx) + { + locMin.LeftBound = E->Prev; + locMin.RightBound = E; + leftBoundIsForward = false; //Q.nextInLML = Q.prev + } else + { + locMin.LeftBound = E; + locMin.RightBound = E->Prev; + leftBoundIsForward = true; //Q.nextInLML = Q.next + } + locMin.LeftBound->Side = esLeft; + locMin.RightBound->Side = esRight; + + if (!Closed) locMin.LeftBound->WindDelta = 0; + else if (locMin.LeftBound->Next == locMin.RightBound) + locMin.LeftBound->WindDelta = -1; + else locMin.LeftBound->WindDelta = 1; + locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; + + E = ProcessBound(locMin.LeftBound, leftBoundIsForward); + if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); + + TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); + if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); + + if (locMin.LeftBound->OutIdx == Skip) + locMin.LeftBound = 0; + else if (locMin.RightBound->OutIdx == Skip) + locMin.RightBound = 0; + m_MinimaList.push_back(locMin); + if (!leftBoundIsForward) E = E2; + } + return true; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Clear() +{ + PROFILE_FUNC(); + m_MinimaList.clear(); + m_edges.clear(); + m_UseFullRange = false; + m_HasOpenPaths = false; +} +//------------------------------------------------------------------------------ + +// Initialize the Local Minima List: +// Sort the LML entries, initialize the left / right bound edges of each Local Minima. +void ClipperBase::Reset() +{ + PROFILE_FUNC(); + if (m_MinimaList.empty()) return; //ie nothing to process + std::sort(m_MinimaList.begin(), m_MinimaList.end(), [](const LocalMinimum& lm1, const LocalMinimum& lm2){ return lm1.Y < lm2.Y; }); + + //reset all edges ... + for (LocalMinimum &lm : m_MinimaList) { + TEdge* e = lm.LeftBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esLeft; + e->OutIdx = Unassigned; + } + + e = lm.RightBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esRight; + e->OutIdx = Unassigned; + } + } +} +//------------------------------------------------------------------------------ + +// Get bounds of the edges referenced by the Local Minima List. +// Returns (0,0,0,0) for an empty rectangle. +IntRect ClipperBase::GetBounds() +{ + PROFILE_FUNC(); + IntRect result; + auto lm = m_MinimaList.begin(); + if (lm == m_MinimaList.end()) + { + result.left = result.top = result.right = result.bottom = 0; + return result; + } + result.left = lm->LeftBound->Bot.X; + result.top = lm->LeftBound->Bot.Y; + result.right = lm->LeftBound->Bot.X; + result.bottom = lm->LeftBound->Bot.Y; + while (lm != m_MinimaList.end()) + { + result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); + TEdge* e = lm->LeftBound; + for (;;) { + TEdge* bottomE = e; + while (e->NextInLML) + { + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + e = e->NextInLML; + } + result.left = std::min(result.left, e->Bot.X); + result.right = std::max(result.right, e->Bot.X); + result.left = std::min(result.left, e->Top.X); + result.right = std::max(result.right, e->Top.X); + result.top = std::min(result.top, e->Top.Y); + if (bottomE == lm->LeftBound) e = lm->RightBound; + else break; + } + ++lm; + } + return result; +} + +//------------------------------------------------------------------------------ +// TClipper methods ... +//------------------------------------------------------------------------------ + +Clipper::Clipper(int initOptions) : + ClipperBase(), + m_OutPtsFree(nullptr), + m_OutPtsChunkSize(32), + m_OutPtsChunkLast(32), + m_ActiveEdges(nullptr), + m_SortedEdges(nullptr) +{ + m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); + m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); + m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); + m_HasOpenPaths = false; +#ifdef use_xyz + m_ZFill = 0; +#endif +} +//------------------------------------------------------------------------------ + +void Clipper::Reset() +{ + PROFILE_FUNC(); + ClipperBase::Reset(); + m_Scanbeam = std::priority_queue(); + m_Maxima.clear(); + m_ActiveEdges = 0; + m_SortedEdges = 0; + for (auto lm = m_MinimaList.rbegin(); lm != m_MinimaList.rend(); ++lm) + m_Scanbeam.push(lm->Y); +} + +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, Paths &solution, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + PROFILE_FUNC(); + if (m_HasOpenPaths) + throw clipperException("Error: PolyTree struct is needed for open path clipping."); + solution.resize(0); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = false; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult(solution); + DisposeAllOutRecs(); + return succeeded; +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree& polytree, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + PROFILE_FUNC(); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = true; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult2(polytree); + DisposeAllOutRecs(); + return succeeded; +} +//------------------------------------------------------------------------------ + +bool Clipper::ExecuteInternal() +{ + PROFILE_FUNC(); + bool succeeded = true; + try { + PROFILE_BLOCK(Clipper_ExecuteInternal_Process); + Reset(); + if (m_MinimaList.empty()) return true; + cInt botY = m_Scanbeam.top(); + do { m_Scanbeam.pop(); } while (! m_Scanbeam.empty() && botY == m_Scanbeam.top()); + do { + InsertLocalMinimaIntoAEL(botY); + ProcessHorizontals(); + m_GhostJoins.clear(); + if (m_Scanbeam.empty()) break; + cInt topY = m_Scanbeam.top(); + do { m_Scanbeam.pop(); } while (! m_Scanbeam.empty() && topY == m_Scanbeam.top()); + succeeded = ProcessIntersections(topY); + if (!succeeded) break; + ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + } while (!m_Scanbeam.empty() || !m_MinimaList.empty()); + } + catch(...) + { + succeeded = false; + } + + if (succeeded) + { + PROFILE_BLOCK(Clipper_ExecuteInternal_Fix); + + //fix orientations ... + //FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers? + //FIXME Vojtech: The area is calculated with floats, it may not be numerically stable! + { + PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_orientations); + for (OutRec *outRec : m_PolyOuts) + if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) + ReversePolyPtLinks(outRec->Pts); + } + + JoinCommonEdges(); + + //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() + { + PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_fixup); + for (OutRec *outRec : m_PolyOuts) + if (outRec->Pts) { + if (outRec->IsOpen) + // Removes duplicate points. + FixupOutPolyline(*outRec); + else + // Removes duplicate points and simplifies consecutive parallel edges by removing the middle vertex. + FixupOutPolygon(*outRec); + } + } + // For each polygon, search for exactly duplicate non-successive points. + // If such a point is found, the loop is split into two pieces. + // Search for the duplicate points is O(n^2)! + // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm + if (m_StrictSimple) DoSimplePolygons(); + } + + m_Joins.clear(); + m_GhostJoins.clear(); + return succeeded; +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AllocateOutPt() +{ + OutPt *pt; + if (m_OutPtsFree) { + // Recycle some of the already released points. + pt = m_OutPtsFree; + m_OutPtsFree = pt->Next; + } else if (m_OutPtsChunkLast < m_OutPtsChunkSize) { + // Get a point from the last chunk. + pt = m_OutPts.back() + (m_OutPtsChunkLast ++); + } else { + // The last chunk is full. Allocate a new one. + m_OutPts.push_back(new OutPt[m_OutPtsChunkSize]); + m_OutPtsChunkLast = 1; + pt = m_OutPts.back(); + } + return pt; +} + +void Clipper::DisposeAllOutRecs() +{ + for (OutPt *pts : m_OutPts) + delete[] pts; + for (OutRec *rec : m_PolyOuts) + delete rec; + m_OutPts.clear(); + m_OutPtsFree = nullptr; + m_OutPtsChunkLast = m_OutPtsChunkSize; + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::SetWindingCount(TEdge &edge) const +{ + TEdge *e = edge.PrevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; + if (!e) + { + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + edge.WindCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta == 0 && m_ClipType != ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else if (IsEvenOddFillType(edge)) + { + //EvenOdd filling ... + if (edge.WindDelta == 0) + { + //are we inside a subj polygon ... + bool Inside = true; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) + Inside = !Inside; + e2 = e2->PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else + { + //nonZero, Positive or Negative filling ... + if (e->WindCnt * e->WindDelta < 0) + { + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (std::abs(e->WindCnt) > 1) + { + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise continue to 'decrease' WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + else + //now outside all polys of same polytype so set own WC ... + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + } else + { + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta == 0) + edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); + //if wind direction is reversing prev then use same WC + else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise add to WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + + //update WindCnt2 ... + if (IsEvenOddAltFillType(edge)) + { + //EvenOdd filling ... + while (e != &edge) + { + if (e->WindDelta != 0) + edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); + e = e->NextInAEL; + } + } else + { + //nonZero, Positive or Negative filling ... + while ( e != &edge ) + { + edge.WindCnt2 += e->WindDelta; + e = e->NextInAEL; + } + } +} +//------------------------------------------------------------------------------ + +bool Clipper::IsContributing(const TEdge& edge) const +{ + PolyFillType pft, pft2; + if (edge.PolyTyp == ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch(pft) + { + case pftEvenOdd: + //return false if a subj line has been flagged as inside a subj polygon + if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; + break; + case pftNonZero: + if (std::abs(edge.WindCnt) != 1) return false; + break; + case pftPositive: + if (edge.WindCnt != 1) return false; + break; + default: //pftNegative + if (edge.WindCnt != -1) return false; + } + + switch(m_ClipType) + { + case ctIntersection: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctUnion: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + break; + case ctDifference: + if (edge.PolyTyp == ptSubject) + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctXor: + if (edge.WindDelta == 0) //XOr always contributing unless open + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + return true; + break; + default: + return true; + } +} +//------------------------------------------------------------------------------ + +// Called from Clipper::InsertLocalMinimaIntoAEL() and Clipper::IntersectEdges(). +OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + PROFILE_FUNC(); + OutPt* result; + TEdge *e, *prevE; + if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) + { + result = AddOutPt(e1, Pt); + e2->OutIdx = e1->OutIdx; + e1->Side = esLeft; + e2->Side = esRight; + e = e1; + if (e->PrevInAEL == e2) + prevE = e2->PrevInAEL; + else + prevE = e->PrevInAEL; + } else + { + result = AddOutPt(e2, Pt); + e1->OutIdx = e2->OutIdx; + e1->Side = esRight; + e2->Side = esLeft; + e = e2; + if (e->PrevInAEL == e1) + prevE = e1->PrevInAEL; + else + prevE = e->PrevInAEL; + } + + if (prevE && prevE->OutIdx >= 0 && + (TopX(*prevE, Pt.Y) == TopX(*e, Pt.Y)) && + SlopesEqual(*e, *prevE, m_UseFullRange) && + (e->WindDelta != 0) && (prevE->WindDelta != 0)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + m_Joins.emplace_back(Join(result, outPt, e->Top)); + } + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + AddOutPt( e1, Pt ); + if (e2->WindDelta == 0) AddOutPt(e2, Pt); + if( e1->OutIdx == e2->OutIdx ) + { + e1->OutIdx = Unassigned; + e2->OutIdx = Unassigned; + } + else if (e1->OutIdx < e2->OutIdx) + AppendPolygon(e1, e2); + else + AppendPolygon(e2, e1); +} +//------------------------------------------------------------------------------ + +void Clipper::AddEdgeToSEL(TEdge *edge) +{ + //SEL pointers in PEdge are reused to build a list of horizontal edges. + //However, we don't need to worry about order with horizontal edge processing. + if( !m_SortedEdges ) + { + m_SortedEdges = edge; + edge->PrevInSEL = 0; + edge->NextInSEL = 0; + } + else + { + edge->NextInSEL = m_SortedEdges; + edge->PrevInSEL = 0; + m_SortedEdges->PrevInSEL = edge; + m_SortedEdges = edge; + } +} +//------------------------------------------------------------------------------ + +void Clipper::CopyAELToSEL() +{ + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while ( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e = e->NextInAEL; + } +} + +//------------------------------------------------------------------------------ + +// Called from Clipper::ExecuteInternal() +void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) +{ + PROFILE_FUNC(); + while (!m_MinimaList.empty() && m_MinimaList.back().Y == botY) + { + TEdge* lb = m_MinimaList.back().LeftBound; + TEdge* rb = m_MinimaList.back().RightBound; + m_MinimaList.pop_back(); + + OutPt *Op1 = 0; + if (!lb) + { + //nb: don't insert LB into either AEL or SEL + InsertEdgeIntoAEL(rb, 0); + SetWindingCount(*rb); + if (IsContributing(*rb)) + Op1 = AddOutPt(rb, rb->Bot); + } + else if (!rb) + { + InsertEdgeIntoAEL(lb, 0); + SetWindingCount(*lb); + if (IsContributing(*lb)) + Op1 = AddOutPt(lb, lb->Bot); + m_Scanbeam.push(lb->Top.Y); + } + else + { + InsertEdgeIntoAEL(lb, 0); + InsertEdgeIntoAEL(rb, lb); + SetWindingCount( *lb ); + rb->WindCnt = lb->WindCnt; + rb->WindCnt2 = lb->WindCnt2; + if (IsContributing(*lb)) + Op1 = AddLocalMinPoly(lb, rb, lb->Bot); + m_Scanbeam.push(lb->Top.Y); + } + + if (rb) + { + if(IsHorizontal(*rb)) AddEdgeToSEL(rb); + else m_Scanbeam.push(rb->Top.Y); + } + + if (!lb || !rb) continue; + + //if any output polygons share an edge, they'll need joining later ... + if (Op1 && IsHorizontal(*rb) && + m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) + { + for (Join &jr : m_GhostJoins) + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + if (HorzSegmentsOverlap(jr.OutPt1->Pt.X, jr.OffPt.X, rb->Bot.X, rb->Top.X)) + m_Joins.emplace_back(Join(jr.OutPt1, Op1, jr.OffPt)); + } + + if (lb->OutIdx >= 0 && lb->PrevInAEL && + lb->PrevInAEL->Curr.X == lb->Bot.X && + lb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(*lb->PrevInAEL, *lb, m_UseFullRange) && + (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); + m_Joins.emplace_back(Join(Op1, Op2, lb->Top)); + } + + if(lb->NextInAEL != rb) + { + + if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(*rb->PrevInAEL, *rb, m_UseFullRange) && + (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); + m_Joins.emplace_back(Join(Op1, Op2, rb->Top)); + } + + TEdge* e = lb->NextInAEL; + if (e) + { + while( e != rb ) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the Right of param2 ABOVE the intersection ... + IntersectEdges(rb , e , lb->Curr); //order important here + e = e->NextInAEL; + } + } + } + + } +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted + if( AelPrev ) AelPrev->NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if( AelNext ) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = 0; + e->PrevInAEL = 0; +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromSEL(TEdge *e) +{ + TEdge* SelPrev = e->PrevInSEL; + TEdge* SelNext = e->NextInSEL; + if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted + if( SelPrev ) SelPrev->NextInSEL = SelNext; + else m_SortedEdges = SelNext; + if( SelNext ) SelNext->PrevInSEL = SelPrev; + e->NextInSEL = 0; + e->PrevInSEL = 0; +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) +{ + if (pt.Z != 0 || !m_ZFill) return; + else if (pt == e1.Bot) pt.Z = e1.Bot.Z; + else if (pt == e1.Top) pt.Z = e1.Top.Z; + else if (pt == e2.Bot) pt.Z = e2.Bot.Z; + else if (pt == e2.Top) pt.Z = e2.Top.Z; + else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) +{ + bool e1Contributing = ( e1->OutIdx >= 0 ); + bool e2Contributing = ( e2->OutIdx >= 0 ); + +#ifdef use_xyz + SetZ(Pt, *e1, *e2); +#endif + +#ifdef use_lines + //if either edge is on an OPEN path ... + if (e1->WindDelta == 0 || e2->WindDelta == 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1->WindDelta == 0 && e2->WindDelta == 0) return; + + //if intersecting a subj line with a subj poly ... + else if (e1->PolyTyp == e2->PolyTyp && + e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) + { + if (e1->WindDelta == 0) + { + if (e2Contributing) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + } + else + { + if (e1Contributing) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + } + else if (e1->PolyTyp != e2->PolyTyp) + { + //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... + if ((e1->WindDelta == 0) && std::abs(e2->WindCnt) == 1 && + (m_ClipType != ctUnion || e2->WindCnt2 == 0)) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + else if ((e2->WindDelta == 0) && (std::abs(e1->WindCnt) == 1) && + (m_ClipType != ctUnion || e1->WindCnt2 == 0)) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + return; + } +#endif + + //update winding counts... + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if ( e1->PolyTyp == e2->PolyTyp ) + { + if ( IsEvenOddFillType( *e1) ) + { + int oldE1WindCnt = e1->WindCnt; + e1->WindCnt = e2->WindCnt; + e2->WindCnt = oldE1WindCnt; + } else + { + if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; + else e1->WindCnt += e2->WindDelta; + if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; + else e2->WindCnt -= e1->WindDelta; + } + } else + { + if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; + else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; + else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; + } + + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1->PolyTyp == ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2->PolyTyp == ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + cInt e1Wc, e2Wc; + switch (e1FillType) + { + case pftPositive: e1Wc = e1->WindCnt; break; + case pftNegative: e1Wc = -e1->WindCnt; break; + default: e1Wc = std::abs(e1->WindCnt); + } + switch(e2FillType) + { + case pftPositive: e2Wc = e2->WindCnt; break; + case pftNegative: e2Wc = -e2->WindCnt; break; + default: e2Wc = std::abs(e2->WindCnt); + } + + if ( e1Contributing && e2Contributing ) + { + if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) + { + AddLocalMaxPoly(e1, e2, Pt); + } + else + { + AddOutPt(e1, Pt); + AddOutPt(e2, Pt); + std::swap(e1->Side, e2->Side); + std::swap(e1->OutIdx, e2->OutIdx); + } + } + else if ( e1Contributing ) + { + if (e2Wc == 0 || e2Wc == 1) + { + AddOutPt(e1, Pt); + std::swap(e1->Side, e2->Side); + std::swap(e1->OutIdx, e2->OutIdx); + } + } + else if ( e2Contributing ) + { + if (e1Wc == 0 || e1Wc == 1) + { + AddOutPt(e2, Pt); + std::swap(e1->Side, e2->Side); + std::swap(e1->OutIdx, e2->OutIdx); + } + } + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) + { + //neither edge is currently contributing ... + + cInt e1Wc2, e2Wc2; + switch (e1FillType2) + { + case pftPositive: e1Wc2 = e1->WindCnt2; break; + case pftNegative : e1Wc2 = -e1->WindCnt2; break; + default: e1Wc2 = std::abs(e1->WindCnt2); + } + switch (e2FillType2) + { + case pftPositive: e2Wc2 = e2->WindCnt2; break; + case pftNegative: e2Wc2 = -e2->WindCnt2; break; + default: e2Wc2 = std::abs(e2->WindCnt2); + } + + if (e1->PolyTyp != e2->PolyTyp) + { + AddLocalMinPoly(e1, e2, Pt); + } + else if (e1Wc == 1 && e2Wc == 1) + switch( m_ClipType ) { + case ctIntersection: + if (e1Wc2 > 0 && e2Wc2 > 0) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctUnion: + if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctDifference: + if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctXor: + AddLocalMinPoly(e1, e2, Pt); + } + else + std::swap(e1->Side, e2->Side); + } +} +//------------------------------------------------------------------------------ + +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const +{ + bool IsHole = false; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->OutIdx >= 0 && e2->WindDelta != 0) + { + IsHole = !IsHole; + if (! outrec->FirstLeft) + outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; + } + e2 = e2->PrevInAEL; + } + if (IsHole) outrec->IsHole = true; +} +//------------------------------------------------------------------------------ + +OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) +{ + //work out which polygon fragment has the correct hole state ... + if (!outRec1->BottomPt) + outRec1->BottomPt = GetBottomPt(outRec1->Pts); + if (!outRec2->BottomPt) + outRec2->BottomPt = GetBottomPt(outRec2->Pts); + OutPt *OutPt1 = outRec1->BottomPt; + OutPt *OutPt2 = outRec2->BottomPt; + if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; + else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; + else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; + else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; + else if (OutPt1->Next == OutPt1) return outRec2; + else if (OutPt2->Next == OutPt2) return outRec1; + else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; + else return outRec2; +} +//------------------------------------------------------------------------------ + +bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) +{ + do + { + outRec1 = outRec1->FirstLeft; + if (outRec1 == outRec2) return true; + } while (outRec1); + return false; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::GetOutRec(int Idx) +{ + OutRec* outrec = m_PolyOuts[Idx]; + while (outrec != m_PolyOuts[outrec->Idx]) + outrec = m_PolyOuts[outrec->Idx]; + return outrec; +} +//------------------------------------------------------------------------------ + +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const +{ + //get the start and ends of both output polygons ... + OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + + OutRec *holeStateRec; + if (Param1RightOfParam2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (Param1RightOfParam2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = GetLowermostRec(outRec1, outRec2); + + //get the start and ends of both output polygons and + //join e2 poly onto e1 poly and delete pointers to e2 ... + + OutPt* p1_lft = outRec1->Pts; + OutPt* p1_rt = p1_lft->Prev; + OutPt* p2_lft = outRec2->Pts; + OutPt* p2_rt = p2_lft->Prev; + + EdgeSide Side; + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1->Side == esLeft ) + { + if( e2->Side == esLeft ) + { + //z y x a b c + ReversePolyPtLinks(p2_lft); + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + outRec1->Pts = p2_rt; + } else + { + //x y z a b c + p2_rt->Next = p1_lft; + p1_lft->Prev = p2_rt; + p2_lft->Prev = p1_rt; + p1_rt->Next = p2_lft; + outRec1->Pts = p2_lft; + } + Side = esLeft; + } else + { + if( e2->Side == esRight ) + { + //a b c z y x + ReversePolyPtLinks(p2_lft); + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + } else + { + //a b c x y z + p1_rt->Next = p2_lft; + p2_lft->Prev = p1_rt; + p1_lft->Prev = p2_rt; + p2_rt->Next = p1_lft; + } + Side = esRight; + } + + outRec1->BottomPt = 0; + if (holeStateRec == outRec2) + { + if (outRec2->FirstLeft != outRec1) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec1->IsHole = outRec2->IsHole; + } + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->FirstLeft = outRec1; + + int OKIdx = e1->OutIdx; + int ObsoleteIdx = e2->OutIdx; + + e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly + e2->OutIdx = Unassigned; + + TEdge* e = m_ActiveEdges; + while( e ) + { + if( e->OutIdx == ObsoleteIdx ) + { + e->OutIdx = OKIdx; + e->Side = Side; + break; + } + e = e->NextInAEL; + } + + outRec2->Idx = outRec1->Idx; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::CreateOutRec() +{ + OutRec* result = new OutRec; + result->IsHole = false; + result->IsOpen = false; + result->FirstLeft = 0; + result->Pts = 0; + result->BottomPt = 0; + result->PolyNd = 0; + m_PolyOuts.push_back(result); + result->Idx = (int)m_PolyOuts.size()-1; + return result; +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) +{ + if( e->OutIdx < 0 ) + { + OutRec *outRec = CreateOutRec(); + outRec->IsOpen = (e->WindDelta == 0); + OutPt* newOp = this->AllocateOutPt(); + outRec->Pts = newOp; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = newOp; + newOp->Prev = newOp; + if (!outRec->IsOpen) + SetHoleState(e, outRec); + e->OutIdx = outRec->Idx; + return newOp; + } else + { + OutRec *outRec = m_PolyOuts[e->OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + OutPt* op = outRec->Pts; + + bool ToFront = (e->Side == esLeft); + if (ToFront && (pt == op->Pt)) return op; + else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; + + OutPt* newOp = this->AllocateOutPt(); + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = op; + newOp->Prev = op->Prev; + newOp->Prev->Next = newOp; + op->Prev = newOp; + if (ToFront) outRec->Pts = newOp; + return newOp; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::GetLastOutPt(TEdge *e) +{ + OutRec *outRec = m_PolyOuts[e->OutIdx]; + if (e->Side == esLeft) + return outRec->Pts; + else + return outRec->Pts->Prev; +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals() +{ + PROFILE_FUNC(); + TEdge* horzEdge = m_SortedEdges; + while(horzEdge) + { + DeleteFromSEL(horzEdge); + ProcessHorizontal(horzEdge); + horzEdge = m_SortedEdges; + } +} +//------------------------------------------------------------------------------ + +inline bool IsMaxima(TEdge *e, const cInt Y) +{ + return e && e->Top.Y == Y && !e->NextInLML; +} +//------------------------------------------------------------------------------ + +inline bool IsIntermediate(TEdge *e, const cInt Y) +{ + return e->Top.Y == Y && e->NextInLML; +} +//------------------------------------------------------------------------------ + +inline TEdge *GetMaximaPair(TEdge *e) +{ + TEdge* result = 0; + if ((e->Next->Top == e->Top) && !e->Next->NextInLML) + result = e->Next; + else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) + result = e->Prev; + + if (result && (result->OutIdx == Skip || + //result is false if both NextInAEL & PrevInAEL are nil & not horizontal ... + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) + return 0; + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +{ + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if( Edge1->NextInAEL == Edge2 ) + { + TEdge* Next = Edge2->NextInAEL; + if( Next ) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if( Prev ) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; + } + else if( Edge2->NextInAEL == Edge1 ) + { + TEdge* Next = Edge1->NextInAEL; + if( Next ) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if( Prev ) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; + } + else + { + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if( Edge1->NextInAEL ) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if( Edge1->PrevInAEL ) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if( Edge2->NextInAEL ) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if( Edge2->PrevInAEL ) Edge2->PrevInAEL->NextInAEL = Edge2; + } + + if( !Edge1->PrevInAEL ) m_ActiveEdges = Edge1; + else if( !Edge2->PrevInAEL ) m_ActiveEdges = Edge2; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) +{ + if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; + if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; + + if( Edge1->NextInSEL == Edge2 ) + { + TEdge* Next = Edge2->NextInSEL; + if( Next ) Next->PrevInSEL = Edge1; + TEdge* Prev = Edge1->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge2; + Edge2->PrevInSEL = Prev; + Edge2->NextInSEL = Edge1; + Edge1->PrevInSEL = Edge2; + Edge1->NextInSEL = Next; + } + else if( Edge2->NextInSEL == Edge1 ) + { + TEdge* Next = Edge1->NextInSEL; + if( Next ) Next->PrevInSEL = Edge2; + TEdge* Prev = Edge2->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge1; + Edge1->PrevInSEL = Prev; + Edge1->NextInSEL = Edge2; + Edge2->PrevInSEL = Edge1; + Edge2->NextInSEL = Next; + } + else + { + TEdge* Next = Edge1->NextInSEL; + TEdge* Prev = Edge1->PrevInSEL; + Edge1->NextInSEL = Edge2->NextInSEL; + if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; + Edge1->PrevInSEL = Edge2->PrevInSEL; + if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; + Edge2->NextInSEL = Next; + if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; + Edge2->PrevInSEL = Prev; + if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; + } + + if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; + else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; +} +//------------------------------------------------------------------------------ + +inline void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) +{ + if (HorzEdge.Bot.X < HorzEdge.Top.X) + { + Left = HorzEdge.Bot.X; + Right = HorzEdge.Top.X; + Dir = dLeftToRight; + } else + { + Left = HorzEdge.Top.X; + Right = HorzEdge.Bot.X; + Dir = dRightToLeft; + } +} +//------------------------------------------------------------------------ + +/******************************************************************************* +* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * +* Bottom of a scanbeam) are processed as if layered. The order in which HEs * +* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * +* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * +* and with other non-horizontal edges [*]. Once these intersections are * +* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * +* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * +*******************************************************************************/ + +void Clipper::ProcessHorizontal(TEdge *horzEdge) +{ + Direction dir; + cInt horzLeft, horzRight; + bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen); + + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + TEdge* eLastHorz = horzEdge, *eMaxPair = 0; + while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) + eLastHorz = eLastHorz->NextInLML; + if (!eLastHorz->NextInLML) + eMaxPair = GetMaximaPair(eLastHorz); + + std::vector::const_iterator maxIt; + std::vector::const_reverse_iterator maxRit; + if (!m_Maxima.empty()) + { + //get the first maxima in range (X) ... + if (dir == dLeftToRight) + { + maxIt = m_Maxima.begin(); + while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) ++maxIt; + if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) + maxIt = m_Maxima.end(); + } + else + { + maxRit = m_Maxima.rbegin(); + while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) ++maxRit; + if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) + maxRit = m_Maxima.rend(); + } + } + + OutPt* op1 = 0; + + for (;;) //loop through consec. horizontal edges + { + + bool IsLastHorz = (horzEdge == eLastHorz); + TEdge* e = (dir == dLeftToRight) ? horzEdge->NextInAEL : horzEdge->PrevInAEL; + while(e) + { + + //this code block inserts extra coords into horizontal edges (in output + //polygons) whereever maxima touch these horizontal edges. This helps + //'simplifying' polygons (ie if the Simplify property is set). + if (!m_Maxima.empty()) + { + if (dir == dLeftToRight) + { + while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); + ++maxIt; + } + } + else + { + while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); + ++maxRit; + } + } + }; + + if ((dir == dLeftToRight && e->Curr.X > horzRight) || + (dir == dRightToLeft && e->Curr.X < horzLeft)) break; + + //Also break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times + { + op1 = AddOutPt(horzEdge, e->Curr); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + m_Joins.emplace_back(Join(op2, op1, eNextHorz->Top)); + } + eNextHorz = eNextHorz->NextInSEL; + } + m_GhostJoins.emplace_back(Join(op1, 0, horzEdge->Bot)); + } + + //OK, so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if(e == eMaxPair && IsLastHorz) + { + if (horzEdge->OutIdx >= 0) + AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); + return; + } + + if(dir == dLeftToRight) + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges(horzEdge, e, Pt); + } + else + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges( e, horzEdge, Pt); + } + TEdge* eNext = (dir == dLeftToRight) ? e->NextInAEL : e->PrevInAEL; + SwapPositionsInAEL( horzEdge, e ); + e = eNext; + } //end while(e) + + //Break out of loop if HorzEdge.NextInLML is not also horizontal ... + if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; + + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + } //end for (;;) + + if (horzEdge->OutIdx >= 0 && !op1) + { + op1 = GetLastOutPt(horzEdge); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + m_Joins.emplace_back(Join(op2, op1, eNextHorz->Top)); + } + eNextHorz = eNextHorz->NextInSEL; + } + m_GhostJoins.emplace_back(Join(op1, 0, horzEdge->Top)); + } + + if (horzEdge->NextInLML) + { + if(horzEdge->OutIdx >= 0) + { + op1 = AddOutPt( horzEdge, horzEdge->Top); + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->WindDelta == 0) return; + //nb: HorzEdge is no longer horizontal here + TEdge* ePrev = horzEdge->PrevInAEL; + TEdge* eNext = horzEdge->NextInAEL; + if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && + ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && + (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) + { + OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); + m_Joins.emplace_back(Join(op1, op2, horzEdge->Top)); + } + else if (eNext && eNext->Curr.X == horzEdge->Bot.X && + eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) + { + OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); + m_Joins.emplace_back(Join(op1, op2, horzEdge->Top)); + } + } + else + UpdateEdgeIntoAEL(horzEdge); + } + else + { + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); + DeleteFromAEL(horzEdge); + } +} +//------------------------------------------------------------------------------ + +void Clipper::UpdateEdgeIntoAEL(TEdge *&e) +{ + if( !e->NextInLML ) + throw clipperException("UpdateEdgeIntoAEL: invalid call"); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) + m_Scanbeam.push(e->Top.Y); +} +//------------------------------------------------------------------------------ + +bool Clipper::ProcessIntersections(const cInt topY) +{ + PROFILE_FUNC(); + if( !m_ActiveEdges ) return true; + try { + BuildIntersectList(topY); + size_t IlSize = m_IntersectList.size(); + if (IlSize == 0) return true; + if (IlSize == 1 || FixupIntersectionOrder()) { + for (IntersectNode &iNode : m_IntersectList) { + IntersectEdges( iNode.Edge1, iNode.Edge2, iNode.Pt); + SwapPositionsInAEL( iNode.Edge1 , iNode.Edge2 ); + } + m_IntersectList.clear(); + } + else return false; + } + catch(...) + { + m_SortedEdges = 0; + m_IntersectList.clear(); + throw clipperException("ProcessIntersections error"); + } + m_SortedEdges = 0; + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::BuildIntersectList(const cInt topY) +{ + if ( !m_ActiveEdges ) return; + + //prepare for sorting ... + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e->Curr.X = TopX( *e, topY ); + e = e->NextInAEL; + } + + //bubblesort ... + bool isModified; + do + { + isModified = false; + e = m_SortedEdges; + while( e->NextInSEL ) + { + TEdge *eNext = e->NextInSEL; + IntPoint Pt; + if(e->Curr.X > eNext->Curr.X) + { + IntersectPoint(*e, *eNext, Pt); + m_IntersectList.emplace_back(IntersectNode(e, eNext, Pt)); + SwapPositionsInSEL(e, eNext); + isModified = true; + } + else + e = eNext; + } + if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; + else break; + } + while ( isModified ); + m_SortedEdges = 0; //important +} +//------------------------------------------------------------------------------ + + +inline bool EdgesAdjacent(const IntersectNode &inode) +{ + return (inode.Edge1->NextInSEL == inode.Edge2) || + (inode.Edge1->PrevInSEL == inode.Edge2); +} +//------------------------------------------------------------------------------ + +bool Clipper::FixupIntersectionOrder() +{ + //pre-condition: intersections are sorted Bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + CopyAELToSEL(); + std::sort(m_IntersectList.begin(), m_IntersectList.end(), [](const IntersectNode &node1, const IntersectNode &node2) { return node2.Pt.Y < node1.Pt.Y; }); + + size_t cnt = m_IntersectList.size(); + for (size_t i = 0; i < cnt; ++i) + { + if (!EdgesAdjacent(m_IntersectList[i])) + { + size_t j = i + 1; + while (j < cnt && !EdgesAdjacent(m_IntersectList[j])) j++; + if (j == cnt) return false; + std::swap(m_IntersectList[i], m_IntersectList[j]); + } + SwapPositionsInSEL(m_IntersectList[i].Edge1, m_IntersectList[i].Edge2); + } + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DoMaxima(TEdge *e) +{ + TEdge* eMaxPair = GetMaximaPair(e); + if (!eMaxPair) + { + if (e->OutIdx >= 0) + AddOutPt(e, e->Top); + DeleteFromAEL(e); + return; + } + + TEdge* eNext = e->NextInAEL; + while(eNext && eNext != eMaxPair) + { + IntersectEdges(e, eNext, e->Top); + SwapPositionsInAEL(e, eNext); + eNext = e->NextInAEL; + } + + if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) + { + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } + else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) + { + if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } +#ifdef use_lines + else if (e->WindDelta == 0) + { + if (e->OutIdx >= 0) + { + AddOutPt(e, e->Top); + e->OutIdx = Unassigned; + } + DeleteFromAEL(e); + + if (eMaxPair->OutIdx >= 0) + { + AddOutPt(eMaxPair, e->Top); + eMaxPair->OutIdx = Unassigned; + } + DeleteFromAEL(eMaxPair); + } +#endif + else throw clipperException("DoMaxima error"); +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) +{ + PROFILE_FUNC(); + TEdge* e = m_ActiveEdges; + while( e ) + { + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + bool IsMaximaEdge = IsMaxima(e, topY); + + if(IsMaximaEdge) + { + TEdge* eMaxPair = GetMaximaPair(e); + IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); + } + + if(IsMaximaEdge) + { + if (m_StrictSimple) m_Maxima.push_back(e->Top.X); + TEdge* ePrev = e->PrevInAEL; + DoMaxima(e); + if( !ePrev ) e = m_ActiveEdges; + else e = ePrev->NextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) + { + UpdateEdgeIntoAEL(e); + if (e->OutIdx >= 0) + AddOutPt(e, e->Bot); + AddEdgeToSEL(e); + } + else + { + e->Curr.X = TopX( *e, topY ); + e->Curr.Y = topY; + } + + //When StrictlySimple and 'e' is being touched by another edge, then + //make sure both edges have a vertex here ... + if (m_StrictSimple) + { + TEdge* ePrev = e->PrevInAEL; + if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && + (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) + { + IntPoint pt = e->Curr; +#ifdef use_xyz + SetZ(pt, *ePrev, *e); +#endif + OutPt* op = AddOutPt(ePrev, pt); + OutPt* op2 = AddOutPt(e, pt); + m_Joins.emplace_back(Join(op, op2, pt)); //StrictlySimple (type-3) join + } + } + + e = e->NextInAEL; + } + } + + //3. Process horizontals at the Top of the scanbeam ... + std::sort(m_Maxima.begin(), m_Maxima.end()); + ProcessHorizontals(); + m_Maxima.clear(); + + //4. Promote intermediate vertices ... + e = m_ActiveEdges; + while(e) + { + if(IsIntermediate(e, topY)) + { + OutPt* op = 0; + if( e->OutIdx >= 0 ) + op = AddOutPt(e, e->Top); + UpdateEdgeIntoAEL(e); + + //if output polygons share an edge, they'll need joining later ... + TEdge* ePrev = e->PrevInAEL; + TEdge* eNext = e->NextInAEL; + if (ePrev && ePrev->Curr.X == e->Bot.X && + ePrev->Curr.Y == e->Bot.Y && op && + ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*e, *ePrev, m_UseFullRange) && + (e->WindDelta != 0) && (ePrev->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(ePrev, e->Bot); + m_Joins.emplace_back(Join(op, op2, e->Top)); + } + else if (eNext && eNext->Curr.X == e->Bot.X && + eNext->Curr.Y == e->Bot.Y && op && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*e, *eNext, m_UseFullRange) && + (e->WindDelta != 0) && (eNext->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(eNext, e->Bot); + m_Joins.emplace_back(Join(op, op2, e->Top)); + } + } + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolyline(OutRec &outrec) +{ + OutPt *pp = outrec.Pts; + OutPt *lastPP = pp->Prev; + while (pp != lastPP) + { + pp = pp->Next; + if (pp->Pt == pp->Prev->Pt) + { + if (pp == lastPP) lastPP = pp->Prev; + OutPt *tmpPP = pp->Prev; + tmpPP->Next = pp->Next; + pp->Next->Prev = tmpPP; + this->DisposeOutPt(pp); + pp = tmpPP; + } + } + + if (pp == pp->Prev) + { + this->DisposeOutPts(pp); + outrec.Pts = 0; + return; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolygon(OutRec &outrec) +{ + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt *lastOK = nullptr; + outrec.BottomPt = nullptr; + OutPt *pp = outrec.Pts; + bool preserveCol = m_PreserveCollinear || m_StrictSimple; + + for (;;) + { + if (pp->Prev == pp || pp->Prev == pp->Next) + { + // Empty loop or a stick. Release the polygon. + this->DisposeOutPts(pp); + outrec.Pts = nullptr; + return; + } + + //test for duplicate points and collinear edges ... + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && + (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + { + lastOK = nullptr; + OutPt *tmp = pp; + pp->Prev->Next = pp->Next; + pp->Next->Prev = pp->Prev; + pp = pp->Prev; + this->DisposeOutPt(tmp); + } + else if (pp == lastOK) break; + else + { + if (!lastOK) lastOK = pp; + pp = pp->Next; + } + } + outrec.Pts = pp; +} +//------------------------------------------------------------------------------ + +// Count the number of points in a closed linked loop starting with Pts. +int PointCount(OutPt *Pts) +{ + if (!Pts) return 0; + int result = 0; + OutPt* p = Pts; + do + { + result++; + p = p->Next; + } + while (p != Pts); + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult(Paths &polys) +{ + polys.reserve(m_PolyOuts.size()); + for (OutRec* outRec : m_PolyOuts) + { + assert(! outRec->IsOpen); + if (!outRec->Pts) continue; + Path pg; + OutPt* p = outRec->Pts->Prev; + int cnt = PointCount(p); + if (cnt < 2) continue; + pg.reserve(cnt); + for (int i = 0; i < cnt; ++i) + { + pg.emplace_back(p->Pt); + p = p->Prev; + } + polys.emplace_back(std::move(pg)); + } +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult2(PolyTree& polytree) +{ + polytree.Clear(); + polytree.AllNodes.reserve(m_PolyOuts.size()); + //add each output polygon/contour to polytree ... + for (OutRec* outRec : m_PolyOuts) + { + int cnt = PointCount(outRec->Pts); + if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) + // Ignore an invalid output loop or a polyline. + continue; + + //skip OutRecs that (a) contain outermost polygons or + //(b) already have the correct owner/child linkage ... + if (outRec->FirstLeft && + (outRec->IsHole == outRec->FirstLeft->IsHole || ! outRec->FirstLeft->Pts)) { + OutRec* orfl = outRec->FirstLeft; + while (orfl && ((orfl->IsHole == outRec->IsHole) || !orfl->Pts)) + orfl = orfl->FirstLeft; + outRec->FirstLeft = orfl; + } + + //nb: polytree takes ownership of all the PolyNodes + polytree.AllNodes.emplace_back(PolyNode()); + PolyNode* pn = &polytree.AllNodes.back(); + outRec->PolyNd = pn; + pn->Parent = 0; + pn->Index = 0; + pn->Contour.reserve(cnt); + OutPt *op = outRec->Pts->Prev; + for (int j = 0; j < cnt; j++) + { + pn->Contour.emplace_back(op->Pt); + op = op->Prev; + } + } + + //fixup PolyNode links etc ... + polytree.Childs.reserve(m_PolyOuts.size()); + for (OutRec* outRec : m_PolyOuts) + { + if (!outRec->PolyNd) continue; + if (outRec->IsOpen) + { + outRec->PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec->PolyNd); + } + else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) + outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else + polytree.AddChild(*outRec->PolyNd); + } +} +//------------------------------------------------------------------------------ + +inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) +{ + if (e2.Curr.X == e1.Curr.X) + { + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < TopX(e1, e2.Top.Y); + else return e1.Top.X > TopX(e2, e1.Top.Y); + } + else return e2.Curr.X < e1.Curr.X; +} +//------------------------------------------------------------------------------ + +bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, + cInt& Left, cInt& Right) +{ + if (a1 < a2) + { + if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} + else {Left = std::max(a1,b2); Right = std::min(a2,b1);} + } + else + { + if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} + else {Left = std::max(a2,b2); Right = std::min(a1,b1);} + } + return Left < Right; +} +//------------------------------------------------------------------------------ + +// Make all points of outrec point to outrec.Idx +inline void UpdateOutPtIdxs(OutRec& outrec) +{ + OutPt* op = outrec.Pts; + do + { + op->Idx = outrec.Idx; + op = op->Prev; + } + while(op != outrec.Pts); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) +{ + if(!m_ActiveEdges) + { + edge->PrevInAEL = 0; + edge->NextInAEL = 0; + m_ActiveEdges = edge; + } + else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) + { + edge->PrevInAEL = 0; + edge->NextInAEL = m_ActiveEdges; + m_ActiveEdges->PrevInAEL = edge; + m_ActiveEdges = edge; + } + else + { + if(!startEdge) startEdge = m_ActiveEdges; + while(startEdge->NextInAEL && + !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) + startEdge = startEdge->NextInAEL; + edge->NextInAEL = startEdge->NextInAEL; + if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; + edge->PrevInAEL = startEdge; + startEdge->NextInAEL = edge; + } +} +//---------------------------------------------------------------------- + +OutPt* Clipper::DupOutPt(OutPt* outPt, bool InsertAfter) +{ + OutPt* result = this->AllocateOutPt(); + result->Pt = outPt->Pt; + result->Idx = outPt->Idx; + if (InsertAfter) + { + result->Next = outPt->Next; + result->Prev = outPt; + outPt->Next->Prev = result; + outPt->Next = result; + } + else + { + result->Prev = outPt->Prev; + result->Next = outPt; + outPt->Prev->Next = result; + outPt->Prev = result; + } + return result; +} +//------------------------------------------------------------------------------ + +bool Clipper::JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, + const IntPoint &Pt, bool DiscardLeft) +{ + Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); + Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); + if (Dir1 == Dir2) return false; + + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == dLeftToRight) + { + while (op1->Next->Pt.X <= Pt.X && + op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = this->DupOutPt(op1, !DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = this->DupOutPt(op1, !DiscardLeft); + } + } + else + { + while (op1->Next->Pt.X >= Pt.X && + op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = this->DupOutPt(op1, DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = this->DupOutPt(op1, DiscardLeft); + } + } + + if (Dir2 == dLeftToRight) + { + while (op2->Next->Pt.X <= Pt.X && + op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = this->DupOutPt(op2, !DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = this->DupOutPt(op2, !DiscardLeft); + }; + } else + { + while (op2->Next->Pt.X >= Pt.X && + op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = this->DupOutPt(op2, DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = this->DupOutPt(op2, DiscardLeft); + }; + }; + + if ((Dir1 == dLeftToRight) == DiscardLeft) + { + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + } + else + { + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + } + return true; +} +//------------------------------------------------------------------------------ + +bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) +{ + OutPt *op1 = j->OutPt1, *op1b; + OutPt *op2 = j->OutPt2, *op2b; + + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictSimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); + + if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && + (j->OffPt == j->OutPt2->Pt)) + { + //Strictly Simple join ... + if (outRec1 != outRec2) return false; + op1b = j->OutPt1->Next; + while (op1b != op1 && (op1b->Pt == j->OffPt)) + op1b = op1b->Next; + bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); + op2b = j->OutPt2->Next; + while (op2b != op2 && (op2b->Pt == j->OffPt)) + op2b = op2b->Next; + bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); + if (reverse1 == reverse2) return false; + if (reverse1) + { + op1b = this->DupOutPt(op1, false); + op2b = this->DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = this->DupOutPt(op1, true); + op2b = this->DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) + op1 = op1->Prev; + while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) + op1b = op1b->Next; + if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' + + op2b = op2; + while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) + op2 = op2->Prev; + while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) + op2b = op2b->Next; + if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' + + cInt Left, Right; + //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges + if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) + return false; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + IntPoint Pt; + bool DiscardLeftSide; + if (op1->Pt.X >= Left && op1->Pt.X <= Right) + { + Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); + } + else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) + { + Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); + } + else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) + { + Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; + } + else + { + Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); + } + j->OutPt1 = op1; j->OutPt2 = op2; + return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } else + { + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + + //make sure the polygons are correctly oriented ... + op1b = op1->Next; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; + bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse1) + { + op1b = op1->Prev; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; + if ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; + }; + op2b = op2->Next; + while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; + bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse2) + { + op2b = op2->Prev; + while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; + if ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; + } + + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; + + if (Reverse1) + { + op1b = this->DupOutPt(op1, false); + op2b = this->DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = this->DupOutPt(op1, true); + op2b = this->DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } +} +//---------------------------------------------------------------------- + +// This is potentially very expensive! O(n^3)! +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const +{ + PROFILE_FUNC(); + //tests if NewOutRec contains the polygon before reassigning FirstLeft + for (OutRec *outRec : m_PolyOuts) + { + if (!outRec->Pts || !outRec->FirstLeft) continue; + OutRec* firstLeft = outRec->FirstLeft; + // Skip empty polygons. + while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; + if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) + outRec->FirstLeft = NewOutRec; + } +} +//---------------------------------------------------------------------- + +void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const +{ + //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon + for (OutRec *outRec : m_PolyOuts) + if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; +} +//---------------------------------------------------------------------- + +void Clipper::JoinCommonEdges() +{ + PROFILE_FUNC(); + for (Join &join : m_Joins) + { + OutRec *outRec1 = GetOutRec(join.OutPt1->Idx); + OutRec *outRec2 = GetOutRec(join.OutPt2->Idx); + + if (!outRec1->Pts || !outRec2->Pts) continue; + if (outRec1->IsOpen || outRec2->IsOpen) continue; + + //get the polygon fragment with the correct hole state (FirstLeft) + //before calling JoinPoints() ... + OutRec *holeStateRec; + if (outRec1 == outRec2) holeStateRec = outRec1; + else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2; + else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; + else holeStateRec = GetLowermostRec(outRec1, outRec2); + + if (!JoinPoints(&join, outRec1, outRec2)) continue; + + if (outRec1 == outRec2) + { + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1->Pts = join.OutPt1; + outRec1->BottomPt = 0; + outRec2 = CreateOutRec(); + outRec2->Pts = join.OutPt2; + + //update all OutRec2.Pts Idx's ... + UpdateOutPtIdxs(*outRec2); + + //We now need to check every OutRec.FirstLeft pointer. If it points + //to OutRec1 it may need to point to OutRec2 instead ... + if (m_UsingPolyTree) + for (size_t j = 0; j < m_PolyOuts.size() - 1; j++) + { + OutRec* oRec = m_PolyOuts[j]; + OutRec* firstLeft = oRec->FirstLeft; + while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; + if (!oRec->Pts || firstLeft != outRec1 || + oRec->IsHole == outRec1->IsHole) continue; + if (Poly2ContainsPoly1(oRec->Pts, join.OutPt2)) + oRec->FirstLeft = outRec2; + } + + if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) + { + //outRec2 is contained by outRec1 ... + outRec2->IsHole = !outRec1->IsHole; + outRec2->FirstLeft = outRec1; + + // For each m_PolyOuts, replace FirstLeft from outRec2 to outRec1. + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + + if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) + ReversePolyPtLinks(outRec2->Pts); + + } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) + { + //outRec1 is contained by outRec2 ... + outRec2->IsHole = outRec1->IsHole; + outRec1->IsHole = !outRec2->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + outRec1->FirstLeft = outRec2; + + // For each m_PolyOuts, replace FirstLeft from outRec1 to outRec2. + if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); + + if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) + ReversePolyPtLinks(outRec1->Pts); + } + else + { + //the 2 polygons are completely separate ... + outRec2->IsHole = outRec1->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + + //fixup FirstLeft pointers that may need reassigning to OutRec2 + // For each polygon of m_PolyOuts, replace FirstLeft from outRec1 to outRec2 if the polygon is inside outRec2. + //FIXME This is potentially very expensive! O(n^3)! + if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); + } + + } else + { + //joined 2 polygons together ... + + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->Idx = outRec1->Idx; + + outRec1->IsHole = holeStateRec->IsHole; + if (holeStateRec == outRec2) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec2->FirstLeft = outRec1; + + // For each m_PolyOuts, replace FirstLeft from outRec2 to outRec1. + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + } + } +} + +//------------------------------------------------------------------------------ +// ClipperOffset support functions ... +//------------------------------------------------------------------------------ + +DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) +{ + if(pt2.X == pt1.X && pt2.Y == pt1.Y) + return DoublePoint(0, 0); + + double Dx = (double)(pt2.X - pt1.X); + double dy = (double)(pt2.Y - pt1.Y); + double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); + Dx *= f; + dy *= f; + return DoublePoint(dy, -Dx); +} + +//------------------------------------------------------------------------------ +// ClipperOffset class +//------------------------------------------------------------------------------ + +void ClipperOffset::Clear() +{ + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + delete m_polyNodes.Childs[i]; + m_polyNodes.Childs.clear(); + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) +{ + int highI = (int)path.size() - 1; + if (highI < 0) return; + PolyNode* newNode = new PolyNode(); + newNode->m_jointype = joinType; + newNode->m_endtype = endType; + + //strip duplicate points from path and also get index to the lowest point ... + bool has_shortest_edge_length = ShortestEdgeLength > 0.; + double shortest_edge_length2 = has_shortest_edge_length ? ShortestEdgeLength * ShortestEdgeLength : 0.; + if (endType == etClosedLine || endType == etClosedPolygon) + for (; highI > 0; -- highI) { + bool same = false; + if (has_shortest_edge_length) { + double dx = double(path[highI].X - path[0].X); + double dy = double(path[highI].Y - path[0].Y); + same = dx*dx + dy*dy < shortest_edge_length2; + } else + same = path[0] == path[highI]; + if (! same) + break; + } + newNode->Contour.reserve(highI + 1); + newNode->Contour.push_back(path[0]); + int j = 0, k = 0; + for (int i = 1; i <= highI; i++) { + bool same = false; + if (has_shortest_edge_length) { + double dx = double(path[i].X - newNode->Contour[j].X); + double dy = double(path[i].Y - newNode->Contour[j].Y); + same = dx*dx + dy*dy < shortest_edge_length2; + } else + same = newNode->Contour[j] == path[i]; + if (same) + continue; + j++; + newNode->Contour.push_back(path[i]); + if (path[i].Y > newNode->Contour[k].Y || + (path[i].Y == newNode->Contour[k].Y && + path[i].X < newNode->Contour[k].X)) k = j; + } + if (endType == etClosedPolygon && j < 2) + { + delete newNode; + return; + } + m_polyNodes.AddChild(*newNode); + + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != etClosedPolygon) return; + if (m_lowest.X < 0) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + else + { + IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; + if (newNode->Contour[k].Y > ip.Y || + (newNode->Contour[k].Y == ip.Y && + newNode->Contour[k].X < ip.X)) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) +{ + for (const Path &path : paths) + AddPath(path, joinType, endType); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::FixOrientations() +{ + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (m_lowest.X >= 0 && + !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon || + (node.m_endtype == etClosedLine && Orientation(node.Contour))) + ReversePath(node.Contour); + } + } else + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) + ReversePath(node.Contour); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(Paths& solution, double delta) +{ + solution.clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + if (solution.size() > 0) solution.erase(solution.begin()); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(PolyTree& solution, double delta) +{ + solution.Clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) + { + PolyNode* outerNode = solution.Childs[0]; + solution.Childs.reserve(outerNode->ChildCount()); + solution.Childs[0] = outerNode->Childs[0]; + solution.Childs[0]->Parent = outerNode->Parent; + for (int i = 1; i < outerNode->ChildCount(); ++i) + solution.AddChild(*outerNode->Childs[i]); + } + else + solution.Clear(); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoOffset(double delta) +{ + m_destPolys.clear(); + m_delta = delta; + + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (NEAR_ZERO(delta)) + { + m_destPolys.reserve(m_polyNodes.ChildCount()); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon) + m_destPolys.push_back(node.Contour); + } + return; + } + + //see offset_triginometry3.svg in the documentation folder ... + if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); + else m_miterLim = 0.5; + + double y; + if (ArcTolerance <= 0.0) y = def_arc_tolerance; + else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) + y = std::fabs(delta) * def_arc_tolerance; + else y = ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + double steps = pi / std::acos(1 - y / std::fabs(delta)); + if (steps > std::fabs(delta) * pi) + steps = std::fabs(delta) * pi; //ie excessive precision check + m_sin = std::sin(two_pi / steps); + m_cos = std::cos(two_pi / steps); + m_StepsPerRad = steps / two_pi; + if (delta < 0.0) m_sin = -m_sin; + + m_destPolys.reserve(m_polyNodes.ChildCount() * 2); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + m_srcPoly = node.Contour; + + int len = (int)m_srcPoly.size(); + if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) + continue; + + m_destPoly.clear(); + if (len == 1) + { + if (node.m_jointype == jtRound) + { + double X = 1.0, Y = 0.0; + for (cInt j = 1; j <= steps; j++) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + } + else + { + double X = -1.0, Y = -1.0; + for (int j = 0; j < 4; ++j) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + if (X < 0) X = 1; + else if (Y < 0) Y = 1; + else X = -1; + } + } + m_destPolys.push_back(m_destPoly); + continue; + } + //build m_normals ... + m_normals.clear(); + m_normals.reserve(len); + for (int j = 0; j < len - 1; ++j) + m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) + m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + else + m_normals.push_back(DoublePoint(m_normals[len - 2])); + + if (node.m_endtype == etClosedPolygon) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else if (node.m_endtype == etClosedLine) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + m_destPoly.clear(); + //re-build m_normals ... + DoublePoint n = m_normals[len -1]; + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-n.X, -n.Y); + k = 0; + for (int j = len - 1; j >= 0; j--) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else + { + int k = 0; + for (int j = 1; j < len - 1; ++j) + OffsetPoint(j, k, node.m_jointype); + + IntPoint pt1; + if (node.m_endtype == etOpenButt) + { + int j = len - 1; + pt1 = IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * + delta), Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint(Round(m_srcPoly[j].X - m_normals[j].X * + delta), Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + int j = len - 1; + k = len - 2; + m_sinA = 0; + m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); + if (node.m_endtype == etOpenSquare) + DoSquare(j, k); + else + DoRound(j, k); + } + + //re-build m_normals ... + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); + + k = len - 1; + for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); + + if (node.m_endtype == etOpenButt) + { + pt1 = IntPoint(Round(m_srcPoly[0].X - m_normals[0].X * delta), + Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint(Round(m_srcPoly[0].X + m_normals[0].X * delta), + Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + k = 1; + m_sinA = 0; + if (node.m_endtype == etOpenSquare) + DoSquare(0, 1); + else + DoRound(0, 1); + } + m_destPolys.push_back(m_destPoly); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) +{ + //cross product ... + m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); + if (std::fabs(m_sinA * m_delta) < 1.0) + { + //dot product ... + double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); + if (cosA > 0) // angle => 0 degrees + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + return; + } + //else angle => 180 degrees + } + else if (m_sinA > 1.0) m_sinA = 1.0; + else if (m_sinA < -1.0) m_sinA = -1.0; + + if (m_sinA * m_delta < 0) + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + m_destPoly.push_back(m_srcPoly[j]); + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + } + else + switch (jointype) + { + case jtMiter: + { + double r = 1 + (m_normals[j].X * m_normals[k].X + + m_normals[j].Y * m_normals[k].Y); + if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); + break; + } + case jtSquare: DoSquare(j, k); break; + case jtRound: DoRound(j, k); break; + } + k = j; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoSquare(int j, int k) +{ + double dx = std::tan(std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoMiter(int j, int k, double r) +{ + double q = m_delta / r; + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), + Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoRound(int j, int k) +{ + double a = std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); + int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); + + double X = m_normals[k].X, Y = m_normals[k].Y, X2; + for (int i = 0; i < steps; ++i) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + X * m_delta), + Round(m_srcPoly[j].Y + Y * m_delta))); + X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); +} + +//------------------------------------------------------------------------------ +// Miscellaneous public functions +//------------------------------------------------------------------------------ + +// Called by Clipper::ExecuteInternal() +// For each polygon, search for exactly duplicate non-successive points. +// If such a point is found, the loop is split into two pieces. +// Search for the duplicate points is O(n^2)! +// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm +void Clipper::DoSimplePolygons() +{ + PROFILE_FUNC(); + size_t i = 0; + while (i < m_PolyOuts.size()) + { + OutRec* outrec = m_PolyOuts[i++]; + OutPt* op = outrec->Pts; + if (!op || outrec->IsOpen) continue; + do //for each Pt in Polygon until duplicate found do ... + { + OutPt* op2 = op->Next; + while (op2 != outrec->Pts) + { + if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) + { + //split the polygon into two ... + OutPt* op3 = op->Prev; + OutPt* op4 = op2->Prev; + op->Prev = op4; + op4->Next = op; + op2->Prev = op3; + op3->Next = op2; + + outrec->Pts = op; + OutRec* outrec2 = CreateOutRec(); + outrec2->Pts = op2; + UpdateOutPtIdxs(*outrec2); + if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2->IsHole = !outrec->IsHole; + outrec2->FirstLeft = outrec; + // For each m_PolyOuts, replace FirstLeft from outRec2 to outrec. + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + } + else + if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2->IsHole = outrec->IsHole; + outrec->IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + outrec->FirstLeft = outrec2; + // For each m_PolyOuts, replace FirstLeft from outrec to outrec2. + if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + } + else + { + //the 2 polygons are separate ... + outrec2->IsHole = outrec->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + // For each polygon of m_PolyOuts, replace FirstLeft from outrec to outrec2 if the polygon is inside outRec2. + //FIXME This is potentially very expensive! O(n^3)! + if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + } + op2 = op; //ie get ready for the Next iteration + } + op2 = op2->Next; + } + op = op->Next; + } + while (op != outrec->Pts); + } +} +//------------------------------------------------------------------------------ + +void ReversePath(Path& p) +{ + std::reverse(p.begin(), p.end()); +} +//------------------------------------------------------------------------------ + +void ReversePaths(Paths& p) +{ + for (Paths::size_type i = 0; i < p.size(); ++i) + ReversePath(p[i]); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPath(in_poly, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPaths(in_polys, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(Paths &polys, PolyFillType fillType) +{ + SimplifyPolygons(polys, polys, fillType); +} +//------------------------------------------------------------------------------ + +inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) +{ + double Dx = ((double)pt1.X - pt2.X); + double dy = ((double)pt1.Y - pt2.Y); + return (Dx*Dx + dy*dy); +} +//------------------------------------------------------------------------------ + +double DistanceFromLineSqrd( + const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) +{ + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x¹,y¹) & (x²,y²) is ... + //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 + //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ + //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double A = double(ln1.Y - ln2.Y); + double B = double(ln2.X - ln1.X); + double C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); +} +//--------------------------------------------------------------------------- + +bool SlopesNearCollinear(const IntPoint& pt1, + const IntPoint& pt2, const IntPoint& pt3, double distSqrd) +{ + //this function is more accurate when the point that's geometrically + //between the other 2 points is the one that's tested for distance. + //ie makes it more likely to pick up 'spikes' ... + if (std::abs(pt1.X - pt2.X) > std::abs(pt1.Y - pt2.Y)) + { + if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + else + { + if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } +} +//------------------------------------------------------------------------------ + +bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) +{ + double Dx = (double)pt1.X - pt2.X; + double dy = (double)pt1.Y - pt2.Y; + return ((Dx * Dx) + (dy * dy) <= distSqrd); +} +//------------------------------------------------------------------------------ + +OutPt* ExcludeOp(OutPt* op) +{ + OutPt* result = op->Prev; + result->Next = op->Next; + op->Next->Prev = result; + result->Idx = 0; + return result; +} +//------------------------------------------------------------------------------ + +// Simplify a polygon using a linked list of points. +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) +{ + //distance = proximity in units/pixels below which vertices + //will be stripped. Default ~= sqrt(2). + + size_t size = in_poly.size(); + + if (size == 0) + { + out_poly.clear(); + return; + } + + std::vector outPts(size); + for (size_t i = 0; i < size; ++i) + { + outPts[i].Pt = in_poly[i]; + outPts[i].Next = &outPts[(i + 1) % size]; + outPts[i].Next->Prev = &outPts[i]; + outPts[i].Idx = 0; + } + + double distSqrd = distance * distance; + OutPt* op = &outPts[0]; + while (op->Idx == 0 && op->Next != op->Prev) + { + if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) + { + ExcludeOp(op->Next); + op = ExcludeOp(op); + size -= 2; + } + else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else + { + op->Idx = 1; + op = op->Next; + } + } + + if (size < 3) size = 0; + out_poly.resize(size); + for (size_t i = 0; i < size; ++i) + { + out_poly[i] = op->Pt; + op = op->Next; + } +} +//------------------------------------------------------------------------------ + +void CleanPolygon(Path& poly, double distance) +{ + CleanPolygon(poly, poly, distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) +{ + for (Paths::size_type i = 0; i < in_polys.size(); ++i) + CleanPolygon(in_polys[i], out_polys[i], distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(Paths& polys, double distance) +{ + CleanPolygons(polys, polys, distance); +} +//------------------------------------------------------------------------------ + +void Minkowski(const Path& poly, const Path& path, + Paths& solution, bool isSum, bool isClosed) +{ + int delta = (isClosed ? 1 : 0); + size_t polyCnt = poly.size(); + size_t pathCnt = path.size(); + Paths pp; + pp.reserve(pathCnt); + if (isSum) + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); + pp.push_back(p); + } + else + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); + pp.push_back(p); + } + + solution.clear(); + solution.reserve((pathCnt + delta) * (polyCnt + 1)); + for (size_t i = 0; i < pathCnt - 1 + delta; ++i) + for (size_t j = 0; j < polyCnt; ++j) + { + Path quad; + quad.reserve(4); + quad.push_back(pp[i % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + if (!Orientation(quad)) ReversePath(quad); + solution.push_back(quad); + } +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) +{ + Minkowski(pattern, path, solution, true, pathIsClosed); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void TranslatePath(const Path& input, Path& output, const IntPoint& delta) +{ + //precondition: input != output + output.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) +{ + Clipper c; + for (size_t i = 0; i < paths.size(); ++i) + { + Paths tmp; + Minkowski(pattern, paths[i], tmp, true, pathIsClosed); + c.AddPaths(tmp, ptSubject, true); + if (pathIsClosed) + { + Path tmp2; + TranslatePath(paths[i], tmp2, pattern[0]); + c.AddPath(tmp2, ptClip, true); + } + } + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) +{ + Minkowski(poly1, poly2, solution, false, true); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +enum NodeType {ntAny, ntOpen, ntClosed}; + +void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) +{ + bool match = true; + if (nodetype == ntClosed) match = !polynode.IsOpen(); + else if (nodetype == ntOpen) return; + + if (!polynode.Contour.empty() && match) + paths.push_back(polynode.Contour); + for (int i = 0; i < polynode.ChildCount(); ++i) + AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); +} +//------------------------------------------------------------------------------ + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntAny, paths); +} +//------------------------------------------------------------------------------ + +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntClosed, paths); +} +//------------------------------------------------------------------------------ + +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + //Open paths are top level only, so ... + for (int i = 0; i < polytree.ChildCount(); ++i) + if (polytree.Childs[i]->IsOpen()) + paths.push_back(polytree.Childs[i]->Contour); +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const IntPoint &p) +{ + s << "(" << p.X << "," << p.Y << ")"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Path &p) +{ + if (p.empty()) return s; + Path::size_type last = p.size() -1; + for (Path::size_type i = 0; i < last; i++) + s << "(" << p[i].X << "," << p[i].Y << "), "; + s << "(" << p[last].X << "," << p[last].Y << ")\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Paths &p) +{ + for (Paths::size_type i = 0; i < p.size(); i++) + s << p[i]; + s << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +} //ClipperLib namespace diff --git a/xs/src/clipper.hpp b/src/clipper/clipper.hpp similarity index 97% rename from xs/src/clipper.hpp rename to src/clipper/clipper.hpp index 2e70696197..8a28fe46f7 100644 --- a/xs/src/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -1,487 +1,487 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.2.9 * -* Date : 16 February 2015 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -#ifndef clipper_hpp -#define clipper_hpp - -#include - -#define CLIPPER_VERSION "6.2.6" - -//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. -//#define use_xyz - -//use_lines: Enables line clipping. Adds a very minor cost to performance. -#define use_lines - -//use_deprecated: Enables temporary support for the obsolete functions -//#define use_deprecated - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ClipperLib { - -enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; -enum PolyType { ptSubject, ptClip }; -//By far the most widely used winding rules for polygon filling are -//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) -//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) -//see http://glprogramming.com/red/chapter11.html -enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; - -// Point coordinate type -typedef int64_t cInt; -// Maximum cInt value to allow a cross product calculation using 32bit expressions. -static cInt const loRange = 0x3FFFFFFF; -// Maximum allowed cInt value. -static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; - -struct IntPoint { - cInt X; - cInt Y; -#ifdef use_xyz - cInt Z; - IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; -#else - IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; -#endif - - friend inline bool operator== (const IntPoint& a, const IntPoint& b) - { - return a.X == b.X && a.Y == b.Y; - } - friend inline bool operator!= (const IntPoint& a, const IntPoint& b) - { - return a.X != b.X || a.Y != b.Y; - } -}; -//------------------------------------------------------------------------------ - -typedef std::vector< IntPoint > Path; -typedef std::vector< Path > Paths; - -inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} -inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} - -std::ostream& operator <<(std::ostream &s, const IntPoint &p); -std::ostream& operator <<(std::ostream &s, const Path &p); -std::ostream& operator <<(std::ostream &s, const Paths &p); - -struct DoublePoint -{ - double X; - double Y; - DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} - DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} -}; -//------------------------------------------------------------------------------ - -#ifdef use_xyz -typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); -#endif - -enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; -enum JoinType {jtSquare, jtRound, jtMiter}; -enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; - -class PolyNode; -typedef std::vector< PolyNode* > PolyNodes; - -class PolyNode -{ -public: - PolyNode() : Childs(), Parent(0), Index(0), m_IsOpen(false) {} - virtual ~PolyNode(){}; - Path Contour; - PolyNodes Childs; - PolyNode* Parent; - // Traversal of the polygon tree in a depth first fashion. - PolyNode* GetNext() const { return Childs.empty() ? GetNextSiblingUp() : Childs.front(); } - bool IsHole() const; - bool IsOpen() const { return m_IsOpen; } - int ChildCount() const { return (int)Childs.size(); } -private: - unsigned Index; //node index in Parent.Childs - bool m_IsOpen; - JoinType m_jointype; - EndType m_endtype; - PolyNode* GetNextSiblingUp() const { return Parent ? ((Index == Parent->Childs.size() - 1) ? Parent->GetNextSiblingUp() : Parent->Childs[Index + 1]) : nullptr; } - void AddChild(PolyNode& child); - friend class Clipper; //to access Index - friend class ClipperOffset; - friend class PolyTree; //to implement the PolyTree::move operator -}; - -class PolyTree: public PolyNode -{ -public: - PolyTree() {} - PolyTree(PolyTree &&src) { *this = std::move(src); } - virtual ~PolyTree(){Clear();}; - PolyTree& operator=(PolyTree &&src) { - AllNodes = std::move(src.AllNodes); - Contour = std::move(src.Contour); - Childs = std::move(src.Childs); - Parent = nullptr; - Index = src.Index; - m_IsOpen = src.m_IsOpen; - m_jointype = src.m_jointype; - m_endtype = src.m_endtype; - for (size_t i = 0; i < Childs.size(); ++ i) - Childs[i]->Parent = this; - return *this; - } - PolyNode* GetFirst() const { return Childs.empty() ? nullptr : Childs.front(); } - void Clear() { AllNodes.clear(); Childs.clear(); } - int Total() const; -private: - PolyTree(const PolyTree &src) = delete; - PolyTree& operator=(const PolyTree &src) = delete; - std::vector AllNodes; - friend class Clipper; //to access AllNodes -}; - -double Area(const Path &poly); -inline bool Orientation(const Path &poly) { return Area(poly) >= 0; } -int PointInPolygon(const IntPoint &pt, const Path &path); - -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); - -void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); -void CleanPolygon(Path& poly, double distance = 1.415); -void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); -void CleanPolygons(Paths& polys, double distance = 1.415); - -void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); -void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); -void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); - -void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); -void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); -void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); - -void ReversePath(Path& p); -void ReversePaths(Paths& p); - -struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; - -//enums that are used internally ... -enum EdgeSide { esLeft = 1, esRight = 2}; - -// namespace Internal { - //forward declarations (for stuff used internally) ... - struct TEdge { - // Bottom point of this edge (with minimum Y). - IntPoint Bot; - // Current position. - IntPoint Curr; - // Top point of this edge (with maximum Y). - IntPoint Top; - // Vector from Bot to Top. - IntPoint Delta; - // Slope (dx/dy). For horiontal edges, the slope is set to HORIZONTAL (-1.0E+40). - double Dx; - PolyType PolyTyp; - EdgeSide Side; - // Winding number delta. 1 or -1 depending on winding direction, 0 for open paths and flat closed paths. - int WindDelta; - int WindCnt; - int WindCnt2; //winding count of the opposite polytype - int OutIdx; - // Next edge in the input path. - TEdge *Next; - // Previous edge in the input path. - TEdge *Prev; - // Next edge in the Local Minima List chain. - TEdge *NextInLML; - TEdge *NextInAEL; - TEdge *PrevInAEL; - TEdge *NextInSEL; - TEdge *PrevInSEL; - }; - - struct IntersectNode { - IntersectNode(TEdge *Edge1, TEdge *Edge2, IntPoint Pt) : - Edge1(Edge1), Edge2(Edge2), Pt(Pt) {} - TEdge *Edge1; - TEdge *Edge2; - IntPoint Pt; - }; - - struct LocalMinimum { - cInt Y; - TEdge *LeftBound; - TEdge *RightBound; - }; - - // Point of an output polygon. - // 36B on 64bit system without use_xyz. - struct OutPt { - // 4B - int Idx; - // 16B without use_xyz / 24B with use_xyz - IntPoint Pt; - // 4B on 32bit system, 8B on 64bit system - OutPt *Next; - // 4B on 32bit system, 8B on 64bit system - OutPt *Prev; - }; - - struct OutRec; - struct Join { - Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : - OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {} - OutPt *OutPt1; - OutPt *OutPt2; - IntPoint OffPt; - }; -// }; // namespace Internal - -//------------------------------------------------------------------------------ - -//ClipperBase is the ancestor to the Clipper class. It should not be -//instantiated directly. This class simply abstracts the conversion of sets of -//polygon coordinates into edge objects that are stored in a LocalMinima list. -class ClipperBase -{ -public: - ClipperBase() : m_UseFullRange(false), m_HasOpenPaths(false) {} - ~ClipperBase() { Clear(); } - bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); - bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); - void Clear(); - IntRect GetBounds(); - // By default, when three or more vertices are collinear in input polygons (subject or clip), the Clipper object removes the 'inner' vertices before clipping. - // When enabled the PreserveCollinear property prevents this default behavior to allow these inner vertices to appear in the solution. - bool PreserveCollinear() const {return m_PreserveCollinear;}; - void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; -protected: - bool AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, bool Closed, TEdge* edges); - TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); - void Reset(); - TEdge* ProcessBound(TEdge* E, bool IsClockwise); - TEdge* DescendToMin(TEdge *&E); - void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); - - // Local minima (Y, left edge, right edge) sorted by ascending Y. - std::vector m_MinimaList; - - // True if the input polygons have abs values higher than loRange, but lower than hiRange. - // False if the input polygons have abs values lower or equal to loRange. - bool m_UseFullRange; - // A vector of edges per each input path. - std::vector> m_edges; - // Don't remove intermediate vertices of a collinear sequence of points. - bool m_PreserveCollinear; - // Is any of the paths inserted by AddPath() or AddPaths() open? - bool m_HasOpenPaths; -}; -//------------------------------------------------------------------------------ - -class Clipper : public ClipperBase -{ -public: - Clipper(int initOptions = 0); - ~Clipper() { Clear(); } - void Clear() { ClipperBase::Clear(); DisposeAllOutRecs(); } - bool Execute(ClipType clipType, - Paths &solution, - PolyFillType fillType = pftEvenOdd) - { return Execute(clipType, solution, fillType, fillType); } - bool Execute(ClipType clipType, - Paths &solution, - PolyFillType subjFillType, - PolyFillType clipFillType); - bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType fillType = pftEvenOdd) - { return Execute(clipType, polytree, fillType, fillType); } - bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType subjFillType, - PolyFillType clipFillType); - bool ReverseSolution() const { return m_ReverseOutput; }; - void ReverseSolution(bool value) {m_ReverseOutput = value;}; - bool StrictlySimple() const {return m_StrictSimple;}; - void StrictlySimple(bool value) {m_StrictSimple = value;}; - //set the callback function for z value filling on intersections (otherwise Z is 0) -#ifdef use_xyz - void ZFillFunction(ZFillCallback zFillFunc) { m_ZFill = zFillFunc; } -#endif -protected: - void Reset(); - virtual bool ExecuteInternal(); -private: - - // Output polygons. - std::vector m_PolyOuts; - // Output points, allocated by a continuous sets of m_OutPtsChunkSize. - std::vector m_OutPts; - // List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk. - OutPt *m_OutPtsFree; - size_t m_OutPtsChunkSize; - size_t m_OutPtsChunkLast; - - std::vector m_Joins; - std::vector m_GhostJoins; - std::vector m_IntersectList; - ClipType m_ClipType; - // A priority queue (a binary heap) of Y coordinates. - std::priority_queue m_Scanbeam; - // Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal(). - std::vector m_Maxima; - TEdge *m_ActiveEdges; - TEdge *m_SortedEdges; - PolyFillType m_ClipFillType; - PolyFillType m_SubjFillType; - bool m_ReverseOutput; - // Does the result go to a PolyTree or Paths? - bool m_UsingPolyTree; - bool m_StrictSimple; -#ifdef use_xyz - ZFillCallback m_ZFill; //custom callback -#endif - void SetWindingCount(TEdge& edge) const; - bool IsEvenOddFillType(const TEdge& edge) const - { return (edge.PolyTyp == ptSubject) ? m_SubjFillType == pftEvenOdd : m_ClipFillType == pftEvenOdd; } - bool IsEvenOddAltFillType(const TEdge& edge) const - { return (edge.PolyTyp == ptSubject) ? m_ClipFillType == pftEvenOdd : m_SubjFillType == pftEvenOdd; } - void InsertLocalMinimaIntoAEL(const cInt botY); - void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); - void AddEdgeToSEL(TEdge *edge); - void CopyAELToSEL(); - void DeleteFromSEL(TEdge *e); - void DeleteFromAEL(TEdge *e); - void UpdateEdgeIntoAEL(TEdge *&e); - void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); - bool IsContributing(const TEdge& edge) const; - bool IsTopHorz(const cInt XPos); - void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); - void DoMaxima(TEdge *e); - void ProcessHorizontals(); - void ProcessHorizontal(TEdge *horzEdge); - void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - OutRec* GetOutRec(int idx); - void AppendPolygon(TEdge *e1, TEdge *e2) const; - void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); - OutRec* CreateOutRec(); - OutPt* AddOutPt(TEdge *e, const IntPoint &pt); - OutPt* GetLastOutPt(TEdge *e); - OutPt* AllocateOutPt(); - OutPt* DupOutPt(OutPt* outPt, bool InsertAfter); - // Add the point to a list of free points. - void DisposeOutPt(OutPt *pt) { pt->Next = m_OutPtsFree; m_OutPtsFree = pt; } - void DisposeOutPts(OutPt*& pp) { if (pp != nullptr) { pp->Prev->Next = m_OutPtsFree; m_OutPtsFree = pp; } } - void DisposeAllOutRecs(); - bool ProcessIntersections(const cInt topY); - void BuildIntersectList(const cInt topY); - void ProcessEdgesAtTopOfScanbeam(const cInt topY); - void BuildResult(Paths& polys); - void BuildResult2(PolyTree& polytree); - void SetHoleState(TEdge *e, OutRec *outrec) const; - bool FixupIntersectionOrder(); - void FixupOutPolygon(OutRec &outrec); - void FixupOutPolyline(OutRec &outrec); - bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); - void FixHoleLinkage(OutRec &outrec); - bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); - bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft); - void JoinCommonEdges(); - void DoSimplePolygons(); - void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; - void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; -#ifdef use_xyz - void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); -#endif -}; -//------------------------------------------------------------------------------ - -class ClipperOffset -{ -public: - ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25, double shortestEdgeLength = 0.) : - MiterLimit(miterLimit), ArcTolerance(roundPrecision), ShortestEdgeLength(shortestEdgeLength), m_lowest(-1, 0) {} - ~ClipperOffset() { Clear(); } - void AddPath(const Path& path, JoinType joinType, EndType endType); - void AddPaths(const Paths& paths, JoinType joinType, EndType endType); - void Execute(Paths& solution, double delta); - void Execute(PolyTree& solution, double delta); - void Clear(); - double MiterLimit; - double ArcTolerance; - double ShortestEdgeLength; -private: - Paths m_destPolys; - Path m_srcPoly; - Path m_destPoly; - std::vector m_normals; - double m_delta, m_sinA, m_sin, m_cos; - double m_miterLim, m_StepsPerRad; - IntPoint m_lowest; - PolyNode m_polyNodes; - - void FixOrientations(); - void DoOffset(double delta); - void OffsetPoint(int j, int& k, JoinType jointype); - void DoSquare(int j, int k); - void DoMiter(int j, int k, double r); - void DoRound(int j, int k); -}; -//------------------------------------------------------------------------------ - -class clipperException : public std::exception -{ - public: - clipperException(const char* description): m_descr(description) {} - virtual ~clipperException() throw() {} - virtual const char* what() const throw() {return m_descr.c_str();} - private: - std::string m_descr; -}; -//------------------------------------------------------------------------------ - -} //ClipperLib namespace - -#endif //clipper_hpp - - +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.2.9 * +* Date : 16 February 2015 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2015 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +#ifndef clipper_hpp +#define clipper_hpp + +#include + +#define CLIPPER_VERSION "6.2.6" + +//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. +//#define use_xyz + +//use_lines: Enables line clipping. Adds a very minor cost to performance. +#define use_lines + +//use_deprecated: Enables temporary support for the obsolete functions +//#define use_deprecated + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; +enum PolyType { ptSubject, ptClip }; +//By far the most widely used winding rules for polygon filling are +//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) +//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) +//see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + +// Point coordinate type +typedef int64_t cInt; +// Maximum cInt value to allow a cross product calculation using 32bit expressions. +static cInt const loRange = 0x3FFFFFFF; +// Maximum allowed cInt value. +static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + +struct IntPoint { + cInt X; + cInt Y; +#ifdef use_xyz + cInt Z; + IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; +#else + IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; +#endif + + friend inline bool operator== (const IntPoint& a, const IntPoint& b) + { + return a.X == b.X && a.Y == b.Y; + } + friend inline bool operator!= (const IntPoint& a, const IntPoint& b) + { + return a.X != b.X || a.Y != b.Y; + } +}; +//------------------------------------------------------------------------------ + +typedef std::vector< IntPoint > Path; +typedef std::vector< Path > Paths; + +inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} +inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} + +std::ostream& operator <<(std::ostream &s, const IntPoint &p); +std::ostream& operator <<(std::ostream &s, const Path &p); +std::ostream& operator <<(std::ostream &s, const Paths &p); + +struct DoublePoint +{ + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} + DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} +}; +//------------------------------------------------------------------------------ + +#ifdef use_xyz +typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); +#endif + +enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; +enum JoinType {jtSquare, jtRound, jtMiter}; +enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; + +class PolyNode; +typedef std::vector< PolyNode* > PolyNodes; + +class PolyNode +{ +public: + PolyNode() : Childs(), Parent(0), Index(0), m_IsOpen(false) {} + virtual ~PolyNode(){}; + Path Contour; + PolyNodes Childs; + PolyNode* Parent; + // Traversal of the polygon tree in a depth first fashion. + PolyNode* GetNext() const { return Childs.empty() ? GetNextSiblingUp() : Childs.front(); } + bool IsHole() const; + bool IsOpen() const { return m_IsOpen; } + int ChildCount() const { return (int)Childs.size(); } +private: + unsigned Index; //node index in Parent.Childs + bool m_IsOpen; + JoinType m_jointype; + EndType m_endtype; + PolyNode* GetNextSiblingUp() const { return Parent ? ((Index == Parent->Childs.size() - 1) ? Parent->GetNextSiblingUp() : Parent->Childs[Index + 1]) : nullptr; } + void AddChild(PolyNode& child); + friend class Clipper; //to access Index + friend class ClipperOffset; + friend class PolyTree; //to implement the PolyTree::move operator +}; + +class PolyTree: public PolyNode +{ +public: + PolyTree() {} + PolyTree(PolyTree &&src) { *this = std::move(src); } + virtual ~PolyTree(){Clear();}; + PolyTree& operator=(PolyTree &&src) { + AllNodes = std::move(src.AllNodes); + Contour = std::move(src.Contour); + Childs = std::move(src.Childs); + Parent = nullptr; + Index = src.Index; + m_IsOpen = src.m_IsOpen; + m_jointype = src.m_jointype; + m_endtype = src.m_endtype; + for (size_t i = 0; i < Childs.size(); ++ i) + Childs[i]->Parent = this; + return *this; + } + PolyNode* GetFirst() const { return Childs.empty() ? nullptr : Childs.front(); } + void Clear() { AllNodes.clear(); Childs.clear(); } + int Total() const; +private: + PolyTree(const PolyTree &src) = delete; + PolyTree& operator=(const PolyTree &src) = delete; + std::vector AllNodes; + friend class Clipper; //to access AllNodes +}; + +double Area(const Path &poly); +inline bool Orientation(const Path &poly) { return Area(poly) >= 0; } +int PointInPolygon(const IntPoint &pt, const Path &path); + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); +void CleanPolygon(Path& poly, double distance = 1.415); +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); +void CleanPolygons(Paths& polys, double distance = 1.415); + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); + +void ReversePath(Path& p); +void ReversePaths(Paths& p); + +struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; + +//enums that are used internally ... +enum EdgeSide { esLeft = 1, esRight = 2}; + +// namespace Internal { + //forward declarations (for stuff used internally) ... + struct TEdge { + // Bottom point of this edge (with minimum Y). + IntPoint Bot; + // Current position. + IntPoint Curr; + // Top point of this edge (with maximum Y). + IntPoint Top; + // Vector from Bot to Top. + IntPoint Delta; + // Slope (dx/dy). For horiontal edges, the slope is set to HORIZONTAL (-1.0E+40). + double Dx; + PolyType PolyTyp; + EdgeSide Side; + // Winding number delta. 1 or -1 depending on winding direction, 0 for open paths and flat closed paths. + int WindDelta; + int WindCnt; + int WindCnt2; //winding count of the opposite polytype + int OutIdx; + // Next edge in the input path. + TEdge *Next; + // Previous edge in the input path. + TEdge *Prev; + // Next edge in the Local Minima List chain. + TEdge *NextInLML; + TEdge *NextInAEL; + TEdge *PrevInAEL; + TEdge *NextInSEL; + TEdge *PrevInSEL; + }; + + struct IntersectNode { + IntersectNode(TEdge *Edge1, TEdge *Edge2, IntPoint Pt) : + Edge1(Edge1), Edge2(Edge2), Pt(Pt) {} + TEdge *Edge1; + TEdge *Edge2; + IntPoint Pt; + }; + + struct LocalMinimum { + cInt Y; + TEdge *LeftBound; + TEdge *RightBound; + }; + + // Point of an output polygon. + // 36B on 64bit system without use_xyz. + struct OutPt { + // 4B + int Idx; + // 16B without use_xyz / 24B with use_xyz + IntPoint Pt; + // 4B on 32bit system, 8B on 64bit system + OutPt *Next; + // 4B on 32bit system, 8B on 64bit system + OutPt *Prev; + }; + + struct OutRec; + struct Join { + Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : + OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {} + OutPt *OutPt1; + OutPt *OutPt2; + IntPoint OffPt; + }; +// }; // namespace Internal + +//------------------------------------------------------------------------------ + +//ClipperBase is the ancestor to the Clipper class. It should not be +//instantiated directly. This class simply abstracts the conversion of sets of +//polygon coordinates into edge objects that are stored in a LocalMinima list. +class ClipperBase +{ +public: + ClipperBase() : m_UseFullRange(false), m_HasOpenPaths(false) {} + ~ClipperBase() { Clear(); } + bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); + void Clear(); + IntRect GetBounds(); + // By default, when three or more vertices are collinear in input polygons (subject or clip), the Clipper object removes the 'inner' vertices before clipping. + // When enabled the PreserveCollinear property prevents this default behavior to allow these inner vertices to appear in the solution. + bool PreserveCollinear() const {return m_PreserveCollinear;}; + void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; +protected: + bool AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, bool Closed, TEdge* edges); + TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); + void Reset(); + TEdge* ProcessBound(TEdge* E, bool IsClockwise); + TEdge* DescendToMin(TEdge *&E); + void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); + + // Local minima (Y, left edge, right edge) sorted by ascending Y. + std::vector m_MinimaList; + + // True if the input polygons have abs values higher than loRange, but lower than hiRange. + // False if the input polygons have abs values lower or equal to loRange. + bool m_UseFullRange; + // A vector of edges per each input path. + std::vector> m_edges; + // Don't remove intermediate vertices of a collinear sequence of points. + bool m_PreserveCollinear; + // Is any of the paths inserted by AddPath() or AddPaths() open? + bool m_HasOpenPaths; +}; +//------------------------------------------------------------------------------ + +class Clipper : public ClipperBase +{ +public: + Clipper(int initOptions = 0); + ~Clipper() { Clear(); } + void Clear() { ClipperBase::Clear(); DisposeAllOutRecs(); } + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType fillType = pftEvenOdd) + { return Execute(clipType, solution, fillType, fillType); } + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType fillType = pftEvenOdd) + { return Execute(clipType, polytree, fillType, fillType); } + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool ReverseSolution() const { return m_ReverseOutput; }; + void ReverseSolution(bool value) {m_ReverseOutput = value;}; + bool StrictlySimple() const {return m_StrictSimple;}; + void StrictlySimple(bool value) {m_StrictSimple = value;}; + //set the callback function for z value filling on intersections (otherwise Z is 0) +#ifdef use_xyz + void ZFillFunction(ZFillCallback zFillFunc) { m_ZFill = zFillFunc; } +#endif +protected: + void Reset(); + virtual bool ExecuteInternal(); +private: + + // Output polygons. + std::vector m_PolyOuts; + // Output points, allocated by a continuous sets of m_OutPtsChunkSize. + std::vector m_OutPts; + // List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk. + OutPt *m_OutPtsFree; + size_t m_OutPtsChunkSize; + size_t m_OutPtsChunkLast; + + std::vector m_Joins; + std::vector m_GhostJoins; + std::vector m_IntersectList; + ClipType m_ClipType; + // A priority queue (a binary heap) of Y coordinates. + std::priority_queue m_Scanbeam; + // Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal(). + std::vector m_Maxima; + TEdge *m_ActiveEdges; + TEdge *m_SortedEdges; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; + // Does the result go to a PolyTree or Paths? + bool m_UsingPolyTree; + bool m_StrictSimple; +#ifdef use_xyz + ZFillCallback m_ZFill; //custom callback +#endif + void SetWindingCount(TEdge& edge) const; + bool IsEvenOddFillType(const TEdge& edge) const + { return (edge.PolyTyp == ptSubject) ? m_SubjFillType == pftEvenOdd : m_ClipFillType == pftEvenOdd; } + bool IsEvenOddAltFillType(const TEdge& edge) const + { return (edge.PolyTyp == ptSubject) ? m_ClipFillType == pftEvenOdd : m_SubjFillType == pftEvenOdd; } + void InsertLocalMinimaIntoAEL(const cInt botY); + void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); + void AddEdgeToSEL(TEdge *edge); + void CopyAELToSEL(); + void DeleteFromSEL(TEdge *e); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); + void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); + bool IsContributing(const TEdge& edge) const; + bool IsTopHorz(const cInt XPos); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DoMaxima(TEdge *e); + void ProcessHorizontals(); + void ProcessHorizontal(TEdge *horzEdge); + void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec* GetOutRec(int idx); + void AppendPolygon(TEdge *e1, TEdge *e2) const; + void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); + OutRec* CreateOutRec(); + OutPt* AddOutPt(TEdge *e, const IntPoint &pt); + OutPt* GetLastOutPt(TEdge *e); + OutPt* AllocateOutPt(); + OutPt* DupOutPt(OutPt* outPt, bool InsertAfter); + // Add the point to a list of free points. + void DisposeOutPt(OutPt *pt) { pt->Next = m_OutPtsFree; m_OutPtsFree = pt; } + void DisposeOutPts(OutPt*& pp) { if (pp != nullptr) { pp->Prev->Next = m_OutPtsFree; m_OutPtsFree = pp; } } + void DisposeAllOutRecs(); + bool ProcessIntersections(const cInt topY); + void BuildIntersectList(const cInt topY); + void ProcessEdgesAtTopOfScanbeam(const cInt topY); + void BuildResult(Paths& polys); + void BuildResult2(PolyTree& polytree); + void SetHoleState(TEdge *e, OutRec *outrec) const; + bool FixupIntersectionOrder(); + void FixupOutPolygon(OutRec &outrec); + void FixupOutPolyline(OutRec &outrec); + bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); + void FixHoleLinkage(OutRec &outrec); + bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); + bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft); + void JoinCommonEdges(); + void DoSimplePolygons(); + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; + void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; +#ifdef use_xyz + void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); +#endif +}; +//------------------------------------------------------------------------------ + +class ClipperOffset +{ +public: + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25, double shortestEdgeLength = 0.) : + MiterLimit(miterLimit), ArcTolerance(roundPrecision), ShortestEdgeLength(shortestEdgeLength), m_lowest(-1, 0) {} + ~ClipperOffset() { Clear(); } + void AddPath(const Path& path, JoinType joinType, EndType endType); + void AddPaths(const Paths& paths, JoinType joinType, EndType endType); + void Execute(Paths& solution, double delta); + void Execute(PolyTree& solution, double delta); + void Clear(); + double MiterLimit; + double ArcTolerance; + double ShortestEdgeLength; +private: + Paths m_destPolys; + Path m_srcPoly; + Path m_destPoly; + std::vector m_normals; + double m_delta, m_sinA, m_sin, m_cos; + double m_miterLim, m_StepsPerRad; + IntPoint m_lowest; + PolyNode m_polyNodes; + + void FixOrientations(); + void DoOffset(double delta); + void OffsetPoint(int j, int& k, JoinType jointype); + void DoSquare(int j, int k); + void DoMiter(int j, int k, double r); + void DoRound(int j, int k); +}; +//------------------------------------------------------------------------------ + +class clipperException : public std::exception +{ + public: + clipperException(const char* description): m_descr(description) {} + virtual ~clipperException() throw() {} + virtual const char* what() const throw() {return m_descr.c_str();} + private: + std::string m_descr; +}; +//------------------------------------------------------------------------------ + +} //ClipperLib namespace + +#endif //clipper_hpp + + diff --git a/xs/src/eigen/COPYING.README b/src/eigen/COPYING.README similarity index 100% rename from xs/src/eigen/COPYING.README rename to src/eigen/COPYING.README diff --git a/xs/src/eigen/Eigen/CMakeLists.txt b/src/eigen/Eigen/CMakeLists.txt similarity index 100% rename from xs/src/eigen/Eigen/CMakeLists.txt rename to src/eigen/Eigen/CMakeLists.txt diff --git a/xs/src/eigen/Eigen/Cholesky b/src/eigen/Eigen/Cholesky similarity index 93% rename from xs/src/eigen/Eigen/Cholesky rename to src/eigen/Eigen/Cholesky index 369d1f5ec9..1332b540d8 100644 --- a/xs/src/eigen/Eigen/Cholesky +++ b/src/eigen/Eigen/Cholesky @@ -9,6 +9,7 @@ #define EIGEN_CHOLESKY_MODULE_H #include "Core" +#include "Jacobi" #include "src/Core/util/DisableStupidWarnings.h" @@ -31,7 +32,11 @@ #include "src/Cholesky/LLT.h" #include "src/Cholesky/LDLT.h" #ifdef EIGEN_USE_LAPACKE +#ifdef EIGEN_USE_MKL +#include "mkl_lapacke.h" +#else #include "src/misc/lapacke.h" +#endif #include "src/Cholesky/LLT_LAPACKE.h" #endif diff --git a/xs/src/eigen/Eigen/CholmodSupport b/src/eigen/Eigen/CholmodSupport similarity index 100% rename from xs/src/eigen/Eigen/CholmodSupport rename to src/eigen/Eigen/CholmodSupport diff --git a/xs/src/eigen/Eigen/Core b/src/eigen/Eigen/Core similarity index 96% rename from xs/src/eigen/Eigen/Core rename to src/eigen/Eigen/Core index 0f7fa630dd..4d4901e030 100644 --- a/xs/src/eigen/Eigen/Core +++ b/src/eigen/Eigen/Core @@ -14,6 +14,22 @@ // first thing Eigen does: stop the compiler from committing suicide #include "src/Core/util/DisableStupidWarnings.h" +#if defined(__CUDACC__) && !defined(EIGEN_NO_CUDA) + #define EIGEN_CUDACC __CUDACC__ +#endif + +#if defined(__CUDA_ARCH__) && !defined(EIGEN_NO_CUDA) + #define EIGEN_CUDA_ARCH __CUDA_ARCH__ +#endif + +#if defined(__CUDACC_VER_MAJOR__) && (__CUDACC_VER_MAJOR__ >= 9) +#define EIGEN_CUDACC_VER ((__CUDACC_VER_MAJOR__ * 10000) + (__CUDACC_VER_MINOR__ * 100)) +#elif defined(__CUDACC_VER__) +#define EIGEN_CUDACC_VER __CUDACC_VER__ +#else +#define EIGEN_CUDACC_VER 0 +#endif + // Handle NVCC/CUDA/SYCL #if defined(__CUDACC__) || defined(__SYCL_DEVICE_ONLY__) // Do not try asserts on CUDA and SYCL! @@ -155,6 +171,9 @@ #ifdef __AVX512DQ__ #define EIGEN_VECTORIZE_AVX512DQ #endif + #ifdef __AVX512ER__ + #define EIGEN_VECTORIZE_AVX512ER + #endif #endif // include files @@ -229,7 +248,7 @@ #if defined __CUDACC__ #define EIGEN_VECTORIZE_CUDA #include - #if defined __CUDACC_VER__ && __CUDACC_VER__ >= 70500 + #if EIGEN_CUDACC_VER >= 70500 #define EIGEN_HAS_CUDA_FP16 #endif #endif @@ -352,6 +371,7 @@ using std::ptrdiff_t; #include "src/Core/MathFunctions.h" #include "src/Core/GenericPacketMath.h" #include "src/Core/MathFunctionsImpl.h" +#include "src/Core/arch/Default/ConjHelper.h" #if defined EIGEN_VECTORIZE_AVX512 #include "src/Core/arch/SSE/PacketMath.h" @@ -367,6 +387,7 @@ using std::ptrdiff_t; #include "src/Core/arch/AVX/MathFunctions.h" #include "src/Core/arch/AVX/Complex.h" #include "src/Core/arch/AVX/TypeCasting.h" + #include "src/Core/arch/SSE/TypeCasting.h" #elif defined EIGEN_VECTORIZE_SSE #include "src/Core/arch/SSE/PacketMath.h" #include "src/Core/arch/SSE/MathFunctions.h" diff --git a/xs/src/eigen/Eigen/Dense b/src/eigen/Eigen/Dense similarity index 100% rename from xs/src/eigen/Eigen/Dense rename to src/eigen/Eigen/Dense diff --git a/xs/src/eigen/Eigen/Eigen b/src/eigen/Eigen/Eigen similarity index 100% rename from xs/src/eigen/Eigen/Eigen rename to src/eigen/Eigen/Eigen diff --git a/xs/src/eigen/Eigen/Eigenvalues b/src/eigen/Eigen/Eigenvalues similarity index 96% rename from xs/src/eigen/Eigen/Eigenvalues rename to src/eigen/Eigen/Eigenvalues index 009e529e19..f3f661b074 100644 --- a/xs/src/eigen/Eigen/Eigenvalues +++ b/src/eigen/Eigen/Eigenvalues @@ -45,7 +45,11 @@ #include "src/Eigenvalues/GeneralizedEigenSolver.h" #include "src/Eigenvalues/MatrixBaseEigenvalues.h" #ifdef EIGEN_USE_LAPACKE +#ifdef EIGEN_USE_MKL +#include "mkl_lapacke.h" +#else #include "src/misc/lapacke.h" +#endif #include "src/Eigenvalues/RealSchur_LAPACKE.h" #include "src/Eigenvalues/ComplexSchur_LAPACKE.h" #include "src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h" diff --git a/xs/src/eigen/Eigen/Geometry b/src/eigen/Eigen/Geometry similarity index 100% rename from xs/src/eigen/Eigen/Geometry rename to src/eigen/Eigen/Geometry diff --git a/xs/src/eigen/Eigen/Householder b/src/eigen/Eigen/Householder similarity index 100% rename from xs/src/eigen/Eigen/Householder rename to src/eigen/Eigen/Householder diff --git a/xs/src/eigen/Eigen/IterativeLinearSolvers b/src/eigen/Eigen/IterativeLinearSolvers similarity index 100% rename from xs/src/eigen/Eigen/IterativeLinearSolvers rename to src/eigen/Eigen/IterativeLinearSolvers diff --git a/xs/src/eigen/Eigen/Jacobi b/src/eigen/Eigen/Jacobi similarity index 100% rename from xs/src/eigen/Eigen/Jacobi rename to src/eigen/Eigen/Jacobi diff --git a/xs/src/eigen/Eigen/LU b/src/eigen/Eigen/LU similarity index 95% rename from xs/src/eigen/Eigen/LU rename to src/eigen/Eigen/LU index 6f6c55629c..6418a86e19 100644 --- a/xs/src/eigen/Eigen/LU +++ b/src/eigen/Eigen/LU @@ -28,7 +28,11 @@ #include "src/LU/FullPivLU.h" #include "src/LU/PartialPivLU.h" #ifdef EIGEN_USE_LAPACKE +#ifdef EIGEN_USE_MKL +#include "mkl_lapacke.h" +#else #include "src/misc/lapacke.h" +#endif #include "src/LU/PartialPivLU_LAPACKE.h" #endif #include "src/LU/Determinant.h" diff --git a/xs/src/eigen/Eigen/MetisSupport b/src/eigen/Eigen/MetisSupport similarity index 100% rename from xs/src/eigen/Eigen/MetisSupport rename to src/eigen/Eigen/MetisSupport diff --git a/xs/src/eigen/Eigen/OrderingMethods b/src/eigen/Eigen/OrderingMethods similarity index 100% rename from xs/src/eigen/Eigen/OrderingMethods rename to src/eigen/Eigen/OrderingMethods diff --git a/xs/src/eigen/Eigen/PaStiXSupport b/src/eigen/Eigen/PaStiXSupport similarity index 100% rename from xs/src/eigen/Eigen/PaStiXSupport rename to src/eigen/Eigen/PaStiXSupport diff --git a/xs/src/eigen/Eigen/PardisoSupport b/src/eigen/Eigen/PardisoSupport similarity index 100% rename from xs/src/eigen/Eigen/PardisoSupport rename to src/eigen/Eigen/PardisoSupport diff --git a/xs/src/eigen/Eigen/QR b/src/eigen/Eigen/QR similarity index 95% rename from xs/src/eigen/Eigen/QR rename to src/eigen/Eigen/QR index 80838e3bdd..c7e9144699 100644 --- a/xs/src/eigen/Eigen/QR +++ b/src/eigen/Eigen/QR @@ -36,7 +36,11 @@ #include "src/QR/ColPivHouseholderQR.h" #include "src/QR/CompleteOrthogonalDecomposition.h" #ifdef EIGEN_USE_LAPACKE +#ifdef EIGEN_USE_MKL +#include "mkl_lapacke.h" +#else #include "src/misc/lapacke.h" +#endif #include "src/QR/HouseholderQR_LAPACKE.h" #include "src/QR/ColPivHouseholderQR_LAPACKE.h" #endif diff --git a/xs/src/eigen/Eigen/QtAlignedMalloc b/src/eigen/Eigen/QtAlignedMalloc similarity index 96% rename from xs/src/eigen/Eigen/QtAlignedMalloc rename to src/eigen/Eigen/QtAlignedMalloc index c6571f1291..4f07df02ae 100644 --- a/xs/src/eigen/Eigen/QtAlignedMalloc +++ b/src/eigen/Eigen/QtAlignedMalloc @@ -27,7 +27,7 @@ void qFree(void *ptr) void *qRealloc(void *ptr, std::size_t size) { void* newPtr = Eigen::internal::aligned_malloc(size); - memcpy(newPtr, ptr, size); + std::memcpy(newPtr, ptr, size); Eigen::internal::aligned_free(ptr); return newPtr; } diff --git a/xs/src/eigen/Eigen/SPQRSupport b/src/eigen/Eigen/SPQRSupport similarity index 100% rename from xs/src/eigen/Eigen/SPQRSupport rename to src/eigen/Eigen/SPQRSupport diff --git a/xs/src/eigen/Eigen/SVD b/src/eigen/Eigen/SVD similarity index 96% rename from xs/src/eigen/Eigen/SVD rename to src/eigen/Eigen/SVD index 86143c23d7..5d0e75f7f7 100644 --- a/xs/src/eigen/Eigen/SVD +++ b/src/eigen/Eigen/SVD @@ -37,7 +37,11 @@ #include "src/SVD/JacobiSVD.h" #include "src/SVD/BDCSVD.h" #if defined(EIGEN_USE_LAPACKE) && !defined(EIGEN_USE_LAPACKE_STRICT) +#ifdef EIGEN_USE_MKL +#include "mkl_lapacke.h" +#else #include "src/misc/lapacke.h" +#endif #include "src/SVD/JacobiSVD_LAPACKE.h" #endif diff --git a/xs/src/eigen/Eigen/Sparse b/src/eigen/Eigen/Sparse similarity index 100% rename from xs/src/eigen/Eigen/Sparse rename to src/eigen/Eigen/Sparse diff --git a/xs/src/eigen/Eigen/SparseCholesky b/src/eigen/Eigen/SparseCholesky similarity index 100% rename from xs/src/eigen/Eigen/SparseCholesky rename to src/eigen/Eigen/SparseCholesky diff --git a/xs/src/eigen/Eigen/SparseCore b/src/eigen/Eigen/SparseCore similarity index 100% rename from xs/src/eigen/Eigen/SparseCore rename to src/eigen/Eigen/SparseCore diff --git a/xs/src/eigen/Eigen/SparseLU b/src/eigen/Eigen/SparseLU similarity index 100% rename from xs/src/eigen/Eigen/SparseLU rename to src/eigen/Eigen/SparseLU diff --git a/xs/src/eigen/Eigen/SparseQR b/src/eigen/Eigen/SparseQR similarity index 100% rename from xs/src/eigen/Eigen/SparseQR rename to src/eigen/Eigen/SparseQR diff --git a/xs/src/eigen/Eigen/StdDeque b/src/eigen/Eigen/StdDeque similarity index 100% rename from xs/src/eigen/Eigen/StdDeque rename to src/eigen/Eigen/StdDeque diff --git a/xs/src/eigen/Eigen/StdList b/src/eigen/Eigen/StdList similarity index 100% rename from xs/src/eigen/Eigen/StdList rename to src/eigen/Eigen/StdList diff --git a/xs/src/eigen/Eigen/StdVector b/src/eigen/Eigen/StdVector similarity index 100% rename from xs/src/eigen/Eigen/StdVector rename to src/eigen/Eigen/StdVector diff --git a/xs/src/eigen/Eigen/SuperLUSupport b/src/eigen/Eigen/SuperLUSupport similarity index 100% rename from xs/src/eigen/Eigen/SuperLUSupport rename to src/eigen/Eigen/SuperLUSupport diff --git a/xs/src/eigen/Eigen/UmfPackSupport b/src/eigen/Eigen/UmfPackSupport similarity index 100% rename from xs/src/eigen/Eigen/UmfPackSupport rename to src/eigen/Eigen/UmfPackSupport diff --git a/xs/src/eigen/Eigen/src/Cholesky/LDLT.h b/src/eigen/Eigen/src/Cholesky/LDLT.h similarity index 98% rename from xs/src/eigen/Eigen/src/Cholesky/LDLT.h rename to src/eigen/Eigen/src/Cholesky/LDLT.h index fcee7b2e3f..0313a54bf3 100644 --- a/xs/src/eigen/Eigen/src/Cholesky/LDLT.h +++ b/src/eigen/Eigen/src/Cholesky/LDLT.h @@ -248,7 +248,7 @@ template class LDLT /** \brief Reports whether previous computation was successful. * * \returns \c Success if computation was succesful, - * \c NumericalIssue if the matrix.appears to be negative. + * \c NumericalIssue if the factorization failed because of a zero pivot. */ ComputationInfo info() const { @@ -376,6 +376,8 @@ template<> struct ldlt_inplace if((rs>0) && pivot_is_valid) A21 /= realAkk; + else if(rs>0) + ret = ret && (A21.array()==Scalar(0)).all(); if(found_zero_pivot && pivot_is_valid) ret = false; // factorization failed else if(!pivot_is_valid) found_zero_pivot = true; @@ -568,13 +570,14 @@ void LDLT<_MatrixType,_UpLo>::_solve_impl(const RhsType &rhs, DstType &dst) cons // more precisely, use pseudo-inverse of D (see bug 241) using std::abs; const typename Diagonal::RealReturnType vecD(vectorD()); - // In some previous versions, tolerance was set to the max of 1/highest and the maximal diagonal entry * epsilon - // as motivated by LAPACK's xGELSS: + // In some previous versions, tolerance was set to the max of 1/highest (or rather numeric_limits::min()) + // and the maximal diagonal entry * epsilon as motivated by LAPACK's xGELSS: // RealScalar tolerance = numext::maxi(vecD.array().abs().maxCoeff() * NumTraits::epsilon(),RealScalar(1) / NumTraits::highest()); // However, LDLT is not rank revealing, and so adjusting the tolerance wrt to the highest // diagonal element is not well justified and leads to numerical issues in some cases. // Moreover, Lapack's xSYTRS routines use 0 for the tolerance. - RealScalar tolerance = RealScalar(1) / NumTraits::highest(); + // Using numeric_limits::min() gives us more robustness to denormals. + RealScalar tolerance = (std::numeric_limits::min)(); for (Index i = 0; i < vecD.size(); ++i) { diff --git a/xs/src/eigen/Eigen/src/Cholesky/LLT.h b/src/eigen/Eigen/src/Cholesky/LLT.h similarity index 93% rename from xs/src/eigen/Eigen/src/Cholesky/LLT.h rename to src/eigen/Eigen/src/Cholesky/LLT.h index 87ca8d4236..e1624d21b6 100644 --- a/xs/src/eigen/Eigen/src/Cholesky/LLT.h +++ b/src/eigen/Eigen/src/Cholesky/LLT.h @@ -24,7 +24,7 @@ template struct LLT_Traits; * * \tparam _MatrixType the type of the matrix of which we are computing the LL^T Cholesky decomposition * \tparam _UpLo the triangular part that will be used for the decompositon: Lower (default) or Upper. - * The other triangular part won't be read. + * The other triangular part won't be read. * * This class performs a LL^T Cholesky decomposition of a symmetric, positive definite * matrix A such that A = LL^* = U^*U, where L is lower triangular. @@ -41,14 +41,18 @@ template struct LLT_Traits; * Example: \include LLT_example.cpp * Output: \verbinclude LLT_example.out * + * \b Performance: for best performance, it is recommended to use a column-major storage format + * with the Lower triangular part (the default), or, equivalently, a row-major storage format + * with the Upper triangular part. Otherwise, you might get a 20% slowdown for the full factorization + * step, and rank-updates can be up to 3 times slower. + * * This class supports the \link InplaceDecomposition inplace decomposition \endlink mechanism. * + * Note that during the decomposition, only the lower (or upper, as defined by _UpLo) triangular part of A is considered. + * Therefore, the strict lower part does not have to store correct values. + * * \sa MatrixBase::llt(), SelfAdjointView::llt(), class LDLT */ - /* HEY THIS DOX IS DISABLED BECAUSE THERE's A BUG EITHER HERE OR IN LDLT ABOUT THAT (OR BOTH) - * Note that during the decomposition, only the upper triangular part of A is considered. Therefore, - * the strict lower part does not have to store correct values. - */ template class LLT { public: @@ -146,7 +150,7 @@ template class LLT } template - void solveInPlace(MatrixBase &bAndX) const; + void solveInPlace(const MatrixBase &bAndX) const; template LLT& compute(const EigenBase& matrix); @@ -177,7 +181,7 @@ template class LLT /** \brief Reports whether previous computation was successful. * * \returns \c Success if computation was succesful, - * \c NumericalIssue if the matrix.appears to be negative. + * \c NumericalIssue if the matrix.appears not to be positive definite. */ ComputationInfo info() const { @@ -425,7 +429,8 @@ LLT& LLT::compute(const EigenBase eigen_assert(a.rows()==a.cols()); const Index size = a.rows(); m_matrix.resize(size, size); - m_matrix = a.derived(); + if (!internal::is_same_dense(m_matrix, a.derived())) + m_matrix = a.derived(); // Compute matrix L1 norm = max abs column sum. m_l1_norm = RealScalar(0); @@ -485,11 +490,14 @@ void LLT<_MatrixType,_UpLo>::_solve_impl(const RhsType &rhs, DstType &dst) const * * This version avoids a copy when the right hand side matrix b is not needed anymore. * + * \warning The parameter is only marked 'const' to make the C++ compiler accept a temporary expression here. + * This function will const_cast it, so constness isn't honored here. + * * \sa LLT::solve(), MatrixBase::llt() */ template template -void LLT::solveInPlace(MatrixBase &bAndX) const +void LLT::solveInPlace(const MatrixBase &bAndX) const { eigen_assert(m_isInitialized && "LLT is not initialized."); eigen_assert(m_matrix.rows()==bAndX.rows()); diff --git a/xs/src/eigen/Eigen/src/Cholesky/LLT_LAPACKE.h b/src/eigen/Eigen/src/Cholesky/LLT_LAPACKE.h similarity index 100% rename from xs/src/eigen/Eigen/src/Cholesky/LLT_LAPACKE.h rename to src/eigen/Eigen/src/Cholesky/LLT_LAPACKE.h diff --git a/xs/src/eigen/Eigen/src/CholmodSupport/CholmodSupport.h b/src/eigen/Eigen/src/CholmodSupport/CholmodSupport.h similarity index 100% rename from xs/src/eigen/Eigen/src/CholmodSupport/CholmodSupport.h rename to src/eigen/Eigen/src/CholmodSupport/CholmodSupport.h diff --git a/xs/src/eigen/Eigen/src/Core/Array.h b/src/eigen/Eigen/src/Core/Array.h similarity index 97% rename from xs/src/eigen/Eigen/src/Core/Array.h rename to src/eigen/Eigen/src/Core/Array.h index 0d34269fd4..e10020d4fd 100644 --- a/xs/src/eigen/Eigen/src/Core/Array.h +++ b/src/eigen/Eigen/src/Core/Array.h @@ -231,10 +231,16 @@ class Array : Base(other) { } + private: + struct PrivateType {}; + public: + /** \sa MatrixBase::operator=(const EigenBase&) */ template EIGEN_DEVICE_FUNC - EIGEN_STRONG_INLINE Array(const EigenBase &other) + EIGEN_STRONG_INLINE Array(const EigenBase &other, + typename internal::enable_if::value, + PrivateType>::type = PrivateType()) : Base(other.derived()) { } diff --git a/xs/src/eigen/Eigen/src/Core/ArrayBase.h b/src/eigen/Eigen/src/Core/ArrayBase.h similarity index 97% rename from xs/src/eigen/Eigen/src/Core/ArrayBase.h rename to src/eigen/Eigen/src/Core/ArrayBase.h index f0232f65ef..3dbc7084cd 100644 --- a/xs/src/eigen/Eigen/src/Core/ArrayBase.h +++ b/src/eigen/Eigen/src/Core/ArrayBase.h @@ -175,7 +175,7 @@ template class ArrayBase */ template template -EIGEN_STRONG_INLINE Derived & +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived & ArrayBase::operator-=(const ArrayBase &other) { call_assignment(derived(), other.derived(), internal::sub_assign_op()); @@ -188,7 +188,7 @@ ArrayBase::operator-=(const ArrayBase &other) */ template template -EIGEN_STRONG_INLINE Derived & +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived & ArrayBase::operator+=(const ArrayBase& other) { call_assignment(derived(), other.derived(), internal::add_assign_op()); @@ -201,7 +201,7 @@ ArrayBase::operator+=(const ArrayBase& other) */ template template -EIGEN_STRONG_INLINE Derived & +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived & ArrayBase::operator*=(const ArrayBase& other) { call_assignment(derived(), other.derived(), internal::mul_assign_op()); @@ -214,7 +214,7 @@ ArrayBase::operator*=(const ArrayBase& other) */ template template -EIGEN_STRONG_INLINE Derived & +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived & ArrayBase::operator/=(const ArrayBase& other) { call_assignment(derived(), other.derived(), internal::div_assign_op()); diff --git a/xs/src/eigen/Eigen/src/Core/ArrayWrapper.h b/src/eigen/Eigen/src/Core/ArrayWrapper.h similarity index 95% rename from xs/src/eigen/Eigen/src/Core/ArrayWrapper.h rename to src/eigen/Eigen/src/Core/ArrayWrapper.h index a04521a161..688aadd626 100644 --- a/xs/src/eigen/Eigen/src/Core/ArrayWrapper.h +++ b/src/eigen/Eigen/src/Core/ArrayWrapper.h @@ -32,7 +32,8 @@ struct traits > // Let's remove NestByRefBit enum { Flags0 = traits::type >::Flags, - Flags = Flags0 & ~NestByRefBit + LvalueBitFlag = is_lvalue::value ? LvalueBit : 0, + Flags = (Flags0 & ~(NestByRefBit | LvalueBit)) | LvalueBitFlag }; }; } @@ -129,7 +130,8 @@ struct traits > // Let's remove NestByRefBit enum { Flags0 = traits::type >::Flags, - Flags = Flags0 & ~NestByRefBit + LvalueBitFlag = is_lvalue::value ? LvalueBit : 0, + Flags = (Flags0 & ~(NestByRefBit | LvalueBit)) | LvalueBitFlag }; }; } diff --git a/xs/src/eigen/Eigen/src/Core/Assign.h b/src/eigen/Eigen/src/Core/Assign.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Assign.h rename to src/eigen/Eigen/src/Core/Assign.h diff --git a/xs/src/eigen/Eigen/src/Core/AssignEvaluator.h b/src/eigen/Eigen/src/Core/AssignEvaluator.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/AssignEvaluator.h rename to src/eigen/Eigen/src/Core/AssignEvaluator.h index b0ec7b7cad..dbe435d86b 100644 --- a/xs/src/eigen/Eigen/src/Core/AssignEvaluator.h +++ b/src/eigen/Eigen/src/Core/AssignEvaluator.h @@ -39,7 +39,7 @@ public: enum { DstAlignment = DstEvaluator::Alignment, SrcAlignment = SrcEvaluator::Alignment, - DstHasDirectAccess = DstFlags & DirectAccessBit, + DstHasDirectAccess = (DstFlags & DirectAccessBit) == DirectAccessBit, JointAlignment = EIGEN_PLAIN_ENUM_MIN(DstAlignment,SrcAlignment) }; @@ -83,7 +83,7 @@ private: && int(OuterStride)!=Dynamic && int(OuterStride)%int(InnerPacketSize)==0 && (EIGEN_UNALIGNED_VECTORIZE || int(JointAlignment)>=int(InnerRequiredAlignment)), MayLinearize = bool(StorageOrdersAgree) && (int(DstFlags) & int(SrcFlags) & LinearAccessBit), - MayLinearVectorize = bool(MightVectorize) && MayLinearize && DstHasDirectAccess + MayLinearVectorize = bool(MightVectorize) && bool(MayLinearize) && bool(DstHasDirectAccess) && (EIGEN_UNALIGNED_VECTORIZE || (int(DstAlignment)>=int(LinearRequiredAlignment)) || MaxSizeAtCompileTime == Dynamic), /* If the destination isn't aligned, we have to do runtime checks and we don't unroll, so it's only good for large enough sizes. */ diff --git a/xs/src/eigen/Eigen/src/Core/Assign_MKL.h b/src/eigen/Eigen/src/Core/Assign_MKL.h similarity index 96% rename from xs/src/eigen/Eigen/src/Core/Assign_MKL.h rename to src/eigen/Eigen/src/Core/Assign_MKL.h index 6c2ab92648..6866095bf8 100644 --- a/xs/src/eigen/Eigen/src/Core/Assign_MKL.h +++ b/src/eigen/Eigen/src/Core/Assign_MKL.h @@ -84,7 +84,8 @@ class vml_assign_traits struct Assignment, SrcXprNested>, assign_op, \ Dense2Dense, typename enable_if::EnableVml>::type> { \ typedef CwiseUnaryOp, SrcXprNested> SrcXprType; \ - static void run(DstXprType &dst, const SrcXprType &src, const assign_op &/*func*/) { \ + static void run(DstXprType &dst, const SrcXprType &src, const assign_op &func) { \ + resize_if_allowed(dst, src, func); \ eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \ if(vml_assign_traits::Traversal==LinearTraversal) { \ VMLOP(dst.size(), (const VMLTYPE*)src.nestedExpression().data(), \ @@ -144,7 +145,8 @@ EIGEN_MKL_VML_DECLARE_UNARY_CALLS_REAL(ceil, Ceil, _) Dense2Dense, typename enable_if::EnableVml>::type> { \ typedef CwiseBinaryOp, SrcXprNested, \ const CwiseNullaryOp,Plain> > SrcXprType; \ - static void run(DstXprType &dst, const SrcXprType &src, const assign_op &/*func*/) { \ + static void run(DstXprType &dst, const SrcXprType &src, const assign_op &func) { \ + resize_if_allowed(dst, src, func); \ eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \ VMLTYPE exponent = reinterpret_cast(src.rhs().functor().m_other); \ if(vml_assign_traits::Traversal==LinearTraversal) \ diff --git a/xs/src/eigen/Eigen/src/Core/BandMatrix.h b/src/eigen/Eigen/src/Core/BandMatrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/BandMatrix.h rename to src/eigen/Eigen/src/Core/BandMatrix.h diff --git a/xs/src/eigen/Eigen/src/Core/Block.h b/src/eigen/Eigen/src/Core/Block.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Block.h rename to src/eigen/Eigen/src/Core/Block.h diff --git a/xs/src/eigen/Eigen/src/Core/BooleanRedux.h b/src/eigen/Eigen/src/Core/BooleanRedux.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/BooleanRedux.h rename to src/eigen/Eigen/src/Core/BooleanRedux.h diff --git a/xs/src/eigen/Eigen/src/Core/CommaInitializer.h b/src/eigen/Eigen/src/Core/CommaInitializer.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/CommaInitializer.h rename to src/eigen/Eigen/src/Core/CommaInitializer.h diff --git a/xs/src/eigen/Eigen/src/Core/ConditionEstimator.h b/src/eigen/Eigen/src/Core/ConditionEstimator.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/ConditionEstimator.h rename to src/eigen/Eigen/src/Core/ConditionEstimator.h diff --git a/xs/src/eigen/Eigen/src/Core/CoreEvaluators.h b/src/eigen/Eigen/src/Core/CoreEvaluators.h similarity index 97% rename from xs/src/eigen/Eigen/src/Core/CoreEvaluators.h rename to src/eigen/Eigen/src/Core/CoreEvaluators.h index f7c1effca7..910889efa7 100644 --- a/xs/src/eigen/Eigen/src/Core/CoreEvaluators.h +++ b/src/eigen/Eigen/src/Core/CoreEvaluators.h @@ -977,7 +977,7 @@ struct evaluator > OuterStrideAtCompileTime = HasSameStorageOrderAsArgType ? int(outer_stride_at_compile_time::ret) : int(inner_stride_at_compile_time::ret), - MaskPacketAccessBit = (InnerStrideAtCompileTime == 1) ? PacketAccessBit : 0, + MaskPacketAccessBit = (InnerStrideAtCompileTime == 1 || HasSameStorageOrderAsArgType) ? PacketAccessBit : 0, FlagsLinearAccessBit = (RowsAtCompileTime == 1 || ColsAtCompileTime == 1 || (InnerPanel && (evaluator::Flags&LinearAccessBit))) ? LinearAccessBit : 0, FlagsRowMajorBit = XprType::Flags&RowMajorBit, @@ -987,7 +987,9 @@ struct evaluator > Flags = Flags0 | FlagsLinearAccessBit | FlagsRowMajorBit, PacketAlignment = unpacket_traits::alignment, - Alignment0 = (InnerPanel && (OuterStrideAtCompileTime!=Dynamic) && (((OuterStrideAtCompileTime * int(sizeof(Scalar))) % int(PacketAlignment)) == 0)) ? int(PacketAlignment) : 0, + Alignment0 = (InnerPanel && (OuterStrideAtCompileTime!=Dynamic) + && (OuterStrideAtCompileTime!=0) + && (((OuterStrideAtCompileTime * int(sizeof(Scalar))) % int(PacketAlignment)) == 0)) ? int(PacketAlignment) : 0, Alignment = EIGEN_PLAIN_ENUM_MIN(evaluator::Alignment, Alignment0) }; typedef block_evaluator block_evaluator_type; @@ -1018,14 +1020,16 @@ struct unary_evaluator, IndexBa EIGEN_DEVICE_FUNC explicit unary_evaluator(const XprType& block) : m_argImpl(block.nestedExpression()), m_startRow(block.startRow()), - m_startCol(block.startCol()) + m_startCol(block.startCol()), + m_linear_offset(InnerPanel?(XprType::IsRowMajor ? block.startRow()*block.cols() : block.startCol()*block.rows()):0) { } typedef typename XprType::Scalar Scalar; typedef typename XprType::CoeffReturnType CoeffReturnType; enum { - RowsAtCompileTime = XprType::RowsAtCompileTime + RowsAtCompileTime = XprType::RowsAtCompileTime, + ForwardLinearAccess = InnerPanel && bool(evaluator::Flags&LinearAccessBit) }; EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE @@ -1037,7 +1041,10 @@ struct unary_evaluator, IndexBa EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const { - return coeff(RowsAtCompileTime == 1 ? 0 : index, RowsAtCompileTime == 1 ? index : 0); + if (ForwardLinearAccess) + return m_argImpl.coeff(m_linear_offset.value() + index); + else + return coeff(RowsAtCompileTime == 1 ? 0 : index, RowsAtCompileTime == 1 ? index : 0); } EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE @@ -1049,7 +1056,10 @@ struct unary_evaluator, IndexBa EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& coeffRef(Index index) { - return coeffRef(RowsAtCompileTime == 1 ? 0 : index, RowsAtCompileTime == 1 ? index : 0); + if (ForwardLinearAccess) + return m_argImpl.coeffRef(m_linear_offset.value() + index); + else + return coeffRef(RowsAtCompileTime == 1 ? 0 : index, RowsAtCompileTime == 1 ? index : 0); } template @@ -1063,8 +1073,11 @@ struct unary_evaluator, IndexBa EIGEN_STRONG_INLINE PacketType packet(Index index) const { - return packet(RowsAtCompileTime == 1 ? 0 : index, - RowsAtCompileTime == 1 ? index : 0); + if (ForwardLinearAccess) + return m_argImpl.template packet(m_linear_offset.value() + index); + else + return packet(RowsAtCompileTime == 1 ? 0 : index, + RowsAtCompileTime == 1 ? index : 0); } template @@ -1078,15 +1091,19 @@ struct unary_evaluator, IndexBa EIGEN_STRONG_INLINE void writePacket(Index index, const PacketType& x) { - return writePacket(RowsAtCompileTime == 1 ? 0 : index, - RowsAtCompileTime == 1 ? index : 0, - x); + if (ForwardLinearAccess) + return m_argImpl.template writePacket(m_linear_offset.value() + index, x); + else + return writePacket(RowsAtCompileTime == 1 ? 0 : index, + RowsAtCompileTime == 1 ? index : 0, + x); } protected: evaluator m_argImpl; const variable_if_dynamic m_startRow; const variable_if_dynamic m_startCol; + const variable_if_dynamic m_linear_offset; }; // TODO: This evaluator does not actually use the child evaluator; diff --git a/xs/src/eigen/Eigen/src/Core/CoreIterators.h b/src/eigen/Eigen/src/Core/CoreIterators.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/CoreIterators.h rename to src/eigen/Eigen/src/Core/CoreIterators.h diff --git a/xs/src/eigen/Eigen/src/Core/CwiseBinaryOp.h b/src/eigen/Eigen/src/Core/CwiseBinaryOp.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/CwiseBinaryOp.h rename to src/eigen/Eigen/src/Core/CwiseBinaryOp.h diff --git a/xs/src/eigen/Eigen/src/Core/CwiseNullaryOp.h b/src/eigen/Eigen/src/Core/CwiseNullaryOp.h similarity index 88% rename from xs/src/eigen/Eigen/src/Core/CwiseNullaryOp.h rename to src/eigen/Eigen/src/Core/CwiseNullaryOp.h index dd498f758d..ddd607e383 100644 --- a/xs/src/eigen/Eigen/src/Core/CwiseNullaryOp.h +++ b/src/eigen/Eigen/src/Core/CwiseNullaryOp.h @@ -105,7 +105,7 @@ class CwiseNullaryOp : public internal::dense_xpr_base< CwiseNullaryOp template -EIGEN_STRONG_INLINE const CwiseNullaryOp::PlainObject> +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const CwiseNullaryOp::PlainObject> DenseBase::NullaryExpr(Index rows, Index cols, const CustomNullaryOp& func) { return CwiseNullaryOp(rows, cols, func); @@ -150,7 +150,7 @@ DenseBase::NullaryExpr(Index size, const CustomNullaryOp& func) */ template template -EIGEN_STRONG_INLINE const CwiseNullaryOp::PlainObject> +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const CwiseNullaryOp::PlainObject> DenseBase::NullaryExpr(const CustomNullaryOp& func) { return CwiseNullaryOp(RowsAtCompileTime, ColsAtCompileTime, func); @@ -192,7 +192,7 @@ DenseBase::Constant(Index rows, Index cols, const Scalar& value) * \sa class CwiseNullaryOp */ template -EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType DenseBase::Constant(Index size, const Scalar& value) { return DenseBase::NullaryExpr(size, internal::scalar_constant_op(value)); @@ -208,7 +208,7 @@ DenseBase::Constant(Index size, const Scalar& value) * \sa class CwiseNullaryOp */ template -EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType DenseBase::Constant(const Scalar& value) { EIGEN_STATIC_ASSERT_FIXED_SIZE(Derived) @@ -220,7 +220,7 @@ DenseBase::Constant(const Scalar& value) * \sa LinSpaced(Index,Scalar,Scalar), setLinSpaced(Index,const Scalar&,const Scalar&) */ template -EIGEN_STRONG_INLINE const typename DenseBase::RandomAccessLinSpacedReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::RandomAccessLinSpacedReturnType DenseBase::LinSpaced(Sequential_t, Index size, const Scalar& low, const Scalar& high) { EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived) @@ -232,7 +232,7 @@ DenseBase::LinSpaced(Sequential_t, Index size, const Scalar& low, const * \sa LinSpaced(Scalar,Scalar) */ template -EIGEN_STRONG_INLINE const typename DenseBase::RandomAccessLinSpacedReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::RandomAccessLinSpacedReturnType DenseBase::LinSpaced(Sequential_t, const Scalar& low, const Scalar& high) { EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived) @@ -264,7 +264,7 @@ DenseBase::LinSpaced(Sequential_t, const Scalar& low, const Scalar& hig * \sa setLinSpaced(Index,const Scalar&,const Scalar&), CwiseNullaryOp */ template -EIGEN_STRONG_INLINE const typename DenseBase::RandomAccessLinSpacedReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::RandomAccessLinSpacedReturnType DenseBase::LinSpaced(Index size, const Scalar& low, const Scalar& high) { EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived) @@ -276,7 +276,7 @@ DenseBase::LinSpaced(Index size, const Scalar& low, const Scalar& high) * Special version for fixed size types which does not require the size parameter. */ template -EIGEN_STRONG_INLINE const typename DenseBase::RandomAccessLinSpacedReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::RandomAccessLinSpacedReturnType DenseBase::LinSpaced(const Scalar& low, const Scalar& high) { EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived) @@ -286,7 +286,7 @@ DenseBase::LinSpaced(const Scalar& low, const Scalar& high) /** \returns true if all coefficients in this matrix are approximately equal to \a val, to within precision \a prec */ template -bool DenseBase::isApproxToConstant +EIGEN_DEVICE_FUNC bool DenseBase::isApproxToConstant (const Scalar& val, const RealScalar& prec) const { typename internal::nested_eval::type self(derived()); @@ -301,7 +301,7 @@ bool DenseBase::isApproxToConstant * * \returns true if all coefficients in this matrix are approximately equal to \a value, to within precision \a prec */ template -bool DenseBase::isConstant +EIGEN_DEVICE_FUNC bool DenseBase::isConstant (const Scalar& val, const RealScalar& prec) const { return isApproxToConstant(val, prec); @@ -312,7 +312,7 @@ bool DenseBase::isConstant * \sa setConstant(), Constant(), class CwiseNullaryOp */ template -EIGEN_STRONG_INLINE void DenseBase::fill(const Scalar& val) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void DenseBase::fill(const Scalar& val) { setConstant(val); } @@ -322,7 +322,7 @@ EIGEN_STRONG_INLINE void DenseBase::fill(const Scalar& val) * \sa fill(), setConstant(Index,const Scalar&), setConstant(Index,Index,const Scalar&), setZero(), setOnes(), Constant(), class CwiseNullaryOp, setZero(), setOnes() */ template -EIGEN_STRONG_INLINE Derived& DenseBase::setConstant(const Scalar& val) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& DenseBase::setConstant(const Scalar& val) { return derived() = Constant(rows(), cols(), val); } @@ -337,7 +337,7 @@ EIGEN_STRONG_INLINE Derived& DenseBase::setConstant(const Scalar& val) * \sa MatrixBase::setConstant(const Scalar&), setConstant(Index,Index,const Scalar&), class CwiseNullaryOp, MatrixBase::Constant(const Scalar&) */ template -EIGEN_STRONG_INLINE Derived& +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& PlainObjectBase::setConstant(Index size, const Scalar& val) { resize(size); @@ -356,7 +356,7 @@ PlainObjectBase::setConstant(Index size, const Scalar& val) * \sa MatrixBase::setConstant(const Scalar&), setConstant(Index,const Scalar&), class CwiseNullaryOp, MatrixBase::Constant(const Scalar&) */ template -EIGEN_STRONG_INLINE Derived& +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& PlainObjectBase::setConstant(Index rows, Index cols, const Scalar& val) { resize(rows, cols); @@ -380,7 +380,7 @@ PlainObjectBase::setConstant(Index rows, Index cols, const Scalar& val) * \sa LinSpaced(Index,const Scalar&,const Scalar&), CwiseNullaryOp */ template -EIGEN_STRONG_INLINE Derived& DenseBase::setLinSpaced(Index newSize, const Scalar& low, const Scalar& high) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& DenseBase::setLinSpaced(Index newSize, const Scalar& low, const Scalar& high) { EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived) return derived() = Derived::NullaryExpr(newSize, internal::linspaced_op(low,high,newSize)); @@ -400,7 +400,7 @@ EIGEN_STRONG_INLINE Derived& DenseBase::setLinSpaced(Index newSize, con * \sa LinSpaced(Index,const Scalar&,const Scalar&), setLinSpaced(Index, const Scalar&, const Scalar&), CwiseNullaryOp */ template -EIGEN_STRONG_INLINE Derived& DenseBase::setLinSpaced(const Scalar& low, const Scalar& high) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& DenseBase::setLinSpaced(const Scalar& low, const Scalar& high) { EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived) return setLinSpaced(size(), low, high); @@ -423,7 +423,7 @@ EIGEN_STRONG_INLINE Derived& DenseBase::setLinSpaced(const Scalar& low, * \sa Zero(), Zero(Index) */ template -EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType DenseBase::Zero(Index rows, Index cols) { return Constant(rows, cols, Scalar(0)); @@ -446,7 +446,7 @@ DenseBase::Zero(Index rows, Index cols) * \sa Zero(), Zero(Index,Index) */ template -EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType DenseBase::Zero(Index size) { return Constant(size, Scalar(0)); @@ -463,7 +463,7 @@ DenseBase::Zero(Index size) * \sa Zero(Index), Zero(Index,Index) */ template -EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType DenseBase::Zero() { return Constant(Scalar(0)); @@ -478,7 +478,7 @@ DenseBase::Zero() * \sa class CwiseNullaryOp, Zero() */ template -bool DenseBase::isZero(const RealScalar& prec) const +EIGEN_DEVICE_FUNC bool DenseBase::isZero(const RealScalar& prec) const { typename internal::nested_eval::type self(derived()); for(Index j = 0; j < cols(); ++j) @@ -496,7 +496,7 @@ bool DenseBase::isZero(const RealScalar& prec) const * \sa class CwiseNullaryOp, Zero() */ template -EIGEN_STRONG_INLINE Derived& DenseBase::setZero() +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& DenseBase::setZero() { return setConstant(Scalar(0)); } @@ -511,7 +511,7 @@ EIGEN_STRONG_INLINE Derived& DenseBase::setZero() * \sa DenseBase::setZero(), setZero(Index,Index), class CwiseNullaryOp, DenseBase::Zero() */ template -EIGEN_STRONG_INLINE Derived& +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& PlainObjectBase::setZero(Index newSize) { resize(newSize); @@ -529,7 +529,7 @@ PlainObjectBase::setZero(Index newSize) * \sa DenseBase::setZero(), setZero(Index), class CwiseNullaryOp, DenseBase::Zero() */ template -EIGEN_STRONG_INLINE Derived& +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& PlainObjectBase::setZero(Index rows, Index cols) { resize(rows, cols); @@ -553,7 +553,7 @@ PlainObjectBase::setZero(Index rows, Index cols) * \sa Ones(), Ones(Index), isOnes(), class Ones */ template -EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType DenseBase::Ones(Index rows, Index cols) { return Constant(rows, cols, Scalar(1)); @@ -576,7 +576,7 @@ DenseBase::Ones(Index rows, Index cols) * \sa Ones(), Ones(Index,Index), isOnes(), class Ones */ template -EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType DenseBase::Ones(Index newSize) { return Constant(newSize, Scalar(1)); @@ -593,7 +593,7 @@ DenseBase::Ones(Index newSize) * \sa Ones(Index), Ones(Index,Index), isOnes(), class Ones */ template -EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase::ConstantReturnType DenseBase::Ones() { return Constant(Scalar(1)); @@ -608,7 +608,7 @@ DenseBase::Ones() * \sa class CwiseNullaryOp, Ones() */ template -bool DenseBase::isOnes +EIGEN_DEVICE_FUNC bool DenseBase::isOnes (const RealScalar& prec) const { return isApproxToConstant(Scalar(1), prec); @@ -622,7 +622,7 @@ bool DenseBase::isOnes * \sa class CwiseNullaryOp, Ones() */ template -EIGEN_STRONG_INLINE Derived& DenseBase::setOnes() +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& DenseBase::setOnes() { return setConstant(Scalar(1)); } @@ -637,7 +637,7 @@ EIGEN_STRONG_INLINE Derived& DenseBase::setOnes() * \sa MatrixBase::setOnes(), setOnes(Index,Index), class CwiseNullaryOp, MatrixBase::Ones() */ template -EIGEN_STRONG_INLINE Derived& +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& PlainObjectBase::setOnes(Index newSize) { resize(newSize); @@ -655,7 +655,7 @@ PlainObjectBase::setOnes(Index newSize) * \sa MatrixBase::setOnes(), setOnes(Index), class CwiseNullaryOp, MatrixBase::Ones() */ template -EIGEN_STRONG_INLINE Derived& +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& PlainObjectBase::setOnes(Index rows, Index cols) { resize(rows, cols); @@ -679,7 +679,7 @@ PlainObjectBase::setOnes(Index rows, Index cols) * \sa Identity(), setIdentity(), isIdentity() */ template -EIGEN_STRONG_INLINE const typename MatrixBase::IdentityReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename MatrixBase::IdentityReturnType MatrixBase::Identity(Index rows, Index cols) { return DenseBase::NullaryExpr(rows, cols, internal::scalar_identity_op()); @@ -696,7 +696,7 @@ MatrixBase::Identity(Index rows, Index cols) * \sa Identity(Index,Index), setIdentity(), isIdentity() */ template -EIGEN_STRONG_INLINE const typename MatrixBase::IdentityReturnType +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename MatrixBase::IdentityReturnType MatrixBase::Identity() { EIGEN_STATIC_ASSERT_FIXED_SIZE(Derived) @@ -771,7 +771,7 @@ struct setIdentity_impl * \sa class CwiseNullaryOp, Identity(), Identity(Index,Index), isIdentity() */ template -EIGEN_STRONG_INLINE Derived& MatrixBase::setIdentity() +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& MatrixBase::setIdentity() { return internal::setIdentity_impl::run(derived()); } @@ -787,7 +787,7 @@ EIGEN_STRONG_INLINE Derived& MatrixBase::setIdentity() * \sa MatrixBase::setIdentity(), class CwiseNullaryOp, MatrixBase::Identity() */ template -EIGEN_STRONG_INLINE Derived& MatrixBase::setIdentity(Index rows, Index cols) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& MatrixBase::setIdentity(Index rows, Index cols) { derived().resize(rows, cols); return setIdentity(); @@ -800,7 +800,7 @@ EIGEN_STRONG_INLINE Derived& MatrixBase::setIdentity(Index rows, Index * \sa MatrixBase::Unit(Index), MatrixBase::UnitX(), MatrixBase::UnitY(), MatrixBase::UnitZ(), MatrixBase::UnitW() */ template -EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::Unit(Index newSize, Index i) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::Unit(Index newSize, Index i) { EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived) return BasisReturnType(SquareMatrixType::Identity(newSize,newSize), i); @@ -815,7 +815,7 @@ EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBa * \sa MatrixBase::Unit(Index,Index), MatrixBase::UnitX(), MatrixBase::UnitY(), MatrixBase::UnitZ(), MatrixBase::UnitW() */ template -EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::Unit(Index i) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::Unit(Index i) { EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived) return BasisReturnType(SquareMatrixType::Identity(),i); @@ -828,7 +828,7 @@ EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBa * \sa MatrixBase::Unit(Index,Index), MatrixBase::Unit(Index), MatrixBase::UnitY(), MatrixBase::UnitZ(), MatrixBase::UnitW() */ template -EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::UnitX() +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::UnitX() { return Derived::Unit(0); } /** \returns an expression of the Y axis unit vector (0,1{,0}^*) @@ -838,7 +838,7 @@ EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBa * \sa MatrixBase::Unit(Index,Index), MatrixBase::Unit(Index), MatrixBase::UnitY(), MatrixBase::UnitZ(), MatrixBase::UnitW() */ template -EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::UnitY() +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::UnitY() { return Derived::Unit(1); } /** \returns an expression of the Z axis unit vector (0,0,1{,0}^*) @@ -848,7 +848,7 @@ EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBa * \sa MatrixBase::Unit(Index,Index), MatrixBase::Unit(Index), MatrixBase::UnitY(), MatrixBase::UnitZ(), MatrixBase::UnitW() */ template -EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::UnitZ() +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::UnitZ() { return Derived::Unit(2); } /** \returns an expression of the W axis unit vector (0,0,0,1) @@ -858,7 +858,7 @@ EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBa * \sa MatrixBase::Unit(Index,Index), MatrixBase::Unit(Index), MatrixBase::UnitY(), MatrixBase::UnitZ(), MatrixBase::UnitW() */ template -EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::UnitW() +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename MatrixBase::BasisReturnType MatrixBase::UnitW() { return Derived::Unit(3); } } // end namespace Eigen diff --git a/xs/src/eigen/Eigen/src/Core/CwiseTernaryOp.h b/src/eigen/Eigen/src/Core/CwiseTernaryOp.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/CwiseTernaryOp.h rename to src/eigen/Eigen/src/Core/CwiseTernaryOp.h diff --git a/xs/src/eigen/Eigen/src/Core/CwiseUnaryOp.h b/src/eigen/Eigen/src/Core/CwiseUnaryOp.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/CwiseUnaryOp.h rename to src/eigen/Eigen/src/Core/CwiseUnaryOp.h diff --git a/xs/src/eigen/Eigen/src/Core/CwiseUnaryView.h b/src/eigen/Eigen/src/Core/CwiseUnaryView.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/CwiseUnaryView.h rename to src/eigen/Eigen/src/Core/CwiseUnaryView.h diff --git a/xs/src/eigen/Eigen/src/Core/DenseBase.h b/src/eigen/Eigen/src/Core/DenseBase.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/DenseBase.h rename to src/eigen/Eigen/src/Core/DenseBase.h index 46fe5193c2..90066ae73f 100644 --- a/xs/src/eigen/Eigen/src/Core/DenseBase.h +++ b/src/eigen/Eigen/src/Core/DenseBase.h @@ -296,7 +296,7 @@ template class DenseBase EIGEN_DEVICE_FUNC Derived& operator=(const ReturnByValue& func); - /** \ínternal + /** \internal * Copies \a other into *this without evaluating other. \returns a reference to *this. * \deprecated */ template @@ -484,9 +484,9 @@ template class DenseBase return derived().coeff(0,0); } - bool all() const; - bool any() const; - Index count() const; + EIGEN_DEVICE_FUNC bool all() const; + EIGEN_DEVICE_FUNC bool any() const; + EIGEN_DEVICE_FUNC Index count() const; typedef VectorwiseOp RowwiseReturnType; typedef const VectorwiseOp ConstRowwiseReturnType; diff --git a/xs/src/eigen/Eigen/src/Core/DenseCoeffsBase.h b/src/eigen/Eigen/src/Core/DenseCoeffsBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/DenseCoeffsBase.h rename to src/eigen/Eigen/src/Core/DenseCoeffsBase.h diff --git a/xs/src/eigen/Eigen/src/Core/DenseStorage.h b/src/eigen/Eigen/src/Core/DenseStorage.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/DenseStorage.h rename to src/eigen/Eigen/src/Core/DenseStorage.h diff --git a/xs/src/eigen/Eigen/src/Core/Diagonal.h b/src/eigen/Eigen/src/Core/Diagonal.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/Diagonal.h rename to src/eigen/Eigen/src/Core/Diagonal.h index 49e7112571..afcaf35756 100644 --- a/xs/src/eigen/Eigen/src/Core/Diagonal.h +++ b/src/eigen/Eigen/src/Core/Diagonal.h @@ -70,7 +70,10 @@ template class Diagonal EIGEN_DENSE_PUBLIC_INTERFACE(Diagonal) EIGEN_DEVICE_FUNC - explicit inline Diagonal(MatrixType& matrix, Index a_index = DiagIndex) : m_matrix(matrix), m_index(a_index) {} + explicit inline Diagonal(MatrixType& matrix, Index a_index = DiagIndex) : m_matrix(matrix), m_index(a_index) + { + eigen_assert( a_index <= m_matrix.cols() && -a_index <= m_matrix.rows() ); + } EIGEN_INHERIT_ASSIGNMENT_OPERATORS(Diagonal) diff --git a/xs/src/eigen/Eigen/src/Core/DiagonalMatrix.h b/src/eigen/Eigen/src/Core/DiagonalMatrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/DiagonalMatrix.h rename to src/eigen/Eigen/src/Core/DiagonalMatrix.h diff --git a/xs/src/eigen/Eigen/src/Core/DiagonalProduct.h b/src/eigen/Eigen/src/Core/DiagonalProduct.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/DiagonalProduct.h rename to src/eigen/Eigen/src/Core/DiagonalProduct.h diff --git a/xs/src/eigen/Eigen/src/Core/Dot.h b/src/eigen/Eigen/src/Core/Dot.h similarity index 94% rename from xs/src/eigen/Eigen/src/Core/Dot.h rename to src/eigen/Eigen/src/Core/Dot.h index 06ef18b8be..1fe7a84a48 100644 --- a/xs/src/eigen/Eigen/src/Core/Dot.h +++ b/src/eigen/Eigen/src/Core/Dot.h @@ -31,7 +31,8 @@ struct dot_nocheck typedef scalar_conj_product_op::Scalar,typename traits::Scalar> conj_prod; typedef typename conj_prod::result_type ResScalar; EIGEN_DEVICE_FUNC - static inline ResScalar run(const MatrixBase& a, const MatrixBase& b) + EIGEN_STRONG_INLINE + static ResScalar run(const MatrixBase& a, const MatrixBase& b) { return a.template binaryExpr(b).sum(); } @@ -43,7 +44,8 @@ struct dot_nocheck typedef scalar_conj_product_op::Scalar,typename traits::Scalar> conj_prod; typedef typename conj_prod::result_type ResScalar; EIGEN_DEVICE_FUNC - static inline ResScalar run(const MatrixBase& a, const MatrixBase& b) + EIGEN_STRONG_INLINE + static ResScalar run(const MatrixBase& a, const MatrixBase& b) { return a.transpose().template binaryExpr(b).sum(); } @@ -65,6 +67,7 @@ struct dot_nocheck template template EIGEN_DEVICE_FUNC +EIGEN_STRONG_INLINE typename ScalarBinaryOpTraits::Scalar,typename internal::traits::Scalar>::ReturnType MatrixBase::dot(const MatrixBase& other) const { @@ -102,7 +105,7 @@ EIGEN_STRONG_INLINE typename NumTraits::Scala * \sa lpNorm(), dot(), squaredNorm() */ template -inline typename NumTraits::Scalar>::Real MatrixBase::norm() const +EIGEN_STRONG_INLINE typename NumTraits::Scalar>::Real MatrixBase::norm() const { return numext::sqrt(squaredNorm()); } @@ -117,7 +120,7 @@ inline typename NumTraits::Scalar>::Real Matr * \sa norm(), normalize() */ template -inline const typename MatrixBase::PlainObject +EIGEN_STRONG_INLINE const typename MatrixBase::PlainObject MatrixBase::normalized() const { typedef typename internal::nested_eval::type _Nested; @@ -139,7 +142,7 @@ MatrixBase::normalized() const * \sa norm(), normalized() */ template -inline void MatrixBase::normalize() +EIGEN_STRONG_INLINE void MatrixBase::normalize() { RealScalar z = squaredNorm(); // NOTE: after extensive benchmarking, this conditional does not impact performance, at least on recent x86 CPU @@ -160,7 +163,7 @@ inline void MatrixBase::normalize() * \sa stableNorm(), stableNormalize(), normalized() */ template -inline const typename MatrixBase::PlainObject +EIGEN_STRONG_INLINE const typename MatrixBase::PlainObject MatrixBase::stableNormalized() const { typedef typename internal::nested_eval::type _Nested; @@ -185,7 +188,7 @@ MatrixBase::stableNormalized() const * \sa stableNorm(), stableNormalized(), normalize() */ template -inline void MatrixBase::stableNormalize() +EIGEN_STRONG_INLINE void MatrixBase::stableNormalize() { RealScalar w = cwiseAbs().maxCoeff(); RealScalar z = (derived()/w).squaredNorm(); diff --git a/xs/src/eigen/Eigen/src/Core/EigenBase.h b/src/eigen/Eigen/src/Core/EigenBase.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/EigenBase.h rename to src/eigen/Eigen/src/Core/EigenBase.h index f76995af90..b195506a91 100644 --- a/xs/src/eigen/Eigen/src/Core/EigenBase.h +++ b/src/eigen/Eigen/src/Core/EigenBase.h @@ -14,6 +14,7 @@ namespace Eigen { /** \class EigenBase + * \ingroup Core_Module * * Common base class for all classes T such that MatrixBase has an operator=(T) and a constructor MatrixBase(T). * @@ -128,6 +129,7 @@ template struct EigenBase */ template template +EIGEN_DEVICE_FUNC Derived& DenseBase::operator=(const EigenBase &other) { call_assignment(derived(), other.derived()); @@ -136,6 +138,7 @@ Derived& DenseBase::operator=(const EigenBase &other) template template +EIGEN_DEVICE_FUNC Derived& DenseBase::operator+=(const EigenBase &other) { call_assignment(derived(), other.derived(), internal::add_assign_op()); @@ -144,6 +147,7 @@ Derived& DenseBase::operator+=(const EigenBase &other) template template +EIGEN_DEVICE_FUNC Derived& DenseBase::operator-=(const EigenBase &other) { call_assignment(derived(), other.derived(), internal::sub_assign_op()); diff --git a/xs/src/eigen/Eigen/src/Core/ForceAlignedAccess.h b/src/eigen/Eigen/src/Core/ForceAlignedAccess.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/ForceAlignedAccess.h rename to src/eigen/Eigen/src/Core/ForceAlignedAccess.h diff --git a/xs/src/eigen/Eigen/src/Core/Fuzzy.h b/src/eigen/Eigen/src/Core/Fuzzy.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Fuzzy.h rename to src/eigen/Eigen/src/Core/Fuzzy.h diff --git a/xs/src/eigen/Eigen/src/Core/GeneralProduct.h b/src/eigen/Eigen/src/Core/GeneralProduct.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/GeneralProduct.h rename to src/eigen/Eigen/src/Core/GeneralProduct.h index 0f16cd8e39..6f0cc80e94 100644 --- a/xs/src/eigen/Eigen/src/Core/GeneralProduct.h +++ b/src/eigen/Eigen/src/Core/GeneralProduct.h @@ -24,12 +24,17 @@ template struct product_type_selector; template struct product_size_category { - enum { is_large = MaxSize == Dynamic || - Size >= EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD || - (Size==Dynamic && MaxSize>=EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD), - value = is_large ? Large - : Size == 1 ? 1 - : Small + enum { + #ifndef EIGEN_CUDA_ARCH + is_large = MaxSize == Dynamic || + Size >= EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD || + (Size==Dynamic && MaxSize>=EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD), + #else + is_large = 0, + #endif + value = is_large ? Large + : Size == 1 ? 1 + : Small }; }; @@ -379,8 +384,6 @@ template<> struct gemv_dense_selector * * \sa lazyProduct(), operator*=(const MatrixBase&), Cwise::operator*() */ -#ifndef __CUDACC__ - template template inline const Product @@ -412,8 +415,6 @@ MatrixBase::operator*(const MatrixBase &other) const return Product(derived(), other.derived()); } -#endif // __CUDACC__ - /** \returns an expression of the matrix product of \c *this and \a other without implicit evaluation. * * The returned product will behave like any other expressions: the coefficients of the product will be diff --git a/xs/src/eigen/Eigen/src/Core/GenericPacketMath.h b/src/eigen/Eigen/src/Core/GenericPacketMath.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/GenericPacketMath.h rename to src/eigen/Eigen/src/Core/GenericPacketMath.h index 27033a2dd0..029f8ac36f 100644 --- a/xs/src/eigen/Eigen/src/Core/GenericPacketMath.h +++ b/src/eigen/Eigen/src/Core/GenericPacketMath.h @@ -230,7 +230,7 @@ pload1(const typename unpacket_traits::type *a) { return pset1( * duplicated to form: {from[0],from[0],from[1],from[1],from[2],from[2],from[3],from[3]} * Currently, this function is only used for scalar * complex products. */ -template EIGEN_DEVICE_FUNC inline Packet +template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet ploaddup(const typename unpacket_traits::type* from) { return *from; } /** \internal \returns a packet with elements of \a *from quadrupled. @@ -278,7 +278,7 @@ inline void pbroadcast2(const typename unpacket_traits::type *a, } /** \internal \brief Returns a packet with coefficients (a,a+1,...,a+packet_size-1). */ -template inline Packet +template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet plset(const typename unpacket_traits::type& a) { return a; } /** \internal copy the packet \a from to \a *to, \a to must be 16 bytes aligned */ @@ -482,7 +482,7 @@ EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE void pstoret(Scalar* to, const Packet& fro * by the current computation. */ template -inline Packet ploadt_ro(const typename unpacket_traits::type* from) +EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Packet ploadt_ro(const typename unpacket_traits::type* from) { return ploadt(from); } diff --git a/xs/src/eigen/Eigen/src/Core/GlobalFunctions.h b/src/eigen/Eigen/src/Core/GlobalFunctions.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/GlobalFunctions.h rename to src/eigen/Eigen/src/Core/GlobalFunctions.h diff --git a/xs/src/eigen/Eigen/src/Core/IO.h b/src/eigen/Eigen/src/Core/IO.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/IO.h rename to src/eigen/Eigen/src/Core/IO.h diff --git a/xs/src/eigen/Eigen/src/Core/Inverse.h b/src/eigen/Eigen/src/Core/Inverse.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Inverse.h rename to src/eigen/Eigen/src/Core/Inverse.h diff --git a/xs/src/eigen/Eigen/src/Core/Map.h b/src/eigen/Eigen/src/Core/Map.h similarity index 88% rename from xs/src/eigen/Eigen/src/Core/Map.h rename to src/eigen/Eigen/src/Core/Map.h index 06d1967024..548bf9a2d5 100644 --- a/xs/src/eigen/Eigen/src/Core/Map.h +++ b/src/eigen/Eigen/src/Core/Map.h @@ -20,11 +20,17 @@ struct traits > { typedef traits TraitsBase; enum { + PlainObjectTypeInnerSize = ((traits::Flags&RowMajorBit)==RowMajorBit) + ? PlainObjectType::ColsAtCompileTime + : PlainObjectType::RowsAtCompileTime, + InnerStrideAtCompileTime = StrideType::InnerStrideAtCompileTime == 0 ? int(PlainObjectType::InnerStrideAtCompileTime) : int(StrideType::InnerStrideAtCompileTime), OuterStrideAtCompileTime = StrideType::OuterStrideAtCompileTime == 0 - ? int(PlainObjectType::OuterStrideAtCompileTime) + ? (InnerStrideAtCompileTime==Dynamic || PlainObjectTypeInnerSize==Dynamic + ? Dynamic + : int(InnerStrideAtCompileTime) * int(PlainObjectTypeInnerSize)) : int(StrideType::OuterStrideAtCompileTime), Alignment = int(MapOptions)&int(AlignedMask), Flags0 = TraitsBase::Flags & (~NestByRefBit), @@ -107,10 +113,11 @@ template class Ma EIGEN_DEVICE_FUNC inline Index outerStride() const { - return StrideType::OuterStrideAtCompileTime != 0 ? m_stride.outer() - : IsVectorAtCompileTime ? this->size() - : int(Flags)&RowMajorBit ? this->cols() - : this->rows(); + return int(StrideType::OuterStrideAtCompileTime) != 0 ? m_stride.outer() + : int(internal::traits::OuterStrideAtCompileTime) != Dynamic ? Index(internal::traits::OuterStrideAtCompileTime) + : IsVectorAtCompileTime ? (this->size() * innerStride()) + : (int(Flags)&RowMajorBit) ? (this->cols() * innerStride()) + : (this->rows() * innerStride()); } /** Constructor in the fixed-size case. diff --git a/xs/src/eigen/Eigen/src/Core/MapBase.h b/src/eigen/Eigen/src/Core/MapBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/MapBase.h rename to src/eigen/Eigen/src/Core/MapBase.h diff --git a/xs/src/eigen/Eigen/src/Core/MathFunctions.h b/src/eigen/Eigen/src/Core/MathFunctions.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/MathFunctions.h rename to src/eigen/Eigen/src/Core/MathFunctions.h index 8d47fb8a46..6eb974d41d 100644 --- a/xs/src/eigen/Eigen/src/Core/MathFunctions.h +++ b/src/eigen/Eigen/src/Core/MathFunctions.h @@ -348,31 +348,7 @@ struct norm1_retval * Implementation of hypot * ****************************************************************************/ -template -struct hypot_impl -{ - typedef typename NumTraits::Real RealScalar; - static inline RealScalar run(const Scalar& x, const Scalar& y) - { - EIGEN_USING_STD_MATH(abs); - EIGEN_USING_STD_MATH(sqrt); - RealScalar _x = abs(x); - RealScalar _y = abs(y); - Scalar p, qp; - if(_x>_y) - { - p = _x; - qp = _y / p; - } - else - { - p = _y; - qp = _x / p; - } - if(p==RealScalar(0)) return RealScalar(0); - return p * sqrt(RealScalar(1) + qp*qp); - } -}; +template struct hypot_impl; template struct hypot_retval @@ -495,7 +471,7 @@ namespace std_fallback { typedef typename NumTraits::Real RealScalar; EIGEN_USING_STD_MATH(log); Scalar x1p = RealScalar(1) + x; - return ( x1p == Scalar(1) ) ? x : x * ( log(x1p) / (x1p - RealScalar(1)) ); + return numext::equal_strict(x1p, Scalar(1)) ? x : x * ( log(x1p) / (x1p - RealScalar(1)) ); } } @@ -1061,11 +1037,24 @@ double log(const double &x) { return ::log(x); } template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE -typename NumTraits::Real abs(const T &x) { +typename internal::enable_if::IsSigned || NumTraits::IsComplex,typename NumTraits::Real>::type +abs(const T &x) { EIGEN_USING_STD_MATH(abs); return abs(x); } +template +EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE +typename internal::enable_if::IsSigned || NumTraits::IsComplex),typename NumTraits::Real>::type +abs(const T &x) { + return x; +} + +#if defined(__SYCL_DEVICE_ONLY__) +EIGEN_ALWAYS_INLINE float abs(float x) { return cl::sycl::fabs(x); } +EIGEN_ALWAYS_INLINE double abs(double x) { return cl::sycl::fabs(x); } +#endif // defined(__SYCL_DEVICE_ONLY__) + #ifdef __CUDACC__ template<> EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE float abs(const float &x) { return ::fabsf(x); } diff --git a/xs/src/eigen/Eigen/src/Core/MathFunctionsImpl.h b/src/eigen/Eigen/src/Core/MathFunctionsImpl.h similarity index 82% rename from xs/src/eigen/Eigen/src/Core/MathFunctionsImpl.h rename to src/eigen/Eigen/src/Core/MathFunctionsImpl.h index 3c9ef22fa6..9c1ceb0eb0 100644 --- a/xs/src/eigen/Eigen/src/Core/MathFunctionsImpl.h +++ b/src/eigen/Eigen/src/Core/MathFunctionsImpl.h @@ -71,6 +71,29 @@ T generic_fast_tanh_float(const T& a_x) return pdiv(p, q); } +template +EIGEN_STRONG_INLINE +RealScalar positive_real_hypot(const RealScalar& x, const RealScalar& y) +{ + EIGEN_USING_STD_MATH(sqrt); + RealScalar p, qp; + p = numext::maxi(x,y); + if(p==RealScalar(0)) return RealScalar(0); + qp = numext::mini(y,x) / p; + return p * sqrt(RealScalar(1) + qp*qp); +} + +template +struct hypot_impl +{ + typedef typename NumTraits::Real RealScalar; + static inline RealScalar run(const Scalar& x, const Scalar& y) + { + EIGEN_USING_STD_MATH(abs); + return positive_real_hypot(abs(x), abs(y)); + } +}; + } // end namespace internal } // end namespace Eigen diff --git a/xs/src/eigen/Eigen/src/Core/Matrix.h b/src/eigen/Eigen/src/Core/Matrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Matrix.h rename to src/eigen/Eigen/src/Core/Matrix.h diff --git a/xs/src/eigen/Eigen/src/Core/MatrixBase.h b/src/eigen/Eigen/src/Core/MatrixBase.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/MatrixBase.h rename to src/eigen/Eigen/src/Core/MatrixBase.h index f7cf04cde3..05db488131 100644 --- a/xs/src/eigen/Eigen/src/Core/MatrixBase.h +++ b/src/eigen/Eigen/src/Core/MatrixBase.h @@ -160,20 +160,11 @@ template class MatrixBase EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& operator-=(const MatrixBase& other); -#ifdef __CUDACC__ template EIGEN_DEVICE_FUNC - const Product - operator*(const MatrixBase &other) const - { return this->lazyProduct(other); } -#else - - template const Product operator*(const MatrixBase &other) const; -#endif - template EIGEN_DEVICE_FUNC const Product @@ -294,7 +285,7 @@ template class MatrixBase * fuzzy comparison such as isApprox() * \sa isApprox(), operator!= */ template - inline bool operator==(const MatrixBase& other) const + EIGEN_DEVICE_FUNC inline bool operator==(const MatrixBase& other) const { return cwiseEqual(other).all(); } /** \returns true if at least one pair of coefficients of \c *this and \a other are not exactly equal to each other. @@ -302,7 +293,7 @@ template class MatrixBase * fuzzy comparison such as isApprox() * \sa isApprox(), operator== */ template - inline bool operator!=(const MatrixBase& other) const + EIGEN_DEVICE_FUNC inline bool operator!=(const MatrixBase& other) const { return cwiseNotEqual(other).any(); } NoAlias noalias(); diff --git a/xs/src/eigen/Eigen/src/Core/NestByValue.h b/src/eigen/Eigen/src/Core/NestByValue.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/NestByValue.h rename to src/eigen/Eigen/src/Core/NestByValue.h diff --git a/xs/src/eigen/Eigen/src/Core/NoAlias.h b/src/eigen/Eigen/src/Core/NoAlias.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/NoAlias.h rename to src/eigen/Eigen/src/Core/NoAlias.h diff --git a/xs/src/eigen/Eigen/src/Core/NumTraits.h b/src/eigen/Eigen/src/Core/NumTraits.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/NumTraits.h rename to src/eigen/Eigen/src/Core/NumTraits.h index dd61195bc2..daf4898789 100644 --- a/xs/src/eigen/Eigen/src/Core/NumTraits.h +++ b/src/eigen/Eigen/src/Core/NumTraits.h @@ -215,6 +215,8 @@ struct NumTraits > static inline RealScalar epsilon() { return NumTraits::epsilon(); } EIGEN_DEVICE_FUNC static inline RealScalar dummy_precision() { return NumTraits::dummy_precision(); } + + static inline int digits10() { return NumTraits::digits10(); } }; template<> struct NumTraits diff --git a/xs/src/eigen/Eigen/src/Core/PermutationMatrix.h b/src/eigen/Eigen/src/Core/PermutationMatrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/PermutationMatrix.h rename to src/eigen/Eigen/src/Core/PermutationMatrix.h diff --git a/xs/src/eigen/Eigen/src/Core/PlainObjectBase.h b/src/eigen/Eigen/src/Core/PlainObjectBase.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/PlainObjectBase.h rename to src/eigen/Eigen/src/Core/PlainObjectBase.h index 77f4f60667..1dc7e223af 100644 --- a/xs/src/eigen/Eigen/src/Core/PlainObjectBase.h +++ b/src/eigen/Eigen/src/Core/PlainObjectBase.h @@ -577,6 +577,10 @@ class PlainObjectBase : public internal::dense_xpr_base::type * while the AlignedMap() functions return aligned Map objects and thus should be called only with 16-byte-aligned * \a data pointers. * + * Here is an example using strides: + * \include Matrix_Map_stride.cpp + * Output: \verbinclude Matrix_Map_stride.out + * * \see class Map */ //@{ diff --git a/xs/src/eigen/Eigen/src/Core/Product.h b/src/eigen/Eigen/src/Core/Product.h similarity index 94% rename from xs/src/eigen/Eigen/src/Core/Product.h rename to src/eigen/Eigen/src/Core/Product.h index ae0c94b38e..676c480277 100644 --- a/xs/src/eigen/Eigen/src/Core/Product.h +++ b/src/eigen/Eigen/src/Core/Product.h @@ -97,8 +97,8 @@ class Product : public ProductImpl<_Lhs,_Rhs,Option, && "if you wanted a coeff-wise or a dot product use the respective explicit functions"); } - EIGEN_DEVICE_FUNC inline Index rows() const { return m_lhs.rows(); } - EIGEN_DEVICE_FUNC inline Index cols() const { return m_rhs.cols(); } + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index rows() const { return m_lhs.rows(); } + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index cols() const { return m_rhs.cols(); } EIGEN_DEVICE_FUNC const LhsNestedCleaned& lhs() const { return m_lhs; } EIGEN_DEVICE_FUNC const RhsNestedCleaned& rhs() const { return m_rhs; } @@ -127,7 +127,7 @@ public: using Base::derived; typedef typename Base::Scalar Scalar; - operator const Scalar() const + EIGEN_STRONG_INLINE operator const Scalar() const { return internal::evaluator(derived()).coeff(0,0); } @@ -162,7 +162,7 @@ class ProductImpl public: - EIGEN_DEVICE_FUNC Scalar coeff(Index row, Index col) const + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar coeff(Index row, Index col) const { EIGEN_STATIC_ASSERT(EnableCoeff, THIS_METHOD_IS_ONLY_FOR_INNER_OR_LAZY_PRODUCTS); eigen_assert( (Option==LazyProduct) || (this->rows() == 1 && this->cols() == 1) ); @@ -170,7 +170,7 @@ class ProductImpl return internal::evaluator(derived()).coeff(row,col); } - EIGEN_DEVICE_FUNC Scalar coeff(Index i) const + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar coeff(Index i) const { EIGEN_STATIC_ASSERT(EnableCoeff, THIS_METHOD_IS_ONLY_FOR_INNER_OR_LAZY_PRODUCTS); eigen_assert( (Option==LazyProduct) || (this->rows() == 1 && this->cols() == 1) ); diff --git a/xs/src/eigen/Eigen/src/Core/ProductEvaluators.h b/src/eigen/Eigen/src/Core/ProductEvaluators.h similarity index 96% rename from xs/src/eigen/Eigen/src/Core/ProductEvaluators.h rename to src/eigen/Eigen/src/Core/ProductEvaluators.h index 583b7f59e3..9b99bd7696 100644 --- a/xs/src/eigen/Eigen/src/Core/ProductEvaluators.h +++ b/src/eigen/Eigen/src/Core/ProductEvaluators.h @@ -32,7 +32,7 @@ struct evaluator > typedef Product XprType; typedef product_evaluator Base; - EIGEN_DEVICE_FUNC explicit evaluator(const XprType& xpr) : Base(xpr) {} + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit evaluator(const XprType& xpr) : Base(xpr) {} }; // Catch "scalar * ( A * B )" and transform it to "(A*scalar) * B" @@ -55,7 +55,7 @@ struct evaluator, const Product > XprType; typedef evaluator > Base; - EIGEN_DEVICE_FUNC explicit evaluator(const XprType& xpr) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit evaluator(const XprType& xpr) : Base(xpr.lhs().functor().m_other * xpr.rhs().lhs() * xpr.rhs().rhs()) {} }; @@ -68,7 +68,7 @@ struct evaluator, DiagIndex> > typedef Diagonal, DiagIndex> XprType; typedef evaluator, DiagIndex> > Base; - EIGEN_DEVICE_FUNC explicit evaluator(const XprType& xpr) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit evaluator(const XprType& xpr) : Base(Diagonal, DiagIndex>( Product(xpr.nestedExpression().lhs(), xpr.nestedExpression().rhs()), xpr.index() )) @@ -207,6 +207,12 @@ struct evaluator_assume_aliasing +struct evaluator_assume_aliasing::Scalar>, const OtherXpr, + const Product >, DenseShape > { + static const bool value = true; +}; + template struct assignment_from_xpr_op_product { @@ -240,19 +246,19 @@ template struct generic_product_impl { template - static inline void evalTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) + static EIGEN_STRONG_INLINE void evalTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) { dst.coeffRef(0,0) = (lhs.transpose().cwiseProduct(rhs)).sum(); } template - static inline void addTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) + static EIGEN_STRONG_INLINE void addTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) { dst.coeffRef(0,0) += (lhs.transpose().cwiseProduct(rhs)).sum(); } template - static void subTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) + static EIGEN_STRONG_INLINE void subTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) { dst.coeffRef(0,0) -= (lhs.transpose().cwiseProduct(rhs)).sum(); } }; @@ -306,25 +312,25 @@ struct generic_product_impl }; template - static inline void evalTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) + static EIGEN_STRONG_INLINE void evalTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) { internal::outer_product_selector_run(dst, lhs, rhs, set(), is_row_major()); } template - static inline void addTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) + static EIGEN_STRONG_INLINE void addTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) { internal::outer_product_selector_run(dst, lhs, rhs, add(), is_row_major()); } template - static inline void subTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) + static EIGEN_STRONG_INLINE void subTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) { internal::outer_product_selector_run(dst, lhs, rhs, sub(), is_row_major()); } template - static inline void scaleAndAddTo(Dst& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) + static EIGEN_STRONG_INLINE void scaleAndAddTo(Dst& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) { internal::outer_product_selector_run(dst, lhs, rhs, adds(alpha), is_row_major()); } @@ -779,7 +785,11 @@ public: _Vectorizable = bool(int(MatrixFlags)&PacketAccessBit) && _SameTypes && (_ScalarAccessOnDiag || (bool(int(DiagFlags)&PacketAccessBit))), _LinearAccessMask = (MatrixType::RowsAtCompileTime==1 || MatrixType::ColsAtCompileTime==1) ? LinearAccessBit : 0, Flags = ((HereditaryBits|_LinearAccessMask) & (unsigned int)(MatrixFlags)) | (_Vectorizable ? PacketAccessBit : 0), - Alignment = evaluator::Alignment + Alignment = evaluator::Alignment, + + AsScalarProduct = (DiagonalType::SizeAtCompileTime==1) + || (DiagonalType::SizeAtCompileTime==Dynamic && MatrixType::RowsAtCompileTime==1 && ProductOrder==OnTheLeft) + || (DiagonalType::SizeAtCompileTime==Dynamic && MatrixType::ColsAtCompileTime==1 && ProductOrder==OnTheRight) }; diagonal_product_evaluator_base(const MatrixType &mat, const DiagonalType &diag) @@ -791,7 +801,10 @@ public: EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar coeff(Index idx) const { - return m_diagImpl.coeff(idx) * m_matImpl.coeff(idx); + if(AsScalarProduct) + return m_diagImpl.coeff(0) * m_matImpl.coeff(idx); + else + return m_diagImpl.coeff(idx) * m_matImpl.coeff(idx); } protected: diff --git a/xs/src/eigen/Eigen/src/Core/Random.h b/src/eigen/Eigen/src/Core/Random.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Random.h rename to src/eigen/Eigen/src/Core/Random.h diff --git a/xs/src/eigen/Eigen/src/Core/Redux.h b/src/eigen/Eigen/src/Core/Redux.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/Redux.h rename to src/eigen/Eigen/src/Core/Redux.h index b6e8f88870..760e9f8615 100644 --- a/xs/src/eigen/Eigen/src/Core/Redux.h +++ b/src/eigen/Eigen/src/Core/Redux.h @@ -407,7 +407,7 @@ protected: */ template template -typename internal::traits::Scalar +EIGEN_STRONG_INLINE typename internal::traits::Scalar DenseBase::redux(const Func& func) const { eigen_assert(this->rows()>0 && this->cols()>0 && "you are using an empty matrix"); diff --git a/xs/src/eigen/Eigen/src/Core/Ref.h b/src/eigen/Eigen/src/Core/Ref.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/Ref.h rename to src/eigen/Eigen/src/Core/Ref.h index bdf24f52ad..9c6e3c5d9b 100644 --- a/xs/src/eigen/Eigen/src/Core/Ref.h +++ b/src/eigen/Eigen/src/Core/Ref.h @@ -95,6 +95,8 @@ protected: template EIGEN_DEVICE_FUNC void construct(Expression& expr) { + EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(PlainObjectType,Expression); + if(PlainObjectType::RowsAtCompileTime==1) { eigen_assert(expr.rows()==1 || expr.cols()==1); diff --git a/xs/src/eigen/Eigen/src/Core/Replicate.h b/src/eigen/Eigen/src/Core/Replicate.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Replicate.h rename to src/eigen/Eigen/src/Core/Replicate.h diff --git a/xs/src/eigen/Eigen/src/Core/ReturnByValue.h b/src/eigen/Eigen/src/Core/ReturnByValue.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/ReturnByValue.h rename to src/eigen/Eigen/src/Core/ReturnByValue.h diff --git a/xs/src/eigen/Eigen/src/Core/Reverse.h b/src/eigen/Eigen/src/Core/Reverse.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Reverse.h rename to src/eigen/Eigen/src/Core/Reverse.h diff --git a/xs/src/eigen/Eigen/src/Core/Select.h b/src/eigen/Eigen/src/Core/Select.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Select.h rename to src/eigen/Eigen/src/Core/Select.h diff --git a/xs/src/eigen/Eigen/src/Core/SelfAdjointView.h b/src/eigen/Eigen/src/Core/SelfAdjointView.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/SelfAdjointView.h rename to src/eigen/Eigen/src/Core/SelfAdjointView.h index 504c98f0ec..b2e51f37ac 100644 --- a/xs/src/eigen/Eigen/src/Core/SelfAdjointView.h +++ b/src/eigen/Eigen/src/Core/SelfAdjointView.h @@ -71,7 +71,9 @@ template class SelfAdjointView EIGEN_DEVICE_FUNC explicit inline SelfAdjointView(MatrixType& matrix) : m_matrix(matrix) - {} + { + EIGEN_STATIC_ASSERT(UpLo==Lower || UpLo==Upper,SELFADJOINTVIEW_ACCEPTS_UPPER_AND_LOWER_MODE_ONLY); + } EIGEN_DEVICE_FUNC inline Index rows() const { return m_matrix.rows(); } @@ -189,7 +191,7 @@ template class SelfAdjointView TriangularView >::type(tmp2); } - typedef SelfAdjointView ConjugateReturnType; + typedef SelfAdjointView ConjugateReturnType; /** \sa MatrixBase::conjugate() const */ EIGEN_DEVICE_FUNC inline const ConjugateReturnType conjugate() const diff --git a/xs/src/eigen/Eigen/src/Core/SelfCwiseBinaryOp.h b/src/eigen/Eigen/src/Core/SelfCwiseBinaryOp.h similarity index 70% rename from xs/src/eigen/Eigen/src/Core/SelfCwiseBinaryOp.h rename to src/eigen/Eigen/src/Core/SelfCwiseBinaryOp.h index 719ed72a52..7c89c2e23c 100644 --- a/xs/src/eigen/Eigen/src/Core/SelfCwiseBinaryOp.h +++ b/src/eigen/Eigen/src/Core/SelfCwiseBinaryOp.h @@ -15,33 +15,29 @@ namespace Eigen { // TODO generalize the scalar type of 'other' template -EIGEN_STRONG_INLINE Derived& DenseBase::operator*=(const Scalar& other) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& DenseBase::operator*=(const Scalar& other) { - typedef typename Derived::PlainObject PlainObject; internal::call_assignment(this->derived(), PlainObject::Constant(rows(),cols(),other), internal::mul_assign_op()); return derived(); } template -EIGEN_STRONG_INLINE Derived& ArrayBase::operator+=(const Scalar& other) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& ArrayBase::operator+=(const Scalar& other) { - typedef typename Derived::PlainObject PlainObject; internal::call_assignment(this->derived(), PlainObject::Constant(rows(),cols(),other), internal::add_assign_op()); return derived(); } template -EIGEN_STRONG_INLINE Derived& ArrayBase::operator-=(const Scalar& other) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& ArrayBase::operator-=(const Scalar& other) { - typedef typename Derived::PlainObject PlainObject; internal::call_assignment(this->derived(), PlainObject::Constant(rows(),cols(),other), internal::sub_assign_op()); return derived(); } template -EIGEN_STRONG_INLINE Derived& DenseBase::operator/=(const Scalar& other) +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& DenseBase::operator/=(const Scalar& other) { - typedef typename Derived::PlainObject PlainObject; internal::call_assignment(this->derived(), PlainObject::Constant(rows(),cols(),other), internal::div_assign_op()); return derived(); } diff --git a/xs/src/eigen/Eigen/src/Core/Solve.h b/src/eigen/Eigen/src/Core/Solve.h similarity index 97% rename from xs/src/eigen/Eigen/src/Core/Solve.h rename to src/eigen/Eigen/src/Core/Solve.h index 960a585970..a8daea5113 100644 --- a/xs/src/eigen/Eigen/src/Core/Solve.h +++ b/src/eigen/Eigen/src/Core/Solve.h @@ -34,12 +34,12 @@ template struct s template struct solve_traits { - typedef Matrix PlainObject; + RhsType::MaxColsAtCompileTime>::type PlainObject; }; template diff --git a/xs/src/eigen/Eigen/src/Core/SolveTriangular.h b/src/eigen/Eigen/src/Core/SolveTriangular.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/SolveTriangular.h rename to src/eigen/Eigen/src/Core/SolveTriangular.h diff --git a/xs/src/eigen/Eigen/src/Core/SolverBase.h b/src/eigen/Eigen/src/Core/SolverBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/SolverBase.h rename to src/eigen/Eigen/src/Core/SolverBase.h diff --git a/xs/src/eigen/Eigen/src/Core/StableNorm.h b/src/eigen/Eigen/src/Core/StableNorm.h similarity index 97% rename from xs/src/eigen/Eigen/src/Core/StableNorm.h rename to src/eigen/Eigen/src/Core/StableNorm.h index d2fe1e1998..88c8d98902 100644 --- a/xs/src/eigen/Eigen/src/Core/StableNorm.h +++ b/src/eigen/Eigen/src/Core/StableNorm.h @@ -165,12 +165,13 @@ MatrixBase::stableNorm() const typedef typename internal::nested_eval::type DerivedCopy; typedef typename internal::remove_all::type DerivedCopyClean; - DerivedCopy copy(derived()); + const DerivedCopy copy(derived()); enum { CanAlign = ( (int(DerivedCopyClean::Flags)&DirectAccessBit) || (int(internal::evaluator::Alignment)>0) // FIXME Alignment)>0 might not be enough - ) && (blockSize*sizeof(Scalar)*20) // if we cannot allocate on the stack, then let's not bother about this optimization }; typedef typename internal::conditional, internal::evaluator::Alignment>, typename DerivedCopyClean::ConstSegmentReturnType>::type SegmentWrapper; diff --git a/xs/src/eigen/Eigen/src/Core/Stride.h b/src/eigen/Eigen/src/Core/Stride.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Stride.h rename to src/eigen/Eigen/src/Core/Stride.h diff --git a/xs/src/eigen/Eigen/src/Core/Swap.h b/src/eigen/Eigen/src/Core/Swap.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Swap.h rename to src/eigen/Eigen/src/Core/Swap.h diff --git a/xs/src/eigen/Eigen/src/Core/Transpose.h b/src/eigen/Eigen/src/Core/Transpose.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Transpose.h rename to src/eigen/Eigen/src/Core/Transpose.h diff --git a/xs/src/eigen/Eigen/src/Core/Transpositions.h b/src/eigen/Eigen/src/Core/Transpositions.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/Transpositions.h rename to src/eigen/Eigen/src/Core/Transpositions.h index 19c17bb4a6..86da5af593 100644 --- a/xs/src/eigen/Eigen/src/Core/Transpositions.h +++ b/src/eigen/Eigen/src/Core/Transpositions.h @@ -384,7 +384,7 @@ class Transpose > const Product operator*(const MatrixBase& matrix, const Transpose& trt) { - return Product(matrix.derived(), trt.derived()); + return Product(matrix.derived(), trt); } /** \returns the \a matrix with the inverse transpositions applied to the rows. diff --git a/xs/src/eigen/Eigen/src/Core/TriangularMatrix.h b/src/eigen/Eigen/src/Core/TriangularMatrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/TriangularMatrix.h rename to src/eigen/Eigen/src/Core/TriangularMatrix.h diff --git a/xs/src/eigen/Eigen/src/Core/VectorBlock.h b/src/eigen/Eigen/src/Core/VectorBlock.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/VectorBlock.h rename to src/eigen/Eigen/src/Core/VectorBlock.h diff --git a/xs/src/eigen/Eigen/src/Core/VectorwiseOp.h b/src/eigen/Eigen/src/Core/VectorwiseOp.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/VectorwiseOp.h rename to src/eigen/Eigen/src/Core/VectorwiseOp.h diff --git a/xs/src/eigen/Eigen/src/Core/Visitor.h b/src/eigen/Eigen/src/Core/Visitor.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Visitor.h rename to src/eigen/Eigen/src/Core/Visitor.h diff --git a/xs/src/eigen/Eigen/src/Core/arch/AVX/Complex.h b/src/eigen/Eigen/src/Core/arch/AVX/Complex.h similarity index 92% rename from xs/src/eigen/Eigen/src/Core/arch/AVX/Complex.h rename to src/eigen/Eigen/src/Core/arch/AVX/Complex.h index 99439c8aa1..7fa61969dc 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/AVX/Complex.h +++ b/src/eigen/Eigen/src/Core/arch/AVX/Complex.h @@ -204,23 +204,7 @@ template<> struct conj_helper } }; -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet4cf pmadd(const Packet8f& x, const Packet4cf& y, const Packet4cf& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet4cf pmul(const Packet8f& x, const Packet4cf& y) const - { return Packet4cf(Eigen::internal::pmul(x, y.v)); } -}; - -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet4cf pmadd(const Packet4cf& x, const Packet8f& y, const Packet4cf& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet4cf pmul(const Packet4cf& x, const Packet8f& y) const - { return Packet4cf(Eigen::internal::pmul(x.v, y)); } -}; +EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(Packet4cf,Packet8f) template<> EIGEN_STRONG_INLINE Packet4cf pdiv(const Packet4cf& a, const Packet4cf& b) { @@ -400,23 +384,7 @@ template<> struct conj_helper } }; -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet2cd pmadd(const Packet4d& x, const Packet2cd& y, const Packet2cd& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet2cd pmul(const Packet4d& x, const Packet2cd& y) const - { return Packet2cd(Eigen::internal::pmul(x, y.v)); } -}; - -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet2cd pmadd(const Packet2cd& x, const Packet4d& y, const Packet2cd& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet2cd pmul(const Packet2cd& x, const Packet4d& y) const - { return Packet2cd(Eigen::internal::pmul(x.v, y)); } -}; +EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(Packet2cd,Packet4d) template<> EIGEN_STRONG_INLINE Packet2cd pdiv(const Packet2cd& a, const Packet2cd& b) { diff --git a/xs/src/eigen/Eigen/src/Core/arch/AVX/MathFunctions.h b/src/eigen/Eigen/src/Core/arch/AVX/MathFunctions.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/AVX/MathFunctions.h rename to src/eigen/Eigen/src/Core/arch/AVX/MathFunctions.h diff --git a/xs/src/eigen/Eigen/src/Core/arch/AVX/PacketMath.h b/src/eigen/Eigen/src/Core/arch/AVX/PacketMath.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/arch/AVX/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/AVX/PacketMath.h index 195d40fb4c..61c3dfcab8 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/AVX/PacketMath.h +++ b/src/eigen/Eigen/src/Core/arch/AVX/PacketMath.h @@ -308,9 +308,9 @@ template<> EIGEN_STRONG_INLINE void pstore1(int* to, const int& a) } #ifndef EIGEN_VECTORIZE_AVX512 -template<> EIGEN_STRONG_INLINE void prefetch(const float* addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } -template<> EIGEN_STRONG_INLINE void prefetch(const double* addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } -template<> EIGEN_STRONG_INLINE void prefetch(const int* addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch(const float* addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch(const double* addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch(const int* addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } #endif template<> EIGEN_STRONG_INLINE float pfirst(const Packet8f& a) { @@ -333,9 +333,12 @@ template<> EIGEN_STRONG_INLINE Packet4d preverse(const Packet4d& a) { __m256d tmp = _mm256_shuffle_pd(a,a,5); return _mm256_permute2f128_pd(tmp, tmp, 1); - + #if 0 + // This version is unlikely to be faster as _mm256_shuffle_ps and _mm256_permute_pd + // exhibit the same latency/throughput, but it is here for future reference/benchmarking... __m256d swap_halves = _mm256_permute2f128_pd(a,a,1); return _mm256_permute_pd(swap_halves,5); + #endif } // pabs should be ok diff --git a/xs/src/eigen/Eigen/src/Core/arch/AVX/TypeCasting.h b/src/eigen/Eigen/src/Core/arch/AVX/TypeCasting.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/AVX/TypeCasting.h rename to src/eigen/Eigen/src/Core/arch/AVX/TypeCasting.h diff --git a/xs/src/eigen/Eigen/src/Core/arch/AVX512/MathFunctions.h b/src/eigen/Eigen/src/Core/arch/AVX512/MathFunctions.h similarity index 93% rename from xs/src/eigen/Eigen/src/Core/arch/AVX512/MathFunctions.h rename to src/eigen/Eigen/src/Core/arch/AVX512/MathFunctions.h index 399be0ee4b..9c1717f76d 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/AVX512/MathFunctions.h +++ b/src/eigen/Eigen/src/Core/arch/AVX512/MathFunctions.h @@ -88,9 +88,9 @@ plog(const Packet16f& _x) { // x = x + x - 1.0; // } else { x = x - 1.0; } __mmask16 mask = _mm512_cmp_ps_mask(x, p16f_cephes_SQRTHF, _CMP_LT_OQ); - Packet16f tmp = _mm512_mask_blend_ps(mask, x, _mm512_setzero_ps()); + Packet16f tmp = _mm512_mask_blend_ps(mask, _mm512_setzero_ps(), x); x = psub(x, p16f_1); - e = psub(e, _mm512_mask_blend_ps(mask, p16f_1, _mm512_setzero_ps())); + e = psub(e, _mm512_mask_blend_ps(mask, _mm512_setzero_ps(), p16f_1)); x = padd(x, tmp); Packet16f x2 = pmul(x, x); @@ -119,8 +119,9 @@ plog(const Packet16f& _x) { x = padd(x, y2); // Filter out invalid inputs, i.e. negative arg will be NAN, 0 will be -INF. - return _mm512_mask_blend_ps(iszero_mask, p16f_minus_inf, - _mm512_mask_blend_ps(invalid_mask, p16f_nan, x)); + return _mm512_mask_blend_ps(iszero_mask, + _mm512_mask_blend_ps(invalid_mask, x, p16f_nan), + p16f_minus_inf); } #endif @@ -266,8 +267,7 @@ psqrt(const Packet16f& _x) { // select only the inverse sqrt of positive normal inputs (denormals are // flushed to zero and cause infs as well). __mmask16 non_zero_mask = _mm512_cmp_ps_mask(_x, p16f_flt_min, _CMP_GE_OQ); - Packet16f x = _mm512_mask_blend_ps(non_zero_mask, _mm512_rsqrt14_ps(_x), - _mm512_setzero_ps()); + Packet16f x = _mm512_mask_blend_ps(non_zero_mask, _mm512_setzero_ps(), _mm512_rsqrt14_ps(_x)); // Do a single step of Newton's iteration. x = pmul(x, pmadd(neg_half, pmul(x, x), p16f_one_point_five)); @@ -289,8 +289,7 @@ psqrt(const Packet8d& _x) { // select only the inverse sqrt of positive normal inputs (denormals are // flushed to zero and cause infs as well). __mmask8 non_zero_mask = _mm512_cmp_pd_mask(_x, p8d_dbl_min, _CMP_GE_OQ); - Packet8d x = _mm512_mask_blend_pd(non_zero_mask, _mm512_rsqrt14_pd(_x), - _mm512_setzero_pd()); + Packet8d x = _mm512_mask_blend_pd(non_zero_mask, _mm512_setzero_pd(), _mm512_rsqrt14_pd(_x)); // Do a first step of Newton's iteration. x = pmul(x, pmadd(neg_half, pmul(x, x), p8d_one_point_five)); @@ -333,20 +332,18 @@ prsqrt(const Packet16f& _x) { // select only the inverse sqrt of positive normal inputs (denormals are // flushed to zero and cause infs as well). __mmask16 le_zero_mask = _mm512_cmp_ps_mask(_x, p16f_flt_min, _CMP_LT_OQ); - Packet16f x = _mm512_mask_blend_ps(le_zero_mask, _mm512_setzero_ps(), - _mm512_rsqrt14_ps(_x)); + Packet16f x = _mm512_mask_blend_ps(le_zero_mask, _mm512_rsqrt14_ps(_x), _mm512_setzero_ps()); // Fill in NaNs and Infs for the negative/zero entries. __mmask16 neg_mask = _mm512_cmp_ps_mask(_x, _mm512_setzero_ps(), _CMP_LT_OQ); Packet16f infs_and_nans = _mm512_mask_blend_ps( - neg_mask, p16f_nan, - _mm512_mask_blend_ps(le_zero_mask, p16f_inf, _mm512_setzero_ps())); + neg_mask, _mm512_mask_blend_ps(le_zero_mask, _mm512_setzero_ps(), p16f_inf), p16f_nan); // Do a single step of Newton's iteration. x = pmul(x, pmadd(neg_half, pmul(x, x), p16f_one_point_five)); // Insert NaNs and Infs in all the right places. - return _mm512_mask_blend_ps(le_zero_mask, infs_and_nans, x); + return _mm512_mask_blend_ps(le_zero_mask, x, infs_and_nans); } template <> @@ -363,14 +360,12 @@ prsqrt(const Packet8d& _x) { // select only the inverse sqrt of positive normal inputs (denormals are // flushed to zero and cause infs as well). __mmask8 le_zero_mask = _mm512_cmp_pd_mask(_x, p8d_dbl_min, _CMP_LT_OQ); - Packet8d x = _mm512_mask_blend_pd(le_zero_mask, _mm512_setzero_pd(), - _mm512_rsqrt14_pd(_x)); + Packet8d x = _mm512_mask_blend_pd(le_zero_mask, _mm512_rsqrt14_pd(_x), _mm512_setzero_pd()); // Fill in NaNs and Infs for the negative/zero entries. __mmask8 neg_mask = _mm512_cmp_pd_mask(_x, _mm512_setzero_pd(), _CMP_LT_OQ); Packet8d infs_and_nans = _mm512_mask_blend_pd( - neg_mask, p8d_nan, - _mm512_mask_blend_pd(le_zero_mask, p8d_inf, _mm512_setzero_pd())); + neg_mask, _mm512_mask_blend_pd(le_zero_mask, _mm512_setzero_pd(), p8d_inf), p8d_nan); // Do a first step of Newton's iteration. x = pmul(x, pmadd(neg_half, pmul(x, x), p8d_one_point_five)); @@ -379,9 +374,9 @@ prsqrt(const Packet8d& _x) { x = pmul(x, pmadd(neg_half, pmul(x, x), p8d_one_point_five)); // Insert NaNs and Infs in all the right places. - return _mm512_mask_blend_pd(le_zero_mask, infs_and_nans, x); + return _mm512_mask_blend_pd(le_zero_mask, x, infs_and_nans); } -#else +#elif defined(EIGEN_VECTORIZE_AVX512ER) template <> EIGEN_STRONG_INLINE Packet16f prsqrt(const Packet16f& x) { return _mm512_rsqrt28_ps(x); diff --git a/xs/src/eigen/Eigen/src/Core/arch/AVX512/PacketMath.h b/src/eigen/Eigen/src/Core/arch/AVX512/PacketMath.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/arch/AVX512/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/AVX512/PacketMath.h index f6500a16e6..89705248aa 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/AVX512/PacketMath.h +++ b/src/eigen/Eigen/src/Core/arch/AVX512/PacketMath.h @@ -618,9 +618,9 @@ EIGEN_STRONG_INLINE void pstore1(int* to, const int& a) { pstore(to, pa); } -template<> EIGEN_STRONG_INLINE void prefetch(const float* addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } -template<> EIGEN_STRONG_INLINE void prefetch(const double* addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } -template<> EIGEN_STRONG_INLINE void prefetch(const int* addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch(const float* addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch(const double* addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch(const int* addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } template <> EIGEN_STRONG_INLINE float pfirst(const Packet16f& a) { diff --git a/xs/src/eigen/Eigen/src/Core/arch/AltiVec/Complex.h b/src/eigen/Eigen/src/Core/arch/AltiVec/Complex.h similarity index 92% rename from xs/src/eigen/Eigen/src/Core/arch/AltiVec/Complex.h rename to src/eigen/Eigen/src/Core/arch/AltiVec/Complex.h index 67db2f8ee9..3e665730cf 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/AltiVec/Complex.h +++ b/src/eigen/Eigen/src/Core/arch/AltiVec/Complex.h @@ -224,23 +224,7 @@ template<> struct conj_helper } }; -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet2cf pmadd(const Packet4f& x, const Packet2cf& y, const Packet2cf& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet2cf pmul(const Packet4f& x, const Packet2cf& y) const - { return Packet2cf(internal::pmul(x, y.v)); } -}; - -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet2cf pmadd(const Packet2cf& x, const Packet4f& y, const Packet2cf& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet2cf pmul(const Packet2cf& x, const Packet4f& y) const - { return Packet2cf(internal::pmul(x.v, y)); } -}; +EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(Packet2cf,Packet4f) template<> EIGEN_STRONG_INLINE Packet2cf pdiv(const Packet2cf& a, const Packet2cf& b) { @@ -416,23 +400,8 @@ template<> struct conj_helper return pconj(internal::pmul(a, b)); } }; -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet1cd pmadd(const Packet2d& x, const Packet1cd& y, const Packet1cd& c) const - { return padd(c, pmul(x,y)); } - EIGEN_STRONG_INLINE Packet1cd pmul(const Packet2d& x, const Packet1cd& y) const - { return Packet1cd(internal::pmul(x, y.v)); } -}; - -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet1cd pmadd(const Packet1cd& x, const Packet2d& y, const Packet1cd& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet1cd pmul(const Packet1cd& x, const Packet2d& y) const - { return Packet1cd(internal::pmul(x.v, y)); } -}; +EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(Packet1cd,Packet2d) template<> EIGEN_STRONG_INLINE Packet1cd pdiv(const Packet1cd& a, const Packet1cd& b) { diff --git a/xs/src/eigen/Eigen/src/Core/arch/AltiVec/MathFunctions.h b/src/eigen/Eigen/src/Core/arch/AltiVec/MathFunctions.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/AltiVec/MathFunctions.h rename to src/eigen/Eigen/src/Core/arch/AltiVec/MathFunctions.h diff --git a/xs/src/eigen/Eigen/src/Core/arch/AltiVec/PacketMath.h b/src/eigen/Eigen/src/Core/arch/AltiVec/PacketMath.h similarity index 97% rename from xs/src/eigen/Eigen/src/Core/arch/AltiVec/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/AltiVec/PacketMath.h index b3f1ea199c..08a27d1530 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/AltiVec/PacketMath.h +++ b/src/eigen/Eigen/src/Core/arch/AltiVec/PacketMath.h @@ -103,7 +103,7 @@ static Packet16uc p16uc_PSET32_WODD = vec_sld((Packet16uc) vec_splat((Packet4u static Packet16uc p16uc_PSET32_WEVEN = vec_sld(p16uc_DUPLICATE32_HI, (Packet16uc) vec_splat((Packet4ui)p16uc_FORWARD, 3), 8);//{ 4,5,6,7, 4,5,6,7, 12,13,14,15, 12,13,14,15 }; static Packet16uc p16uc_HALF64_0_16 = vec_sld((Packet16uc)p4i_ZERO, vec_splat((Packet16uc) vec_abs(p4i_MINUS16), 3), 8); //{ 0,0,0,0, 0,0,0,0, 16,16,16,16, 16,16,16,16}; #else -static Packet16uc p16uc_FORWARD = p16uc_REVERSE32; +static Packet16uc p16uc_FORWARD = p16uc_REVERSE32; static Packet16uc p16uc_REVERSE64 = { 8,9,10,11, 12,13,14,15, 0,1,2,3, 4,5,6,7 }; static Packet16uc p16uc_PSET32_WODD = vec_sld((Packet16uc) vec_splat((Packet4ui)p16uc_FORWARD, 1), (Packet16uc) vec_splat((Packet4ui)p16uc_FORWARD, 3), 8);//{ 0,1,2,3, 0,1,2,3, 8,9,10,11, 8,9,10,11 }; static Packet16uc p16uc_PSET32_WEVEN = vec_sld((Packet16uc) vec_splat((Packet4ui)p16uc_FORWARD, 0), (Packet16uc) vec_splat((Packet4ui)p16uc_FORWARD, 2), 8);//{ 4,5,6,7, 4,5,6,7, 12,13,14,15, 12,13,14,15 }; @@ -388,10 +388,28 @@ template<> EIGEN_STRONG_INLINE Packet4i pdiv(const Packet4i& /*a*/, co template<> EIGEN_STRONG_INLINE Packet4f pmadd(const Packet4f& a, const Packet4f& b, const Packet4f& c) { return vec_madd(a,b,c); } template<> EIGEN_STRONG_INLINE Packet4i pmadd(const Packet4i& a, const Packet4i& b, const Packet4i& c) { return a*b + c; } -template<> EIGEN_STRONG_INLINE Packet4f pmin(const Packet4f& a, const Packet4f& b) { return vec_min(a, b); } +template<> EIGEN_STRONG_INLINE Packet4f pmin(const Packet4f& a, const Packet4f& b) +{ + #ifdef __VSX__ + Packet4f ret; + __asm__ ("xvcmpgesp %x0,%x1,%x2\n\txxsel %x0,%x1,%x2,%x0" : "=&wa" (ret) : "wa" (a), "wa" (b)); + return ret; + #else + return vec_min(a, b); + #endif +} template<> EIGEN_STRONG_INLINE Packet4i pmin(const Packet4i& a, const Packet4i& b) { return vec_min(a, b); } -template<> EIGEN_STRONG_INLINE Packet4f pmax(const Packet4f& a, const Packet4f& b) { return vec_max(a, b); } +template<> EIGEN_STRONG_INLINE Packet4f pmax(const Packet4f& a, const Packet4f& b) +{ + #ifdef __VSX__ + Packet4f ret; + __asm__ ("xvcmpgtsp %x0,%x2,%x1\n\txxsel %x0,%x1,%x2,%x0" : "=&wa" (ret) : "wa" (a), "wa" (b)); + return ret; + #else + return vec_max(a, b); + #endif +} template<> EIGEN_STRONG_INLINE Packet4i pmax(const Packet4i& a, const Packet4i& b) { return vec_max(a, b); } template<> EIGEN_STRONG_INLINE Packet4f pand(const Packet4f& a, const Packet4f& b) { return vec_and(a, b); } @@ -764,7 +782,7 @@ typedef __vector __bool long Packet2bl; static Packet2l p2l_ONE = { 1, 1 }; static Packet2l p2l_ZERO = reinterpret_cast(p4i_ZERO); -static Packet2d p2d_ONE = { 1.0, 1.0 }; +static Packet2d p2d_ONE = { 1.0, 1.0 }; static Packet2d p2d_ZERO = reinterpret_cast(p4f_ZERO); static Packet2d p2d_MZERO = { -0.0, -0.0 }; @@ -910,9 +928,19 @@ template<> EIGEN_STRONG_INLINE Packet2d pdiv(const Packet2d& a, const // for some weird raisons, it has to be overloaded for packet of integers template<> EIGEN_STRONG_INLINE Packet2d pmadd(const Packet2d& a, const Packet2d& b, const Packet2d& c) { return vec_madd(a, b, c); } -template<> EIGEN_STRONG_INLINE Packet2d pmin(const Packet2d& a, const Packet2d& b) { return vec_min(a, b); } +template<> EIGEN_STRONG_INLINE Packet2d pmin(const Packet2d& a, const Packet2d& b) +{ + Packet2d ret; + __asm__ ("xvcmpgedp %x0,%x1,%x2\n\txxsel %x0,%x1,%x2,%x0" : "=&wa" (ret) : "wa" (a), "wa" (b)); + return ret; + } -template<> EIGEN_STRONG_INLINE Packet2d pmax(const Packet2d& a, const Packet2d& b) { return vec_max(a, b); } +template<> EIGEN_STRONG_INLINE Packet2d pmax(const Packet2d& a, const Packet2d& b) +{ + Packet2d ret; + __asm__ ("xvcmpgtdp %x0,%x2,%x1\n\txxsel %x0,%x1,%x2,%x0" : "=&wa" (ret) : "wa" (a), "wa" (b)); + return ret; +} template<> EIGEN_STRONG_INLINE Packet2d pand(const Packet2d& a, const Packet2d& b) { return vec_and(a, b); } @@ -969,7 +997,7 @@ template<> EIGEN_STRONG_INLINE Packet2d preduxp(const Packet2d* vecs) Packet2d v[2], sum; v[0] = vecs[0] + reinterpret_cast(vec_sld(reinterpret_cast(vecs[0]), reinterpret_cast(vecs[0]), 8)); v[1] = vecs[1] + reinterpret_cast(vec_sld(reinterpret_cast(vecs[1]), reinterpret_cast(vecs[1]), 8)); - + #ifdef _BIG_ENDIAN sum = reinterpret_cast(vec_sld(reinterpret_cast(v[0]), reinterpret_cast(v[1]), 8)); #else @@ -1022,7 +1050,7 @@ ptranspose(PacketBlock& kernel) { template<> EIGEN_STRONG_INLINE Packet2d pblend(const Selector<2>& ifPacket, const Packet2d& thenPacket, const Packet2d& elsePacket) { Packet2l select = { ifPacket.select[0], ifPacket.select[1] }; - Packet2bl mask = vec_cmpeq(reinterpret_cast(select), reinterpret_cast(p2l_ONE)); + Packet2bl mask = reinterpret_cast( vec_cmpeq(reinterpret_cast(select), reinterpret_cast(p2l_ONE)) ); return vec_sel(elsePacket, thenPacket, mask); } #endif // __VSX__ diff --git a/xs/src/eigen/Eigen/src/Core/arch/CUDA/Complex.h b/src/eigen/Eigen/src/Core/arch/CUDA/Complex.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/CUDA/Complex.h rename to src/eigen/Eigen/src/Core/arch/CUDA/Complex.h diff --git a/xs/src/eigen/Eigen/src/Core/arch/CUDA/Half.h b/src/eigen/Eigen/src/Core/arch/CUDA/Half.h similarity index 80% rename from xs/src/eigen/Eigen/src/Core/arch/CUDA/Half.h rename to src/eigen/Eigen/src/Core/arch/CUDA/Half.h index 52892db385..02ac0c23a4 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/CUDA/Half.h +++ b/src/eigen/Eigen/src/Core/arch/CUDA/Half.h @@ -13,7 +13,7 @@ // Redistribution and use in source and binary forms, with or without // modification, are permitted. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// “AS IS†AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, @@ -147,55 +147,55 @@ namespace half_impl { // versions to get the ALU speed increased), but you do save the // conversion steps back and forth. -__device__ half operator + (const half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ half operator + (const half& a, const half& b) { return __hadd(a, b); } -__device__ half operator * (const half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ half operator * (const half& a, const half& b) { return __hmul(a, b); } -__device__ half operator - (const half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ half operator - (const half& a, const half& b) { return __hsub(a, b); } -__device__ half operator / (const half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ half operator / (const half& a, const half& b) { float num = __half2float(a); float denom = __half2float(b); return __float2half(num / denom); } -__device__ half operator - (const half& a) { +EIGEN_STRONG_INLINE __device__ half operator - (const half& a) { return __hneg(a); } -__device__ half& operator += (half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ half& operator += (half& a, const half& b) { a = a + b; return a; } -__device__ half& operator *= (half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ half& operator *= (half& a, const half& b) { a = a * b; return a; } -__device__ half& operator -= (half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ half& operator -= (half& a, const half& b) { a = a - b; return a; } -__device__ half& operator /= (half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ half& operator /= (half& a, const half& b) { a = a / b; return a; } -__device__ bool operator == (const half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ bool operator == (const half& a, const half& b) { return __heq(a, b); } -__device__ bool operator != (const half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ bool operator != (const half& a, const half& b) { return __hne(a, b); } -__device__ bool operator < (const half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ bool operator < (const half& a, const half& b) { return __hlt(a, b); } -__device__ bool operator <= (const half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ bool operator <= (const half& a, const half& b) { return __hle(a, b); } -__device__ bool operator > (const half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ bool operator > (const half& a, const half& b) { return __hgt(a, b); } -__device__ bool operator >= (const half& a, const half& b) { +EIGEN_STRONG_INLINE __device__ bool operator >= (const half& a, const half& b) { return __hge(a, b); } @@ -238,10 +238,10 @@ EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator /= (half& a, const half& b) return a; } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator == (const half& a, const half& b) { - return float(a) == float(b); + return numext::equal_strict(float(a),float(b)); } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator != (const half& a, const half& b) { - return float(a) != float(b); + return numext::not_equal_strict(float(a), float(b)); } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator < (const half& a, const half& b) { return float(a) < float(b); @@ -386,11 +386,15 @@ EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half abs(const half& a) { return result; } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half exp(const half& a) { - return half(::expf(float(a))); +#if EIGEN_CUDACC_VER >= 80000 && defined EIGEN_CUDA_ARCH && EIGEN_CUDA_ARCH >= 530 + return half(hexp(a)); +#else + return half(::expf(float(a))); +#endif } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half log(const half& a) { -#if defined(EIGEN_HAS_CUDA_FP16) && defined __CUDACC_VER__ && __CUDACC_VER__ >= 80000 && defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 530 - return Eigen::half(::hlog(a)); +#if defined(EIGEN_HAS_CUDA_FP16) && EIGEN_CUDACC_VER >= 80000 && defined(EIGEN_CUDA_ARCH) && EIGEN_CUDA_ARCH >= 530 + return half(::hlog(a)); #else return half(::logf(float(a))); #endif @@ -402,7 +406,11 @@ EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half log10(const half& a) { return half(::log10f(float(a))); } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half sqrt(const half& a) { - return half(::sqrtf(float(a))); +#if EIGEN_CUDACC_VER >= 80000 && defined EIGEN_CUDA_ARCH && EIGEN_CUDA_ARCH >= 530 + return half(hsqrt(a)); +#else + return half(::sqrtf(float(a))); +#endif } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half pow(const half& a, const half& b) { return half(::powf(float(a), float(b))); @@ -420,10 +428,18 @@ EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half tanh(const half& a) { return half(::tanhf(float(a))); } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half floor(const half& a) { +#if EIGEN_CUDACC_VER >= 80000 && defined EIGEN_CUDA_ARCH && EIGEN_CUDA_ARCH >= 300 + return half(hfloor(a)); +#else return half(::floorf(float(a))); +#endif } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half ceil(const half& a) { +#if EIGEN_CUDACC_VER >= 80000 && defined EIGEN_CUDA_ARCH && EIGEN_CUDA_ARCH >= 300 + return half(hceil(a)); +#else return half(::ceilf(float(a))); +#endif } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half (min)(const half& a, const half& b) { @@ -474,9 +490,59 @@ template<> struct is_arithmetic { enum { value = true }; }; } // end namespace internal +} // end namespace Eigen + +namespace std { +template<> +struct numeric_limits { + static const bool is_specialized = true; + static const bool is_signed = true; + static const bool is_integer = false; + static const bool is_exact = false; + static const bool has_infinity = true; + static const bool has_quiet_NaN = true; + static const bool has_signaling_NaN = true; + static const float_denorm_style has_denorm = denorm_present; + static const bool has_denorm_loss = false; + static const std::float_round_style round_style = std::round_to_nearest; + static const bool is_iec559 = false; + static const bool is_bounded = false; + static const bool is_modulo = false; + static const int digits = 11; + static const int digits10 = 3; // according to http://half.sourceforge.net/structstd_1_1numeric__limits_3_01half__float_1_1half_01_4.html + static const int max_digits10 = 5; // according to http://half.sourceforge.net/structstd_1_1numeric__limits_3_01half__float_1_1half_01_4.html + static const int radix = 2; + static const int min_exponent = -13; + static const int min_exponent10 = -4; + static const int max_exponent = 16; + static const int max_exponent10 = 4; + static const bool traps = true; + static const bool tinyness_before = false; + + static Eigen::half (min)() { return Eigen::half_impl::raw_uint16_to_half(0x400); } + static Eigen::half lowest() { return Eigen::half_impl::raw_uint16_to_half(0xfbff); } + static Eigen::half (max)() { return Eigen::half_impl::raw_uint16_to_half(0x7bff); } + static Eigen::half epsilon() { return Eigen::half_impl::raw_uint16_to_half(0x0800); } + static Eigen::half round_error() { return Eigen::half(0.5); } + static Eigen::half infinity() { return Eigen::half_impl::raw_uint16_to_half(0x7c00); } + static Eigen::half quiet_NaN() { return Eigen::half_impl::raw_uint16_to_half(0x7e00); } + static Eigen::half signaling_NaN() { return Eigen::half_impl::raw_uint16_to_half(0x7e00); } + static Eigen::half denorm_min() { return Eigen::half_impl::raw_uint16_to_half(0x1); } +}; +} + +namespace Eigen { + template<> struct NumTraits : GenericNumTraits { + enum { + IsSigned = true, + IsInteger = false, + IsComplex = false, + RequireInitialization = false + }; + EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Eigen::half epsilon() { return half_impl::raw_uint16_to_half(0x0800); } @@ -507,7 +573,7 @@ EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC Eigen::half exph(const Eigen::half& a) { return Eigen::half(::expf(float(a))); } EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC Eigen::half logh(const Eigen::half& a) { -#if defined __CUDACC_VER__ && __CUDACC_VER__ >= 80000 && defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 530 +#if EIGEN_CUDACC_VER >= 80000 && defined(EIGEN_CUDA_ARCH) && EIGEN_CUDA_ARCH >= 530 return Eigen::half(::hlog(a)); #else return Eigen::half(::logf(float(a))); diff --git a/xs/src/eigen/Eigen/src/Core/arch/CUDA/MathFunctions.h b/src/eigen/Eigen/src/Core/arch/CUDA/MathFunctions.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/CUDA/MathFunctions.h rename to src/eigen/Eigen/src/Core/arch/CUDA/MathFunctions.h diff --git a/xs/src/eigen/Eigen/src/Core/arch/CUDA/PacketMath.h b/src/eigen/Eigen/src/Core/arch/CUDA/PacketMath.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/arch/CUDA/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/CUDA/PacketMath.h index ad66399e0e..4dda63188d 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/CUDA/PacketMath.h +++ b/src/eigen/Eigen/src/Core/arch/CUDA/PacketMath.h @@ -291,7 +291,7 @@ template<> EIGEN_DEVICE_FUNC inline double2 pabs(const double2& a) { EIGEN_DEVICE_FUNC inline void ptranspose(PacketBlock& kernel) { - double tmp = kernel.packet[0].y; + float tmp = kernel.packet[0].y; kernel.packet[0].y = kernel.packet[1].x; kernel.packet[1].x = tmp; diff --git a/xs/src/eigen/Eigen/src/Core/arch/CUDA/PacketMathHalf.h b/src/eigen/Eigen/src/Core/arch/CUDA/PacketMathHalf.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/arch/CUDA/PacketMathHalf.h rename to src/eigen/Eigen/src/Core/arch/CUDA/PacketMathHalf.h index ae54225f8e..943e0b06d7 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/CUDA/PacketMathHalf.h +++ b/src/eigen/Eigen/src/Core/arch/CUDA/PacketMathHalf.h @@ -275,7 +275,7 @@ template<> __device__ EIGEN_STRONG_INLINE half2 plog1p(const half2& a) { return __floats2half2_rn(r1, r2); } -#if defined __CUDACC_VER__ && __CUDACC_VER__ >= 80000 && defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 530 +#if EIGEN_CUDACC_VER >= 80000 && defined EIGEN_CUDA_ARCH && EIGEN_CUDA_ARCH >= 530 template<> __device__ EIGEN_STRONG_INLINE half2 plog(const half2& a) { diff --git a/xs/src/eigen/Eigen/src/Core/arch/CUDA/TypeCasting.h b/src/eigen/Eigen/src/Core/arch/CUDA/TypeCasting.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/CUDA/TypeCasting.h rename to src/eigen/Eigen/src/Core/arch/CUDA/TypeCasting.h diff --git a/src/eigen/Eigen/src/Core/arch/Default/ConjHelper.h b/src/eigen/Eigen/src/Core/arch/Default/ConjHelper.h new file mode 100644 index 0000000000..4cfe34e052 --- /dev/null +++ b/src/eigen/Eigen/src/Core/arch/Default/ConjHelper.h @@ -0,0 +1,29 @@ + +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2017 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_ARCH_CONJ_HELPER_H +#define EIGEN_ARCH_CONJ_HELPER_H + +#define EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(PACKET_CPLX, PACKET_REAL) \ + template<> struct conj_helper { \ + EIGEN_STRONG_INLINE PACKET_CPLX pmadd(const PACKET_REAL& x, const PACKET_CPLX& y, const PACKET_CPLX& c) const \ + { return padd(c, pmul(x,y)); } \ + EIGEN_STRONG_INLINE PACKET_CPLX pmul(const PACKET_REAL& x, const PACKET_CPLX& y) const \ + { return PACKET_CPLX(Eigen::internal::pmul(x, y.v)); } \ + }; \ + \ + template<> struct conj_helper { \ + EIGEN_STRONG_INLINE PACKET_CPLX pmadd(const PACKET_CPLX& x, const PACKET_REAL& y, const PACKET_CPLX& c) const \ + { return padd(c, pmul(x,y)); } \ + EIGEN_STRONG_INLINE PACKET_CPLX pmul(const PACKET_CPLX& x, const PACKET_REAL& y) const \ + { return PACKET_CPLX(Eigen::internal::pmul(x.v, y)); } \ + }; + +#endif // EIGEN_ARCH_CONJ_HELPER_H diff --git a/xs/src/eigen/Eigen/src/Core/arch/Default/Settings.h b/src/eigen/Eigen/src/Core/arch/Default/Settings.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/Default/Settings.h rename to src/eigen/Eigen/src/Core/arch/Default/Settings.h diff --git a/xs/src/eigen/Eigen/src/Core/arch/NEON/Complex.h b/src/eigen/Eigen/src/Core/arch/NEON/Complex.h similarity index 97% rename from xs/src/eigen/Eigen/src/Core/arch/NEON/Complex.h rename to src/eigen/Eigen/src/Core/arch/NEON/Complex.h index 57e9b431f2..306a309beb 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/NEON/Complex.h +++ b/src/eigen/Eigen/src/Core/arch/NEON/Complex.h @@ -67,7 +67,7 @@ template<> struct unpacket_traits { typedef std::complex type; template<> EIGEN_STRONG_INLINE Packet2cf pset1(const std::complex& from) { float32x2_t r64; - r64 = vld1_f32((float *)&from); + r64 = vld1_f32((const float *)&from); return Packet2cf(vcombine_f32(r64, r64)); } @@ -142,7 +142,7 @@ template<> EIGEN_DEVICE_FUNC inline void pscatter, Packet2cf to[stride*1] = std::complex(vgetq_lane_f32(from.v, 2), vgetq_lane_f32(from.v, 3)); } -template<> EIGEN_STRONG_INLINE void prefetch >(const std::complex * addr) { EIGEN_ARM_PREFETCH((float *)addr); } +template<> EIGEN_STRONG_INLINE void prefetch >(const std::complex * addr) { EIGEN_ARM_PREFETCH((const float *)addr); } template<> EIGEN_STRONG_INLINE std::complex pfirst(const Packet2cf& a) { @@ -265,6 +265,8 @@ template<> struct conj_helper } }; +EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(Packet2cf,Packet4f) + template<> EIGEN_STRONG_INLINE Packet2cf pdiv(const Packet2cf& a, const Packet2cf& b) { // TODO optimize it for NEON @@ -275,7 +277,7 @@ template<> EIGEN_STRONG_INLINE Packet2cf pdiv(const Packet2cf& a, con s = vmulq_f32(b.v, b.v); rev_s = vrev64q_f32(s); - return Packet2cf(pdiv(res.v, vaddq_f32(s,rev_s))); + return Packet2cf(pdiv(res.v, vaddq_f32(s,rev_s))); } EIGEN_DEVICE_FUNC inline void @@ -381,7 +383,7 @@ template<> EIGEN_STRONG_INLINE Packet1cd ploaddup(const std::complex< template<> EIGEN_STRONG_INLINE void pstore >(std::complex * to, const Packet1cd& from) { EIGEN_DEBUG_ALIGNED_STORE pstore((double*)to, from.v); } template<> EIGEN_STRONG_INLINE void pstoreu >(std::complex * to, const Packet1cd& from) { EIGEN_DEBUG_UNALIGNED_STORE pstoreu((double*)to, from.v); } -template<> EIGEN_STRONG_INLINE void prefetch >(const std::complex * addr) { EIGEN_ARM_PREFETCH((double *)addr); } +template<> EIGEN_STRONG_INLINE void prefetch >(const std::complex * addr) { EIGEN_ARM_PREFETCH((const double *)addr); } template<> EIGEN_DEVICE_FUNC inline Packet1cd pgather, Packet1cd>(const std::complex* from, Index stride) { @@ -456,6 +458,8 @@ template<> struct conj_helper } }; +EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(Packet1cd,Packet2d) + template<> EIGEN_STRONG_INLINE Packet1cd pdiv(const Packet1cd& a, const Packet1cd& b) { // TODO optimize it for NEON diff --git a/xs/src/eigen/Eigen/src/Core/arch/NEON/MathFunctions.h b/src/eigen/Eigen/src/Core/arch/NEON/MathFunctions.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/NEON/MathFunctions.h rename to src/eigen/Eigen/src/Core/arch/NEON/MathFunctions.h diff --git a/xs/src/eigen/Eigen/src/Core/arch/NEON/PacketMath.h b/src/eigen/Eigen/src/Core/arch/NEON/PacketMath.h similarity index 95% rename from xs/src/eigen/Eigen/src/Core/arch/NEON/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/NEON/PacketMath.h index 84a56bdcc6..3d5ed0d240 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/NEON/PacketMath.h +++ b/src/eigen/Eigen/src/Core/arch/NEON/PacketMath.h @@ -36,12 +36,43 @@ namespace internal { #endif #endif +#if EIGEN_COMP_MSVC + +// In MSVC's arm_neon.h header file, all NEON vector types +// are aliases to the same underlying type __n128. +// We thus have to wrap them to make them different C++ types. +// (See also bug 1428) + +template +struct eigen_packet_wrapper +{ + operator T&() { return m_val; } + operator const T&() const { return m_val; } + eigen_packet_wrapper() {} + eigen_packet_wrapper(const T &v) : m_val(v) {} + eigen_packet_wrapper& operator=(const T &v) { + m_val = v; + return *this; + } + + T m_val; +}; +typedef eigen_packet_wrapper Packet2f; +typedef eigen_packet_wrapper Packet4f; +typedef eigen_packet_wrapper Packet4i; +typedef eigen_packet_wrapper Packet2i; +typedef eigen_packet_wrapper Packet4ui; + +#else + typedef float32x2_t Packet2f; typedef float32x4_t Packet4f; typedef int32x4_t Packet4i; typedef int32x2_t Packet2i; typedef uint32x4_t Packet4ui; +#endif // EIGEN_COMP_MSVC + #define _EIGEN_DECLARE_CONST_Packet4f(NAME,X) \ const Packet4f p4f_##NAME = pset1(X) @@ -51,14 +82,17 @@ typedef uint32x4_t Packet4ui; #define _EIGEN_DECLARE_CONST_Packet4i(NAME,X) \ const Packet4i p4i_##NAME = pset1(X) -// arm64 does have the pld instruction. If available, let's trust the __builtin_prefetch built-in function -// which available on LLVM and GCC (at least) -#if EIGEN_HAS_BUILTIN(__builtin_prefetch) || EIGEN_COMP_GNUC +#if EIGEN_ARCH_ARM64 + // __builtin_prefetch tends to do nothing on ARM64 compilers because the + // prefetch instructions there are too detailed for __builtin_prefetch to map + // meaningfully to them. + #define EIGEN_ARM_PREFETCH(ADDR) __asm__ __volatile__("prfm pldl1keep, [%[addr]]\n" ::[addr] "r"(ADDR) : ); +#elif EIGEN_HAS_BUILTIN(__builtin_prefetch) || EIGEN_COMP_GNUC #define EIGEN_ARM_PREFETCH(ADDR) __builtin_prefetch(ADDR); #elif defined __pld #define EIGEN_ARM_PREFETCH(ADDR) __pld(ADDR) -#elif !EIGEN_ARCH_ARM64 - #define EIGEN_ARM_PREFETCH(ADDR) __asm__ __volatile__ ( " pld [%[addr]]\n" :: [addr] "r" (ADDR) : "cc" ); +#elif EIGEN_ARCH_ARM32 + #define EIGEN_ARM_PREFETCH(ADDR) __asm__ __volatile__ ("pld [%[addr]]\n" :: [addr] "r" (ADDR) : ); #else // by default no explicit prefetching #define EIGEN_ARM_PREFETCH(ADDR) @@ -113,7 +147,7 @@ template<> EIGEN_STRONG_INLINE Packet4i pset1(const int32_t& from) template<> EIGEN_STRONG_INLINE Packet4f plset(const float& a) { - const float32_t f[] = {0, 1, 2, 3}; + const float f[] = {0, 1, 2, 3}; Packet4f countdown = vld1q_f32(f); return vaddq_f32(pset1(a), countdown); } diff --git a/xs/src/eigen/Eigen/src/Core/arch/SSE/Complex.h b/src/eigen/Eigen/src/Core/arch/SSE/Complex.h similarity index 92% rename from xs/src/eigen/Eigen/src/Core/arch/SSE/Complex.h rename to src/eigen/Eigen/src/Core/arch/SSE/Complex.h index 5607fe0abb..d075043ce1 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/SSE/Complex.h +++ b/src/eigen/Eigen/src/Core/arch/SSE/Complex.h @@ -128,7 +128,7 @@ template<> EIGEN_DEVICE_FUNC inline void pscatter, Packet2cf _mm_cvtss_f32(_mm_shuffle_ps(from.v, from.v, 3))); } -template<> EIGEN_STRONG_INLINE void prefetch >(const std::complex * addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch >(const std::complex * addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } template<> EIGEN_STRONG_INLINE std::complex pfirst(const Packet2cf& a) { @@ -229,23 +229,7 @@ template<> struct conj_helper } }; -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet2cf pmadd(const Packet4f& x, const Packet2cf& y, const Packet2cf& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet2cf pmul(const Packet4f& x, const Packet2cf& y) const - { return Packet2cf(Eigen::internal::pmul(x, y.v)); } -}; - -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet2cf pmadd(const Packet2cf& x, const Packet4f& y, const Packet2cf& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet2cf pmul(const Packet2cf& x, const Packet4f& y) const - { return Packet2cf(Eigen::internal::pmul(x.v, y)); } -}; +EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(Packet2cf,Packet4f) template<> EIGEN_STRONG_INLINE Packet2cf pdiv(const Packet2cf& a, const Packet2cf& b) { @@ -340,7 +324,7 @@ template<> EIGEN_STRONG_INLINE Packet1cd ploaddup(const std::complex< template<> EIGEN_STRONG_INLINE void pstore >(std::complex * to, const Packet1cd& from) { EIGEN_DEBUG_ALIGNED_STORE pstore((double*)to, Packet2d(from.v)); } template<> EIGEN_STRONG_INLINE void pstoreu >(std::complex * to, const Packet1cd& from) { EIGEN_DEBUG_UNALIGNED_STORE pstoreu((double*)to, Packet2d(from.v)); } -template<> EIGEN_STRONG_INLINE void prefetch >(const std::complex * addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch >(const std::complex * addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } template<> EIGEN_STRONG_INLINE std::complex pfirst(const Packet1cd& a) { @@ -430,23 +414,7 @@ template<> struct conj_helper } }; -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet1cd pmadd(const Packet2d& x, const Packet1cd& y, const Packet1cd& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet1cd pmul(const Packet2d& x, const Packet1cd& y) const - { return Packet1cd(Eigen::internal::pmul(x, y.v)); } -}; - -template<> struct conj_helper -{ - EIGEN_STRONG_INLINE Packet1cd pmadd(const Packet1cd& x, const Packet2d& y, const Packet1cd& c) const - { return padd(c, pmul(x,y)); } - - EIGEN_STRONG_INLINE Packet1cd pmul(const Packet1cd& x, const Packet2d& y) const - { return Packet1cd(Eigen::internal::pmul(x.v, y)); } -}; +EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(Packet1cd,Packet2d) template<> EIGEN_STRONG_INLINE Packet1cd pdiv(const Packet1cd& a, const Packet1cd& b) { diff --git a/xs/src/eigen/Eigen/src/Core/arch/SSE/MathFunctions.h b/src/eigen/Eigen/src/Core/arch/SSE/MathFunctions.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/SSE/MathFunctions.h rename to src/eigen/Eigen/src/Core/arch/SSE/MathFunctions.h diff --git a/xs/src/eigen/Eigen/src/Core/arch/SSE/PacketMath.h b/src/eigen/Eigen/src/Core/arch/SSE/PacketMath.h similarity index 97% rename from xs/src/eigen/Eigen/src/Core/arch/SSE/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/SSE/PacketMath.h index 3832de147b..5e652cc790 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/SSE/PacketMath.h +++ b/src/eigen/Eigen/src/Core/arch/SSE/PacketMath.h @@ -409,10 +409,16 @@ template<> EIGEN_STRONG_INLINE void pstore1(double* to, const double& pstore(to, Packet2d(vec2d_swizzle1(pa,0,0))); } +#if EIGEN_COMP_PGI +typedef const void * SsePrefetchPtrType; +#else +typedef const char * SsePrefetchPtrType; +#endif + #ifndef EIGEN_VECTORIZE_AVX -template<> EIGEN_STRONG_INLINE void prefetch(const float* addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } -template<> EIGEN_STRONG_INLINE void prefetch(const double* addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } -template<> EIGEN_STRONG_INLINE void prefetch(const int* addr) { _mm_prefetch((const char*)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch(const float* addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch(const double* addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } +template<> EIGEN_STRONG_INLINE void prefetch(const int* addr) { _mm_prefetch((SsePrefetchPtrType)(addr), _MM_HINT_T0); } #endif #if EIGEN_COMP_MSVC_STRICT && EIGEN_OS_WIN64 @@ -876,4 +882,14 @@ template<> EIGEN_STRONG_INLINE double pmadd(const double& a, const double& b, co } // end namespace Eigen +#if EIGEN_COMP_PGI +// PGI++ does not define the following intrinsics in C++ mode. +static inline __m128 _mm_castpd_ps (__m128d x) { return reinterpret_cast<__m128&>(x); } +static inline __m128i _mm_castpd_si128(__m128d x) { return reinterpret_cast<__m128i&>(x); } +static inline __m128d _mm_castps_pd (__m128 x) { return reinterpret_cast<__m128d&>(x); } +static inline __m128i _mm_castps_si128(__m128 x) { return reinterpret_cast<__m128i&>(x); } +static inline __m128 _mm_castsi128_ps(__m128i x) { return reinterpret_cast<__m128&>(x); } +static inline __m128d _mm_castsi128_pd(__m128i x) { return reinterpret_cast<__m128d&>(x); } +#endif + #endif // EIGEN_PACKET_MATH_SSE_H diff --git a/xs/src/eigen/Eigen/src/Core/arch/SSE/TypeCasting.h b/src/eigen/Eigen/src/Core/arch/SSE/TypeCasting.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/arch/SSE/TypeCasting.h rename to src/eigen/Eigen/src/Core/arch/SSE/TypeCasting.h index c848932306..c6ca8c716c 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/SSE/TypeCasting.h +++ b/src/eigen/Eigen/src/Core/arch/SSE/TypeCasting.h @@ -14,6 +14,7 @@ namespace Eigen { namespace internal { +#ifndef EIGEN_VECTORIZE_AVX template <> struct type_casting_traits { enum { @@ -23,11 +24,6 @@ struct type_casting_traits { }; }; -template<> EIGEN_STRONG_INLINE Packet4i pcast(const Packet4f& a) { - return _mm_cvttps_epi32(a); -} - - template <> struct type_casting_traits { enum { @@ -37,11 +33,6 @@ struct type_casting_traits { }; }; -template<> EIGEN_STRONG_INLINE Packet4f pcast(const Packet4i& a) { - return _mm_cvtepi32_ps(a); -} - - template <> struct type_casting_traits { enum { @@ -51,10 +42,6 @@ struct type_casting_traits { }; }; -template<> EIGEN_STRONG_INLINE Packet4f pcast(const Packet2d& a, const Packet2d& b) { - return _mm_shuffle_ps(_mm_cvtpd_ps(a), _mm_cvtpd_ps(b), (1 << 2) | (1 << 6)); -} - template <> struct type_casting_traits { enum { @@ -63,6 +50,19 @@ struct type_casting_traits { TgtCoeffRatio = 2 }; }; +#endif + +template<> EIGEN_STRONG_INLINE Packet4i pcast(const Packet4f& a) { + return _mm_cvttps_epi32(a); +} + +template<> EIGEN_STRONG_INLINE Packet4f pcast(const Packet4i& a) { + return _mm_cvtepi32_ps(a); +} + +template<> EIGEN_STRONG_INLINE Packet4f pcast(const Packet2d& a, const Packet2d& b) { + return _mm_shuffle_ps(_mm_cvtpd_ps(a), _mm_cvtpd_ps(b), (1 << 2) | (1 << 6)); +} template<> EIGEN_STRONG_INLINE Packet2d pcast(const Packet4f& a) { // Simply discard the second half of the input diff --git a/xs/src/eigen/Eigen/src/Core/arch/ZVector/Complex.h b/src/eigen/Eigen/src/Core/arch/ZVector/Complex.h similarity index 99% rename from xs/src/eigen/Eigen/src/Core/arch/ZVector/Complex.h rename to src/eigen/Eigen/src/Core/arch/ZVector/Complex.h index d39d2d105e..1bfb73397d 100644 --- a/xs/src/eigen/Eigen/src/Core/arch/ZVector/Complex.h +++ b/src/eigen/Eigen/src/Core/arch/ZVector/Complex.h @@ -336,6 +336,9 @@ template<> struct conj_helper } }; +EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(Packet2cf,Packet4f) +EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(Packet1cd,Packet2d) + template<> EIGEN_STRONG_INLINE Packet1cd pdiv(const Packet1cd& a, const Packet1cd& b) { // TODO optimize it for AltiVec diff --git a/xs/src/eigen/Eigen/src/Core/arch/ZVector/MathFunctions.h b/src/eigen/Eigen/src/Core/arch/ZVector/MathFunctions.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/ZVector/MathFunctions.h rename to src/eigen/Eigen/src/Core/arch/ZVector/MathFunctions.h diff --git a/xs/src/eigen/Eigen/src/Core/arch/ZVector/PacketMath.h b/src/eigen/Eigen/src/Core/arch/ZVector/PacketMath.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/ZVector/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/ZVector/PacketMath.h diff --git a/xs/src/eigen/Eigen/src/Core/functors/AssignmentFunctors.h b/src/eigen/Eigen/src/Core/functors/AssignmentFunctors.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/functors/AssignmentFunctors.h rename to src/eigen/Eigen/src/Core/functors/AssignmentFunctors.h diff --git a/xs/src/eigen/Eigen/src/Core/functors/BinaryFunctors.h b/src/eigen/Eigen/src/Core/functors/BinaryFunctors.h similarity index 96% rename from xs/src/eigen/Eigen/src/Core/functors/BinaryFunctors.h rename to src/eigen/Eigen/src/Core/functors/BinaryFunctors.h index 96747bac78..3eae6b8cad 100644 --- a/xs/src/eigen/Eigen/src/Core/functors/BinaryFunctors.h +++ b/src/eigen/Eigen/src/Core/functors/BinaryFunctors.h @@ -255,7 +255,7 @@ struct scalar_cmp_op : binary_op_base struct scalar_hypot_op : binary_op_base { EIGEN_EMPTY_STRUCT_CTOR(scalar_hypot_op) -// typedef typename NumTraits::Real result_type; - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar operator() (const Scalar& _x, const Scalar& _y) const + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar operator() (const Scalar &x, const Scalar &y) const { - EIGEN_USING_STD_MATH(sqrt) - Scalar p, qp; - if(_x>_y) - { - p = _x; - qp = _y / p; - } - else - { - p = _y; - qp = _x / p; - } - return p * sqrt(Scalar(1) + qp*qp); + // This functor is used by hypotNorm only for which it is faster to first apply abs + // on all coefficients prior to reduction through hypot. + // This way we avoid calling abs on positive and real entries, and this also permits + // to seamlessly handle complexes. Otherwise we would have to handle both real and complexes + // through the same functor... + return internal::positive_real_hypot(x,y); } }; template diff --git a/xs/src/eigen/Eigen/src/Core/functors/NullaryFunctors.h b/src/eigen/Eigen/src/Core/functors/NullaryFunctors.h similarity index 96% rename from xs/src/eigen/Eigen/src/Core/functors/NullaryFunctors.h rename to src/eigen/Eigen/src/Core/functors/NullaryFunctors.h index 6a30466fb9..b03be0269c 100644 --- a/xs/src/eigen/Eigen/src/Core/functors/NullaryFunctors.h +++ b/src/eigen/Eigen/src/Core/functors/NullaryFunctors.h @@ -44,16 +44,16 @@ struct linspaced_op_impl { linspaced_op_impl(const Scalar& low, const Scalar& high, Index num_steps) : m_low(low), m_high(high), m_size1(num_steps==1 ? 1 : num_steps-1), m_step(num_steps==1 ? Scalar() : (high-low)/Scalar(num_steps-1)), - m_interPacket(plset(0)), m_flip(numext::abs(high) EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar operator() (IndexType i) const { + typedef typename NumTraits::Real RealScalar; if(m_flip) - return (i==0)? m_low : (m_high - (m_size1-i)*m_step); + return (i==0)? m_low : (m_high - RealScalar(m_size1-i)*m_step); else - return (i==m_size1)? m_high : (m_low + i*m_step); + return (i==m_size1)? m_high : (m_low + RealScalar(i)*m_step); } template @@ -63,7 +63,7 @@ struct linspaced_op_impl // [low, ..., low] + ( [step, ..., step] * ( [i, ..., i] + [0, ..., size] ) ) if(m_flip) { - Packet pi = padd(pset1(Scalar(i-m_size1)),m_interPacket); + Packet pi = plset(Scalar(i-m_size1)); Packet res = padd(pset1(m_high), pmul(pset1(m_step), pi)); if(i==0) res = pinsertfirst(res, m_low); @@ -71,7 +71,7 @@ struct linspaced_op_impl } else { - Packet pi = padd(pset1(Scalar(i)),m_interPacket); + Packet pi = plset(Scalar(i)); Packet res = padd(pset1(m_low), pmul(pset1(m_step), pi)); if(i==m_size1-unpacket_traits::size+1) res = pinsertlast(res, m_high); @@ -83,7 +83,6 @@ struct linspaced_op_impl const Scalar m_high; const Index m_size1; const Scalar m_step; - const Packet m_interPacket; const bool m_flip; }; diff --git a/xs/src/eigen/Eigen/src/Core/functors/StlFunctors.h b/src/eigen/Eigen/src/Core/functors/StlFunctors.h similarity index 95% rename from xs/src/eigen/Eigen/src/Core/functors/StlFunctors.h rename to src/eigen/Eigen/src/Core/functors/StlFunctors.h index 6df3fa501b..9c1d75850b 100644 --- a/xs/src/eigen/Eigen/src/Core/functors/StlFunctors.h +++ b/src/eigen/Eigen/src/Core/functors/StlFunctors.h @@ -83,13 +83,17 @@ struct functor_traits > { enum { Cost = functor_traits::Cost, PacketAccess = false }; }; #endif +#if (__cplusplus < 201703L) && (EIGEN_COMP_MSVC < 1910) +// std::unary_negate is deprecated since c++17 and will be removed in c++20 template struct functor_traits > { enum { Cost = 1 + functor_traits::Cost, PacketAccess = false }; }; +// std::binary_negate is deprecated since c++17 and will be removed in c++20 template struct functor_traits > { enum { Cost = 1 + functor_traits::Cost, PacketAccess = false }; }; +#endif #ifdef EIGEN_STDEXT_SUPPORT diff --git a/xs/src/eigen/Eigen/src/Core/functors/TernaryFunctors.h b/src/eigen/Eigen/src/Core/functors/TernaryFunctors.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/functors/TernaryFunctors.h rename to src/eigen/Eigen/src/Core/functors/TernaryFunctors.h diff --git a/xs/src/eigen/Eigen/src/Core/functors/UnaryFunctors.h b/src/eigen/Eigen/src/Core/functors/UnaryFunctors.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/functors/UnaryFunctors.h rename to src/eigen/Eigen/src/Core/functors/UnaryFunctors.h diff --git a/xs/src/eigen/Eigen/src/Core/products/GeneralBlockPanelKernel.h b/src/eigen/Eigen/src/Core/products/GeneralBlockPanelKernel.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/GeneralBlockPanelKernel.h rename to src/eigen/Eigen/src/Core/products/GeneralBlockPanelKernel.h diff --git a/xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrix.h b/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrix.h rename to src/eigen/Eigen/src/Core/products/GeneralMatrixMatrix.h diff --git a/xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h b/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h similarity index 95% rename from xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h rename to src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h index 7122efa605..e844e37d16 100644 --- a/xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h +++ b/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h @@ -269,10 +269,13 @@ struct general_product_to_triangular_selector enum { IsRowMajor = (internal::traits::Flags&RowMajorBit) ? 1 : 0, LhsIsRowMajor = _ActualLhs::Flags&RowMajorBit ? 1 : 0, - RhsIsRowMajor = _ActualRhs::Flags&RowMajorBit ? 1 : 0 + RhsIsRowMajor = _ActualRhs::Flags&RowMajorBit ? 1 : 0, + SkipDiag = (UpLo&(UnitDiag|ZeroDiag))!=0 }; Index size = mat.cols(); + if(SkipDiag) + size--; Index depth = actualLhs.cols(); typedef internal::gemm_blocking_space internal::general_matrix_matrix_triangular_product + IsRowMajor ? RowMajor : ColMajor, UpLo&(Lower|Upper)> ::run(size, depth, - &actualLhs.coeffRef(0,0), actualLhs.outerStride(), &actualRhs.coeffRef(0,0), actualRhs.outerStride(), - mat.data(), mat.outerStride(), actualAlpha, blocking); + &actualLhs.coeffRef(SkipDiag&&(UpLo&Lower)==Lower ? 1 : 0,0), actualLhs.outerStride(), + &actualRhs.coeffRef(0,SkipDiag&&(UpLo&Upper)==Upper ? 1 : 0), actualRhs.outerStride(), + mat.data() + (SkipDiag ? (bool(IsRowMajor) != ((UpLo&Lower)==Lower) ? 1 : mat.outerStride() ) : 0), mat.outerStride(), actualAlpha, blocking); } }; @@ -294,6 +298,7 @@ template template TriangularView& TriangularViewImpl::_assignProduct(const ProductType& prod, const Scalar& alpha, bool beta) { + EIGEN_STATIC_ASSERT((UpLo&UnitDiag)==0, WRITING_TO_TRIANGULAR_PART_WITH_UNIT_DIAGONAL_IS_NOT_SUPPORTED); eigen_assert(derived().nestedExpression().rows() == prod.rows() && derived().cols() == prod.cols()); general_product_to_triangular_selector::InnerSize==1>::run(derived().nestedExpression().const_cast_derived(), prod, alpha, beta); diff --git a/xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular_BLAS.h b/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular_BLAS.h similarity index 95% rename from xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular_BLAS.h rename to src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular_BLAS.h index 5b7c15ccab..9176a13826 100644 --- a/xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular_BLAS.h +++ b/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular_BLAS.h @@ -52,7 +52,7 @@ struct general_matrix_matrix_triangular_product& blocking) \ { \ - if (lhs==rhs) { \ + if ( lhs==rhs && ((UpLo&(Lower|Upper)==UpLo)) ) { \ general_matrix_matrix_rankupdate \ ::run(size,depth,lhs,lhsStride,rhs,rhsStride,res,resStride,alpha,blocking); \ } else { \ @@ -88,7 +88,7 @@ struct general_matrix_matrix_rankupdate(lhsStride), ldc=convert_index(resStride), n=convert_index(size), k=convert_index(depth); \ char uplo=((IsLower) ? 'L' : 'U'), trans=((AStorageOrder==RowMajor) ? 'T':'N'); \ EIGTYPE beta(1); \ - BLASFUNC(&uplo, &trans, &n, &k, &numext::real_ref(alpha), lhs, &lda, &numext::real_ref(beta), res, &ldc); \ + BLASFUNC(&uplo, &trans, &n, &k, (const BLASTYPE*)&numext::real_ref(alpha), lhs, &lda, (const BLASTYPE*)&numext::real_ref(beta), res, &ldc); \ } \ }; @@ -125,9 +125,13 @@ struct general_matrix_matrix_rankupdate(b_tmp.outerStride()); \ } else b = _rhs; \ \ - BLASPREFIX##gemm_(&transa, &transb, &m, &n, &k, &numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)b, &ldb, &numext::real_ref(beta), (BLASTYPE*)res, &ldc); \ + BLASFUNC(&transa, &transb, &m, &n, &k, (const BLASTYPE*)&numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)b, &ldb, (const BLASTYPE*)&numext::real_ref(beta), (BLASTYPE*)res, &ldc); \ }}; -GEMM_SPECIALIZATION(double, d, double, d) -GEMM_SPECIALIZATION(float, f, float, s) -GEMM_SPECIALIZATION(dcomplex, cd, double, z) -GEMM_SPECIALIZATION(scomplex, cf, float, c) +#ifdef EIGEN_USE_MKL +GEMM_SPECIALIZATION(double, d, double, dgemm) +GEMM_SPECIALIZATION(float, f, float, sgemm) +GEMM_SPECIALIZATION(dcomplex, cd, MKL_Complex16, zgemm) +GEMM_SPECIALIZATION(scomplex, cf, MKL_Complex8, cgemm) +#else +GEMM_SPECIALIZATION(double, d, double, dgemm_) +GEMM_SPECIALIZATION(float, f, float, sgemm_) +GEMM_SPECIALIZATION(dcomplex, cd, double, zgemm_) +GEMM_SPECIALIZATION(scomplex, cf, float, cgemm_) +#endif } // end namespase internal diff --git a/xs/src/eigen/Eigen/src/Core/products/GeneralMatrixVector.h b/src/eigen/Eigen/src/Core/products/GeneralMatrixVector.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/products/GeneralMatrixVector.h rename to src/eigen/Eigen/src/Core/products/GeneralMatrixVector.h index 3c1a7fc402..a597c1f4ee 100644 --- a/xs/src/eigen/Eigen/src/Core/products/GeneralMatrixVector.h +++ b/src/eigen/Eigen/src/Core/products/GeneralMatrixVector.h @@ -183,8 +183,8 @@ EIGEN_DONT_INLINE void general_matrix_vector_product \ struct general_matrix_vector_product_gemv \ { \ @@ -113,14 +113,21 @@ static void run( \ x_ptr=x_tmp.data(); \ incx=1; \ } else x_ptr=rhs; \ - BLASPREFIX##gemv_(&trans, &m, &n, &numext::real_ref(alpha), (const BLASTYPE*)lhs, &lda, (const BLASTYPE*)x_ptr, &incx, &numext::real_ref(beta), (BLASTYPE*)res, &incy); \ + BLASFUNC(&trans, &m, &n, (const BLASTYPE*)&numext::real_ref(alpha), (const BLASTYPE*)lhs, &lda, (const BLASTYPE*)x_ptr, &incx, (const BLASTYPE*)&numext::real_ref(beta), (BLASTYPE*)res, &incy); \ }\ }; -EIGEN_BLAS_GEMV_SPECIALIZATION(double, double, d) -EIGEN_BLAS_GEMV_SPECIALIZATION(float, float, s) -EIGEN_BLAS_GEMV_SPECIALIZATION(dcomplex, double, z) -EIGEN_BLAS_GEMV_SPECIALIZATION(scomplex, float, c) +#ifdef EIGEN_USE_MKL +EIGEN_BLAS_GEMV_SPECIALIZATION(double, double, dgemv) +EIGEN_BLAS_GEMV_SPECIALIZATION(float, float, sgemv) +EIGEN_BLAS_GEMV_SPECIALIZATION(dcomplex, MKL_Complex16, zgemv) +EIGEN_BLAS_GEMV_SPECIALIZATION(scomplex, MKL_Complex8 , cgemv) +#else +EIGEN_BLAS_GEMV_SPECIALIZATION(double, double, dgemv_) +EIGEN_BLAS_GEMV_SPECIALIZATION(float, float, sgemv_) +EIGEN_BLAS_GEMV_SPECIALIZATION(dcomplex, double, zgemv_) +EIGEN_BLAS_GEMV_SPECIALIZATION(scomplex, float, cgemv_) +#endif } // end namespase internal diff --git a/xs/src/eigen/Eigen/src/Core/products/Parallelizer.h b/src/eigen/Eigen/src/Core/products/Parallelizer.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/Parallelizer.h rename to src/eigen/Eigen/src/Core/products/Parallelizer.h diff --git a/xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix.h b/src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix.h rename to src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix.h diff --git a/xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix_BLAS.h b/src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix_BLAS.h similarity index 83% rename from xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix_BLAS.h rename to src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix_BLAS.h index a45238d693..9a5318507a 100644 --- a/xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix_BLAS.h +++ b/src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix_BLAS.h @@ -40,7 +40,7 @@ namespace internal { /* Optimized selfadjoint matrix * matrix (?SYMM/?HEMM) product */ -#define EIGEN_BLAS_SYMM_L(EIGTYPE, BLASTYPE, EIGPREFIX, BLASPREFIX) \ +#define EIGEN_BLAS_SYMM_L(EIGTYPE, BLASTYPE, EIGPREFIX, BLASFUNC) \ template \ @@ -81,13 +81,13 @@ struct product_selfadjoint_matrix(b_tmp.outerStride()); \ } else b = _rhs; \ \ - BLASPREFIX##symm_(&side, &uplo, &m, &n, &numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)b, &ldb, &numext::real_ref(beta), (BLASTYPE*)res, &ldc); \ + BLASFUNC(&side, &uplo, &m, &n, (const BLASTYPE*)&numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)b, &ldb, (const BLASTYPE*)&numext::real_ref(beta), (BLASTYPE*)res, &ldc); \ \ } \ }; -#define EIGEN_BLAS_HEMM_L(EIGTYPE, BLASTYPE, EIGPREFIX, BLASPREFIX) \ +#define EIGEN_BLAS_HEMM_L(EIGTYPE, BLASTYPE, EIGPREFIX, BLASFUNC) \ template \ @@ -144,20 +144,26 @@ struct product_selfadjoint_matrix(b_tmp.outerStride()); \ } \ \ - BLASPREFIX##hemm_(&side, &uplo, &m, &n, &numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)b, &ldb, &numext::real_ref(beta), (BLASTYPE*)res, &ldc); \ + BLASFUNC(&side, &uplo, &m, &n, (const BLASTYPE*)&numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)b, &ldb, (const BLASTYPE*)&numext::real_ref(beta), (BLASTYPE*)res, &ldc); \ \ } \ }; -EIGEN_BLAS_SYMM_L(double, double, d, d) -EIGEN_BLAS_SYMM_L(float, float, f, s) -EIGEN_BLAS_HEMM_L(dcomplex, double, cd, z) -EIGEN_BLAS_HEMM_L(scomplex, float, cf, c) - +#ifdef EIGEN_USE_MKL +EIGEN_BLAS_SYMM_L(double, double, d, dsymm) +EIGEN_BLAS_SYMM_L(float, float, f, ssymm) +EIGEN_BLAS_HEMM_L(dcomplex, MKL_Complex16, cd, zhemm) +EIGEN_BLAS_HEMM_L(scomplex, MKL_Complex8, cf, chemm) +#else +EIGEN_BLAS_SYMM_L(double, double, d, dsymm_) +EIGEN_BLAS_SYMM_L(float, float, f, ssymm_) +EIGEN_BLAS_HEMM_L(dcomplex, double, cd, zhemm_) +EIGEN_BLAS_HEMM_L(scomplex, float, cf, chemm_) +#endif /* Optimized matrix * selfadjoint matrix (?SYMM/?HEMM) product */ -#define EIGEN_BLAS_SYMM_R(EIGTYPE, BLASTYPE, EIGPREFIX, BLASPREFIX) \ +#define EIGEN_BLAS_SYMM_R(EIGTYPE, BLASTYPE, EIGPREFIX, BLASFUNC) \ template \ @@ -197,13 +203,13 @@ struct product_selfadjoint_matrix(b_tmp.outerStride()); \ } else b = _lhs; \ \ - BLASPREFIX##symm_(&side, &uplo, &m, &n, &numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)b, &ldb, &numext::real_ref(beta), (BLASTYPE*)res, &ldc); \ + BLASFUNC(&side, &uplo, &m, &n, (const BLASTYPE*)&numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)b, &ldb, (const BLASTYPE*)&numext::real_ref(beta), (BLASTYPE*)res, &ldc); \ \ } \ }; -#define EIGEN_BLAS_HEMM_R(EIGTYPE, BLASTYPE, EIGPREFIX, BLASPREFIX) \ +#define EIGEN_BLAS_HEMM_R(EIGTYPE, BLASTYPE, EIGPREFIX, BLASFUNC) \ template \ @@ -259,15 +265,21 @@ struct product_selfadjoint_matrix(b_tmp.outerStride()); \ } \ \ - BLASPREFIX##hemm_(&side, &uplo, &m, &n, &numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)b, &ldb, &numext::real_ref(beta), (BLASTYPE*)res, &ldc); \ + BLASFUNC(&side, &uplo, &m, &n, (const BLASTYPE*)&numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)b, &ldb, (const BLASTYPE*)&numext::real_ref(beta), (BLASTYPE*)res, &ldc); \ } \ }; -EIGEN_BLAS_SYMM_R(double, double, d, d) -EIGEN_BLAS_SYMM_R(float, float, f, s) -EIGEN_BLAS_HEMM_R(dcomplex, double, cd, z) -EIGEN_BLAS_HEMM_R(scomplex, float, cf, c) - +#ifdef EIGEN_USE_MKL +EIGEN_BLAS_SYMM_R(double, double, d, dsymm) +EIGEN_BLAS_SYMM_R(float, float, f, ssymm) +EIGEN_BLAS_HEMM_R(dcomplex, MKL_Complex16, cd, zhemm) +EIGEN_BLAS_HEMM_R(scomplex, MKL_Complex8, cf, chemm) +#else +EIGEN_BLAS_SYMM_R(double, double, d, dsymm_) +EIGEN_BLAS_SYMM_R(float, float, f, ssymm_) +EIGEN_BLAS_HEMM_R(dcomplex, double, cd, zhemm_) +EIGEN_BLAS_HEMM_R(scomplex, float, cf, chemm_) +#endif } // end namespace internal } // end namespace Eigen diff --git a/xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector.h b/src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector.h rename to src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector.h diff --git a/xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector_BLAS.h b/src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector_BLAS.h similarity index 91% rename from xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector_BLAS.h rename to src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector_BLAS.h index 38f23accf5..1238345e3f 100644 --- a/xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector_BLAS.h +++ b/src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector_BLAS.h @@ -95,14 +95,21 @@ const EIGTYPE* _rhs, EIGTYPE* res, EIGTYPE alpha) \ x_tmp=map_x.conjugate(); \ x_ptr=x_tmp.data(); \ } else x_ptr=_rhs; \ - BLASFUNC(&uplo, &n, &numext::real_ref(alpha), (const BLASTYPE*)lhs, &lda, (const BLASTYPE*)x_ptr, &incx, &numext::real_ref(beta), (BLASTYPE*)res, &incy); \ + BLASFUNC(&uplo, &n, (const BLASTYPE*)&numext::real_ref(alpha), (const BLASTYPE*)lhs, &lda, (const BLASTYPE*)x_ptr, &incx, (const BLASTYPE*)&numext::real_ref(beta), (BLASTYPE*)res, &incy); \ }\ }; +#ifdef EIGEN_USE_MKL +EIGEN_BLAS_SYMV_SPECIALIZATION(double, double, dsymv) +EIGEN_BLAS_SYMV_SPECIALIZATION(float, float, ssymv) +EIGEN_BLAS_SYMV_SPECIALIZATION(dcomplex, MKL_Complex16, zhemv) +EIGEN_BLAS_SYMV_SPECIALIZATION(scomplex, MKL_Complex8, chemv) +#else EIGEN_BLAS_SYMV_SPECIALIZATION(double, double, dsymv_) EIGEN_BLAS_SYMV_SPECIALIZATION(float, float, ssymv_) EIGEN_BLAS_SYMV_SPECIALIZATION(dcomplex, double, zhemv_) EIGEN_BLAS_SYMV_SPECIALIZATION(scomplex, float, chemv_) +#endif } // end namespace internal diff --git a/xs/src/eigen/Eigen/src/Core/products/SelfadjointProduct.h b/src/eigen/Eigen/src/Core/products/SelfadjointProduct.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/SelfadjointProduct.h rename to src/eigen/Eigen/src/Core/products/SelfadjointProduct.h diff --git a/xs/src/eigen/Eigen/src/Core/products/SelfadjointRank2Update.h b/src/eigen/Eigen/src/Core/products/SelfadjointRank2Update.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/SelfadjointRank2Update.h rename to src/eigen/Eigen/src/Core/products/SelfadjointRank2Update.h diff --git a/xs/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix.h b/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix.h similarity index 93% rename from xs/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix.h rename to src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix.h index 6ec5a8a0b7..f784507e77 100644 --- a/xs/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix.h +++ b/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix.h @@ -137,7 +137,13 @@ EIGEN_DONT_INLINE void product_triangular_matrix_matrix triangularBuffer((internal::constructor_without_unaligned_array_assert())); + // To work around an "error: member reference base type 'Matrix<...> + // (Eigen::internal::constructor_without_unaligned_array_assert (*)())' is + // not a structure or union" compilation error in nvcc (tested V8.0.61), + // create a dummy internal::constructor_without_unaligned_array_assert + // object to pass to the Matrix constructor. + internal::constructor_without_unaligned_array_assert a; + Matrix triangularBuffer(a); triangularBuffer.setZero(); if((Mode&ZeroDiag)==ZeroDiag) triangularBuffer.diagonal().setZero(); @@ -284,7 +290,8 @@ EIGEN_DONT_INLINE void product_triangular_matrix_matrix triangularBuffer((internal::constructor_without_unaligned_array_assert())); + internal::constructor_without_unaligned_array_assert a; + Matrix triangularBuffer(a); triangularBuffer.setZero(); if((Mode&ZeroDiag)==ZeroDiag) triangularBuffer.diagonal().setZero(); @@ -393,7 +400,9 @@ struct triangular_product_impl { template static void run(Dest& dst, const Lhs &a_lhs, const Rhs &a_rhs, const typename Dest::Scalar& alpha) { - typedef typename Dest::Scalar Scalar; + typedef typename Lhs::Scalar LhsScalar; + typedef typename Rhs::Scalar RhsScalar; + typedef typename Dest::Scalar Scalar; typedef internal::blas_traits LhsBlasTraits; typedef typename LhsBlasTraits::DirectLinearAccessType ActualLhsType; @@ -405,8 +414,9 @@ struct triangular_product_impl typename internal::add_const_on_value_type::type lhs = LhsBlasTraits::extract(a_lhs); typename internal::add_const_on_value_type::type rhs = RhsBlasTraits::extract(a_rhs); - Scalar actualAlpha = alpha * LhsBlasTraits::extractScalarFactor(a_lhs) - * RhsBlasTraits::extractScalarFactor(a_rhs); + LhsScalar lhs_alpha = LhsBlasTraits::extractScalarFactor(a_lhs); + RhsScalar rhs_alpha = RhsBlasTraits::extractScalarFactor(a_rhs); + Scalar actualAlpha = alpha * lhs_alpha * rhs_alpha; typedef internal::gemm_blocking_space<(Dest::Flags&RowMajorBit) ? RowMajor : ColMajor,Scalar,Scalar, Lhs::MaxRowsAtCompileTime, Rhs::MaxColsAtCompileTime, Lhs::MaxColsAtCompileTime,4> BlockingType; @@ -431,6 +441,21 @@ struct triangular_product_impl &dst.coeffRef(0,0), dst.outerStride(), // result info actualAlpha, blocking ); + + // Apply correction if the diagonal is unit and a scalar factor was nested: + if ((Mode&UnitDiag)==UnitDiag) + { + if (LhsIsTriangular && lhs_alpha!=LhsScalar(1)) + { + Index diagSize = (std::min)(lhs.rows(),lhs.cols()); + dst.topRows(diagSize) -= ((lhs_alpha-LhsScalar(1))*a_rhs).topRows(diagSize); + } + else if ((!LhsIsTriangular) && rhs_alpha!=RhsScalar(1)) + { + Index diagSize = (std::min)(rhs.rows(),rhs.cols()); + dst.leftCols(diagSize) -= (rhs_alpha-RhsScalar(1))*a_lhs.leftCols(diagSize); + } + } } }; diff --git a/xs/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix_BLAS.h b/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix_BLAS.h similarity index 90% rename from xs/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix_BLAS.h rename to src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix_BLAS.h index aecded6bbf..a25197ab01 100644 --- a/xs/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix_BLAS.h +++ b/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix_BLAS.h @@ -75,7 +75,7 @@ EIGEN_BLAS_TRMM_SPECIALIZE(scomplex, true) EIGEN_BLAS_TRMM_SPECIALIZE(scomplex, false) // implements col-major += alpha * op(triangular) * op(general) -#define EIGEN_BLAS_TRMM_L(EIGTYPE, BLASTYPE, EIGPREFIX, BLASPREFIX) \ +#define EIGEN_BLAS_TRMM_L(EIGTYPE, BLASTYPE, EIGPREFIX, BLASFUNC) \ template \ @@ -172,7 +172,7 @@ struct product_triangular_matrix_matrix_trmm > res_tmp(res,rows,cols,OuterStride<>(resStride)); \ @@ -180,13 +180,20 @@ struct product_triangular_matrix_matrix_trmm \ @@ -282,7 +289,7 @@ struct product_triangular_matrix_matrix_trmm > res_tmp(res,rows,cols,OuterStride<>(resStride)); \ @@ -290,11 +297,17 @@ struct product_triangular_matrix_matrix_trmm struct trmv_selector typename internal::add_const_on_value_type::type actualLhs = LhsBlasTraits::extract(lhs); typename internal::add_const_on_value_type::type actualRhs = RhsBlasTraits::extract(rhs); - ResScalar actualAlpha = alpha * LhsBlasTraits::extractScalarFactor(lhs) - * RhsBlasTraits::extractScalarFactor(rhs); + LhsScalar lhs_alpha = LhsBlasTraits::extractScalarFactor(lhs); + RhsScalar rhs_alpha = RhsBlasTraits::extractScalarFactor(rhs); + ResScalar actualAlpha = alpha * lhs_alpha * rhs_alpha; enum { // FIXME find a way to allow an inner stride on the result if packet_traits::size==1 @@ -274,6 +275,12 @@ template struct trmv_selector else dest = MappedDest(actualDestPtr, dest.size()); } + + if ( ((Mode&UnitDiag)==UnitDiag) && (lhs_alpha!=LhsScalar(1)) ) + { + Index diagSize = (std::min)(lhs.rows(),lhs.cols()); + dest.head(diagSize) -= (lhs_alpha-LhsScalar(1))*rhs.head(diagSize); + } } }; @@ -295,8 +302,9 @@ template struct trmv_selector typename add_const::type actualLhs = LhsBlasTraits::extract(lhs); typename add_const::type actualRhs = RhsBlasTraits::extract(rhs); - ResScalar actualAlpha = alpha * LhsBlasTraits::extractScalarFactor(lhs) - * RhsBlasTraits::extractScalarFactor(rhs); + LhsScalar lhs_alpha = LhsBlasTraits::extractScalarFactor(lhs); + RhsScalar rhs_alpha = RhsBlasTraits::extractScalarFactor(rhs); + ResScalar actualAlpha = alpha * lhs_alpha * rhs_alpha; enum { DirectlyUseRhs = ActualRhsTypeCleaned::InnerStrideAtCompileTime==1 @@ -326,6 +334,12 @@ template struct trmv_selector actualRhsPtr,1, dest.data(),dest.innerStride(), actualAlpha); + + if ( ((Mode&UnitDiag)==UnitDiag) && (lhs_alpha!=LhsScalar(1)) ) + { + Index diagSize = (std::min)(lhs.rows(),lhs.cols()); + dest.head(diagSize) -= (lhs_alpha-LhsScalar(1))*rhs.head(diagSize); + } } }; diff --git a/xs/src/eigen/Eigen/src/Core/products/TriangularMatrixVector_BLAS.h b/src/eigen/Eigen/src/Core/products/TriangularMatrixVector_BLAS.h similarity index 83% rename from xs/src/eigen/Eigen/src/Core/products/TriangularMatrixVector_BLAS.h rename to src/eigen/Eigen/src/Core/products/TriangularMatrixVector_BLAS.h index 07bf26ce51..3d47a2b94c 100644 --- a/xs/src/eigen/Eigen/src/Core/products/TriangularMatrixVector_BLAS.h +++ b/src/eigen/Eigen/src/Core/products/TriangularMatrixVector_BLAS.h @@ -71,7 +71,7 @@ EIGEN_BLAS_TRMV_SPECIALIZE(dcomplex) EIGEN_BLAS_TRMV_SPECIALIZE(scomplex) // implements col-major: res += alpha * op(triangular) * vector -#define EIGEN_BLAS_TRMV_CM(EIGTYPE, BLASTYPE, EIGPREFIX, BLASPREFIX) \ +#define EIGEN_BLAS_TRMV_CM(EIGTYPE, BLASTYPE, EIGPREFIX, BLASPREFIX, BLASPOSTFIX) \ template \ struct triangular_matrix_vector_product_trmv { \ enum { \ @@ -121,10 +121,10 @@ struct triangular_matrix_vector_product_trmv(size); \ n = convert_index(cols-size); \ } \ - BLASPREFIX##gemv_(&trans, &m, &n, &numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)x, &incx, &numext::real_ref(beta), (BLASTYPE*)y, &incy); \ + BLASPREFIX##gemv##BLASPOSTFIX(&trans, &m, &n, (const BLASTYPE*)&numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)x, &incx, (const BLASTYPE*)&numext::real_ref(beta), (BLASTYPE*)y, &incy); \ } \ } \ }; -EIGEN_BLAS_TRMV_CM(double, double, d, d) -EIGEN_BLAS_TRMV_CM(dcomplex, double, cd, z) -EIGEN_BLAS_TRMV_CM(float, float, f, s) -EIGEN_BLAS_TRMV_CM(scomplex, float, cf, c) +#ifdef EIGEN_USE_MKL +EIGEN_BLAS_TRMV_CM(double, double, d, d,) +EIGEN_BLAS_TRMV_CM(dcomplex, MKL_Complex16, cd, z,) +EIGEN_BLAS_TRMV_CM(float, float, f, s,) +EIGEN_BLAS_TRMV_CM(scomplex, MKL_Complex8, cf, c,) +#else +EIGEN_BLAS_TRMV_CM(double, double, d, d, _) +EIGEN_BLAS_TRMV_CM(dcomplex, double, cd, z, _) +EIGEN_BLAS_TRMV_CM(float, float, f, s, _) +EIGEN_BLAS_TRMV_CM(scomplex, float, cf, c, _) +#endif // implements row-major: res += alpha * op(triangular) * vector -#define EIGEN_BLAS_TRMV_RM(EIGTYPE, BLASTYPE, EIGPREFIX, BLASPREFIX) \ +#define EIGEN_BLAS_TRMV_RM(EIGTYPE, BLASTYPE, EIGPREFIX, BLASPREFIX, BLASPOSTFIX) \ template \ struct triangular_matrix_vector_product_trmv { \ enum { \ @@ -203,10 +210,10 @@ struct triangular_matrix_vector_product_trmv(size); \ n = convert_index(cols-size); \ } \ - BLASPREFIX##gemv_(&trans, &n, &m, &numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)x, &incx, &numext::real_ref(beta), (BLASTYPE*)y, &incy); \ + BLASPREFIX##gemv##BLASPOSTFIX(&trans, &n, &m, (const BLASTYPE*)&numext::real_ref(alpha), (const BLASTYPE*)a, &lda, (const BLASTYPE*)x, &incx, (const BLASTYPE*)&numext::real_ref(beta), (BLASTYPE*)y, &incy); \ } \ } \ }; -EIGEN_BLAS_TRMV_RM(double, double, d, d) -EIGEN_BLAS_TRMV_RM(dcomplex, double, cd, z) -EIGEN_BLAS_TRMV_RM(float, float, f, s) -EIGEN_BLAS_TRMV_RM(scomplex, float, cf, c) +#ifdef EIGEN_USE_MKL +EIGEN_BLAS_TRMV_RM(double, double, d, d,) +EIGEN_BLAS_TRMV_RM(dcomplex, MKL_Complex16, cd, z,) +EIGEN_BLAS_TRMV_RM(float, float, f, s,) +EIGEN_BLAS_TRMV_RM(scomplex, MKL_Complex8, cf, c,) +#else +EIGEN_BLAS_TRMV_RM(double, double, d, d,_) +EIGEN_BLAS_TRMV_RM(dcomplex, double, cd, z,_) +EIGEN_BLAS_TRMV_RM(float, float, f, s,_) +EIGEN_BLAS_TRMV_RM(scomplex, float, cf, c,_) +#endif } // end namespase internal diff --git a/xs/src/eigen/Eigen/src/Core/products/TriangularSolverMatrix.h b/src/eigen/Eigen/src/Core/products/TriangularSolverMatrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/TriangularSolverMatrix.h rename to src/eigen/Eigen/src/Core/products/TriangularSolverMatrix.h diff --git a/xs/src/eigen/Eigen/src/Core/products/TriangularSolverMatrix_BLAS.h b/src/eigen/Eigen/src/Core/products/TriangularSolverMatrix_BLAS.h similarity index 81% rename from xs/src/eigen/Eigen/src/Core/products/TriangularSolverMatrix_BLAS.h rename to src/eigen/Eigen/src/Core/products/TriangularSolverMatrix_BLAS.h index 88c0fb7941..f0775116ac 100644 --- a/xs/src/eigen/Eigen/src/Core/products/TriangularSolverMatrix_BLAS.h +++ b/src/eigen/Eigen/src/Core/products/TriangularSolverMatrix_BLAS.h @@ -38,7 +38,7 @@ namespace Eigen { namespace internal { // implements LeftSide op(triangular)^-1 * general -#define EIGEN_BLAS_TRSM_L(EIGTYPE, BLASTYPE, BLASPREFIX) \ +#define EIGEN_BLAS_TRSM_L(EIGTYPE, BLASTYPE, BLASFUNC) \ template \ struct triangular_solve_matrix \ { \ @@ -80,18 +80,24 @@ struct triangular_solve_matrix \ struct triangular_solve_matrix \ { \ @@ -133,16 +139,22 @@ struct triangular_solve_matrix /*Check IMKL version for compatibility: < 10.3 is not usable with Eigen*/ @@ -108,6 +109,10 @@ #endif #endif +#if defined(EIGEN_USE_BLAS) && !defined(EIGEN_USE_MKL) +#include "../../misc/blas.h" +#endif + namespace Eigen { typedef std::complex dcomplex; @@ -121,8 +126,5 @@ typedef int BlasIndex; } // end namespace Eigen -#if defined(EIGEN_USE_BLAS) -#include "../../misc/blas.h" -#endif #endif // EIGEN_MKL_SUPPORT_H diff --git a/xs/src/eigen/Eigen/src/Core/util/Macros.h b/src/eigen/Eigen/src/Core/util/Macros.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/util/Macros.h rename to src/eigen/Eigen/src/Core/util/Macros.h index 427d3cd6bd..02d21d2cdd 100644 --- a/xs/src/eigen/Eigen/src/Core/util/Macros.h +++ b/src/eigen/Eigen/src/Core/util/Macros.h @@ -13,7 +13,7 @@ #define EIGEN_WORLD_VERSION 3 #define EIGEN_MAJOR_VERSION 3 -#define EIGEN_MINOR_VERSION 3 +#define EIGEN_MINOR_VERSION 5 #define EIGEN_VERSION_AT_LEAST(x,y,z) (EIGEN_WORLD_VERSION>x || (EIGEN_WORLD_VERSION>=x && \ (EIGEN_MAJOR_VERSION>y || (EIGEN_MAJOR_VERSION>=y && \ @@ -399,7 +399,7 @@ // Does the compiler support variadic templates? #ifndef EIGEN_HAS_VARIADIC_TEMPLATES #if EIGEN_MAX_CPP_VER>=11 && (__cplusplus > 199711L || EIGEN_COMP_MSVC >= 1900) \ - && ( !defined(__NVCC__) || !EIGEN_ARCH_ARM_OR_ARM64 || (defined __CUDACC_VER__ && __CUDACC_VER__ >= 80000) ) + && (!defined(__NVCC__) || !EIGEN_ARCH_ARM_OR_ARM64 || (EIGEN_CUDACC_VER >= 80000) ) // ^^ Disable the use of variadic templates when compiling with versions of nvcc older than 8.0 on ARM devices: // this prevents nvcc from crashing when compiling Eigen on Tegra X1 #define EIGEN_HAS_VARIADIC_TEMPLATES 1 @@ -413,7 +413,7 @@ #ifdef __CUDACC__ // Const expressions are supported provided that c++11 is enabled and we're using either clang or nvcc 7.5 or above -#if EIGEN_MAX_CPP_VER>=14 && (__cplusplus > 199711L && defined(__CUDACC_VER__) && (EIGEN_COMP_CLANG || __CUDACC_VER__ >= 70500)) +#if EIGEN_MAX_CPP_VER>=14 && (__cplusplus > 199711L && (EIGEN_COMP_CLANG || EIGEN_CUDACC_VER >= 70500)) #define EIGEN_HAS_CONSTEXPR 1 #endif #elif EIGEN_MAX_CPP_VER>=14 && (__has_feature(cxx_relaxed_constexpr) || (defined(__cplusplus) && __cplusplus >= 201402L) || \ @@ -487,11 +487,13 @@ // EIGEN_STRONG_INLINE is a stronger version of the inline, using __forceinline on MSVC, // but it still doesn't use GCC's always_inline. This is useful in (common) situations where MSVC needs forceinline // but GCC is still doing fine with just inline. +#ifndef EIGEN_STRONG_INLINE #if EIGEN_COMP_MSVC || EIGEN_COMP_ICC #define EIGEN_STRONG_INLINE __forceinline #else #define EIGEN_STRONG_INLINE inline #endif +#endif // EIGEN_ALWAYS_INLINE is the stronget, it has the effect of making the function inline and adding every possible // attribute to maximize inlining. This should only be used when really necessary: in particular, @@ -812,7 +814,8 @@ namespace Eigen { // just an empty macro ! #define EIGEN_EMPTY -#if EIGEN_COMP_MSVC_STRICT && (EIGEN_COMP_MSVC < 1900 || defined(__CUDACC_VER__)) // for older MSVC versions, as well as 1900 && CUDA 8, using the base operator is sufficient (cf Bugs 1000, 1324) +#if EIGEN_COMP_MSVC_STRICT && (EIGEN_COMP_MSVC < 1900 || EIGEN_CUDACC_VER>0) + // for older MSVC versions, as well as 1900 && CUDA 8, using the base operator is sufficient (cf Bugs 1000, 1324) #define EIGEN_INHERIT_ASSIGNMENT_EQUAL_OPERATOR(Derived) \ using Base::operator =; #elif EIGEN_COMP_CLANG // workaround clang bug (see http://forum.kde.org/viewtopic.php?f=74&t=102653) @@ -986,7 +989,13 @@ namespace Eigen { # define EIGEN_NOEXCEPT # define EIGEN_NOEXCEPT_IF(x) # define EIGEN_NO_THROW throw() -# define EIGEN_EXCEPTION_SPEC(X) throw(X) +# if EIGEN_COMP_MSVC + // MSVC does not support exception specifications (warning C4290), + // and they are deprecated in c++11 anyway. +# define EIGEN_EXCEPTION_SPEC(X) throw() +# else +# define EIGEN_EXCEPTION_SPEC(X) throw(X) +# endif #endif #endif // EIGEN_MACROS_H diff --git a/xs/src/eigen/Eigen/src/Core/util/Memory.h b/src/eigen/Eigen/src/Core/util/Memory.h similarity index 98% rename from xs/src/eigen/Eigen/src/Core/util/Memory.h rename to src/eigen/Eigen/src/Core/util/Memory.h index c634d7ea05..66cdbd8dd5 100644 --- a/xs/src/eigen/Eigen/src/Core/util/Memory.h +++ b/src/eigen/Eigen/src/Core/util/Memory.h @@ -70,7 +70,7 @@ inline void throw_std_bad_alloc() throw std::bad_alloc(); #else std::size_t huge = static_cast(-1); - new int[huge]; + ::operator new(huge); #endif } @@ -493,7 +493,7 @@ template struct smart_copy_helper { IntPtr size = IntPtr(end)-IntPtr(start); if(size==0) return; eigen_internal_assert(start!=0 && end!=0 && target!=0); - memcpy(target, start, size); + std::memcpy(target, start, size); } }; @@ -696,7 +696,15 @@ template void swap(scoped_array &a,scoped_array &b) /** \class aligned_allocator * \ingroup Core_Module * -* \brief STL compatible allocator to use with with 16 byte aligned types +* \brief STL compatible allocator to use with types requiring a non standrad alignment. +* +* The memory is aligned as for dynamically aligned matrix/array types such as MatrixXd. +* By default, it will thus provide at least 16 bytes alignment and more in following cases: +* - 32 bytes alignment if AVX is enabled. +* - 64 bytes alignment if AVX512 is enabled. +* +* This can be controled using the \c EIGEN_MAX_ALIGN_BYTES macro as documented +* \link TopicPreprocessorDirectivesPerformance there \endlink. * * Example: * \code diff --git a/xs/src/eigen/Eigen/src/Core/util/Meta.h b/src/eigen/Eigen/src/Core/util/Meta.h similarity index 95% rename from xs/src/eigen/Eigen/src/Core/util/Meta.h rename to src/eigen/Eigen/src/Core/util/Meta.h index 7f63707559..1d73f05d67 100644 --- a/xs/src/eigen/Eigen/src/Core/util/Meta.h +++ b/src/eigen/Eigen/src/Core/util/Meta.h @@ -485,6 +485,26 @@ T div_ceil(const T &a, const T &b) return (a+b-1) / b; } +// The aim of the following functions is to bypass -Wfloat-equal warnings +// when we really want a strict equality comparison on floating points. +template EIGEN_STRONG_INLINE +bool equal_strict(const X& x,const Y& y) { return x == y; } + +template<> EIGEN_STRONG_INLINE +bool equal_strict(const float& x,const float& y) { return std::equal_to()(x,y); } + +template<> EIGEN_STRONG_INLINE +bool equal_strict(const double& x,const double& y) { return std::equal_to()(x,y); } + +template EIGEN_STRONG_INLINE +bool not_equal_strict(const X& x,const Y& y) { return x != y; } + +template<> EIGEN_STRONG_INLINE +bool not_equal_strict(const float& x,const float& y) { return std::not_equal_to()(x,y); } + +template<> EIGEN_STRONG_INLINE +bool not_equal_strict(const double& x,const double& y) { return std::not_equal_to()(x,y); } + } // end namespace numext } // end namespace Eigen diff --git a/xs/src/eigen/Eigen/src/Core/util/NonMPL2.h b/src/eigen/Eigen/src/Core/util/NonMPL2.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/util/NonMPL2.h rename to src/eigen/Eigen/src/Core/util/NonMPL2.h diff --git a/xs/src/eigen/Eigen/src/Core/util/ReenableStupidWarnings.h b/src/eigen/Eigen/src/Core/util/ReenableStupidWarnings.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/util/ReenableStupidWarnings.h rename to src/eigen/Eigen/src/Core/util/ReenableStupidWarnings.h diff --git a/xs/src/eigen/Eigen/src/Core/util/StaticAssert.h b/src/eigen/Eigen/src/Core/util/StaticAssert.h similarity index 74% rename from xs/src/eigen/Eigen/src/Core/util/StaticAssert.h rename to src/eigen/Eigen/src/Core/util/StaticAssert.h index 983361a45b..500e47792a 100644 --- a/xs/src/eigen/Eigen/src/Core/util/StaticAssert.h +++ b/src/eigen/Eigen/src/Core/util/StaticAssert.h @@ -24,6 +24,7 @@ * */ +#ifndef EIGEN_STATIC_ASSERT #ifndef EIGEN_NO_STATIC_ASSERT #if EIGEN_MAX_CPP_VER>=11 && (__has_feature(cxx_static_assert) || (defined(__cplusplus) && __cplusplus >= 201103L) || (EIGEN_COMP_MSVC >= 1600)) @@ -44,64 +45,65 @@ struct static_assertion { enum { - YOU_TRIED_CALLING_A_VECTOR_METHOD_ON_A_MATRIX, - YOU_MIXED_VECTORS_OF_DIFFERENT_SIZES, - YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES, - THIS_METHOD_IS_ONLY_FOR_VECTORS_OF_A_SPECIFIC_SIZE, - THIS_METHOD_IS_ONLY_FOR_MATRICES_OF_A_SPECIFIC_SIZE, - THIS_METHOD_IS_ONLY_FOR_OBJECTS_OF_A_SPECIFIC_SIZE, - OUT_OF_RANGE_ACCESS, - YOU_MADE_A_PROGRAMMING_MISTAKE, - EIGEN_INTERNAL_ERROR_PLEASE_FILE_A_BUG_REPORT, - EIGEN_INTERNAL_COMPILATION_ERROR_OR_YOU_MADE_A_PROGRAMMING_MISTAKE, - YOU_CALLED_A_FIXED_SIZE_METHOD_ON_A_DYNAMIC_SIZE_MATRIX_OR_VECTOR, - YOU_CALLED_A_DYNAMIC_SIZE_METHOD_ON_A_FIXED_SIZE_MATRIX_OR_VECTOR, - UNALIGNED_LOAD_AND_STORE_OPERATIONS_UNIMPLEMENTED_ON_ALTIVEC, - THIS_FUNCTION_IS_NOT_FOR_INTEGER_NUMERIC_TYPES, - FLOATING_POINT_ARGUMENT_PASSED__INTEGER_WAS_EXPECTED, - NUMERIC_TYPE_MUST_BE_REAL, - COEFFICIENT_WRITE_ACCESS_TO_SELFADJOINT_NOT_SUPPORTED, - WRITING_TO_TRIANGULAR_PART_WITH_UNIT_DIAGONAL_IS_NOT_SUPPORTED, - THIS_METHOD_IS_ONLY_FOR_FIXED_SIZE, - INVALID_MATRIX_PRODUCT, - INVALID_VECTOR_VECTOR_PRODUCT__IF_YOU_WANTED_A_DOT_OR_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTIONS, - INVALID_MATRIX_PRODUCT__IF_YOU_WANTED_A_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTION, - YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY, - THIS_METHOD_IS_ONLY_FOR_COLUMN_MAJOR_MATRICES, - THIS_METHOD_IS_ONLY_FOR_ROW_MAJOR_MATRICES, - INVALID_MATRIX_TEMPLATE_PARAMETERS, - INVALID_MATRIXBASE_TEMPLATE_PARAMETERS, - BOTH_MATRICES_MUST_HAVE_THE_SAME_STORAGE_ORDER, - THIS_METHOD_IS_ONLY_FOR_DIAGONAL_MATRIX, - THE_MATRIX_OR_EXPRESSION_THAT_YOU_PASSED_DOES_NOT_HAVE_THE_EXPECTED_TYPE, - THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES, - YOU_ALREADY_SPECIFIED_THIS_STRIDE, - INVALID_STORAGE_ORDER_FOR_THIS_VECTOR_EXPRESSION, - THE_BRACKET_OPERATOR_IS_ONLY_FOR_VECTORS__USE_THE_PARENTHESIS_OPERATOR_INSTEAD, - PACKET_ACCESS_REQUIRES_TO_HAVE_INNER_STRIDE_FIXED_TO_1, - THIS_METHOD_IS_ONLY_FOR_SPECIFIC_TRANSFORMATIONS, - YOU_CANNOT_MIX_ARRAYS_AND_MATRICES, - YOU_PERFORMED_AN_INVALID_TRANSFORMATION_CONVERSION, - THIS_EXPRESSION_IS_NOT_A_LVALUE__IT_IS_READ_ONLY, - YOU_ARE_TRYING_TO_USE_AN_INDEX_BASED_ACCESSOR_ON_AN_EXPRESSION_THAT_DOES_NOT_SUPPORT_THAT, - THIS_METHOD_IS_ONLY_FOR_1x1_EXPRESSIONS, - THIS_METHOD_IS_ONLY_FOR_INNER_OR_LAZY_PRODUCTS, - THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_OF_BOOL, - THIS_METHOD_IS_ONLY_FOR_ARRAYS_NOT_MATRICES, - YOU_PASSED_A_ROW_VECTOR_BUT_A_COLUMN_VECTOR_WAS_EXPECTED, - YOU_PASSED_A_COLUMN_VECTOR_BUT_A_ROW_VECTOR_WAS_EXPECTED, - THE_INDEX_TYPE_MUST_BE_A_SIGNED_TYPE, - THE_STORAGE_ORDER_OF_BOTH_SIDES_MUST_MATCH, - OBJECT_ALLOCATED_ON_STACK_IS_TOO_BIG, - IMPLICIT_CONVERSION_TO_SCALAR_IS_FOR_INNER_PRODUCT_ONLY, - STORAGE_LAYOUT_DOES_NOT_MATCH, - EIGEN_INTERNAL_ERROR_PLEASE_FILE_A_BUG_REPORT__INVALID_COST_VALUE, - THIS_COEFFICIENT_ACCESSOR_TAKING_ONE_ACCESS_IS_ONLY_FOR_EXPRESSIONS_ALLOWING_LINEAR_ACCESS, - MATRIX_FREE_CONJUGATE_GRADIENT_IS_COMPATIBLE_WITH_UPPER_UNION_LOWER_MODE_ONLY, - THIS_TYPE_IS_NOT_SUPPORTED, - STORAGE_KIND_MUST_MATCH, - STORAGE_INDEX_MUST_MATCH, - CHOLMOD_SUPPORTS_DOUBLE_PRECISION_ONLY + YOU_TRIED_CALLING_A_VECTOR_METHOD_ON_A_MATRIX=1, + YOU_MIXED_VECTORS_OF_DIFFERENT_SIZES=1, + YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES=1, + THIS_METHOD_IS_ONLY_FOR_VECTORS_OF_A_SPECIFIC_SIZE=1, + THIS_METHOD_IS_ONLY_FOR_MATRICES_OF_A_SPECIFIC_SIZE=1, + THIS_METHOD_IS_ONLY_FOR_OBJECTS_OF_A_SPECIFIC_SIZE=1, + OUT_OF_RANGE_ACCESS=1, + YOU_MADE_A_PROGRAMMING_MISTAKE=1, + EIGEN_INTERNAL_ERROR_PLEASE_FILE_A_BUG_REPORT=1, + EIGEN_INTERNAL_COMPILATION_ERROR_OR_YOU_MADE_A_PROGRAMMING_MISTAKE=1, + YOU_CALLED_A_FIXED_SIZE_METHOD_ON_A_DYNAMIC_SIZE_MATRIX_OR_VECTOR=1, + YOU_CALLED_A_DYNAMIC_SIZE_METHOD_ON_A_FIXED_SIZE_MATRIX_OR_VECTOR=1, + UNALIGNED_LOAD_AND_STORE_OPERATIONS_UNIMPLEMENTED_ON_ALTIVEC=1, + THIS_FUNCTION_IS_NOT_FOR_INTEGER_NUMERIC_TYPES=1, + FLOATING_POINT_ARGUMENT_PASSED__INTEGER_WAS_EXPECTED=1, + NUMERIC_TYPE_MUST_BE_REAL=1, + COEFFICIENT_WRITE_ACCESS_TO_SELFADJOINT_NOT_SUPPORTED=1, + WRITING_TO_TRIANGULAR_PART_WITH_UNIT_DIAGONAL_IS_NOT_SUPPORTED=1, + THIS_METHOD_IS_ONLY_FOR_FIXED_SIZE=1, + INVALID_MATRIX_PRODUCT=1, + INVALID_VECTOR_VECTOR_PRODUCT__IF_YOU_WANTED_A_DOT_OR_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTIONS=1, + INVALID_MATRIX_PRODUCT__IF_YOU_WANTED_A_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTION=1, + YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY=1, + THIS_METHOD_IS_ONLY_FOR_COLUMN_MAJOR_MATRICES=1, + THIS_METHOD_IS_ONLY_FOR_ROW_MAJOR_MATRICES=1, + INVALID_MATRIX_TEMPLATE_PARAMETERS=1, + INVALID_MATRIXBASE_TEMPLATE_PARAMETERS=1, + BOTH_MATRICES_MUST_HAVE_THE_SAME_STORAGE_ORDER=1, + THIS_METHOD_IS_ONLY_FOR_DIAGONAL_MATRIX=1, + THE_MATRIX_OR_EXPRESSION_THAT_YOU_PASSED_DOES_NOT_HAVE_THE_EXPECTED_TYPE=1, + THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES=1, + YOU_ALREADY_SPECIFIED_THIS_STRIDE=1, + INVALID_STORAGE_ORDER_FOR_THIS_VECTOR_EXPRESSION=1, + THE_BRACKET_OPERATOR_IS_ONLY_FOR_VECTORS__USE_THE_PARENTHESIS_OPERATOR_INSTEAD=1, + PACKET_ACCESS_REQUIRES_TO_HAVE_INNER_STRIDE_FIXED_TO_1=1, + THIS_METHOD_IS_ONLY_FOR_SPECIFIC_TRANSFORMATIONS=1, + YOU_CANNOT_MIX_ARRAYS_AND_MATRICES=1, + YOU_PERFORMED_AN_INVALID_TRANSFORMATION_CONVERSION=1, + THIS_EXPRESSION_IS_NOT_A_LVALUE__IT_IS_READ_ONLY=1, + YOU_ARE_TRYING_TO_USE_AN_INDEX_BASED_ACCESSOR_ON_AN_EXPRESSION_THAT_DOES_NOT_SUPPORT_THAT=1, + THIS_METHOD_IS_ONLY_FOR_1x1_EXPRESSIONS=1, + THIS_METHOD_IS_ONLY_FOR_INNER_OR_LAZY_PRODUCTS=1, + THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_OF_BOOL=1, + THIS_METHOD_IS_ONLY_FOR_ARRAYS_NOT_MATRICES=1, + YOU_PASSED_A_ROW_VECTOR_BUT_A_COLUMN_VECTOR_WAS_EXPECTED=1, + YOU_PASSED_A_COLUMN_VECTOR_BUT_A_ROW_VECTOR_WAS_EXPECTED=1, + THE_INDEX_TYPE_MUST_BE_A_SIGNED_TYPE=1, + THE_STORAGE_ORDER_OF_BOTH_SIDES_MUST_MATCH=1, + OBJECT_ALLOCATED_ON_STACK_IS_TOO_BIG=1, + IMPLICIT_CONVERSION_TO_SCALAR_IS_FOR_INNER_PRODUCT_ONLY=1, + STORAGE_LAYOUT_DOES_NOT_MATCH=1, + EIGEN_INTERNAL_ERROR_PLEASE_FILE_A_BUG_REPORT__INVALID_COST_VALUE=1, + THIS_COEFFICIENT_ACCESSOR_TAKING_ONE_ACCESS_IS_ONLY_FOR_EXPRESSIONS_ALLOWING_LINEAR_ACCESS=1, + MATRIX_FREE_CONJUGATE_GRADIENT_IS_COMPATIBLE_WITH_UPPER_UNION_LOWER_MODE_ONLY=1, + THIS_TYPE_IS_NOT_SUPPORTED=1, + STORAGE_KIND_MUST_MATCH=1, + STORAGE_INDEX_MUST_MATCH=1, + CHOLMOD_SUPPORTS_DOUBLE_PRECISION_ONLY=1, + SELFADJOINTVIEW_ACCEPTS_UPPER_AND_LOWER_MODE_ONLY=1 }; }; @@ -131,7 +133,7 @@ #define EIGEN_STATIC_ASSERT(CONDITION,MSG) eigen_assert((CONDITION) && #MSG); #endif // EIGEN_NO_STATIC_ASSERT - +#endif // EIGEN_STATIC_ASSERT // static assertion failing if the type \a TYPE is not a vector type #define EIGEN_STATIC_ASSERT_VECTOR_ONLY(TYPE) \ diff --git a/xs/src/eigen/Eigen/src/Core/util/XprHelper.h b/src/eigen/Eigen/src/Core/util/XprHelper.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/util/XprHelper.h rename to src/eigen/Eigen/src/Core/util/XprHelper.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/ComplexEigenSolver.h b/src/eigen/Eigen/src/Eigenvalues/ComplexEigenSolver.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/ComplexEigenSolver.h rename to src/eigen/Eigen/src/Eigenvalues/ComplexEigenSolver.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/ComplexSchur.h b/src/eigen/Eigen/src/Eigenvalues/ComplexSchur.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/ComplexSchur.h rename to src/eigen/Eigen/src/Eigenvalues/ComplexSchur.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/ComplexSchur_LAPACKE.h b/src/eigen/Eigen/src/Eigenvalues/ComplexSchur_LAPACKE.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/ComplexSchur_LAPACKE.h rename to src/eigen/Eigen/src/Eigenvalues/ComplexSchur_LAPACKE.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/EigenSolver.h b/src/eigen/Eigen/src/Eigenvalues/EigenSolver.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/EigenSolver.h rename to src/eigen/Eigen/src/Eigenvalues/EigenSolver.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h b/src/eigen/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h similarity index 98% rename from xs/src/eigen/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h rename to src/eigen/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h index 36a91dffc0..87d789b3f4 100644 --- a/xs/src/eigen/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h +++ b/src/eigen/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h @@ -311,7 +311,6 @@ GeneralizedEigenSolver::compute(const MatrixType& A, const MatrixTyp // Aliases: Map v(reinterpret_cast(m_tmp.data()), size); ComplexVectorType &cv = m_tmp; - const MatrixType &mZ = m_realQZ.matrixZ(); const MatrixType &mS = m_realQZ.matrixS(); const MatrixType &mT = m_realQZ.matrixT(); @@ -351,7 +350,7 @@ GeneralizedEigenSolver::compute(const MatrixType& A, const MatrixTyp } } } - m_eivec.col(i).real().noalias() = mZ.transpose() * v; + m_eivec.col(i).real().noalias() = m_realQZ.matrixZ().transpose() * v; m_eivec.col(i).real().normalize(); m_eivec.col(i).imag().setConstant(0); } @@ -400,7 +399,7 @@ GeneralizedEigenSolver::compute(const MatrixType& A, const MatrixTyp / (alpha*mT.coeffRef(j,j) - static_cast(beta*mS.coeffRef(j,j))); } } - m_eivec.col(i+1).noalias() = (mZ.transpose() * cv); + m_eivec.col(i+1).noalias() = (m_realQZ.matrixZ().transpose() * cv); m_eivec.col(i+1).normalize(); m_eivec.col(i) = m_eivec.col(i+1).conjugate(); } diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h b/src/eigen/Eigen/src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h rename to src/eigen/Eigen/src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/HessenbergDecomposition.h b/src/eigen/Eigen/src/Eigenvalues/HessenbergDecomposition.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/HessenbergDecomposition.h rename to src/eigen/Eigen/src/Eigenvalues/HessenbergDecomposition.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/MatrixBaseEigenvalues.h b/src/eigen/Eigen/src/Eigenvalues/MatrixBaseEigenvalues.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/MatrixBaseEigenvalues.h rename to src/eigen/Eigen/src/Eigenvalues/MatrixBaseEigenvalues.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/RealQZ.h b/src/eigen/Eigen/src/Eigenvalues/RealQZ.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/RealQZ.h rename to src/eigen/Eigen/src/Eigenvalues/RealQZ.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/RealSchur.h b/src/eigen/Eigen/src/Eigenvalues/RealSchur.h similarity index 99% rename from xs/src/eigen/Eigen/src/Eigenvalues/RealSchur.h rename to src/eigen/Eigen/src/Eigenvalues/RealSchur.h index f5c86041da..17ea903f5f 100644 --- a/xs/src/eigen/Eigen/src/Eigenvalues/RealSchur.h +++ b/src/eigen/Eigen/src/Eigenvalues/RealSchur.h @@ -303,7 +303,7 @@ RealSchur& RealSchur::computeFromHessenberg(const HessMa Scalar exshift(0); // sum of exceptional shifts Scalar norm = computeNormOfT(); - if(norm!=0) + if(norm!=Scalar(0)) { while (iu >= 0) { @@ -327,7 +327,7 @@ RealSchur& RealSchur::computeFromHessenberg(const HessMa else // No convergence yet { // The firstHouseholderVector vector has to be initialized to something to get rid of a silly GCC warning (-O1 -Wall -DNDEBUG ) - Vector3s firstHouseholderVector(0,0,0), shiftInfo; + Vector3s firstHouseholderVector = Vector3s::Zero(), shiftInfo; computeShift(iu, iter, exshift, shiftInfo); iter = iter + 1; totalIter = totalIter + 1; diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/RealSchur_LAPACKE.h b/src/eigen/Eigen/src/Eigenvalues/RealSchur_LAPACKE.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/RealSchur_LAPACKE.h rename to src/eigen/Eigen/src/Eigenvalues/RealSchur_LAPACKE.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h b/src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h rename to src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h b/src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h similarity index 81% rename from xs/src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h rename to src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h index 3891cf8833..b0c947dc07 100644 --- a/xs/src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h +++ b/src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h @@ -37,7 +37,7 @@ namespace Eigen { /** \internal Specialization for the data types supported by LAPACKe */ -#define EIGEN_LAPACKE_EIG_SELFADJ(EIGTYPE, LAPACKE_TYPE, LAPACKE_RTYPE, LAPACKE_NAME, EIGCOLROW, LAPACKE_COLROW ) \ +#define EIGEN_LAPACKE_EIG_SELFADJ_2(EIGTYPE, LAPACKE_TYPE, LAPACKE_RTYPE, LAPACKE_NAME, EIGCOLROW ) \ template<> template inline \ SelfAdjointEigenSolver >& \ SelfAdjointEigenSolver >::compute(const EigenBase& matrix, int options) \ @@ -47,7 +47,7 @@ SelfAdjointEigenSolver >::compute(c && (options&EigVecMask)!=EigVecMask \ && "invalid option parameter"); \ bool computeEigenvectors = (options&ComputeEigenvectors)==ComputeEigenvectors; \ - lapack_int n = internal::convert_index(matrix.cols()), lda, matrix_order, info; \ + lapack_int n = internal::convert_index(matrix.cols()), lda, info; \ m_eivalues.resize(n,1); \ m_subdiag.resize(n-1); \ m_eivec = matrix; \ @@ -63,27 +63,24 @@ SelfAdjointEigenSolver >::compute(c } \ \ lda = internal::convert_index(m_eivec.outerStride()); \ - matrix_order=LAPACKE_COLROW; \ char jobz, uplo='L'/*, range='A'*/; \ jobz = computeEigenvectors ? 'V' : 'N'; \ \ - info = LAPACKE_##LAPACKE_NAME( matrix_order, jobz, uplo, n, (LAPACKE_TYPE*)m_eivec.data(), lda, (LAPACKE_RTYPE*)m_eivalues.data() ); \ + info = LAPACKE_##LAPACKE_NAME( LAPACK_COL_MAJOR, jobz, uplo, n, (LAPACKE_TYPE*)m_eivec.data(), lda, (LAPACKE_RTYPE*)m_eivalues.data() ); \ m_info = (info==0) ? Success : NoConvergence; \ m_isInitialized = true; \ m_eigenvectorsOk = computeEigenvectors; \ return *this; \ } +#define EIGEN_LAPACKE_EIG_SELFADJ(EIGTYPE, LAPACKE_TYPE, LAPACKE_RTYPE, LAPACKE_NAME ) \ + EIGEN_LAPACKE_EIG_SELFADJ_2(EIGTYPE, LAPACKE_TYPE, LAPACKE_RTYPE, LAPACKE_NAME, ColMajor ) \ + EIGEN_LAPACKE_EIG_SELFADJ_2(EIGTYPE, LAPACKE_TYPE, LAPACKE_RTYPE, LAPACKE_NAME, RowMajor ) -EIGEN_LAPACKE_EIG_SELFADJ(double, double, double, dsyev, ColMajor, LAPACK_COL_MAJOR) -EIGEN_LAPACKE_EIG_SELFADJ(float, float, float, ssyev, ColMajor, LAPACK_COL_MAJOR) -EIGEN_LAPACKE_EIG_SELFADJ(dcomplex, lapack_complex_double, double, zheev, ColMajor, LAPACK_COL_MAJOR) -EIGEN_LAPACKE_EIG_SELFADJ(scomplex, lapack_complex_float, float, cheev, ColMajor, LAPACK_COL_MAJOR) - -EIGEN_LAPACKE_EIG_SELFADJ(double, double, double, dsyev, RowMajor, LAPACK_ROW_MAJOR) -EIGEN_LAPACKE_EIG_SELFADJ(float, float, float, ssyev, RowMajor, LAPACK_ROW_MAJOR) -EIGEN_LAPACKE_EIG_SELFADJ(dcomplex, lapack_complex_double, double, zheev, RowMajor, LAPACK_ROW_MAJOR) -EIGEN_LAPACKE_EIG_SELFADJ(scomplex, lapack_complex_float, float, cheev, RowMajor, LAPACK_ROW_MAJOR) +EIGEN_LAPACKE_EIG_SELFADJ(double, double, double, dsyev) +EIGEN_LAPACKE_EIG_SELFADJ(float, float, float, ssyev) +EIGEN_LAPACKE_EIG_SELFADJ(dcomplex, lapack_complex_double, double, zheev) +EIGEN_LAPACKE_EIG_SELFADJ(scomplex, lapack_complex_float, float, cheev) } // end namespace Eigen diff --git a/xs/src/eigen/Eigen/src/Eigenvalues/Tridiagonalization.h b/src/eigen/Eigen/src/Eigenvalues/Tridiagonalization.h similarity index 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/Tridiagonalization.h rename to src/eigen/Eigen/src/Eigenvalues/Tridiagonalization.h diff --git a/xs/src/eigen/Eigen/src/Geometry/AlignedBox.h b/src/eigen/Eigen/src/Geometry/AlignedBox.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/AlignedBox.h rename to src/eigen/Eigen/src/Geometry/AlignedBox.h diff --git a/xs/src/eigen/Eigen/src/Geometry/AngleAxis.h b/src/eigen/Eigen/src/Geometry/AngleAxis.h similarity index 99% rename from xs/src/eigen/Eigen/src/Geometry/AngleAxis.h rename to src/eigen/Eigen/src/Geometry/AngleAxis.h index 0af3c1b089..83ee1be461 100644 --- a/xs/src/eigen/Eigen/src/Geometry/AngleAxis.h +++ b/src/eigen/Eigen/src/Geometry/AngleAxis.h @@ -178,7 +178,7 @@ EIGEN_DEVICE_FUNC AngleAxis& AngleAxis::operator=(const Quaterni if (n != Scalar(0)) { m_angle = Scalar(2)*atan2(n, abs(q.w())); - if(q.w() < 0) + if(q.w() < Scalar(0)) n = -n; m_axis = q.vec() / n; } diff --git a/xs/src/eigen/Eigen/src/Geometry/EulerAngles.h b/src/eigen/Eigen/src/Geometry/EulerAngles.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/EulerAngles.h rename to src/eigen/Eigen/src/Geometry/EulerAngles.h diff --git a/xs/src/eigen/Eigen/src/Geometry/Homogeneous.h b/src/eigen/Eigen/src/Geometry/Homogeneous.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/Homogeneous.h rename to src/eigen/Eigen/src/Geometry/Homogeneous.h diff --git a/xs/src/eigen/Eigen/src/Geometry/Hyperplane.h b/src/eigen/Eigen/src/Geometry/Hyperplane.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/Hyperplane.h rename to src/eigen/Eigen/src/Geometry/Hyperplane.h diff --git a/xs/src/eigen/Eigen/src/Geometry/OrthoMethods.h b/src/eigen/Eigen/src/Geometry/OrthoMethods.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/OrthoMethods.h rename to src/eigen/Eigen/src/Geometry/OrthoMethods.h diff --git a/xs/src/eigen/Eigen/src/Geometry/ParametrizedLine.h b/src/eigen/Eigen/src/Geometry/ParametrizedLine.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/ParametrizedLine.h rename to src/eigen/Eigen/src/Geometry/ParametrizedLine.h diff --git a/xs/src/eigen/Eigen/src/Geometry/Quaternion.h b/src/eigen/Eigen/src/Geometry/Quaternion.h similarity index 95% rename from xs/src/eigen/Eigen/src/Geometry/Quaternion.h rename to src/eigen/Eigen/src/Geometry/Quaternion.h index f6ef1bcf62..c3fd8c3e0f 100644 --- a/xs/src/eigen/Eigen/src/Geometry/Quaternion.h +++ b/src/eigen/Eigen/src/Geometry/Quaternion.h @@ -43,6 +43,11 @@ class QuaternionBase : public RotationBase typedef typename internal::traits::Scalar Scalar; typedef typename NumTraits::Real RealScalar; typedef typename internal::traits::Coefficients Coefficients; + typedef typename Coefficients::CoeffReturnType CoeffReturnType; + typedef typename internal::conditional::Flags&LvalueBit), + Scalar&, CoeffReturnType>::type NonConstCoeffReturnType; + + enum { Flags = Eigen::internal::traits::Flags }; @@ -58,22 +63,22 @@ class QuaternionBase : public RotationBase /** \returns the \c x coefficient */ - EIGEN_DEVICE_FUNC inline Scalar x() const { return this->derived().coeffs().coeff(0); } + EIGEN_DEVICE_FUNC inline CoeffReturnType x() const { return this->derived().coeffs().coeff(0); } /** \returns the \c y coefficient */ - EIGEN_DEVICE_FUNC inline Scalar y() const { return this->derived().coeffs().coeff(1); } + EIGEN_DEVICE_FUNC inline CoeffReturnType y() const { return this->derived().coeffs().coeff(1); } /** \returns the \c z coefficient */ - EIGEN_DEVICE_FUNC inline Scalar z() const { return this->derived().coeffs().coeff(2); } + EIGEN_DEVICE_FUNC inline CoeffReturnType z() const { return this->derived().coeffs().coeff(2); } /** \returns the \c w coefficient */ - EIGEN_DEVICE_FUNC inline Scalar w() const { return this->derived().coeffs().coeff(3); } + EIGEN_DEVICE_FUNC inline CoeffReturnType w() const { return this->derived().coeffs().coeff(3); } - /** \returns a reference to the \c x coefficient */ - EIGEN_DEVICE_FUNC inline Scalar& x() { return this->derived().coeffs().coeffRef(0); } - /** \returns a reference to the \c y coefficient */ - EIGEN_DEVICE_FUNC inline Scalar& y() { return this->derived().coeffs().coeffRef(1); } - /** \returns a reference to the \c z coefficient */ - EIGEN_DEVICE_FUNC inline Scalar& z() { return this->derived().coeffs().coeffRef(2); } - /** \returns a reference to the \c w coefficient */ - EIGEN_DEVICE_FUNC inline Scalar& w() { return this->derived().coeffs().coeffRef(3); } + /** \returns a reference to the \c x coefficient (if Derived is a non-const lvalue) */ + EIGEN_DEVICE_FUNC inline NonConstCoeffReturnType x() { return this->derived().coeffs().x(); } + /** \returns a reference to the \c y coefficient (if Derived is a non-const lvalue) */ + EIGEN_DEVICE_FUNC inline NonConstCoeffReturnType y() { return this->derived().coeffs().y(); } + /** \returns a reference to the \c z coefficient (if Derived is a non-const lvalue) */ + EIGEN_DEVICE_FUNC inline NonConstCoeffReturnType z() { return this->derived().coeffs().z(); } + /** \returns a reference to the \c w coefficient (if Derived is a non-const lvalue) */ + EIGEN_DEVICE_FUNC inline NonConstCoeffReturnType w() { return this->derived().coeffs().w(); } /** \returns a read-only vector expression of the imaginary part (x,y,z) */ EIGEN_DEVICE_FUNC inline const VectorBlock vec() const { return coeffs().template head<3>(); } @@ -423,7 +428,7 @@ typedef Map, Aligned> QuaternionMapAlignedd; // Generic Quaternion * Quaternion product // This product can be specialized for a given architecture via the Arch template argument. namespace internal { -template struct quat_product +template struct quat_product { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Quaternion run(const QuaternionBase& a, const QuaternionBase& b){ return Quaternion @@ -446,8 +451,7 @@ QuaternionBase::operator* (const QuaternionBase& other) c EIGEN_STATIC_ASSERT((internal::is_same::value), YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) return internal::quat_product::Scalar, - EIGEN_PLAIN_ENUM_MIN(internal::traits::Alignment, internal::traits::Alignment)>::run(*this, other); + typename internal::traits::Scalar>::run(*this, other); } /** \sa operator*(Quaternion) */ @@ -672,7 +676,7 @@ EIGEN_DEVICE_FUNC inline Quaternion::Scalar> // Generic conjugate of a Quaternion namespace internal { -template struct quat_conj +template struct quat_conj { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Quaternion run(const QuaternionBase& q){ return Quaternion(q.w(),-q.x(),-q.y(),-q.z()); @@ -691,8 +695,7 @@ EIGEN_DEVICE_FUNC inline Quaternion::Scalar> QuaternionBase::conjugate() const { return internal::quat_conj::Scalar, - internal::traits::Alignment>::run(*this); + typename internal::traits::Scalar>::run(*this); } diff --git a/xs/src/eigen/Eigen/src/Geometry/Rotation2D.h b/src/eigen/Eigen/src/Geometry/Rotation2D.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/Rotation2D.h rename to src/eigen/Eigen/src/Geometry/Rotation2D.h diff --git a/xs/src/eigen/Eigen/src/Geometry/RotationBase.h b/src/eigen/Eigen/src/Geometry/RotationBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/RotationBase.h rename to src/eigen/Eigen/src/Geometry/RotationBase.h diff --git a/xs/src/eigen/Eigen/src/Geometry/Scaling.h b/src/eigen/Eigen/src/Geometry/Scaling.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/Scaling.h rename to src/eigen/Eigen/src/Geometry/Scaling.h diff --git a/xs/src/eigen/Eigen/src/Geometry/Transform.h b/src/eigen/Eigen/src/Geometry/Transform.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/Transform.h rename to src/eigen/Eigen/src/Geometry/Transform.h diff --git a/xs/src/eigen/Eigen/src/Geometry/Translation.h b/src/eigen/Eigen/src/Geometry/Translation.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/Translation.h rename to src/eigen/Eigen/src/Geometry/Translation.h diff --git a/xs/src/eigen/Eigen/src/Geometry/Umeyama.h b/src/eigen/Eigen/src/Geometry/Umeyama.h similarity index 100% rename from xs/src/eigen/Eigen/src/Geometry/Umeyama.h rename to src/eigen/Eigen/src/Geometry/Umeyama.h diff --git a/xs/src/eigen/Eigen/src/Geometry/arch/Geometry_SSE.h b/src/eigen/Eigen/src/Geometry/arch/Geometry_SSE.h similarity index 65% rename from xs/src/eigen/Eigen/src/Geometry/arch/Geometry_SSE.h rename to src/eigen/Eigen/src/Geometry/arch/Geometry_SSE.h index 1a86ff837b..f68cab5834 100644 --- a/xs/src/eigen/Eigen/src/Geometry/arch/Geometry_SSE.h +++ b/src/eigen/Eigen/src/Geometry/arch/Geometry_SSE.h @@ -16,17 +16,23 @@ namespace Eigen { namespace internal { template -struct quat_product +struct quat_product { + enum { + AAlignment = traits::Alignment, + BAlignment = traits::Alignment, + ResAlignment = traits >::Alignment + }; static inline Quaternion run(const QuaternionBase& _a, const QuaternionBase& _b) { Quaternion res; const __m128 mask = _mm_setr_ps(0.f,0.f,0.f,-0.f); - __m128 a = _a.coeffs().template packet(0); - __m128 b = _b.coeffs().template packet(0); + __m128 a = _a.coeffs().template packet(0); + __m128 b = _b.coeffs().template packet(0); __m128 s1 = _mm_mul_ps(vec4f_swizzle1(a,1,2,0,2),vec4f_swizzle1(b,2,0,1,2)); __m128 s2 = _mm_mul_ps(vec4f_swizzle1(a,3,3,3,1),vec4f_swizzle1(b,0,1,2,1)); - pstore(&res.x(), + pstoret( + &res.x(), _mm_add_ps(_mm_sub_ps(_mm_mul_ps(a,vec4f_swizzle1(b,3,3,3,3)), _mm_mul_ps(vec4f_swizzle1(a,2,0,1,0), vec4f_swizzle1(b,1,2,0,0))), @@ -36,14 +42,17 @@ struct quat_product } }; -template -struct quat_conj +template +struct quat_conj { + enum { + ResAlignment = traits >::Alignment + }; static inline Quaternion run(const QuaternionBase& q) { Quaternion res; const __m128 mask = _mm_setr_ps(-0.f,-0.f,-0.f,0.f); - pstore(&res.x(), _mm_xor_ps(mask, q.coeffs().template packet(0))); + pstoret(&res.x(), _mm_xor_ps(mask, q.coeffs().template packet::Alignment>(0))); return res; } }; @@ -52,6 +61,9 @@ struct quat_conj template struct cross3_impl { + enum { + ResAlignment = traits::type>::Alignment + }; static inline typename plain_matrix_type::type run(const VectorLhs& lhs, const VectorRhs& rhs) { @@ -60,7 +72,7 @@ struct cross3_impl __m128 mul1=_mm_mul_ps(vec4f_swizzle1(a,1,2,0,3),vec4f_swizzle1(b,2,0,1,3)); __m128 mul2=_mm_mul_ps(vec4f_swizzle1(a,2,0,1,3),vec4f_swizzle1(b,1,2,0,3)); typename plain_matrix_type::type res; - pstore(&res.x(),_mm_sub_ps(mul1,mul2)); + pstoret(&res.x(),_mm_sub_ps(mul1,mul2)); return res; } }; @@ -68,9 +80,14 @@ struct cross3_impl -template -struct quat_product +template +struct quat_product { + enum { + BAlignment = traits::Alignment, + ResAlignment = traits >::Alignment + }; + static inline Quaternion run(const QuaternionBase& _a, const QuaternionBase& _b) { const Packet2d mask = _mm_castsi128_pd(_mm_set_epi32(0x0,0x0,0x80000000,0x0)); @@ -78,8 +95,8 @@ struct quat_product Quaternion res; const double* a = _a.coeffs().data(); - Packet2d b_xy = _b.coeffs().template packet(0); - Packet2d b_zw = _b.coeffs().template packet(2); + Packet2d b_xy = _b.coeffs().template packet(0); + Packet2d b_zw = _b.coeffs().template packet(2); Packet2d a_xx = pset1(a[0]); Packet2d a_yy = pset1(a[1]); Packet2d a_zz = pset1(a[2]); @@ -97,9 +114,9 @@ struct quat_product t2 = psub(pmul(a_zz, b_xy), pmul(a_xx, b_zw)); #ifdef EIGEN_VECTORIZE_SSE3 EIGEN_UNUSED_VARIABLE(mask) - pstore(&res.x(), _mm_addsub_pd(t1, preverse(t2))); + pstoret(&res.x(), _mm_addsub_pd(t1, preverse(t2))); #else - pstore(&res.x(), padd(t1, pxor(mask,preverse(t2)))); + pstoret(&res.x(), padd(t1, pxor(mask,preverse(t2)))); #endif /* @@ -111,25 +128,28 @@ struct quat_product t2 = padd(pmul(a_zz, b_zw), pmul(a_xx, b_xy)); #ifdef EIGEN_VECTORIZE_SSE3 EIGEN_UNUSED_VARIABLE(mask) - pstore(&res.z(), preverse(_mm_addsub_pd(preverse(t1), t2))); + pstoret(&res.z(), preverse(_mm_addsub_pd(preverse(t1), t2))); #else - pstore(&res.z(), psub(t1, pxor(mask,preverse(t2)))); + pstoret(&res.z(), psub(t1, pxor(mask,preverse(t2)))); #endif return res; } }; -template -struct quat_conj +template +struct quat_conj { + enum { + ResAlignment = traits >::Alignment + }; static inline Quaternion run(const QuaternionBase& q) { Quaternion res; const __m128d mask0 = _mm_setr_pd(-0.,-0.); const __m128d mask2 = _mm_setr_pd(-0.,0.); - pstore(&res.x(), _mm_xor_pd(mask0, q.coeffs().template packet(0))); - pstore(&res.z(), _mm_xor_pd(mask2, q.coeffs().template packet(2))); + pstoret(&res.x(), _mm_xor_pd(mask0, q.coeffs().template packet::Alignment>(0))); + pstoret(&res.z(), _mm_xor_pd(mask2, q.coeffs().template packet::Alignment>(2))); return res; } }; diff --git a/xs/src/eigen/Eigen/src/Householder/BlockHouseholder.h b/src/eigen/Eigen/src/Householder/BlockHouseholder.h similarity index 100% rename from xs/src/eigen/Eigen/src/Householder/BlockHouseholder.h rename to src/eigen/Eigen/src/Householder/BlockHouseholder.h diff --git a/xs/src/eigen/Eigen/src/Householder/Householder.h b/src/eigen/Eigen/src/Householder/Householder.h similarity index 100% rename from xs/src/eigen/Eigen/src/Householder/Householder.h rename to src/eigen/Eigen/src/Householder/Householder.h diff --git a/xs/src/eigen/Eigen/src/Householder/HouseholderSequence.h b/src/eigen/Eigen/src/Householder/HouseholderSequence.h similarity index 100% rename from xs/src/eigen/Eigen/src/Householder/HouseholderSequence.h rename to src/eigen/Eigen/src/Householder/HouseholderSequence.h diff --git a/xs/src/eigen/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h b/src/eigen/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h similarity index 89% rename from xs/src/eigen/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h rename to src/eigen/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h index 358444afff..f66c846ef7 100644 --- a/xs/src/eigen/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h +++ b/src/eigen/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h @@ -152,13 +152,28 @@ class LeastSquareDiagonalPreconditioner : public DiagonalPreconditioner<_Scalar> { // Compute the inverse squared-norm of each column of mat m_invdiag.resize(mat.cols()); - for(Index j=0; j0) - m_invdiag(j) = RealScalar(1)/sum; - else - m_invdiag(j) = RealScalar(1); + m_invdiag.setZero(); + for(Index j=0; jRealScalar(0)) + m_invdiag(j) = RealScalar(1)/numext::real(m_invdiag(j)); + } + else + { + for(Index j=0; jRealScalar(0)) + m_invdiag(j) = RealScalar(1)/sum; + else + m_invdiag(j) = RealScalar(1); + } } Base::m_isInitialized = true; return *this; diff --git a/xs/src/eigen/Eigen/src/IterativeLinearSolvers/BiCGSTAB.h b/src/eigen/Eigen/src/IterativeLinearSolvers/BiCGSTAB.h similarity index 100% rename from xs/src/eigen/Eigen/src/IterativeLinearSolvers/BiCGSTAB.h rename to src/eigen/Eigen/src/IterativeLinearSolvers/BiCGSTAB.h diff --git a/xs/src/eigen/Eigen/src/IterativeLinearSolvers/ConjugateGradient.h b/src/eigen/Eigen/src/IterativeLinearSolvers/ConjugateGradient.h similarity index 100% rename from xs/src/eigen/Eigen/src/IterativeLinearSolvers/ConjugateGradient.h rename to src/eigen/Eigen/src/IterativeLinearSolvers/ConjugateGradient.h diff --git a/xs/src/eigen/Eigen/src/IterativeLinearSolvers/IncompleteCholesky.h b/src/eigen/Eigen/src/IterativeLinearSolvers/IncompleteCholesky.h similarity index 100% rename from xs/src/eigen/Eigen/src/IterativeLinearSolvers/IncompleteCholesky.h rename to src/eigen/Eigen/src/IterativeLinearSolvers/IncompleteCholesky.h diff --git a/xs/src/eigen/Eigen/src/IterativeLinearSolvers/IncompleteLUT.h b/src/eigen/Eigen/src/IterativeLinearSolvers/IncompleteLUT.h similarity index 100% rename from xs/src/eigen/Eigen/src/IterativeLinearSolvers/IncompleteLUT.h rename to src/eigen/Eigen/src/IterativeLinearSolvers/IncompleteLUT.h diff --git a/xs/src/eigen/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h b/src/eigen/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h rename to src/eigen/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h diff --git a/xs/src/eigen/Eigen/src/IterativeLinearSolvers/LeastSquareConjugateGradient.h b/src/eigen/Eigen/src/IterativeLinearSolvers/LeastSquareConjugateGradient.h similarity index 100% rename from xs/src/eigen/Eigen/src/IterativeLinearSolvers/LeastSquareConjugateGradient.h rename to src/eigen/Eigen/src/IterativeLinearSolvers/LeastSquareConjugateGradient.h diff --git a/xs/src/eigen/Eigen/src/IterativeLinearSolvers/SolveWithGuess.h b/src/eigen/Eigen/src/IterativeLinearSolvers/SolveWithGuess.h similarity index 100% rename from xs/src/eigen/Eigen/src/IterativeLinearSolvers/SolveWithGuess.h rename to src/eigen/Eigen/src/IterativeLinearSolvers/SolveWithGuess.h diff --git a/xs/src/eigen/Eigen/src/Jacobi/Jacobi.h b/src/eigen/Eigen/src/Jacobi/Jacobi.h similarity index 70% rename from xs/src/eigen/Eigen/src/Jacobi/Jacobi.h rename to src/eigen/Eigen/src/Jacobi/Jacobi.h index d25af8e904..437e666a38 100644 --- a/xs/src/eigen/Eigen/src/Jacobi/Jacobi.h +++ b/src/eigen/Eigen/src/Jacobi/Jacobi.h @@ -298,12 +298,144 @@ inline void MatrixBase::applyOnTheRight(Index p, Index q, const JacobiR } namespace internal { + +template +struct apply_rotation_in_the_plane_selector +{ + static inline void run(Scalar *x, Index incrx, Scalar *y, Index incry, Index size, OtherScalar c, OtherScalar s) + { + for(Index i=0; i +struct apply_rotation_in_the_plane_selector +{ + static inline void run(Scalar *x, Index incrx, Scalar *y, Index incry, Index size, OtherScalar c, OtherScalar s) + { + enum { + PacketSize = packet_traits::size, + OtherPacketSize = packet_traits::size + }; + typedef typename packet_traits::type Packet; + typedef typename packet_traits::type OtherPacket; + + /*** dynamic-size vectorized paths ***/ + if(SizeAtCompileTime == Dynamic && ((incrx==1 && incry==1) || PacketSize == 1)) + { + // both vectors are sequentially stored in memory => vectorization + enum { Peeling = 2 }; + + Index alignedStart = internal::first_default_aligned(y, size); + Index alignedEnd = alignedStart + ((size-alignedStart)/PacketSize)*PacketSize; + + const OtherPacket pc = pset1(c); + const OtherPacket ps = pset1(s); + conj_helper::IsComplex,false> pcj; + conj_helper pm; + + for(Index i=0; i(px); + Packet yi = pload(py); + pstore(px, padd(pm.pmul(pc,xi),pcj.pmul(ps,yi))); + pstore(py, psub(pcj.pmul(pc,yi),pm.pmul(ps,xi))); + px += PacketSize; + py += PacketSize; + } + } + else + { + Index peelingEnd = alignedStart + ((size-alignedStart)/(Peeling*PacketSize))*(Peeling*PacketSize); + for(Index i=alignedStart; i(px); + Packet xi1 = ploadu(px+PacketSize); + Packet yi = pload (py); + Packet yi1 = pload (py+PacketSize); + pstoreu(px, padd(pm.pmul(pc,xi),pcj.pmul(ps,yi))); + pstoreu(px+PacketSize, padd(pm.pmul(pc,xi1),pcj.pmul(ps,yi1))); + pstore (py, psub(pcj.pmul(pc,yi),pm.pmul(ps,xi))); + pstore (py+PacketSize, psub(pcj.pmul(pc,yi1),pm.pmul(ps,xi1))); + px += Peeling*PacketSize; + py += Peeling*PacketSize; + } + if(alignedEnd!=peelingEnd) + { + Packet xi = ploadu(x+peelingEnd); + Packet yi = pload (y+peelingEnd); + pstoreu(x+peelingEnd, padd(pm.pmul(pc,xi),pcj.pmul(ps,yi))); + pstore (y+peelingEnd, psub(pcj.pmul(pc,yi),pm.pmul(ps,xi))); + } + } + + for(Index i=alignedEnd; i0) // FIXME should be compared to the required alignment + { + const OtherPacket pc = pset1(c); + const OtherPacket ps = pset1(s); + conj_helper::IsComplex,false> pcj; + conj_helper pm; + Scalar* EIGEN_RESTRICT px = x; + Scalar* EIGEN_RESTRICT py = y; + for(Index i=0; i(px); + Packet yi = pload(py); + pstore(px, padd(pm.pmul(pc,xi),pcj.pmul(ps,yi))); + pstore(py, psub(pcj.pmul(pc,yi),pm.pmul(ps,xi))); + px += PacketSize; + py += PacketSize; + } + } + + /*** non-vectorized path ***/ + else + { + apply_rotation_in_the_plane_selector::run(x,incrx,y,incry,size,c,s); + } + } +}; + template void /*EIGEN_DONT_INLINE*/ apply_rotation_in_the_plane(DenseBase& xpr_x, DenseBase& xpr_y, const JacobiRotation& j) { typedef typename VectorX::Scalar Scalar; - enum { PacketSize = packet_traits::size }; - typedef typename packet_traits::type Packet; + const bool Vectorizable = (VectorX::Flags & VectorY::Flags & PacketAccessBit) + && (int(packet_traits::size) == int(packet_traits::size)); + eigen_assert(xpr_x.size() == xpr_y.size()); Index size = xpr_x.size(); Index incrx = xpr_x.derived().innerStride(); @@ -317,113 +449,11 @@ void /*EIGEN_DONT_INLINE*/ apply_rotation_in_the_plane(DenseBase& xpr_x if (c==OtherScalar(1) && s==OtherScalar(0)) return; - /*** dynamic-size vectorized paths ***/ - - if(VectorX::SizeAtCompileTime == Dynamic && - (VectorX::Flags & VectorY::Flags & PacketAccessBit) && - ((incrx==1 && incry==1) || PacketSize == 1)) - { - // both vectors are sequentially stored in memory => vectorization - enum { Peeling = 2 }; - - Index alignedStart = internal::first_default_aligned(y, size); - Index alignedEnd = alignedStart + ((size-alignedStart)/PacketSize)*PacketSize; - - const Packet pc = pset1(c); - const Packet ps = pset1(s); - conj_helper::IsComplex,false> pcj; - - for(Index i=0; i(px); - Packet yi = pload(py); - pstore(px, padd(pmul(pc,xi),pcj.pmul(ps,yi))); - pstore(py, psub(pcj.pmul(pc,yi),pmul(ps,xi))); - px += PacketSize; - py += PacketSize; - } - } - else - { - Index peelingEnd = alignedStart + ((size-alignedStart)/(Peeling*PacketSize))*(Peeling*PacketSize); - for(Index i=alignedStart; i(px); - Packet xi1 = ploadu(px+PacketSize); - Packet yi = pload (py); - Packet yi1 = pload (py+PacketSize); - pstoreu(px, padd(pmul(pc,xi),pcj.pmul(ps,yi))); - pstoreu(px+PacketSize, padd(pmul(pc,xi1),pcj.pmul(ps,yi1))); - pstore (py, psub(pcj.pmul(pc,yi),pmul(ps,xi))); - pstore (py+PacketSize, psub(pcj.pmul(pc,yi1),pmul(ps,xi1))); - px += Peeling*PacketSize; - py += Peeling*PacketSize; - } - if(alignedEnd!=peelingEnd) - { - Packet xi = ploadu(x+peelingEnd); - Packet yi = pload (y+peelingEnd); - pstoreu(x+peelingEnd, padd(pmul(pc,xi),pcj.pmul(ps,yi))); - pstore (y+peelingEnd, psub(pcj.pmul(pc,yi),pmul(ps,xi))); - } - } - - for(Index i=alignedEnd; i::Alignment, evaluator::Alignment)>0)) // FIXME should be compared to the required alignment - { - const Packet pc = pset1(c); - const Packet ps = pset1(s); - conj_helper::IsComplex,false> pcj; - Scalar* EIGEN_RESTRICT px = x; - Scalar* EIGEN_RESTRICT py = y; - for(Index i=0; i(px); - Packet yi = pload(py); - pstore(px, padd(pmul(pc,xi),pcj.pmul(ps,yi))); - pstore(py, psub(pcj.pmul(pc,yi),pmul(ps,xi))); - px += PacketSize; - py += PacketSize; - } - } - - /*** non-vectorized path ***/ - else - { - for(Index i=0; i::Alignment, evaluator::Alignment), + Vectorizable>::run(x,incrx,y,incry,size,c,s); } } // end namespace internal diff --git a/xs/src/eigen/Eigen/src/LU/Determinant.h b/src/eigen/Eigen/src/LU/Determinant.h similarity index 100% rename from xs/src/eigen/Eigen/src/LU/Determinant.h rename to src/eigen/Eigen/src/LU/Determinant.h diff --git a/xs/src/eigen/Eigen/src/LU/FullPivLU.h b/src/eigen/Eigen/src/LU/FullPivLU.h similarity index 100% rename from xs/src/eigen/Eigen/src/LU/FullPivLU.h rename to src/eigen/Eigen/src/LU/FullPivLU.h diff --git a/xs/src/eigen/Eigen/src/LU/InverseImpl.h b/src/eigen/Eigen/src/LU/InverseImpl.h similarity index 99% rename from xs/src/eigen/Eigen/src/LU/InverseImpl.h rename to src/eigen/Eigen/src/LU/InverseImpl.h index 018f99b58f..f49f233600 100644 --- a/xs/src/eigen/Eigen/src/LU/InverseImpl.h +++ b/src/eigen/Eigen/src/LU/InverseImpl.h @@ -404,7 +404,7 @@ inline void MatrixBase::computeInverseWithCheck( const RealScalar& absDeterminantThreshold ) const { - RealScalar determinant; + Scalar determinant; // i'd love to put some static assertions there, but SFINAE means that they have no effect... eigen_assert(rows() == cols()); computeInverseAndDetWithCheck(inverse,determinant,invertible,absDeterminantThreshold); diff --git a/xs/src/eigen/Eigen/src/LU/PartialPivLU.h b/src/eigen/Eigen/src/LU/PartialPivLU.h similarity index 100% rename from xs/src/eigen/Eigen/src/LU/PartialPivLU.h rename to src/eigen/Eigen/src/LU/PartialPivLU.h diff --git a/xs/src/eigen/Eigen/src/LU/PartialPivLU_LAPACKE.h b/src/eigen/Eigen/src/LU/PartialPivLU_LAPACKE.h similarity index 100% rename from xs/src/eigen/Eigen/src/LU/PartialPivLU_LAPACKE.h rename to src/eigen/Eigen/src/LU/PartialPivLU_LAPACKE.h diff --git a/xs/src/eigen/Eigen/src/LU/arch/Inverse_SSE.h b/src/eigen/Eigen/src/LU/arch/Inverse_SSE.h similarity index 100% rename from xs/src/eigen/Eigen/src/LU/arch/Inverse_SSE.h rename to src/eigen/Eigen/src/LU/arch/Inverse_SSE.h diff --git a/xs/src/eigen/Eigen/src/MetisSupport/MetisSupport.h b/src/eigen/Eigen/src/MetisSupport/MetisSupport.h similarity index 100% rename from xs/src/eigen/Eigen/src/MetisSupport/MetisSupport.h rename to src/eigen/Eigen/src/MetisSupport/MetisSupport.h diff --git a/xs/src/eigen/Eigen/src/OrderingMethods/Amd.h b/src/eigen/Eigen/src/OrderingMethods/Amd.h similarity index 100% rename from xs/src/eigen/Eigen/src/OrderingMethods/Amd.h rename to src/eigen/Eigen/src/OrderingMethods/Amd.h diff --git a/xs/src/eigen/Eigen/src/OrderingMethods/Eigen_Colamd.h b/src/eigen/Eigen/src/OrderingMethods/Eigen_Colamd.h similarity index 99% rename from xs/src/eigen/Eigen/src/OrderingMethods/Eigen_Colamd.h rename to src/eigen/Eigen/src/OrderingMethods/Eigen_Colamd.h index 933cd564bc..da85b4d6ea 100644 --- a/xs/src/eigen/Eigen/src/OrderingMethods/Eigen_Colamd.h +++ b/src/eigen/Eigen/src/OrderingMethods/Eigen_Colamd.h @@ -1004,7 +1004,7 @@ static IndexType find_ordering /* return the number of garbage collections */ COLAMD_ASSERT (head [min_score] >= COLAMD_EMPTY) ; /* get pivot column from head of minimum degree list */ - while (head [min_score] == COLAMD_EMPTY && min_score < n_col) + while (min_score < n_col && head [min_score] == COLAMD_EMPTY) { min_score++ ; } diff --git a/xs/src/eigen/Eigen/src/OrderingMethods/Ordering.h b/src/eigen/Eigen/src/OrderingMethods/Ordering.h similarity index 100% rename from xs/src/eigen/Eigen/src/OrderingMethods/Ordering.h rename to src/eigen/Eigen/src/OrderingMethods/Ordering.h diff --git a/xs/src/eigen/Eigen/src/PaStiXSupport/PaStiXSupport.h b/src/eigen/Eigen/src/PaStiXSupport/PaStiXSupport.h similarity index 96% rename from xs/src/eigen/Eigen/src/PaStiXSupport/PaStiXSupport.h rename to src/eigen/Eigen/src/PaStiXSupport/PaStiXSupport.h index d2ebfd7bbe..160d8a5234 100644 --- a/xs/src/eigen/Eigen/src/PaStiXSupport/PaStiXSupport.h +++ b/src/eigen/Eigen/src/PaStiXSupport/PaStiXSupport.h @@ -64,28 +64,28 @@ namespace internal typedef typename _MatrixType::StorageIndex StorageIndex; }; - void eigen_pastix(pastix_data_t **pastix_data, int pastix_comm, int n, int *ptr, int *idx, float *vals, int *perm, int * invp, float *x, int nbrhs, int *iparm, double *dparm) + inline void eigen_pastix(pastix_data_t **pastix_data, int pastix_comm, int n, int *ptr, int *idx, float *vals, int *perm, int * invp, float *x, int nbrhs, int *iparm, double *dparm) { if (n == 0) { ptr = NULL; idx = NULL; vals = NULL; } if (nbrhs == 0) {x = NULL; nbrhs=1;} s_pastix(pastix_data, pastix_comm, n, ptr, idx, vals, perm, invp, x, nbrhs, iparm, dparm); } - void eigen_pastix(pastix_data_t **pastix_data, int pastix_comm, int n, int *ptr, int *idx, double *vals, int *perm, int * invp, double *x, int nbrhs, int *iparm, double *dparm) + inline void eigen_pastix(pastix_data_t **pastix_data, int pastix_comm, int n, int *ptr, int *idx, double *vals, int *perm, int * invp, double *x, int nbrhs, int *iparm, double *dparm) { if (n == 0) { ptr = NULL; idx = NULL; vals = NULL; } if (nbrhs == 0) {x = NULL; nbrhs=1;} d_pastix(pastix_data, pastix_comm, n, ptr, idx, vals, perm, invp, x, nbrhs, iparm, dparm); } - void eigen_pastix(pastix_data_t **pastix_data, int pastix_comm, int n, int *ptr, int *idx, std::complex *vals, int *perm, int * invp, std::complex *x, int nbrhs, int *iparm, double *dparm) + inline void eigen_pastix(pastix_data_t **pastix_data, int pastix_comm, int n, int *ptr, int *idx, std::complex *vals, int *perm, int * invp, std::complex *x, int nbrhs, int *iparm, double *dparm) { if (n == 0) { ptr = NULL; idx = NULL; vals = NULL; } if (nbrhs == 0) {x = NULL; nbrhs=1;} c_pastix(pastix_data, pastix_comm, n, ptr, idx, reinterpret_cast(vals), perm, invp, reinterpret_cast(x), nbrhs, iparm, dparm); } - void eigen_pastix(pastix_data_t **pastix_data, int pastix_comm, int n, int *ptr, int *idx, std::complex *vals, int *perm, int * invp, std::complex *x, int nbrhs, int *iparm, double *dparm) + inline void eigen_pastix(pastix_data_t **pastix_data, int pastix_comm, int n, int *ptr, int *idx, std::complex *vals, int *perm, int * invp, std::complex *x, int nbrhs, int *iparm, double *dparm) { if (n == 0) { ptr = NULL; idx = NULL; vals = NULL; } if (nbrhs == 0) {x = NULL; nbrhs=1;} diff --git a/xs/src/eigen/Eigen/src/PardisoSupport/PardisoSupport.h b/src/eigen/Eigen/src/PardisoSupport/PardisoSupport.h similarity index 100% rename from xs/src/eigen/Eigen/src/PardisoSupport/PardisoSupport.h rename to src/eigen/Eigen/src/PardisoSupport/PardisoSupport.h diff --git a/xs/src/eigen/Eigen/src/QR/ColPivHouseholderQR.h b/src/eigen/Eigen/src/QR/ColPivHouseholderQR.h similarity index 97% rename from xs/src/eigen/Eigen/src/QR/ColPivHouseholderQR.h rename to src/eigen/Eigen/src/QR/ColPivHouseholderQR.h index 0e47c83325..a7b47d55dc 100644 --- a/xs/src/eigen/Eigen/src/QR/ColPivHouseholderQR.h +++ b/src/eigen/Eigen/src/QR/ColPivHouseholderQR.h @@ -506,8 +506,8 @@ void ColPivHouseholderQR::computeInPlace() m_colNormsUpdated.coeffRef(k) = m_colNormsDirect.coeffRef(k); } - RealScalar threshold_helper = numext::abs2(m_colNormsUpdated.maxCoeff() * NumTraits::epsilon()) / RealScalar(rows); - RealScalar norm_downdate_threshold = numext::sqrt(NumTraits::epsilon()); + RealScalar threshold_helper = numext::abs2(m_colNormsUpdated.maxCoeff() * NumTraits::epsilon()) / RealScalar(rows); + RealScalar norm_downdate_threshold = numext::sqrt(NumTraits::epsilon()); m_nonzero_pivots = size; // the generic case is that in which all pivots are nonzero (invertible case) m_maxpivot = RealScalar(0); @@ -553,12 +553,12 @@ void ColPivHouseholderQR::computeInPlace() // http://www.netlib.org/lapack/lawnspdf/lawn176.pdf // and used in LAPACK routines xGEQPF and xGEQP3. // See lines 278-297 in http://www.netlib.org/lapack/explore-html/dc/df4/sgeqpf_8f_source.html - if (m_colNormsUpdated.coeffRef(j) != 0) { + if (m_colNormsUpdated.coeffRef(j) != RealScalar(0)) { RealScalar temp = abs(m_qr.coeffRef(k, j)) / m_colNormsUpdated.coeffRef(j); temp = (RealScalar(1) + temp) * (RealScalar(1) - temp); - temp = temp < 0 ? 0 : temp; - RealScalar temp2 = temp * numext::abs2(m_colNormsUpdated.coeffRef(j) / - m_colNormsDirect.coeffRef(j)); + temp = temp < RealScalar(0) ? RealScalar(0) : temp; + RealScalar temp2 = temp * numext::abs2(m_colNormsUpdated.coeffRef(j) / + m_colNormsDirect.coeffRef(j)); if (temp2 <= norm_downdate_threshold) { // The updated norm has become too inaccurate so re-compute the column // norm directly. diff --git a/xs/src/eigen/Eigen/src/QR/ColPivHouseholderQR_LAPACKE.h b/src/eigen/Eigen/src/QR/ColPivHouseholderQR_LAPACKE.h similarity index 100% rename from xs/src/eigen/Eigen/src/QR/ColPivHouseholderQR_LAPACKE.h rename to src/eigen/Eigen/src/QR/ColPivHouseholderQR_LAPACKE.h diff --git a/xs/src/eigen/Eigen/src/QR/CompleteOrthogonalDecomposition.h b/src/eigen/Eigen/src/QR/CompleteOrthogonalDecomposition.h similarity index 100% rename from xs/src/eigen/Eigen/src/QR/CompleteOrthogonalDecomposition.h rename to src/eigen/Eigen/src/QR/CompleteOrthogonalDecomposition.h diff --git a/xs/src/eigen/Eigen/src/QR/FullPivHouseholderQR.h b/src/eigen/Eigen/src/QR/FullPivHouseholderQR.h similarity index 100% rename from xs/src/eigen/Eigen/src/QR/FullPivHouseholderQR.h rename to src/eigen/Eigen/src/QR/FullPivHouseholderQR.h diff --git a/xs/src/eigen/Eigen/src/QR/HouseholderQR.h b/src/eigen/Eigen/src/QR/HouseholderQR.h similarity index 100% rename from xs/src/eigen/Eigen/src/QR/HouseholderQR.h rename to src/eigen/Eigen/src/QR/HouseholderQR.h diff --git a/xs/src/eigen/Eigen/src/QR/HouseholderQR_LAPACKE.h b/src/eigen/Eigen/src/QR/HouseholderQR_LAPACKE.h similarity index 100% rename from xs/src/eigen/Eigen/src/QR/HouseholderQR_LAPACKE.h rename to src/eigen/Eigen/src/QR/HouseholderQR_LAPACKE.h diff --git a/xs/src/eigen/Eigen/src/SPQRSupport/SuiteSparseQRSupport.h b/src/eigen/Eigen/src/SPQRSupport/SuiteSparseQRSupport.h similarity index 100% rename from xs/src/eigen/Eigen/src/SPQRSupport/SuiteSparseQRSupport.h rename to src/eigen/Eigen/src/SPQRSupport/SuiteSparseQRSupport.h diff --git a/xs/src/eigen/Eigen/src/SVD/BDCSVD.h b/src/eigen/Eigen/src/SVD/BDCSVD.h similarity index 91% rename from xs/src/eigen/Eigen/src/SVD/BDCSVD.h rename to src/eigen/Eigen/src/SVD/BDCSVD.h index 25fca6f4d4..1134d66e7e 100644 --- a/xs/src/eigen/Eigen/src/SVD/BDCSVD.h +++ b/src/eigen/Eigen/src/SVD/BDCSVD.h @@ -11,7 +11,7 @@ // Copyright (C) 2013 Jean Ceccato // Copyright (C) 2013 Pierre Zoppitelli // Copyright (C) 2013 Jitse Niesen -// Copyright (C) 2014-2016 Gael Guennebaud +// Copyright (C) 2014-2017 Gael Guennebaud // // Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -77,6 +77,7 @@ public: typedef _MatrixType MatrixType; typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; + typedef typename NumTraits::Literal Literal; enum { RowsAtCompileTime = MatrixType::RowsAtCompileTime, ColsAtCompileTime = MatrixType::ColsAtCompileTime, @@ -259,7 +260,7 @@ BDCSVD& BDCSVD::compute(const MatrixType& matrix, unsign //**** step 0 - Copy the input matrix and apply scaling to reduce over/under-flows RealScalar scale = matrix.cwiseAbs().maxCoeff(); - if(scale==RealScalar(0)) scale = RealScalar(1); + if(scale==Literal(0)) scale = Literal(1); MatrixX copy; if (m_isTranspose) copy = matrix.adjoint()/scale; else copy = matrix/scale; @@ -351,13 +352,13 @@ void BDCSVD::structured_update(Block A, co Index k1=0, k2=0; for(Index j=0; j::divide (Index firstCol, Index lastCol, Index firstRowW, l = m_naiveU.row(1).segment(firstCol, k); f = m_naiveU.row(0).segment(firstCol + k + 1, n - k - 1); } - if (m_compV) m_naiveV(firstRowW+k, firstColW) = 1; + if (m_compV) m_naiveV(firstRowW+k, firstColW) = Literal(1); if (r0::computeSVDofM(Index firstCol, Index n, MatrixXr& U, Vec ArrayRef col0 = m_computed.col(firstCol).segment(firstCol, n); m_workspace.head(n) = m_computed.block(firstCol, firstCol, n, n).diagonal(); ArrayRef diag = m_workspace.head(n); - diag(0) = 0; + diag(0) = Literal(0); // Allocate space for singular values and vectors singVals.resize(n); @@ -590,7 +591,7 @@ void BDCSVD::computeSVDofM(Index firstCol, Index n, MatrixXr& U, Vec // but others are interleaved and we must ignore them at this stage. // To this end, let's compute a permutation skipping them: Index actual_n = n; - while(actual_n>1 && diag(actual_n-1)==0) --actual_n; + while(actual_n>1 && diag(actual_n-1)==Literal(0)) --actual_n; Index m = 0; // size of the deflated problem for(Index k=0;kconsiderZero) @@ -691,11 +692,13 @@ template typename BDCSVD::RealScalar BDCSVD::secularEq(RealScalar mu, const ArrayRef& col0, const ArrayRef& diag, const IndicesRef &perm, const ArrayRef& diagShifted, RealScalar shift) { Index m = perm.size(); - RealScalar res = 1; + RealScalar res = Literal(1); for(Index i=0; i::computeSingVals(const ArrayRef& col0, const ArrayRef& d { using std::abs; using std::swap; + using std::sqrt; Index n = col0.size(); Index actual_n = n; - while(actual_n>1 && col0(actual_n-1)==0) --actual_n; + // Note that here actual_n is computed based on col0(i)==0 instead of diag(i)==0 as above + // because 1) we have diag(i)==0 => col0(i)==0 and 2) if col0(i)==0, then diag(i) is already a singular value. + while(actual_n>1 && col0(actual_n-1)==Literal(0)) --actual_n; for (Index k = 0; k < n; ++k) { - if (col0(k) == 0 || actual_n==1) + if (col0(k) == Literal(0) || actual_n==1) { // if col0(k) == 0, then entry is deflated, so singular value is on diagonal // if actual_n==1, then the deflated problem is already diagonalized singVals(k) = k==0 ? col0(0) : diag(k); - mus(k) = 0; + mus(k) = Literal(0); shifts(k) = k==0 ? col0(0) : diag(k); continue; } @@ -731,15 +737,17 @@ void BDCSVD::computeSingVals(const ArrayRef& col0, const ArrayRef& d right = (diag(actual_n-1) + col0.matrix().norm()); else { - // Skip deflated singular values + // Skip deflated singular values, + // recall that at this stage we assume that z[j]!=0 and all entries for which z[j]==0 have been put aside. + // This should be equivalent to using perm[] Index l = k+1; - while(col0(l)==0) { ++l; eigen_internal_assert(l::computeSingVals(const ArrayRef& col0, const ArrayRef& d << " " << secularEq(0.8*(left+right), col0, diag, perm, diag, 0) << " " << secularEq(0.9*(left+right), col0, diag, perm, diag, 0) << "\n"; #endif - RealScalar shift = (k == actual_n-1 || fMid > 0) ? left : right; + RealScalar shift = (k == actual_n-1 || fMid > Literal(0)) ? left : right; // measure everything relative to shift Map diagShifted(m_workspace.data()+4*n, n); @@ -785,13 +793,13 @@ void BDCSVD::computeSingVals(const ArrayRef& col0, const ArrayRef& d // rational interpolation: fit a function of the form a / mu + b through the two previous // iterates and use its zero to compute the next iterate - bool useBisection = fPrev*fCur>0; - while (fCur!=0 && abs(muCur - muPrev) > 8 * NumTraits::epsilon() * numext::maxi(abs(muCur), abs(muPrev)) && abs(fCur - fPrev)>NumTraits::epsilon() && !useBisection) + bool useBisection = fPrev*fCur>Literal(0); + while (fCur!=Literal(0) && abs(muCur - muPrev) > Literal(8) * NumTraits::epsilon() * numext::maxi(abs(muCur), abs(muPrev)) && abs(fCur - fPrev)>NumTraits::epsilon() && !useBisection) { ++m_numIters; // Find a and b such that the function f(mu) = a / mu + b matches the current and previous samples. - RealScalar a = (fCur - fPrev) / (1/muCur - 1/muPrev); + RealScalar a = (fCur - fPrev) / (Literal(1)/muCur - Literal(1)/muPrev); RealScalar b = fCur - a / muCur; // And find mu such that f(mu)==0: RealScalar muZero = -a/b; @@ -803,8 +811,8 @@ void BDCSVD::computeSingVals(const ArrayRef& col0, const ArrayRef& d fCur = fZero; - if (shift == left && (muCur < 0 || muCur > right - left)) useBisection = true; - if (shift == right && (muCur < -(right - left) || muCur > 0)) useBisection = true; + if (shift == left && (muCur < Literal(0) || muCur > right - left)) useBisection = true; + if (shift == right && (muCur < -(right - left) || muCur > Literal(0))) useBisection = true; if (abs(fCur)>abs(fPrev)) useBisection = true; } @@ -817,15 +825,23 @@ void BDCSVD::computeSingVals(const ArrayRef& col0, const ArrayRef& d RealScalar leftShifted, rightShifted; if (shift == left) { - leftShifted = (std::numeric_limits::min)(); + // to avoid overflow, we must have mu > max(real_min, |z(k)|/sqrt(real_max)), + // the factor 2 is to be more conservative + leftShifted = numext::maxi( (std::numeric_limits::min)(), Literal(2) * abs(col0(k)) / sqrt((std::numeric_limits::max)()) ); + + // check that we did it right: + eigen_internal_assert( (numext::isfinite)( (col0(k)/leftShifted)*(col0(k)/(diag(k)+shift+leftShifted)) ) ); // I don't understand why the case k==0 would be special there: - // if (k == 0) rightShifted = right - left; else - rightShifted = (k==actual_n-1) ? right : ((right - left) * RealScalar(0.6)); // theoretically we can take 0.5, but let's be safe + // if (k == 0) rightShifted = right - left; else + rightShifted = (k==actual_n-1) ? right : ((right - left) * RealScalar(0.51)); // theoretically we can take 0.5, but let's be safe } else { - leftShifted = -(right - left) * RealScalar(0.6); - rightShifted = -(std::numeric_limits::min)(); + leftShifted = -(right - left) * RealScalar(0.51); + if(k+1( (std::numeric_limits::min)(), abs(col0(k+1)) / sqrt((std::numeric_limits::max)()) ); + else + rightShifted = -(std::numeric_limits::min)(); } RealScalar fLeft = secularEq(leftShifted, col0, diag, perm, diagShifted, shift); @@ -841,13 +857,13 @@ void BDCSVD::computeSingVals(const ArrayRef& col0, const ArrayRef& d std::cout << k << " : " << fLeft << " * " << fRight << " == " << fLeft * fRight << " ; " << left << " - " << right << " -> " << leftShifted << " " << rightShifted << " shift=" << shift << "\n"; } #endif - eigen_internal_assert(fLeft * fRight < 0); + eigen_internal_assert(fLeft * fRight < Literal(0)); - while (rightShifted - leftShifted > 2 * NumTraits::epsilon() * numext::maxi(abs(leftShifted), abs(rightShifted))) + while (rightShifted - leftShifted > Literal(2) * NumTraits::epsilon() * numext::maxi(abs(leftShifted), abs(rightShifted))) { - RealScalar midShifted = (leftShifted + rightShifted) / 2; + RealScalar midShifted = (leftShifted + rightShifted) / Literal(2); fMid = secularEq(midShifted, col0, diag, perm, diagShifted, shift); - if (fLeft * fMid < 0) + if (fLeft * fMid < Literal(0)) { rightShifted = midShifted; } @@ -858,7 +874,7 @@ void BDCSVD::computeSingVals(const ArrayRef& col0, const ArrayRef& d } } - muCur = (leftShifted + rightShifted) / 2; + muCur = (leftShifted + rightShifted) / Literal(2); } singVals[k] = shift + muCur; @@ -892,8 +908,8 @@ void BDCSVD::perturbCol0 // The offset permits to skip deflated entries while computing zhat for (Index k = 0; k < n; ++k) { - if (col0(k) == 0) // deflated - zhat(k) = 0; + if (col0(k) == Literal(0)) // deflated + zhat(k) = Literal(0); else { // see equation (3.6) @@ -918,7 +934,7 @@ void BDCSVD::perturbCol0 std::cout << "zhat(" << k << ") = sqrt( " << prod << ") ; " << (singVals(last) + dk) << " * " << mus(last) + shifts(last) << " - " << dk << "\n"; #endif RealScalar tmp = sqrt(prod); - zhat(k) = col0(k) > 0 ? tmp : -tmp; + zhat(k) = col0(k) > Literal(0) ? tmp : -tmp; } } } @@ -934,7 +950,7 @@ void BDCSVD::computeSingVecs for (Index k = 0; k < n; ++k) { - if (zhat(k) == 0) + if (zhat(k) == Literal(0)) { U.col(k) = VectorType::Unit(n+1, k); if (m_compV) V.col(k) = VectorType::Unit(n, k); @@ -947,7 +963,7 @@ void BDCSVD::computeSingVecs Index i = perm(l); U(i,k) = zhat(i)/(((diag(i) - shifts(k)) - mus(k)) )/( (diag(i) + singVals[k])); } - U(n,k) = 0; + U(n,k) = Literal(0); U.col(k).normalize(); if (m_compV) @@ -958,7 +974,7 @@ void BDCSVD::computeSingVecs Index i = perm(l); V(i,k) = diag(i) * zhat(i) / (((diag(i) - shifts(k)) - mus(k)) )/( (diag(i) + singVals[k])); } - V(0,k) = -1; + V(0,k) = Literal(-1); V.col(k).normalize(); } } @@ -979,15 +995,15 @@ void BDCSVD::deflation43(Index firstCol, Index shift, Index i, Index Index start = firstCol + shift; RealScalar c = m_computed(start, start); RealScalar s = m_computed(start+i, start); - RealScalar r = sqrt(numext::abs2(c) + numext::abs2(s)); - if (r == 0) + RealScalar r = numext::hypot(c,s); + if (r == Literal(0)) { - m_computed(start+i, start+i) = 0; + m_computed(start+i, start+i) = Literal(0); return; } m_computed(start,start) = r; - m_computed(start+i, start) = 0; - m_computed(start+i, start+i) = 0; + m_computed(start+i, start) = Literal(0); + m_computed(start+i, start+i) = Literal(0); JacobiRotation J(c/r,-s/r); if (m_compU) m_naiveU.middleRows(firstCol, size+1).applyOnTheRight(firstCol, firstCol+i, J); @@ -1020,7 +1036,7 @@ void BDCSVD::deflation44(Index firstColu , Index firstColm, Index fi << m_computed(firstColm + i+1, firstColm+i+1) << " " << m_computed(firstColm + i+2, firstColm+i+2) << "\n"; #endif - if (r==0) + if (r==Literal(0)) { m_computed(firstColm + i, firstColm + i) = m_computed(firstColm + j, firstColm + j); return; @@ -1029,7 +1045,7 @@ void BDCSVD::deflation44(Index firstColu , Index firstColm, Index fi s/=r; m_computed(firstColm + i, firstColm) = r; m_computed(firstColm + j, firstColm + j) = m_computed(firstColm + i, firstColm + i); - m_computed(firstColm + j, firstColm) = 0; + m_computed(firstColm + j, firstColm) = Literal(0); JacobiRotation J(c,-s); if (m_compU) m_naiveU.middleRows(firstColu, size+1).applyOnTheRight(firstColu + i, firstColu + j, J); @@ -1053,7 +1069,7 @@ void BDCSVD::deflation(Index firstCol, Index lastCol, Index k, Index const RealScalar considerZero = (std::numeric_limits::min)(); RealScalar maxDiag = diag.tail((std::max)(Index(1),length-1)).cwiseAbs().maxCoeff(); RealScalar epsilon_strict = numext::maxi(considerZero,NumTraits::epsilon() * maxDiag); - RealScalar epsilon_coarse = 8 * NumTraits::epsilon() * numext::maxi(col0.cwiseAbs().maxCoeff(), maxDiag); + RealScalar epsilon_coarse = Literal(8) * NumTraits::epsilon() * numext::maxi(col0.cwiseAbs().maxCoeff(), maxDiag); #ifdef EIGEN_BDCSVD_SANITY_CHECKS assert(m_naiveU.allFinite()); @@ -1081,7 +1097,7 @@ void BDCSVD::deflation(Index firstCol, Index lastCol, Index k, Index #ifdef EIGEN_BDCSVD_DEBUG_VERBOSE std::cout << "deflation 4.2, set z(" << i << ") to zero because " << abs(col0(i)) << " < " << epsilon_strict << " (diag(" << i << ")=" << diag(i) << ")\n"; #endif - col0(i) = 0; + col0(i) = Literal(0); } //condition 4.3 diff --git a/xs/src/eigen/Eigen/src/SVD/JacobiSVD.h b/src/eigen/Eigen/src/SVD/JacobiSVD.h similarity index 100% rename from xs/src/eigen/Eigen/src/SVD/JacobiSVD.h rename to src/eigen/Eigen/src/SVD/JacobiSVD.h diff --git a/xs/src/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h b/src/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h similarity index 94% rename from xs/src/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h rename to src/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h index 50272154f8..ff0516f611 100644 --- a/xs/src/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h +++ b/src/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h @@ -61,9 +61,10 @@ JacobiSVD, ColPiv u = (LAPACKE_TYPE*)m_matrixU.data(); \ } else { ldu=1; u=&dummy; }\ MatrixType localV; \ - ldvt = (m_computeFullV) ? internal::convert_index(m_cols) : (m_computeThinV) ? internal::convert_index(m_diagSize) : 1; \ + lapack_int vt_rows = (m_computeFullV) ? internal::convert_index(m_cols) : (m_computeThinV) ? internal::convert_index(m_diagSize) : 1; \ if (computeV()) { \ - localV.resize(ldvt, m_cols); \ + localV.resize(vt_rows, m_cols); \ + ldvt = internal::convert_index(localV.outerStride()); \ vt = (LAPACKE_TYPE*)localV.data(); \ } else { ldvt=1; vt=&dummy; }\ Matrix superb; superb.resize(m_diagSize, 1); \ diff --git a/xs/src/eigen/Eigen/src/SVD/SVDBase.h b/src/eigen/Eigen/src/SVD/SVDBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/SVD/SVDBase.h rename to src/eigen/Eigen/src/SVD/SVDBase.h diff --git a/xs/src/eigen/Eigen/src/SVD/UpperBidiagonalization.h b/src/eigen/Eigen/src/SVD/UpperBidiagonalization.h similarity index 99% rename from xs/src/eigen/Eigen/src/SVD/UpperBidiagonalization.h rename to src/eigen/Eigen/src/SVD/UpperBidiagonalization.h index 0b14608940..11ac847e1d 100644 --- a/xs/src/eigen/Eigen/src/SVD/UpperBidiagonalization.h +++ b/src/eigen/Eigen/src/SVD/UpperBidiagonalization.h @@ -159,6 +159,8 @@ void upperbidiagonalization_blocked_helper(MatrixType& A, traits::Flags & RowMajorBit> > Y) { typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + typedef typename NumTraits::Literal Literal; enum { StorageOrder = traits::Flags & RowMajorBit }; typedef InnerStride ColInnerStride; typedef InnerStride RowInnerStride; @@ -263,7 +265,7 @@ void upperbidiagonalization_blocked_helper(MatrixType& A, SubMatType A10( A.block(bs,0, brows-bs,bs) ); SubMatType A01( A.block(0,bs, bs,bcols-bs) ); Scalar tmp = A01(bs-1,0); - A01(bs-1,0) = 1; + A01(bs-1,0) = Literal(1); A11.noalias() -= A10 * Y.topLeftCorner(bcols,bs).bottomRows(bcols-bs).adjoint(); A11.noalias() -= X.topLeftCorner(brows,bs).bottomRows(brows-bs) * A01; A01(bs-1,0) = tmp; diff --git a/xs/src/eigen/Eigen/src/SparseCholesky/SimplicialCholesky.h b/src/eigen/Eigen/src/SparseCholesky/SimplicialCholesky.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCholesky/SimplicialCholesky.h rename to src/eigen/Eigen/src/SparseCholesky/SimplicialCholesky.h diff --git a/xs/src/eigen/Eigen/src/SparseCholesky/SimplicialCholesky_impl.h b/src/eigen/Eigen/src/SparseCholesky/SimplicialCholesky_impl.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCholesky/SimplicialCholesky_impl.h rename to src/eigen/Eigen/src/SparseCholesky/SimplicialCholesky_impl.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/AmbiVector.h b/src/eigen/Eigen/src/SparseCore/AmbiVector.h similarity index 99% rename from xs/src/eigen/Eigen/src/SparseCore/AmbiVector.h rename to src/eigen/Eigen/src/SparseCore/AmbiVector.h index 8a5cc91f2a..e0295f2af1 100644 --- a/xs/src/eigen/Eigen/src/SparseCore/AmbiVector.h +++ b/src/eigen/Eigen/src/SparseCore/AmbiVector.h @@ -94,7 +94,7 @@ class AmbiVector Index allocSize = m_allocatedElements * sizeof(ListEl); allocSize = (allocSize + sizeof(Scalar) - 1)/sizeof(Scalar); Scalar* newBuffer = new Scalar[allocSize]; - memcpy(newBuffer, m_buffer, copyElements * sizeof(ListEl)); + std::memcpy(newBuffer, m_buffer, copyElements * sizeof(ListEl)); delete[] m_buffer; m_buffer = newBuffer; } diff --git a/xs/src/eigen/Eigen/src/SparseCore/CompressedStorage.h b/src/eigen/Eigen/src/SparseCore/CompressedStorage.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/CompressedStorage.h rename to src/eigen/Eigen/src/SparseCore/CompressedStorage.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/ConservativeSparseSparseProduct.h b/src/eigen/Eigen/src/SparseCore/ConservativeSparseSparseProduct.h similarity index 82% rename from xs/src/eigen/Eigen/src/SparseCore/ConservativeSparseSparseProduct.h rename to src/eigen/Eigen/src/SparseCore/ConservativeSparseSparseProduct.h index 492eb0a29c..9db119b67f 100644 --- a/xs/src/eigen/Eigen/src/SparseCore/ConservativeSparseSparseProduct.h +++ b/src/eigen/Eigen/src/SparseCore/ConservativeSparseSparseProduct.h @@ -17,7 +17,9 @@ namespace internal { template static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& rhs, ResultType& res, bool sortedInsertion = false) { - typedef typename remove_all::type::Scalar Scalar; + typedef typename remove_all::type::Scalar LhsScalar; + typedef typename remove_all::type::Scalar RhsScalar; + typedef typename remove_all::type::Scalar ResScalar; // make sure to call innerSize/outerSize since we fake the storage order. Index rows = lhs.innerSize(); @@ -25,7 +27,7 @@ static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& r eigen_assert(lhs.outerSize() == rhs.innerSize()); ei_declare_aligned_stack_constructed_variable(bool, mask, rows, 0); - ei_declare_aligned_stack_constructed_variable(Scalar, values, rows, 0); + ei_declare_aligned_stack_constructed_variable(ResScalar, values, rows, 0); ei_declare_aligned_stack_constructed_variable(Index, indices, rows, 0); std::memset(mask,0,sizeof(bool)*rows); @@ -51,12 +53,12 @@ static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& r Index nnz = 0; for (typename evaluator::InnerIterator rhsIt(rhsEval, j); rhsIt; ++rhsIt) { - Scalar y = rhsIt.value(); + RhsScalar y = rhsIt.value(); Index k = rhsIt.index(); for (typename evaluator::InnerIterator lhsIt(lhsEval, k); lhsIt; ++lhsIt) { Index i = lhsIt.index(); - Scalar x = lhsIt.value(); + LhsScalar x = lhsIt.value(); if(!mask[i]) { mask[i] = true; @@ -166,11 +168,12 @@ struct conservative_sparse_sparse_product_selector RowMajorMatrix; - RowMajorMatrix rhsRow = rhs; - RowMajorMatrix resRow(lhs.rows(), rhs.cols()); - internal::conservative_sparse_sparse_product_impl(rhsRow, lhs, resRow); - res = resRow; + typedef SparseMatrix RowMajorRhs; + typedef SparseMatrix RowMajorRes; + RowMajorRhs rhsRow = rhs; + RowMajorRes resRow(lhs.rows(), rhs.cols()); + internal::conservative_sparse_sparse_product_impl(rhsRow, lhs, resRow); + res = resRow; } }; @@ -179,10 +182,11 @@ struct conservative_sparse_sparse_product_selector RowMajorMatrix; - RowMajorMatrix lhsRow = lhs; - RowMajorMatrix resRow(lhs.rows(), rhs.cols()); - internal::conservative_sparse_sparse_product_impl(rhs, lhsRow, resRow); + typedef SparseMatrix RowMajorLhs; + typedef SparseMatrix RowMajorRes; + RowMajorLhs lhsRow = lhs; + RowMajorRes resRow(lhs.rows(), rhs.cols()); + internal::conservative_sparse_sparse_product_impl(rhs, lhsRow, resRow); res = resRow; } }; @@ -219,10 +223,11 @@ struct conservative_sparse_sparse_product_selector ColMajorMatrix; - ColMajorMatrix lhsCol = lhs; - ColMajorMatrix resCol(lhs.rows(), rhs.cols()); - internal::conservative_sparse_sparse_product_impl(lhsCol, rhs, resCol); + typedef SparseMatrix ColMajorLhs; + typedef SparseMatrix ColMajorRes; + ColMajorLhs lhsCol = lhs; + ColMajorRes resCol(lhs.rows(), rhs.cols()); + internal::conservative_sparse_sparse_product_impl(lhsCol, rhs, resCol); res = resCol; } }; @@ -232,10 +237,11 @@ struct conservative_sparse_sparse_product_selector ColMajorMatrix; - ColMajorMatrix rhsCol = rhs; - ColMajorMatrix resCol(lhs.rows(), rhs.cols()); - internal::conservative_sparse_sparse_product_impl(lhs, rhsCol, resCol); + typedef SparseMatrix ColMajorRhs; + typedef SparseMatrix ColMajorRes; + ColMajorRhs rhsCol = rhs; + ColMajorRes resCol(lhs.rows(), rhs.cols()); + internal::conservative_sparse_sparse_product_impl(lhs, rhsCol, resCol); res = resCol; } }; @@ -263,7 +269,8 @@ namespace internal { template static void sparse_sparse_to_dense_product_impl(const Lhs& lhs, const Rhs& rhs, ResultType& res) { - typedef typename remove_all::type::Scalar Scalar; + typedef typename remove_all::type::Scalar LhsScalar; + typedef typename remove_all::type::Scalar RhsScalar; Index cols = rhs.outerSize(); eigen_assert(lhs.outerSize() == rhs.innerSize()); @@ -274,12 +281,12 @@ static void sparse_sparse_to_dense_product_impl(const Lhs& lhs, const Rhs& rhs, { for (typename evaluator::InnerIterator rhsIt(rhsEval, j); rhsIt; ++rhsIt) { - Scalar y = rhsIt.value(); + RhsScalar y = rhsIt.value(); Index k = rhsIt.index(); for (typename evaluator::InnerIterator lhsIt(lhsEval, k); lhsIt; ++lhsIt) { Index i = lhsIt.index(); - Scalar x = lhsIt.value(); + LhsScalar x = lhsIt.value(); res.coeffRef(i,j) += x * y; } } @@ -310,9 +317,9 @@ struct sparse_sparse_to_dense_product_selector ColMajorMatrix; - ColMajorMatrix lhsCol(lhs); - internal::sparse_sparse_to_dense_product_impl(lhsCol, rhs, res); + typedef SparseMatrix ColMajorLhs; + ColMajorLhs lhsCol(lhs); + internal::sparse_sparse_to_dense_product_impl(lhsCol, rhs, res); } }; @@ -321,9 +328,9 @@ struct sparse_sparse_to_dense_product_selector ColMajorMatrix; - ColMajorMatrix rhsCol(rhs); - internal::sparse_sparse_to_dense_product_impl(lhs, rhsCol, res); + typedef SparseMatrix ColMajorRhs; + ColMajorRhs rhsCol(rhs); + internal::sparse_sparse_to_dense_product_impl(lhs, rhsCol, res); } }; diff --git a/xs/src/eigen/Eigen/src/SparseCore/MappedSparseMatrix.h b/src/eigen/Eigen/src/SparseCore/MappedSparseMatrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/MappedSparseMatrix.h rename to src/eigen/Eigen/src/SparseCore/MappedSparseMatrix.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseAssign.h b/src/eigen/Eigen/src/SparseCore/SparseAssign.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseAssign.h rename to src/eigen/Eigen/src/SparseCore/SparseAssign.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseBlock.h b/src/eigen/Eigen/src/SparseCore/SparseBlock.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseBlock.h rename to src/eigen/Eigen/src/SparseCore/SparseBlock.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseColEtree.h b/src/eigen/Eigen/src/SparseCore/SparseColEtree.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseColEtree.h rename to src/eigen/Eigen/src/SparseCore/SparseColEtree.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseCompressedBase.h b/src/eigen/Eigen/src/SparseCore/SparseCompressedBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseCompressedBase.h rename to src/eigen/Eigen/src/SparseCore/SparseCompressedBase.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseCwiseBinaryOp.h b/src/eigen/Eigen/src/SparseCore/SparseCwiseBinaryOp.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseCwiseBinaryOp.h rename to src/eigen/Eigen/src/SparseCore/SparseCwiseBinaryOp.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseCwiseUnaryOp.h b/src/eigen/Eigen/src/SparseCore/SparseCwiseUnaryOp.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseCwiseUnaryOp.h rename to src/eigen/Eigen/src/SparseCore/SparseCwiseUnaryOp.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseDenseProduct.h b/src/eigen/Eigen/src/SparseCore/SparseDenseProduct.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseDenseProduct.h rename to src/eigen/Eigen/src/SparseCore/SparseDenseProduct.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseDiagonalProduct.h b/src/eigen/Eigen/src/SparseCore/SparseDiagonalProduct.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseDiagonalProduct.h rename to src/eigen/Eigen/src/SparseCore/SparseDiagonalProduct.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseDot.h b/src/eigen/Eigen/src/SparseCore/SparseDot.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseDot.h rename to src/eigen/Eigen/src/SparseCore/SparseDot.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseFuzzy.h b/src/eigen/Eigen/src/SparseCore/SparseFuzzy.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseFuzzy.h rename to src/eigen/Eigen/src/SparseCore/SparseFuzzy.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseMap.h b/src/eigen/Eigen/src/SparseCore/SparseMap.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseMap.h rename to src/eigen/Eigen/src/SparseCore/SparseMap.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseMatrix.h b/src/eigen/Eigen/src/SparseCore/SparseMatrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseMatrix.h rename to src/eigen/Eigen/src/SparseCore/SparseMatrix.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseMatrixBase.h b/src/eigen/Eigen/src/SparseCore/SparseMatrixBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseMatrixBase.h rename to src/eigen/Eigen/src/SparseCore/SparseMatrixBase.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparsePermutation.h b/src/eigen/Eigen/src/SparseCore/SparsePermutation.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparsePermutation.h rename to src/eigen/Eigen/src/SparseCore/SparsePermutation.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseProduct.h b/src/eigen/Eigen/src/SparseCore/SparseProduct.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseProduct.h rename to src/eigen/Eigen/src/SparseCore/SparseProduct.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseRedux.h b/src/eigen/Eigen/src/SparseCore/SparseRedux.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseRedux.h rename to src/eigen/Eigen/src/SparseCore/SparseRedux.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseRef.h b/src/eigen/Eigen/src/SparseCore/SparseRef.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseRef.h rename to src/eigen/Eigen/src/SparseCore/SparseRef.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseSelfAdjointView.h b/src/eigen/Eigen/src/SparseCore/SparseSelfAdjointView.h similarity index 98% rename from xs/src/eigen/Eigen/src/SparseCore/SparseSelfAdjointView.h rename to src/eigen/Eigen/src/SparseCore/SparseSelfAdjointView.h index 9e39be738d..65611b3d4c 100644 --- a/xs/src/eigen/Eigen/src/SparseCore/SparseSelfAdjointView.h +++ b/src/eigen/Eigen/src/SparseCore/SparseSelfAdjointView.h @@ -47,6 +47,7 @@ template class SparseSelfAdjointView enum { Mode = _Mode, + TransposeMode = ((Mode & Upper) ? Lower : 0) | ((Mode & Lower) ? Upper : 0), RowsAtCompileTime = internal::traits::RowsAtCompileTime, ColsAtCompileTime = internal::traits::ColsAtCompileTime }; @@ -310,7 +311,7 @@ inline void sparse_selfadjoint_time_dense_product(const SparseLhsType& lhs, cons while (i && i.index() dstT(dst); - internal::sparse_selfadjoint_time_dense_product(rhsNested.transpose(), lhsNested.transpose(), dstT, alpha); + internal::sparse_selfadjoint_time_dense_product(rhsNested.transpose(), lhsNested.transpose(), dstT, alpha); } }; diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseSolverBase.h b/src/eigen/Eigen/src/SparseCore/SparseSolverBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseSolverBase.h rename to src/eigen/Eigen/src/SparseCore/SparseSolverBase.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseSparseProductWithPruning.h b/src/eigen/Eigen/src/SparseCore/SparseSparseProductWithPruning.h similarity index 88% rename from xs/src/eigen/Eigen/src/SparseCore/SparseSparseProductWithPruning.h rename to src/eigen/Eigen/src/SparseCore/SparseSparseProductWithPruning.h index 21c419002e..88820a48f3 100644 --- a/xs/src/eigen/Eigen/src/SparseCore/SparseSparseProductWithPruning.h +++ b/src/eigen/Eigen/src/SparseCore/SparseSparseProductWithPruning.h @@ -21,7 +21,8 @@ static void sparse_sparse_product_with_pruning_impl(const Lhs& lhs, const Rhs& r { // return sparse_sparse_product_with_pruning_impl2(lhs,rhs,res); - typedef typename remove_all::type::Scalar Scalar; + typedef typename remove_all::type::Scalar RhsScalar; + typedef typename remove_all::type::Scalar ResScalar; typedef typename remove_all::type::StorageIndex StorageIndex; // make sure to call innerSize/outerSize since we fake the storage order. @@ -31,7 +32,7 @@ static void sparse_sparse_product_with_pruning_impl(const Lhs& lhs, const Rhs& r eigen_assert(lhs.outerSize() == rhs.innerSize()); // allocate a temporary buffer - AmbiVector tempVector(rows); + AmbiVector tempVector(rows); // mimics a resizeByInnerOuter: if(ResultType::IsRowMajor) @@ -63,14 +64,14 @@ static void sparse_sparse_product_with_pruning_impl(const Lhs& lhs, const Rhs& r { // FIXME should be written like this: tmp += rhsIt.value() * lhs.col(rhsIt.index()) tempVector.restart(); - Scalar x = rhsIt.value(); + RhsScalar x = rhsIt.value(); for (typename evaluator::InnerIterator lhsIt(lhsEval, rhsIt.index()); lhsIt; ++lhsIt) { tempVector.coeffRef(lhsIt.index()) += lhsIt.value() * x; } } res.startVec(j); - for (typename AmbiVector::Iterator it(tempVector,tolerance); it; ++it) + for (typename AmbiVector::Iterator it(tempVector,tolerance); it; ++it) res.insertBackByOuterInner(j,it.index()) = it.value(); } res.finalize(); @@ -85,7 +86,6 @@ struct sparse_sparse_product_with_pruning_selector; template struct sparse_sparse_product_with_pruning_selector { - typedef typename traits::type>::Scalar Scalar; typedef typename ResultType::RealScalar RealScalar; static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res, const RealScalar& tolerance) @@ -129,8 +129,8 @@ struct sparse_sparse_product_with_pruning_selector ColMajorMatrixLhs; - typedef SparseMatrix ColMajorMatrixRhs; + typedef SparseMatrix ColMajorMatrixLhs; + typedef SparseMatrix ColMajorMatrixRhs; ColMajorMatrixLhs colLhs(lhs); ColMajorMatrixRhs colRhs(rhs); internal::sparse_sparse_product_with_pruning_impl(colLhs, colRhs, res, tolerance); @@ -149,7 +149,7 @@ struct sparse_sparse_product_with_pruning_selector RowMajorMatrixLhs; + typedef SparseMatrix RowMajorMatrixLhs; RowMajorMatrixLhs rowLhs(lhs); sparse_sparse_product_with_pruning_selector(rowLhs,rhs,res,tolerance); } @@ -161,7 +161,7 @@ struct sparse_sparse_product_with_pruning_selector RowMajorMatrixRhs; + typedef SparseMatrix RowMajorMatrixRhs; RowMajorMatrixRhs rowRhs(rhs); sparse_sparse_product_with_pruning_selector(lhs,rowRhs,res,tolerance); } @@ -173,7 +173,7 @@ struct sparse_sparse_product_with_pruning_selector ColMajorMatrixRhs; + typedef SparseMatrix ColMajorMatrixRhs; ColMajorMatrixRhs colRhs(rhs); internal::sparse_sparse_product_with_pruning_impl(lhs, colRhs, res, tolerance); } @@ -185,7 +185,7 @@ struct sparse_sparse_product_with_pruning_selector ColMajorMatrixLhs; + typedef SparseMatrix ColMajorMatrixLhs; ColMajorMatrixLhs colLhs(lhs); internal::sparse_sparse_product_with_pruning_impl(colLhs, rhs, res, tolerance); } diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseTranspose.h b/src/eigen/Eigen/src/SparseCore/SparseTranspose.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseTranspose.h rename to src/eigen/Eigen/src/SparseCore/SparseTranspose.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseTriangularView.h b/src/eigen/Eigen/src/SparseCore/SparseTriangularView.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseTriangularView.h rename to src/eigen/Eigen/src/SparseCore/SparseTriangularView.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseUtil.h b/src/eigen/Eigen/src/SparseCore/SparseUtil.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseUtil.h rename to src/eigen/Eigen/src/SparseCore/SparseUtil.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseVector.h b/src/eigen/Eigen/src/SparseCore/SparseVector.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseVector.h rename to src/eigen/Eigen/src/SparseCore/SparseVector.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/SparseView.h b/src/eigen/Eigen/src/SparseCore/SparseView.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseView.h rename to src/eigen/Eigen/src/SparseCore/SparseView.h diff --git a/xs/src/eigen/Eigen/src/SparseCore/TriangularSolver.h b/src/eigen/Eigen/src/SparseCore/TriangularSolver.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseCore/TriangularSolver.h rename to src/eigen/Eigen/src/SparseCore/TriangularSolver.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU.h b/src/eigen/Eigen/src/SparseLU/SparseLU.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU.h rename to src/eigen/Eigen/src/SparseLU/SparseLU.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLUImpl.h b/src/eigen/Eigen/src/SparseLU/SparseLUImpl.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLUImpl.h rename to src/eigen/Eigen/src/SparseLU/SparseLUImpl.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_Memory.h b/src/eigen/Eigen/src/SparseLU/SparseLU_Memory.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_Memory.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_Memory.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_Structs.h b/src/eigen/Eigen/src/SparseLU/SparseLU_Structs.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_Structs.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_Structs.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_SupernodalMatrix.h b/src/eigen/Eigen/src/SparseLU/SparseLU_SupernodalMatrix.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_SupernodalMatrix.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_SupernodalMatrix.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_Utils.h b/src/eigen/Eigen/src/SparseLU/SparseLU_Utils.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_Utils.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_Utils.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_column_bmod.h b/src/eigen/Eigen/src/SparseLU/SparseLU_column_bmod.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_column_bmod.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_column_bmod.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_column_dfs.h b/src/eigen/Eigen/src/SparseLU/SparseLU_column_dfs.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_column_dfs.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_column_dfs.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_copy_to_ucol.h b/src/eigen/Eigen/src/SparseLU/SparseLU_copy_to_ucol.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_copy_to_ucol.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_copy_to_ucol.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_gemm_kernel.h b/src/eigen/Eigen/src/SparseLU/SparseLU_gemm_kernel.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_gemm_kernel.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_gemm_kernel.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h b/src/eigen/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_kernel_bmod.h b/src/eigen/Eigen/src/SparseLU/SparseLU_kernel_bmod.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_kernel_bmod.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_kernel_bmod.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_panel_bmod.h b/src/eigen/Eigen/src/SparseLU/SparseLU_panel_bmod.h similarity index 98% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_panel_bmod.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_panel_bmod.h index 822cf32c34..6f1ae00379 100644 --- a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_panel_bmod.h +++ b/src/eigen/Eigen/src/SparseLU/SparseLU_panel_bmod.h @@ -146,7 +146,7 @@ void SparseLUImpl::panel_bmod(const Index m, const Index w, Index ldl = internal::first_multiple(nrow, PacketSize); Index offset = (PacketSize-internal::first_default_aligned(B.data(), PacketSize)) % PacketSize; - MappedMatrixBlock L(tempv.data()+w*ldu+offset, nrow, u_cols, OuterStride<>(ldl)); + auto L = MappedMatrixBlock(tempv.data()+w*ldu+offset, nrow, u_cols, OuterStride<>(ldl)); L.setZero(); internal::sparselu_gemm(L.rows(), L.cols(), B.cols(), B.data(), B.outerStride(), U.data(), U.outerStride(), L.data(), L.outerStride()); diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_panel_dfs.h b/src/eigen/Eigen/src/SparseLU/SparseLU_panel_dfs.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_panel_dfs.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_panel_dfs.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_pivotL.h b/src/eigen/Eigen/src/SparseLU/SparseLU_pivotL.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_pivotL.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_pivotL.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_pruneL.h b/src/eigen/Eigen/src/SparseLU/SparseLU_pruneL.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_pruneL.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_pruneL.h diff --git a/xs/src/eigen/Eigen/src/SparseLU/SparseLU_relax_snode.h b/src/eigen/Eigen/src/SparseLU/SparseLU_relax_snode.h similarity index 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_relax_snode.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_relax_snode.h diff --git a/xs/src/eigen/Eigen/src/SparseQR/SparseQR.h b/src/eigen/Eigen/src/SparseQR/SparseQR.h similarity index 96% rename from xs/src/eigen/Eigen/src/SparseQR/SparseQR.h rename to src/eigen/Eigen/src/SparseQR/SparseQR.h index 2d4498b038..7409fcae94 100644 --- a/xs/src/eigen/Eigen/src/SparseQR/SparseQR.h +++ b/src/eigen/Eigen/src/SparseQR/SparseQR.h @@ -52,7 +52,7 @@ namespace internal { * rank-revealing permutations. Use colsPermutation() to get it. * * Q is the orthogonal matrix represented as products of Householder reflectors. - * Use matrixQ() to get an expression and matrixQ().transpose() to get the transpose. + * Use matrixQ() to get an expression and matrixQ().adjoint() to get the adjoint. * You can then apply it to a vector. * * R is the sparse triangular or trapezoidal matrix. The later occurs when A is rank-deficient. @@ -65,6 +65,7 @@ namespace internal { * \implsparsesolverconcept * * \warning The input sparse matrix A must be in compressed mode (see SparseMatrix::makeCompressed()). + * \warning For complex matrices matrixQ().transpose() will actually return the adjoint matrix. * */ template @@ -196,9 +197,9 @@ class SparseQR : public SparseSolverBase > Index rank = this->rank(); - // Compute Q^T * b; + // Compute Q^* * b; typename Dest::PlainObject y, b; - y = this->matrixQ().transpose() * B; + y = this->matrixQ().adjoint() * B; b = y; // Solve with the triangular matrix R @@ -604,7 +605,7 @@ struct SparseQR_QProduct : ReturnByValue @@ -668,13 +672,14 @@ struct SparseQRMatrixQReturnType : public EigenBase(m_qr,other.derived(),false); } + // To use for operations with the adjoint of Q SparseQRMatrixQTransposeReturnType adjoint() const { return SparseQRMatrixQTransposeReturnType(m_qr); } inline Index rows() const { return m_qr.rows(); } - inline Index cols() const { return (std::min)(m_qr.rows(),m_qr.cols()); } - // To use for operations with the transpose of Q + inline Index cols() const { return m_qr.rows(); } + // To use for operations with the transpose of Q FIXME this is the same as adjoint at the moment SparseQRMatrixQTransposeReturnType transpose() const { return SparseQRMatrixQTransposeReturnType(m_qr); @@ -682,6 +687,7 @@ struct SparseQRMatrixQReturnType : public EigenBase struct SparseQRMatrixQTransposeReturnType { @@ -712,7 +718,7 @@ struct Assignment, internal: typedef typename DstXprType::StorageIndex StorageIndex; static void run(DstXprType &dst, const SrcXprType &src, const internal::assign_op &/*func*/) { - typename DstXprType::PlainObject idMat(src.m_qr.rows(), src.m_qr.rows()); + typename DstXprType::PlainObject idMat(src.rows(), src.cols()); idMat.setIdentity(); // Sort the sparse householder reflectors if needed const_cast(&src.m_qr)->_sort_matrix_Q(); diff --git a/xs/src/eigen/Eigen/src/StlSupport/StdDeque.h b/src/eigen/Eigen/src/StlSupport/StdDeque.h similarity index 100% rename from xs/src/eigen/Eigen/src/StlSupport/StdDeque.h rename to src/eigen/Eigen/src/StlSupport/StdDeque.h diff --git a/xs/src/eigen/Eigen/src/StlSupport/StdList.h b/src/eigen/Eigen/src/StlSupport/StdList.h similarity index 100% rename from xs/src/eigen/Eigen/src/StlSupport/StdList.h rename to src/eigen/Eigen/src/StlSupport/StdList.h diff --git a/xs/src/eigen/Eigen/src/StlSupport/StdVector.h b/src/eigen/Eigen/src/StlSupport/StdVector.h similarity index 100% rename from xs/src/eigen/Eigen/src/StlSupport/StdVector.h rename to src/eigen/Eigen/src/StlSupport/StdVector.h diff --git a/xs/src/eigen/Eigen/src/StlSupport/details.h b/src/eigen/Eigen/src/StlSupport/details.h similarity index 100% rename from xs/src/eigen/Eigen/src/StlSupport/details.h rename to src/eigen/Eigen/src/StlSupport/details.h diff --git a/xs/src/eigen/Eigen/src/SuperLUSupport/SuperLUSupport.h b/src/eigen/Eigen/src/SuperLUSupport/SuperLUSupport.h similarity index 100% rename from xs/src/eigen/Eigen/src/SuperLUSupport/SuperLUSupport.h rename to src/eigen/Eigen/src/SuperLUSupport/SuperLUSupport.h diff --git a/xs/src/eigen/Eigen/src/UmfPackSupport/UmfPackSupport.h b/src/eigen/Eigen/src/UmfPackSupport/UmfPackSupport.h similarity index 85% rename from xs/src/eigen/Eigen/src/UmfPackSupport/UmfPackSupport.h rename to src/eigen/Eigen/src/UmfPackSupport/UmfPackSupport.h index dc74de935d..91c09ab133 100644 --- a/xs/src/eigen/Eigen/src/UmfPackSupport/UmfPackSupport.h +++ b/src/eigen/Eigen/src/UmfPackSupport/UmfPackSupport.h @@ -10,19 +10,37 @@ #ifndef EIGEN_UMFPACKSUPPORT_H #define EIGEN_UMFPACKSUPPORT_H -namespace Eigen { +namespace Eigen { /* TODO extract L, extract U, compute det, etc... */ // generic double/complex wrapper functions: -inline void umfpack_defaults(double control[UMFPACK_CONTROL], double) +inline void umfpack_defaults(double control[UMFPACK_CONTROL], double) { umfpack_di_defaults(control); } -inline void umfpack_defaults(double control[UMFPACK_CONTROL], std::complex) +inline void umfpack_defaults(double control[UMFPACK_CONTROL], std::complex) { umfpack_zi_defaults(control); } +inline void umfpack_report_info(double control[UMFPACK_CONTROL], double info[UMFPACK_INFO], double) +{ umfpack_di_report_info(control, info);} + +inline void umfpack_report_info(double control[UMFPACK_CONTROL], double info[UMFPACK_INFO], std::complex) +{ umfpack_zi_report_info(control, info);} + +inline void umfpack_report_status(double control[UMFPACK_CONTROL], int status, double) +{ umfpack_di_report_status(control, status);} + +inline void umfpack_report_status(double control[UMFPACK_CONTROL], int status, std::complex) +{ umfpack_zi_report_status(control, status);} + +inline void umfpack_report_control(double control[UMFPACK_CONTROL], double) +{ umfpack_di_report_control(control);} + +inline void umfpack_report_control(double control[UMFPACK_CONTROL], std::complex) +{ umfpack_zi_report_control(control);} + inline void umfpack_free_numeric(void **Numeric, double) { umfpack_di_free_numeric(Numeric); *Numeric = 0; } @@ -156,6 +174,7 @@ class UmfPackLU : public SparseSolverBase > public: typedef Array UmfpackControl; + typedef Array UmfpackInfo; UmfPackLU() : m_dummy(0,0), mp_matrix(m_dummy) @@ -215,7 +234,7 @@ class UmfPackLU : public SparseSolverBase > return m_q; } - /** Computes the sparse Cholesky decomposition of \a matrix + /** Computes the sparse Cholesky decomposition of \a matrix * Note that the matrix should be column-major, and in compressed format for best performance. * \sa SparseMatrix::makeCompressed(). */ @@ -240,7 +259,7 @@ class UmfPackLU : public SparseSolverBase > { if(m_symbolic) umfpack_free_symbolic(&m_symbolic,Scalar()); if(m_numeric) umfpack_free_numeric(&m_numeric,Scalar()); - + grab(matrix.derived()); analyzePattern_impl(); @@ -267,7 +286,7 @@ class UmfPackLU : public SparseSolverBase > { return m_control; } - + /** Provides access to the control settings array used by UmfPack. * * If this array contains NaN's, the default values are used. @@ -278,7 +297,7 @@ class UmfPackLU : public SparseSolverBase > { return m_control; } - + /** Performs a numeric decomposition of \a matrix * * The given matrix must has the same sparcity than the matrix on which the pattern anylysis has been performed. @@ -293,10 +312,38 @@ class UmfPackLU : public SparseSolverBase > umfpack_free_numeric(&m_numeric,Scalar()); grab(matrix.derived()); - + factorize_impl(); } + /** Prints the current UmfPack control settings. + * + * \sa umfpackControl() + */ + void umfpackReportControl() + { + umfpack_report_control(m_control.data(), Scalar()); + } + + /** Prints statistics collected by UmfPack. + * + * \sa analyzePattern(), compute() + */ + void umfpackReportInfo() + { + eigen_assert(m_analysisIsOk && "UmfPackLU: you must first call analyzePattern()"); + umfpack_report_info(m_control.data(), m_umfpackInfo.data(), Scalar()); + } + + /** Prints the status of the previous factorization operation performed by UmfPack (symbolic or numerical factorization). + * + * \sa analyzePattern(), compute() + */ + void umfpackReportStatus() { + eigen_assert(m_analysisIsOk && "UmfPackLU: you must first call analyzePattern()"); + umfpack_report_status(m_control.data(), m_fact_errorCode, Scalar()); + } + /** \internal */ template bool _solve_impl(const MatrixBase &b, MatrixBase &x) const; @@ -314,41 +361,42 @@ class UmfPackLU : public SparseSolverBase > m_numeric = 0; m_symbolic = 0; m_extractedDataAreDirty = true; + + umfpack_defaults(m_control.data(), Scalar()); } - + void analyzePattern_impl() { - umfpack_defaults(m_control.data(), Scalar()); - int errorCode = 0; - errorCode = umfpack_symbolic(internal::convert_index(mp_matrix.rows()), - internal::convert_index(mp_matrix.cols()), - mp_matrix.outerIndexPtr(), mp_matrix.innerIndexPtr(), mp_matrix.valuePtr(), - &m_symbolic, m_control.data(), 0); + m_fact_errorCode = umfpack_symbolic(internal::convert_index(mp_matrix.rows()), + internal::convert_index(mp_matrix.cols()), + mp_matrix.outerIndexPtr(), mp_matrix.innerIndexPtr(), mp_matrix.valuePtr(), + &m_symbolic, m_control.data(), m_umfpackInfo.data()); m_isInitialized = true; - m_info = errorCode ? InvalidInput : Success; + m_info = m_fact_errorCode ? InvalidInput : Success; m_analysisIsOk = true; m_factorizationIsOk = false; m_extractedDataAreDirty = true; } - + void factorize_impl() { + m_fact_errorCode = umfpack_numeric(mp_matrix.outerIndexPtr(), mp_matrix.innerIndexPtr(), mp_matrix.valuePtr(), - m_symbolic, &m_numeric, m_control.data(), 0); + m_symbolic, &m_numeric, m_control.data(), m_umfpackInfo.data()); m_info = m_fact_errorCode == UMFPACK_OK ? Success : NumericalIssue; m_factorizationIsOk = true; m_extractedDataAreDirty = true; } - + template void grab(const EigenBase &A) { mp_matrix.~UmfpackMatrixRef(); ::new (&mp_matrix) UmfpackMatrixRef(A.derived()); } - + void grab(const UmfpackMatrixRef &A) { if(&(A.derived()) != &mp_matrix) @@ -357,19 +405,20 @@ class UmfPackLU : public SparseSolverBase > ::new (&mp_matrix) UmfpackMatrixRef(A); } } - + // cached data to reduce reallocation, etc. mutable LUMatrixType m_l; int m_fact_errorCode; UmfpackControl m_control; - + mutable UmfpackInfo m_umfpackInfo; + mutable LUMatrixType m_u; mutable IntColVectorType m_p; mutable IntRowVectorType m_q; UmfpackMatrixType m_dummy; UmfpackMatrixRef mp_matrix; - + void* m_numeric; void* m_symbolic; @@ -377,7 +426,7 @@ class UmfPackLU : public SparseSolverBase > int m_factorizationIsOk; int m_analysisIsOk; mutable bool m_extractedDataAreDirty; - + private: UmfPackLU(const UmfPackLU& ) { } }; @@ -427,7 +476,7 @@ bool UmfPackLU::_solve_impl(const MatrixBase &b, MatrixBas eigen_assert((BDerived::Flags&RowMajorBit)==0 && "UmfPackLU backend does not support non col-major rhs yet"); eigen_assert((XDerived::Flags&RowMajorBit)==0 && "UmfPackLU backend does not support non col-major result yet"); eigen_assert(b.derived().data() != x.derived().data() && " Umfpack does not support inplace solve"); - + int errorCode; Scalar* x_ptr = 0; Matrix x_tmp; @@ -442,7 +491,7 @@ bool UmfPackLU::_solve_impl(const MatrixBase &b, MatrixBas x_ptr = &x.col(j).coeffRef(0); errorCode = umfpack_solve(UMFPACK_A, mp_matrix.outerIndexPtr(), mp_matrix.innerIndexPtr(), mp_matrix.valuePtr(), - x_ptr, &b.const_cast_derived().col(j).coeffRef(0), m_numeric, m_control.data(), 0); + x_ptr, &b.const_cast_derived().col(j).coeffRef(0), m_numeric, m_control.data(), m_umfpackInfo.data()); if(x.innerStride()!=1) x.col(j) = x_tmp; if (errorCode!=0) diff --git a/xs/src/eigen/Eigen/src/misc/Image.h b/src/eigen/Eigen/src/misc/Image.h similarity index 100% rename from xs/src/eigen/Eigen/src/misc/Image.h rename to src/eigen/Eigen/src/misc/Image.h diff --git a/xs/src/eigen/Eigen/src/misc/Kernel.h b/src/eigen/Eigen/src/misc/Kernel.h similarity index 100% rename from xs/src/eigen/Eigen/src/misc/Kernel.h rename to src/eigen/Eigen/src/misc/Kernel.h diff --git a/xs/src/eigen/Eigen/src/misc/RealSvd2x2.h b/src/eigen/Eigen/src/misc/RealSvd2x2.h similarity index 100% rename from xs/src/eigen/Eigen/src/misc/RealSvd2x2.h rename to src/eigen/Eigen/src/misc/RealSvd2x2.h diff --git a/xs/src/eigen/Eigen/src/misc/blas.h b/src/eigen/Eigen/src/misc/blas.h similarity index 100% rename from xs/src/eigen/Eigen/src/misc/blas.h rename to src/eigen/Eigen/src/misc/blas.h diff --git a/xs/src/eigen/Eigen/src/misc/lapack.h b/src/eigen/Eigen/src/misc/lapack.h similarity index 100% rename from xs/src/eigen/Eigen/src/misc/lapack.h rename to src/eigen/Eigen/src/misc/lapack.h diff --git a/xs/src/eigen/Eigen/src/misc/lapacke.h b/src/eigen/Eigen/src/misc/lapacke.h similarity index 100% rename from xs/src/eigen/Eigen/src/misc/lapacke.h rename to src/eigen/Eigen/src/misc/lapacke.h diff --git a/xs/src/eigen/Eigen/src/misc/lapacke_mangling.h b/src/eigen/Eigen/src/misc/lapacke_mangling.h similarity index 100% rename from xs/src/eigen/Eigen/src/misc/lapacke_mangling.h rename to src/eigen/Eigen/src/misc/lapacke_mangling.h diff --git a/xs/src/eigen/Eigen/src/plugins/ArrayCwiseBinaryOps.h b/src/eigen/Eigen/src/plugins/ArrayCwiseBinaryOps.h similarity index 100% rename from xs/src/eigen/Eigen/src/plugins/ArrayCwiseBinaryOps.h rename to src/eigen/Eigen/src/plugins/ArrayCwiseBinaryOps.h diff --git a/xs/src/eigen/Eigen/src/plugins/ArrayCwiseUnaryOps.h b/src/eigen/Eigen/src/plugins/ArrayCwiseUnaryOps.h similarity index 100% rename from xs/src/eigen/Eigen/src/plugins/ArrayCwiseUnaryOps.h rename to src/eigen/Eigen/src/plugins/ArrayCwiseUnaryOps.h diff --git a/xs/src/eigen/Eigen/src/plugins/BlockMethods.h b/src/eigen/Eigen/src/plugins/BlockMethods.h similarity index 100% rename from xs/src/eigen/Eigen/src/plugins/BlockMethods.h rename to src/eigen/Eigen/src/plugins/BlockMethods.h diff --git a/xs/src/eigen/Eigen/src/plugins/CommonCwiseBinaryOps.h b/src/eigen/Eigen/src/plugins/CommonCwiseBinaryOps.h similarity index 100% rename from xs/src/eigen/Eigen/src/plugins/CommonCwiseBinaryOps.h rename to src/eigen/Eigen/src/plugins/CommonCwiseBinaryOps.h diff --git a/xs/src/eigen/Eigen/src/plugins/CommonCwiseUnaryOps.h b/src/eigen/Eigen/src/plugins/CommonCwiseUnaryOps.h similarity index 100% rename from xs/src/eigen/Eigen/src/plugins/CommonCwiseUnaryOps.h rename to src/eigen/Eigen/src/plugins/CommonCwiseUnaryOps.h diff --git a/xs/src/eigen/Eigen/src/plugins/MatrixCwiseBinaryOps.h b/src/eigen/Eigen/src/plugins/MatrixCwiseBinaryOps.h similarity index 100% rename from xs/src/eigen/Eigen/src/plugins/MatrixCwiseBinaryOps.h rename to src/eigen/Eigen/src/plugins/MatrixCwiseBinaryOps.h diff --git a/xs/src/eigen/Eigen/src/plugins/MatrixCwiseUnaryOps.h b/src/eigen/Eigen/src/plugins/MatrixCwiseUnaryOps.h similarity index 100% rename from xs/src/eigen/Eigen/src/plugins/MatrixCwiseUnaryOps.h rename to src/eigen/Eigen/src/plugins/MatrixCwiseUnaryOps.h diff --git a/xs/src/eigen/README.md b/src/eigen/README.md similarity index 79% rename from xs/src/eigen/README.md rename to src/eigen/README.md index f56262cca2..39892c00bb 100644 --- a/xs/src/eigen/README.md +++ b/src/eigen/README.md @@ -1,5 +1,5 @@ THIS IS NOT THE COMPLETE EIGEN DISTRIBUTION. ONLY FILES NEEDED FOR COMPILING EIGEN INTO SLIC3R WERE PUT INTO THE SLIC3R SOURCE DISTRIBUTION. -THIS DIRECTORY CONTAINS PIECES OF THE EIGEN 3.3.3 SOURCE DISTRIBUTION. +THIS DIRECTORY CONTAINS PIECES OF THE EIGEN 3.3.5 b3f3d4950030 SOURCE DISTRIBUTION. **Eigen is a C++ template library for linear algebra: matrices, vectors, numerical solvers, and related algorithms.** diff --git a/xs/src/expat/COPYING b/src/expat/COPYING similarity index 100% rename from xs/src/expat/COPYING rename to src/expat/COPYING diff --git a/xs/src/expat/README b/src/expat/README similarity index 100% rename from xs/src/expat/README rename to src/expat/README diff --git a/xs/src/expat/ascii.h b/src/expat/ascii.h similarity index 100% rename from xs/src/expat/ascii.h rename to src/expat/ascii.h diff --git a/xs/src/expat/asciitab.h b/src/expat/asciitab.h similarity index 100% rename from xs/src/expat/asciitab.h rename to src/expat/asciitab.h diff --git a/xs/src/expat/expat.h b/src/expat/expat.h similarity index 100% rename from xs/src/expat/expat.h rename to src/expat/expat.h diff --git a/xs/src/expat/expat_config.h b/src/expat/expat_config.h similarity index 100% rename from xs/src/expat/expat_config.h rename to src/expat/expat_config.h diff --git a/xs/src/expat/expat_external.h b/src/expat/expat_external.h similarity index 100% rename from xs/src/expat/expat_external.h rename to src/expat/expat_external.h diff --git a/xs/src/expat/iasciitab.h b/src/expat/iasciitab.h similarity index 100% rename from xs/src/expat/iasciitab.h rename to src/expat/iasciitab.h diff --git a/xs/src/expat/internal.h b/src/expat/internal.h similarity index 100% rename from xs/src/expat/internal.h rename to src/expat/internal.h diff --git a/xs/src/expat/latin1tab.h b/src/expat/latin1tab.h similarity index 100% rename from xs/src/expat/latin1tab.h rename to src/expat/latin1tab.h diff --git a/xs/src/expat/nametab.h b/src/expat/nametab.h similarity index 100% rename from xs/src/expat/nametab.h rename to src/expat/nametab.h diff --git a/xs/src/expat/utf8tab.h b/src/expat/utf8tab.h similarity index 100% rename from xs/src/expat/utf8tab.h rename to src/expat/utf8tab.h diff --git a/xs/src/expat/xmlparse.c b/src/expat/xmlparse.c similarity index 100% rename from xs/src/expat/xmlparse.c rename to src/expat/xmlparse.c diff --git a/xs/src/expat/xmlrole.c b/src/expat/xmlrole.c similarity index 100% rename from xs/src/expat/xmlrole.c rename to src/expat/xmlrole.c diff --git a/xs/src/expat/xmlrole.h b/src/expat/xmlrole.h similarity index 100% rename from xs/src/expat/xmlrole.h rename to src/expat/xmlrole.h diff --git a/xs/src/expat/xmltok.c b/src/expat/xmltok.c similarity index 100% rename from xs/src/expat/xmltok.c rename to src/expat/xmltok.c diff --git a/xs/src/expat/xmltok.h b/src/expat/xmltok.h similarity index 100% rename from xs/src/expat/xmltok.h rename to src/expat/xmltok.h diff --git a/xs/src/expat/xmltok_impl.h b/src/expat/xmltok_impl.h similarity index 100% rename from xs/src/expat/xmltok_impl.h rename to src/expat/xmltok_impl.h diff --git a/xs/src/expat/xmltok_impl.inc b/src/expat/xmltok_impl.inc similarity index 100% rename from xs/src/expat/xmltok_impl.inc rename to src/expat/xmltok_impl.inc diff --git a/xs/src/expat/xmltok_ns.inc b/src/expat/xmltok_ns.inc similarity index 100% rename from xs/src/expat/xmltok_ns.inc rename to src/expat/xmltok_ns.inc diff --git a/xs/src/glew/LICENSE.txt b/src/glew/LICENSE.txt similarity index 100% rename from xs/src/glew/LICENSE.txt rename to src/glew/LICENSE.txt diff --git a/xs/src/glew/README.md b/src/glew/README.md similarity index 100% rename from xs/src/glew/README.md rename to src/glew/README.md diff --git a/xs/src/glew/include/GL/glew.h b/src/glew/include/GL/glew.h similarity index 100% rename from xs/src/glew/include/GL/glew.h rename to src/glew/include/GL/glew.h diff --git a/xs/src/glew/include/GL/glxew.h b/src/glew/include/GL/glxew.h similarity index 100% rename from xs/src/glew/include/GL/glxew.h rename to src/glew/include/GL/glxew.h diff --git a/xs/src/glew/include/GL/wglew.h b/src/glew/include/GL/wglew.h similarity index 100% rename from xs/src/glew/include/GL/wglew.h rename to src/glew/include/GL/wglew.h diff --git a/xs/src/glew/src/glew.c b/src/glew/src/glew.c similarity index 100% rename from xs/src/glew/src/glew.c rename to src/glew/src/glew.c diff --git a/src/igl/AABB.cpp b/src/igl/AABB.cpp new file mode 100644 index 0000000000..1b88e9faa4 --- /dev/null +++ b/src/igl/AABB.cpp @@ -0,0 +1,1076 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "AABB.h" +#include "EPS.h" +#include "barycenter.h" +#include "colon.h" +#include "doublearea.h" +#include "point_simplex_squared_distance.h" +#include "project_to_line_segment.h" +#include "sort.h" +#include "volume.h" +#include "ray_box_intersect.h" +#include "parallel_for.h" +#include "ray_mesh_intersect.h" +#include +#include +#include +#include +#include +#include + +template +template +IGL_INLINE void igl::AABB::init( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const Eigen::MatrixBase & bb_mins, + const Eigen::MatrixBase & bb_maxs, + const Eigen::MatrixBase & elements, + const int i) +{ + using namespace std; + using namespace Eigen; + deinit(); + if(bb_mins.size() > 0) + { + assert(bb_mins.rows() == bb_maxs.rows() && "Serial tree arrays must match"); + assert(bb_mins.cols() == V.cols() && "Serial tree array dim must match V"); + assert(bb_mins.cols() == bb_maxs.cols() && "Serial tree arrays must match"); + assert(bb_mins.rows() == elements.rows() && + "Serial tree arrays must match"); + // construct from serialization + m_box.extend(bb_mins.row(i).transpose()); + m_box.extend(bb_maxs.row(i).transpose()); + m_primitive = elements(i); + // Not leaf then recurse + if(m_primitive == -1) + { + m_left = new AABB(); + m_left->init( V,Ele,bb_mins,bb_maxs,elements,2*i+1); + m_right = new AABB(); + m_right->init( V,Ele,bb_mins,bb_maxs,elements,2*i+2); + //m_depth = std::max( m_left->m_depth, m_right->m_depth)+1; + } + }else + { + VectorXi allI = colon(0,Ele.rows()-1); + MatrixXDIMS BC; + if(Ele.cols() == 1) + { + // points + BC = V; + }else + { + // Simplices + barycenter(V,Ele,BC); + } + MatrixXi SI(BC.rows(),BC.cols()); + { + MatrixXDIMS _; + MatrixXi IS; + igl::sort(BC,1,true,_,IS); + // Need SI(i) to tell which place i would be sorted into + const int dim = IS.cols(); + for(int i = 0;i +template +void igl::AABB::init( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele) +{ + using namespace Eigen; + // deinit will be immediately called... + return init(V,Ele,MatrixXDIMS(),MatrixXDIMS(),VectorXi(),0); +} + + template +template < + typename DerivedEle, + typename DerivedSI, + typename DerivedI> +IGL_INLINE void igl::AABB::init( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const Eigen::MatrixBase & SI, + const Eigen::MatrixBase & I) +{ + using namespace Eigen; + using namespace std; + deinit(); + if(V.size() == 0 || Ele.size() == 0 || I.size() == 0) + { + return; + } + assert(DIM == V.cols() && "V.cols() should matched declared dimension"); + //const Scalar inf = numeric_limits::infinity(); + m_box = AlignedBox(); + // Compute bounding box + for(int i = 0;iint + { + size_t n = (A.size()-1)/2; + nth_element(A.data(),A.data()+n,A.data()+A.size()); + return A(n); + }; + const int med = median(SIdI); + VectorXi LI((I.rows()+1)/2),RI(I.rows()/2); + assert(LI.rows()+RI.rows() == I.rows()); + // Distribute left and right + { + int li = 0; + int ri = 0; + for(int i = 0;i0) + { + m_left = new AABB(); + m_left->init(V,Ele,SI,LI); + //m_depth = std::max(m_depth, m_left->m_depth+1); + } + if(RI.rows()>0) + { + m_right = new AABB(); + m_right->init(V,Ele,SI,RI); + //m_depth = std::max(m_depth, m_right->m_depth+1); + } + } + } +} + +template +IGL_INLINE bool igl::AABB::is_leaf() const +{ + return m_primitive != -1; +} + +template +template +IGL_INLINE std::vector igl::AABB::find( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const Eigen::MatrixBase & q, + const bool first) const +{ + using namespace std; + using namespace Eigen; + assert(q.size() == DIM && + "Query dimension should match aabb dimension"); + assert(Ele.cols() == V.cols()+1 && + "AABB::find only makes sense for (d+1)-simplices"); + const Scalar epsilon = igl::EPS(); + // Check if outside bounding box + bool inside = m_box.contains(q.transpose()); + if(!inside) + { + return std::vector(); + } + assert(m_primitive==-1 || (m_left == NULL && m_right == NULL)); + if(is_leaf()) + { + // Initialize to some value > -epsilon + Scalar a1=0,a2=0,a3=0,a4=0; + switch(DIM) + { + case 3: + { + // Barycentric coordinates + typedef Eigen::Matrix RowVector3S; + const RowVector3S V1 = V.row(Ele(m_primitive,0)); + const RowVector3S V2 = V.row(Ele(m_primitive,1)); + const RowVector3S V3 = V.row(Ele(m_primitive,2)); + const RowVector3S V4 = V.row(Ele(m_primitive,3)); + a1 = volume_single(V2,V4,V3,(RowVector3S)q); + a2 = volume_single(V1,V3,V4,(RowVector3S)q); + a3 = volume_single(V1,V4,V2,(RowVector3S)q); + a4 = volume_single(V1,V2,V3,(RowVector3S)q); + break; + } + case 2: + { + // Barycentric coordinates + typedef Eigen::Matrix Vector2S; + const Vector2S V1 = V.row(Ele(m_primitive,0)); + const Vector2S V2 = V.row(Ele(m_primitive,1)); + const Vector2S V3 = V.row(Ele(m_primitive,2)); + // Hack for now to keep templates simple. If becomes bottleneck + // consider using std::enable_if_t + const Vector2S q2 = q.head(2); + a1 = doublearea_single(V1,V2,q2); + a2 = doublearea_single(V2,V3,q2); + a3 = doublearea_single(V3,V1,q2); + break; + } + default:assert(false); + } + // Normalization is important for correcting sign + Scalar sum = a1+a2+a3+a4; + a1 /= sum; + a2 /= sum; + a3 /= sum; + a4 /= sum; + if( + a1>=-epsilon && + a2>=-epsilon && + a3>=-epsilon && + a4>=-epsilon) + { + return std::vector(1,m_primitive); + }else + { + return std::vector(); + } + } + std::vector left = m_left->find(V,Ele,q,first); + if(first && !left.empty()) + { + return left; + } + std::vector right = m_right->find(V,Ele,q,first); + if(first) + { + return right; + } + left.insert(left.end(),right.begin(),right.end()); + return left; +} + +template +IGL_INLINE int igl::AABB::subtree_size() const +{ + // 1 for self + int n = 1; + int n_left = 0,n_right = 0; + if(m_left != NULL) + { + n_left = m_left->subtree_size(); + } + if(m_right != NULL) + { + n_right = m_right->subtree_size(); + } + n += 2*std::max(n_left,n_right); + return n; +} + + +template +template +IGL_INLINE void igl::AABB::serialize( + Eigen::PlainObjectBase & bb_mins, + Eigen::PlainObjectBase & bb_maxs, + Eigen::PlainObjectBase & elements, + const int i) const +{ + using namespace std; + using namespace Eigen; + // Calling for root then resize output + if(i==0) + { + const int m = subtree_size(); + //cout<<"m: "<serialize(bb_mins,bb_maxs,elements,2*i+1); + } + if(m_right != NULL) + { + m_right->serialize(bb_mins,bb_maxs,elements,2*i+2); + } +} + +template +template +IGL_INLINE typename igl::AABB::Scalar +igl::AABB::squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & p, + int & i, + Eigen::PlainObjectBase & c) const +{ + return squared_distance(V,Ele,p,std::numeric_limits::infinity(),i,c); +} + + +template +template +IGL_INLINE typename igl::AABB::Scalar +igl::AABB::squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & p, + Scalar low_sqr_d, + Scalar up_sqr_d, + int & i, + Eigen::PlainObjectBase & c) const +{ + using namespace Eigen; + using namespace std; + //assert(low_sqr_d <= up_sqr_d); + if(low_sqr_d > up_sqr_d) + { + return low_sqr_d; + } + Scalar sqr_d = up_sqr_d; + //assert(DIM == 3 && "Code has only been tested for DIM == 3"); + assert((Ele.cols() == 3 || Ele.cols() == 2 || Ele.cols() == 1) + && "Code has only been tested for simplex sizes 3,2,1"); + + assert(m_primitive==-1 || (m_left == NULL && m_right == NULL)); + if(is_leaf()) + { + leaf_squared_distance(V,Ele,p,low_sqr_d,sqr_d,i,c); + }else + { + bool looked_left = false; + bool looked_right = false; + const auto & look_left = [&]() + { + int i_left; + RowVectorDIMS c_left = c; + Scalar sqr_d_left = + m_left->squared_distance(V,Ele,p,low_sqr_d,sqr_d,i_left,c_left); + this->set_min(p,sqr_d_left,i_left,c_left,sqr_d,i,c); + looked_left = true; + }; + const auto & look_right = [&]() + { + int i_right; + RowVectorDIMS c_right = c; + Scalar sqr_d_right = + m_right->squared_distance(V,Ele,p,low_sqr_d,sqr_d,i_right,c_right); + this->set_min(p,sqr_d_right,i_right,c_right,sqr_d,i,c); + looked_right = true; + }; + + // must look left or right if in box + if(m_left->m_box.contains(p.transpose())) + { + look_left(); + } + if(m_right->m_box.contains(p.transpose())) + { + look_right(); + } + // if haven't looked left and could be less than current min, then look + Scalar left_up_sqr_d = + m_left->m_box.squaredExteriorDistance(p.transpose()); + Scalar right_up_sqr_d = + m_right->m_box.squaredExteriorDistance(p.transpose()); + if(left_up_sqr_d < right_up_sqr_d) + { + if(!looked_left && left_up_sqr_d +template +IGL_INLINE typename igl::AABB::Scalar +igl::AABB::squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & p, + Scalar up_sqr_d, + int & i, + Eigen::PlainObjectBase & c) const +{ + return squared_distance(V,Ele,p,0.0,up_sqr_d,i,c); +} + +template +template < + typename DerivedEle, + typename DerivedP, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> +IGL_INLINE void igl::AABB::squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const Eigen::MatrixBase & P, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C) const +{ + assert(P.cols() == V.cols() && "cols in P should match dim of cols in V"); + sqrD.resize(P.rows(),1); + I.resize(P.rows(),1); + C.resizeLike(P); + // O( #P * log #Ele ), where log #Ele is really the depth of this AABB + // hierarchy + //for(int p = 0;p +template < + typename DerivedEle, + typename Derivedother_V, + typename Derivedother_Ele, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> +IGL_INLINE void igl::AABB::squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const AABB & other, + const Eigen::MatrixBase & other_V, + const Eigen::MatrixBase & other_Ele, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C) const +{ + assert(other_Ele.cols() == 1 && + "Only implemented for other as list of points"); + assert(other_V.cols() == V.cols() && "other must match this dimension"); + sqrD.setConstant(other_Ele.rows(),1,std::numeric_limits::infinity()); + I.resize(other_Ele.rows(),1); + C.resize(other_Ele.rows(),other_V.cols()); + // All points in other_V currently think they need to check against root of + // this. The point of using another AABB is to quickly prune chunks of + // other_V so that most points just check some subtree of this. + + // This holds a conservative estimate of max(sqr_D) where sqr_D is the + // current best minimum squared distance for all points in this subtree + double up_sqr_d = std::numeric_limits::infinity(); + squared_distance_helper( + V,Ele,&other,other_V,other_Ele,0,up_sqr_d,sqrD,I,C); +} + +template +template < + typename DerivedEle, + typename Derivedother_V, + typename Derivedother_Ele, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> +IGL_INLINE typename igl::AABB::Scalar + igl::AABB::squared_distance_helper( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const AABB * other, + const Eigen::MatrixBase & other_V, + const Eigen::MatrixBase & other_Ele, + const Scalar /*up_sqr_d*/, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C) const +{ + using namespace std; + using namespace Eigen; + + // This implementation is a bit disappointing. There's no major speed up. Any + // performance gains seem to come from accidental cache coherency and + // diminish for larger "other" (the opposite of what was intended). + + // Base case + if(other->is_leaf() && this->is_leaf()) + { + Scalar sqr_d = sqrD(other->m_primitive); + int i = I(other->m_primitive); + RowVectorDIMS c = C.row( other->m_primitive); + RowVectorDIMS p = other_V.row(other->m_primitive); + leaf_squared_distance(V,Ele,p,sqr_d,i,c); + sqrD( other->m_primitive) = sqr_d; + I( other->m_primitive) = i; + C.row(other->m_primitive) = c; + //cout<<"leaf: "<m_low_sqr_d = sqr_d; + return sqr_d; + } + + if(other->is_leaf()) + { + Scalar sqr_d = sqrD(other->m_primitive); + int i = I(other->m_primitive); + RowVectorDIMS c = C.row( other->m_primitive); + RowVectorDIMS p = other_V.row(other->m_primitive); + sqr_d = squared_distance(V,Ele,p,sqr_d,i,c); + sqrD( other->m_primitive) = sqr_d; + I( other->m_primitive) = i; + C.row(other->m_primitive) = c; + //other->m_low_sqr_d = sqr_d; + return sqr_d; + } + + //// Exact minimum squared distance between arbitrary primitives inside this and + //// othre's bounding boxes + //const auto & min_squared_distance = [&]( + // const AABB * A, + // const AABB * B)->Scalar + //{ + // return A->m_box.squaredExteriorDistance(B->m_box); + //}; + + if(this->is_leaf()) + { + //if(min_squared_distance(this,other) < other->m_low_sqr_d) + if(true) + { + this->squared_distance_helper( + V,Ele,other->m_left,other_V,other_Ele,0,sqrD,I,C); + this->squared_distance_helper( + V,Ele,other->m_right,other_V,other_Ele,0,sqrD,I,C); + }else + { + // This is never reached... + } + //// we know other is not a leaf + //other->m_low_sqr_d = std::max(other->m_left->m_low_sqr_d,other->m_right->m_low_sqr_d); + return 0; + } + + // FORCE DOWN TO OTHER LEAF EVAL + //if(min_squared_distance(this,other) < other->m_low_sqr_d) + if(true) + { + if(true) + { + this->squared_distance_helper( + V,Ele,other->m_left,other_V,other_Ele,0,sqrD,I,C); + this->squared_distance_helper( + V,Ele,other->m_right,other_V,other_Ele,0,sqrD,I,C); + }else // this direction never seems to be faster + { + this->m_left->squared_distance_helper( + V,Ele,other,other_V,other_Ele,0,sqrD,I,C); + this->m_right->squared_distance_helper( + V,Ele,other,other_V,other_Ele,0,sqrD,I,C); + } + }else + { + // this is never reached ... :-( + } + //// we know other is not a leaf + //other->m_low_sqr_d = std::max(other->m_left->m_low_sqr_d,other->m_right->m_low_sqr_d); + + return 0; +#if 0 // False + + // _Very_ conservative approximation of maximum squared distance between + // primitives inside this and other's bounding boxes + const auto & max_squared_distance = []( + const AABB * A, + const AABB * B)->Scalar + { + AlignedBox combo = A->m_box; + combo.extend(B->m_box); + return combo.diagonal().squaredNorm(); + }; + + //// other base-case + //if(other->is_leaf()) + //{ + // double sqr_d = sqrD(other->m_primitive); + // int i = I(other->m_primitive); + // RowVectorDIMS c = C.row(m_primitive); + // RowVectorDIMS p = other_V.row(m_primitive); + // leaf_squared_distance(V,Ele,p,sqr_d,i,c); + // sqrD(other->m_primitive) = sqr_d; + // I(other->m_primitive) = i; + // C.row(m_primitive) = c; + // return; + //} + std::vector * > this_list; + if(this->is_leaf()) + { + this_list.push_back(this); + }else + { + assert(this->m_left); + this_list.push_back(this->m_left); + assert(this->m_right); + this_list.push_back(this->m_right); + } + std::vector *> other_list; + if(other->is_leaf()) + { + other_list.push_back(other); + }else + { + assert(other->m_left); + other_list.push_back(other->m_left); + assert(other->m_right); + other_list.push_back(other->m_right); + } + + //const std::function * other) + // > low_sqr_d = [&sqrD,&low_sqr_d](const AABB * other)->Scalar + // { + // if(other->is_leaf()) + // { + // return sqrD(other->m_primitive); + // }else + // { + // return std::max(low_sqr_d(other->m_left),low_sqr_d(other->m_right)); + // } + // }; + + //// Potentially recurse on all pairs, if minimum distance is less than running + //// bound + //Eigen::Matrix other_low_sqr_d = + // Eigen::Matrix::Constant(other_list.size(),1,up_sqr_d); + for(size_t child = 0;child this_low_sqr_d(this_list.size(),1); + for(size_t t = 0;t this_low_sqr_d(1)) + ) + { + std::swap(this_list[0],this_list[1]); + //std::swap(this_low_sqr_d(0),this_low_sqr_d(1)); + } + const Scalar sqr_d = this_low_sqr_d.minCoeff(); + + + for(size_t t = 0;tm_low_sqr_d) + { + //cout<<"before: "<squared_distance_helper( + // V,Ele,other_tree,other_V,other_Ele,other_low_sqr_d(child),sqrD,I,C)); + //cout<<"after: "<squared_distance_helper( + V,Ele,other_tree,other_V,other_Ele,0,sqrD,I,C); + } + } + } + //const Scalar ret = other_low_sqr_d.maxCoeff(); + //const auto mm = low_sqr_d(other); + //assert(mm == ret); + //cout<<"non-leaf: "<is_leaf()) + { + other->m_low_sqr_d = std::max(other->m_left->m_low_sqr_d,other->m_right->m_low_sqr_d); + } + return 0; +#endif +} + +template +template +IGL_INLINE void igl::AABB::leaf_squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & p, + const Scalar low_sqr_d, + Scalar & sqr_d, + int & i, + Eigen::PlainObjectBase & c) const +{ + using namespace Eigen; + using namespace std; + if(low_sqr_d > sqr_d) + { + sqr_d = low_sqr_d; + return; + } + RowVectorDIMS c_candidate; + Scalar sqr_d_candidate; + igl::point_simplex_squared_distance( + p,V,Ele,m_primitive,sqr_d_candidate,c_candidate); + set_min(p,sqr_d_candidate,m_primitive,c_candidate,sqr_d,i,c); +} + +template +template +IGL_INLINE void igl::AABB::leaf_squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & p, + Scalar & sqr_d, + int & i, + Eigen::PlainObjectBase & c) const +{ + return leaf_squared_distance(V,Ele,p,0,sqr_d,i,c); +} + + +template +IGL_INLINE void igl::AABB::set_min( + const RowVectorDIMS & +#ifndef NDEBUG + p +#endif + , + const Scalar sqr_d_candidate, + const int i_candidate, + const RowVectorDIMS & c_candidate, + Scalar & sqr_d, + int & i, + Eigen::PlainObjectBase & c) const +{ +#ifndef NDEBUG + //std::cout< +template +IGL_INLINE bool +igl::AABB::intersect_ray( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & origin, + const RowVectorDIMS & dir, + std::vector & hits) const +{ + hits.clear(); + const Scalar t0 = 0; + const Scalar t1 = std::numeric_limits::infinity(); + { + Scalar _1,_2; + if(!ray_box_intersect(origin,dir,m_box,t0,t1,_1,_2)) + { + return false; + } + } + if(this->is_leaf()) + { + // Actually process elements + assert((Ele.size() == 0 || Ele.cols() == 3) && "Elements should be triangles"); + // Cheesecake way of hitting element + bool ret = ray_mesh_intersect(origin,dir,V,Ele.row(m_primitive),hits); + // Since we only gave ray_mesh_intersect a single face, it will have set + // any hits to id=0. Set these to this primitive's id + for(auto & hit : hits) + { + hit.id = m_primitive; + } + return ret; + } + std::vector left_hits; + std::vector right_hits; + const bool left_ret = m_left->intersect_ray(V,Ele,origin,dir,left_hits); + const bool right_ret = m_right->intersect_ray(V,Ele,origin,dir,right_hits); + hits.insert(hits.end(),left_hits.begin(),left_hits.end()); + hits.insert(hits.end(),right_hits.begin(),right_hits.end()); + return left_ret || right_ret; +} + +template +template +IGL_INLINE bool +igl::AABB::intersect_ray( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & origin, + const RowVectorDIMS & dir, + igl::Hit & hit) const +{ + // FIXME: Needs a proper path +#if /*false*/ 0 + // BFS + std::queue Q; + // Or DFS + //std::stack Q; + Q.push(this); + bool any_hit = false; + hit.t = std::numeric_limits::infinity(); + while(!Q.empty()) + { + const AABB * tree = Q.front(); + //const AABB * tree = Q.top(); + Q.pop(); + { + Scalar _1,_2; + if(!ray_box_intersect( + origin,dir,tree->m_box,Scalar(0),Scalar(hit.t),_1,_2)) + { + continue; + } + } + if(tree->is_leaf()) + { + // Actually process elements + assert((Ele.size() == 0 || Ele.cols() == 3) && "Elements should be triangles"); + igl::Hit leaf_hit; + if( + ray_mesh_intersect(origin,dir,V,Ele.row(tree->m_primitive),leaf_hit)&& + leaf_hit.t < hit.t) + { + // correct the id + leaf_hit.id = tree->m_primitive; + hit = leaf_hit; + } + continue; + } + // Add children to queue + Q.push(tree->m_left); + Q.push(tree->m_right); + } + return any_hit; +#else + // DFS + return intersect_ray( + V,Ele,origin,dir,std::numeric_limits::infinity(),hit); +#endif +} + +template +template +IGL_INLINE bool +igl::AABB::intersect_ray( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & origin, + const RowVectorDIMS & dir, + const Scalar _min_t, + igl::Hit & hit) const +{ + //// Naive, slow + //std::vector hits; + //intersect_ray(V,Ele,origin,dir,hits); + //if(hits.size() > 0) + //{ + // hit = hits.front(); + // return true; + //}else + //{ + // return false; + //} + Scalar min_t = _min_t; + const Scalar t0 = 0; + { + Scalar _1,_2; + if(!ray_box_intersect(origin,dir,m_box,t0,min_t,_1,_2)) + { + return false; + } + } + if(this->is_leaf()) + { + // Actually process elements + assert((Ele.size() == 0 || Ele.cols() == 3) && "Elements should be triangles"); + // Cheesecake way of hitting element + bool ret = ray_mesh_intersect(origin,dir,V,Ele.row(m_primitive),hit); + hit.id = m_primitive; + return ret; + } + + // Doesn't seem like smartly choosing left before/after right makes a + // differnce + igl::Hit left_hit; + igl::Hit right_hit; + bool left_ret = m_left->intersect_ray(V,Ele,origin,dir,min_t,left_hit); + if(left_ret && left_hit.tintersect_ray(V,Ele,origin,dir,min_t,right_hit); + if(right_ret && right_hit.t template<> IGL_INLINE float AABB, 2>::squared_distance( Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, int&, Eigen::PlainObjectBase >&) const { assert(false);return -1;}; + template<> template<> IGL_INLINE float igl::AABB, 2>::squared_distance( Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, float, float, int&, Eigen::PlainObjectBase >&) const { assert(false);return -1;}; + template<> template<> IGL_INLINE void igl::AABB, 2>::init (Eigen::MatrixBase > const&, Eigen::MatrixBase > const&) { assert(false);}; + template<> template<> IGL_INLINE double AABB, 2>::squared_distance( Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, int&, Eigen::PlainObjectBase >&) const { assert(false);return -1;}; + template<> template<> IGL_INLINE double igl::AABB, 2>::squared_distance( Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, double, double, int&, Eigen::PlainObjectBase >&) const { assert(false);return -1;}; + template<> template<> IGL_INLINE void igl::AABB, 2>::init (Eigen::MatrixBase > const&, Eigen::MatrixBase > const&) { assert(false);}; + template<> template<> IGL_INLINE void igl::AABB, 2>::init(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&) {assert(false);}; + template<> template<> IGL_INLINE float igl::AABB, 2>::squared_distance(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, float, float, int&, Eigen::PlainObjectBase >&) const { assert(false);return -1;}; +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template double igl::AABB, 3>::squared_distance >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, double, double, int&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +template void igl::AABB, 3>::init >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +// generated by autoexplicit.sh +template float igl::AABB, 3>::squared_distance >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, float, float, int&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +// generated by autoexplicit.sh +template void igl::AABB, 3>::init >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template void igl::AABB, 3>::serialize, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, int) const; +// generated by autoexplicit.sh +template std::vector > igl::AABB, 3>::find, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, bool) const; +// generated by autoexplicit.sh +template void igl::AABB, 2>::serialize, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, int) const; +// generated by autoexplicit.sh +template std::vector > igl::AABB, 2>::find, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, bool) const; +// generated by autoexplicit.sh +template void igl::AABB, 3>::init, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int); +// generated by autoexplicit.sh +template void igl::AABB, 2>::init, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int); +// generated by autoexplicit.sh +template float igl::AABB, 3>::squared_distance >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, int&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +template void igl::AABB, 3>::squared_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +template void igl::AABB, 3>::squared_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +template void igl::AABB, 3>::squared_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +template double igl::AABB, 3>::squared_distance >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, int&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +template void igl::AABB, 2>::squared_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +template void igl::AABB, 2>::squared_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +template void igl::AABB, 2>::squared_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +template double igl::AABB, 2>::squared_distance >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, int&, Eigen::PlainObjectBase >&) const; +// generated by autoexplicit.sh +template void igl::AABB, 3>::init >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template void igl::AABB, 3>::init >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template void igl::AABB, 2>::init >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +template double igl::AABB, 3>::squared_distance >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, double, int&, Eigen::PlainObjectBase >&) const; +template bool igl::AABB, 3>::intersect_ray >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, Eigen::Matrix const&, igl::Hit&) const; +#endif diff --git a/src/igl/AABB.h b/src/igl/AABB.h new file mode 100644 index 0000000000..891481aa0a --- /dev/null +++ b/src/igl/AABB.h @@ -0,0 +1,413 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_AABB_H +#define IGL_AABB_H + +#include "Hit.h" +#include "igl_inline.h" +#include +#include +#include +namespace igl +{ + // Implementation of semi-general purpose axis-aligned bounding box hierarchy. + // The mesh (V,Ele) is stored and managed by the caller and each routine here + // simply takes it as references (it better not change between calls). + // + // It's a little annoying that the Dimension is a template parameter and not + // picked up at run time from V. This leads to duplicated code for 2d/3d (up to + // dim). + template + class AABB + { +public: + typedef typename DerivedV::Scalar Scalar; + typedef Eigen::Matrix RowVectorDIMS; + typedef Eigen::Matrix VectorDIMS; + typedef Eigen::Matrix MatrixXDIMS; + // Shared pointers are slower... + AABB * m_left; + AABB * m_right; + Eigen::AlignedBox m_box; + // -1 non-leaf + int m_primitive; + //Scalar m_low_sqr_d; + //int m_depth; + AABB(): + m_left(NULL), m_right(NULL), + m_box(), m_primitive(-1) + //m_low_sqr_d(std::numeric_limits::infinity()), + //m_depth(0) + {} + // http://stackoverflow.com/a/3279550/148668 + AABB(const AABB& other): + m_left(other.m_left ? new AABB(*other.m_left) : NULL), + m_right(other.m_right ? new AABB(*other.m_right) : NULL), + m_box(other.m_box), + m_primitive(other.m_primitive) + //m_low_sqr_d(other.m_low_sqr_d), + //m_depth(std::max( + // m_left ? m_left->m_depth + 1 : 0, + // m_right ? m_right->m_depth + 1 : 0)) + { + } + // copy-swap idiom + friend void swap(AABB& first, AABB& second) + { + // Enable ADL + using std::swap; + swap(first.m_left,second.m_left); + swap(first.m_right,second.m_right); + swap(first.m_box,second.m_box); + swap(first.m_primitive,second.m_primitive); + //swap(first.m_low_sqr_d,second.m_low_sqr_d); + //swap(first.m_depth,second.m_depth); + } + // Pass-by-value (aka copy) + AABB& operator=(AABB other) + { + swap(*this,other); + return *this; + } + AABB(AABB&& other): + // initialize via default constructor + AABB() + { + swap(*this,other); + } + // Seems like there should have been an elegant solution to this using + // the copy-swap idiom above: + IGL_INLINE void deinit() + { + m_primitive = -1; + m_box = Eigen::AlignedBox(); + delete m_left; + m_left = NULL; + delete m_right; + m_right = NULL; + } + ~AABB() + { + deinit(); + } + // Build an Axis-Aligned Bounding Box tree for a given mesh and given + // serialization of a previous AABB tree. + // + // Inputs: + // V #V by dim list of mesh vertex positions. + // Ele #Ele by dim+1 list of mesh indices into #V. + // bb_mins max_tree by dim list of bounding box min corner positions + // bb_maxs max_tree by dim list of bounding box max corner positions + // elements max_tree list of element or (not leaf id) indices into Ele + // i recursive call index {0} + template < + typename DerivedEle, + typename Derivedbb_mins, + typename Derivedbb_maxs, + typename Derivedelements> + IGL_INLINE void init( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const Eigen::MatrixBase & bb_mins, + const Eigen::MatrixBase & bb_maxs, + const Eigen::MatrixBase & elements, + const int i = 0); + // Wrapper for root with empty serialization + template + IGL_INLINE void init( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele); + // Build an Axis-Aligned Bounding Box tree for a given mesh. + // + // Inputs: + // V #V by dim list of mesh vertex positions. + // Ele #Ele by dim+1 list of mesh indices into #V. + // SI #Ele by dim list revealing for each coordinate where Ele's + // barycenters would be sorted: SI(e,d) = i --> the dth coordinate of + // the barycenter of the eth element would be placed at position i in a + // sorted list. + // I #I list of indices into Ele of elements to include (for recursive + // calls) + // + template + IGL_INLINE void init( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const Eigen::MatrixBase & SI, + const Eigen::MatrixBase& I); + // Return whether at leaf node + IGL_INLINE bool is_leaf() const; + // Find the indices of elements containing given point: this makes sense + // when Ele is a co-dimension 0 simplex (tets in 3D, triangles in 2D). + // + // Inputs: + // V #V by dim list of mesh vertex positions. **Should be same as used to + // construct mesh.** + // Ele #Ele by dim+1 list of mesh indices into #V. **Should be same as used to + // construct mesh.** + // q dim row-vector query position + // first whether to only return first element containing q + // Returns: + // list of indices of elements containing q + template + IGL_INLINE std::vector find( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const Eigen::MatrixBase & q, + const bool first=false) const; + + // If number of elements m then total tree size should be 2*h where h is + // the deepest depth 2^ceil(log(#Ele*2-1)) + IGL_INLINE int subtree_size() const; + + // Serialize this class into 3 arrays (so we can pass it pack to matlab) + // + // Outputs: + // bb_mins max_tree by dim list of bounding box min corner positions + // bb_maxs max_tree by dim list of bounding box max corner positions + // elements max_tree list of element or (not leaf id) indices into Ele + // i recursive call index into these arrays {0} + template < + typename Derivedbb_mins, + typename Derivedbb_maxs, + typename Derivedelements> + IGL_INLINE void serialize( + Eigen::PlainObjectBase & bb_mins, + Eigen::PlainObjectBase & bb_maxs, + Eigen::PlainObjectBase & elements, + const int i = 0) const; + // Compute squared distance to a query point + // + // Inputs: + // V #V by dim list of vertex positions + // Ele #Ele by dim list of simplex indices + // p dim-long query point + // Outputs: + // i facet index corresponding to smallest distances + // c closest point + // Returns squared distance + // + // Known bugs: currently assumes Elements are triangles regardless of + // dimension. + template + IGL_INLINE Scalar squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & p, + int & i, + Eigen::PlainObjectBase & c) const; +//private: + // Compute squared distance to a query point + // + // Inputs: + // V #V by dim list of vertex positions + // Ele #Ele by dim list of simplex indices + // p dim-long query point + // low_sqr_d lower bound on squared distance, specified maximum squared + // distance + // up_sqr_d current upper bounded on squared distance, current minimum + // squared distance (only consider distances less than this), see + // output. + // Outputs: + // up_sqr_d updated current minimum squared distance + // i facet index corresponding to smallest distances + // c closest point + // Returns squared distance + // + // Known bugs: currently assumes Elements are triangles regardless of + // dimension. + template + IGL_INLINE Scalar squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & p, + const Scalar low_sqr_d, + const Scalar up_sqr_d, + int & i, + Eigen::PlainObjectBase & c) const; + // Default low_sqr_d + template + IGL_INLINE Scalar squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & p, + const Scalar up_sqr_d, + int & i, + Eigen::PlainObjectBase & c) const; + // All hits + template + IGL_INLINE bool intersect_ray( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & origin, + const RowVectorDIMS & dir, + std::vector & hits) const; + // First hit + template + IGL_INLINE bool intersect_ray( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & origin, + const RowVectorDIMS & dir, + igl::Hit & hit) const; +//private: + template + IGL_INLINE bool intersect_ray( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & origin, + const RowVectorDIMS & dir, + const Scalar min_t, + igl::Hit & hit) const; + + +public: + // Compute the squared distance from all query points in P to the + // _closest_ points on the primitives stored in the AABB hierarchy for + // the mesh (V,Ele). + // + // Inputs: + // V #V by dim list of vertex positions + // Ele #Ele by dim list of simplex indices + // P #P by dim list of query points + // Outputs: + // sqrD #P list of squared distances + // I #P list of indices into Ele of closest primitives + // C #P by dim list of closest points + template < + typename DerivedEle, + typename DerivedP, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> + IGL_INLINE void squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const Eigen::MatrixBase & P, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C) const; + + // Compute the squared distance from all query points in P already stored + // in its own AABB hierarchy to the _closest_ points on the primitives + // stored in the AABB hierarchy for the mesh (V,Ele). + // + // Inputs: + // V #V by dim list of vertex positions + // Ele #Ele by dim list of simplex indices + // other AABB hierarchy of another set of primitives (must be points) + // other_V #other_V by dim list of query points + // other_Ele #other_Ele by ss list of simplex indices into other_V + // (must be simple list of points: ss == 1) + // Outputs: + // sqrD #P list of squared distances + // I #P list of indices into Ele of closest primitives + // C #P by dim list of closest points + template < + typename DerivedEle, + typename Derivedother_V, + typename Derivedother_Ele, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> + IGL_INLINE void squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const AABB & other, + const Eigen::MatrixBase & other_V, + const Eigen::MatrixBase & other_Ele, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C) const; +private: + template < + typename DerivedEle, + typename Derivedother_V, + typename Derivedother_Ele, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> + IGL_INLINE Scalar squared_distance_helper( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const AABB * other, + const Eigen::MatrixBase & other_V, + const Eigen::MatrixBase& other_Ele, + const Scalar up_sqr_d, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C) const; + // Compute the squared distance to the primitive in this node: assumes + // that this is indeed a leaf node. + // + // Inputs: + // V #V by dim list of vertex positions + // Ele #Ele by dim list of simplex indices + // p dim-long query point + // sqr_d current minimum distance for this query, see output + // i current index into Ele of closest point, see output + // c dim-long current closest point, see output + // Outputs: + // sqr_d minimum of initial value and squared distance to this + // primitive + // i possibly updated index into Ele of closest point + // c dim-long possibly updated closest point + template + IGL_INLINE void leaf_squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & p, + const Scalar low_sqr_d, + Scalar & sqr_d, + int & i, + Eigen::PlainObjectBase & c) const; + // Default low_sqr_d + template + IGL_INLINE void leaf_squared_distance( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const RowVectorDIMS & p, + Scalar & sqr_d, + int & i, + Eigen::PlainObjectBase & c) const; + // If new distance (sqr_d_candidate) is less than current distance + // (sqr_d), then update this distance and its associated values + // _in-place_: + // + // Inputs: + // p dim-long query point (only used in DEBUG mode) + // sqr_d candidate minimum distance for this query, see output + // i candidate index into Ele of closest point, see output + // c dim-long candidate closest point, see output + // sqr_d current minimum distance for this query, see output + // i current index into Ele of closest point, see output + // c dim-long current closest point, see output + // Outputs: + // sqr_d minimum of initial value and squared distance to this + // primitive + // i possibly updated index into Ele of closest point + // c dim-long possibly updated closest point + IGL_INLINE void set_min( + const RowVectorDIMS & p, + const Scalar sqr_d_candidate, + const int i_candidate, + const RowVectorDIMS & c_candidate, + Scalar & sqr_d, + int & i, + Eigen::PlainObjectBase & c) const; +public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + }; +} + + +#ifndef IGL_STATIC_LIBRARY +# include "AABB.cpp" +#endif + +#endif diff --git a/src/igl/ARAPEnergyType.h b/src/igl/ARAPEnergyType.h new file mode 100644 index 0000000000..68be24f5d7 --- /dev/null +++ b/src/igl/ARAPEnergyType.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ARAPENERGYTYPE_H +#define IGL_ARAPENERGYTYPE_H +namespace igl +{ + // ARAP_ENERGY_TYPE_SPOKES "As-rigid-as-possible Surface Modeling" by [Sorkine and + // Alexa 2007], rotations defined at vertices affecting incident edges, + // default + // ARAP_ENERGY_TYPE_SPOKES-AND-RIMS Adapted version of "As-rigid-as-possible Surface + // Modeling" by [Sorkine and Alexa 2007] presented in section 4.2 of or + // "A simple geometric model for elastic deformation" by [Chao et al. + // 2010], rotations defined at vertices affecting incident edges and + // opposite edges + // ARAP_ENERGY_TYPE_ELEMENTS "A local-global approach to mesh parameterization" by + // [Liu et al. 2010] or "A simple geometric model for elastic + // deformation" by [Chao et al. 2010], rotations defined at elements + // (triangles or tets) + // ARAP_ENERGY_TYPE_DEFAULT Choose one automatically: spokes and rims + // for surfaces, elements for planar meshes and tets (not fully + // supported) + enum ARAPEnergyType + { + ARAP_ENERGY_TYPE_SPOKES = 0, + ARAP_ENERGY_TYPE_SPOKES_AND_RIMS = 1, + ARAP_ENERGY_TYPE_ELEMENTS = 2, + ARAP_ENERGY_TYPE_DEFAULT = 3, + NUM_ARAP_ENERGY_TYPES = 4 + }; +} +#endif diff --git a/src/igl/AtA_cached.cpp b/src/igl/AtA_cached.cpp new file mode 100644 index 0000000000..af7d0ad861 --- /dev/null +++ b/src/igl/AtA_cached.cpp @@ -0,0 +1,130 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "AtA_cached.h" + +#include +#include +#include + +template +IGL_INLINE void igl::AtA_cached_precompute( + const Eigen::SparseMatrix& A, + igl::AtA_cached_data& data, + Eigen::SparseMatrix& AtA) +{ + // 1 Compute At (this could be avoided, but performance-wise it will not make a difference) + std::vector > Col_RowPtr; + std::vector > Col_IndexPtr; + + Col_RowPtr.resize(A.cols()); + Col_IndexPtr.resize(A.cols()); + + for (unsigned k=0; k= 0); + assert(row < A.rows()); + assert(row >= 0); + assert(value_index >= 0); + assert(value_index < A.nonZeros()); + + Col_RowPtr[col].push_back(row); + Col_IndexPtr[col].push_back(value_index); + } + } + + Eigen::SparseMatrix At = A.transpose(); + At.makeCompressed(); + AtA = At * A; + AtA.makeCompressed(); + + assert(AtA.isCompressed()); + + // If weights are not provided, use 1 + if (data.W.size() == 0) + data.W = Eigen::VectorXd::Ones(A.rows()); + assert(data.W.size() == A.rows()); + + data.I_outer.reserve(AtA.outerSize()); + data.I_row.reserve(2*AtA.nonZeros()); + data.I_col.reserve(2*AtA.nonZeros()); + data.I_w.reserve(2*AtA.nonZeros()); + + // 2 Construct the rules + for (unsigned k=0; k= 0); + assert(row < AtA.rows()); + assert(row >= 0); + assert(value_index >= 0); + assert(value_index < AtA.nonZeros()); + + data.I_outer.push_back(data.I_row.size()); + + // Find correspondences + unsigned i=0; + unsigned j=0; + while (i Col_RowPtr[col][j]) + ++j; + else + ++i; + + } + } + } + data.I_outer.push_back(data.I_row.size()); // makes it more efficient to iterate later on + + igl::AtA_cached(A,data,AtA); +} + +template +IGL_INLINE void igl::AtA_cached( + const Eigen::SparseMatrix& A, + const igl::AtA_cached_data& data, + Eigen::SparseMatrix& AtA) +{ + for (unsigned i=0; i(Eigen::SparseMatrix const&, igl::AtA_cached_data const&, Eigen::SparseMatrix&); +template void igl::AtA_cached_precompute(Eigen::SparseMatrix const&, igl::AtA_cached_data&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/AtA_cached.h b/src/igl/AtA_cached.h new file mode 100644 index 0000000000..7768254111 --- /dev/null +++ b/src/igl/AtA_cached.h @@ -0,0 +1,70 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ATA_CACHED_H +#define IGL_ATA_CACHED_H +#include "igl_inline.h" +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include +namespace igl +{ + struct AtA_cached_data + { + // Weights + Eigen::VectorXd W; + + // Flatten composition rules + std::vector I_row; + std::vector I_col; + std::vector I_w; + + // For each entry of AtA, points to the beginning + // of the composition rules + std::vector I_outer; + }; + + // Computes At * W * A, where A is sparse and W is diagonal. Divides the + // construction in two phases, one + // for fixing the sparsity pattern, and one to populate it with values. Compared to + // evaluating it directly, this version is slower for the first time (since it requires a + // precomputation), but faster to the subsequent evaluations. + // + // Input: + // A m x n sparse matrix + // data stores the precomputed sparsity pattern, data.W contains the optional diagonal weights (stored as a dense vector). If W is not provided, it is replaced by the identity. + // Outputs: + // AtA m by m matrix computed as AtA * W * A + // + // Example: + // AtA_data = igl::AtA_cached_data(); + // AtA_data.W = W; + // if (s.AtA.rows() == 0) + // igl::AtA_cached_precompute(s.A,s.AtA_data,s.AtA); + // else + // igl::AtA_cached(s.A,s.AtA_data,s.AtA); + template + IGL_INLINE void AtA_cached_precompute( + const Eigen::SparseMatrix& A, + AtA_cached_data& data, + Eigen::SparseMatrix& AtA + ); + + template + IGL_INLINE void AtA_cached( + const Eigen::SparseMatrix& A, + const AtA_cached_data& data, + Eigen::SparseMatrix& AtA + ); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "AtA_cached.cpp" +#endif + +#endif diff --git a/src/igl/C_STR.h b/src/igl/C_STR.h new file mode 100644 index 0000000000..9844b35a51 --- /dev/null +++ b/src/igl/C_STR.h @@ -0,0 +1,18 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_C_STR_H +#define IGL_C_STR_H +// http://stackoverflow.com/a/2433143/148668 +// Suppose you have a function: +// void func(const char * c); +// Then you can write: +// func(C_STR("foo"<<1<<"bar")); +#include +#include +#define C_STR(X) static_cast(std::ostringstream().flush() << X).str().c_str() +#endif diff --git a/src/igl/Camera.h b/src/igl/Camera.h new file mode 100644 index 0000000000..79ebf603e8 --- /dev/null +++ b/src/igl/Camera.h @@ -0,0 +1,359 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CAMERA_H +#define IGL_CAMERA_H + +// you're idiot, M$! +#if defined(_WIN32) +#undef far +#undef near +#endif + +#include +#include +#include + +#define IGL_CAMERA_MIN_ANGLE 5.0 +namespace igl +{ + + // A simple camera class. The camera stores projection parameters (field of + // view angle, aspect ratio, near and far clips) as well as a rigid + // transformation *of the camera as if it were also a scene object*. Thus, the + // **inverse** of this rigid transformation is the modelview transformation. + class Camera + { + public: + // On windows you might need: -fno-delayed-template-parsing + //static constexpr double IGL_CAMERA_MIN_ANGLE = 5.; + // m_angle Field of view angle in degrees {45} + // m_aspect Aspect ratio {1} + // m_near near clipping plane {1e-2} + // m_far far clipping plane {100} + // m_at_dist distance of looking at point {1} + // m_orthographic whether to use othrographic projection {false} + // m_rotation_conj Conjugate of rotation part of rigid transformation of + // camera {identity}. Note: we purposefully store the conjugate because + // this is what TW_TYPE_QUAT4D is expecting. + // m_translation Translation part of rigid transformation of camera + // {(0,0,1)} + double m_angle, m_aspect, m_near, m_far, m_at_dist; + bool m_orthographic; + Eigen::Quaterniond m_rotation_conj; + Eigen::Vector3d m_translation; + public: + inline Camera(); + inline virtual ~Camera(){} + // Return projection matrix that takes relative camera coordinates and + // transforms it to viewport coordinates + // + // Note: + // + // if(m_angle > 0) + // { + // gluPerspective(m_angle,m_aspect,m_near,m_at_dist+m_far); + // }else + // { + // gluOrtho(-0.5*aspect,0.5*aspect,-0.5,0.5,m_at_dist+m_near,m_far); + // } + // + // Is equivalent to + // + // glMultMatrixd(projection().data()); + // + inline Eigen::Matrix4d projection() const; + // Return an Affine transformation (rigid actually) that + // takes relative coordinates and tramsforms them into world 3d + // coordinates: moves the camera into the scene. + inline Eigen::Affine3d affine() const; + // Return an Affine transformation (rigid actually) that puts the takes a + // world 3d coordinate and transforms it into the relative camera + // coordinates: moves the scene in front of the camera. + // + // Note: + // + // gluLookAt( + // eye()(0), eye()(1), eye()(2), + // at()(0), at()(1), at()(2), + // up()(0), up()(1), up()(2)); + // + // Is equivalent to + // + // glMultMatrixd(camera.inverse().matrix().data()); + // + // See also: affine, eye, at, up + inline Eigen::Affine3d inverse() const; + // Returns world coordinates position of center or "eye" of camera. + inline Eigen::Vector3d eye() const; + // Returns world coordinate position of a point "eye" is looking at. + inline Eigen::Vector3d at() const; + // Returns world coordinate unit vector of "up" vector + inline Eigen::Vector3d up() const; + // Return top right corner of unit plane in relative coordinates, that is + // (w/2,h/2,1) + inline Eigen::Vector3d unit_plane() const; + // Move dv in the relative coordinate frame of the camera (move the FPS) + // + // Inputs: + // dv (x,y,z) displacement vector + // + inline void dolly(const Eigen::Vector3d & dv); + // "Scale zoom": Move `eye`, but leave `at` + // + // Input: + // s amount to scale distance to at + inline void push_away(const double s); + // Aka "Hitchcock", "Vertigo", "Spielberg" or "Trombone" zoom: + // simultaneously dolly while changing angle so that `at` not only stays + // put in relative coordinates but also projected coordinates. That is + // + // Inputs: + // da change in angle in degrees + inline void dolly_zoom(const double da); + // Turn around eye so that rotation is now q + // + // Inputs: + // q new rotation as quaternion + inline void turn_eye(const Eigen::Quaterniond & q); + // Orbit around at so that rotation is now q + // + // Inputs: + // q new rotation as quaternion + inline void orbit(const Eigen::Quaterniond & q); + // Rotate and translate so that camera is situated at "eye" looking at "at" + // with "up" pointing up. + // + // Inputs: + // eye (x,y,z) coordinates of eye position + // at (x,y,z) coordinates of at position + // up (x,y,z) coordinates of up vector + inline void look_at( + const Eigen::Vector3d & eye, + const Eigen::Vector3d & at, + const Eigen::Vector3d & up); + // Needed any time Eigen Structures are used as class members + // http://eigen.tuxfamily.org/dox-devel/group__TopicStructHavingEigenMembers.html + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + }; +} + +// Implementation +#include "PI.h" +#include "EPS.h" +#include +#include +#include + +inline igl::Camera::Camera(): + m_angle(45.0),m_aspect(1),m_near(1e-2),m_far(100),m_at_dist(1), + m_orthographic(false), + m_rotation_conj(1,0,0,0), + m_translation(0,0,1) +{ +} + +inline Eigen::Matrix4d igl::Camera::projection() const +{ + Eigen::Matrix4d P; + using namespace std; + const double far = m_at_dist + m_far; + const double near = m_near; + // http://stackoverflow.com/a/3738696/148668 + if(m_orthographic) + { + const double f = 0.5; + const double left = -f*m_aspect; + const double right = f*m_aspect; + const double bottom = -f; + const double top = f; + const double tx = (right+left)/(right-left); + const double ty = (top+bottom)/(top-bottom); + const double tz = (far+near)/(far-near); + const double z_fix = 0.5 /m_at_dist / tan(m_angle*0.5 * (igl::PI/180.) ); + P<< + z_fix*2./(right-left), 0, 0, -tx, + 0, z_fix*2./(top-bottom), 0, -ty, + 0, 0, -z_fix*2./(far-near), -tz, + 0, 0, 0, 1; + }else + { + const double yScale = tan(PI*0.5 - 0.5*m_angle*PI/180.); + // http://stackoverflow.com/a/14975139/148668 + const double xScale = yScale/m_aspect; + P<< + xScale, 0, 0, 0, + 0, yScale, 0, 0, + 0, 0, -(far+near)/(far-near), -1, + 0, 0, -2.*near*far/(far-near), 0; + P = P.transpose().eval(); + } + return P; +} + +inline Eigen::Affine3d igl::Camera::affine() const +{ + using namespace Eigen; + Affine3d t = Affine3d::Identity(); + t.rotate(m_rotation_conj.conjugate()); + t.translate(m_translation); + return t; +} + +inline Eigen::Affine3d igl::Camera::inverse() const +{ + using namespace Eigen; + Affine3d t = Affine3d::Identity(); + t.translate(-m_translation); + t.rotate(m_rotation_conj); + return t; +} + +inline Eigen::Vector3d igl::Camera::eye() const +{ + using namespace Eigen; + return affine() * Vector3d(0,0,0); +} + +inline Eigen::Vector3d igl::Camera::at() const +{ + using namespace Eigen; + return affine() * (Vector3d(0,0,-1)*m_at_dist); +} + +inline Eigen::Vector3d igl::Camera::up() const +{ + using namespace Eigen; + Affine3d t = Affine3d::Identity(); + t.rotate(m_rotation_conj.conjugate()); + return t * Vector3d(0,1,0); +} + +inline Eigen::Vector3d igl::Camera::unit_plane() const +{ + // Distance of center pixel to eye + const double d = 1.0; + const double a = m_aspect; + const double theta = m_angle*PI/180.; + const double w = + 2.*sqrt(-d*d/(a*a*pow(tan(0.5*theta),2.)-1.))*a*tan(0.5*theta); + const double h = w/a; + return Eigen::Vector3d(w*0.5,h*0.5,-d); +} + +inline void igl::Camera::dolly(const Eigen::Vector3d & dv) +{ + m_translation += dv; +} + +inline void igl::Camera::push_away(const double s) +{ + using namespace Eigen; +#ifndef NDEBUG + Vector3d old_at = at(); +#endif + const double old_at_dist = m_at_dist; + m_at_dist = old_at_dist * s; + dolly(Vector3d(0,0,1)*(m_at_dist - old_at_dist)); + assert((old_at-at()).squaredNorm() < DOUBLE_EPS); +} + +inline void igl::Camera::dolly_zoom(const double da) +{ + using namespace std; + using namespace Eigen; +#ifndef NDEBUG + Vector3d old_at = at(); +#endif + const double old_angle = m_angle; + if(old_angle + da < IGL_CAMERA_MIN_ANGLE) + { + m_orthographic = true; + }else if(old_angle + da > IGL_CAMERA_MIN_ANGLE) + { + m_orthographic = false; + } + if(!m_orthographic) + { + m_angle += da; + m_angle = min(89.,max(IGL_CAMERA_MIN_ANGLE,m_angle)); + // change in distance + const double s = + (2.*tan(old_angle/2./180.*igl::PI)) / + (2.*tan(m_angle/2./180.*igl::PI)) ; + const double old_at_dist = m_at_dist; + m_at_dist = old_at_dist * s; + dolly(Vector3d(0,0,1)*(m_at_dist - old_at_dist)); + assert((old_at-at()).squaredNorm() < DOUBLE_EPS); + } +} + +inline void igl::Camera::turn_eye(const Eigen::Quaterniond & q) +{ + using namespace Eigen; + Vector3d old_eye = eye(); + // eye should be fixed + // + // eye_1 = R_1 * t_1 = eye_0 + // t_1 = R_1' * eye_0 + m_rotation_conj = q.conjugate(); + m_translation = m_rotation_conj * old_eye; + assert((old_eye - eye()).squaredNorm() < DOUBLE_EPS); +} + +inline void igl::Camera::orbit(const Eigen::Quaterniond & q) +{ + using namespace Eigen; + Vector3d old_at = at(); + // at should be fixed + // + // at_1 = R_1 * t_1 - R_1 * z = at_0 + // t_1 = R_1' * (at_0 + R_1 * z) + m_rotation_conj = q.conjugate(); + m_translation = + m_rotation_conj * + (old_at + + m_rotation_conj.conjugate() * Vector3d(0,0,1) * m_at_dist); + assert((old_at - at()).squaredNorm() < DOUBLE_EPS); +} + +inline void igl::Camera::look_at( + const Eigen::Vector3d & eye, + const Eigen::Vector3d & at, + const Eigen::Vector3d & up) +{ + using namespace Eigen; + using namespace std; + // http://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml + // Normalize vector from at to eye + Vector3d F = eye-at; + m_at_dist = F.norm(); + F.normalize(); + // Project up onto plane orthogonal to F and normalize + assert(up.cross(F).norm() > DOUBLE_EPS && "(eye-at) x up ≈ 0"); + const Vector3d proj_up = (up-(up.dot(F))*F).normalized(); + Quaterniond a,b; + a.setFromTwoVectors(Vector3d(0,0,-1),-F); + b.setFromTwoVectors(a*Vector3d(0,1,0),proj_up); + m_rotation_conj = (b*a).conjugate(); + m_translation = m_rotation_conj * eye; + //cout<<"m_at_dist: "<eye().transpose()<at().transpose()<eye()-this->at()).normalized().transpose()<eye(): "<<(eye-this->eye()).squaredNorm()<eye()).squaredNorm() < DOUBLE_EPS); + //assert((F-(this->eye()-this->at()).normalized()).squaredNorm() < + // DOUBLE_EPS); + assert( (at-this->at()).squaredNorm() < DOUBLE_EPS); + //assert( (proj_up-this->up()).squaredNorm() < DOUBLE_EPS); +} + +#endif diff --git a/src/igl/EPS.cpp b/src/igl/EPS.cpp new file mode 100644 index 0000000000..fc592cc2a6 --- /dev/null +++ b/src/igl/EPS.cpp @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "EPS.h" + +template <> IGL_INLINE float igl::EPS() +{ + return igl::FLOAT_EPS; +} +template <> IGL_INLINE double igl::EPS() +{ + return igl::DOUBLE_EPS; +} + +template <> IGL_INLINE float igl::EPS_SQ() +{ + return igl::FLOAT_EPS_SQ; +} +template <> IGL_INLINE double igl::EPS_SQ() +{ + return igl::DOUBLE_EPS_SQ; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/EPS.h b/src/igl/EPS.h new file mode 100644 index 0000000000..17f3b8c25f --- /dev/null +++ b/src/igl/EPS.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EPS_H +#define IGL_EPS_H +#include "igl_inline.h" +namespace igl +{ + // Define a standard value for double epsilon + const double DOUBLE_EPS = 1.0e-14; + const double DOUBLE_EPS_SQ = 1.0e-28; + const float FLOAT_EPS = 1.0e-7; + const float FLOAT_EPS_SQ = 1.0e-14; + // Function returning EPS for corresponding type + template IGL_INLINE S_type EPS(); + template IGL_INLINE S_type EPS_SQ(); + // Template specializations for float and double + template <> IGL_INLINE float EPS(); + template <> IGL_INLINE double EPS(); + template <> IGL_INLINE float EPS_SQ(); + template <> IGL_INLINE double EPS_SQ(); +} + +#ifndef IGL_STATIC_LIBRARY +# include "EPS.cpp" +#endif + +#endif diff --git a/src/igl/HalfEdgeIterator.cpp b/src/igl/HalfEdgeIterator.cpp new file mode 100644 index 0000000000..7c3b9a8863 --- /dev/null +++ b/src/igl/HalfEdgeIterator.cpp @@ -0,0 +1,158 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "HalfEdgeIterator.h" + +template +IGL_INLINE igl::HalfEdgeIterator::HalfEdgeIterator( + const Eigen::PlainObjectBase& _F, + const Eigen::PlainObjectBase& _FF, + const Eigen::PlainObjectBase& _FFi, + int _fi, + int _ei, + bool _reverse +) +: fi(_fi), ei(_ei), reverse(_reverse), F(_F), FF(_FF), FFi(_FFi) +{} + +template +IGL_INLINE void igl::HalfEdgeIterator::flipF() +{ + if (isBorder()) + return; + + int fin = (FF)(fi,ei); + int ein = (FFi)(fi,ei); + + fi = fin; + ei = ein; + reverse = !reverse; +} + + +// Change Edge +template +IGL_INLINE void igl::HalfEdgeIterator::flipE() +{ + if (!reverse) + ei = (ei+2)%3; // ei-1 + else + ei = (ei+1)%3; + + reverse = !reverse; +} + +// Change Vertex +template +IGL_INLINE void igl::HalfEdgeIterator::flipV() +{ + reverse = !reverse; +} + +template +IGL_INLINE bool igl::HalfEdgeIterator::isBorder() +{ + return (FF)(fi,ei) == -1; +} + +/*! + * Returns the next edge skipping the border + * _________ + * /\ c | b /\ + * / \ | / \ + * / d \ | / a \ + * /______\|/______\ + * v + * In this example, if a and d are of-border and the pos is iterating counterclockwise, this method iterate through the faces incident on vertex v, + * producing the sequence a, b, c, d, a, b, c, ... + */ +template +IGL_INLINE bool igl::HalfEdgeIterator::NextFE() +{ + if ( isBorder() ) // we are on a border + { + do + { + flipF(); + flipE(); + } while (!isBorder()); + flipE(); + return false; + } + else + { + flipF(); + flipE(); + return true; + } +} + +// Get vertex index +template +IGL_INLINE int igl::HalfEdgeIterator::Vi() +{ + assert(fi >= 0); + assert(fi < F.rows()); + assert(ei >= 0); + assert(ei <= 2); + + if (!reverse) + return (F)(fi,ei); + else + return (F)(fi,(ei+1)%3); +} + +// Get face index +template +IGL_INLINE int igl::HalfEdgeIterator::Fi() +{ + return fi; +} + +// Get edge index +template +IGL_INLINE int igl::HalfEdgeIterator::Ei() +{ + return ei; +} + + +template +IGL_INLINE bool igl::HalfEdgeIterator::operator==(HalfEdgeIterator& p2) +{ + return + ( + (fi == p2.fi) && + (ei == p2.ei) && + (reverse == p2.reverse) && + (F == p2.F) && + (FF == p2.FF) && + (FFi == p2.FFi) + ); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template igl::HalfEdgeIterator ,Eigen::Matrix ,Eigen::Matrix >::HalfEdgeIterator(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, int, bool); +template igl::HalfEdgeIterator, Eigen::Matrix, Eigen::Matrix >::HalfEdgeIterator(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, int, bool); +template bool igl::HalfEdgeIterator,Eigen::Matrix,Eigen::Matrix >::NextFE(); +template int igl::HalfEdgeIterator,Eigen::Matrix,Eigen::Matrix >::Ei(); +template int igl::HalfEdgeIterator ,Eigen::Matrix,Eigen::Matrix >::Ei(); +template int igl::HalfEdgeIterator ,Eigen::Matrix ,Eigen::Matrix >::Ei(); +template int igl::HalfEdgeIterator ,Eigen::Matrix ,Eigen::Matrix >::Fi(); +template bool igl::HalfEdgeIterator ,Eigen::Matrix ,Eigen::Matrix >::NextFE(); +template int igl::HalfEdgeIterator,Eigen::Matrix,Eigen::Matrix >::Vi(); +template igl::HalfEdgeIterator,Eigen::Matrix,Eigen::Matrix >::HalfEdgeIterator(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, int, bool); +template int igl::HalfEdgeIterator,Eigen::Matrix,Eigen::Matrix >::Fi(); +template void igl::HalfEdgeIterator,Eigen::Matrix,Eigen::Matrix >::flipE(); +template void igl::HalfEdgeIterator,Eigen::Matrix,Eigen::Matrix >::flipF(); +template void igl::HalfEdgeIterator,Eigen::Matrix,Eigen::Matrix >::flipV(); +template bool igl::HalfEdgeIterator,Eigen::Matrix,Eigen::Matrix >::operator==(igl::HalfEdgeIterator,Eigen::Matrix,Eigen::Matrix >&); +template int igl::HalfEdgeIterator, Eigen::Matrix, Eigen::Matrix >::Fi(); +template bool igl::HalfEdgeIterator, Eigen::Matrix, Eigen::Matrix >::NextFE(); +#endif diff --git a/src/igl/HalfEdgeIterator.h b/src/igl/HalfEdgeIterator.h new file mode 100644 index 0000000000..3c429e1d17 --- /dev/null +++ b/src/igl/HalfEdgeIterator.h @@ -0,0 +1,114 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_HALFEDGEITERATOR_H +#define IGL_HALFEDGEITERATOR_H + +#include + +#include +#include + +// This file violates many of the libigl style guidelines. + +namespace igl +{ + // HalfEdgeIterator - Fake halfedge for fast and easy navigation + // on triangle meshes with vertex_triangle_adjacency and + // triangle_triangle adjacency + // + // Note: this is different to classical Half Edge data structure. + // Instead, it follows cell-tuple in [Brisson, 1989] + // "Representing geometric structures in d dimensions: topology and order." + // This class can achieve local navigation similar to half edge in OpenMesh + // But the logic behind each atom operation is different. + // So this should be more properly called TriangleTupleIterator. + // + // Each tuple contains information on (face, edge, vertex) + // and encoded by (face, edge \in {0,1,2}, bool reverse) + // + // Inputs: + // F #F by 3 list of "faces" + // FF #F by 3 list of triangle-triangle adjacency. + // FFi #F by 3 list of FF inverse. For FF and FFi, refer to + // "triangle_triangle_adjacency.h" + // Usages: + // FlipF/E/V changes solely one actual face/edge/vertex resp. + // NextFE iterates through one-ring of a vertex robustly. + // + template < + typename DerivedF, + typename DerivedFF, + typename DerivedFFi> + class HalfEdgeIterator + { + public: + // Init the HalfEdgeIterator by specifying Face,Edge Index and Orientation + IGL_INLINE HalfEdgeIterator( + const Eigen::PlainObjectBase& _F, + const Eigen::PlainObjectBase& _FF, + const Eigen::PlainObjectBase& _FFi, + int _fi, + int _ei, + bool _reverse = false + ); + + // Change Face + IGL_INLINE void flipF(); + + // Change Edge + IGL_INLINE void flipE(); + + // Change Vertex + IGL_INLINE void flipV(); + + IGL_INLINE bool isBorder(); + + /*! + * Returns the next edge skipping the border + * _________ + * /\ c | b /\ + * / \ | / \ + * / d \ | / a \ + * /______\|/______\ + * v + * In this example, if a and d are of-border and the pos is iterating + counterclockwise, this method iterate through the faces incident on vertex + v, + * producing the sequence a, b, c, d, a, b, c, ... + */ + IGL_INLINE bool NextFE(); + + // Get vertex index + IGL_INLINE int Vi(); + + // Get face index + IGL_INLINE int Fi(); + + // Get edge index + IGL_INLINE int Ei(); + + IGL_INLINE bool operator==(HalfEdgeIterator& p2); + + private: + int fi; + int ei; + bool reverse; + + // All the same type? This is likely to break. + const Eigen::PlainObjectBase & F; + const Eigen::PlainObjectBase & FF; + const Eigen::PlainObjectBase & FFi; + }; + +} + +#ifndef IGL_STATIC_LIBRARY +# include "HalfEdgeIterator.cpp" +#endif + +#endif diff --git a/src/igl/Hit.h b/src/igl/Hit.h new file mode 100644 index 0000000000..8bffcc8431 --- /dev/null +++ b/src/igl/Hit.h @@ -0,0 +1,25 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// 2014 Christian Schüller +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_HIT_H +#define IGL_HIT_H + +namespace igl +{ + // Reimplementation of the embree::Hit struct from embree1.0 + // + // TODO: template on floating point type + struct Hit + { + int id; // primitive id + int gid; // geometry id + float u,v; // barycentric coordinates + float t; // distance = direction*t to intersection + }; +} +#endif diff --git a/src/igl/IO b/src/igl/IO new file mode 100644 index 0000000000..f054d8c99a --- /dev/null +++ b/src/igl/IO @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IO +#define IGL_IO +// Input and output functions +#include "read_triangle_mesh.h" +#include "readDMAT.h" +#include "readMESH.h" +#include "readNODE.h" +#include "readOBJ.h" +#include "readOFF.h" +#include "readTGF.h" +#include "readWRL.h" +#include "readCSV.h" +#include "file_contents_as_string.h" +#include "write_triangle_mesh.h" +#include "writeDMAT.h" +#include "writeMESH.h" +#include "writeOBJ.h" +#include "writeOFF.h" +#include "writeTGF.h" + +#endif diff --git a/src/igl/IndexComparison.h b/src/igl/IndexComparison.h new file mode 100644 index 0000000000..44515df513 --- /dev/null +++ b/src/igl/IndexComparison.h @@ -0,0 +1,117 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_INDEXCOMPARISON_H +#define IGL_INDEXCOMPARISON_H +#include +namespace igl{ + // Comparison struct used by sort + // http://bytes.com/topic/c/answers/132045-sort-get-index + + // For use with functions like std::sort + template struct IndexLessThan + { + IndexLessThan(const T arr) : arr(arr) {} + bool operator()(const size_t a, const size_t b) const + { + return arr[a] < arr[b]; + } + const T arr; + }; + + // For use with functions like std::unique + template struct IndexEquals + { + IndexEquals(const T arr) : arr(arr) {} + bool operator()(const size_t a, const size_t b) const + { + return arr[a] == arr[b]; + } + const T arr; + }; + + // For use with functions like std::sort + template struct IndexVectorLessThan + { + IndexVectorLessThan(const T & vec) : vec ( vec) {} + bool operator()(const size_t a, const size_t b) const + { + return vec(a) < vec(b); + } + const T & vec; + }; + + // For use with functions like std::sort + template struct IndexDimLessThan + { + IndexDimLessThan(const T & mat,const int & dim, const int & j) : + mat(mat), + dim(dim), + j(j) + {} + bool operator()(const size_t a, const size_t b) const + { + if(dim == 1) + { + return mat(a,j) < mat(b,j); + }else + { + return mat(j,a) < mat(j,b); + } + } + const T & mat; + const int & dim; + const int & j; + }; + + // For use with functions like std::sort + template struct IndexRowLessThan + { + IndexRowLessThan(const T & mat) : mat ( mat) {} + bool operator()(const size_t a, const size_t b) const + { + const int cols = mat.cols(); + // Lexicographical order + for(int j = 0;j mat(b,j)) + { + return false; + } else if(mat(a,j) < mat(b,j)) + { + return true; + } + } + // equality is false + return false; + } + const T & mat; + }; + + // For use with functions like std::sort + template struct IndexRowEquals + { + IndexRowEquals(const T & mat) : mat ( mat) {} + bool operator()(const size_t a, const size_t b) const + { + const int cols = mat.cols(); + // Lexicographical order + for(int j = 0;j +// This function is not intended to be a permanent function of libigl. Rather +// it is a "drop-in" workaround for documented bug in Eigen: +// http://eigen.tuxfamily.org/bz/show_bug.cgi?id=1383 +// +// Replace: +// +// Eigen::VectorXi::LinSpaced(size,low,high); +// +// With: +// +// igl::LinSpaced(size,low,high); +// +// Specifcally, this version will _always_ return an empty vector if size==0, +// regardless of the values for low and high. If size != 0, then this simply +// returns the result of Eigen::Derived::LinSpaced. +// +// Until this bug is fixed, we should also avoid calls to the member function +// `.setLinSpaced`. This means replacing: +// +// a.setLinSpaced(size,low,high); +// +// with +// +// a = igl::LinSpaced(size,low,high); +// +namespace igl +{ + template + //inline typename Eigen::DenseBase< Derived >::RandomAccessLinSpacedReturnType + inline Derived LinSpaced( + typename Derived::Index size, + const typename Derived::Scalar & low, + const typename Derived::Scalar & high); +} + +// Implementation + +template +//inline typename Eigen::DenseBase< Derived >::RandomAccessLinSpacedReturnType +inline Derived +igl::LinSpaced( + typename Derived::Index size, + const typename Derived::Scalar & low, + const typename Derived::Scalar & high) +{ + if(size == 0) + { + // Force empty vector with correct "RandomAccessLinSpacedReturnType" type. + return Derived::LinSpaced(0,0,1); + }else if(high < low) + { + return low-Derived::LinSpaced(size,low-low,low-high).array(); + }else{ + return Derived::LinSpaced(size,low,high); + } +} + +#endif diff --git a/src/igl/MeshBooleanType.h b/src/igl/MeshBooleanType.h new file mode 100644 index 0000000000..2eb2936242 --- /dev/null +++ b/src/igl/MeshBooleanType.h @@ -0,0 +1,23 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MESH_BOOLEAN_TYPE_H +#define IGL_MESH_BOOLEAN_TYPE_H +namespace igl +{ + enum MeshBooleanType + { + MESH_BOOLEAN_TYPE_UNION = 0, + MESH_BOOLEAN_TYPE_INTERSECT = 1, + MESH_BOOLEAN_TYPE_MINUS = 2, + MESH_BOOLEAN_TYPE_XOR = 3, + MESH_BOOLEAN_TYPE_RESOLVE = 4, + NUM_MESH_BOOLEAN_TYPES = 5 + }; +}; + +#endif diff --git a/src/igl/NormalType.h b/src/igl/NormalType.h new file mode 100644 index 0000000000..b74b57b6a8 --- /dev/null +++ b/src/igl/NormalType.h @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_NORMALTYPE_H +#define IGL_NORMALTYPE_H + +namespace igl +{ + // PER_VERTEX_NORMALS Normals computed per vertex based on incident faces + // PER_FACE_NORMALS Normals computed per face + // PER_CORNER_NORMALS Normals computed per corner (aka wedge) based on + // incident faces without sharp edge + enum NormalType + { + PER_VERTEX_NORMALS, + PER_FACE_NORMALS, + PER_CORNER_NORMALS + }; +# define NUM_NORMAL_TYPE 3 +} + +#endif + diff --git a/src/igl/ONE.h b/src/igl/ONE.h new file mode 100644 index 0000000000..93c509d4e2 --- /dev/null +++ b/src/igl/ONE.h @@ -0,0 +1,22 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ONE_H +#define IGL_ONE_H +namespace igl +{ + // Often one needs a reference to a dummy variable containing one as its + // value, for example when using AntTweakBar's + // TwSetParam( "3D View", "opened", TW_PARAM_INT32, 1, &INT_ONE); + const char CHAR_ONE = 1; + const int INT_ONE = 1; + const unsigned int UNSIGNED_INT_ONE = 1; + const double DOUBLE_ONE = 1; + const float FLOAT_ONE = 1; +} +#endif + diff --git a/src/igl/PI.h b/src/igl/PI.h new file mode 100644 index 0000000000..f1ef4359d2 --- /dev/null +++ b/src/igl/PI.h @@ -0,0 +1,19 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PI_H +#define IGL_PI_H +namespace igl +{ + // Use standard mathematical constants' M_PI if available +#ifdef M_PI + const double PI = M_PI; +#else + const double PI = 3.1415926535897932384626433832795; +#endif +} +#endif diff --git a/src/igl/REDRUM.h b/src/igl/REDRUM.h new file mode 100644 index 0000000000..79ab72ffd1 --- /dev/null +++ b/src/igl/REDRUM.h @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_REDRUM_H +#define IGL_REDRUM_H + +// Q: These should probably be inside the igl namespace. What's the correct +// way to do that? +// A: I guess the right way is to not use a macro but a proper function with +// streams as input and output. + +// ANSI color codes for formatting iostream style output + +#ifdef IGL_REDRUM_NOOP + +// Bold Red, etc. +#define NORUM(X) X +#define REDRUM(X) X +#define GREENRUM(X) X +#define YELLOWRUM(X) X +#define BLUERUM(X) X +#define MAGENTARUM(X) X +#define CYANRUM(X) X +// Regular Red, etc. +#define REDGIN(X) X +#define GREENGIN(X) X +#define YELLOWGIN(X) X +#define BLUEGIN(X) X +#define MAGENTAGIN(X) X +#define CYANGIN(X) X + +#else + +// Bold Red, etc. +#define NORUM(X) ""< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_STR_H +#define IGL_STR_H +// http://stackoverflow.com/a/2433143/148668 +#include +#include +// Suppose you have a function: +// void func(std::string c); +// Then you can write: +// func(STR("foo"<<1<<"bar")); +#define STR(X) static_cast(std::ostringstream().flush() << X).str() +#endif diff --git a/src/igl/Singular_Value_Decomposition_Givens_QR_Factorization_Kernel.hpp b/src/igl/Singular_Value_Decomposition_Givens_QR_Factorization_Kernel.hpp new file mode 100644 index 0000000000..2207285aa7 --- /dev/null +++ b/src/igl/Singular_Value_Decomposition_Givens_QR_Factorization_Kernel.hpp @@ -0,0 +1,128 @@ +//##################################################################### +// Copyright (c) 2010-2011, Eftychios Sifakis. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//##################################################################### + +//########################################################### +// Compute the Givens half-angle, construct the Givens quaternion and the rotation sine/cosine (for the full angle) +//########################################################### + +#ifdef _WIN32 + #undef max + #undef min +#endif + +ENABLE_SCALAR_IMPLEMENTATION(Ssh.f=SANPIVOT.f*SANPIVOT.f;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_mul_ps(VANPIVOT,VANPIVOT);) ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_mul_ps(VANPIVOT,VANPIVOT);) +ENABLE_SCALAR_IMPLEMENTATION(Ssh.ui=(Ssh.f>=Ssmall_number.f)?0xffffffff:0;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_cmpge_ps(Vsh,Vsmall_number);) ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_cmp_ps(Vsh,Vsmall_number, _CMP_GE_OS);) //ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_cmpge_ps(Vsh,Vsmall_number);) +ENABLE_SCALAR_IMPLEMENTATION(Ssh.ui=Ssh.ui&SANPIVOT.ui;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_and_ps(Vsh,VANPIVOT);) ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_and_ps(Vsh,VANPIVOT);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp5.f=0.;) ENABLE_SSE_IMPLEMENTATION(Vtmp5=_mm_xor_ps(Vtmp5,Vtmp5);) ENABLE_AVX_IMPLEMENTATION(Vtmp5=_mm256_xor_ps(Vtmp5,Vtmp5);) +ENABLE_SCALAR_IMPLEMENTATION(Sch.f=Stmp5.f-SAPIVOT.f;) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_sub_ps(Vtmp5,VAPIVOT);) ENABLE_AVX_IMPLEMENTATION(Vch=_mm256_sub_ps(Vtmp5,VAPIVOT);) +ENABLE_SCALAR_IMPLEMENTATION(Sch.f=std::max(Sch.f,SAPIVOT.f);) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_max_ps(Vch,VAPIVOT);) ENABLE_AVX_IMPLEMENTATION(Vch=_mm256_max_ps(Vch,VAPIVOT);) +ENABLE_SCALAR_IMPLEMENTATION(Sch.f=std::max(Sch.f,Ssmall_number.f);) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_max_ps(Vch,Vsmall_number);) ENABLE_AVX_IMPLEMENTATION(Vch=_mm256_max_ps(Vch,Vsmall_number);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp5.ui=(SAPIVOT.f>=Stmp5.f)?0xffffffff:0;) ENABLE_SSE_IMPLEMENTATION(Vtmp5=_mm_cmpge_ps(VAPIVOT,Vtmp5);) ENABLE_AVX_IMPLEMENTATION(Vtmp5=_mm256_cmp_ps(VAPIVOT,Vtmp5, _CMP_GE_OS);) //ENABLE_AVX_IMPLEMENTATION(Vtmp5=_mm256_cmpge_ps(VAPIVOT,Vtmp5);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sch.f*Sch.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vch,Vch);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vch,Vch);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ssh.f*Ssh.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vsh,Vsh);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vsh,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Stmp1.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_add_ps(Vtmp1,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_add_ps(Vtmp1,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=rsqrt(Stmp2.f);) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_rsqrt_ps(Vtmp2);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_rsqrt_ps(Vtmp2);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp4.f=Stmp1.f*Sone_half.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp4=_mm_mul_ps(Vtmp1,Vone_half);) ENABLE_AVX_IMPLEMENTATION(Vtmp4=_mm256_mul_ps(Vtmp1,Vone_half);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp3.f=Stmp1.f*Stmp4.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp3=_mm_mul_ps(Vtmp1,Vtmp4);) ENABLE_AVX_IMPLEMENTATION(Vtmp3=_mm256_mul_ps(Vtmp1,Vtmp4);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp3.f=Stmp1.f*Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp3=_mm_mul_ps(Vtmp1,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(Vtmp3=_mm256_mul_ps(Vtmp1,Vtmp3);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp3.f=Stmp2.f*Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp3=_mm_mul_ps(Vtmp2,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(Vtmp3=_mm256_mul_ps(Vtmp2,Vtmp3);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Stmp1.f+Stmp4.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_add_ps(Vtmp1,Vtmp4);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_add_ps(Vtmp1,Vtmp4);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Stmp1.f-Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_sub_ps(Vtmp1,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_sub_ps(Vtmp1,Vtmp3);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Stmp1.f*Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vtmp1,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vtmp1,Vtmp2);) + +ENABLE_SCALAR_IMPLEMENTATION(Sch.f=Sch.f+Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_add_ps(Vch,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(Vch=_mm256_add_ps(Vch,Vtmp1);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.ui=~Stmp5.ui&Ssh.ui;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_andnot_ps(Vtmp5,Vsh);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=Vch;) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.ui=~Stmp5.ui&Sch.ui;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_andnot_ps(Vtmp5,Vch);) ENABLE_AVX_IMPLEMENTATION(Vch=_mm256_blendv_ps(Vsh,Vch,Vtmp5);) +ENABLE_SCALAR_IMPLEMENTATION(Sch.ui=Stmp5.ui&Sch.ui;) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_and_ps(Vtmp5,Vch);) ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_blendv_ps(Vtmp1,Vsh,Vtmp5);) +ENABLE_SCALAR_IMPLEMENTATION(Ssh.ui=Stmp5.ui&Ssh.ui;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_and_ps(Vtmp5,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Sch.ui=Sch.ui|Stmp1.ui;) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_or_ps(Vch,Vtmp1);) +ENABLE_SCALAR_IMPLEMENTATION(Ssh.ui=Ssh.ui|Stmp2.ui;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_or_ps(Vsh,Vtmp2);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sch.f*Sch.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vch,Vch);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vch,Vch);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ssh.f*Ssh.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vsh,Vsh);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vsh,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Stmp1.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_add_ps(Vtmp1,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_add_ps(Vtmp1,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=rsqrt(Stmp2.f);) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_rsqrt_ps(Vtmp2);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_rsqrt_ps(Vtmp2);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp4.f=Stmp1.f*Sone_half.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp4=_mm_mul_ps(Vtmp1,Vone_half);) ENABLE_AVX_IMPLEMENTATION(Vtmp4=_mm256_mul_ps(Vtmp1,Vone_half);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp3.f=Stmp1.f*Stmp4.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp3=_mm_mul_ps(Vtmp1,Vtmp4);) ENABLE_AVX_IMPLEMENTATION(Vtmp3=_mm256_mul_ps(Vtmp1,Vtmp4);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp3.f=Stmp1.f*Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp3=_mm_mul_ps(Vtmp1,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(Vtmp3=_mm256_mul_ps(Vtmp1,Vtmp3);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp3.f=Stmp2.f*Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp3=_mm_mul_ps(Vtmp2,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(Vtmp3=_mm256_mul_ps(Vtmp2,Vtmp3);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Stmp1.f+Stmp4.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_add_ps(Vtmp1,Vtmp4);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_add_ps(Vtmp1,Vtmp4);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Stmp1.f-Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_sub_ps(Vtmp1,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_sub_ps(Vtmp1,Vtmp3);) + +ENABLE_SCALAR_IMPLEMENTATION(Sch.f=Sch.f*Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_mul_ps(Vch,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(Vch=_mm256_mul_ps(Vch,Vtmp1);) +ENABLE_SCALAR_IMPLEMENTATION(Ssh.f=Ssh.f*Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_mul_ps(Vsh,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_mul_ps(Vsh,Vtmp1);) + +ENABLE_SCALAR_IMPLEMENTATION(Sc.f=Sch.f*Sch.f;) ENABLE_SSE_IMPLEMENTATION(Vc=_mm_mul_ps(Vch,Vch);) ENABLE_AVX_IMPLEMENTATION(Vc=_mm256_mul_ps(Vch,Vch);)ENABLE_SCALAR_IMPLEMENTATION(Ss.f=Ssh.f*Ssh.f;) ENABLE_SSE_IMPLEMENTATION(Vs=_mm_mul_ps(Vsh,Vsh);) ENABLE_AVX_IMPLEMENTATION(Vs=_mm256_mul_ps(Vsh,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Sc.f=Sc.f-Ss.f;) ENABLE_SSE_IMPLEMENTATION(Vc=_mm_sub_ps(Vc,Vs);) ENABLE_AVX_IMPLEMENTATION(Vc=_mm256_sub_ps(Vc,Vs);) +ENABLE_SCALAR_IMPLEMENTATION(Ss.f=Ssh.f*Sch.f;) ENABLE_SSE_IMPLEMENTATION(Vs=_mm_mul_ps(Vsh,Vch);) ENABLE_AVX_IMPLEMENTATION(Vs=_mm256_mul_ps(Vsh,Vch);) +ENABLE_SCALAR_IMPLEMENTATION(Ss.f=Ss.f+Ss.f;) ENABLE_SSE_IMPLEMENTATION(Vs=_mm_add_ps(Vs,Vs);) ENABLE_AVX_IMPLEMENTATION(Vs=_mm256_add_ps(Vs,Vs);) + +//########################################################### +// Rotate matrix A +//########################################################### + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Ss.f*SA11.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vs,VA11);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vs,VA11);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ss.f*SA21.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vs,VA21);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vs,VA21);) +ENABLE_SCALAR_IMPLEMENTATION(SA11.f=Sc.f*SA11.f;) ENABLE_SSE_IMPLEMENTATION(VA11=_mm_mul_ps(Vc,VA11);) ENABLE_AVX_IMPLEMENTATION(VA11=_mm256_mul_ps(Vc,VA11);) +ENABLE_SCALAR_IMPLEMENTATION(SA21.f=Sc.f*SA21.f;) ENABLE_SSE_IMPLEMENTATION(VA21=_mm_mul_ps(Vc,VA21);) ENABLE_AVX_IMPLEMENTATION(VA21=_mm256_mul_ps(Vc,VA21);) +ENABLE_SCALAR_IMPLEMENTATION(SA11.f=SA11.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(VA11=_mm_add_ps(VA11,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(VA11=_mm256_add_ps(VA11,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(SA21.f=SA21.f-Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(VA21=_mm_sub_ps(VA21,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(VA21=_mm256_sub_ps(VA21,Vtmp1);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Ss.f*SA12.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vs,VA12);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vs,VA12);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ss.f*SA22.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vs,VA22);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vs,VA22);) +ENABLE_SCALAR_IMPLEMENTATION(SA12.f=Sc.f*SA12.f;) ENABLE_SSE_IMPLEMENTATION(VA12=_mm_mul_ps(Vc,VA12);) ENABLE_AVX_IMPLEMENTATION(VA12=_mm256_mul_ps(Vc,VA12);) +ENABLE_SCALAR_IMPLEMENTATION(SA22.f=Sc.f*SA22.f;) ENABLE_SSE_IMPLEMENTATION(VA22=_mm_mul_ps(Vc,VA22);) ENABLE_AVX_IMPLEMENTATION(VA22=_mm256_mul_ps(Vc,VA22);) +ENABLE_SCALAR_IMPLEMENTATION(SA12.f=SA12.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(VA12=_mm_add_ps(VA12,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(VA12=_mm256_add_ps(VA12,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(SA22.f=SA22.f-Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(VA22=_mm_sub_ps(VA22,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(VA22=_mm256_sub_ps(VA22,Vtmp1);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Ss.f*SA13.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vs,VA13);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vs,VA13);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ss.f*SA23.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vs,VA23);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vs,VA23);) +ENABLE_SCALAR_IMPLEMENTATION(SA13.f=Sc.f*SA13.f;) ENABLE_SSE_IMPLEMENTATION(VA13=_mm_mul_ps(Vc,VA13);) ENABLE_AVX_IMPLEMENTATION(VA13=_mm256_mul_ps(Vc,VA13);) +ENABLE_SCALAR_IMPLEMENTATION(SA23.f=Sc.f*SA23.f;) ENABLE_SSE_IMPLEMENTATION(VA23=_mm_mul_ps(Vc,VA23);) ENABLE_AVX_IMPLEMENTATION(VA23=_mm256_mul_ps(Vc,VA23);) +ENABLE_SCALAR_IMPLEMENTATION(SA13.f=SA13.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(VA13=_mm_add_ps(VA13,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(VA13=_mm256_add_ps(VA13,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(SA23.f=SA23.f-Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(VA23=_mm_sub_ps(VA23,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(VA23=_mm256_sub_ps(VA23,Vtmp1);) + +//########################################################### +// Update matrix U +//########################################################### + +#ifdef COMPUTE_U_AS_MATRIX +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Ss.f*SU11.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vs,VU11);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vs,VU11);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ss.f*SU12.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vs,VU12);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vs,VU12);) +ENABLE_SCALAR_IMPLEMENTATION(SU11.f=Sc.f*SU11.f;) ENABLE_SSE_IMPLEMENTATION(VU11=_mm_mul_ps(Vc,VU11);) ENABLE_AVX_IMPLEMENTATION(VU11=_mm256_mul_ps(Vc,VU11);) +ENABLE_SCALAR_IMPLEMENTATION(SU12.f=Sc.f*SU12.f;) ENABLE_SSE_IMPLEMENTATION(VU12=_mm_mul_ps(Vc,VU12);) ENABLE_AVX_IMPLEMENTATION(VU12=_mm256_mul_ps(Vc,VU12);) +ENABLE_SCALAR_IMPLEMENTATION(SU11.f=SU11.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(VU11=_mm_add_ps(VU11,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(VU11=_mm256_add_ps(VU11,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(SU12.f=SU12.f-Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(VU12=_mm_sub_ps(VU12,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(VU12=_mm256_sub_ps(VU12,Vtmp1);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Ss.f*SU21.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vs,VU21);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vs,VU21);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ss.f*SU22.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vs,VU22);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vs,VU22);) +ENABLE_SCALAR_IMPLEMENTATION(SU21.f=Sc.f*SU21.f;) ENABLE_SSE_IMPLEMENTATION(VU21=_mm_mul_ps(Vc,VU21);) ENABLE_AVX_IMPLEMENTATION(VU21=_mm256_mul_ps(Vc,VU21);) +ENABLE_SCALAR_IMPLEMENTATION(SU22.f=Sc.f*SU22.f;) ENABLE_SSE_IMPLEMENTATION(VU22=_mm_mul_ps(Vc,VU22);) ENABLE_AVX_IMPLEMENTATION(VU22=_mm256_mul_ps(Vc,VU22);) +ENABLE_SCALAR_IMPLEMENTATION(SU21.f=SU21.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(VU21=_mm_add_ps(VU21,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(VU21=_mm256_add_ps(VU21,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(SU22.f=SU22.f-Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(VU22=_mm_sub_ps(VU22,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(VU22=_mm256_sub_ps(VU22,Vtmp1);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Ss.f*SU31.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vs,VU31);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vs,VU31);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ss.f*SU32.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vs,VU32);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vs,VU32);) +ENABLE_SCALAR_IMPLEMENTATION(SU31.f=Sc.f*SU31.f;) ENABLE_SSE_IMPLEMENTATION(VU31=_mm_mul_ps(Vc,VU31);) ENABLE_AVX_IMPLEMENTATION(VU31=_mm256_mul_ps(Vc,VU31);) +ENABLE_SCALAR_IMPLEMENTATION(SU32.f=Sc.f*SU32.f;) ENABLE_SSE_IMPLEMENTATION(VU32=_mm_mul_ps(Vc,VU32);) ENABLE_AVX_IMPLEMENTATION(VU32=_mm256_mul_ps(Vc,VU32);) +ENABLE_SCALAR_IMPLEMENTATION(SU31.f=SU31.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(VU31=_mm_add_ps(VU31,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(VU31=_mm256_add_ps(VU31,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(SU32.f=SU32.f-Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(VU32=_mm_sub_ps(VU32,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(VU32=_mm256_sub_ps(VU32,Vtmp1);) +#endif diff --git a/src/igl/Singular_Value_Decomposition_Jacobi_Conjugation_Kernel.hpp b/src/igl/Singular_Value_Decomposition_Jacobi_Conjugation_Kernel.hpp new file mode 100644 index 0000000000..70b4371317 --- /dev/null +++ b/src/igl/Singular_Value_Decomposition_Jacobi_Conjugation_Kernel.hpp @@ -0,0 +1,118 @@ +//##################################################################### +// Copyright (c) 2010-2011, Eftychios Sifakis. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//##################################################################### + +//########################################################### +// Compute the Givens angle (and half-angle) +//########################################################### + +ENABLE_SCALAR_IMPLEMENTATION(Ssh.f=SS21.f*Sone_half.f;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_mul_ps(VS21,Vone_half);) ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_mul_ps(VS21,Vone_half);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp5.f=SS11.f-SS22.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp5=_mm_sub_ps(VS11,VS22);) ENABLE_AVX_IMPLEMENTATION(Vtmp5=_mm256_sub_ps(VS11,VS22);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ssh.f*Ssh.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vsh,Vsh);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vsh,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.ui=(Stmp2.f>=Stiny_number.f)?0xffffffff:0;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_cmpge_ps(Vtmp2,Vtiny_number);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_cmp_ps(Vtmp2,Vtiny_number, _CMP_GE_OS);) //ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_cmpge_ps(Vtmp2,Vtiny_number);) +ENABLE_SCALAR_IMPLEMENTATION(Ssh.ui=Stmp1.ui&Ssh.ui;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_and_ps(Vtmp1,Vsh);) ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_and_ps(Vtmp1,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Sch.ui=Stmp1.ui&Stmp5.ui;) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_and_ps(Vtmp1,Vtmp5);) ENABLE_AVX_IMPLEMENTATION(Vch=_mm256_blendv_ps(Vone,Vtmp5,Vtmp1);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.ui=~Stmp1.ui&Sone.ui;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_andnot_ps(Vtmp1,Vone);) +ENABLE_SCALAR_IMPLEMENTATION(Sch.ui=Sch.ui|Stmp2.ui;) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_or_ps(Vch,Vtmp2);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Ssh.f*Ssh.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vsh,Vsh);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vsh,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Sch.f*Sch.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vch,Vch);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vch,Vch);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp3.f=Stmp1.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp3=_mm_add_ps(Vtmp1,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(Vtmp3=_mm256_add_ps(Vtmp1,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp4.f=rsqrt(Stmp3.f);) ENABLE_SSE_IMPLEMENTATION(Vtmp4=_mm_rsqrt_ps(Vtmp3);) ENABLE_AVX_IMPLEMENTATION(Vtmp4=_mm256_rsqrt_ps(Vtmp3);) + +#ifdef USE_ACCURATE_RSQRT_IN_JACOBI_CONJUGATION +ENABLE_SCALAR_IMPLEMENTATION(Ss.f=Stmp4.f*Sone_half.f;) ENABLE_SSE_IMPLEMENTATION(Vs=_mm_mul_ps(Vtmp4,Vone_half);) ENABLE_AVX_IMPLEMENTATION(Vs=_mm256_mul_ps(Vtmp4,Vone_half);) +ENABLE_SCALAR_IMPLEMENTATION(Sc.f=Stmp4.f*Ss.f;) ENABLE_SSE_IMPLEMENTATION(Vc=_mm_mul_ps(Vtmp4,Vs);) ENABLE_AVX_IMPLEMENTATION(Vc=_mm256_mul_ps(Vtmp4,Vs);) +ENABLE_SCALAR_IMPLEMENTATION(Sc.f=Stmp4.f*Sc.f;) ENABLE_SSE_IMPLEMENTATION(Vc=_mm_mul_ps(Vtmp4,Vc);) ENABLE_AVX_IMPLEMENTATION(Vc=_mm256_mul_ps(Vtmp4,Vc);) +ENABLE_SCALAR_IMPLEMENTATION(Sc.f=Stmp3.f*Sc.f;) ENABLE_SSE_IMPLEMENTATION(Vc=_mm_mul_ps(Vtmp3,Vc);) ENABLE_AVX_IMPLEMENTATION(Vc=_mm256_mul_ps(Vtmp3,Vc);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp4.f=Stmp4.f+Ss.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp4=_mm_add_ps(Vtmp4,Vs);) ENABLE_AVX_IMPLEMENTATION(Vtmp4=_mm256_add_ps(Vtmp4,Vs);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp4.f=Stmp4.f-Sc.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp4=_mm_sub_ps(Vtmp4,Vc);) ENABLE_AVX_IMPLEMENTATION(Vtmp4=_mm256_sub_ps(Vtmp4,Vc);) +#endif + +ENABLE_SCALAR_IMPLEMENTATION(Ssh.f=Stmp4.f*Ssh.f;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_mul_ps(Vtmp4,Vsh);) ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_mul_ps(Vtmp4,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Sch.f=Stmp4.f*Sch.f;) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_mul_ps(Vtmp4,Vch);) ENABLE_AVX_IMPLEMENTATION(Vch=_mm256_mul_ps(Vtmp4,Vch);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sfour_gamma_squared.f*Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vfour_gamma_squared,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vfour_gamma_squared,Vtmp1);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.ui=(Stmp2.f<=Stmp1.f)?0xffffffff:0;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_cmple_ps(Vtmp2,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_cmp_ps(Vtmp2,Vtmp1, _CMP_LE_OS);) //ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_cmple_ps(Vtmp2,Vtmp1);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.ui=Ssine_pi_over_eight.ui&Stmp1.ui;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_and_ps(Vsine_pi_over_eight,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_blendv_ps(Vsh,Vsine_pi_over_eight,Vtmp1);) +ENABLE_SCALAR_IMPLEMENTATION(Ssh.ui=~Stmp1.ui&Ssh.ui;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_andnot_ps(Vtmp1,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Ssh.ui=Ssh.ui|Stmp2.ui;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_or_ps(Vsh,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.ui=Scosine_pi_over_eight.ui&Stmp1.ui;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_and_ps(Vcosine_pi_over_eight,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(Vch=_mm256_blendv_ps(Vch,Vcosine_pi_over_eight,Vtmp1);) +ENABLE_SCALAR_IMPLEMENTATION(Sch.ui=~Stmp1.ui&Sch.ui;) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_andnot_ps(Vtmp1,Vch);) +ENABLE_SCALAR_IMPLEMENTATION(Sch.ui=Sch.ui|Stmp2.ui;) ENABLE_SSE_IMPLEMENTATION(Vch=_mm_or_ps(Vch,Vtmp2);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Ssh.f*Ssh.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vsh,Vsh);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vsh,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Sch.f*Sch.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vch,Vch);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vch,Vch);) +ENABLE_SCALAR_IMPLEMENTATION(Sc.f=Stmp2.f-Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(Vc=_mm_sub_ps(Vtmp2,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(Vc=_mm256_sub_ps(Vtmp2,Vtmp1);) +ENABLE_SCALAR_IMPLEMENTATION(Ss.f=Sch.f*Ssh.f;) ENABLE_SSE_IMPLEMENTATION(Vs=_mm_mul_ps(Vch,Vsh);) ENABLE_AVX_IMPLEMENTATION(Vs=_mm256_mul_ps(Vch,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Ss.f=Ss.f+Ss.f;) ENABLE_SSE_IMPLEMENTATION(Vs=_mm_add_ps(Vs,Vs);) ENABLE_AVX_IMPLEMENTATION(Vs=_mm256_add_ps(Vs,Vs);) + +//########################################################### +// Perform the actual Givens conjugation +//########################################################### + +#ifndef USE_ACCURATE_RSQRT_IN_JACOBI_CONJUGATION +ENABLE_SCALAR_IMPLEMENTATION(Stmp3.f=Stmp1.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp3=_mm_add_ps(Vtmp1,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(Vtmp3=_mm256_add_ps(Vtmp1,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(SS33.f=SS33.f*Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(VS33=_mm_mul_ps(VS33,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(VS33=_mm256_mul_ps(VS33,Vtmp3);) +ENABLE_SCALAR_IMPLEMENTATION(SS31.f=SS31.f*Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(VS31=_mm_mul_ps(VS31,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(VS31=_mm256_mul_ps(VS31,Vtmp3);) +ENABLE_SCALAR_IMPLEMENTATION(SS32.f=SS32.f*Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(VS32=_mm_mul_ps(VS32,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(VS32=_mm256_mul_ps(VS32,Vtmp3);) +ENABLE_SCALAR_IMPLEMENTATION(SS33.f=SS33.f*Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(VS33=_mm_mul_ps(VS33,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(VS33=_mm256_mul_ps(VS33,Vtmp3);) +#endif + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Ss.f*SS31.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vs,VS31);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vs,VS31);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ss.f*SS32.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vs,VS32);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vs,VS32);) +ENABLE_SCALAR_IMPLEMENTATION(SS31.f=Sc.f*SS31.f;) ENABLE_SSE_IMPLEMENTATION(VS31=_mm_mul_ps(Vc,VS31);) ENABLE_AVX_IMPLEMENTATION(VS31=_mm256_mul_ps(Vc,VS31);) +ENABLE_SCALAR_IMPLEMENTATION(SS32.f=Sc.f*SS32.f;) ENABLE_SSE_IMPLEMENTATION(VS32=_mm_mul_ps(Vc,VS32);) ENABLE_AVX_IMPLEMENTATION(VS32=_mm256_mul_ps(Vc,VS32);) +ENABLE_SCALAR_IMPLEMENTATION(SS31.f=Stmp2.f+SS31.f;) ENABLE_SSE_IMPLEMENTATION(VS31=_mm_add_ps(Vtmp2,VS31);) ENABLE_AVX_IMPLEMENTATION(VS31=_mm256_add_ps(Vtmp2,VS31);) +ENABLE_SCALAR_IMPLEMENTATION(SS32.f=SS32.f-Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(VS32=_mm_sub_ps(VS32,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(VS32=_mm256_sub_ps(VS32,Vtmp1);) + +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ss.f*Ss.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vs,Vs);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vs,Vs);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=SS22.f*Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(VS22,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(VS22,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp3.f=SS11.f*Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp3=_mm_mul_ps(VS11,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(Vtmp3=_mm256_mul_ps(VS11,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp4.f=Sc.f*Sc.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp4=_mm_mul_ps(Vc,Vc);) ENABLE_AVX_IMPLEMENTATION(Vtmp4=_mm256_mul_ps(Vc,Vc);) +ENABLE_SCALAR_IMPLEMENTATION(SS11.f=SS11.f*Stmp4.f;) ENABLE_SSE_IMPLEMENTATION(VS11=_mm_mul_ps(VS11,Vtmp4);) ENABLE_AVX_IMPLEMENTATION(VS11=_mm256_mul_ps(VS11,Vtmp4);) +ENABLE_SCALAR_IMPLEMENTATION(SS22.f=SS22.f*Stmp4.f;) ENABLE_SSE_IMPLEMENTATION(VS22=_mm_mul_ps(VS22,Vtmp4);) ENABLE_AVX_IMPLEMENTATION(VS22=_mm256_mul_ps(VS22,Vtmp4);) +ENABLE_SCALAR_IMPLEMENTATION(SS11.f=SS11.f+Stmp1.f;) ENABLE_SSE_IMPLEMENTATION(VS11=_mm_add_ps(VS11,Vtmp1);) ENABLE_AVX_IMPLEMENTATION(VS11=_mm256_add_ps(VS11,Vtmp1);) +ENABLE_SCALAR_IMPLEMENTATION(SS22.f=SS22.f+Stmp3.f;) ENABLE_SSE_IMPLEMENTATION(VS22=_mm_add_ps(VS22,Vtmp3);) ENABLE_AVX_IMPLEMENTATION(VS22=_mm256_add_ps(VS22,Vtmp3);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp4.f=Stmp4.f-Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp4=_mm_sub_ps(Vtmp4,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(Vtmp4=_mm256_sub_ps(Vtmp4,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=SS21.f+SS21.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_add_ps(VS21,VS21);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_add_ps(VS21,VS21);) +ENABLE_SCALAR_IMPLEMENTATION(SS21.f=SS21.f*Stmp4.f;) ENABLE_SSE_IMPLEMENTATION(VS21=_mm_mul_ps(VS21,Vtmp4);) ENABLE_AVX_IMPLEMENTATION(VS21=_mm256_mul_ps(VS21,Vtmp4);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp4.f=Sc.f*Ss.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp4=_mm_mul_ps(Vc,Vs);) ENABLE_AVX_IMPLEMENTATION(Vtmp4=_mm256_mul_ps(Vc,Vs);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Stmp2.f*Stmp4.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vtmp2,Vtmp4);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vtmp2,Vtmp4);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp5.f=Stmp5.f*Stmp4.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp5=_mm_mul_ps(Vtmp5,Vtmp4);) ENABLE_AVX_IMPLEMENTATION(Vtmp5=_mm256_mul_ps(Vtmp5,Vtmp4);) +ENABLE_SCALAR_IMPLEMENTATION(SS11.f=SS11.f+Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(VS11=_mm_add_ps(VS11,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(VS11=_mm256_add_ps(VS11,Vtmp2);) +ENABLE_SCALAR_IMPLEMENTATION(SS21.f=SS21.f-Stmp5.f;) ENABLE_SSE_IMPLEMENTATION(VS21=_mm_sub_ps(VS21,Vtmp5);) ENABLE_AVX_IMPLEMENTATION(VS21=_mm256_sub_ps(VS21,Vtmp5);) +ENABLE_SCALAR_IMPLEMENTATION(SS22.f=SS22.f-Stmp2.f;) ENABLE_SSE_IMPLEMENTATION(VS22=_mm_sub_ps(VS22,Vtmp2);) ENABLE_AVX_IMPLEMENTATION(VS22=_mm256_sub_ps(VS22,Vtmp2);) + +//########################################################### +// Compute the cumulative rotation, in quaternion form +//########################################################### + +ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Ssh.f*Sqvvx.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Vsh,Vqvvx);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Vsh,Vqvvx);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp2.f=Ssh.f*Sqvvy.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp2=_mm_mul_ps(Vsh,Vqvvy);) ENABLE_AVX_IMPLEMENTATION(Vtmp2=_mm256_mul_ps(Vsh,Vqvvy);) +ENABLE_SCALAR_IMPLEMENTATION(Stmp3.f=Ssh.f*Sqvvz.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp3=_mm_mul_ps(Vsh,Vqvvz);) ENABLE_AVX_IMPLEMENTATION(Vtmp3=_mm256_mul_ps(Vsh,Vqvvz);) +ENABLE_SCALAR_IMPLEMENTATION(Ssh.f=Ssh.f*Sqvs.f;) ENABLE_SSE_IMPLEMENTATION(Vsh=_mm_mul_ps(Vsh,Vqvs);) ENABLE_AVX_IMPLEMENTATION(Vsh=_mm256_mul_ps(Vsh,Vqvs);) + +ENABLE_SCALAR_IMPLEMENTATION(Sqvs.f=Sch.f*Sqvs.f;) ENABLE_SSE_IMPLEMENTATION(Vqvs=_mm_mul_ps(Vch,Vqvs);) ENABLE_AVX_IMPLEMENTATION(Vqvs=_mm256_mul_ps(Vch,Vqvs);) +ENABLE_SCALAR_IMPLEMENTATION(Sqvvx.f=Sch.f*Sqvvx.f;) ENABLE_SSE_IMPLEMENTATION(Vqvvx=_mm_mul_ps(Vch,Vqvvx);) ENABLE_AVX_IMPLEMENTATION(Vqvvx=_mm256_mul_ps(Vch,Vqvvx);) +ENABLE_SCALAR_IMPLEMENTATION(Sqvvy.f=Sch.f*Sqvvy.f;) ENABLE_SSE_IMPLEMENTATION(Vqvvy=_mm_mul_ps(Vch,Vqvvy);) ENABLE_AVX_IMPLEMENTATION(Vqvvy=_mm256_mul_ps(Vch,Vqvvy);) +ENABLE_SCALAR_IMPLEMENTATION(Sqvvz.f=Sch.f*Sqvvz.f;) ENABLE_SSE_IMPLEMENTATION(Vqvvz=_mm_mul_ps(Vch,Vqvvz);) ENABLE_AVX_IMPLEMENTATION(Vqvvz=_mm256_mul_ps(Vch,Vqvvz);) + +ENABLE_SCALAR_IMPLEMENTATION(SQVVZ.f=SQVVZ.f+Ssh.f;) ENABLE_SSE_IMPLEMENTATION(VQVVZ=_mm_add_ps(VQVVZ,Vsh);) ENABLE_AVX_IMPLEMENTATION(VQVVZ=_mm256_add_ps(VQVVZ,Vsh);) +ENABLE_SCALAR_IMPLEMENTATION(Sqvs.f=Sqvs.f-STMP3.f;) ENABLE_SSE_IMPLEMENTATION(Vqvs=_mm_sub_ps(Vqvs,VTMP3);) ENABLE_AVX_IMPLEMENTATION(Vqvs=_mm256_sub_ps(Vqvs,VTMP3);) +ENABLE_SCALAR_IMPLEMENTATION(SQVVX.f=SQVVX.f+STMP2.f;) ENABLE_SSE_IMPLEMENTATION(VQVVX=_mm_add_ps(VQVVX,VTMP2);) ENABLE_AVX_IMPLEMENTATION(VQVVX=_mm256_add_ps(VQVVX,VTMP2);) +ENABLE_SCALAR_IMPLEMENTATION(SQVVY.f=SQVVY.f-STMP1.f;) ENABLE_SSE_IMPLEMENTATION(VQVVY=_mm_sub_ps(VQVVY,VTMP1);) ENABLE_AVX_IMPLEMENTATION(VQVVY=_mm256_sub_ps(VQVVY,VTMP1);) diff --git a/src/igl/Singular_Value_Decomposition_Kernel_Declarations.hpp b/src/igl/Singular_Value_Decomposition_Kernel_Declarations.hpp new file mode 100644 index 0000000000..0c3b3ff878 --- /dev/null +++ b/src/igl/Singular_Value_Decomposition_Kernel_Declarations.hpp @@ -0,0 +1,137 @@ +//##################################################################### +// Copyright (c) 2010-2011, Eftychios Sifakis. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//##################################################################### + +//########################################################### +// Local variable declarations +//########################################################### + +#ifdef PRINT_DEBUGGING_OUTPUT + +#ifdef USE_SSE_IMPLEMENTATION + float buf[4]; + float A11,A21,A31,A12,A22,A32,A13,A23,A33; + float S11,S21,S31,S22,S32,S33; +#ifdef COMPUTE_V_AS_QUATERNION + float QVS,QVVX,QVVY,QVVZ; +#endif +#ifdef COMPUTE_V_AS_MATRIX + float V11,V21,V31,V12,V22,V32,V13,V23,V33; +#endif +#ifdef COMPUTE_U_AS_QUATERNION + float QUS,QUVX,QUVY,QUVZ; +#endif +#ifdef COMPUTE_U_AS_MATRIX + float U11,U21,U31,U12,U22,U32,U13,U23,U33; +#endif +#endif + +#ifdef USE_AVX_IMPLEMENTATION + float buf[8]; + float A11,A21,A31,A12,A22,A32,A13,A23,A33; + float S11,S21,S31,S22,S32,S33; +#ifdef COMPUTE_V_AS_QUATERNION + float QVS,QVVX,QVVY,QVVZ; +#endif +#ifdef COMPUTE_V_AS_MATRIX + float V11,V21,V31,V12,V22,V32,V13,V23,V33; +#endif +#ifdef COMPUTE_U_AS_QUATERNION + float QUS,QUVX,QUVY,QUVZ; +#endif +#ifdef COMPUTE_U_AS_MATRIX + float U11,U21,U31,U12,U22,U32,U13,U23,U33; +#endif +#endif + +#endif + +const float Four_Gamma_Squared=sqrt(8.)+3.; +const float Sine_Pi_Over_Eight=.5*sqrt(2.-sqrt(2.)); +const float Cosine_Pi_Over_Eight=.5*sqrt(2.+sqrt(2.)); + +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sfour_gamma_squared;) ENABLE_SSE_IMPLEMENTATION(__m128 Vfour_gamma_squared;) ENABLE_AVX_IMPLEMENTATION(__m256 Vfour_gamma_squared;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Ssine_pi_over_eight;) ENABLE_SSE_IMPLEMENTATION(__m128 Vsine_pi_over_eight;) ENABLE_AVX_IMPLEMENTATION(__m256 Vsine_pi_over_eight;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Scosine_pi_over_eight;) ENABLE_SSE_IMPLEMENTATION(__m128 Vcosine_pi_over_eight;) ENABLE_AVX_IMPLEMENTATION(__m256 Vcosine_pi_over_eight;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sone_half;) ENABLE_SSE_IMPLEMENTATION(__m128 Vone_half;) ENABLE_AVX_IMPLEMENTATION(__m256 Vone_half;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sone;) ENABLE_SSE_IMPLEMENTATION(__m128 Vone;) ENABLE_AVX_IMPLEMENTATION(__m256 Vone;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Stiny_number;) ENABLE_SSE_IMPLEMENTATION(__m128 Vtiny_number;) ENABLE_AVX_IMPLEMENTATION(__m256 Vtiny_number;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Ssmall_number;) ENABLE_SSE_IMPLEMENTATION(__m128 Vsmall_number;) ENABLE_AVX_IMPLEMENTATION(__m256 Vsmall_number;) + +ENABLE_SCALAR_IMPLEMENTATION(Sfour_gamma_squared.f=Four_Gamma_Squared;) ENABLE_SSE_IMPLEMENTATION(Vfour_gamma_squared=_mm_set1_ps(Four_Gamma_Squared);) ENABLE_AVX_IMPLEMENTATION(Vfour_gamma_squared=_mm256_set1_ps(Four_Gamma_Squared);) +ENABLE_SCALAR_IMPLEMENTATION(Ssine_pi_over_eight.f=Sine_Pi_Over_Eight;) ENABLE_SSE_IMPLEMENTATION(Vsine_pi_over_eight=_mm_set1_ps(Sine_Pi_Over_Eight);) ENABLE_AVX_IMPLEMENTATION(Vsine_pi_over_eight=_mm256_set1_ps(Sine_Pi_Over_Eight);) +ENABLE_SCALAR_IMPLEMENTATION(Scosine_pi_over_eight.f=Cosine_Pi_Over_Eight;) ENABLE_SSE_IMPLEMENTATION(Vcosine_pi_over_eight=_mm_set1_ps(Cosine_Pi_Over_Eight);) ENABLE_AVX_IMPLEMENTATION(Vcosine_pi_over_eight=_mm256_set1_ps(Cosine_Pi_Over_Eight);) +ENABLE_SCALAR_IMPLEMENTATION(Sone_half.f=.5;) ENABLE_SSE_IMPLEMENTATION(Vone_half=_mm_set1_ps(.5);) ENABLE_AVX_IMPLEMENTATION(Vone_half=_mm256_set1_ps(.5);) +ENABLE_SCALAR_IMPLEMENTATION(Sone.f=1.;) ENABLE_SSE_IMPLEMENTATION(Vone=_mm_set1_ps(1.);) ENABLE_AVX_IMPLEMENTATION(Vone=_mm256_set1_ps(1.);) +ENABLE_SCALAR_IMPLEMENTATION(Stiny_number.f=1.e-20;) ENABLE_SSE_IMPLEMENTATION(Vtiny_number=_mm_set1_ps(1.e-20);) ENABLE_AVX_IMPLEMENTATION(Vtiny_number=_mm256_set1_ps(1.e-20);) +ENABLE_SCALAR_IMPLEMENTATION(Ssmall_number.f=1.e-12;) ENABLE_SSE_IMPLEMENTATION(Vsmall_number=_mm_set1_ps(1.e-12);) ENABLE_AVX_IMPLEMENTATION(Vsmall_number=_mm256_set1_ps(1.e-12);) + +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sa11;) ENABLE_SSE_IMPLEMENTATION(__m128 Va11;) ENABLE_AVX_IMPLEMENTATION(__m256 Va11;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sa21;) ENABLE_SSE_IMPLEMENTATION(__m128 Va21;) ENABLE_AVX_IMPLEMENTATION(__m256 Va21;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sa31;) ENABLE_SSE_IMPLEMENTATION(__m128 Va31;) ENABLE_AVX_IMPLEMENTATION(__m256 Va31;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sa12;) ENABLE_SSE_IMPLEMENTATION(__m128 Va12;) ENABLE_AVX_IMPLEMENTATION(__m256 Va12;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sa22;) ENABLE_SSE_IMPLEMENTATION(__m128 Va22;) ENABLE_AVX_IMPLEMENTATION(__m256 Va22;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sa32;) ENABLE_SSE_IMPLEMENTATION(__m128 Va32;) ENABLE_AVX_IMPLEMENTATION(__m256 Va32;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sa13;) ENABLE_SSE_IMPLEMENTATION(__m128 Va13;) ENABLE_AVX_IMPLEMENTATION(__m256 Va13;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sa23;) ENABLE_SSE_IMPLEMENTATION(__m128 Va23;) ENABLE_AVX_IMPLEMENTATION(__m256 Va23;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sa33;) ENABLE_SSE_IMPLEMENTATION(__m128 Va33;) ENABLE_AVX_IMPLEMENTATION(__m256 Va33;) + +#ifdef COMPUTE_V_AS_MATRIX +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sv11;) ENABLE_SSE_IMPLEMENTATION(__m128 Vv11;) ENABLE_AVX_IMPLEMENTATION(__m256 Vv11;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sv21;) ENABLE_SSE_IMPLEMENTATION(__m128 Vv21;) ENABLE_AVX_IMPLEMENTATION(__m256 Vv21;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sv31;) ENABLE_SSE_IMPLEMENTATION(__m128 Vv31;) ENABLE_AVX_IMPLEMENTATION(__m256 Vv31;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sv12;) ENABLE_SSE_IMPLEMENTATION(__m128 Vv12;) ENABLE_AVX_IMPLEMENTATION(__m256 Vv12;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sv22;) ENABLE_SSE_IMPLEMENTATION(__m128 Vv22;) ENABLE_AVX_IMPLEMENTATION(__m256 Vv22;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sv32;) ENABLE_SSE_IMPLEMENTATION(__m128 Vv32;) ENABLE_AVX_IMPLEMENTATION(__m256 Vv32;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sv13;) ENABLE_SSE_IMPLEMENTATION(__m128 Vv13;) ENABLE_AVX_IMPLEMENTATION(__m256 Vv13;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sv23;) ENABLE_SSE_IMPLEMENTATION(__m128 Vv23;) ENABLE_AVX_IMPLEMENTATION(__m256 Vv23;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sv33;) ENABLE_SSE_IMPLEMENTATION(__m128 Vv33;) ENABLE_AVX_IMPLEMENTATION(__m256 Vv33;) +#endif + +#ifdef COMPUTE_V_AS_QUATERNION +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sqvs;) ENABLE_SSE_IMPLEMENTATION(__m128 Vqvs;) ENABLE_AVX_IMPLEMENTATION(__m256 Vqvs;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sqvvx;) ENABLE_SSE_IMPLEMENTATION(__m128 Vqvvx;) ENABLE_AVX_IMPLEMENTATION(__m256 Vqvvx;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sqvvy;) ENABLE_SSE_IMPLEMENTATION(__m128 Vqvvy;) ENABLE_AVX_IMPLEMENTATION(__m256 Vqvvy;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sqvvz;) ENABLE_SSE_IMPLEMENTATION(__m128 Vqvvz;) ENABLE_AVX_IMPLEMENTATION(__m256 Vqvvz;) +#endif + +#ifdef COMPUTE_U_AS_MATRIX +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Su11;) ENABLE_SSE_IMPLEMENTATION(__m128 Vu11;) ENABLE_AVX_IMPLEMENTATION(__m256 Vu11;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Su21;) ENABLE_SSE_IMPLEMENTATION(__m128 Vu21;) ENABLE_AVX_IMPLEMENTATION(__m256 Vu21;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Su31;) ENABLE_SSE_IMPLEMENTATION(__m128 Vu31;) ENABLE_AVX_IMPLEMENTATION(__m256 Vu31;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Su12;) ENABLE_SSE_IMPLEMENTATION(__m128 Vu12;) ENABLE_AVX_IMPLEMENTATION(__m256 Vu12;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Su22;) ENABLE_SSE_IMPLEMENTATION(__m128 Vu22;) ENABLE_AVX_IMPLEMENTATION(__m256 Vu22;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Su32;) ENABLE_SSE_IMPLEMENTATION(__m128 Vu32;) ENABLE_AVX_IMPLEMENTATION(__m256 Vu32;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Su13;) ENABLE_SSE_IMPLEMENTATION(__m128 Vu13;) ENABLE_AVX_IMPLEMENTATION(__m256 Vu13;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Su23;) ENABLE_SSE_IMPLEMENTATION(__m128 Vu23;) ENABLE_AVX_IMPLEMENTATION(__m256 Vu23;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Su33;) ENABLE_SSE_IMPLEMENTATION(__m128 Vu33;) ENABLE_AVX_IMPLEMENTATION(__m256 Vu33;) +#endif + +#ifdef COMPUTE_U_AS_QUATERNION +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Squs;) ENABLE_SSE_IMPLEMENTATION(__m128 Vqus;) ENABLE_AVX_IMPLEMENTATION(__m256 Vqus;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Squvx;) ENABLE_SSE_IMPLEMENTATION(__m128 Vquvx;) ENABLE_AVX_IMPLEMENTATION(__m256 Vquvx;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Squvy;) ENABLE_SSE_IMPLEMENTATION(__m128 Vquvy;) ENABLE_AVX_IMPLEMENTATION(__m256 Vquvy;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Squvz;) ENABLE_SSE_IMPLEMENTATION(__m128 Vquvz;) ENABLE_AVX_IMPLEMENTATION(__m256 Vquvz;) +#endif + +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sc;) ENABLE_SSE_IMPLEMENTATION(__m128 Vc;) ENABLE_AVX_IMPLEMENTATION(__m256 Vc;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Ss;) ENABLE_SSE_IMPLEMENTATION(__m128 Vs;) ENABLE_AVX_IMPLEMENTATION(__m256 Vs;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sch;) ENABLE_SSE_IMPLEMENTATION(__m128 Vch;) ENABLE_AVX_IMPLEMENTATION(__m256 Vch;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Ssh;) ENABLE_SSE_IMPLEMENTATION(__m128 Vsh;) ENABLE_AVX_IMPLEMENTATION(__m256 Vsh;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Stmp1;) ENABLE_SSE_IMPLEMENTATION(__m128 Vtmp1;) ENABLE_AVX_IMPLEMENTATION(__m256 Vtmp1;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Stmp2;) ENABLE_SSE_IMPLEMENTATION(__m128 Vtmp2;) ENABLE_AVX_IMPLEMENTATION(__m256 Vtmp2;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Stmp3;) ENABLE_SSE_IMPLEMENTATION(__m128 Vtmp3;) ENABLE_AVX_IMPLEMENTATION(__m256 Vtmp3;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Stmp4;) ENABLE_SSE_IMPLEMENTATION(__m128 Vtmp4;) ENABLE_AVX_IMPLEMENTATION(__m256 Vtmp4;) +ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Stmp5;) ENABLE_SSE_IMPLEMENTATION(__m128 Vtmp5;) ENABLE_AVX_IMPLEMENTATION(__m256 Vtmp5;) diff --git a/src/igl/Singular_Value_Decomposition_Main_Kernel_Body.hpp b/src/igl/Singular_Value_Decomposition_Main_Kernel_Body.hpp new file mode 100644 index 0000000000..e8898a8aa8 --- /dev/null +++ b/src/igl/Singular_Value_Decomposition_Main_Kernel_Body.hpp @@ -0,0 +1,1277 @@ +//##################################################################### +// Copyright (c) 2010-2011, Eftychios Sifakis. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//##################################################################### + +#ifdef __INTEL_COMPILER +#pragma warning( disable : 592 ) +#endif + +// #define USE_ACCURATE_RSQRT_IN_JACOBI_CONJUGATION +// #define PERFORM_STRICT_QUATERNION_RENORMALIZATION + +{ // Begin block : Scope of qV (if not maintained) + +#ifndef COMPUTE_V_AS_QUATERNION + ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sqvs;) ENABLE_SSE_IMPLEMENTATION(__m128 Vqvs;) ENABLE_AVX_IMPLEMENTATION(__m256 Vqvs;) + ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sqvvx;) ENABLE_SSE_IMPLEMENTATION(__m128 Vqvvx;) ENABLE_AVX_IMPLEMENTATION(__m256 Vqvvx;) + ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sqvvy;) ENABLE_SSE_IMPLEMENTATION(__m128 Vqvvy;) ENABLE_AVX_IMPLEMENTATION(__m256 Vqvvy;) + ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Sqvvz;) ENABLE_SSE_IMPLEMENTATION(__m128 Vqvvz;) ENABLE_AVX_IMPLEMENTATION(__m256 Vqvvz;) +#endif + +{ // Begin block : Symmetric eigenanalysis + + ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Ss11;) ENABLE_SSE_IMPLEMENTATION(__m128 Vs11;) ENABLE_AVX_IMPLEMENTATION(__m256 Vs11;) + ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Ss21;) ENABLE_SSE_IMPLEMENTATION(__m128 Vs21;) ENABLE_AVX_IMPLEMENTATION(__m256 Vs21;) + ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Ss31;) ENABLE_SSE_IMPLEMENTATION(__m128 Vs31;) ENABLE_AVX_IMPLEMENTATION(__m256 Vs31;) + ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Ss22;) ENABLE_SSE_IMPLEMENTATION(__m128 Vs22;) ENABLE_AVX_IMPLEMENTATION(__m256 Vs22;) + ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Ss32;) ENABLE_SSE_IMPLEMENTATION(__m128 Vs32;) ENABLE_AVX_IMPLEMENTATION(__m256 Vs32;) + ENABLE_SCALAR_IMPLEMENTATION(union {float f;unsigned int ui;} Ss33;) ENABLE_SSE_IMPLEMENTATION(__m128 Vs33;) ENABLE_AVX_IMPLEMENTATION(__m256 Vs33;) + + ENABLE_SCALAR_IMPLEMENTATION(Sqvs.f=1.;) ENABLE_SSE_IMPLEMENTATION(Vqvs=Vone;) ENABLE_AVX_IMPLEMENTATION(Vqvs=Vone;) + ENABLE_SCALAR_IMPLEMENTATION(Sqvvx.f=0.;) ENABLE_SSE_IMPLEMENTATION(Vqvvx=_mm_xor_ps(Vqvvx,Vqvvx);) ENABLE_AVX_IMPLEMENTATION(Vqvvx=_mm256_xor_ps(Vqvvx,Vqvvx);) + ENABLE_SCALAR_IMPLEMENTATION(Sqvvy.f=0.;) ENABLE_SSE_IMPLEMENTATION(Vqvvy=_mm_xor_ps(Vqvvy,Vqvvy);) ENABLE_AVX_IMPLEMENTATION(Vqvvy=_mm256_xor_ps(Vqvvy,Vqvvy);) + ENABLE_SCALAR_IMPLEMENTATION(Sqvvz.f=0.;) ENABLE_SSE_IMPLEMENTATION(Vqvvz=_mm_xor_ps(Vqvvz,Vqvvz);) ENABLE_AVX_IMPLEMENTATION(Vqvvz=_mm256_xor_ps(Vqvvz,Vqvvz);) + + //########################################################### + // Compute normal equations matrix + //########################################################### + + ENABLE_SCALAR_IMPLEMENTATION(Ss11.f=Sa11.f*Sa11.f;) ENABLE_SSE_IMPLEMENTATION(Vs11=_mm_mul_ps(Va11,Va11);) ENABLE_AVX_IMPLEMENTATION(Vs11=_mm256_mul_ps(Va11,Va11);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa21.f*Sa21.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va21,Va21);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va21,Va21);) + ENABLE_SCALAR_IMPLEMENTATION(Ss11.f=Stmp1.f+Ss11.f;) ENABLE_SSE_IMPLEMENTATION(Vs11=_mm_add_ps(Vtmp1,Vs11);) ENABLE_AVX_IMPLEMENTATION(Vs11=_mm256_add_ps(Vtmp1,Vs11);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa31.f*Sa31.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va31,Va31);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va31,Va31);) + ENABLE_SCALAR_IMPLEMENTATION(Ss11.f=Stmp1.f+Ss11.f;) ENABLE_SSE_IMPLEMENTATION(Vs11=_mm_add_ps(Vtmp1,Vs11);) ENABLE_AVX_IMPLEMENTATION(Vs11=_mm256_add_ps(Vtmp1,Vs11);) + + ENABLE_SCALAR_IMPLEMENTATION(Ss21.f=Sa12.f*Sa11.f;) ENABLE_SSE_IMPLEMENTATION(Vs21=_mm_mul_ps(Va12,Va11);) ENABLE_AVX_IMPLEMENTATION(Vs21=_mm256_mul_ps(Va12,Va11);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa22.f*Sa21.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va22,Va21);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va22,Va21);) + ENABLE_SCALAR_IMPLEMENTATION(Ss21.f=Stmp1.f+Ss21.f;) ENABLE_SSE_IMPLEMENTATION(Vs21=_mm_add_ps(Vtmp1,Vs21);) ENABLE_AVX_IMPLEMENTATION(Vs21=_mm256_add_ps(Vtmp1,Vs21);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa32.f*Sa31.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va32,Va31);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va32,Va31);) + ENABLE_SCALAR_IMPLEMENTATION(Ss21.f=Stmp1.f+Ss21.f;) ENABLE_SSE_IMPLEMENTATION(Vs21=_mm_add_ps(Vtmp1,Vs21);) ENABLE_AVX_IMPLEMENTATION(Vs21=_mm256_add_ps(Vtmp1,Vs21);) + + ENABLE_SCALAR_IMPLEMENTATION(Ss31.f=Sa13.f*Sa11.f;) ENABLE_SSE_IMPLEMENTATION(Vs31=_mm_mul_ps(Va13,Va11);) ENABLE_AVX_IMPLEMENTATION(Vs31=_mm256_mul_ps(Va13,Va11);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa23.f*Sa21.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va23,Va21);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va23,Va21);) + ENABLE_SCALAR_IMPLEMENTATION(Ss31.f=Stmp1.f+Ss31.f;) ENABLE_SSE_IMPLEMENTATION(Vs31=_mm_add_ps(Vtmp1,Vs31);) ENABLE_AVX_IMPLEMENTATION(Vs31=_mm256_add_ps(Vtmp1,Vs31);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa33.f*Sa31.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va33,Va31);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va33,Va31);) + ENABLE_SCALAR_IMPLEMENTATION(Ss31.f=Stmp1.f+Ss31.f;) ENABLE_SSE_IMPLEMENTATION(Vs31=_mm_add_ps(Vtmp1,Vs31);) ENABLE_AVX_IMPLEMENTATION(Vs31=_mm256_add_ps(Vtmp1,Vs31);) + + ENABLE_SCALAR_IMPLEMENTATION(Ss22.f=Sa12.f*Sa12.f;) ENABLE_SSE_IMPLEMENTATION(Vs22=_mm_mul_ps(Va12,Va12);) ENABLE_AVX_IMPLEMENTATION(Vs22=_mm256_mul_ps(Va12,Va12);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa22.f*Sa22.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va22,Va22);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va22,Va22);) + ENABLE_SCALAR_IMPLEMENTATION(Ss22.f=Stmp1.f+Ss22.f;) ENABLE_SSE_IMPLEMENTATION(Vs22=_mm_add_ps(Vtmp1,Vs22);) ENABLE_AVX_IMPLEMENTATION(Vs22=_mm256_add_ps(Vtmp1,Vs22);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa32.f*Sa32.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va32,Va32);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va32,Va32);) + ENABLE_SCALAR_IMPLEMENTATION(Ss22.f=Stmp1.f+Ss22.f;) ENABLE_SSE_IMPLEMENTATION(Vs22=_mm_add_ps(Vtmp1,Vs22);) ENABLE_AVX_IMPLEMENTATION(Vs22=_mm256_add_ps(Vtmp1,Vs22);) + + ENABLE_SCALAR_IMPLEMENTATION(Ss32.f=Sa13.f*Sa12.f;) ENABLE_SSE_IMPLEMENTATION(Vs32=_mm_mul_ps(Va13,Va12);) ENABLE_AVX_IMPLEMENTATION(Vs32=_mm256_mul_ps(Va13,Va12);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa23.f*Sa22.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va23,Va22);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va23,Va22);) + ENABLE_SCALAR_IMPLEMENTATION(Ss32.f=Stmp1.f+Ss32.f;) ENABLE_SSE_IMPLEMENTATION(Vs32=_mm_add_ps(Vtmp1,Vs32);) ENABLE_AVX_IMPLEMENTATION(Vs32=_mm256_add_ps(Vtmp1,Vs32);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa33.f*Sa32.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va33,Va32);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va33,Va32);) + ENABLE_SCALAR_IMPLEMENTATION(Ss32.f=Stmp1.f+Ss32.f;) ENABLE_SSE_IMPLEMENTATION(Vs32=_mm_add_ps(Vtmp1,Vs32);) ENABLE_AVX_IMPLEMENTATION(Vs32=_mm256_add_ps(Vtmp1,Vs32);) + + ENABLE_SCALAR_IMPLEMENTATION(Ss33.f=Sa13.f*Sa13.f;) ENABLE_SSE_IMPLEMENTATION(Vs33=_mm_mul_ps(Va13,Va13);) ENABLE_AVX_IMPLEMENTATION(Vs33=_mm256_mul_ps(Va13,Va13);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa23.f*Sa23.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va23,Va23);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va23,Va23);) + ENABLE_SCALAR_IMPLEMENTATION(Ss33.f=Stmp1.f+Ss33.f;) ENABLE_SSE_IMPLEMENTATION(Vs33=_mm_add_ps(Vtmp1,Vs33);) ENABLE_AVX_IMPLEMENTATION(Vs33=_mm256_add_ps(Vtmp1,Vs33);) + ENABLE_SCALAR_IMPLEMENTATION(Stmp1.f=Sa33.f*Sa33.f;) ENABLE_SSE_IMPLEMENTATION(Vtmp1=_mm_mul_ps(Va33,Va33);) ENABLE_AVX_IMPLEMENTATION(Vtmp1=_mm256_mul_ps(Va33,Va33);) + ENABLE_SCALAR_IMPLEMENTATION(Ss33.f=Stmp1.f+Ss33.f;) ENABLE_SSE_IMPLEMENTATION(Vs33=_mm_add_ps(Vtmp1,Vs33);) ENABLE_AVX_IMPLEMENTATION(Vs33=_mm256_add_ps(Vtmp1,Vs33);) + + //########################################################### + // Solve symmetric eigenproblem using Jacobi iteration + //########################################################### + + for(int sweep=1;sweep<=4;sweep++){ + + // First Jacobi conjugation + +#define SS11 Ss11 +#define SS21 Ss21 +#define SS31 Ss31 +#define SS22 Ss22 +#define SS32 Ss32 +#define SS33 Ss33 +#define SQVVX Sqvvx +#define SQVVY Sqvvy +#define SQVVZ Sqvvz +#define STMP1 Stmp1 +#define STMP2 Stmp2 +#define STMP3 Stmp3 + +#define VS11 Vs11 +#define VS21 Vs21 +#define VS31 Vs31 +#define VS22 Vs22 +#define VS32 Vs32 +#define VS33 Vs33 +#define VQVVX Vqvvx +#define VQVVY Vqvvy +#define VQVVZ Vqvvz +#define VTMP1 Vtmp1 +#define VTMP2 Vtmp2 +#define VTMP3 Vtmp3 + +#include "Singular_Value_Decomposition_Jacobi_Conjugation_Kernel.hpp" + +#undef SS11 +#undef SS21 +#undef SS31 +#undef SS22 +#undef SS32 +#undef SS33 +#undef SQVVX +#undef SQVVY +#undef SQVVZ +#undef STMP1 +#undef STMP2 +#undef STMP3 + +#undef VS11 +#undef VS21 +#undef VS31 +#undef VS22 +#undef VS32 +#undef VS33 +#undef VQVVX +#undef VQVVY +#undef VQVVZ +#undef VTMP1 +#undef VTMP2 +#undef VTMP3 + + // Second Jacobi conjugation + +#define SS11 Ss22 +#define SS21 Ss32 +#define SS31 Ss21 +#define SS22 Ss33 +#define SS32 Ss31 +#define SS33 Ss11 +#define SQVVX Sqvvy +#define SQVVY Sqvvz +#define SQVVZ Sqvvx +#define STMP1 Stmp2 +#define STMP2 Stmp3 +#define STMP3 Stmp1 + +#define VS11 Vs22 +#define VS21 Vs32 +#define VS31 Vs21 +#define VS22 Vs33 +#define VS32 Vs31 +#define VS33 Vs11 +#define VQVVX Vqvvy +#define VQVVY Vqvvz +#define VQVVZ Vqvvx +#define VTMP1 Vtmp2 +#define VTMP2 Vtmp3 +#define VTMP3 Vtmp1 + +#include "Singular_Value_Decomposition_Jacobi_Conjugation_Kernel.hpp" + +#undef SS11 +#undef SS21 +#undef SS31 +#undef SS22 +#undef SS32 +#undef SS33 +#undef SQVVX +#undef SQVVY +#undef SQVVZ +#undef STMP1 +#undef STMP2 +#undef STMP3 + +#undef VS11 +#undef VS21 +#undef VS31 +#undef VS22 +#undef VS32 +#undef VS33 +#undef VQVVX +#undef VQVVY +#undef VQVVZ +#undef VTMP1 +#undef VTMP2 +#undef VTMP3 + + // Third Jacobi conjugation + +#define SS11 Ss33 +#define SS21 Ss31 +#define SS31 Ss32 +#define SS22 Ss11 +#define SS32 Ss21 +#define SS33 Ss22 +#define SQVVX Sqvvz +#define SQVVY Sqvvx +#define SQVVZ Sqvvy +#define STMP1 Stmp3 +#define STMP2 Stmp1 +#define STMP3 Stmp2 + +#define VS11 Vs33 +#define VS21 Vs31 +#define VS31 Vs32 +#define VS22 Vs11 +#define VS32 Vs21 +#define VS33 Vs22 +#define VQVVX Vqvvz +#define VQVVY Vqvvx +#define VQVVZ Vqvvy +#define VTMP1 Vtmp3 +#define VTMP2 Vtmp1 +#define VTMP3 Vtmp2 + +#include "Singular_Value_Decomposition_Jacobi_Conjugation_Kernel.hpp" + +#undef SS11 +#undef SS21 +#undef SS31 +#undef SS22 +#undef SS32 +#undef SS33 +#undef SQVVX +#undef SQVVY +#undef SQVVZ +#undef STMP1 +#undef STMP2 +#undef STMP3 + +#undef VS11 +#undef VS21 +#undef VS31 +#undef VS22 +#undef VS32 +#undef VS33 +#undef VQVVX +#undef VQVVY +#undef VQVVZ +#undef VTMP1 +#undef VTMP2 +#undef VTMP3 + } + +#ifdef PRINT_DEBUGGING_OUTPUT +#ifdef USE_SCALAR_IMPLEMENTATION + std::cout<<"Scalar S ="< +#include +#endif + +// Prevent warnings +#ifdef ENABLE_SCALAR_IMPLEMENTATION +# undef ENABLE_SCALAR_IMPLEMENTATION +#endif +#ifdef ENABLE_SSE_IMPLEMENTATION +# undef ENABLE_SSE_IMPLEMENTATION +#endif +#ifdef ENABLE_AVX_IMPLEMENTATION +# undef ENABLE_AVX_IMPLEMENTATION +#endif + +#ifdef USE_SCALAR_IMPLEMENTATION +#define ENABLE_SCALAR_IMPLEMENTATION(X) X +#else +#define ENABLE_SCALAR_IMPLEMENTATION(X) +#endif + +#ifdef USE_SSE_IMPLEMENTATION +#define ENABLE_SSE_IMPLEMENTATION(X) X +#else +#define ENABLE_SSE_IMPLEMENTATION(X) +#endif + +#ifdef USE_AVX_IMPLEMENTATION +#include +#define ENABLE_AVX_IMPLEMENTATION(X) X +#else +// Stefan: removed include. Why does it import MMX instructions, shouldn't this be under the #ifdef USE_SSE_IMPLEMENTATION above? +//#include +#define ENABLE_AVX_IMPLEMENTATION(X) +#endif + +#ifdef USE_SCALAR_IMPLEMENTATION +// Alec: Why is this using sse intrinsics if it's supposed to be the scalar +// implementation? +#ifdef __SSE__ +#include +// Changed to inline +inline float rsqrt(const float f) +{ + float buf[4]; + buf[0]=f; + __m128 v=_mm_loadu_ps(buf); + v=_mm_rsqrt_ss(v); + _mm_storeu_ps(buf,v); + return buf[0]; +} +#else +#include +inline float rsqrt(const float f) +{ + return 1./sqrtf(f); +} +#endif +#endif + + diff --git a/src/igl/SolverStatus.h b/src/igl/SolverStatus.h new file mode 100644 index 0000000000..d9151f5c05 --- /dev/null +++ b/src/igl/SolverStatus.h @@ -0,0 +1,23 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SOLVER_STATUS_H +#define IGL_SOLVER_STATUS_H +namespace igl +{ + enum SolverStatus + { + // Good + SOLVER_STATUS_CONVERGED = 0, + // OK + SOLVER_STATUS_MAX_ITER = 1, + // Bad + SOLVER_STATUS_ERROR = 2, + NUM_SOLVER_STATUSES = 3, + }; +}; +#endif diff --git a/src/igl/SortableRow.h b/src/igl/SortableRow.h new file mode 100644 index 0000000000..5f172987bf --- /dev/null +++ b/src/igl/SortableRow.h @@ -0,0 +1,70 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SORTABLE_ROW_H +#define IGL_SORTABLE_ROW_H + +// Simple class to contain a rowvector which allows rowwise sorting and +// reordering +#include + +namespace igl +{ + // Templates: + // T should be a matrix that implements .size(), and operator(int i) + template + class SortableRow + { + public: + T data; + public: + SortableRow():data(){}; + SortableRow(const T & data):data(data){}; + bool operator<(const SortableRow & that) const + { + // Get reference so that I can use parenthesis + const SortableRow & THIS = *this; + // Lexicographical + int minc = (THIS.data.size() < that.data.size()? + THIS.data.size() : that.data.size()); + // loop over columns + for(int i = 0;i & THIS = *this; + if(THIS.data.size() != that.data.size()) + { + return false; + } + for(int i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// High Resolution Timer. +// +// Resolution on Mac (clock tick) +// Resolution on Linux (1 us not tested) +// Resolution on Windows (clock tick not tested) + +#ifndef IGL_TIMER_H +#define IGL_TIMER_H + +#ifdef WIN32 // Windows system specific +#include +#elif __APPLE__ // Unix based system specific +#include // for mach_absolute_time +#else +#include +#endif +#include + +namespace igl +{ + class Timer + { + public: + // default constructor + Timer(): + stopped(0), +#ifdef WIN32 + frequency(), + startCount(), + endCount() +#elif __APPLE__ + startCount(0), + endCount(0) +#else + startCount(), + endCount() +#endif + { +#ifdef WIN32 + QueryPerformanceFrequency(&frequency); + startCount.QuadPart = 0; + endCount.QuadPart = 0; +#elif __APPLE__ + startCount = 0; + endCount = 0; +#else + startCount.tv_sec = startCount.tv_usec = 0; + endCount.tv_sec = endCount.tv_usec = 0; +#endif + + stopped = 0; + } + // default destructor + ~Timer() + { + + } + +#ifdef __APPLE__ + //Raw mach_absolute_times going in, difference in seconds out + double subtractTimes( uint64_t endTime, uint64_t startTime ) + { + uint64_t difference = endTime - startTime; + static double conversion = 0.0; + + if( conversion == 0.0 ) + { + mach_timebase_info_data_t info; + kern_return_t err = mach_timebase_info( &info ); + + //Convert the timebase into seconds + if( err == 0 ) + conversion = 1e-9 * (double) info.numer / (double) info.denom; + } + + return conversion * (double) difference; + } +#endif + + // start timer + void start() + { + stopped = 0; // reset stop flag +#ifdef WIN32 + QueryPerformanceCounter(&startCount); +#elif __APPLE__ + startCount = mach_absolute_time(); +#else + gettimeofday(&startCount, NULL); +#endif + + } + + // stop the timer + void stop() + { + stopped = 1; // set timer stopped flag + +#ifdef WIN32 + QueryPerformanceCounter(&endCount); +#elif __APPLE__ + endCount = mach_absolute_time(); +#else + gettimeofday(&endCount, NULL); +#endif + + } + // get elapsed time in second + double getElapsedTime() + { + return this->getElapsedTimeInSec(); + } + // get elapsed time in second (same as getElapsedTime) + double getElapsedTimeInSec() + { + return this->getElapsedTimeInMicroSec() * 0.000001; + } + + // get elapsed time in milli-second + double getElapsedTimeInMilliSec() + { + return this->getElapsedTimeInMicroSec() * 0.001; + } + // get elapsed time in micro-second + double getElapsedTimeInMicroSec() + { + double startTimeInMicroSec = 0; + double endTimeInMicroSec = 0; + +#ifdef WIN32 + if(!stopped) + QueryPerformanceCounter(&endCount); + + startTimeInMicroSec = + startCount.QuadPart * (1000000.0 / frequency.QuadPart); + endTimeInMicroSec = endCount.QuadPart * (1000000.0 / frequency.QuadPart); +#elif __APPLE__ + if (!stopped) + endCount = mach_absolute_time(); + + return subtractTimes(endCount,startCount)/1e-6; +#else + if(!stopped) + gettimeofday(&endCount, NULL); + + startTimeInMicroSec = + (startCount.tv_sec * 1000000.0) + startCount.tv_usec; + endTimeInMicroSec = (endCount.tv_sec * 1000000.0) + endCount.tv_usec; +#endif + + return endTimeInMicroSec - startTimeInMicroSec; + } + + private: + // stop flag + int stopped; +#ifdef WIN32 + // ticks per second + LARGE_INTEGER frequency; + LARGE_INTEGER startCount; + LARGE_INTEGER endCount; +#elif __APPLE__ + uint64_t startCount; + uint64_t endCount; +#else + timeval startCount; + timeval endCount; +#endif + }; +} +#endif // TIMER_H_DEF + diff --git a/src/igl/Viewport.h b/src/igl/Viewport.h new file mode 100644 index 0000000000..717d3f97bf --- /dev/null +++ b/src/igl/Viewport.h @@ -0,0 +1,69 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_VIEWPORT_H +#define IGL_VIEWPORT_H + +namespace igl +{ + // Simple Viewport class for an opengl context. Handles reshaping and mouse. + struct Viewport + { + int x,y,width,height; + // Constructors + Viewport( + const int x=0, + const int y=0, + const int width=0, + const int height=0): + x(x), + y(y), + width(width), + height(height) + { + }; + virtual ~Viewport(){} + void reshape( + const int x, + const int y, + const int width, + const int height) + { + this->x = x; + this->y = y; + this->width = width; + this->height = height; + }; + // Given mouse_x,mouse_y on the entire window return mouse_x, mouse_y in + // this viewport. + // + // Inputs: + // my mouse y-coordinate + // wh window height + // Returns y-coordinate in viewport + int mouse_y(const int my,const int wh) + { + return my - (wh - height - y); + } + // Inputs: + // mx mouse x-coordinate + // Returns x-coordinate in viewport + int mouse_x(const int mx) + { + return mx - x; + } + // Returns whether point (mx,my) is in extend of Viewport + bool inside(const int mx, const int my) const + { + return + mx >= x && my >= y && + mx < x+width && my < y+height; + } + }; +} + +#endif diff --git a/src/igl/WindingNumberAABB.h b/src/igl/WindingNumberAABB.h new file mode 100644 index 0000000000..478026989f --- /dev/null +++ b/src/igl/WindingNumberAABB.h @@ -0,0 +1,377 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +// # MUTUAL DEPENDENCY ISSUE FOR HEADER ONLY VERSION +// MUST INCLUDE winding_number.h first before guard: +#include "winding_number.h" + +#ifndef IGL_WINDINGNUMBERAABB_H +#define IGL_WINDINGNUMBERAABB_H +#include "WindingNumberTree.h" + +namespace igl +{ + template < + typename Point, + typename DerivedV, + typename DerivedF > + class WindingNumberAABB : public WindingNumberTree + { + protected: + Point min_corner; + Point max_corner; + typename DerivedV::Scalar total_positive_area; + public: + enum SplitMethod + { + CENTER_ON_LONGEST_AXIS = 0, + MEDIAN_ON_LONGEST_AXIS = 1, + NUM_SPLIT_METHODS = 2 + } split_method; + public: + inline WindingNumberAABB(): + total_positive_area(std::numeric_limits::infinity()), + split_method(MEDIAN_ON_LONGEST_AXIS) + {} + inline WindingNumberAABB( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F); + inline WindingNumberAABB( + const WindingNumberTree & parent, + const Eigen::MatrixBase & F); + // Initialize some things + inline void set_mesh( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F); + inline void init(); + inline bool inside(const Point & p) const; + inline virtual void grow(); + // Compute min and max corners + inline void compute_min_max_corners(); + inline typename DerivedV::Scalar max_abs_winding_number(const Point & p) const; + inline typename DerivedV::Scalar max_simple_abs_winding_number(const Point & p) const; + }; +} + +// Implementation + +#include "winding_number.h" + +#include "barycenter.h" +#include "median.h" +#include "doublearea.h" +#include "per_face_normals.h" + +#include +#include +#include + +// Minimum number of faces in a hierarchy element (this is probably dependent +// on speed of machine and compiler optimization) +#ifndef WindingNumberAABB_MIN_F +# define WindingNumberAABB_MIN_F 100 +#endif + +template +inline void igl::WindingNumberAABB::set_mesh( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F) +{ + igl::WindingNumberTree::set_mesh(V,F); + init(); +} + +template +inline void igl::WindingNumberAABB::init() +{ + using namespace Eigen; + assert(max_corner.size() == 3); + assert(min_corner.size() == 3); + compute_min_max_corners(); + Eigen::Matrix dblA; + doublearea(this->getV(),this->getF(),dblA); + total_positive_area = dblA.sum()/2.0; +} + +template +inline igl::WindingNumberAABB::WindingNumberAABB( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F): + WindingNumberTree(V,F), + min_corner(), + max_corner(), + total_positive_area( + std::numeric_limits::infinity()), + split_method(MEDIAN_ON_LONGEST_AXIS) +{ + init(); +} + +template +inline igl::WindingNumberAABB::WindingNumberAABB( + const WindingNumberTree & parent, + const Eigen::MatrixBase & F): + WindingNumberTree(parent,F), + min_corner(), + max_corner(), + total_positive_area( + std::numeric_limits::infinity()), + split_method(MEDIAN_ON_LONGEST_AXIS) +{ + init(); +} + +template +inline void igl::WindingNumberAABB::grow() +{ + using namespace std; + using namespace Eigen; + // Clear anything that already exists + this->delete_children(); + + //cout<<"cap.rows(): "<getcap().rows()<getF().rows()<getF().rows() <= (WindingNumberAABB_MIN_F>0?WindingNumberAABB_MIN_F:0) || + (this->getcap().rows() - 2) >= this->getF().rows()) + { + // Don't grow + return; + } + + // Compute longest direction + int max_d = -1; + typename DerivedV::Scalar max_len = + -numeric_limits::infinity(); + for(int d = 0;d max_len ) + { + max_len = (max_corner[d] - min_corner[d]); + max_d = d; + } + } + // Compute facet barycenters + Eigen::Matrix BC; + barycenter(this->getV(),this->getF(),BC); + + + // Blerg, why is selecting rows so difficult + + typename DerivedV::Scalar split_value; + // Split in longest direction + switch(split_method) + { + case MEDIAN_ON_LONGEST_AXIS: + // Determine median + median(BC.col(max_d),split_value); + break; + default: + assert(false); + case CENTER_ON_LONGEST_AXIS: + split_value = 0.5*(max_corner[max_d] + min_corner[max_d]); + break; + } + //cout<<"c: "<<0.5*(max_corner[max_d] + min_corner[max_d])<<" "<< + // "m: "< id( this->getF().rows()); + for(int i = 0;igetF().rows();i++) + { + if(BC(i,max_d) <= split_value) + { + id[i] = 0; //left + }else + { + id[i] = 1; //right + } + } + + const int lefts = (int) count(id.begin(),id.end(),0); + const int rights = (int) count(id.begin(),id.end(),1); + if(lefts == 0 || rights == 0) + { + // badly balanced base case (could try to recut) + return; + } + assert(lefts+rights == this->getF().rows()); + DerivedF leftF(lefts, this->getF().cols()); + DerivedF rightF(rights,this->getF().cols()); + int left_i = 0; + int right_i = 0; + for(int i = 0;igetF().rows();i++) + { + if(id[i] == 0) + { + leftF.row(left_i++) = this->getF().row(i); + }else if(id[i] == 1) + { + rightF.row(right_i++) = this->getF().row(i); + }else + { + assert(false); + } + } + assert(right_i == rightF.rows()); + assert(left_i == leftF.rows()); + // Finally actually grow children and Recursively grow + WindingNumberAABB * leftWindingNumberAABB = + new WindingNumberAABB(*this,leftF); + leftWindingNumberAABB->grow(); + this->children.push_back(leftWindingNumberAABB); + WindingNumberAABB * rightWindingNumberAABB = + new WindingNumberAABB(*this,rightF); + rightWindingNumberAABB->grow(); + this->children.push_back(rightWindingNumberAABB); +} + +template +inline bool igl::WindingNumberAABB::inside(const Point & p) const +{ + assert(p.size() == max_corner.size()); + assert(p.size() == min_corner.size()); + for(int i = 0;i= max_corner(i)) + // **MUST** be conservative + if( p(i) < min_corner(i) || p(i) > max_corner(i)) + { + return false; + } + } + return true; +} + +template +inline void igl::WindingNumberAABB::compute_min_max_corners() +{ + using namespace std; + // initialize corners + for(int d = 0;d::infinity(); + max_corner[d] = -numeric_limits::infinity(); + } + + this->center = Point(0,0,0); + // Loop over facets + for(int i = 0;igetF().rows();i++) + { + for(int j = 0;jgetF().cols();j++) + { + for(int d = 0;dgetV()(this->getF()(i,j),d) < min_corner[d] ? + this->getV()(this->getF()(i,j),d) : min_corner[d]; + max_corner[d] = + this->getV()(this->getF()(i,j),d) > max_corner[d] ? + this->getV()(this->getF()(i,j),d) : max_corner[d]; + } + // This is biased toward vertices incident on more than one face, but + // perhaps that's good + this->center += this->getV().row(this->getF()(i,j)); + } + } + // Average + this->center.array() /= this->getF().size(); + + //cout<<"min_corner: "<min_corner.transpose()<center.transpose()<max_corner.transpose()<max_corner + this->min_corner)*0.5).transpose()<radius = (max_corner-min_corner).norm()/2.0; +} + +template +inline typename DerivedV::Scalar +igl::WindingNumberAABB::max_abs_winding_number(const Point & p) const +{ + using namespace std; + // Only valid if not inside + if(inside(p)) + { + return numeric_limits::infinity(); + } + // Q: we know the total positive area so what's the most this could project + // to? Remember it could be layered in the same direction. + return numeric_limits::infinity(); +} + +template +inline typename DerivedV::Scalar + igl::WindingNumberAABB::max_simple_abs_winding_number( + const Point & p) const +{ + using namespace std; + using namespace Eigen; + // Only valid if not inside + if(inside(p)) + { + return numeric_limits::infinity(); + } + // Max simple is the same as sum of positive winding number contributions of + // bounding box + + // begin precomputation + //MatrixXd BV((int)pow(2,3),3); + typedef + Eigen::Matrix + MatrixXS; + typedef + Eigen::Matrix + MatrixXF; + MatrixXS BV((int)(1<<3),3); + BV << + min_corner[0],min_corner[1],min_corner[2], + min_corner[0],min_corner[1],max_corner[2], + min_corner[0],max_corner[1],min_corner[2], + min_corner[0],max_corner[1],max_corner[2], + max_corner[0],min_corner[1],min_corner[2], + max_corner[0],min_corner[1],max_corner[2], + max_corner[0],max_corner[1],min_corner[2], + max_corner[0],max_corner[1],max_corner[2]; + MatrixXF BF(2*2*3,3); + BF << + 0,6,4, + 0,2,6, + 0,3,2, + 0,1,3, + 2,7,6, + 2,3,7, + 4,6,7, + 4,7,5, + 0,4,5, + 0,5,1, + 1,5,7, + 1,7,3; + MatrixXS BFN; + per_face_normals(BV,BF,BFN); + // end of precomputation + + // Only keep those with positive dot products + MatrixXF PBF(BF.rows(),BF.cols()); + int pbfi = 0; + Point p2c = 0.5*(min_corner+max_corner)-p; + for(int i = 0;i 0) + { + PBF.row(pbfi++) = BF.row(i); + } + } + PBF.conservativeResize(pbfi,PBF.cols()); + return igl::winding_number(BV,PBF,p); +} + +#endif diff --git a/src/igl/WindingNumberMethod.h b/src/igl/WindingNumberMethod.h new file mode 100644 index 0000000000..3bb9062b15 --- /dev/null +++ b/src/igl/WindingNumberMethod.h @@ -0,0 +1,23 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WINDINGNUMBERMETHOD_H +#define IGL_WINDINGNUMBERMETHOD_H +namespace igl +{ + // EXACT_WINDING_NUMBER_METHOD exact hierarchical evaluation + // APPROX_SIMPLE_WINDING_NUMBER_METHOD poor approximation + // APPROX_CACHE_WINDING_NUMBER_METHOD another poor approximation + enum WindingNumberMethod + { + EXACT_WINDING_NUMBER_METHOD = 0, + APPROX_SIMPLE_WINDING_NUMBER_METHOD = 1, + APPROX_CACHE_WINDING_NUMBER_METHOD = 2, + NUM_WINDING_NUMBER_METHODS = 3 + }; +} +#endif diff --git a/src/igl/WindingNumberTree.h b/src/igl/WindingNumberTree.h new file mode 100644 index 0000000000..317ad94426 --- /dev/null +++ b/src/igl/WindingNumberTree.h @@ -0,0 +1,503 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WINDINGNUMBERTREE_H +#define IGL_WINDINGNUMBERTREE_H +#include +#include +#include +#include "WindingNumberMethod.h" + +namespace igl +{ + // Space partitioning tree for computing winding number hierarchically. + // + // Templates: + // Point type for points in space, e.g. Eigen::Vector3d + template < + typename Point, + typename DerivedV, + typename DerivedF > + class WindingNumberTree + { + public: + // Method to use (see enum above) + //static double min_max_w; + static std::map< + std::pair, + typename DerivedV::Scalar> + cached; + // This is only need to fill in references, it should never actually be touched + // and shouldn't cause race conditions. (This is a hack, but I think it's "safe") + static DerivedV dummyV; + protected: + WindingNumberMethod method; + const WindingNumberTree * parent; + std::list children; + typedef + Eigen::Matrix + MatrixXS; + typedef + Eigen::Matrix + MatrixXF; + //// List of boundary edges (recall edges are vertices in 2d) + //const Eigen::MatrixXi boundary; + // Base mesh vertices + DerivedV & V; + // Base mesh vertices with duplicates removed + MatrixXS SV; + // Facets in this bounding volume + MatrixXF F; + // Tessellated boundary curve + MatrixXF cap; + // Upper Bound on radius of enclosing ball + typename DerivedV::Scalar radius; + // (Approximate) center (of mass) + Point center; + public: + inline WindingNumberTree(); + // For root + inline WindingNumberTree( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F); + // For chilluns + inline WindingNumberTree( + const WindingNumberTree & parent, + const Eigen::MatrixBase & F); + inline virtual ~WindingNumberTree(); + inline void delete_children(); + inline virtual void set_mesh( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F); + // Set method + inline void set_method( const WindingNumberMethod & m); + public: + inline const DerivedV & getV() const; + inline const MatrixXF & getF() const; + inline const MatrixXF & getcap() const; + // Grow the Tree recursively + inline virtual void grow(); + // Determine whether a given point is inside the bounding + // + // Inputs: + // p query point + // Returns true if the point p is inside this bounding volume + inline virtual bool inside(const Point & p) const; + // Compute the (partial) winding number of a given point p + // According to method + // + // Inputs: + // p query point + // Returns winding number + inline typename DerivedV::Scalar winding_number(const Point & p) const; + // Same as above, but always computes winding number using exact method + // (sum over every facet) + inline typename DerivedV::Scalar winding_number_all(const Point & p) const; + // Same as above, but always computes using sum over tessllated boundary + inline typename DerivedV::Scalar winding_number_boundary(const Point & p) const; + //// Same as winding_number above, but if max_simple_abs_winding_number is + //// less than some threshold min_max_w just return 0 (colloquially the "fast + //// multipole method) + //// + //// + //// Inputs: + //// p query point + //// min_max_w minimum max simple w to be processed + //// Returns approximate winding number + //double winding_number_approx_simple( + // const Point & p, + // const double min_max_w); + // Print contents of Tree + // + // Optional input: + // tab tab to show depth + inline void print(const char * tab=""); + // Determine max absolute winding number + // + // Inputs: + // p query point + // Returns max winding number of + inline virtual typename DerivedV::Scalar max_abs_winding_number(const Point & p) const; + // Same as above, but stronger assumptions on (V,F). Assumes (V,F) is a + // simple polyhedron + inline virtual typename DerivedV::Scalar max_simple_abs_winding_number(const Point & p) const; + // Compute or read cached winding number for point p with respect to mesh + // in bounding box, recursing according to approximation criteria + // + // Inputs: + // p query point + // that WindingNumberTree containing mesh w.r.t. which we're computing w.n. + // Returns cached winding number + inline virtual typename DerivedV::Scalar cached_winding_number(const WindingNumberTree & that, const Point & p) const; + }; +} + +// Implementation + +#include "WindingNumberTree.h" +#include "winding_number.h" +#include "triangle_fan.h" +#include "exterior_edges.h" + +#include +#include + +#include +#include + +//template +//WindingNumberMethod WindingNumberTree::method = EXACT_WINDING_NUMBER_METHOD; +//template +//double WindingNumberTree::min_max_w = 0; +template +std::map< std::pair*,const igl::WindingNumberTree*>, typename DerivedV::Scalar> + igl::WindingNumberTree::cached; + +template +inline igl::WindingNumberTree::WindingNumberTree(): + method(EXACT_WINDING_NUMBER_METHOD), + parent(NULL), + V(dummyV), + SV(), + F(), + //boundary(igl::boundary_facets(F)) + cap(), + radius(std::numeric_limits::infinity()), + center(0,0,0) +{ +} + +template +inline igl::WindingNumberTree::WindingNumberTree( + const Eigen::MatrixBase & _V, + const Eigen::MatrixBase & _F): + method(EXACT_WINDING_NUMBER_METHOD), + parent(NULL), + V(dummyV), + SV(), + F(), + //boundary(igl::boundary_facets(F)) + cap(), + radius(std::numeric_limits::infinity()), + center(0,0,0) +{ + set_mesh(_V,_F); +} + +template +inline void igl::WindingNumberTree::set_mesh( + const Eigen::MatrixBase & _V, + const Eigen::MatrixBase & _F) +{ + using namespace std; + // Remove any exactly duplicate vertices + // Q: Can this ever increase the complexity of the boundary? + // Q: Would we gain even more by remove almost exactly duplicate vertices? + MatrixXF SF,SVI,SVJ; + igl::remove_duplicate_vertices(_V,_F,0.0,SV,SVI,SVJ,F); + triangle_fan(igl::exterior_edges(F),cap); + V = SV; +} + +template +inline igl::WindingNumberTree::WindingNumberTree( + const igl::WindingNumberTree & parent, + const Eigen::MatrixBase & _F): + method(parent.method), + parent(&parent), + V(parent.V), + SV(), + F(_F), + cap(triangle_fan(igl::exterior_edges(_F))) +{ +} + +template +inline igl::WindingNumberTree::~WindingNumberTree() +{ + delete_children(); +} + +template +inline void igl::WindingNumberTree::delete_children() +{ + using namespace std; + // Delete children + typename list* >::iterator cit = children.begin(); + while(cit != children.end()) + { + // clear the memory of this item + delete (* cit); + // erase from list, returns next element in iterator + cit = children.erase(cit); + } +} + +template +inline void igl::WindingNumberTree::set_method(const WindingNumberMethod & m) +{ + this->method = m; + for(auto child : children) + { + child->set_method(m); + } +} + +template +inline const DerivedV & igl::WindingNumberTree::getV() const +{ + return V; +} + +template +inline const typename igl::WindingNumberTree::MatrixXF& + igl::WindingNumberTree::getF() const +{ + return F; +} + +template +inline const typename igl::WindingNumberTree::MatrixXF& + igl::WindingNumberTree::getcap() const +{ + return cap; +} + +template +inline void igl::WindingNumberTree::grow() +{ + // Don't grow + return; +} + +template +inline bool igl::WindingNumberTree::inside(const Point & /*p*/) const +{ + return true; +} + +template +inline typename DerivedV::Scalar +igl::WindingNumberTree::winding_number(const Point & p) const +{ + using namespace std; + //cout<<"+"<0) + { + // Recurse on each child and accumulate + typename DerivedV::Scalar sum = 0; + for( + typename list* >::const_iterator cit = children.begin(); + cit != children.end(); + cit++) + { + switch(method) + { + case EXACT_WINDING_NUMBER_METHOD: + sum += (*cit)->winding_number(p); + break; + case APPROX_SIMPLE_WINDING_NUMBER_METHOD: + case APPROX_CACHE_WINDING_NUMBER_METHOD: + //if((*cit)->max_simple_abs_winding_number(p) > min_max_w) + //{ + sum += (*cit)->winding_number(p); + //} + break; + default: + assert(false); + break; + } + } + return sum; + }else + { + return winding_number_all(p); + } + }else{ + // Otherwise we can just consider boundary + // Q: If we using the "multipole" method should we also subdivide the + // boundary case? + if((cap.rows() - 2) < F.rows()) + { + switch(method) + { + case EXACT_WINDING_NUMBER_METHOD: + return winding_number_boundary(p); + case APPROX_SIMPLE_WINDING_NUMBER_METHOD: + { + typename DerivedV::Scalar dist = (p-center).norm(); + // Radius is already an overestimate of inside + if(dist>1.0*radius) + { + return 0; + }else + { + return winding_number_boundary(p); + } + } + case APPROX_CACHE_WINDING_NUMBER_METHOD: + { + return parent->cached_winding_number(*this,p); + } + default: assert(false);break; + } + }else + { + // doesn't pay off to use boundary + return winding_number_all(p); + } + } + return 0; +} + +template +inline typename DerivedV::Scalar + igl::WindingNumberTree::winding_number_all(const Point & p) const +{ + return igl::winding_number(V,F,p); +} + +template +inline typename DerivedV::Scalar +igl::WindingNumberTree::winding_number_boundary(const Point & p) const +{ + using namespace Eigen; + using namespace std; + return igl::winding_number(V,cap,p); +} + +//template +//inline double igl::WindingNumberTree::winding_number_approx_simple( +// const Point & p, +// const double min_max_w) +//{ +// using namespace std; +// if(max_simple_abs_winding_number(p) > min_max_w) +// { +// return winding_number(p); +// }else +// { +// cout<<"Skipped! "< +inline void igl::WindingNumberTree::print(const char * tab) +{ + using namespace std; + // Print all facets + cout<* >::iterator cit = children.begin(); + cit != children.end(); + cit++) + { + cout<<","<print((string(tab)+"").c_str()); + } +} + +template +inline typename DerivedV::Scalar +igl::WindingNumberTree::max_abs_winding_number(const Point & /*p*/) const +{ + return std::numeric_limits::infinity(); +} + +template +inline typename DerivedV::Scalar +igl::WindingNumberTree::max_simple_abs_winding_number( + const Point & /*p*/) const +{ + using namespace std; + return numeric_limits::infinity(); +} + +template +inline typename DerivedV::Scalar +igl::WindingNumberTree::cached_winding_number( + const igl::WindingNumberTree & that, + const Point & p) const +{ + using namespace std; + // Simple metric for `is_far` + // + // this that + // -------- + // ----- / | \ . + // / r \ / R \ . + // | p ! | | ! | + // \_____/ \ / + // \________/ + // + // + // a = angle formed by trapazoid formed by raising sides with lengths r and R + // at respective centers. + // + // a = atan2(R-r,d), where d is the distance between centers + + // That should be bigger (what about parent? what about sister?) + bool is_far = this->radiusradius, + (that.center - this->center).norm()); + assert(a>0); + is_far = (a this_that(this,&that); + // Need to compute it for first time? + if(cached.count(this_that)==0) + { + cached[this_that] = + that.winding_number_boundary(this->center); + } + return cached[this_that]; + }else if(children.size() == 0) + { + // not far and hierarchy ended too soon: can't use cache + return that.winding_number_boundary(p); + }else + { + for( + typename list* >::const_iterator cit = children.begin(); + cit != children.end(); + cit++) + { + if((*cit)->inside(p)) + { + return (*cit)->cached_winding_number(that,p); + } + } + // Not inside any children? This can totally happen because bounding boxes + // are set to bound contained facets. So sibilings may overlap and their + // union may not contain their parent (though, their union is certainly a + // subset of their parent). + assert(false); + } + return 0; +} + +// Explicit instantiation of static variable +template < + typename Point, + typename DerivedV, + typename DerivedF > +DerivedV igl::WindingNumberTree::dummyV; + +#endif diff --git a/src/igl/ZERO.h b/src/igl/ZERO.h new file mode 100644 index 0000000000..bd28299056 --- /dev/null +++ b/src/igl/ZERO.h @@ -0,0 +1,21 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ZERO_H +#define IGL_ZERO_H +namespace igl +{ + // Often one needs a reference to a dummy variable containing zero as its + // value, for example when using AntTweakBar's + // TwSetParam( "3D View", "opened", TW_PARAM_INT32, 1, &INT_ZERO); + const char CHAR_ZERO = 0; + const int INT_ZERO = 0; + const unsigned int UNSIGNED_INT_ZERO = 0; + const double DOUBLE_ZERO = 0; + const float FLOAT_ZERO = 0; +} +#endif diff --git a/src/igl/active_set.cpp b/src/igl/active_set.cpp new file mode 100755 index 0000000000..fd5dfba887 --- /dev/null +++ b/src/igl/active_set.cpp @@ -0,0 +1,370 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "active_set.h" +#include "min_quad_with_fixed.h" +#include "slice.h" +#include "slice_into.h" +#include "cat.h" +//#include "matlab_format.h" + +#include +#include +#include + +template < + typename AT, + typename DerivedB, + typename Derivedknown, + typename DerivedY, + typename AeqT, + typename DerivedBeq, + typename AieqT, + typename DerivedBieq, + typename Derivedlx, + typename Derivedux, + typename DerivedZ + > +IGL_INLINE igl::SolverStatus igl::active_set( + const Eigen::SparseMatrix& A, + const Eigen::PlainObjectBase & B, + const Eigen::PlainObjectBase & known, + const Eigen::PlainObjectBase & Y, + const Eigen::SparseMatrix& Aeq, + const Eigen::PlainObjectBase & Beq, + const Eigen::SparseMatrix& Aieq, + const Eigen::PlainObjectBase & Bieq, + const Eigen::PlainObjectBase & p_lx, + const Eigen::PlainObjectBase & p_ux, + const igl::active_set_params & params, + Eigen::PlainObjectBase & Z + ) +{ +//#define ACTIVE_SET_CPP_DEBUG +#if defined(ACTIVE_SET_CPP_DEBUG) && !defined(_MSC_VER) +# warning "ACTIVE_SET_CPP_DEBUG" +#endif + using namespace Eigen; + using namespace std; + SolverStatus ret = SOLVER_STATUS_ERROR; + const int n = A.rows(); + assert(n == A.cols() && "A must be square"); + // Discard const qualifiers + //if(B.size() == 0) + //{ + // B = DerivedB::Zero(n,1); + //} + assert(n == B.rows() && "B.rows() must match A.rows()"); + assert(B.cols() == 1 && "B must be a column vector"); + assert(Y.cols() == 1 && "Y must be a column vector"); + assert((Aeq.size() == 0 && Beq.size() == 0) || Aeq.cols() == n); + assert((Aeq.size() == 0 && Beq.size() == 0) || Aeq.rows() == Beq.rows()); + assert((Aeq.size() == 0 && Beq.size() == 0) || Beq.cols() == 1); + assert((Aieq.size() == 0 && Bieq.size() == 0) || Aieq.cols() == n); + assert((Aieq.size() == 0 && Bieq.size() == 0) || Aieq.rows() == Bieq.rows()); + assert((Aieq.size() == 0 && Bieq.size() == 0) || Bieq.cols() == 1); + Eigen::Matrix lx; + Eigen::Matrix ux; + if(p_lx.size() == 0) + { + lx = Derivedlx::Constant( + n,1,-numeric_limits::max()); + }else + { + lx = p_lx; + } + if(p_ux.size() == 0) + { + ux = Derivedux::Constant( + n,1,numeric_limits::max()); + }else + { + ux = p_ux; + } + assert(lx.rows() == n && "lx must have n rows"); + assert(ux.rows() == n && "ux must have n rows"); + assert(ux.cols() == 1 && "lx must be a column vector"); + assert(lx.cols() == 1 && "ux must be a column vector"); + assert((ux.array()-lx.array()).minCoeff() > 0 && "ux(i) must be > lx(i)"); + if(Z.size() != 0) + { + // Initial guess should have correct size + assert(Z.rows() == n && "Z must have n rows"); + assert(Z.cols() == 1 && "Z must be a column vector"); + } + assert(known.cols() == 1 && "known must be a column vector"); + // Number of knowns + const int nk = known.size(); + + // Initialize active sets + typedef int BOOL; +#define TRUE 1 +#define FALSE 0 + Matrix as_lx = Matrix::Constant(n,1,FALSE); + Matrix as_ux = Matrix::Constant(n,1,FALSE); + Matrix as_ieq = Matrix::Constant(Aieq.rows(),1,FALSE); + + // Keep track of previous Z for comparison + DerivedZ old_Z; + old_Z = DerivedZ::Constant( + n,1,numeric_limits::max()); + + int iter = 0; + while(true) + { +#ifdef ACTIVE_SET_CPP_DEBUG + cout<<"Iteration: "< 0) + { + for(int z = 0;z < n;z++) + { + if(Z(z) < lx(z)) + { + new_as_lx += (as_lx(z)?0:1); + //new_as_lx++; + as_lx(z) = TRUE; + } + if(Z(z) > ux(z)) + { + new_as_ux += (as_ux(z)?0:1); + //new_as_ux++; + as_ux(z) = TRUE; + } + } + if(Aieq.rows() > 0) + { + DerivedZ AieqZ; + AieqZ = Aieq*Z; + for(int a = 0;a Bieq(a)) + { + new_as_ieq += (as_ieq(a)?0:1); + as_ieq(a) = TRUE; + } + } + } +#ifdef ACTIVE_SET_CPP_DEBUG + cout<<" new_as_lx: "< Aeq_i,Aieq_i; + slice(Aieq,as_ieq_list,1,Aieq_i); + // Append to equality constraints + cat(1,Aeq,Aieq_i,Aeq_i); + + + min_quad_with_fixed_data data; +#ifndef NDEBUG + { + // NO DUPES! + Matrix fixed = Matrix::Constant(n,1,FALSE); + for(int k = 0;k 0 && Aeq_i.rows() > Aeq.rows()) + { + cerr<<" *Are you sure rows of [Aeq;Aieq] are linearly independent?*"<< + endl; + } + ret = SOLVER_STATUS_ERROR; + break; + } +#ifdef ACTIVE_SET_CPP_DEBUG + cout<<" min_quad_with_fixed_solve"< Ak; + // Slow + slice(A,known_i,1,Ak); + DerivedB Bk; + slice(B,known_i,Bk); + MatrixXd Lambda_known_i = -(0.5*Ak*Z + 0.5*Bk); + // reverse the lambda values for lx + Lambda_known_i.block(nk,0,as_lx_count,1) = + (-1*Lambda_known_i.block(nk,0,as_lx_count,1)).eval(); + + // Extract Lagrange multipliers for Aieq_i (always at back of sol) + VectorXd Lambda_Aieq_i(Aieq_i.rows(),1); + for(int l = 0;l0 && iter>=params.max_iter) + { + ret = SOLVER_STATUS_MAX_ITER; + break; + } + + } + + return ret; +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template igl::SolverStatus igl::active_set, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix, double, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::SparseMatrix const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::SparseMatrix const&, Eigen::PlainObjectBase > const&, Eigen::SparseMatrix const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, igl::active_set_params const&, Eigen::PlainObjectBase >&); +template igl::SolverStatus igl::active_set, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix, double, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::SparseMatrix const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::SparseMatrix const&, Eigen::PlainObjectBase > const&, Eigen::SparseMatrix const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, igl::active_set_params const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/active_set.h b/src/igl/active_set.h new file mode 100644 index 0000000000..b82d0e52bc --- /dev/null +++ b/src/igl/active_set.h @@ -0,0 +1,111 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ACTIVE_SET_H +#define IGL_ACTIVE_SET_H + +#include "igl_inline.h" +#include "SolverStatus.h" +#include +#include + +namespace igl +{ + struct active_set_params; + // Known Bugs: rows of [Aeq;Aieq] **must** be linearly independent. Should be + // using QR decomposition otherwise: + // http://www.okstate.edu/sas/v8/sashtml/ormp/chap5/sect32.htm + // + // ACTIVE_SET Minimize quadratic energy + // + // 0.5*Z'*A*Z + Z'*B + C with constraints + // + // that Z(known) = Y, optionally also subject to the constraints Aeq*Z = Beq, + // and further optionally subject to the linear inequality constraints that + // Aieq*Z <= Bieq and constant inequality constraints lx <= x <= ux + // + // Inputs: + // A n by n matrix of quadratic coefficients + // B n by 1 column of linear coefficients + // known list of indices to known rows in Z + // Y list of fixed values corresponding to known rows in Z + // Aeq meq by n list of linear equality constraint coefficients + // Beq meq by 1 list of linear equality constraint constant values + // Aieq mieq by n list of linear inequality constraint coefficients + // Bieq mieq by 1 list of linear inequality constraint constant values + // lx n by 1 list of lower bounds [] implies -Inf + // ux n by 1 list of upper bounds [] implies Inf + // params struct of additional parameters (see below) + // Z if not empty, is taken to be an n by 1 list of initial guess values + // (see output) + // Outputs: + // Z n by 1 list of solution values + // Returns true on success, false on error + // + // Benchmark: For a harmonic solve on a mesh with 325K facets, matlab 2.2 + // secs, igl/min_quad_with_fixed.h 7.1 secs + // + template < + typename AT, + typename DerivedB, + typename Derivedknown, + typename DerivedY, + typename AeqT, + typename DerivedBeq, + typename AieqT, + typename DerivedBieq, + typename Derivedlx, + typename Derivedux, + typename DerivedZ + > + IGL_INLINE igl::SolverStatus active_set( + const Eigen::SparseMatrix& A, + const Eigen::PlainObjectBase & B, + const Eigen::PlainObjectBase & known, + const Eigen::PlainObjectBase & Y, + const Eigen::SparseMatrix& Aeq, + const Eigen::PlainObjectBase & Beq, + const Eigen::SparseMatrix& Aieq, + const Eigen::PlainObjectBase & Bieq, + const Eigen::PlainObjectBase & lx, + const Eigen::PlainObjectBase & ux, + const igl::active_set_params & params, + Eigen::PlainObjectBase & Z + ); +}; + +#include "EPS.h" +struct igl::active_set_params +{ + // Input parameters for active_set: + // Auu_pd whether Auu is positive definite {false} + // max_iter Maximum number of iterations (0 = Infinity, {100}) + // inactive_threshold Threshold on Lagrange multiplier values to determine + // whether to keep constraints active {EPS} + // constraint_threshold Threshold on whether constraints are violated (0 + // is perfect) {EPS} + // solution_diff_threshold Threshold on the squared norm of the difference + // between two consecutive solutions {EPS} + bool Auu_pd; + int max_iter; + double inactive_threshold; + double constraint_threshold; + double solution_diff_threshold; + active_set_params(): + Auu_pd(false), + max_iter(100), + inactive_threshold(igl::DOUBLE_EPS), + constraint_threshold(igl::DOUBLE_EPS), + solution_diff_threshold(igl::DOUBLE_EPS) + {}; +}; + +#ifndef IGL_STATIC_LIBRARY +# include "active_set.cpp" +#endif + +#endif diff --git a/src/igl/adjacency_list.cpp b/src/igl/adjacency_list.cpp new file mode 100644 index 0000000000..1fb61ebf17 --- /dev/null +++ b/src/igl/adjacency_list.cpp @@ -0,0 +1,168 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "adjacency_list.h" + +#include "verbose.h" +#include + +template +IGL_INLINE void igl::adjacency_list( + const Eigen::PlainObjectBase & F, + std::vector >& A, + bool sorted) +{ + A.clear(); + A.resize(F.maxCoeff()+1); + + // Loop over faces + for(int i = 0;i d + int s = F(i,j); + int d = F(i,(j+1)%F.cols()); + A.at(s).push_back(d); + A.at(d).push_back(s); + } + } + + // Remove duplicates + for(int i=0; i<(int)A.size();++i) + { + std::sort(A[i].begin(), A[i].end()); + A[i].erase(std::unique(A[i].begin(), A[i].end()), A[i].end()); + } + + // If needed, sort every VV + if (sorted) + { + // Loop over faces + + // for every vertex v store a set of ordered edges not incident to v that belongs to triangle incident on v. + std::vector > > SR; + SR.resize(A.size()); + + for(int i = 0;i d + int s = F(i,j); + int d = F(i,(j+1)%F.cols()); + // Get index of opposing vertex v + int v = F(i,(j+2)%F.cols()); + + std::vector e(2); + e[0] = d; + e[1] = v; + SR[s].push_back(e); + } + } + + for(int v=0; v<(int)SR.size();++v) + { + std::vector& vv = A.at(v); + std::vector >& sr = SR[v]; + + std::vector > pn = sr; + + // Compute previous/next for every element in sr + for(int i=0;i<(int)sr.size();++i) + { + int a = sr[i][0]; + int b = sr[i][1]; + + // search for previous + int p = -1; + for(int j=0;j<(int)sr.size();++j) + if(sr[j][1] == a) + p = j; + pn[i][0] = p; + + // search for next + int n = -1; + for(int j=0;j<(int)sr.size();++j) + if(sr[j][0] == b) + n = j; + pn[i][1] = n; + + } + + // assume manifoldness (look for beginning of a single chain) + int c = 0; + for(int j=0; j<=(int)sr.size();++j) + if (pn[c][0] != -1) + c = pn[c][0]; + + if (pn[c][0] == -1) // border case + { + // finally produce the new vv relation + for(int j=0; j<(int)sr.size();++j) + { + vv[j] = sr[c][0]; + if (pn[c][1] != -1) + c = pn[c][1]; + } + vv.back() = sr[c][1]; + } + else + { + // finally produce the new vv relation + for(int j=0; j<(int)sr.size();++j) + { + vv[j] = sr[c][0]; + + c = pn[c][1]; + } + } + } + } +} + +template +IGL_INLINE void igl::adjacency_list( + const std::vector > & F, + std::vector >& A) +{ + A.clear(); + A.resize(F.maxCoeff()+1); + + // Loop over faces + for(int i = 0;i d + int s = F(i,j); + int d = F(i,(j+1)%F[i].size()); + A.at(s).push_back(d); + A.at(d).push_back(s); + } + } + + // Remove duplicates + for(int i=0; i<(int)A.size();++i) + { + std::sort(A[i].begin(), A[i].end()); + A[i].erase(std::unique(A[i].begin(), A[i].end()), A[i].end()); + } + +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::adjacency_list, int>(Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > >&, bool); +// generated by autoexplicit.sh +template void igl::adjacency_list, int>(Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > >&, bool); +template void igl::adjacency_list, int>(Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > >&, bool); +#endif diff --git a/src/igl/adjacency_list.h b/src/igl/adjacency_list.h new file mode 100644 index 0000000000..7438cde428 --- /dev/null +++ b/src/igl/adjacency_list.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ADJACENCY_LIST_H +#define IGL_ADJACENCY_LIST_H +#include "igl_inline.h" + +#include +#include +#include +namespace igl +{ + // Constructs the graph adjacency list of a given mesh (V,F) + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Inputs: + // F #F by dim list of mesh faces (must be triangles) + // sorted flag that indicates if the list should be sorted counter-clockwise + // Outputs: + // A vector > containing at row i the adjacent vertices of vertex i + // + // Example: + // // Mesh in (V,F) + // vector > A; + // adjacency_list(F,A); + // + // See also: edges, cotmatrix, diag + template + IGL_INLINE void adjacency_list( + const Eigen::PlainObjectBase & F, + std::vector >& A, + bool sorted = false); + + // Variant that accepts polygonal faces. + // Each element of F is a set of indices of a polygonal face. + template + IGL_INLINE void adjacency_list( + const std::vector > & F, + std::vector >& A); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "adjacency_list.cpp" +#endif + +#endif diff --git a/src/igl/adjacency_matrix.cpp b/src/igl/adjacency_matrix.cpp new file mode 100644 index 0000000000..f6db955c20 --- /dev/null +++ b/src/igl/adjacency_matrix.cpp @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "adjacency_matrix.h" + +#include "verbose.h" + +#include + +template +IGL_INLINE void igl::adjacency_matrix( + const Eigen::MatrixBase & F, + Eigen::SparseMatrix& A) +{ + using namespace std; + using namespace Eigen; + typedef typename DerivedF::Scalar Index; + + typedef Triplet IJV; + vector ijv; + ijv.reserve(F.size()*2); + // Loop over **simplex** (i.e., **not quad**) + for(int i = 0;i d + Index s = F(i,j); + Index d = F(i,k); + ijv.push_back(IJV(s,d,1)); + ijv.push_back(IJV(d,s,1)); + } + } + + const Index n = F.maxCoeff()+1; + A.resize(n,n); + switch(F.cols()) + { + case 3: + A.reserve(6*(F.maxCoeff()+1)); + break; + case 4: + A.reserve(26*(F.maxCoeff()+1)); + break; + } + A.setFromTriplets(ijv.begin(),ijv.end()); + + // Force all non-zeros to be one + + // Iterate over outside + for(int k=0; k::InnerIterator it (A,k); it; ++it) + { + assert(it.value() != 0); + A.coeffRef(it.row(),it.col()) = 1; + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::adjacency_matrix, bool>(Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +template void igl::adjacency_matrix, double>(Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +template void igl::adjacency_matrix, int>(Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/adjacency_matrix.h b/src/igl/adjacency_matrix.h new file mode 100644 index 0000000000..b117bccf7d --- /dev/null +++ b/src/igl/adjacency_matrix.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ADJACENCY_MATRIX_H +#define IGL_ADJACENCY_MATRIX_H +#include "igl_inline.h" + +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include + +namespace igl +{ + // Constructs the graph adjacency matrix of a given mesh (V,F) + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Inputs: + // F #F by dim list of mesh simplices + // Outputs: + // A max(F) by max(F) cotangent matrix, each row i corresponding to V(i,:) + // + // Example: + // // Mesh in (V,F) + // Eigen::SparseMatrix A; + // adjacency_matrix(F,A); + // // sum each row + // SparseVector Asum; + // sum(A,1,Asum); + // // Convert row sums into diagonal of sparse matrix + // SparseMatrix Adiag; + // diag(Asum,Adiag); + // // Build uniform laplacian + // SparseMatrix U; + // U = A-Adiag; + // + // See also: edges, cotmatrix, diag + template + IGL_INLINE void adjacency_matrix( + const Eigen::MatrixBase & F, + Eigen::SparseMatrix& A); +} + +#ifndef IGL_STATIC_LIBRARY +# include "adjacency_matrix.cpp" +#endif + +#endif diff --git a/src/igl/all.cpp b/src/igl/all.cpp new file mode 100644 index 0000000000..35245eacbe --- /dev/null +++ b/src/igl/all.cpp @@ -0,0 +1,26 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "all.h" +#include "redux.h" + + +template +IGL_INLINE void igl::all( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase& B) +{ + typedef typename DerivedB::Scalar Scalar; + igl::redux(A,dim,[](Scalar a, Scalar b){ return a && b!=0;},B); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif + + diff --git a/src/igl/all.h b/src/igl/all.h new file mode 100644 index 0000000000..6e84fdd52f --- /dev/null +++ b/src/igl/all.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ALL_H +#define IGL_ALL_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // For Dense matrices use: A.rowwise().all() or A.colwise().all() + // + // Inputs: + // A m by n sparse matrix + // dim dimension along which to check for all (1 or 2) + // Output: + // B n-long vector (if dim == 1) + // or + // B m-long vector (if dim == 2) + // + template + IGL_INLINE void all( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase& B); +} +#ifndef IGL_STATIC_LIBRARY +# include "all.cpp" +#endif +#endif + + diff --git a/src/igl/all_edges.cpp b/src/igl/all_edges.cpp new file mode 100644 index 0000000000..eabad3038b --- /dev/null +++ b/src/igl/all_edges.cpp @@ -0,0 +1,26 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "all_edges.h" +#include "oriented_facets.h" + +template +IGL_INLINE void igl::all_edges( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & E) +{ + return oriented_facets(F,E); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::all_edges, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::all_edges, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::all_edges, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::all_edges, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::all_edges, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/all_edges.h b/src/igl/all_edges.h new file mode 100644 index 0000000000..d5fe13a9ac --- /dev/null +++ b/src/igl/all_edges.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ALL_EDGES_H +#define IGL_ALL_EDGES_H +#include "igl_inline.h" +#include +namespace igl +{ + // Deprecated: call oriented_facets instead. + // + // ALL_EDGES Determines all "directed edges" of a given set of simplices. For + // a manifold mesh, this computes all of the half-edges + // + // Inputs: + // F #F by simplex_size list of "faces" + // Outputs: + // E #E by simplex_size-1 list of edges + // + // Note: this is not the same as igl::edges because this includes every + // directed edge including repeats (meaning interior edges on a surface will + // show up once for each direction and non-manifold edges may appear more than + // once for each direction). + template + IGL_INLINE void all_edges( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & E); +} + +#ifndef IGL_STATIC_LIBRARY +# include "all_edges.cpp" +#endif + +#endif diff --git a/src/igl/all_pairs_distances.cpp b/src/igl/all_pairs_distances.cpp new file mode 100644 index 0000000000..608cf9b857 --- /dev/null +++ b/src/igl/all_pairs_distances.cpp @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "all_pairs_distances.h" +#include + +template +IGL_INLINE void igl::all_pairs_distances( + const Mat & V, + const Mat & U, + const bool squared, + Mat & D) +{ + // dimension should be the same + assert(V.cols() == U.cols()); + // resize output + D.resize(V.rows(),U.rows()); + for(int i = 0;i >(Eigen::Matrix const&, Eigen::Matrix const&, bool, Eigen::Matrix&); +#endif diff --git a/src/igl/all_pairs_distances.h b/src/igl/all_pairs_distances.h new file mode 100644 index 0000000000..9acc1b7307 --- /dev/null +++ b/src/igl/all_pairs_distances.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ALL_PAIRS_DISTANCES_H +#define IGL_ALL_PAIRS_DISTANCES_H +#include "igl_inline.h" + +namespace igl +{ + // ALL_PAIRS_DISTANCES compute distances between each point i in V and point j + // in U + // + // D = all_pairs_distances(V,U) + // + // Templates: + // Mat matrix class like MatrixXd + // Inputs: + // V #V by dim list of points + // U #U by dim list of points + // squared whether to return squared distances + // Outputs: + // D #V by #U matrix of distances, where D(i,j) gives the distance or + // squareed distance between V(i,:) and U(j,:) + // + template + IGL_INLINE void all_pairs_distances( + const Mat & V, + const Mat & U, + const bool squared, + Mat & D); +} + +#ifndef IGL_STATIC_LIBRARY +# include "all_pairs_distances.cpp" +#endif + +#endif diff --git a/src/igl/ambient_occlusion.cpp b/src/igl/ambient_occlusion.cpp new file mode 100644 index 0000000000..343f8ae8af --- /dev/null +++ b/src/igl/ambient_occlusion.cpp @@ -0,0 +1,137 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "ambient_occlusion.h" +#include "random_dir.h" +#include "ray_mesh_intersect.h" +#include "EPS.h" +#include "Hit.h" +#include "parallel_for.h" +#include +#include +#include + +template < + typename DerivedP, + typename DerivedN, + typename DerivedS > +IGL_INLINE void igl::ambient_occlusion( + const std::function< + bool( + const Eigen::Vector3f&, + const Eigen::Vector3f&) + > & shoot_ray, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + using namespace Eigen; + const int n = P.rows(); + // Resize output + S.resize(n,1); + // Embree seems to be parallel when constructing but not when tracing rays + const MatrixXf D = random_dir_stratified(num_samples).cast(); + + const auto & inner = [&P,&N,&num_samples,&D,&S,&shoot_ray](const int p) + { + const Vector3f origin = P.row(p).template cast(); + const Vector3f normal = N.row(p).template cast(); + int num_hits = 0; + for(int s = 0;s +IGL_INLINE void igl::ambient_occlusion( + const igl::AABB & aabb, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + const auto & shoot_ray = [&aabb,&V,&F]( + const Eigen::Vector3f& _s, + const Eigen::Vector3f& dir)->bool + { + Eigen::Vector3f s = _s+1e-4*dir; + igl::Hit hit; + return aabb.intersect_ray( + V, + F, + s .cast().eval(), + dir.cast().eval(), + hit); + }; + return ambient_occlusion(shoot_ray,P,N,num_samples,S); + +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > +IGL_INLINE void igl::ambient_occlusion( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + if(F.rows() < 100) + { + // Super naive + const auto & shoot_ray = [&V,&F]( + const Eigen::Vector3f& _s, + const Eigen::Vector3f& dir)->bool + { + Eigen::Vector3f s = _s+1e-4*dir; + igl::Hit hit; + return ray_mesh_intersect(s,dir,V,F,hit); + }; + return ambient_occlusion(shoot_ray,P,N,num_samples,S); + } + AABB aabb; + aabb.init(V,F); + return ambient_occlusion(aabb,V,F,P,N,num_samples,S); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::ambient_occlusion, Eigen::Matrix, Eigen::Matrix >(std::function const&, Eigen::Matrix const&)> const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::ambient_occlusion, Eigen::Matrix, Eigen::Matrix >(std::function const&, Eigen::Matrix const&)> const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::ambient_occlusion, Eigen::Matrix, Eigen::Matrix >(std::function const&, Eigen::Matrix const&)> const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::ambient_occlusion, Eigen::Matrix, Eigen::Matrix >(std::function const&, Eigen::Matrix const&)> const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/ambient_occlusion.h b/src/igl/ambient_occlusion.h new file mode 100644 index 0000000000..1c173121ee --- /dev/null +++ b/src/igl/ambient_occlusion.h @@ -0,0 +1,80 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_AMBIENT_OCCLUSION_H +#define IGL_AMBIENT_OCCLUSION_H +#include "igl_inline.h" +#include "AABB.h" +#include +#include +namespace igl +{ + // Compute ambient occlusion per given point + // + // Inputs: + // shoot_ray function handle that outputs hits of a given ray against a + // mesh (embedded in function handles as captured variable/data) + // P #P by 3 list of origin points + // N #P by 3 list of origin normals + // Outputs: + // S #P list of ambient occlusion values between 1 (fully occluded) and + // 0 (not occluded) + // + template < + typename DerivedP, + typename DerivedN, + typename DerivedS > + IGL_INLINE void ambient_occlusion( + const std::function< + bool( + const Eigen::Vector3f&, + const Eigen::Vector3f&) + > & shoot_ray, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S); + // Inputs: + // AABB axis-aligned bounding box hierarchy around (V,F) + template < + typename DerivedV, + int DIM, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > + IGL_INLINE void ambient_occlusion( + const igl::AABB & aabb, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S); + // Inputs: + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of mesh face indices into V + template < + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > + IGL_INLINE void ambient_occlusion( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S); + +}; +#ifndef IGL_STATIC_LIBRARY +# include "ambient_occlusion.cpp" +#endif + +#endif diff --git a/src/igl/angular_distance.cpp b/src/igl/angular_distance.cpp new file mode 100644 index 0000000000..803c290c97 --- /dev/null +++ b/src/igl/angular_distance.cpp @@ -0,0 +1,20 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "angular_distance.h" +#include +#include +IGL_INLINE double igl::angular_distance( + const Eigen::Quaterniond & A, + const Eigen::Quaterniond & B) +{ + assert(fabs(A.norm()-1) +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ANGULAR_DISTANCE_H +#define IGL_ANGULAR_DISTANCE_H +#include "igl_inline.h" +#include +namespace igl +{ + // The "angular distance" between two unit quaternions is the angle of the + // smallest rotation (treated as an Axis and Angle) that takes A to B. + // + // Inputs: + // A unit quaternion + // B unit quaternion + // Returns angular distance + IGL_INLINE double angular_distance( + const Eigen::Quaterniond & A, + const Eigen::Quaterniond & B); +} + +#ifndef IGL_STATIC_LIBRARY +#include "angular_distance.cpp" +#endif + +#endif diff --git a/src/igl/anttweakbar/ReAntTweakBar.cpp b/src/igl/anttweakbar/ReAntTweakBar.cpp new file mode 100644 index 0000000000..3664c6bcef --- /dev/null +++ b/src/igl/anttweakbar/ReAntTweakBar.cpp @@ -0,0 +1,934 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "ReAntTweakBar.h" + +#include +#include +#include +#include +#include +#include + +// GLOBAL WRAPPERS +namespace +{ + std::map< + TwType,std::pair > + > ReTw_custom_types; +} + +IGL_INLINE TwType igl::anttweakbar::ReTwDefineEnum( + const char *name, + const TwEnumVal *enumValues, + unsigned int nbValues) +{ + using namespace std; + // copy enum valus into vector + std::vector enum_vals; + enum_vals.resize(nbValues); + for(unsigned int j = 0; j >(name,enum_vals); + + return type; +} + +IGL_INLINE TwType igl::anttweakbar::ReTwDefineEnumFromString( + const char * _Name, + const char * _EnumString) +{ + // Taken directly from TwMgr.cpp, just replace TwDefineEnum with + // ReTwDefineEnum + using namespace std; + { + if (_EnumString == NULL) + return ReTwDefineEnum(_Name, NULL, 0); + + // split enumString + stringstream EnumStream(_EnumString); + string Label; + vector Labels; + while( getline(EnumStream, Label, ',') ) { + // trim Label + size_t Start = Label.find_first_not_of(" \n\r\t"); + size_t End = Label.find_last_not_of(" \n\r\t"); + if( Start==string::npos || End==string::npos ) + Label = ""; + else + Label = Label.substr(Start, (End-Start)+1); + // store Label + Labels.push_back(Label); + } + // create TwEnumVal array + vector Vals(Labels.size()); + for( int i=0; i<(int)Labels.size(); i++ ) + { + Vals[i].Value = i; + // Wrong: + //Vals[i].Label = Labels[i].c_str(); + // Allocate char on heap + // http://stackoverflow.com/a/10050258/148668 + char * c_label = new char[Labels[i].length()+1]; + std::strcpy(c_label, Labels[i].c_str()); + Vals[i].Label = c_label; + } + + const TwType type = + ReTwDefineEnum(_Name, Vals.empty() ? + NULL : + &(Vals[0]), (unsigned int)Vals.size()); + return type; + } +} + +namespace +{ + struct ReTwTypeString + { + TwType type; + const char * type_str; + }; + + #define RETW_NUM_DEFAULT_TYPE_STRINGS 23 + ReTwTypeString ReTwDefaultTypeStrings[RETW_NUM_DEFAULT_TYPE_STRINGS] = + { + {TW_TYPE_UNDEF,"TW_TYPE_UNDEF"}, + {TW_TYPE_BOOLCPP,"TW_TYPE_BOOLCPP"}, + {TW_TYPE_BOOL8,"TW_TYPE_BOOL8"}, + {TW_TYPE_BOOL16,"TW_TYPE_BOOL16"}, + {TW_TYPE_BOOL32,"TW_TYPE_BOOL32"}, + {TW_TYPE_CHAR,"TW_TYPE_CHAR"}, + {TW_TYPE_INT8,"TW_TYPE_INT8"}, + {TW_TYPE_UINT8,"TW_TYPE_UINT8"}, + {TW_TYPE_INT16,"TW_TYPE_INT16"}, + {TW_TYPE_UINT16,"TW_TYPE_UINT16"}, + {TW_TYPE_INT32,"TW_TYPE_INT32"}, + {TW_TYPE_UINT32,"TW_TYPE_UINT32"}, + {TW_TYPE_FLOAT,"TW_TYPE_FLOAT"}, + {TW_TYPE_DOUBLE,"TW_TYPE_DOUBLE"}, + {TW_TYPE_COLOR32,"TW_TYPE_COLOR32"}, + {TW_TYPE_COLOR3F,"TW_TYPE_COLOR3F"}, + {TW_TYPE_COLOR4F,"TW_TYPE_COLOR4F"}, + {TW_TYPE_CDSTRING,"TW_TYPE_CDSTRING"}, + {TW_TYPE_STDSTRING,"TW_TYPE_STDSTRING"}, + {TW_TYPE_QUAT4F,"TW_TYPE_QUAT4F"}, + {TW_TYPE_QUAT4D,"TW_TYPE_QUAT4D"}, + {TW_TYPE_DIR3F,"TW_TYPE_DIR3F"}, + {TW_TYPE_DIR3D,"TW_TYPE_DIR3D"} + }; +} + +IGL_INLINE igl::anttweakbar::ReTwBar::ReTwBar(): + bar(NULL), + name(), + rw_items(),cb_items() +{ +} + +IGL_INLINE igl::anttweakbar::ReTwBar::ReTwBar( + const igl::anttweakbar::ReTwBar & that): + bar(that.bar), + name(that.name), + rw_items(that.rw_items), + cb_items(that.cb_items) +{ +} + +IGL_INLINE igl::anttweakbar::ReTwBar & +igl::anttweakbar::ReTwBar::operator=(const igl::anttweakbar::ReTwBar & that) +{ + // check for self assignment + if(this != &that) + { + bar = that.bar; + rw_items = that.rw_items; + cb_items = that.cb_items; + } + return *this; +} + + +// BAR WRAPPERS +IGL_INLINE void igl::anttweakbar::ReTwBar::TwNewBar(const char * _name) +{ + this->bar = ::TwNewBar(_name); + // Alec: This causes trouble (not sure why) in multiple applications + // (medit, puppet) Probably there is some sort of memory corrpution. + // this->name = _name; + // Suspiciously this also fails: + //this->name = "foobar"; +} + +IGL_INLINE int igl::anttweakbar::ReTwBar::TwAddVarRW( + const char *name, + TwType type, + void *var, + const char *def, + const bool record) +{ + int ret = ::TwAddVarRW(this->bar,name,type,var,def); + if(ret && record) + { + rw_items.push_back(ReTwRWItem(name,type,var)); + } + return ret; +} + +IGL_INLINE int igl::anttweakbar::ReTwBar::TwAddVarCB( + const char *name, + TwType type, + TwSetVarCallback setCallback, + TwGetVarCallback getCallback, + void *clientData, + const char *def, + const bool record) +{ + int ret = + ::TwAddVarCB(this->bar,name,type,setCallback,getCallback,clientData,def); + if(ret && record) + { + cb_items.push_back(ReTwCBItem(name,type,setCallback,getCallback,clientData)); + } + return ret; +} + +IGL_INLINE int igl::anttweakbar::ReTwBar::TwAddVarRO( + const char *name, + TwType type, + void *var, + const char *def) +{ + int ret = ::TwAddVarRO(this->bar,name,type,var,def); + // Read only variables are not recorded + //if(ret) + //{ + // rw_items.push_back(ReTwRWItem(name,type,var)); + //} + return ret; +} + +IGL_INLINE int igl::anttweakbar::ReTwBar::TwAddButton( + const char *name, + TwButtonCallback buttonCallback, + void *clientData, + const char *def) +{ + int ret = + ::TwAddButton(this->bar,name,buttonCallback,clientData,def); + // buttons are not recorded + //if(ret) + //{ + // cb_items.push_back(ReTwCBItem(name,type,setCallback,getCallback,clientData)); + //} + return ret; +} + +IGL_INLINE int igl::anttweakbar::ReTwBar::TwSetParam( + const char *varName, + const char *paramName, + TwParamValueType paramValueType, + unsigned int inValueCount, + const void *inValues) +{ + // For now just pass these along + return + ::TwSetParam( + this->bar, + varName, + paramName, + paramValueType, + inValueCount, + inValues); +} + +IGL_INLINE int igl::anttweakbar::ReTwBar::TwGetParam( + const char *varName, + const char *paramName, + TwParamValueType paramValueType, + unsigned int outValueMaxCount, + void *outValues) +{ + return + ::TwGetParam( + this->bar, + varName, + paramName, + paramValueType, + outValueMaxCount, + outValues); +} + +IGL_INLINE int igl::anttweakbar::ReTwBar::TwRefreshBar() +{ + return ::TwRefreshBar(this->bar); +} + +IGL_INLINE int igl::anttweakbar::ReTwBar::TwTerminate() +{ + //std::cout<<"TwTerminate"<::iterator it = rw_items.begin(); + it != rw_items.end(); + it++) + { + std::string s = (*it).name; + const char * name = s.c_str(); + TwType type = (*it).type; + void * var = (*it).var; + fprintf(fp,"%s: %s\n", + name, + get_value_as_string(var,type).c_str()); + } + + char var[REANTTWEAKBAR_MAX_CB_VAR_SIZE]; + // Print all CB variables + for( + std::vector::iterator it = cb_items.begin(); + it != cb_items.end(); + it++) + { + const char * name = it->name.c_str(); + TwType type = it->type; + //TwSetVarCallback setCallback = it->setCallback; + TwGetVarCallback getCallback = it->getCallback; + void * clientData = it->clientData; + // I'm not sure how to do what I want to do. getCallback needs to be sure + // that it can write to var. So var needs to point to a valid and big + // enough chunk of memory + getCallback(var,clientData); + fprintf(fp,"%s: %s\n", + name, + get_value_as_string(var,type).c_str()); + } + + fprintf(fp,"\n"); + + if(file_name != NULL) + { + fclose(fp); + } + // everything succeeded + return true; +} + +IGL_INLINE std::string igl::anttweakbar::ReTwBar::get_value_as_string( + void * var, + TwType type) +{ + std::stringstream sstr; + switch(type) + { + case TW_TYPE_BOOLCPP: + { + sstr << "TW_TYPE_BOOLCPP" << " "; + sstr << *(static_cast(var)); + break; + } + case TW_TYPE_QUAT4D: + { + sstr << "TW_TYPE_QUAT4D" << " "; + // Q: Why does casting to double* work? shouldn't I have to cast to + // double**? + double * q = static_cast(var); + sstr << std::setprecision(15) << q[0] << " " << q[1] << " " << q[2] << " " << q[3]; + break; + } + case TW_TYPE_QUAT4F: + { + sstr << "TW_TYPE_QUAT4F" << " "; + // Q: Why does casting to float* work? shouldn't I have to cast to + // float**? + float * q = static_cast(var); + sstr << q[0] << " " << q[1] << " " << q[2] << " " << q[3]; + break; + } + case TW_TYPE_COLOR4F: + { + sstr << "TW_TYPE_COLOR4F" << " "; + float * c = static_cast(var); + sstr << c[0] << " " << c[1] << " " << c[2] << " " << c[3]; + break; + } + case TW_TYPE_COLOR3F: + { + sstr << "TW_TYPE_COLOR3F" << " "; + float * c = static_cast(var); + sstr << c[0] << " " << c[1] << " " << c[2]; + break; + } + case TW_TYPE_DIR3D: + { + sstr << "TW_TYPE_DIR3D" << " "; + double * d = static_cast(var); + sstr << std::setprecision(15) << d[0] << " " << d[1] << " " << d[2]; + break; + } + case TW_TYPE_DIR3F: + { + sstr << "TW_TYPE_DIR3F" << " "; + float * d = static_cast(var); + sstr << d[0] << " " << d[1] << " " << d[2]; + break; + } + case TW_TYPE_BOOL32: + { + sstr << "TW_TYPE_BOOL32" << " "; + sstr << *(static_cast(var)); + break; + } + case TW_TYPE_UINT8: + { + sstr << "TW_TYPE_UINT8" << " "; + // Cast to int so that it's human readable + sstr << (int)*(static_cast(var)); + break; + } + case TW_TYPE_INT32: + { + sstr << "TW_TYPE_INT32" << " "; + sstr << *(static_cast(var)); + break; + } + case TW_TYPE_UINT32: + { + sstr << "TW_TYPE_UINT32" << " "; + sstr << *(static_cast(var)); + break; + } + case TW_TYPE_FLOAT: + { + sstr << "TW_TYPE_FLOAT" << " "; + sstr << *(static_cast(var)); + break; + } + case TW_TYPE_DOUBLE: + { + sstr << "TW_TYPE_DOUBLE" << " "; + sstr << std::setprecision(15) << *(static_cast(var)); + break; + } + case TW_TYPE_STDSTRING: + { + sstr << "TW_TYPE_STDSTRING" << " "; + std::string *destPtr = static_cast(var); + sstr << destPtr->c_str(); + break; + } + default: + { + using namespace std; + std::map > >::const_iterator iter = + ReTw_custom_types.find(type); + if(iter != ReTw_custom_types.end()) + { + sstr << (*iter).second.first << " "; + int enum_val = *(static_cast(var)); + // try find display name for enum value + std::vector::const_iterator eit = (*iter).second.second.begin(); + bool found = false; + for(;eit<(*iter).second.second.end();eit++) + { + if(enum_val == eit->Value) + { + sstr << eit->Label; + found = true; + break; + } + } + if(!found) + { + sstr << "ERROR_ENUM_VALUE_NOT_DEFINED"; + } + }else + { + sstr << "ERROR_TYPE_NOT_SUPPORTED"; + } + break; + } + } + return sstr.str(); +} + +IGL_INLINE bool igl::anttweakbar::ReTwBar::load(const char *file_name) +{ + FILE * fp; + fp = fopen(file_name,"r"); + + if(fp == NULL) + { + printf("ERROR: not able to open %s for reading...\n",file_name); + return false; + } + + // go through file line by line + char line[REANTTWEAKBAR_MAX_LINE]; + bool still_comments; + char name[REANTTWEAKBAR_MAX_WORD]; + char type_str[REANTTWEAKBAR_MAX_WORD]; + char value_str[REANTTWEAKBAR_MAX_WORD]; + + + // line number + int j = 0; + bool finished = false; + while(true) + { + // Eat comments + still_comments = true; + while(still_comments) + { + if(fgets(line,REANTTWEAKBAR_MAX_LINE,fp) == NULL) + { + finished = true; + break; + } + // Blank lines and lines that begin with # are comments + still_comments = (line[0] == '#' || line[0] == '\n'); + j++; + } + if(finished) + { + break; + } + + sscanf(line,"%[^:]: %s %[^\n]",name,type_str,value_str); + //printf("%s: %s %s\n",name, type_str,value_str); + + TwType type; + if(!type_from_string(type_str,type)) + { + printf("ERROR: %s type not found... Skipping...\n",type_str); + continue; + } + set_value_from_string(name,type,value_str); + + } + + fclose(fp); + + // everything succeeded + return true; +} + +IGL_INLINE bool igl::anttweakbar::ReTwBar::type_from_string( + const char *type_str, TwType & type) +{ + // first check default types + for(int j = 0; j < RETW_NUM_DEFAULT_TYPE_STRINGS; j++) + { + if(strcmp(type_str,ReTwDefaultTypeStrings[j].type_str) == 0) + { + type = ReTwDefaultTypeStrings[j].type; + return true; + break; + } + } + + // then check custom types + std::map< + TwType,std::pair > + >::const_iterator iter = + ReTw_custom_types.begin(); + for(;iter != ReTw_custom_types.end(); iter++) + { + if(strcmp((*iter).second.first,type_str)==0) + { + type = (*iter).first; + return true; + } + } + return false; +} + +bool igl::anttweakbar::ReTwBar::set_value_from_string( + const char * name, + TwType type, + const char * value_str) +{ + void * value = NULL; + // possible value slots + int i; + float v; + double dv; + float f[4]; + double d[4]; + bool b; + unsigned int u; + unsigned char uc; + std::string s; + + // First try to get value from default types + switch(type) + { + case TW_TYPE_BOOLCPP: + { + int ib; + if(sscanf(value_str," %d",&ib) == 1) + { + b = ib!=0; + value = &b; + }else + { + printf("ERROR: Bad value format...\n"); + return false; + } + break; + } + case TW_TYPE_QUAT4D: + //case TW_TYPE_COLOR4D: + { + if(sscanf(value_str," %lf %lf %lf %lf",&d[0],&d[1],&d[2],&d[3]) == 4) + { + value = &d; + }else + { + printf("ERROR: Bad value format...\n"); + return false; + } + break; + } + case TW_TYPE_QUAT4F: + case TW_TYPE_COLOR4F: + { + if(sscanf(value_str," %f %f %f %f",&f[0],&f[1],&f[2],&f[3]) == 4) + { + value = &f; + }else + { + printf("ERROR: Bad value format...\n"); + return false; + } + break; + } + //case TW_TYPE_COLOR3D: + case TW_TYPE_DIR3D: + { + if(sscanf(value_str," %lf %lf %lf",&d[0],&d[1],&d[2]) == 3) + { + value = &d; + }else + { + printf("ERROR: Bad value format...\n"); + return false; + } + break; + } + case TW_TYPE_COLOR3F: + case TW_TYPE_DIR3F: + { + if(sscanf(value_str," %f %f %f",&f[0],&f[1],&f[2]) == 3) + { + value = &f; + }else + { + printf("ERROR: Bad value format...\n"); + return false; + } + break; + } + case TW_TYPE_UINT8: + { + if(sscanf(value_str," %d",&i) == 1) + { + // Cast to unsigned char + uc = (unsigned char) i; + value = &uc; + }else + { + printf("ERROR: Bad value format...\n"); + return false; + } + break; + } + case TW_TYPE_BOOL32: + case TW_TYPE_INT32: + { + if(sscanf(value_str," %d",&i) == 1) + { + value = &i; + }else + { + printf("ERROR: Bad value format...\n"); + return false; + } + break; + } + case TW_TYPE_UINT32: + { + if(sscanf(value_str," %u",&u) == 1) + { + value = &u; + }else + { + printf("ERROR: Bad value format...\n"); + return false; + } + break; + } + case TW_TYPE_FLOAT: + { + if(sscanf(value_str," %f",&v) == 1) + { + value = &v; + }else + { + printf("ERROR: Bad value format...\n"); + return false; + } + break; + } + case TW_TYPE_DOUBLE: + { + if(sscanf(value_str," %lf",&dv) == 1) + { + value = &dv; + }else + { + printf("ERROR: Bad value format...\n"); + return false; + } + break; + } + case TW_TYPE_STDSTRING: + { + s = value_str; + value = &s; + break; + } + default: + // Try to find type in custom enum types + std::map > >::const_iterator iter = + ReTw_custom_types.find(type); + if(iter != ReTw_custom_types.end()) + { + std::vector::const_iterator eit = (*iter).second.second.begin(); + bool found = false; + for(;eit<(*iter).second.second.end();eit++) + { + if(strcmp(value_str,eit->Label) == 0) + { + i = eit->Value; + value = &i; + found = true; + break; + } + } + if(!found) + { + printf("ERROR_ENUM_VALUE_NOT_DEFINED"); + } + }else + { + printf("ERROR_TYPE_NOT_SUPPORTED\n"); + } + + break; + } + + + // Find variable based on name + // First look in RW items + bool item_found = false; + for( + std::vector::iterator it = rw_items.begin(); + it != rw_items.end(); + it++) + { + if(it->name == name) + { + void * var = it->var; + switch(type) + { + case TW_TYPE_BOOLCPP: + { + bool * bvar = static_cast(var); + bool * bvalue = static_cast(value); + *bvar = *bvalue; + break; + } + case TW_TYPE_QUAT4D: + //case TW_TYPE_COLOR4D: + { + double * dvar = static_cast(var); + double * dvalue = static_cast(value); + dvar[0] = dvalue[0]; + dvar[1] = dvalue[1]; + dvar[2] = dvalue[2]; + dvar[3] = dvalue[3]; + break; + } + case TW_TYPE_QUAT4F: + case TW_TYPE_COLOR4F: + { + float * fvar = static_cast(var); + float * fvalue = static_cast(value); + fvar[0] = fvalue[0]; + fvar[1] = fvalue[1]; + fvar[2] = fvalue[2]; + fvar[3] = fvalue[3]; + break; + } + //case TW_TYPE_COLOR3D: + case TW_TYPE_DIR3D: + { + double * dvar = static_cast(var); + double * dvalue = static_cast(value); + dvar[0] = dvalue[0]; + dvar[1] = dvalue[1]; + dvar[2] = dvalue[2]; + break; + } + case TW_TYPE_COLOR3F: + case TW_TYPE_DIR3F: + { + float * fvar = static_cast(var); + float * fvalue = static_cast(value); + fvar[0] = fvalue[0]; + fvar[1] = fvalue[1]; + fvar[2] = fvalue[2]; + break; + } + case TW_TYPE_UINT8: + { + unsigned char * ucvar = static_cast(var); + unsigned char * ucvalue = static_cast(value); + *ucvar = *ucvalue; + break; + } + case TW_TYPE_BOOL32: + case TW_TYPE_INT32: + { + int * ivar = static_cast(var); + int * ivalue = static_cast(value); + *ivar = *ivalue; + break; + } + case TW_TYPE_UINT32: + { + unsigned int * uvar = static_cast(var); + unsigned int * uvalue = static_cast(value); + *uvar = *uvalue; + break; + } + case TW_TYPE_FLOAT: + { + float * fvar = static_cast(var); + float * fvalue = static_cast(value); + *fvar = *fvalue; + break; + } + case TW_TYPE_DOUBLE: + { + double * dvar = static_cast(var); + double * fvalue = static_cast(value); + *dvar = *fvalue; + break; + } + case TW_TYPE_STDSTRING: + { + std::string * svar = static_cast(var); + std::string * svalue = static_cast(value); + *svar = *svalue; + break; + } + default: + // Try to find type in custom enum types + std::map > >::iterator iter = + ReTw_custom_types.find(type); + if(iter != ReTw_custom_types.end()) + { + int * ivar = static_cast(var); + std::vector::iterator eit = (*iter).second.second.begin(); + bool found = false; + for(;eit<(*iter).second.second.end();eit++) + { + if(strcmp(value_str,eit->Label) == 0) + { + *ivar = eit->Value; + found = true; + break; + } + } + if(!found) + { + printf("ERROR_ENUM_VALUE_NOT_DEFINED"); + } + }else + { + printf("ERROR_TYPE_NOT_SUPPORTED\n"); + } + break; + } + item_found = true; + break; + } + } + + // Try looking in CB items + if(!item_found) + { + for( + std::vector::iterator it = cb_items.begin(); + it != cb_items.end(); + it++) + { + if(it->name==name) + { + it->setCallback(value,it->clientData); + item_found = true; + break; + } + } + } + + if(!item_found) + { + printf("ERROR: item '%s' not found\n",name); + } + return true; +} + +IGL_INLINE const std::vector & + igl::anttweakbar::ReTwBar::get_rw_items() +{ + return rw_items; +} + +IGL_INLINE const std::vector & + igl::anttweakbar::ReTwBar::get_cb_items() +{ + return cb_items; +} diff --git a/src/igl/anttweakbar/ReAntTweakBar.h b/src/igl/anttweakbar/ReAntTweakBar.h new file mode 100644 index 0000000000..d1dede9279 --- /dev/null +++ b/src/igl/anttweakbar/ReAntTweakBar.h @@ -0,0 +1,286 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ANTTWEAKBAR_REANTTWEAKBAR_H +#define IGL_ANTTWEAKBAR_REANTTWEAKBAR_H +#include "../igl_inline.h" +// ReAntTweakBar is a minimal wrapper for the AntTweakBar library that allows +// "bars" to be saved and load from disk. Changing your existing app that uses +// AntTweakBar to use ReAntTweakBar is trivial. +// +// Many (but not all) variable types are supported. I'll try to keep track them +// here: +// TW_TYPE_BOOLCPP +// TW_TYPE_QUAT4F +// TW_TYPE_QUAT4D +// TW_TYPE_COLOR4F +// TW_TYPE_COLOR4D +// TW_TYPE_COLOR3F +// TW_TYPE_DIR3F +// TW_TYPE_DIR3D +// TW_TYPE_BOOL32 +// TW_TYPE_INT32 +// TW_TYPE_UINT32 +// TW_TYPE_FLOAT +// TW_TYPE_DOUBLE +// TW_TYPE_UINT8 +// and +// custom TwTypes made with TwDefineEnum +// +// I'm working on adding the rest on an as-needed basis. Adding a new type only +// requires changes in a few places... +// +// +// + +// This allows the user to have a non-global, static installation of +// AntTweakBar +#include +// Instead of including AntTweakBar.h, just define the necessary types +// Types used: +// - TwType +// - TwEnumVal +// - TwSetVarCallback +// - TwGetVarCallback +// - TwBar +// - TwButtonCallback + + +#include +#include + +#define REANTTWEAKBAR_MAX_CB_VAR_SIZE 1000 +// Max line size for reading files +#define REANTTWEAKBAR_MAX_LINE 1000 +#define REANTTWEAKBAR_MAX_WORD 100 + +namespace igl +{ + namespace anttweakbar + { + TwType ReTwDefineEnum( + const char *name, + const TwEnumVal *enumValues, + unsigned int nbValues); + TwType ReTwDefineEnumFromString(const char * name,const char * enumString); + + struct ReTwRWItem + { + //const char * name; + std::string name; + TwType type; + void * var; + // Default constructor + IGL_INLINE ReTwRWItem( + const std::string _name, + TwType _type, + void *_var): + name(_name), + type(_type), + var(_var) + { + } + // Shallow copy constructor + // I solemnly swear it's OK to copy var this way + // Q: Is it really? + IGL_INLINE ReTwRWItem(const ReTwRWItem & that): + name(that.name), + type(that.type), + var(that.var) + { + } + // Shallow assignment + // I solemnly swear it's OK to copy var this way + IGL_INLINE ReTwRWItem & operator=(const ReTwRWItem & that) + { + if(this != &that) + { + this->name = that.name; + this->type = that.type; + this->var = that.var; + } + return *this; + } + }; + + struct ReTwCBItem + { + //const char * name; + std::string name; + TwType type; + TwSetVarCallback setCallback; + TwGetVarCallback getCallback; + void * clientData; + // Default constructor + IGL_INLINE ReTwCBItem( + const std::string _name, + TwType _type, + TwSetVarCallback _setCallback, + TwGetVarCallback _getCallback, + void * _clientData): + name(_name), + type(_type), + setCallback(_setCallback), + getCallback(_getCallback), + clientData(_clientData) + { + } + // Shallow copy + // I solemnly swear it's OK to copy clientData this way + IGL_INLINE ReTwCBItem(const ReTwCBItem & that): + name(that.name), + type(that.type), + setCallback(that.setCallback), + getCallback(that.getCallback), + clientData(that.clientData) + { + } + // Shallow assignment + // I solemnly swear it's OK to copy clientData this way + IGL_INLINE ReTwCBItem & operator=(const ReTwCBItem & that) + { + if(this != &that) + { + name = that.name; + type = that.type; + setCallback = that.setCallback; + getCallback = that.getCallback; + clientData = that.clientData; + } + return *this; + } + + }; + + class ReTwBar + { + // VARIABLES + // Should be private, but seeing as I'm not going to implement all of the + // AntTweakBar public functions right away, I'll expose this so that at + // anytime AntTweakBar functions can be called directly on the bar + public: + TwBar * bar; + std::string name; + protected: + std::vector rw_items; + std::vector cb_items; + public: + // Default constructor with explicit initialization + IGL_INLINE ReTwBar(); + private: + // Copy constructor does shallow copy + IGL_INLINE ReTwBar(const ReTwBar & that); + // Assignment operator does shallow assignment + IGL_INLINE ReTwBar &operator=(const ReTwBar & that); + + // WRAPPERS FOR ANTTWEAKBAR FUNCTIONS + public: + IGL_INLINE void TwNewBar(const char *_name); + IGL_INLINE int TwAddVarRW( + const char *name, + TwType type, + void *var, + const char *def, + const bool record=true); + IGL_INLINE int TwAddVarCB( + const char *name, + TwType type, + TwSetVarCallback setCallback, + TwGetVarCallback getCallback, + void *clientData, + const char *def, + const bool record=true); + // Wrappers for convenience (not recorded, just passed on) + IGL_INLINE int TwAddVarRO(const char *name, TwType type, void *var, const char *def); + IGL_INLINE int TwAddButton( + const char *name, + TwButtonCallback buttonCallback, + void *clientData, + const char *def); + IGL_INLINE int TwSetParam( + const char *varName, + const char *paramName, + TwParamValueType paramValueType, + unsigned int inValueCount, + const void *inValues); + IGL_INLINE int TwGetParam( + const char *varName, + const char *paramName, + TwParamValueType paramValueType, + unsigned int outValueMaxCount, + void *outValues); + IGL_INLINE int TwRefreshBar(); + IGL_INLINE int TwTerminate(); + + + // IO FUNCTIONS + public: + // Save current items to file + // Input: + // file_name name of file to save data to, can be null which means print + // to stdout + // Return: + // true only if there were no (fatal) errors + IGL_INLINE bool save(const char *file_name); + std::string get_value_as_string( + void * var, + TwType type); + // Load into current items from file + // Input: + // file_name name of input file to load + // Return: + // true only if there were no (fatal) errors + IGL_INLINE bool load(const char *file_name); + // Get TwType from string + // Input + // type_str string of type + // Output + // type TwType converted from string + // Returns + // true only if string matched a valid type + IGL_INLINE bool type_from_string(const char *type_str, TwType & type); + // I realize that I mix std::string and const char * all over the place. + // What can you do... + IGL_INLINE bool set_value_from_string( + const char * name, + TwType type, + const char * value_str); + IGL_INLINE const std::vector & get_rw_items(); + IGL_INLINE const std::vector & get_cb_items(); + }; + } +} + +// List of TwBar functions +//TW_API TwBar * TW_CALL TwNewBar(const char *barName); +//TW_API int TW_CALL TwDeleteBar(TwBar *bar); +//TW_API int TW_CALL TwDeleteAllBars(); +//TW_API int TW_CALL TwSetTopBar(const TwBar *bar); +//TW_API TwBar * TW_CALL TwGetTopBar(); +//TW_API int TW_CALL TwSetBottomBar(const TwBar *bar); +//TW_API TwBar * TW_CALL TwGetBottomBar(); +//TW_API const char * TW_CALL TwGetBarName(TwBar *bar); +//TW_API int TW_CALL TwGetBarCount(); +//TW_API TwBar * TW_CALL TwGetBarByIndex(int barIndex); +//TW_API TwBar * TW_CALL TwGetBarByName(const char *barName); +//TW_API int TW_CALL TwRefreshBar(TwBar *bar); +//TW_API int TW_CALL TwTerminate(); +// +//TW_API int TW_CALL TwAddVarRW(TwBar *bar, const char *name, TwType type, void *var, const char *def); +//TW_API int TW_CALL TwAddVarRO(TwBar *bar, const char *name, TwType type, const void *var, const char *def); +//TW_API int TW_CALL TwAddVarCB(TwBar *bar, const char *name, TwType type, TwSetVarCallback setCallback, TwGetVarCallback getCallback, void *clientData, const char *def); +//TW_API int TW_CALL TwAddButton(TwBar *bar, const char *name, TwButtonCallback callback, void *clientData, const char *def); +//TW_API int TW_CALL TwAddSeparator(TwBar *bar, const char *name, const char *def); +//TW_API int TW_CALL TwRemoveVar(TwBar *bar, const char *name); +//TW_API int TW_CALL TwRemoveAllVars(TwBar *bar); + +// Until AntTweakBar dependency folder exists, this is header-only +#ifndef IGL_STATIC_LIBRARY +# include "ReAntTweakBar.cpp" +#endif + +#endif diff --git a/src/igl/anttweakbar/cocoa_key_to_anttweakbar_key.cpp b/src/igl/anttweakbar/cocoa_key_to_anttweakbar_key.cpp new file mode 100644 index 0000000000..bcd6e2be5f --- /dev/null +++ b/src/igl/anttweakbar/cocoa_key_to_anttweakbar_key.cpp @@ -0,0 +1,81 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "cocoa_key_to_anttweakbar_key.h" + +#include + +IGL_INLINE int igl::anttweakbar::cocoa_key_to_anttweakbar_key(int key) +{ + // I've left commented the AntTweakBar key codes that correspond to keys I + // don't have on my keyboard. Please fill this in if you have those keys + switch(key) + { + case 127: + return TW_KEY_BACKSPACE; + case 9: + return TW_KEY_TAB; + //TW_KEY_CLEAR = 0x0c, + case 3://ENTER + case 13: + return TW_KEY_RETURN; + case 27: + return TW_KEY_ESCAPE; + case 32: + return TW_KEY_SPACE; + // IN A GLUT APP 40 is ( + //case 40: + case 63272: + return TW_KEY_DELETE; + case 63232: + return TW_KEY_UP; + case 63233: + return TW_KEY_DOWN; + case 63235: + return TW_KEY_RIGHT; + case 63234: + return TW_KEY_LEFT; + //TW_KEY_INSERT, + //TW_KEY_HOME, + //TW_KEY_END, + //TW_KEY_PAGE_UP, + //TW_KEY_PAGE_DOWN, + case 63236: + return TW_KEY_F1; + case 63237: + return TW_KEY_F2; + case 63238: + return TW_KEY_F3; + case 63239: + return TW_KEY_F4; + case 63240: + return TW_KEY_F5; + case 63241: + return TW_KEY_F6; + case 63242: + return TW_KEY_F7; + case 63243: + return TW_KEY_F8; + case 63244: + return TW_KEY_F9; + case 63245: + return TW_KEY_F10; + case 63246: + return TW_KEY_F11; + case 63247: + return TW_KEY_F12; + case 63248: + return TW_KEY_F13; + case 63249: + return TW_KEY_F14; + case 63250: + return TW_KEY_F15; + default: + break; + } + return key; +} diff --git a/src/igl/anttweakbar/cocoa_key_to_anttweakbar_key.h b/src/igl/anttweakbar/cocoa_key_to_anttweakbar_key.h new file mode 100644 index 0000000000..fdf3d3bea8 --- /dev/null +++ b/src/igl/anttweakbar/cocoa_key_to_anttweakbar_key.h @@ -0,0 +1,31 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ANTTWEAKBAR_COCOA_KEY_TO_ANTTWEAKBAR_KEY_H +#define IGL_ANTTWEAKBAR_COCOA_KEY_TO_ANTTWEAKBAR_KEY_H +#include "../igl_inline.h" + + +namespace igl +{ + namespace anttweakbar + { + // Convert an unsigned char (like that from Cocoa apps) to AntTweakBar key + // code. + // See also: TranslateKey() in TwMgr.cpp in AntTweakBar source + // Inputs: + // key unsigned char key from keyboard + // Returns int of new key code + IGL_INLINE int cocoa_key_to_anttweakbar_key(int key); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "cocoa_key_to_anttweakbar_key.cpp" +#endif + +#endif diff --git a/src/igl/any.cpp b/src/igl/any.cpp new file mode 100644 index 0000000000..0488c8ffb0 --- /dev/null +++ b/src/igl/any.cpp @@ -0,0 +1,26 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "any.h" +#include "redux.h" + + +template +IGL_INLINE void igl::any( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase& B) +{ + typedef typename DerivedB::Scalar Scalar; + igl::redux(A,dim,[](Scalar a, Scalar b){ return a || b!=0;},B); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::any >(Eigen::SparseMatrix const&, int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/any.h b/src/igl/any.h new file mode 100644 index 0000000000..08a80e4cb7 --- /dev/null +++ b/src/igl/any.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ANY_H +#define IGL_ANY_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // For Dense matrices use: A.rowwise().any() or A.colwise().any() + // + // Inputs: + // A m by n sparse matrix + // dim dimension along which to check for any (1 or 2) + // Output: + // B n-long vector (if dim == 1) + // or + // B m-long vector (if dim == 2) + // + template + IGL_INLINE void any( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase& B); +} +#ifndef IGL_STATIC_LIBRARY +# include "any.cpp" +#endif +#endif + diff --git a/src/igl/any_of.cpp b/src/igl/any_of.cpp new file mode 100644 index 0000000000..9defd03395 --- /dev/null +++ b/src/igl/any_of.cpp @@ -0,0 +1,20 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "any_of.h" +#include +template +IGL_INLINE bool igl::any_of(const Mat & S) +{ + return std::any_of(S.data(),S.data()+S.size(),[](bool s){return s;}); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::any_of >(Eigen::Matrix const&); +#endif + diff --git a/src/igl/any_of.h b/src/igl/any_of.h new file mode 100644 index 0000000000..95ec1d373e --- /dev/null +++ b/src/igl/any_of.h @@ -0,0 +1,26 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ANY_OF_H +#define IGL_ANY_OF_H +#include "igl_inline.h" +namespace igl +{ + // Wrapper for STL `any_of` for matrix types + // + // Inputs: + // S matrix + // Returns whether any entries are true + // + // Seems that Eigen (now) implements this for `Eigen::Array` + template + IGL_INLINE bool any_of(const Mat & S); +} +#ifndef IGL_STATIC_LIBRARY +# include "any_of.cpp" +#endif +#endif diff --git a/src/igl/arap.cpp b/src/igl/arap.cpp new file mode 100644 index 0000000000..dc5af4cf6f --- /dev/null +++ b/src/igl/arap.cpp @@ -0,0 +1,312 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "arap.h" +#include "colon.h" +#include "cotmatrix.h" +#include "massmatrix.h" +#include "group_sum_matrix.h" +#include "covariance_scatter_matrix.h" +#include "speye.h" +#include "mode.h" +#include "project_isometrically_to_plane.h" +#include "slice.h" +#include "arap_rhs.h" +#include "repdiag.h" +#include "columnize.h" +#include "fit_rotations.h" +#include +#include + +template < + typename DerivedV, + typename DerivedF, + typename Derivedb> +IGL_INLINE bool igl::arap_precomputation( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const int dim, + const Eigen::PlainObjectBase & b, + ARAPData & data) +{ + using namespace std; + using namespace Eigen; + typedef typename DerivedV::Scalar Scalar; + // number of vertices + const int n = V.rows(); + data.n = n; + assert((b.size() == 0 || b.maxCoeff() < n) && "b out of bounds"); + assert((b.size() == 0 || b.minCoeff() >=0) && "b out of bounds"); + // remember b + data.b = b; + //assert(F.cols() == 3 && "For now only triangles"); + // dimension + //const int dim = V.cols(); + assert((dim == 3 || dim ==2) && "dim should be 2 or 3"); + data.dim = dim; + //assert(dim == 3 && "Only 3d supported"); + // Defaults + data.f_ext = MatrixXd::Zero(n,data.dim); + + assert(data.dim <= V.cols() && "solve dim should be <= embedding"); + bool flat = (V.cols() - data.dim)==1; + + DerivedV plane_V; + DerivedF plane_F; + typedef SparseMatrix SparseMatrixS; + SparseMatrixS ref_map,ref_map_dim; + if(flat) + { + project_isometrically_to_plane(V,F,plane_V,plane_F,ref_map); + repdiag(ref_map,dim,ref_map_dim); + } + const PlainObjectBase& ref_V = (flat?plane_V:V); + const PlainObjectBase& ref_F = (flat?plane_F:F); + SparseMatrixS L; + cotmatrix(V,F,L); + + ARAPEnergyType eff_energy = data.energy; + if(eff_energy == ARAP_ENERGY_TYPE_DEFAULT) + { + switch(F.cols()) + { + case 3: + if(data.dim == 3) + { + eff_energy = ARAP_ENERGY_TYPE_SPOKES_AND_RIMS; + }else + { + eff_energy = ARAP_ENERGY_TYPE_ELEMENTS; + } + break; + case 4: + eff_energy = ARAP_ENERGY_TYPE_ELEMENTS; + break; + default: + assert(false); + } + } + + + // Get covariance scatter matrix, when applied collects the covariance + // matrices used to fit rotations to during optimization + covariance_scatter_matrix(ref_V,ref_F,eff_energy,data.CSM); + if(flat) + { + data.CSM = (data.CSM * ref_map_dim.transpose()).eval(); + } + assert(data.CSM.cols() == V.rows()*data.dim); + + // Get group sum scatter matrix, when applied sums all entries of the same + // group according to G + SparseMatrix G_sum; + if(data.G.size() == 0) + { + if(eff_energy == ARAP_ENERGY_TYPE_ELEMENTS) + { + speye(F.rows(),G_sum); + }else + { + speye(n,G_sum); + } + }else + { + // groups are defined per vertex, convert to per face using mode + if(eff_energy == ARAP_ENERGY_TYPE_ELEMENTS) + { + Eigen::Matrix GG; + MatrixXi GF(F.rows(),F.cols()); + for(int j = 0;j GFj; + slice(data.G,F.col(j),GFj); + GF.col(j) = GFj; + } + mode(GF,2,GG); + data.G=GG; + } + //printf("group_sum_matrix()\n"); + group_sum_matrix(data.G,G_sum); + } + SparseMatrix G_sum_dim; + repdiag(G_sum,data.dim,G_sum_dim); + assert(G_sum_dim.cols() == data.CSM.rows()); + data.CSM = (G_sum_dim * data.CSM).eval(); + + + arap_rhs(ref_V,ref_F,data.dim,eff_energy,data.K); + if(flat) + { + data.K = (ref_map_dim * data.K).eval(); + } + assert(data.K.rows() == data.n*data.dim); + + SparseMatrix Q = (-L).eval(); + + if(data.with_dynamics) + { + const double h = data.h; + assert(h != 0); + SparseMatrix M; + massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,data.M); + const double dw = (1./data.ym)*(h*h); + SparseMatrix DQ = dw * 1./(h*h)*data.M; + Q += DQ; + // Dummy external forces + data.f_ext = MatrixXd::Zero(n,data.dim); + data.vel = MatrixXd::Zero(n,data.dim); + } + + return min_quad_with_fixed_precompute( + Q,b,SparseMatrix(),true,data.solver_data); +} + +template < + typename Derivedbc, + typename DerivedU> +IGL_INLINE bool igl::arap_solve( + const Eigen::PlainObjectBase & bc, + ARAPData & data, + Eigen::PlainObjectBase & U) +{ + using namespace Eigen; + using namespace std; + assert(data.b.size() == bc.rows()); + if(bc.size() > 0) + { + assert(bc.cols() == data.dim && "bc.cols() match data.dim"); + } + const int n = data.n; + int iter = 0; + if(U.size() == 0) + { + // terrible initial guess.. should at least copy input mesh +#ifndef NDEBUG + cerr<<"arap_solve: Using terrible initial guess for U. Try U = V."<0) + { + bcc = bc.col(c); + } + min_quad_with_fixed_solve( + data.solver_data, + Bc,bcc,Beq, + Uc); + U.col(c) = Uc; + } + + iter++; + } + if(data.with_dynamics) + { + // Keep track of velocity for next time + data.vel = (U-U0)/data.h; + } + + return true; +} + +#ifdef IGL_STATIC_LIBRARY +template bool igl::arap_solve, Eigen::Matrix >(Eigen::PlainObjectBase > const&, igl::ARAPData&, Eigen::PlainObjectBase >&); +template bool igl::arap_precomputation, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase > const&, igl::ARAPData&); +#endif diff --git a/src/igl/arap.h b/src/igl/arap.h new file mode 100644 index 0000000000..b2210e64fd --- /dev/null +++ b/src/igl/arap.h @@ -0,0 +1,104 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ARAP_H +#define IGL_ARAP_H +#include "igl_inline.h" +#include "min_quad_with_fixed.h" +#include "ARAPEnergyType.h" +#include +#include + +namespace igl +{ + struct ARAPData + { + // n #V + // G #V list of group indices (1 to k) for each vertex, such that vertex i + // is assigned to group G(i) + // energy type of energy to use + // with_dynamics whether using dynamics (need to call arap_precomputation + // after changing) + // f_ext #V by dim list of external forces + // vel #V by dim list of velocities + // h dynamics time step + // ym ~Young's modulus smaller is softer, larger is more rigid/stiff + // max_iter maximum inner iterations + // K rhs pre-multiplier + // M mass matrix + // solver_data quadratic solver data + // b list of boundary indices into V + // dim dimension being used for solving + int n; + Eigen::VectorXi G; + ARAPEnergyType energy; + bool with_dynamics; + Eigen::MatrixXd f_ext,vel; + double h; + double ym; + int max_iter; + Eigen::SparseMatrix K,M; + Eigen::SparseMatrix CSM; + min_quad_with_fixed_data solver_data; + Eigen::VectorXi b; + int dim; + ARAPData(): + n(0), + G(), + energy(ARAP_ENERGY_TYPE_DEFAULT), + with_dynamics(false), + f_ext(), + h(1), + ym(1), + max_iter(10), + K(), + CSM(), + solver_data(), + b(), + dim(-1) // force this to be set by _precomputation + { + }; + }; + + // Compute necessary information to start using an ARAP deformation + // + // Inputs: + // V #V by dim list of mesh positions + // F #F by simplex-size list of triangle|tet indices into V + // dim dimension being used at solve time. For deformation usually dim = + // V.cols(), for surface parameterization V.cols() = 3 and dim = 2 + // b #b list of "boundary" fixed vertex indices into V + // Outputs: + // data struct containing necessary precomputation + template < + typename DerivedV, + typename DerivedF, + typename Derivedb> + IGL_INLINE bool arap_precomputation( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const int dim, + const Eigen::PlainObjectBase & b, + ARAPData & data); + // Inputs: + // bc #b by dim list of boundary conditions + // data struct containing necessary precomputation and parameters + // U #V by dim initial guess + template < + typename Derivedbc, + typename DerivedU> + IGL_INLINE bool arap_solve( + const Eigen::PlainObjectBase & bc, + ARAPData & data, + Eigen::PlainObjectBase & U); +}; + +#ifndef IGL_STATIC_LIBRARY +#include "arap.cpp" +#endif + +#endif diff --git a/src/igl/arap_dof.cpp b/src/igl/arap_dof.cpp new file mode 100644 index 0000000000..033d1deb37 --- /dev/null +++ b/src/igl/arap_dof.cpp @@ -0,0 +1,884 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "arap_dof.h" + +#include "cotmatrix.h" +#include "massmatrix.h" +#include "speye.h" +#include "repdiag.h" +#include "repmat.h" +#include "slice.h" +#include "colon.h" +#include "is_sparse.h" +#include "mode.h" +#include "is_symmetric.h" +#include "group_sum_matrix.h" +#include "arap_rhs.h" +#include "covariance_scatter_matrix.h" +#include "fit_rotations.h" + +#include "verbose.h" +#include "print_ijv.h" + +#include "get_seconds_hires.h" +//#include "MKLEigenInterface.h" +#include "min_quad_dense.h" +#include "get_seconds.h" +#include "columnize.h" + +// defined if no early exit is supported, i.e., always take a fixed number of iterations +#define IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT + +// A careful derivation of this implementation is given in the corresponding +// matlab function arap_dof.m +template +IGL_INLINE bool igl::arap_dof_precomputation( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const LbsMatrixType & M, + const Eigen::Matrix & G, + ArapDOFData & data) +{ + using namespace Eigen; + typedef Matrix MatrixXS; + // number of mesh (domain) vertices + int n = V.rows(); + // cache problem size + data.n = n; + // dimension of mesh + data.dim = V.cols(); + assert(data.dim == M.rows()/n); + assert(data.dim*n == M.rows()); + if(data.dim == 3) + { + // Check if z-coordinate is all zeros + if(V.col(2).minCoeff() == 0 && V.col(2).maxCoeff() == 0) + { + data.effective_dim = 2; + } + }else + { + data.effective_dim = data.dim; + } + // Number of handles + data.m = M.cols()/data.dim/(data.dim+1); + assert(data.m*data.dim*(data.dim+1) == M.cols()); + //assert(m == C.rows()); + + //printf("n=%d; dim=%d; m=%d;\n",n,data.dim,data.m); + + // Build cotangent laplacian + SparseMatrix Lcot; + //printf("cotmatrix()\n"); + cotmatrix(V,F,Lcot); + // Discrete laplacian (should be minus matlab version) + SparseMatrix Lapl = -2.0*Lcot; +#ifdef EXTREME_VERBOSE + cout<<"LaplIJV=["< G_sum; + if(G.size() == 0) + { + speye(n,G_sum); + }else + { + // groups are defined per vertex, convert to per face using mode + Eigen::Matrix GG; + if(data.energy == ARAP_ENERGY_TYPE_ELEMENTS) + { + MatrixXi GF(F.rows(),F.cols()); + for(int j = 0;j GFj; + slice(G,F.col(j),GFj); + GF.col(j) = GFj; + } + mode(GF,2,GG); + }else + { + GG=G; + } + //printf("group_sum_matrix()\n"); + group_sum_matrix(GG,G_sum); + } + +#ifdef EXTREME_VERBOSE + cout<<"G_sumIJV=["< CSM; + //printf("covariance_scatter_matrix()\n"); + covariance_scatter_matrix(V,F,data.energy,CSM); +#ifdef EXTREME_VERBOSE + cout<<"CSMIJV=["< G_sum_dim; + repdiag(G_sum,data.dim,G_sum_dim); + CSM = (G_sum_dim * CSM).eval(); +#ifdef EXTREME_VERBOSE + cout<<"CSMIJV=["< span_n(n); + for(int i = 0;i span_mlbs_cols(M.cols()); + for(int i = 0;i CSMj; + //printf("CSM_M(): slice\n"); + slice( + CSM, + colon(j*k,(j+1)*k-1), + colon(j*n,(j+1)*n-1), + CSMj); + assert(CSMj.rows() == k); + assert(CSMj.cols() == n); + LbsMatrixType CSMjM_i = CSMj * M_i; + if(is_sparse(CSMjM_i)) + { + // Convert to full + //printf("CSM_M(): full\n"); + MatrixXd CSMjM_ifull(CSMjM_i); +// printf("CSM_M[%d]: %d %d\n",i,data.CSM_M[i].rows(),data.CSM_M[i].cols()); +// printf("CSM_M[%d].block(%d*%d=%d,0,%d,%d): %d %d\n",i,j,k,CSMjM_i.rows(),CSMjM_i.cols(), +// data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()).rows(), +// data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()).cols()); +// printf("CSM_MjMi: %d %d\n",i,CSMjM_i.rows(),CSMjM_i.cols()); +// printf("CSM_MjM_ifull: %d %d\n",i,CSMjM_ifull.rows(),CSMjM_ifull.cols()); + data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()) = CSMjM_ifull; + }else + { + data.CSM_M[i].block(j*k,0,CSMjM_i.rows(),CSMjM_i.cols()) = CSMjM_i; + } + } +#ifdef EXTREME_VERBOSE + cout<<"CSM_Mi=["< K; + arap_rhs(V,F,V.cols(),data.energy,K); +//#ifdef EXTREME_VERBOSE +// cout<<"KIJV=["< G_sumT = G_sum.transpose(); + SparseMatrix G_sumT_dim_dim; + repdiag(G_sumT,data.dim*data.dim,G_sumT_dim_dim); + LbsMatrixType MT = M.transpose(); + // If this is a bottle neck then consider reordering matrix multiplication + data.M_KG = -4.0 * (MT * (K * G_sumT_dim_dim)); +//#ifdef EXTREME_VERBOSE +// cout<<"data.M_KGIJV=["< A; + repdiag(Lapl,data.dim,A); + data.Q = MT * (A * M); +//#ifdef EXTREME_VERBOSE +// cout<<"QIJV=["< Mass; + //printf("massmatrix()\n"); + massmatrix(V,F,(F.cols()>3?MASSMATRIX_TYPE_BARYCENTRIC:MASSMATRIX_TYPE_VORONOI),Mass); + //cout<<"MIJV=["< Mass_rep; + repdiag(Mass,data.dim,Mass_rep); + + // Multiply either side by weights matrix (should be dense) + data.Mass_tilde = MT * Mass_rep * M; + MatrixXd ones(data.dim*data.n,data.dim); + for(int i = 0;i + inline static SSCALAR maxBlokErr(const Eigen::Matrix3f &blok) + { + SSCALAR mD; + SSCALAR value = blok(0,0); + SSCALAR diff1 = fabs(blok(1,1) - value); + SSCALAR diff2 = fabs(blok(2,2) - value); + if (diff1 > diff2) mD = diff1; + else mD = diff2; + + for (int v=0; v<3; v++) + { + for (int w=0; w<3; w++) + { + if (v == w) + { + continue; + } + if (mD < fabs(blok(v, w))) + { + mD = fabs(blok(v, w)); + } + } + } + + return mD; + } + + // converts CSM_M_SSCALAR[0], CSM_M_SSCALAR[1], CSM_M_SSCALAR[2] into one + // "condensed" matrix CSM while checking we're not losing any information by + // this process; specifically, returns maximal difference from scaled 3x3 + // identity blocks, which should be pretty small number + template + static typename MatrixXS::Scalar condense_CSM( + const std::vector &CSM_M_SSCALAR, + int numBones, + int dim, + MatrixXS &CSM) + { + const int numRows = CSM_M_SSCALAR[0].rows(); + assert(CSM_M_SSCALAR[0].cols() == dim*(dim+1)*numBones); + assert(CSM_M_SSCALAR[1].cols() == dim*(dim+1)*numBones); + assert(CSM_M_SSCALAR[2].cols() == dim*(dim+1)*numBones); + assert(CSM_M_SSCALAR[1].rows() == numRows); + assert(CSM_M_SSCALAR[2].rows() == numRows); + + const int numCols = (dim + 1)*numBones; + CSM.resize(numRows, numCols); + + typedef typename MatrixXS::Scalar SSCALAR; + SSCALAR maxDiff = 0.0f; + + for (int r=0; r(blok); + if (mD > maxDiff) maxDiff = mD; + + // use the first value: + CSM(r, coord*numBones + b) = blok(0,0); + } + } + } + + return maxDiff; + } + + // splits x_0, ... , x_dim coordinates in column vector 'L' into a numBones*(dimp1) x dim matrix 'Lsep'; + // assumes 'Lsep' has already been preallocated + // + // is this the same as uncolumnize? no. + template + static void splitColumns( + const MatL &L, + int numBones, + int dim, + int dimp1, + MatLsep &Lsep) + { + assert(L.cols() == 1); + assert(L.rows() == dim*(dimp1)*numBones); + + assert(Lsep.rows() == (dimp1)*numBones && Lsep.cols() == dim); + + for (int b=0; b + static void mergeColumns(const MatrixXS &Lsep, int numBones, int dim, int dimp1, MatrixXS &L) + { + assert(L.cols() == 1); + assert(L.rows() == dim*(dimp1)*numBones); + + assert(Lsep.rows() == (dimp1)*numBones && Lsep.cols() == dim); + + for (int b=0; b + static typename MatrixXS::Scalar condense_Solve1(MatrixXS &Solve1, int numBones, int numGroups, int dim, MatrixXS &CSolve1) + { + assert(Solve1.rows() == dim*(dim + 1)*numBones); + assert(Solve1.cols() == dim*dim*numGroups); + + typedef typename MatrixXS::Scalar SSCALAR; + SSCALAR maxDiff = 0.0f; + + CSolve1.resize((dim + 1)*numBones, dim*numGroups); + for (int rowCoord=0; rowCoord(blok); + if (mD > maxDiff) maxDiff = mD; + + CSolve1(rowCoord*numBones + b, colCoord*numGroups + g) = blok(0,0); + } + } + } + } + + return maxDiff; + } +} + +template +IGL_INLINE bool igl::arap_dof_recomputation( + const Eigen::Matrix & fixed_dim, + const Eigen::SparseMatrix & A_eq, + ArapDOFData & data) +{ + using namespace Eigen; + typedef Matrix MatrixXS; + + LbsMatrixType * Q; + LbsMatrixType Qdyn; + if(data.with_dynamics) + { + // multiply by 1/timestep and to quadratic coefficients matrix + // Might be missing a 0.5 here + LbsMatrixType Q_copy = data.Q; + Qdyn = Q_copy + (1.0/(data.h*data.h))*data.Mass_tilde; + Q = &Qdyn; + + // This may/should be superfluous + //printf("is_symmetric()\n"); + if(!is_symmetric(*Q)) + { + //printf("Fixing symmetry...\n"); + // "Fix" symmetry + LbsMatrixType QT = (*Q).transpose(); + LbsMatrixType Q_copy = *Q; + *Q = 0.5*(Q_copy+QT); + // Check that ^^^ this really worked. It doesn't always + //assert(is_symmetric(*Q)); + } + }else + { + Q = &data.Q; + } + + assert((int)data.CSM_M.size() == data.dim); + assert(A_eq.cols() == data.m*data.dim*(data.dim+1)); + data.fixed_dim = fixed_dim; + + if(fixed_dim.size() > 0) + { + assert(fixed_dim.maxCoeff() < data.m*data.dim*(data.dim+1)); + assert(fixed_dim.minCoeff() >= 0); + } + +#ifdef EXTREME_VERBOSE + cout<<"data.fixed_dim=["<(), + M_Solve.block(0, fsRows, fsRows, fsCols2).template cast(); + + if(data.with_dynamics) + { + printf( + "---------------------------------------------------------------------\n" + "\n\n\nWITH DYNAMICS recomputation\n\n\n" + "---------------------------------------------------------------------\n" + ); + // Also need to save Π1 before it gets multiplied by Ktilde (aka M_KG) + data.Pi_1 = M_Solve.block(0, 0, fsRows, fsRows).template cast(); + } + + // Precompute condensed matrices, + // first CSM: + std::vector CSM_M_SSCALAR; + CSM_M_SSCALAR.resize(data.dim); + for (int i=0; i(); + SSCALAR maxErr1 = condense_CSM(CSM_M_SSCALAR, data.m, data.dim, data.CSM); + verbose("condense_CSM maxErr = %.15f (this should be close to zero)\n", maxErr1); + assert(fabs(maxErr1) < 1e-5); + + // and then solveBlock1: + // number of groups + const int k = data.CSM_M[0].rows()/data.dim; + MatrixXS SolveBlock1 = data.M_FullSolve.block(0, 0, data.M_FullSolve.rows(), data.dim * data.dim * k); + SSCALAR maxErr2 = condense_Solve1(SolveBlock1, data.m, k, data.dim, data.CSolveBlock1); + verbose("condense_Solve1 maxErr = %.15f (this should be close to zero)\n", maxErr2); + assert(fabs(maxErr2) < 1e-5); + + return true; +} + +template +IGL_INLINE bool igl::arap_dof_update( + const ArapDOFData & data, + const Eigen::Matrix & B_eq, + const Eigen::MatrixXd & L0, + const int max_iters, + const double +#ifdef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT + tol, +#else + /*tol*/, +#endif + Eigen::MatrixXd & L + ) +{ + using namespace Eigen; + typedef Matrix MatrixXS; +#ifdef ARAP_GLOBAL_TIMING + double timer_start = get_seconds_hires(); +#endif + + // number of dimensions + assert((int)data.CSM_M.size() == data.dim); + assert((int)L0.size() == (data.m)*data.dim*(data.dim+1)); + assert(max_iters >= 0); + assert(tol >= 0); + + // timing variables + double + sec_start, + sec_covGather, + sec_fitRotations, + //sec_rhs, + sec_prepMult, + sec_solve, sec_end; + + assert(L0.cols() == 1); +#ifdef EXTREME_VERBOSE + cout<<"dim="<(); + + int iters = 0; +#ifndef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT + double max_diff = tol+1; +#endif + + MatrixXS S(k*data.dim,data.dim); + MatrixXS R(data.dim,data.dim*k); + Eigen::Matrix Rcol(data.dim * data.dim * k); + Matrix B_eq_SSCALAR = B_eq.cast(); + Matrix B_eq_fix_SSCALAR; + Matrix L0SSCALAR = L0.cast(); + slice(L0SSCALAR, data.fixed_dim, B_eq_fix_SSCALAR); + //MatrixXS rhsFull(Rcol.rows() + B_eq.rows() + B_eq_fix_SSCALAR.rows(), 1); + + MatrixXS Lsep(data.m*(data.dim + 1), 3); + const MatrixXS L_part2 = + data.M_FullSolve.block(0, Rcol.rows(), data.M_FullSolve.rows(), B_eq_SSCALAR.rows()) * B_eq_SSCALAR; + const MatrixXS L_part3 = + data.M_FullSolve.block(0, Rcol.rows() + B_eq_SSCALAR.rows(), data.M_FullSolve.rows(), B_eq_fix_SSCALAR.rows()) * B_eq_fix_SSCALAR; + MatrixXS L_part2and3 = L_part2 + L_part3; + + // preallocate workspace variables: + MatrixXS Rxyz(k*data.dim, data.dim); + MatrixXS L_part1xyz((data.dim + 1) * data.m, data.dim); + MatrixXS L_part1(data.dim * (data.dim + 1) * data.m, 1); + +#ifdef ARAP_GLOBAL_TIMING + double timer_prepFinished = get_seconds_hires(); +#endif + +#ifdef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT + while(iters < max_iters) +#else + while(iters < max_iters && max_diff > tol) +#endif + { + if(data.print_timings) + { + sec_start = get_seconds_hires(); + } + +#ifndef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT + L_prev = L_SSCALAR; +#endif + /////////////////////////////////////////////////////////////////////////// + // Local step: Fix positions, fit rotations + /////////////////////////////////////////////////////////////////////////// + + // Gather covariance matrices + + splitColumns(L_SSCALAR, data.m, data.dim, data.dim + 1, Lsep); + + S = data.CSM * Lsep; + // interestingly, this doesn't seem to be so slow, but + //MKL is still 2x faster (probably due to AVX) + //#ifdef IGL_ARAP_DOF_DOUBLE_PRECISION_SOLVE + // MKL_matMatMult_double(S, data.CSM, Lsep); + //#else + // MKL_matMatMult_single(S, data.CSM, Lsep); + //#endif + + if(data.print_timings) + { + sec_covGather = get_seconds_hires(); + } + +#ifdef EXTREME_VERBOSE + cout<<"S=["<(); + + MatrixXd temp_g = data.fgrav*(data.grav_mag*data.grav_dir); + + assert(data.fext.rows() == temp_g.rows()); + assert(data.fext.cols() == temp_g.cols()); + MatrixXd temp2 = data.Mass_tilde * temp_d + temp_g + data.fext.template cast(); + MatrixXS temp2_f = temp2.template cast(); + L_part1_dyn = data.Pi_1 * temp2_f; + L_part1.array() = L_part1.array() + L_part1_dyn.array(); + } + + //L_SSCALAR = L_part1 + L_part2and3; + assert(L_SSCALAR.rows() == L_part1.rows() && L_SSCALAR.rows() == L_part2and3.rows()); + for (int i=0; i(); + assert(L.cols() == 1); + +#ifdef ARAP_GLOBAL_TIMING + double timer_finito = get_seconds_hires(); + printf( + "ARAP preparation = %f, " + "all %i iterations = %f [ms]\n", + (timer_prepFinished - timer_start)*1000.0, + max_iters, + (timer_finito - timer_prepFinished)*1000.0); +#endif + + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::arap_dof_update, double>(ArapDOFData, double> const&, Eigen::Matrix const&, Eigen::Matrix const&, int, double, Eigen::Matrix&); +template bool igl::arap_dof_recomputation, double>(Eigen::Matrix const&, Eigen::SparseMatrix const&, ArapDOFData, double>&); +template bool igl::arap_dof_precomputation, double>(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, ArapDOFData, double>&); +template bool igl::arap_dof_update, float>(igl::ArapDOFData, float> const&, Eigen::Matrix const&, Eigen::Matrix const&, int, double, Eigen::Matrix&); +template bool igl::arap_dof_recomputation, float>(Eigen::Matrix const&, Eigen::SparseMatrix const&, igl::ArapDOFData, float>&); +template bool igl::arap_dof_precomputation, float>(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, igl::ArapDOFData, float>&); +#endif diff --git a/src/igl/arap_dof.h b/src/igl/arap_dof.h new file mode 100644 index 0000000000..f3647a3a18 --- /dev/null +++ b/src/igl/arap_dof.h @@ -0,0 +1,244 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ARAP_ENERGY_TYPE_DOF_H +#define IGL_ARAP_ENERGY_TYPE_DOF_H +#include "igl_inline.h" + +#include +#include +#include "ARAPEnergyType.h" +#include + +namespace igl +{ + // Caller example: + // + // Once: + // arap_dof_precomputation(...) + // + // Each frame: + // while(not satisfied) + // arap_dof_update(...) + // end + + template + struct ArapDOFData; + + /////////////////////////////////////////////////////////////////////////// + // + // Arap DOF precomputation consists of two parts the computation. The first is + // that which depends solely on the mesh (V,F), the linear blend skinning + // weights (M) and the groups G. Then there's the part that depends on the + // previous precomputation and the list of free and fixed vertices. + // + /////////////////////////////////////////////////////////////////////////// + + + // The code and variables differ from the description in Section 3 of "Fast + // Automatic Skinning Transformations" by [Jacobson et al. 2012] + // + // Here is a useful conversion table: + // + // [article] [code] + // S = \tilde{K} T S = CSM * Lsep + // S --> R S --> R --shuffled--> Rxyz + // Gamma_solve RT = Pi_1 \tilde{K} RT L_part1xyz = CSolveBlock1 * Rxyz + // Pi_1 \tilde{K} CSolveBlock1 + // Peq = [T_full; P_pos] + // T_full B_eq_fix <--- L0 + // P_pos B_eq + // Pi_2 * P_eq = Lpart2and3 = Lpart2 + Lpart3 + // Pi_2_left T_full + Lpart3 = M_fullsolve(right) * B_eq_fix + // Pi_2_right P_pos Lpart2 = M_fullsolve(left) * B_eq + // T = [Pi_1 Pi_2] [\tilde{K}TRT P_eq] L = Lpart1 + Lpart2and3 + // + + // Precomputes the system we are going to optimize. This consists of building + // constructor matrices (to compute covariance matrices from transformations + // and to build the poisson solve right hand side from rotation matrix entries) + // and also prefactoring the poisson system. + // + // Inputs: + // V #V by dim list of vertex positions + // F #F by {3|4} list of face indices + // M #V * dim by #handles * dim * (dim+1) matrix such that + // new_V(:) = LBS(V,W,A) = reshape(M * A,size(V)), where A is a column + // vectors formed by the entries in each handle's dim by dim+1 + // transformation matrix. Specifcally, A = + // reshape(permute(Astack,[3 1 2]),n*dim*(dim+1),1) + // or A = [Lxx;Lyx;Lxy;Lyy;tx;ty], and likewise for other dim + // if Astack(:,:,i) is the dim by (dim+1) transformation at handle i + // handles are ordered according to P then BE (point handles before bone + // handles) + // G #V list of group indices (1 to k) for each vertex, such that vertex i + // is assigned to group G(i) + // Outputs: + // data structure containing all necessary precomputation for calling + // arap_dof_update + // Returns true on success, false on error + // + // See also: lbs_matrix_column + template + IGL_INLINE bool arap_dof_precomputation( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const LbsMatrixType & M, + const Eigen::Matrix & G, + ArapDOFData & data); + + // Should always be called after arap_dof_precomputation, but may be called in + // between successive calls to arap_dof_update, recomputes precomputation + // given that there are only changes in free and fixed + // + // Inputs: + // fixed_dim list of transformation element indices for fixed (or partailly + // fixed) handles: not necessarily the complement of 'free' + // NOTE: the constraints for fixed transformations still need to be + // present in A_eq + // A_eq dim*#constraint_points by m*dim*(dim+1) matrix of linear equality + // constraint coefficients. Each row corresponds to a linear constraint, + // so that A_eq * L = Beq says that the linear transformation entries in + // the column L should produce the user supplied positional constraints + // for each handle in Beq. The row A_eq(i*dim+d) corresponds to the + // constrain on coordinate d of position i + // Outputs: + // data structure containing all necessary precomputation for calling + // arap_dof_update + // Returns true on success, false on error + // + // See also: lbs_matrix_column + template + IGL_INLINE bool arap_dof_recomputation( + const Eigen::Matrix & fixed_dim, + const Eigen::SparseMatrix & A_eq, + ArapDOFData & data); + + // Optimizes the transformations attached to each weight function based on + // precomputed system. + // + // Inputs: + // data precomputation data struct output from arap_dof_precomputation + // Beq dim*#constraint_points constraint values. + // L0 #handles * dim * dim+1 list of initial guess transformation entries, + // also holds fixed transformation entries for fixed handles + // max_iters maximum number of iterations + // tol stopping criteria parameter. If variables (linear transformation + // matrix entries) change by less than 'tol' the optimization terminates, + // 0.75 (weak tolerance) + // 0.0 (extreme tolerance) + // Outputs: + // L #handles * dim * dim+1 list of final optimized transformation entries, + // allowed to be the same as L + template + IGL_INLINE bool arap_dof_update( + const ArapDOFData & data, + const Eigen::Matrix & B_eq, + const Eigen::MatrixXd & L0, + const int max_iters, + const double tol, + Eigen::MatrixXd & L + ); + + // Structure that contains fields for all precomputed data or data that needs + // to be remembered at update + template + struct ArapDOFData + { + typedef Eigen::Matrix MatrixXS; + // Type of arap energy we're solving + igl::ARAPEnergyType energy; + //// LU decomposition precomptation data; note: not used by araf_dop_update + //// any more, replaced by M_FullSolve + //igl::min_quad_with_fixed_data lu_data; + // List of indices of fixed transformation entries + Eigen::Matrix fixed_dim; + // List of precomputed covariance scatter matrices multiplied by lbs + // matrices + //std::vector > CSM_M; + std::vector CSM_M; + LbsMatrixType M_KG; + // Number of mesh vertices + int n; + // Number of weight functions + int m; + // Number of dimensions + int dim; + // Effective dimensions + int effective_dim; + // List of indices into C of positional constraints + Eigen::Matrix interpolated; + std::vector free_mask; + // Full quadratic coefficients matrix before lagrangian (should be dense) + LbsMatrixType Q; + + + //// Solve matrix for the global step + //Eigen::MatrixXd M_Solve; // TODO: remove from here + + // Full solve matrix that contains also conversion from rotations to the right hand side, + // i.e., solves Poisson transformations just from rotations and positional constraints + MatrixXS M_FullSolve; + + // Precomputed condensed matrices (3x3 commutators folded to 1x1): + MatrixXS CSM; + MatrixXS CSolveBlock1; + + // Print timings at each update + bool print_timings; + + // Dynamics + bool with_dynamics; + // I'm hiding the extra dynamics stuff in this struct, which sort of defeats + // the purpose of this function-based coding style... + + // Time step + double h; + + // L0 #handles * dim * dim+1 list of transformation entries from + // previous solve + MatrixXS L0; + //// Lm1 #handles * dim * dim+1 list of transformation entries from + //// previous-previous solve + //MatrixXS Lm1; + // "Velocity" + MatrixXS Lvel0; + + // #V by dim matrix of external forces + // fext + MatrixXS fext; + + // Mass_tilde: MT * Mass * M + LbsMatrixType Mass_tilde; + + // Force due to gravity (premultiplier) + Eigen::MatrixXd fgrav; + // Direction of gravity + Eigen::Vector3d grav_dir; + // Magnitude of gravity + double grav_mag; + + // Π1 from the paper + MatrixXS Pi_1; + + // Default values + ArapDOFData(): + energy(igl::ARAP_ENERGY_TYPE_SPOKES), + with_dynamics(false), + h(1), + grav_dir(0,-1,0), + grav_mag(0) + { + } + }; +} + +#ifndef IGL_STATIC_LIBRARY +# include "arap_dof.cpp" +#endif + +#endif diff --git a/src/igl/arap_linear_block.cpp b/src/igl/arap_linear_block.cpp new file mode 100644 index 0000000000..14886b2bc7 --- /dev/null +++ b/src/igl/arap_linear_block.cpp @@ -0,0 +1,253 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "arap_linear_block.h" +#include "verbose.h" +#include "cotmatrix_entries.h" +#include + +template +IGL_INLINE void igl::arap_linear_block( + const MatV & V, + const MatF & F, + const int d, + const igl::ARAPEnergyType energy, + Eigen::SparseMatrix & Kd) +{ + switch(energy) + { + case ARAP_ENERGY_TYPE_SPOKES: + return igl::arap_linear_block_spokes(V,F,d,Kd); + break; + case ARAP_ENERGY_TYPE_SPOKES_AND_RIMS: + return igl::arap_linear_block_spokes_and_rims(V,F,d,Kd); + break; + case ARAP_ENERGY_TYPE_ELEMENTS: + return igl::arap_linear_block_elements(V,F,d,Kd); + break; + default: + verbose("Unsupported energy type: %d\n",energy); + assert(false); + } +} + + +template +IGL_INLINE void igl::arap_linear_block_spokes( + const MatV & V, + const MatF & F, + const int d, + Eigen::SparseMatrix & Kd) +{ + using namespace std; + using namespace Eigen; + // simplex size (3: triangles, 4: tetrahedra) + int simplex_size = F.cols(); + // Number of elements + int m = F.rows(); + // Temporary output + Matrix edges; + Kd.resize(V.rows(), V.rows()); + vector > Kd_IJV; + if(simplex_size == 3) + { + // triangles + Kd.reserve(7*V.rows()); + Kd_IJV.reserve(7*V.rows()); + edges.resize(3,2); + edges << + 1,2, + 2,0, + 0,1; + }else if(simplex_size == 4) + { + // tets + Kd.reserve(17*V.rows()); + Kd_IJV.reserve(17*V.rows()); + edges.resize(6,2); + edges << + 1,2, + 2,0, + 0,1, + 3,0, + 3,1, + 3,2; + } + // gather cotangent weights + Matrix C; + cotmatrix_entries(V,F,C); + // should have weights for each edge + assert(C.cols() == edges.rows()); + // loop over elements + for(int i = 0;i(source,dest,v)); + Kd_IJV.push_back(Triplet(dest,source,-v)); + Kd_IJV.push_back(Triplet(source,source,v)); + Kd_IJV.push_back(Triplet(dest,dest,-v)); + } + } + Kd.setFromTriplets(Kd_IJV.begin(),Kd_IJV.end()); + Kd.makeCompressed(); +} + +template +IGL_INLINE void igl::arap_linear_block_spokes_and_rims( + const MatV & V, + const MatF & F, + const int d, + Eigen::SparseMatrix & Kd) +{ + using namespace std; + using namespace Eigen; + // simplex size (3: triangles, 4: tetrahedra) + int simplex_size = F.cols(); + // Number of elements + int m = F.rows(); + // Temporary output + Kd.resize(V.rows(), V.rows()); + vector > Kd_IJV; + Matrix edges; + if(simplex_size == 3) + { + // triangles + Kd.reserve(7*V.rows()); + Kd_IJV.reserve(7*V.rows()); + edges.resize(3,2); + edges << + 1,2, + 2,0, + 0,1; + }else if(simplex_size == 4) + { + // tets + Kd.reserve(17*V.rows()); + Kd_IJV.reserve(17*V.rows()); + edges.resize(6,2); + edges << + 1,2, + 2,0, + 0,1, + 3,0, + 3,1, + 3,2; + // Not implemented yet for tets + assert(false); + } + // gather cotangent weights + Matrix C; + cotmatrix_entries(V,F,C); + // should have weights for each edge + assert(C.cols() == edges.rows()); + // loop over elements + for(int i = 0;i(Rs,Rd,v)); + Kd_IJV.push_back(Triplet(Rd,Rs,-v)); + }else if(Rd == source) + { + Kd_IJV.push_back(Triplet(Rd,Rs,v)); + }else if(Rs == dest) + { + Kd_IJV.push_back(Triplet(Rs,Rd,-v)); + } + } + Kd_IJV.push_back(Triplet(source,source,v)); + Kd_IJV.push_back(Triplet(dest,dest,-v)); + } + } + Kd.setFromTriplets(Kd_IJV.begin(),Kd_IJV.end()); + Kd.makeCompressed(); +} + +template +IGL_INLINE void igl::arap_linear_block_elements( + const MatV & V, + const MatF & F, + const int d, + Eigen::SparseMatrix & Kd) +{ + using namespace std; + using namespace Eigen; + // simplex size (3: triangles, 4: tetrahedra) + int simplex_size = F.cols(); + // Number of elements + int m = F.rows(); + // Temporary output + Kd.resize(V.rows(), F.rows()); + vector > Kd_IJV; + Matrix edges; + if(simplex_size == 3) + { + // triangles + Kd.reserve(7*V.rows()); + Kd_IJV.reserve(7*V.rows()); + edges.resize(3,2); + edges << + 1,2, + 2,0, + 0,1; + }else if(simplex_size == 4) + { + // tets + Kd.reserve(17*V.rows()); + Kd_IJV.reserve(17*V.rows()); + edges.resize(6,2); + edges << + 1,2, + 2,0, + 0,1, + 3,0, + 3,1, + 3,2; + } + // gather cotangent weights + Matrix C; + cotmatrix_entries(V,F,C); + // should have weights for each edge + assert(C.cols() == edges.rows()); + // loop over elements + for(int i = 0;i(source,i,v)); + Kd_IJV.push_back(Triplet(dest,i,-v)); + } + } + Kd.setFromTriplets(Kd_IJV.begin(),Kd_IJV.end()); + Kd.makeCompressed(); +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template IGL_INLINE void igl::arap_linear_block, Eigen::Matrix, double>(Eigen::Matrix const&, Eigen::Matrix const&, int, igl::ARAPEnergyType, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/arap_linear_block.h b/src/igl/arap_linear_block.h new file mode 100644 index 0000000000..8dfb744f6a --- /dev/null +++ b/src/igl/arap_linear_block.h @@ -0,0 +1,78 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ARAP_LINEAR_BLOCK_H +#define IGL_ARAP_LINEAR_BLOCK_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // ARAP_LINEAR_BLOCK constructs a block of the matrix which constructs the + // linear terms of a given arap energy. When treating rotations as knowns + // (arranged in a column) then this constructs Kd of K such that the linear + // portion of the energy is as a column: + // K * R = [Kx Z ... Ky Z ... + // Z Kx ... Z Ky ... + // ... ] + // These blocks are also used to build the "covariance scatter matrices". + // Here we want to build a scatter matrix that multiplies against positions + // (treated as known) producing covariance matrices to fit each rotation. + // Notice that in the case of the RHS of the poisson solve the rotations are + // known and the positions unknown, and vice versa for rotation fitting. + // These linear block just relate the rotations to the positions, linearly in + // each. + // + // Templates: + // MatV vertex position matrix, e.g. Eigen::MatrixXd + // MatF face index matrix, e.g. Eigen::MatrixXd + // Scalar e.g. double + // Inputs: + // V #V by dim list of initial domain positions + // F #F by #simplex size list of triangle indices into V + // d coordinate of linear constructor to build + // energy ARAPEnergyType enum value defining which energy is being used. + // See ARAPEnergyType.h for valid options and explanations. + // Outputs: + // Kd #V by #V/#F block of the linear constructor matrix corresponding to + // coordinate d + // + template + IGL_INLINE void arap_linear_block( + const MatV & V, + const MatF & F, + const int d, + const igl::ARAPEnergyType energy, + Eigen::SparseMatrix & Kd); + // Helper functions for each energy type + template + IGL_INLINE void arap_linear_block_spokes( + const MatV & V, + const MatF & F, + const int d, + Eigen::SparseMatrix & Kd); + template + IGL_INLINE void arap_linear_block_spokes_and_rims( + const MatV & V, + const MatF & F, + const int d, + Eigen::SparseMatrix & Kd); + template + IGL_INLINE void arap_linear_block_elements( + const MatV & V, + const MatF & F, + const int d, + Eigen::SparseMatrix & Kd); +} + +#ifndef IGL_STATIC_LIBRARY +# include "arap_linear_block.cpp" +#endif + +#endif diff --git a/src/igl/arap_rhs.cpp b/src/igl/arap_rhs.cpp new file mode 100644 index 0000000000..b46462168b --- /dev/null +++ b/src/igl/arap_rhs.cpp @@ -0,0 +1,89 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "arap_rhs.h" +#include "arap_linear_block.h" +#include "verbose.h" +#include "repdiag.h" +#include "cat.h" +#include + +IGL_INLINE void igl::arap_rhs( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const int dim, + const igl::ARAPEnergyType energy, + Eigen::SparseMatrix& K) +{ + using namespace std; + using namespace Eigen; + // Number of dimensions + int Vdim = V.cols(); + //// Number of mesh vertices + //int n = V.rows(); + //// Number of mesh elements + //int m = F.rows(); + //// number of rotations + //int nr; + switch(energy) + { + case ARAP_ENERGY_TYPE_SPOKES: + //nr = n; + break; + case ARAP_ENERGY_TYPE_SPOKES_AND_RIMS: + //nr = n; + break; + case ARAP_ENERGY_TYPE_ELEMENTS: + //nr = m; + break; + default: + fprintf( + stderr, + "arap_rhs.h: Error: Unsupported arap energy %d\n", + energy); + return; + } + + SparseMatrix KX,KY,KZ; + arap_linear_block(V,F,0,energy,KX); + arap_linear_block(V,F,1,energy,KY); + if(Vdim == 2) + { + K = cat(2,repdiag(KX,dim),repdiag(KY,dim)); + }else if(Vdim == 3) + { + arap_linear_block(V,F,2,energy,KZ); + if(dim == 3) + { + K = cat(2,cat(2,repdiag(KX,dim),repdiag(KY,dim)),repdiag(KZ,dim)); + }else if(dim ==2) + { + SparseMatrix ZZ(KX.rows()*2,KX.cols()); + K = cat(2,cat(2, + cat(2,repdiag(KX,dim),ZZ), + cat(2,repdiag(KY,dim),ZZ)), + cat(2,repdiag(KZ,dim),ZZ)); + }else + { + assert(false); + fprintf( + stderr, + "arap_rhs.h: Error: Unsupported dimension %d\n", + dim); + } + }else + { + assert(false); + fprintf( + stderr, + "arap_rhs.h: Error: Unsupported dimension %d\n", + Vdim); + return; + } + +} + diff --git a/src/igl/arap_rhs.h b/src/igl/arap_rhs.h new file mode 100644 index 0000000000..5a1c370c12 --- /dev/null +++ b/src/igl/arap_rhs.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ARAP_RHS_H +#define IGL_ARAP_RHS_H +#include "igl_inline.h" + +#include +#include +#include + +namespace igl +{ + // ARAP_RHS build right-hand side constructor of global poisson solve for + // various Arap energies + // Inputs: + // V #V by Vdim list of initial domain positions + // F #F by 3 list of triangle indices into V + // dim dimension being used at solve time. For deformation usually dim = + // V.cols(), for surface parameterization V.cols() = 3 and dim = 2 + // energy igl::ARAPEnergyType enum value defining which energy is being + // used. See igl::ARAPEnergyType.h for valid options and explanations. + // Outputs: + // K #V*dim by #(F|V)*dim*dim matrix such that: + // b = K * reshape(permute(R,[3 1 2]),size(V|F,1)*size(V,2)*size(V,2),1); + // + // See also: arap_linear_block + IGL_INLINE void arap_rhs( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const int dim, + const igl::ARAPEnergyType energy, + Eigen::SparseMatrix& K); +} +#ifndef IGL_STATIC_LIBRARY +#include "arap_rhs.cpp" +#endif +#endif diff --git a/src/igl/average_onto_faces.cpp b/src/igl/average_onto_faces.cpp new file mode 100644 index 0000000000..b5cec91ad8 --- /dev/null +++ b/src/igl/average_onto_faces.cpp @@ -0,0 +1,29 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "average_onto_faces.h" + +template +IGL_INLINE void igl::average_onto_faces(const Eigen::Matrix &V, + const Eigen::Matrix &F, + const Eigen::Matrix &S, + Eigen::Matrix &SF) +{ + + SF = Eigen::Matrix::Zero(F.rows(),S.cols()); + + for (int i = 0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_AVERAGE_ONTO_FACES_H +#define IGL_AVERAGE_ONTO_FACES_H +#include "igl_inline.h" + +#include +namespace igl +{ + // average_onto_vertices + // Move a scalar field defined on faces to vertices by averaging + // + // Input: + // V,F: mesh + // S: scalar field defined on vertices, Vx1 + // + // Output: + // SV: scalar field defined on faces + template + IGL_INLINE void average_onto_faces( + const Eigen::Matrix &V, + const Eigen::Matrix &F, + const Eigen::Matrix &S, + Eigen::Matrix &SF); +} + +#ifndef IGL_STATIC_LIBRARY +# include "average_onto_faces.cpp" +#endif + +#endif diff --git a/src/igl/average_onto_vertices.cpp b/src/igl/average_onto_vertices.cpp new file mode 100644 index 0000000000..a30854edb5 --- /dev/null +++ b/src/igl/average_onto_vertices.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "average_onto_vertices.h" + +template +IGL_INLINE void igl::average_onto_vertices(const Eigen::MatrixBase &V, + const Eigen::MatrixBase &F, + const Eigen::MatrixBase &S, + Eigen::MatrixBase &SV) +{ + SV = DerivedS::Zero(V.rows(),S.cols()); + Eigen::Matrix COUNT(V.rows()); + COUNT.setZero(); + for (int i = 0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_AVERAGE_ONTO_VERTICES_H +#define IGL_AVERAGE_ONTO_VERTICES_H +#include "igl_inline.h" + +#include +namespace igl +{ + // average_onto_vertices + // Move a scalar field defined on faces to vertices by averaging + // + // Input: + // V,F: mesh + // S: scalar field defined on faces, Fx1 + // + // Output: + // SV: scalar field defined on vertices + template + IGL_INLINE void average_onto_vertices(const Eigen::MatrixBase &V, + const Eigen::MatrixBase &F, + const Eigen::MatrixBase &S, + Eigen::MatrixBase &SV); +} + +#ifndef IGL_STATIC_LIBRARY +# include "average_onto_vertices.cpp" +#endif + +#endif diff --git a/src/igl/avg_edge_length.cpp b/src/igl/avg_edge_length.cpp new file mode 100644 index 0000000000..b813d484a9 --- /dev/null +++ b/src/igl/avg_edge_length.cpp @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "avg_edge_length.h" + +#include + +template +IGL_INLINE double igl::avg_edge_length( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F) +{ + double avg = 0; + long int count = 0; + + // Augh. Technically this is double counting interior edges... + for (unsigned i=0;i, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +template double igl::avg_edge_length, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +#endif diff --git a/src/igl/avg_edge_length.h b/src/igl/avg_edge_length.h new file mode 100644 index 0000000000..613f8f5dad --- /dev/null +++ b/src/igl/avg_edge_length.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_AVERAGEEDGELENGTH_H +#define IGL_AVERAGEEDGELENGTH_H + +#include "igl_inline.h" +#include +#include +#include + +namespace igl +{ + // Compute the average edge length for the given triangle mesh + // Templates: + // DerivedV derived from vertex positions matrix type: i.e. MatrixXd + // DerivedF derived from face indices matrix type: i.e. MatrixXi + // DerivedL derived from edge lengths matrix type: i.e. MatrixXd + // Inputs: + // V eigen matrix #V by 3 + // F #F by simplex-size list of mesh faces (must be simplex) + // Outputs: + // l average edge length + // + // See also: adjacency_matrix + template + IGL_INLINE double avg_edge_length( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "avg_edge_length.cpp" +#endif + +#endif diff --git a/src/igl/axis_angle_to_quat.cpp b/src/igl/axis_angle_to_quat.cpp new file mode 100644 index 0000000000..009d0522ce --- /dev/null +++ b/src/igl/axis_angle_to_quat.cpp @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "axis_angle_to_quat.h" +#include "EPS.h" +#include + +// http://www.antisphere.com/Wiki/tools:anttweakbar +template +IGL_INLINE void igl::axis_angle_to_quat( + const Q_type *axis, + const Q_type angle, + Q_type *out) +{ + Q_type n = axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]; + if( fabs(n)>igl::EPS()) + { + Q_type f = 0.5*angle; + out[3] = cos(f); + f = sin(f)/sqrt(n); + out[0] = axis[0]*f; + out[1] = axis[1]*f; + out[2] = axis[2]*f; + } + else + { + out[3] = 1.0; + out[0] = out[1] = out[2] = 0.0; + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::axis_angle_to_quat(double const*, double, double*); +// generated by autoexplicit.sh +template void igl::axis_angle_to_quat(float const*, float, float*); +#endif diff --git a/src/igl/axis_angle_to_quat.h b/src/igl/axis_angle_to_quat.h new file mode 100644 index 0000000000..6533b1becd --- /dev/null +++ b/src/igl/axis_angle_to_quat.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_AXIS_ANGLE_TO_QUAT_H +#define IGL_AXIS_ANGLE_TO_QUAT_H +#include "igl_inline.h" + +namespace igl +{ + // Convert axis angle representation of a rotation to a quaternion + // A Quaternion, q, is defined here as an arrays of four scalars (x,y,z,w), + // such that q = x*i + y*j + z*k + w + // Inputs: + // axis 3d vector + // angle scalar + // Outputs: + // quaternion + template + IGL_INLINE void axis_angle_to_quat( + const Q_type *axis, + const Q_type angle, + Q_type *out); +} + +#ifndef IGL_STATIC_LIBRARY +# include "axis_angle_to_quat.cpp" +#endif + +#endif diff --git a/src/igl/barycenter.cpp b/src/igl/barycenter.cpp new file mode 100644 index 0000000000..065f82aab0 --- /dev/null +++ b/src/igl/barycenter.cpp @@ -0,0 +1,57 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "barycenter.h" + +template < + typename DerivedV, + typename DerivedF, + typename DerivedBC> +IGL_INLINE void igl::barycenter( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & BC) +{ + BC.setZero(F.rows(),V.cols()); + // Loop over faces + for(int i = 0;i, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/barycenter.h b/src/igl/barycenter.h new file mode 100644 index 0000000000..ef78e94a63 --- /dev/null +++ b/src/igl/barycenter.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BARYCENTER_H +#define IGL_BARYCENTER_H +#include "igl_inline.h" +#include +namespace igl +{ + // Computes the barycenter of every simplex + // + // Inputs: + // V #V x dim matrix of vertex coordinates + // F #F x simplex_size matrix of indices of simplex corners into V + // Output: + // BC #F x dim matrix of 3d vertices + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedBC> + IGL_INLINE void barycenter( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & BC); +} + +#ifndef IGL_STATIC_LIBRARY +# include "barycenter.cpp" +#endif + +#endif diff --git a/src/igl/barycentric_coordinates.cpp b/src/igl/barycentric_coordinates.cpp new file mode 100644 index 0000000000..998a737bf2 --- /dev/null +++ b/src/igl/barycentric_coordinates.cpp @@ -0,0 +1,113 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "barycentric_coordinates.h" +#include "volume.h" + +template < + typename DerivedP, + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedD, + typename DerivedL> +IGL_INLINE void igl::barycentric_coordinates( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C, + const Eigen::MatrixBase & D, + Eigen::PlainObjectBase & L) +{ + using namespace Eigen; + assert(P.cols() == 3 && "query must be in 3d"); + assert(A.cols() == 3 && "corners must be in 3d"); + assert(B.cols() == 3 && "corners must be in 3d"); + assert(C.cols() == 3 && "corners must be in 3d"); + assert(D.cols() == 3 && "corners must be in 3d"); + assert(P.rows() == A.rows() && "Must have same number of queries as corners"); + assert(A.rows() == B.rows() && "Corners must be same size"); + assert(A.rows() == C.rows() && "Corners must be same size"); + assert(A.rows() == D.rows() && "Corners must be same size"); + typedef Matrix + VectorXS; + // Total volume + VectorXS vol,LA,LB,LC,LD; + volume(B,D,C,P,LA); + volume(A,C,D,P,LB); + volume(A,D,B,P,LC); + volume(A,B,C,P,LD); + volume(A,B,C,D,vol); + L.resize(P.rows(),4); + L< +IGL_INLINE void igl::barycentric_coordinates( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C, + Eigen::PlainObjectBase & L) +{ + using namespace Eigen; +#ifndef NDEBUG + const int DIM = P.cols(); + assert(A.cols() == DIM && "corners must be in same dimension as query"); + assert(B.cols() == DIM && "corners must be in same dimension as query"); + assert(C.cols() == DIM && "corners must be in same dimension as query"); + assert(P.rows() == A.rows() && "Must have same number of queries as corners"); + assert(A.rows() == B.rows() && "Corners must be same size"); + assert(A.rows() == C.rows() && "Corners must be same size"); +#endif + + // http://gamedev.stackexchange.com/a/23745 + typedef + Eigen::Array< + typename DerivedP::Scalar, + DerivedP::RowsAtCompileTime, + DerivedP::ColsAtCompileTime> + ArrayS; + typedef + Eigen::Array< + typename DerivedP::Scalar, + DerivedP::RowsAtCompileTime, + 1> + VectorS; + + const ArrayS v0 = B.array() - A.array(); + const ArrayS v1 = C.array() - A.array(); + const ArrayS v2 = P.array() - A.array(); + VectorS d00 = (v0*v0).rowwise().sum(); + VectorS d01 = (v0*v1).rowwise().sum(); + VectorS d11 = (v1*v1).rowwise().sum(); + VectorS d20 = (v2*v0).rowwise().sum(); + VectorS d21 = (v2*v1).rowwise().sum(); + VectorS denom = d00 * d11 - d01 * d01; + L.resize(P.rows(),3); + L.col(1) = (d11 * d20 - d01 * d21) / denom; + L.col(2) = (d00 * d21 - d01 * d20) / denom; + L.col(0) = 1.0f -(L.col(1) + L.col(2)).array(); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::barycentric_coordinates, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycentric_coordinates, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycentric_coordinates, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycentric_coordinates, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycentric_coordinates, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::barycentric_coordinates, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::PlainObjectBase >&); +template void igl::barycentric_coordinates, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::PlainObjectBase >&); +template void igl::barycentric_coordinates, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/barycentric_coordinates.h b/src/igl/barycentric_coordinates.h new file mode 100644 index 0000000000..ec08669f29 --- /dev/null +++ b/src/igl/barycentric_coordinates.h @@ -0,0 +1,68 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BARYCENTRIC_COORDINATES_H +#define IGL_BARYCENTRIC_COORDINATES_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute barycentric coordinates in a tet + // + // Inputs: + // P #P by 3 Query points in 3d + // A #P by 3 Tet corners in 3d + // B #P by 3 Tet corners in 3d + // C #P by 3 Tet corners in 3d + // D #P by 3 Tet corners in 3d + // Outputs: + // L #P by 4 list of barycentric coordinates + // + template < + typename DerivedP, + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedD, + typename DerivedL> + IGL_INLINE void barycentric_coordinates( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C, + const Eigen::MatrixBase & D, + Eigen::PlainObjectBase & L); + // Compute barycentric coordinates in a triangle + // + // Inputs: + // P #P by dim Query points + // A #P by dim Triangle corners + // B #P by dim Triangle corners + // C #P by dim Triangle corners + // Outputs: + // L #P by 3 list of barycentric coordinates + // + template < + typename DerivedP, + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedL> + IGL_INLINE void barycentric_coordinates( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C, + Eigen::PlainObjectBase & L); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "barycentric_coordinates.cpp" +#endif + +#endif diff --git a/src/igl/barycentric_to_global.cpp b/src/igl/barycentric_to_global.cpp new file mode 100755 index 0000000000..e2656ad703 --- /dev/null +++ b/src/igl/barycentric_to_global.cpp @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "barycentric_to_global.h" + +// For error printing +#include +#include + +namespace igl +{ + template + IGL_INLINE Eigen::Matrix barycentric_to_global( + const Eigen::Matrix & V, + const Eigen::Matrix & F, + const Eigen::Matrix & bc) + { + Eigen::Matrix R; + R.resize(bc.rows(),3); + + for (unsigned i=0; i igl::barycentric_to_global(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); +#endif diff --git a/src/igl/barycentric_to_global.h b/src/igl/barycentric_to_global.h new file mode 100755 index 0000000000..466187173b --- /dev/null +++ b/src/igl/barycentric_to_global.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BARYCENTRIC2GLOBAL_H +#define IGL_BARYCENTRIC2GLOBAL_H +#include + +#include +#include + +namespace igl +{ + // Converts barycentric coordinates in the embree form to 3D coordinates + // Embree stores barycentric coordinates as triples: fid, bc1, bc2 + // fid is the id of a face, bc1 is the displacement of the point wrt the + // first vertex v0 and the edge v1-v0. Similarly, bc2 is the displacement + // wrt v2-v0. + // + // Input: + // V: #Vx3 Vertices of the mesh + // F: #Fxe Faces of the mesh + // bc: #Xx3 Barycentric coordinates, one row per point + // + // Output: + // #X: #Xx3 3D coordinates of all points in bc + template + IGL_INLINE Eigen::Matrix + barycentric_to_global( + const Eigen::Matrix & V, + const Eigen::Matrix & F, + const Eigen::Matrix & bc); +} + +#ifndef IGL_STATIC_LIBRARY +# include "barycentric_to_global.cpp" +#endif + +#endif diff --git a/src/igl/basename.cpp b/src/igl/basename.cpp new file mode 100644 index 0000000000..ae4fb118e5 --- /dev/null +++ b/src/igl/basename.cpp @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "basename.h" + +#include + +IGL_INLINE std::string igl::basename(const std::string & path) +{ + if(path == "") + { + return std::string(""); + } + // http://stackoverflow.com/questions/5077693/dirnamephp-similar-function-in-c + std::string::const_reverse_iterator last_slash = + std::find( + path.rbegin(), + path.rend(), '/'); + if( last_slash == path.rend() ) + { + // No slashes found + return path; + }else if(1 == (last_slash.base() - path.begin())) + { + // Slash is first char + return std::string(path.begin()+1,path.end()); + }else if(path.end() == last_slash.base() ) + { + // Slash is last char + std::string redo = std::string(path.begin(),path.end()-1); + return igl::basename(redo); + } + return std::string(last_slash.base(),path.end()); +} diff --git a/src/igl/basename.h b/src/igl/basename.h new file mode 100644 index 0000000000..fb333b5c42 --- /dev/null +++ b/src/igl/basename.h @@ -0,0 +1,29 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BASENAME_H +#define IGL_BASENAME_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Function like PHP's basename: /etc/sudoers.d --> sudoers.d + // Input: + // path string containing input path + // Returns string containing basename (see php's basename) + // + // See also: dirname, pathinfo + IGL_INLINE std::string basename(const std::string & path); +} + +#ifndef IGL_STATIC_LIBRARY +# include "basename.cpp" +#endif + +#endif diff --git a/src/igl/bbw.cpp b/src/igl/bbw.cpp new file mode 100644 index 0000000000..c7df7dd3b3 --- /dev/null +++ b/src/igl/bbw.cpp @@ -0,0 +1,143 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "bbw.h" +#include "min_quad_with_fixed.h" +#include "harmonic.h" +#include "parallel_for.h" +#include +#include +#include +#include + +igl::BBWData::BBWData(): + partition_unity(false), + W0(), + active_set_params(), + verbosity(0) +{ + // We know that the Bilaplacian is positive semi-definite + active_set_params.Auu_pd = true; +} + +void igl::BBWData::print() +{ + using namespace std; + cout<<"partition_unity: "< +IGL_INLINE bool igl::bbw( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & Ele, + const Eigen::PlainObjectBase & b, + const Eigen::PlainObjectBase & bc, + igl::BBWData & data, + Eigen::PlainObjectBase & W + ) +{ + using namespace std; + using namespace Eigen; + assert(!data.partition_unity && "partition_unity not implemented yet"); + // number of domain vertices + int n = V.rows(); + // number of handles + int m = bc.cols(); + // Build biharmonic operator + Eigen::SparseMatrix Q; + harmonic(V,Ele,2,Q); + W.derived().resize(n,m); + // No linear terms + VectorXd c = VectorXd::Zero(n); + // No linear constraints + SparseMatrix A(0,n),Aeq(0,n),Aieq(0,n); + VectorXd Beq(0,1),Bieq(0,1); + // Upper and lower box constraints (Constant bounds) + VectorXd ux = VectorXd::Ones(n); + VectorXd lx = VectorXd::Zero(n); + active_set_params eff_params = data.active_set_params; + if(data.verbosity >= 1) + { + cout<<"BBW: max_iter: "<= 1) + { + cout<<"BBW: Computing initial weights for "< mqwf; + min_quad_with_fixed_precompute(Q,b,Aeq,true,mqwf); + min_quad_with_fixed_solve(mqwf,c,bc,Beq,W); + // decrement + eff_params.max_iter--; + bool error = false; + // Loop over handles + std::mutex critical; + const auto & optimize_weight = [&](const int i) + { + // Quicker exit for paralle_for + if(error) + { + return; + } + if(data.verbosity >= 1) + { + std::lock_guard lock(critical); + cout<<"BBW: Computing weight for handle "<, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, igl::BBWData&, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/bbw.h b/src/igl/bbw.h new file mode 100644 index 0000000000..bec20bb984 --- /dev/null +++ b/src/igl/bbw.h @@ -0,0 +1,77 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BBW_H +#define IGL_BBW_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Container for BBW computation related data and flags + class BBWData + { + public: + // Enforce partition of unity during optimization (optimize all weight + // simultaneously) + bool partition_unity; + // Initial guess + Eigen::MatrixXd W0; + igl::active_set_params active_set_params; + // Verbosity level + // 0: quiet + // 1: loud + // 2: louder + int verbosity; + public: + IGL_INLINE BBWData(); + // Print current state of object + IGL_INLINE void print(); + }; + + // Compute Bounded Biharmonic Weights on a given domain (V,Ele) with a given + // set of boundary conditions + // + // Templates + // DerivedV derived type of eigen matrix for V (e.g. MatrixXd) + // DerivedF derived type of eigen matrix for F (e.g. MatrixXi) + // Derivedb derived type of eigen matrix for b (e.g. VectorXi) + // Derivedbc derived type of eigen matrix for bc (e.g. MatrixXd) + // DerivedW derived type of eigen matrix for W (e.g. MatrixXd) + // Inputs: + // V #V by dim vertex positions + // Ele #Elements by simplex-size list of element indices + // b #b boundary indices into V + // bc #b by #W list of boundary values + // data object containing options, initial guess --> solution and results + // Outputs: + // W #V by #W list of *unnormalized* weights to normalize use + // igl::normalize_row_sums(W,W); + // Returns true on success, false on failure + template < + typename DerivedV, + typename DerivedEle, + typename Derivedb, + typename Derivedbc, + typename DerivedW> + IGL_INLINE bool bbw( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & Ele, + const Eigen::PlainObjectBase & b, + const Eigen::PlainObjectBase & bc, + BBWData & data, + Eigen::PlainObjectBase & W); +} + +#ifndef IGL_STATIC_LIBRARY +# include "bbw.cpp" +#endif + +#endif + diff --git a/src/igl/bfs.cpp b/src/igl/bfs.cpp new file mode 100644 index 0000000000..8ff51c0500 --- /dev/null +++ b/src/igl/bfs.cpp @@ -0,0 +1,93 @@ +#include "bfs.h" +#include "list_to_matrix.h" +#include +#include + +template < + typename AType, + typename DerivedD, + typename DerivedP> +IGL_INLINE void igl::bfs( + const AType & A, + const size_t s, + Eigen::PlainObjectBase & D, + Eigen::PlainObjectBase & P) +{ + std::vector vD; + std::vector vP; + bfs(A,s,vD,vP); + list_to_matrix(vD,D); + list_to_matrix(vP,P); +} + +template < + typename AType, + typename DType, + typename PType> +IGL_INLINE void igl::bfs( + const std::vector > & A, + const size_t s, + std::vector & D, + std::vector & P) +{ + // number of nodes + int N = s+1; + for(const auto & Ai : A) for(const auto & a : Ai) N = std::max(N,a+1); + std::vector seen(N,false); + P.resize(N,-1); + std::queue > Q; + Q.push({s,-1}); + while(!Q.empty()) + { + const int f = Q.front().first; + const int p = Q.front().second; + Q.pop(); + if(seen[f]) + { + continue; + } + D.push_back(f); + P[f] = p; + seen[f] = true; + for(const auto & n : A[f]) Q.push({n,f}); + } +} + + +template < + typename AType, + typename DType, + typename PType> +IGL_INLINE void igl::bfs( + const Eigen::SparseMatrix & A, + const size_t s, + std::vector & D, + std::vector & P) +{ + // number of nodes + int N = A.rows(); + assert(A.rows() == A.cols()); + std::vector seen(N,false); + P.resize(N,-1); + std::queue > Q; + Q.push({s,-1}); + while(!Q.empty()) + { + const int f = Q.front().first; + const int p = Q.front().second; + Q.pop(); + if(seen[f]) + { + continue; + } + D.push_back(f); + P[f] = p; + seen[f] = true; + for(typename Eigen::SparseMatrix::InnerIterator it (A,f); it; ++it) + { + if(it.value()) Q.push({it.index(),f}); + } + } + +} + diff --git a/src/igl/bfs.h b/src/igl/bfs.h new file mode 100644 index 0000000000..6f49e93eb8 --- /dev/null +++ b/src/igl/bfs.h @@ -0,0 +1,54 @@ +#ifndef IGL_BFS_H +#define IGL_BFS_H +#include "igl_inline.h" +#include +#include +#include +namespace igl +{ + // Traverse a **directed** graph represented by an adjacency list using + // breadth first search + // + // Inputs: + // A #V list of adjacency lists or #V by #V adjacency matrix + // s starting node (index into A) + // Outputs: + // D #V list of indices into rows of A in the order in which graph nodes + // are discovered. + // P #V list of indices into rows of A of predecessor in resulting + // spanning tree {-1 indicates root/not discovered), order corresponds to + // V **not** D. + template < + typename AType, + typename DerivedD, + typename DerivedP> + IGL_INLINE void bfs( + const AType & A, + const size_t s, + Eigen::PlainObjectBase & D, + Eigen::PlainObjectBase & P); + + template < + typename AType, + typename DType, + typename PType> + IGL_INLINE void bfs( + const std::vector > & A, + const size_t s, + std::vector & D, + std::vector & P); + template < + typename AType, + typename DType, + typename PType> + IGL_INLINE void bfs( + const Eigen::SparseMatrix & A, + const size_t s, + std::vector & D, + std::vector & P); +} +#ifndef IGL_STATIC_LIBRARY +# include "bfs.cpp" +#endif +#endif + diff --git a/src/igl/bfs_orient.cpp b/src/igl/bfs_orient.cpp new file mode 100644 index 0000000000..23dd93a495 --- /dev/null +++ b/src/igl/bfs_orient.cpp @@ -0,0 +1,100 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "bfs_orient.h" +#include "orientable_patches.h" +#include +#include + +template +IGL_INLINE void igl::bfs_orient( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & C) +{ + using namespace Eigen; + using namespace std; + SparseMatrix A; + orientable_patches(F,C,A); + + // number of faces + const int m = F.rows(); + // number of patches + const int num_cc = C.maxCoeff()+1; + VectorXi seen = VectorXi::Zero(m); + + // Edge sets + const int ES[3][2] = {{1,2},{2,0},{0,1}}; + + if(&FF != &F) + { + FF = F; + } + // loop over patches +#pragma omp parallel for + for(int c = 0;c Q; + // find first member of patch c + for(int f = 0;f 0) + { + continue; + } + seen(f)++; + // loop over neighbors of f + for(typename SparseMatrix::InnerIterator it (A,f); it; ++it) + { + // might be some lingering zeros, and skip self-adjacency + if(it.value() != 0 && it.row() != f) + { + const int n = it.row(); + assert(n != f); + // loop over edges of f + for(int efi = 0;efi<3;efi++) + { + // efi'th edge of face f + Vector2i ef(FF(f,ES[efi][0]),FF(f,ES[efi][1])); + // loop over edges of n + for(int eni = 0;eni<3;eni++) + { + // eni'th edge of face n + Vector2i en(FF(n,ES[eni][0]),FF(n,ES[eni][1])); + // Match (half-edges go same direction) + if(ef(0) == en(0) && ef(1) == en(1)) + { + // flip face n + FF.row(n) = FF.row(n).reverse().eval(); + } + } + } + // add neighbor to queue + Q.push(n); + } + } + } + } + + // make sure flip is OK if &FF = &F +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::bfs_orient, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/bfs_orient.h b/src/igl/bfs_orient.h new file mode 100644 index 0000000000..b37f397d8f --- /dev/null +++ b/src/igl/bfs_orient.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BFS_ORIENT_H +#define IGL_BFS_ORIENT_H +#include +#include + +namespace igl +{ + // Consistently orient faces in orientable patches using BFS + // + // F = bfs_orient(F,V); + // + // Inputs: + // F #F by 3 list of faces + // Outputs: + // FF #F by 3 list of faces (OK if same as F) + // C #F list of component ids + // + // + template + IGL_INLINE void bfs_orient( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & C); +}; +#ifndef IGL_STATIC_LIBRARY +# include "bfs_orient.cpp" +#endif + +#endif diff --git a/src/igl/biharmonic_coordinates.cpp b/src/igl/biharmonic_coordinates.cpp new file mode 100644 index 0000000000..686ecd5e49 --- /dev/null +++ b/src/igl/biharmonic_coordinates.cpp @@ -0,0 +1,203 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "biharmonic_coordinates.h" +#include "cotmatrix.h" +#include "sum.h" +#include "massmatrix.h" +#include "min_quad_with_fixed.h" +#include "crouzeix_raviart_massmatrix.h" +#include "crouzeix_raviart_cotmatrix.h" +#include "normal_derivative.h" +#include "on_boundary.h" +#include + +template < + typename DerivedV, + typename DerivedT, + typename SType, + typename DerivedW> +IGL_INLINE bool igl::biharmonic_coordinates( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & T, + const std::vector > & S, + Eigen::PlainObjectBase & W) +{ + return biharmonic_coordinates(V,T,S,2,W); +} + +template < + typename DerivedV, + typename DerivedT, + typename SType, + typename DerivedW> +IGL_INLINE bool igl::biharmonic_coordinates( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & T, + const std::vector > & S, + const int k, + Eigen::PlainObjectBase & W) +{ + using namespace Eigen; + using namespace std; + // This is not the most efficient way to build A, but follows "Linear + // Subspace Design for Real-Time Shape Deformation" [Wang et al. 2015]. + SparseMatrix A; + { + DiagonalMatrix Minv; + SparseMatrix L,K; + Array C; + { + Array I; + on_boundary(T,I,C); + } +#ifdef false + // Version described in paper is "wrong" + // http://www.cs.toronto.edu/~jacobson/images/error-in-linear-subspace-design-for-real-time-shape-deformation-2017-wang-et-al.pdf + SparseMatrix N,Z,M; + normal_derivative(V,T,N); + { + std::vector >ZIJV; + for(int t =0;t M; + Eigen::MatrixXi E; + Eigen::VectorXi EMAP; + crouzeix_raviart_massmatrix(V,T,M,E,EMAP); + crouzeix_raviart_cotmatrix(V,T,E,EMAP,L); + // Ad #E by #V facet-vertex incidence matrix + Eigen::SparseMatrix Ad(E.rows(),V.rows()); + { + std::vector > AIJV(E.size()); + for(int e = 0;e(e,E(e,c),1); + } + } + Ad.setFromTriplets(AIJV.begin(),AIJV.end()); + } + // Degrees + Eigen::VectorXd De; + sum(Ad,2,De); + Eigen::DiagonalMatrix De_diag = + De.array().inverse().matrix().asDiagonal(); + K = L*(De_diag*Ad); + // normalize + M /= ((VectorXd)M.diagonal()).array().abs().maxCoeff(); + Minv = ((VectorXd)M.diagonal().array().inverse()).asDiagonal(); + // kill boundary edges + for(int f = 0;f & h){return h.size()==1;}); + // number of region handles + const size_t r = S.size()-mp; + // Vertices in region handles + size_t mr = 0; + for(const auto & h : S) + { + if(h.size() > 1) + { + mr += h.size(); + } + } + const size_t dim = T.cols()-1; + // Might as well be dense... I think... + MatrixXd J = MatrixXd::Zero(mp+mr,mp+r*(dim+1)); + VectorXi b(mp+mr); + MatrixXd H(mp+r*(dim+1),dim); + { + int v = 0; + int c = 0; + for(int h = 0;h= dim+1); + for(int p = 0;p(),VectorXd(),true,W); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::biharmonic_coordinates, Eigen::Matrix, int, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/biharmonic_coordinates.h b/src/igl/biharmonic_coordinates.h new file mode 100644 index 0000000000..26f67329a3 --- /dev/null +++ b/src/igl/biharmonic_coordinates.h @@ -0,0 +1,90 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BIHARMONIC_COORDINATES_H +#define IGL_BIHARMONIC_COORDINATES_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Compute "discrete biharmonic generalized barycentric coordinates" as + // described in "Linear Subspace Design for Real-Time Shape Deformation" + // [Wang et al. 2015]. Not to be confused with "Bounded Biharmonic Weights + // for Real-Time Deformation" [Jacobson et al. 2011] or "Biharmonic + // Coordinates" (2D complex barycentric coordinates) [Weber et al. 2012]. + // These weights minimize a discrete version of the squared Laplacian energy + // subject to positional interpolation constraints at selected vertices + // (point handles) and transformation interpolation constraints at regions + // (region handles). + // + // Templates: + // HType should be a simple index type e.g. `int`,`size_t` + // Inputs: + // V #V by dim list of mesh vertex positions + // T #T by dim+1 list of / triangle indices into V if dim=2 + // \ tetrahedron indices into V if dim=3 + // S #point-handles+#region-handles list of lists of selected vertices for + // each handle. Point handles should have singleton lists and region + // handles should have lists of size at least dim+1 (and these points + // should be in general position). + // Outputs: + // W #V by #points-handles+(#region-handles * dim+1) matrix of weights so + // that columns correspond to each handles generalized barycentric + // coordinates (for point-handles) or animation space weights (for region + // handles). + // returns true only on success + // + // Example: + // + // MatrixXd W; + // igl::biharmonic_coordinates(V,F,S,W); + // const size_t dim = T.cols()-1; + // MatrixXd H(W.cols(),dim); + // { + // int c = 0; + // for(int h = 0;h + IGL_INLINE bool biharmonic_coordinates( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & T, + const std::vector > & S, + Eigen::PlainObjectBase & W); + // k 2-->biharmonic, 3-->triharmonic + template < + typename DerivedV, + typename DerivedT, + typename SType, + typename DerivedW> + IGL_INLINE bool biharmonic_coordinates( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & T, + const std::vector > & S, + const int k, + Eigen::PlainObjectBase & W); + +}; +# ifndef IGL_STATIC_LIBRARY +# include "biharmonic_coordinates.cpp" +# endif +#endif diff --git a/src/igl/bijective_composite_harmonic_mapping.cpp b/src/igl/bijective_composite_harmonic_mapping.cpp new file mode 100644 index 0000000000..517109c04f --- /dev/null +++ b/src/igl/bijective_composite_harmonic_mapping.cpp @@ -0,0 +1,115 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "bijective_composite_harmonic_mapping.h" + +#include "slice.h" +#include "doublearea.h" +#include "harmonic.h" +//#include "matlab/MatlabWorkspace.h" +#include + +template < + typename DerivedV, + typename DerivedF, + typename Derivedb, + typename Derivedbc, + typename DerivedU> +IGL_INLINE bool igl::bijective_composite_harmonic_mapping( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & b, + const Eigen::MatrixBase & bc, + Eigen::PlainObjectBase & U) +{ + return bijective_composite_harmonic_mapping(V,F,b,bc,1,200,20,true,U); +} + +template < + typename DerivedV, + typename DerivedF, + typename Derivedb, + typename Derivedbc, + typename DerivedU> +IGL_INLINE bool igl::bijective_composite_harmonic_mapping( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & b, + const Eigen::MatrixBase & bc, + const int min_steps, + const int max_steps, + const int num_inner_iters, + const bool test_for_flips, + Eigen::PlainObjectBase & U) +{ + typedef typename Derivedbc::Scalar Scalar; + assert(V.cols() == 2 && bc.cols() == 2 && "Input should be 2D"); + assert(F.cols() == 3 && "F should contain triangles"); + int tries = 0; + int nsteps = min_steps; + Derivedbc bc0; + slice(V,b,1,bc0); + + // It's difficult to check for flips "robustly" in the sense that the input + // mesh might not have positive/consistent sign to begin with. + + while(nsteps<=max_steps) + { + U = V; + int flipped = 0; + int nans = 0; + int step = 0; + for(;step<=nsteps;step++) + { + const Scalar t = ((Scalar)step)/((Scalar)nsteps); + // linearly interpolate boundary conditions + // TODO: replace this with something that guarantees a homotopic "morph" + // of the boundary conditions. Something like "Homotopic Morphing of + // Planar Curves" [Dym et al. 2015] but also handling multiple connected + // components. + Derivedbc bct = bc0 + t*(bc - bc0); + // Compute dsicrete harmonic map using metric of previous step + for(int iter = 0;iter A; + doublearea(U,F,A); + flipped = (A.array() < 0 ).count(); + //std::cout<<" "< 0 || nans>0) break; + } + if(flipped == 0 && nans == 0) + { + return step == nsteps+1; + } + nsteps *= 2; + } + //std::cout<<"failed to finish in "<, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::bijective_composite_harmonic_mapping, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, int, int, bool, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/bijective_composite_harmonic_mapping.h b/src/igl/bijective_composite_harmonic_mapping.h new file mode 100644 index 0000000000..f055e5293c --- /dev/null +++ b/src/igl/bijective_composite_harmonic_mapping.h @@ -0,0 +1,79 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BIJECTIVE_COMPOSITE_HARMONIC_MAPPING_H +#define IGL_BIJECTIVE_COMPOSITE_HARMONIC_MAPPING_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Compute a planar mapping of a triangulated polygon (V,F) subjected to + // boundary conditions (b,bc). The mapping should be bijective in the sense + // that no triangles' areas become negative (this assumes they started + // positive). This mapping is computed by "composing" harmonic mappings + // between incremental morphs of the boundary conditions. This is a bit like + // a discrete version of "Bijective Composite Mean Value Mappings" [Schneider + // et al. 2013] but with a discrete harmonic map (cf. harmonic coordinates) + // instead of mean value coordinates. This is inspired by "Embedding a + // triangular graph within a given boundary" [Xu et al. 2011]. + // + // Inputs: + // V #V by 2 list of triangle mesh vertex positions + // F #F by 3 list of triangle indices into V + // b #b list of boundary indices into V + // bc #b by 2 list of boundary conditions corresponding to b + // Outputs: + // U #V by 2 list of output mesh vertex locations + // Returns true if and only if U contains a successful bijectie mapping + // + // + template < + typename DerivedV, + typename DerivedF, + typename Derivedb, + typename Derivedbc, + typename DerivedU> + IGL_INLINE bool bijective_composite_harmonic_mapping( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & b, + const Eigen::MatrixBase & bc, + Eigen::PlainObjectBase & U); + // + // Inputs: + // min_steps minimum number of steps to take from V(b,:) to bc + // max_steps minimum number of steps to take from V(b,:) to bc (if + // max_steps == min_steps then no further number of steps will be tried) + // num_inner_iters number of iterations of harmonic solves to run after + // for each morph step (to try to push flips back in) + // test_for_flips whether to check if flips occurred (and trigger more + // steps). if test_for_flips = false then this function always returns + // true + // + template < + typename DerivedV, + typename DerivedF, + typename Derivedb, + typename Derivedbc, + typename DerivedU> + IGL_INLINE bool bijective_composite_harmonic_mapping( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & b, + const Eigen::MatrixBase & bc, + const int min_steps, + const int max_steps, + const int num_inner_iters, + const bool test_for_flips, + Eigen::PlainObjectBase & U); +} + +#ifndef IGL_STATIC_LIBRARY +# include "bijective_composite_harmonic_mapping.cpp" +#endif +#endif diff --git a/src/igl/bone_parents.cpp b/src/igl/bone_parents.cpp new file mode 100644 index 0000000000..d5cd6ddfa4 --- /dev/null +++ b/src/igl/bone_parents.cpp @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "bone_parents.h" + +template +IGL_INLINE void igl::bone_parents( + const Eigen::PlainObjectBase& BE, + Eigen::PlainObjectBase& P) +{ + P.resize(BE.rows(),1); + // Stupid O(n²) version + for(int e = 0;e, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/bone_parents.h b/src/igl/bone_parents.h new file mode 100644 index 0000000000..e0091cdfc7 --- /dev/null +++ b/src/igl/bone_parents.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BONE_PARENTS_H +#define IGL_BONE_PARENTS_H +#include "igl_inline.h" +#include +namespace igl +{ + // BONE_PARENTS Recover "parent" bones from directed graph representation. + // + // Inputs: + // BE #BE by 2 list of directed bone edges + // Outputs: + // P #BE by 1 list of parent indices into BE, -1 means root. + // + template + IGL_INLINE void bone_parents( + const Eigen::PlainObjectBase& BE, + Eigen::PlainObjectBase& P); +} + +#ifndef IGL_STATIC_LIBRARY +# include "bone_parents.cpp" +#endif + +#endif + diff --git a/src/igl/boundary_conditions.cpp b/src/igl/boundary_conditions.cpp new file mode 100644 index 0000000000..f7146cd70b --- /dev/null +++ b/src/igl/boundary_conditions.cpp @@ -0,0 +1,192 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "boundary_conditions.h" + +#include "verbose.h" +#include "EPS.h" +#include "project_to_line.h" + +#include +#include +#include + +IGL_INLINE bool igl::boundary_conditions( + const Eigen::MatrixXd & V , + const Eigen::MatrixXi & /*Ele*/, + const Eigen::MatrixXd & C , + const Eigen::VectorXi & P , + const Eigen::MatrixXi & BE , + const Eigen::MatrixXi & CE , + Eigen::VectorXi & b , + Eigen::MatrixXd & bc ) +{ + using namespace Eigen; + using namespace std; + + if(P.size()+BE.rows() == 0) + { + verbose("^%s: Error: no handles found\n",__FUNCTION__); + return false; + } + + vector bci; + vector bcj; + vector bcv; + + // loop over points + for(int p = 0;p FLOAT_EPS) + { + verbose("^%s: Error: handle %d does not receive 0 weight\n",__FUNCTION__,i); + return false; + } + if(max_c< (1-FLOAT_EPS)) + { + verbose("^%s: Error: handle %d does not receive 1 weight\n",__FUNCTION__,i); + return false; + } + } + + return true; +} diff --git a/src/igl/boundary_conditions.h b/src/igl/boundary_conditions.h new file mode 100644 index 0000000000..643dbe7c1d --- /dev/null +++ b/src/igl/boundary_conditions.h @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BOUNDARY_CONDITIONS_H +#define IGL_BOUNDARY_CONDITIONS_H +#include "igl_inline.h" +#include + +namespace igl +{ + + // Compute boundary conditions for automatic weights computation. This + // function expects that the given mesh (V,Ele) has sufficient samples + // (vertices) exactly at point handle locations and exactly along bone and + // cage edges. + // + // Inputs: + // V #V by dim list of domain vertices + // Ele #Ele by simplex-size list of simplex indices + // C #C by dim list of handle positions + // P #P by 1 list of point handle indices into C + // BE #BE by 2 list of bone edge indices into C + // CE #CE by 2 list of cage edge indices into *P* + // Outputs: + // b #b list of boundary indices (indices into V of vertices which have + // known, fixed values) + // bc #b by #weights list of known/fixed values for boundary vertices + // (notice the #b != #weights in general because #b will include all the + // intermediary samples along each bone, etc.. The ordering of the + // weights corresponds to [P;BE] + // Returns false if boundary conditions are suspicious: + // P and BE are empty + // bc is empty + // some column of bc doesn't have a 0 (assuming bc has >1 columns) + // some column of bc doesn't have a 1 (assuming bc has >1 columns) + IGL_INLINE bool boundary_conditions( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & Ele, + const Eigen::MatrixXd & C, + const Eigen::VectorXi & P, + const Eigen::MatrixXi & BE, + const Eigen::MatrixXi & CE, + Eigen::VectorXi & b, + Eigen::MatrixXd & bc); +} + +#ifndef IGL_STATIC_LIBRARY +# include "boundary_conditions.cpp" +#endif + +#endif diff --git a/src/igl/boundary_facets.cpp b/src/igl/boundary_facets.cpp new file mode 100644 index 0000000000..67e74db4f4 --- /dev/null +++ b/src/igl/boundary_facets.cpp @@ -0,0 +1,141 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "boundary_facets.h" +#include "face_occurrences.h" + +// IGL includes +#include "sort.h" + +// STL includes +#include +#include + +template +IGL_INLINE void igl::boundary_facets( + const std::vector > & T, + std::vector > & F) +{ + using namespace std; + + if(T.size() == 0) + { + F.clear(); + return; + } + + int simplex_size = T[0].size(); + // Get a list of all faces + vector > allF( + T.size()*simplex_size, + vector(simplex_size-1)); + + // Gather faces, loop over tets + for(int i = 0; i< (int)T.size();i++) + { + assert((int)T[i].size() == simplex_size); + switch(simplex_size) + { + case 4: + // get face in correct order + allF[i*simplex_size+0][0] = T[i][1]; + allF[i*simplex_size+0][1] = T[i][3]; + allF[i*simplex_size+0][2] = T[i][2]; + // get face in correct order + allF[i*simplex_size+1][0] = T[i][0]; + allF[i*simplex_size+1][1] = T[i][2]; + allF[i*simplex_size+1][2] = T[i][3]; + // get face in correct order + allF[i*simplex_size+2][0] = T[i][0]; + allF[i*simplex_size+2][1] = T[i][3]; + allF[i*simplex_size+2][2] = T[i][1]; + // get face in correct order + allF[i*simplex_size+3][0] = T[i][0]; + allF[i*simplex_size+3][1] = T[i][1]; + allF[i*simplex_size+3][2] = T[i][2]; + break; + case 3: + allF[i*simplex_size+0][0] = T[i][1]; + allF[i*simplex_size+0][1] = T[i][2]; + allF[i*simplex_size+1][0] = T[i][2]; + allF[i*simplex_size+1][1] = T[i][0]; + allF[i*simplex_size+2][0] = T[i][0]; + allF[i*simplex_size+2][1] = T[i][1]; + break; + } + } + + // Counts + vector C; + face_occurrences(allF,C); + + // Q: Why not just count the number of ones? + // A: because we are including non-manifold edges as boundary edges + int twos = (int) count(C.begin(),C.end(),2); + //int ones = (int) count(C.begin(),C.end(),1); + // Resize output to fit number of ones + F.resize(allF.size() - twos); + //F.resize(ones); + int k = 0; + for(int i = 0;i< (int)allF.size();i++) + { + if(C[i] != 2) + { + assert(k<(int)F.size()); + F[k] = allF[i]; + k++; + } + } + assert(k==(int)F.size()); + //if(k != F.size()) + //{ + // printf("%d =? %d\n",k,F.size()); + //} + +} + +#include "list_to_matrix.h" +#include "matrix_to_list.h" + +template +IGL_INLINE void igl::boundary_facets( + const Eigen::PlainObjectBase& T, + Eigen::PlainObjectBase& F) +{ + assert(T.cols() == 0 || T.cols() == 4 || T.cols() == 3); + using namespace std; + using namespace Eigen; + // Cop out: use vector of vectors version + vector > vT; + matrix_to_list(T,vT); + vector > vF; + boundary_facets(vT,vF); + list_to_matrix(vF,F); +} + +template +Ret igl::boundary_facets( + const Eigen::PlainObjectBase& T) +{ + Ret F; + igl::boundary_facets(T,F); + return F; +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::boundary_facets, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::boundary_facets, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::boundary_facets, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::boundary_facets(std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > >&); +//template Eigen::PlainObjectBase > igl::boundary_facets(Eigen::PlainObjectBase > const&); +template Eigen::Matrix igl::boundary_facets, Eigen::Matrix >(Eigen::PlainObjectBase > const&); +template void igl::boundary_facets, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/boundary_facets.h b/src/igl/boundary_facets.h new file mode 100644 index 0000000000..74ac032a1a --- /dev/null +++ b/src/igl/boundary_facets.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BOUNDARY_FACETS_H +#define IGL_BOUNDARY_FACETS_H +#include "igl_inline.h" + +#include + +#include + +namespace igl +{ + // BOUNDARY_FACETS Determine boundary faces (edges) of tetrahedra (triangles) + // stored in T (analogous to qptoolbox's `outline` and `boundary_faces`). + // + // Templates: + // IntegerT integer-value: e.g. int + // IntegerF integer-value: e.g. int + // Input: + // T tetrahedron (triangle) index list, m by 4 (3), where m is the number of tetrahedra + // Output: + // F list of boundary faces, n by 3 (2), where n is the number of boundary faces + // + // + template + IGL_INLINE void boundary_facets( + const std::vector > & T, + std::vector > & F); + + // Templates: + // DerivedT integer-value: i.e. from MatrixXi + // DerivedF integer-value: i.e. from MatrixXi + template + IGL_INLINE void boundary_facets( + const Eigen::PlainObjectBase& T, + Eigen::PlainObjectBase& F); + // Same as above but returns F + template + Ret boundary_facets( + const Eigen::PlainObjectBase& T); +} + +#ifndef IGL_STATIC_LIBRARY +# include "boundary_facets.cpp" +#endif + +#endif diff --git a/src/igl/boundary_loop.cpp b/src/igl/boundary_loop.cpp new file mode 100755 index 0000000000..9a3ec625ef --- /dev/null +++ b/src/igl/boundary_loop.cpp @@ -0,0 +1,153 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Stefan Brugger +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "boundary_loop.h" +#include "slice.h" +#include "triangle_triangle_adjacency.h" +#include "vertex_triangle_adjacency.h" +#include "is_border_vertex.h" +#include + +template +IGL_INLINE void igl::boundary_loop( + const Eigen::PlainObjectBase & F, + std::vector >& L) +{ + using namespace std; + using namespace Eigen; + + if(F.rows() == 0) + return; + + VectorXd Vdummy(F.maxCoeff()+1,1); + DerivedF TT,TTi; + vector > VF, VFi; + triangle_triangle_adjacency(F,TT,TTi); + vertex_triangle_adjacency(Vdummy,F,VF,VFi); + + vector unvisited = is_border_vertex(Vdummy,F); + set unseen; + for (size_t i = 0; i < unvisited.size(); ++i) + { + if (unvisited[i]) + unseen.insert(unseen.end(),i); + } + + while (!unseen.empty()) + { + vector l; + + // Get first vertex of loop + int start = *unseen.begin(); + unseen.erase(unseen.begin()); + unvisited[start] = false; + l.push_back(start); + + bool done = false; + while (!done) + { + // Find next vertex + bool newBndEdge = false; + int v = l[l.size()-1]; + int next; + for (int i = 0; i < (int)VF[v].size() && !newBndEdge; i++) + { + int fid = VF[v][i]; + + if (TT.row(fid).minCoeff() < 0.) // Face contains boundary edge + { + int vLoc = -1; + if (F(fid,0) == v) vLoc = 0; + if (F(fid,1) == v) vLoc = 1; + if (F(fid,2) == v) vLoc = 2; + + int vNext = F(fid,(vLoc + 1) % F.cols()); + + newBndEdge = false; + if (unvisited[vNext] && TT(fid,vLoc) < 0) + { + next = vNext; + newBndEdge = true; + } + } + } + + if (newBndEdge) + { + l.push_back(next); + unseen.erase(next); + unvisited[next] = false; + } + else + done = true; + } + L.push_back(l); + } +} + +template +IGL_INLINE void igl::boundary_loop( + const Eigen::PlainObjectBase& F, + std::vector& L) +{ + using namespace Eigen; + using namespace std; + + if(F.rows() == 0) + return; + + vector > Lall; + boundary_loop(F,Lall); + + int idxMax = -1; + size_t maxLen = 0; + for (size_t i = 0; i < Lall.size(); ++i) + { + if (Lall[i].size() > maxLen) + { + maxLen = Lall[i].size(); + idxMax = i; + } + } + + //Check for meshes without boundary + if (idxMax == -1) + { + L.clear(); + return; + } + + L.resize(Lall[idxMax].size()); + for (size_t i = 0; i < Lall[idxMax].size(); ++i) + { + L[i] = Lall[idxMax][i]; + } +} + +template +IGL_INLINE void igl::boundary_loop( + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& L) +{ + using namespace Eigen; + using namespace std; + + if(F.rows() == 0) + return; + + vector Lvec; + boundary_loop(F,Lvec); + + L.resize(Lvec.size()); + for (size_t i = 0; i < Lvec.size(); ++i) + L(i) = Lvec[i]; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::boundary_loop, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/boundary_loop.h b/src/igl/boundary_loop.h new file mode 100755 index 0000000000..ebb5072a5d --- /dev/null +++ b/src/igl/boundary_loop.h @@ -0,0 +1,66 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Stefan Brugger +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BOUNDARY_LOOP_H +#define IGL_BOUNDARY_LOOP_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Compute list of ordered boundary loops for a manifold mesh. + // + // Templates: + // Index index type + // Inputs: + // F #V by dim list of mesh faces + // Outputs: + // L list of loops where L[i] = ordered list of boundary vertices in loop i + // + template + IGL_INLINE void boundary_loop( + const Eigen::PlainObjectBase& F, + std::vector >& L); + + + // Compute ordered boundary loops for a manifold mesh and return the + // longest loop in terms of vertices. + // + // Templates: + // Index index type + // Inputs: + // F #V by dim list of mesh faces + // Outputs: + // L ordered list of boundary vertices of longest boundary loop + // + template + IGL_INLINE void boundary_loop( + const Eigen::PlainObjectBase& F, + std::vector& L); + + // Compute ordered boundary loops for a manifold mesh and return the + // longest loop in terms of vertices. + // + // Templates: + // Index index type + // Inputs: + // F #V by dim list of mesh faces + // Outputs: + // L ordered list of boundary vertices of longest boundary loop + // + template + IGL_INLINE void boundary_loop( + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& L); +} + +#ifndef IGL_STATIC_LIBRARY +# include "boundary_loop.cpp" +#endif +#endif diff --git a/src/igl/bounding_box.cpp b/src/igl/bounding_box.cpp new file mode 100644 index 0000000000..e740b058f9 --- /dev/null +++ b/src/igl/bounding_box.cpp @@ -0,0 +1,103 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "bounding_box.h" +#include + +template +IGL_INLINE void igl::bounding_box( + const Eigen::MatrixBase& V, + Eigen::PlainObjectBase& BV, + Eigen::PlainObjectBase& BF) +{ + return bounding_box(V,0.,BV,BF); +} + +template +IGL_INLINE void igl::bounding_box( + const Eigen::MatrixBase& V, + const typename DerivedV::Scalar pad, + Eigen::PlainObjectBase& BV, + Eigen::PlainObjectBase& BF) +{ + using namespace std; + + const int dim = V.cols(); + const auto & minV = V.colwise().minCoeff().array()-pad; + const auto & maxV = V.colwise().maxCoeff().array()+pad; + // 2^n vertices + BV.resize((1< combos = + [&BV,&minV,&maxV,&combos]( + const int dim, + const int i, + int * X, + const int pre_index) + { + for(X[i] = 0;X[i]<2;X[i]++) + { + int index = pre_index*2+X[i]; + if((i+1), Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::bounding_box, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::Matrix::Scalar, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::bounding_box, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::bounding_box, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::bounding_box, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/bounding_box.h b/src/igl/bounding_box.h new file mode 100644 index 0000000000..7e053d2cc6 --- /dev/null +++ b/src/igl/bounding_box.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BOUNDING_BOX_H +#define IGL_BOUNDING_BOX_H +#include "igl_inline.h" +#include +namespace igl +{ + // Build a triangle mesh of the bounding box of a given list of vertices + // + // Inputs: + // V #V by dim list of rest domain positions + // Outputs: + // BV 2^dim by dim list of bounding box corners positions + // BF #BF by dim list of simplex facets + template + IGL_INLINE void bounding_box( + const Eigen::MatrixBase& V, + Eigen::PlainObjectBase& BV, + Eigen::PlainObjectBase& BF); + template + IGL_INLINE void bounding_box( + const Eigen::MatrixBase& V, + const typename DerivedV::Scalar pad, + Eigen::PlainObjectBase& BV, + Eigen::PlainObjectBase& BF); +} + +#ifndef IGL_STATIC_LIBRARY +# include "bounding_box.cpp" +#endif + +#endif + diff --git a/src/igl/bounding_box_diagonal.cpp b/src/igl/bounding_box_diagonal.cpp new file mode 100644 index 0000000000..1023abb506 --- /dev/null +++ b/src/igl/bounding_box_diagonal.cpp @@ -0,0 +1,26 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "bounding_box_diagonal.h" +#include "mat_max.h" +#include "mat_min.h" +#include + +IGL_INLINE double igl::bounding_box_diagonal( + const Eigen::MatrixXd & V) +{ + using namespace Eigen; + VectorXd maxV,minV; + VectorXi maxVI,minVI; + mat_max(V,1,maxV,maxVI); + mat_min(V,1,minV,minVI); + return sqrt((maxV-minV).array().square().sum()); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/bounding_box_diagonal.h b/src/igl/bounding_box_diagonal.h new file mode 100644 index 0000000000..d89025bd55 --- /dev/null +++ b/src/igl/bounding_box_diagonal.h @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_BOUNDING_BOX_DIAGONAL_H +#define IGL_BOUNDING_BOX_DIAGONAL_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute the length of the diagonal of a given meshes axis-aligned bounding + // box + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // Returns length of bounding box diagonal + IGL_INLINE double bounding_box_diagonal( const Eigen::MatrixXd & V); +} + +#ifndef IGL_STATIC_LIBRARY +# include "bounding_box_diagonal.cpp" +#endif + +#endif diff --git a/src/igl/canonical_quaternions.cpp b/src/igl/canonical_quaternions.cpp new file mode 100644 index 0000000000..55d68da860 --- /dev/null +++ b/src/igl/canonical_quaternions.cpp @@ -0,0 +1,21 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "canonical_quaternions.h" + +template <> IGL_INLINE float igl::CANONICAL_VIEW_QUAT(int i, int j) +{ + return (float)igl::CANONICAL_VIEW_QUAT_F[i][j]; +} +template <> IGL_INLINE double igl::CANONICAL_VIEW_QUAT(int i, int j) +{ + return (double)igl::CANONICAL_VIEW_QUAT_D[i][j]; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/canonical_quaternions.h b/src/igl/canonical_quaternions.h new file mode 100644 index 0000000000..86d90112dd --- /dev/null +++ b/src/igl/canonical_quaternions.h @@ -0,0 +1,129 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CANONICAL_QUATERNIONS_H +#define IGL_CANONICAL_QUATERNIONS_H +#include "igl_inline.h" +// Define some canonical quaternions for floats and doubles +// A Quaternion, q, is defined here as an arrays of four scalars (x,y,z,w), +// such that q = x*i + y*j + z*k + w +namespace igl +{ + // Float versions +#define SQRT_2_OVER_2 0.707106781f + // Identity + const float IDENTITY_QUAT_F[4] = {0,0,0,1}; + // The following match the Matlab canonical views + // X point right, Y pointing up and Z point out + const float XY_PLANE_QUAT_F[4] = {0,0,0,1}; + // X points right, Y points *in* and Z points up + const float XZ_PLANE_QUAT_F[4] = {-SQRT_2_OVER_2,0,0,SQRT_2_OVER_2}; + // X points out, Y points right, and Z points up + const float YZ_PLANE_QUAT_F[4] = {-0.5,-0.5,-0.5,0.5}; + const float CANONICAL_VIEW_QUAT_F[][4] = + { + { 0, 0, 0, 1}, // 0 + { 0, 0, SQRT_2_OVER_2, SQRT_2_OVER_2}, // 1 + { 0, 0, 1, 0}, // 2 + { 0, 0, SQRT_2_OVER_2,-SQRT_2_OVER_2}, // 3 + + { 0, -1, 0, 0}, // 4 + {-SQRT_2_OVER_2, SQRT_2_OVER_2, 0, 0}, // 5 + { -1, 0, 0, 0}, // 6 + {-SQRT_2_OVER_2,-SQRT_2_OVER_2, 0, 0}, // 7 + + { -0.5, -0.5, -0.5, 0.5}, // 8 + { 0,-SQRT_2_OVER_2, 0, SQRT_2_OVER_2}, // 9 + { 0.5, -0.5, 0.5, 0.5}, // 10 + { SQRT_2_OVER_2, 0, SQRT_2_OVER_2, 0}, // 11 + + { SQRT_2_OVER_2, 0,-SQRT_2_OVER_2, 0}, // 12 + { 0.5, 0.5, -0.5, 0.5}, // 13 + { 0, SQRT_2_OVER_2, 0, SQRT_2_OVER_2}, // 14 + { -0.5, 0.5, 0.5, 0.5}, // 15 + + { 0, SQRT_2_OVER_2, SQRT_2_OVER_2, 0}, // 16 + { -0.5, 0.5, 0.5, -0.5}, // 17 + {-SQRT_2_OVER_2, 0, 0,-SQRT_2_OVER_2}, // 18 + { -0.5, -0.5, -0.5, -0.5}, // 19 + + {-SQRT_2_OVER_2, 0, 0, SQRT_2_OVER_2}, // 20 + { -0.5, -0.5, 0.5, 0.5}, // 21 + { 0,-SQRT_2_OVER_2, SQRT_2_OVER_2, 0}, // 22 + { 0.5, -0.5, 0.5, -0.5} // 23 + }; +#undef SQRT_2_OVER_2 + // Double versions +#define SQRT_2_OVER_2 0.70710678118654757 + // Identity + const double IDENTITY_QUAT_D[4] = {0,0,0,1}; + // The following match the Matlab canonical views + // X point right, Y pointing up and Z point out + const double XY_PLANE_QUAT_D[4] = {0,0,0,1}; + // X points right, Y points *in* and Z points up + const double XZ_PLANE_QUAT_D[4] = {-SQRT_2_OVER_2,0,0,SQRT_2_OVER_2}; + // X points out, Y points right, and Z points up + const double YZ_PLANE_QUAT_D[4] = {-0.5,-0.5,-0.5,0.5}; + const double CANONICAL_VIEW_QUAT_D[][4] = + { + { 0, 0, 0, 1}, + { 0, 0, SQRT_2_OVER_2, SQRT_2_OVER_2}, + { 0, 0, 1, 0}, + { 0, 0, SQRT_2_OVER_2,-SQRT_2_OVER_2}, + + { 0, -1, 0, 0}, + {-SQRT_2_OVER_2, SQRT_2_OVER_2, 0, 0}, + { -1, 0, 0, 0}, + {-SQRT_2_OVER_2,-SQRT_2_OVER_2, 0, 0}, + + { -0.5, -0.5, -0.5, 0.5}, + { 0,-SQRT_2_OVER_2, 0, SQRT_2_OVER_2}, + { 0.5, -0.5, 0.5, 0.5}, + { SQRT_2_OVER_2, 0, SQRT_2_OVER_2, 0}, + + { SQRT_2_OVER_2, 0,-SQRT_2_OVER_2, 0}, + { 0.5, 0.5, -0.5, 0.5}, + { 0, SQRT_2_OVER_2, 0, SQRT_2_OVER_2}, + { -0.5, 0.5, 0.5, 0.5}, + + { 0, SQRT_2_OVER_2, SQRT_2_OVER_2, 0}, + { -0.5, 0.5, 0.5, -0.5}, + {-SQRT_2_OVER_2, 0, 0,-SQRT_2_OVER_2}, + { -0.5, -0.5, -0.5, -0.5}, + + {-SQRT_2_OVER_2, 0, 0, SQRT_2_OVER_2}, + { -0.5, -0.5, 0.5, 0.5}, + { 0,-SQRT_2_OVER_2, SQRT_2_OVER_2, 0}, + { 0.5, -0.5, 0.5, -0.5} + }; +#undef SQRT_2_OVER_2 +#define NUM_CANONICAL_VIEW_QUAT 24 + + // NOTE: I want to rather be able to return a Q_type[][] but C++ is not + // making it easy. So instead I've written a per-element accessor + + // Return element [i][j] of the corresponding CANONICAL_VIEW_QUAT_* of the + // given templated type + // Inputs: + // i index of quaternion + // j index of coordinate in quaternion i + // Returns values of CANONICAL_VIEW_QUAT_*[i][j] + template + IGL_INLINE Q_type CANONICAL_VIEW_QUAT(int i, int j); + // Template specializations for float and double + template <> + IGL_INLINE float CANONICAL_VIEW_QUAT(int i, int j); + template <> + IGL_INLINE double CANONICAL_VIEW_QUAT(int i, int j); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "canonical_quaternions.cpp" +#endif + +#endif diff --git a/src/igl/cat.cpp b/src/igl/cat.cpp new file mode 100644 index 0000000000..f297fc135f --- /dev/null +++ b/src/igl/cat.cpp @@ -0,0 +1,267 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "cat.h" + +#include + +// Bug in unsupported/Eigen/SparseExtra needs iostream first +#include +#include + + +// Sparse matrices need to be handled carefully. Because C++ does not +// Template: +// Scalar sparse matrix scalar type, e.g. double +template +IGL_INLINE void igl::cat( + const int dim, + const Eigen::SparseMatrix & A, + const Eigen::SparseMatrix & B, + Eigen::SparseMatrix & C) +{ + + assert(dim == 1 || dim == 2); + using namespace Eigen; + // Special case if B or A is empty + if(A.size() == 0) + { + C = B; + return; + } + if(B.size() == 0) + { + C = A; + return; + } + +#if false + // This **must** be DynamicSparseMatrix, otherwise this implementation is + // insanely slow + DynamicSparseMatrix dyn_C; + if(dim == 1) + { + assert(A.cols() == B.cols()); + dyn_C.resize(A.rows()+B.rows(),A.cols()); + }else if(dim == 2) + { + assert(A.rows() == B.rows()); + dyn_C.resize(A.rows(),A.cols()+B.cols()); + }else + { + fprintf(stderr,"cat.h: Error: Unsupported dimension %d\n",dim); + } + + dyn_C.reserve(A.nonZeros()+B.nonZeros()); + + // Iterate over outside of A + for(int k=0; k::InnerIterator it (A,k); it; ++it) + { + dyn_C.coeffRef(it.row(),it.col()) += it.value(); + } + } + + // Iterate over outside of B + for(int k=0; k::InnerIterator it (B,k); it; ++it) + { + int r = (dim == 1 ? A.rows()+it.row() : it.row()); + int c = (dim == 2 ? A.cols()+it.col() : it.col()); + dyn_C.coeffRef(r,c) += it.value(); + } + } + + C = SparseMatrix(dyn_C); +#elif false + std::vector > CIJV; + CIJV.reserve(A.nonZeros() + B.nonZeros()); + { + // Iterate over outside of A + for(int k=0; k::InnerIterator it (A,k); it; ++it) + { + CIJV.emplace_back(it.row(),it.col(),it.value()); + } + } + // Iterate over outside of B + for(int k=0; k::InnerIterator it (B,k); it; ++it) + { + int r = (dim == 1 ? A.rows()+it.row() : it.row()); + int c = (dim == 2 ? A.cols()+it.col() : it.col()); + CIJV.emplace_back(r,c,it.value()); + } + } + + } + + C = SparseMatrix( + dim == 1 ? A.rows()+B.rows() : A.rows(), + dim == 1 ? A.cols() : A.cols()+B.cols()); + C.reserve(A.nonZeros() + B.nonZeros()); + C.setFromTriplets(CIJV.begin(),CIJV.end()); +#else + C = SparseMatrix( + dim == 1 ? A.rows()+B.rows() : A.rows(), + dim == 1 ? A.cols() : A.cols()+B.cols()); + Eigen::VectorXi per_col = Eigen::VectorXi::Zero(C.cols()); + if(dim == 1) + { + assert(A.outerSize() == B.outerSize()); + for(int k = 0;k::InnerIterator it (A,k); it; ++it) + { + per_col(k)++; + } + for(typename SparseMatrix::InnerIterator it (B,k); it; ++it) + { + per_col(k)++; + } + } + }else + { + for(int k = 0;k::InnerIterator it (A,k); it; ++it) + { + per_col(k)++; + } + } + for(int k = 0;k::InnerIterator it (B,k); it; ++it) + { + per_col(A.cols() + k)++; + } + } + } + C.reserve(per_col); + if(dim == 1) + { + for(int k = 0;k::InnerIterator it (A,k); it; ++it) + { + C.insert(it.row(),k) = it.value(); + } + for(typename SparseMatrix::InnerIterator it (B,k); it; ++it) + { + C.insert(A.rows()+it.row(),k) = it.value(); + } + } + }else + { + for(int k = 0;k::InnerIterator it (A,k); it; ++it) + { + C.insert(it.row(),k) = it.value(); + } + } + for(int k = 0;k::InnerIterator it (B,k); it; ++it) + { + C.insert(it.row(),A.cols()+k) = it.value(); + } + } + } + C.makeCompressed(); + +#endif + +} + +template +IGL_INLINE void igl::cat( + const int dim, + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + MatC & C) +{ + assert(dim == 1 || dim == 2); + // Special case if B or A is empty + if(A.size() == 0) + { + C = B; + return; + } + if(B.size() == 0) + { + C = A; + return; + } + + if(dim == 1) + { + assert(A.cols() == B.cols()); + C.resize(A.rows()+B.rows(),A.cols()); + C << A,B; + }else if(dim == 2) + { + assert(A.rows() == B.rows()); + C.resize(A.rows(),A.cols()+B.cols()); + C << A,B; + }else + { + fprintf(stderr,"cat.h: Error: Unsupported dimension %d\n",dim); + } +} + +template +IGL_INLINE Mat igl::cat(const int dim, const Mat & A, const Mat & B) +{ + assert(dim == 1 || dim == 2); + Mat C; + igl::cat(dim,A,B,C); + return C; +} + +template +IGL_INLINE void igl::cat(const std::vector > & A, Mat & C) +{ + using namespace std; + // Start with empty matrix + C.resize(0,0); + for(const auto & row_vec : A) + { + // Concatenate each row horizontally + // Start with empty matrix + Mat row(0,0); + for(const auto & element : row_vec) + { + row = cat(2,row,element); + } + // Concatenate rows vertically + C = cat(1,C,row); + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template Eigen::Matrix igl::cat >(int, Eigen::Matrix const&, Eigen::Matrix const&); +// generated by autoexplicit.sh +template Eigen::SparseMatrix igl::cat >(int, Eigen::SparseMatrix const&, Eigen::SparseMatrix const&); +// generated by autoexplicit.sh +template Eigen::Matrix igl::cat >(int, Eigen::Matrix const&, Eigen::Matrix const&); +template void igl::cat, Eigen::Matrix >(int, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix&); +template Eigen::Matrix igl::cat >(int, Eigen::Matrix const&, Eigen::Matrix const&); +template Eigen::Matrix igl::cat >(int, Eigen::Matrix const&, Eigen::Matrix const&); +template void igl::cat, Eigen::Matrix >(int, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix&); +template void igl::cat, Eigen::Matrix >(int, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix&); +#endif diff --git a/src/igl/cat.h b/src/igl/cat.h new file mode 100644 index 0000000000..615bda1300 --- /dev/null +++ b/src/igl/cat.h @@ -0,0 +1,71 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CAT_H +#define IGL_CAT_H +#include "igl_inline.h" + +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include + +namespace igl +{ + // If you're using Dense matrices you might be better off using the << operator + + // This is an attempt to act like matlab's cat function. + + // Perform concatenation of a two matrices along a single dimension + // If dim == 1, then C = [A;B]. If dim == 2 then C = [A B] + // + // Template: + // Scalar scalar data type for sparse matrices like double or int + // Mat matrix type for all matrices (e.g. MatrixXd, SparseMatrix) + // MatC matrix type for output matrix (e.g. MatrixXd) needs to support + // resize + // Inputs: + // A first input matrix + // B second input matrix + // dim dimension along which to concatenate, 1 or 2 + // Outputs: + // C output matrix + // + template + IGL_INLINE void cat( + const int dim, + const Eigen::SparseMatrix & A, + const Eigen::SparseMatrix & B, + Eigen::SparseMatrix & C); + template + IGL_INLINE void cat( + const int dim, + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + MatC & C); + // Wrapper that returns C + template + IGL_INLINE Mat cat(const int dim, const Mat & A, const Mat & B); + + // Note: Maybe we can autogenerate a bunch of overloads D = cat(int,A,B,C), + // E = cat(int,A,B,C,D), etc. + + // Concatenate a "matrix" of blocks + // C = [A0;A1;A2;...;An] where Ai = [A[i][0] A[i][1] ... A[i][m]]; + // + // Inputs: + // A a matrix (vector of row vectors) + // Output: + // C + template + IGL_INLINE void cat(const std::vector > & A, Mat & C); +} + +#ifndef IGL_STATIC_LIBRARY +# include "cat.cpp" +#endif + +#endif diff --git a/src/igl/ceil.cpp b/src/igl/ceil.cpp new file mode 100644 index 0000000000..278269ab23 --- /dev/null +++ b/src/igl/ceil.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "ceil.h" +#include + +template < typename DerivedX, typename DerivedY> +IGL_INLINE void igl::ceil( + const Eigen::PlainObjectBase& X, + Eigen::PlainObjectBase& Y) +{ + using namespace std; + //Y = DerivedY::Zero(m,n); +//#pragma omp parallel for + //for(int i = 0;iScalar{return std::ceil(x);}).template cast(); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::ceil, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/ceil.h b/src/igl/ceil.h new file mode 100644 index 0000000000..b6acee904d --- /dev/null +++ b/src/igl/ceil.h @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CEIL_H +#define IGL_CEIL_H +#include "igl_inline.h" +#include +namespace igl +{ + // Ceil a given matrix to nearest integers + // + // Inputs: + // X m by n matrix of scalars + // Outputs: + // Y m by n matrix of ceiled integers + template < typename DerivedX, typename DerivedY> + IGL_INLINE void ceil( + const Eigen::PlainObjectBase& X, + Eigen::PlainObjectBase& Y); +} + +#ifndef IGL_STATIC_LIBRARY +# include "ceil.cpp" +#endif + +#endif diff --git a/src/igl/centroid.cpp b/src/igl/centroid.cpp new file mode 100644 index 0000000000..a9ed25df2d --- /dev/null +++ b/src/igl/centroid.cpp @@ -0,0 +1,67 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "centroid.h" +#include + +template < + typename DerivedV, + typename DerivedF, + typename Derivedc, + typename Derivedvol> +IGL_INLINE void igl::centroid( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& cen, + Derivedvol & vol) +{ + using namespace Eigen; + assert(F.cols() == 3 && "F should contain triangles."); + assert(V.cols() == 3 && "V should contain 3d points."); + const int m = F.rows(); + cen.setZero(); + vol = 0; + // loop over faces + for(int f = 0;f RowVector3S; + const RowVector3S & a = V.row(F(f,0)); + const RowVector3S & b = V.row(F(f,1)); + const RowVector3S & c = V.row(F(f,2)); + // un-normalized normal + const RowVector3S & n = (b-a).cross(c-a); + // total volume via divergence theorem: ∫ 1 + vol += n.dot(a)/6.; + // centroid via divergence theorem and midpoint quadrature: ∫ x + cen.array() += (1./24.*n.array()*((a+b).array().square() + (b+c).array().square() + + (c+a).array().square()).array()); + } + cen *= 1./(2.*vol); +} + +template < + typename DerivedV, + typename DerivedF, + typename Derivedc> +IGL_INLINE void igl::centroid( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& c) +{ + typename Derivedc::Scalar vol; + return centroid(V,F,c,vol); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::centroid, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::centroid, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/centroid.h b/src/igl/centroid.h new file mode 100644 index 0000000000..31fa7ae6ba --- /dev/null +++ b/src/igl/centroid.h @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CENTROID_H +#define IGL_CENTROID_H +#include "igl_inline.h" +#include +namespace igl +{ + // CENTROID Computes the centroid of a closed mesh using a surface integral. + // + // Inputs: + // V #V by dim list of rest domain positions + // F #F by 3 list of triangle indices into V + // Outputs: + // c dim vector of centroid coordinates + // vol total volume of solid. + // + template < + typename DerivedV, + typename DerivedF, + typename Derivedc, + typename Derivedvol> + IGL_INLINE void centroid( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& c, + Derivedvol & vol); + template < + typename DerivedV, + typename DerivedF, + typename Derivedc> + IGL_INLINE void centroid( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& c); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "centroid.cpp" +#endif + +#endif + diff --git a/src/igl/circulation.cpp b/src/igl/circulation.cpp new file mode 100644 index 0000000000..6f711b946d --- /dev/null +++ b/src/igl/circulation.cpp @@ -0,0 +1,71 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "circulation.h" +#include "list_to_matrix.h" + +IGL_INLINE std::vector igl::circulation( + const int e, + const bool ccw, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI) +{ + // prepare output + std::vector N; + N.reserve(6); + const int m = F.rows(); + const auto & step = [&]( + const int e, + const int ff, + int & ne, + int & nf) + { + assert((EF(e,1) == ff || EF(e,0) == ff) && "e should touch ff"); + //const int fside = EF(e,1)==ff?1:0; + const int nside = EF(e,0)==ff?1:0; + const int nv = EI(e,nside); + // get next face + nf = EF(e,nside); + // get next edge + const int dir = ccw?-1:1; + ne = EMAP(nf+m*((nv+dir+3)%3)); + }; + // Always start with first face (ccw in step will be sure to turn right + // direction) + const int f0 = EF(e,0); + int fi = f0; + int ei = e; + while(true) + { + step(ei,fi,ei,fi); + N.push_back(fi); + // back to start? + if(fi == f0) + { + assert(ei == e); + break; + } + } + return N; +} + +IGL_INLINE void igl::circulation( + const int e, + const bool ccw, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI, + Eigen::VectorXi & vN) +{ + std::vector N = circulation(e,ccw,F,E,EMAP,EF,EI); + igl::list_to_matrix(N,vN); +} diff --git a/src/igl/circulation.h b/src/igl/circulation.h new file mode 100644 index 0000000000..d8d14c7ed5 --- /dev/null +++ b/src/igl/circulation.h @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CIRCULATION_H +#define IGL_CIRCULATION_H +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Return list of faces around the end point of an edge. Assumes + // data-structures are built from an edge-manifold **closed** mesh. + // + // Inputs: + // e index into E of edge to circulate + // ccw whether to _continue_ in ccw direction of edge (circulate around + // E(e,1)) + // F #F by 3 list of face indices + // E #E by 2 list of edge indices + // EMAP #F*3 list of indices into E, mapping each directed edge to unique + // unique edge in E + // EF #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of + // F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) " + // e=(j->i) + // EI #E by 2 list of edge flap corners (see above). + // Returns list of faces touched by circulation (in cyclically order). + // + IGL_INLINE std::vector circulation( + const int e, + const bool ccw, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI); + // Wrapper with VectorXi output. + IGL_INLINE void circulation( + const int e, + const bool ccw, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI, + Eigen::VectorXi & vN); +} + +#ifndef IGL_STATIC_LIBRARY +# include "circulation.cpp" +#endif +#endif diff --git a/src/igl/circumradius.cpp b/src/igl/circumradius.cpp new file mode 100644 index 0000000000..34424eae28 --- /dev/null +++ b/src/igl/circumradius.cpp @@ -0,0 +1,26 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "circumradius.h" +#include "edge_lengths.h" +#include "doublearea.h" +template < + typename DerivedV, + typename DerivedF, + typename DerivedR> +IGL_INLINE void igl::circumradius( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & R) +{ + Eigen::Matrix l; + igl::edge_lengths(V,F,l); + DerivedR A; + igl::doublearea(l,0.,A); + // use formula: R=abc/(4*area) to compute the circum radius + R = l.col(0).array() * l.col(1).array() * l.col(2).array() / (2.0*A.array()); +} diff --git a/src/igl/circumradius.h b/src/igl/circumradius.h new file mode 100644 index 0000000000..12592ee3fb --- /dev/null +++ b/src/igl/circumradius.h @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CIRCUMRADIUS_H +#define IGL_CIRCUMRADIUS_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute the circumradius of each triangle in a mesh (V,F) + // + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by 3 list of triangle indices into V + // Outputs: + // R #F list of circumradii + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedR> + IGL_INLINE void circumradius( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & R); +} +#ifndef IGL_STATIC_LIBRARY +# include "circumradius.cpp" +#endif +#endif diff --git a/src/igl/collapse_edge.cpp b/src/igl/collapse_edge.cpp new file mode 100644 index 0000000000..9f00b4323e --- /dev/null +++ b/src/igl/collapse_edge.cpp @@ -0,0 +1,394 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "collapse_edge.h" +#include "circulation.h" +#include "edge_collapse_is_valid.h" +#include + +IGL_INLINE bool igl::collapse_edge( + const int e, + const Eigen::RowVectorXd & p, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::MatrixXi & E, + Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI, + int & a_e1, + int & a_e2, + int & a_f1, + int & a_f2) +{ + // Assign this to 0 rather than, say, -1 so that deleted elements will get + // draw as degenerate elements at vertex 0 (which should always exist and + // never get collapsed to anything else since it is the smallest index) + using namespace Eigen; + using namespace std; + const int eflip = E(e,0)>E(e,1); + // source and destination + const int s = eflip?E(e,1):E(e,0); + const int d = eflip?E(e,0):E(e,1); + + if(!edge_collapse_is_valid(e,F,E,EMAP,EF,EI)) + { + return false; + } + + // Important to grab neighbors of d before monkeying with edges + const std::vector nV2Fd = circulation(e,!eflip,F,E,EMAP,EF,EI); + + // The following implementation strongly relies on s & cost_and_placement, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::MatrixXi & E, + Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI, + std::set > & Q, + std::vector >::iterator > & Qit, + Eigen::MatrixXd & C) +{ + int e,e1,e2,f1,f2; + const auto always_try = []( + const Eigen::MatrixXd & ,/*V*/ + const Eigen::MatrixXi & ,/*F*/ + const Eigen::MatrixXi & ,/*E*/ + const Eigen::VectorXi & ,/*EMAP*/ + const Eigen::MatrixXi & ,/*EF*/ + const Eigen::MatrixXi & ,/*EI*/ + const std::set > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + ) -> bool { return true;}; + const auto never_care = []( + const Eigen::MatrixXd & , /*V*/ + const Eigen::MatrixXi & , /*F*/ + const Eigen::MatrixXi & , /*E*/ + const Eigen::VectorXi & ,/*EMAP*/ + const Eigen::MatrixXi & , /*EF*/ + const Eigen::MatrixXi & , /*EI*/ + const std::set > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )-> void { }; + return + collapse_edge( + cost_and_placement,always_try,never_care, + V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2); +} + +IGL_INLINE bool igl::collapse_edge( + const std::function & cost_and_placement, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> & pre_collapse, + const std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> & post_collapse, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::MatrixXi & E, + Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI, + std::set > & Q, + std::vector >::iterator > & Qit, + Eigen::MatrixXd & C) +{ + int e,e1,e2,f1,f2; + return + collapse_edge( + cost_and_placement,pre_collapse,post_collapse, + V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2); +} + + +IGL_INLINE bool igl::collapse_edge( + const std::function & cost_and_placement, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> & pre_collapse, + const std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> & post_collapse, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::MatrixXi & E, + Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI, + std::set > & Q, + std::vector >::iterator > & Qit, + Eigen::MatrixXd & C, + int & e, + int & e1, + int & e2, + int & f1, + int & f2) +{ + using namespace Eigen; + if(Q.empty()) + { + // no edges to collapse + return false; + } + std::pair p = *(Q.begin()); + if(p.first == std::numeric_limits::infinity()) + { + // min cost edge is infinite cost + return false; + } + Q.erase(Q.begin()); + e = p.second; + Qit[e] = Q.end(); + std::vector N = circulation(e, true,F,E,EMAP,EF,EI); + std::vector Nd = circulation(e,false,F,E,EMAP,EF,EI); + N.insert(N.begin(),Nd.begin(),Nd.end()); + bool collapsed = true; + if(pre_collapse(V,F,E,EMAP,EF,EI,Q,Qit,C,e)) + { + collapsed = collapse_edge(e,C.row(e),V,F,E,EMAP,EF,EI,e1,e2,f1,f2); + }else + { + // Aborted by pre collapse callback + collapsed = false; + } + post_collapse(V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2,collapsed); + if(collapsed) + { + // Erase the two, other collapsed edges + Q.erase(Qit[e1]); + Qit[e1] = Q.end(); + Q.erase(Qit[e2]); + Qit[e2] = Q.end(); + // update local neighbors + // loop over original face neighbors + for(auto n : N) + { + if(F(n,0) != IGL_COLLAPSE_EDGE_NULL || + F(n,1) != IGL_COLLAPSE_EDGE_NULL || + F(n,2) != IGL_COLLAPSE_EDGE_NULL) + { + for(int v = 0;v<3;v++) + { + // get edge id + const int ei = EMAP(v*F.rows()+n); + // erase old entry + Q.erase(Qit[ei]); + // compute cost and potential placement + double cost; + RowVectorXd place; + cost_and_placement(ei,V,F,E,EMAP,EF,EI,cost,place); + // Replace in queue + Qit[ei] = Q.insert(std::pair(cost,ei)).first; + C.row(ei) = place; + } + } + } + }else + { + // reinsert with infinite weight (the provided cost function must **not** + // have given this un-collapsable edge inf cost already) + p.first = std::numeric_limits::infinity(); + Qit[e] = Q.insert(p).first; + } + return collapsed; +} diff --git a/src/igl/collapse_edge.h b/src/igl/collapse_edge.h new file mode 100644 index 0000000000..90e5625565 --- /dev/null +++ b/src/igl/collapse_edge.h @@ -0,0 +1,216 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COLLAPSE_EDGE_H +#define IGL_COLLAPSE_EDGE_H +#include "igl_inline.h" +#include +#include +#include +namespace igl +{ + // Assumes (V,F) is a closed manifold mesh (except for previously collapsed + // faces which should be set to: + // [IGL_COLLAPSE_EDGE_NULL IGL_COLLAPSE_EDGE_NULL IGL_COLLAPSE_EDGE_NULL]. + // Collapses exactly two faces and exactly 3 edges from E (e and one side of + // each face gets collapsed to the other). This is implemented in a way that + // it can be repeatedly called until satisfaction and then the garbage in F + // can be collected by removing NULL faces. + // + // Inputs: + // e index into E of edge to try to collapse. E(e,:) = [s d] or [d s] so + // that sj) is the edge of + // F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) " + // e=(j->i) + // EI #E by 2 list of edge flap corners (see above). + // e1 index into E of edge collpased on left + // e2 index into E of edge collpased on left + // f1 index into E of edge collpased on left + // f2 index into E of edge collpased on left + // Returns true if edge was collapsed + #define IGL_COLLAPSE_EDGE_NULL 0 + IGL_INLINE bool collapse_edge( + const int e, + const Eigen::RowVectorXd & p, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::MatrixXi & E, + Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI, + int & e1, + int & e2, + int & f1, + int & f2); + IGL_INLINE bool collapse_edge( + const int e, + const Eigen::RowVectorXd & p, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::MatrixXi & E, + Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI); + // Collapse least-cost edge from a priority queue and update queue + // + // Inputs/Outputs: + // cost_and_placement function computing cost of collapsing an edge and 3d + // position where it should be placed: + // cost_and_placement(V,F,E,EMAP,EF,EI,cost,placement); + // **If the edges is collapsed** then this function will be called on all + // edges of all faces previously incident on the endpoints of the + // collapsed edge. + // Q queue containing pairs of costs and edge indices + // Qit list of iterators so that Qit[e] --> iterator of edge e in Q + // C #E by dim list of stored placements + IGL_INLINE bool collapse_edge( + const std::function & cost_and_placement, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::MatrixXi & E, + Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI, + std::set > & Q, + std::vector >::iterator > & Qit, + Eigen::MatrixXd & C); + // Inputs: + // pre_collapse callback called with index of edge whose collapse is about + // to be attempted. This function should return whether to **proceed** + // with the collapse: returning true means "yes, try to collapse", + // returning false means "No, consider this edge 'uncollapsable', behave + // as if collapse_edge(e) returned false. + // post_collapse callback called with index of edge whose collapse was + // just attempted and a flag revealing whether this was successful. + IGL_INLINE bool collapse_edge( + const std::function & cost_and_placement, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> & pre_collapse, + const std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> & post_collapse, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::MatrixXi & E, + Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI, + std::set > & Q, + std::vector >::iterator > & Qit, + Eigen::MatrixXd & C); + + IGL_INLINE bool collapse_edge( + const std::function & cost_and_placement, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> & pre_collapse, + const std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> & post_collapse, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::MatrixXi & E, + Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI, + std::set > & Q, + std::vector >::iterator > & Qit, + Eigen::MatrixXd & C, + int & e, + int & e1, + int & e2, + int & f1, + int & f2); +} + +#ifndef IGL_STATIC_LIBRARY +# include "collapse_edge.cpp" +#endif +#endif diff --git a/src/igl/collapse_small_triangles.cpp b/src/igl/collapse_small_triangles.cpp new file mode 100644 index 0000000000..5177c584f0 --- /dev/null +++ b/src/igl/collapse_small_triangles.cpp @@ -0,0 +1,139 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "collapse_small_triangles.h" + +#include "bounding_box_diagonal.h" +#include "doublearea.h" +#include "edge_lengths.h" +#include "colon.h" +#include "faces_first.h" + +#include + +#include + +void igl::collapse_small_triangles( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const double eps, + Eigen::MatrixXi & FF) +{ + using namespace Eigen; + using namespace std; + + // Compute bounding box diagonal length + double bbd = bounding_box_diagonal(V); + MatrixXd l; + edge_lengths(V,F,l); + VectorXd dblA; + doublearea(l,0.,dblA); + + // Minimum area tolerance + const double min_dblarea = 2.0*eps*bbd*bbd; + + Eigen::VectorXi FIM = colon(0,V.rows()-1); + int num_edge_collapses = 0; + // Loop over triangles + for(int f = 0;fmaxl) + { + maxli = e; + maxl = l(f,e); + } + } + // Be sure that min and max aren't the same + maxli = (minli==maxli?(minli+1)%3:maxli); + + // Collapse min edge maintaining max edge: i-->j + // Q: Why this direction? + int i = maxli; + int j = ((minli+1)%3 == maxli ? (minli+2)%3: (minli+1)%3); + assert(i != minli); + assert(j != minli); + assert(i != j); + FIM(F(f,i)) = FIM(F(f,j)); + num_edge_collapses++; + } + } + + // Reindex faces + MatrixXi rF = F; + // Loop over triangles + for(int f = 0;f +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COLLAPSE_SMALL_TRIANGLES_H +#define IGL_COLLAPSE_SMALL_TRIANGLES_H +#include +namespace igl +{ + // Given a triangle mesh (V,F) compute a new mesh (VV,FF) which contains the + // original faces and vertices of (V,F) except any small triangles have been + // removed via collapse. + // + // We are *not* following the rules in "Mesh Optimization" [Hoppe et al] + // Section 4.2. But for our purposes we don't care about this criteria. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // eps epsilon for smallest allowed area treated as fraction of squared bounding box + // diagonal + // Outputs: + // FF #FF by 3 list of triangle indices into V + // + // + void collapse_small_triangles( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const double eps, + Eigen::MatrixXi & FF); +} + +#ifndef IGL_STATIC_LIBRARY +# include "collapse_small_triangles.cpp" +#endif + +#endif diff --git a/src/igl/colon.cpp b/src/igl/colon.cpp new file mode 100644 index 0000000000..a10c2982ea --- /dev/null +++ b/src/igl/colon.cpp @@ -0,0 +1,66 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "colon.h" +#include "LinSpaced.h" + +#include + +template +IGL_INLINE void igl::colon( + const L low, + const S step, + const H hi, + Eigen::Matrix & I) +{ + const int size = ((hi-low)/step)+1; + I = igl::LinSpaced >(size,low,low+step*(size-1)); +} + +template +IGL_INLINE void igl::colon( + const L low, + const H hi, + Eigen::Matrix & I) +{ + return igl::colon(low,(T)1,hi,I); +} + +template +IGL_INLINE Eigen::Matrix igl::colon( + const L low, + const H hi) +{ + Eigen::Matrix I; + igl::colon(low,hi,I); + return I; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template Eigen::Matrix igl::colon(int, int); +template Eigen::Matrix igl::colon(int,long); +template Eigen::Matrix igl::colon(int,long long int); +template Eigen::Matrix igl::colon(double, double); +// generated by autoexplicit.sh +template void igl::colon(int, long, int, Eigen::Matrix&); +template void igl::colon(int, int, long, Eigen::Matrix&); +template void igl::colon(int, long, Eigen::Matrix&); +template void igl::colon(int, int, Eigen::Matrix&); +template void igl::colon(int,long long int,Eigen::Matrix &); +template void igl::colon(int, int, int, Eigen::Matrix&); +template void igl::colon(int, long, Eigen::Matrix&); +template void igl::colon(int, double, double, Eigen::Matrix&); +template void igl::colon(double, double, Eigen::Matrix&); +template void igl::colon(double, double, double, Eigen::Matrix&); +template void igl::colon(int, int, Eigen::Matrix&); +#ifdef WIN32 +template void igl::colon(int, long long, class Eigen::Matrix &); +template void igl::colon(int, __int64, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1> &); +#endif +#endif diff --git a/src/igl/colon.h b/src/igl/colon.h new file mode 100644 index 0000000000..1f2b9bf6f4 --- /dev/null +++ b/src/igl/colon.h @@ -0,0 +1,65 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COLON_H +#define IGL_COLON_H +#include "igl_inline.h" +#include +namespace igl +{ + // Note: + // This should be potentially replaced with eigen's LinSpaced() function + // + // If step = 1, it's about 5 times faster to use: + // X = Eigen::VectorXi::LinSpaced(n,0,n-1); + // than + // X = igl::colon(0,n-1); + // + + // Colon operator like matlab's colon operator. Enumerats values between low + // and hi with step step. + // Templates: + // L should be a eigen matrix primitive type like int or double + // S should be a eigen matrix primitive type like int or double + // H should be a eigen matrix primitive type like int or double + // T should be a eigen matrix primitive type like int or double + // Inputs: + // low starting value if step is valid then this is *always* the first + // element of I + // step step difference between sequential elements returned in I, + // remember this will be cast to template T at compile time. If lowhi then step must be negative. + // Otherwise I will be set to empty. + // hi ending value, if (hi-low)%step is zero then this will be the last + // element in I. If step is positive there will be no elements greater + // than hi, vice versa if hi + IGL_INLINE void colon( + const L low, + const S step, + const H hi, + Eigen::Matrix & I); + // Same as above but step == (T)1 + template + IGL_INLINE void colon( + const L low, + const H hi, + Eigen::Matrix & I); + // Return output rather than set in reference + template + IGL_INLINE Eigen::Matrix colon( + const L low, + const H hi); +} + +#ifndef IGL_STATIC_LIBRARY +# include "colon.cpp" +#endif + +#endif diff --git a/src/igl/colormap.cpp b/src/igl/colormap.cpp new file mode 100644 index 0000000000..560cbe6c09 --- /dev/null +++ b/src/igl/colormap.cpp @@ -0,0 +1,1434 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Joe Graus , Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "colormap.h" +#include "jet.h" +#include + +// One of the new matplotlib colormaps by Nathaniel J.Smith, Stefan van der Walt, and (in the case of viridis) Eric Firing. +// Released under the CC0 license / public domain dedication + +namespace igl +{ +static double inferno_cm[256][3] = { + { 0.001462, 0.000466, 0.013866 }, + { 0.002267, 0.001270, 0.018570 }, + { 0.003299, 0.002249, 0.024239 }, + { 0.004547, 0.003392, 0.030909 }, + { 0.006006, 0.004692, 0.038558 }, + { 0.007676, 0.006136, 0.046836 }, + { 0.009561, 0.007713, 0.055143 }, + { 0.011663, 0.009417, 0.063460 }, + { 0.013995, 0.011225, 0.071862 }, + { 0.016561, 0.013136, 0.080282 }, + { 0.019373, 0.015133, 0.088767 }, + { 0.022447, 0.017199, 0.097327 }, + { 0.025793, 0.019331, 0.105930 }, + { 0.029432, 0.021503, 0.114621 }, + { 0.033385, 0.023702, 0.123397 }, + { 0.037668, 0.025921, 0.132232 }, + { 0.042253, 0.028139, 0.141141 }, + { 0.046915, 0.030324, 0.150164 }, + { 0.051644, 0.032474, 0.159254 }, + { 0.056449, 0.034569, 0.168414 }, + { 0.061340, 0.036590, 0.177642 }, + { 0.066331, 0.038504, 0.186962 }, + { 0.071429, 0.040294, 0.196354 }, + { 0.076637, 0.041905, 0.205799 }, + { 0.081962, 0.043328, 0.215289 }, + { 0.087411, 0.044556, 0.224813 }, + { 0.092990, 0.045583, 0.234358 }, + { 0.098702, 0.046402, 0.243904 }, + { 0.104551, 0.047008, 0.253430 }, + { 0.110536, 0.047399, 0.262912 }, + { 0.116656, 0.047574, 0.272321 }, + { 0.122908, 0.047536, 0.281624 }, + { 0.129285, 0.047293, 0.290788 }, + { 0.135778, 0.046856, 0.299776 }, + { 0.142378, 0.046242, 0.308553 }, + { 0.149073, 0.045468, 0.317085 }, + { 0.155850, 0.044559, 0.325338 }, + { 0.162689, 0.043554, 0.333277 }, + { 0.169575, 0.042489, 0.340874 }, + { 0.176493, 0.041402, 0.348111 }, + { 0.183429, 0.040329, 0.354971 }, + { 0.190367, 0.039309, 0.361447 }, + { 0.197297, 0.038400, 0.367535 }, + { 0.204209, 0.037632, 0.373238 }, + { 0.211095, 0.037030, 0.378563 }, + { 0.217949, 0.036615, 0.383522 }, + { 0.224763, 0.036405, 0.388129 }, + { 0.231538, 0.036405, 0.392400 }, + { 0.238273, 0.036621, 0.396353 }, + { 0.244967, 0.037055, 0.400007 }, + { 0.251620, 0.037705, 0.403378 }, + { 0.258234, 0.038571, 0.406485 }, + { 0.264810, 0.039647, 0.409345 }, + { 0.271347, 0.040922, 0.411976 }, + { 0.277850, 0.042353, 0.414392 }, + { 0.284321, 0.043933, 0.416608 }, + { 0.290763, 0.045644, 0.418637 }, + { 0.297178, 0.047470, 0.420491 }, + { 0.303568, 0.049396, 0.422182 }, + { 0.309935, 0.051407, 0.423721 }, + { 0.316282, 0.053490, 0.425116 }, + { 0.322610, 0.055634, 0.426377 }, + { 0.328921, 0.057827, 0.427511 }, + { 0.335217, 0.060060, 0.428524 }, + { 0.341500, 0.062325, 0.429425 }, + { 0.347771, 0.064616, 0.430217 }, + { 0.354032, 0.066925, 0.430906 }, + { 0.360284, 0.069247, 0.431497 }, + { 0.366529, 0.071579, 0.431994 }, + { 0.372768, 0.073915, 0.432400 }, + { 0.379001, 0.076253, 0.432719 }, + { 0.385228, 0.078591, 0.432955 }, + { 0.391453, 0.080927, 0.433109 }, + { 0.397674, 0.083257, 0.433183 }, + { 0.403894, 0.085580, 0.433179 }, + { 0.410113, 0.087896, 0.433098 }, + { 0.416331, 0.090203, 0.432943 }, + { 0.422549, 0.092501, 0.432714 }, + { 0.428768, 0.094790, 0.432412 }, + { 0.434987, 0.097069, 0.432039 }, + { 0.441207, 0.099338, 0.431594 }, + { 0.447428, 0.101597, 0.431080 }, + { 0.453651, 0.103848, 0.430498 }, + { 0.459875, 0.106089, 0.429846 }, + { 0.466100, 0.108322, 0.429125 }, + { 0.472328, 0.110547, 0.428334 }, + { 0.478558, 0.112764, 0.427475 }, + { 0.484789, 0.114974, 0.426548 }, + { 0.491022, 0.117179, 0.425552 }, + { 0.497257, 0.119379, 0.424488 }, + { 0.503493, 0.121575, 0.423356 }, + { 0.509730, 0.123769, 0.422156 }, + { 0.515967, 0.125960, 0.420887 }, + { 0.522206, 0.128150, 0.419549 }, + { 0.528444, 0.130341, 0.418142 }, + { 0.534683, 0.132534, 0.416667 }, + { 0.540920, 0.134729, 0.415123 }, + { 0.547157, 0.136929, 0.413511 }, + { 0.553392, 0.139134, 0.411829 }, + { 0.559624, 0.141346, 0.410078 }, + { 0.565854, 0.143567, 0.408258 }, + { 0.572081, 0.145797, 0.406369 }, + { 0.578304, 0.148039, 0.404411 }, + { 0.584521, 0.150294, 0.402385 }, + { 0.590734, 0.152563, 0.400290 }, + { 0.596940, 0.154848, 0.398125 }, + { 0.603139, 0.157151, 0.395891 }, + { 0.609330, 0.159474, 0.393589 }, + { 0.615513, 0.161817, 0.391219 }, + { 0.621685, 0.164184, 0.388781 }, + { 0.627847, 0.166575, 0.386276 }, + { 0.633998, 0.168992, 0.383704 }, + { 0.640135, 0.171438, 0.381065 }, + { 0.646260, 0.173914, 0.378359 }, + { 0.652369, 0.176421, 0.375586 }, + { 0.658463, 0.178962, 0.372748 }, + { 0.664540, 0.181539, 0.369846 }, + { 0.670599, 0.184153, 0.366879 }, + { 0.676638, 0.186807, 0.363849 }, + { 0.682656, 0.189501, 0.360757 }, + { 0.688653, 0.192239, 0.357603 }, + { 0.694627, 0.195021, 0.354388 }, + { 0.700576, 0.197851, 0.351113 }, + { 0.706500, 0.200728, 0.347777 }, + { 0.712396, 0.203656, 0.344383 }, + { 0.718264, 0.206636, 0.340931 }, + { 0.724103, 0.209670, 0.337424 }, + { 0.729909, 0.212759, 0.333861 }, + { 0.735683, 0.215906, 0.330245 }, + { 0.741423, 0.219112, 0.326576 }, + { 0.747127, 0.222378, 0.322856 }, + { 0.752794, 0.225706, 0.319085 }, + { 0.758422, 0.229097, 0.315266 }, + { 0.764010, 0.232554, 0.311399 }, + { 0.769556, 0.236077, 0.307485 }, + { 0.775059, 0.239667, 0.303526 }, + { 0.780517, 0.243327, 0.299523 }, + { 0.785929, 0.247056, 0.295477 }, + { 0.791293, 0.250856, 0.291390 }, + { 0.796607, 0.254728, 0.287264 }, + { 0.801871, 0.258674, 0.283099 }, + { 0.807082, 0.262692, 0.278898 }, + { 0.812239, 0.266786, 0.274661 }, + { 0.817341, 0.270954, 0.270390 }, + { 0.822386, 0.275197, 0.266085 }, + { 0.827372, 0.279517, 0.261750 }, + { 0.832299, 0.283913, 0.257383 }, + { 0.837165, 0.288385, 0.252988 }, + { 0.841969, 0.292933, 0.248564 }, + { 0.846709, 0.297559, 0.244113 }, + { 0.851384, 0.302260, 0.239636 }, + { 0.855992, 0.307038, 0.235133 }, + { 0.860533, 0.311892, 0.230606 }, + { 0.865006, 0.316822, 0.226055 }, + { 0.869409, 0.321827, 0.221482 }, + { 0.873741, 0.326906, 0.216886 }, + { 0.878001, 0.332060, 0.212268 }, + { 0.882188, 0.337287, 0.207628 }, + { 0.886302, 0.342586, 0.202968 }, + { 0.890341, 0.347957, 0.198286 }, + { 0.894305, 0.353399, 0.193584 }, + { 0.898192, 0.358911, 0.188860 }, + { 0.902003, 0.364492, 0.184116 }, + { 0.905735, 0.370140, 0.179350 }, + { 0.909390, 0.375856, 0.174563 }, + { 0.912966, 0.381636, 0.169755 }, + { 0.916462, 0.387481, 0.164924 }, + { 0.919879, 0.393389, 0.160070 }, + { 0.923215, 0.399359, 0.155193 }, + { 0.926470, 0.405389, 0.150292 }, + { 0.929644, 0.411479, 0.145367 }, + { 0.932737, 0.417627, 0.140417 }, + { 0.935747, 0.423831, 0.135440 }, + { 0.938675, 0.430091, 0.130438 }, + { 0.941521, 0.436405, 0.125409 }, + { 0.944285, 0.442772, 0.120354 }, + { 0.946965, 0.449191, 0.115272 }, + { 0.949562, 0.455660, 0.110164 }, + { 0.952075, 0.462178, 0.105031 }, + { 0.954506, 0.468744, 0.099874 }, + { 0.956852, 0.475356, 0.094695 }, + { 0.959114, 0.482014, 0.089499 }, + { 0.961293, 0.488716, 0.084289 }, + { 0.963387, 0.495462, 0.079073 }, + { 0.965397, 0.502249, 0.073859 }, + { 0.967322, 0.509078, 0.068659 }, + { 0.969163, 0.515946, 0.063488 }, + { 0.970919, 0.522853, 0.058367 }, + { 0.972590, 0.529798, 0.053324 }, + { 0.974176, 0.536780, 0.048392 }, + { 0.975677, 0.543798, 0.043618 }, + { 0.977092, 0.550850, 0.039050 }, + { 0.978422, 0.557937, 0.034931 }, + { 0.979666, 0.565057, 0.031409 }, + { 0.980824, 0.572209, 0.028508 }, + { 0.981895, 0.579392, 0.026250 }, + { 0.982881, 0.586606, 0.024661 }, + { 0.983779, 0.593849, 0.023770 }, + { 0.984591, 0.601122, 0.023606 }, + { 0.985315, 0.608422, 0.024202 }, + { 0.985952, 0.615750, 0.025592 }, + { 0.986502, 0.623105, 0.027814 }, + { 0.986964, 0.630485, 0.030908 }, + { 0.987337, 0.637890, 0.034916 }, + { 0.987622, 0.645320, 0.039886 }, + { 0.987819, 0.652773, 0.045581 }, + { 0.987926, 0.660250, 0.051750 }, + { 0.987945, 0.667748, 0.058329 }, + { 0.987874, 0.675267, 0.065257 }, + { 0.987714, 0.682807, 0.072489 }, + { 0.987464, 0.690366, 0.079990 }, + { 0.987124, 0.697944, 0.087731 }, + { 0.986694, 0.705540, 0.095694 }, + { 0.986175, 0.713153, 0.103863 }, + { 0.985566, 0.720782, 0.112229 }, + { 0.984865, 0.728427, 0.120785 }, + { 0.984075, 0.736087, 0.129527 }, + { 0.983196, 0.743758, 0.138453 }, + { 0.982228, 0.751442, 0.147565 }, + { 0.981173, 0.759135, 0.156863 }, + { 0.980032, 0.766837, 0.166353 }, + { 0.978806, 0.774545, 0.176037 }, + { 0.977497, 0.782258, 0.185923 }, + { 0.976108, 0.789974, 0.196018 }, + { 0.974638, 0.797692, 0.206332 }, + { 0.973088, 0.805409, 0.216877 }, + { 0.971468, 0.813122, 0.227658 }, + { 0.969783, 0.820825, 0.238686 }, + { 0.968041, 0.828515, 0.249972 }, + { 0.966243, 0.836191, 0.261534 }, + { 0.964394, 0.843848, 0.273391 }, + { 0.962517, 0.851476, 0.285546 }, + { 0.960626, 0.859069, 0.298010 }, + { 0.958720, 0.866624, 0.310820 }, + { 0.956834, 0.874129, 0.323974 }, + { 0.954997, 0.881569, 0.337475 }, + { 0.953215, 0.888942, 0.351369 }, + { 0.951546, 0.896226, 0.365627 }, + { 0.950018, 0.903409, 0.380271 }, + { 0.948683, 0.910473, 0.395289 }, + { 0.947594, 0.917399, 0.410665 }, + { 0.946809, 0.924168, 0.426373 }, + { 0.946392, 0.930761, 0.442367 }, + { 0.946403, 0.937159, 0.458592 }, + { 0.946903, 0.943348, 0.474970 }, + { 0.947937, 0.949318, 0.491426 }, + { 0.949545, 0.955063, 0.507860 }, + { 0.951740, 0.960587, 0.524203 }, + { 0.954529, 0.965896, 0.540361 }, + { 0.957896, 0.971003, 0.556275 }, + { 0.961812, 0.975924, 0.571925 }, + { 0.966249, 0.980678, 0.587206 }, + { 0.971162, 0.985282, 0.602154 }, + { 0.976511, 0.989753, 0.616760 }, + { 0.982257, 0.994109, 0.631017 }, + { 0.988362, 0.998364, 0.644924 } +}; + +static double magma_cm[256][3] = { + { 0.001462, 0.000466, 0.013866 }, + { 0.002258, 0.001295, 0.018331 }, + { 0.003279, 0.002305, 0.023708 }, + { 0.004512, 0.003490, 0.029965 }, + { 0.005950, 0.004843, 0.037130 }, + { 0.007588, 0.006356, 0.044973 }, + { 0.009426, 0.008022, 0.052844 }, + { 0.011465, 0.009828, 0.060750 }, + { 0.013708, 0.011771, 0.068667 }, + { 0.016156, 0.013840, 0.076603 }, + { 0.018815, 0.016026, 0.084584 }, + { 0.021692, 0.018320, 0.092610 }, + { 0.024792, 0.020715, 0.100676 }, + { 0.028123, 0.023201, 0.108787 }, + { 0.031696, 0.025765, 0.116965 }, + { 0.035520, 0.028397, 0.125209 }, + { 0.039608, 0.031090, 0.133515 }, + { 0.043830, 0.033830, 0.141886 }, + { 0.048062, 0.036607, 0.150327 }, + { 0.052320, 0.039407, 0.158841 }, + { 0.056615, 0.042160, 0.167446 }, + { 0.060949, 0.044794, 0.176129 }, + { 0.065330, 0.047318, 0.184892 }, + { 0.069764, 0.049726, 0.193735 }, + { 0.074257, 0.052017, 0.202660 }, + { 0.078815, 0.054184, 0.211667 }, + { 0.083446, 0.056225, 0.220755 }, + { 0.088155, 0.058133, 0.229922 }, + { 0.092949, 0.059904, 0.239164 }, + { 0.097833, 0.061531, 0.248477 }, + { 0.102815, 0.063010, 0.257854 }, + { 0.107899, 0.064335, 0.267289 }, + { 0.113094, 0.065492, 0.276784 }, + { 0.118405, 0.066479, 0.286321 }, + { 0.123833, 0.067295, 0.295879 }, + { 0.129380, 0.067935, 0.305443 }, + { 0.135053, 0.068391, 0.315000 }, + { 0.140858, 0.068654, 0.324538 }, + { 0.146785, 0.068738, 0.334011 }, + { 0.152839, 0.068637, 0.343404 }, + { 0.159018, 0.068354, 0.352688 }, + { 0.165308, 0.067911, 0.361816 }, + { 0.171713, 0.067305, 0.370771 }, + { 0.178212, 0.066576, 0.379497 }, + { 0.184801, 0.065732, 0.387973 }, + { 0.191460, 0.064818, 0.396152 }, + { 0.198177, 0.063862, 0.404009 }, + { 0.204935, 0.062907, 0.411514 }, + { 0.211718, 0.061992, 0.418647 }, + { 0.218512, 0.061158, 0.425392 }, + { 0.225302, 0.060445, 0.431742 }, + { 0.232077, 0.059889, 0.437695 }, + { 0.238826, 0.059517, 0.443256 }, + { 0.245543, 0.059352, 0.448436 }, + { 0.252220, 0.059415, 0.453248 }, + { 0.258857, 0.059706, 0.457710 }, + { 0.265447, 0.060237, 0.461840 }, + { 0.271994, 0.060994, 0.465660 }, + { 0.278493, 0.061978, 0.469190 }, + { 0.284951, 0.063168, 0.472451 }, + { 0.291366, 0.064553, 0.475462 }, + { 0.297740, 0.066117, 0.478243 }, + { 0.304081, 0.067835, 0.480812 }, + { 0.310382, 0.069702, 0.483186 }, + { 0.316654, 0.071690, 0.485380 }, + { 0.322899, 0.073782, 0.487408 }, + { 0.329114, 0.075972, 0.489287 }, + { 0.335308, 0.078236, 0.491024 }, + { 0.341482, 0.080564, 0.492631 }, + { 0.347636, 0.082946, 0.494121 }, + { 0.353773, 0.085373, 0.495501 }, + { 0.359898, 0.087831, 0.496778 }, + { 0.366012, 0.090314, 0.497960 }, + { 0.372116, 0.092816, 0.499053 }, + { 0.378211, 0.095332, 0.500067 }, + { 0.384299, 0.097855, 0.501002 }, + { 0.390384, 0.100379, 0.501864 }, + { 0.396467, 0.102902, 0.502658 }, + { 0.402548, 0.105420, 0.503386 }, + { 0.408629, 0.107930, 0.504052 }, + { 0.414709, 0.110431, 0.504662 }, + { 0.420791, 0.112920, 0.505215 }, + { 0.426877, 0.115395, 0.505714 }, + { 0.432967, 0.117855, 0.506160 }, + { 0.439062, 0.120298, 0.506555 }, + { 0.445163, 0.122724, 0.506901 }, + { 0.451271, 0.125132, 0.507198 }, + { 0.457386, 0.127522, 0.507448 }, + { 0.463508, 0.129893, 0.507652 }, + { 0.469640, 0.132245, 0.507809 }, + { 0.475780, 0.134577, 0.507921 }, + { 0.481929, 0.136891, 0.507989 }, + { 0.488088, 0.139186, 0.508011 }, + { 0.494258, 0.141462, 0.507988 }, + { 0.500438, 0.143719, 0.507920 }, + { 0.506629, 0.145958, 0.507806 }, + { 0.512831, 0.148179, 0.507648 }, + { 0.519045, 0.150383, 0.507443 }, + { 0.525270, 0.152569, 0.507192 }, + { 0.531507, 0.154739, 0.506895 }, + { 0.537755, 0.156894, 0.506551 }, + { 0.544015, 0.159033, 0.506159 }, + { 0.550287, 0.161158, 0.505719 }, + { 0.556571, 0.163269, 0.505230 }, + { 0.562866, 0.165368, 0.504692 }, + { 0.569172, 0.167454, 0.504105 }, + { 0.575490, 0.169530, 0.503466 }, + { 0.581819, 0.171596, 0.502777 }, + { 0.588158, 0.173652, 0.502035 }, + { 0.594508, 0.175701, 0.501241 }, + { 0.600868, 0.177743, 0.500394 }, + { 0.607238, 0.179779, 0.499492 }, + { 0.613617, 0.181811, 0.498536 }, + { 0.620005, 0.183840, 0.497524 }, + { 0.626401, 0.185867, 0.496456 }, + { 0.632805, 0.187893, 0.495332 }, + { 0.639216, 0.189921, 0.494150 }, + { 0.645633, 0.191952, 0.492910 }, + { 0.652056, 0.193986, 0.491611 }, + { 0.658483, 0.196027, 0.490253 }, + { 0.664915, 0.198075, 0.488836 }, + { 0.671349, 0.200133, 0.487358 }, + { 0.677786, 0.202203, 0.485819 }, + { 0.684224, 0.204286, 0.484219 }, + { 0.690661, 0.206384, 0.482558 }, + { 0.697098, 0.208501, 0.480835 }, + { 0.703532, 0.210638, 0.479049 }, + { 0.709962, 0.212797, 0.477201 }, + { 0.716387, 0.214982, 0.475290 }, + { 0.722805, 0.217194, 0.473316 }, + { 0.729216, 0.219437, 0.471279 }, + { 0.735616, 0.221713, 0.469180 }, + { 0.742004, 0.224025, 0.467018 }, + { 0.748378, 0.226377, 0.464794 }, + { 0.754737, 0.228772, 0.462509 }, + { 0.761077, 0.231214, 0.460162 }, + { 0.767398, 0.233705, 0.457755 }, + { 0.773695, 0.236249, 0.455289 }, + { 0.779968, 0.238851, 0.452765 }, + { 0.786212, 0.241514, 0.450184 }, + { 0.792427, 0.244242, 0.447543 }, + { 0.798608, 0.247040, 0.444848 }, + { 0.804752, 0.249911, 0.442102 }, + { 0.810855, 0.252861, 0.439305 }, + { 0.816914, 0.255895, 0.436461 }, + { 0.822926, 0.259016, 0.433573 }, + { 0.828886, 0.262229, 0.430644 }, + { 0.834791, 0.265540, 0.427671 }, + { 0.840636, 0.268953, 0.424666 }, + { 0.846416, 0.272473, 0.421631 }, + { 0.852126, 0.276106, 0.418573 }, + { 0.857763, 0.279857, 0.415496 }, + { 0.863320, 0.283729, 0.412403 }, + { 0.868793, 0.287728, 0.409303 }, + { 0.874176, 0.291859, 0.406205 }, + { 0.879464, 0.296125, 0.403118 }, + { 0.884651, 0.300530, 0.400047 }, + { 0.889731, 0.305079, 0.397002 }, + { 0.894700, 0.309773, 0.393995 }, + { 0.899552, 0.314616, 0.391037 }, + { 0.904281, 0.319610, 0.388137 }, + { 0.908884, 0.324755, 0.385308 }, + { 0.913354, 0.330052, 0.382563 }, + { 0.917689, 0.335500, 0.379915 }, + { 0.921884, 0.341098, 0.377376 }, + { 0.925937, 0.346844, 0.374959 }, + { 0.929845, 0.352734, 0.372677 }, + { 0.933606, 0.358764, 0.370541 }, + { 0.937221, 0.364929, 0.368567 }, + { 0.940687, 0.371224, 0.366762 }, + { 0.944006, 0.377643, 0.365136 }, + { 0.947180, 0.384178, 0.363701 }, + { 0.950210, 0.390820, 0.362468 }, + { 0.953099, 0.397563, 0.361438 }, + { 0.955849, 0.404400, 0.360619 }, + { 0.958464, 0.411324, 0.360014 }, + { 0.960949, 0.418323, 0.359630 }, + { 0.963310, 0.425390, 0.359469 }, + { 0.965549, 0.432519, 0.359529 }, + { 0.967671, 0.439703, 0.359810 }, + { 0.969680, 0.446936, 0.360311 }, + { 0.971582, 0.454210, 0.361030 }, + { 0.973381, 0.461520, 0.361965 }, + { 0.975082, 0.468861, 0.363111 }, + { 0.976690, 0.476226, 0.364466 }, + { 0.978210, 0.483612, 0.366025 }, + { 0.979645, 0.491014, 0.367783 }, + { 0.981000, 0.498428, 0.369734 }, + { 0.982279, 0.505851, 0.371874 }, + { 0.983485, 0.513280, 0.374198 }, + { 0.984622, 0.520713, 0.376698 }, + { 0.985693, 0.528148, 0.379371 }, + { 0.986700, 0.535582, 0.382210 }, + { 0.987646, 0.543015, 0.385210 }, + { 0.988533, 0.550446, 0.388365 }, + { 0.989363, 0.557873, 0.391671 }, + { 0.990138, 0.565296, 0.395122 }, + { 0.990871, 0.572706, 0.398714 }, + { 0.991558, 0.580107, 0.402441 }, + { 0.992196, 0.587502, 0.406299 }, + { 0.992785, 0.594891, 0.410283 }, + { 0.993326, 0.602275, 0.414390 }, + { 0.993834, 0.609644, 0.418613 }, + { 0.994309, 0.616999, 0.422950 }, + { 0.994738, 0.624350, 0.427397 }, + { 0.995122, 0.631696, 0.431951 }, + { 0.995480, 0.639027, 0.436607 }, + { 0.995810, 0.646344, 0.441361 }, + { 0.996096, 0.653659, 0.446213 }, + { 0.996341, 0.660969, 0.451160 }, + { 0.996580, 0.668256, 0.456192 }, + { 0.996775, 0.675541, 0.461314 }, + { 0.996925, 0.682828, 0.466526 }, + { 0.997077, 0.690088, 0.471811 }, + { 0.997186, 0.697349, 0.477182 }, + { 0.997254, 0.704611, 0.482635 }, + { 0.997325, 0.711848, 0.488154 }, + { 0.997351, 0.719089, 0.493755 }, + { 0.997351, 0.726324, 0.499428 }, + { 0.997341, 0.733545, 0.505167 }, + { 0.997285, 0.740772, 0.510983 }, + { 0.997228, 0.747981, 0.516859 }, + { 0.997138, 0.755190, 0.522806 }, + { 0.997019, 0.762398, 0.528821 }, + { 0.996898, 0.769591, 0.534892 }, + { 0.996727, 0.776795, 0.541039 }, + { 0.996571, 0.783977, 0.547233 }, + { 0.996369, 0.791167, 0.553499 }, + { 0.996162, 0.798348, 0.559820 }, + { 0.995932, 0.805527, 0.566202 }, + { 0.995680, 0.812706, 0.572645 }, + { 0.995424, 0.819875, 0.579140 }, + { 0.995131, 0.827052, 0.585701 }, + { 0.994851, 0.834213, 0.592307 }, + { 0.994524, 0.841387, 0.598983 }, + { 0.994222, 0.848540, 0.605696 }, + { 0.993866, 0.855711, 0.612482 }, + { 0.993545, 0.862859, 0.619299 }, + { 0.993170, 0.870024, 0.626189 }, + { 0.992831, 0.877168, 0.633109 }, + { 0.992440, 0.884330, 0.640099 }, + { 0.992089, 0.891470, 0.647116 }, + { 0.991688, 0.898627, 0.654202 }, + { 0.991332, 0.905763, 0.661309 }, + { 0.990930, 0.912915, 0.668481 }, + { 0.990570, 0.920049, 0.675675 }, + { 0.990175, 0.927196, 0.682926 }, + { 0.989815, 0.934329, 0.690198 }, + { 0.989434, 0.941470, 0.697519 }, + { 0.989077, 0.948604, 0.704863 }, + { 0.988717, 0.955742, 0.712242 }, + { 0.988367, 0.962878, 0.719649 }, + { 0.988033, 0.970012, 0.727077 }, + { 0.987691, 0.977154, 0.734536 }, + { 0.987387, 0.984288, 0.742002 }, + { 0.987053, 0.991438, 0.749504 } +}; + +static double plasma_cm[256][3] = { + { 0.050383, 0.029803, 0.527975 }, + { 0.063536, 0.028426, 0.533124 }, + { 0.075353, 0.027206, 0.538007 }, + { 0.086222, 0.026125, 0.542658 }, + { 0.096379, 0.025165, 0.547103 }, + { 0.105980, 0.024309, 0.551368 }, + { 0.115124, 0.023556, 0.555468 }, + { 0.123903, 0.022878, 0.559423 }, + { 0.132381, 0.022258, 0.563250 }, + { 0.140603, 0.021687, 0.566959 }, + { 0.148607, 0.021154, 0.570562 }, + { 0.156421, 0.020651, 0.574065 }, + { 0.164070, 0.020171, 0.577478 }, + { 0.171574, 0.019706, 0.580806 }, + { 0.178950, 0.019252, 0.584054 }, + { 0.186213, 0.018803, 0.587228 }, + { 0.193374, 0.018354, 0.590330 }, + { 0.200445, 0.017902, 0.593364 }, + { 0.207435, 0.017442, 0.596333 }, + { 0.214350, 0.016973, 0.599239 }, + { 0.221197, 0.016497, 0.602083 }, + { 0.227983, 0.016007, 0.604867 }, + { 0.234715, 0.015502, 0.607592 }, + { 0.241396, 0.014979, 0.610259 }, + { 0.248032, 0.014439, 0.612868 }, + { 0.254627, 0.013882, 0.615419 }, + { 0.261183, 0.013308, 0.617911 }, + { 0.267703, 0.012716, 0.620346 }, + { 0.274191, 0.012109, 0.622722 }, + { 0.280648, 0.011488, 0.625038 }, + { 0.287076, 0.010855, 0.627295 }, + { 0.293478, 0.010213, 0.629490 }, + { 0.299855, 0.009561, 0.631624 }, + { 0.306210, 0.008902, 0.633694 }, + { 0.312543, 0.008239, 0.635700 }, + { 0.318856, 0.007576, 0.637640 }, + { 0.325150, 0.006915, 0.639512 }, + { 0.331426, 0.006261, 0.641316 }, + { 0.337683, 0.005618, 0.643049 }, + { 0.343925, 0.004991, 0.644710 }, + { 0.350150, 0.004382, 0.646298 }, + { 0.356359, 0.003798, 0.647810 }, + { 0.362553, 0.003243, 0.649245 }, + { 0.368733, 0.002724, 0.650601 }, + { 0.374897, 0.002245, 0.651876 }, + { 0.381047, 0.001814, 0.653068 }, + { 0.387183, 0.001434, 0.654177 }, + { 0.393304, 0.001114, 0.655199 }, + { 0.399411, 0.000859, 0.656133 }, + { 0.405503, 0.000678, 0.656977 }, + { 0.411580, 0.000577, 0.657730 }, + { 0.417642, 0.000564, 0.658390 }, + { 0.423689, 0.000646, 0.658956 }, + { 0.429719, 0.000831, 0.659425 }, + { 0.435734, 0.001127, 0.659797 }, + { 0.441732, 0.001540, 0.660069 }, + { 0.447714, 0.002080, 0.660240 }, + { 0.453677, 0.002755, 0.660310 }, + { 0.459623, 0.003574, 0.660277 }, + { 0.465550, 0.004545, 0.660139 }, + { 0.471457, 0.005678, 0.659897 }, + { 0.477344, 0.006980, 0.659549 }, + { 0.483210, 0.008460, 0.659095 }, + { 0.489055, 0.010127, 0.658534 }, + { 0.494877, 0.011990, 0.657865 }, + { 0.500678, 0.014055, 0.657088 }, + { 0.506454, 0.016333, 0.656202 }, + { 0.512206, 0.018833, 0.655209 }, + { 0.517933, 0.021563, 0.654109 }, + { 0.523633, 0.024532, 0.652901 }, + { 0.529306, 0.027747, 0.651586 }, + { 0.534952, 0.031217, 0.650165 }, + { 0.540570, 0.034950, 0.648640 }, + { 0.546157, 0.038954, 0.647010 }, + { 0.551715, 0.043136, 0.645277 }, + { 0.557243, 0.047331, 0.643443 }, + { 0.562738, 0.051545, 0.641509 }, + { 0.568201, 0.055778, 0.639477 }, + { 0.573632, 0.060028, 0.637349 }, + { 0.579029, 0.064296, 0.635126 }, + { 0.584391, 0.068579, 0.632812 }, + { 0.589719, 0.072878, 0.630408 }, + { 0.595011, 0.077190, 0.627917 }, + { 0.600266, 0.081516, 0.625342 }, + { 0.605485, 0.085854, 0.622686 }, + { 0.610667, 0.090204, 0.619951 }, + { 0.615812, 0.094564, 0.617140 }, + { 0.620919, 0.098934, 0.614257 }, + { 0.625987, 0.103312, 0.611305 }, + { 0.631017, 0.107699, 0.608287 }, + { 0.636008, 0.112092, 0.605205 }, + { 0.640959, 0.116492, 0.602065 }, + { 0.645872, 0.120898, 0.598867 }, + { 0.650746, 0.125309, 0.595617 }, + { 0.655580, 0.129725, 0.592317 }, + { 0.660374, 0.134144, 0.588971 }, + { 0.665129, 0.138566, 0.585582 }, + { 0.669845, 0.142992, 0.582154 }, + { 0.674522, 0.147419, 0.578688 }, + { 0.679160, 0.151848, 0.575189 }, + { 0.683758, 0.156278, 0.571660 }, + { 0.688318, 0.160709, 0.568103 }, + { 0.692840, 0.165141, 0.564522 }, + { 0.697324, 0.169573, 0.560919 }, + { 0.701769, 0.174005, 0.557296 }, + { 0.706178, 0.178437, 0.553657 }, + { 0.710549, 0.182868, 0.550004 }, + { 0.714883, 0.187299, 0.546338 }, + { 0.719181, 0.191729, 0.542663 }, + { 0.723444, 0.196158, 0.538981 }, + { 0.727670, 0.200586, 0.535293 }, + { 0.731862, 0.205013, 0.531601 }, + { 0.736019, 0.209439, 0.527908 }, + { 0.740143, 0.213864, 0.524216 }, + { 0.744232, 0.218288, 0.520524 }, + { 0.748289, 0.222711, 0.516834 }, + { 0.752312, 0.227133, 0.513149 }, + { 0.756304, 0.231555, 0.509468 }, + { 0.760264, 0.235976, 0.505794 }, + { 0.764193, 0.240396, 0.502126 }, + { 0.768090, 0.244817, 0.498465 }, + { 0.771958, 0.249237, 0.494813 }, + { 0.775796, 0.253658, 0.491171 }, + { 0.779604, 0.258078, 0.487539 }, + { 0.783383, 0.262500, 0.483918 }, + { 0.787133, 0.266922, 0.480307 }, + { 0.790855, 0.271345, 0.476706 }, + { 0.794549, 0.275770, 0.473117 }, + { 0.798216, 0.280197, 0.469538 }, + { 0.801855, 0.284626, 0.465971 }, + { 0.805467, 0.289057, 0.462415 }, + { 0.809052, 0.293491, 0.458870 }, + { 0.812612, 0.297928, 0.455338 }, + { 0.816144, 0.302368, 0.451816 }, + { 0.819651, 0.306812, 0.448306 }, + { 0.823132, 0.311261, 0.444806 }, + { 0.826588, 0.315714, 0.441316 }, + { 0.830018, 0.320172, 0.437836 }, + { 0.833422, 0.324635, 0.434366 }, + { 0.836801, 0.329105, 0.430905 }, + { 0.840155, 0.333580, 0.427455 }, + { 0.843484, 0.338062, 0.424013 }, + { 0.846788, 0.342551, 0.420579 }, + { 0.850066, 0.347048, 0.417153 }, + { 0.853319, 0.351553, 0.413734 }, + { 0.856547, 0.356066, 0.410322 }, + { 0.859750, 0.360588, 0.406917 }, + { 0.862927, 0.365119, 0.403519 }, + { 0.866078, 0.369660, 0.400126 }, + { 0.869203, 0.374212, 0.396738 }, + { 0.872303, 0.378774, 0.393355 }, + { 0.875376, 0.383347, 0.389976 }, + { 0.878423, 0.387932, 0.386600 }, + { 0.881443, 0.392529, 0.383229 }, + { 0.884436, 0.397139, 0.379860 }, + { 0.887402, 0.401762, 0.376494 }, + { 0.890340, 0.406398, 0.373130 }, + { 0.893250, 0.411048, 0.369768 }, + { 0.896131, 0.415712, 0.366407 }, + { 0.898984, 0.420392, 0.363047 }, + { 0.901807, 0.425087, 0.359688 }, + { 0.904601, 0.429797, 0.356329 }, + { 0.907365, 0.434524, 0.352970 }, + { 0.910098, 0.439268, 0.349610 }, + { 0.912800, 0.444029, 0.346251 }, + { 0.915471, 0.448807, 0.342890 }, + { 0.918109, 0.453603, 0.339529 }, + { 0.920714, 0.458417, 0.336166 }, + { 0.923287, 0.463251, 0.332801 }, + { 0.925825, 0.468103, 0.329435 }, + { 0.928329, 0.472975, 0.326067 }, + { 0.930798, 0.477867, 0.322697 }, + { 0.933232, 0.482780, 0.319325 }, + { 0.935630, 0.487712, 0.315952 }, + { 0.937990, 0.492667, 0.312575 }, + { 0.940313, 0.497642, 0.309197 }, + { 0.942598, 0.502639, 0.305816 }, + { 0.944844, 0.507658, 0.302433 }, + { 0.947051, 0.512699, 0.299049 }, + { 0.949217, 0.517763, 0.295662 }, + { 0.951344, 0.522850, 0.292275 }, + { 0.953428, 0.527960, 0.288883 }, + { 0.955470, 0.533093, 0.285490 }, + { 0.957469, 0.538250, 0.282096 }, + { 0.959424, 0.543431, 0.278701 }, + { 0.961336, 0.548636, 0.275305 }, + { 0.963203, 0.553865, 0.271909 }, + { 0.965024, 0.559118, 0.268513 }, + { 0.966798, 0.564396, 0.265118 }, + { 0.968526, 0.569700, 0.261721 }, + { 0.970205, 0.575028, 0.258325 }, + { 0.971835, 0.580382, 0.254931 }, + { 0.973416, 0.585761, 0.251540 }, + { 0.974947, 0.591165, 0.248151 }, + { 0.976428, 0.596595, 0.244767 }, + { 0.977856, 0.602051, 0.241387 }, + { 0.979233, 0.607532, 0.238013 }, + { 0.980556, 0.613039, 0.234646 }, + { 0.981826, 0.618572, 0.231287 }, + { 0.983041, 0.624131, 0.227937 }, + { 0.984199, 0.629718, 0.224595 }, + { 0.985301, 0.635330, 0.221265 }, + { 0.986345, 0.640969, 0.217948 }, + { 0.987332, 0.646633, 0.214648 }, + { 0.988260, 0.652325, 0.211364 }, + { 0.989128, 0.658043, 0.208100 }, + { 0.989935, 0.663787, 0.204859 }, + { 0.990681, 0.669558, 0.201642 }, + { 0.991365, 0.675355, 0.198453 }, + { 0.991985, 0.681179, 0.195295 }, + { 0.992541, 0.687030, 0.192170 }, + { 0.993032, 0.692907, 0.189084 }, + { 0.993456, 0.698810, 0.186041 }, + { 0.993814, 0.704741, 0.183043 }, + { 0.994103, 0.710698, 0.180097 }, + { 0.994324, 0.716681, 0.177208 }, + { 0.994474, 0.722691, 0.174381 }, + { 0.994553, 0.728728, 0.171622 }, + { 0.994561, 0.734791, 0.168938 }, + { 0.994495, 0.740880, 0.166335 }, + { 0.994355, 0.746995, 0.163821 }, + { 0.994141, 0.753137, 0.161404 }, + { 0.993851, 0.759304, 0.159092 }, + { 0.993482, 0.765499, 0.156891 }, + { 0.993033, 0.771720, 0.154808 }, + { 0.992505, 0.777967, 0.152855 }, + { 0.991897, 0.784239, 0.151042 }, + { 0.991209, 0.790537, 0.149377 }, + { 0.990439, 0.796859, 0.147870 }, + { 0.989587, 0.803205, 0.146529 }, + { 0.988648, 0.809579, 0.145357 }, + { 0.987621, 0.815978, 0.144363 }, + { 0.986509, 0.822401, 0.143557 }, + { 0.985314, 0.828846, 0.142945 }, + { 0.984031, 0.835315, 0.142528 }, + { 0.982653, 0.841812, 0.142303 }, + { 0.981190, 0.848329, 0.142279 }, + { 0.979644, 0.854866, 0.142453 }, + { 0.977995, 0.861432, 0.142808 }, + { 0.976265, 0.868016, 0.143351 }, + { 0.974443, 0.874622, 0.144061 }, + { 0.972530, 0.881250, 0.144923 }, + { 0.970533, 0.887896, 0.145919 }, + { 0.968443, 0.894564, 0.147014 }, + { 0.966271, 0.901249, 0.148180 }, + { 0.964021, 0.907950, 0.149370 }, + { 0.961681, 0.914672, 0.150520 }, + { 0.959276, 0.921407, 0.151566 }, + { 0.956808, 0.928152, 0.152409 }, + { 0.954287, 0.934908, 0.152921 }, + { 0.951726, 0.941671, 0.152925 }, + { 0.949151, 0.948435, 0.152178 }, + { 0.946602, 0.955190, 0.150328 }, + { 0.944152, 0.961916, 0.146861 }, + { 0.941896, 0.968590, 0.140956 }, + { 0.940015, 0.975158, 0.131326 } +}; + +static double viridis_cm[256][3] = { + { 0.267004, 0.004874, 0.329415 }, + { 0.268510, 0.009605, 0.335427 }, + { 0.269944, 0.014625, 0.341379 }, + { 0.271305, 0.019942, 0.347269 }, + { 0.272594, 0.025563, 0.353093 }, + { 0.273809, 0.031497, 0.358853 }, + { 0.274952, 0.037752, 0.364543 }, + { 0.276022, 0.044167, 0.370164 }, + { 0.277018, 0.050344, 0.375715 }, + { 0.277941, 0.056324, 0.381191 }, + { 0.278791, 0.062145, 0.386592 }, + { 0.279566, 0.067836, 0.391917 }, + { 0.280267, 0.073417, 0.397163 }, + { 0.280894, 0.078907, 0.402329 }, + { 0.281446, 0.084320, 0.407414 }, + { 0.281924, 0.089666, 0.412415 }, + { 0.282327, 0.094955, 0.417331 }, + { 0.282656, 0.100196, 0.422160 }, + { 0.282910, 0.105393, 0.426902 }, + { 0.283091, 0.110553, 0.431554 }, + { 0.283197, 0.115680, 0.436115 }, + { 0.283229, 0.120777, 0.440584 }, + { 0.283187, 0.125848, 0.444960 }, + { 0.283072, 0.130895, 0.449241 }, + { 0.282884, 0.135920, 0.453427 }, + { 0.282623, 0.140926, 0.457517 }, + { 0.282290, 0.145912, 0.461510 }, + { 0.281887, 0.150881, 0.465405 }, + { 0.281412, 0.155834, 0.469201 }, + { 0.280868, 0.160771, 0.472899 }, + { 0.280255, 0.165693, 0.476498 }, + { 0.279574, 0.170599, 0.479997 }, + { 0.278826, 0.175490, 0.483397 }, + { 0.278012, 0.180367, 0.486697 }, + { 0.277134, 0.185228, 0.489898 }, + { 0.276194, 0.190074, 0.493001 }, + { 0.275191, 0.194905, 0.496005 }, + { 0.274128, 0.199721, 0.498911 }, + { 0.273006, 0.204520, 0.501721 }, + { 0.271828, 0.209303, 0.504434 }, + { 0.270595, 0.214069, 0.507052 }, + { 0.269308, 0.218818, 0.509577 }, + { 0.267968, 0.223549, 0.512008 }, + { 0.266580, 0.228262, 0.514349 }, + { 0.265145, 0.232956, 0.516599 }, + { 0.263663, 0.237631, 0.518762 }, + { 0.262138, 0.242286, 0.520837 }, + { 0.260571, 0.246922, 0.522828 }, + { 0.258965, 0.251537, 0.524736 }, + { 0.257322, 0.256130, 0.526563 }, + { 0.255645, 0.260703, 0.528312 }, + { 0.253935, 0.265254, 0.529983 }, + { 0.252194, 0.269783, 0.531579 }, + { 0.250425, 0.274290, 0.533103 }, + { 0.248629, 0.278775, 0.534556 }, + { 0.246811, 0.283237, 0.535941 }, + { 0.244972, 0.287675, 0.537260 }, + { 0.243113, 0.292092, 0.538516 }, + { 0.241237, 0.296485, 0.539709 }, + { 0.239346, 0.300855, 0.540844 }, + { 0.237441, 0.305202, 0.541921 }, + { 0.235526, 0.309527, 0.542944 }, + { 0.233603, 0.313828, 0.543914 }, + { 0.231674, 0.318106, 0.544834 }, + { 0.229739, 0.322361, 0.545706 }, + { 0.227802, 0.326594, 0.546532 }, + { 0.225863, 0.330805, 0.547314 }, + { 0.223925, 0.334994, 0.548053 }, + { 0.221989, 0.339161, 0.548752 }, + { 0.220057, 0.343307, 0.549413 }, + { 0.218130, 0.347432, 0.550038 }, + { 0.216210, 0.351535, 0.550627 }, + { 0.214298, 0.355619, 0.551184 }, + { 0.212395, 0.359683, 0.551710 }, + { 0.210503, 0.363727, 0.552206 }, + { 0.208623, 0.367752, 0.552675 }, + { 0.206756, 0.371758, 0.553117 }, + { 0.204903, 0.375746, 0.553533 }, + { 0.203063, 0.379716, 0.553925 }, + { 0.201239, 0.383670, 0.554294 }, + { 0.199430, 0.387607, 0.554642 }, + { 0.197636, 0.391528, 0.554969 }, + { 0.195860, 0.395433, 0.555276 }, + { 0.194100, 0.399323, 0.555565 }, + { 0.192357, 0.403199, 0.555836 }, + { 0.190631, 0.407061, 0.556089 }, + { 0.188923, 0.410910, 0.556326 }, + { 0.187231, 0.414746, 0.556547 }, + { 0.185556, 0.418570, 0.556753 }, + { 0.183898, 0.422383, 0.556944 }, + { 0.182256, 0.426184, 0.557120 }, + { 0.180629, 0.429975, 0.557282 }, + { 0.179019, 0.433756, 0.557430 }, + { 0.177423, 0.437527, 0.557565 }, + { 0.175841, 0.441290, 0.557685 }, + { 0.174274, 0.445044, 0.557792 }, + { 0.172719, 0.448791, 0.557885 }, + { 0.171176, 0.452530, 0.557965 }, + { 0.169646, 0.456262, 0.558030 }, + { 0.168126, 0.459988, 0.558082 }, + { 0.166617, 0.463708, 0.558119 }, + { 0.165117, 0.467423, 0.558141 }, + { 0.163625, 0.471133, 0.558148 }, + { 0.162142, 0.474838, 0.558140 }, + { 0.160665, 0.478540, 0.558115 }, + { 0.159194, 0.482237, 0.558073 }, + { 0.157729, 0.485932, 0.558013 }, + { 0.156270, 0.489624, 0.557936 }, + { 0.154815, 0.493313, 0.557840 }, + { 0.153364, 0.497000, 0.557724 }, + { 0.151918, 0.500685, 0.557587 }, + { 0.150476, 0.504369, 0.557430 }, + { 0.149039, 0.508051, 0.557250 }, + { 0.147607, 0.511733, 0.557049 }, + { 0.146180, 0.515413, 0.556823 }, + { 0.144759, 0.519093, 0.556572 }, + { 0.143343, 0.522773, 0.556295 }, + { 0.141935, 0.526453, 0.555991 }, + { 0.140536, 0.530132, 0.555659 }, + { 0.139147, 0.533812, 0.555298 }, + { 0.137770, 0.537492, 0.554906 }, + { 0.136408, 0.541173, 0.554483 }, + { 0.135066, 0.544853, 0.554029 }, + { 0.133743, 0.548535, 0.553541 }, + { 0.132444, 0.552216, 0.553018 }, + { 0.131172, 0.555899, 0.552459 }, + { 0.129933, 0.559582, 0.551864 }, + { 0.128729, 0.563265, 0.551229 }, + { 0.127568, 0.566949, 0.550556 }, + { 0.126453, 0.570633, 0.549841 }, + { 0.125394, 0.574318, 0.549086 }, + { 0.124395, 0.578002, 0.548287 }, + { 0.123463, 0.581687, 0.547445 }, + { 0.122606, 0.585371, 0.546557 }, + { 0.121831, 0.589055, 0.545623 }, + { 0.121148, 0.592739, 0.544641 }, + { 0.120565, 0.596422, 0.543611 }, + { 0.120092, 0.600104, 0.542530 }, + { 0.119738, 0.603785, 0.541400 }, + { 0.119512, 0.607464, 0.540218 }, + { 0.119423, 0.611141, 0.538982 }, + { 0.119483, 0.614817, 0.537692 }, + { 0.119699, 0.618490, 0.536347 }, + { 0.120081, 0.622161, 0.534946 }, + { 0.120638, 0.625828, 0.533488 }, + { 0.121380, 0.629492, 0.531973 }, + { 0.122312, 0.633153, 0.530398 }, + { 0.123444, 0.636809, 0.528763 }, + { 0.124780, 0.640461, 0.527068 }, + { 0.126326, 0.644107, 0.525311 }, + { 0.128087, 0.647749, 0.523491 }, + { 0.130067, 0.651384, 0.521608 }, + { 0.132268, 0.655014, 0.519661 }, + { 0.134692, 0.658636, 0.517649 }, + { 0.137339, 0.662252, 0.515571 }, + { 0.140210, 0.665859, 0.513427 }, + { 0.143303, 0.669459, 0.511215 }, + { 0.146616, 0.673050, 0.508936 }, + { 0.150148, 0.676631, 0.506589 }, + { 0.153894, 0.680203, 0.504172 }, + { 0.157851, 0.683765, 0.501686 }, + { 0.162016, 0.687316, 0.499129 }, + { 0.166383, 0.690856, 0.496502 }, + { 0.170948, 0.694384, 0.493803 }, + { 0.175707, 0.697900, 0.491033 }, + { 0.180653, 0.701402, 0.488189 }, + { 0.185783, 0.704891, 0.485273 }, + { 0.191090, 0.708366, 0.482284 }, + { 0.196571, 0.711827, 0.479221 }, + { 0.202219, 0.715272, 0.476084 }, + { 0.208030, 0.718701, 0.472873 }, + { 0.214000, 0.722114, 0.469588 }, + { 0.220124, 0.725509, 0.466226 }, + { 0.226397, 0.728888, 0.462789 }, + { 0.232815, 0.732247, 0.459277 }, + { 0.239374, 0.735588, 0.455688 }, + { 0.246070, 0.738910, 0.452024 }, + { 0.252899, 0.742211, 0.448284 }, + { 0.259857, 0.745492, 0.444467 }, + { 0.266941, 0.748751, 0.440573 }, + { 0.274149, 0.751988, 0.436601 }, + { 0.281477, 0.755203, 0.432552 }, + { 0.288921, 0.758394, 0.428426 }, + { 0.296479, 0.761561, 0.424223 }, + { 0.304148, 0.764704, 0.419943 }, + { 0.311925, 0.767822, 0.415586 }, + { 0.319809, 0.770914, 0.411152 }, + { 0.327796, 0.773980, 0.406640 }, + { 0.335885, 0.777018, 0.402049 }, + { 0.344074, 0.780029, 0.397381 }, + { 0.352360, 0.783011, 0.392636 }, + { 0.360741, 0.785964, 0.387814 }, + { 0.369214, 0.788888, 0.382914 }, + { 0.377779, 0.791781, 0.377939 }, + { 0.386433, 0.794644, 0.372886 }, + { 0.395174, 0.797475, 0.367757 }, + { 0.404001, 0.800275, 0.362552 }, + { 0.412913, 0.803041, 0.357269 }, + { 0.421908, 0.805774, 0.351910 }, + { 0.430983, 0.808473, 0.346476 }, + { 0.440137, 0.811138, 0.340967 }, + { 0.449368, 0.813768, 0.335384 }, + { 0.458674, 0.816363, 0.329727 }, + { 0.468053, 0.818921, 0.323998 }, + { 0.477504, 0.821444, 0.318195 }, + { 0.487026, 0.823929, 0.312321 }, + { 0.496615, 0.826376, 0.306377 }, + { 0.506271, 0.828786, 0.300362 }, + { 0.515992, 0.831158, 0.294279 }, + { 0.525776, 0.833491, 0.288127 }, + { 0.535621, 0.835785, 0.281908 }, + { 0.545524, 0.838039, 0.275626 }, + { 0.555484, 0.840254, 0.269281 }, + { 0.565498, 0.842430, 0.262877 }, + { 0.575563, 0.844566, 0.256415 }, + { 0.585678, 0.846661, 0.249897 }, + { 0.595839, 0.848717, 0.243329 }, + { 0.606045, 0.850733, 0.236712 }, + { 0.616293, 0.852709, 0.230052 }, + { 0.626579, 0.854645, 0.223353 }, + { 0.636902, 0.856542, 0.216620 }, + { 0.647257, 0.858400, 0.209861 }, + { 0.657642, 0.860219, 0.203082 }, + { 0.668054, 0.861999, 0.196293 }, + { 0.678489, 0.863742, 0.189503 }, + { 0.688944, 0.865448, 0.182725 }, + { 0.699415, 0.867117, 0.175971 }, + { 0.709898, 0.868751, 0.169257 }, + { 0.720391, 0.870350, 0.162603 }, + { 0.730889, 0.871916, 0.156029 }, + { 0.741388, 0.873449, 0.149561 }, + { 0.751884, 0.874951, 0.143228 }, + { 0.762373, 0.876424, 0.137064 }, + { 0.772852, 0.877868, 0.131109 }, + { 0.783315, 0.879285, 0.125405 }, + { 0.793760, 0.880678, 0.120005 }, + { 0.804182, 0.882046, 0.114965 }, + { 0.814576, 0.883393, 0.110347 }, + { 0.824940, 0.884720, 0.106217 }, + { 0.835270, 0.886029, 0.102646 }, + { 0.845561, 0.887322, 0.099702 }, + { 0.855810, 0.888601, 0.097452 }, + { 0.866013, 0.889868, 0.095953 }, + { 0.876168, 0.891125, 0.095250 }, + { 0.886271, 0.892374, 0.095374 }, + { 0.896320, 0.893616, 0.096335 }, + { 0.906311, 0.894855, 0.098125 }, + { 0.916242, 0.896091, 0.100717 }, + { 0.926106, 0.897330, 0.104071 }, + { 0.935904, 0.898570, 0.108131 }, + { 0.945636, 0.899815, 0.112838 }, + { 0.955300, 0.901065, 0.118128 }, + { 0.964894, 0.902323, 0.123941 }, + { 0.974417, 0.903590, 0.130215 }, + { 0.983868, 0.904867, 0.136897 }, + { 0.993248, 0.906157, 0.143936 } +}; + +static double parula_cm[256][3] = { + { 0.2081, 0.1663, 0.5292 }, + { 0.2091, 0.1721, 0.5411 }, + { 0.2101, 0.1779, 0.553 }, + { 0.2109, 0.1837, 0.565 }, + { 0.2116, 0.1895, 0.5771 }, + { 0.2121, 0.1954, 0.5892 }, + { 0.2124, 0.2013, 0.6013 }, + { 0.2125, 0.2072, 0.6135 }, + { 0.2123, 0.2132, 0.6258 }, + { 0.2118, 0.2192, 0.6381 }, + { 0.2111, 0.2253, 0.6505 }, + { 0.2099, 0.2315, 0.6629 }, + { 0.2084, 0.2377, 0.6753 }, + { 0.2063, 0.244, 0.6878 }, + { 0.2038, 0.2503, 0.7003 }, + { 0.2006, 0.2568, 0.7129 }, + { 0.1968, 0.2632, 0.7255 }, + { 0.1921, 0.2698, 0.7381 }, + { 0.1867, 0.2764, 0.7507 }, + { 0.1802, 0.2832, 0.7634 }, + { 0.1728, 0.2902, 0.7762 }, + { 0.1641, 0.2975, 0.789 }, + { 0.1541, 0.3052, 0.8017 }, + { 0.1427, 0.3132, 0.8145 }, + { 0.1295, 0.3217, 0.8269 }, + { 0.1147, 0.3306, 0.8387 }, + { 0.0986, 0.3397, 0.8495 }, + { 0.0816, 0.3486, 0.8588 }, + { 0.0646, 0.3572, 0.8664 }, + { 0.0482, 0.3651, 0.8722 }, + { 0.0329, 0.3724, 0.8765 }, + { 0.0213, 0.3792, 0.8796 }, + { 0.0136, 0.3853, 0.8815 }, + { 0.0086, 0.3911, 0.8827 }, + { 0.006, 0.3965, 0.8833 }, + { 0.0051, 0.4017, 0.8834 }, + { 0.0054, 0.4066, 0.8831 }, + { 0.0067, 0.4113, 0.8825 }, + { 0.0089, 0.4159, 0.8816 }, + { 0.0116, 0.4203, 0.8805 }, + { 0.0148, 0.4246, 0.8793 }, + { 0.0184, 0.4288, 0.8779 }, + { 0.0223, 0.4329, 0.8763 }, + { 0.0264, 0.437, 0.8747 }, + { 0.0306, 0.441, 0.8729 }, + { 0.0349, 0.4449, 0.8711 }, + { 0.0394, 0.4488, 0.8692 }, + { 0.0437, 0.4526, 0.8672 }, + { 0.0477, 0.4564, 0.8652 }, + { 0.0514, 0.4602, 0.8632 }, + { 0.0549, 0.464, 0.8611 }, + { 0.0582, 0.4677, 0.8589 }, + { 0.0612, 0.4714, 0.8568 }, + { 0.064, 0.4751, 0.8546 }, + { 0.0666, 0.4788, 0.8525 }, + { 0.0689, 0.4825, 0.8503 }, + { 0.071, 0.4862, 0.8481 }, + { 0.0729, 0.4899, 0.846 }, + { 0.0746, 0.4937, 0.8439 }, + { 0.0761, 0.4974, 0.8418 }, + { 0.0773, 0.5012, 0.8398 }, + { 0.0782, 0.5051, 0.8378 }, + { 0.0789, 0.5089, 0.8359 }, + { 0.0794, 0.5129, 0.8341 }, + { 0.0795, 0.5169, 0.8324 }, + { 0.0793, 0.521, 0.8308 }, + { 0.0788, 0.5251, 0.8293 }, + { 0.0778, 0.5295, 0.828 }, + { 0.0764, 0.5339, 0.827 }, + { 0.0746, 0.5384, 0.8261 }, + { 0.0724, 0.5431, 0.8253 }, + { 0.0698, 0.5479, 0.8247 }, + { 0.0668, 0.5527, 0.8243 }, + { 0.0636, 0.5577, 0.8239 }, + { 0.06, 0.5627, 0.8237 }, + { 0.0562, 0.5677, 0.8234 }, + { 0.0523, 0.5727, 0.8231 }, + { 0.0484, 0.5777, 0.8228 }, + { 0.0445, 0.5826, 0.8223 }, + { 0.0408, 0.5874, 0.8217 }, + { 0.0372, 0.5922, 0.8209 }, + { 0.0342, 0.5968, 0.8198 }, + { 0.0317, 0.6012, 0.8186 }, + { 0.0296, 0.6055, 0.8171 }, + { 0.0279, 0.6097, 0.8154 }, + { 0.0265, 0.6137, 0.8135 }, + { 0.0255, 0.6176, 0.8114 }, + { 0.0248, 0.6214, 0.8091 }, + { 0.0243, 0.625, 0.8066 }, + { 0.0239, 0.6285, 0.8039 }, + { 0.0237, 0.6319, 0.801 }, + { 0.0235, 0.6352, 0.798 }, + { 0.0233, 0.6384, 0.7948 }, + { 0.0231, 0.6415, 0.7916 }, + { 0.023, 0.6445, 0.7881 }, + { 0.0229, 0.6474, 0.7846 }, + { 0.0227, 0.6503, 0.781, }, + { 0.0227, 0.6531, 0.7773 }, + { 0.0232, 0.6558, 0.7735 }, + { 0.0238, 0.6585, 0.7696 }, + { 0.0246, 0.6611, 0.7656 }, + { 0.0263, 0.6637, 0.7615 }, + { 0.0282, 0.6663, 0.7574 }, + { 0.0306, 0.6688, 0.7532 }, + { 0.0338, 0.6712, 0.749 }, + { 0.0373, 0.6737, 0.7446 }, + { 0.0418, 0.6761, 0.7402 }, + { 0.0467, 0.6784, 0.7358 }, + { 0.0516, 0.6808, 0.7313 }, + { 0.0574, 0.6831, 0.7267 }, + { 0.0629, 0.6854, 0.7221 }, + { 0.0692, 0.6877, 0.7173 }, + { 0.0755, 0.6899, 0.7126 }, + { 0.082, 0.6921, 0.7078 }, + { 0.0889, 0.6943, 0.7029 }, + { 0.0956, 0.6965, 0.6979 }, + { 0.1031, 0.6986, 0.6929 }, + { 0.1104, 0.7007, 0.6878 }, + { 0.118, 0.7028, 0.6827 }, + { 0.1258, 0.7049, 0.6775 }, + { 0.1335, 0.7069, 0.6723 }, + { 0.1418, 0.7089, 0.6669 }, + { 0.1499, 0.7109, 0.6616 }, + { 0.1585, 0.7129, 0.6561 }, + { 0.1671, 0.7148, 0.6507 }, + { 0.1758, 0.7168, 0.6451 }, + { 0.1849, 0.7186, 0.6395 }, + { 0.1938, 0.7205, 0.6338 }, + { 0.2033, 0.7223, 0.6281 }, + { 0.2128, 0.7241, 0.6223 }, + { 0.2224, 0.7259, 0.6165 }, + { 0.2324, 0.7275, 0.6107 }, + { 0.2423, 0.7292, 0.6048 }, + { 0.2527, 0.7308, 0.5988 }, + { 0.2631, 0.7324, 0.5929 }, + { 0.2735, 0.7339, 0.5869 }, + { 0.2845, 0.7354, 0.5809 }, + { 0.2953, 0.7368, 0.5749 }, + { 0.3064, 0.7381, 0.5689 }, + { 0.3177, 0.7394, 0.563 }, + { 0.3289, 0.7406, 0.557 }, + { 0.3405, 0.7417, 0.5512 }, + { 0.352, 0.7428, 0.5453 }, + { 0.3635, 0.7438, 0.5396 }, + { 0.3753, 0.7446, 0.5339 }, + { 0.3869, 0.7454, 0.5283 }, + { 0.3986, 0.7461, 0.5229 }, + { 0.4103, 0.7467, 0.5175 }, + { 0.4218, 0.7473, 0.5123 }, + { 0.4334, 0.7477, 0.5072 }, + { 0.4447, 0.7482, 0.5021 }, + { 0.4561, 0.7485, 0.4972 }, + { 0.4672, 0.7487, 0.4924 }, + { 0.4783, 0.7489, 0.4877 }, + { 0.4892, 0.7491, 0.4831 }, + { 0.5, 0.7491, 0.4786 }, + { 0.5106, 0.7492, 0.4741 }, + { 0.5212, 0.7492, 0.4698 }, + { 0.5315, 0.7491, 0.4655 }, + { 0.5418, 0.749, 0.4613 }, + { 0.5519, 0.7489, 0.4571 }, + { 0.5619, 0.7487, 0.4531 }, + { 0.5718, 0.7485, 0.449 }, + { 0.5816, 0.7482, 0.4451 }, + { 0.5913, 0.7479, 0.4412 }, + { 0.6009, 0.7476, 0.4374 }, + { 0.6103, 0.7473, 0.4335 }, + { 0.6197, 0.7469, 0.4298 }, + { 0.629, 0.7465, 0.4261 }, + { 0.6382, 0.746, 0.4224 }, + { 0.6473, 0.7456, 0.4188 }, + { 0.6564, 0.7451, 0.4152 }, + { 0.6653, 0.7446, 0.4116 }, + { 0.6742, 0.7441, 0.4081 }, + { 0.683, 0.7435, 0.4046 }, + { 0.6918, 0.743, 0.4011 }, + { 0.7004, 0.7424, 0.3976 }, + { 0.7091, 0.7418, 0.3942 }, + { 0.7176, 0.7412, 0.3908 }, + { 0.7261, 0.7405, 0.3874 }, + { 0.7346, 0.7399, 0.384 }, + { 0.743, 0.7392, 0.3806 }, + { 0.7513, 0.7385, 0.3773 }, + { 0.7596, 0.7378, 0.3739 }, + { 0.7679, 0.7372, 0.3706 }, + { 0.7761, 0.7364, 0.3673 }, + { 0.7843, 0.7357, 0.3639 }, + { 0.7924, 0.735, 0.3606 }, + { 0.8005, 0.7343, 0.3573 }, + { 0.8085, 0.7336, 0.3539 }, + { 0.8166, 0.7329, 0.3506 }, + { 0.8246, 0.7322, 0.3472 }, + { 0.8325, 0.7315, 0.3438 }, + { 0.8405, 0.7308, 0.3404 }, + { 0.8484, 0.7301, 0.337 }, + { 0.8563, 0.7294, 0.3336 }, + { 0.8642, 0.7288, 0.33 }, + { 0.872, 0.7282, 0.3265 }, + { 0.8798, 0.7276, 0.3229 }, + { 0.8877, 0.7271, 0.3193 }, + { 0.8954, 0.7266, 0.3156 }, + { 0.9032, 0.7262, 0.3117 }, + { 0.911, 0.7259, 0.3078 }, + { 0.9187, 0.7256, 0.3038 }, + { 0.9264, 0.7256, 0.2996 }, + { 0.9341, 0.7256, 0.2953 }, + { 0.9417, 0.7259, 0.2907 }, + { 0.9493, 0.7264, 0.2859 }, + { 0.9567, 0.7273, 0.2808 }, + { 0.9639, 0.7285, 0.2754 }, + { 0.9708, 0.7303, 0.2696 }, + { 0.9773, 0.7326, 0.2634 }, + { 0.9831, 0.7355, 0.257 }, + { 0.9882, 0.739, 0.2504 }, + { 0.9922, 0.7431, 0.2437 }, + { 0.9952, 0.7476, 0.2373 }, + { 0.9973, 0.7524, 0.231 }, + { 0.9986, 0.7573, 0.2251 }, + { 0.9991, 0.7624, 0.2195 }, + { 0.999, 0.7675, 0.2141 }, + { 0.9985, 0.7726, 0.209 }, + { 0.9976, 0.7778, 0.2042 }, + { 0.9964, 0.7829, 0.1995 }, + { 0.995, 0.788, 0.1949 }, + { 0.9933, 0.7931, 0.1905 }, + { 0.9914, 0.7981, 0.1863 }, + { 0.9894, 0.8032, 0.1821 }, + { 0.9873, 0.8083, 0.178 }, + { 0.9851, 0.8133, 0.174 }, + { 0.9828, 0.8184, 0.17 }, + { 0.9805, 0.8235, 0.1661 }, + { 0.9782, 0.8286, 0.1622 }, + { 0.9759, 0.8337, 0.1583 }, + { 0.9736, 0.8389, 0.1544 }, + { 0.9713, 0.8441, 0.1505 }, + { 0.9692, 0.8494, 0.1465 }, + { 0.9672, 0.8548, 0.1425 }, + { 0.9654, 0.8603, 0.1385 }, + { 0.9638, 0.8659, 0.1343 }, + { 0.9623, 0.8716, 0.1301 }, + { 0.9611, 0.8774, 0.1258 }, + { 0.96, 0.8834, 0.1215 }, + { 0.9593, 0.8895, 0.1171 }, + { 0.9588, 0.8958, 0.1126 }, + { 0.9586, 0.9022, 0.1082 }, + { 0.9587, 0.9088, 0.1036 }, + { 0.9591, 0.9155, 0.099 }, + { 0.9599, 0.9225, 0.0944 }, + { 0.961, 0.9296, 0.0897 }, + { 0.9624, 0.9368, 0.085 }, + { 0.9641, 0.9443, 0.0802 }, + { 0.9662, 0.9518, 0.0753 }, + { 0.9685, 0.9595, 0.0703 }, + { 0.971, 0.9673, 0.0651 }, + { 0.9736, 0.9752, 0.0597 }, + { 0.9763, 0.9831, 0.0538 } +}; +} + +template +IGL_INLINE void igl::colormap(const ColorMapType cm, const T x, T * rgb) +{ + return colormap(cm,x,rgb[0],rgb[1],rgb[2]); +} + +template +IGL_INLINE void igl::colormap( + const ColorMapType cm, const T x_in, T & r, T & g, T & b) +{ + switch (cm) + { + case COLOR_MAP_TYPE_INFERNO: + colormap(inferno_cm, x_in, r, g, b); + break; + case COLOR_MAP_TYPE_JET: + jet(x_in, r, g, b); + break; + case COLOR_MAP_TYPE_MAGMA: + colormap(magma_cm, x_in, r, g, b); + break; + case COLOR_MAP_TYPE_PARULA: + colormap(parula_cm, x_in, r, g, b); + break; + case COLOR_MAP_TYPE_PLASMA: + colormap(plasma_cm, x_in, r, g, b); + break; + case COLOR_MAP_TYPE_VIRIDIS: + colormap(viridis_cm, x_in, r, g, b); + break; + default: + throw std::invalid_argument("igl::colormap(): Selected colormap is unsupported!"); + break; + } +} + +template +IGL_INLINE void igl::colormap( + const double palette[256][3], const T x_in, T & r, T & g, T & b) +{ + static const unsigned int pal = 256; + const T zero = 0.0; + const T one = 1.0; + T x_in_clamped = static_cast(std::max(zero, std::min(one, x_in))); + + // simple rgb lerp from palette + unsigned int least = std::floor(x_in_clamped * static_cast(pal - 1)); + unsigned int most = std::ceil(x_in_clamped * static_cast(pal - 1)); + + T _r[2] = { static_cast(palette[least][0]), static_cast(palette[most][0]) }; + T _g[2] = { static_cast(palette[least][1]), static_cast(palette[most][1]) }; + T _b[2] = { static_cast(palette[least][2]), static_cast(palette[most][2]) }; + + T t = std::max(zero, std::min(one, static_cast(fmod(x_in_clamped * static_cast(pal), one)))); + + r = std::max(zero, std::min(one, (one - t) * _r[0] + t * _r[1])); + g = std::max(zero, std::min(one, (one - t) * _g[0] + t * _g[1])); + b = std::max(zero, std::min(one, (one - t) * _b[0] + t * _b[1])); +} + +template +IGL_INLINE void igl::colormap( + const ColorMapType cm, + const Eigen::MatrixBase & Z, + const bool normalize, + Eigen::PlainObjectBase & C) +{ + const double min_z = normalize ? Z.minCoeff() : 0; + const double max_z = normalize ? Z.maxCoeff() : 1; + return colormap(cm, Z, min_z, max_z, C); +} + +template +IGL_INLINE void igl::colormap( + const ColorMapType cm, + const Eigen::MatrixBase & Z, + const double min_z, + const double max_z, + Eigen::PlainObjectBase & C) +{ + C.resize(Z.rows(),3); + double denom = (max_z - min_z); + denom = (denom == 0) ? 1 : denom; + for(int r = 0; r < Z.rows(); ++r) { + colormap( + cm, + (typename DerivedC::Scalar)((-min_z + Z(r,0)) / denom), + C(r,0), + C(r,1), + C(r,2)); + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::colormap(igl::ColorMapType, float, float*); +// generated by autoexplicit.sh +template void igl::colormap, Eigen::Matrix >(igl::ColorMapType, Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::colormap, Eigen::Matrix >(igl::ColorMapType, Eigen::MatrixBase > const&, double, double, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::colormap, Eigen::Matrix >(igl::ColorMapType, Eigen::MatrixBase > const&, double, double, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::colormap, Eigen::Matrix >(igl::ColorMapType, Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::colormap(igl::ColorMapType, double, double&, double&, double&); +// generated by autoexplicit.sh +template void igl::colormap(igl::ColorMapType, double, double*); +// generated by autoexplicit.sh +template void igl::colormap, Eigen::Matrix >(igl::ColorMapType, Eigen::MatrixBase > const&, double, double, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::colormap, Eigen::Matrix >(igl::ColorMapType, Eigen::MatrixBase > const&, double, double, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::colormap, Eigen::Matrix >(igl::ColorMapType, Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::colormap, Eigen::Matrix >(igl::ColorMapType, Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::colormap, Eigen::Matrix >(igl::ColorMapType, Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); + +template void igl::colormap, Eigen::Matrix >(igl::ColorMapType, Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/colormap.h b/src/igl/colormap.h new file mode 100644 index 0000000000..1d93d73b18 --- /dev/null +++ b/src/igl/colormap.h @@ -0,0 +1,76 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Joe Graus , Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COLORMAP_H +#define IGL_COLORMAP_H +#include "igl_inline.h" + +#include + +namespace igl { + + enum ColorMapType + { + COLOR_MAP_TYPE_INFERNO = 0, + COLOR_MAP_TYPE_JET = 1, + COLOR_MAP_TYPE_MAGMA = 2, + COLOR_MAP_TYPE_PARULA = 3, + COLOR_MAP_TYPE_PLASMA = 4, + COLOR_MAP_TYPE_VIRIDIS = 5, + NUM_COLOR_MAP_TYPES = 6 + }; + // Comput [r,g,b] values of the selected colormap for + // a given factor f between 0 and 1 + // + // Inputs: + // c colormap enum + // f factor determining color value as if 0 was min and 1 was max + // Outputs: + // rgb red, green, blue value + template + IGL_INLINE void colormap(const ColorMapType cm, const T f, T * rgb); + // Outputs: + // r red value + // g green value + // b blue value + template + IGL_INLINE void colormap(const ColorMapType cm, const T f, T & r, T & g, T & b); + // Inputs: + // palette 256 by 3 array of color values + template + IGL_INLINE void colormap( + const double palette[256][3], const T x_in, T & r, T & g, T & b); + // Inputs: + // cm selected colormap palette to interpolate from + // Z #Z list of factors + // normalize whether to normalize Z to be tightly between [0,1] + // Outputs: + // C #C by 3 list of rgb colors + template + IGL_INLINE void colormap( + const ColorMapType cm, + const Eigen::MatrixBase & Z, + const bool normalize, + Eigen::PlainObjectBase & C); + // Inputs: + // min_z value at "0" + // max_z value at "1" + template + IGL_INLINE void colormap( + const ColorMapType cm, + const Eigen::MatrixBase & Z, + const double min_Z, + const double max_Z, + Eigen::PlainObjectBase & C); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "colormap.cpp" +#endif + +#endif diff --git a/src/igl/column_to_quats.cpp b/src/igl/column_to_quats.cpp new file mode 100644 index 0000000000..f87adab06b --- /dev/null +++ b/src/igl/column_to_quats.cpp @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "column_to_quats.h" +IGL_INLINE bool igl::column_to_quats( + const Eigen::VectorXd & Q, + std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & vQ) +{ + using namespace Eigen; + if(Q.size() % 4 != 0) + { + return false; + } + const int nQ = Q.size()/4; + vQ.resize(nQ); + for(int q=0;q +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COLUMN_TO_QUATS_H +#define IGL_COLUMN_TO_QUATS_H +#include "igl_inline.h" +#include +#include +#include +#include +namespace igl +{ + // "Columnize" a list of quaternions (q1x,q1y,q1z,q1w,q2x,q2y,q2z,q2w,...) + // + // Inputs: + // Q n*4-long list of coefficients + // Outputs: + // vQ n-long list of quaternions + // Returns false if n%4!=0 + IGL_INLINE bool column_to_quats( + const Eigen::VectorXd & Q, + std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & vQ); +} + +#ifndef IGL_STATIC_LIBRARY +# include "column_to_quats.cpp" +#endif + +#endif diff --git a/src/igl/columnize.cpp b/src/igl/columnize.cpp new file mode 100644 index 0000000000..29e91c6694 --- /dev/null +++ b/src/igl/columnize.cpp @@ -0,0 +1,63 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "columnize.h" +#include + +template +IGL_INLINE void igl::columnize( + const Eigen::PlainObjectBase & A, + const int k, + const int dim, + Eigen::PlainObjectBase & B) +{ + // Eigen matrices must be 2d so dim must be only 1 or 2 + assert(dim == 1 || dim == 2); + + // block height, width, and number of blocks + int m,n; + if(dim == 1) + { + m = A.rows()/k; + assert(m*(int)k == (int)A.rows()); + n = A.cols(); + }else// dim == 2 + { + m = A.rows(); + n = A.cols()/k; + assert(n*(int)k == (int)A.cols()); + } + + // resize output + B.resize(A.rows()*A.cols(),1); + + for(int b = 0;b<(int)k;b++) + { + for(int i = 0;i, Eigen::Matrix >(Eigen::PlainObjectBase > const&, int, int, Eigen::PlainObjectBase >&); +template void igl::columnize, Eigen::Matrix >(Eigen::PlainObjectBase > const&, int, int, Eigen::PlainObjectBase >&); +template void igl::columnize, Eigen::Matrix >(Eigen::PlainObjectBase > const&, int, int, Eigen::PlainObjectBase >&); +template void igl::columnize, Eigen::Matrix >(Eigen::PlainObjectBase > const&, int, int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/columnize.h b/src/igl/columnize.h new file mode 100644 index 0000000000..1bb453af19 --- /dev/null +++ b/src/igl/columnize.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COLUMNIZE_H +#define IGL_COLUMNIZE_H +#include "igl_inline.h" + +#include +namespace igl +{ + // "Columnize" a stack of block matrices. If A = [A1,A2,A3,...,Ak] with each A* + // an m by n block then this produces the column vector whose entries are + // B(j*m*k+i*k+b) = A(i,b*n+j); + // or if A = [A1;A2;...;Ak] then + // B(j*m*k+i*k+b) = A(i+b*m,j); + // + // Templates: + // T should be a eigen matrix primitive type like int or double + // Inputs: + // A m*k by n (dim: 1) or m by n*k (dim: 2) eigen Matrix of type T values + // k number of blocks + // dim dimension in which blocks are stacked + // Output + // B m*n*k eigen vector of type T values, + // + // See also: transpose_blocks + template + IGL_INLINE void columnize( + const Eigen::PlainObjectBase & A, + const int k, + const int dim, + Eigen::PlainObjectBase & B); +} +#ifndef IGL_STATIC_LIBRARY +# include "columnize.cpp" +#endif +#endif diff --git a/src/igl/comb_cross_field.cpp b/src/igl/comb_cross_field.cpp new file mode 100644 index 0000000000..c277b056d0 --- /dev/null +++ b/src/igl/comb_cross_field.cpp @@ -0,0 +1,148 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "comb_cross_field.h" + +#include +#include +#include +#include "per_face_normals.h" +#include "is_border_vertex.h" +#include "rotation_matrix_from_directions.h" + +#include "triangle_triangle_adjacency.h" + +namespace igl { + template + class Comb + { + public: + + const Eigen::PlainObjectBase &V; + const Eigen::PlainObjectBase &F; + const Eigen::PlainObjectBase &PD1; + const Eigen::PlainObjectBase &PD2; + DerivedV N; + + private: + // internal + DerivedF TT; + DerivedF TTi; + + + private: + + + static inline double Sign(double a){return (double)((a>0)?+1:-1);} + + + private: + + // returns the 90 deg rotation of a (around n) most similar to target b + /// a and b should be in the same plane orthogonal to N + static inline Eigen::Matrix K_PI_new(const Eigen::Matrix& a, + const Eigen::Matrix& b, + const Eigen::Matrix& n) + { + Eigen::Matrix c = (a.cross(n)).normalized(); + typename DerivedV::Scalar scorea = a.dot(b); + typename DerivedV::Scalar scorec = c.dot(b); + if (fabs(scorea)>=fabs(scorec)) + return a*Sign(scorea); + else + return c*Sign(scorec); + } + + + + public: + inline Comb(const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const Eigen::PlainObjectBase &_PD1, + const Eigen::PlainObjectBase &_PD2 + ): + V(_V), + F(_F), + PD1(_PD1), + PD2(_PD2) + { + igl::per_face_normals(V,F,N); + igl::triangle_triangle_adjacency(F,TT,TTi); + } + inline void comb(Eigen::PlainObjectBase &PD1out, + Eigen::PlainObjectBase &PD2out) + { +// PD1out = PD1; +// PD2out = PD2; + PD1out.setZero(F.rows(),3);PD1out< d; + + d.push_back(0); + mark(0) = true; + + while (!d.empty()) + { + int f0 = d.at(0); + d.pop_front(); + for (int k=0; k<3; k++) + { + int f1 = TT(f0,k); + if (f1==-1) continue; + if (mark(f1)) continue; + + Eigen::Matrix dir0 = PD1out.row(f0); + Eigen::Matrix dir1 = PD1out.row(f1); + Eigen::Matrix n0 = N.row(f0); + Eigen::Matrix n1 = N.row(f1); + + + Eigen::Matrix dir0Rot = igl::rotation_matrix_from_directions(n0, n1)*dir0; + dir0Rot.normalize(); + Eigen::Matrix targD = K_PI_new(dir1,dir0Rot,n1); + + PD1out.row(f1) = targD; + PD2out.row(f1) = n1.cross(targD).normalized(); + + mark(f1) = true; + d.push_back(f1); + + } + } + + // everything should be marked + for (int i=0; i +IGL_INLINE void igl::comb_cross_field(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const Eigen::PlainObjectBase &PD2, + Eigen::PlainObjectBase &PD1out, + Eigen::PlainObjectBase &PD2out) +{ + igl::Comb cmb(V, F, PD1, PD2); + cmb.comb(PD1out, PD2out); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::comb_cross_field, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::comb_cross_field, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/comb_cross_field.h b/src/igl/comb_cross_field.h new file mode 100644 index 0000000000..fa3b576485 --- /dev/null +++ b/src/igl/comb_cross_field.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COMB_CROSS_FIELD_H +#define IGL_COMB_CROSS_FIELD_H +#include "igl_inline.h" +#include +namespace igl +{ + // Computes principal matchings of the vectors of a cross field across face edges, + // and generates a combed cross field defined on the mesh faces + + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 4 eigen Matrix of face (quad) indices + // PD1in #F by 3 eigen Matrix of the first per face cross field vector + // PD2in #F by 3 eigen Matrix of the second per face cross field vector + // Output: + // PD1out #F by 3 eigen Matrix of the first combed cross field vector + // PD2out #F by 3 eigen Matrix of the second combed cross field vector + // + + + template + IGL_INLINE void comb_cross_field(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1in, + const Eigen::PlainObjectBase &PD2in, + Eigen::PlainObjectBase &PD1out, + Eigen::PlainObjectBase &PD2out); +} +#ifndef IGL_STATIC_LIBRARY +#include "comb_cross_field.cpp" +#endif + +#endif diff --git a/src/igl/comb_frame_field.cpp b/src/igl/comb_frame_field.cpp new file mode 100644 index 0000000000..f8afe56e97 --- /dev/null +++ b/src/igl/comb_frame_field.cpp @@ -0,0 +1,82 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifdef WIN32 + #define _USE_MATH_DEFINES +#endif +#include + +#include "comb_frame_field.h" +#include "local_basis.h" +#include "PI.h" + +template +IGL_INLINE void igl::comb_frame_field(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const Eigen::PlainObjectBase &PD2, + const Eigen::PlainObjectBase &BIS1_combed, + const Eigen::PlainObjectBase &BIS2_combed, + Eigen::PlainObjectBase &PD1_combed, + Eigen::PlainObjectBase &PD2_combed) +{ + DerivedV B1, B2, B3; + igl::local_basis(V,F,B1,B2,B3); + + PD1_combed.resize(BIS1_combed.rows(),3); + PD2_combed.resize(BIS2_combed.rows(),3); + + for (unsigned i=0; i DIRs; + DIRs << + PD1.row(i), + -PD1.row(i), + PD2.row(i), + -PD2.row(i); + + std::vector a(4); + + + double a_combed = atan2(B2.row(i).dot(BIS1_combed.row(i)),B1.row(i).dot(BIS1_combed.row(i))); + + // center on the combed sector center + for (unsigned j=0;j<4;++j) + { + a[j] = atan2(B2.row(i).dot(DIRs.row(j)),B1.row(i).dot(DIRs.row(j))) - a_combed; + //make it positive by adding some multiple of 2pi + a[j] += std::ceil (std::max(0., -a[j]) / (igl::PI*2.)) * (igl::PI*2.); + //take modulo 2pi + a[j] = fmod(a[j], (igl::PI*2.)); + } + // now the max is u and the min is v + + int m = std::min_element(a.begin(),a.end())-a.begin(); + int M = std::max_element(a.begin(),a.end())-a.begin(); + + assert( + ((m>=0 && m<=1) && (M>=2 && M<=3)) + || + ((m>=2 && m<=3) && (M>=0 && M<=1)) + ); + + PD1_combed.row(i) = DIRs.row(m); + PD2_combed.row(i) = DIRs.row(M); + + } + + + // PD1_combed = BIS1_combed; + // PD2_combed = BIS2_combed; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::comb_frame_field, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::comb_frame_field, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/comb_frame_field.h b/src/igl/comb_frame_field.h new file mode 100644 index 0000000000..4691dc19d8 --- /dev/null +++ b/src/igl/comb_frame_field.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COMB_FRAME_FIELD_H +#define IGL_COMB_FRAME_FIELD_H +#include "igl_inline.h" +#include +namespace igl +{ + // Computes principal matchings of the vectors of a frame field across face edges, + // and generates a combed frame field defined on the mesh faces. This makes use of a + // combed cross field generated by combing the field created by the bisectors of the + // frame field. + + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 4 eigen Matrix of face (quad) indices + // PD1 #F by 3 eigen Matrix of the first per face cross field vector + // PD2 #F by 3 eigen Matrix of the second per face cross field vector + // BIS1_combed #F by 3 eigen Matrix of the first combed bisector field vector + // BIS2_combed #F by 3 eigen Matrix of the second combed bisector field vector + // Output: + // PD1_combed #F by 3 eigen Matrix of the first combed cross field vector + // PD2_combed #F by 3 eigen Matrix of the second combed cross field vector + // + + + template + IGL_INLINE void comb_frame_field(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const Eigen::PlainObjectBase &PD2, + const Eigen::PlainObjectBase &BIS1_combed, + const Eigen::PlainObjectBase &BIS2_combed, + Eigen::PlainObjectBase &PD1_combed, + Eigen::PlainObjectBase &PD2_combed); +} +#ifndef IGL_STATIC_LIBRARY +#include "comb_frame_field.cpp" +#endif + +#endif diff --git a/src/igl/comb_line_field.cpp b/src/igl/comb_line_field.cpp new file mode 100644 index 0000000000..66899319a5 --- /dev/null +++ b/src/igl/comb_line_field.cpp @@ -0,0 +1,132 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Nico Pietroni +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "comb_line_field.h" + +#include +#include +#include "per_face_normals.h" +#include "is_border_vertex.h" +#include "rotation_matrix_from_directions.h" + +#include "triangle_triangle_adjacency.h" + +namespace igl { +template +class CombLine +{ +public: + + const Eigen::PlainObjectBase &V; + const Eigen::PlainObjectBase &F; + const Eigen::PlainObjectBase &PD1; + DerivedV N; + +private: + // internal + DerivedF TT; + DerivedF TTi; + + +private: + + + static inline double Sign(double a){return (double)((a>0)?+1:-1);} + + +private: + + // returns the 180 deg rotation of a (around n) most similar to target b + // a and b should be in the same plane orthogonal to N + static inline Eigen::Matrix K_PI_line(const Eigen::Matrix& a, + const Eigen::Matrix& b) + { + typename DerivedV::Scalar scorea = a.dot(b); + if (scorea<0) + return -a; + else + return a; + } + + + +public: + + inline CombLine(const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const Eigen::PlainObjectBase &_PD1): + V(_V), + F(_F), + PD1(_PD1) + { + igl::per_face_normals(V,F,N); + igl::triangle_triangle_adjacency(F,TT,TTi); + } + + inline void comb(Eigen::PlainObjectBase &PD1out) + { + PD1out.setZero(F.rows(),3);PD1out< d; + + d.push_back(0); + mark(0) = true; + + while (!d.empty()) + { + int f0 = d.at(0); + d.pop_front(); + for (int k=0; k<3; k++) + { + int f1 = TT(f0,k); + if (f1==-1) continue; + if (mark(f1)) continue; + + Eigen::Matrix dir0 = PD1out.row(f0); + Eigen::Matrix dir1 = PD1out.row(f1); + Eigen::Matrix n0 = N.row(f0); + Eigen::Matrix n1 = N.row(f1); + + Eigen::Matrix dir0Rot = igl::rotation_matrix_from_directions(n0, n1)*dir0; + dir0Rot.normalize(); + Eigen::Matrix targD = K_PI_line(dir1,dir0Rot); + + PD1out.row(f1) = targD; + //PD2out.row(f1) = n1.cross(targD).normalized(); + + mark(f1) = true; + d.push_back(f1); + + } + } + + // everything should be marked + for (int i=0; i +IGL_INLINE void igl::comb_line_field(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + Eigen::PlainObjectBase &PD1out) +{ + igl::CombLine cmb(V, F, PD1); + cmb.comb(PD1out); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/comb_line_field.h b/src/igl/comb_line_field.h new file mode 100644 index 0000000000..e2f22b37f2 --- /dev/null +++ b/src/igl/comb_line_field.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Nico Pietroni +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COMB_LINE_FIELD_H +#define IGL_COMB_LINE_FIELD_H +#include "igl_inline.h" +#include +namespace igl +{ + // Computes principal matchings of the vectors of a cross field across face edges, + // and generates a combed cross field defined on the mesh faces + + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 4 eigen Matrix of face (quad) indices + // PD1in #F by 3 eigen Matrix of the first per face cross field vector + // PD2in #F by 3 eigen Matrix of the second per face cross field vector + // Output: + // PD1out #F by 3 eigen Matrix of the first combed cross field vector + // PD2out #F by 3 eigen Matrix of the second combed cross field vector + // + + + template + IGL_INLINE void comb_line_field(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1in, + Eigen::PlainObjectBase &PD1out); +} +#ifndef IGL_STATIC_LIBRARY +#include "comb_line_field.cpp" +#endif + +#endif diff --git a/src/igl/combine.cpp b/src/igl/combine.cpp new file mode 100644 index 0000000000..7e0af151a7 --- /dev/null +++ b/src/igl/combine.cpp @@ -0,0 +1,97 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "combine.h" +#include + +template < + typename DerivedVV, + typename DerivedFF, + typename DerivedV, + typename DerivedF, + typename DerivedVsizes, + typename DerivedFsizes> +IGL_INLINE void igl::combine( + const std::vector & VV, + const std::vector & FF, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & Vsizes, + Eigen::PlainObjectBase & Fsizes) +{ + assert(VV.size() == FF.size() && + "Lists of verex lists and face lists should be same size"); + Vsizes.resize(VV.size()); + Fsizes.resize(FF.size()); + // Dimension of vertex positions + const int dim = VV.size() > 0 ? VV[0].cols() : 0; + // Simplex/element size + const int ss = FF.size() > 0 ? FF[0].cols() : 0; + int n = 0; + int m = 0; + for(int i = 0;i0) + { + F.block(kf,0,mi,ss) = Fi.array()+kv; + } + kf+=mi; + if(Vi.size() >0) + { + V.block(kv,0,ni,dim) = Vi; + } + kv+=ni; + } + assert(kv == V.rows()); + assert(kf == F.rows()); + } +} + +template < + typename DerivedVV, + typename DerivedFF, + typename DerivedV, + typename DerivedF> +IGL_INLINE void igl::combine( + const std::vector & VV, + const std::vector & FF, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F) +{ + Eigen::VectorXi Vsizes,Fsizes; + return igl::combine(VV,FF,V,F,Vsizes,Fsizes); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::combine, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::vector, std::allocator > > const&, std::vector, std::allocator > > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::combine, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::vector, std::allocator > > const&, std::vector, std::allocator > > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::combine, Eigen::Matrix,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(class std::vector,class std::allocator > > const &,class std::vector,class std::allocator > > const &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &); +template void igl::combine, Eigen::Matrix,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(class std::vector,class std::allocator > > const &,class std::vector,class std::allocator > > const &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &); +#endif +#endif diff --git a/src/igl/combine.h b/src/igl/combine.h new file mode 100644 index 0000000000..ff5e1ae34c --- /dev/null +++ b/src/igl/combine.h @@ -0,0 +1,65 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COMBINE_H +#define IGL_COMBINE_H + +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Concatenate k meshes into a single >=k connected component mesh with a + // single vertex list and face list. Similar to Maya's Combine operation. + // + // Inputs: + // VV k-long list of lists of mesh vertex positions + // FF k-long list of lists of mesh face indices so that FF[i] indexes + // VV[i] + // Outputs: + // V VV[0].rows()+...+VV[k-1].rows() by VV[0].cols() list of mesh + // vertex positions + // F FF[0].rows()+...+FF[k-1].rows() by FF[0].cols() list of mesh faces + // indices into V + // Vsizes k list so that Vsizes(i) is the #vertices in the ith input + // Fsizes k list so that Fsizes(i) is the #faces in the ith input + // Example: + // // Suppose you have mesh A (VA,FA) and mesh B (VB,FB) + // igl::combine({VA,VB},{FA,FB},V,F); + // + // + template < + typename DerivedVV, + typename DerivedFF, + typename DerivedV, + typename DerivedF, + typename DerivedVsizes, + typename DerivedFsizes> + IGL_INLINE void combine( + const std::vector & VV, + const std::vector & FF, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & Vsizes, + Eigen::PlainObjectBase & Fsizes); + template < + typename DerivedVV, + typename DerivedFF, + typename DerivedV, + typename DerivedF> + IGL_INLINE void combine( + const std::vector & VV, + const std::vector & FF, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F); +} + +#ifndef IGL_STATIC_LIBRARY +# include "combine.cpp" +#endif +#endif diff --git a/src/igl/components.cpp b/src/igl/components.cpp new file mode 100644 index 0000000000..2531868899 --- /dev/null +++ b/src/igl/components.cpp @@ -0,0 +1,98 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "components.h" +#include "adjacency_matrix.h" +#include +#include + +template +IGL_INLINE void igl::components( + const Eigen::SparseMatrix & A, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & counts) +{ + using namespace Eigen; + using namespace std; + assert(A.rows() == A.cols() && "A should be square."); + const size_t n = A.rows(); + Array seen = Array::Zero(n,1); + C.resize(n,1); + typename DerivedC::Scalar id = 0; + vector vcounts; + // breadth first search + for(int k=0; k Q; + Q.push(k); + vcounts.push_back(0); + while(!Q.empty()) + { + const int f = Q.front(); + Q.pop(); + if(seen(f)) + { + continue; + } + seen(f) = true; + C(f,0) = id; + vcounts[id]++; + // Iterate over inside + for(typename SparseMatrix::InnerIterator it (A,f); it; ++it) + { + const int g = it.index(); + if(!seen(g) && it.value()) + { + Q.push(g); + } + } + } + id++; + } + assert((size_t) id == vcounts.size()); + const size_t ncc = vcounts.size(); + assert((size_t)C.maxCoeff()+1 == ncc); + counts.resize(ncc,1); + for(size_t i = 0;i +IGL_INLINE void igl::components( + const Eigen::SparseMatrix & A, + Eigen::PlainObjectBase & C) +{ + Eigen::VectorXi counts; + return components(A,C,counts); +} + +template +IGL_INLINE void igl::components( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & C) +{ + Eigen::SparseMatrix A; + adjacency_matrix(F,A); + return components(A,C); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::components >(Eigen::SparseMatrix const&, Eigen::PlainObjectBase >&); +template void igl::components, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::components >(Eigen::SparseMatrix const&, Eigen::PlainObjectBase >&); +template void igl::components >(Eigen::SparseMatrix const&, Eigen::PlainObjectBase >&); +template void igl::components, Eigen::Matrix >(Eigen::SparseMatrix const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::components, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/components.h b/src/igl/components.h new file mode 100644 index 0000000000..d43a64c897 --- /dev/null +++ b/src/igl/components.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COMPONENTS_H +#define IGL_COMPONENTS_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Compute connected components of a graph represented by an adjacency + // matrix. This version is faster than the previous version using boost. + // + // Inputs: + // A n by n adjacency matrix + // Outputs: + // C n list of component ids (starting with 0) + // counts #components list of counts for each component + // + template + IGL_INLINE void components( + const Eigen::SparseMatrix & A, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & counts); + template + IGL_INLINE void components( + const Eigen::SparseMatrix & A, + Eigen::PlainObjectBase & C); + // Ditto but for mesh faces as input. This computes connected components of + // **vertices** where **edges** establish connectivity. + // + // Inputs: + // F n by 3 list of triangle indices + // Outputs: + // C max(F) list of component ids + template + IGL_INLINE void components( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & C); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "components.cpp" +#endif + +#endif + diff --git a/src/igl/compute_frame_field_bisectors.cpp b/src/igl/compute_frame_field_bisectors.cpp new file mode 100644 index 0000000000..e3c47c608a --- /dev/null +++ b/src/igl/compute_frame_field_bisectors.cpp @@ -0,0 +1,85 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifdef WIN32 + #define _USE_MATH_DEFINES +#endif +#include + +#include "compute_frame_field_bisectors.h" +#include "igl/local_basis.h" +#include "PI.h" + +template +IGL_INLINE void igl::compute_frame_field_bisectors( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& B1, + const Eigen::PlainObjectBase& B2, + const Eigen::PlainObjectBase& PD1, + const Eigen::PlainObjectBase& PD2, + Eigen::PlainObjectBase& BIS1, + Eigen::PlainObjectBase& BIS2) +{ + BIS1.resize(PD1.rows(),3); + BIS2.resize(PD1.rows(),3); + + for (unsigned i=0; i +IGL_INLINE void igl::compute_frame_field_bisectors( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& PD1, + const Eigen::PlainObjectBase& PD2, + Eigen::PlainObjectBase& BIS1, + Eigen::PlainObjectBase& BIS2) +{ + DerivedV B1, B2, B3; + igl::local_basis(V,F,B1,B2,B3); + + compute_frame_field_bisectors( V, F, B1, B2, PD1, PD2, BIS1, BIS2); + +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::compute_frame_field_bisectors, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::compute_frame_field_bisectors, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/compute_frame_field_bisectors.h b/src/igl/compute_frame_field_bisectors.h new file mode 100644 index 0000000000..4c853febfc --- /dev/null +++ b/src/igl/compute_frame_field_bisectors.h @@ -0,0 +1,53 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COMPUTE_FRAME_FIELD_BISECTORS_H +#define IGL_COMPUTE_FRAME_FIELD_BISECTORS_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute bisectors of a frame field defined on mesh faces + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (triangle) indices + // B1 #F by 3 eigen Matrix of face (triangle) base vector 1 + // B2 #F by 3 eigen Matrix of face (triangle) base vector 2 + // PD1 #F by 3 eigen Matrix of the first per face frame field vector + // PD2 #F by 3 eigen Matrix of the second per face frame field vector + // Output: + // BIS1 #F by 3 eigen Matrix of the first per face frame field bisector + // BIS2 #F by 3 eigen Matrix of the second per face frame field bisector + // + template + IGL_INLINE void compute_frame_field_bisectors( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& B1, + const Eigen::PlainObjectBase& B2, + const Eigen::PlainObjectBase& PD1, + const Eigen::PlainObjectBase& PD2, + Eigen::PlainObjectBase& BIS1, + Eigen::PlainObjectBase& BIS2); + + // Wrapper without given basis vectors. + template + IGL_INLINE void compute_frame_field_bisectors( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& PD1, + const Eigen::PlainObjectBase& PD2, + Eigen::PlainObjectBase& BIS1, + Eigen::PlainObjectBase& BIS2); +} + +#ifndef IGL_STATIC_LIBRARY +# include "compute_frame_field_bisectors.cpp" +#endif + +#endif diff --git a/src/igl/connect_boundary_to_infinity.cpp b/src/igl/connect_boundary_to_infinity.cpp new file mode 100644 index 0000000000..627b0b9b63 --- /dev/null +++ b/src/igl/connect_boundary_to_infinity.cpp @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "connect_boundary_to_infinity.h" +#include "boundary_facets.h" + +template +IGL_INLINE void igl::connect_boundary_to_infinity( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & FO) +{ + return connect_boundary_to_infinity(F,F.maxCoeff(),FO); +} +template +IGL_INLINE void igl::connect_boundary_to_infinity( + const Eigen::PlainObjectBase & F, + const typename DerivedF::Scalar inf_index, + Eigen::PlainObjectBase & FO) +{ + // Determine boundary edges + Eigen::Matrix O; + boundary_facets(F,O); + FO.resize(F.rows()+O.rows(),F.cols()); + typedef Eigen::Matrix VectorXI; + FO.topLeftCorner(F.rows(),F.cols()) = F; + FO.bottomLeftCorner(O.rows(),O.cols()) = O.rowwise().reverse(); + FO.bottomRightCorner(O.rows(),1).setConstant(inf_index); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedVO, + typename DerivedFO> +IGL_INLINE void igl::connect_boundary_to_infinity( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & VO, + Eigen::PlainObjectBase & FO) +{ + typename DerivedV::Index inf_index = V.rows(); + connect_boundary_to_infinity(F,inf_index,FO); + VO.resize(V.rows()+1,V.cols()); + VO.topLeftCorner(V.rows(),V.cols()) = V; + auto inf = std::numeric_limits::infinity(); + VO.row(V.rows()).setConstant(inf); +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::connect_boundary_to_infinity, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/connect_boundary_to_infinity.h b/src/igl/connect_boundary_to_infinity.h new file mode 100644 index 0000000000..2fa2783d65 --- /dev/null +++ b/src/igl/connect_boundary_to_infinity.h @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CONNECT_BOUNDARY_TO_INFINITY_H +#define IGL_CONNECT_BOUNDARY_TO_INFINITY_H +#include "igl_inline.h" +#include +namespace igl +{ + // Connect all boundary edges to a fictitious point at infinity. + // + // Inputs: + // F #F by 3 list of face indices into some V + // Outputs: + // FO #F+#O by 3 list of face indices into [V;inf inf inf], original F are + // guaranteed to come first. If (V,F) was a manifold mesh, now it is + // closed with a possibly non-manifold vertex at infinity (but it will be + // edge-manifold). + template + IGL_INLINE void connect_boundary_to_infinity( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & FO); + // Inputs: + // inf_index index of point at infinity (usually V.rows() or F.maxCoeff()) + template + IGL_INLINE void connect_boundary_to_infinity( + const Eigen::PlainObjectBase & F, + const typename DerivedF::Scalar inf_index, + Eigen::PlainObjectBase & FO); + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of face indices into some V + // Outputs: + // VO #V+1 by 3 list of vertex positions, original V are guaranteed to + // come first. Last point is inf, inf, inf + // FO #F+#O by 3 list of face indices into VO + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedVO, + typename DerivedFO> + IGL_INLINE void connect_boundary_to_infinity( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & VO, + Eigen::PlainObjectBase & FO); +} +#ifndef IGL_STATIC_LIBRARY +# include "connect_boundary_to_infinity.cpp" +#endif +#endif diff --git a/src/igl/copyleft/README.md b/src/igl/copyleft/README.md new file mode 100644 index 0000000000..78454affc6 --- /dev/null +++ b/src/igl/copyleft/README.md @@ -0,0 +1,14 @@ +## IGL copyleft subdirectory + +Functions in the `include/igl/copyleft/` subdirectory are in the +`igl::copyleft::` namespace to indicate that they are under a more aggressive +[copyleft](https://en.wikipedia.org/wiki/Copyleft) than +[MPL2](https://en.wikipedia.org/wiki/Mozilla_Public_License) used for the main +`include/igl` directory and `igl::` namespace. Most notably, this subdirectory +includes code that is under +[GPL](https://en.wikipedia.org/wiki/GNU_General_Public_License). + +Typically a company planning on developing software without releasing its +source code will avoid or purchase licenses for such dependencies. If you do +obtain such a license for the dependencies employed here, you are free to use +the libigl functions here as per their MPL2 license. diff --git a/src/igl/copyleft/cgal/BinaryWindingNumberOperations.h b/src/igl/copyleft/cgal/BinaryWindingNumberOperations.h new file mode 100644 index 0000000000..9bae0b7bb9 --- /dev/null +++ b/src/igl/copyleft/cgal/BinaryWindingNumberOperations.h @@ -0,0 +1,167 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_COPYLEFT_CGAL_BINARY_WINDING_NUMBER_OPERATIONS_H +#define IGL_COPYLEFT_CGAL_BINARY_WINDING_NUMBER_OPERATIONS_H + +#include +#include "../../igl_inline.h" +#include "../../MeshBooleanType.h" +#include + +// TODO: This is not written according to libigl style. These should be +// function handles. +// +// Why is this templated on DerivedW +// +// These are all generalized to n-ary operations +namespace igl +{ + namespace copyleft + { + namespace cgal + { + template + class BinaryWindingNumberOperations { + public: + template + typename DerivedW::Scalar operator()( + const Eigen::PlainObjectBase& /*win_nums*/) const { + throw (std::runtime_error("not implemented!")); + } + }; + + // A ∪ B ∪ ... ∪ Z + template <> + class BinaryWindingNumberOperations { + public: + template + typename DerivedW::Scalar operator()( + const Eigen::PlainObjectBase& win_nums) const + { + for(int i = 0;i 0) return true; + } + return false; + } + }; + + // A ∩ B ∩ ... ∩ Z + template <> + class BinaryWindingNumberOperations { + public: + template + typename DerivedW::Scalar operator()( + const Eigen::PlainObjectBase& win_nums) const + { + for(int i = 0;i + class BinaryWindingNumberOperations { + public: + template + typename DerivedW::Scalar operator()( + const Eigen::PlainObjectBase& win_nums) const + { + assert(win_nums.size()>1); + // Union of objects 1 through n-1 + bool union_rest = false; + for(int i = 1;i 0; + if(union_rest) break; + } + // Must be in object 0 and not in union of objects 1 through n-1 + return win_nums(0) > 0 && !union_rest; + } + }; + + // A ∆ B ∆ ... ∆ Z (equivalent to set inside odd number of objects) + template <> + class BinaryWindingNumberOperations { + public: + template + typename DerivedW::Scalar operator()( + const Eigen::PlainObjectBase& win_nums) const + { + // If inside an odd number of objects + int count = 0; + for(int i = 0;i 0) count++; + } + return count % 2 == 1; + } + }; + + template <> + class BinaryWindingNumberOperations { + public: + template + typename DerivedW::Scalar operator()( + const Eigen::PlainObjectBase& /*win_nums*/) const { + return true; + } + }; + + typedef BinaryWindingNumberOperations BinaryUnion; + typedef BinaryWindingNumberOperations BinaryIntersect; + typedef BinaryWindingNumberOperations BinaryMinus; + typedef BinaryWindingNumberOperations BinaryXor; + typedef BinaryWindingNumberOperations BinaryResolve; + + enum KeeperType { + KEEP_INSIDE, + KEEP_ALL + }; + + template + class WindingNumberFilter { + public: + template + short operator()( + const Eigen::PlainObjectBase& /*win_nums*/) const { + throw std::runtime_error("Not implemented"); + } + }; + + template<> + class WindingNumberFilter { + public: + template + short operator()(T out_w, T in_w) const { + if (in_w > 0 && out_w <= 0) return 1; + else if (in_w <= 0 && out_w > 0) return -1; + else return 0; + } + }; + + template<> + class WindingNumberFilter { + public: + template + short operator()(T /*out_w*/, T /*in_w*/) const { + return 1; + } + }; + + typedef WindingNumberFilter KeepInside; + typedef WindingNumberFilter KeepAll; + } + } +} + +#endif diff --git a/src/igl/copyleft/cgal/CGAL_includes.hpp b/src/igl/copyleft/cgal/CGAL_includes.hpp new file mode 100644 index 0000000000..38d2fc7497 --- /dev/null +++ b/src/igl/copyleft/cgal/CGAL_includes.hpp @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CGAL_INCLUDES_H +#define IGL_CGAL_INCLUDES_H + +// This causes unknown bugs during intersection meshing: +//// http://www.alecjacobson.com/weblog/?p=4291 +//#define CGAL_INTERSECTION_VERSION 1 +// Use this instead to mute errors resulting from bad CGAL assertions +#define CGAL_KERNEL_NO_ASSERTIONS +// Triangle triangle intersection +#include +// THIS CANNOT BE INCLUDED IN THE SAME FILE AS +// #include + +// Constrained Delaunay Triangulation types +#include +#include + +// Axis-align boxes for all-pairs self-intersection detection +#include +#include +#include +#include +#include +#include +#include + +// Axis-aligned bounding box tree for tet tri intersection +#include +#include +#include + +// Boolean operations +#include +// Is this actually used? +//#include + +// Delaunay Triangulation in 3D +#include + +#include +#include + +#endif diff --git a/src/igl/copyleft/cgal/CSGTree.h b/src/igl/copyleft/cgal/CSGTree.h new file mode 100644 index 0000000000..599572d3f6 --- /dev/null +++ b/src/igl/copyleft/cgal/CSGTree.h @@ -0,0 +1,189 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_CSG_TREE_H +#define IGL_COPYLEFT_CGAL_CSG_TREE_H + +#include "../../MeshBooleanType.h" +#include "string_to_mesh_boolean_type.h" +#include "mesh_boolean.h" +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Class for defining and computing a constructive solid geometry result + // out of a tree of boolean operations on "solid" triangle meshes. + // + //template + class CSGTree + { + public: + typedef CGAL::Epeck::FT ExactScalar; + //typedef Eigen::PlainObjectBase POBF; + typedef Eigen::MatrixXi POBF; + typedef POBF::Index FIndex; + typedef Eigen::Matrix MatrixX3E; + typedef Eigen::Matrix VectorJ; + private: + // Resulting mesh + MatrixX3E m_V; + POBF m_F; + VectorJ m_J; + // Number of birth faces in A + those in B. I.e. sum of original "leaf" + // faces involved in result. + size_t m_number_of_birth_faces; + public: + CSGTree() + { + } + //typedef Eigen::MatrixXd MatrixX3E; + //typedef Eigen::MatrixXi POBF; + // http://stackoverflow.com/a/3279550/148668 + CSGTree(const CSGTree & other) + : + // copy things + m_V(other.m_V), + // This is an issue if m_F is templated + // https://forum.kde.org/viewtopic.php?f=74&t=128414 + m_F(other.m_F), + m_J(other.m_J), + m_number_of_birth_faces(other.m_number_of_birth_faces) + { + } + // copy-swap idiom + friend void swap(CSGTree& first, CSGTree& second) + { + using std::swap; + // swap things + swap(first.m_V,second.m_V); + // This is an issue if m_F is templated, similar to + // https://forum.kde.org/viewtopic.php?f=74&t=128414 + swap(first.m_F,second.m_F); + swap(first.m_J,second.m_J); + swap(first.m_number_of_birth_faces,second.m_number_of_birth_faces); + } + // Pass-by-value (aka copy) + CSGTree& operator=(CSGTree other) + { + swap(*this,other); + return *this; + } + CSGTree(CSGTree&& other): + // initialize via default constructor + CSGTree() + { + swap(*this,other); + } + // Construct and compute a boolean operation on existing CSGTree nodes. + // + // Inputs: + // A Solid result of previous CSG operation (or identity, see below) + // B Solid result of previous CSG operation (or identity, see below) + // type type of mesh boolean to compute + CSGTree( + const CSGTree & A, + const CSGTree & B, + const MeshBooleanType & type) + { + // conduct boolean operation + mesh_boolean(A.V(),A.F(),B.V(),B.F(),type,m_V,m_F,m_J); + // reindex m_J + std::for_each(m_J.data(),m_J.data()+m_J.size(), + [&](typename VectorJ::Scalar & j) -> void + { + if(j < A.F().rows()) + { + j = A.J()(j); + }else + { + assert(j<(A.F().rows()+B.F().rows())); + j = A.number_of_birth_faces()+(B.J()(j-A.F().rows())); + } + }); + m_number_of_birth_faces = + A.number_of_birth_faces() + B.number_of_birth_faces(); + } + // Overload using string for type + CSGTree( + const CSGTree & A, + const CSGTree & B, + const std::string & s): + CSGTree(A,B,string_to_mesh_boolean_type(s)) + { + // do nothing (all done in constructor). + } + // "Leaf" node with identity operation on assumed "solid" mesh (V,F) + // + // Inputs: + // V #V by 3 list of mesh vertices (in any precision, will be + // converted to exact) + // F #F by 3 list of mesh face indices into V + template + CSGTree(const Eigen::PlainObjectBase & V, const POBF & F)//: + // Possible Eigen bug: + // https://forum.kde.org/viewtopic.php?f=74&t=128414 + //m_V(V.template cast()),m_F(F) + { + m_V = V.template cast(); + m_F = F; + // number of faces + m_number_of_birth_faces = m_F.rows(); + // identity birth index + m_J = VectorJ::LinSpaced( + m_number_of_birth_faces,0,m_number_of_birth_faces-1); + } + // Returns reference to resulting mesh vertices m_V in exact scalar + // representation + const MatrixX3E & V() const + { + return m_V; + } + // Returns mesh vertices in the desired output type, casting when + // appropriate to floating precision. + template + DerivedV cast_V() const + { + DerivedV dV; + dV.resize(m_V.rows(),m_V.cols()); + for(int i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_REMESH_SELF_INTERSECTIONS_PARAM_H +#define IGL_COPYLEFT_CGAL_REMESH_SELF_INTERSECTIONS_PARAM_H + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Optional Parameters + // DetectOnly Only compute IF, leave VV and FF alone + struct RemeshSelfIntersectionsParam + { + bool detect_only; + bool first_only; + bool stitch_all; + inline RemeshSelfIntersectionsParam( + bool _detect_only=false, + bool _first_only=false, + bool _stitch_all=false): + detect_only(_detect_only), + first_only(_first_only), + stitch_all(_stitch_all){}; + }; + } + } +} + +#endif diff --git a/src/igl/copyleft/cgal/SelfIntersectMesh.h b/src/igl/copyleft/cgal/SelfIntersectMesh.h new file mode 100644 index 0000000000..5a70fc39e7 --- /dev/null +++ b/src/igl/copyleft/cgal/SelfIntersectMesh.h @@ -0,0 +1,939 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_SELFINTERSECTMESH_H +#define IGL_COPYLEFT_CGAL_SELFINTERSECTMESH_H + +#include "CGAL_includes.hpp" +#include "RemeshSelfIntersectionsParam.h" +#include "../../unique.h" + +#include +#include +#include +#include +#include +#include + +//#define IGL_SELFINTERSECTMESH_DEBUG +#ifndef IGL_FIRST_HIT_EXCEPTION +#define IGL_FIRST_HIT_EXCEPTION 10 +#endif + +// The easiest way to keep track of everything is to use a class + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Kernel is a CGAL kernel like: + // CGAL::Exact_predicates_inexact_constructions_kernel + // or + // CGAL::Exact_predicates_exact_constructions_kernel + + template < + typename Kernel, + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> + class SelfIntersectMesh + { + typedef + SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM> Self; + public: + // 3D Primitives + typedef CGAL::Point_3 Point_3; + typedef CGAL::Segment_3 Segment_3; + typedef CGAL::Triangle_3 Triangle_3; + typedef CGAL::Plane_3 Plane_3; + typedef CGAL::Tetrahedron_3 Tetrahedron_3; + // 2D Primitives + typedef CGAL::Point_2 Point_2; + typedef CGAL::Segment_2 Segment_2; + typedef CGAL::Triangle_2 Triangle_2; + // 2D Constrained Delaunay Triangulation types + typedef CGAL::Exact_intersections_tag Itag; + // Axis-align boxes for all-pairs self-intersection detection + typedef std::vector Triangles; + typedef typename Triangles::iterator TrianglesIterator; + typedef typename Triangles::const_iterator TrianglesConstIterator; + typedef + CGAL::Box_intersection_d::Box_with_handle_d + Box; + + // Input mesh + const Eigen::MatrixBase & V; + const Eigen::MatrixBase & F; + // Number of self-intersecting triangle pairs + typedef typename DerivedF::Index Index; + Index count; + typedef std::vector> ObjectList; + // Using a vector here makes this **not** output sensitive + Triangles T; + typedef std::vector IndexList; + IndexList lIF; + // #F-long list of faces with intersections mapping to the order in + // which they were first found + std::map offending; + // Make a short name for the edge map's key + typedef std::pair EMK; + // Make a short name for the type stored at each edge, the edge map's + // value + typedef std::vector EMV; + // Make a short name for the edge map + typedef std::map EdgeMap; + // Maps edges of offending faces to all incident offending faces + std::vector > + candidate_triangle_pairs; + + public: + RemeshSelfIntersectionsParam params; + public: + // Constructs (VV,FF) a new mesh with self-intersections of (V,F) + // subdivided + // + // See also: remesh_self_intersections.h + inline SelfIntersectMesh( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const RemeshSelfIntersectionsParam & params, + Eigen::PlainObjectBase & VV, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & IF, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM); + private: + // Helper function to mark a face as offensive + // + // Inputs: + // f index of face in F + inline void mark_offensive(const Index f); + // Helper function to count intersections between faces + // + // Input: + // fa index of face A in F + // fb index of face B in F + inline void count_intersection( const Index fa, const Index fb); + // Helper function for box_intersect. Intersect two triangles A and B, + // append the intersection object (point,segment,triangle) to a running + // list for A and B + // + // Inputs: + // A triangle in 3D + // B triangle in 3D + // fa index of A in F (and key into offending) + // fb index of B in F (and key into offending) + // Returns true only if A intersects B + // + inline bool intersect( + const Triangle_3 & A, + const Triangle_3 & B, + const Index fa, + const Index fb); + // Helper function for box_intersect. In the case where A and B have + // already been identified to share a vertex, then we only want to + // add possible segment intersections. Assumes truly duplicate + // triangles are not given as input + // + // Inputs: + // A triangle in 3D + // B triangle in 3D + // fa index of A in F (and key into offending) + // fb index of B in F (and key into offending) + // va index of shared vertex in A (and key into offending) + // vb index of shared vertex in B (and key into offending) + // Returns true if intersection (besides shared point) + // + inline bool single_shared_vertex( + const Triangle_3 & A, + const Triangle_3 & B, + const Index fa, + const Index fb, + const Index va, + const Index vb); + // Helper handling one direction + inline bool single_shared_vertex( + const Triangle_3 & A, + const Triangle_3 & B, + const Index fa, + const Index fb, + const Index va); + // Helper function for box_intersect. In the case where A and B have + // already been identified to share two vertices, then we only want + // to add a possible coplanar (Triangle) intersection. Assumes truly + // degenerate facets are not givin as input. + inline bool double_shared_vertex( + const Triangle_3 & A, + const Triangle_3 & B, + const Index fa, + const Index fb, + const std::vector > shared); + + public: + // Callback function called during box self intersections test. Means + // boxes a and b intersect. This method then checks if the triangles + // in each box intersect and if so, then processes the intersections + // + // Inputs: + // a box containing a triangle + // b box containing a triangle + inline void box_intersect(const Box& a, const Box& b); + inline void process_intersecting_boxes(); + public: + // Getters: + //const IndexList& get_lIF() const{ return lIF;} + static inline void box_intersect_static( + SelfIntersectMesh * SIM, + const Box &a, + const Box &b); + private: + std::mutex m_offending_lock; + }; + } + } +} + +// Implementation + +#include "mesh_to_cgal_triangle_list.h" +#include "remesh_intersections.h" + +#include "../../REDRUM.h" +#include "../../get_seconds.h" +#include "../../C_STR.h" + + +#include +#include +#include +#include +#include + +// References: +// http://minregret.googlecode.com/svn/trunk/skyline/src/extern/CGAL-3.3.1/examples/Polyhedron/polyhedron_self_intersection.cpp +// http://www.cgal.org/Manual/3.9/examples/Boolean_set_operations_2/do_intersect.cpp + +// Q: Should we be using CGAL::Polyhedron_3? +// A: No! Input is just a list of unoriented triangles. Polyhedron_3 requires +// a 2-manifold. +// A: But! It seems we could use CGAL::Triangulation_3. Though it won't be easy +// to take advantage of functions like insert_in_facet because we want to +// constrain segments. Hmmm. Actually Triangulation_3 doesn't look right... + +// CGAL's box_self_intersection_d uses C-style function callbacks without +// userdata. This is a leapfrog method for calling a member function. It should +// be bound as if the prototype was: +// static void box_intersect(const Box &a, const Box &b) +// using boost: +// boost::function cb +// = boost::bind(&::box_intersect, this, _1,_2); +// +template < + typename Kernel, + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> +inline void igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM>::box_intersect_static( + Self * SIM, + const typename Self::Box &a, + const typename Self::Box &b) +{ + SIM->box_intersect(a,b); +} + +template < + typename Kernel, + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> +inline igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM>::SelfIntersectMesh( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const RemeshSelfIntersectionsParam & params, + Eigen::PlainObjectBase & VV, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & IF, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM): + V(V), + F(F), + count(0), + T(), + lIF(), + offending(), + params(params) +{ + using namespace std; + using namespace Eigen; + +#ifdef IGL_SELFINTERSECTMESH_DEBUG + const auto & tictoc = []() -> double + { + static double t_start = igl::get_seconds(); + double diff = igl::get_seconds()-t_start; + t_start += diff; + return diff; + }; + const auto log_time = [&](const std::string& label) -> void{ + std::cout << "SelfIntersectMesh." << label << ": " + << tictoc() << std::endl; + }; + tictoc(); +#endif + + // Compute and process self intersections + mesh_to_cgal_triangle_list(V,F,T); +#ifdef IGL_SELFINTERSECTMESH_DEBUG + log_time("convert_to_triangle_list"); +#endif + // http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Box_intersection_d/Chapter_main.html#Section_63.5 + // Create the corresponding vector of bounding boxes + std::vector boxes; + boxes.reserve(T.size()); + for ( + TrianglesIterator tit = T.begin(); + tit != T.end(); + ++tit) + { + if (!tit->is_degenerate()) + { + boxes.push_back(Box(tit->bbox(), tit)); + } + } + // Leapfrog callback + std::function cb = + std::bind(&box_intersect_static, this, + // Explicitly use std namespace to avoid confusion with boost (who puts + // _1 etc. in global namespace) + std::placeholders::_1, + std::placeholders::_2); +#ifdef IGL_SELFINTERSECTMESH_DEBUG + log_time("box_and_bind"); +#endif + // Run the self intersection algorithm with all defaults + CGAL::box_self_intersection_d(boxes.begin(), boxes.end(),cb); +#ifdef IGL_SELFINTERSECTMESH_DEBUG + log_time("box_intersection_d"); +#endif + try{ + process_intersecting_boxes(); + }catch(int e) + { + // Rethrow if not IGL_FIRST_HIT_EXCEPTION + if(e != IGL_FIRST_HIT_EXCEPTION) + { + throw e; + } + // Otherwise just fall through + } +#ifdef IGL_SELFINTERSECTMESH_DEBUG + log_time("resolve_intersection"); +#endif + + // Convert lIF to Eigen matrix + assert(lIF.size()%2 == 0); + IF.resize(lIF.size()/2,2); + { + Index i=0; + for( + typename IndexList::const_iterator ifit = lIF.begin(); + ifit!=lIF.end(); + ) + { + IF(i,0) = (*ifit); + ifit++; + IF(i,1) = (*ifit); + ifit++; + i++; + } + } +#ifdef IGL_SELFINTERSECTMESH_DEBUG + log_time("store_intersecting_face_pairs"); +#endif + + if(params.detect_only) + { + return; + } + + remesh_intersections( + V,F,T,offending,params.stitch_all,VV,FF,J,IM); + +#ifdef IGL_SELFINTERSECTMESH_DEBUG + log_time("remesh_intersection"); +#endif +} + + +template < + typename Kernel, + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> +inline void igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM>::mark_offensive(const Index f) +{ + using namespace std; + lIF.push_back(f); + if(offending.count(f) == 0) + { + // first time marking, initialize with new id and empty list + offending[f] = {}; + } +} + +template < + typename Kernel, + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> +inline void igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM>::count_intersection( + const Index fa, + const Index fb) +{ + std::lock_guard guard(m_offending_lock); + mark_offensive(fa); + mark_offensive(fb); + this->count++; + // We found the first intersection + if(params.first_only && this->count >= 1) + { + throw IGL_FIRST_HIT_EXCEPTION; + } + +} + +template < + typename Kernel, + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> +inline bool igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM>::intersect( + const Triangle_3 & A, + const Triangle_3 & B, + const Index fa, + const Index fb) +{ + // Determine whether there is an intersection + if(!CGAL::do_intersect(A,B)) + { + return false; + } + count_intersection(fa,fb); + if(!params.detect_only) + { + // Construct intersection + CGAL::Object result = CGAL::intersection(A,B); + std::lock_guard guard(m_offending_lock); + offending[fa].push_back({fb, result}); + offending[fb].push_back({fa, result}); + } + return true; +} + +template < + typename Kernel, + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> +inline bool igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM>::single_shared_vertex( + const Triangle_3 & A, + const Triangle_3 & B, + const Index fa, + const Index fb, + const Index va, + const Index vb) +{ + if(single_shared_vertex(A,B,fa,fb,va)) + { + return true; + } + return single_shared_vertex(B,A,fb,fa,vb); +} + +template < + typename Kernel, + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> +inline bool igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM>::single_shared_vertex( + const Triangle_3 & A, + const Triangle_3 & B, + const Index fa, + const Index fb, + const Index va) +{ + // This was not a good idea. It will not handle coplanar triangles well. + using namespace std; + Segment_3 sa( + A.vertex((va+1)%3), + A.vertex((va+2)%3)); + + if(CGAL::do_intersect(sa,B)) + { + // can't put count_intersection(fa,fb) here since we use intersect below + // and then it will be counted twice. + if(params.detect_only) + { + count_intersection(fa,fb); + return true; + } + CGAL::Object result = CGAL::intersection(sa,B); + if(const Point_3 * p = CGAL::object_cast(&result)) + { + // Single intersection --> segment from shared point to intersection + CGAL::Object seg = CGAL::make_object(Segment_3( + A.vertex(va), + *p)); + count_intersection(fa,fb); + std::lock_guard guard(m_offending_lock); + offending[fa].push_back({fb, seg}); + offending[fb].push_back({fa, seg}); + return true; + }else if(CGAL::object_cast(&result)) + { + // Need to do full test. Intersection could be a general poly. + bool test = intersect(A,B,fa,fb); + ((void)test); + assert(test && "intersect should agree with do_intersect"); + return true; + }else + { + cerr< +inline bool igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM>::double_shared_vertex( + const Triangle_3 & A, + const Triangle_3 & B, + const Index fa, + const Index fb, + const std::vector > shared) +{ + using namespace std; + + // must be co-planar + if( + A.supporting_plane() != B.supporting_plane() && + A.supporting_plane() != B.supporting_plane().opposite()) + { + return false; + } + // Since A and B are non-degenerate the intersection must be a polygon + // (triangle). Either + // - the vertex of A (B) opposite the shared edge of lies on B (A), or + // - an edge of A intersects and edge of B without sharing a vertex + // + // Determine if the vertex opposite edge (a0,a1) in triangle A lies in + // (intersects) triangle B + const auto & opposite_point_inside = []( + const Triangle_3 & A, const Index a0, const Index a1, const Triangle_3 & B) + -> bool + { + // get opposite index + Index a2 = -1; + for(int c = 0;c<3;c++) + { + if(c != a0 && c != a1) + { + a2 = c; + break; + } + } + assert(a2 != -1); + bool ret = CGAL::do_intersect(A.vertex(a2),B); + return ret; + }; + + // Determine if edge opposite vertex va in triangle A intersects edge + // opposite vertex vb in triangle B. + const auto & opposite_edges_intersect = []( + const Triangle_3 & A, const Index va, + const Triangle_3 & B, const Index vb) -> bool + { + Segment_3 sa( A.vertex((va+1)%3), A.vertex((va+2)%3)); + Segment_3 sb( B.vertex((vb+1)%3), B.vertex((vb+2)%3)); + bool ret = CGAL::do_intersect(sa,sb); + return ret; + }; + + if( + !opposite_point_inside(A,shared[0].first,shared[1].first,B) && + !opposite_point_inside(B,shared[0].second,shared[1].second,A) && + !opposite_edges_intersect(A,shared[0].first,B,shared[1].second) && + !opposite_edges_intersect(A,shared[1].first,B,shared[0].second)) + { + return false; + } + + // there is an intersection indeed + count_intersection(fa,fb); + if(params.detect_only) + { + return true; + } + // Construct intersection + try + { + // This can fail for Epick but not Epeck + CGAL::Object result = CGAL::intersection(A,B); + if(!result.empty()) + { + if(CGAL::object_cast(&result)) + { + // not coplanar + assert(false && + "Co-planar non-degenerate triangles should intersect over triangle"); + return false; + } else if(CGAL::object_cast(&result)) + { + // this "shouldn't" happen but does for inexact + assert(false && + "Co-planar non-degenerate triangles should intersect over triangle"); + return false; + } else + { + // Triangle object + std::lock_guard guard(m_offending_lock); + offending[fa].push_back({fb, result}); + offending[fb].push_back({fa, result}); + return true; + } + }else + { + // CGAL::intersection is disagreeing with do_intersect + assert(false && "CGAL::intersection should agree with predicate tests"); + return false; + } + }catch(...) + { + // This catches some cgal assertion: + // CGAL error: assertion violation! + // Expression : is_finite(d) + // File : /opt/local/include/CGAL/GMP/Gmpq_type.h + // Line : 132 + // Explanation: + // But only if NDEBUG is not defined, otherwise there's an uncaught + // "Floating point exception: 8" SIGFPE + return false; + } + // No intersection. + return false; +} + +template < + typename Kernel, + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> +inline void igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM>::box_intersect( + const Box& a, + const Box& b) +{ + candidate_triangle_pairs.push_back({a.handle(), b.handle()}); +} + +template < + typename Kernel, + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> +inline void igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM>::process_intersecting_boxes() +{ + std::vector triangle_locks(T.size()); + std::vector vertex_locks(V.rows()); + std::mutex index_lock; + std::mutex exception_mutex; + bool exception_fired = false; + int exception = -1; + auto process_chunk = + [&]( + const size_t first, + const size_t last) -> void + { + try + { + assert(last >= first); + + for (size_t i=first; i guard(index_lock); + const auto& tri_pair = candidate_triangle_pairs[i]; + fa = tri_pair.first - T.begin(); + fb = tri_pair.second - T.begin(); + } + assert(fa < T.size()); + assert(fb < T.size()); + + // Lock triangles + std::lock_guard guard_A(triangle_locks[fa]); + std::lock_guard guard_B(triangle_locks[fb]); + + // Lock vertices + std::list > guard_vertices; + { + std::vector unique_vertices; + std::vector tmp1, tmp2; + igl::unique({F(fa,0), F(fa,1), F(fa,2), F(fb,0), F(fb,1), F(fb,2)}, + unique_vertices, tmp1, tmp2); + std::for_each(unique_vertices.begin(), unique_vertices.end(), + [&](const typename DerivedF::Scalar& vi) { + guard_vertices.emplace_back(vertex_locks[vi]); + }); + } + if(exception_fired) return; + + const Triangle_3& A = T[fa]; + const Triangle_3& B = T[fb]; + + // Number of combinatorially shared vertices + Index comb_shared_vertices = 0; + // Number of geometrically shared vertices (*not* including + // combinatorially shared) + Index geo_shared_vertices = 0; + // Keep track of shared vertex indices + std::vector > shared; + Index ea,eb; + for(ea=0;ea<3;ea++) + { + for(eb=0;eb<3;eb++) + { + if(F(fa,ea) == F(fb,eb)) + { + comb_shared_vertices++; + shared.emplace_back(ea,eb); + }else if(A.vertex(ea) == B.vertex(eb)) + { + geo_shared_vertices++; + shared.emplace_back(ea,eb); + } + } + } + const Index total_shared_vertices = + comb_shared_vertices + geo_shared_vertices; + if(exception_fired) return; + + if(comb_shared_vertices== 3) + { + assert(shared.size() == 3); + // Combinatorially duplicate face, these should be removed by + // preprocessing + continue; + } + if(total_shared_vertices== 3) + { + assert(shared.size() == 3); + // Geometrically duplicate face, these should be removed by + // preprocessing + continue; + } + if(total_shared_vertices == 2) + { + assert(shared.size() == 2); + // Q: What about coplanar? + // + // o o + // |\ /| + // | \/ | + // | /\ | + // |/ \| + // o----o + double_shared_vertex(A,B,fa,fb,shared); + continue; + } + assert(total_shared_vertices<=1); + if(total_shared_vertices==1) + { + single_shared_vertex(A,B,fa,fb,shared[0].first,shared[0].second); + }else + { + intersect(A,B,fa,fb); + } + } + }catch(int e) + { + std::lock_guard exception_lock(exception_mutex); + exception_fired = true; + exception = e; + } + }; + size_t num_threads=0; + const size_t hardware_limit = std::thread::hardware_concurrency(); + if (const char* igl_num_threads = std::getenv("LIBIGL_NUM_THREADS")) { + num_threads = atoi(igl_num_threads); + } + if (num_threads == 0 || num_threads > hardware_limit) { + num_threads = hardware_limit; + } + assert(num_threads > 0); + const size_t num_pairs = candidate_triangle_pairs.size(); + const size_t chunk_size = num_pairs / num_threads; + std::vector threads; + for (size_t i=0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "assign.h" +#include "assign_scalar.h" + +template +IGL_INLINE void igl::copyleft::cgal::assign( + const Eigen::MatrixBase & C, + Eigen::PlainObjectBase & D) +{ + D.resizeLike(C); + for(int i = 0;i +IGL_INLINE +Eigen::Matrix< + ReturnScalar, + DerivedC::RowsAtCompileTime, + DerivedC::ColsAtCompileTime, + 1, + DerivedC::MaxRowsAtCompileTime, + DerivedC::MaxColsAtCompileTime> +igl::copyleft::cgal::assign( + const Eigen::MatrixBase & C) +{ + Eigen::Matrix< + ReturnScalar, + DerivedC::RowsAtCompileTime, + DerivedC::ColsAtCompileTime, + 1, + DerivedC::MaxRowsAtCompileTime, + DerivedC::MaxColsAtCompileTime> D; + assign(C,D); + return D; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::cgal::assign, -1, -1, 1, -1, -1>, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::assign, Eigen::Matrix, -1, -1, 1, -1, -1> >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::assign, Eigen::Matrix, -1, -1, 1, -1, -1> >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::assign, -1, 3, 0, -1, 3>, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::assign, -1, -1, 1, -1, -1>, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::assign, -1, -1, 1, -1, -1>, Eigen::Matrix, -1, -1, 0, -1, -1> >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::assign, -1, -1, 1, -1, -1>, Eigen::Matrix, -1, 3, 0, -1, 3> >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::assign, -1, -1, 1, -1, -1>, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::assign, -1, -1, 0, -1, -1>, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::assign, -1, -1, 0, -1, -1>, Eigen::Matrix, -1, 3, 0, -1, 3> >(Eigen::MatrixBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&); +template void igl::copyleft::cgal::assign, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::assign, Eigen::Matrix, -1, -1, 0, -1, -1> >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&); +template void igl::copyleft::cgal::assign, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::assign, Eigen::Matrix, -1, -1, 0, -1, -1> >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&); +template void igl::copyleft::cgal::assign, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::assign, Eigen::Matrix, -1, 3, 0, -1, 3> >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&); +template void igl::copyleft::cgal::assign, -1, -1, 0, -1, -1>, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::assign, -1, 3, 0, -1, 3>, Eigen::Matrix, -1, 3, 0, -1, 3> >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&); +template void igl::copyleft::cgal::assign, -1, 3, 0, -1, 3>, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::assign, 8, 3, 0, 8, 3>, Eigen::Matrix, 8, 3, 0, 8, 3> >(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::PlainObjectBase, 8, 3, 0, 8, 3> >&); +template void igl::copyleft::cgal::assign, 8, 3, 0, 8, 3>, Eigen::Matrix >(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/assign.h b/src/igl/copyleft/cgal/assign.h new file mode 100644 index 0000000000..db9081f236 --- /dev/null +++ b/src/igl/copyleft/cgal/assign.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_ASSIGN_H +#define IGL_COPYLEFT_CGAL_ASSIGN_H +#include "../../igl_inline.h" +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + template + IGL_INLINE void assign( + const Eigen::MatrixBase & C, + Eigen::PlainObjectBase & D); + template + IGL_INLINE + Eigen::Matrix< + ReturnScalar, + DerivedC::RowsAtCompileTime, + DerivedC::ColsAtCompileTime, + 1, + DerivedC::MaxRowsAtCompileTime, + DerivedC::MaxColsAtCompileTime> + assign( + const Eigen::MatrixBase & C); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "assign.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/assign_scalar.cpp b/src/igl/copyleft/cgal/assign_scalar.cpp new file mode 100644 index 0000000000..c38ee03a15 --- /dev/null +++ b/src/igl/copyleft/cgal/assign_scalar.cpp @@ -0,0 +1,136 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "assign_scalar.h" + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const CGAL::Epeck::FT & cgal, + CGAL::Epeck::FT & d) +{ + d = cgal; +} + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const CGAL::Epeck::FT & _cgal, + double & d) +{ + // FORCE evaluation of the exact type otherwise interval might be huge. + const CGAL::Epeck::FT cgal = _cgal.exact(); + const auto interval = CGAL::to_interval(cgal); + d = interval.first; + do { + const double next = nextafter(d, interval.second); + if (CGAL::abs(cgal-d) < CGAL::abs(cgal-next)) break; + d = next; + } while (d < interval.second); +} + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const CGAL::Epeck::FT & _cgal, + float& d) +{ + // FORCE evaluation of the exact type otherwise interval might be huge. + const CGAL::Epeck::FT cgal = _cgal.exact(); + const auto interval = CGAL::to_interval(cgal); + d = interval.first; + do { + const float next = nextafter(d, float(interval.second)); + if (CGAL::abs(cgal-d) < CGAL::abs(cgal-next)) break; + d = next; + } while (d < float(interval.second)); +} + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const double & c, + double & d) +{ + d = c; +} + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const float& c, + float& d) +{ + d = c; +} + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const float& c, + double& d) +{ + d = c; +} + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt::FT & cgal, + CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt::FT & d) +{ + d = cgal; +} + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt::FT & cgal, + double & d) +{ + const auto interval = CGAL::to_interval(cgal); + d = interval.first; + do { + const double next = nextafter(d, interval.second); + if (CGAL::abs(cgal-d) < CGAL::abs(cgal-next)) break; + d = next; + } while (d < interval.second); +} + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt::FT & cgal, + float& d) +{ + const auto interval = CGAL::to_interval(cgal); + d = interval.first; + do { + const float next = nextafter(d, float(interval.second)); + if (CGAL::abs(cgal-d) < CGAL::abs(cgal-next)) break; + d = next; + } while (d < float(interval.second)); +} + +#ifndef WIN32 + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const CGAL::Simple_cartesian::FT & cgal, + CGAL::Simple_cartesian::FT & d) +{ + d = cgal; +} + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const CGAL::Simple_cartesian::FT & cgal, + double & d) +{ + const auto interval = CGAL::to_interval(cgal); + d = interval.first; + do { + const double next = nextafter(d, interval.second); + if (CGAL::abs(cgal-d) < CGAL::abs(cgal-next)) break; + d = next; + } while (d < interval.second); +} + +IGL_INLINE void igl::copyleft::cgal::assign_scalar( + const CGAL::Simple_cartesian::FT & cgal, + float& d) +{ + const auto interval = CGAL::to_interval(cgal); + d = interval.first; + do { + const float next = nextafter(d, float(interval.second)); + if (CGAL::abs(cgal-d) < CGAL::abs(cgal-next)) break; + d = next; + } while (d < float(interval.second)); +} + +#endif // WIN32 diff --git a/src/igl/copyleft/cgal/assign_scalar.h b/src/igl/copyleft/cgal/assign_scalar.h new file mode 100644 index 0000000000..feec928d5f --- /dev/null +++ b/src/igl/copyleft/cgal/assign_scalar.h @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_ASSIGN_SCALAR_H +#define IGL_COPYLEFT_CGAL_ASSIGN_SCALAR_H +#include "../../igl_inline.h" +#include +#include +#ifndef WIN32 +#include +#endif + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Inputs: + // cgal cgal scalar + // Outputs: + // d output scalar + IGL_INLINE void assign_scalar( + const CGAL::Epeck::FT & cgal, + CGAL::Epeck::FT & d); + IGL_INLINE void assign_scalar( + const CGAL::Epeck::FT & cgal, + double & d); + IGL_INLINE void assign_scalar( + const CGAL::Epeck::FT & cgal, + float& d); + IGL_INLINE void assign_scalar( + const double & c, + double & d); + IGL_INLINE void assign_scalar( + const float& c, + float & d); + IGL_INLINE void assign_scalar( + const float& c, + double& d); + + IGL_INLINE void assign_scalar( + const CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt::FT & cgal, + CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt::FT & d); + IGL_INLINE void assign_scalar( + const CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt::FT & cgal, + double & d); + IGL_INLINE void assign_scalar( + const CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt::FT & cgal, + float& d); + +#ifndef WIN32 + IGL_INLINE void assign_scalar( + const CGAL::Simple_cartesian::FT & cgal, + CGAL::Simple_cartesian::FT & d); + IGL_INLINE void assign_scalar( + const CGAL::Simple_cartesian::FT & cgal, + double & d); + IGL_INLINE void assign_scalar( + const CGAL::Simple_cartesian::FT & cgal, + float& d); +#endif // WIN32 + + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "assign_scalar.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/barycenter.cpp b/src/igl/copyleft/cgal/barycenter.cpp new file mode 100644 index 0000000000..b3ed8aeaf2 --- /dev/null +++ b/src/igl/copyleft/cgal/barycenter.cpp @@ -0,0 +1,15 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "../../barycenter.h" +#include +#include +#ifdef IGL_STATIC_LIBRARY +#undef IGL_STATIC_LIBRARY +#include "../../barycenter.cpp" +template void igl::barycenter, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1> >(Eigen::MatrixBase, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&); +#endif diff --git a/src/igl/copyleft/cgal/cell_adjacency.cpp b/src/igl/copyleft/cgal/cell_adjacency.cpp new file mode 100644 index 0000000000..1824a37c8b --- /dev/null +++ b/src/igl/copyleft/cgal/cell_adjacency.cpp @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// + +#include "cell_adjacency.h" + +template +IGL_INLINE void igl::copyleft::cgal::cell_adjacency( + const Eigen::PlainObjectBase& per_patch_cells, + const size_t num_cells, + std::vector > >& + adjacency_list) { + + const size_t num_patches = per_patch_cells.rows(); + adjacency_list.resize(num_cells); + for (size_t i=0; i >(Eigen::PlainObjectBase > const&, unsigned long, std::vector::Scalar, bool, unsigned long>, std::less::Scalar, bool, unsigned long> >, std::allocator::Scalar, bool, unsigned long> > >, std::allocator::Scalar, bool, unsigned long>, std::less::Scalar, bool, unsigned long> >, std::allocator::Scalar, bool, unsigned long> > > > >&); +#ifdef WIN32 +template void igl::copyleft::cgal::cell_adjacency>(class Eigen::PlainObjectBase> const &, unsigned __int64, class std::vector, struct std::less>, class std::allocator>>, class std::allocator, struct std::less>, class std::allocator>>>> &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/cell_adjacency.h b/src/igl/copyleft/cgal/cell_adjacency.h new file mode 100644 index 0000000000..2244d3c4ac --- /dev/null +++ b/src/igl/copyleft/cgal/cell_adjacency.h @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_COPYLEFT_CGAL_CELL_ADJACENCY_H +#define IGL_COPYLEFT_CGAL_CELL_ADJACENCY_H +#include "../../igl_inline.h" +#include +#include +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Inputs: + // per_patch_cells #P by 2 list of cell labels on each side of each + // patch. Cell labels are assumed to be continuous + // from 0 to #C. + // num_cells number of cells. + // + // Outputs: + // adjacency_list #C array of list of adjcent cell information. If + // cell i and cell j are adjacent via patch x, where i + // is on the positive side of x, and j is on the + // negative side. Then, + // adjacency_list[i] will contain the entry {j, false, x} + // and + // adjacency_list[j] will contain the entry {i, true, x} + template < typename DerivedC > + IGL_INLINE void cell_adjacency( + const Eigen::PlainObjectBase& per_patch_cells, + const size_t num_cells, + std::vector > >& + adjacency_list); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "cell_adjacency.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/closest_facet.cpp b/src/igl/copyleft/cgal/closest_facet.cpp new file mode 100644 index 0000000000..0b08dd4bc7 --- /dev/null +++ b/src/igl/copyleft/cgal/closest_facet.cpp @@ -0,0 +1,504 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#include "closest_facet.h" + +#include +#include +#include + +#include "order_facets_around_edge.h" +#include "submesh_aabb_tree.h" +#include "../../vertex_triangle_adjacency.h" +#include "../../LinSpaced.h" +//#include "../../writePLY.h" + +template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename DerivedP, + typename uE2EType, + typename DerivedEMAP, + typename DerivedR, + typename DerivedS > +IGL_INLINE void igl::copyleft::cgal::closest_facet( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const Eigen::PlainObjectBase& P, + const std::vector >& uE2E, + const Eigen::PlainObjectBase& EMAP, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& S) +{ + + typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; + typedef Kernel::Point_3 Point_3; + typedef Kernel::Plane_3 Plane_3; + typedef Kernel::Segment_3 Segment_3; + typedef Kernel::Triangle_3 Triangle; + typedef std::vector::iterator Iterator; + typedef CGAL::AABB_triangle_primitive Primitive; + typedef CGAL::AABB_traits AABB_triangle_traits; + typedef CGAL::AABB_tree Tree; + + if (F.rows() <= 0 || I.rows() <= 0) { + throw std::runtime_error( + "Closest facet cannot be computed on empty mesh."); + } + + std::vector > VF, VFi; + igl::vertex_triangle_adjacency(V.rows(), F, VF, VFi); + std::vector in_I; + std::vector triangles; + Tree tree; + submesh_aabb_tree(V,F,I,tree,triangles,in_I); + + return closest_facet( + V,F,I,P,uE2E,EMAP,VF,VFi,tree,triangles,in_I,R,S); +} + +template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename DerivedP, + typename uE2EType, + typename DerivedEMAP, + typename Kernel, + typename DerivedR, + typename DerivedS > +IGL_INLINE void igl::copyleft::cgal::closest_facet( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const Eigen::PlainObjectBase& P, + const std::vector >& uE2E, + const Eigen::PlainObjectBase& EMAP, + const std::vector > & VF, + const std::vector > & VFi, + const CGAL::AABB_tree< + CGAL::AABB_traits< + Kernel, + CGAL::AABB_triangle_primitive< + Kernel, typename std::vector< + typename Kernel::Triangle_3 >::iterator > > > & tree, + const std::vector & triangles, + const std::vector & in_I, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& S) +{ + typedef typename Kernel::Point_3 Point_3; + typedef typename Kernel::Plane_3 Plane_3; + typedef typename Kernel::Segment_3 Segment_3; + typedef typename Kernel::Triangle_3 Triangle; + typedef typename std::vector::iterator Iterator; + typedef typename CGAL::AABB_triangle_primitive Primitive; + typedef typename CGAL::AABB_traits AABB_triangle_traits; + typedef typename CGAL::AABB_tree Tree; + + const size_t num_faces = I.rows(); + if (F.rows() <= 0 || I.rows() <= 0) { + throw std::runtime_error( + "Closest facet cannot be computed on empty mesh."); + } + + auto on_the_positive_side = [&](size_t fid, const Point_3& p) -> bool + { + const auto& f = F.row(fid).eval(); + Point_3 v0(V(f[0], 0), V(f[0], 1), V(f[0], 2)); + Point_3 v1(V(f[1], 0), V(f[1], 1), V(f[1], 2)); + Point_3 v2(V(f[2], 0), V(f[2], 1), V(f[2], 2)); + auto ori = CGAL::orientation(v0, v1, v2, p); + switch (ori) { + case CGAL::POSITIVE: + return true; + case CGAL::NEGATIVE: + return false; + case CGAL::COPLANAR: + // Warning: + // This can only happen if fid contains a boundary edge. + // Categorized this ambiguous case as negative side. + return false; + default: + throw std::runtime_error("Unknown CGAL state."); + } + return false; + }; + + auto get_orientation = [&](size_t fid, size_t s, size_t d) -> bool + { + const auto& f = F.row(fid); + if ((size_t)f[0] == s && (size_t)f[1] == d) return false; + else if ((size_t)f[1] == s && (size_t)f[2] == d) return false; + else if ((size_t)f[2] == s && (size_t)f[0] == d) return false; + else if ((size_t)f[0] == d && (size_t)f[1] == s) return true; + else if ((size_t)f[1] == d && (size_t)f[2] == s) return true; + else if ((size_t)f[2] == d && (size_t)f[0] == s) return true; + else { + throw std::runtime_error( + "Cannot compute orientation due to incorrect connectivity"); + return false; + } + }; + auto index_to_signed_index = [&](size_t index, bool ori) -> int{ + return (index+1) * (ori? 1:-1); + }; + //auto signed_index_to_index = [&](int signed_index) -> size_t { + // return abs(signed_index) - 1; + //}; + + enum ElementType { VERTEX, EDGE, FACE }; + auto determine_element_type = [&](const Point_3& p, const size_t fid, + size_t& element_index) -> ElementType { + const auto& tri = triangles[fid]; + const Point_3 p0 = tri[0]; + const Point_3 p1 = tri[1]; + const Point_3 p2 = tri[2]; + + if (p == p0) { element_index = 0; return VERTEX; } + if (p == p1) { element_index = 1; return VERTEX; } + if (p == p2) { element_index = 2; return VERTEX; } + if (CGAL::collinear(p0, p1, p)) { element_index = 2; return EDGE; } + if (CGAL::collinear(p1, p2, p)) { element_index = 0; return EDGE; } + if (CGAL::collinear(p2, p0, p)) { element_index = 1; return EDGE; } + + element_index = 0; + return FACE; + }; + + auto process_edge_case = [&]( + size_t query_idx, + const size_t s, const size_t d, + size_t preferred_facet, + bool& orientation) -> size_t + { + Point_3 query_point( + P(query_idx, 0), + P(query_idx, 1), + P(query_idx, 2)); + + size_t corner_idx = std::numeric_limits::max(); + if ((s == F(preferred_facet, 0) && d == F(preferred_facet, 1)) || + (s == F(preferred_facet, 1) && d == F(preferred_facet, 0))) + { + corner_idx = 2; + } else if ((s == F(preferred_facet, 0) && d == F(preferred_facet, 2)) || + (s == F(preferred_facet, 2) && d == F(preferred_facet, 0))) + { + corner_idx = 1; + } else if ((s == F(preferred_facet, 1) && d == F(preferred_facet, 2)) || + (s == F(preferred_facet, 2) && d == F(preferred_facet, 1))) + { + corner_idx = 0; + } else + { + std::cerr << "s: " << s << "\t d:" << d << std::endl; + std::cerr << F.row(preferred_facet) << std::endl; + throw std::runtime_error( + "Invalid connectivity, edge does not belong to facet"); + } + + auto ueid = EMAP(preferred_facet + corner_idx * F.rows()); + auto eids = uE2E[ueid]; + std::vector intersected_face_indices; + for (auto eid : eids) + { + const size_t fid = eid % F.rows(); + if (in_I[fid]) + { + intersected_face_indices.push_back(fid); + } + } + + const size_t num_intersected_faces = intersected_face_indices.size(); + std::vector intersected_face_signed_indices(num_intersected_faces); + std::transform( + intersected_face_indices.begin(), + intersected_face_indices.end(), + intersected_face_signed_indices.begin(), + [&](size_t index) { + return index_to_signed_index( + index, get_orientation(index, s,d)); + }); + + assert(num_intersected_faces >= 1); + if (num_intersected_faces == 1) + { + // The edge must be a boundary edge. Thus, the orientation can be + // simply determined by checking if the query point is on the + // positive side of the facet. + const size_t fid = intersected_face_indices[0]; + orientation = on_the_positive_side(fid, query_point); + return fid; + } + + Eigen::VectorXi order; + DerivedP pivot = P.row(query_idx).eval(); + igl::copyleft::cgal::order_facets_around_edge(V, F, s, d, + intersected_face_signed_indices, + pivot, order); + + // Although first and last are equivalent, make the choice based on + // preferred_facet. + const size_t first = order[0]; + const size_t last = order[num_intersected_faces-1]; + if (intersected_face_indices[first] == preferred_facet) { + orientation = intersected_face_signed_indices[first] < 0; + return intersected_face_indices[first]; + } else if (intersected_face_indices[last] == preferred_facet) { + orientation = intersected_face_signed_indices[last] > 0; + return intersected_face_indices[last]; + } else { + orientation = intersected_face_signed_indices[order[0]] < 0; + return intersected_face_indices[order[0]]; + } + }; + + auto process_face_case = [&]( + const size_t query_idx, const Point_3& closest_point, + const size_t fid, bool& orientation) -> size_t { + const auto& f = F.row(I(fid, 0)); + return process_edge_case(query_idx, f[0], f[1], I(fid, 0), orientation); + }; + + // Given that the closest point to query point P(query_idx,:) on (V,F(I,:)) + // is the vertex at V(s,:) which is incident at least on triangle + // F(preferred_facet,:), determine a facet incident on V(s,:) that is + // _exposed_ to the query point and determine whether that facet is facing + // _toward_ or _away_ from the query point. + // + // Inputs: + // query_idx index into P of query point + // s index into V of closest point at vertex + // preferred_facet facet incident on s + // Outputs: + // orientation whether returned face is facing toward or away from + // query (parity unclear) + // Returns face guaranteed to be "exposed" to P(query_idx,:) + auto process_vertex_case = [&]( + const size_t query_idx, + size_t s, + size_t preferred_facet, + bool& orientation) -> size_t + { + const Point_3 query_point( + P(query_idx, 0), P(query_idx, 1), P(query_idx, 2)); + const Point_3 closest_point(V(s, 0), V(s, 1), V(s, 2)); + std::vector adj_faces; + std::vector adj_face_corners; + { + // Gather adj faces to s within I. + const auto& all_adj_faces = VF[s]; + const auto& all_adj_face_corners = VFi[s]; + const size_t num_all_adj_faces = all_adj_faces.size(); + for (size_t i=0; i 0); + + std::set adj_vertices_set; + std::unordered_multimap v2f; + for (size_t i=0; i adj_vertices(num_adj_vertices); + std::copy(adj_vertices_set.begin(), adj_vertices_set.end(), + adj_vertices.begin()); + + std::vector adj_points; + for (size_t vid : adj_vertices) + { + adj_points.emplace_back(V(vid,0), V(vid,1), V(vid,2)); + } + + // A plane is on the exterior if all adj_points lies on or to + // one side of the plane. + auto is_on_exterior = [&](const Plane_3& separator) -> bool{ + size_t positive=0; + size_t negative=0; + size_t coplanar=0; + for (const auto& point : adj_points) { + switch(separator.oriented_side(point)) { + case CGAL::ON_POSITIVE_SIDE: + positive++; + break; + case CGAL::ON_NEGATIVE_SIDE: + negative++; + break; + case CGAL::ON_ORIENTED_BOUNDARY: + coplanar++; + break; + default: + throw "Unknown plane-point orientation"; + } + } + auto query_orientation = separator.oriented_side(query_point); + if (query_orientation == CGAL::ON_ORIENTED_BOUNDARY && + (positive == 0 && negative == 0)) { + // All adj vertices and query point are coplanar. + // In this case, all separators are equally valid. + return true; + } else { + bool r = (positive == 0 && query_orientation == CGAL::POSITIVE) + || (negative == 0 && query_orientation == CGAL::NEGATIVE); + return r; + } + }; + + size_t d = std::numeric_limits::max(); + for (size_t i=0; i::max()) { + Eigen::MatrixXd tmp_vertices(V.rows(), V.cols()); + for (size_t i=0; isecond, orientation); + }; + + const size_t num_queries = P.rows(); + R.resize(num_queries, 1); + S.resize(num_queries, 1); + for (size_t i=0; i intersected_faces; + tree.all_intersected_primitives(Segment_3(closest_point, query), + std::back_inserter(intersected_faces)); + const size_t num_intersected_faces = intersected_faces.size(); + std::vector intersected_face_indices(num_intersected_faces); + std::transform(intersected_faces.begin(), + intersected_faces.end(), + intersected_face_indices.begin(), + [&](const typename Tree::Primitive_id& itr) -> size_t + { return I(itr-triangles.begin(), 0); }); + + size_t element_index; + auto element_type = determine_element_type(closest_point, fid, + element_index); + switch(element_type) { + case VERTEX: + { + const auto& f = F.row(I(fid, 0)); + const size_t s = f[element_index]; + fid = process_vertex_case(i, s, I(fid, 0), fid_ori); + } + break; + case EDGE: + { + const auto& f = F.row(I(fid, 0)); + const size_t s = f[(element_index+1)%3]; + const size_t d = f[(element_index+2)%3]; + fid = process_edge_case(i, s, d, I(fid, 0), fid_ori); + } + break; + case FACE: + { + fid = process_face_case(i, closest_point, fid, fid_ori); + } + break; + default: + throw std::runtime_error("Unknown element type."); + } + + + R(i,0) = fid; + S(i,0) = fid_ori; + } +} + +template< + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename uE2EType, + typename DerivedEMAP, + typename DerivedR, + typename DerivedS > +IGL_INLINE void igl::copyleft::cgal::closest_facet( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& P, + const std::vector >& uE2E, + const Eigen::PlainObjectBase& EMAP, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& S) { + const size_t num_faces = F.rows(); + Eigen::VectorXi I = igl::LinSpaced(num_faces, 0, num_faces-1); + igl::copyleft::cgal::closest_facet(V, F, I, P, uE2E, EMAP, R, S); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::cgal::closest_facet, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, unsigned long, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > > const&, CGAL::AABB_tree >::iterator, CGAL::Boolean_tag > > > const&, std::vector > const&, std::vector > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::closest_facet, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, unsigned long, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > > const&, CGAL::AABB_tree >::iterator, CGAL::Boolean_tag > > > const&, std::vector > const&, std::vector > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::closest_facet, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, unsigned long, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::copyleft::cgal::closest_facet, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, -1, -1, 0, -1, -1>, unsigned __int64, class Eigen::Matrix, class CGAL::Epeck, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> const &, class std::vector>, class std::allocator>>> const &, class Eigen::PlainObjectBase> const &, class std::vector>, class std::allocator>>> const &, class std::vector>, class std::allocator>>> const &, class CGAL::AABB_tree>>>, struct CGAL::Boolean_tag<0>>>> const &, class std::vector, class std::allocator>> const &, class std::vector> const &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::closest_facet, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, -1, -1, 1, -1, -1>, unsigned __int64, class Eigen::Matrix, class CGAL::Epeck, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class std::vector>, class std::allocator>>> const &, class Eigen::PlainObjectBase> const &, class std::vector>, class std::allocator>>> const &, class std::vector>, class std::allocator>>> const &, class CGAL::AABB_tree>>>, struct CGAL::Boolean_tag<0>>>> const &, class std::vector, class std::allocator>> const &, class std::vector> const &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::closest_facet, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, -1, -1, 1, -1, -1>, unsigned __int64, class Eigen::Matrix, class CGAL::Epeck, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class std::vector>, class std::allocator>>> const &, class Eigen::PlainObjectBase> const &, class std::vector>, class std::allocator>>> const &, class std::vector>, class std::allocator>>> const &, class CGAL::AABB_tree>>>, struct CGAL::Boolean_tag<0>>>> const &, class std::vector, class std::allocator>> const &, class std::vector> const &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/closest_facet.h b/src/igl/copyleft/cgal/closest_facet.h new file mode 100644 index 0000000000..737ca2e22a --- /dev/null +++ b/src/igl/copyleft/cgal/closest_facet.h @@ -0,0 +1,110 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_COPYLET_CGAL_CLOSEST_FACET_H +#define IGL_COPYLET_CGAL_CLOSEST_FACET_H + +#include "../../igl_inline.h" +#include +#include + +#include +#include +#include +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Determine the closest facet for each of the input points. + // + // Inputs: + // V #V by 3 array of vertices. + // F #F by 3 array of faces. + // I #I list of triangle indices to consider. + // P #P by 3 array of query points. + // + // Outputs: + // R #P list of closest facet indices. + // S #P list of bools indicating on which side of the closest facet + // each query point lies. + template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename DerivedP, + typename uE2EType, + typename DerivedEMAP, + typename DerivedR, + typename DerivedS > + IGL_INLINE void closest_facet( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const Eigen::PlainObjectBase& P, + const std::vector >& uE2E, + const Eigen::PlainObjectBase& EMAP, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& S); + template< + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename uE2EType, + typename DerivedEMAP, + typename DerivedR, + typename DerivedS > + IGL_INLINE void closest_facet( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& P, + const std::vector >& uE2E, + const Eigen::PlainObjectBase& EMAP, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& S); + template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename DerivedP, + typename uE2EType, + typename DerivedEMAP, + typename Kernel, + typename DerivedR, + typename DerivedS > + IGL_INLINE void closest_facet( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const Eigen::PlainObjectBase& P, + const std::vector >& uE2E, + const Eigen::PlainObjectBase& EMAP, + const std::vector > & VF, + const std::vector > & VFi, + const CGAL::AABB_tree< + CGAL::AABB_traits< + Kernel, + CGAL::AABB_triangle_primitive< + Kernel, typename std::vector< + typename Kernel::Triangle_3 >::iterator > > > & tree, + const std::vector & triangles, + const std::vector & in_I, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& S); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +#include "closest_facet.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/complex_to_mesh.cpp b/src/igl/copyleft/cgal/complex_to_mesh.cpp new file mode 100644 index 0000000000..74bf085ab0 --- /dev/null +++ b/src/igl/copyleft/cgal/complex_to_mesh.cpp @@ -0,0 +1,152 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "complex_to_mesh.h" + +#include "../../centroid.h" +#include "../../remove_unreferenced.h" + +#include +#include +#include +#include + +template +IGL_INLINE bool igl::copyleft::cgal::complex_to_mesh( + const CGAL::Complex_2_in_triangulation_3 & c2t3, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F) +{ + using namespace Eigen; + // CGAL/IO/Complex_2_in_triangulation_3_file_writer.h + using CGAL::Surface_mesher::number_of_facets_on_surface; + + typedef typename CGAL::Complex_2_in_triangulation_3 C2t3; + typedef typename Tr::Finite_facets_iterator Finite_facets_iterator; + typedef typename Tr::Finite_vertices_iterator Finite_vertices_iterator; + typedef typename Tr::Facet Facet; + typedef typename Tr::Edge Edge; + typedef typename Tr::Vertex_handle Vertex_handle; + + // Header. + const Tr& tr = c2t3.triangulation(); + + bool success = true; + + const int n = tr.number_of_vertices(); + const int m = c2t3.number_of_facets(); + + assert(m == number_of_facets_on_surface(tr)); + + // Finite vertices coordinates. + std::map v2i; + V.resize(n,3); + { + int v = 0; + for(Finite_vertices_iterator vit = tr.finite_vertices_begin(); + vit != tr.finite_vertices_end(); + ++vit) + { + V(v,0) = vit->point().x(); + V(v,1) = vit->point().y(); + V(v,2) = vit->point().z(); + v2i[vit] = v++; + } + } + + { + Finite_facets_iterator fit = tr.finite_facets_begin(); + std::set oriented_set; + std::stack stack; + + while ((int)oriented_set.size() != m) + { + while ( fit->first->is_facet_on_surface(fit->second) == false || + oriented_set.find(*fit) != oriented_set.end() || + oriented_set.find(c2t3.opposite_facet(*fit)) != + oriented_set.end() ) + { + ++fit; + } + oriented_set.insert(*fit); + stack.push(*fit); + while(! stack.empty() ) + { + Facet f = stack.top(); + stack.pop(); + for(int ih = 0 ; ih < 3 ; ++ih) + { + const int i1 = tr.vertex_triple_index(f.second, tr. cw(ih)); + const int i2 = tr.vertex_triple_index(f.second, tr.ccw(ih)); + + const typename C2t3::Face_status face_status + = c2t3.face_status(Edge(f.first, i1, i2)); + if(face_status == C2t3::REGULAR) + { + Facet fn = c2t3.neighbor(f, ih); + if (oriented_set.find(fn) == oriented_set.end()) + { + if(oriented_set.find(c2t3.opposite_facet(fn)) == oriented_set.end()) + { + oriented_set.insert(fn); + stack.push(fn); + }else { + success = false; // non-orientable + } + } + }else if(face_status != C2t3::BOUNDARY) + { + success = false; // non manifold, thus non-orientable + } + } // end "for each neighbor of f" + } // end "stack non empty" + } // end "oriented_set not full" + + F.resize(m,3); + int f = 0; + for(typename std::set::const_iterator fit = + oriented_set.begin(); + fit != oriented_set.end(); + ++fit) + { + const typename Tr::Cell_handle cell = fit->first; + const int& index = fit->second; + const int index1 = v2i[cell->vertex(tr.vertex_triple_index(index, 0))]; + const int index2 = v2i[cell->vertex(tr.vertex_triple_index(index, 1))]; + const int index3 = v2i[cell->vertex(tr.vertex_triple_index(index, 2))]; + // This order is flipped + F(f,0) = index1; + F(f,1) = index2; + F(f,2) = index3; + f++; + } + assert(f == m); + } // end if(facets must be oriented) + + // This CGAL code seems to randomly assign the global orientation + // Flip based on the signed volume. + Eigen::Vector3d cen; + double vol; + igl::centroid(V,F,cen,vol); + if(vol < 0) + { + // Flip + F = F.rowwise().reverse().eval(); + } + + // CGAL code somehow can end up with unreferenced vertices + { + VectorXi _1; + remove_unreferenced( MatrixXd(V), MatrixXi(F), V,F,_1); + } + + return success; +} + +#ifdef IGL_STATIC_LIBRARY +template bool igl::copyleft::cgal::complex_to_mesh, CGAL::Triangulation_data_structure_3, CGAL::Triangulation_vertex_base_3, CGAL::Triangulation_ds_vertex_base_3 > >, CGAL::Delaunay_triangulation_cell_base_with_circumcenter_3, CGAL::Surface_mesh_cell_base_3, CGAL::Triangulation_cell_base_3, CGAL::Triangulation_ds_cell_base_3 > > >, CGAL::Sequential_tag>, CGAL::Default, CGAL::Default>, Eigen::Matrix, Eigen::Matrix >(CGAL::Complex_2_in_triangulation_3, CGAL::Triangulation_data_structure_3, CGAL::Triangulation_vertex_base_3, CGAL::Triangulation_ds_vertex_base_3 > >, CGAL::Delaunay_triangulation_cell_base_with_circumcenter_3, CGAL::Surface_mesh_cell_base_3, CGAL::Triangulation_cell_base_3, CGAL::Triangulation_ds_cell_base_3 > > >, CGAL::Sequential_tag>, CGAL::Default, CGAL::Default>, void> const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/complex_to_mesh.h b/src/igl/copyleft/cgal/complex_to_mesh.h new file mode 100644 index 0000000000..ed66064b76 --- /dev/null +++ b/src/igl/copyleft/cgal/complex_to_mesh.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_COMPLEX_TO_MESH_H +#define IGL_COPYLEFT_CGAL_COMPLEX_TO_MESH_H +#include "../../igl_inline.h" + +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Templates: + // Tr CGAL triangulation type, e.g. + // CGAL::Surface_mesh_default_triangulation_3 + // Inputs + // c2t3 2-complex (surface) living in a 3d triangulation (e.g. result of + // CGAL::make_surface_mesh) + // Outputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // Returns true iff conversion was successful, failure can ok if CGAL code + // can't figure out ordering. + // + template + IGL_INLINE bool complex_to_mesh( + const CGAL::Complex_2_in_triangulation_3 & c2t3, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "complex_to_mesh.cpp" +#endif + +#endif + diff --git a/src/igl/copyleft/cgal/component_inside_component.cpp b/src/igl/copyleft/cgal/component_inside_component.cpp new file mode 100644 index 0000000000..251e919491 --- /dev/null +++ b/src/igl/copyleft/cgal/component_inside_component.cpp @@ -0,0 +1,83 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "component_inside_component.h" + +#include "order_facets_around_edge.h" +#include "../../LinSpaced.h" +#include "points_inside_component.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +template +IGL_INLINE bool igl::copyleft::cgal::component_inside_component( + const Eigen::PlainObjectBase& V1, + const Eigen::PlainObjectBase& F1, + const Eigen::PlainObjectBase& I1, + const Eigen::PlainObjectBase& V2, + const Eigen::PlainObjectBase& F2, + const Eigen::PlainObjectBase& I2) { + if (F1.rows() <= 0 || I1.rows() <= 0 || F2.rows() <= 0 || I2.rows() <= 0) { + throw "Component inside test cannot be done on empty component!"; + } + + const Eigen::Vector3i& f = F1.row(I1(0, 0)); + const Eigen::Matrix query( + (V1(f[0],0) + V1(f[1],0) + V1(f[2],0))/3.0, + (V1(f[0],1) + V1(f[1],1) + V1(f[2],1))/3.0, + (V1(f[0],2) + V1(f[1],2) + V1(f[2],2))/3.0); + Eigen::VectorXi inside; + igl::copyleft::cgal::points_inside_component(V2, F2, I2, query, inside); + assert(inside.size() == 1); + return inside[0]; +} + +template +IGL_INLINE bool igl::copyleft::cgal::component_inside_component( + const Eigen::PlainObjectBase& V1, + const Eigen::PlainObjectBase& F1, + const Eigen::PlainObjectBase& V2, + const Eigen::PlainObjectBase& F2) { + if (F1.rows() <= 0 || F2.rows() <= 0) { + throw "Component inside test cannot be done on empty component!"; + } + Eigen::VectorXi I1(F1.rows()), I2(F2.rows()); + I1 = igl::LinSpaced(F1.rows(), 0, F1.rows()-1); + I2 = igl::LinSpaced(F2.rows(), 0, F2.rows()-1); + return igl::copyleft::cgal::component_inside_component(V1, F1, I1, V2, F2, I2); +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::copyleft::cgal::component_inside_component< +Eigen::Matrix, +Eigen::Matrix< int, -1, -1, 0, -1, -1>, +Eigen::Matrix< int, -1, -1, 0, -1, -1> > ( +Eigen::PlainObjectBase > const&, +Eigen::PlainObjectBase > const&, +Eigen::PlainObjectBase > const&, +Eigen::PlainObjectBase > const&, +Eigen::PlainObjectBase > const&, +Eigen::PlainObjectBase > const&); + +template bool igl::copyleft::cgal::component_inside_component< +Eigen::Matrix, +Eigen::Matrix< int, -1, -1, 0, -1, -1> > ( +Eigen::PlainObjectBase > const&, +Eigen::PlainObjectBase > const&, +Eigen::PlainObjectBase > const&, +Eigen::PlainObjectBase > const&); +#endif diff --git a/src/igl/copyleft/cgal/component_inside_component.h b/src/igl/copyleft/cgal/component_inside_component.h new file mode 100644 index 0000000000..17fdd6255d --- /dev/null +++ b/src/igl/copyleft/cgal/component_inside_component.h @@ -0,0 +1,75 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_COMONENT_INSIDE_COMPONENT +#define IGL_COPYLEFT_CGAL_COMONENT_INSIDE_COMPONENT + +#include "../../igl_inline.h" +#include +#include + +namespace igl { + namespace copyleft + { + namespace cgal { + + // Determine if connected facet component (V1, F1, I1) is inside of + // connected facet component (V2, F2, I2). + // + // Precondition: + // Both components must represent closed, self-intersection free, + // non-degenerated surfaces that are the boundary of 3D volumes. In + // addition, (V1, F1, I1) must not intersect with (V2, F2, I2). + // + // Inputs: + // V1 #V1 by 3 list of vertex position of mesh 1 + // F1 #F1 by 3 list of triangles indices into V1 + // I1 #I1 list of indices into F1, indicate the facets of component + // V2 #V2 by 3 list of vertex position of mesh 2 + // F2 #F2 by 3 list of triangles indices into V2 + // I2 #I2 list of indices into F2, indicate the facets of component + // + // Outputs: + // return true iff (V1, F1, I1) is entirely inside of (V2, F2, I2). + template + IGL_INLINE bool component_inside_component( + const Eigen::PlainObjectBase& V1, + const Eigen::PlainObjectBase& F1, + const Eigen::PlainObjectBase& I1, + const Eigen::PlainObjectBase& V2, + const Eigen::PlainObjectBase& F2, + const Eigen::PlainObjectBase& I2); + + // Determine if mesh (V1, F1) is inside of mesh (V2, F2). + // + // Precondition: + // Both meshes must be closed, self-intersection free, non-degenerated + // surfaces that are the boundary of 3D volumes. They should not + // intersect each other. + // + // Inputs: + // V1 #V1 by 3 list of vertex position of mesh 1 + // F1 #F1 by 3 list of triangles indices into V1 + // V2 #V2 by 3 list of vertex position of mesh 2 + // F2 #F2 by 3 list of triangles indices into V2 + // + // Outputs: + // return true iff (V1, F1) is entirely inside of (V2, F2). + template + IGL_INLINE bool component_inside_component( + const Eigen::PlainObjectBase& V1, + const Eigen::PlainObjectBase& F1, + const Eigen::PlainObjectBase& V2, + const Eigen::PlainObjectBase& F2); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +#include "component_inside_component.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/convex_hull.cpp b/src/igl/copyleft/cgal/convex_hull.cpp new file mode 100644 index 0000000000..8f89d55930 --- /dev/null +++ b/src/igl/copyleft/cgal/convex_hull.cpp @@ -0,0 +1,72 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "convex_hull.h" +#include "../../ismember.h" +#include "polyhedron_to_mesh.h" +#include +#include +#include +#include +#include + +template < + typename DerivedV, + typename DerivedW, + typename DerivedG> +IGL_INLINE void igl::copyleft::cgal::convex_hull( + const Eigen::MatrixBase & V, + Eigen::PlainObjectBase & W, + Eigen::PlainObjectBase & G) +{ + typedef CGAL::Exact_predicates_inexact_constructions_kernel K; + typedef K::Point_3 Point_3; + //typedef CGAL::Delaunay_triangulation_3 Delaunay; + //typedef Delaunay::Vertex_handle Vertex_handle; + //typedef CGAL::Surface_mesh Surface_mesh; + typedef CGAL::Polyhedron_3 Polyhedron_3; + std::vector points(V.rows()); + for(int i = 0;i +IGL_INLINE void igl::copyleft::cgal::convex_hull( + const Eigen::MatrixBase & V, + Eigen::PlainObjectBase & F) +{ + Eigen::Matrix W; + Eigen::Matrix G; + convex_hull(V,W,G); + // This is a lazy way to reindex into the original mesh + Eigen::Matrix I; + Eigen::VectorXi J; + igl::ismember_rows(W,V,I,J); + assert(I.all() && "Should find all W in V"); + F.resizeLike(G); + for(int f = 0;f, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/convex_hull.h b/src/igl/copyleft/cgal/convex_hull.h new file mode 100644 index 0000000000..ba04dd7cc8 --- /dev/null +++ b/src/igl/copyleft/cgal/convex_hull.h @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_CONVEX_HULL_H +#define IGL_COPYLEFT_CGAL_CONVEX_HULL_H +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Given a set of points (V), compute the convex hull as a triangle mesh (W,G) + // + // Inputs: + // V #V by 3 list of input points + // Outputs: + // W #W by 3 list of convex hull points + // G #G by 3 list of triangle indices into W + template < + typename DerivedV, + typename DerivedW, + typename DerivedG> + IGL_INLINE void convex_hull( + const Eigen::MatrixBase & V, + Eigen::PlainObjectBase & W, + Eigen::PlainObjectBase & G); + // Given a set of points (V), compute the convex hull as a triangle mesh (F) + // over input vertex set (V) + // + // Inputs: + // V #V by 3 list of input points + // Outputs: + // F #F by 3 list of triangle indices into V + // + template < + typename DerivedV, + typename DerivedF> + IGL_INLINE void convex_hull( + const Eigen::MatrixBase & V, + Eigen::PlainObjectBase & F); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "convex_hull.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/delaunay_triangulation.cpp b/src/igl/copyleft/cgal/delaunay_triangulation.cpp new file mode 100644 index 0000000000..787f79f9e7 --- /dev/null +++ b/src/igl/copyleft/cgal/delaunay_triangulation.cpp @@ -0,0 +1,62 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Alec Jacobson +// Copyright (C) 2016 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "delaunay_triangulation.h" +#include "../../delaunay_triangulation.h" +#include "orient2D.h" +#include "incircle.h" + +template< + typename DerivedV, + typename DerivedF> +IGL_INLINE void igl::copyleft::cgal::delaunay_triangulation( + const Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F) +{ + typedef typename DerivedV::Scalar Scalar; + igl::delaunay_triangulation(V, orient2D, incircle, F); + // This function really exists to test our igl::delaunay_triangulation + // + // It's currently much faster to call cgal's native Delaunay routine + // +//#include +//#include +//#include +//#include +// const auto delaunay = +// [&](const Eigen::MatrixXd & V,Eigen::MatrixXi & F) +// { +// typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +// typedef CGAL::Triangulation_vertex_base_with_info_2 Vb; +// typedef CGAL::Triangulation_data_structure_2 Tds; +// typedef CGAL::Delaunay_triangulation_2 Delaunay; +// typedef Kernel::Point_2 Point; +// std::vector< std::pair > points(V.rows()); +// for(int i = 0;ivertex(0)->info(); +// F(j,1) = face->vertex(1)->info(); +// F(j,2) = face->vertex(2)->info(); +// j++; +// } +// } +// }; +} + diff --git a/src/igl/copyleft/cgal/delaunay_triangulation.h b/src/igl/copyleft/cgal/delaunay_triangulation.h new file mode 100644 index 0000000000..88cbf9fe0b --- /dev/null +++ b/src/igl/copyleft/cgal/delaunay_triangulation.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COPYLEFT_CGAL_DELAUNAY_TRIANGULATION_H +#define IGL_COPYLEFT_CGAL_DELAUNAY_TRIANGULATION_H + +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + + // Given a set of points in 2D, return a Delaunay triangulation of these + // points. + // + // Inputs: + // V #V by 2 list of vertex positions + // + // Outputs: + // F #F by 3 of faces in Delaunay triangulation. + template< + typename DerivedV, + typename DerivedF + > + IGL_INLINE void delaunay_triangulation( + const Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F); + } + } +} + + + + +#ifndef IGL_STATIC_LIBRARY +# include "delaunay_triangulation.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/extract_cells.cpp b/src/igl/copyleft/cgal/extract_cells.cpp new file mode 100644 index 0000000000..a314edfae5 --- /dev/null +++ b/src/igl/copyleft/cgal/extract_cells.cpp @@ -0,0 +1,547 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#include "extract_cells.h" +#include "closest_facet.h" +#include "order_facets_around_edge.h" +#include "outer_facet.h" +#include "submesh_aabb_tree.h" +#include "../../extract_manifold_patches.h" +#include "../../facet_components.h" +#include "../../get_seconds.h" +#include "../../triangle_triangle_adjacency.h" +#include "../../unique_edge_map.h" +#include "../../vertex_triangle_adjacency.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//#define EXTRACT_CELLS_DEBUG + +template< + typename DerivedV, + typename DerivedF, + typename DerivedC > +IGL_INLINE size_t igl::copyleft::cgal::extract_cells( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& cells) +{ + const size_t num_faces = F.rows(); + // Construct edge adjacency + Eigen::MatrixXi E, uE; + Eigen::VectorXi EMAP; + std::vector > uE2E; + igl::unique_edge_map(F, E, uE, EMAP, uE2E); + // Cluster into manifold patches + Eigen::VectorXi P; + igl::extract_manifold_patches(F, EMAP, uE2E, P); + // Extract cells + DerivedC per_patch_cells; + const size_t num_cells = + igl::copyleft::cgal::extract_cells(V,F,P,E,uE,uE2E,EMAP,per_patch_cells); + // Distribute per-patch cell information to each face + cells.resize(num_faces, 2); + for (size_t i=0; i +IGL_INLINE size_t igl::copyleft::cgal::extract_cells( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& E, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + const Eigen::PlainObjectBase& EMAP, + Eigen::PlainObjectBase& cells) +{ + // Trivial base case + if(P.size() == 0) + { + assert(F.size() == 0); + cells.resize(0,2); + return 0; + } + + typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; + typedef Kernel::Point_3 Point_3; + typedef Kernel::Plane_3 Plane_3; + typedef Kernel::Segment_3 Segment_3; + typedef Kernel::Triangle_3 Triangle; + typedef std::vector::iterator Iterator; + typedef CGAL::AABB_triangle_primitive Primitive; + typedef CGAL::AABB_traits AABB_triangle_traits; + typedef CGAL::AABB_tree Tree; + +#ifdef EXTRACT_CELLS_DEBUG + const auto & tictoc = []() -> double + { + static double t_start = igl::get_seconds(); + double diff = igl::get_seconds()-t_start; + t_start += diff; + return diff; + }; + const auto log_time = [&](const std::string& label) -> void { + std::cout << "extract_cells." << label << ": " + << tictoc() << std::endl; + }; + tictoc(); +#else + // no-op + const auto log_time = [](const std::string){}; +#endif + const size_t num_faces = F.rows(); + typedef typename DerivedF::Scalar Index; + assert(P.size() > 0); + const size_t num_patches = P.maxCoeff()+1; + + // Extract all cells... + DerivedC raw_cells; + const size_t num_raw_cells = + extract_cells_single_component(V,F,P,uE,uE2E,EMAP,raw_cells); + log_time("extract_single_component_cells"); + + // Compute triangle-triangle adjacency data-structure + std::vector > > TT,_1; + igl::triangle_triangle_adjacency(E, EMAP, uE2E, false, TT, _1); + log_time("compute_face_adjacency"); + + // Compute connected components of the mesh + Eigen::VectorXi C, counts; + igl::facet_components(TT, C, counts); + log_time("form_components"); + + const size_t num_components = counts.size(); + // components[c] --> list of face indices into F of faces in component c + std::vector > components(num_components); + // Loop over all faces + for (size_t i=0; i > VF,VFi; + igl::vertex_triangle_adjacency(V.rows(), F, VF, VFi); + std::vector Is(num_components); + std::vector< + CGAL::AABB_tree< + CGAL::AABB_traits< + Kernel, + CGAL::AABB_triangle_primitive< + Kernel, std::vector< + Kernel::Triangle_3 >::iterator > > > > trees(num_components); + std::vector< std::vector > + triangle_lists(num_components); + std::vector > in_Is(num_components); + + // Find outer facets, their orientations and cells for each component + Eigen::VectorXi outer_facets(num_components); + Eigen::VectorXi outer_facet_orientation(num_components); + Eigen::VectorXi outer_cells(num_components); + for (size_t i=0; i > nested_cells(num_raw_cells); + std::vector > ambient_cells(num_raw_cells); + std::vector > ambient_comps(num_components); + // Only bother if there's more than one component + if(num_components > 1) + { + // construct bounding boxes for each component + DerivedV bbox_min(num_components, 3); + DerivedV bbox_max(num_components, 3); + // Assuming our mesh (in exact numbers) fits in the range of double. + bbox_min.setConstant(std::numeric_limits::max()); + bbox_max.setConstant(std::numeric_limits::min()); + // Loop over faces + for (size_t i=0; i candidate_comps; + candidate_comps.reserve(num_components); + // Loop over components + for (size_t j=0; j component index inside component i, because the cell of the + // closest facet on i to component index is **not** the same as the + // "outer cell" of component i: component index is **not** outside of + // component i (therefore it's inside). + nested_cells[ambient_cell].push_back(outer_cells[index]); + ambient_cells[outer_cells[index]].push_back(ambient_cell); + ambient_comps[index].push_back(i); + } + } + } + } + +#ifdef EXTRACT_CELLS_DEBUG + log_time("nested_relationship"); +#endif + + const size_t INVALID = std::numeric_limits::max(); + const size_t INFINITE_CELL = num_raw_cells; + std::vector embedded_cells(num_raw_cells, INVALID); + for (size_t i=0; i 0) { + size_t embedded_comp = INVALID; + size_t embedded_cell = INVALID; + for (size_t j=0; j mapped_indices(num_raw_cells+1, INVALID); + // Always map infinite cell to index 0. + mapped_indices[INFINITE_CELL] = count; + count++; + + for (size_t i=0; i +IGL_INLINE size_t igl::copyleft::cgal::extract_cells_single_component( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + const Eigen::PlainObjectBase& EMAP, + Eigen::PlainObjectBase& cells) +{ + const size_t num_faces = F.rows(); + // Input: + // index index into #F*3 list of undirect edges + // Returns index into face + const auto edge_index_to_face_index = [&num_faces](size_t index) + { + return index % num_faces; + }; + // Determine if a face (containing undirected edge {s,d} is consistently + // oriented with directed edge {s,d} (or otherwise it is with {d,s}) + // + // Inputs: + // fid face index into F + // s source index of edge + // d destination index of edge + // Returns true if face F(fid,:) is consistent with {s,d} + const auto is_consistent = + [&F](const size_t fid, const size_t s, const size_t d) -> bool + { + if ((size_t)F(fid, 0) == s && (size_t)F(fid, 1) == d) return false; + if ((size_t)F(fid, 1) == s && (size_t)F(fid, 2) == d) return false; + if ((size_t)F(fid, 2) == s && (size_t)F(fid, 0) == d) return false; + + if ((size_t)F(fid, 0) == d && (size_t)F(fid, 1) == s) return true; + if ((size_t)F(fid, 1) == d && (size_t)F(fid, 2) == s) return true; + if ((size_t)F(fid, 2) == d && (size_t)F(fid, 0) == s) return true; + throw "Invalid face!"; + return false; + }; + + const size_t num_unique_edges = uE.rows(); + const size_t num_patches = P.maxCoeff() + 1; + + // Build patch-patch adjacency list. + std::vector > patch_adj(num_patches); + for (size_t i=0; i 2) { + for (size_t j=0; j::max(); + std::vector cell_labels(num_patches * 2); + for (size_t i=0; i > equivalent_cells(num_patches*2); + std::vector processed(num_unique_edges, false); + + size_t label_count=0; + for (size_t i=0; i 2); + + const size_t s = uE(uei,0); + const size_t d = uE(uei,1); + + std::vector signed_adj_faces; + for (auto ej : adj_faces) + { + const size_t fid = edge_index_to_face_index(ej); + bool cons = is_consistent(fid, s, d); + signed_adj_faces.push_back((fid+1)*(cons ? 1:-1)); + } + { + // Sort adjacent faces cyclically around {s,d} + Eigen::VectorXi order; + // order[f] will reveal the order of face f in signed_adj_faces + order_facets_around_edge(V, F, s, d, signed_adj_faces, order); + for (size_t j=0; j 0; + const bool next_cons = signed_adj_faces[order[next_idx]] > 0; + const size_t curr_cell_idx = curr_patch_idx*2 + (curr_cons?0:1); + const size_t next_cell_idx = next_patch_idx*2 + (next_cons?1:0); + equivalent_cells[curr_cell_idx].insert(next_cell_idx); + equivalent_cells[next_cell_idx].insert(curr_cell_idx); + } + } + } + } + + size_t count=0; + cells.resize(num_patches, 2); + cells.setConstant(INVALID); + const auto extract_equivalent_cells = [&](size_t i) { + if (cells(i/2, i%2) != INVALID) return; + std::queue Q; + Q.push(i); + cells(i/2, i%2) = count; + while (!Q.empty()) { + const size_t index = Q.front(); + Q.pop(); + for (const auto j : equivalent_cells[index]) { + if (cells(j/2, j%2) == INVALID) { + cells(j/2, j%2) = count; + Q.push(j); + } + } + } + count++; + }; + for (size_t i=0; i, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, unsigned long, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template unsigned long igl::copyleft::cgal::extract_cells, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, unsigned long, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#include +template unsigned long igl::copyleft::cgal::extract_cells, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, unsigned long, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template unsigned long igl::copyleft::cgal::extract_cells, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template unsigned __int64 igl::copyleft::cgal::extract_cells, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, unsigned __int64, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class std::vector>, class std::allocator>>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> &); +template unsigned __int64 igl::copyleft::cgal::extract_cells, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, unsigned __int64, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class std::vector>, class std::allocator>>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> &); +template unsigned __int64 igl::copyleft::cgal::extract_cells, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, unsigned __int64, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class std::vector>, class std::allocator>>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/extract_cells.h b/src/igl/copyleft/cgal/extract_cells.h new file mode 100644 index 0000000000..9575ffbaed --- /dev/null +++ b/src/igl/copyleft/cgal/extract_cells.h @@ -0,0 +1,116 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_COPYLEFT_CGAL_EXTRACT_CELLS +#define IGL_COPYLEFT_CGAL_EXTRACT_CELLS + +#include "../../igl_inline.h" +#include +#include + +namespace igl { + namespace copyleft + { + namespace cgal + { + // Extract connected 3D space partitioned by mesh (V, F). + // + // Inputs: + // V #V by 3 array of vertices. + // F #F by 3 array of faces. + // + // Output: + // cells #F by 2 array of cell indices. cells(i,0) represents the + // cell index on the positive side of face i, and cells(i,1) + // represents cell index of the negqtive side. + // By convension cell with index 0 is the infinite cell. + // Returns the number of cells + template< + typename DerivedV, + typename DerivedF, + typename DerivedC > + IGL_INLINE size_t extract_cells( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& cells); + + // Extract connected 3D space partitioned by mesh (V, F). + // + // Inputs: + // V #V by 3 array of vertices. + // F #F by 3 array of faces. + // P #F list of patch indices. + // E #E by 2 array of vertex indices, one edge per row. + // uE #uE by 2 list of vertex_indices, represents undirected edges. + // uE2E #uE list of lists that maps uE to E. (a one-to-many map) + // EMAP #F*3 list of indices into uE. + // + // Output: + // cells #P by 2 array of cell indices. cells(i,0) represents the + // cell index on the positive side of patch i, and cells(i,1) + // represents cell index of the negqtive side. + // By convension cell with index 0 is the infinite cell. + template< + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedE, + typename DeriveduE, + typename uE2EType, + typename DerivedEMAP, + typename DerivedC > + IGL_INLINE size_t extract_cells( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& E, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + const Eigen::PlainObjectBase& EMAP, + Eigen::PlainObjectBase& cells); + + // Extract connected 3D space partitioned by mesh (V,F) composed of + // **possibly multiple components** (the name of this function is + // dubious). + // + // Inputs: + // V #V by 3 array of vertices. + // F #F by 3 array of faces. + // P #F list of patch indices. + // E #E by 2 array of vertex indices, one edge per row. + // uE #uE by 2 list of vertex_indices, represents undirected edges. + // uE2E #uE list of lists that maps uE to E. (a one-to-many map) + // EMAP #F*3 list of indices into uE. + // Output: + // cells #P by 2 array of cell indices. cells(i,0) represents the + // cell index on the positive side of patch i, and cells(i,1) + // represents cell index of the negative side. + template< + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DeriveduE, + typename uE2EType, + typename DerivedEMAP, + typename DerivedC > + IGL_INLINE size_t extract_cells_single_component( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + const Eigen::PlainObjectBase& EMAP, + Eigen::PlainObjectBase& cells); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "extract_cells.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/extract_feature.cpp b/src/igl/copyleft/cgal/extract_feature.cpp new file mode 100644 index 0000000000..ff8205d352 --- /dev/null +++ b/src/igl/copyleft/cgal/extract_feature.cpp @@ -0,0 +1,123 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "extract_feature.h" +#include +#include +#include + +template< + typename DerivedV, + typename DerivedF, + typename DerivedE > +IGL_INLINE void igl::copyleft::cgal::extract_feature( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const double tol, + Eigen::PlainObjectBase& feature_edges) { + + using IndexType = typename DerivedE::Scalar; + DerivedE E, uE; + Eigen::VectorXi EMAP; + std::vector > uE2E; + igl::unique_edge_map(F, E, uE, EMAP, uE2E); + + igl::copyleft::cgal::extract_feature(V, F, tol, E, uE, uE2E, feature_edges); +} + +template< + typename DerivedV, + typename DerivedF, + typename DerivedE > +IGL_INLINE void igl::copyleft::cgal::extract_feature( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const double tol, + const Eigen::PlainObjectBase& E, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + Eigen::PlainObjectBase& feature_edges) { + + assert(V.cols() == 3); + assert(F.cols() == 3); + using Scalar = typename DerivedV::Scalar; + using IndexType = typename DerivedE::Scalar; + using Vertex = Eigen::Matrix; + using Kernel = typename CGAL::Exact_predicates_exact_constructions_kernel; + using Point = typename Kernel::Point_3; + + const size_t num_unique_edges = uE.rows(); + const size_t num_faces = F.rows(); + // NOTE: CGAL's definition of dihedral angle measures the angle between two + // facets instead of facet normals. + const double cos_tol = cos(igl::PI - tol); + std::vector result; // Indices into uE + + auto is_non_manifold = [&uE2E](size_t ei) -> bool { + return uE2E[ei].size() > 2; + }; + + auto is_boundary = [&uE2E](size_t ei) -> bool { + return uE2E[ei].size() == 1; + }; + + auto opposite_vertex = [&uE, &F](size_t ei, size_t fi) -> IndexType { + const size_t v0 = uE(ei, 0); + const size_t v1 = uE(ei, 1); + for (size_t i=0; i<3; i++) { + const size_t v = F(fi, i); + if (v != v0 && v != v1) { return v; } + } + throw "Input face must be topologically degenerate!"; + }; + + auto is_feature = [&V, &F, &uE, &uE2E, &opposite_vertex, num_faces]( + size_t ei, double cos_tol) -> bool { + auto adj_faces = uE2E[ei]; + assert(adj_faces.size() == 2); + const Vertex v0 = V.row(uE(ei, 0)); + const Vertex v1 = V.row(uE(ei, 1)); + const Vertex v2 = V.row(opposite_vertex(ei, adj_faces[0] % num_faces)); + const Vertex v3 = V.row(opposite_vertex(ei, adj_faces[1] % num_faces)); + const Point p0(v0[0], v0[1], v0[2]); + const Point p1(v1[0], v1[1], v1[2]); + const Point p2(v2[0], v2[1], v2[2]); + const Point p3(v3[0], v3[1], v3[2]); + const auto ori = CGAL::orientation(p0, p1, p2, p3); + switch (ori) { + case CGAL::POSITIVE: + return CGAL::compare_dihedral_angle(p0, p1, p2, p3, cos_tol) == + CGAL::SMALLER; + case CGAL::NEGATIVE: + return CGAL::compare_dihedral_angle(p0, p1, p3, p2, cos_tol) == + CGAL::SMALLER; + case CGAL::COPLANAR: + if (!CGAL::collinear(p0, p1, p2) && !CGAL::collinear(p0, p1, p3)) { + return CGAL::compare_dihedral_angle(p0, p1, p2, p3, cos_tol) == + CGAL::SMALLER; + } else { + throw "Dihedral angle (and feature edge) is not well defined for" + " degenerate triangles!"; + } + default: + throw "Unknown CGAL orientation"; + } + }; + + for (size_t i=0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COPYLEFT_CGAL_EXTRACT_FEATURE_H +#define IGL_COPYLEFT_CGAL_EXTRACT_FEATURE_H +#include "../../igl_inline.h" +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Extract feature edges based on dihedral angle. + // Here, dihedral angle is defined as the angle between surface + // __normals__ as described in + // http://mathworld.wolfram.com/DihedralAngle.html + // + // Non-manifold and boundary edges are automatically considered as + // features. + // + // Inputs: + // V #V by 3 array of vertices. + // F #F by 3 array of faces. + // tol Edges with dihedral angle larger than this are considered + // as features. Angle is measured in radian. + // + // Output: + // feature_edges: #E by 2 array of edges. Each edge satisfies at + // least one of the following criteria: + // + // * Edge has dihedral angle larger than tol. + // * Edge is boundary. + // * Edge is non-manifold (i.e. it has more than 2 adjacent + // faces). + template < + typename DerivedV, + typename DerivedF, + typename DerivedE> + IGL_INLINE void extract_feature( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const double tol, + Eigen::PlainObjectBase& feature_edges); + + // Inputs: + // V #V by 3 array of vertices. + // F #F by 3 array of faces. + // tol Edges with dihedral angle larger than this are considered + // as features. Angle is measured in radian. + // E #E by 2 array of directed edges. + // uE #uE by 2 array of undirected edges. + // uE2E #uE list of lists mapping undirected edges to all corresponding + // directed edges. + // + // Output: + // feature_edges: #E by 2 array of edges. Each edge satisfies at + // least one of the following criteria: + // + // * Edge has dihedral angle larger than tol. + // * Edge is boundary. + // * Edge is non-manifold (i.e. it has more than 2 adjacent + // faces). + template < + typename DerivedV, + typename DerivedF, + typename DerivedE> + IGL_INLINE void extract_feature( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const double tol, + const Eigen::PlainObjectBase& E, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + Eigen::PlainObjectBase& feature_edges); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "extract_feature.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/fast_winding_number.cpp b/src/igl/copyleft/cgal/fast_winding_number.cpp new file mode 100644 index 0000000000..682754a86b --- /dev/null +++ b/src/igl/copyleft/cgal/fast_winding_number.cpp @@ -0,0 +1,82 @@ +#include "../../fast_winding_number.h" +#include "../../octree.h" +#include "../../knn.h" +#include "../../parallel_for.h" +#include "point_areas.h" +#include + +namespace igl { + namespace copyleft{ + namespace cgal{ + template + IGL_INLINE void fast_winding_number(const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& Q, + const int expansion_order, + const BetaType beta, + Eigen::PlainObjectBase& WN + ){ + typedef typename DerivedWN::Scalar real; + typedef typename Eigen::Matrix + RealMatrix; + + std::vector > point_indices; + Eigen::Matrix CH; + Eigen::Matrix CN; + Eigen::Matrix W; + Eigen::MatrixXi I; + Eigen::Matrix A; + + octree(P,point_indices,CH,CN,W); + knn(P,21,point_indices,CH,CN,W,I); + point_areas(P,I,N,A); + + Eigen::Matrix EC; + Eigen::Matrix CM; + Eigen::Matrix R; + + igl::fast_winding_number(P,N,A,point_indices,CH, + expansion_order,CM,R,EC); + igl::fast_winding_number(P,N,A,point_indices,CH, + CM,R,EC,Q,beta,WN); + } + + template + IGL_INLINE void fast_winding_number( + const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& Q, + Eigen::PlainObjectBase& WN + ){ + fast_winding_number(P,N,Q,2,2.0,WN); + } + } + } +} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/igl/copyleft/cgal/fast_winding_number.h b/src/igl/copyleft/cgal/fast_winding_number.h new file mode 100644 index 0000000000..a4ab153165 --- /dev/null +++ b/src/igl/copyleft/cgal/fast_winding_number.h @@ -0,0 +1,66 @@ +#ifndef IGL_COPYLEFT_CGAL_FAST_WINDING_NUMBER +#define IGL_COPYLEFT_CGAL_FAST_WINDING_NUMBER +#include "../../igl_inline.h" +#include +#include +namespace igl +{ + // Evaluate the fast winding number for point data, without known areas. The + // areas are calculated using igl::knn and igl::copyleft::cgal::point_areas. + // + // This function performes the precomputation and evaluation all in one. + // If you need to acess the precomuptation for repeated evaluations, use the + // two functions designed for exposed precomputation, which are the first two + // functions see in igl/fast_winding_number.h + // + // Inputs: + // P #P by 3 list of point locations + // N #P by 3 list of point normals + // Q #Q by 3 list of query points for the winding number + // beta This is a Barnes-Hut style accuracy term that separates near feild + // from far field. The higher the beta, the more accurate and slower + // the evaluation. We reccommend using a beta value of 2. + // expansion_order the order of the taylor expansion. We support 0,1,2. + // Outputs: + // WN #Q by 1 list of windinng number values at each query point + // + template + IGL_INLINE void fast_winding_number(const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& Q, + const int expansion_order, + const BetaType beta, + Eigen::PlainObjectBase& WN + ); + + // Evaluate the fast winding number for point data, without known areas. The + // areas are calculated using igl::knn and + // igl::point_areas. This function uses the default expansion + // order and beta (both are set to 2). + // + // This function performes the precomputation and evaluation all in one. + // If you need to acess the precomuptation for repeated evaluations, use the + // two functions designed for exposed precomputation (described above). + + // Inputs: + // P #P by 3 list of point locations + // N #P by 3 list of point normals + // Q #Q by 3 list of query points for the winding number + // Outputs: + // WN #Q by 1 list of windinng number values at each query point + // + template + IGL_INLINE void fast_winding_number(const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& Q, + Eigen::PlainObjectBase& WN + ); +} +#ifndef IGL_STATIC_LIBRARY +# include "fast_winding_number.cpp" +#endif + +#endif + diff --git a/src/igl/copyleft/cgal/half_space_box.cpp b/src/igl/copyleft/cgal/half_space_box.cpp new file mode 100644 index 0000000000..1bfa8ea52a --- /dev/null +++ b/src/igl/copyleft/cgal/half_space_box.cpp @@ -0,0 +1,123 @@ +#include "half_space_box.h" +#include "assign_scalar.h" +#include +#include + +template +IGL_INLINE void igl::copyleft::cgal::half_space_box( + const CGAL::Plane_3 & P, + const Eigen::MatrixBase & V, + Eigen::Matrix & BV, + Eigen::Matrix & BF) +{ + typedef CGAL::Plane_3 Plane; + typedef CGAL::Point_3 Point; + typedef CGAL::Vector_3 Vector; + typedef CGAL::Epeck::FT EScalar; + Eigen::Matrix avg(0,0,0); + for(int v = 0;v vBV;vBV.reserve(8); + Vector v = CGAL::cross_product(u,n); + // Scale u,v,n to be longer than bbd + const auto & longer_than = [](const EScalar min_sqr, Vector & x) + { + assert(x.squared_length() > 0); + while(x.squared_length() < min_sqr) + { + x = 2.*x; + } + }; + longer_than(bbd*bbd,u); + longer_than(bbd*bbd,v); + longer_than(bbd*bbd,n); + vBV.emplace_back( o2 + u + v); + vBV.emplace_back( o2 - u + v); + vBV.emplace_back( o2 - u - v); + vBV.emplace_back( o2 + u - v); + vBV.emplace_back( o2 + u + v - n); + vBV.emplace_back( o2 - u + v - n); + vBV.emplace_back( o2 - u - v - n); + vBV.emplace_back( o2 + u - v - n); + BV.resize(8,3); + for(int b = 0;b<8;b++) + { + igl::copyleft::cgal::assign_scalar(vBV[b].x(),BV(b,0)); + igl::copyleft::cgal::assign_scalar(vBV[b].y(),BV(b,1)); + igl::copyleft::cgal::assign_scalar(vBV[b].z(),BV(b,2)); + } + BF.resize(12,3); + BF<< + 1,0,2, + 2,0,3, + 4,5,6, + 4,6,7, + 0,1,4, + 4,1,5, + 1,2,5, + 5,2,6, + 2,3,6, + 6,3,7, + 3,0,7, + 7,0,4; +} + +template +IGL_INLINE void igl::copyleft::cgal::half_space_box( + const Eigen::MatrixBase & p, + const Eigen::MatrixBase & n, + const Eigen::MatrixBase & V, + Eigen::Matrix & BV, + Eigen::Matrix & BF) +{ + typedef CGAL::Plane_3 Plane; + typedef CGAL::Point_3 Point; + typedef CGAL::Vector_3 Vector; + Plane P(Point(p(0),p(1),p(2)),Vector(n(0),n(1),n(2))); + return half_space_box(P,V,BV,BF); +} + +template +IGL_INLINE void igl::copyleft::cgal::half_space_box( + const Eigen::MatrixBase & equ, + const Eigen::MatrixBase & V, + Eigen::Matrix & BV, + Eigen::Matrix & BF) +{ + typedef CGAL::Plane_3 Plane; + Plane P(equ(0),equ(1),equ(2),equ(3)); + return half_space_box(P,V,BV,BF); +} +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::cgal::half_space_box >(CGAL::Plane_3 const&, Eigen::MatrixBase > const&, Eigen::Matrix, 8, 3, 0, 8, 3>&, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::half_space_box >(CGAL::Plane_3 const&, Eigen::MatrixBase > const&, Eigen::Matrix, 8, 3, 0, 8, 3>&, Eigen::Matrix&); +template void igl::copyleft::cgal::half_space_box, 1, 4, 1, 1, 4>, Eigen::Matrix, -1, 3, 0, -1, 3> >(Eigen::MatrixBase, 1, 4, 1, 1, 4> > const&, Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::Matrix, 8, 3, 0, 8, 3>&, Eigen::Matrix&); +template void igl::copyleft::cgal::half_space_box, 1, 4, 1, 1, 4>, Eigen::Matrix, -1, 4, 0, -1, 4> >(Eigen::MatrixBase, 1, 4, 1, 1, 4> > const&, Eigen::MatrixBase, -1, 4, 0, -1, 4> > const&, Eigen::Matrix, 8, 3, 0, 8, 3>&, Eigen::Matrix&); +template void igl::copyleft::cgal::half_space_box, 1, 4, 1, 1, 4>, Eigen::Matrix >(Eigen::MatrixBase, 1, 4, 1, 1, 4> > const&, Eigen::MatrixBase > const&, Eigen::Matrix, 8, 3, 0, 8, 3>&, Eigen::Matrix&); +template void igl::copyleft::cgal::half_space_box, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix, 8, 3, 0, 8, 3>&, Eigen::Matrix&); +#endif diff --git a/src/igl/copyleft/cgal/half_space_box.h b/src/igl/copyleft/cgal/half_space_box.h new file mode 100644 index 0000000000..275502235c --- /dev/null +++ b/src/igl/copyleft/cgal/half_space_box.h @@ -0,0 +1,63 @@ +#ifndef IGL_COPYLEFT_CGAL_HALF_SPACE_BOX_H +#define IGL_COPYLEFT_CGAL_HALF_SPACE_BOX_H +#include "../../igl_inline.h" +#include +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Construct a mesh of box (BV,BF) so that it contains the intersection of + // the half-space under the plane (P) and the bounding box of V, and does not + // contain any of the half-space above (P). + // + // Inputs: + // P plane so that normal points away from half-space + // V #V by 3 list of vertex positions + // Outputs: + // BV #BV by 3 list of box vertex positions + // BF #BF b3 list of box triangle indices into BV + template + IGL_INLINE void half_space_box( + const CGAL::Plane_3 & P, + const Eigen::MatrixBase & V, + Eigen::Matrix & BV, + Eigen::Matrix & BF); + // Inputs: + // p 3d point on plane + // n 3d vector of normal of plane pointing away from inside + // V #V by 3 list of vertex positions + // Outputs: + // BV #BV by 3 list of box vertex positions + // BF #BF b3 list of box triangle indices into BV + template + IGL_INLINE void half_space_box( + const Eigen::MatrixBase & p, + const Eigen::MatrixBase & n, + const Eigen::MatrixBase & V, + Eigen::Matrix & BV, + Eigen::Matrix & BF); + // Inputs: + // equ plane equation: a*x+b*y+c*z + d = 0 + // V #V by 3 list of vertex positions + // Outputs: + // BV #BV by 3 list of box vertex positions + // BF #BF b3 list of box triangle indices into BV + template + IGL_INLINE void half_space_box( + const Eigen::MatrixBase & equ, + const Eigen::MatrixBase & V, + Eigen::Matrix & BV, + Eigen::Matrix & BF); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "half_space_box.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/hausdorff.cpp b/src/igl/copyleft/cgal/hausdorff.cpp new file mode 100644 index 0000000000..382e9b4765 --- /dev/null +++ b/src/igl/copyleft/cgal/hausdorff.cpp @@ -0,0 +1,39 @@ +#include "hausdorff.h" +#include "../../hausdorff.h" +#include + +template < + typename DerivedV, + typename Kernel, + typename Scalar> +IGL_INLINE void igl::copyleft::cgal::hausdorff( + const Eigen::MatrixBase& V, + const CGAL::AABB_tree< + CGAL::AABB_traits >::iterator + > + > + > & treeB, + const std::vector > & /*TB*/, + Scalar & l, + Scalar & u) +{ + // Not sure why using `auto` here doesn't work with the `hausdorff` function + // parameter but explicitly naming the type does... + const std::function + dist_to_B = [&treeB]( + const double & x, const double & y, const double & z)->double + { + CGAL::Point_3 query(x,y,z); + typename CGAL::AABB_tree< + CGAL::AABB_traits >::iterator + > + > + >::Point_and_primitive_id pp = treeB.closest_point_and_primitive(query); + return std::sqrt((query-pp.first).squared_length()); + }; + return igl::hausdorff(V,dist_to_B,l,u); +} diff --git a/src/igl/copyleft/cgal/hausdorff.h b/src/igl/copyleft/cgal/hausdorff.h new file mode 100644 index 0000000000..c377d4576c --- /dev/null +++ b/src/igl/copyleft/cgal/hausdorff.h @@ -0,0 +1,62 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_HAUSDORFF_H +#define IGL_COPYLEFT_CGAL_HAUSDORFF_H +#include "../../igl_inline.h" + +#include +#include "CGAL_includes.hpp" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Compute lower and upper bounds (l,u) on the Hausdorff distance between a triangle + // (V) and a pointset (e.g., mesh, triangle soup) given by a distance function + // handle (dist_to_B). + // + // Inputs: + // V 3 by 3 list of corner positions so that V.row(i) is the position of the + // ith corner + // treeB CGAL's AABB tree containing triangle soup (VB,FB) + // TB list of CGAL triangles in order of FB (for determining which was found + // in computation) + // Outputs: + // l lower bound on Hausdorff distance + // u upper bound on Hausdorff distance + // + template < + typename DerivedV, + typename Kernel, + typename Scalar> + IGL_INLINE void hausdorff( + const Eigen::MatrixBase& V, + const CGAL::AABB_tree< + CGAL::AABB_traits >::iterator + > + > + > & treeB, + const std::vector > & TB, + Scalar & l, + Scalar & u); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "hausdorff.cpp" +#endif + +#endif + + diff --git a/src/igl/copyleft/cgal/incircle.cpp b/src/igl/copyleft/cgal/incircle.cpp new file mode 100644 index 0000000000..d129d3b14f --- /dev/null +++ b/src/igl/copyleft/cgal/incircle.cpp @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "incircle.h" +#include +#include + +template +IGL_INLINE short igl::copyleft::cgal::incircle( + const Scalar pa[2], + const Scalar pb[2], + const Scalar pc[2], + const Scalar pd[2]) +{ + typedef CGAL::Exact_predicates_exact_constructions_kernel Epeck; + typedef CGAL::Exact_predicates_inexact_constructions_kernel Epick; + typedef typename std::conditional::value, + Epeck, Epick>::type Kernel; + + switch(CGAL::side_of_oriented_circle( + typename Kernel::Point_2(pa[0], pa[1]), + typename Kernel::Point_2(pb[0], pb[1]), + typename Kernel::Point_2(pc[0], pc[1]), + typename Kernel::Point_2(pd[0], pd[1]))) { + case CGAL::ON_POSITIVE_SIDE: + return 1; + case CGAL::ON_NEGATIVE_SIDE: + return -1; + case CGAL::ON_ORIENTED_BOUNDARY: + return 0; + default: + throw "Invalid incircle result"; + } +} diff --git a/src/igl/copyleft/cgal/incircle.h b/src/igl/copyleft/cgal/incircle.h new file mode 100644 index 0000000000..4088dc9b58 --- /dev/null +++ b/src/igl/copyleft/cgal/incircle.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COPYLEFT_CGAL_INCIRCLE_H +#define IGL_COPYLEFT_CGAL_INCIRCLE_H + +#include "../../igl_inline.h" + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Inputs: + // pa,pb,pc,pd 2D points. + // Output: + // 1 if pd is inside of the oriented circle formed by pa,pb,pc. + // 0 if pd is co-circular with pa,pb,pc. + // -1 if pd is outside of the oriented circle formed by pa,pb,pc. + template + IGL_INLINE short incircle( + const Scalar pa[2], + const Scalar pb[2], + const Scalar pc[2], + const Scalar pd[2]); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "incircle.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/insert_into_cdt.cpp b/src/igl/copyleft/cgal/insert_into_cdt.cpp new file mode 100644 index 0000000000..2ea4e6de16 --- /dev/null +++ b/src/igl/copyleft/cgal/insert_into_cdt.cpp @@ -0,0 +1,68 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "insert_into_cdt.h" +#include +#include +#include + +template +IGL_INLINE void igl::copyleft::cgal::insert_into_cdt( + const CGAL::Object & obj, + const CGAL::Plane_3 & P, + CGAL::Constrained_triangulation_plus_2< + CGAL::Constrained_Delaunay_triangulation_2< + Kernel, + CGAL::Triangulation_data_structure_2< + CGAL::Triangulation_vertex_base_2, + CGAL::Constrained_triangulation_face_base_2< Kernel> + >, + CGAL::Exact_intersections_tag + > + > + & cdt) +{ + typedef CGAL::Point_3 Point_3; + typedef CGAL::Segment_3 Segment_3; + typedef CGAL::Triangle_3 Triangle_3; + + if(const Segment_3 *iseg = CGAL::object_cast(&obj)) + { + // Add segment constraint + cdt.insert_constraint( P.to_2d(iseg->vertex(0)),P.to_2d(iseg->vertex(1))); + }else if(const Point_3 *ipoint = CGAL::object_cast(&obj)) + { + // Add point + cdt.insert(P.to_2d(*ipoint)); + } else if(const Triangle_3 *itri = CGAL::object_cast(&obj)) + { + // Add 3 segment constraints + cdt.insert_constraint( P.to_2d(itri->vertex(0)),P.to_2d(itri->vertex(1))); + cdt.insert_constraint( P.to_2d(itri->vertex(1)),P.to_2d(itri->vertex(2))); + cdt.insert_constraint( P.to_2d(itri->vertex(2)),P.to_2d(itri->vertex(0))); + } else if(const std::vector *polyp = + CGAL::object_cast< std::vector >(&obj)) + { + const std::vector & poly = *polyp; + const size_t m = poly.size(); + assert(m>=2); + for(size_t p = 0;p +template void igl::copyleft::cgal::insert_into_cdt(CGAL::Object const&, CGAL::Plane_3 const&, CGAL::Constrained_triangulation_plus_2 >, CGAL::Constrained_triangulation_face_base_2 > > >, CGAL::Exact_intersections_tag> >&); +template void igl::copyleft::cgal::insert_into_cdt(CGAL::Object const&, CGAL::Plane_3 const&, CGAL::Constrained_triangulation_plus_2 >, CGAL::Constrained_triangulation_face_base_2 > > >, CGAL::Exact_intersections_tag> >&); +#endif diff --git a/src/igl/copyleft/cgal/insert_into_cdt.h b/src/igl/copyleft/cgal/insert_into_cdt.h new file mode 100644 index 0000000000..afca0233f4 --- /dev/null +++ b/src/igl/copyleft/cgal/insert_into_cdt.h @@ -0,0 +1,60 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_INSERT_INTO_CDT_H +#define IGL_COPYLEFT_CGAL_INSERT_INTO_CDT_H +#include "../../igl_inline.h" + +#include // Workaround https://github.com/CGAL/cgal/issues/2182 with CGAL 4.10-1 +#include +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Given a current 2D constrained Delaunay triangulation (cdt), insert a + // 3D "object" (e.g., resulting from intersecting two triangles) into the + // cdt, by projecting it via the given plane (P) and adding appropriate + // constraints. + // + // Inputs: + // obj CGAL::Object representing a vertex, segment, or (convex) + // polygon. All vertices should lie on the plane P. If not, then this + // adds the _projection_ of this object to the cdt and that might not + // be what you wanted to do. + // P plane obj lies on and upon which the cdt is being performed + // cdt current CDT, see output + // Outputs: + // cdt CDT updated to contain constraints for the given object + // + template + IGL_INLINE void insert_into_cdt( + const CGAL::Object & obj, + const CGAL::Plane_3 & P, + CGAL::Constrained_triangulation_plus_2< + CGAL::Constrained_Delaunay_triangulation_2< + Kernel, + CGAL::Triangulation_data_structure_2< + CGAL::Triangulation_vertex_base_2, + CGAL::Constrained_triangulation_face_base_2< Kernel> + >, + CGAL::Exact_intersections_tag + > + > + & cdt); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "insert_into_cdt.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/insphere.cpp b/src/igl/copyleft/cgal/insphere.cpp new file mode 100644 index 0000000000..db12fea33a --- /dev/null +++ b/src/igl/copyleft/cgal/insphere.cpp @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "insphere.h" +#include +#include + +template +IGL_INLINE short igl::copyleft::cgal::insphere( + const Scalar pa[3], + const Scalar pb[3], + const Scalar pc[3], + const Scalar pd[3], + const Scalar pe[3]) +{ + typedef CGAL::Exact_predicates_exact_constructions_kernel Epeck; + typedef CGAL::Exact_predicates_inexact_constructions_kernel Epick; + typedef typename std::conditional::value, + Epeck, Epick>::type Kernel; + + switch(CGAL::side_of_oriented_sphere( + typename Kernel::Point_3(pa[0], pa[1], pa[2]), + typename Kernel::Point_3(pb[0], pb[1], pb[2]), + typename Kernel::Point_3(pc[0], pc[1], pc[2]), + typename Kernel::Point_3(pd[0], pd[1], pd[2]), + typename Kernel::Point_3(pe[0], pe[1], pe[2]))) { + case CGAL::ON_POSITIVE_SIDE: + return 1; + case CGAL::ON_NEGATIVE_SIDE: + return -1; + case CGAL::ON_ORIENTED_BOUNDARY: + return 0; + default: + throw "Invalid incircle result"; + } +} diff --git a/src/igl/copyleft/cgal/insphere.h b/src/igl/copyleft/cgal/insphere.h new file mode 100644 index 0000000000..88a9266683 --- /dev/null +++ b/src/igl/copyleft/cgal/insphere.h @@ -0,0 +1,40 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COPYLEFT_CGAL_INSPHERE_H +#define IGL_COPYLEFT_CGAL_INSPHERE_H + +#include "../../igl_inline.h" + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Inputs: + // pa,pb,pc,pd,pe 3D points. + // Output: + // 1 if pe is inside of the oriented sphere formed by pa,pb,pc,pd. + // 0 if pe is co-spherical with pa,pb,pc,pd. + // -1 if pe is outside of the oriented sphere formed by pa,pb,pc,pd. + template + IGL_INLINE short insphere( + const Scalar pa[3], + const Scalar pb[3], + const Scalar pc[3], + const Scalar pd[3], + const Scalar pe[3]); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "insphere.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/intersect_other.cpp b/src/igl/copyleft/cgal/intersect_other.cpp new file mode 100644 index 0000000000..9f984f956a --- /dev/null +++ b/src/igl/copyleft/cgal/intersect_other.cpp @@ -0,0 +1,289 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "intersect_other.h" +#include "CGAL_includes.hpp" +#include "mesh_to_cgal_triangle_list.h" +#include "remesh_intersections.h" +#include "../../slice_mask.h" +#include "../../remove_unreferenced.h" + +#ifndef IGL_FIRST_HIT_EXCEPTION +#define IGL_FIRST_HIT_EXCEPTION 10 +#endif + +// Un-exposed helper functions +namespace igl +{ + namespace copyleft + { + namespace cgal + { + template + static IGL_INLINE void push_result( + const Eigen::PlainObjectBase & F, + const int f, + const int f_other, + const CGAL::Object & result, + std::map< + typename DerivedF::Index, + std::vector > > & + offending) + //std::map< + // std::pair, + // std::vector > & edge2faces) + { + typedef typename DerivedF::Index Index; + typedef std::pair EMK; + if(offending.count(f) == 0) + { + // first time marking, initialize with new id and empty list + offending[f] = {}; + for(Index e = 0; e<3;e++) + { + // append face to edge's list + Index i = F(f,(e+1)%3) < F(f,(e+2)%3) ? F(f,(e+1)%3) : F(f,(e+2)%3); + Index j = F(f,(e+1)%3) < F(f,(e+2)%3) ? F(f,(e+2)%3) : F(f,(e+1)%3); + //edge2faces[EMK(i,j)].push_back(f); + } + } + offending[f].push_back({f_other,result}); + } + template < + typename Kernel, + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedIF, + typename DerivedVVAB, + typename DerivedFFAB, + typename DerivedJAB, + typename DerivedIMAB> + static IGL_INLINE bool intersect_other_helper( + const Eigen::PlainObjectBase & VA, + const Eigen::PlainObjectBase & FA, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + const RemeshSelfIntersectionsParam & params, + Eigen::PlainObjectBase & IF, + Eigen::PlainObjectBase & VVAB, + Eigen::PlainObjectBase & FFAB, + Eigen::PlainObjectBase & JAB, + Eigen::PlainObjectBase & IMAB) + { + + using namespace std; + using namespace Eigen; + + typedef typename DerivedFA::Index Index; + // 3D Primitives + typedef CGAL::Point_3 Point_3; + typedef CGAL::Segment_3 Segment_3; + typedef CGAL::Triangle_3 Triangle_3; + typedef CGAL::Plane_3 Plane_3; + typedef CGAL::Tetrahedron_3 Tetrahedron_3; + // 2D Primitives + typedef CGAL::Point_2 Point_2; + typedef CGAL::Segment_2 Segment_2; + typedef CGAL::Triangle_2 Triangle_2; + // 2D Constrained Delaunay Triangulation types + typedef CGAL::Triangulation_vertex_base_2 TVB_2; + typedef CGAL::Constrained_triangulation_face_base_2 CTAB_2; + typedef CGAL::Triangulation_data_structure_2 TDS_2; + typedef CGAL::Exact_intersections_tag Itag; + // Axis-align boxes for all-pairs self-intersection detection + typedef std::vector Triangles; + typedef typename Triangles::iterator TrianglesIterator; + typedef typename Triangles::const_iterator TrianglesConstIterator; + typedef + CGAL::Box_intersection_d::Box_with_handle_d + Box; + typedef + std::map > > + OffendingMap; + typedef std::map,std::vector > EdgeMap; + typedef std::pair EMK; + + Triangles TA,TB; + // Compute and process self intersections + mesh_to_cgal_triangle_list(VA,FA,TA); + mesh_to_cgal_triangle_list(VB,FB,TB); + // http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Box_intersection_d/Chapter_main.html#Section_63.5 + // Create the corresponding vector of bounding boxes + std::vector A_boxes,B_boxes; + const auto box_up = [](Triangles & T, std::vector & boxes) -> void + { + boxes.reserve(T.size()); + for ( + TrianglesIterator tit = T.begin(); + tit != T.end(); + ++tit) + { + boxes.push_back(Box(tit->bbox(), tit)); + } + }; + box_up(TA,A_boxes); + box_up(TB,B_boxes); + OffendingMap offendingA,offendingB; + //EdgeMap edge2facesA,edge2facesB; + + std::list lIF; + const auto cb = [&](const Box &a, const Box &b) -> void + { + using namespace std; + // index in F and T + int fa = a.handle()-TA.begin(); + int fb = b.handle()-TB.begin(); + const Triangle_3 & A = *a.handle(); + const Triangle_3 & B = *b.handle(); + if(CGAL::do_intersect(A,B)) + { + // There was an intersection + lIF.push_back(fa); + lIF.push_back(fb); + if(params.first_only) + { + throw IGL_FIRST_HIT_EXCEPTION; + } + if(!params.detect_only) + { + CGAL::Object result = CGAL::intersection(A,B); + + push_result(FA,fa,fb,result,offendingA); + push_result(FB,fb,fa,result,offendingB); + } + } + }; + try{ + CGAL::box_intersection_d( + A_boxes.begin(), A_boxes.end(), + B_boxes.begin(), B_boxes.end(), + cb); + }catch(int e) + { + // Rethrow if not FIRST_HIT_EXCEPTION + if(e != IGL_FIRST_HIT_EXCEPTION) + { + throw e; + } + // Otherwise just fall through + } + + // Convert lIF to Eigen matrix + assert(lIF.size()%2 == 0); + IF.resize(lIF.size()/2,2); + { + int i=0; + for( + list::const_iterator ifit = lIF.begin(); + ifit!=lIF.end(); + ) + { + IF(i,0) = (*ifit); + ifit++; + IF(i,1) = (*ifit); + ifit++; + i++; + } + } + if(!params.detect_only) + { + // Obsolete, now remesh_intersections expects a single mesh + // remesh_intersections(VA,FA,TA,offendingA,VVA,FFA,JA,IMA); + // remesh_intersections(VB,FB,TB,offendingB,VVB,FFB,JB,IMB); + // Combine mesh and offending maps + DerivedVA VAB(VA.rows()+VB.rows(),3); + VAB< 0; + } + } + } +} + +template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedIF, + typename DerivedVVAB, + typename DerivedFFAB, + typename DerivedJAB, + typename DerivedIMAB> +IGL_INLINE bool igl::copyleft::cgal::intersect_other( + const Eigen::PlainObjectBase & VA, + const Eigen::PlainObjectBase & FA, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + const RemeshSelfIntersectionsParam & params, + Eigen::PlainObjectBase & IF, + Eigen::PlainObjectBase & VVAB, + Eigen::PlainObjectBase & FFAB, + Eigen::PlainObjectBase & JAB, + Eigen::PlainObjectBase & IMAB) +{ + if(params.detect_only) + { + return intersect_other_helper + (VA,FA,VB,FB,params,IF,VVAB,FFAB,JAB,IMAB); + }else + { + return intersect_other_helper + (VA,FA,VB,FB,params,IF,VVAB,FFAB,JAB,IMAB); + } +} + +IGL_INLINE bool igl::copyleft::cgal::intersect_other( + const Eigen::MatrixXd & VA, + const Eigen::MatrixXi & FA, + const Eigen::MatrixXd & VB, + const Eigen::MatrixXi & FB, + const bool first_only, + Eigen::MatrixXi & IF) +{ + Eigen::MatrixXd VVAB; + Eigen::MatrixXi FFAB; + Eigen::VectorXi JAB,IMAB; + return intersect_other( + VA,FA,VB,FB,{true,first_only},IF,VVAB,FFAB,JAB,IMAB); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::intersect_other, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::intersect_other, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/intersect_other.h b/src/igl/copyleft/cgal/intersect_other.h new file mode 100644 index 0000000000..c1afdf87db --- /dev/null +++ b/src/igl/copyleft/cgal/intersect_other.h @@ -0,0 +1,91 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_INTERSECT_OTHER_H +#define IGL_COPYLEFT_CGAL_INTERSECT_OTHER_H +#include "../../igl_inline.h" +#include "RemeshSelfIntersectionsParam.h" + +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // INTERSECT_OTHER Given a triangle mesh (VA,FA) and another mesh (VB,FB) + // find all pairs of intersecting faces. Note that self-intersections are + // ignored. + // + // Inputs: + // VA #V by 3 list of vertex positions + // FA #F by 3 list of triangle indices into VA + // VB #V by 3 list of vertex positions + // FB #F by 3 list of triangle indices into VB + // params whether to detect only and then whether to only find first + // intersection + // Outputs: + // IF #intersecting face pairs by 2 list of intersecting face pairs, + // indexing FA and FB + // VVAB #VVAB by 3 list of vertex positions + // FFAB #FFAB by 3 list of triangle indices into VVA + // JAB #FFAB list of indices into [FA;FB] denoting birth triangle + // IMAB #VVAB list of indices stitching duplicates (resulting from + // mesh intersections) together + template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedIF, + typename DerivedVVAB, + typename DerivedFFAB, + typename DerivedJAB, + typename DerivedIMAB> + IGL_INLINE bool intersect_other( + const Eigen::PlainObjectBase & VA, + const Eigen::PlainObjectBase & FA, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + const RemeshSelfIntersectionsParam & params, + Eigen::PlainObjectBase & IF, + Eigen::PlainObjectBase & VVAB, + Eigen::PlainObjectBase & FFAB, + Eigen::PlainObjectBase & JAB, + Eigen::PlainObjectBase & IMAB); + // Legacy wrapper for detect only using common types. + // + // Inputs: + // VA #V by 3 list of vertex positions + // FA #F by 3 list of triangle indices into VA + // VB #V by 3 list of vertex positions + // FB #F by 3 list of triangle indices into VB + // first_only whether to only detect the first intersection. + // Outputs: + // IF #intersecting face pairs by 2 list of intersecting face pairs, + // indexing FA and FB + // Returns true if any intersections were found + // + // See also: remesh_self_intersections + IGL_INLINE bool intersect_other( + const Eigen::MatrixXd & VA, + const Eigen::MatrixXi & FA, + const Eigen::MatrixXd & VB, + const Eigen::MatrixXi & FB, + const bool first_only, + Eigen::MatrixXi & IF); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "intersect_other.cpp" +#endif + +#endif + diff --git a/src/igl/copyleft/cgal/intersect_with_half_space.cpp b/src/igl/copyleft/cgal/intersect_with_half_space.cpp new file mode 100644 index 0000000000..c4ba4dbb8f --- /dev/null +++ b/src/igl/copyleft/cgal/intersect_with_half_space.cpp @@ -0,0 +1,88 @@ +#include "intersect_with_half_space.h" +#include "mesh_boolean.h" +#include "half_space_box.h" + +template < + typename DerivedV, + typename DerivedF, + typename Derivedp, + typename Derivedn, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> +IGL_INLINE bool igl::copyleft::cgal::intersect_with_half_space( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & p, + const Eigen::MatrixBase & n, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J) +{ + typedef CGAL::Plane_3 Plane; + typedef CGAL::Point_3 Point; + typedef CGAL::Vector_3 Vector; + Plane P(Point(p(0),p(1),p(2)),Vector(n(0),n(1),n(2))); + return intersect_with_half_space(V,F,P,VC,FC,J); +} + +template < + typename DerivedV, + typename DerivedF, + typename Derivedequ, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> +IGL_INLINE bool igl::copyleft::cgal::intersect_with_half_space( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & equ, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J) +{ + typedef CGAL::Plane_3 Plane; + Plane P(equ(0),equ(1),equ(2),equ(3)); + return intersect_with_half_space(V,F,P,VC,FC,J); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> +IGL_INLINE bool igl::copyleft::cgal::intersect_with_half_space( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const CGAL::Plane_3 & P, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J) +{ + Eigen::Matrix BV; + Eigen::Matrix BF; + half_space_box(P,V,BV,BF); + // Disturbingly, (BV,BF) must be first argument + const bool ret = mesh_boolean(BV,BF,V,F,MESH_BOOLEAN_TYPE_INTERSECT,VC,FC,J); + // But now J is wrong... + std::for_each( + J.data(), + J.data()+J.size(), + [&BF,&F](typename DerivedJ::Scalar & j) + {j = (j, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::intersect_with_half_space, Eigen::Matrix, Eigen::Matrix, Eigen::CwiseUnaryOp, Eigen::Matrix const>, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, Eigen::Matrix const> > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::intersect_with_half_space, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::intersect_with_half_space, Eigen::Matrix, Eigen::Matrix, Eigen::CwiseUnaryOp, Eigen::Matrix const>, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, Eigen::Matrix const> > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/intersect_with_half_space.h b/src/igl/copyleft/cgal/intersect_with_half_space.h new file mode 100644 index 0000000000..9fdf9ae2bd --- /dev/null +++ b/src/igl/copyleft/cgal/intersect_with_half_space.h @@ -0,0 +1,96 @@ +#ifndef IGL_COPYLEFT_CGAL_INTERSECT_WITH_HALF_SPACE_H +#define IGL_COPYLEFT_CGAL_INTERSECT_WITH_HALF_SPACE_H +#include "../../igl_inline.h" +#include +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Intersect a PWN mesh with a half-space. Point on plane, normal pointing + // outward. + // + // Inputs: + // V #V by 3 list of mesh vertex positions + // p 3d point on plane + // n 3d vector of normal of plane pointing away from inside + // Outputs: + // VC #VC by 3 list of vertex positions of boolean result mesh + // FC #FC by 3 list of triangle indices into VC + // J #FC list of indices into [F;F.rows()+[1;2]] revealing "birth" + // facet + template < + typename DerivedV, + typename DerivedF, + typename Derivedp, + typename Derivedn, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> + IGL_INLINE bool intersect_with_half_space( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & p, + const Eigen::MatrixBase & n, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J); + // Intersect a PWN mesh with a half-space. Plane equation. + // + // Inputs: + // V #V by 3 list of mesh vertex positions + // equ plane equation: P(x,y,z) = a*x+b*y+c*z + d = 0, P(x,y,z) < 0 is + // _inside_. + // Outputs: + // VC #VC by 3 list of vertex positions of boolean result mesh + // FC #FC by 3 list of triangle indices into VC + // J #FC list of indices into [F;F.rows()+[1;2]] revealing "birth" facet + template < + typename DerivedV, + typename DerivedF, + typename Derivedequ, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> + IGL_INLINE bool intersect_with_half_space( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & equ, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J); + // Intersect a PWN mesh with a half-space. CGAL Plane. + // + // Inputs: + // V #V by 3 list of mesh vertex positions + // P plane + // Outputs: + // VC #VC by 3 list of vertex positions of boolean result mesh + // FC #FC by 3 list of triangle indices into VC + // J #FC list of indices into [F;F.rows()+[1;2]] revealing "birth" facet + template < + typename DerivedV, + typename DerivedF, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> + IGL_INLINE bool intersect_with_half_space( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const CGAL::Plane_3 & P, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "intersect_with_half_space.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/lexicographic_triangulation.cpp b/src/igl/copyleft/cgal/lexicographic_triangulation.cpp new file mode 100644 index 0000000000..719b50dbfa --- /dev/null +++ b/src/igl/copyleft/cgal/lexicographic_triangulation.cpp @@ -0,0 +1,24 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "lexicographic_triangulation.h" +#include "../../lexicographic_triangulation.h" +#include "orient2D.h" + +template< + typename DerivedP, + typename DerivedF + > +IGL_INLINE void igl::copyleft::cgal::lexicographic_triangulation( + const Eigen::PlainObjectBase& P, + Eigen::PlainObjectBase& F) +{ + typedef typename DerivedP::Scalar Scalar; + igl::lexicographic_triangulation(P, orient2D, F); +} diff --git a/src/igl/copyleft/cgal/lexicographic_triangulation.h b/src/igl/copyleft/cgal/lexicographic_triangulation.h new file mode 100644 index 0000000000..5773a39424 --- /dev/null +++ b/src/igl/copyleft/cgal/lexicographic_triangulation.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COPYLEFT_CGAL_LEXICOGRAPHIC_TRIANGULATION_H +#define IGL_COPYLEFT_CGAL_LEXICOGRAPHIC_TRIANGULATION_H +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + + // Given a set of points in 2D, return a lexicographic triangulation of these + // points. + // + // Inputs: + // P #P by 2 list of vertex positions + // + // Outputs: + // F #F by 3 of faces in lexicographic triangulation. + template< + typename DerivedP, + typename DerivedF + > + IGL_INLINE void lexicographic_triangulation( + const Eigen::PlainObjectBase& P, + Eigen::PlainObjectBase& F); + } + } +} + + + + +#ifndef IGL_STATIC_LIBRARY +# include "lexicographic_triangulation.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/list_to_matrix.cpp b/src/igl/copyleft/cgal/list_to_matrix.cpp new file mode 100644 index 0000000000..e94dde6b82 --- /dev/null +++ b/src/igl/copyleft/cgal/list_to_matrix.cpp @@ -0,0 +1,15 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "../../list_to_matrix.h" +#include +#include +#ifdef IGL_STATIC_LIBRARY +#undef IGL_STATIC_LIBRARY +#include "../../list_to_matrix.cpp" +template bool igl::list_to_matrix, Eigen::Matrix, -1, 3, 0, -1, 3> >(std::vector, std::allocator > >, std::allocator, std::allocator > > > > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&); +#endif diff --git a/src/igl/copyleft/cgal/mesh_boolean.cpp b/src/igl/copyleft/cgal/mesh_boolean.cpp new file mode 100644 index 0000000000..5d289f6c31 --- /dev/null +++ b/src/igl/copyleft/cgal/mesh_boolean.cpp @@ -0,0 +1,466 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#include "mesh_boolean.h" +#include "assign.h" +#include "extract_cells.h" +#include "mesh_boolean_type_to_funcs.h" +#include "propagate_winding_numbers.h" +#include "relabel_small_immersed_cells.h" +#include "remesh_self_intersections.h" +#include "string_to_mesh_boolean_type.h" +#include "../../combine.h" +#include "../../cumsum.h" +#include "../../extract_manifold_patches.h" +#include "../../get_seconds.h" +#include "../../remove_unreferenced.h" +#include "../../resolve_duplicated_faces.h" +#include "../../slice.h" +#include "../../unique_edge_map.h" +#include "../../unique_simplices.h" + +#include +#include + +//#define MESH_BOOLEAN_TIMING +//#define DOUBLE_CHECK_EXACT_OUTPUT +//#define SMALL_CELL_REMOVAL + +template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> +IGL_INLINE bool igl::copyleft::cgal::mesh_boolean( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::MatrixBase & VB, + const Eigen::MatrixBase & FB, + const MeshBooleanType & type, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J) +{ + std::function keep; + std::function) > wind_num_op; + mesh_boolean_type_to_funcs(type,wind_num_op,keep); + return mesh_boolean(VA,FA,VB,FB,wind_num_op,keep,VC,FC,J); +} +template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> +IGL_INLINE bool igl::copyleft::cgal::mesh_boolean( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::MatrixBase & VB, + const Eigen::MatrixBase & FB, + const std::string & type_str, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J) +{ + return mesh_boolean( + VA,FA,VB,FB,string_to_mesh_boolean_type(type_str),VC,FC,J); +} + +template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> +IGL_INLINE bool igl::copyleft::cgal::mesh_boolean( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::MatrixBase & VB, + const Eigen::MatrixBase & FB, + const std::function) >& wind_num_op, + const std::function & keep, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J) +{ + // Generate combined mesh (VA,FA,VB,FB) -> (V,F) + Eigen::Matrix sizes(FA.rows(),FB.rows()); + // TODO: This is a precision template **bug** that results in failure to + // compile. If DerivedVA::Scalar is double and DerivedVB::Scalar is + // CGAL::Epeck::FT then the following assignment will not compile. This + // implies that VA must have the trumping precision (and a valid assignment + // operator from VB's type). + Eigen::Matrix VV(VA.rows() + VB.rows(), 3); + DerivedFC FF(FA.rows() + FB.rows(), 3); + // Can't use comma initializer + for(int a = 0;a 0) + { + FF.block(FA.rows(), 0, FB.rows(), 3) = FB.array() + VA.rows(); + } + return mesh_boolean(VV,FF,sizes,wind_num_op,keep,VC,FC,J); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> +IGL_INLINE bool igl::copyleft::cgal::mesh_boolean( + const std::vector & Vlist, + const std::vector & Flist, + const std::function) >& wind_num_op, + const std::function & keep, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J) +{ + DerivedV VV; + DerivedF FF; + Eigen::Matrix Vsizes,Fsizes; + igl::combine(Vlist,Flist,VV,FF,Vsizes,Fsizes); + return mesh_boolean(VV,FF,Fsizes,wind_num_op,keep,VC,FC,J); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> +IGL_INLINE bool igl::copyleft::cgal::mesh_boolean( + const std::vector & Vlist, + const std::vector & Flist, + const MeshBooleanType & type, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J) +{ + DerivedV VV; + DerivedF FF; + Eigen::Matrix Vsizes,Fsizes; + igl::combine(Vlist,Flist,VV,FF,Vsizes,Fsizes); + std::function keep; + std::function) > wind_num_op; + mesh_boolean_type_to_funcs(type,wind_num_op,keep); + return mesh_boolean(VV,FF,Fsizes,wind_num_op,keep,VC,FC,J); +} + +template < + typename DerivedVV, + typename DerivedFF, + typename Derivedsizes, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> +IGL_INLINE bool igl::copyleft::cgal::mesh_boolean( + const Eigen::MatrixBase & VV, + const Eigen::MatrixBase & FF, + const Eigen::MatrixBase & sizes, + const std::function) >& wind_num_op, + const std::function & keep, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J) +{ +#ifdef MESH_BOOLEAN_TIMING + const auto & tictoc = []() -> double + { + static double t_start = igl::get_seconds(); + double diff = igl::get_seconds()-t_start; + t_start += diff; + return diff; + }; + const auto log_time = [&](const std::string& label) -> void { + std::cout << "mesh_boolean." << label << ": " + << tictoc() << std::endl; + }; + tictoc(); +#endif + typedef typename DerivedVC::Scalar Scalar; + typedef CGAL::Epeck Kernel; + typedef Kernel::FT ExactScalar; + typedef Eigen::Matrix MatrixX3S; + typedef Eigen::Matrix VectorXJ; + typedef Eigen::Matrix< + ExactScalar, + Eigen::Dynamic, + Eigen::Dynamic, + DerivedVC::IsRowMajor> MatrixXES; + MatrixXES V; + DerivedFC F; + VectorXJ CJ; + { + Eigen::VectorXi I; + igl::copyleft::cgal::RemeshSelfIntersectionsParam params; + params.stitch_all = true; + MatrixXES Vr; + DerivedFC Fr; + Eigen::MatrixXi IF; + igl::copyleft::cgal::remesh_self_intersections( + VV, FF, params, Vr, Fr, IF, CJ, I); + assert(I.size() == Vr.rows()); + // Merge coinciding vertices into non-manifold vertices. + std::for_each(Fr.data(), Fr.data()+Fr.size(), + [&I](typename DerivedFC::Scalar& a) { a=I[a]; }); + // Remove unreferenced vertices. + Eigen::VectorXi UIM; + igl::remove_unreferenced(Vr, Fr, V, F, UIM); + } +#ifdef MESH_BOOLEAN_TIMING + log_time("resolve_self_intersection"); +#endif + + // Compute edges of (F) --> (E,uE,EMAP,uE2E) + Eigen::MatrixXi E, uE; + Eigen::VectorXi EMAP; + std::vector > uE2E; + igl::unique_edge_map(F, E, uE, EMAP, uE2E); + + // Compute patches (F,EMAP,uE2E) --> (P) + Eigen::VectorXi P; + const size_t num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P); +#ifdef MESH_BOOLEAN_TIMING + log_time("patch_extraction"); +#endif + + // Compute cells (V,F,P,E,uE,EMAP) -> (per_patch_cells) + Eigen::MatrixXi per_patch_cells; + const size_t num_cells = + igl::copyleft::cgal::extract_cells( + V, F, P, E, uE, uE2E, EMAP, per_patch_cells); +#ifdef MESH_BOOLEAN_TIMING + log_time("cell_extraction"); +#endif + + // Compute winding numbers on each side of each facet. + const size_t num_faces = F.rows(); + // W(f,:) --> [w1out,w1in,w2out,w2in, ... wnout,wnint] winding numbers above + // and below each face w.r.t. each input mesh, so that W(f,2*i) is the + // winding number above face f w.r.t. input i, and W(f,2*i+1) is the winding + // number below face f w.r.t. input i. + Eigen::MatrixXi W; + // labels(f) = i means that face f comes from mesh i + Eigen::VectorXi labels(num_faces); + // cumulative sizes + Derivedsizes cumsizes; + igl::cumsum(sizes,1,cumsizes); + const size_t num_inputs = sizes.size(); + std::transform( + CJ.data(), + CJ.data()+CJ.size(), + labels.data(), + // Determine which input mesh birth face i comes from + [&num_inputs,&cumsizes](int i)->int + { + for(int k = 0;k 0) + { + valid = valid & + igl::copyleft::cgal::propagate_winding_numbers( + V, F, uE, uE2E, num_patches, P, num_cells, per_patch_cells, labels, W); + } else + { + W.resize(0, 2*num_inputs); + } + assert((size_t)W.rows() == num_faces); + // If W doesn't have enough columns, pad with zeros + if (W.cols() <= 2*num_inputs) + { + const int old_ncols = W.cols(); + W.conservativeResize(num_faces,2*num_inputs); + W.rightCols(2*num_inputs-old_ncols).setConstant(0); + } + assert((size_t)W.cols() == 2*num_inputs); +#ifdef MESH_BOOLEAN_TIMING + log_time("propagate_input_winding_number"); +#endif + + // Compute resulting winding number. + Eigen::MatrixXi Wr(num_faces, 2); + for (size_t i=0; i int + { + return (i+1)*(ori?1:-1); + }; + //auto signed_index_to_index = [&](int i) -> size_t { + // return abs(i) - 1; + //}; + std::vector selected; + for(size_t i=0; i 0) + { + selected.push_back(index_to_signed_index(i, true)); + } else if (should_keep < 0) + { + selected.push_back(index_to_signed_index(i, false)); + } + } + + const size_t num_selected = selected.size(); + DerivedFC kept_faces(num_selected, 3); + DerivedJ kept_face_indices(num_selected, 1); + for (size_t i=0; i 0) + { + kept_faces.row(i) = F.row(idx); + } else + { + kept_faces.row(i) = F.row(idx).reverse(); + } + kept_face_indices(i, 0) = CJ[idx]; + } +#ifdef MESH_BOOLEAN_TIMING + log_time("extract_output"); +#endif + + // Finally, remove duplicated faces and unreferenced vertices. + { + DerivedFC G; + DerivedJ JJ; + igl::resolve_duplicated_faces(kept_faces, G, JJ); + igl::slice(kept_face_indices, JJ, 1, J); + +#ifdef DOUBLE_CHECK_EXACT_OUTPUT + { + // Sanity check on exact output. + igl::copyleft::cgal::RemeshSelfIntersectionsParam params; + params.detect_only = true; + params.first_only = true; + MatrixXES dummy_VV; + DerivedFC dummy_FF, dummy_IF; + Eigen::VectorXi dummy_J, dummy_IM; + igl::copyleft::cgal::SelfIntersectMesh< + Kernel, + MatrixXES, DerivedFC, + MatrixXES, DerivedFC, + DerivedFC, + Eigen::VectorXi, + Eigen::VectorXi + > checker(V, G, params, + dummy_VV, dummy_FF, dummy_IF, dummy_J, dummy_IM); + if (checker.count != 0) + { + throw "Self-intersection not fully resolved."; + } + } +#endif + + MatrixX3S Vs; + assign(V,Vs); + Eigen::VectorXi newIM; + igl::remove_unreferenced(Vs,G,VC,FC,newIM); + } +#ifdef MESH_BOOLEAN_TIMING + log_time("clean_up"); +#endif + return valid; +} + +template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedVC, + typename DerivedFC> +IGL_INLINE bool igl::copyleft::cgal::mesh_boolean( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::MatrixBase & VB, + const Eigen::MatrixBase & FB, + const MeshBooleanType & type, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC) +{ + Eigen::Matrix J; + return igl::copyleft::cgal::mesh_boolean(VA,FA,VB,FB,type,VC,FC,J); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::mesh_boolean, 8, 3, 0, 8, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::mesh_boolean, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::mesh_boolean, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::mesh_boolean, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::mesh_boolean, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::mesh_boolean, 8, 3, 0, 8, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, 8, 3, 0, 8, 3>, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, 8, 3, 0, 8, 3>, Eigen::Matrix, Eigen::Matrix, -1, 4, 0, -1, 4>, Eigen::Matrix, Eigen::Matrix, -1, 4, 0, -1, 4>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, -1, 4, 0, -1, 4> > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase, -1, 4, 0, -1, 4> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::basic_string, std::allocator > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::basic_string, std::allocator > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, 8, 3, 0, 8, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::vector, std::allocator > > const&, std::vector, std::allocator > > const&, std::function)> const&, std::function const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::mesh_boolean, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::vector, std::allocator > > const&, std::vector, std::allocator > > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template bool igl::copyleft::cgal::mesh_boolean, -1, 3, 0, -1, 3>, class Eigen::Matrix, class Eigen::Matrix, -1, 3, 0, -1, 3>, class Eigen::Matrix, class Eigen::Matrix, -1, 3, 0, -1, 3>, class Eigen::Matrix, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::MatrixBase, -1, 3, 0, -1, 3>> const &, class Eigen::MatrixBase> const &, class Eigen::MatrixBase, -1, 3, 0, -1, 3>> const &, class Eigen::MatrixBase> const &, enum igl::MeshBooleanType const &, class Eigen::PlainObjectBase, -1, 3, 0, -1, 3>> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/mesh_boolean.h b/src/igl/copyleft/cgal/mesh_boolean.h new file mode 100644 index 0000000000..24c99ed41a --- /dev/null +++ b/src/igl/copyleft/cgal/mesh_boolean.h @@ -0,0 +1,229 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_COPYLEFT_CGAL_MESH_BOOLEAN_H +#define IGL_COPYLEFT_CGAL_MESH_BOOLEAN_H + +#include "../../igl_inline.h" +#include "../../MeshBooleanType.h" +#include +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // MESH_BOOLEAN Compute boolean csg operations on "solid", consistently + // oriented meshes. + // + // Inputs: + // VA #VA by 3 list of vertex positions of first mesh + // FA #FA by 3 list of triangle indices into VA + // VB #VB by 3 list of vertex positions of second mesh + // FB #FB by 3 list of triangle indices into VB + // type type of boolean operation + // Outputs: + // VC #VC by 3 list of vertex positions of boolean result mesh + // FC #FC by 3 list of triangle indices into VC + // J #FC list of indices into [FA;FA.rows()+FB] revealing "birth" facet + // Returns true if inputs induce a piecewise constant winding number + // field and type is valid + // + // See also: mesh_boolean_cork, intersect_other, + // remesh_self_intersections + template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> + IGL_INLINE bool mesh_boolean( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::MatrixBase & VB, + const Eigen::MatrixBase & FB, + const MeshBooleanType & type, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J); + template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> + IGL_INLINE bool mesh_boolean( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::MatrixBase & VB, + const Eigen::MatrixBase & FB, + const std::string & type_str, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J); + // + // Inputs: + // VA #VA by 3 list of vertex positions of first mesh + // FA #FA by 3 list of triangle indices into VA + // VB #VB by 3 list of vertex positions of second mesh + // FB #FB by 3 list of triangle indices into VB + // wind_num_op function handle for filtering winding numbers from + // tuples of integer values to [0,1] outside/inside values + // keep function handle for determining if a patch should be "kept" + // in the output based on the winding number on either side + // Outputs: + // VC #VC by 3 list of vertex positions of boolean result mesh + // FC #FC by 3 list of triangle indices into VC + // J #FC list of indices into [FA;FB] revealing "birth" facet + // Returns true iff inputs induce a piecewise constant winding number + // field + // + // See also: mesh_boolean_cork, intersect_other, + // remesh_self_intersections + template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> + IGL_INLINE bool mesh_boolean( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::MatrixBase & VB, + const Eigen::MatrixBase & FB, + const std::function) >& wind_num_op, + const std::function & keep, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J); + // MESH_BOOLEAN Variadic boolean operations + // + // Inputs: + // Vlist k-long list of lists of mesh vertex positions + // Flist k-long list of lists of mesh face indices, so that Flist[i] indexes + // vertices in Vlist[i] + // wind_num_op function handle for filtering winding numbers from + // n-tuples of integer values to [0,1] outside/inside values + // keep function handle for determining if a patch should be "kept" + // in the output based on the winding number on either side + // Outputs: + // VC #VC by 3 list of vertex positions of boolean result mesh + // FC #FC by 3 list of triangle indices into VC + // J #FC list of indices into [Flist[0];Flist[1];...;Flist[k]] + // revealing "birth" facet + // Returns true iff inputs induce a piecewise constant winding number + // field + // + // See also: mesh_boolean_cork, intersect_other, + // remesh_self_intersections + template < + typename DerivedV, + typename DerivedF, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> + IGL_INLINE bool mesh_boolean( + const std::vector & Vlist, + const std::vector & Flist, + const std::function) >& wind_num_op, + const std::function & keep, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J); + template < + typename DerivedV, + typename DerivedF, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> + IGL_INLINE bool mesh_boolean( + const std::vector & Vlist, + const std::vector & Flist, + const MeshBooleanType & type, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J); + // Given a merged mesh (V,F) and list of sizes of inputs + // + // Inputs: + // V #V by 3 list of merged mesh vertex positions + // F #F by 3 list of merged mesh face indices so that first sizes(0) + // faces come from the first input, and the next sizes(1) faces come + // from the second input, and so on. + // sizes #inputs list of sizes so that sizes(i) is the #faces in the + // ith input + // wind_num_op function handle for filtering winding numbers from + // tuples of integer values to [0,1] outside/inside values + // keep function handle for determining if a patch should be "kept" + // in the output based on the winding number on either side + // Outputs: + // VC #VC by 3 list of vertex positions of boolean result mesh + // FC #FC by 3 list of triangle indices into VC + // J #FC list of birth parent indices + // + template < + typename DerivedVV, + typename DerivedFF, + typename Derivedsizes, + typename DerivedVC, + typename DerivedFC, + typename DerivedJ> + IGL_INLINE bool mesh_boolean( + const Eigen::MatrixBase & VV, + const Eigen::MatrixBase & FF, + const Eigen::MatrixBase & sizes, + const std::function) >& wind_num_op, + const std::function & keep, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC, + Eigen::PlainObjectBase & J); + // Inputs: + // VA #VA by 3 list of vertex positions of first mesh + // FA #FA by 3 list of triangle indices into VA + // VB #VB by 3 list of vertex positions of second mesh + // FB #FB by 3 list of triangle indices into VB + // type type of boolean operation + // Outputs: + // VC #VC by 3 list of vertex positions of boolean result mesh + // FC #FC by 3 list of triangle indices into VC + // Returns true ff inputs induce a piecewise constant winding number + // field and type is valid + template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedVC, + typename DerivedFC> + IGL_INLINE bool mesh_boolean( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::MatrixBase & VB, + const Eigen::MatrixBase & FB, + const MeshBooleanType & type, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "mesh_boolean.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/mesh_boolean_type_to_funcs.cpp b/src/igl/copyleft/cgal/mesh_boolean_type_to_funcs.cpp new file mode 100644 index 0000000000..41b67c1b76 --- /dev/null +++ b/src/igl/copyleft/cgal/mesh_boolean_type_to_funcs.cpp @@ -0,0 +1,39 @@ +#include "mesh_boolean_type_to_funcs.h" +#include "BinaryWindingNumberOperations.h" + +IGL_INLINE void igl::copyleft::cgal::mesh_boolean_type_to_funcs( + const MeshBooleanType & type, + std::function) >& wind_num_op, + std::function & keep) +{ + switch (type) + { + case MESH_BOOLEAN_TYPE_UNION: + wind_num_op = BinaryUnion(); + keep = KeepInside(); + return; + case MESH_BOOLEAN_TYPE_INTERSECT: + wind_num_op = BinaryIntersect(); + keep = KeepInside(); + return; + case MESH_BOOLEAN_TYPE_MINUS: + wind_num_op = BinaryMinus(); + keep = KeepInside(); + return; + case MESH_BOOLEAN_TYPE_XOR: + wind_num_op = BinaryXor(); + keep = KeepInside(); + return; + case MESH_BOOLEAN_TYPE_RESOLVE: + wind_num_op = BinaryResolve(); + keep = KeepAll(); + return; + default: + assert(false && "Unsupported boolean type."); + return; + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/copyleft/cgal/mesh_boolean_type_to_funcs.h b/src/igl/copyleft/cgal/mesh_boolean_type_to_funcs.h new file mode 100644 index 0000000000..2a269d7708 --- /dev/null +++ b/src/igl/copyleft/cgal/mesh_boolean_type_to_funcs.h @@ -0,0 +1,37 @@ +#ifndef IGL_COPYLEFT_CGAL_MESH_BOOLEAN_TYPE_TO_FUNCS_H +#define IGL_COPYLEFT_CGAL_MESH_BOOLEAN_TYPE_TO_FUNCS_H + +#include "../../igl_inline.h" +#include "../../MeshBooleanType.h" +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Convert a MeshBooleanType enum to a pair of winding number conversion + // function and "keep" function used by mesh_boolean + // + // Inputs: + // type MeshBooleanType enum value + // Outputs: + // wind_num_op function handle for filtering winding numbers from + // tuples of integer values to [0,1] outside/inside values + // keep function handle for determining if a patch should be "kept" + // in the output based on the winding number on either side + // + // See also: string_to_mesh_boolean_type + IGL_INLINE void mesh_boolean_type_to_funcs( + const MeshBooleanType & type, + std::function) >& + wind_num_op, + std::function & keep); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "mesh_boolean_type_to_funcs.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/mesh_to_cgal_triangle_list.cpp b/src/igl/copyleft/cgal/mesh_to_cgal_triangle_list.cpp new file mode 100644 index 0000000000..7f3af539d2 --- /dev/null +++ b/src/igl/copyleft/cgal/mesh_to_cgal_triangle_list.cpp @@ -0,0 +1,76 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mesh_to_cgal_triangle_list.h" +#include "assign.h" + +#include + +template < + typename DerivedV, + typename DerivedF, + typename Kernel> +IGL_INLINE void igl::copyleft::cgal::mesh_to_cgal_triangle_list( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + std::vector > & T) +{ + typedef CGAL::Point_3 Point_3; + typedef CGAL::Triangle_3 Triangle_3; + // Must be 3D + assert(V.cols() == 3); + // **Copy** to convert to output type (this is especially/only needed if the + // input type DerivedV::Scalar is CGAL::Epeck + Eigen::Matrix< + typename Kernel::FT, + DerivedV::RowsAtCompileTime, + DerivedV::ColsAtCompileTime> + KV(V.rows(),V.cols()); + assign(V,KV); + // Must be triangles + assert(F.cols() == 3); + T.reserve(F.rows()); + // Loop over faces + for(int f = 0;f<(int)F.rows();f++) + { + T.push_back( + Triangle_3( + Point_3( KV(F(f,0),0), KV(F(f,0),1), KV(F(f,0),2)), + Point_3( KV(F(f,1),0), KV(F(f,1),1), KV(F(f,1),2)), + Point_3( KV(F(f,2),0), KV(F(f,2),1), KV(F(f,2),2)))); + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epick>(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epeck>(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, -1, -1, 1, -1, -1>, Eigen::Matrix, CGAL::Epick>(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, -1, -1, 1, -1, -1>, Eigen::Matrix, CGAL::Epeck>(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, Eigen::Matrix, CGAL::Epick>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, Eigen::Matrix, CGAL::Epeck>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, Eigen::Matrix, CGAL::Epick>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, Eigen::Matrix, CGAL::Epeck>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, Eigen::Matrix, CGAL::Epeck>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, Eigen::Matrix, CGAL::Epick>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, Eigen::Matrix, CGAL::Epeck>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, Eigen::Matrix, CGAL::Epick>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epeck>(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epick>(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epick>(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epeck>(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, 8, 3, 0, 8, 3>, Eigen::Matrix, CGAL::Epick>(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +template void igl::copyleft::cgal::mesh_to_cgal_triangle_list, 8, 3, 0, 8, 3>, Eigen::Matrix, CGAL::Epeck>(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > >&); +#endif diff --git a/src/igl/copyleft/cgal/mesh_to_cgal_triangle_list.h b/src/igl/copyleft/cgal/mesh_to_cgal_triangle_list.h new file mode 100644 index 0000000000..492215bd50 --- /dev/null +++ b/src/igl/copyleft/cgal/mesh_to_cgal_triangle_list.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_MESH_TO_CGAL_TRIANGLE_LIST_H +#define IGL_COPYLEFT_CGAL_MESH_TO_CGAL_TRIANGLE_LIST_H +#include "../../igl_inline.h" +#include +#include "CGAL_includes.hpp" +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Convert a mesh (V,F) to a list of CGAL triangles + // + // Templates: + // Kernal CGAL computation and construction kernel (e.g. + // CGAL::Exact_predicates_exact_constructions_kernel) + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // Outputs: + // T #F list of CGAL triangles + template < + typename DerivedV, + typename DerivedF, + typename Kernel> + IGL_INLINE void mesh_to_cgal_triangle_list( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + std::vector > & T); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "mesh_to_cgal_triangle_list.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/mesh_to_polyhedron.cpp b/src/igl/copyleft/cgal/mesh_to_polyhedron.cpp new file mode 100644 index 0000000000..b0bfe70790 --- /dev/null +++ b/src/igl/copyleft/cgal/mesh_to_polyhedron.cpp @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mesh_to_polyhedron.h" +#include +#include + + +template +IGL_INLINE bool igl::copyleft::cgal::mesh_to_polyhedron( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + Polyhedron & poly) +{ + typedef typename Polyhedron::HalfedgeDS HalfedgeDS; + // Postcondition: hds is a valid polyhedral surface. + CGAL::Polyhedron_incremental_builder_3 B(poly.hds()); + B.begin_surface(V.rows(),F.rows()); + typedef typename HalfedgeDS::Vertex Vertex; + typedef typename Vertex::Point Point; + assert(V.cols() == 3 && "V must be #V by 3"); + for(int v = 0;v +#include +template bool igl::copyleft::cgal::mesh_to_polyhedron, CGAL::Polyhedron_items_with_id_3, CGAL::HalfedgeDS_default, std::allocator > >(Eigen::Matrix const&, Eigen::Matrix const&, CGAL::Polyhedron_3, CGAL::Polyhedron_items_with_id_3, CGAL::HalfedgeDS_default, std::allocator >&); +#endif diff --git a/src/igl/copyleft/cgal/mesh_to_polyhedron.h b/src/igl/copyleft/cgal/mesh_to_polyhedron.h new file mode 100644 index 0000000000..de17d8aa24 --- /dev/null +++ b/src/igl/copyleft/cgal/mesh_to_polyhedron.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_MESH_TO_POLYHEDRON_H +#define IGL_COPYLEFT_CGAL_MESH_TO_POLYHEDRON_H +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Convert a mesh (V,F) to a CGAL Polyhedron + // + // Templates: + // Polyhedron CGAL Polyhedron type (e.g. Polyhedron_3) + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // Outputs: + // poly cgal polyhedron + // Returns true only if (V,F) can be converted to a valid polyhedron (i.e. if + // (V,F) is vertex and edge manifold). + template + IGL_INLINE bool mesh_to_polyhedron( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + Polyhedron & poly); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "mesh_to_polyhedron.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/minkowski_sum.cpp b/src/igl/copyleft/cgal/minkowski_sum.cpp new file mode 100644 index 0000000000..c9045a4c70 --- /dev/null +++ b/src/igl/copyleft/cgal/minkowski_sum.cpp @@ -0,0 +1,395 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "minkowski_sum.h" +#include "mesh_boolean.h" + +#include "../../slice.h" +#include "../../slice_mask.h" +#include "../../LinSpaced.h" +#include "../../unique_rows.h" +#include "../../get_seconds.h" +#include "../../edges.h" +#include +#include +#include +#include + + +template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedW, + typename DerivedG, + typename DerivedJ> +IGL_INLINE void igl::copyleft::cgal::minkowski_sum( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::MatrixBase & VB, + const Eigen::MatrixBase & FB, + const bool resolve_overlaps, + Eigen::PlainObjectBase & W, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & J) +{ + using namespace std; + using namespace Eigen; + assert(FA.cols() == 3 && "FA must contain a closed triangle mesh"); + assert(FB.cols() <= FA.cols() && + "FB must contain lower diemnsional simplices than FA"); + const auto tictoc = []()->double + { + static double t_start; + double now = igl::get_seconds(); + double interval = now-t_start; + t_start = now; + return interval; + }; + tictoc(); + Matrix EB; + edges(FB,EB); + Matrix EA(0,2); + if(FB.cols() == 3) + { + edges(FA,EA); + } + // number of copies of A along edges of B + const int n_ab = EB.rows(); + // number of copies of B along edges of A + const int n_ba = EA.rows(); + + vector vW(n_ab + n_ba); + vector vG(n_ab + n_ba); + vector vJ(n_ab + n_ba); + vector offsets(n_ab + n_ba + 1); + offsets[0] = 0; + // sweep A along edges of B + for(int e = 0;e eJ; + minkowski_sum( + VA, + FA, + VB.row(EB(e,0)).eval(), + VB.row(EB(e,1)).eval(), + false, + vW[e], + vG[e], + eJ); + assert(vG[e].rows() == eJ.rows()); + assert(eJ.cols() == 1); + vJ[e].resize(vG[e].rows(),2); + vJ[e].col(0) = eJ; + vJ[e].col(1).setConstant(e); + offsets[e+1] = offsets[e] + vW[e].rows(); + } + // sweep B along edges of A + for(int e = 0;e eJ; + const int ee = n_ab+e; + minkowski_sum( + VB, + FB, + VA.row(EA(e,0)).eval(), + VA.row(EA(e,1)).eval(), + false, + vW[ee], + vG[ee], + eJ); + vJ[ee].resize(vG[ee].rows(),2); + vJ[ee].col(0) = eJ.array() + (FA.rows()+1); + vJ[ee].col(1).setConstant(ee); + offsets[ee+1] = offsets[ee] + vW[ee].rows(); + } + // Combine meshes + int n=0,m=0; + for_each(vW.begin(),vW.end(),[&n](const DerivedW & w){n+=w.rows();}); + for_each(vG.begin(),vG.end(),[&m](const DerivedG & g){m+=g.rows();}); + assert(n == offsets.back()); + + W.resize(n,3); + G.resize(m,3); + J.resize(m,2); + { + int m_off = 0,n_off = 0; + for(int i = 0;i SJ; + mesh_boolean( + DerivedW(W), + DerivedG(G), + Matrix(), + Matrix(), + MESH_BOOLEAN_TYPE_UNION, + W, + G, + SJ); + slice(DerivedJ(J),SJ,1,J); + } +} + +template < + typename DerivedVA, + typename DerivedFA, + typename sType, int sCols, int sOptions, + typename dType, int dCols, int dOptions, + typename DerivedW, + typename DerivedG, + typename DerivedJ> +IGL_INLINE void igl::copyleft::cgal::minkowski_sum( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::Matrix & s, + const Eigen::Matrix & d, + const bool resolve_overlaps, + Eigen::PlainObjectBase & W, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & J) +{ + using namespace Eigen; + using namespace std; + assert(s.cols() == 3 && "s should be a 3d point"); + assert(d.cols() == 3 && "d should be a 3d point"); + // silly base case + if(FA.size() == 0) + { + W.resize(0,3); + G.resize(0,3); + return; + } + const int dim = VA.cols(); + assert(dim == 3 && "dim must be 3D"); + assert(s.size() == 3 && "s must be 3D point"); + assert(d.size() == 3 && "d must be 3D point"); + // segment vector + const CGAL::Vector_3 v(d(0)-s(0),d(1)-s(1),d(2)-s(2)); + // number of vertices + const int n = VA.rows(); + // duplicate vertices at s and d, we'll remove unreferernced later + W.resize(2*n,dim); + for(int i = 0;i P(m,1),N(m,1); + // loop over faces + int mp = 0,mn = 0; + for(int f = 0;f plane( + CGAL::Point_3(VA(FA(f,0),0),VA(FA(f,0),1),VA(FA(f,0),2)), + CGAL::Point_3(VA(FA(f,1),0),VA(FA(f,1),1),VA(FA(f,1),2)), + CGAL::Point_3(VA(FA(f,2),0),VA(FA(f,2),1),VA(FA(f,2),2))); + const auto normal = plane.orthogonal_vector(); + const auto dt = normal * v; + if(dt > 0) + { + P(f) = true; + N(f) = false; + mp++; + }else + //}else if(dt < 0) + { + P(f) = false; + N(f) = true; + mn++; + //}else + //{ + // P(f) = false; + // N(f) = false; + } + } + + typedef Matrix MatrixXI; + typedef Matrix VectorXI; + MatrixXI GT(mp+mn,3); + GT<< slice_mask(FA,N,1), slice_mask((FA.array()+n).eval(),P,1); + // J indexes FA for parts at s and m+FA for parts at d + J.derived() = igl::LinSpaced(m,0,m-1); + DerivedJ JT(mp+mn); + JT << slice_mask(J,P,1), slice_mask(J,N,1); + JT.block(mp,0,mn,1).array()+=m; + + // Original non-co-planar faces with positively oriented reversed + MatrixXI BA(mp+mn,3); + BA << slice_mask(FA,P,1).rowwise().reverse(), slice_mask(FA,N,1); + // Quads along **all** sides + MatrixXI GQ((mp+mn)*3,4); + GQ<< + BA.col(1), BA.col(0), BA.col(0).array()+n, BA.col(1).array()+n, + BA.col(2), BA.col(1), BA.col(1).array()+n, BA.col(2).array()+n, + BA.col(0), BA.col(2), BA.col(2).array()+n, BA.col(0).array()+n; + + MatrixXI uGQ; + VectorXI S,sI,sJ; + // Inputs: + // F #F by d list of polygons + // Outputs: + // S #uF list of signed incidences for each unique face + // uF #uF by d list of unique faces + // I #uF index vector so that uF = sort(F,2)(I,:) + // J #F index vector so that sort(F,2) = uF(J,:) + []( + const MatrixXI & F, + VectorXI & S, + MatrixXI & uF, + VectorXI & I, + VectorXI & J) + { + const int m = F.rows(); + const int d = F.cols(); + MatrixXI sF = F; + const auto MN = sF.rowwise().minCoeff().eval(); + // rotate until smallest index is first + for(int p = 0;p M = Matrix::Zero(m,1); + { + VectorXI P = igl::LinSpaced(d,0,d-1); + for(int p = 0;p SJ; + mesh_boolean( + DerivedW(W),DerivedG(G), + Matrix(),MatrixXI(), + MESH_BOOLEAN_TYPE_UNION, + W,G,SJ); + J.derived() = slice(DerivedJ(J),SJ,1); + } +} + +template < + typename DerivedVA, + typename DerivedFA, + typename sType, int sCols, int sOptions, + typename dType, int dCols, int dOptions, + typename DerivedW, + typename DerivedG, + typename DerivedJ> +IGL_INLINE void igl::copyleft::cgal::minkowski_sum( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::Matrix & s, + const Eigen::Matrix & d, + Eigen::PlainObjectBase & W, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & J) +{ + return minkowski_sum(VA,FA,s,d,true,W,G,J); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::cgal::minkowski_sum, -1, -1, 1, -1, -1>, Eigen::Matrix, CGAL::Lazy_exact_nt, 3, 1, CGAL::Lazy_exact_nt, 3, 1, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, Eigen::Matrix, 1, 3, 1, 1, 3> const&, Eigen::Matrix, 1, 3, 1, 1, 3> const&, bool, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::minkowski_sum< + Eigen::Matrix, + Eigen::Matrix, + double, 3, 1, + float, 3, 1, + Eigen::Matrix, -1, -1, 1, -1, -1>, + Eigen::Matrix, + Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, Eigen::Matrix const&, bool, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/minkowski_sum.h b/src/igl/copyleft/cgal/minkowski_sum.h new file mode 100644 index 0000000000..1631f0766f --- /dev/null +++ b/src/igl/copyleft/cgal/minkowski_sum.h @@ -0,0 +1,110 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_MINKOWSKI_SUM_H +#define IGL_COPYLEFT_CGAL_MINKOWSKI_SUM_H + +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Compute the Minkowski sum of a closed triangle mesh (V,F) and a + // set of simplices in 3D. + // + // Inputs: + // VA #VA by 3 list of mesh vertices in 3D + // FA #FA by 3 list of triangle indices into VA + // VB #VB by 3 list of mesh vertices in 3D + // FB #FB by ss list of simplex indices into VB, ss<=3 + // resolve_overlaps whether or not to resolve self-union. If false + // then result may contain self-intersections if input mesh is + // non-convex. + // Outputs: + // W #W by 3 list of mesh vertices in 3D + // G #G by 3 list of triangle indices into W + // J #G by 2 list of indices into + // + template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedW, + typename DerivedG, + typename DerivedJ> + IGL_INLINE void minkowski_sum( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::MatrixBase & VB, + const Eigen::MatrixBase & FB, + const bool resolve_overlaps, + Eigen::PlainObjectBase & W, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & J); + // Compute the Minkowski sum of a closed triangle mesh (V,F) and a + // segment [s,d] in 3D. + // + // Inputs: + // VA #VA by 3 list of mesh vertices in 3D + // FA #FA by 3 list of triangle indices into VA + // s segment source endpoint in 3D + // d segment source endpoint in 3D + // resolve_overlaps whether or not to resolve self-union. If false + // then result may contain self-intersections if input mesh is + // non-convex. + // Outputs: + // W #W by 3 list of mesh vertices in 3D + // G #G by 3 list of triangle indices into W + // J #G list of indices into [F;#V+F;[s d]] of birth parents + // + template < + typename DerivedVA, + typename DerivedFA, + typename sType, int sCols, int sOptions, + typename dType, int dCols, int dOptions, + typename DerivedW, + typename DerivedG, + typename DerivedJ> + IGL_INLINE void minkowski_sum( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::Matrix & s, + const Eigen::Matrix & d, + const bool resolve_overlaps, + Eigen::PlainObjectBase & W, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & J); + template < + typename DerivedVA, + typename DerivedFA, + typename sType, int sCols, int sOptions, + typename dType, int dCols, int dOptions, + typename DerivedW, + typename DerivedG, + typename DerivedJ> + IGL_INLINE void minkowski_sum( + const Eigen::MatrixBase & VA, + const Eigen::MatrixBase & FA, + const Eigen::Matrix & s, + const Eigen::Matrix & d, + Eigen::PlainObjectBase & W, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & J); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "minkowski_sum.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/order_facets_around_edge.cpp b/src/igl/copyleft/cgal/order_facets_around_edge.cpp new file mode 100644 index 0000000000..13b585b509 --- /dev/null +++ b/src/igl/copyleft/cgal/order_facets_around_edge.cpp @@ -0,0 +1,428 @@ +#include "order_facets_around_edge.h" +#include +#include + +#include + +// adj_faces contains signed index starting from +- 1. +template< + typename DerivedV, + typename DerivedF, + typename DerivedI > +void igl::copyleft::cgal::order_facets_around_edge( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + size_t s, + size_t d, + const std::vector& adj_faces, + Eigen::PlainObjectBase& order, bool debug) +{ + // Although we only need exact predicates in the algorithm, + // exact constructions are needed to avoid degeneracies due to + // casting to double. + typedef CGAL::Exact_predicates_exact_constructions_kernel K; + typedef K::Point_3 Point_3; + typedef K::Plane_3 Plane_3; + + auto get_face_index = [&](int adj_f)->size_t + { + return abs(adj_f) - 1; + }; + + auto get_opposite_vertex = [&](size_t fid)->size_t + { + typedef typename DerivedF::Scalar Index; + if (F(fid, 0) != (Index)s && F(fid, 0) != (Index)d) return F(fid, 0); + if (F(fid, 1) != (Index)s && F(fid, 1) != (Index)d) return F(fid, 1); + if (F(fid, 2) != (Index)s && F(fid, 2) != (Index)d) return F(fid, 2); + assert(false); + return -1; + }; + + // Handle base cases + if (adj_faces.size() == 0) + { + order.resize(0, 1); + return; + } else if (adj_faces.size() == 1) + { + order.resize(1, 1); + order(0, 0) = 0; + return; + } else if (adj_faces.size() == 2) + { + const size_t o1 = get_opposite_vertex(get_face_index(adj_faces[0])); + const size_t o2 = get_opposite_vertex(get_face_index(adj_faces[1])); + const Point_3 ps(V(s, 0), V(s, 1), V(s, 2)); + const Point_3 pd(V(d, 0), V(d, 1), V(d, 2)); + const Point_3 p1(V(o1, 0), V(o1, 1), V(o1, 2)); + const Point_3 p2(V(o2, 0), V(o2, 1), V(o2, 2)); + order.resize(2, 1); + switch (CGAL::orientation(ps, pd, p1, p2)) + { + case CGAL::POSITIVE: + order(0, 0) = 1; + order(1, 0) = 0; + break; + case CGAL::NEGATIVE: + order(0, 0) = 0; + order(1, 0) = 1; + break; + case CGAL::COPLANAR: + { + switch (CGAL::coplanar_orientation(ps, pd, p1, p2)) { + case CGAL::POSITIVE: + // Duplicated face, use index to break tie. + order(0, 0) = adj_faces[0] < adj_faces[1] ? 0:1; + order(1, 0) = adj_faces[0] < adj_faces[1] ? 1:0; + break; + case CGAL::NEGATIVE: + // Coplanar faces, one on each side of the edge. + // It is equally valid to order them (0, 1) or (1, 0). + // I cannot think of any reason to prefer one to the + // other. So just use (0, 1) ordering by default. + order(0, 0) = 0; + order(1, 0) = 1; + break; + case CGAL::COLLINEAR: + std::cerr << "Degenerated triangle detected." << + std::endl; + assert(false); + break; + default: + assert(false); + } + } + break; + default: + assert(false); + } + return; + } + + const size_t num_adj_faces = adj_faces.size(); + const size_t o = get_opposite_vertex( get_face_index(adj_faces[0])); + const Point_3 p_s(V(s, 0), V(s, 1), V(s, 2)); + const Point_3 p_d(V(d, 0), V(d, 1), V(d, 2)); + const Point_3 p_o(V(o, 0), V(o, 1), V(o, 2)); + const Plane_3 separator(p_s, p_d, p_o); + if (separator.is_degenerate()) { + throw std::runtime_error( + "Cannot order facets around edge due to degenerated facets"); + } + + std::vector opposite_vertices; + for (size_t i=0; i positive_side; + std::vector negative_side; + std::vector tie_positive_oriented; + std::vector tie_negative_oriented; + + std::vector positive_side_index; + std::vector negative_side_index; + std::vector tie_positive_oriented_index; + std::vector tie_negative_oriented_index; + + for (size_t i=0; i& data) -> std::vector{ + const size_t len = data.size(); + std::vector order(len); + for (size_t i=0; i tie_positive_order = index_sort(tie_positive_oriented); + std::vector tie_negative_order = index_sort(tie_negative_oriented); + + // Copy results into order vector. + const size_t tie_positive_size = tie_positive_oriented.size(); + const size_t tie_negative_size = tie_negative_oriented.size(); + const size_t positive_size = positive_order.size(); + const size_t negative_size = negative_order.size(); + + order.resize( + tie_positive_size + positive_size + tie_negative_size + negative_size,1); + + size_t count=0; + for (size_t i=0; i +IGL_INLINE +void igl::copyleft::cgal::order_facets_around_edge( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + size_t s, + size_t d, + const std::vector& adj_faces, + const Eigen::PlainObjectBase& pivot_point, + Eigen::PlainObjectBase& order) +{ + assert(V.cols() == 3); + assert(F.cols() == 3); + assert(pivot_point.cols() == 3); + auto signed_index_to_index = [&](int signed_idx) + { + return abs(signed_idx) -1; + }; + auto get_opposite_vertex_index = [&](size_t fid) -> typename DerivedF::Scalar + { + typedef typename DerivedF::Scalar Index; + if (F(fid, 0) != (Index)s && F(fid, 0) != (Index)d) return F(fid, 0); + if (F(fid, 1) != (Index)s && F(fid, 1) != (Index)d) return F(fid, 1); + if (F(fid, 2) != (Index)s && F(fid, 2) != (Index)d) return F(fid, 2); + assert(false); + // avoid warning + return -1; + }; + + { + // Check if s, d and pivot are collinear. + typedef CGAL::Exact_predicates_exact_constructions_kernel K; + K::Point_3 ps(V(s,0), V(s,1), V(s,2)); + K::Point_3 pd(V(d,0), V(d,1), V(d,2)); + K::Point_3 pp(pivot_point(0,0), pivot_point(0,1), pivot_point(0,2)); + if (CGAL::collinear(ps, pd, pp)) { + throw std::runtime_error( + "Pivot point is collinear with the outer edge!"); + } + } + + const size_t N = adj_faces.size(); + const size_t num_faces = N + 1; // N adj faces + 1 pivot face + + // Because face indices are used for tie breaking, the original face indices + // in the new faces array must be ascending. + auto comp = [&](int i, int j) + { + return signed_index_to_index(adj_faces[i]) < + signed_index_to_index(adj_faces[j]); + }; + std::vector adj_order(N); + for (size_t i=0; i adj_faces_with_pivot(num_faces); + for (size_t i=0; i, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, unsigned long, unsigned long, std::vector > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::order_facets_around_edge, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, unsigned long, unsigned long, std::vector > const&, Eigen::PlainObjectBase >&, bool); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::order_facets_around_edge, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, unsigned long, unsigned long, std::vector > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::order_facets_around_edge, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, unsigned long, unsigned long, std::vector > const&, Eigen::PlainObjectBase >&, bool); +template void igl::copyleft::cgal::order_facets_around_edge, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase > const&, unsigned long, unsigned long, std::vector > const&, Eigen::PlainObjectBase >&, bool); +template void igl::copyleft::cgal::order_facets_around_edge, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned long, unsigned long, std::vector > const&, Eigen::PlainObjectBase >&, bool); +template void igl::copyleft::cgal::order_facets_around_edge, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase > const&, unsigned long, unsigned long, std::vector > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::order_facets_around_edge, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned long, unsigned long, std::vector > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::order_facets_around_edge, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned long, unsigned long, std::vector > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::order_facets_around_edge, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, unsigned long, unsigned long, std::vector > const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::copyleft::cgal::order_facets_around_edge, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase> const &, unsigned __int64, unsigned __int64, class std::vector> const &, class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::order_facets_around_edge, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase> const &, unsigned __int64, unsigned __int64, class std::vector> const &, class Eigen::PlainObjectBase> &, bool); +template void igl::copyleft::cgal::order_facets_around_edge, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, unsigned __int64, unsigned __int64, class std::vector> const &, class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::order_facets_around_edge, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, unsigned __int64, unsigned __int64, class std::vector> const &, class Eigen::PlainObjectBase> &, bool); +template void igl::copyleft::cgal::order_facets_around_edge, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, unsigned __int64, unsigned __int64, class std::vector> const &, class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::order_facets_around_edge, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, unsigned __int64, unsigned __int64, class std::vector> const &, class Eigen::PlainObjectBase> &, bool); +template void igl::copyleft::cgal::order_facets_around_edge, -1, 3, 0, -1, 3>, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, 3, 0, -1, 3>> const &, class Eigen::PlainObjectBase> const &, unsigned __int64, unsigned __int64, class std::vector> const &, class Eigen::PlainObjectBase, -1, 3, 0, -1, 3>> const &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::order_facets_around_edge, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, unsigned __int64, unsigned __int64, class std::vector> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::order_facets_around_edge, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, unsigned __int64, unsigned __int64, class std::vector> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/order_facets_around_edge.h b/src/igl/copyleft/cgal/order_facets_around_edge.h new file mode 100644 index 0000000000..6e09129acc --- /dev/null +++ b/src/igl/copyleft/cgal/order_facets_around_edge.h @@ -0,0 +1,77 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_COPYLEFT_CGAL_ORDER_FACETS_AROUND_EDGE_H +#define IGL_COPYLEFT_CGAL_ORDER_FACETS_AROUND_EDGE_H +#include "../../igl_inline.h" +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Given a directed edge, sort its adjacent faces. Assuming the + // directed edge is (s, d). Sort the adjacent faces clockwise around the + // axis (d - s), i.e. left-hand rule. An adjacent face is consistently + // oriented if it contains (d, s) as a directed edge. + // + // For overlapping faces, break the tie using signed face index, smaller + // signed index comes before the larger signed index. Signed index is + // computed as (consistent? 1:-1) * (face_index + 1). + // + // Inputs: + // V #V by 3 list of vertices. + // F #F by 3 list of faces + // s Index of source vertex. + // d Index of destination vertex. + // adj_faces List of adjacent face signed indices. + // Output: + // order List of face indices that orders adjacent faces around edge + // (s, d) clockwise. + template< + typename DerivedV, + typename DerivedF, + typename DerivedI > + IGL_INLINE + void order_facets_around_edge( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + size_t s, + size_t d, + const std::vector& adj_faces, + Eigen::PlainObjectBase& order, + bool debug=false); + + // This function is a wrapper around the one above. Since the ordering + // is circular, the pivot point is used to define a starting point. So + // order[0] is the index into adj_face that is immediately after the + // pivot face (s, d, pivot point) in clockwise order. + template< + typename DerivedV, + typename DerivedF, + typename DerivedI> + IGL_INLINE + void order_facets_around_edge( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + size_t s, + size_t d, + const std::vector& adj_faces, + const Eigen::PlainObjectBase& pivot_point, + Eigen::PlainObjectBase& order); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +#include "order_facets_around_edge.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/order_facets_around_edges.cpp b/src/igl/copyleft/cgal/order_facets_around_edges.cpp new file mode 100644 index 0000000000..767d3e899a --- /dev/null +++ b/src/igl/copyleft/cgal/order_facets_around_edges.cpp @@ -0,0 +1,332 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "order_facets_around_edges.h" +#include "order_facets_around_edge.h" +#include "../../sort_angles.h" +#include +#include +#include + +template< + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DeriveduE, + typename uE2EType, + typename uE2oEType, + typename uE2CType > +IGL_INLINE +typename std::enable_if::value, void>::type +igl::copyleft::cgal::order_facets_around_edges( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& N, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + std::vector >& uE2oE, + std::vector >& uE2C ) { + + typedef Eigen::Matrix Vector3F; + const typename DerivedV::Scalar EPS = 1e-12; + const size_t num_faces = F.rows(); + const size_t num_undirected_edges = uE.rows(); + + auto edge_index_to_face_index = [&](size_t ei) { return ei % num_faces; }; + auto edge_index_to_corner_index = [&](size_t ei) { return ei / num_faces; }; + + uE2oE.resize(num_undirected_edges); + uE2C.resize(num_undirected_edges); + + for(size_t ui = 0;ui 0); + + const auto ref_edge = adj_edges[0]; + const auto ref_face = edge_index_to_face_index(ref_edge); + Vector3F ref_normal = N.row(ref_face); + + const auto ref_corner_o = edge_index_to_corner_index(ref_edge); + const auto ref_corner_s = (ref_corner_o+1)%3; + const auto ref_corner_d = (ref_corner_o+2)%3; + + const typename DerivedF::Scalar o = F(ref_face, ref_corner_o); + const typename DerivedF::Scalar s = F(ref_face, ref_corner_s); + const typename DerivedF::Scalar d = F(ref_face, ref_corner_d); + + Vector3F edge = V.row(d) - V.row(s); + auto edge_len = edge.norm(); + bool degenerated = edge_len < EPS; + if (degenerated) { + if (edge_valance <= 2) { + // There is only one way to order 2 or less faces. + edge.setZero(); + } else { + edge.setZero(); + Eigen::Matrix + normals(edge_valance, 3); + for (size_t fei=0; fei= EPS) { + edge.normalize(); + break; + } + } + + // Ensure edge direction are consistent with reference face. + Vector3F in_face_vec = V.row(o) - V.row(s); + if (edge.cross(in_face_vec).dot(ref_normal) < 0) { + edge *= -1; + } + + if (edge.norm() < EPS) { + std::cerr << "=====================================" << std::endl; + std::cerr << " ui: " << ui << std::endl; + std::cerr << "edge: " << ref_edge << std::endl; + std::cerr << "face: " << ref_face << std::endl; + std::cerr << " vs: " << V.row(s) << std::endl; + std::cerr << " vd: " << V.row(d) << std::endl; + std::cerr << "adj face normals: " << std::endl; + std::cerr << normals << std::endl; + std::cerr << "Very degenerated case detected:" << std::endl; + std::cerr << "Near zero edge surrounded by " + << edge_valance << " neearly colinear faces" << + std::endl; + std::cerr << "=====================================" << std::endl; + } + } + } else { + edge.normalize(); + } + + Eigen::MatrixXd angle_data(edge_valance, 3); + std::vector cons(edge_valance); + + for (size_t fei=0; fei +IGL_INLINE +typename std::enable_if::value, void>::type +igl::copyleft::cgal::order_facets_around_edges( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& N, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + std::vector >& uE2oE, + std::vector >& uE2C ) { + + typedef Eigen::Matrix Vector3F; + typedef Eigen::Matrix Vector3E; + const typename DerivedV::Scalar EPS = 1e-12; + const size_t num_faces = F.rows(); + const size_t num_undirected_edges = uE.rows(); + + auto edge_index_to_face_index = [&](size_t ei) { return ei % num_faces; }; + auto edge_index_to_corner_index = [&](size_t ei) { return ei / num_faces; }; + + uE2oE.resize(num_undirected_edges); + uE2C.resize(num_undirected_edges); + + for(size_t ui = 0;ui 0); + + const auto ref_edge = adj_edges[0]; + const auto ref_face = edge_index_to_face_index(ref_edge); + Vector3F ref_normal = N.row(ref_face); + + const auto ref_corner_o = edge_index_to_corner_index(ref_edge); + const auto ref_corner_s = (ref_corner_o+1)%3; + const auto ref_corner_d = (ref_corner_o+2)%3; + + const typename DerivedF::Scalar o = F(ref_face, ref_corner_o); + const typename DerivedF::Scalar s = F(ref_face, ref_corner_s); + const typename DerivedF::Scalar d = F(ref_face, ref_corner_d); + + Vector3E exact_edge = V.row(d) - V.row(s); + exact_edge.array() /= exact_edge.squaredNorm(); + Vector3F edge( + CGAL::to_double(exact_edge[0]), + CGAL::to_double(exact_edge[1]), + CGAL::to_double(exact_edge[2])); + edge.normalize(); + + Eigen::MatrixXd angle_data(edge_valance, 3); + std::vector cons(edge_valance); + + for (size_t fei=0; fei +IGL_INLINE void igl::copyleft::cgal::order_facets_around_edges( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + std::vector >& uE2oE, + std::vector >& uE2C ) { + + //typedef Eigen::Matrix Vector3E; + const size_t num_faces = F.rows(); + const size_t num_undirected_edges = uE.rows(); + + auto edge_index_to_face_index = [&](size_t ei) { return ei % num_faces; }; + auto edge_index_to_corner_index = [&](size_t ei) { return ei / num_faces; }; + + uE2oE.resize(num_undirected_edges); + uE2C.resize(num_undirected_edges); + + for(size_t ui = 0;ui 0); + + const auto ref_edge = adj_edges[0]; + const auto ref_face = edge_index_to_face_index(ref_edge); + + const auto ref_corner_o = edge_index_to_corner_index(ref_edge); + const auto ref_corner_s = (ref_corner_o+1)%3; + const auto ref_corner_d = (ref_corner_o+2)%3; + + //const typename DerivedF::Scalar o = F(ref_face, ref_corner_o); + const typename DerivedF::Scalar s = F(ref_face, ref_corner_s); + const typename DerivedF::Scalar d = F(ref_face, ref_corner_d); + + std::vector cons(edge_valance); + std::vector adj_faces(edge_valance); + for (size_t fei=0; fei, Eigen::Matrix, Eigen::Matrix, int, int, bool>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +// generated by autoexplicit.sh +template std::enable_if::Scalar, CGAL::Lazy_exact_nt >::value), void>::type igl::copyleft::cgal::order_facets_around_edges, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int, int, bool>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::copyleft::cgal::order_facets_around_edges, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, long, long, bool>(Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::copyleft::cgal::order_facets_around_edges, Eigen::Matrix, Eigen::Matrix, long, long, bool>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::copyleft::cgal::order_facets_around_edges, Eigen::Matrix, Eigen::Matrix, long, long, bool>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::copyleft::cgal::order_facets_around_edges, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, long, long, bool>(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +#endif diff --git a/src/igl/copyleft/cgal/order_facets_around_edges.h b/src/igl/copyleft/cgal/order_facets_around_edges.h new file mode 100644 index 0000000000..89ac2918ee --- /dev/null +++ b/src/igl/copyleft/cgal/order_facets_around_edges.h @@ -0,0 +1,107 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_ORDER_FACETS_AROUND_EDGES_H +#define IGL_COPYLEFT_CGAL_ORDER_FACETS_AROUND_EDGES_H +#include "../../igl_inline.h" +#include +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // For each undirected edge, sort its adjacent faces. Assuming the + // undirected edge is (s, d). Sort the adjacent faces clockwise around the + // axis (d - s), i.e. left-hand rule. An adjacent face is consistently + // oriented if it contains (d, s) as a directed edge. + // + // For overlapping faces, break the tie using signed face index, smaller + // signed index comes before the larger signed index. Signed index is + // computed as (consistent? 1:-1) * index. + // + // Inputs: + // V #V by 3 list of vertices. + // F #F by 3 list of faces + // N #F by 3 list of face normals. + // uE #uE by 2 list of vertex_indices, represents undirected edges. + // uE2E #uE list of lists that maps uE to E. (a one-to-many map) + // + // Outputs: + // uE2oE #uE list of lists that maps uE to an ordered list of E. (a + // one-to-many map) + // uE2C #uE list of lists of bools indicates whether each face in + // uE2oE[i] is consistently orientated as the ordering. + // + template< + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DeriveduE, + typename uE2EType, + typename uE2oEType, + typename uE2CType > + IGL_INLINE + typename std::enable_if::value, void>::type + order_facets_around_edges( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& N, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + std::vector >& uE2oE, + std::vector >& uE2C ); + + template< + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DeriveduE, + typename uE2EType, + typename uE2oEType, + typename uE2CType > + IGL_INLINE + typename std::enable_if::value, void>::type + order_facets_around_edges( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& N, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + std::vector >& uE2oE, + std::vector >& uE2C ); + + // Order faces around each edge. Only exact predicate is used in the algorithm. + // Normal is not needed. + template< + typename DerivedV, + typename DerivedF, + typename DeriveduE, + typename uE2EType, + typename uE2oEType, + typename uE2CType > + IGL_INLINE void order_facets_around_edges( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + std::vector >& uE2oE, + std::vector >& uE2C ); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +#include "order_facets_around_edges.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/orient2D.cpp b/src/igl/copyleft/cgal/orient2D.cpp new file mode 100644 index 0000000000..ee5933bb40 --- /dev/null +++ b/src/igl/copyleft/cgal/orient2D.cpp @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "orient2D.h" +#include +#include + +template +IGL_INLINE short igl::copyleft::cgal::orient2D( + const Scalar pa[2], + const Scalar pb[2], + const Scalar pc[2]) +{ + typedef CGAL::Exact_predicates_exact_constructions_kernel Epeck; + typedef CGAL::Exact_predicates_inexact_constructions_kernel Epick; + typedef typename std::conditional::value, + Epeck, Epick>::type Kernel; + + switch(CGAL::orientation( + typename Kernel::Point_2(pa[0], pa[1]), + typename Kernel::Point_2(pb[0], pb[1]), + typename Kernel::Point_2(pc[0], pc[1]))) { + case CGAL::LEFT_TURN: + return 1; + case CGAL::RIGHT_TURN: + return -1; + case CGAL::COLLINEAR: + return 0; + default: + throw "Invalid orientation"; + } +} diff --git a/src/igl/copyleft/cgal/orient2D.h b/src/igl/copyleft/cgal/orient2D.h new file mode 100644 index 0000000000..0f2483dfd8 --- /dev/null +++ b/src/igl/copyleft/cgal/orient2D.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COPYLEFT_CGAL_ORIENT_2D_H +#define IGL_COPYLEFT_CGAL_ORIENT_2D_H + +#include "../../igl_inline.h" + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Inputs: + // pa,pb,pc 2D points. + // Output: + // 1 if pa,pb,pc are counterclockwise oriented. + // 0 if pa,pb,pc are counterclockwise oriented. + // -1 if pa,pb,pc are clockwise oriented. + template + IGL_INLINE short orient2D( + const Scalar pa[2], + const Scalar pb[2], + const Scalar pc[2]); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "orient2D.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/orient3D.cpp b/src/igl/copyleft/cgal/orient3D.cpp new file mode 100644 index 0000000000..3bdbfb80ef --- /dev/null +++ b/src/igl/copyleft/cgal/orient3D.cpp @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "orient3D.h" +#include +#include + +template +IGL_INLINE short igl::copyleft::cgal::orient3D( + const Scalar pa[3], + const Scalar pb[3], + const Scalar pc[3], + const Scalar pd[3]) +{ + typedef CGAL::Exact_predicates_exact_constructions_kernel Epeck; + typedef CGAL::Exact_predicates_inexact_constructions_kernel Epick; + typedef typename std::conditional::value, + Epeck, Epick>::type Kernel; + + switch(CGAL::orientation( + typename Kernel::Point_3(pa[0], pa[1], pa[2]), + typename Kernel::Point_3(pb[0], pb[1], pb[2]), + typename Kernel::Point_3(pc[0], pc[1], pc[2]), + typename Kernel::Point_3(pd[0], pd[1], pd[2]))) { + case CGAL::POSITIVE: + return 1; + case CGAL::NEGATIVE: + return -1; + case CGAL::COPLANAR: + return 0; + default: + throw "Invalid orientation"; + } +} diff --git a/src/igl/copyleft/cgal/orient3D.h b/src/igl/copyleft/cgal/orient3D.h new file mode 100644 index 0000000000..029e208b12 --- /dev/null +++ b/src/igl/copyleft/cgal/orient3D.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COPYLEFT_CGAL_ORIENT_3D_H +#define IGL_COPYLEFT_CGAL_ORIENT_3D_H + +#include "../../igl_inline.h" + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Inputs: + // pa,pb,pc,pd 3D points. + // Output: + // 1 if pa,pb,pc,pd forms a tet of positive volume. + // 0 if pa,pb,pc,pd are coplanar. + // -1 if pa,pb,pc,pd forms a tet of negative volume. + template + IGL_INLINE short orient3D( + const Scalar pa[3], + const Scalar pb[3], + const Scalar pc[3], + const Scalar pd[3]); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "orient3D.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/outer_element.cpp b/src/igl/copyleft/cgal/outer_element.cpp new file mode 100644 index 0000000000..2bef8cae1e --- /dev/null +++ b/src/igl/copyleft/cgal/outer_element.cpp @@ -0,0 +1,232 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "outer_element.h" +#include +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename IndexType, + typename DerivedA + > +IGL_INLINE void igl::copyleft::cgal::outer_vertex( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & I, + IndexType & v_index, + Eigen::PlainObjectBase & A) +{ + // Algorithm: + // Find an outer vertex (i.e. vertex reachable from infinity) + // Return the vertex with the largest X value. + // If there is a tie, pick the one with largest Y value. + // If there is still a tie, pick the one with the largest Z value. + // If there is still a tie, then there are duplicated vertices within the + // mesh, which violates the precondition. + typedef typename DerivedF::Scalar Index; + const Index INVALID = std::numeric_limits::max(); + const size_t num_selected_faces = I.rows(); + std::vector candidate_faces; + Index outer_vid = INVALID; + typename DerivedV::Scalar outer_val = 0; + for (size_t i=0; i outer_val) + { + outer_val = vx; + outer_vid = v; + candidate_faces = {f}; + } else if (v == outer_vid) + { + candidate_faces.push_back(f); + } else if (vx == outer_val) + { + // Break tie. + auto vy = V(v,1); + auto vz = V(v, 2); + auto outer_y = V(outer_vid, 1); + auto outer_z = V(outer_vid, 2); + assert(!(vy == outer_y && vz == outer_z)); + bool replace = (vy > outer_y) || + ((vy == outer_y) && (vz > outer_z)); + if (replace) + { + outer_val = vx; + outer_vid = v; + candidate_faces = {f}; + } + } + } + } + + assert(outer_vid != INVALID); + assert(candidate_faces.size() > 0); + v_index = outer_vid; + A.resize(candidate_faces.size()); + std::copy(candidate_faces.begin(), candidate_faces.end(), A.data()); +} + +template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename IndexType, + typename DerivedA + > +IGL_INLINE void igl::copyleft::cgal::outer_edge( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & I, + IndexType & v1, + IndexType & v2, + Eigen::PlainObjectBase & A) { + // Algorithm: + // Find an outer vertex first. + // Find the incident edge with largest abs slope when projected onto XY plane. + // If there is a tie, check the signed slope and use the positive one. + // If there is still a tie, break it using the projected slope onto ZX plane. + // If there is still a tie, again check the signed slope and use the positive one. + // If there is still a tie, then there are multiple overlapping edges, + // which violates the precondition. + typedef typename DerivedV::Scalar Scalar; + typedef typename DerivedV::Index Index; + typedef typename Eigen::Matrix ScalarArray3; + typedef typename Eigen::Matrix IndexArray3; + const Index INVALID = std::numeric_limits::max(); + + Index outer_vid; + Eigen::Matrix candidate_faces; + outer_vertex(V, F, I, outer_vid, candidate_faces); + const ScalarArray3& outer_v = V.row(outer_vid); + assert(candidate_faces.size() > 0); + + auto get_vertex_index = [&](const IndexArray3& f, Index vid) -> Index + { + if (f[0] == vid) return 0; + if (f[1] == vid) return 1; + if (f[2] == vid) return 2; + assert(false); + return -1; + }; + + auto unsigned_value = [](Scalar v) -> Scalar { + if (v < 0) return v * -1; + else return v; + }; + + Scalar outer_slope_YX = 0; + Scalar outer_slope_ZX = 0; + Index outer_opp_vid = INVALID; + bool infinite_slope_detected = false; + std::vector incident_faces; + auto check_and_update_outer_edge = [&](Index opp_vid, Index fid) -> void { + if (opp_vid == outer_opp_vid) + { + incident_faces.push_back(fid); + return; + } + + const ScalarArray3 opp_v = V.row(opp_vid); + if (!infinite_slope_detected && outer_v[0] != opp_v[0]) + { + // Finite slope + const ScalarArray3 diff = opp_v - outer_v; + const Scalar slope_YX = diff[1] / diff[0]; + const Scalar slope_ZX = diff[2] / diff[0]; + const Scalar u_slope_YX = unsigned_value(slope_YX); + const Scalar u_slope_ZX = unsigned_value(slope_ZX); + bool update = false; + if (outer_opp_vid == INVALID) { + update = true; + } else { + const Scalar u_outer_slope_YX = unsigned_value(outer_slope_YX); + if (u_slope_YX > u_outer_slope_YX) { + update = true; + } else if (u_slope_YX == u_outer_slope_YX && + slope_YX > outer_slope_YX) { + update = true; + } else if (slope_YX == outer_slope_YX) { + const Scalar u_outer_slope_ZX = + unsigned_value(outer_slope_ZX); + if (u_slope_ZX > u_outer_slope_ZX) { + update = true; + } else if (u_slope_ZX == u_outer_slope_ZX && + slope_ZX > outer_slope_ZX) { + update = true; + } else if (slope_ZX == u_outer_slope_ZX) { + assert(false); + } + } + } + + if (update) { + outer_opp_vid = opp_vid; + outer_slope_YX = slope_YX; + outer_slope_ZX = slope_ZX; + incident_faces = {fid}; + } + } else if (!infinite_slope_detected) + { + // Infinite slope + outer_opp_vid = opp_vid; + infinite_slope_detected = true; + incident_faces = {fid}; + } + }; + + const size_t num_candidate_faces = candidate_faces.size(); + for (size_t i=0; i +template void igl::copyleft::cgal::outer_edge, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, long, Eigen::Matrix >(Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, long&, long&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_edge, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, long, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, long&, long&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_edge, Eigen::Matrix, Eigen::Matrix, long, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, long&, long&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_edge, Eigen::Matrix, Eigen::Matrix, long, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, long&, long&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_edge, Eigen::Matrix, Eigen::Matrix, long, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, long&, long&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_edge, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, long, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, long&, long&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_edge, Eigen::Matrix, Eigen::Matrix, long, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, long&, long&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_edge, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, long, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, long&, long&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_edge, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, long, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, long&, long&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::copyleft::cgal::outer_edge, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, __int64, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, __int64 &, __int64 &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::outer_edge, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, __int64, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, __int64 &, __int64 &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::outer_edge, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, __int64, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, __int64 &, __int64 &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::outer_edge, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, __int64, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, __int64 &, __int64 &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::outer_edge, -1, 3, 0, -1, 3>, class Eigen::Matrix, class Eigen::Matrix, __int64, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::PlainObjectBase, -1, 3, 0, -1, 3>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, __int64 &, __int64 &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::outer_edge, class Eigen::Matrix, class Eigen::Matrix, __int64, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, __int64 &, __int64 &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::outer_edge, class Eigen::Matrix, class Eigen::Matrix, __int64, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, __int64 &, __int64 &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::outer_edge, class Eigen::Matrix, class Eigen::Matrix, __int64, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, __int64 &, __int64 &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::outer_edge, class Eigen::Matrix, class Eigen::Matrix, __int64, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, __int64 &, __int64 &, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/outer_element.h b/src/igl/copyleft/cgal/outer_element.h new file mode 100644 index 0000000000..adaef3464d --- /dev/null +++ b/src/igl/copyleft/cgal/outer_element.h @@ -0,0 +1,83 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_OUTER_ELEMENT_H +#define IGL_COPYLEFT_CGAL_OUTER_ELEMENT_H +#include "../../igl_inline.h" +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Find a vertex that is reachable from infinite without crossing any faces. + // Such vertex is called "outer vertex." + // + // Precondition: The input mesh must have all self-intersection resolved and + // no duplicated vertices. See cgal::remesh_self_intersections.h for how to + // obtain such input. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // I #I list of facets to consider + // Outputs: + // v_index index of outer vertex + // A #A list of facets incident to the outer vertex + template < + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename IndexType, + typename DerivedA + > + IGL_INLINE void outer_vertex( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & I, + IndexType & v_index, + Eigen::PlainObjectBase & A); + // Find an edge that is reachable from infinity without crossing any faces. + // Such edge is called "outer edge." + // + // Precondition: The input mesh must have all self-intersection resolved + // and no duplicated vertices. The correctness of the output depends on + // the fact that there is no edge overlap. See + // cgal::remesh_self_intersections.h for how to obtain such input. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // I #I list of facets to consider + // Outputs: + // v1 index of the first end point of outer edge + // v2 index of the second end point of outer edge + // A #A list of facets incident to the outer edge + template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename IndexType, + typename DerivedA + > + IGL_INLINE void outer_edge( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & I, + IndexType & v1, + IndexType & v2, + Eigen::PlainObjectBase & A); + + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "outer_element.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/outer_facet.cpp b/src/igl/copyleft/cgal/outer_facet.cpp new file mode 100644 index 0000000000..4525dd8568 --- /dev/null +++ b/src/igl/copyleft/cgal/outer_facet.cpp @@ -0,0 +1,180 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "outer_facet.h" +#include "outer_element.h" +#include "order_facets_around_edge.h" +#include +#include + +template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename IndexType + > +IGL_INLINE void igl::copyleft::cgal::outer_facet( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & I, + IndexType & f, + bool & flipped) { + + // Algorithm: + // + // 1. Find an outer edge (s, d). + // + // 2. Order adjacent facets around this edge. Because the edge is an + // outer edge, there exists a plane passing through it such that all its + // adjacent facets lie on the same side. The implementation of + // order_facets_around_edge() will find a natural start facet such that + // The first and last facets according to this order are on the outside. + // + // 3. Because the vertex s is an outer vertex by construction (see + // implemnetation of outer_edge()). The first adjacent facet is facing + // outside (i.e. flipped=false) if it has positive X normal component. + // If it has zero normal component, it is facing outside if it contains + // directed edge (s, d). + + //typedef typename DerivedV::Scalar Scalar; + typedef typename DerivedV::Index Index; + + Index s,d; + Eigen::Matrix incident_faces; + outer_edge(V, F, I, s, d, incident_faces); + assert(incident_faces.size() > 0); + + auto convert_to_signed_index = [&](size_t fid) -> int{ + if ((F(fid, 0) == s && F(fid, 1) == d) || + (F(fid, 1) == s && F(fid, 2) == d) || + (F(fid, 2) == s && F(fid, 0) == d) ) { + return int(fid+1) * -1; + } else { + return int(fid+1); + } + }; + + auto signed_index_to_index = [&](int signed_id) -> size_t { + return size_t(abs(signed_id) - 1); + }; + + std::vector adj_faces(incident_faces.size()); + std::transform(incident_faces.data(), + incident_faces.data() + incident_faces.size(), + adj_faces.begin(), + convert_to_signed_index); + + DerivedV pivot_point = V.row(s); + pivot_point(0, 0) += 1.0; + + Eigen::VectorXi order; + order_facets_around_edge(V, F, s, d, adj_faces, pivot_point, order); + + f = signed_index_to_index(adj_faces[order[0]]); + flipped = adj_faces[order[0]] > 0; +} + + + +template< + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DerivedI, + typename IndexType + > +IGL_INLINE void igl::copyleft::cgal::outer_facet( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & N, + const Eigen::PlainObjectBase & I, + IndexType & f, + bool & flipped) { + // Algorithm: + // Find an outer edge. + // Find the incident facet with the largest absolute X normal component. + // If there is a tie, keep the one with positive X component. + // If there is still a tie, pick the face with the larger signed index + // (flipped face has negative index). + typedef typename DerivedV::Scalar Scalar; + typedef typename DerivedV::Index Index; + const size_t INVALID = std::numeric_limits::max(); + + Index v1,v2; + Eigen::Matrix incident_faces; + outer_edge(V, F, I, v1, v2, incident_faces); + assert(incident_faces.size() > 0); + + auto generic_fabs = [&](const Scalar& val) -> const Scalar { + if (val >= 0) return val; + else return -val; + }; + + Scalar max_nx = 0; + size_t outer_fid = INVALID; + const size_t num_incident_faces = incident_faces.size(); + for (size_t i=0; i generic_fabs(max_nx)) { + max_nx = nx; + outer_fid = fid; + } else if (nx == -max_nx && nx > 0) { + max_nx = nx; + outer_fid = fid; + } else if (nx == max_nx) { + if ((max_nx >= 0 && outer_fid < fid) || + (max_nx < 0 && outer_fid > fid)) { + max_nx = nx; + outer_fid = fid; + } + } + } + } + + assert(outer_fid != INVALID); + f = outer_fid; + flipped = max_nx < 0; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::cgal::outer_facet, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, unsigned long>(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned long&, bool&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::outer_facet, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::outer_facet, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, unsigned long>(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned long&, bool&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::outer_facet, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::outer_facet, Eigen::Matrix, Eigen::Matrix, unsigned long>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned long&, bool&); +#include +#include +template void igl::copyleft::cgal::outer_facet, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +template void igl::copyleft::cgal::outer_facet, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, unsigned long>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned long&, bool&); +template void igl::copyleft::cgal::outer_facet, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +template void igl::copyleft::cgal::outer_facet, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +template void igl::copyleft::cgal::outer_facet, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +template void igl::copyleft::cgal::outer_facet, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +template void igl::copyleft::cgal::outer_facet, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +template void igl::copyleft::cgal::outer_facet, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, unsigned long>(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned long&, bool&); +template void igl::copyleft::cgal::outer_facet, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +template void igl::copyleft::cgal::outer_facet, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +template void igl::copyleft::cgal::outer_facet, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +#ifdef WIN32 +template void igl::copyleft::cgal::outer_facet, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, unsigned __int64>(class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, unsigned __int64 &, bool &); +template void igl::copyleft::cgal::outer_facet, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, unsigned __int64>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, unsigned __int64 &, bool &); +template void igl::copyleft::cgal::outer_facet, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, unsigned __int64>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, unsigned __int64 &, bool &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/outer_facet.h b/src/igl/copyleft/cgal/outer_facet.h new file mode 100644 index 0000000000..b025a94ab1 --- /dev/null +++ b/src/igl/copyleft/cgal/outer_facet.h @@ -0,0 +1,91 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_COPYLEFT_CGAL_OUTER_FACET_H +#define IGL_COPYLEFT_CGAL_OUTER_FACET_H +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Find a facet that is reachable from infinity without crossing any faces. + // Such facet is called "outer facet." + // + // Precondition: The input mesh must have all self-intersection resolved. I.e + // there is no duplicated vertices, no overlapping edge and no intersecting + // faces (the only exception is there could be topologically duplicated faces). + // See cgal::remesh_self_intersections.h for how to obtain such input. + // + // This function differ from igl::outer_facet() in the fact this + // function is more robust because it does not rely on facet normals. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // I #I list of facets to consider + // Outputs: + // f Index of the outer facet. + // flipped true iff the normal of f points inwards. + template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename IndexType + > + IGL_INLINE void outer_facet( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & I, + IndexType & f, + bool & flipped); + + // Find a facet that is reachable from infinity without crossing any faces. + // Such facet is called "outer facet." + // + // Precondition: The input mesh must have all self-intersection resolved. + // I.e there is no duplicated vertices, no overlapping edge and no + // intersecting faces (the only exception is there could be topologically + // duplicated faces). See cgal::remesh_self_intersections.h for how to + // obtain such input. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // N #N by 3 list of face normals + // I #I list of facets to consider + // Outputs: + // f Index of the outer facet. + // flipped true iff the normal of f points inwards. + template< + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DerivedI, + typename IndexType + > + IGL_INLINE void outer_facet( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & N, + const Eigen::PlainObjectBase & I, + IndexType & f, + bool & flipped); + + } + + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "outer_facet.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/outer_hull.cpp b/src/igl/copyleft/cgal/outer_hull.cpp new file mode 100644 index 0000000000..71586f7074 --- /dev/null +++ b/src/igl/copyleft/cgal/outer_hull.cpp @@ -0,0 +1,535 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "outer_hull.h" +#include "extract_cells.h" +#include "remesh_self_intersections.h" +#include "assign.h" +#include "../../remove_unreferenced.h" + +#include +#include +#include +#include +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedHV, + typename DerivedHF, + typename DerivedJ, + typename Derivedflip> +IGL_INLINE void igl::copyleft::cgal::outer_hull( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & HV, + Eigen::PlainObjectBase & HF, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & flip) +{ + // Exact types + typedef CGAL::Epeck Kernel; + typedef Kernel::FT ExactScalar; + typedef + Eigen::Matrix< + ExactScalar, + Eigen::Dynamic, + Eigen::Dynamic, + DerivedHV::IsRowMajor> + MatrixXES; + // Remesh self-intersections + MatrixXES Vr; + DerivedHF Fr; + DerivedJ Jr; + { + RemeshSelfIntersectionsParam params; + params.stitch_all = true; + Eigen::VectorXi I; + Eigen::MatrixXi IF; + remesh_self_intersections(V, F, params, Vr, Fr, IF, Jr, I); + // Merge coinciding vertices into non-manifold vertices. + std::for_each(Fr.data(), Fr.data()+Fr.size(), + [&I](typename DerivedHF::Scalar& a) { a=I[a]; }); + // Remove unreferenced vertices. + Eigen::VectorXi UIM; + remove_unreferenced(MatrixXES(Vr),DerivedHF(Fr), Vr, Fr, UIM); + } + // Extract cells for each face + Eigen::MatrixXi C; + extract_cells(Vr,Fr,C); + // Extract faces on ambient cell + int num_outer = 0; + for(int i = 0;i +#include +#include +#include +#include +#include +#include +//#define IGL_OUTER_HULL_DEBUG + +template < + typename DerivedV, + typename DerivedF, + typename DerivedG, + typename DerivedJ, + typename Derivedflip> +IGL_INLINE void igl::copyleft::cgal::outer_hull_legacy( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & flip) +{ +#ifdef IGL_OUTER_HULL_DEBUG + std::cerr << "Extracting outer hull" << std::endl; +#endif + using namespace Eigen; + using namespace std; + typedef typename DerivedF::Index Index; + Matrix C; + typedef Matrix MatrixXV; + //typedef Matrix MatrixXF; + typedef Matrix MatrixXG; + typedef Matrix MatrixXJ; + const Index m = F.rows(); + + // UNUSED: + //const auto & duplicate_simplex = [&F](const int f, const int g)->bool + //{ + // return + // (F(f,0) == F(g,0) && F(f,1) == F(g,1) && F(f,2) == F(g,2)) || + // (F(f,1) == F(g,0) && F(f,2) == F(g,1) && F(f,0) == F(g,2)) || + // (F(f,2) == F(g,0) && F(f,0) == F(g,1) && F(f,1) == F(g,2)) || + // (F(f,0) == F(g,2) && F(f,1) == F(g,1) && F(f,2) == F(g,0)) || + // (F(f,1) == F(g,2) && F(f,2) == F(g,1) && F(f,0) == F(g,0)) || + // (F(f,2) == F(g,2) && F(f,0) == F(g,1) && F(f,1) == F(g,0)); + //}; + +#ifdef IGL_OUTER_HULL_DEBUG + cout<<"outer hull..."< MatrixX2I; + typedef Matrix VectorXI; + //typedef Matrix Vector3F; + MatrixX2I E,uE; + VectorXI EMAP; + vector > uE2E; + unique_edge_map(F,E,uE,EMAP,uE2E); +#ifdef IGL_OUTER_HULL_DEBUG + for (size_t ui=0; ui > uE2oE; + std::vector > uE2C; + order_facets_around_edges(V, F, uE, uE2E, uE2oE, uE2C); + uE2E = uE2oE; + VectorXI diIM(3*m); + for (auto ue : uE2E) { + for (size_t i=0; i > > TT,_1; + triangle_triangle_adjacency(E,EMAP,uE2E,false,TT,_1); + VectorXI counts; +#ifdef IGL_OUTER_HULL_DEBUG + cout<<"facet components..."< FH(m,false); + vector EH(3*m,false); + vector vG(ncc); + vector vJ(ncc); + vector vIM(ncc); + //size_t face_count = 0; + for(size_t id = 0;id g(ncc,0); + // place order of each face in its respective component + for(Index f = 0;f Q; + Q.push(f+0*m); + Q.push(f+1*m); + Q.push(f+2*m); + flip(f) = f_flip; + //std::cout << "face " << face_count++ << ": " << f << std::endl; + //std::cout << "f " << F.row(f).array()+1 << std::endl; + //cout<<"flip("< "<<(nf+1)<<", |"<< + // di[EMAP(e)][diIM(e)]<<" - "< "<<(nf+1)<= 0) + { + max_ne = uE2E[EMAP(e)][nfei]; + } + + if(max_ne>=0) + { + // face of neighbor + const int nf = max_ne%m; +#ifdef IGL_OUTER_HULL_DEBUG + if(!FH[nf]) + { + // first time seeing face + cout<<(f+1)<<" --> "<<(nf+1)< & V, + const MatrixXG & A, + const MatrixXG & B)->bool + { + const auto & bounding_box = []( + const Eigen::PlainObjectBase & V, + const MatrixXG & F)-> + DerivedV + { + DerivedV BB(2,3); + BB<< + 1e26,1e26,1e26, + -1e26,-1e26,-1e26; + const size_t m = F.rows(); + for(size_t f = 0;f0 || + (ABB.row(0)-BBB.row(1)).maxCoeff()>0 ) + { + // bounding boxes do not overlap + return false; + } else { + return true; + } + }; + + // Reject components which are completely inside other components + vector keep(ncc,true); + size_t nG = 0; + // This is O( ncc * ncc * m) + for(size_t id = 0;id unresolved; + for(size_t oid = 0;oid, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_hull_legacy< Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > &, Eigen::PlainObjectBase > &, Eigen::PlainObjectBase > &); +template void igl::copyleft::cgal::outer_hull_legacy< Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_hull_legacy, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::outer_hull_legacy, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/outer_hull.h b/src/igl/copyleft/cgal/outer_hull.h new file mode 100644 index 0000000000..191a8d4927 --- /dev/null +++ b/src/igl/copyleft/cgal/outer_hull.h @@ -0,0 +1,84 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_OUTER_HULL_H +#define IGL_COPYLEFT_CGAL_OUTER_HULL_H +#include "../../igl_inline.h" +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Compute the "outer hull" of a piecewise constant winding number induce + // triangle mesh (V,F). + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // Outputs: + // HV #HV by 3 list of output vertex positions + // HF #HF by 3 list of output triangle indices into HV + // J #HF list of indices into F + // flip #HF list of whether facet was flipped when added to HF + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedHV, + typename DerivedHF, + typename DerivedJ, + typename Derivedflip> + IGL_INLINE void outer_hull( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & HV, + Eigen::PlainObjectBase & HF, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & flip); + // Compute the "outer hull" of a potentially non-manifold mesh (V,F) whose + // intersections have been "resolved" (e.g. using `cork` or + // `igl::copyleft::cgal::selfintersect`). The outer hull is defined to be all facets + // (regardless of orientation) for which there exists some path from infinity + // to the face without intersecting any other facets. For solids, this is the + // surface of the solid. In general this includes any thin "wings" or + // "flaps". This implementation largely follows Section 3.6 of "Direct + // repair of self-intersecting meshes" [Attene 2014]. + // + // Note: This doesn't require the input mesh to be piecewise constant + // winding number, but won't handle multiple non-nested connected + // components. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // Outputs: + // G #G by 3 list of output triangle indices into V + // J #G list of indices into F + // flip #F list of whether facet was added to G **and** flipped orientation + // (false for faces not added to G) + template < + typename DerivedV, + typename DerivedF, + typename DerivedG, + typename DerivedJ, + typename Derivedflip> + IGL_INLINE void outer_hull_legacy( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & flip); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "outer_hull.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/peel_outer_hull_layers.cpp b/src/igl/copyleft/cgal/peel_outer_hull_layers.cpp new file mode 100644 index 0000000000..73ae07eeac --- /dev/null +++ b/src/igl/copyleft/cgal/peel_outer_hull_layers.cpp @@ -0,0 +1,124 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "peel_outer_hull_layers.h" +#include "outer_hull.h" +#include "../../LinSpaced.h" +#include +#include +//#define IGL_PEEL_OUTER_HULL_LAYERS_DEBUG +#ifdef IGL_PEEL_OUTER_HULL_LAYERS_DEBUG +#include "../../writePLY.h" +#include "../../writeDMAT.h" +#include "../../STR.h" +#endif + +template < + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename Derivedflip> +IGL_INLINE size_t igl::copyleft::cgal::peel_outer_hull_layers( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & flip) +{ + using namespace Eigen; + using namespace std; + typedef typename DerivedF::Index Index; + typedef Matrix MatrixXF; + typedef Matrix MatrixXI; + typedef Matrix MatrixXflip; + const Index m = F.rows(); +#ifdef IGL_PEEL_OUTER_HULL_LAYERS_DEBUG + cout<<"peel outer hull layers..."<(m,0,m-1); + // This is O(n * layers) + MatrixXI P(m,1); + Index iter = 0; + while(Fr.size() > 0) + { + assert(Fr.rows() == IM.rows()); + // Compute outer hull of current Fr + MatrixXF Fo; + MatrixXI Jo; + MatrixXflip flipr; +#ifdef IGL_PEEL_OUTER_HULL_LAYERS_DEBUG + { + cout<<"calling outer hull..." << iter < in_outer(Fr.rows(),false); + for(Index g = 0;g +#include +template unsigned long igl::copyleft::cgal::peel_outer_hull_layers, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template size_t igl::copyleft::cgal::peel_outer_hull_layers, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/peel_outer_hull_layers.h b/src/igl/copyleft/cgal/peel_outer_hull_layers.h new file mode 100644 index 0000000000..ee2752b296 --- /dev/null +++ b/src/igl/copyleft/cgal/peel_outer_hull_layers.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_PEEL_OUTER_HULL_LAYERS_H +#define IGL_COPYLEFT_CGAL_PEEL_OUTER_HULL_LAYERS_H +#include "../../igl_inline.h" +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Computes necessary generic information for boolean operations by + // successively "peeling" off the "outer hull" of a mesh (V,F) resulting from + // "resolving" all (self-)intersections. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // Outputs: + // I #F list of which peel Iation a facet belongs + // flip #F list of whether a facet's orientation was flipped when facet + // "peeled" into its associated outer hull layer. + // Returns number of peels + template < + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename Derivedflip> + IGL_INLINE size_t peel_outer_hull_layers( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & flip); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "peel_outer_hull_layers.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/peel_winding_number_layers.cpp b/src/igl/copyleft/cgal/peel_winding_number_layers.cpp new file mode 100644 index 0000000000..2bb71a7800 --- /dev/null +++ b/src/igl/copyleft/cgal/peel_winding_number_layers.cpp @@ -0,0 +1,33 @@ +#include "peel_winding_number_layers.h" + +#include + +#include "propagate_winding_numbers.h" + +template< +typename DerivedV, +typename DerivedF, +typename DerivedW > +IGL_INLINE size_t igl::copyleft::cgal::peel_winding_number_layers( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase& W) { + const size_t num_faces = F.rows(); + Eigen::VectorXi labels(num_faces); + labels.setZero(); + + Eigen::MatrixXi winding_numbers; + igl::copyleft::cgal::propagate_winding_numbers(V, F, labels, winding_numbers); + assert(winding_numbers.rows() == num_faces); + assert(winding_numbers.cols() == 2); + + int min_w = winding_numbers.minCoeff(); + int max_w = winding_numbers.maxCoeff(); + assert(max_w > min_w); + + W.resize(num_faces, 1); + for (size_t i=0; i + +namespace igl { + namespace copyleft { + namespace cgal { + template< + typename DerivedV, + typename DerivedF, + typename DerivedW > + IGL_INLINE size_t peel_winding_number_layers( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase& W); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "peel_winding_number_layers.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/piecewise_constant_winding_number.cpp b/src/igl/copyleft/cgal/piecewise_constant_winding_number.cpp new file mode 100644 index 0000000000..0156edbe2c --- /dev/null +++ b/src/igl/copyleft/cgal/piecewise_constant_winding_number.cpp @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "piecewise_constant_winding_number.h" +#include "../../piecewise_constant_winding_number.h" +#include "remesh_self_intersections.h" +#include +#include + +template < typename DerivedV, typename DerivedF> +IGL_INLINE bool igl::copyleft::cgal::piecewise_constant_winding_number( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F) +{ + Eigen::Matrix VV; + Eigen::Matrix FF; + Eigen::Matrix IF; + Eigen::Matrix J; + Eigen::Matrix UIM,IM; + // resolve intersections + remesh_self_intersections(V,F,{false,false,true},VV,FF,IF,J,IM); + return igl::piecewise_constant_winding_number(FF); +} diff --git a/src/igl/copyleft/cgal/piecewise_constant_winding_number.h b/src/igl/copyleft/cgal/piecewise_constant_winding_number.h new file mode 100644 index 0000000000..2b93913b33 --- /dev/null +++ b/src/igl/copyleft/cgal/piecewise_constant_winding_number.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_PIECEWISE_CONSTANT_WINDING_NUMBER_H +#define IGL_COPYLEFT_CGAL_PIECEWISE_CONSTANT_WINDING_NUMBER_H +#include "../../igl_inline.h" +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // PIECEWISE_CONSTANT_WINDING_NUMBER Determine if a given mesh induces a + // piecewise constant winding number field: Is this mesh valid input to + // solid set operations. + // + // Inputs: + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of triangle indices into V + // Returns true if the mesh _combinatorially_ induces a piecewise + // constant winding number field. + template < + typename DerivedV, + typename DerivedF> + IGL_INLINE bool piecewise_constant_winding_number( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase& F); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "piecewise_constant_winding_number.cpp" +#endif +#endif + diff --git a/src/igl/copyleft/cgal/point_areas.cpp b/src/igl/copyleft/cgal/point_areas.cpp new file mode 100644 index 0000000000..bb98bac3c7 --- /dev/null +++ b/src/igl/copyleft/cgal/point_areas.cpp @@ -0,0 +1,181 @@ +#include "point_areas.h" +#include "delaunay_triangulation.h" + +#include "../../colon.h" +#include "../../slice.h" +#include "../../slice_mask.h" +#include "../../parallel_for.h" + +#include "CGAL/Exact_predicates_inexact_constructions_kernel.h" +#include "CGAL/Triangulation_vertex_base_with_info_2.h" +#include "CGAL/Triangulation_data_structure_2.h" +#include "CGAL/Delaunay_triangulation_2.h" + + + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef CGAL::Triangulation_vertex_base_with_info_2 Vb; +typedef CGAL::Triangulation_data_structure_2 Tds; +typedef CGAL::Delaunay_triangulation_2 Delaunay; +typedef Kernel::Point_2 Point; + +namespace igl { + namespace copyleft{ + namespace cgal{ + + template + IGL_INLINE void point_areas( + const Eigen::MatrixBase& P, + const Eigen::MatrixBase& I, + const Eigen::MatrixBase& N, + Eigen::PlainObjectBase & A) + { + Eigen::MatrixXd T; + point_areas(P,I,N,A,T); + } + + + template + IGL_INLINE void point_areas( + const Eigen::MatrixBase& P, + const Eigen::MatrixBase& I, + const Eigen::MatrixBase& N, + Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & T) + { + typedef typename DerivedP::Scalar real; + typedef typename DerivedN::Scalar scalarN; + typedef typename DerivedA::Scalar scalarA; + typedef Eigen::Matrix RowVec3; + typedef Eigen::Matrix RowVec2; + + typedef Eigen::Matrix MatrixP; + typedef Eigen::Matrix MatrixN; + typedef Eigen::Matrix VecotorO; + typedef Eigen::Matrix MatrixI; + + + + const int n = P.rows(); + + assert(P.cols() == 3 && "P must have exactly 3 columns"); + assert(P.rows() == N.rows() + && "P and N must have the same number of rows"); + assert(P.rows() == I.rows() + && "P and I must have the same number of rows"); + + A.setZero(n,1); + T.setZero(n,3); + igl::parallel_for(P.rows(),[&](int i) + { + MatrixI neighbor_index = I.row(i); + MatrixP neighbors; + igl::slice(P,neighbor_index,1,neighbors); + if(N.rows() && neighbors.rows() > 1){ + MatrixN neighbor_normals; + igl::slice(N,neighbor_index,1,neighbor_normals); + Eigen::Matrix poi_normal = neighbor_normals.row(0); + Eigen::Matrix dotprod = + poi_normal(0)*neighbor_normals.col(0) + + poi_normal(1)*neighbor_normals.col(1) + + poi_normal(2)*neighbor_normals.col(2); + Eigen::Array keep = dotprod.array() > 0; + igl::slice_mask(Eigen::MatrixXd(neighbors),keep,1,neighbors); + } + if(neighbors.rows() <= 2){ + A(i) = 0; + } else { + //subtract the mean from neighbors, then take svd, + //the scores will be U*S. This is our pca plane fitting + RowVec3 mean = neighbors.colwise().mean(); + MatrixP mean_centered = neighbors.rowwise() - mean; + Eigen::JacobiSVD svd(mean_centered, + Eigen::ComputeThinU | Eigen::ComputeThinV); + MatrixP scores = svd.matrixU() * svd.singularValues().asDiagonal(); + + T.row(i) = svd.matrixV().col(2).transpose(); + if(T.row(i).dot(N.row(i)) < 0){ + T.row(i) *= -1; + } + + MatrixP plane; + igl::slice(scores,igl::colon(0,scores.rows()-1), + igl::colon(0,1),plane); + + std::vector< std::pair > points; + //This is where we obtain a delaunay triangulation of the points + for(unsigned iter = 0; iter < plane.rows(); iter++){ + points.push_back( std::make_pair( + Point(plane(iter,0),plane(iter,1)), iter ) ); + } + Delaunay triangulation; + triangulation.insert(points.begin(),points.end()); + Eigen::MatrixXi F(triangulation.number_of_faces(),3); + int f_row = 0; + for(Delaunay::Finite_faces_iterator fit = + triangulation.finite_faces_begin(); + fit != triangulation.finite_faces_end(); ++fit) { + Delaunay::Face_handle face = fit; + F.row(f_row) = Eigen::RowVector3i((int)face->vertex(0)->info(), + (int)face->vertex(1)->info(), + (int)face->vertex(2)->info()); + f_row++; + } + + //Here we calculate the voronoi area of the point + scalarA area_accumulator = 0; + for(int f = 0; f < F.rows(); f++){ + int X = -1; + for(int face_iter = 0; face_iter < 3; face_iter++){ + if(F(f,face_iter) == 0){ + X = face_iter; + } + } + if(X >= 0){ + //Triangle XYZ with X being the point we want the area of + int Y = (X+1)%3; + int Z = (X+2)%3; + scalarA x = (plane.row(F(f,Y))-plane.row(F(f,Z))).norm(); + scalarA y = (plane.row(F(f,X))-plane.row(F(f,Z))).norm(); + scalarA z = (plane.row(F(f,Y))-plane.row(F(f,X))).norm(); + scalarA cosX = (z*z + y*y - x*x)/(2*y*z); + scalarA cosY = (z*z + x*x - y*y)/(2*x*z); + scalarA cosZ = (x*x + y*y - z*z)/(2*y*x); + Eigen::Matrix barycentric; + barycentric << x*cosX, y*cosY, z*cosZ; + barycentric /= (barycentric(0)+barycentric(1)+barycentric(2)); + + //TODO: to make numerically stable, reorder so that x≥y≥z: + scalarA full_area = 0.25*std::sqrt( + (x+(y+z))*(z-(x-y))*(z+(x-y))*(x+(y-z))); + Eigen::Matrix partial_area = + barycentric * full_area; + if(cosX < 0){ + area_accumulator += 0.5*full_area; + } else if (cosY < 0 || cosZ < 0){ + area_accumulator += 0.25*full_area; + } else { + area_accumulator += (partial_area(1) + partial_area(2))/2; + } + } + } + + if(std::isfinite(area_accumulator)){ + A(i) = area_accumulator; + } else { + A(i) = 0; + } + } + },1000); + } + } + } +} + + + + diff --git a/src/igl/copyleft/cgal/point_areas.h b/src/igl/copyleft/cgal/point_areas.h new file mode 100644 index 0000000000..ec03664886 --- /dev/null +++ b/src/igl/copyleft/cgal/point_areas.h @@ -0,0 +1,78 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Gavin Barill +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/ + +#ifndef IGL_POINT_AREAS_H +#define IGL_POINT_AREAS_H +#include "../../igl_inline.h" +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Given a 3D set of points P, each with a list of k-nearest-neighbours, + // estimate the geodesic voronoi area associated with each point. + // + // The k nearest neighbours may be known from running igl::knn_octree on + // the output data from igl::octree. We reccomend using a k value + // between 15 and 20 inclusive for accurate area estimation. + // + // N is used filter the neighbours, to ensure area estimation only occurs + // using neighbors that are on the same side of the surface (ie for thin + // sheets), as well as to solve the orientation ambiguity of the tangent + // plane normal. + // + // Note: This function *should* be implemented by pre-filtering I, rather + // than filtering in this function using N. In this case, the function + // would only take P and I as input. + // + // Inputs: + // P #P by 3 list of point locations + // I #P by k list of k-nearest-neighbor indices into P + // N #P by 3 list of point normals + // Outputs: + // A #P list of estimated areas + template + IGL_INLINE void point_areas( + const Eigen::MatrixBase& P, + const Eigen::MatrixBase& I, + const Eigen::MatrixBase& N, + Eigen::PlainObjectBase & A); + + // This version can be used to output the tangent plane normal at each + // point. Since we area already fitting a plane to each point's neighbour + // set, the tangent plane normals come "for free" + // + // Inputs: + // P #P by 3 list of point locations + // I #P by k list of k-nearest-neighbor indices into P + // N #P by 3 list of point normals + // Outputs: + // A #P list of estimated areas + // T #P by 3 list of tangent plane normals for each point + template + IGL_INLINE void point_areas( + const Eigen::MatrixBase& P, + const Eigen::MatrixBase& I, + const Eigen::MatrixBase& N, + Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & T); + + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "point_areas.cpp" +#endif + +#endif + diff --git a/src/igl/copyleft/cgal/point_mesh_squared_distance.cpp b/src/igl/copyleft/cgal/point_mesh_squared_distance.cpp new file mode 100644 index 0000000000..18b5706aea --- /dev/null +++ b/src/igl/copyleft/cgal/point_mesh_squared_distance.cpp @@ -0,0 +1,138 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "point_mesh_squared_distance.h" +#include "mesh_to_cgal_triangle_list.h" +#include "assign_scalar.h" + +template < + typename Kernel, + typename DerivedP, + typename DerivedV, + typename DerivedF, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> +IGL_INLINE void igl::copyleft::cgal::point_mesh_squared_distance( + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C) +{ + using namespace std; + typedef CGAL::Triangle_3 Triangle_3; + typedef typename std::vector::iterator Iterator; + typedef CGAL::AABB_triangle_primitive Primitive; + typedef CGAL::AABB_traits AABB_triangle_traits; + typedef CGAL::AABB_tree Tree; + Tree tree; + vector T; + point_mesh_squared_distance_precompute(V,F,tree,T); + return point_mesh_squared_distance(P,tree,T,sqrD,I,C); +} + +template +IGL_INLINE void igl::copyleft::cgal::point_mesh_squared_distance_precompute( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + CGAL::AABB_tree< + CGAL::AABB_traits >::iterator + > + > + > & tree, + std::vector > & T) +{ + using namespace std; + + typedef CGAL::Triangle_3 Triangle_3; + typedef CGAL::Point_3 Point_3; + typedef typename std::vector::iterator Iterator; + typedef CGAL::AABB_triangle_primitive Primitive; + typedef CGAL::AABB_traits AABB_triangle_traits; + typedef CGAL::AABB_tree Tree; + + // Must be 3D + assert(V.cols() == 3); + // Must be triangles + assert(F.cols() == 3); + + // WTF ALERT!!!! + // + // There's a bug in clang probably or at least in cgal. Without calling this + // line (I guess invoking some compilation from ), clang will vomit + // errors inside CGAL. + // + // http://stackoverflow.com/questions/27748442/is-clangs-c11-support-reliable + T.reserve(0); + + // Make list of cgal triangles + mesh_to_cgal_triangle_list(V,F,T); + tree.clear(); + tree.insert(T.begin(),T.end()); + tree.accelerate_distance_queries(); + // accelerate_distance_queries doesn't seem actually to do _all_ of the + // precomputation. the tree (despite being const) will still do more + // precomputation and reorganizing on the first call of `closest_point` or + // `closest_point_and_primitive`. Therefore, call it once here. + tree.closest_point_and_primitive(Point_3(0,0,0)); +} + +template < + typename Kernel, + typename DerivedP, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> +IGL_INLINE void igl::copyleft::cgal::point_mesh_squared_distance( + const Eigen::PlainObjectBase & P, + const CGAL::AABB_tree< + CGAL::AABB_traits >::iterator + > + > + > & tree, + const std::vector > & T, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C) +{ + typedef CGAL::Triangle_3 Triangle_3; + typedef typename std::vector::iterator Iterator; + typedef CGAL::AABB_triangle_primitive Primitive; + typedef CGAL::AABB_traits AABB_triangle_traits; + typedef CGAL::AABB_tree Tree; + typedef typename Tree::Point_and_primitive_id Point_and_primitive_id; + typedef CGAL::Point_3 Point_3; + assert(P.cols() == 3); + const int n = P.rows(); + sqrD.resize(n,1); + I.resize(n,1); + C.resize(n,P.cols()); + for(int p = 0;p < n;p++) + { + Point_3 query(P(p,0),P(p,1),P(p,2)); + // Find closest point and primitive id + Point_and_primitive_id pp = tree.closest_point_and_primitive(query); + Point_3 closest_point = pp.first; + assign_scalar(closest_point[0],C(p,0)); + assign_scalar(closest_point[1],C(p,1)); + assign_scalar(closest_point[2],C(p,2)); + assign_scalar((closest_point-query).squared_length(),sqrD(p)); + I(p) = pp.second - T.begin(); + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::copyleft::cgal::point_mesh_squared_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::point_mesh_squared_distance, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, 1, 0, -1, 1>, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/point_mesh_squared_distance.h b/src/igl/copyleft/cgal/point_mesh_squared_distance.h new file mode 100644 index 0000000000..b525bddda2 --- /dev/null +++ b/src/igl/copyleft/cgal/point_mesh_squared_distance.h @@ -0,0 +1,104 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_POINT_MESH_SQUARED_DISTANCE_H +#define IGL_COPYLEFT_CGAL_POINT_MESH_SQUARED_DISTANCE_H +#include "../../igl_inline.h" +#include +#include +#include "CGAL_includes.hpp" +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Compute distances from a set of points P to a triangle mesh (V,F) + // + // Templates: + // Kernal CGAL computation and construction kernel (e.g. + // CGAL::Simple_cartesian) + // Inputs: + // P #P by 3 list of query point positions + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // Outputs: + // sqrD #P list of smallest squared distances + // I #P list of facet indices corresponding to smallest distances + // C #P by 3 list of closest points + // + // Known bugs: This only computes distances to triangles. So unreferenced + // vertices and degenerate triangles (segments) are ignored. + template < + typename Kernel, + typename DerivedP, + typename DerivedV, + typename DerivedF, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> + IGL_INLINE void point_mesh_squared_distance( + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C); + // Probably can do this in a way that we don't pass around `tree` and `T` + // + // Outputs: + // tree CGAL's AABB tree + // T list of CGAL triangles in order of F (for determining which was found + // in computation) + template < + typename Kernel, + typename DerivedV, + typename DerivedF + > + IGL_INLINE void point_mesh_squared_distance_precompute( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + CGAL::AABB_tree< + CGAL::AABB_traits >::iterator + > + > + > & tree, + std::vector > & T); + // Inputs: + // see above + // Outputs: + // see above + template < + typename Kernel, + typename DerivedP, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> + IGL_INLINE void point_mesh_squared_distance( + const Eigen::PlainObjectBase & P, + const CGAL::AABB_tree< + CGAL::AABB_traits >::iterator + > + > + > & tree, + const std::vector > & T, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "point_mesh_squared_distance.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/point_segment_squared_distance.cpp b/src/igl/copyleft/cgal/point_segment_squared_distance.cpp new file mode 100644 index 0000000000..257206ae6d --- /dev/null +++ b/src/igl/copyleft/cgal/point_segment_squared_distance.cpp @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "point_segment_squared_distance.h" + +template < typename Kernel> +IGL_INLINE void igl::copyleft::cgal::point_segment_squared_distance( + const CGAL::Point_3 & P1, + const CGAL::Segment_3 & S2, + CGAL::Point_3 & P2, + typename Kernel::FT & d) +{ + if(S2.is_degenerate()) + { + P2 = S2.source(); + d = (P1-P2).squared_length(); + return; + } + // http://stackoverflow.com/a/1501725/148668 + const auto sqr_len = S2.squared_length(); + assert(sqr_len != 0); + const auto & V = S2.source(); + const auto & W = S2.target(); + const auto t = (P1-V).dot(W-V)/sqr_len; + if(t<0) + { + P2 = V; + }else if(t>1) + { + P2 = W; + }else + { + P2 = V + t*(W-V); + } + d = (P1-P2).squared_length(); +} + diff --git a/src/igl/copyleft/cgal/point_segment_squared_distance.h b/src/igl/copyleft/cgal/point_segment_squared_distance.h new file mode 100644 index 0000000000..eac628b2ab --- /dev/null +++ b/src/igl/copyleft/cgal/point_segment_squared_distance.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_POINT_SEGMENT_SQUARED_DISTANCE_H +#define IGL_COPYLEFT_CGAL_POINT_SEGMENT_SQUARED_DISTANCE_H +#include "../../igl_inline.h" +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Given a point P1 and segment S2 find the points on each of closest + // approach and the squared distance thereof. + // + // Inputs: + // P1 point + // S2 segment + // Outputs: + // P2 point on S2 closest to P1 + // d distance betwee P1 and S2 + template < typename Kernel> + IGL_INLINE void point_segment_squared_distance( + const CGAL::Point_3 & P1, + const CGAL::Segment_3 & S2, + CGAL::Point_3 & P2, + typename Kernel::FT & d + ); + + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "point_segment_squared_distance.cpp" +#endif + +#endif + diff --git a/src/igl/copyleft/cgal/point_solid_signed_squared_distance.cpp b/src/igl/copyleft/cgal/point_solid_signed_squared_distance.cpp new file mode 100644 index 0000000000..7aef2efd19 --- /dev/null +++ b/src/igl/copyleft/cgal/point_solid_signed_squared_distance.cpp @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "point_solid_signed_squared_distance.h" +#include "points_inside_component.h" +#include "point_mesh_squared_distance.h" +#include "../../list_to_matrix.h" +#include "../../slice_mask.h" +#include +#include + +template < + typename DerivedQ, + typename DerivedVB, + typename DerivedFB, + typename DerivedD> +IGL_INLINE void igl::copyleft::cgal::point_solid_signed_squared_distance( + const Eigen::PlainObjectBase & Q, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + Eigen::PlainObjectBase & D) +{ + // compute unsigned distances + Eigen::VectorXi I; + DerivedVB C; + point_mesh_squared_distance(Q,VB,FB,D,I,C); + // Collect queries that have non-zero distance + Eigen::Array NZ = D.array()!=0; + // Compute sign for non-zero distance queries + DerivedQ QNZ; + slice_mask(Q,NZ,1,QNZ); + Eigen::Array DNZ; + igl::copyleft::cgal::points_inside_component(VB,FB,QNZ,DNZ); + // Apply sign to distances + DerivedD S = DerivedD::Zero(Q.rows(),1); + { + int k = 0; + for(int q = 0;q, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase, -1, 1, 0, -1, 1> >&); +#endif diff --git a/src/igl/copyleft/cgal/point_solid_signed_squared_distance.h b/src/igl/copyleft/cgal/point_solid_signed_squared_distance.h new file mode 100644 index 0000000000..845fad1338 --- /dev/null +++ b/src/igl/copyleft/cgal/point_solid_signed_squared_distance.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_POINT_SOLID_SIGNED_SQUARED_DISTANCE_H +#define IGL_COPYLEFT_CGAL_POINT_SOLID_SIGNED_SQUARED_DISTANCE_H +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // POINT_SOLID_SIGNED_SQUARED_DISTANCE Given a set of points (Q) and the + // boundary mesh (VB,FB) of a solid (as defined in [Zhou et al. 2016], + // determine the signed squared distance for each point q in Q so that d(q,B) is + // negative if inside and positive if outside. + // + // Inputs: + // Q #Q by 3 list of query point positions + // VB #VB by 3 list of mesh vertex positions of B + // FB #FB by 3 list of mesh triangle indices into VB + // Outputs: + // D + template < + typename DerivedQ, + typename DerivedVB, + typename DerivedFB, + typename DerivedD> + IGL_INLINE void point_solid_signed_squared_distance( + const Eigen::PlainObjectBase & Q, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + Eigen::PlainObjectBase & D); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "point_solid_signed_squared_distance.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/point_triangle_squared_distance.cpp b/src/igl/copyleft/cgal/point_triangle_squared_distance.cpp new file mode 100644 index 0000000000..2ea29bc376 --- /dev/null +++ b/src/igl/copyleft/cgal/point_triangle_squared_distance.cpp @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "point_triangle_squared_distance.h" +#include +template < typename Kernel> +IGL_INLINE void point_triangle_squared_distance( + const CGAL::Point_3 & P1, + const CGAL::Triangle_3 & T2, + CGAL::Point_3 & P2, + typename Kernel::FT & d) +{ + assert(!T2.is_degenerate()); + if(T2.has_on(P1)) + { + P2 = P1; + d = 0; + return; + } + const auto proj_1 = T2.supporting_plane().projection(P2); + if(T2.has_on(proj_1)) + { + P2 = proj_1; + d = (proj_1-P1).squared_length(); + return; + } + // closest point must be on the boundary + bool first = true; + // loop over edges + for(int i=0;i<3;i++) + { + CGAL::Point_3 P2i; + typename Kernel::FT di; + const CGAL::Segment_3 si( T2.vertex(i+1), T2.vertex(i+2)); + point_segment_squared_distance(P1,si,P2i,di); + if(first || di < d) + { + first = false; + d = di; + P2 = P2i; + } + } +} diff --git a/src/igl/copyleft/cgal/point_triangle_squared_distance.h b/src/igl/copyleft/cgal/point_triangle_squared_distance.h new file mode 100644 index 0000000000..37b63adcbb --- /dev/null +++ b/src/igl/copyleft/cgal/point_triangle_squared_distance.h @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_POINT_TRIANGLE_SQUARED_DISTANCE_H +#define IGL_COPYLEFT_CGAL_POINT_TRIANGLE_SQUARED_DISTANCE_H +#include "../../igl_inline.h" +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Given a point P1 and triangle T2 find the points on each of closest + // approach and the squared distance thereof. + // + // Inputs: + // P1 point + // T2 triangle + // Outputs: + // P2 point on T2 closest to P1 + // d distance betwee P1 and T2 + template < typename Kernel> + IGL_INLINE void point_triangle_squared_distance( + const CGAL::Point_3 & P1, + const CGAL::Triangle_3 & T2, + CGAL::Point_3 & P2, + typename Kernel::FT & d + ); + + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "point_triangle_squared_distance.cpp" +#endif + +#endif + + diff --git a/src/igl/copyleft/cgal/points_inside_component.cpp b/src/igl/copyleft/cgal/points_inside_component.cpp new file mode 100644 index 0000000000..1558e130d0 --- /dev/null +++ b/src/igl/copyleft/cgal/points_inside_component.cpp @@ -0,0 +1,350 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "points_inside_component.h" +#include "../../LinSpaced.h" +#include "order_facets_around_edge.h" +#include "assign_scalar.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + + +namespace igl { + namespace copyleft + { + namespace cgal { + namespace points_inside_component_helper { + typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; + typedef Kernel::Ray_3 Ray_3; + typedef Kernel::Point_3 Point_3; + typedef Kernel::Vector_3 Vector_3; + typedef Kernel::Triangle_3 Triangle; + typedef Kernel::Plane_3 Plane_3; + typedef std::vector::iterator Iterator; + typedef CGAL::AABB_triangle_primitive Primitive; + typedef CGAL::AABB_traits AABB_triangle_traits; + typedef CGAL::AABB_tree Tree; + + template + void extract_adj_faces( + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const size_t s, const size_t d, + std::vector& adj_faces) { + const size_t num_faces = I.rows(); + for (size_t i=0; i + void extract_adj_vertices( + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const size_t v, std::vector& adj_vertices) { + std::set unique_adj_vertices; + const size_t num_faces = I.rows(); + for (size_t i=0; i + bool determine_point_edge_orientation( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const Point_3& query, size_t s, size_t d) { + // Algorithm: + // + // Order the adj faces around the edge (s,d) clockwise using + // query point as pivot. (i.e. The first face of the ordering + // is directly after the pivot point, and the last face is + // directly before the pivot.) + // + // The point is outside if the first and last faces of the + // ordering forms a convex angle. This check can be done + // without any construction by looking at the orientation of the + // faces. The angle is convex iff the first face contains (s,d) + // as an edge and the last face contains (d,s) as an edge. + // + // The point is inside if the first and last faces of the + // ordering forms a concave angle. That is the first face + // contains (d,s) as an edge and the last face contains (s,d) as + // an edge. + // + // In the special case of duplicated faces. I.e. multiple faces + // sharing the same 3 corners, but not necessarily the same + // orientation. The ordering will always rank faces containing + // edge (s,d) before faces containing edge (d,s). + // + // Therefore, if there are any duplicates of the first faces, + // the ordering will always choose the one with edge (s,d) if + // possible. The same for the last face. + // + // In the very degenerated case where the first and last face + // are duplicates, but with different orientations, it is + // equally valid to think the angle formed by them is either 0 + // or 360 degrees. By default, 0 degree is used, and thus the + // query point is outside. + + std::vector adj_faces; + extract_adj_faces(F, I, s, d, adj_faces); + const size_t num_adj_faces = adj_faces.size(); + assert(num_adj_faces > 0); + + DerivedV pivot_point(1, 3); + igl::copyleft::cgal::assign_scalar(query.x(), pivot_point(0, 0)); + igl::copyleft::cgal::assign_scalar(query.y(), pivot_point(0, 1)); + igl::copyleft::cgal::assign_scalar(query.z(), pivot_point(0, 2)); + Eigen::VectorXi order; + order_facets_around_edge(V, F, s, d, + adj_faces, pivot_point, order); + assert((size_t)order.size() == num_adj_faces); + if (adj_faces[order[0]] > 0 && + adj_faces[order[num_adj_faces-1] < 0]) { + return true; + } else if (adj_faces[order[0]] < 0 && + adj_faces[order[num_adj_faces-1] > 0]) { + return false; + } else { + throw "The input mesh does not represent a valid volume"; + } + throw "The input mesh does not represent a valid volume"; + return false; + } + + template + bool determine_point_vertex_orientation( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const Point_3& query, size_t s) { + std::vector adj_vertices; + extract_adj_vertices(F, I, s, adj_vertices); + const size_t num_adj_vertices = adj_vertices.size(); + + std::vector adj_points; + for (size_t i=0; i bool{ + size_t positive=0; + size_t negative=0; + size_t coplanar=0; + for (const auto& point : adj_points) { + switch(separator.oriented_side(point)) { + case CGAL::ON_POSITIVE_SIDE: + positive++; + break; + case CGAL::ON_NEGATIVE_SIDE: + negative++; + break; + case CGAL::ON_ORIENTED_BOUNDARY: + coplanar++; + break; + default: + throw "Unknown plane-point orientation"; + } + } + auto query_orientation = separator.oriented_side(query); + bool r = + (positive == 0 && query_orientation == CGAL::POSITIVE) + || + (negative == 0 && query_orientation == CGAL::NEGATIVE); + return r; + }; + + size_t d = std::numeric_limits::max(); + Point_3 p(V(s,0), V(s,1), V(s,2)); + for (size_t i=0; i (size_t)V.rows()) { + // All adj faces are coplanar, use the first edge. + d = adj_vertices[0]; + } + return determine_point_edge_orientation(V, F, I, query, s, d); + } + + template + bool determine_point_face_orientation( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const Point_3& query, size_t fid) { + // Algorithm: A point is on the inside of a face if the + // tetrahedron formed by them is negatively oriented. + + Eigen::Vector3i f = F.row(I(fid, 0)); + const Point_3 v0(V(f[0], 0), V(f[0], 1), V(f[0], 2)); + const Point_3 v1(V(f[1], 0), V(f[1], 1), V(f[1], 2)); + const Point_3 v2(V(f[2], 0), V(f[2], 1), V(f[2], 2)); + auto result = CGAL::orientation(v0, v1, v2, query); + if (result == CGAL::COPLANAR) { + throw "Cannot determine inside/outside because query point lies exactly on the input surface."; + } + return result == CGAL::NEGATIVE; + } + } + } + } +} + +template +IGL_INLINE void igl::copyleft::cgal::points_inside_component( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const Eigen::PlainObjectBase& P, + Eigen::PlainObjectBase& inside) { + using namespace igl::copyleft::cgal::points_inside_component_helper; + if (F.rows() <= 0 || I.rows() <= 0) { + throw "Inside check cannot be done on empty facet component."; + } + + const size_t num_faces = I.rows(); + std::vector triangles; + for (size_t i=0; i ElementType{ + const Eigen::Vector3i f = F.row(I(fid, 0)); + const Point_3 p0(V(f[0], 0), V(f[0], 1), V(f[0], 2)); + const Point_3 p1(V(f[1], 0), V(f[1], 1), V(f[1], 2)); + const Point_3 p2(V(f[2], 0), V(f[2], 1), V(f[2], 2)); + + if (p == p0) { element_index = 0; return VERTEX; } + if (p == p1) { element_index = 1; return VERTEX; } + if (p == p2) { element_index = 2; return VERTEX; } + if (CGAL::collinear(p0, p1, p)) { element_index = 2; return EDGE; } + if (CGAL::collinear(p1, p2, p)) { element_index = 0; return EDGE; } + if (CGAL::collinear(p2, p0, p)) { element_index = 1; return EDGE; } + + element_index = 0; + return FACE; + }; + + const size_t num_queries = P.rows(); + inside.resize(num_queries, 1); + for (size_t i=0; i +IGL_INLINE void igl::copyleft::cgal::points_inside_component( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& P, + Eigen::PlainObjectBase& inside) { + Eigen::VectorXi I = igl::LinSpaced(F.rows(), 0, F.rows()-1); + igl::copyleft::cgal::points_inside_component(V, F, I, P, inside); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::cgal::points_inside_component, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Array >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::points_inside_component< Eigen::Matrix, Eigen::Matrix< int, -1, -1, 0, -1, -1>, Eigen::Matrix< int, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix< int, -1, -1, 0, -1, -1> > ( Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::points_inside_component< Eigen::Matrix, Eigen::Matrix< int, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix< int, -1, -1, 0, -1, -1> > ( Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::points_inside_component, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix >(Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::points_inside_component, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::points_inside_component, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::points_inside_component, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/points_inside_component.h b/src/igl/copyleft/cgal/points_inside_component.h new file mode 100644 index 0000000000..f213544557 --- /dev/null +++ b/src/igl/copyleft/cgal/points_inside_component.h @@ -0,0 +1,71 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_POINTS_INSIDE_COMPONENTS +#define IGL_COPYLEFT_CGAL_POINTS_INSIDE_COMPONENTS + +#include "../../igl_inline.h" +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal { + + // Determine if queries points P are inside of connected facet component + // (V, F, I), where I indicates a subset of facets that forms the + // component. + // + // Precondition: + // The input mesh must be a closed, self-intersection free, + // non-degenerated surface. Queries points must be either inside or + // outside of the mesh (i.e. not on the surface of the mesh). + // + // Inputs: + // V #V by 3 array of vertex positions. + // F #F by 3 array of triangles. + // I #I list of triangle indices to consider. + // P #P by 3 array of query points. + // + // Outputs: + // inside #P list of booleans that is true iff the corresponding + // query point is inside of the mesh. + template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename DerivedP, + typename DerivedB> + IGL_INLINE void points_inside_component( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + const Eigen::PlainObjectBase& P, + Eigen::PlainObjectBase& inside); + + // Determine if query points P is inside of the mesh (V, F). + // See above for precondition and I/O specs. + template< + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedB> + IGL_INLINE void points_inside_component( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& P, + Eigen::PlainObjectBase& inside); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +#include "points_inside_component.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/polyhedron_to_mesh.cpp b/src/igl/copyleft/cgal/polyhedron_to_mesh.cpp new file mode 100644 index 0000000000..b51e41b60f --- /dev/null +++ b/src/igl/copyleft/cgal/polyhedron_to_mesh.cpp @@ -0,0 +1,69 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "polyhedron_to_mesh.h" +#include + +template < + typename Polyhedron, + typename DerivedV, + typename DerivedF> +IGL_INLINE void igl::copyleft::cgal::polyhedron_to_mesh( + const Polyhedron & poly, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F) +{ + using namespace std; + V.resize(poly.size_of_vertices(),3); + F.resize(poly.size_of_facets(),3); + typedef typename Polyhedron::Vertex_const_iterator Vertex_iterator; + std::map vertex_to_index; + { + size_t v = 0; + for( + typename Polyhedron::Vertex_const_iterator p = poly.vertices_begin(); + p != poly.vertices_end(); + p++) + { + V(v,0) = p->point().x(); + V(v,1) = p->point().y(); + V(v,2) = p->point().z(); + vertex_to_index[p] = v; + v++; + } + } + { + size_t f = 0; + for( + typename Polyhedron::Facet_const_iterator facet = poly.facets_begin(); + facet != poly.facets_end(); + ++facet) + { + typename Polyhedron::Halfedge_around_facet_const_circulator he = + facet->facet_begin(); + // Facets in polyhedral surfaces are at least triangles. + assert(CGAL::circulator_size(he) == 3 && "Facets should be triangles"); + size_t c = 0; + do { + //// This is stooopidly slow + // F(f,c) = std::distance(poly.vertices_begin(), he->vertex()); + F(f,c) = vertex_to_index[he->vertex()]; + c++; + } while ( ++he != facet->facet_begin()); + f++; + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#include +#include +#include +template void igl::copyleft::cgal::polyhedron_to_mesh >, Eigen::Matrix, Eigen::Matrix >(CGAL::Polyhedron_3 > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::polyhedron_to_mesh,CGAL::Polyhedron_items_with_id_3, CGAL::HalfedgeDS_default, std::allocator >, Eigen::Matrix, Eigen::Matrix >(CGAL::Polyhedron_3,CGAL::Polyhedron_items_with_id_3, CGAL::HalfedgeDS_default, std::allocator > const&,Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/polyhedron_to_mesh.h b/src/igl/copyleft/cgal/polyhedron_to_mesh.h new file mode 100644 index 0000000000..3f8c66a888 --- /dev/null +++ b/src/igl/copyleft/cgal/polyhedron_to_mesh.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_POLYHEDRON_TO_MESH_H +#define IGL_COPYLEFT_CGAL_POLYHEDRON_TO_MESH_H +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Convert a CGAL Polyhedron to a mesh (V,F) + // + // Templates: + // Polyhedron CGAL Polyhedron type (e.g. Polyhedron_3) + // Inputs: + // poly cgal polyhedron + // Outputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + template < + typename Polyhedron, + typename DerivedV, + typename DerivedF> + IGL_INLINE void polyhedron_to_mesh( + const Polyhedron & poly, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "polyhedron_to_mesh.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/projected_cdt.cpp b/src/igl/copyleft/cgal/projected_cdt.cpp new file mode 100644 index 0000000000..f34352c664 --- /dev/null +++ b/src/igl/copyleft/cgal/projected_cdt.cpp @@ -0,0 +1,82 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "projected_cdt.h" +#include "insert_into_cdt.h" +#include "assign_scalar.h" +#include "../../list_to_matrix.h" +template +IGL_INLINE void igl::copyleft::cgal::projected_cdt( + const std::vector & objects, + const CGAL::Plane_3 & P, + std::vector >& vertices, + std::vector >& faces) +{ + typedef CGAL::Triangulation_vertex_base_2 TVB_2; + typedef CGAL::Constrained_triangulation_face_base_2 CTFB_2; + typedef CGAL::Triangulation_data_structure_2 TDS_2; + typedef CGAL::Exact_intersections_tag Itag; + typedef CGAL::Constrained_Delaunay_triangulation_2 CDT_2; + typedef CGAL::Constrained_triangulation_plus_2 CDT_plus_2; + CDT_plus_2 cdt; + for(const auto & obj : objects) insert_into_cdt(obj,P,cdt); + // Read off vertices of the cdt, remembering index + std::map v2i; + size_t count=0; + for ( + auto itr = cdt.finite_vertices_begin(); + itr != cdt.finite_vertices_end(); + itr++) + { + vertices.push_back(P.to_3d(itr->point())); + v2i[itr] = count; + count++; + } + // Read off faces and store index triples + for ( + auto itr = cdt.finite_faces_begin(); + itr != cdt.finite_faces_end(); + itr++) + { + faces.push_back( + { v2i[itr->vertex(0)], v2i[itr->vertex(1)], v2i[itr->vertex(2)] }); + } +} + +template < typename Kernel, typename DerivedV, typename DerivedF> +IGL_INLINE void igl::copyleft::cgal::projected_cdt( + const std::vector & objects, + const CGAL::Plane_3 & P, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F) +{ + std::vector > vertices; + std::vector > faces; + projected_cdt(objects,P,vertices,faces); + V.resize(vertices.size(),3); + for(int v = 0;v(std::vector > const&, CGAL::Plane_3 const&, std::vector, std::allocator > >&, std::vector >, std::allocator > > >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::projected_cdt(std::vector > const&, CGAL::Plane_3 const&, std::vector, std::allocator > >&, std::vector >, std::allocator > > >&); +#include +#ifdef WIN32 +template void igl::copyleft::cgal::projected_cdt(class std::vector> const &, class CGAL::Plane_3 const &, class std::vector, class std::allocator>> &, class std::vector>, class std::allocator>>> &); +template void igl::copyleft::cgal::projected_cdt(class std::vector> const &, class CGAL::Plane_3 const &, class std::vector, class std::allocator>> &, class std::vector>, class std::allocator>>> &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/projected_cdt.h b/src/igl/copyleft/cgal/projected_cdt.h new file mode 100644 index 0000000000..a161cc4503 --- /dev/null +++ b/src/igl/copyleft/cgal/projected_cdt.h @@ -0,0 +1,61 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_PROJECTED_CDT_H +#define IGL_COPYLEFT_CGAL_PROJECTED_CDT_H +#include "../../igl_inline.h" +#include +#include +#include +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Given a list of objects (e.g., resulting from intersecting a triangle + // with many other triangles), construct a constrained Delaunay + // triangulation on a given plane (P), by inersting constraints for each + // object projected onto that plane. + // + // Inputs: + // objects list of objects. This should lie on the given plane (P), + // otherwise they are added to the cdt _after_ their non-trivial + // projection + // P plane upon which all objects lie and upon which the CDT is + // conducted + // Outputs: + // vertices list of vertices of the CDT mesh _back on the 3D plane_ + // faces list of list of triangle indices into vertices + // + template + IGL_INLINE void projected_cdt( + const std::vector & objects, + const CGAL::Plane_3 & P, + std::vector >& vertices, + std::vector >& faces); + // Outputs: + // V #V by 3 list of vertices of the CDT mesh _back on the 3D plane_, + // **cast** from the number type of Kernel to the number type of + // DerivedV + // F #F by 3 list of triangle indices into V + template < typename Kernel, typename DerivedV, typename DerivedF> + IGL_INLINE void projected_cdt( + const std::vector & objects, + const CGAL::Plane_3 & P, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "projected_cdt.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/projected_delaunay.cpp b/src/igl/copyleft/cgal/projected_delaunay.cpp new file mode 100644 index 0000000000..52ea492745 --- /dev/null +++ b/src/igl/copyleft/cgal/projected_delaunay.cpp @@ -0,0 +1,106 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "projected_delaunay.h" +#include "../../REDRUM.h" +#include +#include + +#if CGAL_VERSION_NR < 1040611000 +# warning "CGAL Version < 4.6.1 may result in crashes. Please upgrade CGAL" +#endif + +template +IGL_INLINE void igl::copyleft::cgal::projected_delaunay( + const CGAL::Triangle_3 & A, + const std::vector & A_objects_3, + CGAL::Constrained_triangulation_plus_2< + CGAL::Constrained_Delaunay_triangulation_2< + Kernel, + CGAL::Triangulation_data_structure_2< + CGAL::Triangulation_vertex_base_2, + CGAL::Constrained_triangulation_face_base_2 >, + CGAL::Exact_intersections_tag> > & cdt) +{ + using namespace std; + // 3D Primitives + typedef CGAL::Point_3 Point_3; + typedef CGAL::Segment_3 Segment_3; + typedef CGAL::Triangle_3 Triangle_3; + typedef CGAL::Plane_3 Plane_3; + //typedef CGAL::Tetrahedron_3 Tetrahedron_3; + typedef CGAL::Point_2 Point_2; + //typedef CGAL::Segment_2 Segment_2; + //typedef CGAL::Triangle_2 Triangle_2; + typedef CGAL::Triangulation_vertex_base_2 TVB_2; + typedef CGAL::Constrained_triangulation_face_base_2 CTFB_2; + typedef CGAL::Triangulation_data_structure_2 TDS_2; + typedef CGAL::Exact_intersections_tag Itag; + typedef CGAL::Constrained_Delaunay_triangulation_2 + CDT_2; + typedef CGAL::Constrained_triangulation_plus_2 CDT_plus_2; + + // http://www.cgal.org/Manual/3.2/doc_html/cgal_manual/Triangulation_2/Chapter_main.html#Section_2D_Triangulations_Constrained_Plus + // Plane of triangle A + Plane_3 P(A.vertex(0),A.vertex(1),A.vertex(2)); + // Insert triangle into vertices + typename CDT_plus_2::Vertex_handle corners[3]; + typedef size_t Index; + for(Index i = 0;i<3;i++) + { + const Point_3 & p3 = A.vertex(i); + const Point_2 & p2 = P.to_2d(p3); + typename CDT_plus_2::Vertex_handle corner = cdt.insert(p2); + corners[i] = corner; + } + // Insert triangle edges as constraints + for(Index i = 0;i<3;i++) + { + cdt.insert_constraint( corners[(i+1)%3], corners[(i+2)%3]); + } + // Insert constraints for intersection objects + for( const auto & obj : A_objects_3) + { + if(const Segment_3 *iseg = CGAL::object_cast(&obj)) + { + // Add segment constraint + cdt.insert_constraint(P.to_2d(iseg->vertex(0)),P.to_2d(iseg->vertex(1))); + }else if(const Point_3 *ipoint = CGAL::object_cast(&obj)) + { + // Add point + cdt.insert(P.to_2d(*ipoint)); + } else if(const Triangle_3 *itri = CGAL::object_cast(&obj)) + { + // Add 3 segment constraints + cdt.insert_constraint(P.to_2d(itri->vertex(0)),P.to_2d(itri->vertex(1))); + cdt.insert_constraint(P.to_2d(itri->vertex(1)),P.to_2d(itri->vertex(2))); + cdt.insert_constraint(P.to_2d(itri->vertex(2)),P.to_2d(itri->vertex(0))); + } else if(const std::vector *polyp = + CGAL::object_cast< std::vector >(&obj)) + { + //cerr< & poly = *polyp; + const Index m = poly.size(); + assert(m>=2); + for(Index p = 0;p(CGAL::Triangle_3 const&, std::vector > const&, CGAL::Constrained_triangulation_plus_2 >, CGAL::Constrained_triangulation_face_base_2 > > >, CGAL::Exact_intersections_tag> >&); +template void igl::copyleft::cgal::projected_delaunay(CGAL::Triangle_3 const&, std::vector > const&, CGAL::Constrained_triangulation_plus_2 >, CGAL::Constrained_triangulation_face_base_2 > > >, CGAL::Exact_intersections_tag> >&); +#endif diff --git a/src/igl/copyleft/cgal/projected_delaunay.h b/src/igl/copyleft/cgal/projected_delaunay.h new file mode 100644 index 0000000000..ee72305cdd --- /dev/null +++ b/src/igl/copyleft/cgal/projected_delaunay.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_PROJECTED_DELAUNAY_H +#define IGL_COPYLEFT_CGAL_PROJECTED_DELAUNAY_H +#include "../../igl_inline.h" +#include "CGAL_includes.hpp" +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Compute 2D delaunay triangulation of a given 3d triangle and a list of + // intersection objects (points,segments,triangles). CGAL uses an affine + // projection rather than an isometric projection, so we're not guaranteed + // that the 2D delaunay triangulation here will be a delaunay triangulation + // in 3D. + // + // Inputs: + // A triangle in 3D + // A_objects_3 updated list of intersection objects for A + // Outputs: + // cdt Contrained delaunay triangulation in projected 2D plane + template + IGL_INLINE void projected_delaunay( + const CGAL::Triangle_3 & A, + const std::vector & A_objects_3, + CGAL::Constrained_triangulation_plus_2< + CGAL::Constrained_Delaunay_triangulation_2< + Kernel, + CGAL::Triangulation_data_structure_2< + CGAL::Triangulation_vertex_base_2, + CGAL::Constrained_triangulation_face_base_2 >, + CGAL::Exact_intersections_tag> > & cdt); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "projected_delaunay.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/propagate_winding_numbers.cpp b/src/igl/copyleft/cgal/propagate_winding_numbers.cpp new file mode 100644 index 0000000000..7de9d98d26 --- /dev/null +++ b/src/igl/copyleft/cgal/propagate_winding_numbers.cpp @@ -0,0 +1,324 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#include "propagate_winding_numbers.h" +#include "../../extract_manifold_patches.h" +#include "../../extract_non_manifold_edge_curves.h" +#include "../../facet_components.h" +#include "../../unique_edge_map.h" +#include "../../piecewise_constant_winding_number.h" +#include "../../writeOBJ.h" +#include "../../writePLY.h" +#include "../../get_seconds.h" +#include "../../LinSpaced.h" +#include "order_facets_around_edge.h" +#include "outer_facet.h" +#include "closest_facet.h" +#include "assign.h" +#include "extract_cells.h" +#include "cell_adjacency.h" + +#include +#include +#include +#include +#include + +//#define PROPAGATE_WINDING_NUMBER_TIMING + +template< + typename DerivedV, + typename DerivedF, + typename DerivedL, + typename DerivedW> +IGL_INLINE bool igl::copyleft::cgal::propagate_winding_numbers( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& labels, + Eigen::PlainObjectBase& W) +{ +#ifdef PROPAGATE_WINDING_NUMBER_TIMING + const auto & tictoc = []() -> double + { + static double t_start = igl::get_seconds(); + double diff = igl::get_seconds()-t_start; + t_start += diff; + return diff; + }; + const auto log_time = [&](const std::string& label) -> void { + std::cout << "propagate_winding_num." << label << ": " + << tictoc() << std::endl; + }; + tictoc(); +#endif + + Eigen::MatrixXi E, uE; + Eigen::VectorXi EMAP; + std::vector > uE2E; + igl::unique_edge_map(F, E, uE, EMAP, uE2E); + + Eigen::VectorXi P; + const size_t num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P); + + DerivedW per_patch_cells; + const size_t num_cells = + igl::copyleft::cgal::extract_cells( + V, F, P, E, uE, uE2E, EMAP, per_patch_cells); +#ifdef PROPAGATE_WINDING_NUMBER_TIMING + log_time("cell_extraction"); +#endif + + return propagate_winding_numbers(V, F, + uE, uE2E, + num_patches, P, + num_cells, per_patch_cells, + labels, W); +} + + +template< + typename DerivedV, + typename DerivedF, + typename DeriveduE, + typename uE2EType, + typename DerivedP, + typename DerivedC, + typename DerivedL, + typename DerivedW> +IGL_INLINE bool igl::copyleft::cgal::propagate_winding_numbers( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + const size_t num_patches, + const Eigen::PlainObjectBase& P, + const size_t num_cells, + const Eigen::PlainObjectBase& C, + const Eigen::PlainObjectBase& labels, + Eigen::PlainObjectBase& W) +{ +#ifdef PROPAGATE_WINDING_NUMBER_TIMING + const auto & tictoc = []() -> double + { + static double t_start = igl::get_seconds(); + double diff = igl::get_seconds()-t_start; + t_start += diff; + return diff; + }; + const auto log_time = [&](const std::string& label) -> void { + std::cout << "propagate_winding_num." << label << ": " + << tictoc() << std::endl; + }; + tictoc(); +#endif + + bool valid = true; + // https://github.com/libigl/libigl/issues/674 + if (!igl::piecewise_constant_winding_number(F, uE, uE2E)) + { + assert(false && "Input mesh is not PWN"); + std::cerr << "Input mesh is not PWN!" << std::endl; + valid = false; + } + + const size_t num_faces = F.rows(); + typedef std::tuple CellConnection; + std::vector > cell_adj; + igl::copyleft::cgal::cell_adjacency(C, num_cells, cell_adj); +#ifdef PROPAGATE_WINDING_NUMBER_TIMING + log_time("cell_connectivity"); +#endif + + auto save_cell = [&](const std::string& filename, size_t cell_id) -> void{ + std::vector faces; + for (size_t i=0; i std::list { + std::list path; + path.push_back(idx); + while ((size_t)parents[path.back()] != path.back()) { + path.push_back(parents[path.back()]); + } + return path; + }; + for (size_t i=0; i Q; + Q.push(i); + parents[i] = i; + while (!Q.empty()) { + size_t curr_idx = Q.front(); + Q.pop(); + int curr_label = cell_labels[curr_idx]; + for (const auto& neighbor : cell_adj[curr_idx]) { + if (cell_labels[std::get<0>(neighbor)] == 0) + { + cell_labels[std::get<0>(neighbor)] = curr_label * -1; + Q.push(std::get<0>(neighbor)); + parents[std::get<0>(neighbor)] = curr_idx; + } else + { + if (cell_labels[std::get<0>(neighbor)] != curr_label * -1) + { + std::cerr << "Odd cell cycle detected!" << std::endl; + auto path = trace_parents(curr_idx); + path.reverse(); + auto path2 = trace_parents(std::get<0>(neighbor)); + path.insert(path.end(), path2.begin(), path2.end()); + for (auto cell_id : path) + { + std::cout << cell_id << " "; + std::stringstream filename; + filename << "cell_" << cell_id << ".ply"; + save_cell(filename.str(), cell_id); + } + std::cout << std::endl; + valid = false; + } + // Do not fail when odd cycle is detected because the resulting + // integer winding number field, although inconsistent, may still + // be used if the problem region is local and embedded within a + // valid volume. + //assert(cell_labels[std::get<0>(neighbor)] == curr_label * -1); + } + } + } + } + } +#ifdef PROPAGATE_WINDING_NUMBER_TIMING + log_time("odd_cycle_check"); +#endif + } +#endif + + size_t outer_facet; + bool flipped; + Eigen::VectorXi I = igl::LinSpaced(num_faces, 0, num_faces-1); + igl::copyleft::cgal::outer_facet(V, F, I, outer_facet, flipped); +#ifdef PROPAGATE_WINDING_NUMBER_TIMING + log_time("outer_facet"); +#endif + + const size_t outer_patch = P[outer_facet]; + const size_t infinity_cell = C(outer_patch, flipped?1:0); + + Eigen::VectorXi patch_labels(num_patches); + const int INVALID = std::numeric_limits::max(); + patch_labels.setConstant(INVALID); + for (size_t i=0; i Q; + Q.push(infinity_cell); + while (!Q.empty()) { + size_t curr_cell = Q.front(); + Q.pop(); + for (const auto& neighbor : cell_adj[curr_cell]) { + size_t neighbor_cell, patch_idx; + bool direction; + std::tie(neighbor_cell, direction, patch_idx) = neighbor; + if ((per_cell_W.row(neighbor_cell).array() == INVALID).any()) { + per_cell_W.row(neighbor_cell) = per_cell_W.row(curr_cell); + for (size_t i=0; i, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, unsigned long, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, unsigned long, Eigen::PlainObjectBase > const&, unsigned long, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::copyleft::cgal::propagate_winding_numbers, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, unsigned long, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, unsigned long, Eigen::PlainObjectBase > const&, unsigned long, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template bool igl::copyleft::cgal::propagate_winding_numbers, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template bool igl::copyleft::cgal::propagate_winding_numbers, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, unsigned __int64, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class std::vector>, class std::allocator>>> const &, unsigned __int64, class Eigen::PlainObjectBase> const &, unsigned __int64, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> &); +template bool igl::copyleft::cgal::propagate_winding_numbers, -1, -1, 1, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, unsigned __int64, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix>(class Eigen::PlainObjectBase, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class std::vector>, class std::allocator>>> const &, unsigned __int64, class Eigen::PlainObjectBase> const &, unsigned __int64, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/propagate_winding_numbers.h b/src/igl/copyleft/cgal/propagate_winding_numbers.h new file mode 100644 index 0000000000..77d8e7666b --- /dev/null +++ b/src/igl/copyleft/cgal/propagate_winding_numbers.h @@ -0,0 +1,103 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_COPYLEFT_CGAL_PROPAGATE_WINDING_NUMBERS_H +#define IGL_COPYLEFT_CGAL_PROPAGATE_WINDING_NUMBERS_H +#include "../../igl_inline.h" +#include +#include + +// The following methods compute the winding number on each side of each facet +// or patch of a 3D mesh. The input mesh is valid if it splits the ambient +// space, R^3, into subspaces with constant integer winding numbers. That is +// the input mesh should be closed and for each directed edge the number of +// clockwise facing facets should equal the number of counterclockwise facing +// facets. + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // TODO: This shouldn't need to be in igl::copyleft::cgal, it should + // instead take as input an index of the ambient cell and the winding + // number vector there. + // + // Compute winding number on each side of the face. The input mesh + // could contain multiple connected components. The input mesh must + // represent the boundary of a valid 3D volume, which means it is + // closed, consistently oriented and induces integer winding numbers. + // + // Inputs: + // V #V by 3 list of vertex positions. + // F #F by 3 list of triangle indices into V. + // labels #F list of facet labels ranging from 0 to k-1. + // Output: + // W #F by k*2 list of winding numbers. ``W(i,j*2)`` is the winding + // number on the positive side of facet ``i`` with respect to the + // facets labeled ``j``. Similarly, ``W(i,j*2+1)`` is the winding + // number on the negative side of facet ``i`` with respect to the + // facets labeled ``j``. + // Returns true iff the input induces a piecewise-constant winding number + // field. + template< + typename DerivedV, + typename DerivedF, + typename DerivedL, + typename DerivedW> + IGL_INLINE bool propagate_winding_numbers( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& labels, + Eigen::PlainObjectBase& W); + + // Inputs: + // V #V by 3 list of vertex positions. + // F #F by 3 list of triangle indices into V. + // uE #uE by 2 list of vertex_indices, represents undirected edges. + // uE2E #uE list of lists that maps uE to E. (a one-to-many map) + // num_patches number of patches + // P #F list of patch ids. + // num_cells number of cells + // C #P by 2 list of cell ids on each side of each patch. + // labels #F list of facet labels ranging from 0 to k-1. + // Output: + // W #F by k*2 list of winding numbers. ``W(i,j*2)`` is the winding + // number on the positive side of facet ``i`` with respect to the + // facets labeled ``j``. Similarly, ``W(i,j*2+1)`` is the winding + // number on the negative side of facet ``i`` with respect to the + // facets labeled ``j``. + template< + typename DerivedV, + typename DerivedF, + typename DeriveduE, + typename uE2EType, + typename DerivedP, + typename DerivedC, + typename DerivedL, + typename DerivedW> + IGL_INLINE bool propagate_winding_numbers( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& uE, + const std::vector >& uE2E, + const size_t num_patches, + const Eigen::PlainObjectBase& P, + const size_t num_cells, + const Eigen::PlainObjectBase& C, + const Eigen::PlainObjectBase& labels, + Eigen::PlainObjectBase& W); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "propagate_winding_numbers.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/read_triangle_mesh.cpp b/src/igl/copyleft/cgal/read_triangle_mesh.cpp new file mode 100644 index 0000000000..24ebbb127e --- /dev/null +++ b/src/igl/copyleft/cgal/read_triangle_mesh.cpp @@ -0,0 +1,26 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "read_triangle_mesh.h" +#include "assign.h" +#include "../../read_triangle_mesh.h" + +template +IGL_INLINE bool igl::copyleft::cgal::read_triangle_mesh( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F) +{ + Eigen::MatrixXd Vd; + bool ret = igl::read_triangle_mesh(str,Vd,F); + if(ret) + { + assign(Vd,V); + } + return ret; +} diff --git a/src/igl/copyleft/cgal/read_triangle_mesh.h b/src/igl/copyleft/cgal/read_triangle_mesh.h new file mode 100644 index 0000000000..c1dae2a22d --- /dev/null +++ b/src/igl/copyleft/cgal/read_triangle_mesh.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_READ_TRIANGLE_MESH_H +#define IGL_COPYLEFT_CGAL_READ_TRIANGLE_MESH_H +#include "../../igl_inline.h" + +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Simple wrapper, reads floating point precision but assigns to + // DerivedV::Scalar which may be a CGAL type + // + // Inputs: + // str path to file + // Outputs: + // V eigen double matrix #V by 3 + // F eigen int matrix #F by 3 + // Returns true iff success + template + IGL_INLINE bool read_triangle_mesh( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "read_triangle_mesh.cpp" +#endif + +#endif + diff --git a/src/igl/copyleft/cgal/relabel_small_immersed_cells.cpp b/src/igl/copyleft/cgal/relabel_small_immersed_cells.cpp new file mode 100644 index 0000000000..993e009ec6 --- /dev/null +++ b/src/igl/copyleft/cgal/relabel_small_immersed_cells.cpp @@ -0,0 +1,117 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// + +#include "relabel_small_immersed_cells.h" +#include "../../centroid.h" +#include "assign.h" +#include "cell_adjacency.h" + +#include + +template< + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedC, + typename FT, + typename DerivedW> +IGL_INLINE void igl::copyleft::cgal::relabel_small_immersed_cells( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const size_t num_patches, + const Eigen::PlainObjectBase& P, + const size_t num_cells, + const Eigen::PlainObjectBase& C, + const FT vol_threashold, + Eigen::PlainObjectBase& W) +{ + const size_t num_vertices = V.rows(); + const size_t num_faces = F.rows(); + typedef std::tuple CellConnection; + std::vector > cell_adj; + igl::copyleft::cgal::cell_adjacency(C, num_cells, cell_adj); + + Eigen::MatrixXd VV; + assign(V,VV); + + auto compute_cell_volume = [&](size_t cell_id) { + std::vector is_involved(num_patches, 0); + for (size_t i=0; i involved_positive_faces; + std::vector involved_negative_faces; + for (size_t i=0; i cell_volumes(num_cells); + for (size_t i=0; i cell_values(num_cells); + for (size_t i=0; i= vol_threashold) continue; + std::set neighbor_values; + const auto neighbors = cell_adj[i]; + for (const auto& entry : neighbors) { + const auto& j = std::get<0>(entry); + neighbor_values.insert(cell_values[j]); + } + // If cell i is immersed, assign its value to be the immersed value. + if (neighbor_values.size() == 1) { + cell_values[i] = *neighbor_values.begin(); + } + } + + for (size_t i=0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_RELABEL_SMALL_IMMERSED_CELLS +#define IGL_RELABEL_SMALL_IMMERSED_CELLS + +#include "../../igl_inline.h" +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Inputs: + // V #V by 3 list of vertex positions. + // F #F by 3 list of triangle indices into V. + // num_patches number of patches + // P #F list of patch ids. + // num_cells number of cells + // C #P by 2 list of cell ids on each side of each patch. + // vol_threshold Volume threshold, cells smaller than this + // and is completely immersed will be relabeled. + // + // In/Output: + // W #F by 2 cell labels. W(i,0) is the label on the positive side of + // face i, W(i,1) is the label on the negative side of face i. W + // will be modified in place by this method. + template< + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedC, + typename FT, + typename DerivedW> + IGL_INLINE void relabel_small_immersed_cells( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const size_t num_patches, + const Eigen::PlainObjectBase& P, + const size_t num_cells, + const Eigen::PlainObjectBase& C, + const FT vol_threashold, + Eigen::PlainObjectBase& W); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "relabel_small_immersed_cells.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/remesh_intersections.cpp b/src/igl/copyleft/cgal/remesh_intersections.cpp new file mode 100644 index 0000000000..56a715c23d --- /dev/null +++ b/src/igl/copyleft/cgal/remesh_intersections.cpp @@ -0,0 +1,533 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#include "remesh_intersections.h" +#include "assign_scalar.h" +#include "projected_cdt.h" +#include "../../get_seconds.h" +#include "../../parallel_for.h" +#include "../../LinSpaced.h" +#include "../../unique_rows.h" + +#include +#include +#include +#include +#include + +//#define REMESH_INTERSECTIONS_TIMING + +template < + typename DerivedV, + typename DerivedF, + typename Kernel, + typename DerivedVV, + typename DerivedFF, + typename DerivedJ, + typename DerivedIM> +IGL_INLINE void igl::copyleft::cgal::remesh_intersections( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const std::vector > & T, + const std::map< + typename DerivedF::Index, + std::vector< + std::pair > > & offending, + Eigen::PlainObjectBase & VV, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM) +{ + // by default, no stitching + igl::copyleft::cgal::remesh_intersections(V,F,T,offending,false,VV,FF,J,IM); +} + +template < + typename DerivedV, + typename DerivedF, + typename Kernel, + typename DerivedVV, + typename DerivedFF, + typename DerivedJ, + typename DerivedIM> +IGL_INLINE void igl::copyleft::cgal::remesh_intersections( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const std::vector > & T, + const std::map< + typename DerivedF::Index, + std::vector< + std::pair > > & offending, + bool stitch_all, + Eigen::PlainObjectBase & VV, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM) +{ + +#ifdef REMESH_INTERSECTIONS_TIMING + const auto & tictoc = []() -> double + { + static double t_start = igl::get_seconds(); + double diff = igl::get_seconds()-t_start; + t_start += diff; + return diff; + }; + const auto log_time = [&](const std::string& label) -> void { + std::cout << "remesh_intersections." << label << ": " + << tictoc() << std::endl; + }; + tictoc(); +#endif + + typedef CGAL::Point_3 Point_3; + typedef CGAL::Segment_3 Segment_3; + typedef CGAL::Plane_3 Plane_3; + typedef CGAL::Triangulation_vertex_base_2 TVB_2; + typedef CGAL::Constrained_triangulation_face_base_2 CTFB_2; + typedef CGAL::Triangulation_data_structure_2 TDS_2; + typedef CGAL::Exact_intersections_tag Itag; + typedef CGAL::Constrained_Delaunay_triangulation_2 + CDT_2; + typedef CGAL::Constrained_triangulation_plus_2 CDT_plus_2; + + typedef typename DerivedF::Index Index; + typedef std::pair Edge; + struct EdgeHash { + size_t operator()(const Edge& e) const { + return (e.first * 805306457) ^ (e.second * 201326611); + } + }; + typedef std::unordered_map, EdgeHash > EdgeMap; + + const size_t num_faces = F.rows(); + const size_t num_base_vertices = V.rows(); + assert(num_faces == T.size()); + + std::vector is_offending(num_faces, false); + for (const auto itr : offending) + { + const auto& fid = itr.first; + is_offending[fid] = true; + } + + // Cluster overlaps so that co-planar clusters are resolved only once + std::unordered_map > intersecting_and_coplanar; + for (const auto itr : offending) + { + const auto& fi = itr.first; + const auto P = T[fi].supporting_plane(); + assert(!P.is_degenerate()); + for (const auto jtr : itr.second) + { + const auto& fj = jtr.first; + const auto& tj = T[fj]; + if (P.has_on(tj[0]) && P.has_on(tj[1]) && P.has_on(tj[2])) + { + auto loc = intersecting_and_coplanar.find(fi); + if (loc == intersecting_and_coplanar.end()) + { + intersecting_and_coplanar[fi] = {fj}; + } else + { + loc->second.push_back(fj); + } + } + } + } +#ifdef REMESH_INTERSECTIONS_TIMING + log_time("overlap_analysis"); +#endif + + std::vector > resolved_faces; + std::vector source_faces; + std::vector new_vertices; + EdgeMap edge_vertices; + // face_vertices: Given a face Index, find vertices inside the face + std::unordered_map> face_vertices; + + // Run constraint Delaunay triangulation on the plane. + // + // Inputs: + // P plane to triangulate upone + // involved_faces #F list of indices into triangle of involved faces + // Outputs: + // vertices #V list of vertex positions of output triangulation + // faces #F list of face indices into vertices of output triangulation + // + auto delaunay_triangulation = [&offending, &T]( + const Plane_3& P, + const std::vector& involved_faces, + std::vector& vertices, + std::vector >& faces) -> void + { + std::vector objects; + + CDT_plus_2 cdt; + // insert each face into a common cdt + for (const auto& fid : involved_faces) + { + const auto itr = offending.find(fid); + const auto& triangle = T[fid]; + objects.push_back(CGAL::make_object(triangle)); + if (itr == offending.end()) + { + continue; + } + for (const auto& index_obj : itr->second) + { + //const auto& ofid = index_obj.first; + const auto& obj = index_obj.second; + objects.push_back(obj); + } + } + projected_cdt(objects,P,vertices,faces); + }; + + // Given p on triangle indexed by ori_f, add point to list of vertices return index of p. + // + // Input: + // p point to search for + // ori_f index of triangle p is corner of + // Returns global index of vertex (dependent on whether stitch_all flag is + // set) + // + auto find_or_append_point = [&]( + const Point_3& p, + const size_t ori_f) -> Index + { + if (stitch_all) + { + // No need to check if p shared by multiple triangles because all shared + // vertices would be merged later on. + const size_t index = num_base_vertices + new_vertices.size(); + new_vertices.push_back(p); + return index; + } else + { + // Stitching triangles according to input connectivity. + // This step is potentially costly. + const auto& triangle = T[ori_f]; + const auto& f = F.row(ori_f).eval(); + + // Check if p is one of the triangle corners. + for (size_t i=0; i<3; i++) + { + if (p == triangle[i]) return f[i]; + } + + // Check if p is on one of the edges. + for (size_t i=0; i<3; i++) { + const Point_3 curr_corner = triangle[i]; + const Point_3 next_corner = triangle[(i+1)%3]; + const Segment_3 edge(curr_corner, next_corner); + if (edge.has_on(p)) { + const Index curr = f[i]; + const Index next = f[(i+1)%3]; + Edge key; + key.first = currsecond) { + if (p == new_vertices[vid - num_base_vertices]) { + return vid; + } + } + const size_t index = num_base_vertices + new_vertices.size(); + new_vertices.push_back(p); + itr->second.push_back(index); + return index; + } + } + } + + // p must be in the middle of the triangle. + auto & existing_face_vertices = face_vertices[ori_f]; + for(const auto vid : existing_face_vertices) { + if (p == new_vertices[vid - num_base_vertices]) { + return vid; + } + } + const size_t index = num_base_vertices + new_vertices.size(); + new_vertices.push_back(p); + existing_face_vertices.push_back(index); + return index; + } + }; + + // Determine the vertex indices for each corner of each output triangle. + // + // Inputs: + // vertices #V list of vertices of cdt + // faces #F list of list of face indices into vertices of cdt + // involved_faces list of involved faces on the plane of cdt + // Side effects: + // - add faces to resolved_faces + // - add corresponding original face to source_faces + // - + auto post_triangulation_process = [&]( + const std::vector& vertices, + const std::vector >& faces, + const std::vector& involved_faces) -> void + { + assert(involved_faces.size() > 0); + // for all faces of the cdt + for (const auto& f : faces) + { + const Point_3& v0 = vertices[f[0]]; + const Point_3& v1 = vertices[f[1]]; + const Point_3& v2 = vertices[f[2]]; + Point_3 center( + (v0[0] + v1[0] + v2[0]) / 3.0, + (v0[1] + v1[1] + v2[1]) / 3.0, + (v0[2] + v1[2] + v2[2]) / 3.0); + if (involved_faces.size() == 1) + { + // If only there is only one involved face, all sub-triangles must + // belong to it and have the correct orientation. + const auto& ori_f = involved_faces[0]; + std::vector corners(3); + corners[0] = find_or_append_point(v0, ori_f); + corners[1] = find_or_append_point(v1, ori_f); + corners[2] = find_or_append_point(v2, ori_f); + resolved_faces.emplace_back(corners); + source_faces.push_back(ori_f); + } else + { + for (const auto& ori_f : involved_faces) + { + const auto& triangle = T[ori_f]; + const Plane_3 P = triangle.supporting_plane(); + if (triangle.has_on(center)) { + std::vector corners(3); + corners[0] = find_or_append_point(v0, ori_f); + corners[1] = find_or_append_point(v1, ori_f); + corners[2] = find_or_append_point(v2, ori_f); + if (CGAL::orientation( + P.to_2d(v0), P.to_2d(v1), P.to_2d(v2)) + == CGAL::RIGHT_TURN) { + std::swap(corners[0], corners[1]); + } + resolved_faces.emplace_back(corners); + source_faces.push_back(ori_f); + } + } + } + } + }; + + // Process un-touched faces. + for (size_t i=0; i processed(num_faces, false); + std::vector > > cdt_inputs; + for (const auto itr : offending) + { + const auto fid = itr.first; + if (processed[fid]) continue; + processed[fid] = true; + + const auto loc = intersecting_and_coplanar.find(fid); + std::vector involved_faces; + if (loc == intersecting_and_coplanar.end()) + { + involved_faces.push_back(fid); + } else + { + std::queue Q; + Q.push(fid); + while (!Q.empty()) + { + const auto index = Q.front(); + involved_faces.push_back(index); + Q.pop(); + + const auto overlapping_faces = intersecting_and_coplanar.find(index); + assert(overlapping_faces != intersecting_and_coplanar.end()); + + for (const auto other_index : overlapping_faces->second) + { + if (processed[other_index]) continue; + processed[other_index] = true; + Q.push(other_index); + } + } + } + + Plane_3 P = T[fid].supporting_plane(); + cdt_inputs.emplace_back(P, involved_faces); + } +#ifdef REMESH_INTERSECTIONS_TIMING + log_time("preprocess"); +#endif + + const size_t num_cdts = cdt_inputs.size(); + std::vector > cdt_vertices(num_cdts); + std::vector > > cdt_faces(num_cdts); + + //// Not clear whether this is safe because of reference counting on Point_3 + //// objects... + //// + //// I tried it and got random segfaults (via MATLAB). Seems this is not + //// safe. + //igl::parallel_for(num_cdts,[&](int i) + for (size_t i=0; i + >(unique_vv.rows(), 0, unique_vv.rows()-1); + }else + { + // Vertices with the same coordinates would be represented by one vertex. + // The IM value of a vertex is the index of the representative vertex. + for (Index v=0; v<(Index)VV_size; v++) { + IM(v) = unique_to_vv[vv_to_unique[v]]; + } + } + +#ifdef REMESH_INTERSECTIONS_TIMING + log_time("store_results"); +#endif +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::remesh_intersections, -1, -1, 1, -1, -1>, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, -1, -1, 1, -1, -1>, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, 8, 3, 0, 8, 3>, Eigen::Matrix, CGAL::Epick, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, 8, 3, 0, 8, 3>, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_intersections, Eigen::Matrix, CGAL::Epeck, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector, std::allocator > > const&, std::map::Index, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > >, std::less::Index>, std::allocator::Index const, std::vector::Index, CGAL::Object>, std::allocator::Index, CGAL::Object> > > > > > const&, bool, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::copyleft::cgal::remesh_intersections, class Eigen::Matrix, class CGAL::Epeck, class Eigen::Matrix, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, class Eigen::Matrix>(class Eigen::MatrixBase> const &, class Eigen::MatrixBase> const &, class std::vector, class std::allocator>> const &, class std::map<__int64, class std::vector, class std::allocator>>, struct std::less<__int64>, class std::allocator, class std::allocator>>>>> const &, bool, class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::remesh_intersections, class Eigen::Matrix, class CGAL::Epick, class Eigen::Matrix, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, class Eigen::Matrix>(class Eigen::MatrixBase> const &, class Eigen::MatrixBase> const &, class std::vector, class std::allocator>> const &, class std::map<__int64, class std::vector, class std::allocator>>, struct std::less<__int64>, class std::allocator, class std::allocator>>>>> const &, bool, class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, class Eigen::Matrix, class CGAL::Epeck, class Eigen::Matrix, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, class Eigen::Matrix>(class Eigen::MatrixBase, -1, 3, 0, -1, 3>> const &, class Eigen::MatrixBase> const &, class std::vector, class std::allocator>> const &, class std::map<__int64, class std::vector, class std::allocator>>, struct std::less<__int64>, class std::allocator, class std::allocator>>>>> const &, bool, class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::remesh_intersections, -1, 3, 0, -1, 3>, class Eigen::Matrix, class CGAL::Epick, class Eigen::Matrix, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, class Eigen::Matrix>(class Eigen::MatrixBase, -1, 3, 0, -1, 3>> const &, class Eigen::MatrixBase> const &, class std::vector, class std::allocator>> const &, class std::map<__int64, class std::vector, class std::allocator>>, struct std::less<__int64>, class std::allocator, class std::allocator>>>>> const &, bool, class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/remesh_intersections.h b/src/igl/copyleft/cgal/remesh_intersections.h new file mode 100644 index 0000000000..2b6a44a578 --- /dev/null +++ b/src/igl/copyleft/cgal/remesh_intersections.h @@ -0,0 +1,94 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_COPYLEFT_CGAL_REMESH_INTERSECTIONS_H +#define IGL_COPYLEFT_CGAL_REMESH_INTERSECTIONS_H + +#include "../../igl_inline.h" +#include +#include "CGAL_includes.hpp" + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Remesh faces according to results of intersection detection and + // construction (e.g. from `igl::copyleft::cgal::intersect_other` or + // `igl::copyleft::cgal::SelfIntersectMesh`) + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // T #F list of cgal triangles + // offending #offending map taking face indices into F to pairs of order + // of first finding and list of intersection objects from all + // intersections + // stitch_all if true, merge all vertices with the same coordinate. + // Outputs: + // VV #VV by 3 list of vertex positions, if stitch_all = false then + // first #V vertices will always be V + // FF #FF by 3 list of triangle indices into V + // IF #intersecting face pairs by 2 list of intersecting face pairs, + // indexing F + // J #FF list of indices into F denoting birth triangle + // IM / stitch_all = true #VV list from 0 to #VV-1 + // \ stitch_all = false #VV list of indices into VV of unique vertices. + // + template < + typename DerivedV, + typename DerivedF, + typename Kernel, + typename DerivedVV, + typename DerivedFF, + typename DerivedJ, + typename DerivedIM> + IGL_INLINE void remesh_intersections( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const std::vector > & T, + const std::map< + typename DerivedF::Index, + std::vector< + std::pair > > & offending, + bool stitch_all, + Eigen::PlainObjectBase & VV, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM); + // Same as above except stitch_all is assumed "false" + template < + typename DerivedV, + typename DerivedF, + typename Kernel, + typename DerivedVV, + typename DerivedFF, + typename DerivedJ, + typename DerivedIM> + IGL_INLINE void remesh_intersections( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const std::vector > & T, + const std::map< + typename DerivedF::Index, + std::vector< + std::pair > > & offending, + Eigen::PlainObjectBase & VV, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "remesh_intersections.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/remesh_self_intersections.cpp b/src/igl/copyleft/cgal/remesh_self_intersections.cpp new file mode 100644 index 0000000000..871152b304 --- /dev/null +++ b/src/igl/copyleft/cgal/remesh_self_intersections.cpp @@ -0,0 +1,108 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "remesh_self_intersections.h" +#include "SelfIntersectMesh.h" +#include "../../C_STR.h" +#include +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> +IGL_INLINE void igl::copyleft::cgal::remesh_self_intersections( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const RemeshSelfIntersectionsParam & params, + Eigen::PlainObjectBase & VV, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & IF, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM) +{ + using namespace std; + if(params.detect_only) + { + //// This is probably a terrible idea, but CGAL is throwing floating point + //// exceptions. + +//#ifdef __APPLE__ +//#define IGL_THROW_FPE 11 +// const auto & throw_fpe = [](int e) +// { +// throw "IGL_THROW_FPE"; +// }; +// signal(SIGFPE,throw_fpe); +//#endif + + typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; + typedef + SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM> + SelfIntersectMeshK; + SelfIntersectMeshK SIM(V,F,params,VV,FF,IF,J,IM); + +//#ifdef __APPLE__ +// signal(SIGFPE,SIG_DFL); +//#endif + + }else + { + typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; + typedef + SelfIntersectMesh< + Kernel, + DerivedV, + DerivedF, + DerivedVV, + DerivedFF, + DerivedIF, + DerivedJ, + DerivedIM> + SelfIntersectMeshK; + SelfIntersectMeshK SIM(V,F,params,VV,FF,IF,J,IM); + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::cgal::remesh_self_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::remesh_self_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, 8, 3, 0, 8, 3>, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cgal::remesh_self_intersections, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::copyleft::cgal::RemeshSelfIntersectionsParam const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::copyleft::cgal::remesh_self_intersections, class Eigen::Matrix, class Eigen::Matrix, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, class Eigen::Matrix>(class Eigen::MatrixBase> const &, class Eigen::MatrixBase> const &, struct igl::copyleft::cgal::RemeshSelfIntersectionsParam const &, class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +template void igl::copyleft::cgal::remesh_self_intersections, -1, 3, 0, -1, 3>, class Eigen::Matrix, class Eigen::Matrix, -1, -1, 0, -1, -1>, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, class Eigen::Matrix>(class Eigen::MatrixBase, -1, 3, 0, -1, 3>> const &, class Eigen::MatrixBase> const &, struct igl::copyleft::cgal::RemeshSelfIntersectionsParam const &, class Eigen::PlainObjectBase, -1, -1, 0, -1, -1>> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/copyleft/cgal/remesh_self_intersections.h b/src/igl/copyleft/cgal/remesh_self_intersections.h new file mode 100644 index 0000000000..e8e8cc492a --- /dev/null +++ b/src/igl/copyleft/cgal/remesh_self_intersections.h @@ -0,0 +1,81 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_REMESH_SELF_INTERSECTIONS_H +#define IGL_COPYLEFT_CGAL_REMESH_SELF_INTERSECTIONS_H +#include "../../igl_inline.h" +#include "RemeshSelfIntersectionsParam.h" + +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Given a triangle mesh (V,F) compute a new mesh (VV,FF) which is the same + // as (V,F) except that any self-intersecting triangles in (V,F) have been + // subdivided (new vertices and face created) so that the self-intersection + // contour lies exactly on edges in (VV,FF). New vertices will appear in + // original faces or on original edges. New vertices on edges are "merged" + // only across original faces sharing that edge. This means that if the input + // triangle mesh is a closed manifold the output will be too. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // params struct of optional parameters + // Outputs: + // VV #VV by 3 list of vertex positions + // FF #FF by 3 list of triangle indices into VV + // IF #intersecting face pairs by 2 list of intersecting face pairs, + // indexing F + // J #FF list of indices into F denoting birth triangle + // IM #VV list of indices into VV of unique vertices. + // + // Known bugs: If an existing edge in (V,F) lies exactly on another face then + // any resulting additional vertices along that edge may not get properly + // connected so that the output mesh has the same global topology. This is + // because + // + // Example: + // // resolve intersections + // igl::copyleft::cgal::remesh_self_intersections(V,F,params,VV,FF,IF,J,IM); + // // _apply_ duplicate vertex mapping IM to FF + // for_each(FF.data(),FF.data()+FF.size(),[&IM](int & a){a=IM(a);}); + // // remove any vertices now unreferenced after duplicate mapping. + // igl::remove_unreferenced(VV,FF,SV,SF,UIM); + // // Now (SV,SF) is ready to extract outer hull + // igl::copyleft::cgal::outer_hull(SV,SF,G,J,flip); + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedVV, + typename DerivedFF, + typename DerivedIF, + typename DerivedJ, + typename DerivedIM> + IGL_INLINE void remesh_self_intersections( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const RemeshSelfIntersectionsParam & params, + Eigen::PlainObjectBase & VV, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & IF, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "remesh_self_intersections.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/remove_unreferenced.cpp b/src/igl/copyleft/cgal/remove_unreferenced.cpp new file mode 100644 index 0000000000..03c8864cb8 --- /dev/null +++ b/src/igl/copyleft/cgal/remove_unreferenced.cpp @@ -0,0 +1,20 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "../../remove_unreferenced.h" +#include +#include +#ifdef IGL_STATIC_LIBRARY +#undef IGL_STATIC_LIBRARY +#include "../../remove_unreferenced.cpp" +template void igl::remove_unreferenced, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix, -1, 4, 0, -1, 4>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, 4, 0, -1, 4> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/resolve_intersections.cpp b/src/igl/copyleft/cgal/resolve_intersections.cpp new file mode 100644 index 0000000000..2252d87e1c --- /dev/null +++ b/src/igl/copyleft/cgal/resolve_intersections.cpp @@ -0,0 +1,90 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "resolve_intersections.h" +#include "subdivide_segments.h" +#include "row_to_point.h" +#include "../../unique.h" +#include "../../list_to_matrix.h" +#include +#include +#include +#include +#include + +template < + typename DerivedV, + typename DerivedE, + typename DerivedVI, + typename DerivedEI, + typename DerivedJ, + typename DerivedIM> +IGL_INLINE void igl::copyleft::cgal::resolve_intersections( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & VI, + Eigen::PlainObjectBase & EI, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM) +{ + using namespace Eigen; + using namespace igl; + using namespace std; + // Exact scalar type + typedef CGAL::Epeck K; + typedef K::FT EScalar; + typedef CGAL::Segment_2 Segment_2; + typedef CGAL::Point_2 Point_2; + typedef Matrix MatrixXE; + + // Convert vertex positions to exact kernel + MatrixXE VE(V.rows(),V.cols()); + for(int i = 0;i > steiner(m); + for(int i = 0;i(VE,E(i,0)),row_to_point(VE,E(i,1))); + steiner[i].push_back(si.vertex(0)); + steiner[i].push_back(si.vertex(1)); + for(int j = i+1;j(VE,E(j,0)),row_to_point(VE,E(j,1))); + // do they intersect? + if(CGAL::do_intersect(si,sj)) + { + CGAL::Object result = CGAL::intersection(si,sj); + if(const Point_2 * p = CGAL::object_cast(&result)) + { + steiner[i].push_back(*p); + steiner[j].push_back(*p); + // add intersection point + }else if(const Segment_2 * s = CGAL::object_cast(&result)) + { + // add both endpoints + steiner[i].push_back(s->vertex(0)); + steiner[j].push_back(s->vertex(0)); + steiner[i].push_back(s->vertex(1)); + steiner[j].push_back(s->vertex(1)); + }else + { + assert(false && "Unknown intersection type"); + } + } + } + } + + subdivide_segments(V,E,steiner,VI,EI,J,IM); +} diff --git a/src/igl/copyleft/cgal/resolve_intersections.h b/src/igl/copyleft/cgal/resolve_intersections.h new file mode 100644 index 0000000000..086ef1361d --- /dev/null +++ b/src/igl/copyleft/cgal/resolve_intersections.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_RESOLVE_INTERSECTIONS_H +#define IGL_COPYLEFT_CGAL_RESOLVE_INTERSECTIONS_H +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + // RESOLVE_INTERSECTIONS Given a list of possible intersecting segments with + // endpoints, split segments to overlap only at endpoints + // + // Inputs: + // V #V by 2 list of vertex positions + // E #E by 2 list of segment indices into V + // Outputs: + // VI #VI by 2 list of output vertex positions, copies of V are always + // the first #V vertices + // EI #EI by 2 list of segment indices into V, #EI ≥ #E + // J #EI list of indices into E revealing "parent segments" + // IM #VI list of indices into VV of unique vertices. + namespace cgal + { + template < + typename DerivedV, + typename DerivedE, + typename DerivedVI, + typename DerivedEI, + typename DerivedJ, + typename DerivedIM> + IGL_INLINE void resolve_intersections( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & VI, + Eigen::PlainObjectBase & EI, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "resolve_intersections.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/row_to_point.cpp b/src/igl/copyleft/cgal/row_to_point.cpp new file mode 100644 index 0000000000..97a4b991eb --- /dev/null +++ b/src/igl/copyleft/cgal/row_to_point.cpp @@ -0,0 +1,18 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "row_to_point.h" + +template < + typename Kernel, + typename DerivedV> +IGL_INLINE CGAL::Point_2 igl::copyleft::cgal::row_to_point( + const Eigen::PlainObjectBase & V, + const typename DerivedV::Index & i) +{ + return CGAL::Point_2(V(i,0),V(i,1)); +} diff --git a/src/igl/copyleft/cgal/row_to_point.h b/src/igl/copyleft/cgal/row_to_point.h new file mode 100644 index 0000000000..54e27ad4f9 --- /dev/null +++ b/src/igl/copyleft/cgal/row_to_point.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_ROW_TO_POINT_H +#define IGL_COPYLEFT_CGAL_ROW_TO_POINT_H +#include "../../igl_inline.h" +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Extract a row from V and treat as a 2D cgal point (only first two + // columns of V are used). + // + // Inputs: + // V #V by 2 list of vertex positions + // i row index + // Returns 2D cgal point + template < + typename Kernel, + typename DerivedV> + IGL_INLINE CGAL::Point_2 row_to_point( + const Eigen::PlainObjectBase & V, + const typename DerivedV::Index & i); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "row_to_point.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/segment_segment_squared_distance.cpp b/src/igl/copyleft/cgal/segment_segment_squared_distance.cpp new file mode 100644 index 0000000000..1ca695995c --- /dev/null +++ b/src/igl/copyleft/cgal/segment_segment_squared_distance.cpp @@ -0,0 +1,129 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "segment_segment_squared_distance.h" +#include + +// http://geomalgorithms.com/a07-_distance.html +template < typename Kernel> +IGL_INLINE bool igl::copyleft::cgal::segment_segment_squared_distance( + const CGAL::Segment_3 & S1, + const CGAL::Segment_3 & S2, + CGAL::Point_3 & P1, + CGAL::Point_3 & P2, + typename Kernel::FT & dst) +{ + typedef CGAL::Point_3 Point_3; + typedef CGAL::Vector_3 Vector_3; + typedef typename Kernel::FT EScalar; + if(S1.is_degenerate()) + { + // All points on S1 are the same + P1 = S1.source(); + point_segment_squared_distance(P1,S2,P2,dst); + return true; + }else if(S2.is_degenerate()) + { + assert(!S1.is_degenerate()); + // All points on S2 are the same + P2 = S2.source(); + point_segment_squared_distance(P2,S1,P1,dst); + return true; + } + + assert(!S1.is_degenerate()); + assert(!S2.is_degenerate()); + + Vector_3 u = S1.target() - S1.source(); + Vector_3 v = S2.target() - S2.source(); + Vector_3 w = S1.source() - S2.source(); + + const auto a = u.dot(u); // always >= 0 + const auto b = u.dot(v); + const auto c = v.dot(v); // always >= 0 + const auto d = u.dot(w); + const auto e = v.dot(w); + const auto D = a*c - b*b; // always >= 0 + assert(D>=0); + const auto sc=D, sN=D, sD = D; // sc = sN / sD, default sD = D >= 0 + const auto tc=D, tN=D, tD = D; // tc = tN / tD, default tD = D >= 0 + + bool parallel = false; + // compute the line parameters of the two closest points + if (D==0) + { + // the lines are almost parallel + parallel = true; + sN = 0.0; // force using source point on segment S1 + sD = 1.0; // to prevent possible division by 0.0 later + tN = e; + tD = c; + } else + { + // get the closest points on the infinite lines + sN = (b*e - c*d); + tN = (a*e - b*d); + if (sN < 0.0) + { + // sc < 0 => the s=0 edge is visible + sN = 0.0; + tN = e; + tD = c; + } else if (sN > sD) + { // sc > 1 => the s=1 edge is visible + sN = sD; + tN = e + b; + tD = c; + } + } + + if (tN < 0.0) + { + // tc < 0 => the t=0 edge is visible + tN = 0.0; + // recompute sc for this edge + if (-d < 0.0) + { + sN = 0.0; + }else if (-d > a) + { + sN = sD; + }else + { + sN = -d; + sD = a; + } + }else if (tN > tD) + { + // tc > 1 => the t=1 edge is visible + tN = tD; + // recompute sc for this edge + if ((-d + b) < 0.0) + { + sN = 0; + }else if ((-d + b) > a) + { + sN = sD; + }else + { + sN = (-d + b); + sD = a; + } + } + // finally do the division to get sc and tc + sc = (abs(sN) == 0 ? 0.0 : sN / sD); + tc = (abs(tN) == 0 ? 0.0 : tN / tD); + + // get the difference of the two closest points + P1 = S1.source() + sc*(S1.target()-S1.source()); + P2 = S2.source() + sc*(S2.target()-S2.source()); + Vector_3 dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc) + assert(dP == (P1-P2)); + + dst = dP.squared_length(); // return the closest distance + return parallel; +} diff --git a/src/igl/copyleft/cgal/segment_segment_squared_distance.h b/src/igl/copyleft/cgal/segment_segment_squared_distance.h new file mode 100644 index 0000000000..5bfbfdcab5 --- /dev/null +++ b/src/igl/copyleft/cgal/segment_segment_squared_distance.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_SEGMENT_SEGMENT_SQUARED_DISTANCE_H +#define IGL_COPYLEFT_CGAL_SEGMENT_SEGMENT_SQUARED_DISTANCE_H +#include "../../igl_inline.h" +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Given two segments S1 and S2 find the points on each of closest + // approach and the squared distance thereof. + // + // Inputs: + // S1 first segment + // S2 second segment + // Outputs: + // P1 point on S1 closest to S2 + // P2 point on S2 closest to S1 + // d distance betwee P1 and S2 + // Returns true if the closest approach is unique. + template < typename Kernel> + IGL_INLINE bool segment_segment_squared_distance( + const CGAL::Segment_3 & S1, + const CGAL::Segment_3 & S2, + CGAL::Point_3 & P1, + CGAL::Point_3 & P2, + typename Kernel::FT & d + ); + + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "segment_segment_squared_distance.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/signed_distance_isosurface.cpp b/src/igl/copyleft/cgal/signed_distance_isosurface.cpp new file mode 100644 index 0000000000..eeb6c03c5b --- /dev/null +++ b/src/igl/copyleft/cgal/signed_distance_isosurface.cpp @@ -0,0 +1,138 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "signed_distance_isosurface.h" +#include "point_mesh_squared_distance.h" +#include "complex_to_mesh.h" + +#include "../../AABB.h" +#include "../../per_face_normals.h" +#include "../../per_edge_normals.h" +#include "../../per_vertex_normals.h" +#include "../../centroid.h" +#include "../../WindingNumberAABB.h" + +#include +#include +#include +#include +#include +// Axis-aligned bounding box tree for tet tri intersection +#include +#include +#include +#include + +IGL_INLINE bool igl::copyleft::cgal::signed_distance_isosurface( + const Eigen::MatrixXd & IV, + const Eigen::MatrixXi & IF, + const double level, + const double angle_bound, + const double radius_bound, + const double distance_bound, + const SignedDistanceType sign_type, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F) +{ + using namespace std; + using namespace Eigen; + + // default triangulation for Surface_mesher + typedef CGAL::Surface_mesh_default_triangulation_3 Tr; + // c2t3 + typedef CGAL::Complex_2_in_triangulation_3 C2t3; + typedef Tr::Geom_traits GT;//Kernel + typedef GT::Sphere_3 Sphere_3; + typedef GT::Point_3 Point_3; + typedef GT::FT FT; + typedef std::function Function; + typedef CGAL::Implicit_surface_3 Surface_3; + + AABB tree; + tree.init(IV,IF); + + Eigen::MatrixXd FN,VN,EN; + Eigen::MatrixXi E; + Eigen::VectorXi EMAP; + WindingNumberAABB< Eigen::Vector3d, Eigen::MatrixXd, Eigen::MatrixXi > hier; + switch(sign_type) + { + default: + assert(false && "Unknown SignedDistanceType"); + case SIGNED_DISTANCE_TYPE_UNSIGNED: + // do nothing + break; + case SIGNED_DISTANCE_TYPE_DEFAULT: + case SIGNED_DISTANCE_TYPE_WINDING_NUMBER: + hier.set_mesh(IV,IF); + hier.grow(); + break; + case SIGNED_DISTANCE_TYPE_PSEUDONORMAL: + // "Signed Distance Computation Using the Angle Weighted Pseudonormal" + // [Bærentzen & Aanæs 2005] + per_face_normals(IV,IF,FN); + per_vertex_normals(IV,IF,PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE,FN,VN); + per_edge_normals( + IV,IF,PER_EDGE_NORMALS_WEIGHTING_TYPE_UNIFORM,FN,EN,E,EMAP); + break; + } + + Tr tr; // 3D-Delaunay triangulation + C2t3 c2t3 (tr); // 2D-complex in 3D-Delaunay triangulation + // defining the surface + const auto & IVmax = IV.colwise().maxCoeff(); + const auto & IVmin = IV.colwise().minCoeff(); + const double bbd = (IVmax-IVmin).norm(); + const double r = bbd/2.; + const auto & IVmid = 0.5*(IVmax + IVmin); + // Supposedly the implict needs to evaluate to <0 at cmid... + // http://doc.cgal.org/latest/Surface_mesher/classCGAL_1_1Implicit__surface__3.html + Point_3 cmid(IVmid(0),IVmid(1),IVmid(2)); + Function fun; + switch(sign_type) + { + default: + assert(false && "Unknown SignedDistanceType"); + case SIGNED_DISTANCE_TYPE_UNSIGNED: + fun = + [&tree,&IV,&IF,&level](const Point_3 & q) -> FT + { + int i; + RowVector3d c; + const double sd = tree.squared_distance( + IV,IF,RowVector3d(q.x(),q.y(),q.z()),i,c); + return sd-level; + }; + case SIGNED_DISTANCE_TYPE_DEFAULT: + case SIGNED_DISTANCE_TYPE_WINDING_NUMBER: + fun = + [&tree,&IV,&IF,&hier,&level](const Point_3 & q) -> FT + { + const double sd = signed_distance_winding_number( + tree,IV,IF,hier,Vector3d(q.x(),q.y(),q.z())); + return sd-level; + }; + break; + case SIGNED_DISTANCE_TYPE_PSEUDONORMAL: + fun = [&tree,&IV,&IF,&FN,&VN,&EN,&EMAP,&level](const Point_3 & q) -> FT + { + const double sd = + igl::signed_distance_pseudonormal( + tree,IV,IF,FN,VN,EN,EMAP,RowVector3d(q.x(),q.y(),q.z())); + return sd- level; + }; + break; + } + Sphere_3 bounding_sphere(cmid, (r+level)*(r+level)); + Surface_3 surface(fun,bounding_sphere); + CGAL::Surface_mesh_default_criteria_3 + criteria(angle_bound,radius_bound,distance_bound); + // meshing surface + CGAL::make_surface_mesh(c2t3, surface, criteria, CGAL::Manifold_tag()); + // complex to (V,F) + return igl::copyleft::cgal::complex_to_mesh(c2t3,V,F); +} diff --git a/src/igl/copyleft/cgal/signed_distance_isosurface.h b/src/igl/copyleft/cgal/signed_distance_isosurface.h new file mode 100644 index 0000000000..149ea5e3d2 --- /dev/null +++ b/src/igl/copyleft/cgal/signed_distance_isosurface.h @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_SIGNED_DISTANCE_ISOSURFACE_H +#define IGL_COPYLEFT_CGAL_SIGNED_DISTANCE_ISOSURFACE_H +#include "../../igl_inline.h" +#include "../../signed_distance.h" +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // SIGNED_DISTANCE_ISOSURFACE Compute the contour of an iso-level of the + // signed distance field to a given mesh. + // + // Inputs: + // IV #IV by 3 list of input mesh vertex positions + // IF #IF by 3 list of input triangle indices + // level iso-level to contour in world coords, negative is inside. + // angle_bound lower bound on triangle angles (mesh quality) (e.g. 28) + // radius_bound upper bound on triangle size (mesh density?) (e.g. 0.02) + // distance_bound cgal mysterious parameter (mesh density?) (e.g. 0.01) + // sign_type method for computing distance _sign_ (see + // ../signed_distance.h) + // Outputs: + // V #V by 3 list of input mesh vertex positions + // F #F by 3 list of input triangle indices + // + IGL_INLINE bool signed_distance_isosurface( + const Eigen::MatrixXd & IV, + const Eigen::MatrixXi & IF, + const double level, + const double angle_bound, + const double radius_bound, + const double distance_bound, + const SignedDistanceType sign_type, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "signed_distance_isosurface.cpp" +#endif + +#endif + diff --git a/src/igl/copyleft/cgal/slice.cpp b/src/igl/copyleft/cgal/slice.cpp new file mode 100644 index 0000000000..2e274a2f2c --- /dev/null +++ b/src/igl/copyleft/cgal/slice.cpp @@ -0,0 +1,12 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "../../slice.h" +#ifdef IGL_STATIC_LIBRARY +#undef IGL_STATIC_LIBRARY +#include "../../slice.cpp" +#endif diff --git a/src/igl/copyleft/cgal/slice_mask.cpp b/src/igl/copyleft/cgal/slice_mask.cpp new file mode 100644 index 0000000000..ec920e0d19 --- /dev/null +++ b/src/igl/copyleft/cgal/slice_mask.cpp @@ -0,0 +1,15 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "../../slice_mask.h" +#include +#include +#ifdef IGL_STATIC_LIBRARY +#undef IGL_STATIC_LIBRARY +#include "../../slice_mask.cpp" +template void igl::slice_mask, -1, 3, 0, -1, 3>, Eigen::Matrix, -1, 3, 0, -1, 3> >(Eigen::DenseBase, -1, 3, 0, -1, 3> > const&, Eigen::Array const&, int, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&); +#endif diff --git a/src/igl/copyleft/cgal/snap_rounding.cpp b/src/igl/copyleft/cgal/snap_rounding.cpp new file mode 100644 index 0000000000..ccebfd1b82 --- /dev/null +++ b/src/igl/copyleft/cgal/snap_rounding.cpp @@ -0,0 +1,206 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "snap_rounding.h" +#include "resolve_intersections.h" +#include "subdivide_segments.h" +#include "../../remove_unreferenced.h" +#include "../../unique.h" +#include +#include +#include +#include +#include + +template < + typename DerivedV, + typename DerivedE, + typename DerivedVI, + typename DerivedEI, + typename DerivedJ> +IGL_INLINE void igl::copyleft::cgal::snap_rounding( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & VI, + Eigen::PlainObjectBase & EI, + Eigen::PlainObjectBase & J) +{ + using namespace Eigen; + using namespace igl; + using namespace igl::copyleft::cgal; + using namespace std; + // Exact scalar type + typedef CGAL::Epeck Kernel; + typedef Kernel::FT EScalar; + typedef CGAL::Segment_2 Segment_2; + typedef CGAL::Point_2 Point_2; + typedef CGAL::Vector_2 Vector_2; + typedef Matrix MatrixXE; + // Convert vertex positions to exact kernel + + MatrixXE VE; + { + VectorXi IM; + resolve_intersections(V,E,VE,EI,J,IM); + for_each( + EI.data(), + EI.data()+EI.size(), + [&IM](typename DerivedEI::Scalar& i){i=IM(i);}); + VectorXi _; + remove_unreferenced( MatrixXE(VE), DerivedEI(EI), VE,EI,_); + } + + // find all hot pixels + //// southwest and north east corners + //const RowVector2i SW( + // round(VE.col(0).minCoeff()), + // round(VE.col(1).minCoeff())); + //const RowVector2i NE( + // round(VE.col(0).maxCoeff()), + // round(VE.col(1).maxCoeff())); + + // https://github.com/CGAL/cgal/issues/548 + // Round an exact scalar to the nearest integer. A priori can't just round + // double. Suppose e=0.5+ε but double(e)<0.5 + // + // Inputs: + // e exact number + // Outputs: + // i closest integer + const auto & round = [](const EScalar & e)->int + { + const double d = CGAL::to_double(e); + // get an integer that's near the closest int + int i = std::round(d); + EScalar di_sqr = CGAL::square((e-EScalar(i))); + const auto & search = [&i,&di_sqr,&e](const int dir) + { + while(true) + { + const int j = i+dir; + const EScalar dj_sqr = CGAL::square((e-EScalar(j))); + if(dj_sqr < di_sqr) + { + i = j; + di_sqr = dj_sqr; + }else + { + break; + } + } + }; + // Try to increase/decrease int + search(1); + search(-1); + return i; + }; + vector hot; + for(int i = 0;i _1,_2; + igl::unique(vector(hot),hot,_1,_2); + } + + // find all segments intersecting hot pixels + // split edge at closest point to hot pixel center + vector> steiner(EI.rows()); + // initialize each segment with endpoints + for(int i = 0;i hits; + for(int j = 0;j<4;j++) + { + const Segment_2 & sj = wall[j]; + if(CGAL::do_intersect(si,sj)) + { + CGAL::Object result = CGAL::intersection(si,sj); + if(const Point_2 * p = CGAL::object_cast(&result)) + { + hits.push_back(*p); + }else if(const Segment_2 * s = CGAL::object_cast(&result)) + { + // add both endpoints + hits.push_back(s->vertex(0)); + hits.push_back(s->vertex(1)); + } + } + } + if(hits.size() == 0) + { + continue; + } + // centroid of hits + Vector_2 cen(0,0); + for(const Point_2 & hit : hits) + { + cen = Vector_2(cen.x()+hit.x(), cen.y()+hit.y()); + } + cen = Vector_2(cen.x()/EScalar(hits.size()),cen.y()/EScalar(hits.size())); + const Point_2 rcen(round(cen.x()),round(cen.y())); + // after all of that, don't add as a steiner unless it's going to round + // to h + if(rcen == h) + { + steiner[i].emplace_back(cen.x(),cen.y()); + } + } + } + { + DerivedJ prevJ = J; + VectorXi IM; + subdivide_segments(MatrixXE(VE),MatrixXi(EI),steiner,VE,EI,J,IM); + for_each(J.data(),J.data()+J.size(),[&prevJ](typename DerivedJ::Scalar & j){j=prevJ(j);}); + for_each( + EI.data(), + EI.data()+EI.size(), + [&IM](typename DerivedEI::Scalar& i){i=IM(i);}); + VectorXi _; + remove_unreferenced( MatrixXE(VE), DerivedEI(EI), VE,EI,_); + } + + + VI.resizeLike(VE); + for(int i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_SNAP_ROUNDING_H +#define IGL_COPYLEFT_CGAL_SNAP_ROUNDING_H + +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // SNAP_ROUNDING Snap a list of possible intersecting segments with + // endpoints in any precision to _the_ integer grid. + // + // Inputs: + // V #V by 2 list of vertex positions + // E #E by 2 list of segment indices into V + // Outputs: + // VI #VI by 2 list of output integer vertex positions, rounded copies + // of V are always the first #V vertices + // EI #EI by 2 list of segment indices into V, #EI ≥ #E + // J #EI list of indices into E revealing "parent segments" + template < + typename DerivedV, + typename DerivedE, + typename DerivedVI, + typename DerivedEI, + typename DerivedJ> + IGL_INLINE void snap_rounding( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & VI, + Eigen::PlainObjectBase & EI, + Eigen::PlainObjectBase & J); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "snap_rounding.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/string_to_mesh_boolean_type.cpp b/src/igl/copyleft/cgal/string_to_mesh_boolean_type.cpp new file mode 100644 index 0000000000..8ff1a03c49 --- /dev/null +++ b/src/igl/copyleft/cgal/string_to_mesh_boolean_type.cpp @@ -0,0 +1,53 @@ +#include "string_to_mesh_boolean_type.h" +#include +#include +#include + +IGL_INLINE bool igl::copyleft::cgal::string_to_mesh_boolean_type( + const std::string & s, + MeshBooleanType & type) +{ + using namespace std; + string eff_s = s; + transform(eff_s.begin(), eff_s.end(), eff_s.begin(), ::tolower); + const auto & find_any = + [](const vector & haystack, const string & needle)->bool + { + return find(haystack.begin(), haystack.end(), needle) != haystack.end(); + }; + if(find_any({"union","unite","u","∪"},eff_s)) + { + type = MESH_BOOLEAN_TYPE_UNION; + }else if(find_any({"intersect","intersection","i","∩"},eff_s)) + { + type = MESH_BOOLEAN_TYPE_INTERSECT; + }else if( + find_any( + {"minus","subtract","difference","relative complement","m","\\"},eff_s)) + { + type = MESH_BOOLEAN_TYPE_MINUS; + }else if(find_any({"xor","symmetric difference","x","∆"},eff_s)) + { + type = MESH_BOOLEAN_TYPE_XOR; + }else if(find_any({"resolve"},eff_s)) + { + type = MESH_BOOLEAN_TYPE_RESOLVE; + }else + { + return false; + } + return true; +} + +IGL_INLINE igl::MeshBooleanType +igl::copyleft::cgal::string_to_mesh_boolean_type( + const std::string & s) +{ + MeshBooleanType type; +#ifndef NDEBUG + const bool ret = +#endif + string_to_mesh_boolean_type(s,type); + assert(ret && "Unknown MeshBooleanType name"); + return type; +} diff --git a/src/igl/copyleft/cgal/string_to_mesh_boolean_type.h b/src/igl/copyleft/cgal/string_to_mesh_boolean_type.h new file mode 100644 index 0000000000..4781099f1a --- /dev/null +++ b/src/igl/copyleft/cgal/string_to_mesh_boolean_type.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_STRING_TO_MESH_BOOLEAN_H +#define IGL_COPYLEFT_CGAL_STRING_TO_MESH_BOOLEAN_H + +#include "../../igl_inline.h" +#include "../../MeshBooleanType.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Convert string to boolean type + // + // Inputs: + // s string identifying type, one of the following: + // "union","intersect","minus","xor","resolve" + // Outputs: + // type type of boolean operation + // Returns true only on success + // + IGL_INLINE bool string_to_mesh_boolean_type( + const std::string & s, + MeshBooleanType & type); + // Returns type without error handling + IGL_INLINE MeshBooleanType string_to_mesh_boolean_type( + const std::string & s); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "string_to_mesh_boolean_type.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/subdivide_segments.cpp b/src/igl/copyleft/cgal/subdivide_segments.cpp new file mode 100644 index 0000000000..6cf7df93cf --- /dev/null +++ b/src/igl/copyleft/cgal/subdivide_segments.cpp @@ -0,0 +1,134 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "subdivide_segments.h" +#include "row_to_point.h" +#include "assign_scalar.h" +#include "../../unique.h" +#include "../../list_to_matrix.h" +#include +#include +#include +#include +#include + +template < + typename DerivedV, + typename DerivedE, + typename Kernel, + typename DerivedVI, + typename DerivedEI, + typename DerivedJ, + typename DerivedIM> +IGL_INLINE void igl::copyleft::cgal::subdivide_segments( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & E, + const std::vector > > & _steiner, + Eigen::PlainObjectBase & VI, + Eigen::PlainObjectBase & EI, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM) +{ + using namespace Eigen; + using namespace igl; + using namespace std; + + // Exact scalar type + typedef Kernel K; + typedef typename Kernel::FT EScalar; + typedef CGAL::Segment_2 Segment_2; + typedef CGAL::Point_2 Point_2; + typedef Matrix MatrixXE; + + // non-const copy + std::vector > > steiner = _steiner; + + // Convert vertex positions to exact kernel + MatrixXE VE(V.rows(),V.cols()); + for(int i = 0;i S; + std::vector > vEI; + std::vector vJ; + for(int i = 0;i(VE,E(i,0)); + std::sort( + steiner[i].begin(), + steiner[i].end(), + [&s](const Point_2 & A, const Point_2 & B)->bool + { + return (A-s).squared_length() < (B-s).squared_length(); + }); + } + // remove duplicates + steiner[i].erase( + std::unique(steiner[i].begin(), steiner[i].end()), + steiner[i].end()); + { + int s = E(i,0); + // legs to each steiner in order + for(int j = 1;j vVES,_1; + for(int i = 0;i(VE,i)); + } + vVES.insert(vVES.end(),S.begin(),S.end()); + std::vector vA,vIM; + igl::unique(vVES,_1,vA,vIM); + // Push indices back into vVES + for_each(vIM.data(),vIM.data()+vIM.size(),[&vA](size_t & i){i=vA[i];}); + list_to_matrix(vIM,IM); + } +} diff --git a/src/igl/copyleft/cgal/subdivide_segments.h b/src/igl/copyleft/cgal/subdivide_segments.h new file mode 100644 index 0000000000..cd7d41a002 --- /dev/null +++ b/src/igl/copyleft/cgal/subdivide_segments.h @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_SUBDIVIDE_SEGMENTS_H +#define IGL_COPYLEFT_CGAL_SUBDIVIDE_SEGMENTS_H +#include "../../igl_inline.h" +#include +#include +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Insert steiner points to subdivide a given set of line segments + // + // Inputs: + // V #V by 2 list of vertex positions + // E #E by 2 list of segment indices into V + // steiner #E list of lists of unsorted steiner points (including + // endpoints) along the #E original segments + // Outputs: + // VI #VI by 2 list of output vertex positions, copies of V are always + // the first #V vertices + // EI #EI by 2 list of segment indices into V, #EI ≥ #E + // J #EI list of indices into E revealing "parent segments" + // IM #VI list of indices into VV of unique vertices. + template < + typename DerivedV, + typename DerivedE, + typename Kernel, + typename DerivedVI, + typename DerivedEI, + typename DerivedJ, + typename DerivedIM> + IGL_INLINE void subdivide_segments( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & E, + const std::vector > > & steiner, + Eigen::PlainObjectBase & VI, + Eigen::PlainObjectBase & EI, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & IM); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "subdivide_segments.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/submesh_aabb_tree.cpp b/src/igl/copyleft/cgal/submesh_aabb_tree.cpp new file mode 100644 index 0000000000..4351ec0a92 --- /dev/null +++ b/src/igl/copyleft/cgal/submesh_aabb_tree.cpp @@ -0,0 +1,58 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "submesh_aabb_tree.h" +#include + +template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename Kernel> +IGL_INLINE void igl::copyleft::cgal::submesh_aabb_tree( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + CGAL::AABB_tree< + CGAL::AABB_traits< + Kernel, + CGAL::AABB_triangle_primitive< + Kernel, typename std::vector< + typename Kernel::Triangle_3 >::iterator > > > & tree, + std::vector & triangles, + std::vector & in_I) +{ + in_I.resize(F.rows(), false); + const size_t num_faces = I.rows(); + for (size_t i=0; i, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, CGAL::Epeck>(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, CGAL::AABB_tree >::iterator, CGAL::Boolean_tag > > >&, std::vector >&, std::vector >&); +// generated by autoexplicit.sh +template void igl::copyleft::cgal::submesh_aabb_tree, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix, CGAL::Epeck>(Eigen::PlainObjectBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, CGAL::AABB_tree >::iterator, CGAL::Boolean_tag > > >&, std::vector >&, std::vector >&); +template void igl::copyleft::cgal::submesh_aabb_tree, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix, CGAL::Epeck>(Eigen::PlainObjectBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, CGAL::AABB_tree >::iterator, CGAL::Boolean_tag > > >&, std::vector >&, std::vector >&); +#endif diff --git a/src/igl/copyleft/cgal/submesh_aabb_tree.h b/src/igl/copyleft/cgal/submesh_aabb_tree.h new file mode 100644 index 0000000000..eec9c030f9 --- /dev/null +++ b/src/igl/copyleft/cgal/submesh_aabb_tree.h @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_COPYLET_CGAL_SUBMESH_AABB_TREE_H +#define IGL_COPYLET_CGAL_SUBMESH_AABB_TREE_H + +#include "../../igl_inline.h" +#include +#include + +#include +#include +#include +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Build an AABB tree for a submesh indicated by a face selection list I + // of a full mesh (V,F) + // + // Inputs: + // V #V by 3 array of vertices. + // F #F by 3 array of faces. + // I #I list of triangle indices to consider. + // Outputs: + // tree aabb containing triangles of (V,F(I,:)) + // triangles #I list of cgal triangles + // in_I #F list of whether in submesh + template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename Kernel> + IGL_INLINE void submesh_aabb_tree( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& I, + CGAL::AABB_tree< + CGAL::AABB_traits< + Kernel, + CGAL::AABB_triangle_primitive< + Kernel, typename std::vector< + typename Kernel::Triangle_3 >::iterator > > > & tree, + std::vector & triangles, + std::vector & in_I); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "submesh_aabb_tree.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cgal/triangle_triangle_squared_distance.cpp b/src/igl/copyleft/cgal/triangle_triangle_squared_distance.cpp new file mode 100644 index 0000000000..0c512f8000 --- /dev/null +++ b/src/igl/copyleft/cgal/triangle_triangle_squared_distance.cpp @@ -0,0 +1,114 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "triangle_triangle_squared_distance.h" +#include "point_triangle_squared_distance.h" +#include +#include +#include + +template < typename Kernel> +IGL_INLINE bool igl::copyleft::cgal::triangle_triangle_squared_distance( + const CGAL::Triangle_3 & T1, + const CGAL::Triangle_3 & T2, + CGAL::Point_3 & P1, + CGAL::Point_3 & P2, + typename Kernel::FT & d) +{ + typedef CGAL::Point_3 Point_3; + typedef CGAL::Vector_3 Vector_3; + typedef CGAL::Triangle_3 Triangle_3; + typedef CGAL::Segment_3 Segment_3; + typedef typename Kernel::FT EScalar; + assert(!T1.is_degenerate()); + assert(!T2.is_degenerate()); + + bool unique = true; + if(CGAL::do_intersect(T1,T2)) + { + // intersecting triangles have zero (squared) distance + CGAL::Object result = CGAL::intersection(T1,T2); + // Some point on the intersection result + CGAL::Point_3 Q; + if(const Point_3 * p = CGAL::object_cast(&result)) + { + Q = *p; + }else if(const Segment_3 * s = CGAL::object_cast(&result)) + { + unique = false; + Q = s->source(); + }else if(const Triangle_3 *itri = CGAL::object_cast(&result)) + { + Q = s->vertex(0); + unique = false; + }else if(const std::vector *polyp = + CGAL::object_cast< std::vector >(&result)) + { + assert(polyp->size() > 0 && "intersection poly should not be empty"); + Q = polyp[0]; + unique = false; + }else + { + assert(false && "Unknown intersection result"); + } + P1 = Q; + P2 = Q; + d = 0; + return unique; + } + // triangles do not intersect: the points of closest approach must be on the + // boundary of one of the triangles + d = std::numeric_limits::infinity(); + const auto & vertices_face = [&unique]( + const Triangle_3 & T1, + const Triangle_3 & T2, + Point_3 & P1, + Point_3 & P2, + EScalar & d) + { + for(int i = 0;i<3;i++) + { + const Point_3 vi = T1.vertex(i); + Point_3 P2i; + EScalar di; + point_triangle_squared_distance(vi,T2,P2i,di); + if(di +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_TRIANGLE_TRIANGLE_SQUARED_DISTANCE_H +#define IGL_COPYLEFT_CGAL_TRIANGLE_TRIANGLE_SQUARED_DISTANCE_H +#include "../../igl_inline.h" +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Given two triangles T1 and T2 find the points on each of closest + // approach and the squared distance thereof. + // + // Inputs: + // T1 first triangle + // T2 second triangle + // Outputs: + // P1 point on T1 closest to T2 + // P2 point on T2 closest to T1 + // d distance betwee P1 and T2 + // Returns true if the closest approach is unique. + template < typename Kernel> + IGL_INLINE bool triangle_triangle_squared_distance( + const CGAL::Triangle_3 & T1, + const CGAL::Triangle_3 & T2, + CGAL::Point_3 & P1, + CGAL::Point_3 & P2, + typename Kernel::FT & d); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "triangle_triangle_squared_distance.cpp" +#endif + +#endif + diff --git a/src/igl/copyleft/cgal/trim_with_solid.cpp b/src/igl/copyleft/cgal/trim_with_solid.cpp new file mode 100644 index 0000000000..1e67da71e6 --- /dev/null +++ b/src/igl/copyleft/cgal/trim_with_solid.cpp @@ -0,0 +1,105 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "trim_with_solid.h" +#include "assign.h" +#include "intersect_other.h" +#include "point_solid_signed_squared_distance.h" + +#include "../../extract_manifold_patches.h" +#include "../../list_to_matrix.h" +#include "../../remove_unreferenced.h" +#include "../../slice_mask.h" + +#include + +#include + +template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedV, + typename DerivedF, + typename DerivedD, + typename DerivedJ> +IGL_INLINE void igl::copyleft::cgal::trim_with_solid( + const Eigen::PlainObjectBase & VA, + const Eigen::PlainObjectBase & FA, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + Eigen::PlainObjectBase & Vd, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & D, + Eigen::PlainObjectBase & J) +{ + // resolve intersections using exact representation + typedef Eigen::Matrix MatrixX3E; + typedef Eigen::Matrix VectorXE; + typedef Eigen::Matrix RowVector3E; + MatrixX3E V; + Eigen::MatrixXi _1; + Eigen::VectorXi _2; + // Intersect A and B meshes and stitch together new faces + igl::copyleft::cgal::intersect_other( + VA,FA,VB,FB,{false,false,true},_1,V,F,J,_2); + // Partition result into manifold patches + Eigen::VectorXi P; + const size_t num_patches = igl::extract_manifold_patches(F,P); + // only keep faces from A + Eigen::Matrix A = J.array()< FA.rows(); + igl::slice_mask(Eigen::MatrixXi(F),A,1,F); + igl::slice_mask(Eigen::VectorXi(P),A,1,P); + igl::slice_mask(Eigen::VectorXi(J),A,1,J); + // Aggregate representative query points for each patch + std::vector flag(num_patches); + std::vector > vQ; + Eigen::VectorXi P2Q(num_patches); + for(int f = 0;f q = { + (V(F(f,0),0)+ V(F(f,1),0)+ V(F(f,2),0))/3., + (V(F(f,0),1)+ V(F(f,1),1)+ V(F(f,2),1))/3., + (V(F(f,0),2)+ V(F(f,1),2)+ V(F(f,2),2))/3.}; + vQ.emplace_back(q); + flag[p] = true; + } + } + MatrixX3E Q; + igl::list_to_matrix(vQ,Q); + VectorXE SP; + point_solid_signed_squared_distance(Q,VB,FB,SP); + Eigen::Matrix DP = SP.array()>0; + // distribute flag to all faces + D.resize(F.rows()); + for(int f = 0;f, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix + >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, + Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, + Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/trim_with_solid.h b/src/igl/copyleft/cgal/trim_with_solid.h new file mode 100644 index 0000000000..aa45d80f76 --- /dev/null +++ b/src/igl/copyleft/cgal/trim_with_solid.h @@ -0,0 +1,63 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CGAL_TRIM_WITH_SOLID_H +#define IGL_COPYLEFT_CGAL_TRIM_WITH_SOLID_H + +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // TRIM_WITH_SOLID Given an arbitrary mesh (VA,FA) and the boundary mesh + // (VB,FB) of a solid (as defined in [Zhou et al. 2016]), Resolve intersections + // between A and B subdividing faces of A so that intersections with B exists + // only along edges and vertices (and coplanar faces). Then determine whether + // each of these faces is inside or outside of B. This can be used to extract + // the part of A inside or outside of B. + // + // Inputs: + // VA #VA by 3 list of mesh vertex positions of A + // FA #FA by 3 list of mesh triangle indices into VA + // VB #VB by 3 list of mesh vertex positions of B + // FB #FB by 3 list of mesh triangle indices into VB + // Outputs: + // V #V by 3 list of mesh vertex positions of output + // F #F by 3 list of mesh triangle indices into V + // D #F list of bools whether face is inside B + // J #F list of indices into FA revealing birth parent + // + template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedV, + typename DerivedF, + typename DerivedD, + typename DerivedJ> + IGL_INLINE void trim_with_solid( + const Eigen::PlainObjectBase & VA, + const Eigen::PlainObjectBase & FA, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + Eigen::PlainObjectBase & Vd, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & D, + Eigen::PlainObjectBase & J); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "trim_with_solid.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cgal/unique.cpp b/src/igl/copyleft/cgal/unique.cpp new file mode 100644 index 0000000000..5e45f82b17 --- /dev/null +++ b/src/igl/copyleft/cgal/unique.cpp @@ -0,0 +1,14 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "../../unique.h" +#include +#include +#ifdef IGL_STATIC_LIBRARY +#undef IGL_STATIC_LIBRARY +#include "../../unique.cpp" +#endif diff --git a/src/igl/copyleft/cgal/unique_rows.cpp b/src/igl/copyleft/cgal/unique_rows.cpp new file mode 100644 index 0000000000..e4d946f770 --- /dev/null +++ b/src/igl/copyleft/cgal/unique_rows.cpp @@ -0,0 +1,18 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "../../unique_rows.h" +#include +#include +#ifdef IGL_STATIC_LIBRARY +#undef IGL_STATIC_LIBRARY +#include "../../unique_rows.cpp" +template void igl::unique_rows, -1, -1, 0, -1, -1>, Eigen::Matrix, -1, -1, 0, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_rows, -1, -1, 1, -1, -1>, Eigen::Matrix, -1, -1, 1, -1, -1>, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_rows, -1, 3, 0, -1, 3>, Eigen::Matrix, -1, 3, 0, -1, 3>, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/copyleft/cgal/wire_mesh.cpp b/src/igl/copyleft/cgal/wire_mesh.cpp new file mode 100644 index 0000000000..0e96095017 --- /dev/null +++ b/src/igl/copyleft/cgal/wire_mesh.cpp @@ -0,0 +1,215 @@ +#include "wire_mesh.h" + +#include "../../list_to_matrix.h" +#include "../../slice.h" +#include "../../PI.h" +#include "convex_hull.h" +#include "mesh_boolean.h" +#include +#include + +template < + typename DerivedWV, + typename DerivedWE, + typename DerivedV, + typename DerivedF, + typename DerivedJ> +IGL_INLINE void igl::copyleft::cgal::wire_mesh( + const Eigen::MatrixBase & WV, + const Eigen::MatrixBase & WE, + const double th, + const int poly_size, + const bool solid, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & J) +{ + + typedef typename DerivedWV::Scalar Scalar; + // Canonical polygon to place at each endpoint + typedef Eigen::Matrix MatrixX3S; + MatrixX3S PV(poly_size,3); + for(int p =0;p > > A(WV.rows()); + // Inputs: + // e index of edge + // c index of endpoint [0,1] + // p index of polygon vertex + // Returns index of corresponding vertex in V + const auto index = + [&PV,&WV](const int e, const int c, const int p)->int + { + return WV.rows() + e*2*PV.rows() + PV.rows()*c + p; + }; + const auto unindex = + [&PV,&WV](int v, int & e, int & c, int & p) + { + assert(v>=WV.rows()); + v = v-WV.rows(); + e = v/(2*PV.rows()); + v = v-e*(2*PV.rows()); + c = v/(PV.rows()); + v = v-c*(PV.rows()); + p = v; + }; + // loop over all edges + for(int e = 0;e RowVector3S; + const RowVector3S ev = WV.row(WE(e,1))-WV.row(WE(e,0)); + const Scalar len = ev.norm(); + // Unit edge vector + const RowVector3S uv = ev.normalized(); + Eigen::Quaternion q; + q = q.FromTwoVectors(RowVector3S(0,0,1),uv); + // loop over polygon vertices + for(int p = 0;p > vF; + std::vector vJ; + const auto append_hull = + [&V,&vF,&vJ,&unindex,&WV](const Eigen::VectorXi & I, const int j) + { + MatrixX3S Vv; + igl::slice(V,I,1,Vv); + Eigen::MatrixXi Fv; + convex_hull(Vv,Fv); + for(int f = 0;f face(I(Fv(f,0)), I(Fv(f,1)), I(Fv(f,2))); + //const bool on_vertex = (face +IGL_INLINE void igl::copyleft::cgal::wire_mesh( + const Eigen::MatrixBase & WV, + const Eigen::MatrixBase & WE, + const double th, + const int poly_size, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & J) +{ + return wire_mesh(WV,WE,th,poly_size,true,V,F,J); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::copyleft::cgal::wire_mesh, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cgal/wire_mesh.h b/src/igl/copyleft/cgal/wire_mesh.h new file mode 100644 index 0000000000..9b798e6fd5 --- /dev/null +++ b/src/igl/copyleft/cgal/wire_mesh.h @@ -0,0 +1,67 @@ +#ifndef IGL_COPYLEFT_CGAL_WIRE_MESH_H +#define IGL_COPYLEFT_CGAL_WIRE_MESH_H +#include "../../igl_inline.h" +#include +namespace igl +{ + namespace copyleft + { + namespace cgal + { + // Construct a "wire" or "wireframe" or "strut" surface mesh, given a + // one-dimensional network of straight edges. + // + // Inputs: + // WV #WV by 3 list of vertex positions + // WE #WE by 2 list of edge indices into WV + // th diameter thickness of wire + // poly_size number of sides on each wire (e.g., 4 would produce wires by + // connecting rectangular prisms). + // solid whether to resolve self-intersections to + // create a "solid" output mesh (cf., [Zhou et al. 2016] + // Outputs: + // V #V by 3 list of output vertices + // F #F by 3 list of output triangle indices into V + // J #F list of indices into [0,#WV+#WE) revealing "birth simplex" of + // output faces J(j) < #WV means the face corresponds to the J(j)th + // vertex in WV. J(j) >= #WV means the face corresponds to the + // (J(j)-#WV)th edge in WE. + template < + typename DerivedWV, + typename DerivedWE, + typename DerivedV, + typename DerivedF, + typename DerivedJ> + IGL_INLINE void wire_mesh( + const Eigen::MatrixBase & WV, + const Eigen::MatrixBase & WE, + const double th, + const int poly_size, + const bool solid, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & J); + // Default with solid=true + template < + typename DerivedWV, + typename DerivedWE, + typename DerivedV, + typename DerivedF, + typename DerivedJ> + IGL_INLINE void wire_mesh( + const Eigen::MatrixBase & WV, + const Eigen::MatrixBase & WE, + const double th, + const int poly_size, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & J); + + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "wire_mesh.cpp" +#endif +#endif diff --git a/src/igl/copyleft/comiso/frame_field.cpp b/src/igl/copyleft/comiso/frame_field.cpp new file mode 100644 index 0000000000..c3549eeffc --- /dev/null +++ b/src/igl/copyleft/comiso/frame_field.cpp @@ -0,0 +1,688 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "frame_field.h" + +#include +#include +#include +#include +#include + +namespace igl +{ +namespace copyleft +{ +namespace comiso +{ + +class FrameInterpolator +{ +public: + // Init + IGL_INLINE FrameInterpolator(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F); + IGL_INLINE ~FrameInterpolator(); + + // Reset constraints (at least one constraint must be present or solve will fail) + IGL_INLINE void resetConstraints(); + + IGL_INLINE void setConstraint(const int fid, const Eigen::VectorXd& v); + + IGL_INLINE void interpolateSymmetric(); + + // Generate the frame field + IGL_INLINE void solve(); + + // Convert the frame field in the canonical representation + IGL_INLINE void frame2canonical(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v, double& theta, Eigen::VectorXd& S); + + // Convert the canonical representation in a frame field + IGL_INLINE void canonical2frame(const Eigen::MatrixXd& TP, const double theta, const Eigen::VectorXd& S, Eigen::RowVectorXd& v); + + IGL_INLINE Eigen::MatrixXd getFieldPerFace(); + + IGL_INLINE void PolarDecomposition(Eigen::MatrixXd V, Eigen::MatrixXd& U, Eigen::MatrixXd& P); + + // Symmetric + Eigen::MatrixXd S; + std::vector S_c; + + // ------------------------------------------------- + + // Face Topology + Eigen::MatrixXi TT, TTi; + + // Two faces are consistent if their representative vector are taken modulo PI + std::vector edge_consistency; + Eigen::MatrixXi edge_consistency_TT; + +private: + IGL_INLINE double mod2pi(double d); + IGL_INLINE double modpi2(double d); + IGL_INLINE double modpi(double d); + + // Convert a direction on the tangent space into an angle + IGL_INLINE double vector2theta(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v); + + // Convert an angle in a vector in the tangent space + IGL_INLINE Eigen::RowVectorXd theta2vector(const Eigen::MatrixXd& TP, const double theta); + + // Interpolate the cross field (theta) + IGL_INLINE void interpolateCross(); + + // Compute difference between reference frames + IGL_INLINE void computek(); + + // Compute edge consistency + IGL_INLINE void compute_edge_consistency(); + + // Cross field direction + Eigen::VectorXd thetas; + std::vector thetas_c; + + // Edge Topology + Eigen::MatrixXi EV, FE, EF; + std::vector isBorderEdge; + + // Angle between two reference frames + // R(k) * t0 = t1 + Eigen::VectorXd k; + + // Mesh + Eigen::MatrixXd V; + Eigen::MatrixXi F; + + // Normals per face + Eigen::MatrixXd N; + + // Reference frame per triangle + std::vector TPs; + +}; + +FrameInterpolator::FrameInterpolator(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F) +{ + using namespace std; + using namespace Eigen; + + V = _V; + F = _F; + + assert(V.rows() > 0); + assert(F.rows() > 0); + + + // Generate topological relations + igl::triangle_triangle_adjacency(F,TT,TTi); + igl::edge_topology(V,F, EV, FE, EF); + + // Flag border edges + isBorderEdge.resize(EV.rows()); + for(unsigned i=0; i 3*(igl::PI/4.0); + + // Copy it into edge_consistency_TT + int i1 = -1; + int i2 = -1; + for (unsigned i=0; i<3; ++i) + { + if (TT(fid0,i) == fid1) + i1 = i; + if (TT(fid1,i) == fid0) + i2 = i; + } + assert(i1 != -1); + assert(i2 != -1); + + edge_consistency_TT(fid0,i1) = edge_consistency[eid]; + edge_consistency_TT(fid1,i2) = edge_consistency[eid]; + } + } +} + +void FrameInterpolator::computek() +{ + using namespace std; + using namespace Eigen; + + k.resize(EF.rows()); + + // For every non-border edge + for (unsigned eid=0; eid(); + + assert(tmp(0) - ref1(0) < (0.000001)); + assert(tmp(1) - ref1(1) < (0.000001)); + + k[eid] = ktemp; + } + } + +} + + + void FrameInterpolator::frame2canonical(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v, double& theta, Eigen::VectorXd& S_v) +{ + using namespace std; + using namespace Eigen; + + RowVectorXd v0 = v.segment<3>(0); + RowVectorXd v1 = v.segment<3>(3); + + // Project onto the tangent plane + Vector2d vp0 = TP * v0.transpose(); + Vector2d vp1 = TP * v1.transpose(); + + // Assemble matrix + MatrixXd M(2,2); + M << vp0, vp1; + + if (M.determinant() < 0) + M.col(1) = -M.col(1); + + assert(M.determinant() > 0); + + // cerr << "M: " << M << endl; + + MatrixXd R,S; + PolarDecomposition(M,R,S); + + // Finally, express the cross field as an angle + theta = atan2(R(1,0),R(0,0)); + + MatrixXd R2(2,2); + R2 << cos(theta), -sin(theta), sin(theta), cos(theta); + + assert((R2-R).norm() < 10e-8); + + // Convert into rotation invariant form + S = R * S * R.inverse(); + + // Copy in vector form + S_v = VectorXd(3); + S_v << S(0,0), S(0,1), S(1,1); +} + + void FrameInterpolator::canonical2frame(const Eigen::MatrixXd& TP, const double theta, const Eigen::VectorXd& S_v, Eigen::RowVectorXd& v) +{ + using namespace std; + using namespace Eigen; + + assert(S_v.size() == 3); + + MatrixXd S_temp(2,2); + S_temp << S_v(0), S_v(1), S_v(1), S_v(2); + + // Convert angle in vector in the tangent plane + // Vector2d vp(cos(theta),sin(theta)); + + // First reconstruct R + MatrixXd R(2,2); + + R << cos(theta), -sin(theta), sin(theta), cos(theta); + + // Rotation invariant reconstruction + MatrixXd M = S_temp * R; + + Vector2d vp0(M(0,0),M(1,0)); + Vector2d vp1(M(0,1),M(1,1)); + + // Unproject the vectors + RowVectorXd v0 = vp0.transpose() * TP; + RowVectorXd v1 = vp1.transpose() * TP; + + v.resize(6); + v << v0, v1; +} + +void FrameInterpolator::solve() +{ + interpolateCross(); + interpolateSymmetric(); +} + +void FrameInterpolator::interpolateSymmetric() +{ + using namespace std; + using namespace Eigen; + + // Generate uniform Laplacian matrix + typedef Eigen::Triplet triplet; + std::vector triplets; + + // Variables are stacked as x1,y1,z1,x2,y2,z2 + triplets.reserve(3*4*F.rows()); + + MatrixXd b = MatrixXd::Zero(3*F.rows(),1); + + // Build L and b + for (unsigned eid=0; eid L(3*F.rows(),3*F.rows()); + L.setFromTriplets(triplets.begin(), triplets.end()); + + triplets.clear(); + + // Add soft constraints + double w = 100000; + for (unsigned fid=0; fid < F.rows(); ++fid) + { + if (S_c[fid]) + { + for (unsigned i=0;i<3;++i) + { + triplets.push_back(triplet(3*fid + i,3*fid + i,w)); + b(3*fid + i) += w*S(fid,i); + } + } + } + + SparseMatrix soft(3*F.rows(),3*F.rows()); + soft.setFromTriplets(triplets.begin(), triplets.end()); + + SparseMatrix M; + + M = L + soft; + + // Solve Lx = b; + + SparseLU > solver; + + solver.compute(M); + + if(solver.info()!=Success) + { + std::cerr << "LU failed - frame_interpolator.cpp" << std::endl; + assert(0); + } + + MatrixXd x; + x = solver.solve(b); + + if(solver.info()!=Success) + { + std::cerr << "Linear solve failed - frame_interpolator.cpp" << std::endl; + assert(0); + } + + S = MatrixXd::Zero(F.rows(),3); + + // Copy back the result + for (unsigned i=0;i svd(V,Eigen::ComputeFullU | Eigen::ComputeFullV); + + U = svd.matrixU() * svd.matrixV().transpose(); + P = svd.matrixV() * svd.singularValues().asDiagonal() * svd.matrixV().transpose(); +} + +} +} +} + +IGL_INLINE void igl::copyleft::comiso::frame_field( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::VectorXi& b, + const Eigen::MatrixXd& bc1, + const Eigen::MatrixXd& bc2, + Eigen::MatrixXd& FF1, + Eigen::MatrixXd& FF2 + ) + +{ + using namespace std; + using namespace Eigen; + + assert(b.size() > 0); + + // Init Solver + FrameInterpolator field(V,F); + + for (unsigned i=0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COMISO_FRAMEFIELD_H +#define IGL_COMISO_FRAMEFIELD_H + +#include +#include +#include +#include + +namespace igl +{ +namespace copyleft +{ +namespace comiso +{ +// Generate a piecewise-constant frame-field field from a sparse set of constraints on faces +// using the algorithm proposed in: +// Frame Fields: Anisotropic and Non-Orthogonal Cross Fields +// Daniele Panozzo, Enrico Puppo, Marco Tarini, Olga Sorkine-Hornung, +// ACM Transactions on Graphics (SIGGRAPH, 2014) +// +// Inputs: +// V #V by 3 list of mesh vertex coordinates +// F #F by 3 list of mesh faces (must be triangles) +// b #B by 1 list of constrained face indices +// bc1 #B by 3 list of the constrained first representative vector of the frame field (up to permutation and sign) +// bc2 #B by 3 list of the constrained second representative vector of the frame field (up to permutation and sign) +// +// Outputs: +// FF1 #F by 3 the first representative vector of the frame field (up to permutation and sign) +// FF2 #F by 3 the second representative vector of the frame field (up to permutation and sign) +// +// TODO: it now supports only soft constraints, should be extended to support both hard and soft constraints +IGL_INLINE void frame_field( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::VectorXi& b, + const Eigen::MatrixXd& bc1, + const Eigen::MatrixXd& bc2, + Eigen::MatrixXd& FF1, + Eigen::MatrixXd& FF2 + ); +} +} +} + +#ifndef IGL_STATIC_LIBRARY +# include "frame_field.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/comiso/miq.cpp b/src/igl/copyleft/comiso/miq.cpp new file mode 100644 index 0000000000..ecf3d282be --- /dev/null +++ b/src/igl/copyleft/comiso/miq.cpp @@ -0,0 +1,1534 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti , Kevin Walliman +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "miq.h" +#include "../../local_basis.h" +#include "../../triangle_triangle_adjacency.h" +#include "../../cut_mesh.h" +#include "../../LinSpaced.h" + +// includes for VertexIndexing +#include "../../HalfEdgeIterator.h" +#include "../../is_border_vertex.h" +#include "../../vertex_triangle_adjacency.h" + +// includes for PoissonSolver +#include "../../slice_into.h" +#include "../../grad.h" +#include "../../cotmatrix.h" +#include "../../doublearea.h" +#include +#include +#include +#include + +// +#include "../../cross_field_missmatch.h" +#include "../../comb_frame_field.h" +#include "../../comb_cross_field.h" +#include "../../cut_mesh_from_singularities.h" +#include "../../find_cross_field_singularities.h" +#include "../../compute_frame_field_bisectors.h" +#include "../../rotate_vectors.h" + +#ifndef NDEBUG +#include +#endif +#include +#include "../../matlab_format.h" + +#define DEBUGPRINT 0 + + +namespace igl { +namespace copyleft { +namespace comiso { + struct SeamInfo + { + int v0,v0p; + int integerVar; + unsigned char MMatch; + + IGL_INLINE SeamInfo(int _v0, + int _v0p, + int _MMatch, + int _integerVar); + + IGL_INLINE SeamInfo(const SeamInfo &S1); + }; + + struct MeshSystemInfo + { + ////number of vertices variables + int num_vert_variables; + ///num of integer for cuts + int num_integer_cuts; + ///this are used for drawing purposes + std::vector EdgeSeamInfo; + }; + + + template + class VertexIndexing + { + public: + // Input: + const Eigen::PlainObjectBase &V; + const Eigen::PlainObjectBase &F; + const Eigen::PlainObjectBase &Vcut; + const Eigen::PlainObjectBase &Fcut; + const Eigen::PlainObjectBase &TT; + const Eigen::PlainObjectBase &TTi; + + const Eigen::Matrix &Handle_MMatch; + const Eigen::Matrix &Handle_Singular; // bool + const Eigen::Matrix &Handle_Seams; // 3 bool + + + ///this handle for mesh TODO: move with the other global variables + MeshSystemInfo Handle_SystemInfo; + + IGL_INLINE VertexIndexing(const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const Eigen::PlainObjectBase &_Vcut, + const Eigen::PlainObjectBase &_Fcut, + const Eigen::PlainObjectBase &_TT, + const Eigen::PlainObjectBase &_TTi, + const Eigen::Matrix &_Handle_MMatch, + const Eigen::Matrix &_Handle_Singular, + const Eigen::Matrix &_Handle_Seams + ); + + // provide information about every vertex per seam + IGL_INLINE void InitSeamInfo(); + + + private: + struct VertexInfo{ + int v; // vertex index (according to V) + int f0, k0; // face and local edge information of the edge that connects this vertex to the previous vertex (previous in the vector) + int f1, k1; // face and local edge information of the other face corresponding to the same edge + VertexInfo(int _v, int _f0, int _k0, int _f1, int _k1) : + v(_v), f0(_f0), k0(_k0), f1(_f1), k1(_k1){} + bool operator==(VertexInfo const& other){ + return other.v == v; + } + }; + + IGL_INLINE void GetSeamInfo(const int f0, + const int f1, + const int indexE, + int &v0,int &v1, + int &v0p,int &v1p, + unsigned char &_MMatch); + + IGL_INLINE std::vector > GetVerticesPerSeam(); + }; + + + template + class PoissonSolver + { + + public: + IGL_INLINE void SolvePoisson(Eigen::VectorXd Stiffness, + double vector_field_scale=0.1f, + double grid_res=1.f, + bool direct_round=true, + int localIter=0, + bool _integer_rounding=true, + bool _singularity_rounding=true, + std::vector roundVertices = std::vector(), + std::vector > hardFeatures = std::vector >()); + + IGL_INLINE PoissonSolver(const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const Eigen::PlainObjectBase &_Vcut, + const Eigen::PlainObjectBase &_Fcut, + const Eigen::PlainObjectBase &_TT, + const Eigen::PlainObjectBase &_TTi, + const Eigen::PlainObjectBase &_PD1, + const Eigen::PlainObjectBase &_PD2, + const Eigen::Matrix&_Handle_Singular, + const MeshSystemInfo &_Handle_SystemInfo + ); + + const Eigen::PlainObjectBase &V; + const Eigen::PlainObjectBase &F; + const Eigen::PlainObjectBase &Vcut; + const Eigen::PlainObjectBase &Fcut; + const Eigen::PlainObjectBase &TT; + const Eigen::PlainObjectBase &TTi; + const Eigen::PlainObjectBase &PD1; + const Eigen::PlainObjectBase &PD2; + const Eigen::Matrix &Handle_Singular; // bool + + const MeshSystemInfo &Handle_SystemInfo; + + // Internal: + Eigen::VectorXd Handle_Stiffness; + std::vector > VF; + std::vector > VFi; + Eigen::MatrixXd UV; // this is probably useless + + // Output: + // per wedge UV coordinates, 6 coordinates (1 face) per row + Eigen::MatrixXd WUV; + // per vertex UV coordinates, Vcut.rows() x 2 + Eigen::MatrixXd UV_out; + + // Matrices + Eigen::SparseMatrix Lhs; + Eigen::SparseMatrix Constraints; + Eigen::VectorXd rhs; + Eigen::VectorXd constraints_rhs; + ///vector of unknowns + std::vector< double > X; + + ////REAL PART + ///number of fixed vertex + unsigned int n_fixed_vars; + + ///the number of REAL variables for vertices + unsigned int n_vert_vars; + + ///total number of variables of the system, + ///do not consider constraints, but consider integer vars + unsigned int num_total_vars; + + //////INTEGER PART + ///the total number of integer variables + unsigned int n_integer_vars; + + ///CONSTRAINT PART + ///number of cuts constraints + unsigned int num_cut_constraint; + + // number of user-defined constraints + unsigned int num_userdefined_constraint; + + ///total number of constraints equations + unsigned int num_constraint_equations; + + ///vector of blocked vertices + std::vector Hard_constraints; + + ///vector of indexes to round + std::vector ids_to_round; + + ///vector of indexes to round + std::vector > userdefined_constraints; + + ///boolean that is true if rounding to integer is needed + bool integer_rounding; + + ///START COMMON MATH FUNCTIONS + ///return the complex encoding the rotation + ///for a given missmatch interval + IGL_INLINE std::complex GetRotationComplex(int interval); + ///END COMMON MATH FUNCTIONS + + ///START FIXING VERTICES + ///set a given vertex as fixed + IGL_INLINE void AddFixedVertex(int v); + + ///find vertex to fix in case we're using + ///a vector field NB: multiple components not handled + IGL_INLINE void FindFixedVertField(); + + ///find hard constraint depending if using or not + ///a vector field + IGL_INLINE void FindFixedVert(); + + IGL_INLINE int GetFirstVertexIndex(int v); + + ///fix the vertices which are flagged as fixed + IGL_INLINE void FixBlockedVertex(); + ///END FIXING VERTICES + + ///HANDLING SINGULARITY + //set the singularity round to integer location + IGL_INLINE void AddSingularityRound(); + + IGL_INLINE void AddToRoundVertices(std::vector ids); + + ///START GENERIC SYSTEM FUNCTIONS + //build the laplacian matrix cyclyng over all rangemaps + //and over all faces + IGL_INLINE void BuildLaplacianMatrix(double vfscale=1); + + ///find different sized of the system + IGL_INLINE void FindSizes(); + + IGL_INLINE void AllocateSystem(); + + ///intitialize the whole matrix + IGL_INLINE void InitMatrix(); + + ///map back coordinates after that + ///the system has been solved + IGL_INLINE void MapCoords(); + ///END GENERIC SYSTEM FUNCTIONS + + ///set the constraints for the inter-range cuts + IGL_INLINE void BuildSeamConstraintsExplicitTranslation(); + + ///set the constraints for the inter-range cuts + IGL_INLINE void BuildUserDefinedConstraints(); + + ///call of the mixed integer solver + IGL_INLINE void MixedIntegerSolve(double cone_grid_res=1, + bool direct_round=true, + int localIter=0); + + IGL_INLINE void clearUserConstraint(); + + IGL_INLINE void addSharpEdgeConstraint(int fid, int vid); + + }; + + template + class MIQ_class + { + private: + const Eigen::PlainObjectBase &V; + const Eigen::PlainObjectBase &F; + DerivedV Vcut; + DerivedF Fcut; + Eigen::MatrixXd UV_out; + DerivedF FUV_out; + + // internal + DerivedF TT; + DerivedF TTi; + + // Stiffness per face + Eigen::VectorXd Handle_Stiffness; + DerivedV B1, B2, B3; + + public: + IGL_INLINE MIQ_class(const Eigen::PlainObjectBase &V_, + const Eigen::PlainObjectBase &F_, + const Eigen::PlainObjectBase &PD1_combed, + const Eigen::PlainObjectBase &PD2_combed, + const Eigen::Matrix &Handle_MMatch, + const Eigen::Matrix &Handle_Singular, + const Eigen::Matrix &Handle_Seams, + Eigen::PlainObjectBase &UV, + Eigen::PlainObjectBase &FUV, + double GradientSize = 30.0, + double Stiffness = 5.0, + bool DirectRound = false, + int iter = 5, + int localIter = 5, + bool DoRound = true, + bool SingularityRound=true, + std::vector roundVertices = std::vector(), + std::vector > hardFeatures = std::vector >()); + + + IGL_INLINE void extractUV(Eigen::PlainObjectBase &UV_out, + Eigen::PlainObjectBase &FUV_out); + + private: + IGL_INLINE int NumFlips(const Eigen::MatrixXd& WUV); + + IGL_INLINE double Distortion(int f, double h, const Eigen::MatrixXd& WUV); + + IGL_INLINE double LaplaceDistortion(const int f, double h, const Eigen::MatrixXd& WUV); + + IGL_INLINE bool updateStiffeningJacobianDistorsion(double grad_size, const Eigen::MatrixXd& WUV); + + IGL_INLINE bool IsFlipped(const Eigen::Vector2d &uv0, + const Eigen::Vector2d &uv1, + const Eigen::Vector2d &uv2); + + IGL_INLINE bool IsFlipped(const int i, const Eigen::MatrixXd& WUV); + + }; +}; +}; +} + +IGL_INLINE igl::copyleft::comiso::SeamInfo::SeamInfo(int _v0, + int _v0p, + int _MMatch, + int _integerVar) +{ + v0=_v0; + v0p=_v0p; + integerVar=_integerVar; + MMatch=_MMatch; +} + +IGL_INLINE igl::copyleft::comiso::SeamInfo::SeamInfo(const SeamInfo &S1) +{ + v0=S1.v0; + v0p=S1.v0p; + integerVar=S1.integerVar; + MMatch=S1.MMatch; +} + + +template +IGL_INLINE igl::copyleft::comiso::VertexIndexing::VertexIndexing(const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const Eigen::PlainObjectBase &_Vcut, + const Eigen::PlainObjectBase &_Fcut, + const Eigen::PlainObjectBase &_TT, + const Eigen::PlainObjectBase &_TTi, + const Eigen::Matrix &_Handle_MMatch, + const Eigen::Matrix &_Handle_Singular, + const Eigen::Matrix &_Handle_Seams + ): +V(_V), +F(_F), +Vcut(_Vcut), +Fcut(_Fcut), +TT(_TT), +TTi(_TTi), +Handle_MMatch(_Handle_MMatch), +Handle_Singular(_Handle_Singular), +Handle_Seams(_Handle_Seams) +{ + #ifdef DEBUG_PRINT + cerr< +IGL_INLINE void igl::copyleft::comiso::VertexIndexing::GetSeamInfo(const int f0, + const int f1, + const int indexE, + int &v0,int &v1, + int &v0p,int &v1p, + unsigned char &_MMatch) +{ + int edgef0 = indexE; + v0 = Fcut(f0,edgef0); + v1 = Fcut(f0,(edgef0+1)%3); + ////get the index on opposite side + assert(TT(f0,edgef0) == f1); + int edgef1 = TTi(f0,edgef0); + v1p = Fcut(f1,edgef1); + v0p = Fcut(f1,(edgef1+1)%3); + + _MMatch = Handle_MMatch(f0,edgef0); + assert(F(f0,edgef0) == F(f1,((edgef1+1)%3))); + assert(F(f0,((edgef0+1)%3)) == F(f1,edgef1)); +} + +template +IGL_INLINE std::vector::VertexInfo> > igl::copyleft::comiso::VertexIndexing::GetVerticesPerSeam() +{ + // Return value + std::vector >verticesPerSeam; + + // for every vertex, keep track of their adjacent vertices on seams. + // regular vertices have two neighbors on a seam, start- and endvertices may have any other numbers of neighbors (e.g. 1 or 3) + std::vector > VVSeam(V.rows()); + Eigen::MatrixXi F_hit = Eigen::MatrixXi::Zero(F.rows(), 3); + for (unsigned int f=0; f startVertexIndices; + std::vector isStartVertex(V.rows()); + for (unsigned int i=0;i 0 && VVSeam[i].size() != 2 || Handle_Singular(i) == true) + { + startVertexIndices.push_back(i); + isStartVertex[i] = true; + } + } + + // For each startVertex, walk along its seam + for (unsigned int i=0;isize(); + + // explore every seam to which this vertex is a start vertex + // note: a vertex can never be a start vertex and a regular vertex simultaneously + for (unsigned int j=0;j thisSeam; // temporary container + + // Create vertexInfo struct for start vertex + auto startVertex = VertexInfo(startVertexIndices[i], -1, -1, -1, -1);// -1 values are arbitrary (will never be used) + auto currentVertex = startVertex; + // Add start vertex to the seam + thisSeam.push_back(currentVertex); + + // advance on the seam + auto currentVertexNeighbors = startVertexNeighbors; + auto nextVertex = currentVertexNeighbors->front(); + currentVertexNeighbors->pop_front(); + + auto prevVertex = startVertex; // bogus initialization to get the type + while (true) + { + // move to the next vertex + prevVertex = currentVertex; + currentVertex = nextVertex; + currentVertexNeighbors = &VVSeam[nextVertex.v]; + + // add current vertex to this seam + thisSeam.push_back(currentVertex); + + // remove the previous vertex + auto it = std::find(currentVertexNeighbors->begin(), currentVertexNeighbors->end(), prevVertex); + assert(it != currentVertexNeighbors->end()); + currentVertexNeighbors->erase(it); + + if (currentVertexNeighbors->size() == 1 && !isStartVertex[currentVertex.v]) + { + nextVertex = currentVertexNeighbors->front(); + currentVertexNeighbors->pop_front(); + } + else + break; + } + verticesPerSeam.push_back(thisSeam); + } + } + + return verticesPerSeam; +} + +template +IGL_INLINE void igl::copyleft::comiso::VertexIndexing::InitSeamInfo() +{ + auto verticesPerSeam = GetVerticesPerSeam(); + Handle_SystemInfo.EdgeSeamInfo.clear(); + int integerVar = 0; + // Loop over each seam + for(auto seam : verticesPerSeam){ + //choose initial side of the seam such that the start vertex corresponds to Fcut(f, k) and the end vertex corresponds to Fcut(f, (k+1)%3) and not vice versa. + int priorVertexIdx; + if(seam.size() > 2){ + auto v1 = seam[1]; + auto v2 = seam[2]; + if(Fcut(v1.f0, (v1.k0+1) % 3) == Fcut(v2.f0, v2.k0) || Fcut(v1.f0, (v1.k0+1) % 3) == Fcut(v2.f1, v2.k1)){ + priorVertexIdx = Fcut(v1.f0, v1.k0); + } + else{ + priorVertexIdx = Fcut(v1.f1, v1.k1); + assert(Fcut(v1.f1, (v1.k1+1) % 3) == Fcut(v2.f0, v2.k0) || Fcut(v1.f1, (v1.k1+1) % 3) == Fcut(v2.f1, v2.k1)); + } + } + else{ + auto v1 = seam[1]; + priorVertexIdx = Fcut(v1.f0, v1.k0); + } + + // Loop over each vertex of the seam + for(auto it=seam.begin()+1; it != seam.end(); ++it){ + auto vertex = *it; + // choose the correct side of the seam + int f,k,ff,kk; + if(priorVertexIdx == Fcut(vertex.f0, vertex.k0)){ + f = vertex.f0; ff = vertex.f1; + k = vertex.k0; kk = vertex.k1; + } + else{ + f = vertex.f1; ff = vertex.f0; + k = vertex.k1; kk = vertex.k0; + assert(priorVertexIdx == Fcut(vertex.f1, vertex.k1)); + } + + int vtx0,vtx0p,vtx1,vtx1p; + unsigned char MM; + GetSeamInfo(f,ff,k,vtx0,vtx1,vtx0p,vtx1p,MM); + Handle_SystemInfo.EdgeSeamInfo.push_back(SeamInfo(vtx0,vtx0p,MM,integerVar)); + if(it == seam.end() -1){ + Handle_SystemInfo.EdgeSeamInfo.push_back(SeamInfo(vtx1,vtx1p,MM,integerVar)); + } + priorVertexIdx = vtx1; + } + // use the same integer for each seam + integerVar++; + } + Handle_SystemInfo.num_integer_cuts = integerVar; + +#ifndef NDEBUG + int totalNVerticesOnSeams = 0; + for(auto seam : verticesPerSeam){ + totalNVerticesOnSeams += seam.size(); + } + assert(Handle_SystemInfo.EdgeSeamInfo.size() == totalNVerticesOnSeams); +#endif +} + + + +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::SolvePoisson(Eigen::VectorXd Stiffness, + double vector_field_scale, + double grid_res, + bool direct_round, + int localIter, + bool _integer_rounding, + bool _singularity_rounding, + std::vector roundVertices, + std::vector > hardFeatures) +{ + Handle_Stiffness = Stiffness; + + //initialization of flags and data structures + integer_rounding=_integer_rounding; + + ids_to_round.clear(); + + clearUserConstraint(); + // copy the user constraints number + for (size_t i = 0; i < hardFeatures.size(); ++i) + { + addSharpEdgeConstraint(hardFeatures[i][0],hardFeatures[i][1]); + } + + ///Initializing Matrix + + int t0=clock(); + + ///initialize the matrix ALLOCATING SPACE + InitMatrix(); + if (DEBUGPRINT) + printf("\n ALLOCATED THE MATRIX \n"); + + ///build the laplacian system + BuildLaplacianMatrix(vector_field_scale); + + // add seam constraints + BuildSeamConstraintsExplicitTranslation(); + + // add user defined constraints + BuildUserDefinedConstraints(); + + ////add the lagrange multiplier + FixBlockedVertex(); + + if (DEBUGPRINT) + printf("\n BUILT THE MATRIX \n"); + + if (integer_rounding) + AddToRoundVertices(roundVertices); + + if (_singularity_rounding) + AddSingularityRound(); + + int t1=clock(); + if (DEBUGPRINT) printf("\n time:%d \n",t1-t0); + if (DEBUGPRINT) printf("\n SOLVING \n"); + + MixedIntegerSolve(grid_res,direct_round,localIter); + + int t2=clock(); + if (DEBUGPRINT) printf("\n time:%d \n",t2-t1); + if (DEBUGPRINT) printf("\n ASSIGNING COORDS \n"); + + MapCoords(); + + int t3=clock(); + if (DEBUGPRINT) printf("\n time:%d \n",t3-t2); + if (DEBUGPRINT) printf("\n FINISHED \n"); +} + +template +IGL_INLINE igl::copyleft::comiso::PoissonSolver +::PoissonSolver(const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const Eigen::PlainObjectBase &_Vcut, + const Eigen::PlainObjectBase &_Fcut, + const Eigen::PlainObjectBase &_TT, + const Eigen::PlainObjectBase &_TTi, + const Eigen::PlainObjectBase &_PD1, + const Eigen::PlainObjectBase &_PD2, + const Eigen::Matrix&_Handle_Singular, + const MeshSystemInfo &_Handle_SystemInfo +): +V(_V), +F(_F), +Vcut(_Vcut), +Fcut(_Fcut), +TT(_TT), +TTi(_TTi), +PD1(_PD1), +PD2(_PD2), +Handle_Singular(_Handle_Singular), +Handle_SystemInfo(_Handle_SystemInfo) +{ + UV = Eigen::MatrixXd(V.rows(),2); + WUV = Eigen::MatrixXd(F.rows(),6); + UV_out = Eigen::MatrixXd(Vcut.rows(),2); + igl::vertex_triangle_adjacency(V,F,VF,VFi); +} + +///START COMMON MATH FUNCTIONS +///return the complex encoding the rotation +///for a given missmatch interval +template +IGL_INLINE std::complex igl::copyleft::comiso::PoissonSolver::GetRotationComplex(int interval) +{ + assert((interval>=0)&&(interval<4)); + + switch(interval) + { + case 0:return std::complex(1,0); + case 1:return std::complex(0,1); + case 2:return std::complex(-1,0); + default:return std::complex(0,-1); + } +} + +///END COMMON MATH FUNCTIONS + +///START FIXING VERTICES +///set a given vertex as fixed +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::AddFixedVertex(int v) +{ + n_fixed_vars++; + Hard_constraints.push_back(v); +} + +///find vertex to fix in case we're using +///a vector field NB: multiple components not handled +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::FindFixedVertField() +{ + Hard_constraints.clear(); + + n_fixed_vars=0; + //fix the first singularity + for (unsigned int v=0;v +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::FindFixedVert() +{ + Hard_constraints.clear(); + FindFixedVertField(); +} + +template +IGL_INLINE int igl::copyleft::comiso::PoissonSolver::GetFirstVertexIndex(int v) +{ + return Fcut(VF[v][0],VFi[v][0]); +} + +///fix the vertices which are flagged as fixed +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::FixBlockedVertex() +{ + int offset_row = num_cut_constraint*2; + + unsigned int constr_num = 0; + for (unsigned int i=0;ivertex_index[0]; + int index = GetFirstVertexIndex(v); + + ///multiply times 2 because of uv + int indexvert = index*2; + + ///find the first free row to add the constraint + int indexRow = (offset_row+constr_num*2); + int indexCol = indexRow; + + ///add fixing constraint LHS + Constraints.coeffRef(indexRow, indexvert) += 1; + Constraints.coeffRef(indexRow+1,indexvert+1) += 1; + + ///add fixing constraint RHS + constraints_rhs[indexCol] = UV(v,0); + constraints_rhs[indexCol+1] = UV(v,1); + + constr_num++; + } + assert(constr_num==n_fixed_vars); +} +///END FIXING VERTICES + +///HANDLING SINGULARITY +//set the singularity round to integer location +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::AddSingularityRound() +{ + for (unsigned int v=0;v +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::AddToRoundVertices(std::vector ids) +{ + for (size_t i = 0; i < ids.size(); ++i) + { + if (ids[i] < 0 || ids[i] >= V.rows()) + std::cerr << "WARNING: Ignored round vertex constraint, vertex " << ids[i] << " does not exist in the mesh." << std::endl; + int index0 = GetFirstVertexIndex(ids[i]); + ids_to_round.push_back( index0*2 ); + ids_to_round.push_back((index0*2)+1); + } +} + +///START GENERIC SYSTEM FUNCTIONS +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::BuildLaplacianMatrix(double vfscale) +{ + Eigen::VectorXi idx = igl::LinSpaced(Vcut.rows(), 0, 2*Vcut.rows()-2); + Eigen::VectorXi idx2 = igl::LinSpaced(Vcut.rows(), 1, 2*Vcut.rows()-1); + + // get gradient matrix + Eigen::SparseMatrix G(Fcut.rows() * 3, Vcut.rows()); + igl::grad(Vcut, Fcut, G); + + // get triangle weights + Eigen::VectorXd dblA(Fcut.rows()); + igl::doublearea(Vcut, Fcut, dblA); + + // compute intermediate result + Eigen::SparseMatrix G2; + G2 = G.transpose() * dblA.replicate<3,1>().asDiagonal() * Handle_Stiffness.replicate<3,1>().asDiagonal(); + + /// Compute LHS + Eigen::SparseMatrix Cotmatrix; + Cotmatrix = 0.5 * G2 * G; + igl::slice_into(Cotmatrix, idx, idx, Lhs); + igl::slice_into(Cotmatrix, idx2, idx2, Lhs); + + /// Compute RHS + // reshape nrosy vectors + const Eigen::MatrixXd u = Eigen::Map(PD1.data(),Fcut.rows()*3,1); // this mimics a reshape at the cost of a copy. + const Eigen::MatrixXd v = Eigen::Map(PD2.data(),Fcut.rows()*3,1); // this mimics a reshape at the cost of a copy. + + // multiply with weights + Eigen::VectorXd rhs1 = G2 * u * 0.5 * vfscale; + Eigen::VectorXd rhs2 = -G2 * v * 0.5 * vfscale; + igl::slice_into(rhs1, idx, 1, rhs); + igl::slice_into(rhs2, idx2, 1, rhs); +} + +///find different sized of the system +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::FindSizes() +{ + ///find the vertex that need to be fixed + FindFixedVert(); + + ///REAL PART + n_vert_vars = Handle_SystemInfo.num_vert_variables; + + ///INTEGER PART + ///the total number of integer variables + n_integer_vars = Handle_SystemInfo.num_integer_cuts; + + ///CONSTRAINT PART + num_cut_constraint = Handle_SystemInfo.EdgeSeamInfo.size(); + + num_constraint_equations = num_cut_constraint * 2 + n_fixed_vars * 2 + num_userdefined_constraint; + + ///total variable of the system + num_total_vars = (n_vert_vars+n_integer_vars) * 2; + + ///initialize matrix size + + if (DEBUGPRINT) printf("\n*** SYSTEM VARIABLES *** \n"); + if (DEBUGPRINT) printf("* NUM REAL VERTEX VARIABLES %d \n",n_vert_vars); + + if (DEBUGPRINT) printf("\n*** INTEGER VARIABLES *** \n"); + if (DEBUGPRINT) printf("* NUM INTEGER VARIABLES %d \n",(int)n_integer_vars); + + if (DEBUGPRINT) printf("\n*** CONSTRAINTS *** \n "); + if (DEBUGPRINT) printf("* NUM FIXED CONSTRAINTS %d\n",n_fixed_vars); + if (DEBUGPRINT) printf("* NUM CUTS CONSTRAINTS %d\n",num_cut_constraint); + if (DEBUGPRINT) printf("* NUM USER DEFINED CONSTRAINTS %d\n",num_userdefined_constraint); + + if (DEBUGPRINT) printf("\n*** TOTAL SIZE *** \n"); + if (DEBUGPRINT) printf("* TOTAL VARIABLE SIZE (WITH INTEGER TRASL) %d \n",num_total_vars); + if (DEBUGPRINT) printf("* TOTAL CONSTRAINTS %d \n",num_constraint_equations); +} + +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::AllocateSystem() +{ + Lhs.resize(n_vert_vars * 2, n_vert_vars * 2); + Constraints.resize(num_constraint_equations, num_total_vars); + rhs.resize(n_vert_vars * 2); + constraints_rhs.resize(num_constraint_equations); + + printf("\n INITIALIZED SPARSE MATRIX OF %d x %d \n",n_vert_vars*2, n_vert_vars*2); + printf("\n INITIALIZED SPARSE MATRIX OF %d x %d \n",num_constraint_equations, num_total_vars); + printf("\n INITIALIZED VECTOR OF %d x 1 \n",n_vert_vars*2); + printf("\n INITIALIZED VECTOR OF %d x 1 \n",num_constraint_equations); +} + +///intitialize the whole matrix +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::InitMatrix() +{ + FindSizes(); + AllocateSystem(); +} + +///map back coordinates after that +///the system has been solved +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::MapCoords() +{ + ///map coords to faces + for (unsigned int f=0;f +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::BuildSeamConstraintsExplicitTranslation() +{ + ///current constraint row + int constr_row = 0; + + for (unsigned int i=0; i rot = GetRotationComplex(interval); + + ///get the integer variable + int integerVar = n_vert_vars + Handle_SystemInfo.EdgeSeamInfo[i].integerVar; + + if (integer_rounding) + { + ids_to_round.push_back(integerVar*2); + ids_to_round.push_back(integerVar*2+1); + } + + // cross boundary compatibility conditions + Constraints.coeffRef(constr_row, 2*p0) += rot.real(); + Constraints.coeffRef(constr_row, 2*p0+1) += -rot.imag(); + Constraints.coeffRef(constr_row+1, 2*p0) += rot.imag(); + Constraints.coeffRef(constr_row+1, 2*p0+1) += rot.real(); + + Constraints.coeffRef(constr_row, 2*p0p) += -1; + Constraints.coeffRef(constr_row+1, 2*p0p+1) += -1; + + Constraints.coeffRef(constr_row, 2*integerVar) += 1; + Constraints.coeffRef(constr_row+1, 2*integerVar+1) += 1; + + constraints_rhs[constr_row] = 0; + constraints_rhs[constr_row+1] = 0; + + constr_row += 2; + } + +} + +///set the constraints for the inter-range cuts +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::BuildUserDefinedConstraints() +{ + /// the user defined constraints are at the end + int offset_row = num_cut_constraint*2 + n_fixed_vars*2; + + ///current constraint row + int constr_row = offset_row; + + assert(num_userdefined_constraint == userdefined_constraints.size()); + + for (unsigned int i=0; i +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::MixedIntegerSolve(double cone_grid_res, + bool direct_round, + int localIter) +{ + X = std::vector((n_vert_vars+n_integer_vars)*2); + if (DEBUGPRINT) + printf("\n ALLOCATED X \n"); + + ///variables part + int ScalarSize = n_vert_vars*2; + int SizeMatrix = (n_vert_vars+n_integer_vars)*2; + + ///matrix A + gmm::col_matrix< gmm::wsvector< double > > A(SizeMatrix,SizeMatrix); // lhs matrix variables + + ///constraints part + int CsizeX = num_constraint_equations; + int CsizeY = SizeMatrix+1; + gmm::row_matrix< gmm::wsvector< double > > C(CsizeX,CsizeY); // constraints + + if (DEBUGPRINT) + printf("\n ALLOCATED QMM STRUCTURES \n"); + + std::vector B(SizeMatrix,0); // rhs + + if (DEBUGPRINT) + printf("\n ALLOCATED RHS STRUCTURES \n"); + + //// copy LHS + for (int k=0; k < Lhs.outerSize(); ++k){ + for (Eigen::SparseMatrix::InnerIterator it(Lhs,k); it; ++it){ + int row = it.row(); + int col = it.col(); + A(row, col) += it.value(); + } + } + //// copy Constraints + for (int k=0; k < Constraints.outerSize(); ++k){ + for (Eigen::SparseMatrix::InnerIterator it(Constraints,k); it; ++it){ + int row = it.row(); + int col = it.col(); + C(row, col) += it.value(); + } + } + + if (DEBUGPRINT) + printf("\n SET %d INTEGER VALUES \n",n_integer_vars); + + ///add penalization term for integer variables + double penalization = 0.000001; + int offline_index = ScalarSize; + for(unsigned int i = 0; i < (n_integer_vars)*2; ++i) + { + int index=offline_index+i; + A(index,index)=penalization; + } + + if (DEBUGPRINT) + printf("\n SET RHS \n"); + + // copy RHS + for(int i = 0; i < (int)ScalarSize; ++i) + { + B[i] = rhs[i] * cone_grid_res; + } + + // copy constraint RHS + if (DEBUGPRINT) + printf("\n SET %d CONSTRAINTS \n",num_constraint_equations); + + for(unsigned int i = 0; i < num_constraint_equations; ++i) + { + C(i, SizeMatrix) = -constraints_rhs[i] * cone_grid_res; + } + + COMISO::ConstrainedSolver solver; + + solver.misolver().set_local_iters(localIter); + + solver.misolver().set_direct_rounding(direct_round); + + std::sort(ids_to_round.begin(),ids_to_round.end()); + std::vector::iterator new_end=std::unique(ids_to_round.begin(),ids_to_round.end()); + int dist=distance(ids_to_round.begin(),new_end); + ids_to_round.resize(dist); + + solver.solve( C, A, X, B, ids_to_round, 0.0, false, false); +} + +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::clearUserConstraint() +{ + num_userdefined_constraint = 0; + userdefined_constraints.clear(); +} + +template +IGL_INLINE void igl::copyleft::comiso::PoissonSolver::addSharpEdgeConstraint(int fid, int vid) +{ + // prepare constraint + std::vector c(Handle_SystemInfo.num_vert_variables*2 + 1); + + for (size_t i = 0; i < c.size(); ++i) + { + c[i] = 0; + } + + int v1 = Fcut(fid,vid); + int v2 = Fcut(fid,(vid+1)%3); + + Eigen::Matrix e = Vcut.row(v2) - Vcut.row(v1); + e = e.normalized(); + + double d1 = fabs(e.dot(PD1.row(fid).normalized())); + double d2 = fabs(e.dot(PD2.row(fid).normalized())); + + int offset = 0; + + if (d1>d2) + offset = 1; + + ids_to_round.push_back((v1 * 2) + offset); + ids_to_round.push_back((v2 * 2) + offset); + + // add constraint + c[(v1 * 2) + offset] = 1; + c[(v2 * 2) + offset] = -1; + + // add to the user-defined constraints + num_userdefined_constraint++; + userdefined_constraints.push_back(c); + +} + + + +template +IGL_INLINE igl::copyleft::comiso::MIQ_class::MIQ_class(const Eigen::PlainObjectBase &V_, + const Eigen::PlainObjectBase &F_, + const Eigen::PlainObjectBase &PD1_combed, + const Eigen::PlainObjectBase &PD2_combed, + const Eigen::Matrix &Handle_MMatch, + const Eigen::Matrix &Handle_Singular, + const Eigen::Matrix &Handle_Seams, + Eigen::PlainObjectBase &UV, + Eigen::PlainObjectBase &FUV, + double GradientSize, + double Stiffness, + bool DirectRound, + int iter, + int localIter, + bool DoRound, + bool SingularityRound, + std::vector roundVertices, + std::vector > hardFeatures): +V(V_), +F(F_) +{ + igl::cut_mesh(V, F, Handle_Seams, Vcut, Fcut); + + igl::local_basis(V,F,B1,B2,B3); + igl::triangle_triangle_adjacency(F,TT,TTi); + + // Prepare indexing for the linear system + VertexIndexing VInd(V, F, Vcut, Fcut, TT, TTi, Handle_MMatch, Handle_Singular, Handle_Seams); + + VInd.InitSeamInfo(); + + // Assemble the system and solve + PoissonSolver PSolver(V, + F, + Vcut, + Fcut, + TT, + TTi, + PD1_combed, + PD2_combed, + Handle_Singular, + VInd.Handle_SystemInfo); + Handle_Stiffness = Eigen::VectorXd::Constant(F.rows(),1); + + + if (iter > 0) // do stiffening + { + for (int i=0;i +IGL_INLINE void igl::copyleft::comiso::MIQ_class::extractUV(Eigen::PlainObjectBase &UV_out, + Eigen::PlainObjectBase &FUV_out) +{ + UV_out = this->UV_out; + FUV_out = this->FUV_out; +} + +template +IGL_INLINE int igl::copyleft::comiso::MIQ_class::NumFlips(const Eigen::MatrixXd& WUV) +{ + int numFl=0; + for (unsigned int i=0;i +IGL_INLINE double igl::copyleft::comiso::MIQ_class::Distortion(int f, double h, const Eigen::MatrixXd& WUV) +{ + assert(h > 0); + + Eigen::Vector2d uv0,uv1,uv2; + + uv0 << WUV(f,0), WUV(f,1); + uv1 << WUV(f,2), WUV(f,3); + uv2 << WUV(f,4), WUV(f,5); + + Eigen::Matrix p0 = Vcut.row(Fcut(f,0)); + Eigen::Matrix p1 = Vcut.row(Fcut(f,1)); + Eigen::Matrix p2 = Vcut.row(Fcut(f,2)); + + Eigen::Matrix norm = (p1 - p0).cross(p2 - p0); + double area2 = norm.norm(); + double area2_inv = 1.0 / area2; + norm *= area2_inv; + + if (area2 > 0) + { + // Singular values of the Jacobian + Eigen::Matrix neg_t0 = norm.cross(p2 - p1); + Eigen::Matrix neg_t1 = norm.cross(p0 - p2); + Eigen::Matrix neg_t2 = norm.cross(p1 - p0); + + Eigen::Matrix diffu = (neg_t0 * uv0(0) +neg_t1 *uv1(0) + neg_t2 * uv2(0) )*area2_inv; + Eigen::Matrix diffv = (neg_t0 * uv0(1) + neg_t1*uv1(1) + neg_t2*uv2(1) )*area2_inv; + + // first fundamental form + double I00 = diffu.dot(diffu); // guaranteed non-neg + double I01 = diffu.dot(diffv); // I01 = I10 + double I11 = diffv.dot(diffv); // guaranteed non-neg + + // eigenvalues of a 2x2 matrix + // [a00 a01] + // [a10 a11] + // 1/2 * [ (a00 + a11) +/- sqrt((a00 - a11)^2 + 4 a01 a10) ] + double trI = I00 + I11; // guaranteed non-neg + double diffDiag = I00 - I11; // guaranteed non-neg + double sqrtDet = sqrt(std::max(0.0, diffDiag*diffDiag + + 4 * I01 * I01)); // guaranteed non-neg + double sig1 = 0.5 * (trI + sqrtDet); // higher singular value + double sig2 = 0.5 * (trI - sqrtDet); // lower singular value + + // Avoid sig2 < 0 due to numerical error + if (fabs(sig2) < 1.0e-8) + sig2 = 0; + + assert(sig1 >= 0); + assert(sig2 >= 0); + + if (sig2 < 0) { + printf("Distortion will be NaN! sig1^2 is negative (%lg)\n", + sig2); + } + + // The singular values of the Jacobian are the sqrts of the + // eigenvalues of the first fundamental form. + sig1 = sqrt(sig1); + sig2 = sqrt(sig2); + + // distortion + double tao = IsFlipped(f,WUV) ? -1 : 1; + double factor = tao / h; + double lam = fabs(factor * sig1 - 1) + fabs(factor * sig2 - 1); + return lam; + } + else { + return 10; // something "large" + } +} + +//////////////////////////////////////////////////////////////////////////// +// Approximate the distortion laplacian using a uniform laplacian on +// the dual mesh: +// ___________ +// \-1 / \-1 / +// \ / 3 \ / +// \-----/ +// \-1 / +// \ / +// +// @param[in] f facet on which to compute distortion laplacian +// @param[in] h scaling factor applied to cross field +// @return distortion laplacian for f +/////////////////////////////////////////////////////////////////////////// +template +IGL_INLINE double igl::copyleft::comiso::MIQ_class::LaplaceDistortion(const int f, double h, const Eigen::MatrixXd& WUV) +{ + double mydist = Distortion(f, h, WUV); + double lapl=0; + for (int i=0;i<3;i++) + { + if (TT(f,i) != -1) + lapl += (mydist - Distortion(TT(f,i), h, WUV)); + } + return lapl; +} + +template +IGL_INLINE bool igl::copyleft::comiso::MIQ_class::updateStiffeningJacobianDistorsion(double grad_size, const Eigen::MatrixXd& WUV) +{ + bool flipped = NumFlips(WUV)>0; + + if (!flipped) + return false; + + double maxL=0; + double maxD=0; + + if (flipped) + { + const double c = 1.0; + const double d = 5.0; + + for (unsigned int i = 0; i < Fcut.rows(); ++i) + { + double dist=Distortion(i,grad_size,WUV); + if (dist > maxD) + maxD=dist; + + double absLap=fabs(LaplaceDistortion(i, grad_size,WUV)); + if (absLap > maxL) + maxL = absLap; + + double stiffDelta = std::min(c * absLap, d); + + Handle_Stiffness[i]+=stiffDelta; + } + } + printf("Maximum Distorsion %4.4f \n",maxD); + printf("Maximum Laplacian %4.4f \n",maxL); + return flipped; +} + +template +IGL_INLINE bool igl::copyleft::comiso::MIQ_class::IsFlipped(const Eigen::Vector2d &uv0, + const Eigen::Vector2d &uv1, + const Eigen::Vector2d &uv2) +{ + Eigen::Vector2d e0 = (uv1-uv0); + Eigen::Vector2d e1 = (uv2-uv0); + + double Area = e0(0)*e1(1) - e0(1)*e1(0); + return (Area<=0); +} + +template +IGL_INLINE bool igl::copyleft::comiso::MIQ_class::IsFlipped( + const int i, const Eigen::MatrixXd& WUV) +{ + Eigen::Vector2d uv0,uv1,uv2; + uv0 << WUV(i,0), WUV(i,1); + uv1 << WUV(i,2), WUV(i,3); + uv2 << WUV(i,4), WUV(i,5); + + return (IsFlipped(uv0,uv1,uv2)); +} + + + + +template +IGL_INLINE void igl::copyleft::comiso::miq( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1_combed, + const Eigen::PlainObjectBase &PD2_combed, + const Eigen::Matrix &Handle_MMatch, + const Eigen::Matrix &Handle_Singular, + const Eigen::Matrix &Handle_Seams, + Eigen::PlainObjectBase &UV, + Eigen::PlainObjectBase &FUV, + double GradientSize, + double Stiffness, + bool DirectRound, + int iter, + int localIter, + bool DoRound, + bool SingularityRound, + std::vector roundVertices, + std::vector > hardFeatures) +{ + GradientSize = GradientSize/(V.colwise().maxCoeff()-V.colwise().minCoeff()).norm(); + + igl::copyleft::comiso::MIQ_class miq(V, + F, + PD1_combed, + PD2_combed, + Handle_MMatch, + Handle_Singular, + Handle_Seams, + UV, + FUV, + GradientSize, + Stiffness, + DirectRound, + iter, + localIter, + DoRound, + SingularityRound, + roundVertices, + hardFeatures); + + miq.extractUV(UV,FUV); +} + +template +IGL_INLINE void igl::copyleft::comiso::miq( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const Eigen::PlainObjectBase &PD2, + Eigen::PlainObjectBase &UV, + Eigen::PlainObjectBase &FUV, + double GradientSize, + double Stiffness, + bool DirectRound, + int iter, + int localIter, + bool DoRound, + bool SingularityRound, + std::vector roundVertices, + std::vector > hardFeatures) +{ + + DerivedV BIS1, BIS2; + igl::compute_frame_field_bisectors(V, F, PD1, PD2, BIS1, BIS2); + + DerivedV BIS1_combed, BIS2_combed; + igl::comb_cross_field(V, F, BIS1, BIS2, BIS1_combed, BIS2_combed); + + DerivedF Handle_MMatch; + igl::cross_field_missmatch(V, F, BIS1_combed, BIS2_combed, true, Handle_MMatch); + + Eigen::Matrix isSingularity, singularityIndex; + igl::find_cross_field_singularities(V, F, Handle_MMatch, isSingularity, singularityIndex); + + Eigen::Matrix Handle_Seams; + igl::cut_mesh_from_singularities(V, F, Handle_MMatch, Handle_Seams); + + DerivedV PD1_combed, PD2_combed; + igl::comb_frame_field(V, F, PD1, PD2, BIS1_combed, BIS2_combed, PD1_combed, PD2_combed); + + igl::copyleft::comiso::miq(V, + F, + PD1_combed, + PD2_combed, + Handle_MMatch, + isSingularity, + Handle_Seams, + UV, + FUV, + GradientSize, + Stiffness, + DirectRound, + iter, + localIter, + DoRound, + SingularityRound, + roundVertices, + hardFeatures); + +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::copyleft::comiso::miq, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, double, double, bool, int, int, bool, bool, std::vector >, std::vector >, std::allocator > > >); +template void igl::copyleft::comiso::miq, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, double, double, bool, int, int, bool, bool, std::vector >, std::vector >, std::allocator > > >); +template void igl::copyleft::comiso::miq, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, double, double, bool, int, int, bool, bool, std::vector >, std::vector >, std::allocator > > >); +#endif diff --git a/src/igl/copyleft/comiso/miq.h b/src/igl/copyleft/comiso/miq.h new file mode 100644 index 0000000000..f4581f65da --- /dev/null +++ b/src/igl/copyleft/comiso/miq.h @@ -0,0 +1,99 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti , Kevin Walliman +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COMISO_MIQ_H +#define IGL_COMISO_MIQ_H +#include "../../igl_inline.h" +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace comiso + { + // Global seamless parametrization aligned with a given per-face jacobian (PD1,PD2). + // The algorithm is based on + // "Mixed-Integer Quadrangulation" by D. Bommes, H. Zimmer, L. Kobbelt + // ACM SIGGRAPH 2009, Article No. 77 (http://dl.acm.org/citation.cfm?id=1531383) + // We thank Nico Pietroni for providing a reference implementation of MIQ + // on which our code is based. + + // Inputs: + // V #V by 3 list of mesh vertex 3D positions + // F #F by 3 list of faces indices in V + // PD1 #V by 3 first line of the Jacobian per triangle + // PD2 #V by 3 second line of the Jacobian per triangle + // (optional, if empty it will be a vector in the tangent plane orthogonal to PD1) + // scale global scaling for the gradient (controls the quads resolution) + // stiffness weight for the stiffness iterations + // direct_round greedily round all integer variables at once (greatly improves optimization speed but lowers quality) + // iter stiffness iterations (0 = no stiffness) + // local_iter number of local iterations for the integer rounding + // do_round enables the integer rounding (disabling it could be useful for debugging) + // round_vertices id of additional vertices that should be snapped to integer coordinates + // hard_features #H by 2 list of pairs of vertices that belongs to edges that should be snapped to integer coordinates + // + // Output: + // UV #UV by 2 list of vertices in 2D + // FUV #FUV by 3 list of face indices in UV + // + // TODO: rename the parameters name in the cpp consistently + // improve the handling of hard_features, right now it might fail in difficult cases + + template + IGL_INLINE void miq( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const Eigen::PlainObjectBase &PD2, + Eigen::PlainObjectBase &UV, + Eigen::PlainObjectBase &FUV, + double scale = 30.0, + double stiffness = 5.0, + bool direct_round = false, + int iter = 5, + int local_iter = 5, + bool DoRound = true,bool SingularityRound=true, + std::vector round_vertices = std::vector(), + std::vector > hard_features = std::vector >()); + + // Helper function that allows to directly provided pre-combed bisectors for an already cut mesh + // Additional input: + // PD1_combed, PD2_combed : #F by 3 combed jacobian + // BIS1_combed, BIS2_combed: #F by 3 pre combed bi-sectors + // MMatch: #F by 3 list of per-corner integer PI/2 rotations + // Singular: #V list of flag that denotes if a vertex is singular or not + // SingularDegree: #V list of flag that denotes the degree of the singularity + // Seams: #F by 3 list of per-corner flag that denotes seams + + template + IGL_INLINE void miq(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1_combed, + const Eigen::PlainObjectBase &PD2_combed, + const Eigen::Matrix &MMatch, + const Eigen::Matrix &Singular, + const Eigen::Matrix &Seams, + Eigen::PlainObjectBase &UV, + Eigen::PlainObjectBase &FUV, + double GradientSize = 30.0, + double Stiffness = 5.0, + bool DirectRound = false, + int iter = 5, + int localIter = 5, bool DoRound = true,bool SingularityRound=true, + std::vector roundVertices = std::vector(), + std::vector > hardFeatures = std::vector >()); + }; +}; +}; +#ifndef IGL_STATIC_LIBRARY +#include "miq.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/comiso/nrosy.cpp b/src/igl/copyleft/comiso/nrosy.cpp new file mode 100644 index 0000000000..0f3ee86bc7 --- /dev/null +++ b/src/igl/copyleft/comiso/nrosy.cpp @@ -0,0 +1,941 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "nrosy.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace igl +{ +namespace copyleft +{ + +namespace comiso +{ +class NRosyField +{ +public: + // Init + IGL_INLINE NRosyField(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F); + + // Generate the N-rosy field + // N degree of the rosy field + // roundseparately: round the integer variables one at a time, slower but higher quality + IGL_INLINE void solve(const int N = 4); + + // Set a hard constraint on fid + // fid: face id + // v: direction to fix (in 3d) + IGL_INLINE void setConstraintHard(const int fid, const Eigen::Vector3d& v); + + // Set a soft constraint on fid + // fid: face id + // w: weight of the soft constraint, clipped between 0 and 1 + // v: direction to fix (in 3d) + IGL_INLINE void setConstraintSoft(const int fid, const double w, const Eigen::Vector3d& v); + + // Set the ratio between smoothness and soft constraints (0 -> smoothness only, 1 -> soft constr only) + IGL_INLINE void setSoftAlpha(double alpha); + + // Reset constraints (at least one constraint must be present or solve will fail) + IGL_INLINE void resetConstraints(); + + // Return the current field + IGL_INLINE Eigen::MatrixXd getFieldPerFace(); + + // Return the current field (in Ahish's ffield format) + IGL_INLINE Eigen::MatrixXd getFFieldPerFace(); + + // Compute singularity indexes + IGL_INLINE void findCones(int N); + + // Return the singularities + IGL_INLINE Eigen::VectorXd getSingularityIndexPerVertex(); + +private: + + // Compute angle differences between reference frames + IGL_INLINE void computek(); + + // Remove useless matchings + IGL_INLINE void reduceSpace(); + + // Prepare the system matrix + IGL_INLINE void prepareSystemMatrix(const int N); + + // Solve without roundings + IGL_INLINE void solveNoRoundings(); + + // Solve with roundings using CoMIso + IGL_INLINE void solveRoundings(); + + // Round all p to 0 and fix + IGL_INLINE void roundAndFixToZero(); + + // Round all p and fix + IGL_INLINE void roundAndFix(); + + // Convert a vector in 3d to an angle wrt the local reference system + IGL_INLINE double convert3DtoLocal(unsigned fid, const Eigen::Vector3d& v); + + // Convert an angle wrt the local reference system to a 3d vector + IGL_INLINE Eigen::Vector3d convertLocalto3D(unsigned fid, double a); + + // Compute the per vertex angle defect + IGL_INLINE Eigen::VectorXd angleDefect(); + + // Temporary variable for the field + Eigen::VectorXd angles; + + // Hard constraints + Eigen::VectorXd hard; + std::vector isHard; + + // Soft constraints + Eigen::VectorXd soft; + Eigen::VectorXd wSoft; + double softAlpha; + + // Face Topology + Eigen::MatrixXi TT, TTi; + + // Edge Topology + Eigen::MatrixXi EV, FE, EF; + std::vector isBorderEdge; + + // Per Edge information + // Angle between two reference frames + Eigen::VectorXd k; + + // Jumps + Eigen::VectorXi p; + std::vector pFixed; + + // Mesh + Eigen::MatrixXd V; + Eigen::MatrixXi F; + + // Normals per face + Eigen::MatrixXd N; + + // Singularity index + Eigen::VectorXd singularityIndex; + + // Reference frame per triangle + std::vector TPs; + + // System stuff + Eigen::SparseMatrix A; + Eigen::VectorXd b; + Eigen::VectorXi tag_t; + Eigen::VectorXi tag_p; + +}; + +} // NAMESPACE COMISO +} // NAMESPACE COPYLEFT +} // NAMESPACE IGL + +igl::copyleft::comiso::NRosyField::NRosyField(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F) +{ + using namespace std; + using namespace Eigen; + + V = _V; + F = _F; + + assert(V.rows() > 0); + assert(F.rows() > 0); + + + // Generate topological relations + igl::triangle_triangle_adjacency(F,TT,TTi); + igl::edge_topology(V,F, EV, FE, EF); + + // Flag border edges + isBorderEdge.resize(EV.rows()); + for(unsigned i=0; i= 0 && alpha < 1); + softAlpha = alpha; +} + + +void igl::copyleft::comiso::NRosyField::prepareSystemMatrix(const int N) +{ + using namespace std; + using namespace Eigen; + + double Nd = N; + + // Minimize the MIQ energy + // Energy on edge ij is + // (t_i - t_j + kij + pij*(2*pi/N))^2 + // Partial derivatives: + // t_i: 2 ( t_i - t_j + kij + pij*(2*pi/N)) = 0 + // t_j: 2 (-t_i + t_j - kij - pij*(2*pi/N)) = 0 + // pij: 4pi/N ( t_i - t_j + kij + pij*(2*pi/N)) = 0 + // + // t_i t_j pij kij + // t_i [ 2 -2 4pi/N 2 ] + // t_j [ -2 2 -4pi/N -2 ] + // pij [ 4pi/N -4pi/N 2*(2pi/N)^2 4pi/N ] + + // Count and tag the variables + tag_t = VectorXi::Constant(F.rows(),-1); + vector id_t; + int count = 0; + for(unsigned i=0; i id_p; + for(unsigned i=0; i > T; + T.reserve(3 * 4 * count_p); + + for(unsigned r=0; r(row,tag_t[i] , 2 )); + if (isFixed_j) b(row) += 2 * hard[j]; else T.push_back(Eigen::Triplet(row,tag_t[j] ,-2 )); + if (isFixed_p) b(row) += -((4 * igl::PI)/Nd) * p[eid] ; else T.push_back(Eigen::Triplet(row,tag_p[eid],((4 * igl::PI)/Nd))); + b(row) += -2 * k[eid]; + assert(hard[i] == hard[i]); + assert(hard[j] == hard[j]); + assert(p[eid] == p[eid]); + assert(k[eid] == k[eid]); + assert(b(row) == b(row)); + } + // (j)+1 -th row: t_j [ -2 2 -4pi/N -2 ] + if (!isFixed_j) + { + row = tag_t[j]; + if (isFixed_i) b(row) += 2 * hard[i]; else T.push_back(Eigen::Triplet(row,tag_t[i] , -2 )); + if (isFixed_j) b(row) += -2 * hard[j]; else T.push_back(Eigen::Triplet(row,tag_t[j] , 2 )); + if (isFixed_p) b(row) += ((4 * igl::PI)/Nd) * p[eid] ; else T.push_back(Eigen::Triplet(row,tag_p[eid],-((4 * igl::PI)/Nd))); + b(row) += 2 * k[eid]; + assert(k[eid] == k[eid]); + assert(b(row) == b(row)); + } + // (r*3)+2 -th row: pij [ 4pi/N -4pi/N 2*(2pi/N)^2 4pi/N ] + if (!isFixed_p) + { + row = tag_p[eid]; + if (isFixed_i) b(row) += -(4 * igl::PI)/Nd * hard[i]; else T.push_back(Eigen::Triplet(row,tag_t[i] , (4 * igl::PI)/Nd )); + if (isFixed_j) b(row) += (4 * igl::PI)/Nd * hard[j]; else T.push_back(Eigen::Triplet(row,tag_t[j] , -(4 * igl::PI)/Nd )); + if (isFixed_p) b(row) += -(2 * pow(((2*igl::PI)/Nd),2)) * p[eid] ; else T.push_back(Eigen::Triplet(row,tag_p[eid], (2 * pow(((2*igl::PI)/Nd),2)))); + b(row) += - (4 * igl::PI)/Nd * k[eid]; + assert(k[eid] == k[eid]); + assert(b(row) == b(row)); + } + + } + + A = SparseMatrix(count_t + count_p, count_t + count_p); + A.setFromTriplets(T.begin(), T.end()); + + // Soft constraints + bool addSoft = false; + + for(unsigned i=0; i > TSoft; + TSoft.reserve(2 * count_p); + + for(unsigned i=0; i(varid,varid,wSoft[i])); + bSoft[varid] += wSoft[i] * soft[i]; + } + } + SparseMatrix ASoft(count_t + count_p, count_t + count_p); + ASoft.setFromTriplets(TSoft.begin(), TSoft.end()); + +// ofstream s("/Users/daniele/As.txt"); +// for(unsigned i=0; i Atmp (count_t + count_p, count_t + count_p); + SparseMatrix Atmp2(count_t + count_p, count_t + count_p); + SparseMatrix Atmp3(count_t + count_p, count_t + count_p); + + // Merge the two part of the energy + Atmp = (1.0 - softAlpha)*A; + Atmp2 = softAlpha * ASoft; + Atmp3 = Atmp+Atmp2; + + A = Atmp3; + b = b*(1.0 - softAlpha) + bSoft * softAlpha; + } + +// ofstream s("/Users/daniele/A.txt"); +// for (int k=0; k::InnerIterator it(A,k); it; ++it) +// { +// s << it.row() << " " << it.col() << " " << it.value() << endl; +// } +// s.close(); +// +// ofstream s2("/Users/daniele/b.txt"); +// for(unsigned i=0; i > solver; + solver.compute(A); + VectorXd x = solver.solve(b); + + // Copy the result back + for(unsigned i=0; i > gmm_A; + std::vector gmm_b; + std::vector ids_to_round; + std::vector x; + + gmm_A.resize(n,n); + gmm_b.resize(n); + x.resize(n); + + // Copy A + for (int k=0; k::InnerIterator it(A,k); it; ++it) + { + gmm_A(it.row(),it.col()) += it.value(); + } + + // Copy b + for(unsigned i=0; i > gmm_C(0, n); + + COMISO::ConstrainedSolver cs; + //print_miso_settings(cs.misolver()); + cs.solve(gmm_C, gmm_A, x, gmm_b, ids_to_round, 0.0, false, true); + + // Copy the result back + for(unsigned i=0; i(); + + assert(tmp(0) - ref1(0) < 10^10); + assert(tmp(1) - ref1(1) < 10^10); + + k[eid] = ktemp; + } + } + +} + +void igl::copyleft::comiso::NRosyField::reduceSpace() +{ + using namespace std; + using namespace Eigen; + + // All variables are free in the beginning + for(unsigned i=0; i debug; + + // debug +// MatrixXd B(F.rows(),3); +// for(unsigned i=0; i visited(EV.rows()); + for(unsigned i=0; i starting(EV.rows()); + for(unsigned i=0; i q; + for(unsigned i=0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COMISO_NROSY_H +#define IGL_COMISO_NROSY_H + +#include +#include +#include +#include +#include "../../igl_inline.h" +#include "../../PI.h" + +namespace igl +{ + namespace copyleft + { + namespace comiso + { + // Generate a N-RoSy field from a sparse set of constraints + // + // Inputs: + // V #V by 3 list of mesh vertex coordinates + // F #F by 3 list of mesh faces (must be triangles) + // b #B by 1 list of constrained face indices + // bc #B by 3 list of representative vectors for the constrained + // faces + // b_soft #S by 1 b for soft constraints + // w_soft #S by 1 weight for the soft constraints (0-1) + // bc_soft #S by 3 bc for soft constraints + // N the degree of the N-RoSy vector field + // soft the strength of the soft constraints w.r.t. smoothness + // (0 -> smoothness only, 1->constraints only) + // Outputs: + // R #F by 3 the representative vectors of the interpolated field + // S #V by 1 the singularity index for each vertex (0 = regular) + IGL_INLINE void nrosy( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::VectorXi& b, + const Eigen::MatrixXd& bc, + const Eigen::VectorXi& b_soft, + const Eigen::VectorXd& w_soft, + const Eigen::MatrixXd& bc_soft, + const int N, + const double soft, + Eigen::MatrixXd& R, + Eigen::VectorXd& S + ); + //wrapper for the case without soft constraints + IGL_INLINE void nrosy( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::VectorXi& b, + const Eigen::MatrixXd& bc, + const int N, + Eigen::MatrixXd& R, + Eigen::VectorXd& S + ); + + } +} +} + +#ifndef IGL_STATIC_LIBRARY +# include "nrosy.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cork/from_cork_mesh.cpp b/src/igl/copyleft/cork/from_cork_mesh.cpp new file mode 100644 index 0000000000..9642631118 --- /dev/null +++ b/src/igl/copyleft/cork/from_cork_mesh.cpp @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "from_cork_mesh.h" + +template < + typename DerivedV, + typename DerivedF> +IGL_INLINE void igl::copyleft::cork::from_cork_mesh( + const CorkTriMesh & mesh, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F) +{ + using namespace std; + F.resize(mesh.n_triangles,3); + V.resize(mesh.n_vertices,3); + for(size_t v = 0;v, Eigen::Matrix >(CorkTriMesh const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cork::from_cork_mesh, Eigen::Matrix >(CorkTriMesh const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/cork/from_cork_mesh.h b/src/igl/copyleft/cork/from_cork_mesh.h new file mode 100644 index 0000000000..a2fef28c14 --- /dev/null +++ b/src/igl/copyleft/cork/from_cork_mesh.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CORK_FROM_CORK_MESH_H +#define IGL_COPYLEFT_CORK_FROM_CORK_MESH_H +#include "../../igl_inline.h" +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cork + { + // Convert cork's triangle mesh representation to a (V,F) mesh. + // + // Inputs: + // mesh cork representation of mesh + // Outputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + template < + typename DerivedV, + typename DerivedF> + IGL_INLINE void from_cork_mesh( + const CorkTriMesh & mesh, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "from_cork_mesh.cpp" +#endif +#endif diff --git a/src/igl/copyleft/cork/mesh_boolean.cpp b/src/igl/copyleft/cork/mesh_boolean.cpp new file mode 100644 index 0000000000..61ed4a6ad6 --- /dev/null +++ b/src/igl/copyleft/cork/mesh_boolean.cpp @@ -0,0 +1,99 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mesh_boolean.h" +#include "to_cork_mesh.h" +#include "from_cork_mesh.h" + +template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedVC, + typename DerivedFC> +IGL_INLINE void igl::copyleft::cork::mesh_boolean( + const Eigen::PlainObjectBase & VA, + const Eigen::PlainObjectBase & FA, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + const MeshBooleanType & type, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC) +{ + CorkTriMesh A,B,C; + // pointer to output so it's easy to redirect on degenerate cases + CorkTriMesh *ret = &C; + to_cork_mesh(VA,FA,A); + to_cork_mesh(VB,FB,B); + switch(type) + { + case MESH_BOOLEAN_TYPE_UNION: + if(A.n_triangles == 0) + { + ret = &B; + }else if(B.n_triangles == 0) + { + ret = &A; + }else + { + computeUnion(A,B,ret); + } + break; + case MESH_BOOLEAN_TYPE_INTERSECT: + if(A.n_triangles == 0 || B.n_triangles == 0) + { + ret->n_triangles = 0; + ret->n_vertices = 0; + }else + { + computeIntersection(A,B,ret); + } + break; + case MESH_BOOLEAN_TYPE_MINUS: + if(A.n_triangles == 0) + { + ret->n_triangles = 0; + ret->n_vertices = 0; + }else if(B.n_triangles == 0) + { + ret = &A; + }else + { + computeDifference(A,B,ret); + } + break; + case MESH_BOOLEAN_TYPE_XOR: + if(A.n_triangles == 0) + { + ret = &B; + }else if(B.n_triangles == 0) + { + ret = &A; + }else + { + computeSymmetricDifference(A,B,&C); + } + break; + case MESH_BOOLEAN_TYPE_RESOLVE: + resolveIntersections(A,B,&C); + break; + default: + assert(false && "Unknown type"); + return; + } + from_cork_mesh(*ret,VC,FC); + freeCorkTriMesh(&A); + freeCorkTriMesh(&B); + freeCorkTriMesh(&C); +} +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::copyleft::cork::mesh_boolean, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, MeshBooleanType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::cork::mesh_boolean, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, igl::MeshBooleanType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/copyleft/cork/mesh_boolean.h b/src/igl/copyleft/cork/mesh_boolean.h new file mode 100644 index 0000000000..db2589284a --- /dev/null +++ b/src/igl/copyleft/cork/mesh_boolean.h @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CORK_MESH_BOOLEAN_H +#define IGL_COPYLEFT_CORK_MESH_BOOLEAN_H +#include "../../MeshBooleanType.h" +#include "../../igl_inline.h" +#include +#include // for consistent uint + +namespace igl +{ + namespace copyleft + { + namespace cork + { + // Compute a boolean operation on two input meshes using the cork library. + // + // Inputs: + // VA #VA by 3 list of vertex positions of first mesh + // FA #FA by 3 list of triangle indices into VA + // VB #VB by 3 list of vertex positions of second mesh + // FB #FB by 3 list of triangle indices into VB + // type of boolean operation see MeshBooleanType.h + // Outputs: + // VC #VC by 3 list of vertex positions of output mesh + // FC #FC by 3 list of triangle indices into VC + template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename DerivedVC, + typename DerivedFC> + IGL_INLINE void mesh_boolean( + const Eigen::PlainObjectBase & VA, + const Eigen::PlainObjectBase & FA, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + const MeshBooleanType & type, + Eigen::PlainObjectBase & VC, + Eigen::PlainObjectBase & FC); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "mesh_boolean.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/cork/to_cork_mesh.cpp b/src/igl/copyleft/cork/to_cork_mesh.cpp new file mode 100644 index 0000000000..845f8cb677 --- /dev/null +++ b/src/igl/copyleft/cork/to_cork_mesh.cpp @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "to_cork_mesh.h" +template < + typename DerivedV, + typename DerivedF> +IGL_INLINE void igl::copyleft::cork::to_cork_mesh( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + CorkTriMesh & mesh) +{ + using namespace std; + assert((F.cols() == 0 || F.cols() == 3) && "Facets should be triangles."); + assert((V.cols() == 0 || V.cols() == 3) && "Vertices should be in 3D."); + mesh.n_triangles = F.rows(); + mesh.n_vertices = V.rows(); + mesh.vertices = new float[mesh.n_vertices*3]; + mesh.triangles = new uint[mesh.n_triangles*3]; + for(size_t v = 0;v, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, CorkTriMesh&); +template void igl::copyleft::cork::to_cork_mesh, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, CorkTriMesh&); +#endif diff --git a/src/igl/copyleft/cork/to_cork_mesh.h b/src/igl/copyleft/cork/to_cork_mesh.h new file mode 100644 index 0000000000..b034e357f3 --- /dev/null +++ b/src/igl/copyleft/cork/to_cork_mesh.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_CORK_TO_CORK_MESH_H +#define IGL_COPYLEFT_CORK_TO_CORK_MESH_H +#include "../../igl_inline.h" +#include +#include +namespace igl +{ + namespace copyleft + { + namespace cork + { + // Convert a (V,F) mesh to a cork's triangle mesh representation. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // Outputs: + // mesh cork representation of mesh + template < + typename DerivedV, + typename DerivedF> + IGL_INLINE void to_cork_mesh( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + CorkTriMesh & mesh); + } + } +} +#ifndef IGL_STATIC_LIBRARY +# include "to_cork_mesh.cpp" +#endif +#endif diff --git a/src/igl/copyleft/marching_cubes.cpp b/src/igl/copyleft/marching_cubes.cpp new file mode 100644 index 0000000000..144a022555 --- /dev/null +++ b/src/igl/copyleft/marching_cubes.cpp @@ -0,0 +1,258 @@ +/*===========================================================================*\ + * * + * IsoEx * + * Copyright (C) 2002 by Computer Graphics Group, RWTH Aachen * + * www.rwth-graphics.de * + * * + *---------------------------------------------------------------------------* + * * + * License * + * * + * This library is free software; you can redistribute it and/or modify it * + * under the terms of the GNU Library General Public License as published * + * by the Free Software Foundation, version 2. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * * + \*===========================================================================*/ + +#include "marching_cubes.h" +#include "marching_cubes_tables.h" + +#include + + +extern const int edgeTable[256]; +extern const int triTable[256][2][17]; +extern const int polyTable[8][16]; + +struct EdgeKey +{ + EdgeKey(unsigned i0, unsigned i1) : i0_(i0), i1_(i1) {} + + bool operator==(const EdgeKey& _rhs) const + { + return i0_ == _rhs.i0_ && i1_ == _rhs.i1_; + } + + unsigned i0_, i1_; +}; + +struct EdgeHash +{ + std::size_t operator()(const EdgeKey& key) const { + std::size_t seed = 0; + seed ^= key.i0_ + 0x9e3779b9 + (seed<<6) + (seed>>2); // Copied from boost::hash_combine + seed ^= key.i1_ + 0x9e3779b9 + (seed<<6) + (seed>>2); + return std::hash()(seed); + } +}; + + +template +class MarchingCubes +{ + typedef std::unordered_map MyMap; + typedef typename MyMap::const_iterator MyMapIterator; + +public: + MarchingCubes( + const Eigen::PlainObjectBase &values, + const Eigen::PlainObjectBase &points, + const unsigned x_res, + const unsigned y_res, + const unsigned z_res, + Eigen::PlainObjectBase &vertices, + Eigen::PlainObjectBase &faces) + { + assert(values.cols() == 1); + assert(points.cols() == 3); + + if(x_res <2 || y_res<2 ||z_res<2) + return; + faces.resize(10000,3); + int num_faces = 0; + + vertices.resize(10000,3); + int num_vertices = 0; + + + unsigned n_cubes = (x_res-1) * (y_res-1) * (z_res-1); + assert(unsigned(points.rows()) == x_res * y_res * z_res); + + unsigned int offsets_[8]; + offsets_[0] = 0; + offsets_[1] = 1; + offsets_[2] = 1 + x_res; + offsets_[3] = x_res; + offsets_[4] = x_res*y_res; + offsets_[5] = 1 + x_res*y_res; + offsets_[6] = 1 + x_res + x_res*y_res; + offsets_[7] = x_res + x_res*y_res; + + for (unsigned cube_it =0 ; cube_it < n_cubes; ++cube_it) + { + + unsigned corner[8]; + typename DerivedF::Scalar samples[12]; + unsigned char cubetype(0); + unsigned int i; + + + // get point indices of corner vertices + for (i=0; i<8; ++i) + { + // get cube coordinates + unsigned int _idx = cube_it; + unsigned int X(x_res-1), Y(y_res-1); + unsigned int x = _idx % X; _idx /= X; + unsigned int y = _idx % Y; _idx /= Y; + unsigned int z = _idx; + + // transform to point coordinates + _idx = x + y*x_res + z*x_res*y_res; + + // add offset + corner[i] = _idx + offsets_[i]; + } + + + // determine cube type + for (i=0; i<8; ++i) + if (values(corner[i]) > 0.0) + cubetype |= (1< faces.rows()) + faces.conservativeResize(faces.rows()+10000, Eigen::NoChange); + + faces.row(num_faces-1) << + samples[triTable[cubetype][0][i ]], + samples[triTable[cubetype][0][i+1]], + samples[triTable[cubetype][0][i+2]]; + + } + + } + + vertices.conservativeResize(num_vertices, Eigen::NoChange); + faces.conservativeResize(num_faces, Eigen::NoChange); + + }; + + static typename DerivedF::Scalar add_vertex(const Eigen::PlainObjectBase &values, + const Eigen::PlainObjectBase &points, + unsigned int i0, + unsigned int i1, + Eigen::PlainObjectBase &vertices, + int &num_vertices, + MyMap &edge2vertex) + { + // find vertex if it has been computed already + MyMapIterator it = edge2vertex.find(EdgeKey(i0, i1)); + if (it != edge2vertex.end()) + return it->second; + ; + + // generate new vertex + const Eigen::Matrix & p0 = points.row(i0); + const Eigen::Matrix & p1 = points.row(i1); + + typename Derivedvalues::Scalar s0 = fabs(values(i0)); + typename Derivedvalues::Scalar s1 = fabs(values(i1)); + typename Derivedvalues::Scalar t = s0 / (s0+s1); + + + num_vertices++; + if (num_vertices > vertices.rows()) + vertices.conservativeResize(vertices.rows()+10000, Eigen::NoChange); + + // Linear interpolation based on linearly interpolating values + vertices.row(num_vertices-1) = ((1.0f-t)*p0 + t*p1).template cast(); + edge2vertex[EdgeKey(i0, i1)] = num_vertices-1; + + return num_vertices-1; + } + ; + + // maps an edge to the sample vertex generated on it + MyMap edge2vertex; +}; + + +template +IGL_INLINE void igl::copyleft::marching_cubes( + const Eigen::PlainObjectBase &values, + const Eigen::PlainObjectBase &points, + const unsigned x_res, + const unsigned y_res, + const unsigned z_res, + Eigen::PlainObjectBase &vertices, + Eigen::PlainObjectBase &faces) +{ + MarchingCubes mc(values, + points, + x_res, + y_res, + z_res, + vertices, + faces); +} +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::marching_cubes, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned int, unsigned int, unsigned int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::marching_cubes, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned int, unsigned int, unsigned int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::marching_cubes, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned int, unsigned int, unsigned int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::marching_cubes, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned int, unsigned int, unsigned int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::copyleft::marching_cubes, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned int, unsigned int, unsigned int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::marching_cubes< Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned int, unsigned int, unsigned int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/marching_cubes.h b/src/igl/copyleft/marching_cubes.h new file mode 100644 index 0000000000..4c9f888e4f --- /dev/null +++ b/src/igl/copyleft/marching_cubes.h @@ -0,0 +1,61 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_MARCHINGCUBES_H +#define IGL_COPYLEFT_MARCHINGCUBES_H +#include "../igl_inline.h" + +#include +namespace igl +{ + namespace copyleft + { + // marching_cubes( values, points, x_res, y_res, z_res, vertices, faces ) + // + // performs marching cubes reconstruction on the grid defined by values, and + // points, and generates vertices and faces + // + // Input: + // values #number_of_grid_points x 1 array -- the scalar values of an + // implicit function defined on the grid points (<0 in the inside of the + // surface, 0 on the border, >0 outside) + // points #number_of_grid_points x 3 array -- 3-D positions of the grid + // points, ordered in x,y,z order: + // points[index] = the point at (x,y,z) where : + // x = (index % (xres -1), + // y = (index / (xres-1)) %(yres-1), + // z = index / (xres -1) / (yres -1) ). + // where x,y,z index x, y, z dimensions + // i.e. index = x + y*xres + z*xres*yres + // xres resolutions of the grid in x dimension + // yres resolutions of the grid in y dimension + // zres resolutions of the grid in z dimension + // Output: + // vertices #V by 3 list of mesh vertex positions + // faces #F by 3 list of mesh triangle indices + // + template < + typename Derivedvalues, + typename Derivedpoints, + typename Derivedvertices, + typename DerivedF> + IGL_INLINE void marching_cubes( + const Eigen::PlainObjectBase &values, + const Eigen::PlainObjectBase &points, + const unsigned x_res, + const unsigned y_res, + const unsigned z_res, + Eigen::PlainObjectBase &vertices, + Eigen::PlainObjectBase &faces); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "marching_cubes.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/marching_cubes_tables.h b/src/igl/copyleft/marching_cubes_tables.h new file mode 100644 index 0000000000..4bba533c32 --- /dev/null +++ b/src/igl/copyleft/marching_cubes_tables.h @@ -0,0 +1,917 @@ +/*===========================================================================*\ + * * + * IsoEx * + * Copyright (C) 2002 by Computer Graphics Group, RWTH Aachen * + * www.rwth-graphics.de * + * * + *---------------------------------------------------------------------------* + * * + * License * + * * + * This library is free software; you can redistribute it and/or modify it * + * under the terms of the GNU Library General Public License as published * + * by the Free Software Foundation, version 2. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * * + \*===========================================================================*/ + +//============================================================================= +#ifndef IGL_ISOEX_MC_TABLES_HH +#define IGL_ISOEX_MC_TABLES_HH +//============================================================================= + + +//int edgeTable[256]; +//int triTable[256][2][17]; +//int polyTable[8][16]; + +const int edgeTable[256]= +{ + 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 +}; + + +//----------------------------------------------------------------------------- + + +const int triTable[256][2][17] = +{{{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 1, 9, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 2, 10, 9, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 10, 9, 8, 3, 2 , -1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 0, 8, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 10 */ + {{1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 1, 9, 0, 2, 3,11, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 9, 8, 11, 2, 1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 3, 11,10, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 11, 10, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 11,10, 9, 0, 3, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 15 */ + {{9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 8, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 4, 7, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 7, 3, 1, 9, 4, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 20 */ + {{1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 1, 2,10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 3, 0, 4, 7, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 2,10, 9, 0, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1, -1}, + {1, 6, 7, 3, 2,10, 9, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 25 */ + {{11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 2, 0, 4, 7,11,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1}}, + + {{4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 1, 9, 11, 11,9,4,7, -1, -1, -1, -1, -1 ,-1}}, + + {{3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 3, 11,10, 1, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1, -1}, + {1, 6, 1, 0, 4, 7,11,10, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 30 */ + {{4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1, -1}, + {2, 3, 5, 4, 7, 8, 0, 3, 11, 10, 9, -1, -1, -1, -1, -1, -1}}, + + {{4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 4, 7,11,10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 0, 1, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 35 */ + {{8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 1, 5, 4, 8,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 1, 2,10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1}}, + + {{5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 4, 0, 2,10, 5,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 10, 5, 3, 4, 8, 3, 5, -1, -1, -1, -1, -1, -1}}, + + /* 40 */ + {{9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 0, 8, 11, 2, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 0, 1, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 5, 4, 8,11, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 4, 3, 3,11,10, 1, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}}, + + /* 45 */ + {{4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1, -1}, + {2, 3, 5, 4, 9, 5, 1, 0, 8,11, 10, -1, -1, -1, -1, -1, -1}}, + + {{5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1, -1}, + {1, 6, 5, 4, 0, 3,11, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 5, 4, 8, 11, 10,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 7, 8, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 5, 7, 3, 0, 9,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 50 */ + {{0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 1, 5, 7, 8, 0,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 3, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 7, 8, 9, 5,10, 1, 2, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1, -1}, + { 2, 3, 5,10, 1, 2, 0, 9, 5, 7, 3,-1, -1, -1, -1, -1, -1}}, + + {{8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1, -1}, + {1, 6, 2,10, 5, 7, 8, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 55 */ + {{2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 2,10, 5, 7, 3,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 7, 8, 9, 5, 3,11, 2, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1, -1}, + {1, 6, 2, 0, 9, 5, 7,11, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1, -1}, + {2, 3, 5, 2, 3,11, 8, 0, 1, 5, 7, -1, -1, -1, -1, -1, -1}}, + + {{11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5,11, 2, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 60 */ + {{9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1, -1}, + {2, 4, 4, 3,11, 10, 1, 5, 7, 8, 9, -1, -1, -1, -1, -1, -1}}, + + {{5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1, -1}, + {1, 7, 5, 7, 11,10, 1, 0, 9, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1, -1}, + {1, 7, 11,10,5, 7, 8, 0,3, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 4, 5, 7, 11,10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 3,10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 65 */ + {{0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 1, 9, 8, 3, 5,10, 6, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 1, 2, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 1, 2, 6, 5, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}}, + + /* 70 */ + {{9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 0, 2, 6, 5, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1, -1}, + {1, 6, 2, 6, 5, 9, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 2, 3,11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 4, 3, 0, 8, 11, 2, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}}, + + /* 75 */ + {{5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1, -1}, + {2, 3, 5, 5,10, 6, 2, 1, 9, 8,11, -1, -1, -1, -1, -1, -1}}, + + {{6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 5, 1, 3, 11,6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 0, 8,11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1, -1}, + {2, 4, 4, 3, 11, 6, 0, 5, 9, 0, 6, -1, -1, -1, -1}}, + + {{6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 6, 5, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 80 */ + {{5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 5,10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 4, 7, 3, 0, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 1, 9, 0, 5,10, 6, 8, 4, 7, -1, -1, -1, -1}}, + + {{10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1, -1}, + { 2, 3, 5,10, 6, 5, 9, 4, 7, 3, 1,-1, -1, -1, -1, -1, -1}}, + + {{6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 1, 2, 6, 5, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}}, + + /* 85 */ + {{1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 6, 5, 1, 3, 0, 4, 7, -1, -1, -1, -1, -1, -1}}, + + {{8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1, -1}, + {2, 3, 5, 8, 4, 7, 5, 9, 0, 2, 6, -1, -1, -1, -1, -1, -1}}, + + {{7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1, -1}, + {1, 7, 7, 3, 2, 6, 5, 9, 4,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 3,11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1}}, + + {{5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1, -1}, + {2, 3, 5, 5,10, 6, 7,11, 2, 0, 4, -1, -1, -1, -1, -1, -1}}, + + /* 90 */ + {{0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1}, + {4, 3, 3, 3, 3, 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6}}, + + {{9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1, -1}, + {3, 4, 4, 3, 2, 1, 9,11, 4, 7, 11, 9, 5, 10, 6, -1, -1}}, + + {{8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1, -1}, + {2, 3, 5, 8, 4, 7, 11, 6, 5, 1, 3, -1, -1, -1, -1, -1, -1}}, + + {{5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1, -1}, + {1, 7, 5, 1, 0, 4, 7,11, 6, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1, -1}, + {3, 4, 4, 3, 0, 6, 5, 9, 3, 11, 6, 0, 8, 4, 7, -1, -1}}, + + /* 95 */ + {{6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1, -1}, + {2, 4, 4, 9, 4, 7, 11, 6, 5, 9, 11,-1, -1, -1, -1, -1, -1}}, + + {{10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 4, 4, 9, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 4, 9,10, 6, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 6, 4, 0, 1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1, -1}, + {1, 6, 1,10, 6, 4, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 100 */ + {{1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 2, 6, 4, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1, -1}, + {2, 3, 5, 3, 0, 8, 9, 1, 2, 6, 4, -1, -1, -1, -1, -1, -1}}, + + {{0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 2, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 3, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 4, 3, 10, 6, 4, 9,11, 2, 3, -1, -1, -1, -1, -1, -1, -1}}, + + /* 105 */ + {{0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 11, 8, 0, 10, 6, 4, 9, -1, -1, -1, -1, -1, -1}}, + + {{3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1, -1}, + {2, 3, 5, 3,11, 2, 1, 10,6, 4, 0, -1, -1, -1, -1, -1, -1}}, + + {{6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1, -1}, + {1, 7, 6, 4, 8,11, 2, 1,10, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1, -1}, + {1, 6, 3,11, 6, 4, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1, -1}, + {1, 7, 8,11, 6, 4, 9, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 110 */ + {{3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3,11, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 8, 11, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 9,10, 6, 7,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1, -1}, + {1, 6, 0, 9, 10, 6, 7, 3, -1,-1,-1, -1, -1, -1, -1, -1, -1}}, + + {{10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1, -1}, + { 2, 4, 4, 8, 0, 1, 7, 10, 6, 7, 1,-1, -1, -1, -1, -1, -1}}, + + /* 115 */ + {{10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 10, 6, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1, -1}, + {1, 6, 1, 2, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1, -1}, + {1, 7, 2, 6, 7, 3, 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 7, 8, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 7, 3, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 120 */ + {{2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1, -1}, + {2, 3, 5, 2, 3,11, 6, 7, 8, 9,10, -1, -1, -1, -1, -1, -1}}, + + {{2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1, -1}, + {1, 7, 2, 0, 9,10,6, 7, 11, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1, -1}, + {3, 4, 4, 3, 8, 0, 1, 7, 10, 6, 7, 1, 11, 2, 3, -1, -1}}, + + {{11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1, -1}, + { 2, 4, 4, 11, 2, 1,7, 1, 10, 6, 7,-1, -1, -1, -1, -1, -1}}, + + {{8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1, -1}, + {1, 7, 8, 9, 1, 3, 11, 6, 7,-1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 125 */ + {{0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1, -1}, + {2, 4, 4, 0, 3,11, 6, 7, 8, 0, 6, -1, -1, -1, -1, -1, -1}}, + + {{7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 130 */ + {{0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 1, 9, 8, 3,11, 7, 6, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 3,10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 1, 2,10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1}}, + + {{2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 2, 10, 9, 0, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}}, + + /* 135 */ + {{6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1, -1}, + {2, 3, 5, 6, 11, 7, 3, 2,10, 9, 8, -1, -1, -1, -1, -1, -1}}, + + {{7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 2, 3, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 6, 2, 0, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 2, 3, 7, 6, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1, -1}, + {1, 6, 6, 2, 1, 9, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 140 */ + {{10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 1, 3, 7, 6,10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1, -1}, + { 2, 4, 4, 10, 1, 7, 6, 8, 7, 1, 0,-1, -1, -1, -1, -1, -1}}, + + {{0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1, -1}, + {1, 6,10, 9, 0, 3, 7, 6,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 7, 6, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 6, 11, 8, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 145 */ + {{3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 0, 4, 6,11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 6,11, 8, 4, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1, -1}, + {1, 6, 6,11, 3, 1, 9, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 6, 11, 8, 4, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1, -1}, + {2, 3, 5, 1, 2, 10,11, 3,0,4, 6, -1, -1, -1, -1, -1, -1}}, + + /* 150 */ + {{4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1, -1}, + {2, 4, 4, 4, 6, 11, 8, 2,10, 9, 0, -1, -1, -1, -1, -1, -1}}, + + {{10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1, -1}, + {1, 7, 10,9, 4, 6, 11, 3, 2, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 4, 6, 2, 3, 8,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 4, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1, -1}, + {2, 3, 5, 1, 9, 0, 3, 8, 4, 6, 2, -1, -1, -1, -1, -1, -1}}, + + /* 155 */ + {{1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 1, 9, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1, -1}, + {1, 6, 1, 3, 8, 4, 6,10, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5,10, 1,0,4,6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1, -1}, + {1, 7, 4, 6, 10, 9, 0,3, 8, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 4, 4, 6, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 160 */ + {{4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1}}, + + {{5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 0, 1, 5, 4, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1, -1}, + { 2, 3, 5,11, 7, 6, 4, 8, 3, 1, 5,-1, -1, -1, -1, -1, -1}}, + + {{9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 3, 3, 9, 5, 4,10, 1, 2, 7, 6, 11, -1, -1, -1, -1}}, + + /* 165 */ + {{6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1, -1}, + {4, 3, 3, 3, 3, 6,11, 7, 1, 2,10, 0, 8, 3, 4, 9, 5}}, + + {{7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1, -1}, + {2, 3, 5, 7, 6, 11, 10, 5, 4, 0, 2,-1, -1, -1, -1, -1, -1}}, + + {{3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1, -1}, + {3, 4, 4, 3, 5, 3, 2,10, 4, 8, 3, 5, 6, 11, 7, 6, -1}}, + + {{7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 4, 3, 2, 3, 7, 6, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1, -1}, + {2, 3, 5, 9, 5, 4, 8, 7, 6, 2, 0, -1, -1, -1, -1, -1, -1}}, + + /* 170 */ + {{3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1, -1}, + {2, 4, 4, 3, 7, 6, 2, 0, 1, 5, 4, -1, -1, -1, -1, -1, -1}}, + + {{6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1, -1}, + {1, 7, 6, 2, 1, 5, 4, 8, 7,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1, -1}, + {2, 3, 5, 9, 5, 4, 6,10, 1, 3, 7,-1, -1, -1, -1, -1, -1}}, + + {{1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1, -1}, + {3, 4, 4, 3, 0, 8, 7, 1, 6, 10, 1, 7, 9, 5, 4, -1, -1}}, + + {{4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1, -1}, + {1, 7, 4, 0, 3, 7, 6, 10, 5, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 175 */ + {{7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1, -1}, + {2, 4, 4, 4, 8, 10, 5, 7, 6,10, 8, -1, -1, -1, -1, -1, -1}}, + + {{6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5,11, 8, 9, 5, 6,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1, -1}, + {2, 4, 4, 0, 9, 5, 6, 6,11, 3, 0, -1, -1, -1, -1, -1, -1}}, + + {{0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1, -1}, + {1, 6, 0, 1, 5, 6,11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 6,11, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /*180 */ + {{1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1, -1}, + {2, 3, 5, 1, 2, 10, 5, 6,11, 8, 9, -1, -1, -1, -1, -1, -1}}, + + {{0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1, -1}, + {3, 4, 4, 3, 11, 3,0, 6, 9, 5, 6, 0, 2, 10, 1, 2, 10}}, + + {{11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1, -1}, + { 1, 7,11, 8, 0, 2,10, 5, 6,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1, -1}, + {2, 4, 4, 6,11, 3, 5, 10, 5, 3, 2, -1, -1, -1, -1, -1, -1}}, + + {{5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1, -1}, + {1, 6, 2, 3, 8, 9, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 185 */ + {{9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 9, 5, 6, 2, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1, -1}, + {1, 7, 1, 5, 6, 2, 3, 8, 0, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 1, 5, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1, -1}, + {1, 7, 1, 3, 8, 9, 5, 6,10, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1, -1}, + { 2, 4, 4, 5, 6, 0, 9, 10, 1, 0, 6, -1, -1, -1, -1, -1, -1}}, + + /* 190 */ + {{0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 3,10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 4, 5,10, 11, 7,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 4, 3, 5,10,11, 7, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}}, + + {{5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 4, 3, 5, 10, 11, 7, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}}, + + /* 195 */ + {{10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1, -1}, + { 2, 4, 4, 10, 11, 7, 5, 1, 9, 8, 3, -1, -1, -1, -1, -1, -1}}, + + {{11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 7, 5, 1, 2,11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1, -1}, + {2, 3, 5, 0, 8, 3, 2,11, 7, 5,1, -1, -1, -1, -1, -1, -1}}, + + {{9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1, -1}, + {1, 6, 2,11, 7, 5, 9, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1, -1}, + {1, 7, 7, 5, 9, 8, 3, 2,11,-1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 200 */ + {{2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 7, 5,10, 2,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1, -1}, + {1, 6, 5,10, 2, 0, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1, -1}, + {2, 3, 5, 9, 0, 1, 10, 2, 3, 7, 5, -1, -1, -1, -1, -1, -1}}, + + {{9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1, -1}, + {1, 7, 9, 8, 7, 5,10, 2, 1,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 3, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 205 */ + {{0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 0, 8, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 9, 0, 3, 7, 5,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 7, 5, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 10,11, 8, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1, -1}, + {1, 6, 0, 4, 5,10,11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 210 */ + {{0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1, -1}, + {2, 3, 5, 0, 1, 9, 4, 5, 10, 11, 8, -1, -1, -1, -1, -1, -1}}, + + {{10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1, -1}, + { 1, 7,10, 11, 3, 1, 9,4, 5,-1, -1, -1, -1, -1, -1, -1}}, + + {{2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1, -1}, + {1, 6, 2,11, 8, 4, 5, 1,-1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1, -1}, + {1, 7, 0, 4, 5, 1, 2, 11, 3,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1, -1}, + {1, 7, 0, 2,11, 8, 4, 5, 9, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 215 */ + {{9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 3, 5, 10, 4, 5, 3, 8,-1, -1, -1, -1, -1, -1}}, + + {{5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 5,10, 2, 0, 4,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1, -1}, + {3, 4, 4, 3, 3, 5, 10, 2, 8, 4, 5, 3, 0, 1, 9, -1, -1}}, + + {{5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1, -1}, + {1, 6,10, 2, 1, 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 220 */ + {{8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 4, 5, 1, 3,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 0, 4, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1, -1}, + {2, 4, 4, 0, 3, 5, 9, 8, 4, 5, 3, -1, -1, -1, -1, -1, -1}}, + + {{9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 9,10, 11, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 225 */ + {{0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1, -1}, + {2, 3, 5, 0, 8, 3, 7, 4, 9, 10, 11, -1, -1, -1, -1, -1, -1}}, + + {{1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1, -1}, + {1, 6, 1, 10,11, 7, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1, -1}, + {1, 7, 3, 1,10,11, 7, 4, 8, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 11, 9, 1, 4, 9, 11, 7, -1, -1, -1, -1, -1, -1}}, + + {{9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1, -1}, + {3, 4, 4, 3, 1, 2, 11, 9, 7, 4, 9,11, 8, 3, 0, 8, 3}}, + + /* 230 */ + {{11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 11, 7, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1, -1}, + { 2, 4, 4, 11, 7, 4, 2, 3, 2, 4, 8,-1, -1, -1, -1, -1, -1}}, + + {{2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1, -1}, + {1, 6, 2, 3, 7, 4, 9,10, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1, -1}, + {1, 7, 9,10, 2, 0, 8, 7, 4,-1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1, -1}, + {1, 7, 3, 7, 4, 0, 1,10, 2, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 235 */ + {{1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 3, 1,10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 4, 9, 1, 3, 7,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1, -1}, + {2, 4, 4, 8, 7, 1, 0, 4, 9, 1, 7, -1, -1, -1, -1, -1, -1}}, + + {{4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 3, 7, 4, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 240 */ + {{9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 8, 9, 10,11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 0, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 0, 1, 10,11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 3, 1,10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 1, 2, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 245 */ + {{3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1, -1}, + {2, 4, 4, 2,11, 9, 1, 3, 0, 9, 11, -1, -1, -1, -1, -1,-1}}, + + {{0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 0, 2,11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 2, 3, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 2, 0, 9,10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + /* 250 */ + {{2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1, -1}, + {2, 4, 4, 2, 3, 8, 10, 1, 10, 8, 0, -1, -1, -1, -1, -1, -1}}, + + {{1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 4, 1, 3, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, + + {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}} +}; + + +//----------------------------------------------------------------------------- + + +const int polyTable[8][16] = +{ + {-1}, + {-1}, + {-1}, + {0, 1, 2, -1}, + {0, 1, 2, 2, 3, 0, -1}, + {0, 1, 2, 0, 2, 4, 4, 2, 3, -1}, + {0, 1, 2, 2, 3, 4, 4, 5, 0, 0, 2, 4, -1}, + {0, 1, 5, 0, 5, 6, 1, 2, 5, 4, 5, 3, 2, 3, 5, -1} +}; + + +//============================================================================= + + +//============================================================================= +#endif // ISOEX_MC_TABLES_HH defined +//============================================================================= diff --git a/src/igl/copyleft/offset_surface.cpp b/src/igl/copyleft/offset_surface.cpp new file mode 100644 index 0000000000..c9e90553ff --- /dev/null +++ b/src/igl/copyleft/offset_surface.cpp @@ -0,0 +1,64 @@ +#include "offset_surface.h" +#include "marching_cubes.h" +#include "../voxel_grid.h" +#include "../signed_distance.h" +#include "../flood_fill.h" +#include + +template < + typename DerivedV, + typename DerivedF, + typename isolevelType, + typename DerivedSV, + typename DerivedSF, + typename DerivedGV, + typename Derivedside, + typename DerivedS> +void igl::copyleft::offset_surface( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const isolevelType isolevel, + const typename Derivedside::Scalar s, + const SignedDistanceType & signed_distance_type, + Eigen::PlainObjectBase & SV, + Eigen::PlainObjectBase & SF, + Eigen::PlainObjectBase & GV, + Eigen::PlainObjectBase & side, + Eigen::PlainObjectBase & S) +{ + typedef typename DerivedV::Scalar Scalar; + typedef typename DerivedF::Scalar Index; + { + Eigen::AlignedBox box; + typedef Eigen::Matrix RowVector3S; + assert(V.cols() == 3 && "V must contain positions in 3D"); + RowVector3S min_ext = V.colwise().minCoeff().array() - isolevel; + RowVector3S max_ext = V.colwise().maxCoeff().array() + isolevel; + box.extend(min_ext.transpose()); + box.extend(max_ext.transpose()); + igl::voxel_grid(box,s,1,GV,side); + } + + const Scalar h = + (GV.col(0).maxCoeff()-GV.col(0).minCoeff())/((Scalar)(side(0)-1)); + const Scalar lower_bound = isolevel-sqrt(3.0)*h; + const Scalar upper_bound = isolevel+sqrt(3.0)*h; + { + Eigen::Matrix I; + Eigen::Matrix C,N; + igl::signed_distance( + GV,V,F,signed_distance_type,lower_bound,upper_bound,S,I,C,N); + } + igl::flood_fill(side,S); + + DerivedS SS = S.array()-isolevel; + igl::copyleft::marching_cubes(SS,GV,side(0),side(1),side(2),SV,SF); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::copyleft::offset_surface, Eigen::Matrix, double, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::Matrix::Scalar, igl::SignedDistanceType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::offset_surface, Eigen::Matrix, float, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, float, Eigen::Matrix::Scalar, igl::SignedDistanceType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::copyleft::offset_surface, Eigen::Matrix, double, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::Matrix::Scalar, igl::SignedDistanceType const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/offset_surface.h b/src/igl/copyleft/offset_surface.h new file mode 100644 index 0000000000..7c6307378a --- /dev/null +++ b/src/igl/copyleft/offset_surface.h @@ -0,0 +1,54 @@ +#ifndef IGL_COPYLEFT_OFFSET_SURFACE_H +#define IGL_COPYLEFT_OFFSET_SURFACE_H +#include "../igl_inline.h" +#include "../signed_distance.h" +#include + +namespace igl +{ + namespace copyleft + { + // Compute a triangulated offset surface using matching cubes on a grid of + // signed distance values from the input triangle mesh. + // + // Inputs: + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of mesh triangle indices into V + // isolevel iso level to extract (signed distance: negative inside) + // s number of grid cells along longest side (controls resolution) + // signed_distance_type type of signing to use (see + // ../signed_distance.h) + // Outputs: + // SV #SV by 3 list of output surface mesh vertex positions + // SF #SF by 3 list of output mesh triangle indices into SV + // GV #GV=side(0)*side(1)*side(2) by 3 list of grid cell centers + // side list of number of grid cells in x, y, and z directions + // S #GV by 3 list of signed distance values _near_ `isolevel` ("far" + // from `isolevel` these values are incorrect) + // + template < + typename DerivedV, + typename DerivedF, + typename isolevelType, + typename DerivedSV, + typename DerivedSF, + typename DerivedGV, + typename Derivedside, + typename DerivedS> + void offset_surface( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const isolevelType isolevel, + const typename Derivedside::Scalar s, + const SignedDistanceType & signed_distance_type, + Eigen::PlainObjectBase & SV, + Eigen::PlainObjectBase & SF, + Eigen::PlainObjectBase & GV, + Eigen::PlainObjectBase & side, + Eigen::PlainObjectBase & S); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "offset_surface.cpp" +#endif +#endif diff --git a/src/igl/copyleft/opengl2/render_to_tga.cpp b/src/igl/copyleft/opengl2/render_to_tga.cpp new file mode 100644 index 0000000000..6d0f97ea1c --- /dev/null +++ b/src/igl/copyleft/opengl2/render_to_tga.cpp @@ -0,0 +1,87 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "render_to_tga.h" +#include "tga.h" + +#include "../../opengl2/gl.h" + +#include + +IGL_INLINE bool igl::opengl::render_to_tga( + const std::string tga_file, + const int width, + const int height, + const bool alpha) +{ + + size_t components = 3; + GLenum format = GL_BGR; + if(alpha) + { + format = GL_BGRA; + components = 4; + } + GLubyte * cmap = NULL; + + // OpenGL by default tries to read data in multiples of 4, if our data is + // only RGB or BGR and the width is not divible by 4 then we need to alert + // opengl + if((width % 4) != 0 && + (format == GL_RGB || + format == GL_BGR)) + { + glPixelStorei(GL_PACK_ALIGNMENT, 1); + } + GLubyte *pixels; + pixels = (unsigned char *) malloc (width * height * components); + glReadPixels( + 0, + 0, + width, + height, + format, + GL_UNSIGNED_BYTE, + pixels); + + // set up generic image struct + gliGenericImage * genericImage; + genericImage = (gliGenericImage*) malloc(sizeof(gliGenericImage)); + genericImage->width = width; + genericImage->height = height; + genericImage->format = format; + genericImage->components = components; + genericImage->pixels = pixels; + // CMAP is not supported, but we need to put something here + genericImage->cmapEntries = 0; + genericImage->cmapFormat = GL_BGR; + genericImage->cmap = cmap; + + // write pixels to tga file + FILE * imgFile; + // "-" as output file name is code for write to stdout + if(tga_file.compare("-") == 0) + { + imgFile = stdout; + }else{ + imgFile = fopen(tga_file.c_str(),"w"); + if(NULL==imgFile) + { + printf("IOError: %s could not be opened...\n",tga_file.c_str()); + return false; + } + } + + writeTGA(genericImage,imgFile); + + free(genericImage); + return fclose(imgFile) == 0; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/copyleft/opengl2/render_to_tga.h b/src/igl/copyleft/opengl2/render_to_tga.h new file mode 100644 index 0000000000..d792c1e313 --- /dev/null +++ b/src/igl/copyleft/opengl2/render_to_tga.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_RENDER_TO_TGA_H +#define IGL_OPENGL_RENDER_TO_TGA_H +#include "../../igl_inline.h" +#include + +namespace igl +{ + namespace opengl + { + // Render current open GL image to .tga file + // Inputs: + // tga_file path to output .tga file + // width width of scene and resulting image + // height height of scene and resulting image + /// alpha whether to include alpha channel + // Returns true only if no errors occurred + // + // See also: png/render_to_png which is slower but writes .png files + IGL_INLINE bool render_to_tga( + const std::string tga_file, + const int width, + const int height, + const bool alpha); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "render_to_tga.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/opengl2/texture_from_tga.cpp b/src/igl/copyleft/opengl2/texture_from_tga.cpp new file mode 100644 index 0000000000..717a6734cb --- /dev/null +++ b/src/igl/copyleft/opengl2/texture_from_tga.cpp @@ -0,0 +1,68 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "texture_from_tga.h" +#include "tga.h" +#include + +IGL_INLINE bool igl::opengl::texture_from_tga(const std::string tga_file, GLuint & id) +{ + using namespace std; + + // read pixels to tga file + FILE * imgFile; + // "-" as input file name is code for read from stdin + imgFile = fopen(tga_file.c_str(),"r"); + if(NULL==imgFile) + { + printf("IOError: %s could not be opened...",tga_file.c_str()); + return false; + } + + // gliReadTGA annoyingly uses char * instead of const char * + size_t len = tga_file.length(); + char* tga_file_char = new char [ len + 1 ]; + strcpy( tga_file_char, tga_file.c_str() ); + // read image + gliGenericImage* img = gliReadTGA(imgFile, tga_file_char, 0, 0); + // clean up filename buffer + delete[] tga_file_char; + fclose( imgFile ); + + // set up texture mapping parameters and generate texture id + glGenTextures(1,&id); + glBindTexture(GL_TEXTURE_2D, id); + // Texture parameters + float empty[] = {1.0f,1.0f,1.0f,0.0f}; + glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR,empty); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + // GL_LINEAR_MIPMAP_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR); + + // OpenGL by default tries to read data in multiples of 4, if our data is + // only RGB or BGR and the width is not divible by 4 then we need to alert + // opengl + if((img->width % 4) != 0 && + (img->format == GL_RGB || + img->format == GL_BGR)) + { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + } + + // Load texture + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img->width, + img->height, 0, img->format, GL_UNSIGNED_BYTE, + img->pixels); + return id; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/copyleft/opengl2/texture_from_tga.h b/src/igl/copyleft/opengl2/texture_from_tga.h new file mode 100644 index 0000000000..8a5f704c7e --- /dev/null +++ b/src/igl/copyleft/opengl2/texture_from_tga.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_TEXTURE_FROM_TGA_H +#define IGL_OPENGL_TEXTURE_FROM_TGA_H +#include "../../igl_inline.h" +#include "../../opengl2/gl.h" +#include + +namespace igl +{ + namespace opengl + { + // Read an image from a .tga file and use it as a texture + // + // Input: + // tga_file path to .tga file + // Output: + // id of generated openGL texture + // Returns true on success, false on failure + IGL_INLINE bool texture_from_tga(const std::string tga_file, GLuint & id); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "texture_from_tga.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/opengl2/tga.cpp b/src/igl/copyleft/opengl2/tga.cpp new file mode 100644 index 0000000000..2f1f990220 --- /dev/null +++ b/src/igl/copyleft/opengl2/tga.cpp @@ -0,0 +1,549 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// WARNING +// +// THIS DOES NOT DEAL WITH VERTICALLY FLIPPED DATA CORRECTLY +// +//////////////////////////////////////////////////////////////////////////////// + +/* This file is derived from (actually an earlier version of)... */ + +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * $Id: tga.cpp,v 1.1.2.5 2007-05-10 02:10:07 elif Exp $ + * TrueVision Targa loading and saving file filter for the Gimp. + * Targa code Copyright (C) 1997 Raphael FRANCOIS and Gordon Matzigkeit + * + * The Targa reading and writing code was written from scratch by + * Raphael FRANCOIS and Gordon Matzigkeit + * based on the TrueVision TGA File Format + * Specification, Version 2.0: + * + * + * + * It does not contain any code written for other TGA file loaders. + * Not even the RLE handling. ;) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "tga.h" +#include "../../opengl2/glext.h" +#include +#include +#include +#include + +static char error[256]; +static unsigned int _verbose = 0; +static int totbytes = 0; + +typedef struct { + unsigned char *statebuf; + int statelen; + int laststate; +} RLEstate; + +IGL_INLINE static int +std_fread(RLEstate * /*rleInfo*/, unsigned char *buf, size_t datasize, size_t nelems, FILE *fp) +{ + if (_verbose > 1) { + totbytes += nelems * datasize; + printf("TGA: std_fread %d (total %d)\n", + (int)(nelems * datasize), totbytes); + } + return fread(buf, datasize, nelems, fp); +} + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#define RLE_PACKETSIZE 0x80 + +/* Decode a bufferful of file. */ +IGL_INLINE static int +rle_fread(RLEstate *rleInfo, unsigned char *vbuf, size_t datasize, size_t nelems, FILE *fp) +{ + + unsigned char *buf = vbuf; + int j, k; + int buflen, count, bytes, curbytes; + unsigned char *p; + + /* Scale the buffer length. */ + buflen = nelems * datasize; + + j = 0; + curbytes = totbytes; + while (j < buflen) { + if (rleInfo->laststate < rleInfo->statelen) { + /* Copy bytes from our previously decoded buffer. */ + bytes = MIN(buflen - j, rleInfo->statelen - rleInfo->laststate); + memcpy(buf + j, rleInfo->statebuf + rleInfo->laststate, bytes); + j += bytes; + rleInfo->laststate += bytes; + + /* If we used up all of our state bytes, then reset them. */ + if (rleInfo->laststate >= rleInfo->statelen) { + rleInfo->laststate = 0; + rleInfo->statelen = 0; + } + + /* If we filled the buffer, then exit the loop. */ + if (j >= buflen) break; + } + + /* Decode the next packet. */ + count = fgetc(fp); + if (count == EOF) { + if (_verbose) printf("TGA: hit EOF while looking for count\n"); + return j / datasize; + } + + /* Scale the byte length to the size of the data. */ + bytes = ((count & ~RLE_PACKETSIZE) + 1) * datasize; + + if (j + bytes <= buflen) { + /* We can copy directly into the image buffer. */ + p = buf + j; + } else { +#ifdef PROFILE + printf("TGA: needed to use statebuf for %d bytes\n", buflen - j); +#endif + /* Allocate the state buffer if we haven't already. */ + if (!rleInfo->statebuf) { + rleInfo->statebuf = (unsigned char *) malloc(RLE_PACKETSIZE * datasize); + } + p = rleInfo->statebuf; + } + + if (count & RLE_PACKETSIZE) { + /* Fill the buffer with the next value. */ + if (fread(p, datasize, 1, fp) != 1) { + if (_verbose) { + printf("TGA: EOF while reading %d/%d element RLE packet\n", + bytes, (int)datasize); + } + return j / datasize; + } + + /* Optimized case for single-byte encoded data. */ + if (datasize == 1) { + memset(p + 1, *p, bytes - 1); + } else { + for (k = datasize; k < bytes; k += datasize) { + memcpy(p + k, p, datasize); + } + } + } else { + /* Read in the buffer. */ + if (fread(p, bytes, 1, fp) != 1) { + if (_verbose) { + printf("TGA: EOF while reading %d/%d element raw packet\n", + bytes, (int)datasize); + } + return j / datasize; + } + } + + if (_verbose > 1) { + totbytes += bytes; + if (_verbose > 2) { + printf("TGA: %s packet %d/%d\n", + (count & RLE_PACKETSIZE) ? "RLE" : "raw", + bytes, totbytes); + } + } + + /* We may need to copy bytes from the state buffer. */ + if (p == rleInfo->statebuf) { + rleInfo->statelen = bytes; + } else { + j += bytes; + } + } + + if (_verbose > 1) { + printf("TGA: rle_fread %d/%d (total %d)\n", + (int) ( nelems * datasize), totbytes - curbytes, totbytes); + } + return nelems; +} + +IGL_INLINE igl::opengl::gliGenericImage * +igl::opengl::gliReadTGA(FILE *fp, char *name, int /*hflip*/, int vflip) +{ + igl::opengl::TgaHeader tgaHeader; + igl::opengl::TgaFooter tgaFooter; + char horzrev, vertrev; + int width, height, bpp; + int start, end, dir; + int i, j, k; + int pelbytes, wbytes; + GLenum format; + int components; + RLEstate rleRec; + RLEstate *rleInfo; + int rle; + int index, colors, length; + GLubyte *cmap, *pixels, *data; + int (*myfread)(RLEstate *rleInfo, unsigned char*, size_t, size_t, FILE*); + igl::opengl::gliGenericImage *genericImage; + + /* Check the footer. */ + if (fseek(fp, 0L - sizeof(tgaFooter), SEEK_END) + || fread(&tgaFooter, sizeof(tgaFooter), 1, fp) != 1) { + sprintf(error, "TGA: Cannot read footer from \"%s\"", name); + if (_verbose) printf("%s\n", error); + return NULL; + } + + /* Check the signature. */ + if (memcmp(tgaFooter.signature, TGA_SIGNATURE, + sizeof(tgaFooter.signature)) == 0) { + if (_verbose) printf("TGA: found New TGA\n"); + } else { + if (_verbose) printf("TGA: found Original TGA\n"); + } + + if (fseek(fp, 0, SEEK_SET) || + fread(&tgaHeader, sizeof(tgaHeader), 1, fp) != 1) { + sprintf(error, "TGA: Cannot read header from \"%s\"", name); + if (_verbose) printf("%s\n", error); + return NULL; + } + + if (_verbose && tgaHeader.idLength) { + char *idString = (char*) malloc(tgaHeader.idLength); + + if (fread(idString, tgaHeader.idLength, 1, fp) != 1) { + sprintf(error, "TGA: Cannot read ID field in \"%s\"", name); + printf("%s\n", error); + } else { + printf("TGA: ID field: \"%*s\"\n", tgaHeader.idLength, idString); + } + free(idString); + } else { + /* Skip the image ID field. */ + if (tgaHeader.idLength && fseek(fp, tgaHeader.idLength, SEEK_CUR)) { + sprintf(error, "TGA: Cannot skip ID field in \"%s\"", name); + if (_verbose) printf("%s\n", error); + return NULL; + } + } + + /* Reassemble the multi-byte values correctly, regardless of + host endianness. */ + width = (tgaHeader.widthHi << 8) | tgaHeader.widthLo; + height = (tgaHeader.heightHi << 8) | tgaHeader.heightLo; + bpp = tgaHeader.bpp; + if (_verbose) { + printf("TGA: width=%d, height=%d, bpp=%d\n", width, height, bpp); + } + + horzrev = tgaHeader.descriptor & TGA_DESC_HORIZONTAL; + vertrev = tgaHeader.descriptor & TGA_DESC_VERTICAL; + //vertrev=0; + +// // JASON - we can force this stuff if we want +// if( hflip ) +// horzrev = 1; + if( vflip ) + vertrev = 1; + + if (_verbose && horzrev) printf("TGA: horizontal reversed\n"); + if (_verbose && vertrev) printf("TGA: vertical reversed\n"); + + rle = 0; + switch (tgaHeader.imageType) { + case TGA_TYPE_MAPPED_RLE: + rle = 1; + if (_verbose) printf("TGA: run-length encoded\n"); + case TGA_TYPE_MAPPED: + /* Test for alpha channel. */ + format = GL_COLOR_INDEX; + components = 1; + if (_verbose) { + printf("TGA: %d bit indexed image (%d bit palette)\n", + tgaHeader.colorMapSize, bpp); + } + break; + + case TGA_TYPE_GRAY_RLE: + rle = 1; + if (_verbose) printf("TGA: run-length encoded\n"); + case TGA_TYPE_GRAY: + format = GL_LUMINANCE; + components = 1; + if (_verbose) printf("TGA: %d bit grayscale image\n", bpp); + break; + + case TGA_TYPE_COLOR_RLE: + rle = 1; + if (_verbose) printf("TGA: run-length encoded\n"); + case TGA_TYPE_COLOR: + /* Test for alpha channel. */ + if (bpp == 32) { + format = GL_BGRA_EXT; + components = 4; + if (_verbose) { + printf("TGA: %d bit color image with alpha channel\n", bpp); + } + } else { + format = GL_BGR_EXT; + components = 3; + if (_verbose) printf("TGA: %d bit color image\n", bpp); + } + break; + + default: + sprintf(error, + "TGA: unrecognized image type %d\n", tgaHeader.imageType); + if (_verbose) printf("%s\n", error); + return NULL; + } + + if ((format == GL_BGRA_EXT && bpp != 32) || + (format == GL_BGR_EXT && bpp != 24) || + ((format == GL_LUMINANCE || format == GL_COLOR_INDEX) && bpp != 8)) { + /* FIXME: We haven't implemented bit-packed fields yet. */ + fprintf(stderr, "bpp %d, format %x\n", bpp, (unsigned int)format); + sprintf(error, "TGA: channel sizes other than 8 bits are unimplemented"); + if (_verbose) printf("%s\n", error); + return NULL; + } + + /* Check that we have a color map only when we need it. */ + if (format == GL_COLOR_INDEX) { + if (tgaHeader.colorMapType != 1) { + sprintf(error, "TGA: indexed image has invalid color map type %d\n", + tgaHeader.colorMapType); + if (_verbose) printf("%s\n", error); + return NULL; + } + } else if (tgaHeader.colorMapType != 0) { + sprintf(error, "TGA: non-indexed image has invalid color map type %d\n", + tgaHeader.colorMapType); + if (_verbose) printf("%s\n", error); + return NULL; + } + + if (tgaHeader.colorMapType == 1) { + /* We need to read in the colormap. */ + index = (tgaHeader.colorMapIndexHi << 8) | tgaHeader.colorMapIndexLo; + length = (tgaHeader.colorMapLengthHi << 8) | tgaHeader.colorMapLengthLo; + + if (_verbose) { + printf("TGA: reading color map (%d + %d) * (%d / 8)\n", + index, length, tgaHeader.colorMapSize); + } + if (length == 0) { + sprintf(error, "TGA: invalid color map length %d", length); + if (_verbose) printf("%s\n", error); + return NULL; + } + if (tgaHeader.colorMapSize != 24) { + /* We haven't implemented bit-packed fields yet. */ + sprintf(error, "TGA: channel sizes other than 8 bits are unimplemented"); + if (_verbose) printf("%s\n", error); + return NULL; + } + + pelbytes = tgaHeader.colorMapSize / 8; + colors = length + index; + cmap = (GLubyte*)malloc (colors * pelbytes); + + /* Zero the entries up to the beginning of the map. */ + memset(cmap, 0, index * pelbytes); + + /* Read in the rest of the colormap. */ + if (fread(cmap, pelbytes, length, fp) != (size_t) length) { + sprintf(error, "TGA: error reading colormap (ftell == %ld)\n", + ftell (fp)); + if (_verbose) printf("%s\n", error); + return NULL; + } + + if (pelbytes >= 3) { + /* Rearrange the colors from BGR to RGB. */ + int tmp; + for (j = index; j < length * pelbytes; j += pelbytes) { + tmp = cmap[j]; + cmap[j] = cmap[j + 2]; + cmap[j + 2] = tmp; + } + } + } else { + colors = 0; + cmap = NULL; + } + + /* Allocate the data. */ + pelbytes = bpp / 8; + pixels = (unsigned char *) malloc (width * height * pelbytes); + + if (rle) { + rleRec.statebuf = 0; + rleRec.statelen = 0; + rleRec.laststate = 0; + rleInfo = &rleRec; + myfread = rle_fread; + } else { + rleInfo = NULL; + myfread = std_fread; + } + + wbytes = width * pelbytes; + + if (vertrev) { + start = 0; + end = height; + dir = 1; + } else { + /* We need to reverse loading order of rows. */ + start = height-1; + end = -1; + dir = -1; + } + + for (i = start; i != end; i += dir) { + data = pixels + i*wbytes; + + /* Suck in the data one row at a time. */ + if (myfread(rleInfo, data, pelbytes, width, fp) != width) { + /* Probably premature end of file. */ + if (_verbose) { + printf ("TGA: error reading (ftell == %ld, width=%d)\n", + ftell(fp), width); + } + return NULL; + } + + if (horzrev) { + /* We need to mirror row horizontally. */ + for (j = 0; j < width/2; j++) { + GLubyte tmp; + + for (k = 0; k < pelbytes; k++) { + tmp = data[j*pelbytes+k]; + data[j*pelbytes+k] = data[(width-j-1)*pelbytes+k]; + data[(width-j-1)*pelbytes+k] = tmp; + } + } + } + } + + if (rle) { + free(rleInfo->statebuf); + } + + if (fgetc (fp) != EOF) { + if (_verbose) printf ("TGA: too much input data, ignoring extra...\n"); + } + + genericImage = (igl::opengl::gliGenericImage*) malloc(sizeof(igl::opengl::gliGenericImage)); + genericImage->width = width; + genericImage->height = height; + genericImage->format = format; + genericImage->components = components; + genericImage->cmapEntries = colors; + genericImage->cmapFormat = GL_BGR_EXT; // XXX fix me + genericImage->cmap = cmap; + genericImage->pixels = pixels; + + return genericImage; +} + +IGL_INLINE int igl::opengl::gli_verbose(int new_verbose) +{ + _verbose = new_verbose; + return _verbose; +} + + + +// added 10/2005, Denis Zorin +// a very simple TGA output, supporting +// uncompressed luminance RGB and RGBA +// G22.2270 students: this is C (no C++) +// so this is not the style I would encourage +// you to use; I used it for consistency +// with the rest of the code in this file + + +// fixed header values for the subset of TGA we use for writing +unsigned char TGAHeaderColor[12] = + { 0,// 0 ID length = no id + 0,// 1 color map type = no color map + 2,// 2 image type = uncompressed true color + 0, 0, 0, 0, 0,// color map spec = empty + 0, 0, // x origin of image + 0, 0 // y origin of image + }; + +unsigned char TGAHeaderBW[12] = + { 0,// 0 ID length = no id + 0,// 1 color map type = no color map + 3,// 3 image type = uncompressed black and white + 0, 0, 0, 0, 0,// color map spec = empty + 0, 0, // x origin of image + 0, 0 // y origin of image + }; + +// this makes sure that +// image size is written in correct format +// and byte order (least first) +IGL_INLINE void write16bit(int n, FILE* fp) { + unsigned char bytes[] = { static_cast(n % 256), static_cast(n / 256) }; + fwrite(bytes, 2, sizeof(unsigned char),fp); +} + + + +IGL_INLINE void igl::opengl::writeTGA( igl::opengl::gliGenericImage* image, FILE *fp) { + + assert(!image->cmap); // we do not deal with color map images + + if(image->components == 3 || image->components == 4) + fwrite(TGAHeaderColor, 12, sizeof(unsigned char),fp); + else { + if(image->components == 1 ) + fwrite(TGAHeaderBW, 12, sizeof(unsigned char),fp); + else { fprintf(stderr,"Supported component number: 1,3 or 4\n"); exit(1); } + } + + write16bit(image->width,fp); + write16bit(image->height,fp); + switch (image->components ) { + case 1: + putc(8,fp); + break; + case 3: + putc(24,fp); + break; + case 4: + putc(32,fp); + break; + default: fprintf(stderr,"Supported component number: 1,3 or 4\n"); exit(1); + }; + + if(image-> components == 4) + putc(0x04,fp); // bottom left image (0x00) + 8 bit alpha (0x4) + else + putc(0x00,fp); + + fwrite(image->pixels, image->height*image->width*image->components, sizeof(char),fp); +} + diff --git a/src/igl/copyleft/opengl2/tga.h b/src/igl/copyleft/opengl2/tga.h new file mode 100644 index 0000000000..b69f35496a --- /dev/null +++ b/src/igl/copyleft/opengl2/tga.h @@ -0,0 +1,106 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_TGA_H +#define IGL_OPENGL_TGA_H +#include "../../igl_inline.h" + +#include "../../opengl2/gl.h" +// See license in tga.cpp +/* tga.h - interface for TrueVision (TGA) image file loader */ +#include +#ifdef _WIN32 +#include +#endif + +namespace igl +{ +namespace opengl +{ + +typedef struct { + + GLsizei width; + GLsizei height; + GLint components; + GLenum format; + + GLsizei cmapEntries; + GLenum cmapFormat; + GLubyte *cmap; + + GLubyte *pixels; + +} gliGenericImage; + +typedef struct { + unsigned char idLength; + unsigned char colorMapType; + + /* The image type. */ +#define TGA_TYPE_MAPPED 1 +#define TGA_TYPE_COLOR 2 +#define TGA_TYPE_GRAY 3 +#define TGA_TYPE_MAPPED_RLE 9 +#define TGA_TYPE_COLOR_RLE 10 +#define TGA_TYPE_GRAY_RLE 11 + unsigned char imageType; + + /* Color Map Specification. */ + /* We need to separately specify high and low bytes to avoid endianness + and alignment problems. */ + unsigned char colorMapIndexLo, colorMapIndexHi; + unsigned char colorMapLengthLo, colorMapLengthHi; + unsigned char colorMapSize; + + /* Image Specification. */ + unsigned char xOriginLo, xOriginHi; + unsigned char yOriginLo, yOriginHi; + + unsigned char widthLo, widthHi; + unsigned char heightLo, heightHi; + + unsigned char bpp; + + /* Image descriptor. + 3-0: attribute bpp + 4: left-to-right ordering + 5: top-to-bottom ordering + 7-6: zero + */ +#define TGA_DESC_ABITS 0x0f +#define TGA_DESC_HORIZONTAL 0x10 +#define TGA_DESC_VERTICAL 0x20 + unsigned char descriptor; + +} TgaHeader; + +typedef struct { + unsigned int extensionAreaOffset; + unsigned int developerDirectoryOffset; +#define TGA_SIGNATURE "TRUEVISION-XFILE" + char signature[16]; + char dot; + char null; +} TgaFooter; + +IGL_INLINE extern gliGenericImage *gliReadTGA(FILE *fp, char *name, int hflip, int vflip); +IGL_INLINE int gli_verbose(int new_verbose); +IGL_INLINE extern int gliVerbose(int newVerbose); + +IGL_INLINE void writeTGA( gliGenericImage* image, FILE *fp); + + + +} // end of igl namespace +} + +#ifndef IGL_STATIC_LIBRARY +# include "tga.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/progressive_hulls.cpp b/src/igl/copyleft/progressive_hulls.cpp new file mode 100644 index 0000000000..44738c8b2c --- /dev/null +++ b/src/igl/copyleft/progressive_hulls.cpp @@ -0,0 +1,31 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "progressive_hulls.h" +#include "progressive_hulls_cost_and_placement.h" +#include "../decimate.h" +#include "../max_faces_stopping_condition.h" +IGL_INLINE bool igl::copyleft::progressive_hulls( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const size_t max_m, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J) +{ + int m = F.rows(); + Eigen::VectorXi I; + return decimate( + V, + F, + progressive_hulls_cost_and_placement, + max_faces_stopping_condition(m,(const int)m,max_m), + U, + G, + J, + I); +} diff --git a/src/igl/copyleft/progressive_hulls.h b/src/igl/copyleft/progressive_hulls.h new file mode 100644 index 0000000000..54e0580a07 --- /dev/null +++ b/src/igl/copyleft/progressive_hulls.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_PROGRESSIVE_HULLS_H +#define IGL_COPYLEFT_PROGRESSIVE_HULLS_H +#include "../igl_inline.h" +#include + +namespace igl +{ + namespace copyleft + { + // Assumes (V,F) is a closed manifold mesh + // Collapses edges until desired number of faces is achieved but ensures + // that new vertices are placed outside all previous meshes as per + // "progressive hulls" in "Silhouette clipping" [Sander et al. 2000]. + // + // Inputs: + // V #V by dim list of vertex positions + // F #F by 3 list of face indices into V. + // max_m desired number of output faces + // Outputs: + // U #U by dim list of output vertex posistions (can be same ref as V) + // G #G by 3 list of output face indices into U (can be same ref as G) + // J #G list of indices into F of birth faces + // Returns true if m was reached (otherwise #G > m) + IGL_INLINE bool progressive_hulls( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const size_t max_m, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "progressive_hulls.cpp" +#endif +#endif diff --git a/src/igl/copyleft/progressive_hulls_cost_and_placement.cpp b/src/igl/copyleft/progressive_hulls_cost_and_placement.cpp new file mode 100644 index 0000000000..c9ab79ced4 --- /dev/null +++ b/src/igl/copyleft/progressive_hulls_cost_and_placement.cpp @@ -0,0 +1,107 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "progressive_hulls_cost_and_placement.h" +#include "quadprog.h" +#include "../unique.h" +#include "../circulation.h" +#include +#include +#include + +IGL_INLINE void igl::copyleft::progressive_hulls_cost_and_placement( + const int e, + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI, + double & cost, + Eigen::RowVectorXd & p) +{ + using namespace Eigen; + using namespace std; + // Controls the amount of quadratic energy to add (too small will introduce + // instabilities and flaps) + const double w = 0.1; + + assert(V.cols() == 3 && "V.cols() should be 3"); + // Gather list of unique face neighbors + vector Nall = circulation(e, true,F,E,EMAP,EF,EI); + vector Nother= circulation(e,false,F,E,EMAP,EF,EI); + Nall.insert(Nall.end(),Nother.begin(),Nother.end()); + vector N; + igl::unique(Nall,N); + // Gather: + // A #N by 3 normals scaled by area, + // D #N determinants of matrix formed by points as columns + // B #N point on plane dot normal + MatrixXd A(N.size(),3); + VectorXd D(N.size()); + VectorXd B(N.size()); + //cout<<"N=["; + for(int i = 0;i= B + // A x - B >=0 + // This is annoyingly necessary. Seems the solver is letting some garbage + // slip by. + success = success && ((A*x-B).minCoeff()>-1e-10); + if(success) + { + p = x.transpose(); + //assert(cost>=0 && "Cost should be positive"); + }else + { + cost = std::numeric_limits::infinity(); + //VectorXi NM; + //igl::list_to_matrix(N,NM); + //cout< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_PROGRESSIVE_HULLS_COST_AND_PLACEMENT_H +#define IGL_COPYLEFT_PROGRESSIVE_HULLS_COST_AND_PLACEMENT_H +#include +#include "../igl_inline.h" +namespace igl +{ + namespace copyleft + { + // A "cost and placement" compatible with `igl::decimate` implementing the + // "progressive hulls" algorithm in "Silhouette clipping" [Sander et al. + // 2000]. This implementation fixes an issue that the original linear + // program becomes unstable for flat patches by introducing a small + // quadratic energy term pulling the collapsed edge toward its midpoint. + // This function is not really meant to be called directly but rather + // passed to `igl::decimate` as a handle. + // + // Inputs: + // e index of edge to be collapsed + // V #V by 3 list of vertex positions + // F #F by 3 list of faces indices into V + // E #E by 3 list of edges indices into V + // EMAP #F*3 list of indices into E, mapping each directed edge to unique + // unique edge in E + // EF #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of + // F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) " + // e=(j->i) + // EI #E by 2 list of edge flap corners (see above). + // Outputs: + // cost cost of collapsing edge e + // p position to place collapsed vertex + // + IGL_INLINE void progressive_hulls_cost_and_placement( + const int e, + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI, + double & cost, + Eigen::RowVectorXd & p); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "progressive_hulls_cost_and_placement.cpp" +#endif +#endif diff --git a/src/igl/copyleft/quadprog.cpp b/src/igl/copyleft/quadprog.cpp new file mode 100644 index 0000000000..4c565a0c36 --- /dev/null +++ b/src/igl/copyleft/quadprog.cpp @@ -0,0 +1,599 @@ +#include "quadprog.h" +#include +/* + FILE eiquadprog.hh + + NOTE: this is a modified of uQuadProg++ package, working with Eigen data structures. + uQuadProg++ is itself a port made by Angelo Furfaro of QuadProg++ originally developed by + Luca Di Gaspero, working with ublas data structures. + + The quadprog_solve() function implements the algorithm of Goldfarb and Idnani + for the solution of a (convex) Quadratic Programming problem +by means of a dual method. + +The problem is in the form: + +min 0.5 * x G x + g0 x +s.t. + CE^T x + ce0 = 0 + CI^T x + ci0 >= 0 + + The matrix and vectors dimensions are as follows: + G: n * n + g0: n + + CE: n * p + ce0: p + + CI: n * m + ci0: m + + x: n + + The function will return the cost of the solution written in the x vector or + std::numeric_limits::infinity() if the problem is infeasible. In the latter case + the value of the x vector is not correct. + + References: D. Goldfarb, A. Idnani. A numerically stable dual method for solving + strictly convex quadratic programs. Mathematical Programming 27 (1983) pp. 1-33. + + Notes: + 1. pay attention in setting up the vectors ce0 and ci0. + If the constraints of your problem are specified in the form + A^T x = b and C^T x >= d, then you should set ce0 = -b and ci0 = -d. + 2. The matrix G is modified within the function since it is used to compute + the G = L^T L cholesky factorization for further computations inside the function. + If you need the original matrix G you should make a copy of it and pass the copy + to the function. + + + The author will be grateful if the researchers using this software will + acknowledge the contribution of this modified function and of Di Gaspero's + original version in their research papers. + + +LICENSE + +Copyright (2010) Gael Guennebaud +Copyright (2008) Angelo Furfaro +Copyright (2006) Luca Di Gaspero + + +This file is a porting of QuadProg++ routine, originally developed +by Luca Di Gaspero, exploiting uBlas data structures for vectors and +matrices instead of native C++ array. + +uquadprog is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +uquadprog is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with uquadprog; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include + +IGL_INLINE bool igl::copyleft::quadprog( + const Eigen::MatrixXd & G, + const Eigen::VectorXd & g0, + const Eigen::MatrixXd & CE, + const Eigen::VectorXd & ce0, + const Eigen::MatrixXd & CI, + const Eigen::VectorXd & ci0, + Eigen::VectorXd& x) +{ + using namespace Eigen; + typedef double Scalar; + const auto distance = [](Scalar a, Scalar b)->Scalar + { + Scalar a1, b1, t; + a1 = std::abs(a); + b1 = std::abs(b); + if (a1 > b1) + { + t = (b1 / a1); + return a1 * std::sqrt(1.0 + t * t); + } + else + if (b1 > a1) + { + t = (a1 / b1); + return b1 * std::sqrt(1.0 + t * t); + } + return a1 * std::sqrt(2.0); + }; + const auto compute_d = [](VectorXd &d, const MatrixXd& J, const VectorXd& np) + { + d = J.adjoint() * np; + }; + + const auto update_z = + [](VectorXd& z, const MatrixXd& J, const VectorXd& d, int iq) + { + z = J.rightCols(z.size()-iq) * d.tail(d.size()-iq); + }; + + const auto update_r = + [](const MatrixXd& R, VectorXd& r, const VectorXd& d, int iq) + { + r.head(iq) = + R.topLeftCorner(iq,iq).triangularView().solve(d.head(iq)); + }; + + const auto add_constraint = [&distance]( + MatrixXd& R, + MatrixXd& J, + VectorXd& d, + int& iq, + double& R_norm)->bool + { + int n=J.rows(); +#ifdef TRACE_SOLVER + std::cerr << "Add constraint " << iq << '/'; +#endif + int i, j, k; + double cc, ss, h, t1, t2, xny; + + /* we have to find the Givens rotation which will reduce the element + d(j) to zero. + if it is already zero we don't have to do anything, except of + decreasing j */ + for (j = n - 1; j >= iq + 1; j--) + { + /* The Givens rotation is done with the matrix (cc cs, cs -cc). + If cc is one, then element (j) of d is zero compared with element + (j - 1). Hence we don't have to do anything. + If cc is zero, then we just have to switch column (j) and column (j - 1) + of J. Since we only switch columns in J, we have to be careful how we + update d depending on the sign of gs. + Otherwise we have to apply the Givens rotation to these columns. + The i - 1 element of d has to be updated to h. */ + cc = d(j - 1); + ss = d(j); + h = distance(cc, ss); + if (h == 0.0) + continue; + d(j) = 0.0; + ss = ss / h; + cc = cc / h; + if (cc < 0.0) + { + cc = -cc; + ss = -ss; + d(j - 1) = -h; + } + else + d(j - 1) = h; + xny = ss / (1.0 + cc); + for (k = 0; k < n; k++) + { + t1 = J(k,j - 1); + t2 = J(k,j); + J(k,j - 1) = t1 * cc + t2 * ss; + J(k,j) = xny * (t1 + J(k,j - 1)) - t2; + } + } + /* update the number of constraints added*/ + iq++; + /* To update R we have to put the iq components of the d vector + into column iq - 1 of R + */ + R.col(iq-1).head(iq) = d.head(iq); +#ifdef TRACE_SOLVER + std::cerr << iq << std::endl; +#endif + + if (std::abs(d(iq - 1)) <= std::numeric_limits::epsilon() * R_norm) + { + // problem degenerate + return false; + } + R_norm = std::max(R_norm, std::abs(d(iq - 1))); + return true; + }; + + const auto delete_constraint = [&distance]( + MatrixXd& R, + MatrixXd& J, + VectorXi& A, + VectorXd& u, + int p, + int& iq, + int l) + { + int n = R.rows(); +#ifdef TRACE_SOLVER + std::cerr << "Delete constraint " << l << ' ' << iq; +#endif + int i, j, k, qq; + double cc, ss, h, xny, t1, t2; + + /* Find the index qq for active constraint l to be removed */ + for (i = p; i < iq; i++) + if (A(i) == l) + { + qq = i; + break; + } + + /* remove the constraint from the active set and the duals */ + for (i = qq; i < iq - 1; i++) + { + A(i) = A(i + 1); + u(i) = u(i + 1); + R.col(i) = R.col(i+1); + } + + A(iq - 1) = A(iq); + u(iq - 1) = u(iq); + A(iq) = 0; + u(iq) = 0.0; + for (j = 0; j < iq; j++) + R(j,iq - 1) = 0.0; + /* constraint has been fully removed */ + iq--; +#ifdef TRACE_SOLVER + std::cerr << '/' << iq << std::endl; +#endif + + if (iq == 0) + return; + + for (j = qq; j < iq; j++) + { + cc = R(j,j); + ss = R(j + 1,j); + h = distance(cc, ss); + if (h == 0.0) + continue; + cc = cc / h; + ss = ss / h; + R(j + 1,j) = 0.0; + if (cc < 0.0) + { + R(j,j) = -h; + cc = -cc; + ss = -ss; + } + else + R(j,j) = h; + + xny = ss / (1.0 + cc); + for (k = j + 1; k < iq; k++) + { + t1 = R(j,k); + t2 = R(j + 1,k); + R(j,k) = t1 * cc + t2 * ss; + R(j + 1,k) = xny * (t1 + R(j,k)) - t2; + } + for (k = 0; k < n; k++) + { + t1 = J(k,j); + t2 = J(k,j + 1); + J(k,j) = t1 * cc + t2 * ss; + J(k,j + 1) = xny * (J(k,j) + t1) - t2; + } + } + }; + + int i, j, k, l; /* indices */ + int ip, me, mi; + int n=g0.size(); int p=ce0.size(); int m=ci0.size(); + MatrixXd R(G.rows(),G.cols()), J(G.rows(),G.cols()); + + LLT chol(G.cols()); + + VectorXd s(m+p), z(n), r(m + p), d(n), np(n), u(m + p); + VectorXd x_old(n), u_old(m + p); + double f_value, psi, c1, c2, sum, ss, R_norm; + const double inf = std::numeric_limits::infinity(); + double t, t1, t2; /* t is the step length, which is the minimum of the partial step length t1 + * and the full step length t2 */ + VectorXi A(m + p), A_old(m + p), iai(m + p); + int q; + int iq, iter = 0; + std::vector iaexcl(m + p); + + me = p; /* number of equality constraints */ + mi = m; /* number of inequality constraints */ + q = 0; /* size of the active set A (containing the indices of the active constraints) */ + + /* + * Preprocessing phase + */ + + /* compute the trace of the original matrix G */ + c1 = G.trace(); + + /* decompose the matrix G in the form LL^T */ + chol.compute(G); + + /* initialize the matrix R */ + d.setZero(); + R.setZero(); + R_norm = 1.0; /* this variable will hold the norm of the matrix R */ + + /* compute the inverse of the factorized matrix G^-1, this is the initial value for H */ + // J = L^-T + J.setIdentity(); + J = chol.matrixU().solve(J); + c2 = J.trace(); +#ifdef TRACE_SOLVER + print_matrix("J", J, n); +#endif + + /* c1 * c2 is an estimate for cond(G) */ + + /* + * Find the unconstrained minimizer of the quadratic form 0.5 * x G x + g0 x + * this is a feasible point in the dual space + * x = G^-1 * g0 + */ + x = chol.solve(g0); + x = -x; + /* and compute the current solution value */ + f_value = 0.5 * g0.dot(x); +#ifdef TRACE_SOLVER + std::cerr << "Unconstrained solution: " << f_value << std::endl; + print_vector("x", x, n); +#endif + + /* Add equality constraints to the working set A */ + iq = 0; + for (i = 0; i < me; i++) + { + np = CE.col(i); + compute_d(d, J, np); + update_z(z, J, d, iq); + update_r(R, r, d, iq); +#ifdef TRACE_SOLVER + print_matrix("R", R, iq); + print_vector("z", z, n); + print_vector("r", r, iq); + print_vector("d", d, n); +#endif + + /* compute full step length t2: i.e., the minimum step in primal space s.t. the contraint + becomes feasible */ + t2 = 0.0; + if (std::abs(z.dot(z)) > std::numeric_limits::epsilon()) // i.e. z != 0 + t2 = (-np.dot(x) - ce0(i)) / z.dot(np); + + x += t2 * z; + + /* set u = u+ */ + u(iq) = t2; + u.head(iq) -= t2 * r.head(iq); + + /* compute the new solution value */ + f_value += 0.5 * (t2 * t2) * z.dot(np); + A(i) = -i - 1; + + if (!add_constraint(R, J, d, iq, R_norm)) + { + // FIXME: it should raise an error + // Equality constraints are linearly dependent + return false; + } + } + + /* set iai = K \ A */ + for (i = 0; i < mi; i++) + iai(i) = i; + +l1: iter++; +#ifdef TRACE_SOLVER + print_vector("x", x, n); +#endif + /* step 1: choose a violated constraint */ + for (i = me; i < iq; i++) + { + ip = A(i); + iai(ip) = -1; + } + + /* compute s(x) = ci^T * x + ci0 for all elements of K \ A */ + ss = 0.0; + psi = 0.0; /* this value will contain the sum of all infeasibilities */ + ip = 0; /* ip will be the index of the chosen violated constraint */ + for (i = 0; i < mi; i++) + { + iaexcl[i] = true; + sum = CI.col(i).dot(x) + ci0(i); + s(i) = sum; + psi += std::min(0.0, sum); + } +#ifdef TRACE_SOLVER + print_vector("s", s, mi); +#endif + + + if (std::abs(psi) <= mi * std::numeric_limits::epsilon() * c1 * c2* 100.0) + { + /* numerically there are not infeasibilities anymore */ + q = iq; + return true; + } + + /* save old values for u, x and A */ + u_old.head(iq) = u.head(iq); + A_old.head(iq) = A.head(iq); + x_old = x; + +l2: /* Step 2: check for feasibility and determine a new S-pair */ + for (i = 0; i < mi; i++) + { + if (s(i) < ss && iai(i) != -1 && iaexcl[i]) + { + ss = s(i); + ip = i; + } + } + if (ss >= 0.0) + { + q = iq; + return true; + } + + /* set np = n(ip) */ + np = CI.col(ip); + /* set u = (u 0)^T */ + u(iq) = 0.0; + /* add ip to the active set A */ + A(iq) = ip; + +#ifdef TRACE_SOLVER + std::cerr << "Trying with constraint " << ip << std::endl; + print_vector("np", np, n); +#endif + +l2a:/* Step 2a: determine step direction */ + /* compute z = H np: the step direction in the primal space (through J, see the paper) */ + compute_d(d, J, np); + update_z(z, J, d, iq); + /* compute N* np (if q > 0): the negative of the step direction in the dual space */ + update_r(R, r, d, iq); +#ifdef TRACE_SOLVER + std::cerr << "Step direction z" << std::endl; + print_vector("z", z, n); + print_vector("r", r, iq + 1); + print_vector("u", u, iq + 1); + print_vector("d", d, n); + print_ivector("A", A, iq + 1); +#endif + + /* Step 2b: compute step length */ + l = 0; + /* Compute t1: partial step length (maximum step in dual space without violating dual feasibility */ + t1 = inf; /* +inf */ + /* find the index l s.t. it reaches the minimum of u+(x) / r */ + for (k = me; k < iq; k++) + { + double tmp; + if (r(k) > 0.0 && ((tmp = u(k) / r(k)) < t1) ) + { + t1 = tmp; + l = A(k); + } + } + /* Compute t2: full step length (minimum step in primal space such that the constraint ip becomes feasible */ + if (std::abs(z.dot(z)) > std::numeric_limits::epsilon()) // i.e. z != 0 + t2 = -s(ip) / z.dot(np); + else + t2 = inf; /* +inf */ + + /* the step is chosen as the minimum of t1 and t2 */ + t = std::min(t1, t2); +#ifdef TRACE_SOLVER + std::cerr << "Step sizes: " << t << " (t1 = " << t1 << ", t2 = " << t2 << ") "; +#endif + + /* Step 2c: determine new S-pair and take step: */ + + /* case (i): no step in primal or dual space */ + if (t >= inf) + { + /* QPP is infeasible */ + // FIXME: unbounded to raise + q = iq; + return false; + } + /* case (ii): step in dual space */ + if (t2 >= inf) + { + /* set u = u + t * [-r 1) and drop constraint l from the active set A */ + u.head(iq) -= t * r.head(iq); + u(iq) += t; + iai(l) = l; + delete_constraint(R, J, A, u, p, iq, l); +#ifdef TRACE_SOLVER + std::cerr << " in dual space: " + << f_value << std::endl; + print_vector("x", x, n); + print_vector("z", z, n); + print_ivector("A", A, iq + 1); +#endif + goto l2a; + } + + /* case (iii): step in primal and dual space */ + + x += t * z; + /* update the solution value */ + f_value += t * z.dot(np) * (0.5 * t + u(iq)); + + u.head(iq) -= t * r.head(iq); + u(iq) += t; +#ifdef TRACE_SOLVER + std::cerr << " in both spaces: " + << f_value << std::endl; + print_vector("x", x, n); + print_vector("u", u, iq + 1); + print_vector("r", r, iq + 1); + print_ivector("A", A, iq + 1); +#endif + + if (t == t2) + { +#ifdef TRACE_SOLVER + std::cerr << "Full step has taken " << t << std::endl; + print_vector("x", x, n); +#endif + /* full step has taken */ + /* add constraint ip to the active set*/ + if (!add_constraint(R, J, d, iq, R_norm)) + { + iaexcl[ip] = false; + delete_constraint(R, J, A, u, p, iq, ip); +#ifdef TRACE_SOLVER + print_matrix("R", R, n); + print_ivector("A", A, iq); +#endif + for (i = 0; i < m; i++) + iai(i) = i; + for (i = 0; i < iq; i++) + { + A(i) = A_old(i); + iai(A(i)) = -1; + u(i) = u_old(i); + } + x = x_old; + goto l2; /* go to step 2 */ + } + else + iai(ip) = -1; +#ifdef TRACE_SOLVER + print_matrix("R", R, n); + print_ivector("A", A, iq); +#endif + goto l1; + } + + /* a patial step has taken */ +#ifdef TRACE_SOLVER + std::cerr << "Partial step has taken " << t << std::endl; + print_vector("x", x, n); +#endif + /* drop constraint l */ + iai(l) = l; + delete_constraint(R, J, A, u, p, iq, l); +#ifdef TRACE_SOLVER + print_matrix("R", R, n); + print_ivector("A", A, iq); +#endif + + s(ip) = CI.col(ip).dot(x) + ci0(ip); + +#ifdef TRACE_SOLVER + print_vector("s", s, mi); +#endif + goto l2a; +} diff --git a/src/igl/copyleft/quadprog.h b/src/igl/copyleft/quadprog.h new file mode 100644 index 0000000000..f054bbdba2 --- /dev/null +++ b/src/igl/copyleft/quadprog.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_QUADPROG_H +#define IGL_COPYLEFT_QUADPROG_H + +#include "../igl_inline.h" +#include +namespace igl +{ + namespace copyleft + { + // Solve a (dense) quadratric program of the form: + // + // min 0.5 x G x + g0 x + // s.t. CE' x + ce0 = 0 + // and CI' x + ci0 >= 0 + // + // Inputs: + // G #x by #x matrix of quadratic coefficients + // g0 #x vector of linear coefficients + // CE #x by #CE list of linear equality coefficients + // ce0 #CE list of linear equality right-hand sides + // CI #x by #CI list of linear equality coefficients + // ci0 #CI list of linear equality right-hand sides + // Outputs: + // x #x vector of solution values + // Returns true iff success + IGL_INLINE bool quadprog( + const Eigen::MatrixXd & G, + const Eigen::VectorXd & g0, + const Eigen::MatrixXd & CE, + const Eigen::VectorXd & ce0, + const Eigen::MatrixXd & CI, + const Eigen::VectorXd & ci0, + Eigen::VectorXd& x); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "quadprog.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/swept_volume.cpp b/src/igl/copyleft/swept_volume.cpp new file mode 100644 index 0000000000..f21e3aba5f --- /dev/null +++ b/src/igl/copyleft/swept_volume.cpp @@ -0,0 +1,49 @@ +#include "swept_volume.h" +#include "../swept_volume_bounding_box.h" +#include "../swept_volume_signed_distance.h" +#include "../voxel_grid.h" +#include "marching_cubes.h" +#include + +IGL_INLINE void igl::copyleft::swept_volume( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const std::function & transform, + const size_t steps, + const size_t grid_res, + const size_t isolevel_grid, + Eigen::MatrixXd & SV, + Eigen::MatrixXi & SF) +{ + using namespace std; + using namespace Eigen; + using namespace igl; + using namespace igl::copyleft; + + const auto & Vtransform = + [&V,&transform](const size_t vi,const double t)->RowVector3d + { + Vector3d Vvi = V.row(vi).transpose(); + return (transform(t)*Vvi).transpose(); + }; + AlignedBox3d Mbox; + swept_volume_bounding_box(V.rows(),Vtransform,steps,Mbox); + + // Amount of padding: pad*h should be >= isolevel + const int pad = isolevel_grid+1; + // number of vertices on the largest side + const int s = grid_res+2*pad; + const double h = Mbox.diagonal().maxCoeff()/(double)(s-2.*pad-1.); + const double isolevel = isolevel_grid*h; + + // create grid + RowVector3i res; + MatrixXd GV; + voxel_grid(Mbox,s,pad,GV,res); + + // compute values + VectorXd S; + swept_volume_signed_distance(V,F,transform,steps,GV,res,h,isolevel,S); + S.array()-=isolevel; + marching_cubes(S,GV,res(0),res(1),res(2),SV,SF); +} diff --git a/src/igl/copyleft/swept_volume.h b/src/igl/copyleft/swept_volume.h new file mode 100644 index 0000000000..cc178f78a3 --- /dev/null +++ b/src/igl/copyleft/swept_volume.h @@ -0,0 +1,41 @@ +#ifndef IGL_COPYLEFT_SWEPT_VOLUME_H +#define IGL_COPYLEFT_SWEPT_VOLUME_H +#include "../igl_inline.h" +#include +#include +namespace igl +{ + namespace copyleft + { + // Compute the surface of the swept volume of a solid object with surface + // (V,F) mesh under going rigid motion. + // + // Inputs: + // V #V by 3 list of mesh positions in reference pose + // F #F by 3 list of mesh indices into V + // transform function handle so that transform(t) returns the rigid + // transformation at time t∈[0,1] + // steps number of time steps: steps=3 --> t∈{0,0.5,1} + // grid_res number of grid cells on the longest side containing the + // motion (isolevel+1 cells will also be added on each side as padding) + // isolevel distance level to be contoured as swept volume + // Outputs: + // SV #SV by 3 list of mesh positions of the swept surface + // SF #SF by 3 list of mesh faces into SV + IGL_INLINE void swept_volume( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const std::function & transform, + const size_t steps, + const size_t grid_res, + const size_t isolevel, + Eigen::MatrixXd & SV, + Eigen::MatrixXi & SF); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "swept_volume.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/tetgen/README b/src/igl/copyleft/tetgen/README new file mode 100644 index 0000000000..0315b6c116 --- /dev/null +++ b/src/igl/copyleft/tetgen/README @@ -0,0 +1,7 @@ +IGL interface to tetgen library + +Dependencies: + tetgen + +Travel to $IGL/external/tetgen and issue: + make -f Makefile.igl tetlib diff --git a/src/igl/copyleft/tetgen/cdt.cpp b/src/igl/copyleft/tetgen/cdt.cpp new file mode 100644 index 0000000000..f2602dd661 --- /dev/null +++ b/src/igl/copyleft/tetgen/cdt.cpp @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "cdt.h" +#include "../../bounding_box.h" +#include "tetrahedralize.h" + +template < + typename DerivedV, + typename DerivedF, + typename DerivedTV, + typename DerivedTT, + typename DerivedTF> +IGL_INLINE bool igl::copyleft::tetgen::cdt( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const CDTParam & param, + Eigen::PlainObjectBase& TV, + Eigen::PlainObjectBase& TT, + Eigen::PlainObjectBase& TF) +{ + using namespace Eigen; + using namespace std; + // Effective input mesh + DerivedV U; + DerivedF G; + if(param.use_bounding_box) + { + // Construct bounding box mesh + DerivedV BV; + DerivedF BF; + bounding_box(V,BV,BF); + // scale bounding box + const RowVector3d mid = + (BV.colwise().minCoeff() + BV.colwise().maxCoeff()).eval()*0.5; + BV.rowwise() -= mid; + assert(param.bounding_box_scale >= 1.); + BV.array() *= param.bounding_box_scale; + BV.rowwise() += mid; + // Append bounding box to mesh + U.resize(V.rows()+BV.rows(),V.cols()); + U<, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, igl::copyleft::tetgen::CDTParam const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/tetgen/cdt.h b/src/igl/copyleft/tetgen/cdt.h new file mode 100644 index 0000000000..e59a280291 --- /dev/null +++ b/src/igl/copyleft/tetgen/cdt.h @@ -0,0 +1,72 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_TETGEN_CDT_H +#define IGL_COPYLEFT_TETGEN_CDT_H +#include "../../igl_inline.h" + +#include +#include +#ifndef TETLIBRARY +# define TETLIBRARY +#endif +#include "tetgen.h" // Defined REAL + +namespace igl +{ + namespace copyleft + { + namespace tetgen + { + struct CDTParam + { + // Tetgen can compute mesh of convex hull of input (i.e. "c") but often + // chokes. One workaround is to force it to mesh the entire bounding box. + // {false} + bool use_bounding_box = false; + // Scale the bounding box a bit so that vertices near it do not give tetgen + // problems. {1.01} + double bounding_box_scale = 1.01; + // Flags to tetgen. Do not include the "c" flag here! {"Y"} + std::string flags = "Y"; + }; + // Create a constrained delaunay tessellation containing convex hull of the + // given **non-selfintersecting** mesh. + // + // Inputs: + // V #V by 3 list of input mesh vertices + // F #F by 3 list of input mesh facets + // param see above + // TV #TV by 3 list of output mesh vertices (V come first) + // TT #TT by 3 list of tetrahedra indices into TV. + // TF #TF by 3 list of facets from F potentially subdivided. + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedTV, + typename DerivedTT, + typename DerivedTF> + IGL_INLINE bool cdt( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const CDTParam & param, + Eigen::PlainObjectBase& TV, + Eigen::PlainObjectBase& TT, + Eigen::PlainObjectBase& TF); + } + } +} + + +#ifndef IGL_STATIC_LIBRARY +# include "cdt.cpp" +#endif + +#endif + + diff --git a/src/igl/copyleft/tetgen/mesh_to_tetgenio.cpp b/src/igl/copyleft/tetgen/mesh_to_tetgenio.cpp new file mode 100644 index 0000000000..78eaebb5c7 --- /dev/null +++ b/src/igl/copyleft/tetgen/mesh_to_tetgenio.cpp @@ -0,0 +1,78 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mesh_to_tetgenio.h" + +// IGL includes +#include "../../matrix_to_list.h" + +// STL includes +#include + +IGL_INLINE bool igl::copyleft::tetgen::mesh_to_tetgenio( + const std::vector > & V, + const std::vector > & F, + tetgenio & in) +{ + using namespace std; + // all indices start from 0 + in.firstnumber = 0; + + in.numberofpoints = V.size(); + in.pointlist = new REAL[in.numberofpoints * 3]; + // loop over points + for(int i = 0; i < (int)V.size(); i++) + { + assert(V[i].size() == 3); + in.pointlist[i*3+0] = V[i][0]; + in.pointlist[i*3+1] = V[i][1]; + in.pointlist[i*3+2] = V[i][2]; + } + + in.numberoffacets = F.size(); + in.facetlist = new tetgenio::facet[in.numberoffacets]; + in.facetmarkerlist = new int[in.numberoffacets]; + + // loop over face + for(int i = 0;i < (int)F.size(); i++) + { + in.facetmarkerlist[i] = i; + tetgenio::facet * f = &in.facetlist[i]; + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[f->numberofpolygons]; + f->numberofholes = 0; + f->holelist = NULL; + tetgenio::polygon * p = &f->polygonlist[0]; + p->numberofvertices = F[i].size(); + p->vertexlist = new int[p->numberofvertices]; + // loop around face + for(int j = 0;j < (int)F[i].size(); j++) + { + p->vertexlist[j] = F[i][j]; + } + } + return true; +} + +template +IGL_INLINE bool igl::copyleft::tetgen::mesh_to_tetgenio( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + tetgenio & in) +{ + using namespace std; + vector > vV; + vector > vF; + matrix_to_list(V,vV); + matrix_to_list(F,vF); + return mesh_to_tetgenio(vV,vF,in); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::copyleft::tetgen::mesh_to_tetgenio, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, tetgenio&); +#endif diff --git a/src/igl/copyleft/tetgen/mesh_to_tetgenio.h b/src/igl/copyleft/tetgen/mesh_to_tetgenio.h new file mode 100644 index 0000000000..3295294fe5 --- /dev/null +++ b/src/igl/copyleft/tetgen/mesh_to_tetgenio.h @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_TETGEN_MESH_TO_TETGENIO_H +#define IGL_COPYLEFT_TETGEN_MESH_TO_TETGENIO_H +#include "../../igl_inline.h" + +#ifndef TETLIBRARY +# define TETLIBRARY +#endif +#include "tetgen.h" // Defined tetgenio, REAL +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace tetgen + { + // Load a vertex list and face list into a tetgenio object + // Inputs: + // V #V by 3 vertex position list + // F #F list of polygon face indices into V (0-indexed) + // Outputs: + // in tetgenio input object + // Returns true on success, false on error + IGL_INLINE bool mesh_to_tetgenio( + const std::vector > & V, + const std::vector > & F, + tetgenio & in); + + // Wrapper with Eigen types + // Templates: + // DerivedV real-value: i.e. from MatrixXd + // DerivedF integer-value: i.e. from MatrixXi + template + IGL_INLINE bool mesh_to_tetgenio( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + tetgenio & in); + } + } +} + + +#ifndef IGL_STATIC_LIBRARY +# include "mesh_to_tetgenio.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/tetgen/mesh_with_skeleton.cpp b/src/igl/copyleft/tetgen/mesh_with_skeleton.cpp new file mode 100644 index 0000000000..5c920b36d4 --- /dev/null +++ b/src/igl/copyleft/tetgen/mesh_with_skeleton.cpp @@ -0,0 +1,102 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mesh_with_skeleton.h" +#include "tetrahedralize.h" + +#include "../../sample_edges.h" +#include "../../cat.h" + +#include +// Default settings pq2Y tell tetgen to mesh interior of triangle mesh and +// to produce a graded tet mesh +const static std::string DEFAULT_TETGEN_FLAGS = "pq2Y"; + +IGL_INLINE bool igl::copyleft::tetgen::mesh_with_skeleton( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & C, + const Eigen::VectorXi & /*P*/, + const Eigen::MatrixXi & BE, + const Eigen::MatrixXi & CE, + const int samples_per_bone, + const std::string & tetgen_flags, + Eigen::MatrixXd & VV, + Eigen::MatrixXi & TT, + Eigen::MatrixXi & FF) +{ + using namespace Eigen; + using namespace std; + const string eff_tetgen_flags = + (tetgen_flags.length() == 0?DEFAULT_TETGEN_FLAGS:tetgen_flags); + // Collect all edges that need samples: + MatrixXi BECE = cat(1,BE,CE); + MatrixXd S; + // Sample each edge with 10 samples. (Choice of 10 doesn't seem to matter so + // much, but could under some circumstances) + sample_edges(C,BECE,samples_per_bone,S); + // Vertices we'll constrain tet mesh to meet + MatrixXd VS = cat(1,V,S); + // Use tetgen to mesh the interior of surface, this assumes surface: + // * has no holes + // * has no non-manifold edges or vertices + // * has consistent orientation + // * has no self-intersections + // * has no 0-volume pieces + cerr<<"tetgen begin()"< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_TETGEN_MESH_WITH_SKELETON_H +#define IGL_COPYLEFT_TETGEN_MESH_WITH_SKELETON_H +#include "../../igl_inline.h" +#include +#include + +namespace igl +{ + namespace copyleft + { + namespace tetgen + { + // Mesh the interior of a given surface with tetrahedra which are graded + // (tend to be small near the surface and large inside) and conform to the + // given handles and samplings thereof. + // + // Inputs: + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of triangle indices + // C #C by 3 list of vertex positions + // P #P list of point handle indices + // BE #BE by 2 list of bone-edge indices + // CE #CE by 2 list of cage-edge indices + // samples_per_bone #samples to add per bone + // tetgen_flags flags to pass to tetgen {""-->"pq2Y"} otherwise you're on + // your own and it's your funeral if you pass nonsense flags + // Outputs: + // VV #VV by 3 list of tet-mesh vertex positions + // TT #TT by 4 list of tetrahedra indices + // FF #FF by 3 list of surface triangle indices + // Returns true only on success + IGL_INLINE bool mesh_with_skeleton( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & C, + const Eigen::VectorXi & /*P*/, + const Eigen::MatrixXi & BE, + const Eigen::MatrixXi & CE, + const int samples_per_bone, + const std::string & tetgen_flags, + Eigen::MatrixXd & VV, + Eigen::MatrixXi & TT, + Eigen::MatrixXi & FF); + // Wrapper using default tetgen_flags + IGL_INLINE bool mesh_with_skeleton( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & C, + const Eigen::VectorXi & /*P*/, + const Eigen::MatrixXi & BE, + const Eigen::MatrixXi & CE, + const int samples_per_bone, + Eigen::MatrixXd & VV, + Eigen::MatrixXi & TT, + Eigen::MatrixXi & FF); + } + } +} + + +#ifndef IGL_STATIC_LIBRARY +# include "mesh_with_skeleton.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/tetgen/read_into_tetgenio.cpp b/src/igl/copyleft/tetgen/read_into_tetgenio.cpp new file mode 100644 index 0000000000..506c5e4ba1 --- /dev/null +++ b/src/igl/copyleft/tetgen/read_into_tetgenio.cpp @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "read_into_tetgenio.h" +#include "mesh_to_tetgenio.h" + +// IGL includes +#include "../../pathinfo.h" +#ifndef IGL_NO_EIGEN +# define IGL_NO_EIGEN_WAS_NOT_ALREADY_DEFINED +# define IGL_NO_EIGEN +#endif +// Include igl headers without including Eigen +#include "../../readOBJ.h" +#ifdef IGL_NO_EIGEN_WAS_NOT_ALREADY_DEFINED +# undef IGL_NO_EIGEN +#endif + +// STL includes +#include +#include +#include + +IGL_INLINE bool igl::copyleft::tetgen::read_into_tetgenio( + const std::string & path, + tetgenio & in) +{ + using namespace std; + // get file extension + string dirname,basename,ext,filename; + pathinfo(path,dirname,basename,ext,filename); + // convert to lower case for easy comparison + transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + bool success = false; + + char basename_char[1024]; + strcpy(basename_char,basename.c_str()); + + if(ext == "obj") + { + // read obj into vertex list and face list + vector > V,TC,N; + vector > F,FTC,FN; + success = readOBJ(path,V,TC,N,F,FTC,FN); + success &= mesh_to_tetgenio(V,F,in); + }else if(ext == "off") + { + success = in.load_off(basename_char); + }else if(ext == "node") + { + success = in.load_node(basename_char); + }else + { + if(ext.length() > 0) + { + cerr<<"^read_into_tetgenio Warning: Unsupported extension ("< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_TETGEN_READ_INTO_TETGENIO_H +#define IGL_COPYLEFT_TETGEN_READ_INTO_TETGENIO_H +#include "../../igl_inline.h" + +#include +#ifndef TETLIBRARY +#define TETLIBRARY +#endif +#include "tetgen.h" // Defined tetgenio, REAL + +namespace igl +{ + namespace copyleft + { + namespace tetgen + { + // Read a mesh or point set into tetgenio (input object for calling + // tetgen). Many file formats are already supported by tetgen: + // .off + // .ply + // .node + // .ply + // .medit + // .vtk + // etc. + // Notably it does not support .obj which is loaded by hand here (also + // demonstrating how to load points/faces programmatically) + // + // If the file extension is not recognized the filename is assumed to be + // the basename of a collection describe a tetmesh, (of which at least + // the .node file must exist): + // [filename].node + // [filename].ele + // [filename].face + // [filename].edge + // [filename].vol + // + // Inputs: + // path path to file or basename to files + // Outputs: + // in tetgenio input object + // Returns true on success, false on error + IGL_INLINE bool read_into_tetgenio( + const std::string & path, + tetgenio & in); + } + } +} + + +#ifndef IGL_STATIC_LIBRARY +# include "read_into_tetgenio.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/tetgen/tetgenio_to_tetmesh.cpp b/src/igl/copyleft/tetgen/tetgenio_to_tetmesh.cpp new file mode 100644 index 0000000000..d61153053a --- /dev/null +++ b/src/igl/copyleft/tetgen/tetgenio_to_tetmesh.cpp @@ -0,0 +1,146 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "tetgenio_to_tetmesh.h" + +// IGL includes +#include "../../list_to_matrix.h" + +// STL includes +#include + +IGL_INLINE bool igl::copyleft::tetgen::tetgenio_to_tetmesh( + const tetgenio & out, + std::vector > & V, + std::vector > & T, + std::vector > & F) +{ + using namespace std; + // process points + if(out.pointlist == NULL) + { + cerr<<"^tetgenio_to_tetmesh Error: point list is NULL\n"<(3)); + // loop over points + for(int i = 0;i < out.numberofpoints; i++) + { + V[i][0] = out.pointlist[i*3+0]; + V[i][1] = out.pointlist[i*3+1]; + V[i][2] = out.pointlist[i*3+2]; + } + + + // process tets + if(out.tetrahedronlist == NULL) + { + cerr<<"^tetgenio_to_tetmesh Error: tet list is NULL\n"<(out.numberofcorners)); + int min_index = 1e7; + int max_index = -1e7; + // loop over tetrahedra + for(int i = 0; i < out.numberoftetrahedra; i++) + { + for(int j = 0; j index ? index : min_index); + max_index = (max_index < index ? index : max_index); + } + } + assert(min_index >= 0); + assert(max_index >= 0); + assert(max_index < (int)V.size()); + + cout<=0) + { + vector face(3); + for(int j = 0; j<3; j++) + { + face[j] = out.trifacelist[i * 3 + j]; + } + F.push_back(face); + } + } + + return true; +} + +IGL_INLINE bool igl::copyleft::tetgen::tetgenio_to_tetmesh( + const tetgenio & out, + std::vector > & V, + std::vector > & T) +{ + std::vector > F; + return tetgenio_to_tetmesh(out,V,T,F); +} + +template +IGL_INLINE bool igl::copyleft::tetgen::tetgenio_to_tetmesh( + const tetgenio & out, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& T, + Eigen::PlainObjectBase& F) +{ + using namespace std; + vector > vV; + vector > vT; + vector > vF; + bool success = tetgenio_to_tetmesh(out,vV,vT,vF); + if(!success) + { + return false; + } + bool V_rect = list_to_matrix(vV,V); + if(!V_rect) + { + // igl::list_to_matrix(vV,V) already printed error message to std err + return false; + } + bool T_rect = list_to_matrix(vT,T); + if(!T_rect) + { + // igl::list_to_matrix(vT,T) already printed error message to std err + return false; + } + bool F_rect = list_to_matrix(vF,F); + if(!F_rect) + { + return false; + } + + return true; +} + +template +IGL_INLINE bool igl::copyleft::tetgen::tetgenio_to_tetmesh( + const tetgenio & out, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& T) +{ + Eigen::Matrix F; + return tetgenio_to_tetmesh(out,V,T,F); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::copyleft::tetgen::tetgenio_to_tetmesh, Eigen::Matrix >(tetgenio const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/tetgen/tetgenio_to_tetmesh.h b/src/igl/copyleft/tetgen/tetgenio_to_tetmesh.h new file mode 100644 index 0000000000..2a5575886d --- /dev/null +++ b/src/igl/copyleft/tetgen/tetgenio_to_tetmesh.h @@ -0,0 +1,66 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_TETGEN_TETGENIO_TO_TETMESH_H +#define IGL_COPYLEFT_TETGEN_TETGENIO_TO_TETMESH_H +#include "../../igl_inline.h" + +#ifndef TETLIBRARY +#define TETLIBRARY +#endif +#include "tetgen.h" // Defined tetgenio, REAL +#include +#include +namespace igl +{ + namespace copyleft + { + namespace tetgen + { + // Extract a tetrahedral mesh from a tetgenio object + // Inputs: + // out tetgenio output object + // Outputs: + // V #V by 3 vertex position list + // T #T by 4 list of tetrahedra indices into V + // F #F by 3 list of marked facets + // Returns true on success, false on error + IGL_INLINE bool tetgenio_to_tetmesh( + const tetgenio & out, + std::vector > & V, + std::vector > & T, + std::vector > & F); + IGL_INLINE bool tetgenio_to_tetmesh( + const tetgenio & out, + std::vector > & V, + std::vector > & T); + + // Wrapper with Eigen types + // Templates: + // DerivedV real-value: i.e. from MatrixXd + // DerivedT integer-value: i.e. from MatrixXi + template + IGL_INLINE bool tetgenio_to_tetmesh( + const tetgenio & out, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& T, + Eigen::PlainObjectBase& F); + template + IGL_INLINE bool tetgenio_to_tetmesh( + const tetgenio & out, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& T); + } + } +} + + +#ifndef IGL_STATIC_LIBRARY +# include "tetgenio_to_tetmesh.cpp" +#endif + +#endif diff --git a/src/igl/copyleft/tetgen/tetrahedralize.cpp b/src/igl/copyleft/tetgen/tetrahedralize.cpp new file mode 100644 index 0000000000..fa61114103 --- /dev/null +++ b/src/igl/copyleft/tetgen/tetrahedralize.cpp @@ -0,0 +1,220 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "tetrahedralize.h" +#include "mesh_to_tetgenio.h" +#include "tetgenio_to_tetmesh.h" + +// IGL includes +#include "../../matrix_to_list.h" +#include "../../list_to_matrix.h" +#include "../../boundary_facets.h" + +// STL includes +#include +#include + +IGL_INLINE int igl::copyleft::tetgen::tetrahedralize( + const std::vector > & V, + const std::vector > & F, + const std::string switches, + std::vector > & TV, + std::vector > & TT, + std::vector > & TF) +{ + using namespace std; + tetgenio in,out; + bool success; + success = mesh_to_tetgenio(V,F,in); + if(!success) + { + return -1; + } + try + { + char * cswitches = new char[switches.size() + 1]; + std::strcpy(cswitches,switches.c_str()); + ::tetrahedralize(cswitches,&in, &out); + delete[] cswitches; + }catch(int e) + { + cerr<<"^"<<__FUNCTION__<<": TETGEN CRASHED... KABOOOM!!!"< +IGL_INLINE int igl::copyleft::tetgen::tetrahedralize( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const std::string switches, + Eigen::PlainObjectBase& TV, + Eigen::PlainObjectBase& TT, + Eigen::PlainObjectBase& TF) +{ + using namespace std; + vector > vV,vTV; + vector > vF,vTT,vTF; + matrix_to_list(V,vV); + matrix_to_list(F,vF); + int e = tetrahedralize(vV,vF,switches,vTV,vTT,vTF); + if(e == 0) + { + bool TV_rect = list_to_matrix(vTV,TV); + if(!TV_rect) + { + return 3; + } + bool TT_rect = list_to_matrix(vTT,TT); + if(!TT_rect) + { + return 3; + } + bool TF_rect = list_to_matrix(vTF,TF); + if(!TF_rect) + { + return 3; + } + } + return e; +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedVM, + typename DerivedFM, + typename DerivedTV, + typename DerivedTT, + typename DerivedTF, + typename DerivedTM> +IGL_INLINE int igl::copyleft::tetgen::tetrahedralize( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& VM, + const Eigen::PlainObjectBase& FM, + const std::string switches, + Eigen::PlainObjectBase& TV, + Eigen::PlainObjectBase& TT, + Eigen::PlainObjectBase& TF, + Eigen::PlainObjectBase& TM) +{ + using namespace std; + vector > vV,vTV; + vector > vF,vTT,vTF; + vector vTM; + + matrix_to_list(V,vV); + matrix_to_list(F,vF); + vector vVM = matrix_to_list(VM); + vector vFM = matrix_to_list(FM); + int e = tetrahedralize(vV,vF,vVM,vFM,switches,vTV,vTT,vTF,vTM); + if(e == 0) + { + bool TV_rect = list_to_matrix(vTV,TV); + if(!TV_rect) + { + return false; + } + bool TT_rect = list_to_matrix(vTT,TT); + if(!TT_rect) + { + return false; + } + bool TF_rect = list_to_matrix(vTF,TF); + if(!TF_rect) + { + return false; + } + bool TM_rect = list_to_matrix(vTM,TM); + if(!TM_rect) + { + return false; + } + } + return e; +} +IGL_INLINE int igl::copyleft::tetgen::tetrahedralize( + const std::vector > & V, + const std::vector > & F, + const std::vector & VM, + const std::vector & FM, + const std::string switches, + std::vector > & TV, + std::vector > & TT, + std::vector > & TF, + std::vector & TM) +{ + using namespace std; + tetgenio in,out; + bool success; + success = mesh_to_tetgenio(V,F,in); + if(!success) + { + return -1; + } + in.pointmarkerlist = new int[VM.size()]; + for (int i = 0; i < VM.size(); ++i) { + in.pointmarkerlist[i] = VM[i]; + } + // These have already been created in mesh_to_tetgenio. + // Reset them here. + for (int i = 0; i < FM.size(); ++i) { + in.facetmarkerlist[i] = FM[i]; + } + try + { + char * cswitches = new char[switches.size() + 1]; + std::strcpy(cswitches,switches.c_str()); + ::tetrahedralize(cswitches,&in, &out); + delete[] cswitches; + }catch(int e) + { + cerr<<"^"<<__FUNCTION__<<": TETGEN CRASHED... KABOOOM!!!"<, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template int igl::copyleft::tetgen::tetrahedralize,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(const Eigen::PlainObjectBase > &,const Eigen::PlainObjectBase > &,const Eigen::PlainObjectBase > &,const Eigen::PlainObjectBase > &,const std::basic_string, std::allocator >,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &, Eigen::PlainObjectBase > &); +template int igl::copyleft::tetgen::tetrahedralize, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/copyleft/tetgen/tetrahedralize.h b/src/igl/copyleft/tetgen/tetrahedralize.h new file mode 100644 index 0000000000..170a3f9852 --- /dev/null +++ b/src/igl/copyleft/tetgen/tetrahedralize.h @@ -0,0 +1,136 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COPYLEFT_TETGEN_TETRAHEDRALIZE_H +#define IGL_COPYLEFT_TETGEN_TETRAHEDRALIZE_H +#include "../../igl_inline.h" + +#include +#include +#include +#ifndef TETLIBRARY +#define TETLIBRARY +#endif +#include "tetgen.h" // Defined REAL + +namespace igl +{ + namespace copyleft + { + namespace tetgen + { + // Mesh the interior of a surface mesh (V,F) using tetgen + // + // Inputs: + // V #V by 3 vertex position list + // F #F list of polygon face indices into V (0-indexed) + // switches string of tetgen options (See tetgen documentation) e.g. + // "pq1.414a0.01" tries to mesh the interior of a given surface with + // quality and area constraints + // "" will mesh the convex hull constrained to pass through V (ignores F) + // Outputs: + // TV #V by 3 vertex position list + // TT #T by 4 list of tet face indices + // TF #F by 3 list of triangle face indices + // Returns status: + // 0 success + // 1 tetgen threw exception + // 2 tetgen did not crash but could not create any tets (probably there are + // holes, duplicate faces etc.) + // -1 other error + IGL_INLINE int tetrahedralize( + const std::vector > & V, + const std::vector > & F, + const std::string switches, + std::vector > & TV, + std::vector > & TT, + std::vector > & TF); + + // Wrapper with Eigen types + // Templates: + // DerivedV real-value: i.e. from MatrixXd + // DerivedF integer-value: i.e. from MatrixXi + template < + typename DerivedV, + typename DerivedF, + typename DerivedTV, + typename DerivedTT, + typename DerivedTF> + IGL_INLINE int tetrahedralize( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const std::string switches, + Eigen::PlainObjectBase& TV, + Eigen::PlainObjectBase& TT, + Eigen::PlainObjectBase& TF); + + // Mesh the interior of a surface mesh (V,F) using tetgen + // + // Inputs: + // V #V by 3 vertex position list + // F #F list of polygon face indices into V (0-indexed) + // M #V list of markers for vertices + // switches string of tetgen options (See tetgen documentation) e.g. + // "pq1.414a0.01" tries to mesh the interior of a given surface with + // quality and area constraints + // "" will mesh the convex hull constrained to pass through V (ignores F) + // Outputs: + // TV #V by 3 vertex position list + // TT #T by 4 list of tet face indices + // TF #F by 3 list of triangle face indices + // TM #V list of markers for vertices + // Returns status: + // 0 success + // 1 tetgen threw exception + // 2 tetgen did not crash but could not create any tets (probably there are + // holes, duplicate faces etc.) + // -1 other error + IGL_INLINE int tetrahedralize( + const std::vector > & V, + const std::vector > & F, + const std::vector & VM, + const std::vector & FM, + const std::string switches, + std::vector > & TV, + std::vector > & TT, + std::vector > & TF, + std::vector & TM); + + // Wrapper with Eigen types + // Templates: + // DerivedV real-value: i.e. from MatrixXd + // DerivedF integer-value: i.e. from MatrixXi + template < + typename DerivedV, + typename DerivedF, + typename DerivedVM, + typename DerivedFM, + typename DerivedTV, + typename DerivedTT, + typename DerivedTF, + typename DerivedTM> + IGL_INLINE int tetrahedralize( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& VM, + const Eigen::PlainObjectBase& FM, + const std::string switches, + Eigen::PlainObjectBase& TV, + Eigen::PlainObjectBase& TT, + Eigen::PlainObjectBase& TF, + Eigen::PlainObjectBase& TM); + } + } +} + + +#ifndef IGL_STATIC_LIBRARY +# include "tetrahedralize.cpp" +#endif + +#endif + diff --git a/src/igl/cotmatrix.cpp b/src/igl/cotmatrix.cpp new file mode 100644 index 0000000000..b1fd658ac0 --- /dev/null +++ b/src/igl/cotmatrix.cpp @@ -0,0 +1,88 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "cotmatrix.h" +#include + +// For error printing +#include +#include "cotmatrix_entries.h" + +// Bug in unsupported/Eigen/SparseExtra needs iostream first +#include + +template +IGL_INLINE void igl::cotmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::SparseMatrix& L) +{ + using namespace Eigen; + using namespace std; + + L.resize(V.rows(),V.rows()); + Matrix edges; + int simplex_size = F.cols(); + // 3 for triangles, 4 for tets + assert(simplex_size == 3 || simplex_size == 4); + if(simplex_size == 3) + { + // This is important! it could decrease the comptuation time by a factor of 2 + // Laplacian for a closed 2d manifold mesh will have on average 7 entries per + // row + L.reserve(10*V.rows()); + edges.resize(3,2); + edges << + 1,2, + 2,0, + 0,1; + }else if(simplex_size == 4) + { + L.reserve(17*V.rows()); + edges.resize(6,2); + edges << + 1,2, + 2,0, + 0,1, + 3,0, + 3,1, + 3,2; + }else + { + return; + } + // Gather cotangents + Matrix C; + cotmatrix_entries(V,F,C); + + vector > IJV; + IJV.reserve(F.rows()*edges.rows()*4); + // Loop over triangles + for(int i = 0; i < F.rows(); i++) + { + // loop over edges of element + for(int e = 0;e(source,dest,C(i,e))); + IJV.push_back(Triplet(dest,source,C(i,e))); + IJV.push_back(Triplet(source,source,-C(i,e))); + IJV.push_back(Triplet(dest,dest,-C(i,e))); + } + } + L.setFromTriplets(IJV.begin(),IJV.end()); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::cotmatrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +template void igl::cotmatrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +template void igl::cotmatrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +template void igl::cotmatrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/cotmatrix.h b/src/igl/cotmatrix.h new file mode 100644 index 0000000000..bb8232f5a5 --- /dev/null +++ b/src/igl/cotmatrix.h @@ -0,0 +1,57 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COTMATRIX_H +#define IGL_COTMATRIX_H +#include "igl_inline.h" + +#include +#include + +// History: +// Used const references rather than copying the entire mesh +// Alec 9 October 2011 +// removed cotan (uniform weights) optional parameter it was building a buggy +// half of the uniform laplacian, please see adjacency_matrix instead +// Alec 9 October 2011 + +namespace igl +{ + // Constructs the cotangent stiffness matrix (discrete laplacian) for a given + // mesh (V,F). + // + // Templates: + // DerivedV derived type of eigen matrix for V (e.g. derived from + // MatrixXd) + // DerivedF derived type of eigen matrix for F (e.g. derived from + // MatrixXi) + // Scalar scalar type for eigen sparse matrix (e.g. double) + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by simplex_size list of mesh faces (must be triangles) + // Outputs: + // L #V by #V cotangent matrix, each row i corresponding to V(i,:) + // + // See also: adjacency_matrix + // + // Note: This Laplacian uses the convention that diagonal entries are + // **minus** the sum of off-diagonal entries. The diagonal entries are + // therefore in general negative and the matrix is **negative** semi-definite + // (immediately, -L is **positive** semi-definite) + // + template + IGL_INLINE void cotmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::SparseMatrix& L); +} + +#ifndef IGL_STATIC_LIBRARY +# include "cotmatrix.cpp" +#endif + +#endif diff --git a/src/igl/cotmatrix_entries.cpp b/src/igl/cotmatrix_entries.cpp new file mode 100644 index 0000000000..6f7dfbbd3d --- /dev/null +++ b/src/igl/cotmatrix_entries.cpp @@ -0,0 +1,110 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "cotmatrix_entries.h" +#include "doublearea.h" +#include "squared_edge_lengths.h" +#include "edge_lengths.h" +#include "face_areas.h" +#include "volume.h" +#include "dihedral_angles.h" + +#include "verbose.h" + + +template +IGL_INLINE void igl::cotmatrix_entries( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& C) +{ + using namespace std; + using namespace Eigen; + // simplex size (3: triangles, 4: tetrahedra) + int simplex_size = F.cols(); + // Number of elements + int m = F.rows(); + + // Law of cosines + law of sines + switch(simplex_size) + { + case 3: + { + // Triangles + //Compute Squared Edge lengths + Matrix l2; + igl::squared_edge_lengths(V,F,l2); + //Compute Edge lengths + Matrix l; + l = l2.array().sqrt(); + + // double area + Matrix dblA; + doublearea(l,0.,dblA); + // cotangents and diagonal entries for element matrices + // correctly divided by 4 (alec 2010) + C.resize(m,3); + for(int i = 0;i l; + edge_lengths(V,F,l); + Matrix s; + face_areas(l,s); + Matrix cos_theta,theta; + dihedral_angles_intrinsic(l,s,theta,cos_theta); + + // volume + Matrix vol; + volume(l,vol); + + + // Law of sines + // http://mathworld.wolfram.com/Tetrahedron.html + Matrix sin_theta(m,6); + sin_theta.col(0) = vol.array() / ((2./(3.*l.col(0).array())).array() * s.col(1).array() * s.col(2).array()); + sin_theta.col(1) = vol.array() / ((2./(3.*l.col(1).array())).array() * s.col(2).array() * s.col(0).array()); + sin_theta.col(2) = vol.array() / ((2./(3.*l.col(2).array())).array() * s.col(0).array() * s.col(1).array()); + sin_theta.col(3) = vol.array() / ((2./(3.*l.col(3).array())).array() * s.col(3).array() * s.col(0).array()); + sin_theta.col(4) = vol.array() / ((2./(3.*l.col(4).array())).array() * s.col(3).array() * s.col(1).array()); + sin_theta.col(5) = vol.array() / ((2./(3.*l.col(5).array())).array() * s.col(3).array() * s.col(2).array()); + + + // http://arxiv.org/pdf/1208.0354.pdf Page 18 + C = (1./6.) * l.array() * cos_theta.array() / sin_theta.array(); + + break; + } + default: + { + fprintf(stderr, + "cotmatrix_entries.h: Error: Simplex size (%d) not supported\n", simplex_size); + assert(false); + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::cotmatrix_entries, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::cotmatrix_entries, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::cotmatrix_entries, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::cotmatrix_entries, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/cotmatrix_entries.h b/src/igl/cotmatrix_entries.h new file mode 100644 index 0000000000..6368c02e8a --- /dev/null +++ b/src/igl/cotmatrix_entries.h @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COTMATRIX_ENTRIES_H +#define IGL_COTMATRIX_ENTRIES_H +#include "igl_inline.h" +#include +namespace igl +{ + // COTMATRIX_ENTRIES compute the cotangents of each angle in mesh (V,F) + // + // Inputs: + // V #V by dim list of rest domain positions + // F #F by {3|4} list of {triangle|tetrahedra} indices into V + // Outputs: + // C #F by 3 list of 1/2*cotangents corresponding angles + // for triangles, columns correspond to edges [1,2],[2,0],[0,1] + // OR + // C #F by 6 list of 1/6*cotangents of dihedral angles*edge lengths + // for tets, columns along edges [1,2],[2,0],[0,1],[3,0],[3,1],[3,2] + // + template + IGL_INLINE void cotmatrix_entries( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& C); +} + +#ifndef IGL_STATIC_LIBRARY +# include "cotmatrix_entries.cpp" +#endif + +#endif diff --git a/src/igl/count.cpp b/src/igl/count.cpp new file mode 100644 index 0000000000..bdde79ca7a --- /dev/null +++ b/src/igl/count.cpp @@ -0,0 +1,65 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "count.h" +#include "redux.h" + +template +IGL_INLINE void igl::count( + const Eigen::SparseMatrix& X, + const int dim, + Eigen::SparseVector& S) +{ + // dim must be 2 or 1 + assert(dim == 1 || dim == 2); + // Get size of input + int m = X.rows(); + int n = X.cols(); + // resize output + if(dim==1) + { + S = Eigen::SparseVector(n); + }else + { + S = Eigen::SparseVector(m); + } + + // Iterate over outside + for(int k=0; k::InnerIterator it (X,k); it; ++it) + { + if(dim == 1) + { + S.coeffRef(it.col()) += (it.value() == 0? 0: 1); + }else + { + S.coeffRef(it.row()) += (it.value() == 0? 0: 1); + } + } + } + +} + +template +IGL_INLINE void igl::count( + const Eigen::SparseMatrix& A, + const int dim, + Eigen::PlainObjectBase& B) +{ + typedef typename DerivedB::Scalar Scalar; + igl::redux(A,dim,[](Scalar a, Scalar b){ return a+(b==0?0:1);},B); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::count >(Eigen::SparseMatrix const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::count >(Eigen::SparseMatrix const&, int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/count.h b/src/igl/count.h new file mode 100644 index 0000000000..61aeb94e8a --- /dev/null +++ b/src/igl/count.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COUNT_H +#define IGL_COUNT_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Note: If your looking for dense matrix matlab like sum for eigen matrics + // just use: + // M.colwise().count() or M.rowwise().count() + // + + // Count the number of non-zeros in the columns or rows of a sparse matrix + // + // Inputs: + // X m by n sparse matrix + // dim dimension along which to sum (1 or 2) + // Output: + // S n-long sparse vector (if dim == 1) + // or + // S m-long sparse vector (if dim == 2) + template + IGL_INLINE void count( + const Eigen::SparseMatrix& X, + const int dim, + Eigen::SparseVector& S); + template + IGL_INLINE void count( + const Eigen::SparseMatrix& X, + const int dim, + Eigen::PlainObjectBase& S); +} + +#ifndef IGL_STATIC_LIBRARY +# include "count.cpp" +#endif + +#endif + diff --git a/src/igl/covariance_scatter_matrix.cpp b/src/igl/covariance_scatter_matrix.cpp new file mode 100644 index 0000000000..24fe9f7600 --- /dev/null +++ b/src/igl/covariance_scatter_matrix.cpp @@ -0,0 +1,76 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "covariance_scatter_matrix.h" +#include "arap_linear_block.h" +#include "cotmatrix.h" +#include "diag.h" +#include "sum.h" +#include "edges.h" +#include "verbose.h" +#include "cat.h" +#include "PI.h" + +IGL_INLINE void igl::covariance_scatter_matrix( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const ARAPEnergyType energy, + Eigen::SparseMatrix& CSM) +{ + using namespace Eigen; + // number of mesh vertices + int n = V.rows(); + assert(n > F.maxCoeff()); + // dimension of mesh + int dim = V.cols(); + // Number of mesh elements + int m = F.rows(); + + // number of rotations + int nr; + switch(energy) + { + case ARAP_ENERGY_TYPE_SPOKES: + nr = n; + break; + case ARAP_ENERGY_TYPE_SPOKES_AND_RIMS: + nr = n; + break; + case ARAP_ENERGY_TYPE_ELEMENTS: + nr = m; + break; + default: + fprintf( + stderr, + "covariance_scatter_matrix.h: Error: Unsupported arap energy %d\n", + energy); + return; + } + + SparseMatrix KX,KY,KZ; + arap_linear_block(V,F,0,energy,KX); + arap_linear_block(V,F,1,energy,KY); + SparseMatrix Z(n,nr); + if(dim == 2) + { + CSM = cat(1,cat(2,KX,Z),cat(2,Z,KY)).transpose(); + }else if(dim == 3) + { + arap_linear_block(V,F,2,energy,KZ); + SparseMatrixZZ(n,nr*2); + CSM = + cat(1,cat(1,cat(2,KX,ZZ),cat(2,cat(2,Z,KY),Z)),cat(2,ZZ,KZ)).transpose(); + }else + { + fprintf( + stderr, + "covariance_scatter_matrix.h: Error: Unsupported dimension %d\n", + dim); + return; + } + +} diff --git a/src/igl/covariance_scatter_matrix.h b/src/igl/covariance_scatter_matrix.h new file mode 100644 index 0000000000..e70c77ab13 --- /dev/null +++ b/src/igl/covariance_scatter_matrix.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_COVARIANCE_SCATTER_MATRIX_H +#define IGL_COVARIANCE_SCATTER_MATRIX_H + +#include "igl_inline.h" +#include "ARAPEnergyType.h" +#include +#include + +namespace igl +{ + // Construct the covariance scatter matrix for a given arap energy + // Inputs: + // V #V by Vdim list of initial domain positions + // F #F by 3 list of triangle indices into V + // energy ARAPEnergyType enum value defining which energy is being used. + // See ARAPEnergyType.h for valid options and explanations. + // Outputs: + // CSM dim*#V/#F by dim*#V sparse matrix containing special laplacians along + // the diagonal so that when multiplied by V gives covariance matrix + // elements, can be used to speed up covariance matrix computation + IGL_INLINE void covariance_scatter_matrix( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const ARAPEnergyType energy, + Eigen::SparseMatrix& CSM); +} + +#ifndef IGL_STATIC_LIBRARY +#include "covariance_scatter_matrix.cpp" +#endif +#endif diff --git a/src/igl/cross.cpp b/src/igl/cross.cpp new file mode 100644 index 0000000000..04ee5340bc --- /dev/null +++ b/src/igl/cross.cpp @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "cross.h" + +// http://www.antisphere.com/Wiki/tools:anttweakbar +IGL_INLINE void igl::cross( + const double *a, + const double *b, + double *out) +{ + out[0] = a[1]*b[2]-a[2]*b[1]; + out[1] = a[2]*b[0]-a[0]*b[2]; + out[2] = a[0]*b[1]-a[1]*b[0]; +} + +template < + typename DerivedA, + typename DerivedB, + typename DerivedC> +IGL_INLINE void igl::cross( + const Eigen::PlainObjectBase & A, + const Eigen::PlainObjectBase & B, + Eigen::PlainObjectBase & C) +{ + assert(A.cols() == 3 && "#cols should be 3"); + assert(B.cols() == 3 && "#cols should be 3"); + assert(A.rows() == B.rows() && "#rows in A and B should be equal"); + C.resize(A.rows(),3); + for(int d = 0;d<3;d++) + { + C.col(d) = + A.col((d+1)%3).array() * B.col((d+2)%3).array() - + A.col((d+2)%3).array() * B.col((d+1)%3).array(); + } +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::cross, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::cross, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/cross.h b/src/igl/cross.h new file mode 100644 index 0000000000..cfbe563ed1 --- /dev/null +++ b/src/igl/cross.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CROSS_H +#define IGL_CROSS_H +#include "igl_inline.h" +#include +namespace igl +{ + // Computes out = cross(a,b) + // Inputs: + // a left 3d vector + // b right 3d vector + // Outputs: + // out result 3d vector + IGL_INLINE void cross( const double *a, const double *b, double *out); + // Computes C = cross(A,B,2); + // + // Inputs: + // A #A by 3 list of row-vectors + // B #A by 3 list of row-vectors + // Outputs: + // C #A by 3 list of row-vectors + template < + typename DerivedA, + typename DerivedB, + typename DerivedC> + IGL_INLINE void cross( + const Eigen::PlainObjectBase & A, + const Eigen::PlainObjectBase & B, + Eigen::PlainObjectBase & C); +} + +#ifndef IGL_STATIC_LIBRARY +# include "cross.cpp" +#endif + +#endif diff --git a/src/igl/cross_field_missmatch.cpp b/src/igl/cross_field_missmatch.cpp new file mode 100644 index 0000000000..7d3caf0a8d --- /dev/null +++ b/src/igl/cross_field_missmatch.cpp @@ -0,0 +1,142 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "cross_field_missmatch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace igl { + template + class MissMatchCalculator + { + public: + + const Eigen::PlainObjectBase &V; + const Eigen::PlainObjectBase &F; + const Eigen::PlainObjectBase &PD1; + const Eigen::PlainObjectBase &PD2; + + DerivedV N; + + private: + // internal + std::vector V_border; // bool + std::vector > VF; + std::vector > VFi; + + DerivedF TT; + DerivedF TTi; + + + private: + ///compute the mismatch between 2 faces + inline int MissMatchByCross(const int f0, + const int f1) + { + Eigen::Matrix dir0 = PD1.row(f0); + Eigen::Matrix dir1 = PD1.row(f1); + Eigen::Matrix n0 = N.row(f0); + Eigen::Matrix n1 = N.row(f1); + + Eigen::Matrix dir1Rot = igl::rotation_matrix_from_directions(n1,n0)*dir1; + dir1Rot.normalize(); + + // TODO: this should be equivalent to the other code below, to check! + // Compute the angle between the two vectors + // double a0 = atan2(dir0.dot(B2.row(f0)),dir0.dot(B1.row(f0))); + // double a1 = atan2(dir1Rot.dot(B2.row(f0)),dir1Rot.dot(B1.row(f0))); + // + // double angle_diff = a1-a0; //VectToAngle(f0,dir1Rot); + + double angle_diff = atan2(dir1Rot.dot(PD2.row(f0)),dir1Rot.dot(PD1.row(f0))); + + // std::cerr << "Dani: " << dir0(0) << " " << dir0(1) << " " << dir0(2) << " " << dir1Rot(0) << " " << dir1Rot(1) << " " << dir1Rot(2) << " " << angle_diff << std::endl; + + double step=igl::PI/2.0; + int i=(int)std::floor((angle_diff/step)+0.5); + int k=0; + if (i>=0) + k=i%4; + else + k=(-(3*i))%4; + return k; + } + + +public: + inline MissMatchCalculator(const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const Eigen::PlainObjectBase &_PD1, + const Eigen::PlainObjectBase &_PD2 + ): + V(_V), + F(_F), + PD1(_PD1), + PD2(_PD2) + { + igl::per_face_normals(V,F,N); + V_border = igl::is_border_vertex(V,F); + igl::vertex_triangle_adjacency(V,F,VF,VFi); + igl::triangle_triangle_adjacency(F,TT,TTi); + } + + inline void calculateMissmatch(Eigen::PlainObjectBase &Handle_MMatch) + { + Handle_MMatch.setConstant(F.rows(),3,-1); + for (size_t i=0;i +IGL_INLINE void igl::cross_field_missmatch(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const Eigen::PlainObjectBase &PD2, + const bool isCombed, + Eigen::PlainObjectBase &missmatch) +{ + DerivedV PD1_combed; + DerivedV PD2_combed; + + if (!isCombed) + igl::comb_cross_field(V,F,PD1,PD2,PD1_combed,PD2_combed); + else + { + PD1_combed = PD1; + PD2_combed = PD2; + } + igl::MissMatchCalculator sf(V, F, PD1_combed, PD2_combed); + sf.calculateMissmatch(missmatch); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::cross_field_missmatch, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool, Eigen::PlainObjectBase >&); +template void igl::cross_field_missmatch, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool, Eigen::PlainObjectBase >&); +template void igl::cross_field_missmatch, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool, Eigen::PlainObjectBase >&); + +#endif diff --git a/src/igl/cross_field_missmatch.h b/src/igl/cross_field_missmatch.h new file mode 100644 index 0000000000..9d7a7599d6 --- /dev/null +++ b/src/igl/cross_field_missmatch.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_CROSS_FIELD_MISSMATCH_H +#define IGL_CROSS_FIELD_MISSMATCH_H +#include "igl_inline.h" +#include +namespace igl +{ + // Calculates the missmatch (integer), at each face edge, of a cross field defined on the mesh faces. + // The integer missmatch is a multiple of pi/2 that transforms the cross on one side of the edge to + // the cross on the other side. It represents the deviation from a Lie connection across the edge. + + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (quad) indices + // PD1 #F by 3 eigen Matrix of the first per face cross field vector + // PD2 #F by 3 eigen Matrix of the second per face cross field vector + // isCombed boolean, specifying whether the field is combed (i.e. matching has been precomputed. + // If not, the field is combed first. + // Output: + // Handle_MMatch #F by 3 eigen Matrix containing the integer missmatch of the cross field + // across all face edges + // + + template + IGL_INLINE void cross_field_missmatch(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const Eigen::PlainObjectBase &PD2, + const bool isCombed, + Eigen::PlainObjectBase &missmatch); +} +#ifndef IGL_STATIC_LIBRARY +#include "cross_field_missmatch.cpp" +#endif + +#endif diff --git a/src/igl/crouzeix_raviart_cotmatrix.cpp b/src/igl/crouzeix_raviart_cotmatrix.cpp new file mode 100644 index 0000000000..2fc5f6d4ad --- /dev/null +++ b/src/igl/crouzeix_raviart_cotmatrix.cpp @@ -0,0 +1,100 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "crouzeix_raviart_cotmatrix.h" +#include "unique_simplices.h" +#include "oriented_facets.h" +#include "is_edge_manifold.h" +#include "cotmatrix_entries.h" + +template +void igl::crouzeix_raviart_cotmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::SparseMatrix & L, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP) +{ + // All occurrences of directed "facets" + Eigen::MatrixXi allE; + oriented_facets(F,allE); + Eigen::VectorXi _1; + unique_simplices(allE,E,_1,EMAP); + return crouzeix_raviart_cotmatrix(V,F,E,EMAP,L); +} + +template +void igl::crouzeix_raviart_cotmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & EMAP, + Eigen::SparseMatrix & L) +{ + // number of rows + const int m = F.rows(); + // Element simplex size + const int ss = F.cols(); + // Mesh should be edge-manifold + assert(F.cols() != 3 || is_edge_manifold(F)); + typedef Eigen::Matrix MatrixXS; + MatrixXS C; + cotmatrix_entries(V,F,C); + Eigen::MatrixXi F2E(m,ss); + { + int k =0; + for(int c = 0;c > LIJV;LIJV.reserve(k*m); + Eigen::VectorXi LI(k),LJ(k),LV(k); + // Compensation factor to match scales in matlab version + double factor = 2.0; + + switch(ss) + { + default: assert(false && "unsupported simplex size"); + case 3: + factor = 4.0; + LI<<0,1,2,1,2,0,0,1,2,1,2,0; + LJ<<1,2,0,0,1,2,0,1,2,1,2,0; + LV<<2,0,1,2,0,1,2,0,1,2,0,1; + break; + case 4: + factor *= -1.0; + LI<<0,3,3,3,1,2,1,0,1,2,2,0,0,3,3,3,1,2,1,0,1,2,2,0; + LJ<<1,0,1,2,2,0,0,3,3,3,1,2,0,3,3,3,1,2,1,0,1,2,2,0; + LV<<2,3,4,5,0,1,2,3,4,5,0,1,2,3,4,5,0,1,2,3,4,5,0,1; + break; + } + + for(int f=0;f, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/crouzeix_raviart_cotmatrix.h b/src/igl/crouzeix_raviart_cotmatrix.h new file mode 100644 index 0000000000..1504ddad04 --- /dev/null +++ b/src/igl/crouzeix_raviart_cotmatrix.h @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CROUZEIX_RAVIART_COTMATRIX +#define IGL_CROUZEIX_RAVIART_COTMATRIX +#include "igl_inline.h" +#include +#include +namespace igl +{ + // CROUZEIX_RAVIART_COTMATRIX Compute the Crouzeix-Raviart cotangent + // stiffness matrix. + // + // See for example "Discrete Quadratic Curvature Energies" [Wardetzky, Bergou, + // Harmon, Zorin, Grinspun 2007] + // + // Inputs: + // V #V by dim list of vertex positions + // F #F by 3/4 list of triangle/tetrahedron indices + // Outputs: + // L #E by #E edge/face-based diagonal cotangent matrix + // E #E by 2/3 list of edges/faces + // EMAP #F*3/4 list of indices mapping allE to E + // + // See also: crouzeix_raviart_massmatrix + template + void crouzeix_raviart_cotmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::SparseMatrix & L, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP); + // wrapper if E and EMAP are already computed (better match!) + template + void crouzeix_raviart_cotmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & EMAP, + Eigen::SparseMatrix & L); +} +#ifndef IGL_STATIC_LIBRARY +# include "crouzeix_raviart_cotmatrix.cpp" +#endif +#endif diff --git a/src/igl/crouzeix_raviart_massmatrix.cpp b/src/igl/crouzeix_raviart_massmatrix.cpp new file mode 100644 index 0000000000..0873712d54 --- /dev/null +++ b/src/igl/crouzeix_raviart_massmatrix.cpp @@ -0,0 +1,85 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "crouzeix_raviart_massmatrix.h" +#include "unique_simplices.h" +#include "oriented_facets.h" + +#include "is_edge_manifold.h" +#include "doublearea.h" +#include "volume.h" + +#include +#include + +template +void igl::crouzeix_raviart_massmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::SparseMatrix & M, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP) +{ + // All occurrences of directed "facets" + Eigen::MatrixXi allE; + oriented_facets(F,allE); + Eigen::VectorXi _1; + unique_simplices(allE,E,_1,EMAP); + return crouzeix_raviart_massmatrix(V,F,E,EMAP,M); +} + +template +void igl::crouzeix_raviart_massmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & EMAP, + Eigen::SparseMatrix & M) +{ + using namespace Eigen; + using namespace std; + // Mesh should be edge-manifold (TODO: replace `is_edge_manifold` with + // `is_facet_manifold`) + assert(F.cols() != 3 || is_edge_manifold(F)); + // number of elements (triangles) + const int m = F.rows(); + // Get triangle areas/volumes + VectorXd TA; + // Element simplex size + const int ss = F.cols(); + switch(ss) + { + default: + assert(false && "Unsupported simplex size"); + case 3: + doublearea(V,F,TA); + TA *= 0.5; + break; + case 4: + volume(V,F,TA); + break; + } + vector > MIJV(ss*m); + assert(EMAP.size() == m*ss); + for(int f = 0;f(EMAP(f+m*c),EMAP(f+m*c),TA(f)/(double)(ss)); + } + } + M.resize(E.rows(),E.rows()); + M.setFromTriplets(MIJV.begin(),MIJV.end()); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::crouzeix_raviart_massmatrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::crouzeix_raviart_massmatrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +template void igl::crouzeix_raviart_massmatrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/crouzeix_raviart_massmatrix.h b/src/igl/crouzeix_raviart_massmatrix.h new file mode 100644 index 0000000000..256aa20676 --- /dev/null +++ b/src/igl/crouzeix_raviart_massmatrix.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef CROUZEIX_RAVIART_MASSMATRIX_H +#define CROUZEIX_RAVIART_MASSMATRIX_H +#include +#include + +namespace igl +{ + // CROUZEIX_RAVIART_MASSMATRIX Compute the Crouzeix-Raviart mass matrix where + // M(e,e) is just the sum of the areas of the triangles on either side of an + // edge e. + // + // See for example "Discrete Quadratic Curvature Energies" [Wardetzky, Bergou, + // Harmon, Zorin, Grinspun 2007] + // + // Inputs: + // V #V by dim list of vertex positions + // F #F by 3/4 list of triangle/tetrahedron indices + // Outputs: + // M #E by #E edge/face-based diagonal mass matrix + // E #E by 2/3 list of edges/faces + // EMAP #F*3/4 list of indices mapping allE to E + // + // See also: crouzeix_raviart_cotmatrix + template + void crouzeix_raviart_massmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::SparseMatrix & M, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP); + // wrapper if E and EMAP are already computed (better match!) + template + void crouzeix_raviart_massmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & EMAP, + Eigen::SparseMatrix & M); +} +#ifndef IGL_STATIC_LIBRARY +# include "crouzeix_raviart_massmatrix.cpp" +#endif + +#endif diff --git a/src/igl/cumsum.cpp b/src/igl/cumsum.cpp new file mode 100644 index 0000000000..38eb4edd31 --- /dev/null +++ b/src/igl/cumsum.cpp @@ -0,0 +1,72 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "cumsum.h" +#include +#include + +template +IGL_INLINE void igl::cumsum( + const Eigen::MatrixBase & X, + const int dim, + Eigen::PlainObjectBase & Y) +{ + using namespace Eigen; + using namespace std; + Y.resizeLike(X); + // get number of columns (or rows) + int num_outer = (dim == 1 ? X.cols() : X.rows() ); + // get number of rows (or columns) + int num_inner = (dim == 1 ? X.rows() : X.cols() ); + // This has been optimized so that dim = 1 or 2 is roughly the same cost. + // (Optimizations assume ColMajor order) + if(dim == 1) + { +#pragma omp parallel for + for(int o = 0;o, Eigen::Matrix >(Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::cumsum, Eigen::Matrix >(Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::cumsum, Eigen::Matrix >(Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::cumsum, Eigen::Matrix >(Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::cumsum, Eigen::Matrix >(Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::cumsum, Eigen::Matrix >(Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::cumsum, class Eigen::Matrix>(class Eigen::MatrixBase> const &, int, class Eigen::PlainObjectBase> &); +template void igl::cumsum, class Eigen::Matrix>(class Eigen::MatrixBase> const &, int, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/cumsum.h b/src/igl/cumsum.h new file mode 100644 index 0000000000..c3fa592580 --- /dev/null +++ b/src/igl/cumsum.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CUMSUM_H +#define IGL_CUMSUM_H + +#include "igl_inline.h" +#include + +namespace igl +{ + // Computes a cumulative sum of the columns of X, like matlab's `cumsum`. + // + // Templates: + // DerivedX Type of matrix X + // DerivedY Type of matrix Y + // Inputs: + // X m by n Matrix to be cumulatively summed. + // dim dimension to take cumulative sum (1 or 2) + // Output: + // Y m by n Matrix containing cumulative sum. + // + template + IGL_INLINE void cumsum( + const Eigen::MatrixBase & X, + const int dim, + Eigen::PlainObjectBase & Y); + //template + //IGL_INLINE void cumsum( + // const Eigen::MatrixBase & X, + // Eigen::PlainObjectBase & Y); +} + +#ifndef IGL_STATIC_LIBRARY +# include "cumsum.cpp" +#endif + +#endif + diff --git a/src/igl/cut_mesh.cpp b/src/igl/cut_mesh.cpp new file mode 100644 index 0000000000..33a25ce93c --- /dev/null +++ b/src/igl/cut_mesh.cpp @@ -0,0 +1,330 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include +#include +#include +#include +#include +#include + +// This file violates many of the libigl style guidelines. + +namespace igl { + + + template + class MeshCutterMini + { + public: + // Input + //mesh + const Eigen::PlainObjectBase &V; + const Eigen::PlainObjectBase &F; + // TT is the same type as TTi? This is likely to break at some point + const Eigen::PlainObjectBase &TT; + const Eigen::PlainObjectBase &TTi; + const std::vector >& VF; + const std::vector >& VFi; + const std::vector &V_border; // bool + //edges to cut + const Eigen::PlainObjectBase &Handle_Seams; // 3 bool + + // total number of scalar variables + int num_scalar_variables; + + // per face indexes of vertex in the solver + DerivedF HandleS_Index; + + // per vertex variable indexes + std::vector > HandleV_Integer; + + IGL_INLINE MeshCutterMini( + const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const Eigen::PlainObjectBase &_TT, + const Eigen::PlainObjectBase &_TTi, + const std::vector > &_VF, + const std::vector > &_VFi, + const std::vector &_V_border, + const Eigen::PlainObjectBase &_Handle_Seams); + + // vertex to variable mapping + // initialize the mapping for a given sampled mesh + IGL_INLINE void InitMappingSeam(); + + private: + + IGL_INLINE void FirstPos(const int v, int &f, int &edge); + + IGL_INLINE int AddNewIndex(const int v0); + + IGL_INLINE bool IsSeam(const int f0, const int f1); + + // find initial position of the pos to + // assing face to vert inxex correctly + IGL_INLINE void FindInitialPos(const int vert, int &edge, int &face); + + + // initialize the mapping given an initial pos + // whih must be initialized with FindInitialPos + IGL_INLINE void MapIndexes(const int vert, const int edge_init, const int f_init); + + // initialize the mapping for a given vertex + IGL_INLINE void InitMappingSeam(const int vert); + + }; +} + + +template +IGL_INLINE igl::MeshCutterMini:: +MeshCutterMini( + const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const Eigen::PlainObjectBase &_TT, + const Eigen::PlainObjectBase &_TTi, + const std::vector > &_VF, + const std::vector > &_VFi, + const std::vector &_V_border, + const Eigen::PlainObjectBase &_Handle_Seams): + V(_V), + F(_F), + TT(_TT), + TTi(_TTi), + VF(_VF), + VFi(_VFi), + V_border(_V_border), + Handle_Seams(_Handle_Seams) +{ + num_scalar_variables=0; + HandleS_Index.setConstant(F.rows(),3,-1); + HandleV_Integer.resize(V.rows()); +} + + +template +IGL_INLINE void igl::MeshCutterMini:: +FirstPos(const int v, int &f, int &edge) +{ + f = VF[v][0]; // f=v->cVFp(); + edge = VFi[v][0]; // edge=v->cVFi(); +} + +template +IGL_INLINE int igl::MeshCutterMini:: +AddNewIndex(const int v0) +{ + num_scalar_variables++; + HandleV_Integer[v0].push_back(num_scalar_variables); + return num_scalar_variables; +} + +template +IGL_INLINE bool igl::MeshCutterMini:: +IsSeam(const int f0, const int f1) +{ + for (int i=0;i<3;i++) + { + int f_clos = TT(f0,i); + + if (f_clos == -1) + continue; ///border + + if (f_clos == f1) + return(Handle_Seams(f0,i)); + } + assert(0); + return false; +} + +///find initial position of the pos to +// assing face to vert inxex correctly +template +IGL_INLINE void igl::MeshCutterMini:: +FindInitialPos(const int vert, + int &edge, + int &face) +{ + int f_init; + int edge_init; + FirstPos(vert,f_init,edge_init); // todo manually the function + igl::HalfEdgeIterator VFI(F,TT,TTi,f_init,edge_init); + + bool vertexB = V_border[vert]; + bool possible_split=false; + bool complete_turn=false; + do + { + int curr_f = VFI.Fi(); + int curr_edge=VFI.Ei(); + VFI.NextFE(); + int next_f=VFI.Fi(); + ///test if I've just crossed a border + bool on_border=(TT(curr_f,curr_edge)==-1); + //bool mismatch=false; + bool seam=false; + + ///or if I've just crossed a seam + ///if I'm on a border I MUST start from the one next t othe border + if (!vertexB) + //seam=curr_f->IsSeam(next_f); + seam=IsSeam(curr_f,next_f); + // if (vertexB) + // assert(!Handle_Singular(vert)); + // ; + //assert(!vert->IsSingular()); + possible_split=((on_border)||(seam)); + complete_turn = next_f == f_init; + } while ((!possible_split)&&(!complete_turn)); + face=VFI.Fi(); + edge=VFI.Ei(); +} + + + +///initialize the mapping given an initial pos +///whih must be initialized with FindInitialPos +template +IGL_INLINE void igl::MeshCutterMini:: +MapIndexes(const int vert, + const int edge_init, + const int f_init) +{ + ///check that is not on border.. + ///in such case maybe it's non manyfold + ///insert an initial index + int curr_index=AddNewIndex(vert); + ///and initialize the jumping pos + igl::HalfEdgeIterator VFI(F,TT,TTi,f_init,edge_init); + bool complete_turn=false; + do + { + int curr_f = VFI.Fi(); + int curr_edge = VFI.Ei(); + ///assing the current index + HandleS_Index(curr_f,curr_edge) = curr_index; + VFI.NextFE(); + int next_f = VFI.Fi(); + ///test if I've finiseh with the face exploration + complete_turn = (next_f==f_init); + ///or if I've just crossed a mismatch + if (!complete_turn) + { + bool seam=false; + //seam=curr_f->IsSeam(next_f); + seam=IsSeam(curr_f,next_f); + if (seam) + { + ///then add a new index + curr_index=AddNewIndex(vert); + } + } + } while (!complete_turn); +} + +///initialize the mapping for a given vertex +template +IGL_INLINE void igl::MeshCutterMini:: +InitMappingSeam(const int vert) +{ + ///first rotate until find the first pos after a mismatch + ///or a border or return to the first position... + int f_init = VF[vert][0]; + int indexE = VFi[vert][0]; + + igl::HalfEdgeIterator VFI(F,TT,TTi,f_init,indexE); + + int edge_init; + int face_init; + FindInitialPos(vert,edge_init,face_init); + MapIndexes(vert,edge_init,face_init); +} + +///vertex to variable mapping +///initialize the mapping for a given sampled mesh +template +IGL_INLINE void igl::MeshCutterMini:: +InitMappingSeam() +{ + num_scalar_variables=-1; + for (unsigned int i=0;i0); +} + + +template +IGL_INLINE void igl::cut_mesh( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const std::vector >& VF, + const std::vector >& VFi, + const Eigen::PlainObjectBase& TT, + const Eigen::PlainObjectBase& TTi, + const std::vector &V_border, + const Eigen::PlainObjectBase &cuts, + Eigen::PlainObjectBase &Vcut, + Eigen::PlainObjectBase &Fcut) +{ + //finding the cuts is done, now we need to actually generate a cut mesh + igl::MeshCutterMini mc(V, F, TT, TTi, VF, VFi, V_border, cuts); + mc.InitMappingSeam(); + + Fcut = mc.HandleS_Index; + //we have the faces, we need the vertices; + int newNumV = Fcut.maxCoeff()+1; + Vcut.setZero(newNumV,3); + for (int vi=0; vi +IGL_INLINE void igl::cut_mesh( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &cuts, + Eigen::PlainObjectBase &Vcut, + Eigen::PlainObjectBase &Fcut) +{ + std::vector > VF, VFi; + igl::vertex_triangle_adjacency(V,F,VF,VFi); + // Alec: Cast? Why? This is likely to break. + Eigen::MatrixXd Vt = V; + Eigen::MatrixXi Ft = F; + Eigen::MatrixXi TT, TTi; + igl::triangle_triangle_adjacency(Ft,TT,TTi); + std::vector V_border = igl::is_border_vertex(V,F); + igl::cut_mesh(V, F, VF, VFi, TT, TTi, V_border, cuts, Vcut, Fcut); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::cut_mesh, Eigen::Matrix, int, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::cut_mesh, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::cut_mesh, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::cut_mesh, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/cut_mesh.h b/src/igl/cut_mesh.h new file mode 100644 index 0000000000..e49a3e38bb --- /dev/null +++ b/src/igl/cut_mesh.h @@ -0,0 +1,83 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CUT_MESH +#define IGL_CUT_MESH +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Given a mesh and a list of edges that are to be cut, the function + // generates a new disk-topology mesh that has the cuts at its boundary. + // + // Todo: this combinatorial operation should not depend on the vertex + // positions V. + // + // Known issues: Assumes mesh is edge-manifold. + // + // Inputs: + // V #V by 3 list of the vertex positions + // F #F by 3 list of the faces (must be triangles) + // VF #V list of lists of incident faces (adjacency list), e.g. as + // returned by igl::vertex_triangle_adjacency + // VFi #V list of lists of index of incidence within incident faces listed + // in VF, e.g. as returned by igl::vertex_triangle_adjacency + // TT #F by 3 triangle to triangle adjacent matrix (e.g. computed via + // igl:triangle_triangle_adjacency) + // TTi #F by 3 adjacent matrix, the element i,j is the id of edge of the + // triangle TT(i,j) that is adjacent with triangle i (e.g. computed via + // igl:triangle_triangle_adjacency) + // V_border #V by 1 list of booleans, indicating if the corresponging + // vertex is at the mesh boundary, e.g. as returned by + // igl::is_border_vertex + // cuts #F by 3 list of boolean flags, indicating the edges that need to + // be cut (has 1 at the face edges that are to be cut, 0 otherwise) + // Outputs: + // Vcut #V by 3 list of the vertex positions of the cut mesh. This matrix + // will be similar to the original vertices except some rows will be + // duplicated. + // Fcut #F by 3 list of the faces of the cut mesh(must be triangles). This + // matrix will be similar to the original face matrix except some indices + // will be redirected to point to the newly duplicated vertices. + // + template < + typename DerivedV, + typename DerivedF, + typename VFType, + typename DerivedTT, + typename DerivedC> + IGL_INLINE void cut_mesh( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const std::vector >& VF, + const std::vector >& VFi, + const Eigen::PlainObjectBase& TT, + const Eigen::PlainObjectBase& TTi, + const std::vector &V_border, + const Eigen::PlainObjectBase &cuts, + Eigen::PlainObjectBase &Vcut, + Eigen::PlainObjectBase &Fcut); + //Wrapper of the above with only vertices and faces as mesh input + template + IGL_INLINE void cut_mesh( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &cuts, + Eigen::PlainObjectBase &Vcut, + Eigen::PlainObjectBase &Fcut); +}; + + +#ifndef IGL_STATIC_LIBRARY +#include "cut_mesh.cpp" +#endif + + +#endif diff --git a/src/igl/cut_mesh_from_singularities.cpp b/src/igl/cut_mesh_from_singularities.cpp new file mode 100644 index 0000000000..382f2e063f --- /dev/null +++ b/src/igl/cut_mesh_from_singularities.cpp @@ -0,0 +1,205 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "cut_mesh_from_singularities.h" + +#include +#include + +#include +#include + + +namespace igl { + template < + typename DerivedV, + typename DerivedF, + typename DerivedM, + typename DerivedO + > + class MeshCutter + { + protected: + const Eigen::PlainObjectBase &V; + const Eigen::PlainObjectBase &F; + const Eigen::PlainObjectBase &Handle_MMatch; + + Eigen::VectorXi F_visited; + DerivedF TT; + DerivedF TTi; + + Eigen::MatrixXi E, F2E, E2F; + protected: + + inline bool IsRotSeam(const int f0,const int edge) + { + unsigned char MM = Handle_MMatch(f0,edge); + return (MM!=0); + } + + inline void FloodFill(const int start, Eigen::PlainObjectBase &Handle_Seams) + { + std::deque d; + ///clean the visited flag + F_visited(start) = true; + d.push_back(start); + + while (!d.empty()) + { + int f = d.at(0); d.pop_front(); + for (int s = 0; s<3; s++) + { + int g = TT(f,s); // f->FFp(s); + int j = TTi(f,s); // f->FFi(s); + + if (j == -1) + { + g = f; + j = s; + } + + if ((!(IsRotSeam(f,s))) && (!(IsRotSeam(g,j))) && (!F_visited(g)) ) + { + Handle_Seams(f,s)=false; + Handle_Seams(g,j)=false; + F_visited(g) = true; + d.push_back(g); + } + } + } + } + + inline void Retract(Eigen::PlainObjectBase &Handle_Seams) + { + std::vector e(V.rows(),0); // number of edges per vert + // for (unsigned f=0; fIsD()) + { + for (int s = 0; s<3; s++) + { + if (Handle_Seams(f,s)) + if (!(IsRotSeam(f,s))) // never retract rot seams + { + if (e[ F(f,s) ] == 1) { + // dissolve seam + Handle_Seams(f,s)=false; + if (TT(f,s) != -1) + Handle_Seams(TT(f,s),TTi(f,s))=false; + + e[ F(f,s)] --; + e[ F(f,(s+1)%3) ] --; + over = false; + } + } + } + } + + if (guard++>10000) + over = true; + + } while (!over); + } + + public: + + inline MeshCutter(const Eigen::PlainObjectBase &V_, + const Eigen::PlainObjectBase &F_, + const Eigen::PlainObjectBase &Handle_MMatch_): + V(V_), + F(F_), + Handle_MMatch(Handle_MMatch_) + { + triangle_triangle_adjacency(F,TT,TTi); + edge_topology(V,F,E,F2E,E2F); + }; + + inline void cut(Eigen::PlainObjectBase &Handle_Seams) + { + F_visited.setConstant(F.rows(),0); + Handle_Seams.setConstant(F.rows(),3,1); + + int index=0; + for (unsigned f = 0; f +IGL_INLINE void igl::cut_mesh_from_singularities(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &Handle_MMatch, + Eigen::PlainObjectBase &Handle_Seams) +{ + igl::MeshCutter< DerivedV, DerivedF, DerivedM, DerivedO> mc(V, F, Handle_MMatch); + mc.cut(Handle_Seams); + +} +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::cut_mesh_from_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::cut_mesh_from_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::cut_mesh_from_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::cut_mesh_from_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::cut_mesh_from_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::cut_mesh_from_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::cut_mesh_from_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/cut_mesh_from_singularities.h b/src/igl/cut_mesh_from_singularities.h new file mode 100644 index 0000000000..13b6a98c45 --- /dev/null +++ b/src/igl/cut_mesh_from_singularities.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_CUT_MESH_FROM_SINGULARITIES_H +#define IGL_CUT_MESH_FROM_SINGULARITIES_H +#include "igl_inline.h" +#include +namespace igl +{ + // Given a mesh (V,F) and the integer mismatch of a cross field per edge + // (MMatch), finds the cut_graph connecting the singularities (seams) and the + // degree of the singularities singularity_index + // + // Input: + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of faces + // MMatch #F by 3 list of per corner integer mismatch + // Outputs: + // seams #F by 3 list of per corner booleans that denotes if an edge is a + // seam or not + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedM, + typename DerivedO> + IGL_INLINE void cut_mesh_from_singularities( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &MMatch, + Eigen::PlainObjectBase &seams); +} +#ifndef IGL_STATIC_LIBRARY +#include "cut_mesh_from_singularities.cpp" +#endif + +#endif diff --git a/src/igl/cylinder.cpp b/src/igl/cylinder.cpp new file mode 100644 index 0000000000..ab0fd4d3df --- /dev/null +++ b/src/igl/cylinder.cpp @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "cylinder.h" +#include "PI.h" +#include +#include + +template +IGL_INLINE void igl::cylinder( + const int axis_devisions, + const int height_devisions, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F) +{ + V.resize(axis_devisions*height_devisions,3); + F.resize(2*(axis_devisions*(height_devisions-1)),3); + int f = 0; + typedef typename DerivedV::Scalar Scalar; + for(int th = 0;th 0) + { + F(f,0) = ((th+0)%axis_devisions)+(h-1)*axis_devisions; + F(f,1) = ((th+1)%axis_devisions)+(h-1)*axis_devisions; + F(f,2) = ((th+0)%axis_devisions)+(h+0)*axis_devisions; + f++; + F(f,0) = ((th+1)%axis_devisions)+(h-1)*axis_devisions; + F(f,1) = ((th+1)%axis_devisions)+(h+0)*axis_devisions; + F(f,2) = ((th+0)%axis_devisions)+(h+0)*axis_devisions; + f++; + } + } + } + assert(f == F.rows()); +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::cylinder, Eigen::Matrix >(int, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/cylinder.h b/src/igl/cylinder.h new file mode 100644 index 0000000000..4c5ab7e226 --- /dev/null +++ b/src/igl/cylinder.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_CYLINDER_H +#define IGL_CYLINDER_H +#include "igl_inline.h" +#include +namespace igl +{ + // Construct a triangle mesh of a cylinder (without caps) + // + // Inputs: + // axis_devisions number of vertices _around the cylinder_ + // height_devisions number of vertices _up the cylinder_ + // Outputs: + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of triangle indices into V + // + template + IGL_INLINE void cylinder( + const int axis_devisions, + const int height_devisions, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F); +} +#ifndef IGL_STATIC_LIBRARY +# include "cylinder.cpp" +#endif +#endif diff --git a/src/igl/dated_copy.cpp b/src/igl/dated_copy.cpp new file mode 100644 index 0000000000..735de6fc4c --- /dev/null +++ b/src/igl/dated_copy.cpp @@ -0,0 +1,91 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "dated_copy.h" +#include "dirname.h" +#include "basename.h" + +#include +#include +#include +#include +#include + +#if !defined(_WIN32) +#include + +IGL_INLINE bool igl::dated_copy(const std::string & src_path, const std::string & dir) +{ + using namespace std; + // Get time and date as string + char buffer[80]; + time_t rawtime; + struct tm * timeinfo; + time (&rawtime); + timeinfo = localtime (&rawtime); + // ISO 8601 format with hyphens instead of colons and no timezone offset + strftime (buffer,80,"%Y-%m-%dT%H-%M-%S",timeinfo); + string src_basename = basename(src_path); + string dst_basename = src_basename+"-"+buffer; + string dst_path = dir+"/"+dst_basename; + cerr<<"Saving binary to "< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +// Known issues: This function does not work under windows + +#ifndef IGL_DATED_COPY_H +#define IGL_DATED_COPY_H +#include "igl_inline.h" +#include +namespace igl +{ + // Copy the given file to a new file with the same basename in `dir` + // directory with the current date and time as a suffix. + // + // Inputs: + // src_path path to source file + // dir directory of destination file + // Example: + // dated_copy("/path/to/foo","/bar/"); + // // copies /path/to/foo to /bar/foo-2013-12-12T18-10-56 + IGL_INLINE bool dated_copy(const std::string & src_path, const std::string & dir); + // Wrapper using directory of source file + IGL_INLINE bool dated_copy(const std::string & src_path); +} +#ifndef IGL_STATIC_LIBRARY +# include "dated_copy.cpp" +#endif +#endif + diff --git a/src/igl/decimate.cpp b/src/igl/decimate.cpp new file mode 100644 index 0000000000..a531db2c56 --- /dev/null +++ b/src/igl/decimate.cpp @@ -0,0 +1,366 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "decimate.h" +#include "collapse_edge.h" +#include "edge_flaps.h" +#include "is_edge_manifold.h" +#include "remove_unreferenced.h" +#include "slice_mask.h" +#include "slice.h" +#include "connect_boundary_to_infinity.h" +#include "max_faces_stopping_condition.h" +#include "shortest_edge_and_midpoint.h" + +IGL_INLINE bool igl::decimate( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const size_t max_m, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J, + Eigen::VectorXi & I) +{ + // Original number of faces + const int orig_m = F.rows(); + // Tracking number of faces + int m = F.rows(); + typedef Eigen::MatrixXd DerivedV; + typedef Eigen::MatrixXi DerivedF; + DerivedV VO; + DerivedF FO; + igl::connect_boundary_to_infinity(V,F,VO,FO); + // decimate will not work correctly on non-edge-manifold meshes. By extension + // this includes meshes with non-manifold vertices on the boundary since these + // will create a non-manifold edge when connected to infinity. + if(!is_edge_manifold(FO)) + { + return false; + } + bool ret = decimate( + VO, + FO, + shortest_edge_and_midpoint, + max_faces_stopping_condition(m,orig_m,max_m), + U, + G, + J, + I); + const Eigen::Array keep = (J.array() & cost_and_placement, + const std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> & stopping_condition, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J, + Eigen::VectorXi & I + ) +{ + const auto always_try = []( + const Eigen::MatrixXd & ,/*V*/ + const Eigen::MatrixXi & ,/*F*/ + const Eigen::MatrixXi & ,/*E*/ + const Eigen::VectorXi & ,/*EMAP*/ + const Eigen::MatrixXi & ,/*EF*/ + const Eigen::MatrixXi & ,/*EI*/ + const std::set > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + ) -> bool { return true;}; + const auto never_care = []( + const Eigen::MatrixXd & , /*V*/ + const Eigen::MatrixXi & , /*F*/ + const Eigen::MatrixXi & , /*E*/ + const Eigen::VectorXi & ,/*EMAP*/ + const Eigen::MatrixXi & , /*EF*/ + const Eigen::MatrixXi & , /*EI*/ + const std::set > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )-> void { }; + return igl::decimate( + OV,OF,cost_and_placement,stopping_condition,always_try,never_care,U,G,J,I); +} + +IGL_INLINE bool igl::decimate( + const Eigen::MatrixXd & OV, + const Eigen::MatrixXi & OF, + const std::function & cost_and_placement, + const std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> & stopping_condition, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> & pre_collapse, + const std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> & post_collapse, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J, + Eigen::VectorXi & I + ) +{ + using namespace Eigen; + using namespace std; + VectorXi EMAP; + MatrixXi E,EF,EI; + edge_flaps(OF,E,EMAP,EF,EI); + return igl::decimate( + OV,OF, + cost_and_placement,stopping_condition,pre_collapse,post_collapse, + E,EMAP,EF,EI, + U,G,J,I); +} + + +IGL_INLINE bool igl::decimate( + const Eigen::MatrixXd & OV, + const Eigen::MatrixXi & OF, + const std::function & cost_and_placement, + const std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> & stopping_condition, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> & pre_collapse, + const std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> & post_collapse, + const Eigen::MatrixXi & OE, + const Eigen::VectorXi & OEMAP, + const Eigen::MatrixXi & OEF, + const Eigen::MatrixXi & OEI, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J, + Eigen::VectorXi & I + ) +{ + + // Decimate 1 + using namespace Eigen; + using namespace std; + // Working copies + Eigen::MatrixXd V = OV; + Eigen::MatrixXi F = OF; + Eigen::MatrixXi E = OE; + Eigen::VectorXi EMAP = OEMAP; + Eigen::MatrixXi EF = OEF; + Eigen::MatrixXi EI = OEI; + typedef std::set > PriorityQueue; + PriorityQueue Q; + std::vector Qit; + Qit.resize(E.rows()); + // If an edge were collapsed, we'd collapse it to these points: + MatrixXd C(E.rows(),V.cols()); + for(int e = 0;e(cost,e)).first; + } + int prev_e = -1; + bool clean_finish = false; + + while(true) + { + if(Q.empty()) + { + break; + } + if(Q.begin()->first == std::numeric_limits::infinity()) + { + // min cost edge is infinite cost + break; + } + int e,e1,e2,f1,f2; + if(collapse_edge( + cost_and_placement, pre_collapse, post_collapse, + V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2)) + { + if(stopping_condition(V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2)) + { + clean_finish = true; + break; + } + }else + { + if(prev_e == e) + { + assert(false && "Edge collapse no progress... bad stopping condition?"); + break; + } + // Edge was not collapsed... must have been invalid. collapse_edge should + // have updated its cost to inf... continue + } + prev_e = e; + } + // remove all IGL_COLLAPSE_EDGE_NULL faces + MatrixXi F2(F.rows(),3); + J.resize(F.rows()); + int m = 0; + for(int f = 0;f +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DECIMATE_H +#define IGL_DECIMATE_H +#include "igl_inline.h" +#include +#include +#include +namespace igl +{ + // Assumes (V,F) is a manifold mesh (possibly with boundary) Collapses edges + // until desired number of faces is achieved. This uses default edge cost and + // merged vertex placement functions {edge length, edge midpoint}. + // + // Inputs: + // V #V by dim list of vertex positions + // F #F by 3 list of face indices into V. + // max_m desired number of output faces + // Outputs: + // U #U by dim list of output vertex posistions (can be same ref as V) + // G #G by 3 list of output face indices into U (can be same ref as G) + // J #G list of indices into F of birth face + // I #U list of indices into V of birth vertices + // Returns true if m was reached (otherwise #G > m) + IGL_INLINE bool decimate( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const size_t max_m, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J, + Eigen::VectorXi & I); + // Inputs: + // V #V by dim list of vertex positions + // F #F by 3 list of face indices into V. + // max_m desired number of output faces + // Outputs: + // U #U by dim list of output vertex posistions (can be same ref as V) + // G #G by 3 list of output face indices into U (can be same ref as G) + // J #G list of indices into F of birth face + // Returns true if m was reached (otherwise #G > m) + IGL_INLINE bool decimate( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const size_t max_m, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J); + // Assumes a **closed** manifold mesh. See igl::connect_boundary_to_infinity + // and igl::decimate in decimate.cpp + // is handling meshes with boundary by connecting all boundary edges with + // dummy facets to infinity **and** modifying the stopping criteria. + // + // Inputs: + // cost_and_placement function computing cost of collapsing an edge and 3d + // position where it should be placed: + // cost_and_placement(V,F,E,EMAP,EF,EI,cost,placement); + // stopping_condition function returning whether to stop collapsing edges + // based on current state. Guaranteed to be called after _successfully_ + // collapsing edge e removing edges (e,e1,e2) and faces (f1,f2): + // bool should_stop = + // stopping_condition(V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2); + IGL_INLINE bool decimate( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const std::function & cost_and_placement, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int ,/*e*/ + const int ,/*e1*/ + const int ,/*e2*/ + const int ,/*f1*/ + const int /*f2*/ + )> & stopping_condition, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J, + Eigen::VectorXi & I); + + // Inputs: + // pre_collapse callback called with index of edge whose collapse is about + // to be attempted (see collapse_edge) + // post_collapse callback called with index of edge whose collapse was + // just attempted and a flag revealing whether this was successful (see + // collapse_edge) + IGL_INLINE bool decimate( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const std::function & cost_and_placement, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int ,/*e*/ + const int ,/*e1*/ + const int ,/*e2*/ + const int ,/*f1*/ + const int /*f2*/ + )> & stopping_condition, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> & pre_collapse, + const std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> & post_collapse, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J, + Eigen::VectorXi & I); + + // Inputs: + // EMAP #F*3 list of indices into E, mapping each directed edge to unique + // unique edge in E + // EF #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of + // F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) " + // e=(j->i) + // EI #E by 2 list of edge flap corners (see above). + + IGL_INLINE bool decimate( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const std::function & cost_and_placement, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int ,/*e*/ + const int ,/*e1*/ + const int ,/*e2*/ + const int ,/*f1*/ + const int /*f2*/ + )> & stopping_condition, + const std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> & pre_collapse, + const std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> & post_collapse, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J, + Eigen::VectorXi & I); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "decimate.cpp" +#endif +#endif + diff --git a/src/igl/deform_skeleton.cpp b/src/igl/deform_skeleton.cpp new file mode 100644 index 0000000000..b9d120edd0 --- /dev/null +++ b/src/igl/deform_skeleton.cpp @@ -0,0 +1,58 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "deform_skeleton.h" +void igl::deform_skeleton( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const std::vector< + Eigen::Affine3d,Eigen::aligned_allocator > & vA, + Eigen::MatrixXd & CT, + Eigen::MatrixXi & BET) +{ + using namespace Eigen; + assert(BE.rows() == (int)vA.size()); + CT.resize(2*BE.rows(),C.cols()); + BET.resize(BE.rows(),2); + for(int e = 0;e +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DEFORM_SKELETON_H +#define IGL_DEFORM_SKELETON_H +#include "igl_inline.h" +#include +#include +#include +#include +namespace igl +{ + // Deform a skeleton. + // + // Inputs: + // C #C by 3 list of joint positions + // BE #BE by 2 list of bone edge indices + // vA #BE list of bone transformations + // Outputs + // CT #BE*2 by 3 list of deformed joint positions + // BET #BE by 2 list of bone edge indices (maintains order) + // + IGL_INLINE void deform_skeleton( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const std::vector< + Eigen::Affine3d,Eigen::aligned_allocator > & vA, + Eigen::MatrixXd & CT, + Eigen::MatrixXi & BET); + // Inputs: + // T #BE*4 by 3 list of stacked transformation matrix + IGL_INLINE void deform_skeleton( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const Eigen::MatrixXd & T, + Eigen::MatrixXd & CT, + Eigen::MatrixXi & BET); +} + +#ifndef IGL_STATIC_LIBRARY +# include "deform_skeleton.cpp" +#endif +#endif diff --git a/src/igl/delaunay_triangulation.cpp b/src/igl/delaunay_triangulation.cpp new file mode 100644 index 0000000000..01669966b6 --- /dev/null +++ b/src/igl/delaunay_triangulation.cpp @@ -0,0 +1,86 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "delaunay_triangulation.h" +#include "flip_edge.h" +#include "lexicographic_triangulation.h" +#include "unique_edge_map.h" + +#include +#include + +template< + typename DerivedV, + typename Orient2D, + typename InCircle, + typename DerivedF> +IGL_INLINE void igl::delaunay_triangulation( + const Eigen::PlainObjectBase& V, + Orient2D orient2D, + InCircle incircle, + Eigen::PlainObjectBase& F) +{ + assert(V.cols() == 2); + typedef typename DerivedF::Scalar Index; + typedef typename DerivedV::Scalar Scalar; + igl::lexicographic_triangulation(V, orient2D, F); + const size_t num_faces = F.rows(); + if (num_faces == 0) { + // Input points are degenerate. No faces will be generated. + return; + } + assert(F.cols() == 3); + + Eigen::MatrixXi E; + Eigen::MatrixXi uE; + Eigen::VectorXi EMAP; + std::vector > uE2E; + igl::unique_edge_map(F, E, uE, EMAP, uE2E); + + auto is_delaunay = [&V,&F,&uE2E,num_faces,&incircle](size_t uei) { + auto& half_edges = uE2E[uei]; + if (half_edges.size() != 2) { + throw "Cannot flip non-manifold or boundary edge"; + } + + const size_t f1 = half_edges[0] % num_faces; + const size_t f2 = half_edges[1] % num_faces; + const size_t c1 = half_edges[0] / num_faces; + const size_t c2 = half_edges[1] / num_faces; + assert(c1 < 3); + assert(c2 < 3); + assert(f1 != f2); + const size_t v1 = F(f1, (c1+1)%3); + const size_t v2 = F(f1, (c1+2)%3); + const size_t v4 = F(f1, c1); + const size_t v3 = F(f2, c2); + const Scalar p1[] = {V(v1, 0), V(v1, 1)}; + const Scalar p2[] = {V(v2, 0), V(v2, 1)}; + const Scalar p3[] = {V(v3, 0), V(v3, 1)}; + const Scalar p4[] = {V(v4, 0), V(v4, 1)}; + auto orientation = incircle(p1, p2, p4, p3); + return orientation <= 0; + }; + + bool all_delaunay = false; + while(!all_delaunay) { + all_delaunay = true; + for (size_t i=0; i, short (*)(double const*, double const*, double const*), short (*)(double const*, double const*, double const*, double const*), Eigen::Matrix >(Eigen::PlainObjectBase > const&, short (*)(double const*, double const*, double const*), short (*)(double const*, double const*, double const*, double const*), Eigen::PlainObjectBase >&); +#endif \ No newline at end of file diff --git a/src/igl/delaunay_triangulation.h b/src/igl/delaunay_triangulation.h new file mode 100644 index 0000000000..daeec9fadd --- /dev/null +++ b/src/igl/delaunay_triangulation.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_DELAUNAY_TRIANGULATION_H +#define IGL_DELAUNAY_TRIANGULATION_H + +#include "igl_inline.h" +#include + +namespace igl +{ + // Given a set of points in 2D, return a Delaunay triangulation of these + // points. + // + // Inputs: + // V #V by 2 list of vertex positions + // orient2D A functor such that orient2D(pa, pb, pc) returns + // 1 if pa,pb,pc forms a conterclockwise triangle. + // -1 if pa,pb,pc forms a clockwise triangle. + // 0 if pa,pb,pc are collinear. + // where the argument pa,pb,pc are of type Scalar[2]. + // incircle A functor such that incircle(pa, pb, pc, pd) returns + // 1 if pd is on the positive size of circumcirle of (pa,pb,pc) + // -1 if pd is on the positive size of circumcirle of (pa,pb,pc) + // 0 if pd is cocircular with pa, pb, pc. + // Outputs: + // F #F by 3 of faces in Delaunay triangulation. + template< + typename DerivedV, + typename Orient2D, + typename InCircle, + typename DerivedF + > + IGL_INLINE void delaunay_triangulation( + const Eigen::PlainObjectBase& V, + Orient2D orient2D, + InCircle incircle, + Eigen::PlainObjectBase& F); +} + + + + +#ifndef IGL_STATIC_LIBRARY +# include "delaunay_triangulation.cpp" +#endif +#endif diff --git a/src/igl/deprecated.h b/src/igl/deprecated.h new file mode 100644 index 0000000000..52c72ba799 --- /dev/null +++ b/src/igl/deprecated.h @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DEPRECATED_H +#define IGL_DEPRECATED_H +// Macro for marking a function as deprecated. +// +// http://stackoverflow.com/a/295229/148668 +#ifdef __GNUC__ +#define IGL_DEPRECATED(func) func __attribute__ ((deprecated)) +#elif defined(_MSC_VER) +#define IGL_DEPRECATED(func) __declspec(deprecated) func +#else +#pragma message("WARNING: You need to implement IGL_DEPRECATED for this compiler") +#define IGL_DEPRECATED(func) func +#endif +// Usage: +// +// template IGL_INLINE void my_func(Arg1 a); +// +// becomes +// +// template IGL_INLINE IGL_DEPRECATED(void my_func(Arg1 a)); +#endif diff --git a/src/igl/dfs.cpp b/src/igl/dfs.cpp new file mode 100644 index 0000000000..627fbc26df --- /dev/null +++ b/src/igl/dfs.cpp @@ -0,0 +1,60 @@ +#include "dfs.h" +#include "list_to_matrix.h" +#include + +template < + typename AType, + typename DerivedD, + typename DerivedP, + typename DerivedC> +IGL_INLINE void igl::dfs( + const std::vector > & A, + const size_t s, + Eigen::PlainObjectBase & D, + Eigen::PlainObjectBase & P, + Eigen::PlainObjectBase & C) +{ + std::vector vD; + std::vector vP; + std::vector vC; + dfs(A,s,vD,vP,vC); + list_to_matrix(vD,D); + list_to_matrix(vP,P); + list_to_matrix(vC,C); +} + +template < + typename AType, + typename DType, + typename PType, + typename CType> +IGL_INLINE void igl::dfs( + const std::vector > & A, + const size_t s, + std::vector & D, + std::vector & P, + std::vector & C) +{ + // number of nodes + int N = s+1; + for(const auto & Ai : A) for(const auto & a : Ai) N = std::max(N,a+1); + std::vector seen(N,false); + P.resize(N,-1); + std::function dfs_helper; + dfs_helper = [&D,&P,&C,&dfs_helper,&seen,&A](const size_t s, const size_t p) + { + if(seen[s]) return; + seen[s] = true; + D.push_back(s); + P[s] = p; + for(const auto n : A[s]) dfs_helper(n,s); + C.push_back(s); + }; + dfs_helper(s,-1); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::dfs, Eigen::Matrix, Eigen::Matrix >(std::vector >, std::allocator > > > const&, const size_t, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/dfs.h b/src/igl/dfs.h new file mode 100644 index 0000000000..5f3bf2ce63 --- /dev/null +++ b/src/igl/dfs.h @@ -0,0 +1,49 @@ +#ifndef IGL_DFS_H +#define IGL_DFS_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Traverse a **directed** graph represented by an adjacency list using + // depth first search + // + // Inputs: + // A #V list of adjacency lists + // s starting node (index into A) + // Outputs: + // D #V list of indices into rows of A in the order in which graph nodes + // are discovered. + // P #V list of indices into rows of A of predecessor in resulting + // spanning tree {-1 indicates root/not discovered), order corresponds to + // V **not** D. + // C #V list of indices into rows of A in order that nodes are "closed" + // (all descendants have been discovered) + template < + typename AType, + typename DerivedD, + typename DerivedP, + typename DerivedC> + IGL_INLINE void dfs( + const std::vector > & A, + const size_t s, + Eigen::PlainObjectBase & D, + Eigen::PlainObjectBase & P, + Eigen::PlainObjectBase & C); + template < + typename AType, + typename DType, + typename PType, + typename CType> + IGL_INLINE void dfs( + const std::vector > & A, + const size_t s, + std::vector & D, + std::vector & P, + std::vector & C); + +} +#ifndef IGL_STATIC_LIBRARY +# include "dfs.cpp" +#endif +#endif diff --git a/src/igl/diag.cpp b/src/igl/diag.cpp new file mode 100644 index 0000000000..d29382695e --- /dev/null +++ b/src/igl/diag.cpp @@ -0,0 +1,108 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "diag.h" + +#include "verbose.h" + +// Bug in unsupported/Eigen/SparseExtra needs iostream first +#include +#include + +template +IGL_INLINE void igl::diag( + const Eigen::SparseMatrix& X, + Eigen::SparseVector& V) +{ + assert(false && "Just call X.diagonal().sparseView() directly"); + V = X.diagonal().sparseView(); + //// Get size of input + //int m = X.rows(); + //int n = X.cols(); + //V = Eigen::SparseVector((m>n?n:m)); + //V.reserve(V.size()); + + //// Iterate over outside + //for(int k=0; k::InnerIterator it (X,k); it; ++it) + // { + // if(it.col() == it.row()) + // { + // V.coeffRef(it.col()) += it.value(); + // } + // } + //} +} + +template +IGL_INLINE void igl::diag( + const Eigen::SparseMatrix& X, + Eigen::MatrixBase & V) +{ + assert(false && "Just call X.diagonal() directly"); + V = X.diagonal(); + //// Get size of input + //int m = X.rows(); + //int n = X.cols(); + //V.derived().resize((m>n?n:m),1); + + //// Iterate over outside + //for(int k=0; k::InnerIterator it (X,k); it; ++it) + // { + // if(it.col() == it.row()) + // { + // V(it.col()) = it.value(); + // } + // } + //} +} + +template +IGL_INLINE void igl::diag( + const Eigen::SparseVector& V, + Eigen::SparseMatrix& X) +{ + // clear and resize output + Eigen::DynamicSparseMatrix dyn_X(V.size(),V.size()); + dyn_X.reserve(V.size()); + // loop over non-zeros + for(typename Eigen::SparseVector::InnerIterator it(V); it; ++it) + { + dyn_X.coeffRef(it.index(),it.index()) += it.value(); + } + X = Eigen::SparseMatrix(dyn_X); +} + +template +IGL_INLINE void igl::diag( + const Eigen::MatrixBase & V, + Eigen::SparseMatrix& X) +{ + assert(V.rows() == 1 || V.cols() == 1); + // clear and resize output + Eigen::DynamicSparseMatrix dyn_X(V.size(),V.size()); + dyn_X.reserve(V.size()); + // loop over non-zeros + for(int i = 0;i(dyn_X); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::diag >(Eigen::SparseMatrix const&, Eigen::MatrixBase >&); +template void igl::diag(Eigen::SparseMatrix const&, Eigen::SparseVector&); +template void igl::diag >(Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +template void igl::diag(Eigen::SparseVector const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/diag.h b/src/igl/diag.h new file mode 100644 index 0000000000..8001f01b63 --- /dev/null +++ b/src/igl/diag.h @@ -0,0 +1,62 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DIAG_H +#define IGL_DIAG_H +#include "igl_inline.h" +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include + +namespace igl +{ + // http://forum.kde.org/viewtopic.php?f=74&t=117476&p=292388#p292388 + // + // This is superceded by + // VectorXd V = X.diagonal() and + // SparseVector V = X.diagonal().sparseView() + // SparseMatrix X = V.asDiagonal().sparseView() + // + // + // Either extracts the main diagonal of a matrix as a vector OR converts a + // vector into a matrix with vector along the main diagonal. Like matlab's + // diag function + + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Inputs: + // X an m by n sparse matrix + // Outputs: + // V a min(m,n) sparse vector + template + IGL_INLINE void diag( + const Eigen::SparseMatrix& X, + Eigen::SparseVector& V); + template + IGL_INLINE void diag( + const Eigen::SparseMatrix& X, + Eigen::MatrixBase& V); + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Inputs: + // V a m sparse vector + // Outputs: + // X a m by m sparse matrix + template + IGL_INLINE void diag( + const Eigen::SparseVector& V, + Eigen::SparseMatrix& X); + template + IGL_INLINE void diag( + const Eigen::MatrixBase& V, + Eigen::SparseMatrix& X); +} + +#ifndef IGL_STATIC_LIBRARY +# include "diag.cpp" +#endif + +#endif diff --git a/src/igl/dihedral_angles.cpp b/src/igl/dihedral_angles.cpp new file mode 100644 index 0000000000..a04e6f840d --- /dev/null +++ b/src/igl/dihedral_angles.cpp @@ -0,0 +1,94 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "dihedral_angles.h" +#include + +template < + typename DerivedV, + typename DerivedT, + typename Derivedtheta, + typename Derivedcos_theta> +IGL_INLINE void igl::dihedral_angles( + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& T, + Eigen::PlainObjectBase& theta, + Eigen::PlainObjectBase& cos_theta) +{ + using namespace Eigen; + assert(T.cols() == 4); + Matrix l; + edge_lengths(V,T,l); + Matrix s; + face_areas(l,s); + return dihedral_angles_intrinsic(l,s,theta,cos_theta); +} + +template < + typename DerivedL, + typename DerivedA, + typename Derivedtheta, + typename Derivedcos_theta> +IGL_INLINE void igl::dihedral_angles_intrinsic( + Eigen::PlainObjectBase& L, + Eigen::PlainObjectBase& A, + Eigen::PlainObjectBase& theta, + Eigen::PlainObjectBase& cos_theta) +{ + using namespace Eigen; + const int m = L.rows(); + assert(m == A.rows()); + // Law of cosines + // http://math.stackexchange.com/a/49340/35376 + Matrix H_sqr(m,6); + H_sqr.col(0) = (1./16.) * (4. * L.col(3).array().square() * L.col(0).array().square() - + ((L.col(1).array().square() + L.col(4).array().square()) - + (L.col(2).array().square() + L.col(5).array().square())).square()); + H_sqr.col(1) = (1./16.) * (4. * L.col(4).array().square() * L.col(1).array().square() - + ((L.col(2).array().square() + L.col(5).array().square()) - + (L.col(3).array().square() + L.col(0).array().square())).square()); + H_sqr.col(2) = (1./16.) * (4. * L.col(5).array().square() * L.col(2).array().square() - + ((L.col(3).array().square() + L.col(0).array().square()) - + (L.col(4).array().square() + L.col(1).array().square())).square()); + H_sqr.col(3) = (1./16.) * (4. * L.col(0).array().square() * L.col(3).array().square() - + ((L.col(4).array().square() + L.col(1).array().square()) - + (L.col(5).array().square() + L.col(2).array().square())).square()); + H_sqr.col(4) = (1./16.) * (4. * L.col(1).array().square() * L.col(4).array().square() - + ((L.col(5).array().square() + L.col(2).array().square()) - + (L.col(0).array().square() + L.col(3).array().square())).square()); + H_sqr.col(5) = (1./16.) * (4. * L.col(2).array().square() * L.col(5).array().square() - + ((L.col(0).array().square() + L.col(3).array().square()) - + (L.col(1).array().square() + L.col(4).array().square())).square()); + cos_theta.resize(m,6); + cos_theta.col(0) = (H_sqr.col(0).array() - + A.col(1).array().square() - A.col(2).array().square()).array() / + (-2.*A.col(1).array() * A.col(2).array()); + cos_theta.col(1) = (H_sqr.col(1).array() - + A.col(2).array().square() - A.col(0).array().square()).array() / + (-2.*A.col(2).array() * A.col(0).array()); + cos_theta.col(2) = (H_sqr.col(2).array() - + A.col(0).array().square() - A.col(1).array().square()).array() / + (-2.*A.col(0).array() * A.col(1).array()); + cos_theta.col(3) = (H_sqr.col(3).array() - + A.col(3).array().square() - A.col(0).array().square()).array() / + (-2.*A.col(3).array() * A.col(0).array()); + cos_theta.col(4) = (H_sqr.col(4).array() - + A.col(3).array().square() - A.col(1).array().square()).array() / + (-2.*A.col(3).array() * A.col(1).array()); + cos_theta.col(5) = (H_sqr.col(5).array() - + A.col(3).array().square() - A.col(2).array().square()).array() / + (-2.*A.col(3).array() * A.col(2).array()); + + theta = cos_theta.array().acos(); + + cos_theta.resize(m,6); + +} +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::dihedral_angles_intrinsic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/dihedral_angles.h b/src/igl/dihedral_angles.h new file mode 100644 index 0000000000..92ac0f8625 --- /dev/null +++ b/src/igl/dihedral_angles.h @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DIHEDRAL_ANGLES_H +#define IGL_DIHEDRAL_ANGLES_H +#include "igl_inline.h" +#include +namespace igl +{ + // DIHEDRAL_ANGLES Compute dihedral angles for all tets of a given tet mesh + // (V,T) + // + // theta = dihedral_angles(V,T) + // theta = dihedral_angles(V,T,'ParameterName',parameter_value,...) + // + // Inputs: + // V #V by dim list of vertex positions + // T #V by 4 list of tet indices + // Outputs: + // theta #T by 6 list of dihedral angles (in radians) + // cos_theta #T by 6 list of cosine of dihedral angles (in radians) + // + template < + typename DerivedV, + typename DerivedT, + typename Derivedtheta, + typename Derivedcos_theta> + IGL_INLINE void dihedral_angles( + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& T, + Eigen::PlainObjectBase& theta, + Eigen::PlainObjectBase& cos_theta); + template < + typename DerivedL, + typename DerivedA, + typename Derivedtheta, + typename Derivedcos_theta> + IGL_INLINE void dihedral_angles_intrinsic( + Eigen::PlainObjectBase& L, + Eigen::PlainObjectBase& A, + Eigen::PlainObjectBase& theta, + Eigen::PlainObjectBase& cos_theta); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "dihedral_angles.cpp" +#endif + +#endif + diff --git a/src/igl/dijkstra.cpp b/src/igl/dijkstra.cpp new file mode 100644 index 0000000000..ad92f1d7f5 --- /dev/null +++ b/src/igl/dijkstra.cpp @@ -0,0 +1,71 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include + +template +IGL_INLINE int igl::dijkstra_compute_paths(const IndexType &source, + const std::set &targets, + const std::vector >& VV, + Eigen::PlainObjectBase &min_distance, + Eigen::PlainObjectBase &previous) +{ + int numV = VV.size(); + min_distance.setConstant(numV, 1, std::numeric_limits::infinity()); + min_distance[source] = 0; + previous.setConstant(numV, 1, -1); + std::set > vertex_queue; + vertex_queue.insert(std::make_pair(min_distance[source], source)); + + while (!vertex_queue.empty()) + { + typename DerivedD::Scalar dist = vertex_queue.begin()->first; + IndexType u = vertex_queue.begin()->second; + vertex_queue.erase(vertex_queue.begin()); + + if (targets.find(u)!= targets.end()) + return u; + + // Visit each edge exiting u + const std::vector &neighbors = VV[u]; + for (std::vector::const_iterator neighbor_iter = neighbors.begin(); + neighbor_iter != neighbors.end(); + neighbor_iter++) + { + IndexType v = *neighbor_iter; + typename DerivedD::Scalar distance_through_u = dist + 1.; + if (distance_through_u < min_distance[v]) { + vertex_queue.erase(std::make_pair(min_distance[v], v)); + + min_distance[v] = distance_through_u; + previous[v] = u; + vertex_queue.insert(std::make_pair(min_distance[v], v)); + + } + + } + } + //we should never get here + return -1; +} + +template +IGL_INLINE void igl::dijkstra_get_shortest_path_to(const IndexType &vertex, + const Eigen::PlainObjectBase &previous, + std::vector &path) +{ + IndexType source = vertex; + path.clear(); + for ( ; source != -1; source = previous[source]) + path.push_back(source); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template int igl::dijkstra_compute_paths, Eigen::Matrix >(int const&, std::set, std::allocator > const&, std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::dijkstra_get_shortest_path_to >(int const&, Eigen::PlainObjectBase > const&, std::vector >&); +#endif diff --git a/src/igl/dijkstra.h b/src/igl/dijkstra.h new file mode 100644 index 0000000000..33af160bb8 --- /dev/null +++ b/src/igl/dijkstra.h @@ -0,0 +1,60 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_DIJKSTRA +#define IGL_DIJKSTRA +#include "igl_inline.h" + +#include +#include +#include + +namespace igl { + + // Dijstra's algorithm for shortest paths, with multiple targets. + // Adapted from http://rosettacode.org/wiki/Dijkstra%27s_algorithm . + // + // Inputs: + // source index of source vertex + // targets target vector set + // VV #V list of lists of incident vertices (adjacency list), e.g. + // as returned by igl::adjacency_list + // + // Output: + // min_distance #V by 1 list of the minimum distances from source to all vertices + // previous #V by 1 list of the previous visited vertices (for each vertex) - used for backtracking + // + template + IGL_INLINE int dijkstra_compute_paths(const IndexType &source, + const std::set &targets, + const std::vector >& VV, + Eigen::PlainObjectBase &min_distance, + Eigen::PlainObjectBase &previous); + + // Backtracking after Dijstra's algorithm, to find shortest path. + // + // Inputs: + // vertex vertex to which we want the shortest path (from same source as above) + // previous #V by 1 list of the previous visited vertices (for each vertex) - result of Dijkstra's algorithm + // + // Output: + // path #P by 1 list of vertex indices in the shortest path from source to vertex + // + template + IGL_INLINE void dijkstra_get_shortest_path_to(const IndexType &vertex, + const Eigen::PlainObjectBase &previous, + std::vector &path); +}; + + +#ifndef IGL_STATIC_LIBRARY +#include "dijkstra.cpp" +#endif + + +#endif /* defined(IGL_DIJKSTRA) */ diff --git a/src/igl/directed_edge_orientations.cpp b/src/igl/directed_edge_orientations.cpp new file mode 100644 index 0000000000..a3308bc987 --- /dev/null +++ b/src/igl/directed_edge_orientations.cpp @@ -0,0 +1,29 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "directed_edge_orientations.h" + +template +IGL_INLINE void igl::directed_edge_orientations( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & E, + std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & Q) +{ + using namespace Eigen; + Q.resize(E.rows()); + for(int e = 0;e, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector, Eigen::aligned_allocator > >&); +#endif diff --git a/src/igl/directed_edge_orientations.h b/src/igl/directed_edge_orientations.h new file mode 100644 index 0000000000..b6f0471477 --- /dev/null +++ b/src/igl/directed_edge_orientations.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DIRECTED_EDGE_ORIENTATIONS_H +#define IGL_DIRECTED_EDGE_ORIENTATIONS_H +#include "igl_inline.h" + +#include +#include +#include +#include + +namespace igl +{ + // Determine rotations that take each edge from the x-axis to its given rest + // orientation. + // + // Inputs: + // C #C by 3 list of edge vertex positions + // E #E by 2 list of directed edges + // Outputs: + // Q #E list of quaternions + // + template + IGL_INLINE void directed_edge_orientations( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & E, + std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & Q); +} + +#ifndef IGL_STATIC_LIBRARY +# include "directed_edge_orientations.cpp" +#endif +#endif diff --git a/src/igl/directed_edge_parents.cpp b/src/igl/directed_edge_parents.cpp new file mode 100644 index 0000000000..52e6a41a47 --- /dev/null +++ b/src/igl/directed_edge_parents.cpp @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "directed_edge_parents.h" +#include "slice_into.h" +#include "slice.h" +#include "colon.h" +#include "setdiff.h" +#include + +template +IGL_INLINE void igl::directed_edge_parents( + const Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & P) +{ + using namespace Eigen; + using namespace std; + VectorXi I = VectorXi::Constant(E.maxCoeff()+1,1,-1); + //I(E.col(1)) = 0:E.rows()-1 + slice_into(colon(0,E.rows()-1),E.col(1).eval(),I); + VectorXi roots,_; + setdiff(E.col(0).eval(),E.col(1).eval(),roots,_); + std::for_each(roots.data(),roots.data()+roots.size(),[&](int r){I(r)=-1;}); + slice(I,E.col(0).eval(),P); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::directed_edge_parents, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/directed_edge_parents.h b/src/igl/directed_edge_parents.h new file mode 100644 index 0000000000..bf42ab8f8b --- /dev/null +++ b/src/igl/directed_edge_parents.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DIRECTED_EDGE_PARENTS_H +#define IGL_DIRECTED_EDGE_PARENTS_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Recover "parents" (preceding edges) in a tree given just directed edges. + // + // Inputs: + // E #E by 2 list of directed edges + // Outputs: + // P #E list of parent indices into E (-1) means root + // + template + IGL_INLINE void directed_edge_parents( + const Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & P); +} + +#ifndef IGL_STATIC_LIBRARY +# include "directed_edge_parents.cpp" +#endif +#endif diff --git a/src/igl/dirname.cpp b/src/igl/dirname.cpp new file mode 100644 index 0000000000..5dc49733a7 --- /dev/null +++ b/src/igl/dirname.cpp @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "dirname.h" + +#include +#include "verbose.h" + +IGL_INLINE std::string igl::dirname(const std::string & path) +{ + if(path == "") + { + return std::string(""); + } +#if defined (WIN32) + char del('\\'); +#else + char del('/'); +#endif + // http://stackoverflow.com/questions/5077693/dirnamephp-similar-function-in-c + std::string::const_reverse_iterator last_slash = + std::find( + path.rbegin(), + path.rend(),del); + if( last_slash == path.rend() ) + { + // No slashes found + return std::string("."); + }else if(1 == (last_slash.base() - path.begin())) + { + // Slash is first char + return std::string(&del); + }else if(path.end() == last_slash.base() ) + { + // Slash is last char + std::string redo = std::string(path.begin(),path.end()-1); + return igl::dirname(redo); + } + return std::string(path.begin(),last_slash.base()-1); +} + + diff --git a/src/igl/dirname.h b/src/igl/dirname.h new file mode 100644 index 0000000000..bf5a34a204 --- /dev/null +++ b/src/igl/dirname.h @@ -0,0 +1,29 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DIRNAME_H +#define IGL_DIRNAME_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Function like PHP's dirname: /etc/passwd --> /etc, + // Input: + // path string containing input path + // Returns string containing dirname (see php's dirname) + // + // See also: basename, pathinfo + IGL_INLINE std::string dirname(const std::string & path); +} + +#ifndef IGL_STATIC_LIBRARY +# include "dirname.cpp" +#endif + +#endif diff --git a/src/igl/dot.cpp b/src/igl/dot.cpp new file mode 100644 index 0000000000..38f191546a --- /dev/null +++ b/src/igl/dot.cpp @@ -0,0 +1,16 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "dot.h" + +// http://www.antisphere.com/Wiki/tools:anttweakbar +IGL_INLINE double igl::dot( + const double *a, + const double *b) +{ + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; +} diff --git a/src/igl/dot.h b/src/igl/dot.h new file mode 100644 index 0000000000..9494eac7b6 --- /dev/null +++ b/src/igl/dot.h @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DOT_H +#define IGL_DOT_H +#include "igl_inline.h" +namespace igl +{ + // Computes out = dot(a,b) + // Inputs: + // a left 3d vector + // b right 3d vector + // Returns scalar dot product + IGL_INLINE double dot( + const double *a, + const double *b); +} + +#ifndef IGL_STATIC_LIBRARY +# include "dot.cpp" +#endif + +#endif diff --git a/src/igl/dot_row.cpp b/src/igl/dot_row.cpp new file mode 100644 index 0000000000..6f6d57e06a --- /dev/null +++ b/src/igl/dot_row.cpp @@ -0,0 +1,26 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "igl/dot_row.h" + +template +IGL_INLINE DerivedV igl::dot_row( + const Eigen::PlainObjectBase& A, + const Eigen::PlainObjectBase& B + ) +{ + assert(A.rows() == B.rows()); + assert(A.cols() == B.cols()); + + return (A.array() * B.array()).rowwise().sum(); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template Eigen::Matrix igl::dot_row >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +#endif diff --git a/src/igl/dot_row.h b/src/igl/dot_row.h new file mode 100644 index 0000000000..83d91aada1 --- /dev/null +++ b/src/igl/dot_row.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DOT_ROW_H +#define IGL_DOT_ROW_H + +#include "igl/igl_inline.h" +#include + +namespace igl +{ + // Compute the dot product between each row of A and B + // Templates: + // DerivedV derived from vertex positions matrix type: i.e. MatrixXd + // Inputs: + // A eigen matrix r by c + // B eigen matrix r by c + // Returns: + // d a column vector with r entries that contains the dot product of each corresponding row of A and B + // + template + IGL_INLINE DerivedV dot_row( + const Eigen::PlainObjectBase& A, + const Eigen::PlainObjectBase& B); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "dot_row.cpp" +#endif + +#endif diff --git a/src/igl/doublearea.cpp b/src/igl/doublearea.cpp new file mode 100644 index 0000000000..3cfc41b869 --- /dev/null +++ b/src/igl/doublearea.cpp @@ -0,0 +1,263 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "doublearea.h" +#include "edge_lengths.h" +#include "parallel_for.h" +#include "sort.h" +#include +#include +#include + +template +IGL_INLINE void igl::doublearea( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & dblA) +{ + // quads are handled by a specialized function + if (F.cols() == 4) return doublearea_quad(V,F,dblA); + + const int dim = V.cols(); + // Only support triangles + assert(F.cols() == 3); + const size_t m = F.rows(); + // Compute edge lengths + Eigen::Matrix l; + + // Projected area helper + const auto & proj_doublearea = + [&V,&F](const int x, const int y, const int f) + ->typename DerivedV::Scalar + { + auto rx = V(F(f,0),x)-V(F(f,2),x); + auto sx = V(F(f,1),x)-V(F(f,2),x); + auto ry = V(F(f,0),y)-V(F(f,2),y); + auto sy = V(F(f,1),y)-V(F(f,2),y); + return rx*sy - ry*sx; + }; + + switch(dim) + { + case 3: + { + dblA = DeriveddblA::Zero(m,1); + for(size_t f = 0;f +IGL_INLINE void igl::doublearea( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C, + Eigen::PlainObjectBase & D) +{ + assert((B.cols() == A.cols()) && "dimensions of A and B should match"); + assert((C.cols() == A.cols()) && "dimensions of A and C should match"); + assert(A.rows() == B.rows() && "corners should have same length"); + assert(A.rows() == C.rows() && "corners should have same length"); + switch(A.cols()) + { + case 2: + { + // For 2d compute signed area + const auto & R = A-C; + const auto & S = B-C; + D = (R.col(0).array()*S.col(1).array() - + R.col(1).array()*S.col(0).array()).template cast< + typename DerivedD::Scalar>(); + break; + } + default: + { + Eigen::Matrix + uL(A.rows(),3); + uL.col(0) = ((B-C).rowwise().norm()).template cast(); + uL.col(1) = ((C-A).rowwise().norm()).template cast(); + uL.col(2) = ((A-B).rowwise().norm()).template cast(); + doublearea(uL,D); + } + } +} + +template < + typename DerivedA, + typename DerivedB, + typename DerivedC> +IGL_INLINE typename DerivedA::Scalar igl::doublearea_single( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C) +{ + assert(A.size() == 2 && "Vertices should be 2D"); + assert(B.size() == 2 && "Vertices should be 2D"); + assert(C.size() == 2 && "Vertices should be 2D"); + auto r = A-C; + auto s = B-C; + return r(0)*s(1) - r(1)*s(0); +} + +template +IGL_INLINE void igl::doublearea( + const Eigen::MatrixBase & ul, + Eigen::PlainObjectBase & dblA) +{ + // Default is to leave NaNs and fire asserts in debug mode + return doublearea( + ul,std::numeric_limits::quiet_NaN(),dblA); +} + +template +IGL_INLINE void igl::doublearea( + const Eigen::MatrixBase & ul, + const typename Derivedl::Scalar nan_replacement, + Eigen::PlainObjectBase & dblA) +{ + using namespace Eigen; + using namespace std; + typedef typename Derivedl::Index Index; + // Only support triangles + assert(ul.cols() == 3); + // Number of triangles + const Index m = ul.rows(); + Eigen::Matrix l; + MatrixXi _; + // + // "Miscalculating Area and Angles of a Needle-like Triangle" + // https://people.eecs.berkeley.edu/~wkahan/Triangle.pdf + igl::sort(ul,2,false,l,_); + // semiperimeters + //Matrix s = l.rowwise().sum()*0.5; + //assert((Index)s.rows() == m); + // resize output + dblA.resize(l.rows(),1); + parallel_for( + m, + [&l,&dblA,&nan_replacement](const int i) + { + // Kahan's Heron's formula + typedef typename Derivedl::Scalar Scalar; + const Scalar arg = + (l(i,0)+(l(i,1)+l(i,2)))* + (l(i,2)-(l(i,0)-l(i,1)))* + (l(i,2)+(l(i,0)-l(i,1)))* + (l(i,0)+(l(i,1)-l(i,2))); + dblA(i) = 2.0*0.25*sqrt(arg); + // Alec: If the input edge lengths were computed from floating point + // vertex positions then there's no guarantee that they fulfill the + // triangle inequality (in their floating point approximations). For + // nearly degenerate triangles the round-off error during side-length + // computation may be larger than (or rather smaller) than the height of + // the triangle. In "Lecture Notes on Geometric Robustness" Shewchuck 09, + // Section 3.1 http://www.cs.berkeley.edu/~jrs/meshpapers/robnotes.pdf, + // he recommends computing the triangle areas for 2D and 3D using 2D + // signed areas computed with determinants. + assert( + (nan_replacement == nan_replacement || + (l(i,2) - (l(i,0)-l(i,1)))>=0) + && "Side lengths do not obey the triangle inequality."); + if(dblA(i) != dblA(i)) + { + dblA(i) = nan_replacement; + } + assert(dblA(i) == dblA(i) && "DOUBLEAREA() PRODUCED NaN"); + }, + 1000l); +} + +template +IGL_INLINE void igl::doublearea_quad( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & dblA) +{ + assert(V.cols() == 3); // Only supports points in 3D + assert(F.cols() == 4); // Only support quads + const size_t m = F.rows(); + + // Split the quads into triangles + Eigen::MatrixXi Ft(F.rows()*2,3); + + for(size_t i=0; i, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::doublearea, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template Eigen::Matrix::Scalar igl::doublearea_single, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +template Eigen::Matrix::Scalar igl::doublearea_single, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +#endif diff --git a/src/igl/doublearea.h b/src/igl/doublearea.h new file mode 100644 index 0000000000..95c1515082 --- /dev/null +++ b/src/igl/doublearea.h @@ -0,0 +1,104 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DOUBLEAREA_H +#define IGL_DOUBLEAREA_H +#include "igl_inline.h" +#include +namespace igl +{ + // DOUBLEAREA computes twice the area for each input triangle[quad] + // + // Templates: + // DerivedV derived type of eigen matrix for V (e.g. derived from + // MatrixXd) + // DerivedF derived type of eigen matrix for F (e.g. derived from + // MatrixXi) + // DeriveddblA derived type of eigen matrix for dblA (e.g. derived from + // MatrixXd) + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by simplex_size list of mesh faces (must be triangles or quads) + // Outputs: + // dblA #F list of triangle[quad] double areas (SIGNED only for 2D input) + // + // Known bug: For dim==3 complexity is O(#V + #F)!! Not just O(#F). This is a big deal + // if you have 1million unreferenced vertices and 1 face + template + IGL_INLINE void doublearea( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & dblA); + // Stream of triangles, computes signed area... + template < + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedD> + IGL_INLINE void doublearea( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C, + Eigen::PlainObjectBase & D); + // Single triangle in 2D! + // + // This should handle streams of corners not just single corners + template < + typename DerivedA, + typename DerivedB, + typename DerivedC> + IGL_INLINE typename DerivedA::Scalar doublearea_single( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C); + // Same as above but use instrinsic edge lengths rather than (V,F) mesh. This + // + // Inputs: + // l #F by dim list of edge lengths using + // for triangles, columns correspond to edges 23,31,12 + // nan_replacement what value should be used for triangles whose given + // edge lengths do not obey the triangle inequality. These may be very + // wrong (e.g., [100 1 1]) or may be nearly degenerate triangles whose + // floating point side length computation leads to breach of the triangle + // inequality. One may wish to set this parameter to 0 if side lengths l + // are _known_ to come from a valid embedding (e.g., some mesh (V,F)). In + // that case, the only circumstance the triangle inequality is broken is + // when the triangle is nearly degenerate and floating point error + // dominates: hence replacing with zero is reasonable. + // Outputs: + // dblA #F list of triangle double areas + template + IGL_INLINE void doublearea( + const Eigen::MatrixBase & l, + const typename Derivedl::Scalar nan_replacement, + Eigen::PlainObjectBase & dblA); + // default behavior is to assert on NaNs and leave them in place + template + IGL_INLINE void doublearea( + const Eigen::MatrixBase & l, + Eigen::PlainObjectBase & dblA); + // DOUBLEAREA_QUAD computes twice the area for each input quadrilateral + // + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by simplex_size list of mesh faces (must be quadrilaterals) + // Outputs: + // dblA #F list of quadrilateral double areas + // + template + IGL_INLINE void doublearea_quad( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & dblA); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "doublearea.cpp" +#endif + +#endif diff --git a/src/igl/dqs.cpp b/src/igl/dqs.cpp new file mode 100644 index 0000000000..aab3c65327 --- /dev/null +++ b/src/igl/dqs.cpp @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "dqs.h" +#include +template < + typename DerivedV, + typename DerivedW, + typename Q, + typename QAlloc, + typename T, + typename DerivedU> +IGL_INLINE void igl::dqs( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & W, + const std::vector & vQ, + const std::vector & vT, + Eigen::PlainObjectBase & U) +{ + using namespace std; + assert(V.rows() <= W.rows()); + assert(W.cols() == (int)vQ.size()); + assert(W.cols() == (int)vT.size()); + // resize output + U.resizeLike(V); + + // Convert quats + trans into dual parts + vector vD(vQ.size()); + for(int c = 0;c10000) + for(int i = 0;i, Eigen::Matrix, Eigen::Quaternion, Eigen::aligned_allocator >, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector, Eigen::aligned_allocator > > const&, std::vector, std::allocator > > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/dqs.h b/src/igl/dqs.h new file mode 100644 index 0000000000..9b53ae1c70 --- /dev/null +++ b/src/igl/dqs.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_DQS_H +#define IGL_DQS_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Dual quaternion skinning + // + // Inputs: + // V #V by 3 list of rest positions + // W #W by #C list of weights + // vQ #C list of rotation quaternions + // vT #C list of translation vectors + // Outputs: + // U #V by 3 list of new positions + template < + typename DerivedV, + typename DerivedW, + typename Q, + typename QAlloc, + typename T, + typename DerivedU> + IGL_INLINE void dqs( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & W, + const std::vector & vQ, + const std::vector & vT, + Eigen::PlainObjectBase & U); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "dqs.cpp" +#endif +#endif diff --git a/src/igl/ears.cpp b/src/igl/ears.cpp new file mode 100644 index 0000000000..81576c3212 --- /dev/null +++ b/src/igl/ears.cpp @@ -0,0 +1,34 @@ +#include "ears.h" +#include "on_boundary.h" +#include "find.h" +#include "slice.h" +#include "mat_min.h" +#include + +template < + typename DerivedF, + typename Derivedear, + typename Derivedear_opp> +IGL_INLINE void igl::ears( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & ear, + Eigen::PlainObjectBase & ear_opp) +{ + assert(F.cols() == 3 && "F should contain triangles"); + Eigen::Array B; + { + Eigen::Array I; + on_boundary(F,I,B); + } + find(B.rowwise().count() == 2,ear); + Eigen::Array Bear; + slice(B,ear,1,Bear); + Eigen::Array M; + mat_min(Bear,2,M,ear_opp); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::ears, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/ears.h b/src/igl/ears.h new file mode 100644 index 0000000000..b7c51ec66b --- /dev/null +++ b/src/igl/ears.h @@ -0,0 +1,30 @@ +#ifndef IGL_EARS_H +#define IGL_EARS_H +#include "igl_inline.h" +#include +namespace igl +{ + // FIND_EARS Find all ears (faces with two boundary edges) in a given mesh + // + // [ears,ear_opp] = find_ears(F) + // + // Inputs: + // F #F by 3 list of triangle mesh indices + // Outputs: + // ears #ears list of indices into F of ears + // ear_opp #ears list of indices indicating which edge is non-boundary + // (connecting to flops) + // + template < + typename DerivedF, + typename Derivedear, + typename Derivedear_opp> + IGL_INLINE void ears( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & ear, + Eigen::PlainObjectBase & ear_opp); +} +#ifndef IGL_STATIC_LIBRARY +# include "ears.cpp" +#endif +#endif diff --git a/src/igl/edge_collapse_is_valid.cpp b/src/igl/edge_collapse_is_valid.cpp new file mode 100644 index 0000000000..0c613b7a36 --- /dev/null +++ b/src/igl/edge_collapse_is_valid.cpp @@ -0,0 +1,86 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "edge_collapse_is_valid.h" +#include "collapse_edge.h" +#include "circulation.h" +#include "intersect.h" +#include "unique.h" +#include "list_to_matrix.h" +#include + +IGL_INLINE bool igl::edge_collapse_is_valid( + const int e, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI) +{ + using namespace Eigen; + using namespace std; + // For consistency with collapse_edge.cpp, let's determine edge flipness + // (though not needed to check validity) + const int eflip = E(e,0)>E(e,1); + // source and destination + const int s = eflip?E(e,1):E(e,0); + const int d = eflip?E(e,0):E(e,1); + + if(s == IGL_COLLAPSE_EDGE_NULL && d==IGL_COLLAPSE_EDGE_NULL) + { + return false; + } + // check if edge collapse is valid: intersection of vertex neighbors of s and + // d should be exactly 2+(s,d) = 4 + // http://stackoverflow.com/a/27049418/148668 + { + // all vertex neighbors around edge, including the two vertices of the edge + const auto neighbors = []( + const int e, + const bool ccw, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI) + { + vector N,uN; + vector V2Fe = circulation(e, ccw,F,E,EMAP,EF,EI); + for(auto f : V2Fe) + { + N.push_back(F(f,0)); + N.push_back(F(f,1)); + N.push_back(F(f,2)); + } + vector _1,_2; + igl::unique(N,uN,_1,_2); + VectorXi uNm; + list_to_matrix(uN,uNm); + return uNm; + }; + VectorXi Ns = neighbors(e, eflip,F,E,EMAP,EF,EI); + VectorXi Nd = neighbors(e,!eflip,F,E,EMAP,EF,EI); + VectorXi Nint = igl::intersect(Ns,Nd); + if(Nint.size() != 4) + { + return false; + } + if(Ns.size() == 4 && Nd.size() == 4) + { + VectorXi NsNd(8); + NsNd< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EDGE_COLLAPSE_IS_VALID_H +#define IGL_EDGE_COLLAPSE_IS_VALID_H +#include "igl_inline.h" +#include +namespace igl +{ + // Assumes (V,F) is a closed manifold mesh (except for previouslly collapsed + // faces which should be set to: + // [IGL_COLLAPSE_EDGE_NULL IGL_COLLAPSE_EDGE_NULL IGL_COLLAPSE_EDGE_NULL]. + // Tests whether collapsing exactly two faces and exactly 3 edges from E (e + // and one side of each face gets collapsed to the other) will result in a + // mesh with the same topology. + // + // Inputs: + // e index into E of edge to try to collapse. E(e,:) = [s d] or [d s] so + // that sj) is the edge of + // F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) " + // e=(j->i) + // EI #E by 2 list of edge flap corners (see above). + // Returns true if edge collapse is valid + IGL_INLINE bool edge_collapse_is_valid( + const int e, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI); +} +#ifndef IGL_STATIC_LIBRARY +# include "edge_collapse_is_valid.cpp" +#endif +#endif diff --git a/src/igl/edge_flaps.cpp b/src/igl/edge_flaps.cpp new file mode 100644 index 0000000000..88c515864c --- /dev/null +++ b/src/igl/edge_flaps.cpp @@ -0,0 +1,60 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "edge_flaps.h" +#include "unique_edge_map.h" +#include +#include + +IGL_INLINE void igl::edge_flaps( + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI) +{ + // Initialize to boundary value + EF.setConstant(E.rows(),2,-1); + EI.setConstant(E.rows(),2,-1); + // loop over all faces + for(int f = 0;f > uE2E; + igl::unique_edge_map(F,allE,E,EMAP,uE2E); + // Const-ify to call overload + const auto & cE = E; + const auto & cEMAP = EMAP; + return edge_flaps(F,cE,cEMAP,EF,EI); +} diff --git a/src/igl/edge_flaps.h b/src/igl/edge_flaps.h new file mode 100644 index 0000000000..03ad6eb3d1 --- /dev/null +++ b/src/igl/edge_flaps.h @@ -0,0 +1,53 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EDGE_FLAPS_H +#define IGL_EDGE_FLAPS_H +#include "igl_inline.h" +#include +namespace igl +{ + // Determine "edge flaps": two faces on either side of a unique edge (assumes + // edge-manifold mesh) + // + // Inputs: + // F #F by 3 list of face indices + // E #E by 2 list of edge indices into V. + // EMAP #F*3 list of indices into E, mapping each directed edge to unique + // unique edge in E + // Outputs: + // EF #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of + // F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) " + // e=(j->i) + // EI #E by 2 list of edge flap corners (see above). + // + // TODO: This seems to be a duplicate of edge_topology.h + // igl::edge_topology(V,F,etEV,etFE,etEF); + // igl::edge_flaps(F,efE,efEMAP,efEF,efEI); + // [~,I] = sort(efE,2) + // all( efE(sub2ind(size(efE),repmat(1:size(efE,1),2,1)',I)) == etEV ) + // all( efEF(sub2ind(size(efE),repmat(1:size(efE,1),2,1)',I)) == etEF ) + // all(efEMAP(sub2ind(size(F),repmat(1:size(F,1),3,1)',repmat([1 2 3],size(F,1),1))) == etFE(:,[2 3 1])) + IGL_INLINE void edge_flaps( + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI); + // Only faces as input + IGL_INLINE void edge_flaps( + const Eigen::MatrixXi & F, + Eigen::MatrixXi & E, + Eigen::VectorXi & EMAP, + Eigen::MatrixXi & EF, + Eigen::MatrixXi & EI); +} +#ifndef IGL_STATIC_LIBRARY +# include "edge_flaps.cpp" +#endif + +#endif diff --git a/src/igl/edge_lengths.cpp b/src/igl/edge_lengths.cpp new file mode 100644 index 0000000000..02dbae768a --- /dev/null +++ b/src/igl/edge_lengths.cpp @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "edge_lengths.h" +#include "squared_edge_lengths.h" + +template +IGL_INLINE void igl::edge_lengths( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& L) + { + igl::squared_edge_lengths(V,F,L); + L=L.array().sqrt().eval(); + } + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/edge_lengths.h b/src/igl/edge_lengths.h new file mode 100644 index 0000000000..30dc3474b0 --- /dev/null +++ b/src/igl/edge_lengths.h @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EDGE_LENGTHS_H +#define IGL_EDGE_LENGTHS_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Constructs a list of lengths of edges opposite each index in a face + // (triangle/tet) list + // + // Templates: + // DerivedV derived from vertex positions matrix type: i.e. MatrixXd + // DerivedF derived from face indices matrix type: i.e. MatrixXi + // DerivedL derived from edge lengths matrix type: i.e. MatrixXd + // Inputs: + // V eigen matrix #V by 3 + // F #F by 2 list of mesh edges + // or + // F #F by 3 list of mesh faces (must be triangles) + // or + // T #T by 4 list of mesh elements (must be tets) + // Outputs: + // L #F by {1|3|6} list of edge lengths + // for edges, column of lengths + // for triangles, columns correspond to edges [1,2],[2,0],[0,1] + // for tets, columns correspond to edges + // [3 0],[3 1],[3 2],[1 2],[2 0],[0 1] + // + template + IGL_INLINE void edge_lengths( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& L); +} + +#ifndef IGL_STATIC_LIBRARY +# include "edge_lengths.cpp" +#endif + +#endif + diff --git a/src/igl/edge_topology.cpp b/src/igl/edge_topology.cpp new file mode 100644 index 0000000000..78147d0560 --- /dev/null +++ b/src/igl/edge_topology.cpp @@ -0,0 +1,110 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "edge_topology.h" +#include "is_edge_manifold.h" +#include + +template +IGL_INLINE void igl::edge_topology( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::MatrixXi& EV, + Eigen::MatrixXi& FE, + Eigen::MatrixXi& EF) +{ + // Only needs to be edge-manifold + if (V.rows() ==0 || F.rows()==0) + { + EV = Eigen::MatrixXi::Constant(0,2,-1); + FE = Eigen::MatrixXi::Constant(0,3,-1); + EF = Eigen::MatrixXi::Constant(0,2,-1); + return; + } + assert(igl::is_edge_manifold(F)); + std::vector > ETT; + for(int f=0;f v2) std::swap(v1,v2); + std::vector r(4); + r[0] = v1; r[1] = v2; + r[2] = f; r[3] = i; + ETT.push_back(r); + } + std::sort(ETT.begin(),ETT.end()); + + // count the number of edges (assume manifoldness) + int En = 1; // the last is always counted + for(int i=0;i& r1 = ETT[i]; + EV(En,0) = r1[0]; + EV(En,1) = r1[1]; + EF(En,0) = r1[2]; + FE(r1[2],r1[3]) = En; + } + else + { + std::vector& r1 = ETT[i]; + std::vector& r2 = ETT[i+1]; + EV(En,0) = r1[0]; + EV(En,1) = r1[1]; + EF(En,0) = r1[2]; + EF(En,1) = r2[2]; + FE(r1[2],r1[3]) = En; + FE(r2[2],r2[3]) = En; + ++i; // skip the next one + } + ++En; + } + + // Sort the relation EF, accordingly to EV + // the first one is the face on the left of the edge + for(unsigned i=0; i, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::Matrix&, Eigen::Matrix&, Eigen::Matrix&); +template void igl::edge_topology, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::Matrix&, Eigen::Matrix&, Eigen::Matrix&); +#endif diff --git a/src/igl/edge_topology.h b/src/igl/edge_topology.h new file mode 100644 index 0000000000..b7a41c5974 --- /dev/null +++ b/src/igl/edge_topology.h @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EDGE_TOPOLOGY_H +#define IGL_EDGE_TOPOLOGY_H + +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Initialize Edges and their topological relations (assumes an edge-manifold + // mesh) + // + // Inputs: + // V #V by dim list of mesh vertex positions (unused) + // F #F by 3 list of triangle indices into V + // Outputs: + // EV #Ex2 matrix storing the edge description as pair of indices to + // vertices + // FE #Fx3 matrix storing the Triangle-Edge relation + // EF #Ex2 matrix storing the Edge-Triangle relation + // + // TODO: This seems to be a inferior duplicate of edge_flaps.h: + // - unused input parameter V + // - roughly 2x slower than edge_flaps + // - outputs less information: edge_flaps reveals corner opposite edge + // - FE uses non-standard and ambiguous order: FE(f,c) is merely an edge + // incident on corner c of face f. In contrast, edge_flaps's EMAP(f,c) + // reveals the edge _opposite_ corner c of face f +template + IGL_INLINE void edge_topology( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::MatrixXi& EV, + Eigen::MatrixXi& FE, + Eigen::MatrixXi& EF); +} + +#ifndef IGL_STATIC_LIBRARY +# include "edge_topology.cpp" +#endif + +#endif diff --git a/src/igl/edges.cpp b/src/igl/edges.cpp new file mode 100644 index 0000000000..3d762d410e --- /dev/null +++ b/src/igl/edges.cpp @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "edges.h" +#include "adjacency_matrix.h" +#include + +template +IGL_INLINE void igl::edges( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & E) +{ + // build adjacency matrix + typedef typename DerivedF::Scalar Index; + Eigen::SparseMatrix A; + igl::adjacency_matrix(F,A); + // Number of non zeros should be twice number of edges + assert(A.nonZeros()%2 == 0); + // Resize to fit edges + E.resize(A.nonZeros()/2,2); + int i = 0; + // Iterate over outside + for(int k=0; k::InnerIterator it (A,k); it; ++it) + { + // only add edge in one direction + if(it.row(), Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::edges, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/edges.h b/src/igl/edges.h new file mode 100644 index 0000000000..fa34052632 --- /dev/null +++ b/src/igl/edges.h @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EDGES_H +#define IGL_EDGES_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Constructs a list of unique edges represented in a given mesh (V,F) + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Inputs: + // F #F by 3 list of mesh faces (must be triangles) + // or + // T #T x 4 matrix of indices of tet corners + // Outputs: + // E #E by 2 list of edges in no particular order + // + // See also: adjacency_matrix + template + IGL_INLINE void edges( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & E); +} + +#ifndef IGL_STATIC_LIBRARY +# include "edges.cpp" +#endif + +#endif diff --git a/src/igl/edges_to_path.cpp b/src/igl/edges_to_path.cpp new file mode 100644 index 0000000000..14cd895bc4 --- /dev/null +++ b/src/igl/edges_to_path.cpp @@ -0,0 +1,103 @@ +#include "edges_to_path.h" +#include "dfs.h" +#include "sort.h" +#include "slice.h" +#include "ismember.h" +#include "unique.h" +#include "adjacency_list.h" + +template < + typename DerivedE, + typename DerivedI, + typename DerivedJ, + typename DerivedK> +IGL_INLINE void igl::edges_to_path( + const Eigen::MatrixBase & OE, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & K) +{ + assert(OE.rows()>=1); + if(OE.rows() == 1) + { + I.resize(2); + I(0) = OE(0); + I(1) = OE(1); + J.resize(1); + J(0) = 0; + K.resize(1); + K(0) = 0; + } + + // Compute on reduced graph + DerivedI U; + Eigen::VectorXi vE; + { + Eigen::VectorXi IA; + unique(OE,U,IA,vE); + } + + Eigen::VectorXi V = Eigen::VectorXi::Zero(vE.maxCoeff()+1); + for(int e = 0;e(vE.data(),OE.rows(),OE.cols()).eval(); + { + std::vector > A; + igl::adjacency_list(E,A); + Eigen::VectorXi P,C; + dfs(A,s,I,P,C); + } + if(c == 2) + { + I.conservativeResize(I.size()+1); + I(I.size()-1) = I(0); + } + + DerivedE sE; + Eigen::Matrix sEI; + { + Eigen::MatrixXi _; + sort(E,2,true,sE,_); + Eigen::Matrix EI(I.size()-1,2); + EI.col(0) = I.head(I.size()-1); + EI.col(1) = I.tail(I.size()-1); + sort(EI,2,true,sEI,_); + } + { + Eigen::Array F; + ismember_rows(sEI,sE,F,J); + } + K.resize(I.size()-1); + for(int k = 0;k, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/edges_to_path.h b/src/igl/edges_to_path.h new file mode 100644 index 0000000000..403e7029ae --- /dev/null +++ b/src/igl/edges_to_path.h @@ -0,0 +1,36 @@ +#ifndef IGL_EDGES_TO_PATH_H +#define IGL_EDGES_TO_PATH_H +#include "igl_inline.h" +#include +namespace igl +{ + // EDGES_TO_PATH Given a set of undirected, unique edges such that all form a + // single connected compoent with exactly 0 or 2 nodes with valence =1, + // determine the/a path visiting all nodes. + // + // Inputs: + // E #E by 2 list of undirected edges + // Outputs: + // I #E+1 list of nodes in order tracing the chain (loop), if the output + // is a loop then I(1) == I(end) + // J #I-1 list of indices into E of edges tracing I + // K #I-1 list of indices into columns of E {1,2} so that K(i) means that + // E(i,K(i)) comes before the other (i.e., E(i,3-K(i)) ). This means that + // I(i) == E(J(i),K(i)) for i<#I, or + // I == E(sub2ind(size(E),J([1:end end]),[K;3-K(end)])))) + // + template < + typename DerivedE, + typename DerivedI, + typename DerivedJ, + typename DerivedK> + IGL_INLINE void edges_to_path( + const Eigen::MatrixBase & E, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & K); +} +#ifndef IGL_STATIC_LIBRARY +# include "edges_to_path.cpp" +#endif +#endif diff --git a/src/igl/eigs.cpp b/src/igl/eigs.cpp new file mode 100755 index 0000000000..1fd440ddd2 --- /dev/null +++ b/src/igl/eigs.cpp @@ -0,0 +1,176 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "eigs.h" + +#include "cotmatrix.h" +#include "sort.h" +#include "slice.h" +#include "massmatrix.h" +#include + +template < + typename Atype, + typename Btype, + typename DerivedU, + typename DerivedS> +IGL_INLINE bool igl::eigs( + const Eigen::SparseMatrix & A, + const Eigen::SparseMatrix & iB, + const size_t k, + const EigsType type, + Eigen::PlainObjectBase & sU, + Eigen::PlainObjectBase & sS) +{ + using namespace Eigen; + using namespace std; + const size_t n = A.rows(); + assert(A.cols() == n && "A should be square."); + assert(iB.rows() == n && "B should be match A's dims."); + assert(iB.cols() == n && "B should be square."); + assert(type == EIGS_TYPE_SM && "Only low frequencies are supported"); + DerivedU U(n,k); + DerivedS S(k,1); + typedef Atype Scalar; + typedef Eigen::Matrix VectorXS; + // Rescale B for better numerics + const Scalar rescale = std::abs(iB.diagonal().maxCoeff()); + const Eigen::SparseMatrix B = iB/rescale; + + Scalar tol = 1e-4; + Scalar conv = 1e-14; + int max_iter = 100; + int i = 0; + //std::cout<<"start"<0) + { + eff_sigma = 1e-8+std::abs(S(i-1)); + } + // whether to use rayleigh quotient method + bool ray = false; + Scalar err = std::numeric_limits::infinity(); + int iter; + Scalar sigma = std::numeric_limits::infinity(); + VectorXS x; + for(iter = 0;iter0 && !ray) + { + // project-out existing modes + for(int j = 0;j0?1.:-1.; + + Scalar err_prev = err; + err = (A*x-sigma*B*x).array().abs().maxCoeff(); + if(err > solver; + const SparseMatrix C = A-eff_sigma*B+tikhonov*B; + //mw.save(C,"C"); + //mw.save(eff_sigma,"eff_sigma"); + //mw.save(tikhonov,"tikhonov"); + solver.compute(C); + switch(solver.info()) + { + case Eigen::Success: + break; + case Eigen::NumericalIssue: + cerr<<"Error: Numerical issue."<1e-14 || + ((U.leftCols(i).transpose()*B*x).array().abs()<=1e-7).all() + ) + { + //cout<<"Found "<, Eigen::Matrix >(Eigen::SparseMatrix const&, Eigen::SparseMatrix const&, const size_t, igl::EigsType, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template bool igl::eigs, Eigen::Matrix >(Eigen::SparseMatrix const &,Eigen::SparseMatrix const &, const size_t, igl::EigsType, Eigen::PlainObjectBase< Eigen::Matrix > &, Eigen::PlainObjectBase > &); +#endif +#endif diff --git a/src/igl/eigs.h b/src/igl/eigs.h new file mode 100644 index 0000000000..3b376379c1 --- /dev/null +++ b/src/igl/eigs.h @@ -0,0 +1,61 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EIGS_H +#define IGL_EIGS_H +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Act like MATLAB's eigs function. Compute the first/last k eigen pairs of + // the generalized eigen value problem: + // + // A u = s B u + // + // Solutions are approximate and sorted. + // + // Ideally one should use ARPACK and the Eigen unsupported ARPACK module. + // This implementation does simple, naive power iterations. + // + // Inputs: + // A #A by #A symmetric matrix + // B #A by #A symmetric positive-definite matrix + // k number of eigen pairs to compute + // type whether to extract from the high or low end + // Outputs: + // sU #A by k list of sorted eigen vectors (descending) + // sS k list of sorted eigen values (descending) + // + // Known issues: + // - only the 'sm' small magnitude eigen values are well supported + // + enum EigsType + { + EIGS_TYPE_SM = 0, + EIGS_TYPE_LM = 1, + NUM_EIGS_TYPES = 2 + }; + template < + typename Atype, + typename Btype, + typename DerivedU, + typename DerivedS> + IGL_INLINE bool eigs( + const Eigen::SparseMatrix & A, + const Eigen::SparseMatrix & B, + const size_t k, + const EigsType type, + Eigen::PlainObjectBase & sU, + Eigen::PlainObjectBase & sS); +} + +#ifndef IGL_STATIC_LIBRARY +#include "eigs.cpp" +#endif +#endif diff --git a/src/igl/embree/EmbreeIntersector.h b/src/igl/embree/EmbreeIntersector.h new file mode 100644 index 0000000000..ec59c0a1a4 --- /dev/null +++ b/src/igl/embree/EmbreeIntersector.h @@ -0,0 +1,584 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// 2014 Christian Schüller +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// igl function interface for Embree2.2 +// +// Necessary changes to switch from previous Embree versions: +// * Use igl:Hit instead of embree:Hit (where id0 -> id) +// * For Embree2.2 +// * Uncomment #define __USE_RAY_MASK__ in platform.h to enable masking + +#ifndef IGL_EMBREE_EMBREE_INTERSECTOR_H +#define IGL_EMBREE_EMBREE_INTERSECTOR_H + +#include "../Hit.h" +#include +#include +#include + +#include "embree2/rtcore.h" +#include "embree2/rtcore_ray.h" +#include +#include + +namespace igl +{ + namespace embree + { + class EmbreeIntersector + { + public: + // Initialize embree engine. This will be called on instance `init()` + // calls. If already inited then this function does nothing: it is harmless + // to call more than once. + static inline void global_init(); + private: + // Deinitialize the embree engine. + static inline void global_deinit(); + public: + typedef Eigen::Matrix PointMatrixType; + typedef Eigen::Matrix FaceMatrixType; + public: + inline EmbreeIntersector(); + private: + // Copying and assignment are not allowed. + inline EmbreeIntersector(const EmbreeIntersector & that); + inline EmbreeIntersector & operator=(const EmbreeIntersector &); + public: + virtual inline ~EmbreeIntersector(); + + // Initialize with a given mesh. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of Oriented triangles + // isStatic scene is optimized for static geometry + // Side effects: + // The first time this is ever called the embree engine is initialized. + inline void init( + const PointMatrixType& V, + const FaceMatrixType& F, + bool isStatic = false); + + // Initialize with a given mesh. + // + // Inputs: + // V vector of #V by 3 list of vertex positions for each geometry + // F vector of #F by 3 list of Oriented triangles for each geometry + // masks a 32 bit mask to identify active geometries. + // isStatic scene is optimized for static geometry + // Side effects: + // The first time this is ever called the embree engine is initialized. + inline void init( + const std::vector& V, + const std::vector& F, + const std::vector& masks, + bool isStatic = false); + + // Deinitialize embree datasctructures for current mesh. Also called on + // destruction: no need to call if you just want to init() once and + // destroy. + inline void deinit(); + + // Given a ray find the first hit + // + // Inputs: + // origin 3d origin point of ray + // direction 3d (not necessarily normalized) direction vector of ray + // tnear start of ray segment + // tfar end of ray segment + // masks a 32 bit mask to identify active geometries. + // Output: + // hit information about hit + // Returns true if and only if there was a hit + inline bool intersectRay( + const Eigen::RowVector3f& origin, + const Eigen::RowVector3f& direction, + Hit& hit, + float tnear = 0, + float tfar = std::numeric_limits::infinity(), + int mask = 0xFFFFFFFF) const; + + // Given a ray find the first hit + // This is a conservative hit test where multiple rays within a small radius + // will be tested and only the closesest hit is returned. + // + // Inputs: + // origin 3d origin point of ray + // direction 3d (not necessarily normalized) direction vector of ray + // tnear start of ray segment + // tfar end of ray segment + // masks a 32 bit mask to identify active geometries. + // geoId id of geometry mask (default std::numeric_limits::infinity() if no: no masking) + // closestHit true for gets closest hit, false for furthest hit + // Output: + // hit information about hit + // Returns true if and only if there was a hit + inline bool intersectBeam( + const Eigen::RowVector3f& origin, + const Eigen::RowVector3f& direction, + Hit& hit, + float tnear = 0, + float tfar = std::numeric_limits::infinity(), + int mask = 0xFFFFFFFF, + int geoId = -1, + bool closestHit = true, + unsigned int samples = 4) const; + + // Given a ray find all hits in order + // + // Inputs: + // origin 3d origin point of ray + // direction 3d (not necessarily normalized) direction vector of ray + // tnear start of ray segment + // tfar end of ray segment + // masks a 32 bit mask to identify active geometries. + // Output: + // hit information about hit + // num_rays number of rays shot (at least one) + // Returns true if and only if there was a hit + inline bool intersectRay( + const Eigen::RowVector3f& origin, + const Eigen::RowVector3f& direction, + std::vector &hits, + int& num_rays, + float tnear = 0, + float tfar = std::numeric_limits::infinity(), + int mask = 0xFFFFFFFF) const; + + // Given a ray find the first hit + // + // Inputs: + // a 3d first end point of segment + // ab 3d vector from a to other endpoint b + // Output: + // hit information about hit + // Returns true if and only if there was a hit + inline bool intersectSegment( + const Eigen::RowVector3f& a, + const Eigen::RowVector3f& ab, + Hit &hit, + int mask = 0xFFFFFFFF) const; + + private: + + struct Vertex {float x,y,z,a;}; + struct Triangle {int v0, v1, v2;}; + + RTCScene scene; + unsigned geomID; + Vertex* vertices; + Triangle* triangles; + bool initialized; + + inline void createRay( + RTCRay& ray, + const Eigen::RowVector3f& origin, + const Eigen::RowVector3f& direction, + float tnear, + float tfar, + int mask) const; + }; + } +} + +// Implementation +#include +// This unfortunately cannot be a static field of EmbreeIntersector because it +// would depend on the template and then we might end up with initializing +// embree twice. If only there was a way to ask embree if it's already +// initialized... +namespace igl +{ + namespace embree + { + // Keeps track of whether the **Global** Embree intersector has been + // initialized. This should never been done at the global scope. + static bool EmbreeIntersector_inited = false; + } +} + +inline void igl::embree::EmbreeIntersector::global_init() +{ + if(!EmbreeIntersector_inited) + { + rtcInit(); + if(rtcGetError() != RTC_NO_ERROR) + std::cerr << "Embree: An error occurred while initializing embree core!" << std::endl; +#ifdef IGL_VERBOSE + else + std::cerr << "Embree: core initialized." << std::endl; +#endif + EmbreeIntersector_inited = true; + } +} + +inline void igl::embree::EmbreeIntersector::global_deinit() +{ + EmbreeIntersector_inited = false; + rtcExit(); +} + +inline igl::embree::EmbreeIntersector::EmbreeIntersector() + : + //scene(NULL), + geomID(0), + vertices(NULL), + triangles(NULL), + initialized(false) +{ +} + +inline igl::embree::EmbreeIntersector::EmbreeIntersector( + const EmbreeIntersector &) + :// To make -Weffc++ happy + //scene(NULL), + geomID(0), + vertices(NULL), + triangles(NULL), + initialized(false) +{ + assert(false && "Embree: Copying EmbreeIntersector is not allowed"); +} + +inline igl::embree::EmbreeIntersector & igl::embree::EmbreeIntersector::operator=( + const EmbreeIntersector &) +{ + assert(false && "Embree: Assigning an EmbreeIntersector is not allowed"); + return *this; +} + + +inline void igl::embree::EmbreeIntersector::init( + const PointMatrixType& V, + const FaceMatrixType& F, + bool isStatic) +{ + std::vector Vtemp; + std::vector Ftemp; + std::vector masks; + Vtemp.push_back(&V); + Ftemp.push_back(&F); + masks.push_back(0xFFFFFFFF); + init(Vtemp,Ftemp,masks,isStatic); +} + +inline void igl::embree::EmbreeIntersector::init( + const std::vector& V, + const std::vector& F, + const std::vector& masks, + bool isStatic) +{ + + if(initialized) + deinit(); + + using namespace std; + global_init(); + + if(V.size() == 0 || F.size() == 0) + { + std::cerr << "Embree: No geometry specified!"; + return; + } + + // create a scene + RTCSceneFlags flags = RTC_SCENE_ROBUST | RTC_SCENE_HIGH_QUALITY; + if(isStatic) + flags = flags | RTC_SCENE_STATIC; + scene = rtcNewScene(flags,RTC_INTERSECT1); + + for(int g=0;g<(int)V.size();g++) + { + // create triangle mesh geometry in that scene + geomID = rtcNewTriangleMesh(scene,RTC_GEOMETRY_STATIC,F[g]->rows(),V[g]->rows(),1); + + // fill vertex buffer + vertices = (Vertex*)rtcMapBuffer(scene,geomID,RTC_VERTEX_BUFFER); + for(int i=0;i<(int)V[g]->rows();i++) + { + vertices[i].x = (float)V[g]->coeff(i,0); + vertices[i].y = (float)V[g]->coeff(i,1); + vertices[i].z = (float)V[g]->coeff(i,2); + } + rtcUnmapBuffer(scene,geomID,RTC_VERTEX_BUFFER); + + // fill triangle buffer + triangles = (Triangle*) rtcMapBuffer(scene,geomID,RTC_INDEX_BUFFER); + for(int i=0;i<(int)F[g]->rows();i++) + { + triangles[i].v0 = (int)F[g]->coeff(i,0); + triangles[i].v1 = (int)F[g]->coeff(i,1); + triangles[i].v2 = (int)F[g]->coeff(i,2); + } + rtcUnmapBuffer(scene,geomID,RTC_INDEX_BUFFER); + + rtcSetMask(scene,geomID,masks[g]); + } + + rtcCommit(scene); + + if(rtcGetError() != RTC_NO_ERROR) + std::cerr << "Embree: An error occurred while initializing the provided geometry!" << endl; +#ifdef IGL_VERBOSE + else + std::cerr << "Embree: geometry added." << endl; +#endif + + initialized = true; +} + +igl::embree::EmbreeIntersector +::~EmbreeIntersector() +{ + if(initialized) + deinit(); +} + +void igl::embree::EmbreeIntersector::deinit() +{ + if(EmbreeIntersector_inited && scene) + { + rtcDeleteScene(scene); + + if(rtcGetError() != RTC_NO_ERROR) + { + std::cerr << "Embree: An error occurred while resetting!" << std::endl; + } +#ifdef IGL_VERBOSE + else + { + std::cerr << "Embree: geometry removed." << std::endl; + } +#endif + } +} + +inline bool igl::embree::EmbreeIntersector::intersectRay( + const Eigen::RowVector3f& origin, + const Eigen::RowVector3f& direction, + Hit& hit, + float tnear, + float tfar, + int mask) const +{ + RTCRay ray; + createRay(ray, origin,direction,tnear,tfar,mask); + + // shot ray + rtcIntersect(scene,ray); +#ifdef IGL_VERBOSE + if(rtcGetError() != RTC_NO_ERROR) + std::cerr << "Embree: An error occurred while resetting!" << std::endl; +#endif + + if((unsigned)ray.geomID != RTC_INVALID_GEOMETRY_ID) + { + hit.id = ray.primID; + hit.gid = ray.geomID; + hit.u = ray.u; + hit.v = ray.v; + hit.t = ray.tfar; + return true; + } + + return false; +} + +inline bool igl::embree::EmbreeIntersector::intersectBeam( + const Eigen::RowVector3f& origin, + const Eigen::RowVector3f& direction, + Hit& hit, + float tnear, + float tfar, + int mask, + int geoId, + bool closestHit, + unsigned int samples) const +{ + bool hasHit = false; + Hit bestHit; + + if(closestHit) + bestHit.t = std::numeric_limits::max(); + else + bestHit.t = 0; + + if((intersectRay(origin,direction,hit,tnear,tfar,mask) && (hit.gid == geoId || geoId == -1))) + { + bestHit = hit; + hasHit = true; + } + + // sample points around actual ray (conservative hitcheck) + const float eps= 1e-5; + + Eigen::RowVector3f up(0,1,0); + Eigen::RowVector3f offset = direction.cross(up).normalized(); + + Eigen::Matrix3f rot = Eigen::AngleAxis(2*3.14159265358979/samples,direction).toRotationMatrix(); + + for(int r=0;r<(int)samples;r++) + { + if(intersectRay(origin+offset*eps,direction,hit,tnear,tfar,mask) && + ((closestHit && (hit.t < bestHit.t)) || + (!closestHit && (hit.t > bestHit.t))) && + (hit.gid == geoId || geoId == -1)) + { + bestHit = hit; + hasHit = true; + } + offset = rot*offset.transpose(); + } + + hit = bestHit; + return hasHit; +} + +inline bool +igl::embree::EmbreeIntersector +::intersectRay( + const Eigen::RowVector3f& origin, + const Eigen::RowVector3f& direction, + std::vector &hits, + int& num_rays, + float tnear, + float tfar, + int mask) const +{ + using namespace std; + num_rays = 0; + hits.clear(); + int last_id0 = -1; + double self_hits = 0; + // This epsilon is directly correleated to the number of missed hits, smaller + // means more accurate and slower + //const double eps = DOUBLE_EPS; + const double eps = FLOAT_EPS; + double min_t = tnear; + bool large_hits_warned = false; + RTCRay ray; + createRay(ray,origin,direction,tnear,tfar,mask); + + while(true) + { + ray.tnear = min_t; + ray.tfar = tfar; + ray.geomID = RTC_INVALID_GEOMETRY_ID; + ray.primID = RTC_INVALID_GEOMETRY_ID; + ray.instID = RTC_INVALID_GEOMETRY_ID; + num_rays++; + rtcIntersect(scene,ray); + if((unsigned)ray.geomID != RTC_INVALID_GEOMETRY_ID) + { + // Hit self again, progressively advance + if(ray.primID == last_id0 || ray.tfar <= min_t) + { + // push min_t a bit more + //double t_push = pow(2.0,self_hits-4)*(hit.t1000 && !large_hits_warned) + { + std::cout<<"Warning: Large number of hits..."<::iterator hit = hits.begin(); hit != hits.end();hit++) + { + std::cout<<(hit->id+1)<<" "; + } + + std::cout.precision(std::numeric_limits< double >::digits10); + std::cout<<"[ "; + + for(vector::iterator hit = hits.begin(); hit != hits.end(); hit++) + { + std::cout<<(hit->t)< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EMBREE_EMBREE_CONVENIENCE_H +#define IGL_EMBREE_EMBREE_CONVENIENCE_H + +#undef interface +#undef near +#undef far +// Why are these in quotes? isn't that a bad idea? +#ifdef __GNUC__ +// This is how it should be done +# if __GNUC__ >= 4 +# if __GNUC_MINOR__ >= 6 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Weffc++" +# endif +# endif +// This is a hack +# pragma GCC system_header +#endif +#include +#include +#include +#ifdef __GNUC__ +# if __GNUC__ >= 4 +# if __GNUC_MINOR__ >= 6 +# pragma GCC diagnostic pop +# endif +# endif +#endif + +#endif diff --git a/src/igl/embree/ambient_occlusion.cpp b/src/igl/embree/ambient_occlusion.cpp new file mode 100644 index 0000000000..d19aa9c467 --- /dev/null +++ b/src/igl/embree/ambient_occlusion.cpp @@ -0,0 +1,62 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "ambient_occlusion.h" +#include "../ambient_occlusion.h" +#include "EmbreeIntersector.h" +#include "../Hit.h" + +template < + typename DerivedP, + typename DerivedN, + typename DerivedS > +IGL_INLINE void igl::embree::ambient_occlusion( + const igl::embree::EmbreeIntersector & ei, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + const auto & shoot_ray = [&ei]( + const Eigen::Vector3f& s, + const Eigen::Vector3f& dir)->bool + { + igl::Hit hit; + const float tnear = 1e-4f; + return ei.intersectRay(s,dir,hit,tnear); + }; + return igl::ambient_occlusion(shoot_ray,P,N,num_samples,S); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > +IGL_INLINE void igl::embree::ambient_occlusion( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + using namespace Eigen; + EmbreeIntersector ei; + ei.init(V.template cast(),F.template cast()); + ambient_occlusion(ei,P,N,num_samples,S); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::embree::ambient_occlusion, Eigen::Matrix, Eigen::Matrix >(igl::embree::EmbreeIntersector const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::embree::ambient_occlusion, Eigen::Matrix, Eigen::Matrix >(igl::embree::EmbreeIntersector const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::embree::ambient_occlusion, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::embree::ambient_occlusion, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::embree::ambient_occlusion, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/embree/ambient_occlusion.h b/src/igl/embree/ambient_occlusion.h new file mode 100644 index 0000000000..6df3a53ee3 --- /dev/null +++ b/src/igl/embree/ambient_occlusion.h @@ -0,0 +1,59 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EMBREE_AMBIENT_OCCLUSION_H +#define IGL_EMBREE_AMBIENT_OCCLUSION_H +#include "../igl_inline.h" +#include +namespace igl +{ + namespace embree + { + // Forward define + class EmbreeIntersector; + // Compute ambient occlusion per given point + // + // Inputs: + // ei EmbreeIntersector containing (V,F) + // P #P by 3 list of origin points + // N #P by 3 list of origin normals + // Outputs: + // S #P list of ambient occlusion values between 1 (fully occluded) and + // 0 (not occluded) + // + template < + typename DerivedP, + typename DerivedN, + typename DerivedS > + IGL_INLINE void ambient_occlusion( + const EmbreeIntersector & ei, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S); + // Wrapper which builds new EmbreeIntersector for (V,F). That's expensive so + // avoid this if repeatedly calling. + template < + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > + IGL_INLINE void ambient_occlusion( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S); + } +}; +#ifndef IGL_STATIC_LIBRARY +# include "ambient_occlusion.cpp" +#endif + +#endif diff --git a/src/igl/embree/bone_heat.cpp b/src/igl/embree/bone_heat.cpp new file mode 100644 index 0000000000..8a85af98cf --- /dev/null +++ b/src/igl/embree/bone_heat.cpp @@ -0,0 +1,116 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "bone_heat.h" +#include "EmbreeIntersector.h" +#include "bone_visible.h" +#include "../project_to_line_segment.h" +#include "../cotmatrix.h" +#include "../massmatrix.h" +#include "../mat_min.h" +#include + +bool igl::embree::bone_heat( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & C, + const Eigen::VectorXi & P, + const Eigen::MatrixXi & BE, + const Eigen::MatrixXi & CE, + Eigen::MatrixXd & W) +{ + using namespace std; + using namespace Eigen; + assert(CE.rows() == 0 && "Cage edges not supported."); + assert(C.cols() == V.cols() && "V and C should have same #cols"); + assert(BE.cols() == 2 && "BE should have #cols=2"); + assert(F.cols() == 3 && "F should contain triangles."); + assert(V.cols() == 3 && "V should contain 3D positions."); + + const int n = V.rows(); + const int np = P.rows(); + const int nb = BE.rows(); + const int m = np + nb; + + // "double sided lighting" + MatrixXi FF; + FF.resize(F.rows()*2,F.cols()); + FF << F, F.rowwise().reverse(); + // Initialize intersector + EmbreeIntersector ei; + ei.init(V.cast(),F.cast()); + + typedef Matrix VectorXb; + typedef Matrix MatrixXb; + MatrixXb vis_mask(n,m); + // Distances + MatrixXd D(n,m); + // loop over points + for(int j = 0;j 0) + { + cerr<<"Error: Cage edges are not supported. Ignored."<1e10?1e10:hii); + } + } + SparseMatrix Q,L,M; + cotmatrix(V,F,L); + massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,M); + const auto & H = Hdiag.asDiagonal(); + Q = (-L+M*H); + SimplicialLLT > llt; + llt.compute(Q); + switch(llt.info()) + { + case Eigen::Success: + break; + case Eigen::NumericalIssue: + cerr<<"Error: Numerical issue."< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EMBREE_BONE_HEAT_H +#define IGL_EMBREE_BONE_HEAT_H +#include "../igl_inline.h" +#include + +namespace igl +{ + namespace embree + { + // BONE_HEAT Compute skinning weights W given a surface mesh (V,F) and an + // internal skeleton (C,BE) according to "Automatic Rigging" [Baran and + // Popovic 2007]. + // + // Inputs: + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of mesh corner indices into V + // C #C by 3 list of joint locations + // P #P list of point handle indices into C + // BE #BE by 2 list of bone edge indices into C + // CE #CE by 2 list of cage edge indices into **P** + // Outputs: + // W #V by #P+#BE matrix of weights. + // Returns true only on success. + // + IGL_INLINE bool bone_heat( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & C, + const Eigen::VectorXi & P, + const Eigen::MatrixXi & BE, + const Eigen::MatrixXi & CE, + Eigen::MatrixXd & W); + } +}; + +#ifndef IGL_STATIC_LIBRARY +# include "bone_heat.cpp" +#endif + +#endif diff --git a/src/igl/embree/bone_visible.cpp b/src/igl/embree/bone_visible.cpp new file mode 100644 index 0000000000..4f2fe91263 --- /dev/null +++ b/src/igl/embree/bone_visible.cpp @@ -0,0 +1,145 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "bone_visible.h" +#include "../project_to_line.h" +#include "../EPS.h" +#include "../Hit.h" +#include "../Timer.h" +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedSD, + typename Derivedflag> +IGL_INLINE void igl::embree::bone_visible( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & s, + const Eigen::PlainObjectBase & d, + Eigen::PlainObjectBase & flag) +{ + // "double sided lighting" + Eigen::Matrix FF; + FF.resize(F.rows()*2,F.cols()); + FF << F, F.rowwise().reverse(); + // Initialize intersector + EmbreeIntersector ei; + ei.init(V.template cast(),FF.template cast()); + return bone_visible(V,F,ei,s,d,flag); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedSD, + typename Derivedflag> +IGL_INLINE void igl::embree::bone_visible( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const EmbreeIntersector & ei, + const Eigen::PlainObjectBase & s, + const Eigen::PlainObjectBase & d, + Eigen::PlainObjectBase & flag) +{ + using namespace std; + using namespace Eigen; + flag.resize(V.rows()); + const double sd_norm = (s-d).norm(); + // Embree seems to be parallel when constructing but not when tracing rays +#pragma omp parallel for + // loop over mesh vertices + for(int v = 0;v1) + { + t = 1; + sqrd = (Vv-d).array().pow(2).sum(); + projv = d; + } + } + igl::Hit hit; + // perhaps 1.0 should be 1.0-epsilon, or actually since we checking the + // incident face, perhaps 1.0 should be 1.0+eps + const Vector3d dir = (Vv-projv)*1.0; + if(ei.intersectSegment( + projv.template cast(), + dir.template cast(), + hit)) + { + // mod for double sided lighting + const int fi = hit.id % F.rows(); + + //if(v == 1228-1) + //{ + // Vector3d bc,P; + // bc << 1 - hit.u - hit.v, hit.u, hit.v; // barycentric + // P = V.row(F(fi,0))*bc(0) + + // V.row(F(fi,1))*bc(1) + + // V.row(F(fi,2))*bc(2); + // cout<<(fi+1)<sqrd) + { + flag(v) = true; + } + }else + { + // no hit so vectex v is visible + flag(v) = true; + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::embree::bone_visible, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::embree::bone_visible, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/embree/bone_visible.h b/src/igl/embree/bone_visible.h new file mode 100644 index 0000000000..1e490b51dd --- /dev/null +++ b/src/igl/embree/bone_visible.h @@ -0,0 +1,68 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EMBREE_BONE_VISIBLE_H +#define IGL_EMBREE_BONE_VISIBLE_H +#include +#include +#include "EmbreeIntersector.h" +namespace igl +{ + namespace embree + { + // + // BONE_VISIBLE test whether vertices of mesh are "visible" to a given bone, + // where "visible" is defined as in [Baran & Popovic 07]. Instead of checking + // whether each point can see *any* of the bone, we just check if each point + // can see its own projection onto the bone segment. In other words, we project + // each vertex v onto the bone, projv. Then we check if there are any + // intersections between the line segment (projv-->v) and the mesh. + // + // [flag] = bone_visible(V,F,s,d); + // + // Input: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // s row vector of position of start end point of bone + // d row vector of position of dest end point of bone + // Output: + // flag #V by 1 list of bools (true) visible, (false) obstructed + // + // Note: This checks for hits along the segment which are facing in *any* + // direction from the ray. + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedSD, + typename Derivedflag> + IGL_INLINE void bone_visible( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & s, + const Eigen::PlainObjectBase & d, + Eigen::PlainObjectBase & flag); + // Inputs: + // ei EmbreeIntersector for mesh (V,F) should be double sided + template < + typename DerivedV, + typename DerivedF, + typename DerivedSD, + typename Derivedflag> + IGL_INLINE void bone_visible( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const EmbreeIntersector & ei, + const Eigen::PlainObjectBase & s, + const Eigen::PlainObjectBase & d, + Eigen::PlainObjectBase & flag); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "bone_visible.cpp" +#endif +#endif diff --git a/src/igl/embree/embree2/rtcore.h b/src/igl/embree/embree2/rtcore.h new file mode 100644 index 0000000000..fb24757a96 --- /dev/null +++ b/src/igl/embree/embree2/rtcore.h @@ -0,0 +1,257 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTCORE_H__ +#define __RTCORE_H__ + +#include +#include + +#if defined(_WIN32) +#if defined(_M_X64) +typedef long long ssize_t; +#else +typedef int ssize_t; +#endif +#endif + +#ifndef RTCORE_API +#if defined(_WIN32) && !defined(ENABLE_STATIC_LIB) +# define RTCORE_API extern "C" __declspec(dllimport) +#else +# define RTCORE_API extern "C" +#endif +#endif + +#ifdef _WIN32 +# define RTCORE_ALIGN(...) __declspec(align(__VA_ARGS__)) +#else +# define RTCORE_ALIGN(...) __attribute__((aligned(__VA_ARGS__))) +#endif + +#ifdef __GNUC__ + #define RTCORE_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define RTCORE_DEPRECATED __declspec(deprecated) +#else + #define RTCORE_DEPRECATED +#endif + +/*! Embree API version */ +#define RTCORE_VERSION_MAJOR 2 +#define RTCORE_VERSION_MINOR 9 +#define RTCORE_VERSION_PATCH 0 +#define RTCORE_VERSION 20900 + +/*! \file rtcore.h Defines the Embree Ray Tracing Kernel API for C and C++ + + This file defines the Embree ray tracing kernel API for C and + C++. The user is supposed to include this file, and alternatively + the rtcore_ray.h file, but none of the other .h files in this + folder. */ + +/*! \{ */ + +/*! Axis aligned bounding box representation */ +struct RTCORE_ALIGN(16) RTCBounds +{ + float lower_x, lower_y, lower_z, align0; + float upper_x, upper_y, upper_z, align1; +}; + +/*! \brief Defines an opaque device type */ +typedef struct __RTCDevice {}* RTCDevice; + +/*! \brief Creates a new Embree device. + + Creates a new Embree device to be used by the application. An + application typically creates only a single Embree device, but it is + valid to use multiple devices inside an application. A configuration + string can be passed at construction time, that allows to configure + implementation specific parameters. If this string is NULL, a + default configuration is used. The following configuration flags are + supported by the Embree implementation of the API: + + verbose = num, // sets verbosity level (default is 0) + + If Embree is started on an unsupported CPU, rtcNewDevice will fail and + set the RTC_UNSUPPORTED_CPU error code. + +*/ +RTCORE_API RTCDevice rtcNewDevice(const char* cfg = NULL); + +/*! \brief Deletes an Embree device. + + Deletes the Embree device again. After deletion, all scene handles + are invalid. The application should invoke this call before + terminating. */ +RTCORE_API void rtcDeleteDevice(RTCDevice device); + +/*! \brief Initializes the Embree ray tracing core + + WARNING: This function is deprecated, use rtcNewDevice instead. + + Initializes the ray tracing core and passed some configuration + string. The configuration string allows to configure implementation + specific parameters. If this string is NULL, a default configuration + is used. The following configuration flags are supported by the + Embree implementation of the API: + + verbose = num, // sets verbosity level (default is 0) + + If Embree is started on an unsupported CPU, rtcInit will fail and + set the RTC_UNSUPPORTED_CPU error code. + +*/ +RTCORE_API RTCORE_DEPRECATED void rtcInit(const char* cfg = NULL); + +/*! \brief Shuts down Embree + + WARNING: This function is deprecated, use rtcDeleteDevice instead. + + Shuts down the ray tracing core. After shutdown, all scene handles + are invalid, and invoking any API call except rtcInit is not + allowed. The application should invoke this call before + terminating. It is safe to call rtcInit again after an rtcExit + call. */ +RTCORE_API RTCORE_DEPRECATED void rtcExit(); + +/*! \brief Parameters that can get configured using the rtcSetParameter functions. */ +enum RTCParameter { + RTC_SOFTWARE_CACHE_SIZE = 0, /*! Configures the software cache size (used + to cache subdivision surfaces for + instance). The size is specified as an + integer number of bytes. The software + cache cannot be configured during + rendering. (write only) */ + + RTC_CONFIG_INTERSECT1 = 1, //!< checks if rtcIntersect1 is supported (read only) + RTC_CONFIG_INTERSECT4 = 2, //!< checks if rtcIntersect4 is supported (read only) + RTC_CONFIG_INTERSECT8 = 3, //!< checks if rtcIntersect8 is supported (read only) + RTC_CONFIG_INTERSECT16 = 4, //!< checks if rtcIntersect16 is supported (read only) + RTC_CONFIG_INTERSECTN = 5, //!< checks if rtcIntersectN is supported (read only) + + RTC_CONFIG_RAY_MASK = 6, //!< checks if ray masks are supported (read only) + RTC_CONFIG_BACKFACE_CULLING = 7, //!< checks if backface culling is supported (read only) + RTC_CONFIG_INTERSECTION_FILTER = 8, //!< checks if intersection filters are enabled (read only) + RTC_CONFIG_INTERSECTION_FILTER_RESTORE = 9, //!< checks if intersection filters restores previous hit (read only) + RTC_CONFIG_BUFFER_STRIDE = 10, //!< checks if buffer strides are supported (read only) + RTC_CONFIG_IGNORE_INVALID_RAYS = 11, //!< checks if invalid rays are ignored (read only) + RTC_CONFIG_TASKING_SYSTEM = 12, //!< return used tasking system (0 = INTERNAL, 1 = TBB) (read only) + + RTC_CONFIG_VERSION_MAJOR = 13, //!< returns Embree major version (read only) + RTC_CONFIG_VERSION_MINOR = 14, //!< returns Embree minor version (read only) + RTC_CONFIG_VERSION_PATCH = 15, //!< returns Embree patch version (read only) + RTC_CONFIG_VERSION = 16, //!< returns Embree version as integer (e.g. Embree v2.8.2 -> 20802) (read only) +}; + +/*! \brief Configures some parameters. + WARNING: This function is deprecated, use rtcDeviceSetParameter1i instead. +*/ +RTCORE_API RTCORE_DEPRECATED void rtcSetParameter1i(const RTCParameter parm, ssize_t val); + +/*! \brief Reads some device parameter. + WARNING: This function is deprecated, use rtcDeviceGetParameter1i instead. +*/ +RTCORE_API RTCORE_DEPRECATED ssize_t rtcGetParameter1i(const RTCParameter parm); + +/*! \brief Configures some device parameters. */ +RTCORE_API void rtcDeviceSetParameter1i(RTCDevice device, const RTCParameter parm, ssize_t val); + +/*! \brief Reads some device parameter. */ +RTCORE_API ssize_t rtcDeviceGetParameter1i(RTCDevice device, const RTCParameter parm); + +/*! \brief Error codes returned by the rtcGetError function. */ +enum RTCError { + RTC_NO_ERROR = 0, //!< No error has been recorded. + RTC_UNKNOWN_ERROR = 1, //!< An unknown error has occured. + RTC_INVALID_ARGUMENT = 2, //!< An invalid argument is specified + RTC_INVALID_OPERATION = 3, //!< The operation is not allowed for the specified object. + RTC_OUT_OF_MEMORY = 4, //!< There is not enough memory left to execute the command. + RTC_UNSUPPORTED_CPU = 5, //!< The CPU is not supported as it does not support SSE2. + RTC_CANCELLED = 6, //!< The user has cancelled the operation through the RTC_PROGRESS_MONITOR_FUNCTION callback +}; + +/*! \brief Returns the value of the per-thread error flag. + + WARNING: This function is deprecated, use rtcDeviceGetError instead. + + If an error occurs this flag is set to an error code if it stores no + previous error. The rtcGetError function reads and returns the + currently stored error and clears the error flag again. */ +RTCORE_API RTCORE_DEPRECATED RTCError rtcGetError(); + +/*! \brief Returns the value of the per-thread error flag. + + If an error occurs this flag is set to an error code if it stores no + previous error. The rtcGetError function reads and returns the + currently stored error and clears the error flag again. */ +RTCORE_API RTCError rtcDeviceGetError(RTCDevice device); + +/*! \brief Type of error callback function. */ +typedef void (*RTCErrorFunc)(const RTCError code, const char* str); +RTCORE_DEPRECATED typedef RTCErrorFunc RTC_ERROR_FUNCTION; + +/*! \brief Sets a callback function that is called whenever an error occurs. + WARNING: This function is deprecated, use rtcDeviceSetErrorFunction instead. + */ +RTCORE_API RTCORE_DEPRECATED void rtcSetErrorFunction(RTCErrorFunc func); + +/*! \brief Sets a callback function that is called whenever an error occurs. */ +RTCORE_API void rtcDeviceSetErrorFunction(RTCDevice device, RTCErrorFunc func); + +/*! \brief Type of memory consumption callback function. */ +typedef bool (*RTCMemoryMonitorFunc)(const ssize_t bytes, const bool post); +RTCORE_DEPRECATED typedef RTCMemoryMonitorFunc RTC_MEMORY_MONITOR_FUNCTION; + +/*! \brief Sets the memory consumption callback function which is + * called before or after the library allocates or frees memory. + WARNING: This function is deprecated, use rtcDeviceSetMemoryMonitorFunction instead. +*/ +RTCORE_API RTCORE_DEPRECATED void rtcSetMemoryMonitorFunction(RTCMemoryMonitorFunc func); + +/*! \brief Sets the memory consumption callback function which is + * called before or after the library allocates or frees memory. */ +RTCORE_API void rtcDeviceSetMemoryMonitorFunction(RTCDevice device, RTCMemoryMonitorFunc func); + +/*! \brief Implementation specific (do not call). + + This function is implementation specific and only for debugging + purposes. Do not call it. */ +RTCORE_API RTCORE_DEPRECATED void rtcDebug(); // FIXME: remove + +#include "rtcore_scene.h" +#include "rtcore_geometry.h" +#include "rtcore_geometry_user.h" + +/*! \brief Helper to easily combing scene flags */ +inline RTCSceneFlags operator|(const RTCSceneFlags a, const RTCSceneFlags b) { + return (RTCSceneFlags)((size_t)a | (size_t)b); +} + +/*! \brief Helper to easily combing algorithm flags */ +inline RTCAlgorithmFlags operator|(const RTCAlgorithmFlags a, const RTCAlgorithmFlags b) { + return (RTCAlgorithmFlags)((size_t)a | (size_t)b); +} + +/*! \brief Helper to easily combing geometry flags */ +inline RTCGeometryFlags operator|(const RTCGeometryFlags a, const RTCGeometryFlags b) { + return (RTCGeometryFlags)((size_t)a | (size_t)b); +} + +/*! \} */ + +#endif diff --git a/src/igl/embree/embree2/rtcore.isph b/src/igl/embree/embree2/rtcore.isph new file mode 100644 index 0000000000..f46f052520 --- /dev/null +++ b/src/igl/embree/embree2/rtcore.isph @@ -0,0 +1,220 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTCORE_ISPH__ +#define __RTCORE_ISPH__ + +#ifdef _WIN32 +# define RTCORE_ALIGN(...) // FIXME: need to specify alignment +#else +# define RTCORE_ALIGN(...) // FIXME: need to specify alignment +#endif + +#define RTCORE_DEPRECATED // FIXME: deprecation not supported by ISPC + +/*! Embree API version */ +#define RTCORE_VERSION_MAJOR 2 +#define RTCORE_VERSION_MINOR 9 +#define RTCORE_VERSION_PATCH 0 +#define RTCORE_VERSION 20900 + +/*! \file rtcore.isph Defines the Embree Ray Tracing Kernel API for ISPC. + + This file defines the Embree ray tracing kernel API for C and + C++. The user is supposed to include this file, and alternatively + the rtcore_ray.isph file, but none of the other .isph files in this + folder. */ + +/*! \{ */ + +/*! Axis aligned bounding box representation */ +RTCORE_ALIGN(16) struct RTCBounds +{ + float lower_x, lower_y, lower_z, align0; + float upper_x, upper_y, upper_z, align1; +}; + +/*! \brief Defines an opaque device type */ +typedef uniform struct __RTCDevice {}* uniform RTCDevice; + +/*! \brief Creates a new Embree device. + + Creates a new Embree device to be used by the application. An + application typically creates only a single Embree device, but it is + valid to use multiple devices inside an application. A configuration + string can be passed at construction time, that allows to configure + implementation specific parameters. If this string is NULL, a + default configuration is used. The following configuration flags are + supported by the Embree implementation of the API: + + verbose = num, // sets verbosity level (default is 0) + + If Embree is started on an unsupported CPU, rtcNewDevice will fail and + set the RTC_UNSUPPORTED_CPU error code. + +*/ +RTCDevice rtcNewDevice(const uniform int8* uniform cfg = NULL); + +/*! \brief Deletes an Embree device. + + Deletes the Embree device again. After deletion, all scene handles + are invalid. The application should invoke this call before + terminating. */ +void rtcDeleteDevice(RTCDevice device); + +/*! \brief Initializes the Embree ray tracing core + + WARNING: This function is deprecated, use rtcNewDevice instead. + + Initializes the ray tracing core and passed some configuration + string. The configuration string allows to configure implementation + specific parameters. If this string is NULL, a default configuration + is used. The following configuration flags are supported by the + Embree implementation of the API: + + verbose = num, // sets verbosity level (default is 0) + + If Embree is started on an unsupported CPU, rtcInit will fail and + set the RTC_UNSUPPORTED_CPU error code. + +*/ +RTCORE_DEPRECATED void rtcInit(const uniform int8* uniform cfg = NULL); + +/*! \brief Shuts down Embree. + + WARNING: This function is deprecated, use rtcDeleteDevice instead. + + Shuts down the ray tracing core. After shutdown, all scene handles + are invalid, and invoking any API call except rtcInit is not + allowed. The application should invoke this call before + terminating. It is safe to call rtcInit again after an rtcExit + call. */ +RTCORE_DEPRECATED void rtcExit(); + +/*! \brief Parameters that can get configured using the rtcSetParameter functions. */ +enum RTCParameter { + RTC_SOFTWARE_CACHE_SIZE = 0, /*! Configures the software cache size (used + to cache subdivision surfaces for + instance). The size is specified as an + integer number of bytes. The software + cache cannot be configured during + rendering. (write only) */ + + RTC_CONFIG_INTERSECT1 = 1, //!< checks if rtcIntersect1 is supported (read only) + RTC_CONFIG_INTERSECT4 = 2, //!< checks if rtcIntersect4 is supported (read only) + RTC_CONFIG_INTERSECT8 = 3, //!< checks if rtcIntersect8 is supported (read only) + RTC_CONFIG_INTERSECT16 = 4, //!< checks if rtcIntersect16 is supported (read only) + RTC_CONFIG_INTERSECTN = 5, //!< checks if rtcIntersectN is supported (read only) + + RTC_CONFIG_RAY_MASK = 6, //!< checks if ray masks are supported (read only) + RTC_CONFIG_BACKFACE_CULLING = 7, //!< checks if backface culling is supported (read only) + RTC_CONFIG_INTERSECTION_FILTER = 8, //!< checks if intersection filters are enabled (read only) + RTC_CONFIG_INTERSECTION_FILTER_RESTORE = 9, //!< checks if intersection filters restores previous hit (read only) + RTC_CONFIG_BUFFER_STRIDE = 10, //!< checks if buffer strides are supported (read only) + RTC_CONFIG_IGNORE_INVALID_RAYS = 11, //!< checks if invalid rays are ignored (read only) + RTC_CONFIG_TASKING_SYSTEM = 12, //!< return used tasking system (0 = INTERNAL, 1 = TBB) (read only) + + + RTC_CONFIG_VERSION_MAJOR = 13, //!< returns Embree major version (read only) + RTC_CONFIG_VERSION_MINOR = 14, //!< returns Embree minor version (read only) + RTC_CONFIG_VERSION_PATCH = 15, //!< returns Embree patch version (read only) + RTC_CONFIG_VERSION = 16, //!< returns Embree version as integer (e.g. Embree v2.8.2 -> 20802) (read only) +}; + +/*! \brief Configures some parameters. + WARNING: This function is deprecated, use rtcDeviceSetParameter1i instead. +*/ +RTCORE_DEPRECATED void rtcSetParameter1i(const uniform RTCParameter parm, uniform size_t val); // FIXME: should be ssize_t + +/*! \brief Reads some parameters. + WARNING: This function is deprecated, use rtcDeviceGetParameter1i instead. +*/ +uniform size_t rtcGetParameter1i(const uniform RTCParameter parm); // FIXME: should return ssize_t + +/*! \brief Configures some device parameters.*/ +void rtcDeviceSetParameter1i(RTCDevice device, const uniform RTCParameter parm, uniform size_t val); // FIXME: should be ssize_t + +/*! \brief Reads some device parameters. */ +uniform size_t rtcDeviceGetParameter1i(RTCDevice device, const uniform RTCParameter parm); // FIXME: should return ssize_t + +/*! \brief Error codes returned by the rtcGetError function. */ +enum RTCError { + RTC_NO_ERROR = 0, //!< No error has been recorded. + RTC_UNKNOWN_ERROR = 1, //!< An unknown error has occured. + RTC_INVALID_ARGUMENT = 2, //!< An invalid argument is specified + RTC_INVALID_OPERATION = 3, //!< The operation is not allowed for the specified object. + RTC_OUT_OF_MEMORY = 4, //!< There is not enough memory left to execute the command. + RTC_UNSUPPORTED_CPU = 5, //!< The CPU is not supported as it does not support SSE2. + RTC_CANCELLED = 6 //!< The user has cancelled the operation through the RTCProgressMonitorFunc callback +}; + +/*! \brief Returns the value of the per-thread error flag. + + WARNING: This function is deprecated, use rtcDeviceGetError instead. + + If an error occurs this flag is set to an error code if it stores no + previous error. The rtcGetError function reads and returns the + currently stored error and clears the error flag again. */ +RTCORE_DEPRECATED uniform RTCError rtcGetError(); + +/*! \brief Returns the value of the per-thread error flag. + + If an error occurs this flag is set to an error code if it stores no + previous error. The rtcGetError function reads and returns the + currently stored error and clears the error flag again. */ +uniform RTCError rtcDeviceGetError(RTCDevice device); + +/*! \brief Type of error callback function. */ +typedef void (*uniform RTCErrorFunc)(const uniform RTCError code, const uniform int8* uniform str); +RTCORE_DEPRECATED typedef RTCErrorFunc RTC_ERROR_FUNCTION; + +/*! \brief Sets a callback function that is called whenever an error occurs. + WARNING: This function is deprecated, use rtcDeviceSetErrorFunction instead. +*/ +RTCORE_DEPRECATED void rtcSetErrorFunction(uniform RTCErrorFunc func); + +/*! \brief Sets a callback function that is called whenever an error occurs. */ +void rtcDeviceSetErrorFunction(RTCDevice device, uniform RTCErrorFunc func); + +/*! \brief Type of memory consumption callback function. */ +typedef uniform bool (*uniform RTCMemoryMonitorFunc)(const uniform size_t bytes, const uniform bool post); // FIXME: should be ssize_t +RTCORE_DEPRECATED typedef RTCMemoryMonitorFunc RTC_MEMORY_MONITOR_FUNCTION; + +/*! \brief Sets the memory consumption callback function which is + * called before the library allocates or after the library frees + * memory. + * WARNING: This function is deprecated, use rtcDeviceSetMemoryMonitorFunction instead. +*/ +RTCORE_DEPRECATED void rtcSetMemoryMonitorFunction(RTCMemoryMonitorFunc func); + +/*! \brief Sets the memory consumption callback function which is + * called before the library allocates or after the library frees + * memory. */ +void rtcDeviceSetMemoryMonitorFunction(RTCDevice device, RTCMemoryMonitorFunc func); + +/*! \brief Implementation specific (do not call). + + This function is implementation specific and only for debugging + purposes. Do not call it. */ +RTCORE_DEPRECATED void rtcDebug(); // FIXME: remove + +#include "rtcore_scene.isph" +#include "rtcore_geometry.isph" +#include "rtcore_geometry_user.isph" + +/*! \} */ + +#endif diff --git a/src/igl/embree/embree2/rtcore_geometry.h b/src/igl/embree/embree2/rtcore_geometry.h new file mode 100644 index 0000000000..5b80130fc2 --- /dev/null +++ b/src/igl/embree/embree2/rtcore_geometry.h @@ -0,0 +1,483 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTCORE_GEOMETRY_H__ +#define __RTCORE_GEOMETRY_H__ + +/*! \ingroup embree_kernel_api */ +/*! \{ */ + +/*! invalid geometry ID */ +#define RTC_INVALID_GEOMETRY_ID ((unsigned)-1) + +/*! \brief Specifies the type of buffers when mapping buffers */ +enum RTCBufferType { + RTC_INDEX_BUFFER = 0x01000000, + + RTC_VERTEX_BUFFER = 0x02000000, + RTC_VERTEX_BUFFER0 = 0x02000000, + RTC_VERTEX_BUFFER1 = 0x02000001, + + RTC_USER_VERTEX_BUFFER = 0x02100000, + RTC_USER_VERTEX_BUFFER0 = 0x02100000, + RTC_USER_VERTEX_BUFFER1 = 0x02100001, + + RTC_FACE_BUFFER = 0x03000000, + RTC_LEVEL_BUFFER = 0x04000001, + + RTC_EDGE_CREASE_INDEX_BUFFER = 0x05000000, + RTC_EDGE_CREASE_WEIGHT_BUFFER = 0x06000000, + + RTC_VERTEX_CREASE_INDEX_BUFFER = 0x07000000, + RTC_VERTEX_CREASE_WEIGHT_BUFFER = 0x08000000, + + RTC_HOLE_BUFFER = 0x09000001, +}; + +/*! \brief Supported types of matrix layout for functions involving matrices */ +enum RTCMatrixType { + RTC_MATRIX_ROW_MAJOR = 0, + RTC_MATRIX_COLUMN_MAJOR = 1, + RTC_MATRIX_COLUMN_MAJOR_ALIGNED16 = 2, +}; + +/*! \brief Supported geometry flags to specify handling in dynamic scenes. */ +enum RTCGeometryFlags +{ + RTC_GEOMETRY_STATIC = 0, //!< specifies static geometry that will change rarely + RTC_GEOMETRY_DEFORMABLE = 1, //!< specifies dynamic geometry with deformable motion (BVH refit possible) + RTC_GEOMETRY_DYNAMIC = 2, //!< specifies dynamic geometry with arbitrary motion (BVH refit not possible) +}; + +/*! \brief Boundary interpolation mode for subdivision surfaces */ +enum RTCBoundaryMode +{ + RTC_BOUNDARY_NONE = 0, //!< ignores border patches + RTC_BOUNDARY_EDGE_ONLY = 1, //!< soft boundary (default) + RTC_BOUNDARY_EDGE_AND_CORNER = 2 //!< boundary corner vertices are sharp vertices +}; + +/*! Intersection filter function for single rays. */ +typedef void (*RTCFilterFunc)(void* ptr, /*!< pointer to user data */ + RTCRay& ray /*!< intersection to filter */); + +/*! Intersection filter function for ray packets of size 4. */ +typedef void (*RTCFilterFunc4)(const void* valid, /*!< pointer to valid mask */ + void* ptr, /*!< pointer to user data */ + RTCRay4& ray /*!< intersection to filter */); + +/*! Intersection filter function for ray packets of size 8. */ +typedef void (*RTCFilterFunc8)(const void* valid, /*!< pointer to valid mask */ + void* ptr, /*!< pointer to user data */ + RTCRay8& ray /*!< intersection to filter */); + +/*! Intersection filter function for ray packets of size 16. */ +typedef void (*RTCFilterFunc16)(const void* valid, /*!< pointer to valid mask */ + void* ptr, /*!< pointer to user data */ + RTCRay16& ray /*!< intersection to filter */); + +/*! Displacement mapping function. */ +typedef void (*RTCDisplacementFunc)(void* ptr, /*!< pointer to user data of geometry */ + unsigned geomID, /*!< ID of geometry to displace */ + unsigned primID, /*!< ID of primitive of geometry to displace */ + const float* u, /*!< u coordinates (source) */ + const float* v, /*!< v coordinates (source) */ + const float* nx, /*!< x coordinates of normalized normal at point to displace (source) */ + const float* ny, /*!< y coordinates of normalized normal at point to displace (source) */ + const float* nz, /*!< z coordinates of normalized normal at point to displace (source) */ + float* px, /*!< x coordinates of points to displace (source and target) */ + float* py, /*!< y coordinates of points to displace (source and target) */ + float* pz, /*!< z coordinates of points to displace (source and target) */ + size_t N /*!< number of points to displace */ ); + +/*! \brief Creates a new scene instance. + + A scene instance contains a reference to a scene to instantiate and + the transformation to instantiate the scene with. An implementation + will typically transform the ray with the inverse of the provided + transformation and continue traversing the ray through the provided + scene. If any geometry is hit, the instance ID (instID) member of + the ray will get set to the geometry ID of the instance. */ +RTCORE_API unsigned rtcNewInstance (RTCScene target, //!< the scene the instance belongs to + RTCScene source //!< the scene to instantiate + ); + +/*! \brief Creates a new scene instance. + + A scene instance contains a reference to a scene to instantiate and + the transformation to instantiate the scene with. For motion blurred + instances, a number of timesteps can get specified (currently only 1 + or 2 timesteps are supported). An implementation will typically + transform the ray with the inverse of the provided transformation + and continue traversing the ray through the provided scene. If any + geometry is hit, the instance ID (instID) member of the ray will get + set to the geometry ID of the instance. */ +RTCORE_API unsigned rtcNewInstance2 (RTCScene target, //!< the scene the instance belongs to + RTCScene source, //!< the scene to instantiate + size_t numTimeSteps = 1); //!< number of timesteps, one matrix per timestep + +/*! \brief Sets transformation of the instance */ +RTCORE_API void rtcSetTransform (RTCScene scene, //!< scene handle + unsigned geomID, //!< ID of geometry + RTCMatrixType layout, //!< layout of transformation matrix + const float* xfm //!< pointer to transformation matrix + ); + + +/*! \brief Sets transformation of the instance for specified timestep */ +RTCORE_API void rtcSetTransform2 (RTCScene scene, //!< scene handle + unsigned int geomID, //!< ID of geometry + RTCMatrixType layout, //!< layout of transformation matrix + const float* xfm, //!< pointer to transformation matrix + size_t timeStep = 0 //!< timestep to set the matrix for + ); + +/*! \brief Creates a new triangle mesh. The number of triangles + (numTriangles), number of vertices (numVertices), and number of time + steps (1 for normal meshes, and 2 for linear motion blur), have to + get specified. The triangle indices can be set be mapping and + writing to the index buffer (RTC_INDEX_BUFFER) and the triangle + vertices can be set by mapping and writing into the vertex buffer + (RTC_VERTEX_BUFFER). In case of linear motion blur, two vertex + buffers have to get filled (RTC_VERTEX_BUFFER0, RTC_VERTEX_BUFFER1), + one for each time step. The index buffer has the default layout of + three 32 bit integer indices for each triangle. An index points to + the ith vertex. The vertex buffer stores single precision x,y,z + floating point coordinates aligned to 16 bytes. The value of the 4th + float used for alignment can be arbitrary. */ +RTCORE_API unsigned rtcNewTriangleMesh (RTCScene scene, //!< the scene the mesh belongs to + RTCGeometryFlags flags, //!< geometry flags + size_t numTriangles, //!< number of triangles + size_t numVertices, //!< number of vertices + size_t numTimeSteps = 1 //!< number of motion blur time steps + ); + + +/*! \brief Creates a new quad mesh. The number of quads + (numQuads), number of vertices (numVertices), and number of time + steps (1 for normal meshes, and 2 for linear motion blur), have to + get specified. The quad indices can be set be mapping and + writing to the index buffer (RTC_INDEX_BUFFER) and the quad + vertices can be set by mapping and writing into the vertex buffer + (RTC_VERTEX_BUFFER). In case of linear motion blur, two vertex + buffers have to get filled (RTC_VERTEX_BUFFER0, RTC_VERTEX_BUFFER1), + one for each time step. The index buffer has the default layout of + three 32 bit integer indices for each quad. An index points to + the ith vertex. The vertex buffer stores single precision x,y,z + floating point coordinates aligned to 16 bytes. The value of the 4th + float used for alignment can be arbitrary. */ +RTCORE_API unsigned rtcNewQuadMesh (RTCScene scene, //!< the scene the mesh belongs to + RTCGeometryFlags flags, //!< geometry flags + size_t numQuads, //!< number of quads + size_t numVertices, //!< number of vertices + size_t numTimeSteps = 1 //!< number of motion blur time steps + ); + +/*! \brief Creates a new subdivision mesh. The number of faces + (numFaces), edges/indices (numEdges), vertices (numVertices), edge + creases (numEdgeCreases), vertex creases (numVertexCreases), holes + (numHoles), and time steps (numTimeSteps) have to get speficied at + construction time. + + The following buffers have to get filled by the application: the face + buffer (RTC_FACE_BUFFER) contains the number edges/indices (3 or 4) + of each of the numFaces faces, the index buffer (RTC_INDEX_BUFFER) + contains multiple (3 or 4) 32bit vertex indices for each face and + numEdges indices in total, the vertex buffer (RTC_VERTEX_BUFFER) + stores numVertices vertices as single precision x,y,z floating point + coordinates aligned to 16 bytes. The value of the 4th float used for + alignment can be arbitrary. + + Optionally, the application can fill the hole buffer + (RTC_HOLE_BUFFER) with numHoles many 32 bit indices of faces that + should be considered non-existing. + + Optionally, the application can fill the level buffer + (RTC_LEVEL_BUFFER) with a tessellation level for each of the numEdges + edges. The subdivision level is a positive floating point value, that + specifies how many quads along the edge should get generated during + tessellation. The tessellation level is a lower bound, thus the + implementation is free to choose a larger level. If no level buffer + is specified a level of 1 is used. + + Optionally, the application can fill the sparse edge crease buffers + to make some edges appear sharper. The edge crease index buffer + (RTC_EDGE_CREASE_INDEX_BUFFER) contains numEdgeCreases many pairs of + 32 bit vertex indices that specify unoriented edges. The edge crease + weight buffer (RTC_EDGE_CREASE_WEIGHT_BUFFER) stores for each of + theses crease edges a positive floating point weight. The larger this + weight, the sharper the edge. Specifying a weight of infinify is + supported and marks an edge as infinitely sharp. Storing an edge + multiple times with the same crease weight is allowed, but has lower + performance. Storing the an edge multiple times with different + crease weights results in undefined behaviour. For a stored edge + (i,j), the reverse direction edges (j,i) does not have to get stored, + as both are considered the same edge. + + Optionally, the application can fill the sparse vertex crease buffers + to make some vertices appear sharper. The vertex crease index buffer + (RTC_VERTEX_CREASE_INDEX_BUFFER), contains numVertexCreases many 32 + bit vertex indices to speficy a set of vertices. The vertex crease + weight buffer (RTC_VERTEX_CREASE_WEIGHT_BUFFER) specifies for each of + these vertices a positive floating point weight. The larger this + weight, the sharper the vertex. Specifying a weight of infinity is + supported and makes the vertex infinitely sharp. Storing a vertex + multiple times with the same crease weight is allowed, but has lower + performance. Storing a vertex multiple times with different crease + weights results in undefined behaviour. + +*/ +RTCORE_API unsigned rtcNewSubdivisionMesh (RTCScene scene, //!< the scene the mesh belongs to + RTCGeometryFlags flags, //!< geometry flags + size_t numFaces, //!< number of faces + size_t numEdges, //!< number of edges + size_t numVertices, //!< number of vertices + size_t numEdgeCreases, //!< number of edge creases + size_t numVertexCreases, //!< number of vertex creases + size_t numHoles, //!< number of holes + size_t numTimeSteps = 1 //!< number of motion blur time steps + ); + +/*! \brief Creates a new hair geometry, consisting of multiple hairs + represented as cubic bezier curves with varying radii. The number of + curves (numCurves), number of vertices (numVertices), and number of + time steps (1 for normal curves, and 2 for linear motion blur), have + to get specified at construction time. Further, the curve index + buffer (RTC_INDEX_BUFFER) and the curve vertex buffer + (RTC_VERTEX_BUFFER) have to get set by mapping and writing to the + appropiate buffers. In case of linear motion blur, two vertex + buffers have to get filled (RTC_VERTEX_BUFFER0, RTC_VERTEX_BUFFER1), + one for each time step. The index buffer has the default layout of a + single 32 bit integer index for each curve, that references the + start vertex of the curve. The vertex buffer stores 4 control points + per curve, each such control point consists of a single precision + (x,y,z) position and radius, stored in that order in + memory. Individual hairs are considered to be subpixel sized which + allows the implementation to approximate the intersection + calculation. This in particular means that zooming onto one hair + might show geometric artefacts. */ +RTCORE_API unsigned rtcNewHairGeometry (RTCScene scene, //!< the scene the curves belong to + RTCGeometryFlags flags, //!< geometry flags + size_t numCurves, //!< number of curves + size_t numVertices, //!< number of vertices + size_t numTimeSteps = 1 //!< number of motion blur time steps + ); + +/*! Sets a uniform tessellation rate for subdiv meshes and hair + * geometry. For subdivision meshes the RTC_LEVEL_BUFFER can also be used + * optionally to set a different tessellation rate per edge.*/ +RTCORE_API void rtcSetTessellationRate (RTCScene scene, unsigned geomID, float tessellationRate); + +/*! \brief Creates a new line segment geometry, consisting of multiple + segments with varying radii. The number of line segments (numSegments), + number of vertices (numVertices), and number of time steps (1 for + normal line segments, and 2 for linear motion blur), have to get + specified at construction time. Further, the segment index buffer + (RTC_INDEX_BUFFER) and the segment vertex buffer (RTC_VERTEX_BUFFER) + have to get set by mapping and writing to the appropiate buffers. In + case of linear motion blur, two vertex buffers have to get filled + (RTC_VERTEX_BUFFER0, RTC_VERTEX_BUFFER1), one for each time step. The + index buffer has the default layout of a single 32 bit integer index + for each line segment, that references the start vertex of the segment. + The vertex buffer stores 2 end points per line segment, each such point + consists of a single precision (x,y,z) position and radius, stored in + that order in memory. Individual segments are considered to be subpixel + sized which allows the implementation to approximate the intersection + calculation. This in particular means that zooming onto one line segment + might show geometric artefacts. */ +RTCORE_API unsigned rtcNewLineSegments (RTCScene scene, //!< the scene the line segments belong to + RTCGeometryFlags flags, //!< geometry flags + size_t numSegments, //!< number of line segments + size_t numVertices, //!< number of vertices + size_t numTimeSteps = 1 //!< number of motion blur time steps + ); + +/*! \brief Sets 32 bit ray mask. */ +RTCORE_API void rtcSetMask (RTCScene scene, unsigned geomID, int mask); + +/*! \brief Sets boundary interpolation mode for subdivision surfaces */ +RTCORE_API void rtcSetBoundaryMode(RTCScene scene, unsigned geomID, RTCBoundaryMode mode); + +/*! \brief Maps specified buffer. This function can be used to set index and + * vertex buffers of geometries. */ +RTCORE_API void* rtcMapBuffer(RTCScene scene, unsigned geomID, RTCBufferType type); + +/*! \brief Unmaps specified buffer. + + A buffer has to be unmapped before the rtcEnable, rtcDisable, + rtcUpdate, or rtcDeleteGeometry calls are executed. */ +RTCORE_API void rtcUnmapBuffer(RTCScene scene, unsigned geomID, RTCBufferType type); + +/*! \brief Shares a data buffer between the application and + * Embree. The passed buffer is used by Embree to store index and + * vertex data. It has to remain valid as long as the mesh exists, + * and the user is responsible to free the data when the mesh gets + * deleted. One can optionally speficy a byte offset and byte stride + * of the elements stored inside the buffer. The addresses + * ptr+offset+i*stride have to be aligned to 4 bytes on Xeon CPUs and + * 16 bytes on Xeon Phi accelerators. For vertex buffers, the 4 bytes + * after the z-coordinate of the last vertex have to be readable memory, + * thus padding is required for some layouts. If this function is not + * called, Embree will allocate and manage buffers of the default + * layout. */ +RTCORE_API void rtcSetBuffer(RTCScene scene, unsigned geomID, RTCBufferType type, + const void* ptr, size_t byteOffset, size_t byteStride); + +/*! \brief Enable geometry. Enabled geometry can be hit by a ray. */ +RTCORE_API void rtcEnable (RTCScene scene, unsigned geomID); + +/*! \brief Update all geometry buffers. + + Each time geometry buffers got modified, the user has to call some + update function to tell the ray tracing engine which buffers got + modified. The rtcUpdate function taggs each geometry buffer of the + specified geometry as modified. */ +RTCORE_API void rtcUpdate (RTCScene scene, unsigned geomID); + +/*! \brief Update spefific geometry buffer. + + Each time geometry buffers got modified, the user has to call some + update function to tell the ray tracing engine which buffers got + modified. The rtcUpdateBuffer function taggs a specific buffer of + some geometry as modified. */ +RTCORE_API void rtcUpdateBuffer (RTCScene scene, unsigned geomID, RTCBufferType type); + +/*! \brief Disable geometry. + + Disabled geometry is not hit by any ray. Disabling and enabling + geometry gives higher performance than deleting and recreating + geometry. */ +RTCORE_API void rtcDisable (RTCScene scene, unsigned geomID); + +/*! \brief Sets the displacement function. */ +RTCORE_API void rtcSetDisplacementFunction (RTCScene scene, unsigned geomID, RTCDisplacementFunc func, RTCBounds* bounds); + +/*! \brief Sets the intersection filter function for single rays. */ +RTCORE_API void rtcSetIntersectionFilterFunction (RTCScene scene, unsigned geomID, RTCFilterFunc func); + +/*! \brief Sets the intersection filter function for ray packets of size 4. */ +RTCORE_API void rtcSetIntersectionFilterFunction4 (RTCScene scene, unsigned geomID, RTCFilterFunc4 func); + +/*! \brief Sets the intersection filter function for ray packets of size 8. */ +RTCORE_API void rtcSetIntersectionFilterFunction8 (RTCScene scene, unsigned geomID, RTCFilterFunc8 func); + +/*! \brief Sets the intersection filter function for ray packets of size 16. */ +RTCORE_API void rtcSetIntersectionFilterFunction16 (RTCScene scene, unsigned geomID, RTCFilterFunc16 func); + +/*! \brief Sets the occlusion filter function for single rays. */ +RTCORE_API void rtcSetOcclusionFilterFunction (RTCScene scene, unsigned geomID, RTCFilterFunc func); + +/*! \brief Sets the occlusion filter function for ray packets of size 4. */ +RTCORE_API void rtcSetOcclusionFilterFunction4 (RTCScene scene, unsigned geomID, RTCFilterFunc4 func); + +/*! \brief Sets the occlusion filter function for ray packets of size 8. */ +RTCORE_API void rtcSetOcclusionFilterFunction8 (RTCScene scene, unsigned geomID, RTCFilterFunc8 func); + +/*! \brief Sets the occlusion filter function for ray packets of size 16. */ +RTCORE_API void rtcSetOcclusionFilterFunction16 (RTCScene scene, unsigned geomID, RTCFilterFunc16 func); + +/*! Set pointer for user defined data per geometry. Invokations + * of the various user intersect and occluded functions get passed + * this data pointer when called. */ +RTCORE_API void rtcSetUserData (RTCScene scene, unsigned geomID, void* ptr); + +/*! Get pointer for user defined data per geometry based on geomID. */ +RTCORE_API void* rtcGetUserData (RTCScene scene, unsigned geomID); + +/*! Interpolates user data to some u/v location. The data buffer + * specifies per vertex data to interpolate and can be one of the + * RTC_VERTEX_BUFFER0/1 or RTC_USER_VERTEX_BUFFER0/1 and has to + * contain numFloats floating point values to interpolate for each + * vertex of the geometry. The dP array will get filled with the + * interpolated data and the dPdu and dPdv arrays with the u and v + * derivative of the interpolation. If the pointers dP is NULL, the + * value will not get calculated. If dPdu and dPdv are NULL the + * derivatives will not get calculated. Both dPdu and dPdv have to be + * either valid or NULL. The buffer has to be padded at the end such + * that the last element can be read safely using SSE + * instructions. */ +RTCORE_API void rtcInterpolate(RTCScene scene, unsigned geomID, unsigned primID, float u, float v, RTCBufferType buffer, + float* P, float* dPdu, float* dPdv, size_t numFloats); + +/*! Interpolates user data to some u/v location. The data buffer + * specifies per vertex data to interpolate and can be one of the + * RTC_VERTEX_BUFFER0/1 or RTC_USER_VERTEX_BUFFER0/1 and has to + * contain numFloats floating point values to interpolate for each + * vertex of the geometry. The P array will get filled with the + * interpolated datam the dPdu and dPdv arrays with the u and v + * derivative of the interpolation, and the ddPdudu, ddPdvdv, and + * ddPdudv arrays with the respective second derivatives. One can + * disable 1) the calculation of the interpolated value by setting P + * to NULL, 2) the calculation of the 1st order derivatives by + * setting dPdu and dPdv to NULL, 3) the calculation of the second + * order derivatives by setting ddPdudu, ddPdvdv, and ddPdudv to + * NULL. The buffers have to be padded at the end such that the last + * element can be read or written safely using SSE instructions. */ +RTCORE_API void rtcInterpolate2(RTCScene scene, unsigned geomID, unsigned primID, float u, float v, RTCBufferType buffer, + float* P, float* dPdu, float* dPdv, float* ddPdudu, float* ddPdvdv, float* ddPdudv, size_t numFloats); + +/*! Interpolates user data to an array of u/v locations. The valid + * pointer points to an integer array that specified which entries in + * the u/v arrays are valid (-1 denotes valid, and 0 invalid). If the + * valid pointer is NULL all elements are considers valid. The data + * buffer specifies per vertex data to interpolate and can be one of + * the RTC_VERTEX_BUFFER0/1 or RTC_USER_VERTEX_BUFFER0/1 and has to + * contain numFloats floating point values to interpolate for each + * vertex of the geometry. The P array will get filled with the + * interpolated data, and the dPdu and dPdv arrays with the u and v + * derivative of the interpolation. If the pointers P is NULL, the + * value will not get calculated. If dPdu and dPdv are NULL the + * derivatives will not get calculated. Both dPdu and dPdv have to be + * either valid or NULL. These destination arrays are filled in + * structure of array (SoA) layout. The buffer has to be padded at + * the end such that the last element can be read safely using SSE + * instructions.*/ +RTCORE_API void rtcInterpolateN(RTCScene scene, unsigned geomID, + const void* valid, const unsigned* primIDs, const float* u, const float* v, size_t numUVs, + RTCBufferType buffer, + float* P, float* dPdu, float* dPdv, size_t numFloats); + +/*! Interpolates user data to an array of u/v locations. The valid + * pointer points to an integer array that specified which entries in + * the u/v arrays are valid (-1 denotes valid, and 0 invalid). If the + * valid pointer is NULL all elements are considers valid. The data + * buffer specifies per vertex data to interpolate and can be one of + * the RTC_VERTEX_BUFFER0/1 or RTC_USER_VERTEX_BUFFER0/1 and has to + * contain numFloats floating point values to interpolate for each + * vertex of the geometry. The P array will get filled with the + * interpolated datam the dPdu and dPdv arrays with the u and v + * derivative of the interpolation, and the ddPdudu, ddPdvdv, and + * ddPdudv arrays with the respective second derivatives. One can + * disable 1) the calculation of the interpolated value by setting P + * to NULL, 2) the calculation of the 1st order derivatives by + * setting dPdu and dPdv to NULL, 3) the calculation of the second + * order derivatives by setting ddPdudu, ddPdvdv, and ddPdudv to + * NULL. These destination arrays are filled in structure of array + * (SoA) layout. The buffer has to be padded at the end such that + * the last element can be read safely using SSE + * instructions. */ +RTCORE_API void rtcInterpolateN2(RTCScene scene, unsigned geomID, + const void* valid, const unsigned* primIDs, const float* u, const float* v, size_t numUVs, + RTCBufferType buffer, + float* P, float* dPdu, float* dPdv, float* ddPdudu, float* ddPdvdv, float* ddPdudv, size_t numFloats); + +/*! \brief Deletes the geometry. */ +RTCORE_API void rtcDeleteGeometry (RTCScene scene, unsigned geomID); + + +/*! @} */ + +#endif diff --git a/src/igl/embree/embree2/rtcore_geometry.isph b/src/igl/embree/embree2/rtcore_geometry.isph new file mode 100644 index 0000000000..b2fa68cfc8 --- /dev/null +++ b/src/igl/embree/embree2/rtcore_geometry.isph @@ -0,0 +1,405 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTCORE_GEOMETRY_ISPH__ +#define __RTCORE_GEOMETRY_ISPH__ + +/*! \ingroup embree_kernel_api_ispc */ +/*! \{ */ + +/*! invalid geometry ID */ +#define RTC_INVALID_GEOMETRY_ID ((uniform unsigned int)-1) + +/*! \brief Specifies the type of buffers when mapping buffers */ +enum RTCBufferType { + RTC_INDEX_BUFFER = 0x01000000, + + RTC_VERTEX_BUFFER = 0x02000000, + RTC_VERTEX_BUFFER0 = 0x02000000, + RTC_VERTEX_BUFFER1 = 0x02000001, + + RTC_USER_VERTEX_BUFFER = 0x02100000, + RTC_USER_VERTEX_BUFFER0 = 0x02100000, + RTC_USER_VERTEX_BUFFER1 = 0x02100001, + + RTC_FACE_BUFFER = 0x03000000, + RTC_LEVEL_BUFFER = 0x04000001, + + RTC_EDGE_CREASE_INDEX_BUFFER = 0x05000000, + RTC_EDGE_CREASE_WEIGHT_BUFFER = 0x06000000, + + RTC_VERTEX_CREASE_INDEX_BUFFER = 0x07000000, + RTC_VERTEX_CREASE_WEIGHT_BUFFER = 0x08000000, + + RTC_HOLE_BUFFER = 0x09000001, +}; + +/*! \brief Supported types of matrix layout for functions involving matrices */ +enum RTCMatrixType { + RTC_MATRIX_ROW_MAJOR = 0, + RTC_MATRIX_COLUMN_MAJOR = 1, + RTC_MATRIX_COLUMN_MAJOR_ALIGNED16 = 2, +}; + +/*! \brief Supported geometry flags to specify handling in dynamic scenes. */ +enum RTCGeometryFlags +{ + RTC_GEOMETRY_STATIC = 0, //!< specifies static geometry that will change rarely + RTC_GEOMETRY_DEFORMABLE = 1, //!< specifies dynamic geometry with deformable motion (BVH refit possible) + RTC_GEOMETRY_DYNAMIC = 2, //!< specifies dynamic geometry with arbitrary motion (BVH refit not possible) +}; + +/*! \brief Boundary interpolation mode for subdivision surfaces */ +enum RTCBoundaryMode +{ + RTC_BOUNDARY_NONE = 0, //!< ignores border patches + RTC_BOUNDARY_EDGE_ONLY = 1, //!< soft boundary (default) + RTC_BOUNDARY_EDGE_AND_CORNER = 2 //!< boundary corner vertices are sharp vertices +}; + +/*! Intersection filter function for uniform rays. */ +typedef void (*uniform RTCFilterFuncUniform)(void* uniform ptr, /*!< pointer to user data */ + uniform RTCRay1& ray /*!< intersection to filter */); + +/*! Intersection filter function for varying rays. */ +typedef void (*uniform RTCFilterFuncVarying)(void* uniform ptr, /*!< pointer to user data */ + varying RTCRay& ray /*!< intersection to filter */); + + +/*! \brief Creates a new scene instance. + + A scene instance contains a reference to a scene to instantiate and + the transformation to instantiate the scene with. An implementation + will typically transform the ray with the inverse of the provided + transformation and continue traversing the ray through the provided + scene. If any geometry is hit, the instance ID (instID) member of + the ray will get set to the geometry ID of the instance. */ +uniform unsigned int rtcNewInstance (RTCScene target, //!< the scene the instance belongs to + RTCScene source //!< the geometry to instantiate + ); + +/*! \brief Creates a new scene instance. + + A scene instance contains a reference to a scene to instantiate and + the transformation to instantiate the scene with. For motion blurred + instances, a number of timesteps can get specified (currently only 1 + or 2 timesteps are supported). An implementation will typically + transform the ray with the inverse of the provided transformation + and continue traversing the ray through the provided scene. If any + geometry is hit, the instance ID (instID) member of the ray will get + set to the geometry ID of the instance. */ +uniform unsigned rtcNewInstance2 (RTCScene target, //!< the scene the instance belongs to + RTCScene source, //!< the scene to instantiate + uniform size_t numTimeSteps = 1); //!< number of timesteps, one matrix per timestep + + +/*! \brief Sets transformation of the instance */ +void rtcSetTransform (RTCScene scene, //!< scene handle + uniform unsigned int geomID, //!< ID of geometry + uniform RTCMatrixType layout, //!< layout of transformation matrix + const uniform float* uniform xfm //!< pointer to transformation matrix + ); + +/*! \brief Sets transformation of the instance for specified timestep */ +void rtcSetTransform2 (RTCScene scene, //!< scene handle + uniform unsigned int geomID, //!< ID of geometry + uniform RTCMatrixType layout, //!< layout of transformation matrix + const uniform float* uniform xfm, //!< pointer to transformation matrix + uniform size_t timeStep = 0 //!< timestep to set the matrix for + ); + +/*! \brief Creates a new triangle mesh. The number of triangles + (numTriangles), number of vertices (numVertices), and number of time + steps (1 for normal meshes, and 2 for linear motion blur), have to + get specified. The triangle indices can be set be mapping and + writing to the index buffer (RTC_INDEX_BUFFER) and the triangle + vertices can be set by mapping and writing into the vertex buffer + (RTC_VERTEX_BUFFER). In case of linear motion blur, two vertex + buffers have to get filled (RTC_VERTEX_BUFFER0, RTC_VERTEX_BUFFER1), + one for each time step. The index buffer has the default layout of + three 32 bit integer indices for each triangle. An index points to + the ith vertex. The vertex buffer stores single precision x,y,z + floating point coordinates aligned to 16 bytes. The value of the 4th + float used for alignment can be arbitrary. */ +uniform unsigned int rtcNewTriangleMesh (RTCScene scene, //!< the scene the mesh belongs to + uniform RTCGeometryFlags flags, //!< geometry flags + uniform size_t numTriangles, //!< number of triangles + uniform size_t numVertices, //!< number of vertices + uniform size_t numTimeSteps = 1 //!< number of motion blur time steps + ); + +/*! \brief Creates a new quad mesh. The number of quads + (numQuads), number of vertices (numVertices), and number of time + steps (1 for normal meshes, and 2 for linear motion blur), have to + get specified. The quad indices can be set be mapping and + writing to the index buffer (RTC_INDEX_BUFFER) and the quad + vertices can be set by mapping and writing into the vertex buffer + (RTC_VERTEX_BUFFER). In case of linear motion blur, two vertex + buffers have to get filled (RTC_VERTEX_BUFFER0, RTC_VERTEX_BUFFER1), + one for each time step. The index buffer has the default layout of + three 32 bit integer indices for each quad. An index points to + the ith vertex. The vertex buffer stores single precision x,y,z + floating point coordinates aligned to 16 bytes. The value of the 4th + float used for alignment can be arbitrary. */ +uniform unsigned int rtcNewQuadMesh (RTCScene scene, //!< the scene the mesh belongs to + uniform RTCGeometryFlags flags, //!< geometry flags + uniform size_t numQuads, //!< number of quads + uniform size_t numVertices, //!< number of vertices + uniform size_t numTimeSteps = 1 //!< number of motion blur time steps + ); + +/*! \brief Creates a new subdivision mesh. The number of faces + (numFaces), edges/indices (numEdges), vertices (numVertices), edge + creases (numEdgeCreases), vertex creases (numVertexCreases), holes + (numHoles), and time steps (numTimeSteps) have to get speficied at + construction time. + + The following buffers have to get filled by the application: the face + buffer (RTC_FACE_BUFFER) contains the number edges/indices (3 or 4) + of each of the numFaces faces, the index buffer (RTC_INDEX_BUFFER) + contains multiple (3 or 4) 32bit vertex indices for each face and + numEdges indices in total, the vertex buffer (RTC_VERTEX_BUFFER) + stores numVertices vertices as single precision x,y,z floating point + coordinates aligned to 16 bytes. The value of the 4th float used for + alignment can be arbitrary. + + Optionally, the application can fill the hole buffer + (RTC_HOLE_BUFFER) with numHoles many 32 bit indices of faces that + should be considered non-existing. + + Optionally, the application can fill the level buffer + (RTC_LEVEL_BUFFER) with a tessellation level for each of the numEdges + edges. The subdivision level is a positive floating point value, that + specifies how many quads along the edge should get generated during + tessellation. The tessellation level is a lower bound, thus the + implementation is free to choose a larger level. If no level buffer + is specified a level of 1 is used. + + Optionally, the application can fill the sparse edge crease buffers + to make some edges appear sharper. The edge crease index buffer + (RTC_EDGE_CREASE_INDEX_BUFFER) contains numEdgeCreases many pairs of + 32 bit vertex indices that specify unoriented edges. The edge crease + weight buffer (RTC_EDGE_CREASE_WEIGHT_BUFFER) stores for each of + theses crease edges a positive floating point weight. The larger this + weight, the sharper the edge. Specifying a weight of infinify is + supported and marks an edge as infinitely sharp. Storing an edge + multiple times with the same crease weight is allowed, but has lower + performance. Storing the an edge multiple times with different + crease weights results in undefined behaviour. For a stored edge + (i,j), the reverse direction edges (j,i) does not have to get stored, + as both are considered the same edge. + + Optionally, the application can fill the sparse vertex crease buffers + to make some vertices appear sharper. The vertex crease index buffer + (RTC_VERTEX_CREASE_INDEX_BUFFER), contains numVertexCreases many 32 + bit vertex indices to speficy a set of vertices. The vertex crease + weight buffer (RTC_VERTEX_CREASE_WEIGHT_BUFFER) specifies for each of + these vertices a positive floating point weight. The larger this + weight, the sharper the vertex. Specifying a weight of infinity is + supported and makes the vertex infinitely sharp. Storing a vertex + multiple times with the same crease weight is allowed, but has lower + performance. Storing a vertex multiple times with different crease + weights results in undefined behaviour. + +*/ + +uniform unsigned int rtcNewSubdivisionMesh (RTCScene scene, //!< the scene the mesh belongs to + uniform RTCGeometryFlags flags, //!< geometry flags + uniform size_t numFaces, //!< number of faces + uniform size_t numEdges, //!< number of edges + uniform size_t numVertices, //!< number of vertices + uniform size_t numEdgeCreases, //!< number of edge creases + uniform size_t numVertexCreases, //!< number of vertex creases + uniform size_t numHoles, //!< number of holes + uniform size_t numTimeSteps = 1 //!< number of motion blur time steps + ); + +/*! \brief Creates a new hair geometry, consisting of multiple hairs + represented as cubic bezier curves with varying radii. The number of + curves (numCurves), number of vertices (numVertices), and number of + time steps (1 for normal curves, and 2 for linear motion blur), have + to get specified at construction time. Further, the curve index + buffer (RTC_INDEX_BUFFER) and the curve vertex buffer + (RTC_VERTEX_BUFFER) have to get set by mapping and writing to the + appropiate buffers. In case of linear motion blur, two vertex + buffers have to get filled (RTC_VERTEX_BUFFER0, RTC_VERTEX_BUFFER1), + one for each time step. The index buffer has the default layout of a + single 32 bit integer index for each curve, that references the + start vertex of the curve. The vertex buffer stores 4 control points + per curve, each such control point consists of a single precision + (x,y,z) position and radius, stored in that order in + memory. Individual hairs are considered to be subpixel sized which + allows the implementation to approximate the intersection + calculation. This in particular means that zooming onto one hair + might show geometric artefacts. */ +uniform unsigned int rtcNewHairGeometry (RTCScene scene, //!< the scene the curves belong to + uniform RTCGeometryFlags flags, //!< geometry flags + uniform size_t numCurves, //!< number of curves + uniform size_t numVertices, //!< number of vertices + uniform size_t numTimeSteps = 1 //!< number of motion blur time steps + ); + +/*! Sets a uniform tessellation rate for subdiv meshes and hair + * geometry. For subdivision meshes the RTC_LEVEL_BUFFER can also be used + * optionally to set a different tessellation rate per edge.*/ +void rtcSetTessellationRate (RTCScene scene, uniform unsigned geomID, uniform float tessellationRate); + +/*! \brief Creates a new line segment geometry, consisting of multiple + segments with varying radii. The number of line segments (numSegments), + number of vertices (numVertices), and number of time steps (1 for + normal line segments, and 2 for linear motion blur), have to get + specified at construction time. Further, the segment index buffer + (RTC_INDEX_BUFFER) and the segment vertex buffer (RTC_VERTEX_BUFFER) + have to get set by mapping and writing to the appropiate buffers. In + case of linear motion blur, two vertex buffers have to get filled + (RTC_VERTEX_BUFFER0, RTC_VERTEX_BUFFER1), one for each time step. The + index buffer has the default layout of a single 32 bit integer index + for each line segment, that references the start vertex of the segment. + The vertex buffer stores 2 end points per line segment, each such point + consists of a single precision (x,y,z) position and radius, stored in + that order in memory. Individual segments are considered to be subpixel + sized which allows the implementation to approximate the intersection + calculation. This in particular means that zooming onto one line segment + might show geometric artefacts. */ +uniform unsigned int rtcNewLineSegments (RTCScene scene, //!< the scene the line segments belong to + uniform RTCGeometryFlags flags, //!< geometry flags + uniform size_t numSegments, //!< number of line segments + uniform size_t numVertices, //!< number of vertices + uniform size_t numTimeSteps = 1 //!< number of motion blur time steps + ); + +/*! \brief Sets 32 bit ray mask. */ +void rtcSetMask (RTCScene scene, uniform unsigned int geomID, uniform int mask); + +/*! \brief Sets boundary interpolation mode for subdivision surfaces */ +void rtcSetBoundaryMode(RTCScene scene, uniform unsigned int geomID, uniform RTCBoundaryMode mode); + +/*! \brief Maps specified buffer. This function can be used to set index and + * vertex buffers of geometries. */ +void* uniform rtcMapBuffer(RTCScene scene, uniform unsigned int geomID, uniform RTCBufferType type); + +/*! \brief Unmaps specified buffer. + + A buffer has to be unmapped before the rtcEnable, rtcDisable, + rtcUpdate, or rtcDeleteGeometry calls are executed. */ +void rtcUnmapBuffer(RTCScene scene, uniform unsigned int geomID, uniform RTCBufferType type); + +/*! \brief Shares a data buffer between the application and + * Embree. The passed buffer is used by Embree to store index and + * vertex data. It has to remain valid as long as the mesh exists, + * and the user is responsible to free the data when the mesh gets + * deleted. One can optionally speficy a byte offset and byte stride + * of the elements stored inside the buffer. The addresses + * ptr+offset+i*stride have to be aligned to 4 bytes on Xeon CPUs and + * 16 bytes on Xeon Phi accelerators. For vertex buffers, the 4 bytes + * after the z-coordinate of the last vertex have to be readable memory, + * thus padding is required for some layouts. If this function is not + * called, Embree will allocate and manage buffers of the default + * layout. */ +void rtcSetBuffer(RTCScene scene, uniform unsigned int geomID, uniform RTCBufferType type, + const void* uniform ptr, uniform size_t byteOffset, uniform size_t byteStride); + +/*! \brief Enable geometry. Enabled geometry can be hit by a ray. */ +void rtcEnable (RTCScene scene, uniform unsigned int geomID); + +/*! \brief Update spefific geometry buffer. + + Each time geometry buffers got modified, the user has to call some + update function to tell the ray tracing engine which buffers got + modified. The rtcUpdateBuffer function taggs a specific buffer of + some geometry as modified. */ +void rtcUpdate (RTCScene scene, uniform unsigned int geomID); + +/*! \brief Update spefific geometry buffer. + + Each time geometry buffers got modified, the user has to call some + update function to tell the ray tracing engine which buffers got + modified. The rtcUpdateBuffer function taggs a specific buffer of + some geometry as modified. */ +void rtcUpdateBuffer (RTCScene scene, uniform unsigned int geomID, uniform RTCBufferType type); + +/*! \brief Disable geometry. + + Disabled geometry is not hit by any ray. Disabling and enabling + geometry gives higher performance than deleting and recreating + geometry. */ +void rtcDisable (RTCScene scene, uniform unsigned int geomID); + +/*! \brief Sets the intersection filter function for uniform rays. */ +void rtcSetIntersectionFilterFunction1 (RTCScene scene, uniform unsigned int geomID, uniform RTCFilterFuncUniform func); + +/*! \brief Sets the intersection filter function for varying rays. */ +void rtcSetIntersectionFilterFunction (RTCScene scene, uniform unsigned int geomID, uniform RTCFilterFuncVarying func); + +/*! \brief Sets the occlusion filter function for uniform rays. */ +void rtcSetOcclusionFilterFunction1 (RTCScene scene, uniform unsigned int geomID, uniform RTCFilterFuncUniform func); + +/*! \brief Sets the occlusion filter function for varying rays. */ +void rtcSetOcclusionFilterFunction (RTCScene scene, uniform unsigned int geomID, uniform RTCFilterFuncVarying func); + +/*! Set pointer for user defined data per geometry. Invokations + * of the various user intersect and occluded functions get passed + * this data pointer when called. */ +void rtcSetUserData (RTCScene scene, uniform unsigned int geomID, void* uniform ptr); + +/*! Get pointer for user defined data per geometry based on geomID. */ +void* uniform rtcGetUserData (RTCScene scene, uniform unsigned int geomID); + +/*! Interpolates user data to some varying u/v location. The data + * buffer specifies per vertex data to interpolate and can be one of + * the RTC_VERTEX_BUFFER0/1 or RTC_USER_VERTEX_BUFFER0/1 and has to contain + * numFloats floating point values to interpolate for each vertex of + * the geometry. The P array will get filled with the interpolated + * data, and the dPdu and dPdv arrays with the u and v derivative of + * the interpolation. If the pointers P is NULL, the value will not + * get calculated. If dPdu and dPdv are NULL the derivatives will not + * get calculated. Both dPdu and dPdv have to be either valid or + * NULL. These destination arrays are filled in structure of array + * (SoA) layout. The buffer has to be padded at the end such + * that the last element can be read safely using SSE + * instructions. */ +void rtcInterpolate(RTCScene scene, uniform unsigned int geomID, varying unsigned int primIDs, varying float u, varying float v, + uniform RTCBufferType buffer, + varying float* uniform P, varying float* uniform dPdu, varying float* uniform dPdv, uniform size_t numFloats); + +/*! Interpolates user data to some varying u/v location. The data + * buffer specifies per vertex data to interpolate and can be one of + * the RTC_VERTEX_BUFFER0/1 or RTC_USER_VERTEX_BUFFER0/1 and has to contain + * numFloats floating point values to interpolate for each vertex of + * the geometry. The P array will get filled with the + * interpolated datam the dPdu and dPdv arrays with the u and v + * derivative of the interpolation, and the ddPdudu, ddPdvdv, and + * ddPdudv arrays with the respective second derivatives. One can + * disable 1) the calculation of the interpolated value by setting P + * to NULL, 2) the calculation of the 1st order derivatives by + * setting dPdu and dPdv to NULL, 3) the calculation of the second + * order derivatives by setting ddPdudu, ddPdvdv, and ddPdudv to + * NULL. These destination arrays are filled in structure of array + * (SoA) layout. The buffer has to be padded at the end such that + * the last element can be read safely using SSE + * instructions. */ +void rtcInterpolate2(RTCScene scene, uniform unsigned int geomID, varying unsigned int primIDs, varying float u, varying float v, + uniform RTCBufferType buffer, + varying float* uniform P, varying float* uniform dPdu, varying float* uniform dPdv, + varying float* uniform ddPdudu, varying float* uniform ddPdvdv, varying float* uniform ddPdudv, + uniform size_t numFloats); + +/*! \brief Deletes the geometry. */ +void rtcDeleteGeometry (RTCScene scene, uniform unsigned int geomID); + +/*! @} */ + +#endif diff --git a/src/igl/embree/embree2/rtcore_geometry_user.h b/src/igl/embree/embree2/rtcore_geometry_user.h new file mode 100644 index 0000000000..e4b4d4b8a5 --- /dev/null +++ b/src/igl/embree/embree2/rtcore_geometry_user.h @@ -0,0 +1,154 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTCORE_USER_GEOMETRY_H__ +#define __RTCORE_USER_GEOMETRY_H__ + +/*! \ingroup embree_kernel_api */ +/*! \{ */ + +/*! Type of bounding function. */ +typedef void (*RTCBoundsFunc)(void* ptr, /*!< pointer to user data */ + size_t item, /*!< item to calculate bounds for */ + RTCBounds& bounds_o /*!< returns calculated bounds */); + +/*! Type of bounding function. */ +typedef void (*RTCBoundsFunc2)(void* userPtr, /*!< pointer to user data */ + void* geomUserPtr, /*!< pointer to geometry user data */ + size_t item, /*!< item to calculate bounds for */ + RTCBounds* bounds_o /*!< returns calculated bounds */); + +/*! Type of intersect function pointer for single rays. */ +typedef void (*RTCIntersectFunc)(void* ptr, /*!< pointer to user data */ + RTCRay& ray, /*!< ray to intersect */ + size_t item /*!< item to intersect */); + +/*! Type of intersect function pointer for ray packets of size 4. */ +typedef void (*RTCIntersectFunc4)(const void* valid, /*!< pointer to valid mask */ + void* ptr, /*!< pointer to user data */ + RTCRay4& ray, /*!< ray packet to intersect */ + size_t item /*!< item to intersect */); + +/*! Type of intersect function pointer for ray packets of size 8. */ +typedef void (*RTCIntersectFunc8)(const void* valid, /*!< pointer to valid mask */ + void* ptr, /*!< pointer to user data */ + RTCRay8& ray, /*!< ray packet to intersect */ + size_t item /*!< item to intersect */); + +/*! Type of intersect function pointer for ray packets of size 16. */ +typedef void (*RTCIntersectFunc16)(const void* valid, /*!< pointer to valid mask */ + void* ptr, /*!< pointer to user data */ + RTCRay16& ray, /*!< ray packet to intersect */ + size_t item /*!< item to intersect */); + +/*! Type of occlusion function pointer for single rays. */ +typedef void (*RTCOccludedFunc) (void* ptr, /*!< pointer to user data */ + RTCRay& ray, /*!< ray to test occlusion */ + size_t item /*!< item to test for occlusion */); + +/*! Type of occlusion function pointer for ray packets of size 4. */ +typedef void (*RTCOccludedFunc4) (const void* valid, /*! pointer to valid mask */ + void* ptr, /*!< pointer to user data */ + RTCRay4& ray, /*!< Ray packet to test occlusion. */ + size_t item /*!< item to test for occlusion */); + +/*! Type of occlusion function pointer for ray packets of size 8. */ +typedef void (*RTCOccludedFunc8) (const void* valid, /*! pointer to valid mask */ + void* ptr, /*!< pointer to user data */ + RTCRay8& ray, /*!< Ray packet to test occlusion. */ + size_t item /*!< item to test for occlusion */); + +/*! Type of occlusion function pointer for ray packets of size 16. */ +typedef void (*RTCOccludedFunc16) (const void* valid, /*! pointer to valid mask */ + void* ptr, /*!< pointer to user data */ + RTCRay16& ray, /*!< Ray packet to test occlusion. */ + size_t item /*!< item to test for occlusion */); + +/*! Creates a new user geometry object. This feature makes it possible + * to add arbitrary types of geometry to the scene by providing + * appropiate bounding, intersect and occluded functions. A user + * geometry object is a set of user geometries. As the rtcIntersect + * and rtcOccluded functions support different ray packet sizes, the + * user also has to provide different versions of intersect and + * occluded function pointers for these packet sizes. However, the + * ray packet size of the called function pointer always matches the + * packet size of the originally invoked rtcIntersect and rtcOccluded + * functions. A user data pointer, that points to a user specified + * representation of the geometry, is passed to each intersect and + * occluded function invokation, as well as the index of the geometry + * of the set to intersect. */ +RTCORE_API unsigned rtcNewUserGeometry (RTCScene scene, /*!< the scene the user geometry set is created in */ + size_t numGeometries /*!< the number of geometries contained in the set */); + +RTCORE_API unsigned rtcNewUserGeometry2 (RTCScene scene, /*!< the scene the user geometry set is created in */ + size_t numGeometries, /*!< the number of geometries contained in the set */ + size_t numTimeSteps = 1 /*!< number of motion blur time steps */); + +/*! Sets the bounding function to calculate bounding boxes of the user + * geometry items when building spatial index structures. The + * calculated bounding box have to be conservative and should be + * tight. */ +RTCORE_API void rtcSetBoundsFunction (RTCScene scene, unsigned geomID, RTCBoundsFunc bounds); + +/*! Sets the bounding function to calculate bounding boxes of the user + * geometry items when building spatial index structures. The + * calculated bounding box have to be conservative and should be + * tight. */ +RTCORE_API void rtcSetBoundsFunction2 (RTCScene scene, unsigned geomID, RTCBoundsFunc2 bounds, void* userPtr); + +/*! Set intersect function for single rays. The rtcIntersect function + * will call the passed function for intersecting the user + * geometry. */ +RTCORE_API void rtcSetIntersectFunction (RTCScene scene, unsigned geomID, RTCIntersectFunc intersect); + +/*! Set intersect function for ray packets of size 4. The + * rtcIntersect4 function will call the passed function for + * intersecting the user geometry. */ +RTCORE_API void rtcSetIntersectFunction4 (RTCScene scene, unsigned geomID, RTCIntersectFunc4 intersect4); + +/*! Set intersect function for ray packets of size 8. The + * rtcIntersect8 function will call the passed function for + * intersecting the user geometry.*/ +RTCORE_API void rtcSetIntersectFunction8 (RTCScene scene, unsigned geomID, RTCIntersectFunc8 intersect8); + +/*! Set intersect function for ray packets of size 16. The + * rtcIntersect16 function will call the passed function for + * intersecting the user geometry. */ +RTCORE_API void rtcSetIntersectFunction16 (RTCScene scene, unsigned geomID, RTCIntersectFunc16 intersect16); + +/*! Set occlusion function for single rays. The rtcOccluded function + * will call the passed function for intersecting the user + * geometry. */ +RTCORE_API void rtcSetOccludedFunction (RTCScene scene, unsigned geomID, RTCOccludedFunc occluded); + +/*! Set occlusion function for ray packets of size 4. The rtcOccluded4 + * function will call the passed function for intersecting the user + * geometry. */ +RTCORE_API void rtcSetOccludedFunction4 (RTCScene scene, unsigned geomID, RTCOccludedFunc4 occluded4); + +/*! Set occlusion function for ray packets of size 8. The rtcOccluded8 + * function will call the passed function for intersecting the user + * geometry. */ +RTCORE_API void rtcSetOccludedFunction8 (RTCScene scene, unsigned geomID, RTCOccludedFunc8 occluded8); + +/*! Set occlusion function for ray packets of size 16. The + * rtcOccluded16 function will call the passed function for + * intersecting the user geometry. */ +RTCORE_API void rtcSetOccludedFunction16 (RTCScene scene, unsigned geomID, RTCOccludedFunc16 occluded16); + +/*! @} */ + +#endif diff --git a/src/igl/embree/embree2/rtcore_geometry_user.isph b/src/igl/embree/embree2/rtcore_geometry_user.isph new file mode 100644 index 0000000000..d89ec1b912 --- /dev/null +++ b/src/igl/embree/embree2/rtcore_geometry_user.isph @@ -0,0 +1,128 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTCORE_USER_GEOMETRY_ISPH__ +#define __RTCORE_USER_GEOMETRY_ISPH__ + +/*! \ingroup embree_kernel_api_ispc */ +/*! \{ */ + +/*! Type of bounding function. */ +typedef void (*RTCBoundsFunc)(void* uniform ptr, /*!< pointer to user data */ + uniform size_t item, /*!< item to calculate bounds for */ + uniform RTCBounds& bounds_o /*!< returns calculated bounds */); + +/*! Type of bounding function. */ +typedef void (*RTCBoundsFunc2)(void* uniform userPtr, /*!< pointer to user data */ + void* uniform geomUserPtr, /*!< pointer to geometry user data */ + uniform size_t item, /*!< item to calculate bounds for */ + RTCBounds* uniform bounds_o /*!< returns calculated bounds */); + +/*! Type of intersect function pointer for uniform rays. */ +typedef void (*RTCIntersectFuncUniform)(void* uniform ptr, /*!< pointer to user data */ + uniform RTCRay1& ray, /*!< ray to intersect */ + uniform size_t item /*< item to intersect */); + +/*! Type of intersect function pointer for varying rays. */ +typedef void (*RTCIntersectFuncVarying)(void* uniform ptr, /*!< pointer to user data */ + varying RTCRay& ray, /*!< ray to intersect */ + uniform size_t item /*< item to intersect */); + +/*! Type of occlusion function pointer for uniform rays. */ +typedef void (*RTCOccludedFuncUniform) (void* uniform ptr, /*!< pointer to user data */ + uniform RTCRay1& ray, /*!< ray to test occlusion */ + uniform size_t item /*< item to test for occlusion */); + + +/*! Type of occlusion function pointer for varying rays. */ +typedef void (*RTCOccludedFuncVarying) (void* uniform ptr, /*!< pointer to user data */ + varying RTCRay& ray, /*!< ray to test occlusion */ + uniform size_t item /*< item to test for occlusion */); + + +typedef void (*RTCDisplacementFunc)(void* uniform ptr, /*!< pointer to user data of geometry */ + uniform unsigned int geomID, /*!< ID of geometry to displace */ + uniform unsigned int primID, /*!< ID of primitive of geometry to displace */ + uniform const float* uniform u, /*!< u coordinates (source) */ + uniform const float* uniform v, /*!< v coordinates (source) */ + uniform const float* uniform nx, /*!< x coordinates of normal at point to displace (source) */ + uniform const float* uniform ny, /*!< y coordinates of normal at point to displace (source) */ + uniform const float* uniform nz, /*!< z coordinates of normal at point to displace (source) */ + uniform float* uniform px, /*!< x coordinates of points to displace (source and target) */ + uniform float* uniform py, /*!< y coordinates of points to displace (source and target) */ + uniform float* uniform pz, /*!< z coordinates of points to displace (source and target) */ + uniform size_t N /*!< number of points to displace */ ); + + +/*! Creates a new user geometry object. This feature makes it possible + * to add arbitrary types of geometry to the scene by providing + * appropiate intersect and occluded functions, as well as a bounding + * box of the implemented geometry. As the rtcIntersect and + * rtcOccluded functions support different ray packet sizes, the user + * also has to provide different versions of intersect and occluded + * function pointers for the different packet sized. However, only + * rtcIntersect and rtcOccluded functions of specific packet sizes + * are called, it is sufficient to provide only the corresponding + * function pointer for the user geometry. However, the functions + * provided have to intersect the same geometry. A user data pointer, + * that points to a user specified representation of the geometry, is + * passed to each intersect and occluded function invokation. */ +uniform unsigned int rtcNewUserGeometry (RTCScene scene, /*!< the scene the user geometry set is created in */ + uniform size_t numGeometries /*!< the number of geometries contained in the set */); + +uniform unsigned int rtcNewUserGeometry2 (RTCScene scene, /*!< the scene the user geometry set is created in */ + uniform size_t numGeometries, /*!< the number of geometries contained in the set */ + uniform size_t numTimeSteps = 1 /*!< number of motion blur time steps */); + +/*! Sets the bounding function to calculate bounding boxes of the user + * geometry items when building spatial index structures. The + * calculated bounding box have to be conservative and should be + * tight.*/ +void rtcSetBoundsFunction (RTCScene scene, uniform unsigned int geomID, uniform RTCBoundsFunc bounds); + +/*! Sets the bounding function to calculate bounding boxes of the user + * geometry items when building spatial index structures. The + * calculated bounding box have to be conservative and should be + * tight.*/ +void rtcSetBoundsFunction2 (RTCScene scene, uniform unsigned int geomID, uniform RTCBoundsFunc2 bounds, void* uniform userPtr); + +/*! Set intersect function for uniform rays. The rtcIntersect1 + * function will call the passed function for intersecting the user + * geometry. */ +void rtcSetIntersectFunction1 (RTCScene scene, uniform unsigned int geomID, uniform RTCIntersectFuncUniform intersect); + +/*! Set intersect function for varying rays. The rtcIntersect function + * will call the passed function for intersecting the user + * geometry. */ +void rtcSetIntersectFunction (RTCScene scene, uniform unsigned int geomID, uniform RTCIntersectFuncVarying intersect); + +/*! Set occlusion function for uniform rays. The rtcOccluded1 function + * will call the passed function for intersecting the user + * geometry. */ +void rtcSetOccludedFunction1 (RTCScene scene, uniform unsigned int geomID, uniform RTCOccludedFuncUniform occluded); + +/*! Set occlusion function for varying rays. The rtcOccluded function + * will call the passed function for intersecting the user + * geometry. */ +void rtcSetOccludedFunction (RTCScene scene, uniform unsigned int geomID, uniform RTCOccludedFuncVarying occluded); + + +/*! \brief Sets the displacement function. */ +void rtcSetDisplacementFunction (RTCScene scene, uniform unsigned int geomID, uniform RTCDisplacementFunc func, uniform RTCBounds *uniform bounds); + +/*! @} */ + +#endif diff --git a/src/igl/embree/embree2/rtcore_ray.h b/src/igl/embree/embree2/rtcore_ray.h new file mode 100644 index 0000000000..f20b11b5ff --- /dev/null +++ b/src/igl/embree/embree2/rtcore_ray.h @@ -0,0 +1,195 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTCORE_RAY_H__ +#define __RTCORE_RAY_H__ + +/*! \ingroup embree_kernel_api */ +/*! \{ */ + +/*! \brief Ray structure for an individual ray */ +struct RTCORE_ALIGN(16) RTCRay +{ + /* ray data */ +public: + float org[3]; //!< Ray origin + float align0; + + float dir[3]; //!< Ray direction + float align1; + + float tnear; //!< Start of ray segment + float tfar; //!< End of ray segment (set to hit distance) + + float time; //!< Time of this ray for motion blur + unsigned mask; //!< Used to mask out objects during traversal + + /* hit data */ +public: + float Ng[3]; //!< Unnormalized geometry normal + float align2; + + float u; //!< Barycentric u coordinate of hit + float v; //!< Barycentric v coordinate of hit + + unsigned geomID; //!< geometry ID + unsigned primID; //!< primitive ID + unsigned instID; //!< instance ID +}; + +/*! Ray structure for packets of 4 rays. */ +struct RTCORE_ALIGN(16) RTCRay4 +{ + /* ray data */ +public: + float orgx[4]; //!< x coordinate of ray origin + float orgy[4]; //!< y coordinate of ray origin + float orgz[4]; //!< z coordinate of ray origin + + float dirx[4]; //!< x coordinate of ray direction + float diry[4]; //!< y coordinate of ray direction + float dirz[4]; //!< z coordinate of ray direction + + float tnear[4]; //!< Start of ray segment + float tfar[4]; //!< End of ray segment (set to hit distance) + + float time[4]; //!< Time of this ray for motion blur + unsigned mask[4]; //!< Used to mask out objects during traversal + + /* hit data */ +public: + float Ngx[4]; //!< x coordinate of geometry normal + float Ngy[4]; //!< y coordinate of geometry normal + float Ngz[4]; //!< z coordinate of geometry normal + + float u[4]; //!< Barycentric u coordinate of hit + float v[4]; //!< Barycentric v coordinate of hit + + unsigned geomID[4]; //!< geometry ID + unsigned primID[4]; //!< primitive ID + unsigned instID[4]; //!< instance ID +}; + +/*! Ray structure for packets of 8 rays. */ +struct RTCORE_ALIGN(32) RTCRay8 +{ + /* ray data */ +public: + float orgx[8]; //!< x coordinate of ray origin + float orgy[8]; //!< y coordinate of ray origin + float orgz[8]; //!< z coordinate of ray origin + + float dirx[8]; //!< x coordinate of ray direction + float diry[8]; //!< y coordinate of ray direction + float dirz[8]; //!< z coordinate of ray direction + + float tnear[8]; //!< Start of ray segment + float tfar[8]; //!< End of ray segment (set to hit distance) + + float time[8]; //!< Time of this ray for motion blur + unsigned mask[8]; //!< Used to mask out objects during traversal + + /* hit data */ +public: + float Ngx[8]; //!< x coordinate of geometry normal + float Ngy[8]; //!< y coordinate of geometry normal + float Ngz[8]; //!< z coordinate of geometry normal + + float u[8]; //!< Barycentric u coordinate of hit + float v[8]; //!< Barycentric v coordinate of hit + + unsigned geomID[8]; //!< geometry ID + unsigned primID[8]; //!< primitive ID + unsigned instID[8]; //!< instance ID +}; + +/*! \brief Ray structure for packets of 16 rays. */ +struct RTCORE_ALIGN(64) RTCRay16 +{ + /* ray data */ +public: + float orgx[16]; //!< x coordinate of ray origin + float orgy[16]; //!< y coordinate of ray origin + float orgz[16]; //!< z coordinate of ray origin + + float dirx[16]; //!< x coordinate of ray direction + float diry[16]; //!< y coordinate of ray direction + float dirz[16]; //!< z coordinate of ray direction + + float tnear[16]; //!< Start of ray segment + float tfar[16]; //!< End of ray segment (set to hit distance) + + float time[16]; //!< Time of this ray for motion blur + unsigned mask[16]; //!< Used to mask out objects during traversal + + /* hit data */ +public: + float Ngx[16]; //!< x coordinate of geometry normal + float Ngy[16]; //!< y coordinate of geometry normal + float Ngz[16]; //!< z coordinate of geometry normal + + float u[16]; //!< Barycentric u coordinate of hit + float v[16]; //!< Barycentric v coordinate of hit + + unsigned geomID[16]; //!< geometry ID + unsigned primID[16]; //!< primitive ID + unsigned instID[16]; //!< instance ID +}; + + +/*! \brief Ray structure template for packets of N rays in SOA layout. */ +struct RTCRaySOA +{ + /* ray data */ +public: + + float* orgx; //!< x coordinate of ray origin + float* orgy; //!< y coordinate of ray origin + float* orgz; //!< z coordinate of ray origin + + float* dirx; //!< x coordinate of ray direction + float* diry; //!< y coordinate of ray direction + float* dirz; //!< z coordinate of ray direction + + float* tnear; //!< Start of ray segment (optional) + float* tfar; //!< End of ray segment (set to hit distance) + + + float* time; //!< Time of this ray for motion blur (optional) + unsigned* mask; //!< Used to mask out objects during traversal (optional) + + /* hit data */ + +public: + + float* Ngx; //!< x coordinate of geometry normal (optional) + float* Ngy; //!< y coordinate of geometry normal (optional) + float* Ngz; //!< z coordinate of geometry normal (optional) + + + + float* u; //!< Barycentric u coordinate of hit + float* v; //!< Barycentric v coordinate of hit + + + unsigned* geomID; //!< geometry ID + unsigned* primID; //!< primitive ID + unsigned* instID; //!< instance ID (optional) +}; + +/*! @} */ + +#endif diff --git a/src/igl/embree/embree2/rtcore_ray.isph b/src/igl/embree/embree2/rtcore_ray.isph new file mode 100644 index 0000000000..f1bf4d268b --- /dev/null +++ b/src/igl/embree/embree2/rtcore_ray.isph @@ -0,0 +1,117 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTCORE_RAY_ISPH__ +#define __RTCORE_RAY_ISPH__ + +/*! \ingroup embree_kernel_api_ispc */ +/*! \{ */ + +/*! Ray structure for uniform (single) rays. */ +struct RTCRay1 +{ + /* ray data */ + float org[3]; //!< Ray origin + float align0; //!< unused member to force alignment of following members + + float dir[3]; //!< Ray direction + float align1; //!< unused member to force alignment of following members + + float tnear; //!< Start of ray segment + float tfar; //!< End of ray segment (set to hit distance) + float time; //!< Time of this ray for motion blur + unsigned mask; //!< Used to mask out objects during traversal + + /* hit data */ + float Ng[3]; //!< Unnormalized geometry normal + float align2; + + float u; //!< Barycentric u coordinate of hit + float v; //!< Barycentric v coordinate of hit + + unsigned geomID; //!< geometry ID + unsigned primID; //!< primitive ID + unsigned instID; //!< instance ID + varying unsigned align[0]; //!< aligns ray on stack to at least 16 bytes +}; + +/*! Ray structure for packets of 4 rays. */ +struct RTCRay +{ + /* ray data */ + float orgx; //!< x coordinate of ray origin + float orgy; //!< y coordinate of ray origin + float orgz; //!< z coordinate of ray origin + + float dirx; //!< x coordinate of ray direction + float diry; //!< y coordinate of ray direction + float dirz; //!< z coordinate of ray direction + + float tnear; //!< Start of ray segment + float tfar; //!< End of ray segment + float time; //!< Time of this ray for motion blur + unsigned mask; //!< Used to mask out objects during traversal + + /* hit data */ + float Ngx; //!< x coordinate of geometry normal + float Ngy; //!< y coordinate of geometry normal + float Ngz; //!< z coordinate of geometry normal + + float u; //!< Barycentric u coordinate of hit + float v; //!< Barycentric v coordinate of hit + + unsigned geomID; //!< geometry ID + unsigned primID; //!< primitive ID + unsigned instID; //!< instance ID +}; + + +struct RTCRaySOA +{ + /* ray data */ + + uniform float* uniform orgx; //!< x coordinate of ray origin + uniform float* uniform orgy; //!< y coordinate of ray origin + uniform float* uniform orgz; //!< z coordinate of ray origin + + uniform float* uniform dirx; //!< x coordinate of ray direction + uniform float* uniform diry; //!< y coordinate of ray direction + uniform float* uniform dirz; //!< z coordinate of ray direction + + uniform float* uniform tnear; //!< Start of ray segment (optional) + uniform float* uniform tfar; //!< End of ray segment (set to hit distance) + + uniform float* uniform time; //!< Time of this ray for motion blur (optional) + uniform unsigned* uniform mask; //!< Used to mask out objects during traversal (optional) + + /* hit data */ + + uniform float* uniform Ngx; //!< x coordinate of geometry normal (optional) + uniform float* uniform Ngy; //!< y coordinate of geometry normal (optional) + uniform float* uniform Ngz; //!< z coordinate of geometry normal (optional) + + uniform float* uniform u; //!< Barycentric u coordinate of hit + uniform float* uniform v; //!< Barycentric v coordinate of hit + + uniform unsigned* uniform geomID; //!< geometry ID + uniform unsigned* uniform primID; //!< primitive ID + uniform unsigned* uniform instID; //!< instance ID (optional) +}; + + +/*! @} */ + +#endif diff --git a/src/igl/embree/embree2/rtcore_scene.h b/src/igl/embree/embree2/rtcore_scene.h new file mode 100644 index 0000000000..df04e0a2bf --- /dev/null +++ b/src/igl/embree/embree2/rtcore_scene.h @@ -0,0 +1,187 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTCORE_SCENE_H__ +#define __RTCORE_SCENE_H__ + +/*! \ingroup embree_kernel_api */ +/*! \{ */ + +/*! forward declarations for ray structures */ +struct RTCRay; +struct RTCRay4; +struct RTCRay8; +struct RTCRay16; +struct RTCRaySOA; + +/*! scene flags */ +enum RTCSceneFlags +{ + /* dynamic type flags */ + RTC_SCENE_STATIC = (0 << 0), //!< specifies static scene + RTC_SCENE_DYNAMIC = (1 << 0), //!< specifies dynamic scene + + /* acceleration structure flags */ + RTC_SCENE_COMPACT = (1 << 8), //!< use memory conservative data structures + RTC_SCENE_COHERENT = (1 << 9), //!< optimize data structures for coherent rays + RTC_SCENE_INCOHERENT = (1 << 10), //!< optimize data structures for in-coherent rays (enabled by default) + RTC_SCENE_HIGH_QUALITY = (1 << 11), //!< create higher quality data structures + + /* traversal algorithm flags */ + RTC_SCENE_ROBUST = (1 << 16) //!< use more robust traversal algorithms +}; + +/*! enabled algorithm flags */ +enum RTCAlgorithmFlags +{ + RTC_INTERSECT1 = (1 << 0), //!< enables the rtcIntersect1 and rtcOccluded1 functions for this scene + RTC_INTERSECT4 = (1 << 1), //!< enables the rtcIntersect4 and rtcOccluded4 functions for this scene + RTC_INTERSECT8 = (1 << 2), //!< enables the rtcIntersect8 and rtcOccluded8 functions for this scene + RTC_INTERSECT16 = (1 << 3), //!< enables the rtcIntersect16 and rtcOccluded16 functions for this scene + RTC_INTERPOLATE = (1 << 4), //!< enables the rtcInterpolate function for this scene + + RTC_INTERSECTN = (1 << 5), //!< enables the rtcIntersectN and rtcOccludedN functions for this scene +}; + +/*! layout flags for ray streams */ +enum RTCRayNFlags +{ + RTC_RAYN_DEFAULT = (1 << 0) +}; + + +/*! \brief Defines an opaque scene type */ +typedef struct __RTCScene {}* RTCScene; + +/*! Creates a new scene. + WARNING: This function is deprecated, use rtcDeviceNewScene instead. +*/ +RTCORE_API RTCORE_DEPRECATED RTCScene rtcNewScene (RTCSceneFlags flags, RTCAlgorithmFlags aflags); + +/*! Creates a new scene. */ +RTCORE_API RTCScene rtcDeviceNewScene (RTCDevice device, RTCSceneFlags flags, RTCAlgorithmFlags aflags); + +/*! \brief Type of progress callback function. */ +typedef bool (*RTCProgressMonitorFunc)(void* ptr, const double n); +RTCORE_DEPRECATED typedef RTCProgressMonitorFunc RTC_PROGRESS_MONITOR_FUNCTION; + +/*! \brief Sets the progress callback function which is called during hierarchy build of this scene. */ +RTCORE_API void rtcSetProgressMonitorFunction(RTCScene scene, RTCProgressMonitorFunc func, void* ptr); + +/*! Commits the geometry of the scene. After initializing or modifying + * geometries, commit has to get called before tracing + * rays. */ +RTCORE_API void rtcCommit (RTCScene scene); + +/*! Commits the geometry of the scene. The calling threads will be + * used internally as a worker threads on some implementations. The + * function will wait until 'numThreads' threads have called this + * function and all threads return from the function after the scene + * commit is finished. The application threads will not be used as + * worker threads when the TBB tasking system is enabled (which is + * the default). On CPUs, we recommend also using TBB inside your + * application to share threads. We recommend using the + * rtcCommitThread feature to share threads on the Xeon Phi + * coprocessor. */ +RTCORE_API void rtcCommitThread(RTCScene scene, unsigned int threadID, unsigned int numThreads); + +/*! Returns to AABB of the scene. rtcCommit has to get called + * previously to this function. */ +RTCORE_API void rtcGetBounds(RTCScene scene, RTCBounds& bounds_o); + +/*! Intersects a single ray with the scene. The ray has to be aligned + * to 16 bytes. This function can only be called for scenes with the + * RTC_INTERSECT1 flag set. */ +RTCORE_API void rtcIntersect (RTCScene scene, RTCRay& ray); + +/*! Intersects a packet of 4 rays with the scene. The valid mask and + * ray have both to be aligned to 16 bytes. This function can only be + * called for scenes with the RTC_INTERSECT4 flag set. */ +RTCORE_API void rtcIntersect4 (const void* valid, RTCScene scene, RTCRay4& ray); + +/*! Intersects a packet of 8 rays with the scene. The valid mask and + * ray have both to be aligned to 32 bytes. This function can only be + * called for scenes with the RTC_INTERSECT8 flag set. For performance + * reasons, the rtcIntersect8 function should only get called if the + * CPU supports AVX. */ +RTCORE_API void rtcIntersect8 (const void* valid, RTCScene scene, RTCRay8& ray); + +/*! Intersects a packet of 16 rays with the scene. The valid mask and + * ray have both to be aligned to 64 bytes. This function can only be + * called for scenes with the RTC_INTERSECT16 flag set. For + * performance reasons, the rtcIntersect16 function should only get + * called if the CPU supports the 16-wide SIMD instructions. */ +RTCORE_API void rtcIntersect16 (const void* valid, RTCScene scene, RTCRay16& ray); + +/*! Intersects a stream of N rays in AOS layout with the scene. This + * function can only be called for scenes with the RTC_INTERSECTN + * flag set. The stride specifies the offset between rays in + * bytes. */ +RTCORE_API void rtcIntersectN (RTCScene scene, RTCRay* rayN, const size_t N, const size_t stride, const size_t flags = RTC_RAYN_DEFAULT); + +/*! Intersects one or multiple streams of N rays in compact SOA layout + * with the scene. This function can only be called for scenes with + * the RTC_INTERSECTN flag set. 'streams' specifies the number of + * dense SOA ray streams, and 'stride' the offset in bytes between + * those. */ +RTCORE_API void rtcIntersectN_SOA (RTCScene scene, RTCRaySOA& rayN, const size_t N, const size_t streams, const size_t stride, const size_t flags = RTC_RAYN_DEFAULT); + + +/*! Tests if a single ray is occluded by the scene. The ray has to be + * aligned to 16 bytes. This function can only be called for scenes + * with the RTC_INTERSECT1 flag set. */ +RTCORE_API void rtcOccluded (RTCScene scene, RTCRay& ray); + +/*! Tests if a packet of 4 rays is occluded by the scene. This + * function can only be called for scenes with the RTC_INTERSECT4 + * flag set. The valid mask and ray have both to be aligned to 16 + * bytes. */ +RTCORE_API void rtcOccluded4 (const void* valid, RTCScene scene, RTCRay4& ray); + +/*! Tests if a packet of 8 rays is occluded by the scene. The valid + * mask and ray have both to be aligned to 32 bytes. This function + * can only be called for scenes with the RTC_INTERSECT8 flag + * set. For performance reasons, the rtcOccluded8 function should + * only get called if the CPU supports AVX. */ +RTCORE_API void rtcOccluded8 (const void* valid, RTCScene scene, RTCRay8& ray); + +/*! Tests if a packet of 16 rays is occluded by the scene. The valid + * mask and ray have both to be aligned to 64 bytes. This function + * can only be called for scenes with the RTC_INTERSECT16 flag + * set. For performance reasons, the rtcOccluded16 function should + * only get called if the CPU supports the 16-wide SIMD + * instructions. */ +RTCORE_API void rtcOccluded16 (const void* valid, RTCScene scene, RTCRay16& ray); + +/*! Tests if a stream of N rays on AOS layout is occluded by the + * scene. This function can only be called for scenes with the + * RTC_INTERSECTN flag set. The stride specifies the offset between + * rays in bytes.*/ +RTCORE_API void rtcOccludedN (RTCScene scene, RTCRay* rayN, const size_t N, const size_t stride, const size_t flags = RTC_RAYN_DEFAULT); + +/*! Intersects one or multiple streams of N rays in compact SOA layout + * with the scene. This function can only be called for scenes with + * the RTC_INTERSECTN flag set. 'streams' specifies the number of + * dense SOA ray streams, and 'stride' the offset in bytes between + * those. */ +RTCORE_API void rtcOccludedN_SOA (RTCScene scene, RTCRaySOA& rayN, const size_t N, const size_t streams, const size_t stride, const size_t flags = RTC_RAYN_DEFAULT); + +/*! Deletes the scene. All contained geometry get also destroyed. */ +RTCORE_API void rtcDeleteScene (RTCScene scene); + +/*! @} */ + +#endif diff --git a/src/igl/embree/embree2/rtcore_scene.isph b/src/igl/embree/embree2/rtcore_scene.isph new file mode 100644 index 0000000000..a85eee2191 --- /dev/null +++ b/src/igl/embree/embree2/rtcore_scene.isph @@ -0,0 +1,152 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTCORE_SCENE_ISPH__ +#define __RTCORE_SCENE_ISPH__ + +/*! \ingroup embree_kernel_api */ +/*! \{ */ + +/*! forward declarations for ray structures */ +struct RTCRay1; +struct RTCRay; +struct RTCRaySOA; + +/*! scene flags */ +enum RTCSceneFlags +{ + /* dynamic type flags */ + RTC_SCENE_STATIC = (0 << 0), //!< specifies static scene + RTC_SCENE_DYNAMIC = (1 << 0), //!< specifies dynamic scene + + /* acceleration structure flags */ + RTC_SCENE_COMPACT = (1 << 8), //!< use memory conservative data structures + RTC_SCENE_COHERENT = (1 << 9), //!< optimize data structures for coherent rays (enabled by default) + RTC_SCENE_INCOHERENT = (1 << 10), //!< optimize data structures for in-coherent rays + RTC_SCENE_HIGH_QUALITY = (1 << 11), //!< create higher quality data structures + + /* traversal algorithm flags */ + RTC_SCENE_ROBUST = (1 << 16) //!< use more robust traversal algorithms +}; + +/*! enabled algorithm flags */ +enum RTCAlgorithmFlags +{ + RTC_INTERSECT_UNIFORM = (1 << 0), //!< enables the uniform rtcIntersect1 and uniform rtcOccluded1 functions for this scene + RTC_INTERSECT_VARYING = (1 << 1), //!< enables the varying rtcIntersect and varying rtcOccluded functions for this scene + RTC_INTERPOLATE = (1 << 4) //!< enables the rtcInterpolate function for this scene +}; + +/*! layout flags for ray streams */ +enum RTCRayNFlags +{ + RTC_RAYN_DEFAULT = (1 << 0) +}; + + +/*! \brief Defines an opaque scene type */ +typedef uniform struct __RTCScene {}* uniform RTCScene; + +/*! Creates a new scene. + WARNING: This function is deprecated, use rtcDeviceNewScene instead. +*/ +RTCORE_DEPRECATED RTCScene rtcNewScene (uniform RTCSceneFlags flags, uniform RTCAlgorithmFlags aflags); + +/*! Creates a new scene. */ +RTCScene rtcDeviceNewScene (RTCDevice device, uniform RTCSceneFlags flags, uniform RTCAlgorithmFlags aflags); + +/*! \brief Type of progress callback function. */ +typedef uniform bool (*uniform RTC_PROGRESS_MONITOR_FUNCTION)(void* uniform ptr, const uniform double n); + +/*! \brief Sets the progress callback function which is called during hierarchy build. */ +void rtcSetProgressMonitorFunction(RTCScene scene, RTC_PROGRESS_MONITOR_FUNCTION func, void* uniform ptr); + +/*! Commits the geometry of the scene. After initializing or modifying + * geometries, commit has to get called before tracing + * rays. */ +void rtcCommit (RTCScene scene); + +/*! Commits the geometry of the scene. The calling threads will be + * used internally as a worker threads on some implementations. The + * function will wait until 'numThreads' threads have called this + * function and all threads return from the function after the scene + * commit is finished. The application threads will not be used as + * worker threads when the TBB tasking system is enabled (which is + * the default). On CPUs, we recommend also using TBB inside your + * application to share threads. We recommend using the + * rtcCommitThread feature to share threads on the Xeon Phi + * coprocessor. */ +void rtcCommitThread(RTCScene scene, uniform unsigned int threadID, uniform unsigned int numThreads); + +/*! Returns to AABB of the scene. rtcCommit has to get called + * previously to this function. */ +void rtcGetBounds(RTCScene scene, uniform RTCBounds& bounds_o); + +/*! Intersects a uniform ray with the scene. This function can only be + * called for scenes with the RTC_INTERSECT_UNIFORM flag set. The ray + * has to be aligned to 16 bytes. */ +void rtcIntersect1 (RTCScene scene, uniform RTCRay1& ray); + +/*! Intersects a varying ray with the scene. This function can only be + * called for scenes with the RTC_INTERSECT_VARYING flag set. The + * valid mask and ray have both to be aligned to sizeof(varing float) + * bytes. */ +void rtcIntersect (RTCScene scene, varying RTCRay& ray); + + +/*! Intersects a stream of N rays in AOS layout with the scene. This + * function can only be called for scenes with the RTC_INTERSECTN + * flag set. The stride specifies the offset between rays in + * bytes. */ +void rtcIntersectN (RTCScene scene, uniform RTCRay* uniform rayN, const uniform size_t N, const uniform size_t stride, const uniform size_t flags); + +/*! Intersects one or multiple streams of N rays in compact SOA layout with the scene. This + * function can only be called for scenes with the RTC_INTERSECTN + * flag set. 'streams' specifies the number of dense SOA ray + * streams, and 'stride' the offset in bytes between those. */ +void rtcIntersectN_SOA (RTCScene scene, uniform RTCRaySOA& rayN, const uniform size_t N, const uniform size_t streams, const uniform size_t offset, const uniform size_t flags); + + +/*! Tests if a uniform ray is occluded by the scene. This function can + * only be called for scenes with the RTC_INTERSECT_UNIFORM flag + * set. The ray has to be aligned to 16 bytes. */ +void rtcOccluded1 (RTCScene scene, uniform RTCRay1& ray); + +/*! Tests if a varying ray is occluded by the scene. This function can + * only be called for scenes with the RTC_INTERSECT_VARYING flag + * set. The valid mask and ray have both to be aligned to + * sizeof(varing float) bytes. */ +void rtcOccluded (RTCScene scene, varying RTCRay& ray); + + +/*! Tests if a stream of N rays on AOS layout is occluded by the + * scene. This function can only be called for scenes with the + * RTC_INTERSECTN flag set. The stride specifies the offset between + * rays in bytes.*/ +void rtcOccludedN (RTCScene scene, uniform RTCRay* uniform rayN, const uniform size_t N, const uniform size_t stride, const uniform size_t flags); + +/*! Intersects one or multiple streams of N rays in compact SOA layout with the scene. This + * function can only be called for scenes with the RTC_INTERSECTN + * flag set. 'streams' specifies the number of dense SOA ray + * streams, and 'stride' the offset in bytes between those. */ +void rtcOccludedN_SOA (RTCScene scene, uniform RTCRaySOA& rayN, const uniform size_t N, const uniform size_t streams, const uniform size_t offset, const uniform size_t flags); + +/*! Deletes the geometry again. */ +void rtcDeleteScene (RTCScene scene); + +/*! @} */ + +#endif diff --git a/src/igl/embree/line_mesh_intersection.cpp b/src/igl/embree/line_mesh_intersection.cpp new file mode 100644 index 0000000000..0f6e02b8ca --- /dev/null +++ b/src/igl/embree/line_mesh_intersection.cpp @@ -0,0 +1,85 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "line_mesh_intersection.h" +#include "../Hit.h" + +// For error printing +#include +#include + +#include +#include + +template +IGL_INLINE ScalarMatrix igl::embree::line_mesh_intersection +( + const ScalarMatrix & V_source, + const ScalarMatrix & N_source, + const ScalarMatrix & V_target, + const IndexMatrix & F_target +) +{ + + double tol = 0.00001; + + Eigen::MatrixXd ray_pos = V_source; + Eigen::MatrixXd ray_dir = N_source; + + // Allocate matrix for the result + ScalarMatrix R; + R.resize(V_source.rows(), 3); + + // Initialize embree + EmbreeIntersector embree; + embree.init(V_target.template cast(),F_target.template cast()); + + // Shoot rays from the source to the target + for (unsigned i=0; i(), A_dir.cast(),A); + + Eigen::RowVector3d B_pos = ray_pos.row(i) - tol * ray_dir.row(i); + Eigen::RowVector3d B_dir = ray_dir.row(i); + + bool B_hit = embree.intersectBeam(B_pos.cast(), B_dir.cast(),B); + + + int choice = -1; + + if (A_hit && ! B_hit) + choice = 0; + else if (!A_hit && B_hit) + choice = 1; + else if (A_hit && B_hit) + choice = A.t > B.t; + + Eigen::RowVector3d temp; + + if (choice == -1) + temp << -1, 0, 0; + else if (choice == 0) + temp << A.id, A.u, A.v; + else if (choice == 1) + temp << B.id, B.u, B.v; + + R.row(i) = temp; + + } + + return R; + +} + +#ifdef IGL_STATIC_LIBRARY +template Eigen::Matrix igl::embree::line_mesh_intersection, Eigen::Matrix >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); +#endif diff --git a/src/igl/embree/line_mesh_intersection.h b/src/igl/embree/line_mesh_intersection.h new file mode 100644 index 0000000000..b7b849c8a1 --- /dev/null +++ b/src/igl/embree/line_mesh_intersection.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EMBREE_LINE_MESH_INTERSECTION_H +#define IGL_EMBREE_LINE_MESH_INTERSECTION_H +#include + +#include +#include +#include + +namespace igl +{ + namespace embree + { + // Project the point cloud V_source onto the triangle mesh + // V_target,F_target. + // A ray is casted for every vertex in the direction specified by + // N_source and its opposite. + // + // Input: + // V_source: #Vx3 Vertices of the source mesh + // N_source: #Vx3 Normals of the point cloud + // V_target: #V2x3 Vertices of the target mesh + // F_target: #F2x3 Faces of the target mesh + // + // Output: + // #Vx3 matrix of baricentric coordinate. Each row corresponds to + // a vertex of the projected mesh and it has the following format: + // id b1 b2. id is the id of a face of the source mesh. b1 and b2 are + // the barycentric coordinates wrt the first two edges of the triangle + // To convert to standard global coordinates, see barycentric_to_global.h + template + IGL_INLINE ScalarMatrix line_mesh_intersection + ( + const ScalarMatrix & V_source, + const ScalarMatrix & N_source, + const ScalarMatrix & V_target, + const IndexMatrix & F_target + ); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "line_mesh_intersection.cpp" +#endif + +#endif diff --git a/src/igl/embree/reorient_facets_raycast.cpp b/src/igl/embree/reorient_facets_raycast.cpp new file mode 100644 index 0000000000..fabc021d9d --- /dev/null +++ b/src/igl/embree/reorient_facets_raycast.cpp @@ -0,0 +1,259 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "reorient_facets_raycast.h" +#include "../per_face_normals.h" +#include "../doublearea.h" +#include "../random_dir.h" +#include "../bfs_orient.h" +#include "EmbreeIntersector.h" +#include +#include +#include +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename DerivedC> +IGL_INLINE void igl::embree::reorient_facets_raycast( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + int rays_total, + int rays_minimum, + bool facet_wise, + bool use_parity, + bool is_verbose, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C) +{ + using namespace Eigen; + using namespace std; + assert(F.cols() == 3); + assert(V.cols() == 3); + + // number of faces + const int m = F.rows(); + + MatrixXi FF = F; + if (facet_wise) { + C.resize(m); + for (int i = 0; i < m; ++i) C(i) = i; + + } else { + if (is_verbose) cout << "extracting patches... "; + bfs_orient(F,FF,C); + } + if (is_verbose) cout << (C.maxCoeff() + 1) << " components. "; + + // number of patches + const int num_cc = C.maxCoeff()+1; + + // Init Embree + EmbreeIntersector ei; + ei.init(V.template cast(),FF); + + // face normal + MatrixXd N; + per_face_normals(V,FF,N); + + // face area + Matrix A; + doublearea(V,FF,A); + double area_total = A.sum(); + + // determine number of rays per component according to its area + VectorXd area_per_component; + area_per_component.setZero(num_cc); + for (int f = 0; f < m; ++f) + { + area_per_component(C(f)) += A(f); + } + VectorXi num_rays_per_component(num_cc); + for (int c = 0; c < num_cc; ++c) + { + num_rays_per_component(c) = max(static_cast(rays_total * area_per_component(c) / area_total), rays_minimum); + } + rays_total = num_rays_per_component.sum(); + + // generate all the rays + if (is_verbose) cout << "generating rays... "; + uniform_real_distribution rdist; + mt19937 prng; + prng.seed(time(nullptr)); + vector ray_face; + vector ray_ori; + vector ray_dir; + ray_face.reserve(rays_total); + ray_ori .reserve(rays_total); + ray_dir .reserve(rays_total); + for (int c = 0; c < num_cc; ++c) + { + if (area_per_component[c] == 0) + { + continue; + } + vector CF; // set of faces per component + vector CF_area; + for (int f = 0; f < m; ++f) + { + if (C(f)==c) + { + CF.push_back(f); + CF_area.push_back(A(f)); + } + } + // discrete distribution for random selection of faces with probability proportional to their areas + discrete_distribution ddist(CF.size(), 0, CF.size(), [&](double i){ return CF_area[static_cast(i)]; }); // simple ctor of (Iter, Iter) not provided by the stupid VC11/12 + for (int i = 0; i < num_rays_per_component[c]; ++i) + { + int f = CF[ddist(prng)]; // select face with probability proportional to face area + float s = rdist(prng); // random barycentric coordinate (reference: Generating Random Points in Triangles [Turk, Graphics Gems I 1990]) + float t = rdist(prng); + float sqrt_t = sqrtf(t); + float a = 1 - sqrt_t; + float b = (1 - s) * sqrt_t; + float c = s * sqrt_t; + Vector3f p = a * V.row(FF(f,0)).template cast().eval() // be careful with the index!!! + + b * V.row(FF(f,1)).template cast().eval() + + c * V.row(FF(f,2)).template cast().eval(); + Vector3f n = N.row(f).cast(); + if (n.isZero()) continue; + // random direction in hemisphere around n (avoid too grazing angle) + Vector3f d; + while (true) { + d = random_dir().cast(); + float ndotd = n.dot(d); + if (fabsf(ndotd) < 0.1f) + { + continue; + } + if (ndotd < 0) + { + d *= -1.0f; + } + break; + } + ray_face.push_back(f); + ray_ori .push_back(p); + ray_dir .push_back(d); + + if (is_verbose && ray_face.size() % (rays_total / 10) == 0) cout << "."; + } + } + if (is_verbose) cout << ray_face.size() << " rays. "; + + // per component voting: first=front, second=back + vector> C_vote_distance(num_cc, make_pair(0, 0)); // sum of distance between ray origin and intersection + vector> C_vote_infinity(num_cc, make_pair(0, 0)); // number of rays reaching infinity + vector> C_vote_parity(num_cc, make_pair(0, 0)); // sum of parity count for each ray + + if (is_verbose) cout << "shooting rays... "; +#pragma omp parallel for + for (int i = 0; i < (int)ray_face.size(); ++i) + { + int f = ray_face[i]; + Vector3f o = ray_ori [i]; + Vector3f d = ray_dir [i]; + int c = C(f); + + // shoot ray toward front & back + vector hits_front; + vector hits_back; + int num_rays_front; + int num_rays_back; + ei.intersectRay(o, d, hits_front, num_rays_front); + ei.intersectRay(o, -d, hits_back , num_rays_back ); + if (!hits_front.empty() && hits_front[0].id == f) hits_front.erase(hits_front.begin()); + if (!hits_back .empty() && hits_back [0].id == f) hits_back .erase(hits_back .begin()); + + if (use_parity) { +#pragma omp atomic + C_vote_parity[c].first += hits_front.size() % 2; +#pragma omp atomic + C_vote_parity[c].second += hits_back .size() % 2; + + } else { + if (hits_front.empty()) + { +#pragma omp atomic + C_vote_infinity[c].first++; + } else { +#pragma omp atomic + C_vote_distance[c].first += hits_front[0].t; + } + + if (hits_back.empty()) + { +#pragma omp atomic + C_vote_infinity[c].second++; + } else { +#pragma omp atomic + C_vote_distance[c].second += hits_back[0].t; + } + } + } + + I.resize(m); + for(int f = 0; f < m; ++f) + { + int c = C(f); + if (use_parity) { + I(f) = C_vote_parity[c].first > C_vote_parity[c].second ? 1 : 0; // Ideally, parity for the front/back side should be 1/0 (i.e., parity sum for all rays should be smaller on the front side) + + } else { + I(f) = (C_vote_infinity[c].first == C_vote_infinity[c].second && C_vote_distance[c].first < C_vote_distance[c].second) || + C_vote_infinity[c].first < C_vote_infinity[c].second + ? 1 : 0; + } + // To account for the effect of bfs_orient + if (F.row(f) != FF.row(f)) + I(f) = 1 - I(f); + } + if (is_verbose) cout << "done!" << endl; +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedFF, + typename DerivedI> +IGL_INLINE void igl::embree::reorient_facets_raycast( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & I) +{ + const int rays_total = F.rows()*100; + const int rays_minimum = 10; + const bool facet_wise = false; + const bool use_parity = false; + const bool is_verbose = false; + Eigen::VectorXi C; + reorient_facets_raycast( + V,F,rays_total,rays_minimum,facet_wise,use_parity,is_verbose,I,C); + // Conservative in case FF = F + FF.conservativeResize(F.rows(),F.cols()); + for(int i = 0;i, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::embree::reorient_facets_raycast, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, int, bool, bool, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::embree::reorient_facets_raycast, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, int, bool, bool, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/embree/reorient_facets_raycast.h b/src/igl/embree/reorient_facets_raycast.h new file mode 100644 index 0000000000..a43efac94c --- /dev/null +++ b/src/igl/embree/reorient_facets_raycast.h @@ -0,0 +1,73 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EMBREE_REORIENT_FACETS_RAYCAST_H +#define IGL_EMBREE_REORIENT_FACETS_RAYCAST_H +#include "../igl_inline.h" +#include +namespace igl +{ + namespace embree + { + // Orient each component (identified by C) of a mesh (V,F) using ambient + // occlusion such that the front side is less occluded than back side, as + // described in "A Simple Method for Correcting Facet Orientations in + // Polygon Meshes Based on Ray Casting" [Takayama et al. 2014]. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // rays_total Total number of rays that will be shot + // rays_minimum Minimum number of rays that each patch should receive + // facet_wise Decision made for each face independently, no use of patches + // (i.e., each face is treated as a patch) + // use_parity Use parity mode + // is_verbose Verbose output to cout + // Outputs: + // I #F list of whether face has been flipped + // C #F list of patch ID (output of bfs_orient > manifold patches) + template < + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename DerivedC> + IGL_INLINE void reorient_facets_raycast( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + int rays_total, + int rays_minimum, + bool facet_wise, + bool use_parity, + bool is_verbose, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C); + // Outputs: + // FF #F by 3 list of reoriented faces + // Defaults: + // rays_total = F.rows()*100; + // rays_minimum = 10; + // facet_wise = false; + // use_parity = false; + // is_verbose = false; + template < + typename DerivedV, + typename DerivedF, + typename DerivedFF, + typename DerivedI> + IGL_INLINE void reorient_facets_raycast( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & I); + } +}; + +#ifndef IGL_STATIC_LIBRARY +# include "reorient_facets_raycast.cpp" +#endif + +#endif diff --git a/src/igl/embree/shape_diameter_function.cpp b/src/igl/embree/shape_diameter_function.cpp new file mode 100644 index 0000000000..3b0be489ef --- /dev/null +++ b/src/igl/embree/shape_diameter_function.cpp @@ -0,0 +1,69 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "shape_diameter_function.h" +#include "../shape_diameter_function.h" +#include "EmbreeIntersector.h" +#include "../Hit.h" + +template < + typename DerivedP, + typename DerivedN, + typename DerivedS > +IGL_INLINE void igl::embree::shape_diameter_function( + const igl::embree::EmbreeIntersector & ei, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + const auto & shoot_ray = [&ei]( + const Eigen::Vector3f& s, + const Eigen::Vector3f& dir)->double + { + igl::Hit hit; + const float tnear = 1e-4f; + if(ei.intersectRay(s,dir,hit,tnear)) + { + return hit.t; + }else + { + return std::numeric_limits::infinity(); + } + }; + return igl::shape_diameter_function(shoot_ray,P,N,num_samples,S); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > +IGL_INLINE void igl::embree::shape_diameter_function( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + using namespace Eigen; + EmbreeIntersector ei; + ei.init(V.template cast(),F.template cast()); + shape_diameter_function(ei,P,N,num_samples,S); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::embree::shape_diameter_function, Eigen::Matrix, Eigen::Matrix >(igl::embree::EmbreeIntersector const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::embree::shape_diameter_function, Eigen::Matrix, Eigen::Matrix >(igl::embree::EmbreeIntersector const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::embree::shape_diameter_function, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::embree::shape_diameter_function, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::embree::shape_diameter_function, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/embree/shape_diameter_function.h b/src/igl/embree/shape_diameter_function.h new file mode 100644 index 0000000000..c18922d283 --- /dev/null +++ b/src/igl/embree/shape_diameter_function.h @@ -0,0 +1,60 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EMBREE_SHAPE_DIAMETER_FUNCTION_H +#define IGL_EMBREE_SHAPE_DIAMETER_FUNCTION_H +#include "../igl_inline.h" +#include +namespace igl +{ + namespace embree + { + // Forward define + class EmbreeIntersector; + // Compute shape diamter function per given point + // + // Inputs: + // ei EmbreeIntersector containing (V,F) + // P #P by 3 list of origin points + // N #P by 3 list of origin normals + // Outputs: + // S #P list of shape diamater function values between bounding box + // diagonal (perfect sphere) and 0 (perfect needle hook) + // + template < + typename DerivedP, + typename DerivedN, + typename DerivedS > + IGL_INLINE void shape_diameter_function( + const EmbreeIntersector & ei, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S); + // Wrapper which builds new EmbreeIntersector for (V,F). That's expensive so + // avoid this if repeatedly calling. + template < + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > + IGL_INLINE void shape_diameter_function( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S); + } +}; +#ifndef IGL_STATIC_LIBRARY +# include "shape_diameter_function.cpp" +#endif + +#endif + diff --git a/src/igl/embree/unproject_in_mesh.cpp b/src/igl/embree/unproject_in_mesh.cpp new file mode 100644 index 0000000000..79a24a0abe --- /dev/null +++ b/src/igl/embree/unproject_in_mesh.cpp @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unproject_in_mesh.h" +#include "EmbreeIntersector.h" +#include "../unproject_ray.h" +#include "../unproject_in_mesh.h" +#include + +template +IGL_INLINE int igl::embree::unproject_in_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const EmbreeIntersector & ei, + Eigen::PlainObjectBase & obj, + std::vector & hits) +{ + using namespace std; + using namespace Eigen; + const auto & shoot_ray = [&ei]( + const Eigen::Vector3f& s, + const Eigen::Vector3f& dir, + std::vector & hits) + { + int num_rays_shot; + ei.intersectRay(s,dir,hits,num_rays_shot); + }; + return igl::unproject_in_mesh(pos,model,proj,viewport,shoot_ray,obj,hits); +} + +template +IGL_INLINE int igl::embree::unproject_in_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const EmbreeIntersector & ei, + Eigen::PlainObjectBase & obj) +{ + std::vector hits; + return unproject_in_mesh(pos,model,proj,viewport,ei,obj,hits); +} + + +#ifdef IGL_STATIC_LIBRARY +template int igl::embree::unproject_in_mesh >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, igl::embree::EmbreeIntersector const&, Eigen::PlainObjectBase >&, std::vector >&); +template int igl::embree::unproject_in_mesh >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, igl::embree::EmbreeIntersector const&, Eigen::PlainObjectBase >&, std::vector >&); +template int igl::embree::unproject_in_mesh >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, igl::embree::EmbreeIntersector const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/embree/unproject_in_mesh.h b/src/igl/embree/unproject_in_mesh.h new file mode 100644 index 0000000000..707c1e6253 --- /dev/null +++ b/src/igl/embree/unproject_in_mesh.h @@ -0,0 +1,72 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EMBREE_UNPROJECT_IN_MESH +#define IGL_EMBREE_UNPROJECT_IN_MESH +#include +#include + +#include +#include "../Hit.h" + +namespace igl +{ + namespace embree + { + // Forward define + class EmbreeIntersector; + + // Unproject a screen location (using current opengl viewport, projection, and + // model view) to a 3D position _inside_ a given mesh. If the ray through the + // given screen location (x,y) _hits_ the mesh more than twice then the 3D + // midpoint between the first two hits is return. If it hits once, then that + // point is return. If it does not hit the mesh then obj is not set. + // + // + // Inputs: + // pos screen space coordinates + // model model matrix + // proj projection matrix + // viewport vieweport vector + // ei EmbreeIntersector containing (V,F) + // Outputs: + // obj 3d unprojected mouse point in mesh + // hits vector of embree hits + // Returns number of hits + // + // Note: Previous prototype did not require model, proj, and viewport. This + // has been removed. Instead replace with: + // + // Eigen::Matrix4f model,proj; + // Eigen::Vector4f viewport; + // igl::opengl2::model_proj_viewport(model,proj,viewport); + // igl::embree::unproject_in_mesh(Vector2f(x,y),model,proj,viewport,ei,obj,hits); + // + template < typename Derivedobj> + IGL_INLINE int unproject_in_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const EmbreeIntersector & ei, + Eigen::PlainObjectBase & obj, + std::vector & hits); + template < typename Derivedobj> + IGL_INLINE int unproject_in_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const EmbreeIntersector & ei, + Eigen::PlainObjectBase & obj); + + } +} +#ifndef IGL_STATIC_LIBRARY +# include "unproject_in_mesh.cpp" +#endif +#endif diff --git a/src/igl/embree/unproject_onto_mesh.cpp b/src/igl/embree/unproject_onto_mesh.cpp new file mode 100644 index 0000000000..a9d336690c --- /dev/null +++ b/src/igl/embree/unproject_onto_mesh.cpp @@ -0,0 +1,59 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unproject_onto_mesh.h" +#include "EmbreeIntersector.h" +#include "../unproject_onto_mesh.h" +#include + +IGL_INLINE bool igl::embree::unproject_onto_mesh( + const Eigen::Vector2f& pos, + const Eigen::MatrixXi& F, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const EmbreeIntersector & ei, + int& fid, + Eigen::Vector3f& bc) +{ + using namespace std; + using namespace Eigen; + const auto & shoot_ray = [&ei]( + const Eigen::Vector3f& s, + const Eigen::Vector3f& dir, + igl::Hit & hit)->bool + { + return ei.intersectRay(s,dir,hit); + }; + return igl::unproject_onto_mesh(pos,model,proj,viewport,shoot_ray,fid,bc); +} + +IGL_INLINE bool igl::embree::unproject_onto_mesh( + const Eigen::Vector2f& pos, + const Eigen::MatrixXi& F, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const EmbreeIntersector & ei, + int& fid, + int& vid) +{ + Eigen::Vector3f bc; + if(!igl::embree::unproject_onto_mesh(pos,F,model,proj,viewport,ei,fid,bc)) + { + return false; + } + int i; + bc.maxCoeff(&i); + vid = F(fid,i); + return true; +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/embree/unproject_onto_mesh.h b/src/igl/embree/unproject_onto_mesh.h new file mode 100644 index 0000000000..db0a2e0cba --- /dev/null +++ b/src/igl/embree/unproject_onto_mesh.h @@ -0,0 +1,73 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EMBREE_UNPROJECT_ONTO_MESH_H +#define IGL_EMBREE_UNPROJECT_ONTO_MESH_H +#include +#include + +#include + +namespace igl +{ + namespace embree + { + // Forward define + class EmbreeIntersector; + // Unproject a screen location (using the given model, proj and viewport) to find + // the first hit on a mesh. + // + // Inputs: + // pos screen space coordinates + // F #F by 3 face matrix + // model model matrix + // proj projection matrix + // viewport vieweport vector + // ei EmbreeIntersector containing (V,F) + // Outputs: + // fid id of the first face hit + // bc barycentric coordinates of hit + // Returns true if there is a hit + IGL_INLINE bool unproject_onto_mesh( + const Eigen::Vector2f& pos, + const Eigen::MatrixXi& F, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const EmbreeIntersector & ei, + int& fid, + Eigen::Vector3f& bc); + + // Unproject a screen location (using the given model, proj and viewport) to find + // the first face on the mesh and the closest vertex + // + // Inputs: + // pos screen space coordinates + // F #F by 3 face matrix + // model model matrix + // proj projection matrix + // viewport vieweport vector + // ei EmbreeIntersector containing (V,F) + // Outputs: + // fid id of the first face hit + // vid vertex id of the closest vertex hit + // Returns true if there is a hit + IGL_INLINE bool unproject_onto_mesh( + const Eigen::Vector2f& pos, + const Eigen::MatrixXi& F, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const EmbreeIntersector & ei, + int& fid, + int& vid); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "unproject_onto_mesh.cpp" +#endif +#endif diff --git a/src/igl/euler_characteristic.cpp b/src/igl/euler_characteristic.cpp new file mode 100644 index 0000000000..ab7eba22b9 --- /dev/null +++ b/src/igl/euler_characteristic.cpp @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Michael Rabinovich +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "euler_characteristic.h" + +#include "edge_topology.h" +#include "edges.h" + +template +IGL_INLINE int igl::euler_characteristic( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F) +{ + + int euler_v = V.rows(); + Eigen::MatrixXi EV, FE, EF; + igl::edge_topology(V, F, EV, FE, EF); + int euler_e = EV.rows(); + int euler_f = F.rows(); + + int euler_char = euler_v - euler_e + euler_f; + return euler_char; + +} + +template +IGL_INLINE int igl::euler_characteristic( + const Eigen::MatrixBase & F) +{ + const int nf = F.rows(); + const int nv = F.maxCoeff()+1; + Eigen::Matrix E; + edges(F,E); + const int ne = E.rows(); + return nv - ne + nf; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template int igl::euler_characteristic, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template int igl::euler_characteristic >(Eigen::MatrixBase > const&); +#endif diff --git a/src/igl/euler_characteristic.h b/src/igl/euler_characteristic.h new file mode 100644 index 0000000000..f4c0c0a0f2 --- /dev/null +++ b/src/igl/euler_characteristic.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Michael Rabinovich +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EULER_CHARACTERISTIC_H +#define IGL_EULER_CHARACTERISTIC_H +#include "igl_inline.h" + +#include +#include +#include +namespace igl +{ + // Computes the Euler characteristic of a given mesh (V,F) + // + // Inputs: + // F #F by dim list of mesh faces (must be triangles) + // Returns An int containing the Euler characteristic + template + IGL_INLINE int euler_characteristic( + const Eigen::MatrixBase & F); + + // Computes the Euler characteristic of a given mesh (V,F) + // Templates: + // Scalar should be a floating point number type + // Index should be an integer type + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by dim list of mesh faces (must be triangles) + // Returns An int containing the Euler characteristic + template + IGL_INLINE int euler_characteristic( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "euler_characteristic.cpp" +#endif + +#endif diff --git a/src/igl/exact_geodesic.cpp b/src/igl/exact_geodesic.cpp new file mode 100644 index 0000000000..e20783b88c --- /dev/null +++ b/src/igl/exact_geodesic.cpp @@ -0,0 +1,3225 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Zhongshi Jiang +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "exact_geodesic.h" + +//Copyright (C) 2008 Danil Kirsanov, MIT License +//Code from https://code.google.com/archive/p/geodesic/ +// Compiled into a single file by Zhongshi Jiang + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace igl{ +namespace geodesic{ + +//#include "geodesic_constants_and_simple_functions.h" + +//double const GEODESIC_INF = std::numeric_limits::max(); +double const GEODESIC_INF = 1e100; + +//in order to avoid numerical problems with "infinitely small" intervals, +//we drop all the intervals smaller than SMALLEST_INTERVAL_RATIO*edge_length +double const SMALLEST_INTERVAL_RATIO = 1e-6; +//double const SMALL_EPSILON = 1e-10; + + +inline double cos_from_edges(double const a, //compute the cosine of the angle given the lengths of the edges + double const b, + double const c) +{ + assert(a>1e-50); + assert(b>1e-50); + assert(c>1e-50); + + double result = (b*b + c*c - a*a)/(2.0*b*c); + result = std::max(result, -1.0); + return std::min(result, 1.0); +} + +inline double angle_from_edges(double const a, //compute the cosine of the angle given the lengths of the edges + double const b, + double const c) +{ + return acos(cos_from_edges(a,b,c)); +} + +template +inline bool read_mesh_from_file(char* filename, + Points& points, + Faces& faces) +{ + std::ifstream file(filename); + assert(file.is_open()); + if(!file.is_open()) return false; + + unsigned num_points; + file >> num_points; + assert(num_points>=3); + + unsigned num_faces; + file >> num_faces; + + points.resize(num_points*3); + for(typename Points::iterator i=points.begin(); i!=points.end(); ++i) + { + file >> *i; + } + + faces.resize(num_faces*3); + for(typename Faces::iterator i=faces.begin(); i!=faces.end(); ++i) + { + file >> *i; + } + file.close(); + + return true; +} + +// #include "geodesic_memory" +template //quickly allocates multiple elements of a given type; no deallocation +class SimlpeMemoryAllocator +{ +public: + typedef T* pointer; + + SimlpeMemoryAllocator(unsigned block_size = 0, + unsigned max_number_of_blocks = 0) + { + reset(block_size, + max_number_of_blocks); + }; + + ~SimlpeMemoryAllocator(){}; + + void reset(unsigned block_size, + unsigned max_number_of_blocks) + { + m_block_size = block_size; + m_max_number_of_blocks = max_number_of_blocks; + + + m_current_position = 0; + + m_storage.reserve(max_number_of_blocks); + m_storage.resize(1); + m_storage[0].resize(block_size); + }; + + pointer allocate(unsigned const n) //allocate n units + { + assert(n < m_block_size); + + if(m_current_position + n >= m_block_size) + { + m_storage.push_back( std::vector() ); + m_storage.back().resize(m_block_size); + m_current_position = 0; + } + pointer result = & m_storage.back()[m_current_position]; + m_current_position += n; + + return result; + }; +private: + std::vector > m_storage; + unsigned m_block_size; //size of a single block + unsigned m_max_number_of_blocks; //maximum allowed number of blocks + unsigned m_current_position; //first unused element inside the current block +}; + + +template //quickly allocates and deallocates single elements of a given type +class MemoryAllocator +{ +public: + typedef T* pointer; + + MemoryAllocator(unsigned block_size = 1024, + unsigned max_number_of_blocks = 1024) + { + reset(block_size, + max_number_of_blocks); + }; + + ~MemoryAllocator(){}; + + void clear() + { + reset(m_block_size, + m_max_number_of_blocks); + } + + void reset(unsigned block_size, + unsigned max_number_of_blocks) + { + m_block_size = block_size; + m_max_number_of_blocks = max_number_of_blocks; + + assert(m_block_size > 0); + assert(m_max_number_of_blocks > 0); + + m_current_position = 0; + + m_storage.reserve(max_number_of_blocks); + m_storage.resize(1); + m_storage[0].resize(block_size); + + m_deleted.clear(); + m_deleted.reserve(2*block_size); + }; + + pointer allocate() //allocates single unit of memory + { + pointer result; + if(m_deleted.empty()) + { + if(m_current_position + 1 >= m_block_size) + { + m_storage.push_back( std::vector() ); + m_storage.back().resize(m_block_size); + m_current_position = 0; + } + result = & m_storage.back()[m_current_position]; + ++m_current_position; + } + else + { + result = m_deleted.back(); + m_deleted.pop_back(); + } + + return result; + }; + + void deallocate(pointer p) //allocate n units + { + if(m_deleted.size() < m_deleted.capacity()) + { + m_deleted.push_back(p); + } + }; + +private: + std::vector > m_storage; + unsigned m_block_size; //size of a single block + unsigned m_max_number_of_blocks; //maximum allowed number of blocks + unsigned m_current_position; //first unused element inside the current block + + std::vector m_deleted; //pointers to deleted elemets +}; + + +class OutputBuffer +{ +public: + OutputBuffer(): + m_num_bytes(0) + {} + + void clear() + { + m_num_bytes = 0; + m_buffer = std::shared_ptr(); + } + + template + T* allocate(unsigned n) + { + double wanted = n*sizeof(T); + if(wanted > m_num_bytes) + { + unsigned new_size = (unsigned) ceil(wanted / (double)sizeof(double)); + m_buffer = std::shared_ptr(new double[new_size]); + m_num_bytes = new_size*sizeof(double); + } + + return (T*)m_buffer.get(); + } + + template + T* get() + { + return (T*)m_buffer.get(); + } + + template + unsigned capacity() + { + return (unsigned)floor((double)m_num_bytes/(double)sizeof(T)); + }; + +private: + + std::shared_ptr m_buffer; + unsigned m_num_bytes; +}; + + + + +class Vertex; +class Edge; +class Face; +class Mesh; +class MeshElementBase; + +typedef Vertex* vertex_pointer; +typedef Edge* edge_pointer; +typedef Face* face_pointer; +typedef Mesh* mesh_pointer; +typedef MeshElementBase* base_pointer; + +template //simple vector that stores info about mesh references +class SimpleVector //for efficiency, it uses an outside memory allocator +{ +public: + SimpleVector(): + m_size(0), + m_begin(NULL) + {}; + + typedef Data* iterator; + + unsigned size(){return m_size;}; + iterator begin(){return m_begin;}; + iterator end(){return m_begin + m_size;}; + + template + void set_allocation(DataPointer begin, unsigned size) + { + assert(begin != NULL || size == 0); + m_size = size; + m_begin = (iterator)begin; + } + + Data& operator[](unsigned i) + { + assert(i < m_size); + return *(m_begin + i); + } + + void clear() + { + m_size = 0; + m_begin = NULL; + } + +private: + unsigned m_size; + Data* m_begin; +}; + +enum PointType +{ + VERTEX, + EDGE, + FACE, + UNDEFINED_POINT +}; + +class MeshElementBase //prototype of vertices, edges and faces +{ +public: + typedef SimpleVector vertex_pointer_vector; + typedef SimpleVector edge_pointer_vector; + typedef SimpleVector face_pointer_vector; + + MeshElementBase(): + m_id(0), + m_type(UNDEFINED_POINT) + {}; + + vertex_pointer_vector& adjacent_vertices(){return m_adjacent_vertices;}; + edge_pointer_vector& adjacent_edges(){return m_adjacent_edges;}; + face_pointer_vector& adjacent_faces(){return m_adjacent_faces;}; + + unsigned& id(){return m_id;}; + PointType type(){return m_type;}; + +protected: + vertex_pointer_vector m_adjacent_vertices; //list of the adjacent vertices + edge_pointer_vector m_adjacent_edges; //list of the adjacent edges + face_pointer_vector m_adjacent_faces; //list of the adjacent faces + + unsigned m_id; //unique id + PointType m_type; //vertex, edge or face +}; + +class Point3D //point in 3D and corresponding operations +{ +public: + Point3D(){}; + Point3D(Point3D* p) + { + x() = p->x(); + y() = p->y(); + z() = p->z(); + }; + + double* xyz(){return m_coordinates;}; + double& x(){return *m_coordinates;}; + double& y(){return *(m_coordinates+1);}; + double& z(){return *(m_coordinates+2);}; + + void set(double new_x, double new_y, double new_z) + { + x() = new_x; + y() = new_y; + z() = new_z; + } + + void set(double* data) + { + x() = *data; + y() = *(data+1); + z() = *(data+2); + } + + double distance(double* v) + { + double dx = m_coordinates[0] - v[0]; + double dy = m_coordinates[1] - v[1]; + double dz = m_coordinates[2] - v[2]; + + return sqrt(dx*dx + dy*dy + dz*dz); + }; + + double distance(Point3D* v) + { + return distance(v->xyz()); + }; + + void add(Point3D* v) + { + x() += v->x(); + y() += v->y(); + z() += v->z(); + }; + + void multiply(double v) + { + x() *= v; + y() *= v; + z() *= v; + }; + +private: + double m_coordinates[3]; //xyz +}; + +class Vertex: public MeshElementBase, public Point3D +{ +public: + Vertex() + { + m_type = VERTEX; + }; + + ~Vertex(){}; + + bool& saddle_or_boundary(){return m_saddle_or_boundary;}; +private: + //this flag speeds up exact geodesic algorithm + bool m_saddle_or_boundary; //it is true if total adjacent angle is larger than 2*PI or this vertex belongs to the mesh boundary +}; + + +class Face: public MeshElementBase +{ +public: + Face() + { + m_type = FACE; + }; + + ~Face(){}; + + edge_pointer opposite_edge(vertex_pointer v); + vertex_pointer opposite_vertex(edge_pointer e); + edge_pointer next_edge(edge_pointer e, vertex_pointer v); + + double vertex_angle(vertex_pointer v) + { + for(unsigned i=0; i<3; ++i) + { + if(adjacent_vertices()[i]->id() == v->id()) + { + return m_corner_angles[i]; + } + } + assert(0); + return 0; + } + + double* corner_angles(){return m_corner_angles;}; + +private: + double m_corner_angles[3]; //triangle angles in radians; angles correspond to vertices in m_adjacent_vertices +}; + +class Edge: public MeshElementBase +{ +public: + Edge() + { + m_type = EDGE; + }; + + ~Edge(){}; + + double& length(){return m_length;}; + + face_pointer opposite_face(face_pointer f) + { + if(adjacent_faces().size() == 1) + { + assert(adjacent_faces()[0]->id() == f->id()); + return NULL; + } + + assert(adjacent_faces()[0]->id() == f->id() || + adjacent_faces()[1]->id() == f->id()); + + return adjacent_faces()[0]->id() == f->id() ? + adjacent_faces()[1] : adjacent_faces()[0]; + }; + + vertex_pointer opposite_vertex(vertex_pointer v) + { + assert(belongs(v)); + + return adjacent_vertices()[0]->id() == v->id() ? + adjacent_vertices()[1] : adjacent_vertices()[0]; + }; + + bool belongs(vertex_pointer v) + { + return adjacent_vertices()[0]->id() == v->id() || + adjacent_vertices()[1]->id() == v->id(); + } + + bool is_boundary(){return adjacent_faces().size() == 1;}; + + vertex_pointer v0(){return adjacent_vertices()[0];}; + vertex_pointer v1(){return adjacent_vertices()[1];}; + + void local_coordinates(Point3D* point, + double& x, + double& y) + { + double d0 = point->distance(v0()); + if(d0 < 1e-50) + { + x = 0.0; + y = 0.0; + return; + } + + double d1 = point->distance(v1()); + if(d1 < 1e-50) + { + x = m_length; + y = 0.0; + return; + } + + x = m_length/2.0 + (d0*d0 - d1*d1)/(2.0*m_length); + y = sqrt(std::max(0.0, d0*d0 - x*x)); + return; + } + +private: + double m_length; //length of the edge +}; + +class SurfacePoint:public Point3D //point on the surface of the mesh +{ +public: + SurfacePoint(): + m_p(NULL) + {}; + + SurfacePoint(vertex_pointer v): //set the surface point in the vertex + SurfacePoint::Point3D(v), + m_p(v) + {}; + + SurfacePoint(face_pointer f): //set the surface point in the center of the face + m_p(f) + { + set(0,0,0); + add(f->adjacent_vertices()[0]); + add(f->adjacent_vertices()[1]); + add(f->adjacent_vertices()[2]); + multiply(1./3.); + }; + + SurfacePoint(edge_pointer e, //set the surface point in the middle of the edge + double a = 0.5): + m_p(e) + { + double b = 1 - a; + + vertex_pointer v0 = e->adjacent_vertices()[0]; + vertex_pointer v1 = e->adjacent_vertices()[1]; + + x() = b*v0->x() + a*v1->x(); + y() = b*v0->y() + a*v1->y(); + z() = b*v0->z() + a*v1->z(); + }; + + SurfacePoint(base_pointer g, + double x, + double y, + double z, + PointType t = UNDEFINED_POINT): + m_p(g) + { + set(x,y,z); + }; + + void initialize(SurfacePoint const& p) + { + *this = p; + } + + ~SurfacePoint(){}; + + PointType type(){return m_p ? m_p->type() : UNDEFINED_POINT;}; + base_pointer& base_element(){return m_p;}; +protected: + base_pointer m_p; //could be face, vertex or edge pointer +}; + +inline edge_pointer Face::opposite_edge(vertex_pointer v) +{ + for(unsigned i=0; i<3; ++i) + { + edge_pointer e = adjacent_edges()[i]; + if(!e->belongs(v)) + { + return e; + } + } + assert(0); + return NULL; +} + +inline vertex_pointer Face::opposite_vertex(edge_pointer e) +{ + for(unsigned i=0; i<3; ++i) + { + vertex_pointer v = adjacent_vertices()[i]; + if(!e->belongs(v)) + { + return v; + } + } + assert(0); + return NULL; +} + +inline edge_pointer Face::next_edge(edge_pointer e, vertex_pointer v) +{ + assert(e->belongs(v)); + + for(unsigned i=0; i<3; ++i) + { + edge_pointer next = adjacent_edges()[i]; + if(e->id() != next->id() && next->belongs(v)) + { + return next; + } + } + assert(0); + return NULL; +} + +struct HalfEdge //prototype of the edge; used for mesh construction +{ + unsigned face_id; + unsigned vertex_0; //adjacent vertices sorted by id value + unsigned vertex_1; //they are sorted, vertex_0 < vertex_1 +}; + +inline bool operator < (const HalfEdge &x, const HalfEdge &y) +{ + if(x.vertex_0 == y.vertex_0) + { + return x.vertex_1 < y.vertex_1; + } + else + { + return x.vertex_0 < y.vertex_0; + } +} + +inline bool operator != (const HalfEdge &x, const HalfEdge &y) +{ + return x.vertex_0 != y.vertex_0 || x.vertex_1 != y.vertex_1; +} + +inline bool operator == (const HalfEdge &x, const HalfEdge &y) +{ + return x.vertex_0 == y.vertex_0 && x.vertex_1 == y.vertex_1; +} + +struct edge_visible_from_source +{ + unsigned source; + edge_pointer edge; +}; + +class Mesh +{ +public: + Mesh() + {}; + + ~Mesh(){}; + + template + void initialize_mesh_data(unsigned num_vertices, + Points& p, + unsigned num_faces, + Faces& tri); //build mesh from regular point-triangle representation + + template + void initialize_mesh_data(Points& p, Faces& tri); //build mesh from regular point-triangle representation + + std::vector& vertices(){return m_vertices;}; + std::vector& edges(){return m_edges;}; + std::vector& faces(){return m_faces;}; + + unsigned closest_vertices(SurfacePoint* p, + std::vector* storage = NULL); //list vertices closest to the point + +private: + + void build_adjacencies(); //build internal structure of the mesh + bool verify(); //verifies connectivity of the mesh and prints some debug info + + typedef void* void_pointer; + void_pointer allocate_pointers(unsigned n) + { + return m_pointer_allocator.allocate(n); + } + + std::vector m_vertices; + std::vector m_edges; + std::vector m_faces; + + SimlpeMemoryAllocator m_pointer_allocator; //fast memory allocating for Face/Vertex/Edge cross-references +}; + +inline unsigned Mesh::closest_vertices(SurfacePoint* p, + std::vector* storage) +{ + assert(p->type() != UNDEFINED_POINT); + + if(p->type() == VERTEX) + { + if(storage) + { + storage->push_back(static_cast(p->base_element())); + } + return 1; + } + else if(p->type() == FACE) + { + if(storage) + { + vertex_pointer* vp= p->base_element()->adjacent_vertices().begin(); + storage->push_back(*vp); + storage->push_back(*(vp+1)); + storage->push_back(*(vp+2)); + } + return 2; + } + else if(p->type() == EDGE) //for edge include all 4 adjacent vertices + { + edge_pointer edge = static_cast(p->base_element()); + + if(storage) + { + storage->push_back(edge->adjacent_vertices()[0]); + storage->push_back(edge->adjacent_vertices()[1]); + + for(unsigned i = 0; i < edge->adjacent_faces().size(); ++i) + { + face_pointer face = edge->adjacent_faces()[i]; + storage->push_back(face->opposite_vertex(edge)); + } + } + return 2 + edge->adjacent_faces().size(); + } + + assert(0); + return 0; +} + +template +void Mesh::initialize_mesh_data(Points& p, Faces& tri) //build mesh from regular point-triangle representation +{ + assert(p.size() % 3 == 0); + unsigned const num_vertices = p.size() / 3; + assert(tri.size() % 3 == 0); + unsigned const num_faces = tri.size() / 3; + + initialize_mesh_data(num_vertices, p, num_faces, tri); +} + +template +void Mesh::initialize_mesh_data(unsigned num_vertices, + Points& p, + unsigned num_faces, + Faces& tri) +{ + unsigned const approximate_number_of_internal_pointers = (num_vertices + num_faces)*4; + unsigned const max_number_of_pointer_blocks = 100; + m_pointer_allocator.reset(approximate_number_of_internal_pointers, + max_number_of_pointer_blocks); + + m_vertices.resize(num_vertices); + for(unsigned i=0; iadjacent Faces + std::vector count(m_vertices.size()); //count adjacent vertices + for(unsigned i=0; iid(); + assert(vertex_id < m_vertices.size()); + count[vertex_id]++; + } + } + + for(unsigned i=0; iadjacent_faces()[count[v->id()]++] = &f; + } + } + + //find all edges + //i.e. find all half-edges, sort and combine them into edges + std::vector half_edges(m_faces.size()*3); + unsigned k = 0; + for(unsigned i=0; iid(); + unsigned vertex_id_2 = f.adjacent_vertices()[(j+1) % 3]->id(); + half_edges[k].vertex_0 = std::min(vertex_id_1, vertex_id_2); + half_edges[k].vertex_1 = std::max(vertex_id_1, vertex_id_2); + + k++; + } + } + std::sort(half_edges.begin(), half_edges.end()); + + unsigned number_of_edges = 1; + for(unsigned i=1; iadjacent Vertices and Faces + m_edges.resize(number_of_edges); + unsigned edge_id = 0; + for(unsigned i=0; idistance(e.adjacent_vertices()[1]); + assert(e.length() > 1e-100); //algorithm works well with non-degenerate meshes only + + if(i != half_edges.size()-1 && half_edges[i] == half_edges[i+1]) //double edge + { + e.adjacent_faces().set_allocation(allocate_pointers(2),2); + e.adjacent_faces()[0] = &m_faces[half_edges[i].face_id]; + e.adjacent_faces()[1] = &m_faces[half_edges[i+1].face_id]; + i += 2; + } + else //single edge + { + e.adjacent_faces().set_allocation(allocate_pointers(1),1); //one adjucent faces + e.adjacent_faces()[0] = &m_faces[half_edges[i].face_id]; + i += 1; + } + } + + // Vertices->adjacent Edges + std::fill(count.begin(), count.end(), 0); + for(unsigned i=0; iid()]++; + count[e.adjacent_vertices()[1]->id()]++; + } + for(unsigned i=0; iadjacent_edges()[count[v->id()]++] = &e; + } + } + + // Faces->adjacent Edges + for(unsigned i=0; iid()]<3); + f->adjacent_edges()[count[f->id()]++] = &e; + } + } + + //compute angles for the faces + for(unsigned i=0; ilength(); + } + + double angle = angle_from_edges(abc[0], abc[1], abc[2]); + assert(angle>1e-5); //algorithm works well with non-degenerate meshes only + + f.corner_angles()[j] = angle; + sum += angle; + } + assert(std::abs(sum - igl::PI) < 1e-5); //algorithm works well with non-degenerate meshes only + } + + //define m_turn_around_flag for vertices + std::vector total_vertex_angle(m_vertices.size()); + for(unsigned i=0; iid()] += f.corner_angles()[j]; + } + } + + for(unsigned i=0; i 2.0*igl::PI - 1e-5); + } + + for(unsigned i=0; isaddle_or_boundary() = true; + e.adjacent_vertices()[1]->saddle_or_boundary() = true; + } + } + + assert(verify()); +} + +inline bool Mesh::verify() //verifies connectivity of the mesh and prints some debug info +{ + std::cout << std::endl; + // make sure that all vertices are mentioned at least once. + // though the loose vertex is not a bug, it most likely indicates that something is wrong with the mesh + std::vector map(m_vertices.size(), false); + for(unsigned i=0; iadjacent_vertices()[0]->id()] = true; + map[e->adjacent_vertices()[1]->id()] = true; + } + assert(std::find(map.begin(), map.end(), false) == map.end()); + + //make sure that the mesh is connected trough its edges + //if mesh has more than one connected component, it is most likely a bug + std::vector stack(1,&m_faces[0]); + stack.reserve(m_faces.size()); + + map.resize(m_faces.size()); + std::fill(map.begin(), map.end(), false); + map[0] = true; + + while(!stack.empty()) + { + face_pointer f = stack.back(); + stack.pop_back(); + + for(unsigned i=0; i<3; ++i) + { + edge_pointer e = f->adjacent_edges()[i]; + face_pointer f_adjacent = e->opposite_face(f); + if(f_adjacent && !map[f_adjacent->id()]) + { + map[f_adjacent->id()] = true; + stack.push_back(f_adjacent); + } + } + } + assert(std::find(map.begin(), map.end(), false) == map.end()); + + //print some mesh statistics that can be useful in debugging + // std::cout << "mesh has " << m_vertices.size() + // << " vertices, " << m_faces.size() + // << " faces, " << m_edges.size() + // << " edges\n"; + + unsigned total_boundary_edges = 0; + double longest_edge = 0; + double shortest_edge = 1e100; + for(unsigned i=0; iset(data); + unsigned type = (unsigned) data[3]; + unsigned id = (unsigned) data[4]; + + + if(type == 0) //vertex + { + point->base_element() = &mesh->vertices()[id]; + } + else if(type == 1) //edge + { + point->base_element() = &mesh->edges()[id]; + } + else //face + { + point->base_element() = &mesh->faces()[id]; + } +} + +inline void fill_surface_point_double(geodesic::SurfacePoint* point, + double* data, + long mesh_id) +{ + data[0] = point->x(); + data[1] = point->y(); + data[2] = point->z(); + data[4] = point->base_element()->id(); + + if(point->type() == VERTEX) //vertex + { + data[3] = 0; + } + else if(point->type() == EDGE) //edge + { + data[3] = 1; + } + else //face + { + data[3] = 2; + } +} + +class Interval; +class IntervalList; +typedef Interval* interval_pointer; +typedef IntervalList* list_pointer; + +class Interval //interval of the edge +{ +public: + + Interval(){}; + ~Interval(){}; + + enum DirectionType + { + FROM_FACE_0, + FROM_FACE_1, + FROM_SOURCE, + UNDEFINED_DIRECTION + }; + + double signal(double x) //geodesic distance function at point x + { + assert(x>=0.0 && x <= m_edge->length()); + + if(m_d == GEODESIC_INF) + { + return GEODESIC_INF; + } + else + { + double dx = x - m_pseudo_x; + if(m_pseudo_y == 0.0) + { + return m_d + std::abs(dx); + } + else + { + return m_d + sqrt(dx*dx + m_pseudo_y*m_pseudo_y); + } + } + } + + double max_distance(double end) + { + if(m_d == GEODESIC_INF) + { + return GEODESIC_INF; + } + else + { + double a = std::abs(m_start - m_pseudo_x); + double b = std::abs(end - m_pseudo_x); + + return a > b ? m_d + sqrt(a*a + m_pseudo_y*m_pseudo_y): + m_d + sqrt(b*b + m_pseudo_y*m_pseudo_y); + } + } + + void compute_min_distance(double stop) //compute min, given c,d theta, start, end. + { + assert(stop > m_start); + + if(m_d == GEODESIC_INF) + { + m_min = GEODESIC_INF; + } + else if(m_start > m_pseudo_x) + { + m_min = signal(m_start); + } + else if(stop < m_pseudo_x) + { + m_min = signal(stop); + } + else + { + assert(m_pseudo_y<=0); + m_min = m_d - m_pseudo_y; + } + } + //compare two intervals in the queue + bool operator()(interval_pointer const x, interval_pointer const y) const + { + if(x->min() != y->min()) + { + return x->min() < y->min(); + } + else if(x->start() != y->start()) + { + return x->start() < y->start(); + } + else + { + return x->edge()->id() < y->edge()->id(); + } + } + + double stop() //return the endpoint of the interval + { + return m_next ? m_next->start() : m_edge->length(); + } + + double hypotenuse(double a, double b) + { + return sqrt(a*a + b*b); + } + + void find_closest_point(double const x, + double const y, + double& offset, + double& distance); //find the point on the interval that is closest to the point (alpha, s) + + double& start(){return m_start;}; + double& d(){return m_d;}; + double& pseudo_x(){return m_pseudo_x;}; + double& pseudo_y(){return m_pseudo_y;}; + double& min(){return m_min;}; + interval_pointer& next(){return m_next;}; + edge_pointer& edge(){return m_edge;}; + DirectionType& direction(){return m_direction;}; + bool visible_from_source(){return m_direction == FROM_SOURCE;}; + unsigned& source_index(){return m_source_index;}; + + void initialize(edge_pointer edge, + SurfacePoint* point = NULL, + unsigned source_index = 0); + +protected: + double m_start; //initial point of the interval on the edge + double m_d; //distance from the source to the pseudo-source + double m_pseudo_x; //coordinates of the pseudo-source in the local coordinate system + double m_pseudo_y; //y-coordinate should be always negative + double m_min; //minimum distance on the interval + + interval_pointer m_next; //pointer to the next interval in the list + edge_pointer m_edge; //edge that the interval belongs to + unsigned m_source_index; //the source it belongs to + DirectionType m_direction; //where the interval is coming from +}; + +struct IntervalWithStop : public Interval +{ +public: + double& stop(){return m_stop;}; +protected: + double m_stop; +}; + +class IntervalList //list of the of intervals of the given edge +{ +public: + IntervalList(){m_first = NULL;}; + ~IntervalList(){}; + + void clear() + { + m_first = NULL; + }; + + void initialize(edge_pointer e) + { + m_edge = e; + m_first = NULL; + }; + + interval_pointer covering_interval(double offset) //returns the interval that covers the offset + { + assert(offset >= 0.0 && offset <= m_edge->length()); + + interval_pointer p = m_first; + while(p && p->stop() < offset) + { + p = p->next(); + } + + return p;// && p->start() <= offset ? p : NULL; + }; + + void find_closest_point(SurfacePoint* point, + double& offset, + double& distance, + interval_pointer& interval) + { + interval_pointer p = m_first; + distance = GEODESIC_INF; + interval = NULL; + + double x,y; + m_edge->local_coordinates(point, x, y); + + while(p) + { + if(p->min()find_closest_point(x, y, o, d); + if(d < distance) + { + distance = d; + offset = o; + interval = p; + } + } + p = p->next(); + } + }; + + unsigned number_of_intervals() + { + interval_pointer p = m_first; + unsigned count = 0; + while(p) + { + ++count; + p = p->next(); + } + return count; + } + + interval_pointer last() + { + interval_pointer p = m_first; + if(p) + { + while(p->next()) + { + p = p->next(); + } + } + return p; + } + + double signal(double x) + { + interval_pointer interval = covering_interval(x); + + return interval ? interval->signal(x) : GEODESIC_INF; + } + + interval_pointer& first(){return m_first;}; + edge_pointer& edge(){return m_edge;}; +private: + interval_pointer m_first; //pointer to the first member of the list + edge_pointer m_edge; //edge that owns this list +}; + +class SurfacePointWithIndex : public SurfacePoint +{ +public: + unsigned index(){return m_index;}; + + void initialize(SurfacePoint& p, unsigned index) + { + SurfacePoint::initialize(p); + m_index = index; + } + + bool operator()(SurfacePointWithIndex* x, SurfacePointWithIndex* y) const //used for sorting + { + assert(x->type() != UNDEFINED_POINT && y->type() !=UNDEFINED_POINT); + + if(x->type() != y->type()) + { + return x->type() < y->type(); + } + else + { + return x->base_element()->id() < y->base_element()->id(); + } + } + +private: + unsigned m_index; +}; + +class SortedSources : public std::vector +{ +private: + typedef std::vector sorted_vector_type; +public: + typedef sorted_vector_type::iterator sorted_iterator; + typedef std::pair sorted_iterator_pair; + + sorted_iterator_pair sources(base_pointer mesh_element) + { + m_search_dummy.base_element() = mesh_element; + + return equal_range(m_sorted.begin(), + m_sorted.end(), + &m_search_dummy, + m_compare_less); + } + + void initialize(std::vector& sources) //we initialize the sources by copie + { + resize(sources.size()); + m_sorted.resize(sources.size()); + for(unsigned i=0; ilength(); + if(std::abs(hs+hc) < local_epsilon) + { + if(rs<=m_start) + { + r = m_start; + d_out = signal(m_start) + std::abs(rs - m_start); + } + else if(rs>=end) + { + r = end; + d_out = signal(end) + fabs(end - rs); + } + else + { + r = rs; + d_out = signal(rs); + } + } + else + { + double ri = (rs*hc + hs*rc)/(hs+hc); + + if(riend) + { + r = end; + d_out = signal(end) + hypotenuse(end - rs, hs); + } + else + { + r = ri; + d_out = m_d + hypotenuse(rc - rs, hc + hs); + } + } + } + + +inline void Interval::initialize(edge_pointer edge, + SurfacePoint* source, + unsigned source_index) +{ + m_next = NULL; + //m_geodesic_previous = NULL; + m_direction = UNDEFINED_DIRECTION; + m_edge = edge; + m_source_index = source_index; + + m_start = 0.0; + //m_stop = edge->length(); + if(!source) + { + m_d = GEODESIC_INF; + m_min = GEODESIC_INF; + return; + } + m_d = 0; + + if(source->base_element()->type() == VERTEX) + { + if(source->base_element()->id() == edge->v0()->id()) + { + m_pseudo_x = 0.0; + m_pseudo_y = 0.0; + m_min = 0.0; + return; + } + else if(source->base_element()->id() == edge->v1()->id()) + { + m_pseudo_x = stop(); + m_pseudo_y = 0.0; + m_min = 0.0; + return; + } + } + + edge->local_coordinates(source, m_pseudo_x, m_pseudo_y); + m_pseudo_y = -m_pseudo_y; + + compute_min_distance(stop()); +} + + + +// #include "geodesic_algorithm_base.h" +class GeodesicAlgorithmBase +{ +public: + enum AlgorithmType + { + EXACT, + DIJKSTRA, + SUBDIVISION, + UNDEFINED_ALGORITHM + }; + + GeodesicAlgorithmBase(geodesic::Mesh* mesh): + m_type(UNDEFINED_ALGORITHM), + m_max_propagation_distance(1e100), + m_mesh(mesh) + {}; + + virtual ~GeodesicAlgorithmBase(){}; + + virtual void propagate(std::vector& sources, + double max_propagation_distance = GEODESIC_INF, //propagation algorithm stops after reaching the certain distance from the source + std::vector* stop_points = NULL) = 0; //or after ensuring that all the stop_points are covered + + virtual void trace_back(SurfacePoint& destination, //trace back piecewise-linear path + std::vector& path) = 0; + + void geodesic(SurfacePoint& source, + SurfacePoint& destination, + std::vector& path); //lazy people can find geodesic path with one function call + + void geodesic(std::vector& sources, + std::vector& destinations, + std::vector >& paths); //lazy people can find geodesic paths with one function call + + virtual unsigned best_source(SurfacePoint& point, //after propagation step is done, quickly find what source this point belongs to and what is the distance to this source + double& best_source_distance) = 0; + + virtual void print_statistics() //print info about timing and memory usage in the propagation step of the algorithm + { + std::cout << "propagation step took " << m_time_consumed << " seconds " << std::endl; + }; + + AlgorithmType type(){return m_type;}; + + virtual std::string name(); + + geodesic::Mesh* mesh(){return m_mesh;}; +protected: + + void set_stop_conditions(std::vector* stop_points, + double stop_distance); + double stop_distance() + { + return m_max_propagation_distance; + } + + AlgorithmType m_type; // type of the algorithm + + typedef std::pair stop_vertex_with_distace_type; + std::vector m_stop_vertices; // algorithm stops propagation after covering certain vertices + double m_max_propagation_distance; // or reaching the certain distance + + geodesic::Mesh* m_mesh; + + double m_time_consumed; //how much time does the propagation step takes + double m_propagation_distance_stopped; //at what distance (if any) the propagation algorithm stopped +}; + +inline double length(std::vector& path) +{ + double length = 0; + if(!path.empty()) + { + for(unsigned i=0; i& path) +{ + std::cout << "number of the points in the path = " << path.size() + << ", length of the path = " << length(path) + << std::endl; +} + +inline std::string GeodesicAlgorithmBase::name() +{ + switch(m_type) + { + case EXACT: + return "exact"; + case DIJKSTRA: + return "dijkstra"; + case SUBDIVISION: + return "subdivision"; + default: + case UNDEFINED_ALGORITHM: + return "undefined"; + } +} + +inline void GeodesicAlgorithmBase::geodesic(SurfacePoint& source, + SurfacePoint& destination, + std::vector& path) //lazy people can find geodesic path with one function call +{ + std::vector sources(1, source); + std::vector stop_points(1, destination); + double const max_propagation_distance = GEODESIC_INF; + + propagate(sources, + max_propagation_distance, + &stop_points); + + trace_back(destination, path); +} + +inline void GeodesicAlgorithmBase::geodesic(std::vector& sources, + std::vector& destinations, + std::vector >& paths) //lazy people can find geodesic paths with one function call +{ + double const max_propagation_distance = GEODESIC_INF; + + propagate(sources, + max_propagation_distance, + &destinations); //we use desinations as stop points + + paths.resize(destinations.size()); + + for(unsigned i=0; i* stop_points, + double stop_distance) +{ + m_max_propagation_distance = stop_distance; + + if(!stop_points) + { + m_stop_vertices.clear(); + return; + } + + m_stop_vertices.resize(stop_points->size()); + + std::vector possible_vertices; + for(unsigned i = 0; i < stop_points->size(); ++i) + { + SurfacePoint* point = &(*stop_points)[i]; + + possible_vertices.clear(); + m_mesh->closest_vertices(point, &possible_vertices); + + vertex_pointer closest_vertex = NULL; + double min_distance = 1e100; + for(unsigned j = 0; j < possible_vertices.size(); ++j) + { + double distance = point->distance(possible_vertices[j]); + if(distance < min_distance) + { + min_distance = distance; + closest_vertex = possible_vertices[j]; + } + } + assert(closest_vertex); + + m_stop_vertices[i].first = closest_vertex; + m_stop_vertices[i].second = min_distance; + } +} + + + +class GeodesicAlgorithmExact : public GeodesicAlgorithmBase +{ +public: + GeodesicAlgorithmExact(geodesic::Mesh* mesh): + GeodesicAlgorithmBase(mesh), + m_memory_allocator(mesh->edges().size(), mesh->edges().size()), + m_edge_interval_lists(mesh->edges().size()) + { + m_type = EXACT; + + for(unsigned i=0; iedges()[i]); + } + }; + + ~GeodesicAlgorithmExact(){}; + + void propagate(std::vector& sources, + double max_propagation_distance = GEODESIC_INF, //propagation algorithm stops after reaching the certain distance from the source + std::vector* stop_points = NULL); //or after ensuring that all the stop_points are covered + + void trace_back(SurfacePoint& destination, //trace back piecewise-linear path + std::vector& path); + + unsigned best_source(SurfacePoint& point, //quickly find what source this point belongs to and what is the distance to this source + double& best_source_distance); + + void print_statistics(); + +private: + typedef std::set IntervalQueue; + + void update_list_and_queue(list_pointer list, + IntervalWithStop* candidates, //up to two candidates + unsigned num_candidates); + + unsigned compute_propagated_parameters(double pseudo_x, + double pseudo_y, + double d, //parameters of the interval + double start, + double end, //start/end of the interval + double alpha, //corner angle + double L, //length of the new edge + bool first_interval, //if it is the first interval on the edge + bool last_interval, + bool turn_left, + bool turn_right, + IntervalWithStop* candidates); //if it is the last interval on the edge + + void construct_propagated_intervals(bool invert, + edge_pointer edge, + face_pointer face, //constructs iNew from the rest of the data + IntervalWithStop* candidates, + unsigned& num_candidates, + interval_pointer source_interval); + + double compute_positive_intersection(double start, + double pseudo_x, + double pseudo_y, + double sin_alpha, + double cos_alpha); //used in construct_propagated_intervals + + unsigned intersect_intervals(interval_pointer zero, + IntervalWithStop* one); //intersecting two intervals with up to three intervals in the end + + interval_pointer best_first_interval(SurfacePoint& point, + double& best_total_distance, + double& best_interval_position, + unsigned& best_source_index); + + bool check_stop_conditions(unsigned& index); + + void clear() + { + m_memory_allocator.clear(); + m_queue.clear(); + for(unsigned i=0; iid()]; + }; + + void set_sources(std::vector& sources) + { + m_sources.initialize(sources); + } + + void initialize_propagation_data(); + + void list_edges_visible_from_source(MeshElementBase* p, + std::vector& storage); //used in initialization + + long visible_from_source(SurfacePoint& point); //used in backtracing + + void best_point_on_the_edge_set(SurfacePoint& point, + std::vector const& storage, + interval_pointer& best_interval, + double& best_total_distance, + double& best_interval_position); + + void possible_traceback_edges(SurfacePoint& point, + std::vector& storage); + + bool erase_from_queue(interval_pointer p); + + IntervalQueue m_queue; //interval queue + + MemoryAllocator m_memory_allocator; //quickly allocate and deallocate intervals + std::vector m_edge_interval_lists; //every edge has its interval data + + enum MapType {OLD, NEW}; //used for interval intersection + MapType map[5]; + double start[6]; + interval_pointer i_new[5]; + + unsigned m_queue_max_size; //used for statistics + unsigned m_iterations; //used for statistics + + SortedSources m_sources; +}; + +inline void GeodesicAlgorithmExact::best_point_on_the_edge_set(SurfacePoint& point, + std::vector const& storage, + interval_pointer& best_interval, + double& best_total_distance, + double& best_interval_position) +{ + best_total_distance = 1e100; + for(unsigned i=0; ifind_closest_point(&point, + offset, + distance, + interval); + + if(distance < best_total_distance) + { + best_interval = interval; + best_total_distance = distance; + best_interval_position = offset; + } + } +} + +inline void GeodesicAlgorithmExact::possible_traceback_edges(SurfacePoint& point, + std::vector& storage) +{ + storage.clear(); + + if(point.type() == VERTEX) + { + vertex_pointer v = static_cast(point.base_element()); + for(unsigned i=0; iadjacent_faces().size(); ++i) + { + face_pointer f = v->adjacent_faces()[i]; + storage.push_back(f->opposite_edge(v)); + } + } + else if(point.type() == EDGE) + { + edge_pointer e = static_cast(point.base_element()); + for(unsigned i=0; iadjacent_faces().size(); ++i) + { + face_pointer f = e->adjacent_faces()[i]; + + storage.push_back(f->next_edge(e,e->v0())); + storage.push_back(f->next_edge(e,e->v1())); + } + } + else + { + face_pointer f = static_cast(point.base_element()); + storage.push_back(f->adjacent_edges()[0]); + storage.push_back(f->adjacent_edges()[1]); + storage.push_back(f->adjacent_edges()[2]); + } +} + + +inline long GeodesicAlgorithmExact::visible_from_source(SurfacePoint& point) //negative if not visible +{ + assert(point.type() != UNDEFINED_POINT); + + if(point.type() == EDGE) + { + edge_pointer e = static_cast(point.base_element()); + list_pointer list = interval_list(e); + double position = std::min(point.distance(e->v0()), e->length()); + interval_pointer interval = list->covering_interval(position); + //assert(interval); + if(interval && interval->visible_from_source()) + { + return (long)interval->source_index(); + } + else + { + return -1; + } + } + else if(point.type() == FACE) + { + return -1; + } + else if(point.type() == VERTEX) + { + vertex_pointer v = static_cast(point.base_element()); + for(unsigned i=0; iadjacent_edges().size(); ++i) + { + edge_pointer e = v->adjacent_edges()[i]; + list_pointer list = interval_list(e); + + double position = e->v0()->id() == v->id() ? 0.0 : e->length(); + interval_pointer interval = list->covering_interval(position); + if(interval && interval->visible_from_source()) + { + return (long)interval->source_index(); + } + } + + return -1; + } + + assert(0); + return 0; +} + +inline double GeodesicAlgorithmExact::compute_positive_intersection(double start, + double pseudo_x, + double pseudo_y, + double sin_alpha, + double cos_alpha) +{ + assert(pseudo_y < 0); + + double denominator = sin_alpha*(pseudo_x - start) - cos_alpha*pseudo_y; + if(denominator<0.0) + { + return -1.0; + } + + double numerator = -pseudo_y*start; + + if(numerator < 1e-30) + { + return 0.0; + } + + if(denominator < 1e-30) + { + return -1.0; + } + + return numerator/denominator; +} + +inline void GeodesicAlgorithmExact::list_edges_visible_from_source(MeshElementBase* p, + std::vector& storage) +{ + assert(p->type() != UNDEFINED_POINT); + + if(p->type() == FACE) + { + face_pointer f = static_cast(p); + for(unsigned i=0; i<3; ++i) + { + storage.push_back(f->adjacent_edges()[i]); + } + } + else if(p->type() == EDGE) + { + edge_pointer e = static_cast(p); + storage.push_back(e); + } + else //VERTEX + { + vertex_pointer v = static_cast(p); + for(unsigned i=0; iadjacent_edges().size(); ++i) + { + storage.push_back(v->adjacent_edges()[i]); + } + + } +} + +inline bool GeodesicAlgorithmExact::erase_from_queue(interval_pointer p) +{ + if(p->min() < GEODESIC_INF/10.0)// && p->min >= queue->begin()->first) + { + assert(m_queue.count(p)<=1); //the set is unique + + IntervalQueue::iterator it = m_queue.find(p); + + if(it != m_queue.end()) + { + m_queue.erase(it); + return true; + } + } + + return false; +} + +inline unsigned GeodesicAlgorithmExact::intersect_intervals(interval_pointer zero, + IntervalWithStop* one) //intersecting two intervals with up to three intervals in the end +{ + assert(zero->edge()->id() == one->edge()->id()); + assert(zero->stop() > one->start() && zero->start() < one->stop()); + assert(one->min() < GEODESIC_INF/10.0); + + double const local_epsilon = SMALLEST_INTERVAL_RATIO*one->edge()->length(); + + unsigned N=0; + if(zero->min() > GEODESIC_INF/10.0) + { + start[0] = zero->start(); + if(zero->start() < one->start() - local_epsilon) + { + map[0] = OLD; + start[1] = one->start(); + map[1] = NEW; + N = 2; + } + else + { + map[0] = NEW; + N = 1; + } + + if(zero->stop() > one->stop() + local_epsilon) + { + map[N] = OLD; //"zero" interval + start[N++] = one->stop(); + } + + start[N+1] = zero->stop(); + return N; + } + + double const local_small_epsilon = 1e-8*one->edge()->length(); + + double D = zero->d() - one->d(); + double x0 = zero->pseudo_x(); + double x1 = one->pseudo_x(); + double R0 = x0*x0 + zero->pseudo_y()*zero->pseudo_y(); + double R1 = x1*x1 + one->pseudo_y()*one->pseudo_y(); + + double inter[2]; //points of intersection + char Ninter=0; //number of the points of the intersection + + if(std::abs(D)local_small_epsilon) + { + inter[0] = (R1 - R0)/(2.*denom); //one solution + Ninter = 1; + } + } + else + { + double D2 = D*D; + double Q = 0.5*(R1-R0-D2); + double X = x0 - x1; + + double A = X*X - D2; + double B = Q*X + D2*x0; + double C = Q*Q - D2*R0; + + if (std::abs(A)local_small_epsilon) + { + inter[0] = -C/B; //one solution + Ninter = 1; + } + } + else + { + double det = B*B-A*C; + if(det>local_small_epsilon*local_small_epsilon) //two roots + { + det = sqrt(det); + if(A>0.0) //make sure that the roots are ordered + { + inter[0] = (-B - det)/A; + inter[1] = (-B + det)/A; + } + else + { + inter[0] = (-B + det)/A; + inter[1] = (-B - det)/A; + } + + if(inter[1] - inter[0] > local_small_epsilon) + { + Ninter = 2; + } + else + { + Ninter = 1; + } + } + else if(det>=0.0) //single root + { + inter[0] = -B/A; + Ninter = 1; + } + } + } + //---------------------------find possible intervals--------------------------------------- + double left = std::max(zero->start(), one->start()); //define left and right boundaries of the intersection of the intervals + double right = std::min(zero->stop(), one->stop()); + + double good_start[4]; //points of intersection within the (left, right) limits +"left" + "right" + good_start[0] = left; + char Ngood_start=1; //number of the points of the intersection + + for(char i=0; i left + local_epsilon && x < right - local_epsilon) + { + good_start[Ngood_start++] = x; + } + } + good_start[Ngood_start++] = right; + + MapType mid_map[3]; + for(char i=0; isignal(mid) <= one->signal(mid) ? OLD : NEW; + } + + //-----------------------------------output---------------------------------- + N = 0; + if(zero->start() < left - local_epsilon) //additional "zero" interval + { + if(mid_map[0] == OLD) //first interval in the map is already the old one + { + good_start[0] = zero->start(); + } + else + { + map[N] = OLD; //"zero" interval + start[N++] = zero->start(); + } + } + + for(long i=0;istop() > one->stop() + local_epsilon) + { + if(N==0 || map[N-1] == NEW) + { + map[N] = OLD; //"zero" interval + start[N++] = one->stop(); + } + } + + start[0] = zero->start(); // just to make sure that epsilons do not damage anything + //start[N] = zero->stop(); + + return N; +} + +inline void GeodesicAlgorithmExact::initialize_propagation_data() +{ + clear(); + + IntervalWithStop candidate; + std::vector edges_visible_from_source; + for(unsigned i=0; ibase_element(), + edges_visible_from_source); + + for(unsigned j=0; jlength(); + candidate.compute_min_distance(candidate.stop()); + candidate.direction() = Interval::FROM_SOURCE; + + update_list_and_queue(interval_list(e), &candidate, 1); + } + } +} + +inline void GeodesicAlgorithmExact::propagate(std::vector& sources, + double max_propagation_distance, //propagation algorithm stops after reaching the certain distance from the source + std::vector* stop_points) +{ + set_stop_conditions(stop_points, max_propagation_distance); + set_sources(sources); + initialize_propagation_data(); + + clock_t start = clock(); + + unsigned satisfied_index = 0; + + m_iterations = 0; //for statistics + m_queue_max_size = 0; + + IntervalWithStop candidates[2]; + + while(!m_queue.empty()) + { + m_queue_max_size = std::max(static_cast(m_queue.size()), m_queue_max_size); + + unsigned const check_period = 10; + if(++m_iterations % check_period == 0) //check if we covered all required vertices + { + if (check_stop_conditions(satisfied_index)) + { + break; + } + } + + interval_pointer min_interval = *m_queue.begin(); + m_queue.erase(m_queue.begin()); + edge_pointer edge = min_interval->edge(); + list_pointer list = interval_list(edge); + + assert(min_interval->d() < GEODESIC_INF); + + bool const first_interval = min_interval->start() == 0.0; + //bool const last_interval = min_interval->stop() == edge->length(); + bool const last_interval = min_interval->next() == NULL; + + bool const turn_left = edge->v0()->saddle_or_boundary(); + bool const turn_right = edge->v1()->saddle_or_boundary(); + + for(unsigned i=0; iadjacent_faces().size(); ++i) //two possible faces to propagate + { + if(!edge->is_boundary()) //just in case, always propagate boundary edges + { + if((i == 0 && min_interval->direction() == Interval::FROM_FACE_0) || + (i == 1 && min_interval->direction() == Interval::FROM_FACE_1)) + { + continue; + } + } + + face_pointer face = edge->adjacent_faces()[i]; //if we come from 1, go to 2 + edge_pointer next_edge = face->next_edge(edge,edge->v0()); + + unsigned num_propagated = compute_propagated_parameters(min_interval->pseudo_x(), + min_interval->pseudo_y(), + min_interval->d(), //parameters of the interval + min_interval->start(), + min_interval->stop(), //start/end of the interval + face->vertex_angle(edge->v0()), //corner angle + next_edge->length(), //length of the new edge + first_interval, //if it is the first interval on the edge + last_interval, + turn_left, + turn_right, + candidates); //if it is the last interval on the edge + bool propagate_to_right = true; + + if(num_propagated) + { + if(candidates[num_propagated-1].stop() != next_edge->length()) + { + propagate_to_right = false; + } + + bool const invert = next_edge->v0()->id() != edge->v0()->id(); //if the origins coinside, do not invert intervals + + construct_propagated_intervals(invert, //do not inverse + next_edge, + face, + candidates, + num_propagated, + min_interval); + + update_list_and_queue(interval_list(next_edge), + candidates, + num_propagated); + } + + if(propagate_to_right) + { + //propogation to the right edge + double length = edge->length(); + next_edge = face->next_edge(edge,edge->v1()); + + num_propagated = compute_propagated_parameters(length - min_interval->pseudo_x(), + min_interval->pseudo_y(), + min_interval->d(), //parameters of the interval + length - min_interval->stop(), + length - min_interval->start(), //start/end of the interval + face->vertex_angle(edge->v1()), //corner angle + next_edge->length(), //length of the new edge + last_interval, //if it is the first interval on the edge + first_interval, + turn_right, + turn_left, + candidates); //if it is the last interval on the edge + + if(num_propagated) + { + bool const invert = next_edge->v0()->id() != edge->v1()->id(); //if the origins coinside, do not invert intervals + + construct_propagated_intervals(invert, //do not inverse + next_edge, + face, + candidates, + num_propagated, + min_interval); + + update_list_and_queue(interval_list(next_edge), + candidates, + num_propagated); + } + } + } + } + + m_propagation_distance_stopped = m_queue.empty() ? GEODESIC_INF : (*m_queue.begin())->min(); + clock_t stop = clock(); + m_time_consumed = (static_cast(stop)-static_cast(start))/CLOCKS_PER_SEC; + +/* for(unsigned i=0; ifirst(); + assert(p->start() == 0.0); + while(p->next()) + { + assert(p->stop() == p->next()->start()); + assert(p->d() < GEODESIC_INF); + p = p->next(); + } + }*/ +} + + +inline bool GeodesicAlgorithmExact::check_stop_conditions(unsigned& index) +{ + double queue_distance = (*m_queue.begin())->min(); + if(queue_distance < stop_distance()) + { + return false; + } + + while(index < m_stop_vertices.size()) + { + vertex_pointer v = m_stop_vertices[index].first; + edge_pointer edge = v->adjacent_edges()[0]; //take any edge + + double distance = edge->v0()->id() == v->id() ? + interval_list(edge)->signal(0.0) : + interval_list(edge)->signal(edge->length()); + + if(queue_distance < distance + m_stop_vertices[index].second) + { + return false; + } + + ++index; + } + return true; +} + + +inline void GeodesicAlgorithmExact::update_list_and_queue(list_pointer list, + IntervalWithStop* candidates, //up to two candidates + unsigned num_candidates) +{ + assert(num_candidates <= 2); + //assert(list->first() != NULL); + edge_pointer edge = list->edge(); + double const local_epsilon = SMALLEST_INTERVAL_RATIO * edge->length(); + + if(list->first() == NULL) + { + interval_pointer* p = &list->first(); + IntervalWithStop* first; + IntervalWithStop* second; + + if(num_candidates == 1) + { + first = candidates; + second = candidates; + first->compute_min_distance(first->stop()); + } + else + { + if(candidates->start() <= (candidates+1)->start()) + { + first = candidates; + second = candidates+1; + } + else + { + first = candidates+1; + second = candidates; + } + assert(first->stop() == second->start()); + + first->compute_min_distance(first->stop()); + second->compute_min_distance(second->stop()); + } + + if(first->start() > 0.0) + { + *p = m_memory_allocator.allocate(); + (*p)->initialize(edge); + p = &(*p)->next(); + } + + *p = m_memory_allocator.allocate(); + memcpy(*p,first,sizeof(Interval)); + m_queue.insert(*p); + + if(num_candidates == 2) + { + p = &(*p)->next(); + *p = m_memory_allocator.allocate(); + memcpy(*p,second,sizeof(Interval)); + m_queue.insert(*p); + } + + if(second->stop() < edge->length()) + { + p = &(*p)->next(); + *p = m_memory_allocator.allocate(); + (*p)->initialize(edge); + (*p)->start() = second->stop(); + } + else + { + (*p)->next() = NULL; + } + return; + } + + bool propagate_flag; + + for(unsigned i=0; ifirst(); + assert(p->start() == 0.0); + + while(p != NULL && p->stop() - local_epsilon < q->start()) + { + p = p->next(); + } + + while(p != NULL && p->start() < q->stop() - local_epsilon) //go through all old intervals + { + unsigned const N = intersect_intervals(p, q); //interset two intervals + + if(N == 1) + { + if(map[0]==OLD) //if "p" is always better, we do not need to update anything) + { + if(previous) //close previous interval and put in into the queue + { + previous->next() = p; + previous->compute_min_distance(p->start()); + m_queue.insert(previous); + previous = NULL; + } + + p = p->next(); + + } + else if(previous) //extend previous interval to cover everything; remove p + { + previous->next() = p->next(); + erase_from_queue(p); + m_memory_allocator.deallocate(p); + + p = previous->next(); + } + else //p becomes "previous" + { + previous = p; + interval_pointer next = p->next(); + erase_from_queue(p); + + memcpy(previous,q,sizeof(Interval)); + + previous->start() = start[0]; + previous->next() = next; + + p = next; + } + continue; + } + + //update_flag = true; + + Interval swap(*p); //used for swapping information + propagate_flag = erase_from_queue(p); + + for(unsigned j=1; jnext() = p; + previous->compute_min_distance(previous->stop()); + m_queue.insert(previous); + previous = NULL; + } + i_new[0] = p; + p->next() = i_new[1]; + p->start() = start[0]; + } + else if(previous) //extend previous interval to cover everything; remove p + { + i_new[0] = previous; + previous->next() = i_new[1]; + m_memory_allocator.deallocate(p); + previous = NULL; + } + else //p becomes "previous" + { + i_new[0] = p; + memcpy(p,q,sizeof(Interval)); + + p->next() = i_new[1]; + p->start() = start[0]; + } + + assert(!previous); + + for(unsigned j=1; jnext() = swap.next(); + } + else + { + current_interval->next() = i_new[j+1]; + } + + current_interval->start() = start[j]; + } + + for(unsigned j=0; jcompute_min_distance(current_interval->stop()); //compute minimal distance + + if(map[j]==NEW || (map[j]==OLD && propagate_flag)) + { + m_queue.insert(current_interval); + } + } + } + + p = swap.next(); + } + + if(previous) //close previous interval and put in into the queue + { + previous->compute_min_distance(previous->stop()); + m_queue.insert(previous); + previous = NULL; + } + } +} + +inline unsigned GeodesicAlgorithmExact::compute_propagated_parameters(double pseudo_x, + double pseudo_y, + double d, //parameters of the interval + double begin, + double end, //start/end of the interval + double alpha, //corner angle + double L, //length of the new edge + bool first_interval, //if it is the first interval on the edge + bool last_interval, + bool turn_left, + bool turn_right, + IntervalWithStop* candidates) //if it is the last interval on the edge +{ + assert(pseudo_y<=0.0); + assert(dstart() = 0.0; + p->stop() = L; + p->d() = d - pseudo_x; + p->pseudo_x() = 0.0; + p->pseudo_y() = 0.0; + return 1; + } + else if(last_interval && pseudo_x >= end) + { + p->start() = 0.0; + p->stop() = L; + p->d() = d + pseudo_x-end; + p->pseudo_x() = end*cos(alpha); + p->pseudo_y() = -end*sin(alpha); + return 1; + } + else if(pseudo_x >= begin && pseudo_x <= end) + { + p->start() = 0.0; + p->stop() = L; + p->d() = d; + p->pseudo_x() = pseudo_x*cos(alpha); + p->pseudo_y() = -pseudo_x*sin(alpha); + return 1; + } + else + { + return 0; + } + } + + double sin_alpha = sin(alpha); + double cos_alpha = cos(alpha); + + //important: for the first_interval, this function returns zero only if the new edge is "visible" from the source + //if the new edge can be covered only after turn_over, the value is negative (-1.0) + double L1 = compute_positive_intersection(begin, + pseudo_x, + pseudo_y, + sin_alpha, + cos_alpha); + + if(L1 < 0 || L1 >= L) + { + if(first_interval && turn_left) + { + p->start() = 0.0; + p->stop() = L; + p->d() = d + sqrt(pseudo_x*pseudo_x + pseudo_y*pseudo_y); + p->pseudo_y() = 0.0; + p->pseudo_x() = 0.0; + return 1; + } + else + { + return 0; + } + } + + double L2 = compute_positive_intersection(end, + pseudo_x, + pseudo_y, + sin_alpha, + cos_alpha); + + if(L2 < 0 || L2 >= L) + { + p->start() = L1; + p->stop() = L; + p->d() = d; + p->pseudo_x() = cos_alpha*pseudo_x + sin_alpha*pseudo_y; + p->pseudo_y() = -sin_alpha*pseudo_x + cos_alpha*pseudo_y; + + return 1; + } + + p->start() = L1; + p->stop() = L2; + p->d() = d; + p->pseudo_x() = cos_alpha*pseudo_x + sin_alpha*pseudo_y; + p->pseudo_y() = -sin_alpha*pseudo_x + cos_alpha*pseudo_y; + assert(p->pseudo_y() <= 0.0); + + if(!(last_interval && turn_right)) + { + return 1; + } + else + { + p = candidates + 1; + + p->start() = L2; + p->stop() = L; + double dx = pseudo_x - end; + p->d() = d + sqrt(dx*dx + pseudo_y*pseudo_y); + p->pseudo_x() = end*cos_alpha; + p->pseudo_y() = -end*sin_alpha; + + return 2; + } +} + +inline void GeodesicAlgorithmExact::construct_propagated_intervals(bool invert, + edge_pointer edge, + face_pointer face, //constructs iNew from the rest of the data + IntervalWithStop* candidates, + unsigned& num_candidates, + interval_pointer source_interval) //up to two candidates +{ + double edge_length = edge->length(); + double local_epsilon = SMALLEST_INTERVAL_RATIO * edge_length; + + //kill very small intervals in order to avoid precision problems + if(num_candidates == 2) + { + double start = std::min(candidates->start(), (candidates+1)->start()); + double stop = std::max(candidates->stop(), (candidates+1)->stop()); + if(candidates->stop()-candidates->start() < local_epsilon) // kill interval 0 + { + *candidates = *(candidates+1); + num_candidates = 1; + candidates->start() = start; + candidates->stop() = stop; + } + else if ((candidates+1)->stop() - (candidates+1)->start() < local_epsilon) + { + num_candidates = 1; + candidates->start() = start; + candidates->stop() = stop; + } + } + + IntervalWithStop* first; + IntervalWithStop* second; + if(num_candidates == 1) + { + first = candidates; + second = candidates; + } + else + { + if(candidates->start() <= (candidates+1)->start()) + { + first = candidates; + second = candidates+1; + } + else + { + first = candidates+1; + second = candidates; + } + assert(first->stop() == second->start()); + } + + if(first->start() < local_epsilon) + { + first->start() = 0.0; + } + if(edge_length - second->stop() < local_epsilon) + { + second->stop() = edge_length; + } + + //invert intervals if necessary; fill missing data and set pointers correctly + Interval::DirectionType direction = edge->adjacent_faces()[0]->id() == face->id() ? + Interval::FROM_FACE_0 : + Interval::FROM_FACE_1; + + if(!invert) //in this case everything is straighforward, we do not have to invert the intervals + { + for(unsigned i=0; inext() = (i == num_candidates - 1) ? NULL : candidates + i + 1; + p->edge() = edge; + p->direction() = direction; + p->source_index() = source_interval->source_index(); + + p->min() = 0.0; //it will be changed later on + + assert(p->start() < p->stop()); + } + } + else //now we have to invert the intervals + { + for(unsigned i=0; inext() = (i == 0) ? NULL : candidates + i - 1; + p->edge() = edge; + p->direction() = direction; + p->source_index() = source_interval->source_index(); + + double length = edge_length; + p->pseudo_x() = length - p->pseudo_x(); + + double start = length - p->stop(); + p->stop() = length - p->start(); + p->start() = start; + + p->min() = 0; + + assert(p->start() < p->stop()); + assert(p->start() >= 0.0); + assert(p->stop() <= edge->length()); + } + } +} + + +inline unsigned GeodesicAlgorithmExact::best_source(SurfacePoint& point, //quickly find what source this point belongs to and what is the distance to this source + double& best_source_distance) +{ + double best_interval_position; + unsigned best_source_index; + + best_first_interval(point, + best_source_distance, + best_interval_position, + best_source_index); + + return best_source_index; +} + +inline interval_pointer GeodesicAlgorithmExact::best_first_interval(SurfacePoint& point, + double& best_total_distance, + double& best_interval_position, + unsigned& best_source_index) +{ + assert(point.type() != UNDEFINED_POINT); + + interval_pointer best_interval = NULL; + best_total_distance = GEODESIC_INF; + + if(point.type() == EDGE) + { + edge_pointer e = static_cast(point.base_element()); + list_pointer list = interval_list(e); + + best_interval_position = point.distance(e->v0()); + best_interval = list->covering_interval(best_interval_position); + if(best_interval) + { + //assert(best_interval && best_interval->d() < GEODESIC_INF); + best_total_distance = best_interval->signal(best_interval_position); + best_source_index = best_interval->source_index(); + } + } + else if(point.type() == FACE) + { + face_pointer f = static_cast(point.base_element()); + for(unsigned i=0; i<3; ++i) + { + edge_pointer e = f->adjacent_edges()[i]; + list_pointer list = interval_list(e); + + double offset; + double distance; + interval_pointer interval; + + list->find_closest_point(&point, + offset, + distance, + interval); + + if(interval && distance < best_total_distance) + { + best_interval = interval; + best_total_distance = distance; + best_interval_position = offset; + best_source_index = interval->source_index(); + } + } + + //check for all sources that might be located inside this face + SortedSources::sorted_iterator_pair local_sources = m_sources.sources(f); + for(SortedSources::sorted_iterator it=local_sources.first; it != local_sources.second; ++it) + { + SurfacePointWithIndex* source = *it; + double distance = point.distance(source); + if(distance < best_total_distance) + { + best_interval = NULL; + best_total_distance = distance; + best_interval_position = 0.0; + best_source_index = source->index(); + } + } + } + else if(point.type() == VERTEX) + { + vertex_pointer v = static_cast(point.base_element()); + for(unsigned i=0; iadjacent_edges().size(); ++i) + { + edge_pointer e = v->adjacent_edges()[i]; + list_pointer list = interval_list(e); + + double position = e->v0()->id() == v->id() ? 0.0 : e->length(); + interval_pointer interval = list->covering_interval(position); + if(interval) + { + double distance = interval->signal(position); + + if(distance < best_total_distance) + { + best_interval = interval; + best_total_distance = distance; + best_interval_position = position; + best_source_index = interval->source_index(); + } + } + } + } + + if(best_total_distance > m_propagation_distance_stopped) //result is unreliable + { + best_total_distance = GEODESIC_INF; + return NULL; + } + else + { + return best_interval; + } +} + +inline void GeodesicAlgorithmExact::trace_back(SurfacePoint& destination, //trace back piecewise-linear path + std::vector& path) +{ + path.clear(); + double best_total_distance; + double best_interval_position; + unsigned source_index = std::numeric_limits::max(); + interval_pointer best_interval = best_first_interval(destination, + best_total_distance, + best_interval_position, + source_index); + + if(best_total_distance >= GEODESIC_INF/2.0) //unable to find the right path + { + return; + } + + path.push_back(destination); + + if(best_interval) //if we did not hit the face source immediately + { + std::vector possible_edges; + possible_edges.reserve(10); + + while(visible_from_source(path.back()) < 0) //while this point is not in the direct visibility of some source (if we are inside the FACE, we obviously hit the source) + { + SurfacePoint& q = path.back(); + + possible_traceback_edges(q, possible_edges); + + interval_pointer interval; + double total_distance; + double position; + + best_point_on_the_edge_set(q, + possible_edges, + interval, + total_distance, + position); + + //std::cout << total_distance + length(path) << std::endl; + assert(total_distancesource_index(); + + edge_pointer e = interval->edge(); + double local_epsilon = SMALLEST_INTERVAL_RATIO*e->length(); + if(position < local_epsilon) + { + path.push_back(SurfacePoint(e->v0())); + } + else if(position > e->length()-local_epsilon) + { + path.push_back(SurfacePoint(e->v1())); + } + else + { + double normalized_position = position/e->length(); + path.push_back(SurfacePoint(e, normalized_position)); + } + } + } + + SurfacePoint& source = static_cast(m_sources[source_index]); + if(path.back().distance(&source) > 0) + { + path.push_back(source); + } +} + +inline void GeodesicAlgorithmExact::print_statistics() +{ + GeodesicAlgorithmBase::print_statistics(); + + unsigned interval_counter = 0; + for(unsigned i=0; i +IGL_INLINE void igl::exact_geodesic( + const Eigen::MatrixBase &V, + const Eigen::MatrixBase &F, + const Eigen::MatrixBase &VS, + const Eigen::MatrixBase &FS, + const Eigen::MatrixBase &VT, + const Eigen::MatrixBase &FT, + Eigen::PlainObjectBase &D) +{ + assert(V.cols() == 3 && F.cols() == 3 && "Only support 3D triangle mesh"); + assert(VS.cols() ==1 && FS.cols() == 1 && VT.cols() == 1 && FT.cols() ==1 && "Only support one dimensional inputs"); + std::vector points(V.rows() * V.cols()); + std::vector faces(F.rows() * F.cols()); + for (int i = 0; i < points.size(); i++) + { + points[i] = V(i / 3, i % 3); + } + for (int i = 0; i < faces.size(); i++) + { + faces[i] = F(i / 3, i % 3); + } + + igl::geodesic::Mesh mesh; + mesh.initialize_mesh_data(points, faces); + igl::geodesic::GeodesicAlgorithmExact exact_algorithm(&mesh); + + std::vector source(VS.rows() + FS.rows()); + std::vector target(VT.rows() + FT.rows()); + for (int i = 0; i < VS.rows(); i++) + { + source[i] = (igl::geodesic::SurfacePoint(&mesh.vertices()[VS(i)])); + } + for (int i = 0; i < FS.rows(); i++) + { + source[i] = (igl::geodesic::SurfacePoint(&mesh.faces()[FS(i)])); + } + + for (int i = 0; i < VT.rows(); i++) + { + target[i] = (igl::geodesic::SurfacePoint(&mesh.vertices()[VT(i)])); + } + for (int i = 0; i < FT.rows(); i++) + { + target[i] = (igl::geodesic::SurfacePoint(&mesh.faces()[FT(i)])); + } + + exact_algorithm.propagate(source); + std::vector path; + D.resize(target.size(), 1); + for (int i = 0; i < target.size(); i++) + { + exact_algorithm.trace_back(target[i], path); + D(i) = igl::geodesic::length(path); + } +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::exact_geodesic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix>(Eigen::MatrixBase> const &, Eigen::MatrixBase> const &, Eigen::MatrixBase> const &, Eigen::MatrixBase> const &, Eigen::MatrixBase> const &, Eigen::MatrixBase> const &, Eigen::PlainObjectBase> &); +template void igl::exact_geodesic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/exact_geodesic.h b/src/igl/exact_geodesic.h new file mode 100644 index 0000000000..cec59de69f --- /dev/null +++ b/src/igl/exact_geodesic.h @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Zhongshi Jiang +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_EXACT_GEODESIC_H +#define IGL_EXACT_GEODESIC_H + +#include "igl_inline.h" +#include + +namespace igl +{ + // Exact geodesic algorithm for triangular mesh with the implementation from https://code.google.com/archive/p/geodesic/, + // and the algorithm first described by Mitchell, Mount and Papadimitriou in 1987 + // + // Inputs: + // V #V by 3 list of 3D vertex positions + // F #F by 3 list of mesh faces + // VS #VS by 1 vector specifying indices of source vertices + // FS #FS by 1 vector specifying indices of source faces + // VT #VT by 1 vector specifying indices of target vertices + // FT #FT by 1 vector specifying indices of target faces + // Output: + // D #VT+#FT by 1 vector of geodesic distances of each target w.r.t. the nearest one in the source set + // + // Note: + // Specifying a face as target/source means its center. + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedVS, + typename DerivedFS, + typename DerivedVT, + typename DerivedFT, + typename DerivedD> + IGL_INLINE void exact_geodesic( + const Eigen::MatrixBase &V, + const Eigen::MatrixBase &F, + const Eigen::MatrixBase &VS, + const Eigen::MatrixBase &FS, + const Eigen::MatrixBase &VT, + const Eigen::MatrixBase &FT, + Eigen::PlainObjectBase &D); +} + +#ifndef IGL_STATIC_LIBRARY +# include "exact_geodesic.cpp" +#endif + +#endif \ No newline at end of file diff --git a/src/igl/example_fun.cpp b/src/igl/example_fun.cpp new file mode 100644 index 0000000000..9796c68e35 --- /dev/null +++ b/src/igl/example_fun.cpp @@ -0,0 +1,23 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "example_fun.h" +#include + +template +IGL_INLINE bool igl::example_fun(const Printable & input) +{ + using namespace std; + cout<<"example_fun: "<(const double& input); +template bool igl::example_fun(const int& input); +#endif diff --git a/src/igl/example_fun.h b/src/igl/example_fun.h new file mode 100644 index 0000000000..16c9ec6459 --- /dev/null +++ b/src/igl/example_fun.h @@ -0,0 +1,31 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EXAMPLE_FUN_H +#define IGL_EXAMPLE_FUN_H + +#include "igl_inline.h" + +namespace igl +{ + // This is an example of a function, it takes a templated parameter and + // shovels it into cout + // + // Templates: + // T type that supports + // Input: + // input some input of a Printable type + // Returns true for the sake of returning something + template + IGL_INLINE bool example_fun(const Printable & input); +} + +#ifndef IGL_STATIC_LIBRARY +# include "example_fun.cpp" +#endif + +#endif diff --git a/src/igl/exterior_edges.cpp b/src/igl/exterior_edges.cpp new file mode 100644 index 0000000000..04c1e9b8ad --- /dev/null +++ b/src/igl/exterior_edges.cpp @@ -0,0 +1,106 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "exterior_edges.h" +#include "oriented_facets.h" +#include "sort.h" +#include "unique_rows.h" + +#include +#include +#include +#include + +//template inline int sgn(T val) { +// return (T(0) < val) - (val < T(0)); +//} + +//static void mod2(std::pair, int>& p) +//{ +// using namespace std; +// // Be sure that sign of mod matches sign of argument +// p.second = p.second%2 ? sgn(p.second) : 0; +//} + +//// http://stackoverflow.com/a/5517869/148668 +//struct Compare +//{ +// int i; +// Compare(const int& i) : i(i) {} +//}; +//bool operator==(const std::pair,int>&p, const Compare& c) +//{ +// return c.i == p.second; +//} +//bool operator==(const Compare& c, const std::pair, int> &p) +//{ +// return c.i == p.second; +//} + +IGL_INLINE void igl::exterior_edges( + const Eigen::MatrixXi & F, + Eigen::MatrixXi & E) +{ + using namespace Eigen; + using namespace std; + assert(F.cols() == 3); + const size_t m = F.rows(); + MatrixXi all_E,sall_E,sort_order; + // Sort each edge by index + oriented_facets(F,all_E); + sort(all_E,2,true,sall_E,sort_order); + // Find unique edges + MatrixXi uE; + VectorXi IA,EMAP; + unique_rows(sall_E,uE,IA,EMAP); + VectorXi counts = VectorXi::Zero(uE.rows()); + for(size_t a = 0;a<3*m;a++) + { + counts(EMAP(a)) += (sort_order(a)==0?1:-1); + } + + E.resize(all_E.rows(),2); + { + int e = 0; + const size_t nue = uE.rows(); + // Append each unique edge with a non-zero amount of signed occurrences + for(size_t ue = 0; ue 0) + { + i = uE(ue,0); + j = uE(ue,1); + } + // Append edge for every repeated entry + const int abs_count = abs(count); + for(int k = 0;k +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EXTERIOR_EDGES_H +#define IGL_EXTERIOR_EDGES_H +#include "igl_inline.h" +#include +namespace igl +{ + // EXTERIOR_EDGES Determines boundary "edges" and also edges with an + // odd number of occurrences where seeing edge (i,j) counts as +1 and seeing + // the opposite edge (j,i) counts as -1 + // + // Inputs: + // F #F by simplex_size list of "faces" + // Outputs: + // E #E by simplex_size-1 list of exterior edges + // + IGL_INLINE void exterior_edges( + const Eigen::MatrixXi & F, + Eigen::MatrixXi & E); + // Inline version + IGL_INLINE Eigen::MatrixXi exterior_edges( const Eigen::MatrixXi & F); +} +#ifndef IGL_STATIC_LIBRARY +# include "exterior_edges.cpp" +#endif + +#endif diff --git a/src/igl/extract_manifold_patches.cpp b/src/igl/extract_manifold_patches.cpp new file mode 100644 index 0000000000..92269e2c88 --- /dev/null +++ b/src/igl/extract_manifold_patches.cpp @@ -0,0 +1,103 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "extract_manifold_patches.h" +#include "unique_edge_map.h" +#include +#include +#include + +template< + typename DerivedF, + typename DerivedEMAP, + typename uE2EType, + typename DerivedP> +IGL_INLINE size_t igl::extract_manifold_patches( + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& EMAP, + const std::vector >& uE2E, + Eigen::PlainObjectBase& P) +{ + assert(F.cols() == 3); + const size_t num_faces = F.rows(); + + auto edge_index_to_face_index = [&](size_t ei) { return ei % num_faces; }; + auto face_and_corner_index_to_edge_index = [&](size_t fi, size_t ci) { + return ci*num_faces + fi; + }; + auto is_manifold_edge = [&](size_t fi, size_t ci) -> bool { + const size_t ei = face_and_corner_index_to_edge_index(fi, ci); + return uE2E[EMAP(ei, 0)].size() == 2; + }; + auto get_adj_face_index = [&](size_t fi, size_t ci) -> size_t { + const size_t ei = face_and_corner_index_to_edge_index(fi, ci); + const auto& adj_faces = uE2E[EMAP(ei, 0)]; + assert(adj_faces.size() == 2); + if (adj_faces[0] == ei) { + return edge_index_to_face_index(adj_faces[1]); + } else { + assert(adj_faces[1] == ei); + return edge_index_to_face_index(adj_faces[0]); + } + }; + + typedef typename DerivedP::Scalar Scalar; + const Scalar INVALID = std::numeric_limits::max(); + P.resize(num_faces,1); + P.setConstant(INVALID); + size_t num_patches = 0; + for (size_t i=0; i Q; + Q.push(i); + P(i,0) = num_patches; + while (!Q.empty()) { + const size_t fid = Q.front(); + Q.pop(); + for (size_t j=0; j<3; j++) { + if (is_manifold_edge(fid, j)) { + const size_t adj_fid = get_adj_face_index(fid, j); + if (P(adj_fid,0) == INVALID) { + Q.push(adj_fid); + P(adj_fid,0) = num_patches; + } + } + } + } + num_patches++; + } + assert((P.array() != INVALID).all()); + + return num_patches; +} + +template< + typename DerivedF, + typename DerivedP> +IGL_INLINE size_t igl::extract_manifold_patches( + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& P) +{ + Eigen::MatrixXi E, uE; + Eigen::VectorXi EMAP; + std::vector > uE2E; + igl::unique_edge_map(F, E, uE, EMAP, uE2E); + return igl::extract_manifold_patches(F, EMAP, uE2E, P); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template unsigned long igl::extract_manifold_patches, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template size_t igl::extract_manifold_patches, Eigen::Matrix, unsigned long, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template unsigned long igl::extract_manifold_patches, Eigen::Matrix, unsigned long, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template unsigned __int64 igl::extract_manifold_patches, class Eigen::Matrix, unsigned __int64, class Eigen::Matrix>(class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class std::vector>, class std::allocator>>> const &, class Eigen::PlainObjectBase> &); +template unsigned __int64 igl::extract_manifold_patches, class Eigen::Matrix, unsigned __int64, class Eigen::Matrix>(class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> const &, class std::vector>, class std::allocator>>> const &, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/extract_manifold_patches.h b/src/igl/extract_manifold_patches.h new file mode 100644 index 0000000000..6bc25d5378 --- /dev/null +++ b/src/igl/extract_manifold_patches.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_EXTRACT_MANIFOLD_PATCHES +#define IGL_EXTRACT_MANIFOLD_PATCHES + +#include "igl_inline.h" +#include +#include + +namespace igl { + // Extract a set of maximal patches from a given mesh. + // A maximal patch is a subset of the input faces that are connected via + // manifold edges; a patch is as large as possible. + // + // Inputs: + // F #F by 3 list representing triangles. + // EMAP #F*3 list of indices of unique undirected edges. + // uE2E #uE list of lists of indices into E of coexisting edges. + // + // Output: + // P #F list of patch incides. + // + // Returns: + // number of manifold patches. + template < + typename DerivedF, + typename DerivedEMAP, + typename uE2EType, + typename DerivedP> + IGL_INLINE size_t extract_manifold_patches( + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& EMAP, + const std::vector >& uE2E, + Eigen::PlainObjectBase& P); + template < + typename DerivedF, + typename DerivedP> + IGL_INLINE size_t extract_manifold_patches( + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& P); +} +#ifndef IGL_STATIC_LIBRARY +# include "extract_manifold_patches.cpp" +#endif + +#endif diff --git a/src/igl/extract_non_manifold_edge_curves.cpp b/src/igl/extract_non_manifold_edge_curves.cpp new file mode 100644 index 0000000000..c4a5274a69 --- /dev/null +++ b/src/igl/extract_non_manifold_edge_curves.cpp @@ -0,0 +1,123 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "extract_non_manifold_edge_curves.h" +#include +#include +#include +#include +#include + +template< +typename DerivedF, +typename DerivedEMAP, +typename uE2EType > +IGL_INLINE void igl::extract_non_manifold_edge_curves( + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& /*EMAP*/, + const std::vector >& uE2E, + std::vector >& curves) { + const size_t num_faces = F.rows(); + assert(F.cols() == 3); + //typedef std::pair Edge; + auto edge_index_to_face_index = [&](size_t ei) { return ei % num_faces; }; + auto edge_index_to_corner_index = [&](size_t ei) { return ei / num_faces; }; + auto get_edge_end_points = [&](size_t ei, size_t& s, size_t& d) { + const size_t fi = edge_index_to_face_index(ei); + const size_t ci = edge_index_to_corner_index(ei); + s = F(fi, (ci+1)%3); + d = F(fi, (ci+2)%3); + }; + + curves.clear(); + const size_t num_unique_edges = uE2E.size(); + std::unordered_multimap vertex_edge_adjacency; + std::vector non_manifold_edges; + for (size_t i=0; i 0); + assert(vertex_edge_adjacency.count(d) > 0); + } + + auto expand_forward = [&](std::list& edge_curve, + size_t& front_vertex, size_t& end_vertex) { + while(vertex_edge_adjacency.count(front_vertex) == 2 && + front_vertex != end_vertex) { + auto adj_edges = vertex_edge_adjacency.equal_range(front_vertex); + for (auto itr = adj_edges.first; itr!=adj_edges.second; itr++) { + const size_t uei = itr->second; + assert(uE2E.at(uei).size() != 2); + const size_t ei = uE2E[uei][0]; + if (uei == edge_curve.back()) continue; + size_t s,d; + get_edge_end_points(ei, s, d); + edge_curve.push_back(uei); + if (s == front_vertex) { + front_vertex = d; + } else if (d == front_vertex) { + front_vertex = s; + } else { + throw "Invalid vertex/edge adjacency!"; + } + break; + } + } + }; + + auto expand_backward = [&](std::list& edge_curve, + size_t& front_vertex, size_t& end_vertex) { + while(vertex_edge_adjacency.count(front_vertex) == 2 && + front_vertex != end_vertex) { + auto adj_edges = vertex_edge_adjacency.equal_range(front_vertex); + for (auto itr = adj_edges.first; itr!=adj_edges.second; itr++) { + const size_t uei = itr->second; + assert(uE2E.at(uei).size() != 2); + const size_t ei = uE2E[uei][0]; + if (uei == edge_curve.front()) continue; + size_t s,d; + get_edge_end_points(ei, s, d); + edge_curve.push_front(uei); + if (s == front_vertex) { + front_vertex = d; + } else if (d == front_vertex) { + front_vertex = s; + } else { + throw "Invalid vertex/edge adjcency!"; + } + break; + } + } + }; + + std::vector visited(num_unique_edges, false); + for (const size_t i : non_manifold_edges) { + if (visited[i]) continue; + std::list edge_curve; + edge_curve.push_back(i); + + const auto& adj_edges = uE2E[i]; + assert(adj_edges.size() != 2); + const size_t ei = adj_edges[0]; + size_t s,d; + get_edge_end_points(ei, s, d); + + expand_forward(edge_curve, d, s); + expand_backward(edge_curve, s, d); + curves.emplace_back(edge_curve.begin(), edge_curve.end()); + for (auto itr = edge_curve.begin(); itr!=edge_curve.end(); itr++) { + visited[*itr] = true; + } + } + +} diff --git a/src/igl/extract_non_manifold_edge_curves.h b/src/igl/extract_non_manifold_edge_curves.h new file mode 100644 index 0000000000..173e0c5a2d --- /dev/null +++ b/src/igl/extract_non_manifold_edge_curves.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_NON_MANIFOLD_EDGE_CURVES +#define IGL_NON_MANIFOLD_EDGE_CURVES + +#include "igl_inline.h" +#include +#include + +namespace igl { + // Extract non-manifold curves from a given mesh. + // A non-manifold curves are a set of connected non-manifold edges that + // does not touch other non-manifold edges except at the end points. + // They are also maximal in the sense that they cannot be expanded by + // including more edges. + // + // Assumes the input mesh have all self-intersection resolved. See + // ``igl::cgal::remesh_self_intersection`` for more details. + // + // Inputs: + // F #F by 3 list representing triangles. + // EMAP #F*3 list of indices of unique undirected edges. + // uE2E #uE list of lists of indices into E of coexisting edges. + // + // Output: + // curves An array of arries of unique edge indices. + template< + typename DerivedF, + typename DerivedEMAP, + typename uE2EType> + IGL_INLINE void extract_non_manifold_edge_curves( + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& EMAP, + const std::vector >& uE2E, + std::vector >& curves); +} + +#ifndef IGL_STATIC_LIBRARY +# include "extract_non_manifold_edge_curves.cpp" +#endif + +#endif diff --git a/src/igl/face_areas.cpp b/src/igl/face_areas.cpp new file mode 100644 index 0000000000..74bf7fce04 --- /dev/null +++ b/src/igl/face_areas.cpp @@ -0,0 +1,65 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "face_areas.h" +#include "edge_lengths.h" +#include "doublearea.h" + +template +IGL_INLINE void igl::face_areas( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& T, + Eigen::PlainObjectBase& A) +{ + assert(T.cols() == 4); + DerivedA L; + edge_lengths(V,T,L); + return face_areas(L,A); +} + +template +IGL_INLINE void igl::face_areas( + const Eigen::MatrixBase& L, + Eigen::PlainObjectBase& A) +{ + return face_areas( + L,std::numeric_limits::quiet_NaN(),A); +} + +template +IGL_INLINE void igl::face_areas( + const Eigen::MatrixBase& L, + const typename DerivedL::Scalar doublearea_nan_replacement, + Eigen::PlainObjectBase& A) +{ + using namespace Eigen; + assert(L.cols() == 6); + const int m = L.rows(); + // (unsigned) face Areas (opposite vertices: 1 2 3 4) + Matrix + A0(m,1), A1(m,1), A2(m,1), A3(m,1); + Matrix + L0(m,3), L1(m,3), L2(m,3), L3(m,3); + L0<, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/face_areas.h b/src/igl/face_areas.h new file mode 100644 index 0000000000..9676deadc4 --- /dev/null +++ b/src/igl/face_areas.h @@ -0,0 +1,58 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FACE_AREAS_H +#define IGL_FACE_AREAS_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Constructs a list of face areas of faces opposite each index in a tet list + // + // Inputs: + // V #V by 3 list of mesh vertex positions + // T #T by 3 list of tet mesh indices into V + // Outputs: + // A #T by 4 list of face areas corresponding to faces opposite vertices + // 0,1,2,3 + // + template + IGL_INLINE void face_areas( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& T, + Eigen::PlainObjectBase& A); + // Compute tet-mesh face areas from edge lengths. + // + // Inputs: + // L #T by 6 list of tet-mesh edge lengths corresponding to edges + // [3 0],[3 1],[3 2],[1 2],[2 0],[0 1] + // Outputs: + // A #T by 4 list of face areas corresponding to faces opposite vertices + // 0,1,2,3: i.e. made of edges [123],[024],[015],[345] + // + // + template + IGL_INLINE void face_areas( + const Eigen::MatrixBase& L, + Eigen::PlainObjectBase& A); + // doublearea_nan_replacement See doublearea.h + template + IGL_INLINE void face_areas( + const Eigen::MatrixBase& L, + const typename DerivedL::Scalar doublearea_nan_replacement, + Eigen::PlainObjectBase& A); +} + +#ifndef IGL_STATIC_LIBRARY +# include "face_areas.cpp" +#endif + +#endif + + diff --git a/src/igl/face_occurrences.cpp b/src/igl/face_occurrences.cpp new file mode 100644 index 0000000000..4ef43f8225 --- /dev/null +++ b/src/igl/face_occurrences.cpp @@ -0,0 +1,58 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "face_occurrences.h" + +#include +#include "sort.h" +#include + +template +IGL_INLINE void igl::face_occurrences( + const std::vector > & F, + std::vector & C) +{ + using namespace std; + + // Get a list of sorted faces + vector > sortedF = F; + for(int i = 0; i < (int)F.size();i++) + { + sort(sortedF[i].begin(),sortedF[i].end()); + } + // Count how many times each sorted face occurs + map,int> counts; + for(int i = 0; i < (int)sortedF.size();i++) + { + if(counts.find(sortedF[i]) == counts.end()) + { + // initialize to count of 1 + counts[sortedF[i]] = 1; + }else + { + // increment count + counts[sortedF[i]]++; + assert(counts[sortedF[i]] == 2 && "Input should be manifold"); + } + } + + // Resize output to fit number of ones + C.resize(F.size()); + for(int i = 0;i< (int)F.size();i++) + { + // sorted face should definitely be in counts map + assert(counts.find(sortedF[i]) != counts.end()); + C[i] = counts[sortedF[i]]; + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::face_occurrences(std::vector >, std::allocator > > > const&, std::vector >&); +template void igl::face_occurrences(std::vector >, std::allocator > > > const&, std::vector >&); +#endif diff --git a/src/igl/face_occurrences.h b/src/igl/face_occurrences.h new file mode 100644 index 0000000000..de47b504c0 --- /dev/null +++ b/src/igl/face_occurrences.h @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FACE_OCCURRENCES +#define IGL_FACE_OCCURRENCES +#include "igl_inline.h" + +#include +namespace igl +{ + // Count the occruances of each face (row) in a list of face indices + // (irrespecitive of order) + // Inputs: + // F #F by simplex-size + // Outputs + // C #F list of counts + // Known bug: triangles/tets only (where ignoring order still gives simplex) + template + IGL_INLINE void face_occurrences( + const std::vector > & F, + std::vector & C); +} + +#ifndef IGL_STATIC_LIBRARY +# include "face_occurrences.cpp" +#endif + +#endif + + diff --git a/src/igl/faces_first.cpp b/src/igl/faces_first.cpp new file mode 100644 index 0000000000..9c0fa0d0b7 --- /dev/null +++ b/src/igl/faces_first.cpp @@ -0,0 +1,103 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "faces_first.h" + +#include +#include + +template +IGL_INLINE void igl::faces_first( + const MatV & V, + const MatF & F, + MatV & RV, + MatF & RF, + VecI & IM) +{ + assert(&V != &RV); + assert(&F != &RF); + using namespace std; + using namespace Eigen; + vector in_face(V.rows()); + for(int i = 0; i +IGL_INLINE void igl::faces_first( + MatV & V, + MatF & F, + VecI & IM) +{ + MatV RV; + // Copying F may not be needed, seems RF = F is safe (whereas RV = V is not) + MatF RF; + igl::faces_first(V,F,RV,RF,IM); + V = RV; + F = RF; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::faces_first, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix&, Eigen::Matrix&, Eigen::Matrix&); +#endif diff --git a/src/igl/faces_first.h b/src/igl/faces_first.h new file mode 100644 index 0000000000..92d1d5565d --- /dev/null +++ b/src/igl/faces_first.h @@ -0,0 +1,60 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FACES_FIRST_H +#define IGL_FACES_FIRST_H +#include "igl_inline.h" +namespace igl +{ + // FACES_FIRST Reorder vertices so that vertices in face list come before + // vertices that don't appear in the face list. This is especially useful if + // the face list contains only surface faces and you want surface vertices + // listed before internal vertices + // + // [RV,RT,RF,IM] = faces_first(V,T,F); + // + // Templates: + // MatV matrix for vertex positions, e.g. MatrixXd + // MatF matrix for face indices, e.g. MatrixXi + // VecI vector for index map, e.g. VectorXi + // Input: + // V # vertices by 3 vertex positions + // F # faces by 3 list of face indices + // Output: + // RV # vertices by 3 vertex positions, order such that if the jth vertex is + // some face in F, and the kth vertex is not then j comes before k + // RF # faces by 3 list of face indices, reindexed to use RV + // IM #V by 1 list of indices such that: RF = IM(F) and RT = IM(T) + // and RV(IM,:) = V + // + // + // Example: + // // Tet mesh in (V,T,F) + // faces_first(V,F,IM); + // T = T.unaryExpr(bind1st(mem_fun( static_cast(&VectorXi::operator())), + // &IM)).eval(); + template + IGL_INLINE void faces_first( + const MatV & V, + const MatF & F, + MatV & RV, + MatF & RF, + VecI & IM); + // Virtual "in place" wrapper + template + IGL_INLINE void faces_first( + MatV & V, + MatF & F, + VecI & IM); +} + +#ifndef IGL_STATIC_LIBRARY +# include "faces_first.cpp" +#endif + +#endif diff --git a/src/igl/facet_components.cpp b/src/igl/facet_components.cpp new file mode 100644 index 0000000000..9df7583e3b --- /dev/null +++ b/src/igl/facet_components.cpp @@ -0,0 +1,92 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "facet_components.h" +#include +#include +#include +template +IGL_INLINE void igl::facet_components( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & C) +{ + using namespace std; + typedef typename DerivedF::Index Index; + vector > > TT; + vector > > TTi; + triangle_triangle_adjacency(F,TT,TTi); + Eigen::VectorXi counts; + return facet_components(TT,C,counts); +} + +template < + typename TTIndex, + typename DerivedC, + typename Derivedcounts> +IGL_INLINE void igl::facet_components( + const std::vector > > & TT, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & counts) +{ + using namespace std; + typedef TTIndex Index; + const Index m = TT.size(); + C.resize(m,1); + vector seen(m,false); + Index id = 0; + vector vcounts; + for(Index g = 0;g Q; + Q.push(g); + while(!Q.empty()) + { + const Index f = Q.front(); + Q.pop(); + if(seen[f]) + { + continue; + } + seen[f] = true; + vcounts[id]++; + C(f,0) = id; + // Face f's neighbor lists opposite opposite each corner + for(const auto & c : TT[f]) + { + // Each neighbor + for(const auto & n : c) + { + if(!seen[n]) + { + Q.push(n); + } + } + } + } + id++; + } + assert((size_t) id == vcounts.size()); + const size_t ncc = vcounts.size(); + assert((size_t)C.maxCoeff()+1 == ncc); + counts.resize(ncc,1); + for(size_t i = 0;i, Eigen::Matrix >(std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::facet_components, Eigen::Matrix >(std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::facet_components, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/facet_components.h b/src/igl/facet_components.h new file mode 100644 index 0000000000..8fe28583cc --- /dev/null +++ b/src/igl/facet_components.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef FACET_COMPONENTS_H +#define FACET_COMPONENTS_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Compute connected components of facets based on edge-edge adjacency. + // + // Inputs: + // F #F by 3 list of triangle indices + // Outputs: + // C #F list of connected component ids + template + IGL_INLINE void facet_components( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & C); + // Inputs: + // TT #TT by 3 list of list of adjacency triangles (see + // triangle_triangle_adjacency.h) + // Outputs: + // C #F list of connected component ids + // counts #C list of number of facets in each components + template < + typename TTIndex, + typename DerivedC, + typename Derivedcounts> + IGL_INLINE void facet_components( + const std::vector > > & TT, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & counts); +} +#ifndef IGL_STATIC_LIBRARY +# include "facet_components.cpp" +#endif +#endif diff --git a/src/igl/false_barycentric_subdivision.cpp b/src/igl/false_barycentric_subdivision.cpp new file mode 100644 index 0000000000..8ea807a505 --- /dev/null +++ b/src/igl/false_barycentric_subdivision.cpp @@ -0,0 +1,59 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "false_barycentric_subdivision.h" + +#include "verbose.h" +#include +#include + +template +IGL_INLINE void igl::false_barycentric_subdivision( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & VD, + Eigen::PlainObjectBase & FD) +{ + using namespace Eigen; + // Compute face barycenter + Eigen::MatrixXd BC; + igl::barycenter(V,F,BC); + + // Add the barycenters to the vertices + VD.resize(V.rows()+F.rows(),3); + VD.block(0,0,V.rows(),3) = V; + VD.block(V.rows(),0,F.rows(),3) = BC; + + // Each face is split four ways + FD.resize(F.rows()*3,3); + + for (unsigned i=0; i, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::false_barycentric_subdivision, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/false_barycentric_subdivision.h b/src/igl/false_barycentric_subdivision.h new file mode 100644 index 0000000000..98426b5725 --- /dev/null +++ b/src/igl/false_barycentric_subdivision.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ADD_BARYCENTER_H +#define IGL_ADD_BARYCENTER_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Refine the mesh by adding the barycenter of each face + // Inputs: + // V #V by 3 coordinates of the vertices + // F #F by 3 list of mesh faces (must be triangles) + // Outputs: + // VD #V + #F by 3 coordinate of the vertices of the dual mesh + // The added vertices are added at the end of VD (should not be + // same references as (V,F) + // FD #F*3 by 3 faces of the dual mesh + // + template + IGL_INLINE void false_barycentric_subdivision( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & VD, + Eigen::PlainObjectBase & FD); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "false_barycentric_subdivision.cpp" +#endif + +#endif diff --git a/src/igl/fast_winding_number.cpp b/src/igl/fast_winding_number.cpp new file mode 100644 index 0000000000..a69785feb6 --- /dev/null +++ b/src/igl/fast_winding_number.cpp @@ -0,0 +1,324 @@ +#include "fast_winding_number.h" +#include "octree.h" +#include "knn.h" +#include "parallel_for.h" +#include "PI.h" +#include + +namespace igl { + template + IGL_INLINE void fast_winding_number(const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& A, + const std::vector > & point_indices, + const Eigen::MatrixBase& CH, + const int expansion_order, + Eigen::PlainObjectBase& CM, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& EC) + { + typedef typename DerivedP::Scalar real_p; + typedef typename DerivedN::Scalar real_n; + typedef typename DerivedA::Scalar real_a; + typedef typename DerivedCM::Scalar real_cm; + typedef typename DerivedR::Scalar real_r; + typedef typename DerivedEC::Scalar real_ec; + + typedef Eigen::Matrix RowVec3p; + + int m = CH.size(); + int num_terms; + + assert(expansion_order < 3 && expansion_order >= 0 && "m must be less than n"); + if(expansion_order == 0){ + num_terms = 3; + } else if(expansion_order ==1){ + num_terms = 3 + 9; + } else if(expansion_order == 2){ + num_terms = 3 + 9 + 27; + } + + R.resize(m); + CM.resize(m,3); + EC.resize(m,num_terms); + EC.setZero(m,num_terms); + std::function< void(const int) > helper; + helper = [&helper, + &P,&N,&A,&expansion_order,&point_indices,&CH,&EC,&R,&CM] + (const int index)-> void + { + Eigen::Matrix masscenter; + masscenter << 0,0,0; + Eigen::Matrix zeroth_expansion; + zeroth_expansion << 0,0,0; + real_p areatotal = 0.0; + for(int j = 0; j < point_indices.at(index).size(); j++){ + int curr_point_index = point_indices.at(index).at(j); + + areatotal += A(curr_point_index); + masscenter += A(curr_point_index)*P.row(curr_point_index); + zeroth_expansion += A(curr_point_index)*N.row(curr_point_index); + } + + masscenter = masscenter/areatotal; + CM.row(index) = masscenter; + EC.block(index,0,1,3) = zeroth_expansion; + + real_r max_norm = 0; + real_r curr_norm; + + for(int i = 0; i < point_indices.at(index).size(); i++){ + //Get max distance from center of mass: + int curr_point_index = point_indices.at(index).at(i); + Eigen::Matrix point = + P.row(curr_point_index)-masscenter; + curr_norm = point.norm(); + if(curr_norm > max_norm){ + max_norm = curr_norm; + } + + //Calculate higher order terms if necessary + Eigen::Matrix TempCoeffs; + if(EC.cols() >= (3+9)){ + TempCoeffs = A(curr_point_index)*point.transpose()* + N.row(curr_point_index); + EC.block(index,3,1,9) += + Eigen::Map >(TempCoeffs.data(), + TempCoeffs.size()); + } + + if(EC.cols() == (3+9+27)){ + for(int k = 0; k < 3; k++){ + TempCoeffs = 0.5 * point(k) * (A(curr_point_index)* + point.transpose()*N.row(curr_point_index)); + EC.block(index,12+9*k,1,9) += Eigen::Map< + Eigen::Matrix >(TempCoeffs.data(), + TempCoeffs.size()); + } + } + } + + R(index) = max_norm; + if(CH(index,0) != -1) + { + for(int i = 0; i < 8; i++){ + int child = CH(index,i); + helper(child); + } + } + }; + helper(0); + } + + template + IGL_INLINE void fast_winding_number(const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& A, + const std::vector > & point_indices, + const Eigen::MatrixBase& CH, + const Eigen::MatrixBase& CM, + const Eigen::MatrixBase& R, + const Eigen::MatrixBase& EC, + const Eigen::MatrixBase& Q, + const BetaType beta, + Eigen::PlainObjectBase& WN){ + + typedef typename DerivedP::Scalar real_p; + typedef typename DerivedN::Scalar real_n; + typedef typename DerivedA::Scalar real_a; + typedef typename DerivedCM::Scalar real_cm; + typedef typename DerivedR::Scalar real_r; + typedef typename DerivedEC::Scalar real_ec; + typedef typename DerivedQ::Scalar real_q; + typedef typename DerivedWN::Scalar real_wn; + + typedef Eigen::Matrix RowVec; + typedef Eigen::Matrix EC_3by3; + + auto direct_eval = [](const RowVec & loc, + const Eigen::Matrix & anorm){ + real_wn wn = (loc(0)*anorm(0)+loc(1)*anorm(1)+loc(2)*anorm(2)) + /(4.0*igl::PI*std::pow(loc.norm(),3)); + if(std::isnan(wn)){ + return 0.5; + }else{ + return wn; + } + }; + + auto expansion_eval = [&direct_eval](const RowVec & loc, + const Eigen::RowVectorXd & EC){ + real_wn wn = direct_eval(loc,EC.head<3>()); + double r = loc.norm(); + if(EC.size()>3){ + Eigen::Matrix SecondDerivative = + Eigen::Matrix::Identity()/(4.0*igl::PI*std::pow(r,3)); + SecondDerivative += -3.0*loc.transpose()*loc/(4.0*igl::PI*std::pow(r,5)); + Eigen::Matrix derivative_vector = + Eigen::Map >(SecondDerivative.data(), + SecondDerivative.size()); + wn += derivative_vector.cwiseProduct(EC.segment<9>(3)).sum(); + } + if(EC.size()>3+9){ + Eigen::Matrix ThirdDerivative; + for(int i = 0; i < 3; i++){ + ThirdDerivative = + 15.0*loc(i)*loc.transpose()*loc/(4.0*igl::PI*std::pow(r,7)); + Eigen::Matrix Diagonal; + Diagonal << loc(i), 0, 0, + 0, loc(i), 0, + 0, 0, loc(i); + Eigen::Matrix RowCol; + RowCol.setZero(3,3); + RowCol.row(i) = loc; + Eigen::Matrix RowColT = RowCol.transpose(); + RowCol = RowCol + RowColT; + ThirdDerivative += + -3.0/(4.0*igl::PI*std::pow(r,5))*(RowCol+Diagonal); + Eigen::Matrix derivative_vector = + Eigen::Map >(ThirdDerivative.data(), + ThirdDerivative.size()); + wn += derivative_vector.cwiseProduct( + EC.segment<9>(12 + i*9)).sum(); + } + } + return wn; + }; + + int m = Q.rows(); + WN.resize(m,1); + + std::function< real_wn(const RowVec, const std::vector) > helper; + helper = [&helper, + &P,&N,&A, + &point_indices,&CH, + &CM,&R,&EC,&beta, + &direct_eval,&expansion_eval] + (const RowVec query, const std::vector near_indices)-> real_wn + { + std::vector new_near_indices; + real_wn wn = 0; + for(int i = 0; i < near_indices.size(); i++){ + int index = near_indices.at(i); + //Leaf Case, Brute force + if(CH(index,0) == -1){ + for(int j = 0; j < point_indices.at(index).size(); j++){ + int curr_row = point_indices.at(index).at(j); + wn += direct_eval(P.row(curr_row)-query, + N.row(curr_row)*A(curr_row)); + } + } + //Non-Leaf Case + else { + for(int child = 0; child < 8; child++){ + int child_index = CH(index,child); + if(point_indices.at(child_index).size() > 0){ + if((CM.row(child_index)-query).norm() > beta*R(child_index)){ + if(CH(child_index,0) == -1){ + for(int j=0;j 0){ + wn += helper(query,new_near_indices); + } + return wn; + }; + + + if(beta > 0){ + std::vector near_indices_start = {0}; + igl::parallel_for(m,[&](int iter){ + WN(iter) = helper(Q.row(iter),near_indices_start); + },1000); + } else { + igl::parallel_for(m,[&](int iter){ + double wn = 0; + for(int j = 0; j + IGL_INLINE void fast_winding_number(const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& A, + const Eigen::MatrixBase& Q, + const int expansion_order, + const BetaType beta, + Eigen::PlainObjectBase& WN + ){ + typedef typename DerivedWN::Scalar real; + + std::vector > point_indices; + Eigen::Matrix CH; + Eigen::Matrix CN; + Eigen::Matrix W; + + octree(P,point_indices,CH,CN,W); + + Eigen::Matrix EC; + Eigen::Matrix CM; + Eigen::Matrix R; + + fast_winding_number(P,N,A,point_indices,CH,expansion_order,CM,R,EC); + fast_winding_number(P,N,A,point_indices,CH,CM,R,EC,Q,beta,WN); + } + + template + IGL_INLINE void fast_winding_number(const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& A, + const Eigen::MatrixBase& Q, + Eigen::PlainObjectBase& WN + ){ + fast_winding_number(P,N,A,Q,2,2.0,WN); + } +} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/igl/fast_winding_number.h b/src/igl/fast_winding_number.h new file mode 100644 index 0000000000..6d4bc1ed7d --- /dev/null +++ b/src/igl/fast_winding_number.h @@ -0,0 +1,122 @@ +#ifndef IGL_FAST_WINDING_NUMBER +#define IGL_FAST_WINDING_NUMBER +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Generate the precomputation for the fast winding number for point data + // [Barill et. al 2018]. + // + // Given a set of 3D points P, with normals N, areas A, along with octree + // data, and an expansion order, we define a taylor series expansion at each + // octree cell. + // + // The octree data is designed to come from igl::octree, and the areas + // (if not obtained at scan time), may be calculated using + // igl::copyleft::cgal::point_areas. + // + // Inputs: + // P #P by 3 list of point locations + // N #P by 3 list of point normals + // A #P by 1 list of point areas + // point_indices a vector of vectors, where the ith entry is a vector of + // the indices into P that are the ith octree cell's points + // CH #OctreeCells by 8, where the ith row is the indices of + // the ith octree cell's children + // expansion_order the order of the taylor expansion. We support 0,1,2. + // Outputs: + // CM #OctreeCells by 3 list of each cell's center of mass + // R #OctreeCells by 1 list of each cell's maximum distance of any point + // to the center of mass + // EC #OctreeCells by #TaylorCoefficients list of expansion coefficients. + // (Note that #TaylorCoefficients = ∑_{i=1}^{expansion_order} 3^i) + // + template + IGL_INLINE void fast_winding_number(const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& A, + const std::vector > & point_indices, + const Eigen::MatrixBase& CH, + const int exansion_order, + Eigen::PlainObjectBase& CM, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& EC); + + // Evaluate the fast winding number for point data, having already done the + // the precomputation + // + // Inputs: + // P #P by 3 list of point locations + // N #P by 3 list of point normals + // A #P by 1 list of point areas + // point_indices a vector of vectors, where the ith entry is a vector of + // the indices into P that are the ith octree cell's points + // CH #OctreeCells by 8, where the ith row is the indices of + // the ith octree cell's children + // CM #OctreeCells by 3 list of each cell's center of mass + // R #OctreeCells by 1 list of each cell's maximum distance of any point + // to the center of mass + // EC #OctreeCells by #TaylorCoefficients list of expansion coefficients. + // (Note that #TaylorCoefficients = ∑_{i=1}^{expansion_order} 3^i) + // Q #Q by 3 list of query points for the winding number + // beta This is a Barnes-Hut style accuracy term that separates near feild + // from far field. The higher the beta, the more accurate and slower + // the evaluation. We reccommend using a beta value of 2. Note that + // for a beta value ≤ 0, we use the direct evaluation, rather than + // the fast approximation + // Outputs: + // WN #Q by 1 list of windinng number values at each query point + // + template + IGL_INLINE void fast_winding_number(const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& A, + const std::vector > & point_indices, + const Eigen::MatrixBase& CH, + const Eigen::MatrixBase& CM, + const Eigen::MatrixBase& R, + const Eigen::MatrixBase& EC, + const Eigen::MatrixBase& Q, + const BetaType beta, + Eigen::PlainObjectBase& WN); + + // Evaluate the fast winding number for point data, with default expansion + // order and beta (both are set to 2). + // + // This function performes the precomputation and evaluation all in one. + // If you need to acess the precomuptation for repeated evaluations, use the + // two functions designed for exposed precomputation (described above). + // + // Inputs: + // P #P by 3 list of point locations + // N #P by 3 list of point normals + // A #P by 1 list of point areas + // Q #Q by 3 list of query points for the winding number + // beta This is a Barnes-Hut style accuracy term that separates near feild + // from far field. The higher the beta, the more accurate and slower + // the evaluation. We reccommend using a beta value of 2. + // expansion_order the order of the taylor expansion. We support 0,1,2. + // Outputs: + // WN #Q by 1 list of windinng number values at each query point + // + template + IGL_INLINE void fast_winding_number(const Eigen::MatrixBase& P, + const Eigen::MatrixBase& N, + const Eigen::MatrixBase& A, + const Eigen::MatrixBase& Q, + Eigen::PlainObjectBase& WN + ); +} +#ifndef IGL_STATIC_LIBRARY +# include "fast_winding_number.cpp" +#endif + +#endif + diff --git a/src/igl/file_contents_as_string.cpp b/src/igl/file_contents_as_string.cpp new file mode 100644 index 0000000000..fd945dff2c --- /dev/null +++ b/src/igl/file_contents_as_string.cpp @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "file_contents_as_string.h" + +#include +#include +#include + +IGL_INLINE bool igl::file_contents_as_string( + const std::string file_name, + std::string & content) +{ + std::ifstream ifs(file_name.c_str()); + // Check that opening the stream worked successfully + if(!ifs.good()) + { + fprintf( + stderr, + "IOError: file_contents_as_string() cannot open %s\n", + file_name.c_str()); + return false; + } + // Stream file contents into string + content = std::string( + (std::istreambuf_iterator(ifs)), + (std::istreambuf_iterator())); + return true; +} + +IGL_INLINE std::string igl::file_contents_as_string( + const std::string file_name) +{ + std::string content; +#ifndef NDEBUG + bool ret = +#endif + file_contents_as_string(file_name,content); + assert(ret && "file_contents_as_string failed to read string from file"); + return content; +} diff --git a/src/igl/file_contents_as_string.h b/src/igl/file_contents_as_string.h new file mode 100644 index 0000000000..22ce1fc534 --- /dev/null +++ b/src/igl/file_contents_as_string.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FILE_CONTENTS_AS_STRING_H +#define IGL_FILE_CONTENTS_AS_STRING_H +#include "igl_inline.h" + +#include +namespace igl +{ + // Read a files contents as plain text into a given string + // Inputs: + // file_name path to file to be read + // Outputs: + // content output string containing contents of the given file + // Returns true on succes, false on error + IGL_INLINE bool file_contents_as_string( + const std::string file_name, + std::string & content); + IGL_INLINE std::string file_contents_as_string( + const std::string file_name); +} + +#ifndef IGL_STATIC_LIBRARY +# include "file_contents_as_string.cpp" +#endif + +#endif diff --git a/src/igl/file_dialog_open.cpp b/src/igl/file_dialog_open.cpp new file mode 100644 index 0000000000..66c2aca0e3 --- /dev/null +++ b/src/igl/file_dialog_open.cpp @@ -0,0 +1,87 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "file_dialog_open.h" +#include +#include + +#ifdef _WIN32 + #include + #undef max + #undef min + + #include +#endif + +IGL_INLINE std::string igl::file_dialog_open() +{ + const int FILE_DIALOG_MAX_BUFFER = 1024; + char buffer[FILE_DIALOG_MAX_BUFFER]; + +#ifdef __APPLE__ + // For apple use applescript hack + FILE * output = popen( + "osascript -e \"" + " tell application \\\"System Events\\\"\n" + " activate\n" + " set existing_file to choose file\n" + " end tell\n" + " set existing_file_path to (POSIX path of (existing_file))\n" + "\" 2>/dev/null | tr -d '\n' ","r"); + while ( fgets(buffer, FILE_DIALOG_MAX_BUFFER, output) != NULL ) + { + } +#elif defined _WIN32 + + // Use native windows file dialog box + // (code contributed by Tino Weinkauf) + + OPENFILENAME ofn; // common dialog box structure + char szFile[260]; // buffer for file name + + // Initialize OPENFILENAME + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = NULL; + ofn.lpstrFile = new char[100]; + // Set lpstrFile[0] to '\0' so that GetOpenFileName does not + // use the contents of szFile to initialize itself. + ofn.lpstrFile[0] = '\0'; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = "*.*\0";//off\0*.off\0obj\0*.obj\0mp\0*.mp\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + + // Display the Open dialog box. + int pos = 0; + if (GetOpenFileName(&ofn)==TRUE) + { + while(ofn.lpstrFile[pos] != '\0') + { + buffer[pos] = (char)ofn.lpstrFile[pos]; + pos++; + } + } + buffer[pos] = 0; +#else + + // For linux use zenity + FILE * output = popen("/usr/bin/zenity --file-selection","r"); + while ( fgets(buffer, FILE_DIALOG_MAX_BUFFER, output) != NULL ) + { + } + + if (strlen(buffer) > 0) + { + buffer[strlen(buffer)-1] = 0; + } +#endif + return std::string(buffer); +} diff --git a/src/igl/file_dialog_open.h b/src/igl/file_dialog_open.h new file mode 100644 index 0000000000..1b17ea4c6a --- /dev/null +++ b/src/igl/file_dialog_open.h @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FILE_DIALOG_OPEN_H +#define IGL_FILE_DIALOG_OPEN_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Returns a string with a path to an existing file + // The string is returned empty if no file is selected + // (on Linux machines, it assumes that Zenity is installed) + // + // Usage: + // std::string str = get_open_file_path(); + IGL_INLINE std::string file_dialog_open(); +} + +#ifndef IGL_STATIC_LIBRARY +# include "file_dialog_open.cpp" +#endif + +#endif + diff --git a/src/igl/file_dialog_save.cpp b/src/igl/file_dialog_save.cpp new file mode 100644 index 0000000000..90687fa5c2 --- /dev/null +++ b/src/igl/file_dialog_save.cpp @@ -0,0 +1,86 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "file_dialog_save.h" +#include +#include + +#ifdef _WIN32 + #include + #include +#endif + +IGL_INLINE std::string igl::file_dialog_save() +{ + const int FILE_DIALOG_MAX_BUFFER = 1024; + char buffer[FILE_DIALOG_MAX_BUFFER]; +#ifdef __APPLE__ + // For apple use applescript hack + // There is currently a bug in Applescript that strips extensions off + // of chosen existing files in the "choose file name" dialog + // I'm assuming that will be fixed soon + FILE * output = popen( + "osascript -e \"" + " tell application \\\"System Events\\\"\n" + " activate\n" + " set existing_file to choose file name\n" + " end tell\n" + " set existing_file_path to (POSIX path of (existing_file))\n" + "\" 2>/dev/null | tr -d '\n' ","r"); + while ( fgets(buffer, FILE_DIALOG_MAX_BUFFER, output) != NULL ) + { + } +#elif defined _WIN32 + + // Use native windows file dialog box + // (code contributed by Tino Weinkauf) + + OPENFILENAME ofn; // common dialog box structure + char szFile[260]; // buffer for file name + + // Initialize OPENFILENAME + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = NULL;//hwnd; + ofn.lpstrFile = new char[100]; + // Set lpstrFile[0] to '\0' so that GetOpenFileName does not + // use the contents of szFile to initialize itself. + ofn.lpstrFile[0] = '\0'; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = ""; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + + // Display the Open dialog box. + int pos = 0; + if (GetSaveFileName(&ofn)==TRUE) + { + while(ofn.lpstrFile[pos] != '\0') + { + buffer[pos] = (char)ofn.lpstrFile[pos]; + pos++; + } + buffer[pos] = 0; + } + +#else + // For every other machine type use zenity + FILE * output = popen("/usr/bin/zenity --file-selection --save","r"); + while ( fgets(buffer, FILE_DIALOG_MAX_BUFFER, output) != NULL ) + { + } + + if (strlen(buffer) > 0) + { + buffer[strlen(buffer)-1] = 0; + } +#endif + return std::string(buffer); +} diff --git a/src/igl/file_dialog_save.h b/src/igl/file_dialog_save.h new file mode 100644 index 0000000000..f040757589 --- /dev/null +++ b/src/igl/file_dialog_save.h @@ -0,0 +1,31 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FILE_DIALOG_SAVE_H +#define IGL_FILE_DIALOG_SAVE_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Returns a string with a path to a new/existing file + // The string is returned empty if no file is selected + // (on Linux machines, it assumes that Zenity is installed) + // + // Usage: + // char buffer[FILE_DIALOG_MAX_BUFFER]; + // get_save_file_path(buffer); + IGL_INLINE std::string file_dialog_save(); +} + +#ifndef IGL_STATIC_LIBRARY +# include "file_dialog_save.cpp" +#endif + +#endif + diff --git a/src/igl/file_exists.cpp b/src/igl/file_exists.cpp new file mode 100644 index 0000000000..f1a30630bd --- /dev/null +++ b/src/igl/file_exists.cpp @@ -0,0 +1,16 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "file_exists.h" + +#include + +IGL_INLINE bool igl::file_exists(const std::string filename) +{ + struct stat status; + return (stat(filename.c_str(),&status)==0); +} diff --git a/src/igl/file_exists.h b/src/igl/file_exists.h new file mode 100644 index 0000000000..e011d977eb --- /dev/null +++ b/src/igl/file_exists.h @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FILE_EXISTS_H +#define IGL_FILE_EXISTS_H +#include "igl_inline.h" +#include +namespace igl +{ + // Check if a file or directory exists like PHP's file_exists function: + // http://php.net/manual/en/function.file-exists.php + // Input: + // filename path to file + // Returns true if file exists and is readable and false if file doesn't + // exist or *is not readable* + IGL_INLINE bool file_exists(const std::string filename); +} + +#ifndef IGL_STATIC_LIBRARY +# include "file_exists.cpp" +#endif + +#endif diff --git a/src/igl/find.cpp b/src/igl/find.cpp new file mode 100644 index 0000000000..65e7580ca1 --- /dev/null +++ b/src/igl/find.cpp @@ -0,0 +1,138 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "find.h" + +#include "verbose.h" +#include + +template < + typename T, + typename DerivedI, + typename DerivedJ, + typename DerivedV> +IGL_INLINE void igl::find( + const Eigen::SparseMatrix& X, + Eigen::DenseBase & I, + Eigen::DenseBase & J, + Eigen::DenseBase & V) +{ + // Resize outputs to fit nonzeros + I.derived().resize(X.nonZeros(),1); + J.derived().resize(X.nonZeros(),1); + V.derived().resize(X.nonZeros(),1); + + int i = 0; + // Iterate over outside + for(int k=0; k::InnerIterator it (X,k); it; ++it) + { + V(i) = it.value(); + I(i) = it.row(); + J(i) = it.col(); + i++; + } + } +} + +template < + typename DerivedX, + typename DerivedI, + typename DerivedJ, + typename DerivedV> +IGL_INLINE void igl::find( + const Eigen::DenseBase& X, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & V) +{ + const int nnz = X.count(); + I.resize(nnz,1); + J.resize(nnz,1); + V.resize(nnz,1); + { + int k = 0; + for(int j = 0;j +IGL_INLINE void igl::find( + const Eigen::DenseBase& X, + Eigen::PlainObjectBase & I) +{ + const int nnz = X.count(); + I.resize(nnz,1); + { + int k = 0; + for(int j = 0;j +IGL_INLINE void igl::find( + const Eigen::SparseVector& X, + Eigen::Matrix & I, + Eigen::Matrix & V) +{ + // Resize outputs to fit nonzeros + I.resize(X.nonZeros()); + V.resize(X.nonZeros()); + + int i = 0; + // loop over non-zeros + for(typename Eigen::SparseVector::InnerIterator it(X); it; ++it) + { + I(i) = it.index(); + V(i) = it.value(); + i++; + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation + +template void igl::find, Eigen::Matrix, Eigen::Array >(Eigen::SparseMatrix const&, Eigen::DenseBase >&, Eigen::DenseBase >&, Eigen::DenseBase >&); +template void igl::find, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::find, Eigen::Matrix, Eigen::Matrix >(Eigen::SparseMatrix const&, Eigen::DenseBase >&, Eigen::DenseBase >&, Eigen::DenseBase >&); +template void igl::find, Eigen::Matrix, Eigen::Matrix >(Eigen::SparseMatrix const&, Eigen::DenseBase >&, Eigen::DenseBase >&, Eigen::DenseBase >&); +template void igl::find, Eigen::Matrix, Eigen::Matrix >(Eigen::SparseMatrix const&, Eigen::DenseBase >&, Eigen::DenseBase >&, Eigen::DenseBase >&); +template void igl::find, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::find, Eigen::Matrix, Eigen::Matrix >(Eigen::SparseMatrix const&, Eigen::DenseBase >&, Eigen::DenseBase >&, Eigen::DenseBase >&); +#if EIGEN_VERSION_AT_LEAST(3,3,0) +#else +template void igl::find, Eigen::PartialReduxExpr, Eigen::internal::member_count, 1> const, Eigen::CwiseNullaryOp, Eigen::Array > const>, Eigen::Matrix >(Eigen::DenseBase, Eigen::PartialReduxExpr, Eigen::internal::member_count, 1> const, Eigen::CwiseNullaryOp, Eigen::Array > const> > const&, Eigen::PlainObjectBase >&); +template void igl::find, Eigen::Array const, Eigen::CwiseNullaryOp, Eigen::Array > const>, Eigen::Matrix >(Eigen::DenseBase, Eigen::Array const, Eigen::CwiseNullaryOp, Eigen::Array > const> > const&, Eigen::PlainObjectBase >&); +#endif + +#endif diff --git a/src/igl/find.h b/src/igl/find.h new file mode 100644 index 0000000000..85eb65770c --- /dev/null +++ b/src/igl/find.h @@ -0,0 +1,77 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FIND_H +#define IGL_FIND_H +#include "igl_inline.h" +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include +namespace igl +{ + // Find the non-zero entries and there respective indices in a sparse matrix. + // Like matlab's [I,J,V] = find(X) + // + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Input: + // X m by n matrix whose entries are to be found + // Outputs: + // I nnz vector of row indices of non zeros entries in X + // J nnz vector of column indices of non zeros entries in X + // V nnz vector of type T non-zeros entries in X + // + template < + typename T, + typename DerivedI, + typename DerivedJ, + typename DerivedV> + IGL_INLINE void find( + const Eigen::SparseMatrix& X, + Eigen::DenseBase & I, + Eigen::DenseBase & J, + Eigen::DenseBase & V); + template < + typename DerivedX, + typename DerivedI, + typename DerivedJ, + typename DerivedV> + IGL_INLINE void find( + const Eigen::DenseBase& X, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & V); + template < + typename DerivedX, + typename DerivedI> + IGL_INLINE void find( + const Eigen::DenseBase& X, + Eigen::PlainObjectBase & I); + // Find the non-zero entries and there respective indices in a sparse vector. + // Similar to matlab's [I,J,V] = find(X), but instead of [I,J] being + // subscripts into X, since X is a vector we just return I, a list of indices + // into X + // + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Input: + // X vector whose entries are to be found + // Outputs: + // I nnz vector of indices of non zeros entries in X + // V nnz vector of type T non-zeros entries in X + template + IGL_INLINE void find( + const Eigen::SparseVector& X, + Eigen::Matrix & I, + Eigen::Matrix & V); +} + +#ifndef IGL_STATIC_LIBRARY +# include "find.cpp" +#endif + +#endif diff --git a/src/igl/find_cross_field_singularities.cpp b/src/igl/find_cross_field_singularities.cpp new file mode 100644 index 0000000000..31a5bb7142 --- /dev/null +++ b/src/igl/find_cross_field_singularities.cpp @@ -0,0 +1,86 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "find_cross_field_singularities.h" + +#include +#include +#include +#include +#include +#include + + +template +IGL_INLINE void igl::find_cross_field_singularities(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &Handle_MMatch, + Eigen::PlainObjectBase &isSingularity, + Eigen::PlainObjectBase &singularityIndex) +{ + std::vector V_border = igl::is_border_vertex(V,F); + + std::vector > VF; + std::vector > VFi; + igl::vertex_triangle_adjacency(V,F,VF,VFi); + + + isSingularity.setZero(V.rows(),1); + singularityIndex.setZero(V.rows(),1); + for (unsigned int vid=0;vid +IGL_INLINE void igl::find_cross_field_singularities(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const Eigen::PlainObjectBase &PD2, + Eigen::PlainObjectBase &isSingularity, + Eigen::PlainObjectBase &singularityIndex, + bool isCombed) +{ + Eigen::Matrix Handle_MMatch; + + igl::cross_field_missmatch(V, F, PD1, PD2, isCombed, Handle_MMatch); + igl::find_cross_field_singularities(V, F, Handle_MMatch, isSingularity, singularityIndex); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::find_cross_field_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::find_cross_field_singularities, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, +Eigen::PlainObjectBase >&, bool); +template void igl::find_cross_field_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::find_cross_field_singularities, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, bool); +template void igl::find_cross_field_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::find_cross_field_singularities, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/find_cross_field_singularities.h b/src/igl/find_cross_field_singularities.h new file mode 100644 index 0000000000..caded20195 --- /dev/null +++ b/src/igl/find_cross_field_singularities.h @@ -0,0 +1,58 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_FIND_CROSS_FIELD_SINGULARITIES_H +#define IGL_FIND_CROSS_FIELD_SINGULARITIES_H +#include "igl_inline.h" +#include +namespace igl +{ + // Computes singularities of a cross field, assumed combed + + + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (quad) indices + // Handle_MMatch #F by 3 eigen Matrix containing the integer missmatch of the cross field + // across all face edges + // Output: + // isSingularity #V by 1 boolean eigen Vector indicating the presence of a singularity on a vertex + // singularityIndex #V by 1 integer eigen Vector containing the singularity indices + // + template + IGL_INLINE void find_cross_field_singularities(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &Handle_MMatch, + Eigen::PlainObjectBase &isSingularity, + Eigen::PlainObjectBase &singularityIndex); + + // Wrapper that calculates the missmatch if it is not provided. + // Note that the field in PD1 and PD2 MUST BE combed (see igl::comb_cross_field). + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (quad) indices + // PD1 #F by 3 eigen Matrix of the first per face cross field vector + // PD2 #F by 3 eigen Matrix of the second per face cross field vector + // Output: + // isSingularity #V by 1 boolean eigen Vector indicating the presence of a singularity on a vertex + // singularityIndex #V by 1 integer eigen Vector containing the singularity indices + // + template + IGL_INLINE void find_cross_field_singularities(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const Eigen::PlainObjectBase &PD2, + Eigen::PlainObjectBase &isSingularity, + Eigen::PlainObjectBase &singularityIndex, + bool isCombed = false); +} +#ifndef IGL_STATIC_LIBRARY +#include "find_cross_field_singularities.cpp" +#endif + +#endif diff --git a/src/igl/find_zero.cpp b/src/igl/find_zero.cpp new file mode 100644 index 0000000000..d0a7b35e6d --- /dev/null +++ b/src/igl/find_zero.cpp @@ -0,0 +1,48 @@ +#include "find_zero.h" +#include "for_each.h" +#include "any.h" + +template +IGL_INLINE void igl::find_zero( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase & I) +{ + assert((dim == 1 || dim == 2) && "dim must be 2 or 1"); + // Get size of input + int m = A.rows(); + int n = A.cols(); + // I starts by containing guess where 0 might be + I = DerivedI::Zero(dim==1?n:m); + Eigen::Array found = + Eigen::Array::Zero(dim==1?n:m); + const auto func = [&I,&found,&dim](int i, int j, const int v) + { + if(dim == 2) + { + std::swap(i,j); + } + // Coded as if dim == 1, assuming swap for dim == 2 + // Have we already found a zero? + if(!found(j)) + { + if(I(j) != i || v == 0) + { + // either there was an implicit zero between the last element and this + // one, or this one is zero + found(j) = true; + }else + { + // If not found, then guess that next element will be zero + I(j) = I(j)+1; + } + } + }; + for_each(A,func); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::find_zero >(Eigen::SparseMatrix const&, int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/find_zero.h b/src/igl/find_zero.h new file mode 100644 index 0000000000..0739be1cf2 --- /dev/null +++ b/src/igl/find_zero.h @@ -0,0 +1,28 @@ +#ifndef IGL_FIND_ZERO_H +#define IGL_FIND_ZERO_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Find the first zero (whether implicit or explicitly stored) in the + // rows/columns of a matrix. + // Inputs: + // A m by n sparse matrix + // dim dimension along which to check for any (1 or 2) + // Output: + // I n-long vector (if dim == 1) {m means no zeros found} + // or + // I m-long vector (if dim == 2) {n means no zeros found} + // + template + IGL_INLINE void find_zero( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase & I); +} +#ifndef IGL_STATIC_LIBRARY +# include "find_zero.cpp" +#endif +#endif + diff --git a/src/igl/fit_plane.cpp b/src/igl/fit_plane.cpp new file mode 100644 index 0000000000..83f11738f2 --- /dev/null +++ b/src/igl/fit_plane.cpp @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "fit_plane.h" +#include + +IGL_INLINE void igl::fit_plane( + const Eigen::MatrixXd & V, + Eigen::RowVector3d & N, + Eigen::RowVector3d & C) +{ + assert(V.rows()>0); + + Eigen::Vector3d sum = V.colwise().sum(); + + Eigen::Vector3d center = sum.array()/(double(V.rows())); + + C = center; + + double sumXX=0.0f,sumXY=0.0f,sumXZ=0.0f; + double sumYY=0.0f,sumYZ=0.0f; + double sumZZ=0.0f; + + for(int i=0;i es(m); + + N = es.eigenvectors().col(0); +} + +#ifdef IGL_STATIC_LIBRARY +#endif + + + diff --git a/src/igl/fit_plane.h b/src/igl/fit_plane.h new file mode 100644 index 0000000000..f69ce811b4 --- /dev/null +++ b/src/igl/fit_plane.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FIT_PLANE_H +#define IGL_FIT_PLANE_H + +#include "igl_inline.h" +#include + +namespace igl +{ + // This function fits a plane to a point cloud. + // + // Input: + // V #Vx3 matrix. The 3D point cloud, one row for each vertex. + // Output: + // N 1x3 Vector. The normal of the fitted plane. + // C 1x3 Vector. A point that lies in the fitted plane. + // From http://missingbytes.blogspot.com/2012/06/fitting-plane-to-point-cloud.html + + IGL_INLINE void fit_plane( + const Eigen::MatrixXd & V, + Eigen::RowVector3d & N, + Eigen::RowVector3d & C); +} + +#ifndef IGL_STATIC_LIBRARY +# include "fit_plane.cpp" +#endif + +#endif diff --git a/src/igl/fit_rotations.cpp b/src/igl/fit_rotations.cpp new file mode 100644 index 0000000000..927e8992ed --- /dev/null +++ b/src/igl/fit_rotations.cpp @@ -0,0 +1,225 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "fit_rotations.h" +#include "polar_svd3x3.h" +#include "repmat.h" +#include "verbose.h" +#include "polar_dec.h" +#include "polar_svd.h" +#include "C_STR.h" +#include + +template +IGL_INLINE void igl::fit_rotations( + const Eigen::PlainObjectBase & S, + const bool single_precision, + Eigen::PlainObjectBase & R) +{ + using namespace std; + const int dim = S.cols(); + const int nr = S.rows()/dim; + assert(nr * dim == S.rows()); + assert(dim == 3); + + // resize output + R.resize(dim,dim*nr); // hopefully no op (should be already allocated) + + //std::cout<<"S=["< si;// = Eigen::Matrix3d::Identity(); + // loop over number of rotations we're computing + for(int r = 0;r Mat3; + typedef Eigen::Matrix Vec3; + Mat3 ri; + if(single_precision) + { + polar_svd3x3(si, ri); + }else + { + Mat3 ti,ui,vi; + Vec3 _; + igl::polar_svd(si,ri,ti,ui,_,vi); + } + assert(ri.determinant() >= 0); + R.block(0,r*dim,dim,dim) = ri.block(0,0,dim,dim).transpose(); + //cout< +IGL_INLINE void igl::fit_rotations_planar( + const Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & R) +{ + using namespace std; + const int dim = S.cols(); + const int nr = S.rows()/dim; + //assert(dim == 2 && "_planar input should be 2D"); + assert(nr * dim == S.rows()); + + // resize output + R.resize(dim,dim*nr); // hopefully no op (should be already allocated) + + Eigen::Matrix si; + // loop over number of rotations we're computing + for(int r = 0;r Mat2; + typedef Eigen::Matrix Vec2; + Mat2 ri,ti,ui,vi; + Vec2 _; + igl::polar_svd(si,ri,ti,ui,_,vi); +#ifndef FIT_ROTATIONS_ALLOW_FLIPS + // Check for reflection + if(ri.determinant() < 0) + { + vi.col(1) *= -1.; + ri = ui * vi.transpose(); + } + assert(ri.determinant() >= 0); +#endif + + // Not sure why polar_dec computes transpose... + R.block(0,r*dim,dim,dim).setIdentity(); + R.block(0,r*dim,2,2) = ri.transpose(); + } +} + + +#ifdef __SSE__ +IGL_INLINE void igl::fit_rotations_SSE( + const Eigen::MatrixXf & S, + Eigen::MatrixXf & R) +{ + const int cStep = 4; + + assert(S.cols() == 3); + const int dim = 3; //S.cols(); + const int nr = S.rows()/dim; + assert(nr * dim == S.rows()); + + // resize output + R.resize(dim,dim*nr); // hopefully no op (should be already allocated) + + Eigen::Matrix siBig; + // using SSE decompose cStep matrices at a time: + int r = 0; + for( ; r= nr) numMats = nr - r; + // build siBig: + for (int k=0; k ri; + polar_svd3x3_sse(siBig, ri); + + for (int k=0; k= 0); + + // Not sure why polar_dec computes transpose... + for (int k=0; k(); + Eigen::MatrixXf Rf; + fit_rotations_SSE(Sf,Rf); + R = Rf.cast(); +} +#endif + +#ifdef __AVX__ +IGL_INLINE void igl::fit_rotations_AVX( + const Eigen::MatrixXf & S, + Eigen::MatrixXf & R) +{ + const int cStep = 8; + + assert(S.cols() == 3); + const int dim = 3; //S.cols(); + const int nr = S.rows()/dim; + assert(nr * dim == S.rows()); + + // resize output + R.resize(dim,dim*nr); // hopefully no op (should be already allocated) + + Eigen::Matrix siBig; + // using SSE decompose cStep matrices at a time: + int r = 0; + for( ; r= nr) numMats = nr - r; + // build siBig: + for (int k=0; k ri; + polar_svd3x3_avx(siBig, ri); + + for (int k=0; k= 0); + + // Not sure why polar_dec computes transpose... + for (int k=0; k, Eigen::Matrix >(Eigen::PlainObjectBase > const&, bool, Eigen::PlainObjectBase >&); +template void igl::fit_rotations_planar, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::fit_rotations_planar, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::fit_rotations,Eigen::Matrix >(Eigen::PlainObjectBase > const &,bool,Eigen::PlainObjectBase > &); +#endif diff --git a/src/igl/fit_rotations.h b/src/igl/fit_rotations.h new file mode 100644 index 0000000000..1a18fd322a --- /dev/null +++ b/src/igl/fit_rotations.h @@ -0,0 +1,60 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FIT_ROTATIONS_H +#define IGL_FIT_ROTATIONS_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Known issues: This seems to be implemented in Eigen/Geometry: + // Eigen::umeyama + // + // FIT_ROTATIONS Given an input mesh and new positions find rotations for + // every covariance matrix in a stack of covariance matrices + // + // Inputs: + // S nr*dim by dim stack of covariance matrices + // single_precision whether to use single precision (faster) + // Outputs: + // R dim by dim * nr list of rotations + // + template + IGL_INLINE void fit_rotations( + const Eigen::PlainObjectBase & S, + const bool single_precision, + Eigen::PlainObjectBase & R); + + // FIT_ROTATIONS Given an input mesh and new positions find 2D rotations for + // every vertex that best maps its one ring to the new one ring + // + // Inputs: + // S nr*dim by dim stack of covariance matrices, third column and every + // third row will be ignored + // Outputs: + // R dim by dim * nr list of rotations, third row and third column of each + // rotation will just be identity + // + template + IGL_INLINE void fit_rotations_planar( + const Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & R); +#ifdef __SSE__ + IGL_INLINE void fit_rotations_SSE( const Eigen::MatrixXf & S, Eigen::MatrixXf & R); + IGL_INLINE void fit_rotations_SSE( const Eigen::MatrixXd & S, Eigen::MatrixXd & R); +#endif +#ifdef __AVX__ + IGL_INLINE void fit_rotations_AVX( const Eigen::MatrixXf & S, Eigen::MatrixXf & R); +#endif +} + +#ifndef IGL_STATIC_LIBRARY +# include "fit_rotations.cpp" +#endif + +#endif diff --git a/src/igl/flip_avoiding_line_search.cpp b/src/igl/flip_avoiding_line_search.cpp new file mode 100644 index 0000000000..8554303c27 --- /dev/null +++ b/src/igl/flip_avoiding_line_search.cpp @@ -0,0 +1,320 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Michael Rabinovich +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "flip_avoiding_line_search.h" +#include "line_search.h" +#include "PI.h" + +#include +#include + +namespace igl +{ + namespace flip_avoiding + { + //--------------------------------------------------------------------------- + // x - array of size 3 + // In case 3 real roots: => x[0], x[1], x[2], return 3 + // 2 real roots: x[0], x[1], return 2 + // 1 real root : x[0], x[1] ± i*x[2], return 1 + // http://math.ivanovo.ac.ru/dalgebra/Khashin/poly/index.html + IGL_INLINE int SolveP3(std::vector& x,double a,double b,double c) + { // solve cubic equation x^3 + a*x^2 + b*x + c + using namespace std; + double a2 = a*a; + double q = (a2 - 3*b)/9; + double r = (a*(2*a2-9*b) + 27*c)/54; + double r2 = r*r; + double q3 = q*q*q; + double A,B; + if(r2 1) t= 1; + t=acos(t); + a/=3; q=-2*sqrt(q); + x[0]=q*cos(t/3)-a; + x[1]=q*cos((t+(2*igl::PI))/3)-a; + x[2]=q*cos((t-(2*igl::PI))/3)-a; + return(3); + } + else + { + A =-pow(fabs(r)+sqrt(r2-q3),1./3); + if( r<0 ) A=-A; + B = A==0? 0 : B=q/A; + + a/=3; + x[0] =(A+B)-a; + x[1] =-0.5*(A+B)-a; + x[2] = 0.5*sqrt(3.)*(A-B); + if(fabs(x[2])<1e-14) + { + x[2]=x[1]; return(2); + } + return(1); + } + } + + IGL_INLINE double get_smallest_pos_quad_zero(double a,double b, double c) + { + using namespace std; + double t1, t2; + if(std::abs(a) > 1.0e-10) + { + double delta_in = pow(b, 2) - 4 * a * c; + if(delta_in <= 0) + { + return INFINITY; + } + + double delta = sqrt(delta_in); // delta >= 0 + if(b >= 0) // avoid subtracting two similar numbers + { + double bd = - b - delta; + t1 = 2 * c / bd; + t2 = bd / (2 * a); + } + else + { + double bd = - b + delta; + t1 = bd / (2 * a); + t2 = (2 * c) / bd; + } + + assert (std::isfinite(t1)); + assert (std::isfinite(t2)); + + if(a < 0) std::swap(t1, t2); // make t1 > t2 + // return the smaller positive root if it exists, otherwise return infinity + if(t1 > 0) + { + return t2 > 0 ? t2 : t1; + } + else + { + return INFINITY; + } + } + else + { + if(b == 0) return INFINITY; // just to avoid divide-by-zero + t1 = -c / b; + return t1 > 0 ? t1 : INFINITY; + } + } + + IGL_INLINE double get_min_pos_root_2D(const Eigen::MatrixXd& uv, + const Eigen::MatrixXi& F, + Eigen::MatrixXd& d, + int f) + { + using namespace std; + /* + Finding the smallest timestep t s.t a triangle get degenerated (<=> det = 0) + The following code can be derived by a symbolic expression in matlab: + + Symbolic matlab: + U11 = sym('U11'); + U12 = sym('U12'); + U21 = sym('U21'); + U22 = sym('U22'); + U31 = sym('U31'); + U32 = sym('U32'); + + V11 = sym('V11'); + V12 = sym('V12'); + V21 = sym('V21'); + V22 = sym('V22'); + V31 = sym('V31'); + V32 = sym('V32'); + + t = sym('t'); + + U1 = [U11,U12]; + U2 = [U21,U22]; + U3 = [U31,U32]; + + V1 = [V11,V12]; + V2 = [V21,V22]; + V3 = [V31,V32]; + + A = [(U2+V2*t) - (U1+ V1*t)]; + B = [(U3+V3*t) - (U1+ V1*t)]; + C = [A;B]; + + solve(det(C), t); + cf = coeffs(det(C),t); % Now cf(1),cf(2),cf(3) holds the coefficients for the polynom. at order c,b,a + */ + + int v1 = F(f,0); int v2 = F(f,1); int v3 = F(f,2); + // get quadratic coefficients (ax^2 + b^x + c) + const double& U11 = uv(v1,0); + const double& U12 = uv(v1,1); + const double& U21 = uv(v2,0); + const double& U22 = uv(v2,1); + const double& U31 = uv(v3,0); + const double& U32 = uv(v3,1); + + const double& V11 = d(v1,0); + const double& V12 = d(v1,1); + const double& V21 = d(v2,0); + const double& V22 = d(v2,1); + const double& V31 = d(v3,0); + const double& V32 = d(v3,1); + + double a = V11*V22 - V12*V21 - V11*V32 + V12*V31 + V21*V32 - V22*V31; + double b = U11*V22 - U12*V21 - U21*V12 + U22*V11 - U11*V32 + U12*V31 + U31*V12 - U32*V11 + U21*V32 - U22*V31 - U31*V22 + U32*V21; + double c = U11*U22 - U12*U21 - U11*U32 + U12*U31 + U21*U32 - U22*U31; + + return get_smallest_pos_quad_zero(a,b,c); + } + + IGL_INLINE double get_min_pos_root_3D(const Eigen::MatrixXd& uv, + const Eigen::MatrixXi& F, + Eigen::MatrixXd& direc, + int f) + { + using namespace std; + /* + Searching for the roots of: + +-1/6 * |ax ay az 1| + |bx by bz 1| + |cx cy cz 1| + |dx dy dz 1| + Every point ax,ay,az has a search direction a_dx,a_dy,a_dz, and so we add those to the matrix, and solve the cubic to find the step size t for a 0 volume + Symbolic matlab: + syms a_x a_y a_z a_dx a_dy a_dz % tetrahedera point and search direction + syms b_x b_y b_z b_dx b_dy b_dz + syms c_x c_y c_z c_dx c_dy c_dz + syms d_x d_y d_z d_dx d_dy d_dz + syms t % Timestep var, this is what we're looking for + + + a_plus_t = [a_x,a_y,a_z] + t*[a_dx,a_dy,a_dz]; + b_plus_t = [b_x,b_y,b_z] + t*[b_dx,b_dy,b_dz]; + c_plus_t = [c_x,c_y,c_z] + t*[c_dx,c_dy,c_dz]; + d_plus_t = [d_x,d_y,d_z] + t*[d_dx,d_dy,d_dz]; + + vol_mat = [a_plus_t,1;b_plus_t,1;c_plus_t,1;d_plus_t,1] + //cf = coeffs(det(vol_det),t); % Now cf(1),cf(2),cf(3),cf(4) holds the coefficients for the polynom + [coefficients,terms] = coeffs(det(vol_det),t); % terms = [ t^3, t^2, t, 1], Coefficients hold the coeff we seek + */ + int v1 = F(f,0); int v2 = F(f,1); int v3 = F(f,2); int v4 = F(f,3); + const double& a_x = uv(v1,0); + const double& a_y = uv(v1,1); + const double& a_z = uv(v1,2); + const double& b_x = uv(v2,0); + const double& b_y = uv(v2,1); + const double& b_z = uv(v2,2); + const double& c_x = uv(v3,0); + const double& c_y = uv(v3,1); + const double& c_z = uv(v3,2); + const double& d_x = uv(v4,0); + const double& d_y = uv(v4,1); + const double& d_z = uv(v4,2); + + const double& a_dx = direc(v1,0); + const double& a_dy = direc(v1,1); + const double& a_dz = direc(v1,2); + const double& b_dx = direc(v2,0); + const double& b_dy = direc(v2,1); + const double& b_dz = direc(v2,2); + const double& c_dx = direc(v3,0); + const double& c_dy = direc(v3,1); + const double& c_dz = direc(v3,2); + const double& d_dx = direc(v4,0); + const double& d_dy = direc(v4,1); + const double& d_dz = direc(v4,2); + + // Find solution for: a*t^3 + b*t^2 + c*d +d = 0 + double a = a_dx*b_dy*c_dz - a_dx*b_dz*c_dy - a_dy*b_dx*c_dz + a_dy*b_dz*c_dx + a_dz*b_dx*c_dy - a_dz*b_dy*c_dx - a_dx*b_dy*d_dz + a_dx*b_dz*d_dy + a_dy*b_dx*d_dz - a_dy*b_dz*d_dx - a_dz*b_dx*d_dy + a_dz*b_dy*d_dx + a_dx*c_dy*d_dz - a_dx*c_dz*d_dy - a_dy*c_dx*d_dz + a_dy*c_dz*d_dx + a_dz*c_dx*d_dy - a_dz*c_dy*d_dx - b_dx*c_dy*d_dz + b_dx*c_dz*d_dy + b_dy*c_dx*d_dz - b_dy*c_dz*d_dx - b_dz*c_dx*d_dy + b_dz*c_dy*d_dx; + + double b = a_dy*b_dz*c_x - a_dy*b_x*c_dz - a_dz*b_dy*c_x + a_dz*b_x*c_dy + a_x*b_dy*c_dz - a_x*b_dz*c_dy - a_dx*b_dz*c_y + a_dx*b_y*c_dz + a_dz*b_dx*c_y - a_dz*b_y*c_dx - a_y*b_dx*c_dz + a_y*b_dz*c_dx + a_dx*b_dy*c_z - a_dx*b_z*c_dy - a_dy*b_dx*c_z + a_dy*b_z*c_dx + a_z*b_dx*c_dy - a_z*b_dy*c_dx - a_dy*b_dz*d_x + a_dy*b_x*d_dz + a_dz*b_dy*d_x - a_dz*b_x*d_dy - a_x*b_dy*d_dz + a_x*b_dz*d_dy + a_dx*b_dz*d_y - a_dx*b_y*d_dz - a_dz*b_dx*d_y + a_dz*b_y*d_dx + a_y*b_dx*d_dz - a_y*b_dz*d_dx - a_dx*b_dy*d_z + a_dx*b_z*d_dy + a_dy*b_dx*d_z - a_dy*b_z*d_dx - a_z*b_dx*d_dy + a_z*b_dy*d_dx + a_dy*c_dz*d_x - a_dy*c_x*d_dz - a_dz*c_dy*d_x + a_dz*c_x*d_dy + a_x*c_dy*d_dz - a_x*c_dz*d_dy - a_dx*c_dz*d_y + a_dx*c_y*d_dz + a_dz*c_dx*d_y - a_dz*c_y*d_dx - a_y*c_dx*d_dz + a_y*c_dz*d_dx + a_dx*c_dy*d_z - a_dx*c_z*d_dy - a_dy*c_dx*d_z + a_dy*c_z*d_dx + a_z*c_dx*d_dy - a_z*c_dy*d_dx - b_dy*c_dz*d_x + b_dy*c_x*d_dz + b_dz*c_dy*d_x - b_dz*c_x*d_dy - b_x*c_dy*d_dz + b_x*c_dz*d_dy + b_dx*c_dz*d_y - b_dx*c_y*d_dz - b_dz*c_dx*d_y + b_dz*c_y*d_dx + b_y*c_dx*d_dz - b_y*c_dz*d_dx - b_dx*c_dy*d_z + b_dx*c_z*d_dy + b_dy*c_dx*d_z - b_dy*c_z*d_dx - b_z*c_dx*d_dy + b_z*c_dy*d_dx; + + double c = a_dz*b_x*c_y - a_dz*b_y*c_x - a_x*b_dz*c_y + a_x*b_y*c_dz + a_y*b_dz*c_x - a_y*b_x*c_dz - a_dy*b_x*c_z + a_dy*b_z*c_x + a_x*b_dy*c_z - a_x*b_z*c_dy - a_z*b_dy*c_x + a_z*b_x*c_dy + a_dx*b_y*c_z - a_dx*b_z*c_y - a_y*b_dx*c_z + a_y*b_z*c_dx + a_z*b_dx*c_y - a_z*b_y*c_dx - a_dz*b_x*d_y + a_dz*b_y*d_x + a_x*b_dz*d_y - a_x*b_y*d_dz - a_y*b_dz*d_x + a_y*b_x*d_dz + a_dy*b_x*d_z - a_dy*b_z*d_x - a_x*b_dy*d_z + a_x*b_z*d_dy + a_z*b_dy*d_x - a_z*b_x*d_dy - a_dx*b_y*d_z + a_dx*b_z*d_y + a_y*b_dx*d_z - a_y*b_z*d_dx - a_z*b_dx*d_y + a_z*b_y*d_dx + a_dz*c_x*d_y - a_dz*c_y*d_x - a_x*c_dz*d_y + a_x*c_y*d_dz + a_y*c_dz*d_x - a_y*c_x*d_dz - a_dy*c_x*d_z + a_dy*c_z*d_x + a_x*c_dy*d_z - a_x*c_z*d_dy - a_z*c_dy*d_x + a_z*c_x*d_dy + a_dx*c_y*d_z - a_dx*c_z*d_y - a_y*c_dx*d_z + a_y*c_z*d_dx + a_z*c_dx*d_y - a_z*c_y*d_dx - b_dz*c_x*d_y + b_dz*c_y*d_x + b_x*c_dz*d_y - b_x*c_y*d_dz - b_y*c_dz*d_x + b_y*c_x*d_dz + b_dy*c_x*d_z - b_dy*c_z*d_x - b_x*c_dy*d_z + b_x*c_z*d_dy + b_z*c_dy*d_x - b_z*c_x*d_dy - b_dx*c_y*d_z + b_dx*c_z*d_y + b_y*c_dx*d_z - b_y*c_z*d_dx - b_z*c_dx*d_y + b_z*c_y*d_dx; + + double d = a_x*b_y*c_z - a_x*b_z*c_y - a_y*b_x*c_z + a_y*b_z*c_x + a_z*b_x*c_y - a_z*b_y*c_x - a_x*b_y*d_z + a_x*b_z*d_y + a_y*b_x*d_z - a_y*b_z*d_x - a_z*b_x*d_y + a_z*b_y*d_x + a_x*c_y*d_z - a_x*c_z*d_y - a_y*c_x*d_z + a_y*c_z*d_x + a_z*c_x*d_y - a_z*c_y*d_x - b_x*c_y*d_z + b_x*c_z*d_y + b_y*c_x*d_z - b_y*c_z*d_x - b_z*c_x*d_y + b_z*c_y*d_x; + + if (std::abs(a)<=1.e-10) + { + return get_smallest_pos_quad_zero(b,c,d); + } + b/=a; c/=a; d/=a; // normalize it all + std::vector res(3); + int real_roots_num = SolveP3(res,b,c,d); + switch (real_roots_num) + { + case 1: + return (res[0] >= 0) ? res[0]:INFINITY; + case 2: + { + double max_root = std::max(res[0],res[1]); double min_root = std::min(res[0],res[1]); + if (min_root > 0) return min_root; + if (max_root > 0) return max_root; + return INFINITY; + } + case 3: + default: + { + std::sort(res.begin(),res.end()); + if (res[0] > 0) return res[0]; + if (res[1] > 0) return res[1]; + if (res[2] > 0) return res[2]; + return INFINITY; + } + } + } + + IGL_INLINE double compute_max_step_from_singularities(const Eigen::MatrixXd& uv, + const Eigen::MatrixXi& F, + Eigen::MatrixXd& d) + { + using namespace std; + double max_step = INFINITY; + + // The if statement is outside the for loops to avoid branching/ease parallelizing + if (uv.cols() == 2) + { + for (int f = 0; f < F.rows(); f++) + { + double min_positive_root = get_min_pos_root_2D(uv,F,d,f); + max_step = std::min(max_step, min_positive_root); + } + } + else + { // volumetric deformation + for (int f = 0; f < F.rows(); f++) + { + double min_positive_root = get_min_pos_root_3D(uv,F,d,f); + max_step = std::min(max_step, min_positive_root); + } + } + return max_step; + } + } +} + +IGL_INLINE double igl::flip_avoiding_line_search( + const Eigen::MatrixXi F, + Eigen::MatrixXd& cur_v, + Eigen::MatrixXd& dst_v, + std::function energy, + double cur_energy) +{ + using namespace std; + Eigen::MatrixXd d = dst_v - cur_v; + + double min_step_to_singularity = igl::flip_avoiding::compute_max_step_from_singularities(cur_v,F,d); + double max_step_size = std::min(1., min_step_to_singularity*0.8); + + return igl::line_search(cur_v,d,max_step_size, energy, cur_energy); +} + +#ifdef IGL_STATIC_LIBRARY +#endif diff --git a/src/igl/flip_avoiding_line_search.h b/src/igl/flip_avoiding_line_search.h new file mode 100644 index 0000000000..29eb9dee09 --- /dev/null +++ b/src/igl/flip_avoiding_line_search.h @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Michael Rabinovich +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FLIP_AVOIDING_LINE_SEARCH_H +#define IGL_FLIP_AVOIDING_LINE_SEARCH_H +#include "igl_inline.h" +#include "PI.h" + +#include + +namespace igl +{ + // A bisection line search for a mesh based energy that avoids triangle flips as suggested in + // "Bijective Parameterization with Free Boundaries" (Smith J. and Schaefer S., 2015). + // + // The user specifies an initial vertices position (that has no flips) and target one (that my have flipped triangles). + // This method first computes the largest step in direction of the destination vertices that does not incur flips, + // and then minimizes a given energy using this maximal step and a bisection linesearch (see igl::line_search). + // + // Supports both triangle and tet meshes. + // + // Inputs: + // F #F by 3/4 list of mesh faces or tets + // cur_v #V by dim list of variables + // dst_v #V by dim list of target vertices. This mesh may have flipped triangles + // energy A function to compute the mesh-based energy (return an energy that is bigger than 0) + // cur_energy(OPTIONAL) The energy at the given point. Helps save redundant computations. + // This is optional. If not specified, the function will compute it. + // Outputs: + // cur_v #V by dim list of variables at the new location + // Returns the energy at the new point + IGL_INLINE double flip_avoiding_line_search( + const Eigen::MatrixXi F, + Eigen::MatrixXd& cur_v, + Eigen::MatrixXd& dst_v, + std::function energy, + double cur_energy = -1); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "flip_avoiding_line_search.cpp" +#endif + +#endif diff --git a/src/igl/flip_edge.cpp b/src/igl/flip_edge.cpp new file mode 100644 index 0000000000..b34581449e --- /dev/null +++ b/src/igl/flip_edge.cpp @@ -0,0 +1,153 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "flip_edge.h" + +template < + typename DerivedF, + typename DerivedE, + typename DeriveduE, + typename DerivedEMAP, + typename uE2EType> +IGL_INLINE void igl::flip_edge( + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & uE, + Eigen::PlainObjectBase & EMAP, + std::vector > & uE2E, + const size_t uei) +{ + typedef typename DerivedF::Scalar Index; + const size_t num_faces = F.rows(); + assert(F.cols() == 3); + // v1 v1 + // /|\ / \ + // / | \ /f1 \ + // v3 /f2|f1\ v4 => v3 /_____\ v4 + // \ | / \ f2 / + // \ | / \ / + // \|/ \ / + // v2 v2 + auto& half_edges = uE2E[uei]; + if (half_edges.size() != 2) { + throw "Cannot flip non-manifold or boundary edge"; + } + + const size_t f1 = half_edges[0] % num_faces; + const size_t f2 = half_edges[1] % num_faces; + const size_t c1 = half_edges[0] / num_faces; + const size_t c2 = half_edges[1] / num_faces; + assert(c1 < 3); + assert(c2 < 3); + + assert(f1 != f2); + const size_t v1 = F(f1, (c1+1)%3); + const size_t v2 = F(f1, (c1+2)%3); + const size_t v4 = F(f1, c1); + const size_t v3 = F(f2, c2); + assert(F(f2, (c2+2)%3) == v1); + assert(F(f2, (c2+1)%3) == v2); + + const size_t e_12 = half_edges[0]; + const size_t e_24 = f1 + ((c1 + 1) % 3) * num_faces; + const size_t e_41 = f1 + ((c1 + 2) % 3) * num_faces; + const size_t e_21 = half_edges[1]; + const size_t e_13 = f2 + ((c2 + 1) % 3) * num_faces; + const size_t e_32 = f2 + ((c2 + 2) % 3) * num_faces; + assert(E(e_12, 0) == v1); + assert(E(e_12, 1) == v2); + assert(E(e_24, 0) == v2); + assert(E(e_24, 1) == v4); + assert(E(e_41, 0) == v4); + assert(E(e_41, 1) == v1); + assert(E(e_21, 0) == v2); + assert(E(e_21, 1) == v1); + assert(E(e_13, 0) == v1); + assert(E(e_13, 1) == v3); + assert(E(e_32, 0) == v3); + assert(E(e_32, 1) == v2); + + const size_t ue_24 = EMAP(e_24); + const size_t ue_41 = EMAP(e_41); + const size_t ue_13 = EMAP(e_13); + const size_t ue_32 = EMAP(e_32); + + F(f1, 0) = v1; + F(f1, 1) = v3; + F(f1, 2) = v4; + F(f2, 0) = v2; + F(f2, 1) = v4; + F(f2, 2) = v3; + + uE(uei, 0) = v3; + uE(uei, 1) = v4; + + const size_t new_e_34 = f1; + const size_t new_e_41 = f1 + num_faces; + const size_t new_e_13 = f1 + num_faces*2; + const size_t new_e_43 = f2; + const size_t new_e_32 = f2 + num_faces; + const size_t new_e_24 = f2 + num_faces*2; + + E(new_e_34, 0) = v3; + E(new_e_34, 1) = v4; + E(new_e_41, 0) = v4; + E(new_e_41, 1) = v1; + E(new_e_13, 0) = v1; + E(new_e_13, 1) = v3; + E(new_e_43, 0) = v4; + E(new_e_43, 1) = v3; + E(new_e_32, 0) = v3; + E(new_e_32, 1) = v2; + E(new_e_24, 0) = v2; + E(new_e_24, 1) = v4; + + EMAP(new_e_34) = uei; + EMAP(new_e_43) = uei; + EMAP(new_e_41) = ue_41; + EMAP(new_e_13) = ue_13; + EMAP(new_e_32) = ue_32; + EMAP(new_e_24) = ue_24; + + auto replace = [](std::vector& array, Index old_v, Index new_v) { + std::replace(array.begin(), array.end(), old_v, new_v); + }; + replace(uE2E[uei], e_12, new_e_34); + replace(uE2E[uei], e_21, new_e_43); + replace(uE2E[ue_13], e_13, new_e_13); + replace(uE2E[ue_32], e_32, new_e_32); + replace(uE2E[ue_24], e_24, new_e_24); + replace(uE2E[ue_41], e_41, new_e_41); + +#ifndef NDEBUG + auto sanity_check = [&](size_t ue) { + const auto& adj_faces = uE2E[ue]; + if (adj_faces.size() == 2) { + const size_t first_f = adj_faces[0] % num_faces; + const size_t first_c = adj_faces[0] / num_faces; + const size_t second_f = adj_faces[1] % num_faces; + const size_t second_c = adj_faces[1] / num_faces; + const size_t vertex_0 = F(first_f, (first_c+1) % 3); + const size_t vertex_1 = F(first_f, (first_c+2) % 3); + assert(vertex_0 == F(second_f, (second_c+2) % 3)); + assert(vertex_1 == F(second_f, (second_c+1) % 3)); + } + }; + + sanity_check(uei); + sanity_check(ue_13); + sanity_check(ue_32); + sanity_check(ue_24); + sanity_check(ue_41); +#endif +} + + +#ifdef IGL_STATIC_LIBRARY +template void igl::flip_edge, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&, unsigned long); +#endif \ No newline at end of file diff --git a/src/igl/flip_edge.h b/src/igl/flip_edge.h new file mode 100644 index 0000000000..3c43198a6e --- /dev/null +++ b/src/igl/flip_edge.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_FLIP_EDGE_H +#define IGL_FLIP_EDGE_H + +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Flip an edge in a triangle mesh. The edge specified by uei must have + // exactly **two** adjacent faces. Violation will result in exception. + // Another warning: edge flipping could convert manifold mesh into + // non-manifold. + // + // Inputs: + // F #F by 3 list of triangles. + // E #F*3 by 2 list of all of directed edges + // uE #uE by 2 list of unique undirected edges + // EMAP #F*3 list of indices into uE, mapping each directed edge to unique + // undirected edge + // uE2E #uE list of lists of indices into E of coexisting edges + // ue index into uE the edge to be flipped. + // + // Output: + // Updated F, E, uE, EMAP and uE2E. + template < + typename DerivedF, + typename DerivedE, + typename DeriveduE, + typename DerivedEMAP, + typename uE2EType> + IGL_INLINE void flip_edge( + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & uE, + Eigen::PlainObjectBase & EMAP, + std::vector > & uE2E, + const size_t uei); +} + +#ifndef IGL_STATIC_LIBRARY +# include "flip_edge.cpp" +#endif +#endif diff --git a/src/igl/flipped_triangles.cpp b/src/igl/flipped_triangles.cpp new file mode 100644 index 0000000000..ea76e5c843 --- /dev/null +++ b/src/igl/flipped_triangles.cpp @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Michael Rabinovich +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "flipped_triangles.h" + +#include "list_to_matrix.h" +#include +template +IGL_INLINE void igl::flipped_triangles( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & X) +{ + assert(V.cols() == 2 && "V should contain 2D positions"); + std::vector flip_idx; + for (int i = 0; i < F.rows(); i++) + { + // https://www.cs.cmu.edu/~quake/robust.html + typedef Eigen::Matrix RowVector2S; + RowVector2S v1_n = V.row(F(i,0)); + RowVector2S v2_n = V.row(F(i,1)); + RowVector2S v3_n = V.row(F(i,2)); + Eigen::Matrix T2_Homo; + T2_Homo.col(0) << v1_n(0),v1_n(1),1.; + T2_Homo.col(1) << v2_n(0),v2_n(1),1.; + T2_Homo.col(2) << v3_n(0),v3_n(1),1.; + double det = T2_Homo.determinant(); + assert(det == det && "det should not be NaN"); + if (det < 0) + { + flip_idx.push_back(i); + } + } + igl::list_to_matrix(flip_idx,X); +} + +template +IGL_INLINE Eigen::VectorXi igl::flipped_triangles( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F) +{ + Eigen::VectorXi X; + flipped_triangles(V,F,X); + return X; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::flipped_triangles, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template Eigen::Matrix igl::flipped_triangles, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +#endif diff --git a/src/igl/flipped_triangles.h b/src/igl/flipped_triangles.h new file mode 100644 index 0000000000..b06d9329bb --- /dev/null +++ b/src/igl/flipped_triangles.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Michael Rabinovich +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FLIPPED_TRIANGLES_H +#define IGL_FLIPPED_TRIANGLES_H +#include "igl_inline.h" + +#include +namespace igl +{ + // Finds the ids of the flipped triangles of the mesh V,F in the UV mapping uv + // + // Inputs: + // V #V by 2 list of mesh vertex positions + // F #F by 3 list of mesh faces (must be triangles) + // Outputs: + // X #flipped list of containing the indices into F of the flipped triangles + // Wrapper with return type + template + IGL_INLINE void flipped_triangles( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & X); + template + IGL_INLINE Eigen::VectorXi flipped_triangles( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "flipped_triangles.cpp" +#endif + +#endif diff --git a/src/igl/flood_fill.cpp b/src/igl/flood_fill.cpp new file mode 100644 index 0000000000..a6c91361d5 --- /dev/null +++ b/src/igl/flood_fill.cpp @@ -0,0 +1,103 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "flood_fill.h" +#include + +template +IGL_INLINE void igl::flood_fill( + const Eigen::MatrixBase& res, + Eigen::PlainObjectBase & S) +{ + using namespace Eigen; + using namespace std; + typedef typename DerivedS::Scalar Scalar; + const auto flood = [&res,&S] ( + const int xi, + const int yi, + const int zi, + const int signed_xi, + const int signed_yi, + const int signed_zi, + const Scalar s) + { + // flood fill this value back on this row + for(int bxi = xi;signed_xi<--bxi;) + { + S(bxi+res(0)*(yi + res(1)*zi)) = s; + } + // flood fill this value back on any previous rows + for(int byi = yi;signed_yi<--byi;) + { + for(int xi = 0;xi::quiet_NaN(); + for(int zi = 0;zi, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::flood_fill, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::flood_fill, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::flood_fill, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::flood_fill, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/flood_fill.h b/src/igl/flood_fill.h new file mode 100644 index 0000000000..7eef01b3c8 --- /dev/null +++ b/src/igl/flood_fill.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FLOOD_FILL_H +#define IGL_FLOOD_FILL_H +#include "igl_inline.h" +#include +namespace igl +{ + // Given a 3D array with sparse non-nan (number?) data fill in the NaNs via + // flood fill. This should ensure that, e.g., if data near 0 always has a band + // (surface) of numbered and signed data, then components of nans will be + // correctly signed. + // + // Inputs: + // res 3-long list of dimensions of grid + // S res(0)*res(1)*res(2) list of scalar values (with (many) nans), see + // output + // Outputs: + // S flood fill data in place + template + IGL_INLINE void flood_fill( + const Eigen::MatrixBase& res, + Eigen::PlainObjectBase & S); +} +#ifndef IGL_STATIC_LIBRARY +# include "flood_fill.cpp" +#endif +#endif diff --git a/src/igl/floor.cpp b/src/igl/floor.cpp new file mode 100644 index 0000000000..7538b7e01d --- /dev/null +++ b/src/igl/floor.cpp @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "floor.h" +#include +#include + +template < typename DerivedX, typename DerivedY> +IGL_INLINE void igl::floor( + const Eigen::PlainObjectBase& X, + Eigen::PlainObjectBase& Y) +{ + using namespace std; + //Y = DerivedY::Zero(m,n); +//#pragma omp parallel for + //for(int i = 0;iScalar{return std::floor(x);}).template cast(); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::floor, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::floor, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::floor, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/floor.h b/src/igl/floor.h new file mode 100644 index 0000000000..a84d52068a --- /dev/null +++ b/src/igl/floor.h @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FLOOR_H +#define IGL_FLOOR_H +#include "igl_inline.h" +#include +namespace igl +{ + // Floor a given matrix to nearest integers + // + // Inputs: + // X m by n matrix of scalars + // Outputs: + // Y m by n matrix of floored integers + template < typename DerivedX, typename DerivedY> + IGL_INLINE void floor( + const Eigen::PlainObjectBase& X, + Eigen::PlainObjectBase& Y); +} + +#ifndef IGL_STATIC_LIBRARY +# include "floor.cpp" +#endif + +#endif diff --git a/src/igl/for_each.h b/src/igl/for_each.h new file mode 100644 index 0000000000..6ba03ba1c1 --- /dev/null +++ b/src/igl/for_each.h @@ -0,0 +1,78 @@ +#ifndef IGL_FOR_EACH_H +#define IGL_FOR_EACH_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // FOR_EACH Call a given function for each non-zero (i.e., explicit value + // might actually be ==0) in a Sparse Matrix A _in order (of storage)_. This is + // useless unless func has _side-effects_. + // + // Inputs: + // A m by n SparseMatrix + // func function handle with prototype "compatible with" `void (Index i, + // Index j, Scalar & v)`. Return values will be ignored. + // + // See also: std::for_each + template + inline void for_each( + const Eigen::SparseMatrix & A, + const Func & func); + template + inline void for_each( + const Eigen::DenseBase & A, + const Func & func); +} + +// Implementation + +template +inline void igl::for_each( + const Eigen::SparseMatrix & A, + const Func & func) +{ + // Can **not** use parallel for because this must be _in order_ + // Iterate over outside + for(int k=0; k::InnerIterator it (A,k); it; ++it) + { + func(it.row(),it.col(),it.value()); + } + } +} + +template +inline void igl::for_each( + const Eigen::DenseBase & A, + const Func & func) +{ + // Can **not** use parallel for because this must be _in order_ + if(A.IsRowMajor) + { + for(typename DerivedA::Index i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "forward_kinematics.h" +#include +#include + +IGL_INLINE void igl::forward_kinematics( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const Eigen::VectorXi & P, + const std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & dQ, + const std::vector & dT, + std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & vQ, + std::vector & vT) +{ + using namespace std; + using namespace Eigen; + const int m = BE.rows(); + assert(m == P.rows()); + assert(m == (int)dQ.size()); + assert(m == (int)dT.size()); + vector computed(m,false); + vQ.resize(m); + vT.resize(m); + // Dynamic programming + function fk_helper = [&] (int b) + { + if(!computed[b]) + { + if(P(b) < 0) + { + // base case for roots + vQ[b] = dQ[b]; + const Vector3d r = C.row(BE(b,0)).transpose(); + vT[b] = r-dQ[b]*r + dT[b]; + }else + { + // Otherwise first compute parent's + const int p = P(b); + fk_helper(p); + vQ[b] = vQ[p] * dQ[b]; + const Vector3d r = C.row(BE(b,0)).transpose(); + vT[b] = vT[p] - vQ[b]*r + vQ[p]*(r + dT[b]); + } + computed[b] = true; + } + }; + for(int b = 0;b > & dQ, + std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & vQ, + std::vector & vT) +{ + std::vector dT(BE.rows(),Eigen::Vector3d(0,0,0)); + return forward_kinematics(C,BE,P,dQ,dT,vQ,vT); +} + +IGL_INLINE void igl::forward_kinematics( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const Eigen::VectorXi & P, + const std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & dQ, + const std::vector & dT, + Eigen::MatrixXd & T) +{ + using namespace Eigen; + using namespace std; + vector< Quaterniond,aligned_allocator > vQ; + vector< Vector3d> vT; + forward_kinematics(C,BE,P,dQ,dT,vQ,vT); + const int dim = C.cols(); + T.resize(BE.rows()*(dim+1),dim); + for(int e = 0;e > & dQ, + Eigen::MatrixXd & T) +{ + std::vector dT(BE.rows(),Eigen::Vector3d(0,0,0)); + return forward_kinematics(C,BE,P,dQ,dT,T); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/forward_kinematics.h b/src/igl/forward_kinematics.h new file mode 100644 index 0000000000..2b3d2db6dd --- /dev/null +++ b/src/igl/forward_kinematics.h @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FORWARD_KINEMATICS_H +#define IGL_FORWARD_KINEMATICS_H +#include "igl_inline.h" +#include +#include +#include +#include + +namespace igl +{ + // Given a skeleton and a set of relative bone rotations compute absolute + // rigid transformations for each bone. + // + // Inputs: + // C #C by dim list of joint positions + // BE #BE by 2 list of bone edge indices + // P #BE list of parent indices into BE + // dQ #BE list of relative rotations + // dT #BE list of relative translations + // Outputs: + // vQ #BE list of absolute rotations + // vT #BE list of absolute translations + IGL_INLINE void forward_kinematics( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const Eigen::VectorXi & P, + const std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & dQ, + const std::vector & dT, + std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & vQ, + std::vector & vT); + // Wrapper assuming each dT[i] == {0,0,0} + IGL_INLINE void forward_kinematics( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const Eigen::VectorXi & P, + const std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & dQ, + std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & vQ, + std::vector & vT); + + // Outputs: + // T #BE*(dim+1) by dim stack of transposed transformation matrices + IGL_INLINE void forward_kinematics( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const Eigen::VectorXi & P, + const std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & dQ, + const std::vector & dT, + Eigen::MatrixXd & T); + IGL_INLINE void forward_kinematics( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const Eigen::VectorXi & P, + const std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > & dQ, + Eigen::MatrixXd & T); + +}; + +#ifndef IGL_STATIC_LIBRARY +# include "forward_kinematics.cpp" +#endif +#endif diff --git a/src/igl/frame_field_deformer.cpp b/src/igl/frame_field_deformer.cpp new file mode 100644 index 0000000000..c22200f1b5 --- /dev/null +++ b/src/igl/frame_field_deformer.cpp @@ -0,0 +1,411 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "frame_field_deformer.h" + +#include +#include +#include + +#include +#include +#include + +namespace igl +{ + +class Frame_field_deformer +{ +public: + + IGL_INLINE Frame_field_deformer(); + IGL_INLINE ~Frame_field_deformer(); + + // Initialize the optimizer + IGL_INLINE void init(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F, const Eigen::MatrixXd& _D1, const Eigen::MatrixXd& _D2, double _Lambda, double _perturb_rotations, int _fixed = 1); + + // Run N optimization steps + IGL_INLINE void optimize(int N, bool reset = false); + + // Reset optimization + IGL_INLINE void reset_opt(); + + // Precomputation of all components + IGL_INLINE void precompute_opt(); + + // Precomputation for deformation energy + IGL_INLINE void precompute_ARAP(Eigen::SparseMatrix & Lff, Eigen::MatrixXd & LfcVc); + + // Precomputation for regularization + IGL_INLINE void precompute_SMOOTH(Eigen::SparseMatrix & MS, Eigen::MatrixXd & bS); + + // extracts a r x c block from sparse matrix mat into sparse matrix m1 + // (r0,c0) is upper left entry of block + IGL_INLINE void extractBlock(Eigen::SparseMatrix & mat, int r0, int c0, int r, int c, Eigen::SparseMatrix & m1); + + // computes optimal rotations for faces of m wrt current coords in mw.V + // returns a 3x3 matrix + IGL_INLINE void compute_optimal_rotations(); + + // global optimization step - linear system + IGL_INLINE void compute_optimal_positions(); + + // compute the output XField from deformation gradient + IGL_INLINE void computeXField(std::vector< Eigen::Matrix > & XF); + + // computes in WW the ideal warp at each tri to make the frame field a cross + IGL_INLINE void compute_idealWarp(std::vector< Eigen::Matrix > & WW); + + // -------------------------------- Variables ---------------------------------------------------- + + // Mesh I/O: + + Eigen::MatrixXd V; // Original mesh - vertices + Eigen::MatrixXi F; // Original mesh - faces + + std::vector > VT; // Vertex to triangle topology + std::vector > VTi; // Vertex to triangle topology + + Eigen::MatrixXd V_w; // Warped mesh - vertices + + std::vector< Eigen::Matrix > FF; // frame field FF in 3D (parallel to m.F) + std::vector< Eigen::Matrix > WW; // warping matrices to make a cross field (parallel to m.F) + std::vector< Eigen::Matrix > XF; // pseudo-cross field from solution (parallel to m.F) + + int fixed; + + double perturb_rotations; // perturbation to rotation matrices + + // Numerics + int nfree,nconst; // number of free/constrained vertices in the mesh - default all-but-1/1 + Eigen::MatrixXd C; // cotangent matrix of m + Eigen::SparseMatrix L; // Laplacian matrix of m + + Eigen::SparseMatrix M; // matrix for global optimization - pre-conditioned + Eigen::MatrixXd RHS; // pre-computed part of known term in global optimization + std::vector< Eigen::Matrix > RW; // optimal rotation-warping matrices (parallel to m.F) -- INCORPORATES WW + Eigen::SimplicialCholesky > solver; // solver for linear system in global opt. + + // Parameters +private: + double Lambda = 0.1; // weight of energy regularization + +}; + + IGL_INLINE Frame_field_deformer::Frame_field_deformer() {} + + IGL_INLINE Frame_field_deformer::~Frame_field_deformer() {} + + IGL_INLINE void Frame_field_deformer::init(const Eigen::MatrixXd& _V, + const Eigen::MatrixXi& _F, + const Eigen::MatrixXd& _D1, + const Eigen::MatrixXd& _D2, + double _Lambda, + double _perturb_rotations, + int _fixed) +{ + V = _V; + F = _F; + + assert(_D1.rows() == _D2.rows()); + + FF.clear(); + for (unsigned i=0; i < _D1.rows(); ++i) + { + Eigen::Matrix ff; + ff.col(0) = _D1.row(i); + ff.col(1) = _D2.row(i); + FF.push_back(ff); + } + + fixed = _fixed; + Lambda = _Lambda; + perturb_rotations = _perturb_rotations; + + reset_opt(); + precompute_opt(); +} + + +IGL_INLINE void Frame_field_deformer::optimize(int N, bool reset) +{ + //Reset optimization + if (reset) + reset_opt(); + + // Iterative Local/Global optimization + for (int i=0; i MA; // internal matrix for ARAP-warping energy + MatrixXd LfcVc; // RHS (partial) for ARAP-warping energy + SparseMatrix MS; // internal matrix for smoothing energy + MatrixXd bS; // RHS (full) for smoothing energy + + precompute_ARAP(MA,LfcVc); // precompute terms for the ARAP-warp part + precompute_SMOOTH(MS,bS); // precompute terms for the smoothing part + compute_idealWarp(WW); // computes the ideal warps + RW.resize(F.rows()); // init rotation matrices - global + + M = (1-Lambda)*MA + Lambda*MS; // matrix for linear system - global + + RHS = (1-Lambda)*LfcVc + Lambda*bS; // RHS (partial) for linear system - global + solver.compute(M); // system pre-conditioning + if (solver.info()!=Eigen::Success) {fprintf(stderr,"Decomposition failed in pre-conditioning!\n"); exit(-1);} + + fprintf(stdout,"Preconditioning done.\n"); + +} + +IGL_INLINE void Frame_field_deformer::precompute_ARAP(Eigen::SparseMatrix & Lff, Eigen::MatrixXd & LfcVc) +{ + using namespace Eigen; + fprintf(stdout,"Precomputing ARAP terms\n"); + SparseMatrix LL = -4*L; + Lff = SparseMatrix(nfree,nfree); + extractBlock(LL,0,0,nfree,nfree,Lff); + SparseMatrix Lfc = SparseMatrix(nfree,nconst); + extractBlock(LL,0,nfree,nfree,nconst,Lfc); + LfcVc = - Lfc * V_w.block(nfree,0,nconst,3); +} + +IGL_INLINE void Frame_field_deformer::precompute_SMOOTH(Eigen::SparseMatrix & MS, Eigen::MatrixXd & bS) +{ + using namespace Eigen; + fprintf(stdout,"Precomputing SMOOTH terms\n"); + + SparseMatrix LL = 4*L*L; + + // top-left + MS = SparseMatrix(nfree,nfree); + extractBlock(LL,0,0,nfree,nfree,MS); + + // top-right + SparseMatrix Mfc = SparseMatrix(nfree,nconst); + extractBlock(LL,0,nfree,nfree,nconst,Mfc); + + MatrixXd MfcVc = Mfc * V_w.block(nfree,0,nconst,3); + bS = (LL*V).block(0,0,nfree,3)-MfcVc; + +} + + IGL_INLINE void Frame_field_deformer::extractBlock(Eigen::SparseMatrix & mat, int r0, int c0, int r, int c, Eigen::SparseMatrix & m1) +{ + std::vector > tripletList; + for (int k=c0; k::InnerIterator it(mat,k); it; ++it) + { + if (it.row()>=r0 && it.row()(it.row()-r0,it.col()-c0,it.value())); + } + m1.setFromTriplets(tripletList.begin(), tripletList.end()); +} + +IGL_INLINE void Frame_field_deformer::compute_optimal_rotations() +{ + using namespace Eigen; + Matrix r,S,P,PP,D; + + for (int i=0;i > svd(S, Eigen::ComputeFullU | Eigen::ComputeFullV ); + Matrix su = svd.matrixU(); + Matrix sv = svd.matrixV(); + r = su*sv.transpose(); + + if (r.determinant()<0) // correct reflections + { + su(0,2)=-su(0,2); su(1,2)=-su(1,2); su(2,2)=-su(2,2); + r = su*sv.transpose(); + } + RW[i] = r*WW[i]; // RW INCORPORATES IDEAL WARP WW!!! + } +} + +IGL_INLINE void Frame_field_deformer::compute_optimal_positions() +{ + using namespace Eigen; + // compute variable RHS of ARAP-warp part of the system + MatrixXd b(nfree,3); // fx3 known term of the system + MatrixXd X; // result + int t; // triangles incident to edge (i,j) + int vi,i1,i2; // index of vertex i wrt tri t0 + + for (int i=0;i > & XF) +{ + using namespace Eigen; + Matrix P,PP,DG; + XF.resize(F.rows()); + + for (int i=0;i > & WW) +{ + using namespace Eigen; + + WW.resize(F.rows()); + for (int i=0;i<(int)FF.size();i++) + { + Vector3d v0,v1,v2; + v0 = FF[i].col(0); + v1 = FF[i].col(1); + v2=v0.cross(v1); v2.normalize(); // normal + + Matrix3d A,AI; // compute affine map A that brings: + A << v0[0], v1[0], v2[0], // first vector of FF to x unary vector + v0[1], v1[1], v2[1], // second vector of FF to xy plane + v0[2], v1[2], v2[2]; // triangle normal to z unary vector + AI = A.inverse(); + + // polar decomposition to discard rotational component (unnecessary but makes it easier) + Eigen::JacobiSVD > svd(AI, Eigen::ComputeFullU | Eigen::ComputeFullV ); + //Matrix au = svd.matrixU(); + Matrix av = svd.matrixV(); + DiagonalMatrix as(svd.singularValues()); + WW[i] = av*as*av.transpose(); + } +} + +} + + +IGL_INLINE void igl::frame_field_deformer( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::MatrixXd& FF1, + const Eigen::MatrixXd& FF2, + Eigen::MatrixXd& V_d, + Eigen::MatrixXd& FF1_d, + Eigen::MatrixXd& FF2_d, + const int iterations, + const double lambda, + const bool perturb_initial_guess) +{ + using namespace Eigen; + // Solvers + Frame_field_deformer deformer; + + // Init optimizer + deformer.init(V, F, FF1, FF2, lambda, perturb_initial_guess ? 0.1 : 0); + + // Optimize + deformer.optimize(iterations,true); + + // Copy positions + V_d = deformer.V_w; + + // Allocate + FF1_d.resize(F.rows(),3); + FF2_d.resize(F.rows(),3); + + // Copy frame field + for(unsigned i=0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FRAME_FIELD_DEFORMER_H +#define IGL_FRAME_FIELD_DEFORMER_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Deform a mesh to transform the given per-face frame field to be as close + // as possible to a cross field, in the least square sense. + // + // Inputs: + // V #V by 3 coordinates of the vertices + // F #F by 3 list of mesh faces (must be triangles) + // FF1 #F by 3 first representative vector of the frame field + // FF2 #F by 3 second representative vector of the frame field + // lambda laplacian regularization parameter 0=no regularization 1=full regularization + // + // Outputs: + // V_d #F by 3 deformed, first representative vector + // V_d #F by 3 deformed, first representative vector + // V_d #F by 3 deformed, first representative vector + // + IGL_INLINE void frame_field_deformer( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::MatrixXd& FF1, + const Eigen::MatrixXd& FF2, + Eigen::MatrixXd& V_d, + Eigen::MatrixXd& FF1_d, + Eigen::MatrixXd& FF2_d, + const int iterations = 50, + const double lambda = 0.1, + const bool perturb_initial_guess = true); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "frame_field_deformer.cpp" +#endif + +#endif diff --git a/src/igl/frame_to_cross_field.cpp b/src/igl/frame_to_cross_field.cpp new file mode 100644 index 0000000000..e4d6bc7139 --- /dev/null +++ b/src/igl/frame_to_cross_field.cpp @@ -0,0 +1,58 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "frame_to_cross_field.h" +#include +#include + +IGL_INLINE void igl::frame_to_cross_field( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::MatrixXd& FF1, + const Eigen::MatrixXd& FF2, + Eigen::MatrixXd& X) +{ + using namespace Eigen; + + // Generate local basis + MatrixXd B1, B2, B3; + + igl::local_basis(V,F,B1,B2,B3); + + // Project the frame fields in the local basis + MatrixXd d1, d2; + d1.resize(F.rows(),2); + d2.resize(F.rows(),2); + + d1 << igl::dot_row(B1,FF1), igl::dot_row(B2,FF1); + d2 << igl::dot_row(B1,FF2), igl::dot_row(B2,FF2); + + X.resize(F.rows(), 3); + + for (int i=0;i > svd(A, Eigen::ComputeFullU | Eigen::ComputeFullV ); + Matrix2d C = svd.matrixU() * svd.matrixV().transpose(); + + Vector2d v = C.col(0); + X.row(i) = v(0) * B1.row(i) + v(1) * B2.row(i); + } +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/frame_to_cross_field.h b/src/igl/frame_to_cross_field.h new file mode 100644 index 0000000000..f9bdd3d5f2 --- /dev/null +++ b/src/igl/frame_to_cross_field.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FRAME_TO_CROSS_FIELD_H +#define IGL_FRAME_TO_CROSS_FIELD_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Convert a frame field into its closest cross field + // Inputs: + // V #V by 3 coordinates of the vertices + // F #F by 3 list of mesh faces (must be triangles) + // FF1 #F by 3 the first representative vector of the frame field (up to permutation and sign) + // FF2 #F by 3 the second representative vector of the frame field (up to permutation and sign) + // + // Outputs: + // X #F by 3 representative vector of the closest cross field + // + IGL_INLINE void frame_to_cross_field( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::MatrixXd& FF1, + const Eigen::MatrixXd& FF2, + Eigen::MatrixXd& X); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "frame_to_cross_field.cpp" +#endif + +#endif diff --git a/src/igl/frustum.cpp b/src/igl/frustum.cpp new file mode 100644 index 0000000000..2926106e63 --- /dev/null +++ b/src/igl/frustum.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "frustum.h" +template < typename DerivedP> +IGL_INLINE void igl::frustum( + const typename DerivedP::Scalar left, + const typename DerivedP::Scalar right, + const typename DerivedP::Scalar bottom, + const typename DerivedP::Scalar top, + const typename DerivedP::Scalar nearVal, + const typename DerivedP::Scalar farVal, + Eigen::PlainObjectBase & P) +{ + P.setConstant(4,4,0.); + P(0,0) = (2.0 * nearVal) / (right - left); + P(1,1) = (2.0 * nearVal) / (top - bottom); + P(0,2) = (right + left) / (right - left); + P(1,2) = (top + bottom) / (top - bottom); + P(2,2) = -(farVal + nearVal) / (farVal - nearVal); + P(3,2) = -1.0; + P(2,3) = -(2.0 * farVal * nearVal) / (farVal - nearVal); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::frustum >(Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/frustum.h b/src/igl/frustum.h new file mode 100644 index 0000000000..4a92e1a819 --- /dev/null +++ b/src/igl/frustum.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_FRUSTUM_H +#define IGL_FRUSTUM_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Implementation of the deprecated glFrustum function. + // + // Inputs: + // left coordinate of left vertical clipping plane + // right coordinate of right vertical clipping plane + // bottom coordinate of bottom vertical clipping plane + // top coordinate of top vertical clipping plane + // nearVal distance to near plane + // farVal distance to far plane + // Outputs: + // P 4x4 perspective matrix + template < typename DerivedP> + IGL_INLINE void frustum( + const typename DerivedP::Scalar left, + const typename DerivedP::Scalar right, + const typename DerivedP::Scalar bottom, + const typename DerivedP::Scalar top, + const typename DerivedP::Scalar nearVal, + const typename DerivedP::Scalar farVal, + Eigen::PlainObjectBase & P); +} + +#ifndef IGL_STATIC_LIBRARY +# include "frustum.cpp" +#endif + +#endif + + diff --git a/src/igl/gaussian_curvature.cpp b/src/igl/gaussian_curvature.cpp new file mode 100644 index 0000000000..ca05129df3 --- /dev/null +++ b/src/igl/gaussian_curvature.cpp @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "gaussian_curvature.h" +#include "internal_angles.h" +#include "PI.h" +#include +template +IGL_INLINE void igl::gaussian_curvature( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase & K) +{ + using namespace Eigen; + using namespace std; + // internal corner angles + Matrix< + typename DerivedV::Scalar, + DerivedF::RowsAtCompileTime, + DerivedF::ColsAtCompileTime> A; + internal_angles(V,F,A); + K.resize(V.rows(),1); + K.setConstant(V.rows(),1,2.*PI); + assert(A.rows() == F.rows()); + assert(A.cols() == F.cols()); + assert(K.rows() == V.rows()); + assert(F.maxCoeff() < V.rows()); + assert(K.cols() == 1); + const int Frows = F.rows(); + //K_G(x_i) = (2Ï€ - ∑θj) +//#ifndef IGL_GAUSSIAN_CURVATURE_OMP_MIN_VALUE +//# define IGL_GAUSSIAN_CURVATURE_OMP_MIN_VALUE 1000 +//#endif +//#pragma omp parallel for if (Frows>IGL_GAUSSIAN_CURVATURE_OMP_MIN_VALUE) + for(int f = 0;f, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::gaussian_curvature, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/gaussian_curvature.h b/src/igl/gaussian_curvature.h new file mode 100644 index 0000000000..d8ddbc8ad7 --- /dev/null +++ b/src/igl/gaussian_curvature.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_GAUSSIAN_CURVATURE_H +#define IGL_GAUSSIAN_CURVATURE_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute discrete local integral gaussian curvature (angle deficit, without + // averaging by local area). + // + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (triangle) indices + // Output: + // K #V by 1 eigen Matrix of discrete gaussian curvature values + // + template + IGL_INLINE void gaussian_curvature( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase & K); +} + +#ifndef IGL_STATIC_LIBRARY +# include "gaussian_curvature.cpp" +#endif + +#endif + diff --git a/src/igl/get_seconds.cpp b/src/igl/get_seconds.cpp new file mode 100644 index 0000000000..fac4a3a691 --- /dev/null +++ b/src/igl/get_seconds.cpp @@ -0,0 +1,15 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "get_seconds.h" +#include +IGL_INLINE double igl::get_seconds() +{ + return + std::chrono::duration( + std::chrono::system_clock::now().time_since_epoch()).count(); +} diff --git a/src/igl/get_seconds.h b/src/igl/get_seconds.h new file mode 100644 index 0000000000..411d6e41de --- /dev/null +++ b/src/igl/get_seconds.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_GET_SECONDS_H +#define IGL_GET_SECONDS_H +#include "igl_inline.h" + +namespace igl +{ + // Return the current time in seconds since program start + // + // Example: + // const auto & tictoc = []() + // { + // static double t_start = igl::get_seconds(); + // double diff = igl::get_seconds()-t_start; + // t_start += diff; + // return diff; + // }; + // tictoc(); + // ... // part 1 + // cout<<"part 1: "< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "get_seconds_hires.h" + +#if _WIN32 +# include +# include +IGL_INLINE double igl::get_seconds_hires() +{ + LARGE_INTEGER li_freq, li_current; + const bool ret = QueryPerformanceFrequency(&li_freq); + const bool ret2 = QueryPerformanceCounter(&li_current); + assert(ret && ret2); + assert(li_freq.QuadPart > 0); + return double(li_current.QuadPart) / double(li_freq.QuadPart); +} +#else +# include "get_seconds.h" +IGL_INLINE double igl::get_seconds_hires() +{ + // Sorry I've no idea how performance counters work on Mac... + return igl::get_seconds(); +} +#endif diff --git a/src/igl/get_seconds_hires.h b/src/igl/get_seconds_hires.h new file mode 100644 index 0000000000..abe8956b5c --- /dev/null +++ b/src/igl/get_seconds_hires.h @@ -0,0 +1,22 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_GET_SECONDS_HIRES_H +#define IGL_GET_SECONDS_HIRES_H +#include "igl_inline.h" + +namespace igl +{ + // Return the current time in seconds using performance counters + IGL_INLINE double get_seconds_hires(); +} + +#ifndef IGL_STATIC_LIBRARY +# include "get_seconds_hires.cpp" +#endif + +#endif diff --git a/src/igl/grad.cpp b/src/igl/grad.cpp new file mode 100644 index 0000000000..cc86e9e81e --- /dev/null +++ b/src/igl/grad.cpp @@ -0,0 +1,243 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "grad.h" +#include +#include + +#include "PI.h" +#include "per_face_normals.h" +#include "volume.h" +#include "doublearea.h" + +template +IGL_INLINE void grad_tet(const Eigen::PlainObjectBase&V, + const Eigen::PlainObjectBase&T, + Eigen::SparseMatrix &G, + bool uniform) { + using namespace Eigen; + assert(T.cols() == 4); + const int n = V.rows(); int m = T.rows(); + + /* + F = [ ... + T(:,1) T(:,2) T(:,3); ... + T(:,1) T(:,3) T(:,4); ... + T(:,1) T(:,4) T(:,2); ... + T(:,2) T(:,4) T(:,3)]; */ + MatrixXi F(4*m,3); + for (int i = 0; i < m; i++) { + F.row(0*m + i) << T(i,0), T(i,1), T(i,2); + F.row(1*m + i) << T(i,0), T(i,2), T(i,3); + F.row(2*m + i) << T(i,0), T(i,3), T(i,1); + F.row(3*m + i) << T(i,1), T(i,3), T(i,2); + } + // compute volume of each tet + Eigen::Matrix vol; + igl::volume(V,T,vol); + + Eigen::Matrix A(F.rows()); + Eigen::Matrix N(F.rows(),3); + if (!uniform) { + // compute tetrahedron face normals + igl::per_face_normals(V,F,N); int norm_rows = N.rows(); + for (int i = 0; i < norm_rows; i++) + N.row(i) /= N.row(i).norm(); + igl::doublearea(V,F,A); A/=2.; + } else { + // Use a uniform tetrahedra as a reference, with the same volume as the original one: + // + // Use normals of the uniform tet (V = h*[0,0,0;1,0,0;0.5,sqrt(3)/2.,0;0.5,sqrt(3)/6.,sqrt(2)/sqrt(3)]) + // 0 0 1.0000 + // 0.8165 -0.4714 -0.3333 + // 0 0.9428 -0.3333 + // -0.8165 -0.4714 -0.3333 + for (int i = 0; i < m; i++) { + N.row(0*m+i) << 0,0,1; + double a = sqrt(2)*std::cbrt(3*vol(i)); // area of a face in a uniform tet with volume = vol(i) + A(0*m+i) = (pow(a,2)*sqrt(3))/4.; + } + for (int i = 0; i < m; i++) { + N.row(1*m+i) << 0.8165,-0.4714,-0.3333; + double a = sqrt(2)*std::cbrt(3*vol(i)); + A(1*m+i) = (pow(a,2)*sqrt(3))/4.; + } + for (int i = 0; i < m; i++) { + N.row(2*m+i) << 0,0.9428,-0.3333; + double a = sqrt(2)*std::cbrt(3*vol(i)); + A(2*m+i) = (pow(a,2)*sqrt(3))/4.; + } + for (int i = 0; i < m; i++) { + N.row(3*m+i) << -0.8165,-0.4714,-0.3333; + double a = sqrt(2)*std::cbrt(3*vol(i)); + A(3*m+i) = (pow(a,2)*sqrt(3))/4.; + } + + } + + /* G = sparse( ... + [0*m + repmat(1:m,1,4) ... + 1*m + repmat(1:m,1,4) ... + 2*m + repmat(1:m,1,4)], ... + repmat([T(:,4);T(:,2);T(:,3);T(:,1)],3,1), ... + repmat(A./(3*repmat(vol,4,1)),3,1).*N(:), ... + 3*m,n);*/ + std::vector > G_t; + for (int i = 0; i < 4*m; i++) { + int T_j; // j indexes : repmat([T(:,4);T(:,2);T(:,3);T(:,1)],3,1) + switch (i/m) { + case 0: + T_j = 3; + break; + case 1: + T_j = 1; + break; + case 2: + T_j = 2; + break; + case 3: + T_j = 0; + break; + } + int i_idx = i%m; + int j_idx = T(i_idx,T_j); + + double val_before_n = A(i)/(3*vol(i_idx)); + G_t.push_back(Triplet(0*m+i_idx, j_idx, val_before_n * N(i,0))); + G_t.push_back(Triplet(1*m+i_idx, j_idx, val_before_n * N(i,1))); + G_t.push_back(Triplet(2*m+i_idx, j_idx, val_before_n * N(i,2))); + } + G.resize(3*m,n); + G.setFromTriplets(G_t.begin(), G_t.end()); +} + +template +IGL_INLINE void grad_tri(const Eigen::PlainObjectBase&V, + const Eigen::PlainObjectBase&F, + Eigen::SparseMatrix &G, + bool uniform) +{ + Eigen::Matrix + eperp21(F.rows(),3), eperp13(F.rows(),3); + + for (int i=0;i v32 = V.row(i3) - V.row(i2); + Eigen::Matrix v13 = V.row(i1) - V.row(i3); + Eigen::Matrix v21 = V.row(i2) - V.row(i1); + Eigen::Matrix n = v32.cross(v13); + // area of parallelogram is twice area of triangle + // area of parallelogram is || v1 x v2 || + // This does correct l2 norm of rows, so that it contains #F list of twice + // triangle areas + double dblA = std::sqrt(n.dot(n)); + Eigen::Matrix u(0,0,1); + if (!uniform) { + // now normalize normals to get unit normals + u = n / dblA; + } else { + // Abstract equilateral triangle v1=(0,0), v2=(h,0), v3=(h/2, (sqrt(3)/2)*h) + + // get h (by the area of the triangle) + double h = sqrt( (dblA)/sin(igl::PI / 3.0)); // (h^2*sin(60))/2. = Area => h = sqrt(2*Area/sin_60) + + Eigen::Matrix v1,v2,v3; + v1 << 0,0,0; + v2 << h,0,0; + v3 << h/2.,(sqrt(3)/2.)*h,0; + + // now fix v32,v13,v21 and the normal + v32 = v3-v2; + v13 = v1-v3; + v21 = v2-v1; + n = v32.cross(v13); + } + + // rotate each vector 90 degrees around normal + double norm21 = std::sqrt(v21.dot(v21)); + double norm13 = std::sqrt(v13.dot(v13)); + eperp21.row(i) = u.cross(v21); + eperp21.row(i) = eperp21.row(i) / std::sqrt(eperp21.row(i).dot(eperp21.row(i))); + eperp21.row(i) *= norm21 / dblA; + eperp13.row(i) = u.cross(v13); + eperp13.row(i) = eperp13.row(i) / std::sqrt(eperp13.row(i).dot(eperp13.row(i))); + eperp13.row(i) *= norm13 / dblA; + } + + std::vector rs; + rs.reserve(F.rows()*4*3); + std::vector cs; + cs.reserve(F.rows()*4*3); + std::vector vs; + vs.reserve(F.rows()*4*3); + + // row indices + for(int r=0;r<3;r++) + { + for(int j=0;j<4;j++) + { + for(int i=r*F.rows();i<(r+1)*F.rows();i++) rs.push_back(i); + } + } + + // column indices + for(int r=0;r<3;r++) + { + for(int i=0;i > triplets; + for (int i=0;i<(int)vs.size();++i) + { + triplets.push_back(Eigen::Triplet(rs[i],cs[i],vs[i])); + } + G.setFromTriplets(triplets.begin(), triplets.end()); +} + +template +IGL_INLINE void igl::grad(const Eigen::PlainObjectBase&V, + const Eigen::PlainObjectBase&F, + Eigen::SparseMatrix &G, + bool uniform) +{ + assert(F.cols() == 3 || F.cols() == 4); + if (F.cols() == 3) + return grad_tri(V,F,G,uniform); + if (F.cols() == 4) + return grad_tet(V,F,G,uniform); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::grad, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::SparseMatrix::Scalar, 0, int>&, bool); +template void igl::grad, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::SparseMatrix::Scalar, 0, int>&, bool); +#endif diff --git a/src/igl/grad.h b/src/igl/grad.h new file mode 100644 index 0000000000..df375ba614 --- /dev/null +++ b/src/igl/grad.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_GRAD_MAT_H +#define IGL_GRAD_MAT_H +#include "igl_inline.h" + +#include +#include + +namespace igl { + // GRAD + // G = grad(V,F) + // + // Compute the numerical gradient operator + // + // Inputs: + // V #vertices by 3 list of mesh vertex positions + // F #faces by 3 list of mesh face indices [or a #faces by 4 list of tetrahedral indices] + // uniform boolean (default false) - Use a uniform mesh instead of the vertices V + // Outputs: + // G #faces*dim by #V Gradient operator + // + + // Gradient of a scalar function defined on piecewise linear elements (mesh) + // is constant on each triangle [tetrahedron] i,j,k: + // grad(Xijk) = (Xj-Xi) * (Vi - Vk)^R90 / 2A + (Xk-Xi) * (Vj - Vi)^R90 / 2A + // where Xi is the scalar value at vertex i, Vi is the 3D position of vertex + // i, and A is the area of triangle (i,j,k). ^R90 represent a rotation of + // 90 degrees + // +template +IGL_INLINE void grad(const Eigen::PlainObjectBase&V, + const Eigen::PlainObjectBase&F, + Eigen::SparseMatrix &G, + bool uniform = false); +} +#ifndef IGL_STATIC_LIBRARY +# include "grad.cpp" +#endif + +#endif diff --git a/src/igl/grid.cpp b/src/igl/grid.cpp new file mode 100644 index 0000000000..f572a05715 --- /dev/null +++ b/src/igl/grid.cpp @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "grid.h" + +template < + typename Derivedres, + typename DerivedGV> +IGL_INLINE void igl::grid( + const Eigen::MatrixBase & res, + Eigen::PlainObjectBase & GV) +{ + using namespace Eigen; + typedef typename DerivedGV::Scalar Scalar; + GV.resize(res(0)*res(1)*res(2),3); + for(int zi = 0;ziScalar{return di/(Scalar)(res(d)-1);}; + const Scalar z = lerp(zi,2); + for(int yi = 0;yi(x,y,z); + } + } + } +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::grid, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::grid, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::grid, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::grid, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::grid, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/grid.h b/src/igl/grid.h new file mode 100644 index 0000000000..27894f6281 --- /dev/null +++ b/src/igl/grid.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_GRID_H +#define IGL_GRID_H +#include "igl_inline.h" +#include +namespace igl +{ + // Construct vertices of a regular grid, suitable for input to + // `igl::marching_cubes` + // + // Inputs: + // res 3-long list of number of vertices along the x y and z dimensions + // Outputs: + // GV res(0)*res(1)*res(2) by 3 list of mesh vertex positions. + // + template < + typename Derivedres, + typename DerivedGV> + IGL_INLINE void grid( + const Eigen::MatrixBase & res, + Eigen::PlainObjectBase & GV); +} +#ifndef IGL_STATIC_LIBRARY +# include "grid.cpp" +#endif +#endif diff --git a/src/igl/grid_search.cpp b/src/igl/grid_search.cpp new file mode 100644 index 0000000000..93cbc0ddea --- /dev/null +++ b/src/igl/grid_search.cpp @@ -0,0 +1,64 @@ +#include "grid_search.h" +#include +#include + +template < + typename Scalar, + typename DerivedX, + typename DerivedLB, + typename DerivedUB, + typename DerivedI> +IGL_INLINE Scalar igl::grid_search( + const std::function< Scalar (DerivedX &) > f, + const Eigen::MatrixBase & LB, + const Eigen::MatrixBase & UB, + const Eigen::MatrixBase & I, + DerivedX & X) +{ + Scalar fval = std::numeric_limits::max(); + const int dim = LB.size(); + assert(UB.size() == dim && "UB should match LB size"); + assert(I.size() == dim && "I should match LB size"); + X.resize(dim); + + // Working X value + DerivedX Xrun(dim); + std::function looper; + int calls = 0; + looper = [&]( + const int d, + DerivedX & Xrun) + { + assert(d < dim); + Eigen::Matrix vals = + Eigen::Matrix::LinSpaced(I(d),LB(d),UB(d)); + for(int c = 0;c, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::function&)>, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix&); +template float igl::grid_search, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::function&)>, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix&); +#endif diff --git a/src/igl/grid_search.h b/src/igl/grid_search.h new file mode 100644 index 0000000000..83a86663e8 --- /dev/null +++ b/src/igl/grid_search.h @@ -0,0 +1,42 @@ +#ifndef IGL_GRID_SEARCH_H +#define IGL_GRID_SEARCH_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Solve the problem: + // + // minimize f(x) + // subject to lb ≤ x ≤ ub + // + // by exhaustive grid search. + // + // Inputs: + // f function to minimize + // LB #X vector of finite lower bounds + // UB #X vector of finite upper bounds + // I #X vector of number of steps for each variable + // Outputs: + // X #X optimal parameter vector + // Returns f(X) + // + template < + typename Scalar, + typename DerivedX, + typename DerivedLB, + typename DerivedUB, + typename DerivedI> + IGL_INLINE Scalar grid_search( + const std::function< Scalar (DerivedX &) > f, + const Eigen::MatrixBase & LB, + const Eigen::MatrixBase & UB, + const Eigen::MatrixBase & I, + DerivedX & X); +} + +#ifndef IGL_STATIC_LIBRARY +# include "grid_search.cpp" +#endif + +#endif diff --git a/src/igl/group_sum_matrix.cpp b/src/igl/group_sum_matrix.cpp new file mode 100644 index 0000000000..d3cf0bff27 --- /dev/null +++ b/src/igl/group_sum_matrix.cpp @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "group_sum_matrix.h" + +template +IGL_INLINE void igl::group_sum_matrix( + const Eigen::Matrix & G, + const int k, + Eigen::SparseMatrix& A) +{ + // number of vertices + int n = G.rows(); + assert(k > G.maxCoeff()); + + A.resize(k,n); + + // builds A such that A(i,j) = 1 where i corresponds to group i and j + // corresponds to vertex j + + // Loop over vertices + for(int j = 0;j +IGL_INLINE void igl::group_sum_matrix( + const Eigen::Matrix & G, + Eigen::SparseMatrix& A) +{ + return group_sum_matrix(G,G.maxCoeff()+1,A); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::group_sum_matrix(Eigen::Matrix const&, int, Eigen::SparseMatrix&); +template void igl::group_sum_matrix(Eigen::Matrix const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/group_sum_matrix.h b/src/igl/group_sum_matrix.h new file mode 100644 index 0000000000..805544b0bc --- /dev/null +++ b/src/igl/group_sum_matrix.h @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_GROUP_SUM_MATRIX_H +#define IGL_GROUP_SUM_MATRIX_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // GROUP_SUM_MATRIX Builds a matrix A such that A*V computes the sum of + // vertices in each group specified by G + // + // group_sum_matrix(G,k,A); + // + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Inputs: + // G #V list of group indices (0 to k-1) for each vertex, such that vertex i + // is assigned to group G(i) + // k #groups, good choice is max(G)+1 + // Outputs: + // A #groups by #V sparse matrix such that A*V = group_sums + // + template + IGL_INLINE void group_sum_matrix( + const Eigen::Matrix & G, + const int k, + Eigen::SparseMatrix& A); + // Wrapper with k = max(G)+1 + template + IGL_INLINE void group_sum_matrix( + const Eigen::Matrix & G, + Eigen::SparseMatrix& A); +} +#ifndef IGL_STATIC_LIBRARY +# include "group_sum_matrix.cpp" +#endif +#endif diff --git a/src/igl/guess_extension.cpp b/src/igl/guess_extension.cpp new file mode 100644 index 0000000000..8e8e449a1c --- /dev/null +++ b/src/igl/guess_extension.cpp @@ -0,0 +1,106 @@ +#include "guess_extension.h" + +#include + +#include "is_stl.h" +#include "ply.h" + +IGL_INLINE void igl::guess_extension(FILE * fp, std::string & guess) +{ + const auto is_off = [](FILE * fp)-> bool + { + char header[1000]; + const std::string OFF("OFF"); + const std::string NOFF("NOFF"); + const std::string COFF("COFF"); + bool f = (fscanf(fp,"%s\n",header)==1 && ( + std::string(header).compare(0, OFF.length(), OFF)==0 || + std::string(header).compare(0, COFF.length(), COFF)==0 || + std::string(header).compare(0,NOFF.length(),NOFF)==0)); + rewind(fp); + return f; + }; + const auto is_ply = [](FILE * ply_file) -> bool + { + int nelems; + char ** elem_names; + igl::ply::PlyFile * in_ply = igl::ply::ply_read(ply_file,&nelems,&elem_names); + if(in_ply==NULL) + { + return false; + } + free(in_ply); + rewind(ply_file); + return true; + }; + const auto is_wrl = [](FILE * wrl_file)->bool + { + bool still_comments = true; + char line[1000]; + std::string needle("point ["); + std::string haystack; + while(still_comments) + { + if(fgets(line,1000,wrl_file) == NULL) + { + rewind(wrl_file); + return false; + } + haystack = std::string(line); + still_comments = std::string::npos == haystack.find(needle); + } + rewind(wrl_file); + return true; + }; + const auto is_mesh = [](FILE * mesh_file )->bool + { + char line[2048]; + // eat comments at beginning of file + bool still_comments= true; + while(still_comments) + { + if(fgets(line,2048,mesh_file) == NULL) + { + rewind(mesh_file); + return false; + } + still_comments = (line[0] == '#' || line[0] == '\n'); + } + char str[2048]; + sscanf(line," %s",str); + // check that first word is MeshVersionFormatted + if(0!=strcmp(str,"MeshVersionFormatted")) + { + rewind(mesh_file); + return false; + } + rewind(mesh_file); + return true; + }; + guess = "obj"; + if(is_mesh(fp)) + { + guess = "mesh"; + }else if(is_off(fp)) + { + guess = "off"; + }else if(is_ply(fp)) + { + guess = "ply"; + }else if(igl::is_stl(fp)) + { + guess = "stl"; + }else if(is_wrl(fp)) + { + guess = "wrl"; + } + // else obj + rewind(fp); +} + +IGL_INLINE std::string igl::guess_extension(FILE * fp) +{ + std::string guess; + guess_extension(fp,guess); + return guess; +} diff --git a/src/igl/guess_extension.h b/src/igl/guess_extension.h new file mode 100644 index 0000000000..77df6f523b --- /dev/null +++ b/src/igl/guess_extension.h @@ -0,0 +1,25 @@ +#ifndef IGL_GUESS_EXTENSION_H +#define IGL_GUESS_EXTENSION_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Given a file pointer at the beginning of a "mesh" file, try to guess the + // extension of the file format it comes from. The file pointer is rewound on + // return. + // + // Inputs: + // fp file pointer (see output) + // Outputs: + // fp file pointer rewound + // guess extension as string. One of "mesh",{"obj"},"off","ply","stl", or + // "wrl" + // + IGL_INLINE void guess_extension(FILE * fp, std::string & guess); + IGL_INLINE std::string guess_extension(FILE * fp); +} +#ifndef IGL_STATIC_LIBRARY +# include "guess_extension.cpp" +#endif +#endif diff --git a/src/igl/harmonic.cpp b/src/igl/harmonic.cpp new file mode 100644 index 0000000000..aa07245a1c --- /dev/null +++ b/src/igl/harmonic.cpp @@ -0,0 +1,175 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "harmonic.h" +#include "adjacency_matrix.h" +#include "cotmatrix.h" +#include "diag.h" +#include "invert_diag.h" +#include "isdiag.h" +#include "massmatrix.h" +#include "min_quad_with_fixed.h" +#include "speye.h" +#include "sum.h" +#include + +template < + typename DerivedV, + typename DerivedF, + typename Derivedb, + typename Derivedbc, + typename DerivedW> +IGL_INLINE bool igl::harmonic( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & b, + const Eigen::MatrixBase & bc, + const int k, + Eigen::PlainObjectBase & W) +{ + using namespace Eigen; + typedef typename DerivedV::Scalar Scalar; + SparseMatrix L,M; + cotmatrix(V,F,L); + if(k>1) + { + massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,M); + } + return harmonic(L,M,b,bc,k,W); +} + +template < + typename DerivedF, + typename Derivedb, + typename Derivedbc, + typename DerivedW> +IGL_INLINE bool igl::harmonic( + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & b, + const Eigen::MatrixBase & bc, + const int k, + Eigen::PlainObjectBase & W) +{ + using namespace Eigen; + typedef typename Derivedbc::Scalar Scalar; + SparseMatrix A; + adjacency_matrix(F,A); + // sum each row + SparseVector Asum; + sum(A,1,Asum); + // Convert row sums into diagonal of sparse matrix + SparseMatrix Adiag; + diag(Asum,Adiag); + SparseMatrix L = A-Adiag; + SparseMatrix M; + speye(L.rows(),M); + return harmonic(L,M,b,bc,k,W); +} + +template < + typename DerivedL, + typename DerivedM, + typename Derivedb, + typename Derivedbc, + typename DerivedW> +IGL_INLINE bool igl::harmonic( + const Eigen::SparseMatrix & L, + const Eigen::SparseMatrix & M, + const Eigen::MatrixBase & b, + const Eigen::MatrixBase & bc, + const int k, + Eigen::PlainObjectBase & W) +{ + const int n = L.rows(); + assert(n == L.cols() && "L must be square"); + assert((k==1 || n == M.cols() ) && "M must be same size as L"); + assert((k==1 || n == M.rows() ) && "M must be square"); + assert((k==1 || igl::isdiag(M)) && "Mass matrix should be diagonal"); + + Eigen::SparseMatrix Q; + igl::harmonic(L,M,k,Q); + + typedef DerivedL Scalar; + min_quad_with_fixed_data data; + min_quad_with_fixed_precompute(Q,b,Eigen::SparseMatrix(),true,data); + W.resize(n,bc.cols()); + typedef Eigen::Matrix VectorXS; + const VectorXS B = VectorXS::Zero(n,1); + for(int w = 0;w +IGL_INLINE void igl::harmonic( + const Eigen::SparseMatrix & L, + const Eigen::SparseMatrix & M, + const int k, + Eigen::SparseMatrix & Q) +{ + assert(L.rows() == L.cols()&&"L should be square"); + Q = -L; + if(k == 1) return; + assert(L.rows() == M.rows()&&"L should match M's dimensions"); + assert(M.rows() == M.cols()&&"M should be square"); + Eigen::SparseMatrix Mi; + invert_diag(M,Mi); + // This is **not** robust for k>2. See KKT system in [Jacobson et al. 2010] + // of the kharmonic function in gptoolbox + for(int p = 1;p +IGL_INLINE void igl::harmonic( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const int k, + Eigen::SparseMatrix & Q) +{ + Eigen::SparseMatrix L,M; + cotmatrix(V,F,L); + if(k>1) + { + massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,M); + } + return harmonic(L,M,k,Q); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::harmonic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::harmonic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::harmonic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::harmonic, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::SparseMatrix&); +// generated by autoexplicit.sh +template bool igl::harmonic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::harmonic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +template bool igl::harmonic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/harmonic.h b/src/igl/harmonic.h new file mode 100644 index 0000000000..1a48ecfb5e --- /dev/null +++ b/src/igl/harmonic.h @@ -0,0 +1,122 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_HARMONIC_H +#define IGL_HARMONIC_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Compute k-harmonic weight functions "coordinates". + // + // + // Inputs: + // V #V by dim vertex positions + // F #F by simplex-size list of element indices + // b #b boundary indices into V + // bc #b by #W list of boundary values + // k power of harmonic operation (1: harmonic, 2: biharmonic, etc) + // Outputs: + // W #V by #W list of weights + // + template < + typename DerivedV, + typename DerivedF, + typename Derivedb, + typename Derivedbc, + typename DerivedW> + IGL_INLINE bool harmonic( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & b, + const Eigen::MatrixBase & bc, + const int k, + Eigen::PlainObjectBase & W); + // Compute harmonic map using uniform laplacian operator + // + // Inputs: + // F #F by simplex-size list of element indices + // b #b boundary indices into V + // bc #b by #W list of boundary values + // k power of harmonic operation (1: harmonic, 2: biharmonic, etc) + // Outputs: + // W #V by #W list of weights + // + template < + typename DerivedF, + typename Derivedb, + typename Derivedbc, + typename DerivedW> + IGL_INLINE bool harmonic( + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & b, + const Eigen::MatrixBase & bc, + const int k, + Eigen::PlainObjectBase & W); + // Compute a harmonic map using a given Laplacian and mass matrix + // + // Inputs: + // L #V by #V discrete (integrated) Laplacian + // M #V by #V mass matrix + // b #b boundary indices into V + // bc #b by #W list of boundary values + // k power of harmonic operation (1: harmonic, 2: biharmonic, etc) + // Outputs: + // W #V by #V list of weights + template < + typename DerivedL, + typename DerivedM, + typename Derivedb, + typename Derivedbc, + typename DerivedW> + IGL_INLINE bool harmonic( + const Eigen::SparseMatrix & L, + const Eigen::SparseMatrix & M, + const Eigen::MatrixBase & b, + const Eigen::MatrixBase & bc, + const int k, + Eigen::PlainObjectBase & W); + // Build the discrete k-harmonic operator (computing integrated quantities). + // That is, if the k-harmonic PDE is Q x = 0, then this minimizes x' Q x + // + // Inputs: + // L #V by #V discrete (integrated) Laplacian + // M #V by #V mass matrix + // k power of harmonic operation (1: harmonic, 2: biharmonic, etc) + // Outputs: + // Q #V by #V discrete (integrated) k-Laplacian + template < + typename DerivedL, + typename DerivedM, + typename DerivedQ> + IGL_INLINE void harmonic( + const Eigen::SparseMatrix & L, + const Eigen::SparseMatrix & M, + const int k, + Eigen::SparseMatrix & Q); + // Inputs: + // V #V by dim vertex positions + // F #F by simplex-size list of element indices + // k power of harmonic operation (1: harmonic, 2: biharmonic, etc) + // Outputs: + // Q #V by #V discrete (integrated) k-Laplacian + template < + typename DerivedV, + typename DerivedF, + typename DerivedQ> + IGL_INLINE void harmonic( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const int k, + Eigen::SparseMatrix & Q); +}; + +#ifndef IGL_STATIC_LIBRARY +#include "harmonic.cpp" +#endif +#endif diff --git a/src/igl/harwell_boeing.cpp b/src/igl/harwell_boeing.cpp new file mode 100644 index 0000000000..895416510c --- /dev/null +++ b/src/igl/harwell_boeing.cpp @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "harwell_boeing.h" + +template +IGL_INLINE void igl::harwell_boeing( + const Eigen::SparseMatrix & A, + int & num_rows, + std::vector & V, + std::vector & R, + std::vector & C) +{ + num_rows = A.rows(); + int num_cols = A.cols(); + int nnz = A.nonZeros(); + V.resize(nnz); + R.resize(nnz); + C.resize(num_cols+1); + + // Assumes outersize is columns + assert(A.cols() == A.outerSize()); + int column_pointer = 0; + int i = 0; + int k = 0; + // Iterate over outside + for(; k::InnerIterator it (A,k); it; ++it) + { + V[i] = it.value(); + R[i] = it.row(); + i++; + // Also increment column pointer + column_pointer++; + } + } + // by convention C[num_cols] = nnz + C[k] = column_pointer; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::harwell_boeing(Eigen::SparseMatrix const&, int&, std::vector >&, std::vector >&, std::vector >&); +#endif diff --git a/src/igl/harwell_boeing.h b/src/igl/harwell_boeing.h new file mode 100644 index 0000000000..df166ab829 --- /dev/null +++ b/src/igl/harwell_boeing.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_HARWELL_BOEING_H +#define IGL_HARWELL_BOEING_H +#include "igl_inline.h" + +#include +#include + + +namespace igl +{ + // Convert the matrix to Compressed sparse column (CSC or CCS) format, + // also known as Harwell Boeing format. As described: + // http://netlib.org/linalg/html_templates/node92.html + // or + // http://en.wikipedia.org/wiki/Sparse_matrix + // #Compressed_sparse_column_.28CSC_or_CCS.29 + // Templates: + // Scalar type of sparse matrix like double + // Inputs: + // A sparse m by n matrix + // Outputs: + // num_rows number of rows + // V non-zero values, row indices running fastest, size(V) = nnz + // R row indices corresponding to vals, size(R) = nnz + // C index in vals of first entry in each column, size(C) = num_cols+1 + // + // All indices and pointers are 0-based + template + IGL_INLINE void harwell_boeing( + const Eigen::SparseMatrix & A, + int & num_rows, + std::vector & V, + std::vector & R, + std::vector & C); +} + +#ifndef IGL_STATIC_LIBRARY +# include "harwell_boeing.cpp" +#endif + +#endif diff --git a/src/igl/hausdorff.cpp b/src/igl/hausdorff.cpp new file mode 100644 index 0000000000..9b1ad47433 --- /dev/null +++ b/src/igl/hausdorff.cpp @@ -0,0 +1,89 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "hausdorff.h" +#include "point_mesh_squared_distance.h" + +template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename Scalar> +IGL_INLINE void igl::hausdorff( + const Eigen::PlainObjectBase & VA, + const Eigen::PlainObjectBase & FA, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + Scalar & d) +{ + using namespace Eigen; + assert(VA.cols() == 3 && "VA should contain 3d points"); + assert(FA.cols() == 3 && "FA should contain triangles"); + assert(VB.cols() == 3 && "VB should contain 3d points"); + assert(FB.cols() == 3 && "FB should contain triangles"); + Matrix sqr_DBA,sqr_DAB; + Matrix I; + Matrix C; + point_mesh_squared_distance(VB,VA,FA,sqr_DBA,I,C); + point_mesh_squared_distance(VA,VB,FB,sqr_DAB,I,C); + const Scalar dba = sqr_DBA.maxCoeff(); + const Scalar dab = sqr_DAB.maxCoeff(); + d = sqrt(std::max(dba,dab)); +} + +template < + typename DerivedV, + typename Scalar> +IGL_INLINE void igl::hausdorff( + const Eigen::MatrixBase& V, + const std::function & dist_to_B, + Scalar & l, + Scalar & u) +{ + // e 3-long vector of opposite edge lengths + Eigen::Matrix e; + // Maximum edge length + Scalar e_max = 0; + for(int i=0;i<3;i++) + { + e(i) = (V.row((i+1)%3)-V.row((i+2)%3)).norm(); + e_max = std::max(e_max,e(i)); + } + // Semiperimeter + const Scalar s = (e(0)+e(1)+e(2))*0.5; + // Area + const Scalar A = sqrt(s*(s-e(0))*(s-e(1))*(s-e(2))); + // Circumradius + const Scalar R = e(0)*e(1)*e(2)/(4.*A); + // inradius + const Scalar r = A/s; + // Initialize lower bound to ∞ + l = std::numeric_limits::infinity(); + // d 3-long vector of distance from each corner to B + Eigen::Matrix d; + Scalar u1 = std::numeric_limits::infinity(); + Scalar u2 = 0; + for(int i=0;i<3;i++) + { + d(i) = dist_to_B(V(i,0),V(i,1),V(i,2)); + // Lower bound is simply the max over vertex distances + l = std::max(d(i),l); + // u1 is the minimum of corner distances + maximum adjacent edge + u1 = std::min(u1,d(i) + std::max(e((i+1)%3),e((i+2)%3))); + // u2 first takes the maximum over corner distances + u2 = std::max(u2,d(i)); + } + // u2 is the distance from the circumcenter/midpoint of obtuse edge plus the + // largest corner distance + u2 += (s-r>2.*R ? R : 0.5*e_max); + u = std::min(u1,u2); +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::hausdorff, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, double&); +#endif diff --git a/src/igl/hausdorff.h b/src/igl/hausdorff.h new file mode 100644 index 0000000000..92e22b333f --- /dev/null +++ b/src/igl/hausdorff.h @@ -0,0 +1,83 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_HAUSDORFF_H +#define IGL_HAUSDORFF_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // HAUSDORFF compute the Hausdorff distance between mesh (VA,FA) and mesh + // (VB,FB). This is the + // + // d(A,B) = max ( max min d(a,b) , max min d(b,a) ) + // a∈A b∈B b∈B a∈A + // + // Known issue: This is only computing max(min(va,B),min(vb,A)). This is + // better than max(min(va,Vb),min(vb,Va)). This (at least) is missing + // "edge-edge" cases like the distance between the two different + // triangulations of a non-planar quad in 3D. Even simpler, consider the + // Hausdorff distance between the non-convex, block letter V polygon (with 7 + // vertices) in 2D and its convex hull. The Hausdorff distance is defined by + // the midpoint in the middle of the segment across the concavity and some + // non-vertex point _on the edge_ of the V. + // + // Inputs: + // VA #VA by 3 list of vertex positions + // FA #FA by 3 list of face indices into VA + // VB #VB by 3 list of vertex positions + // FB #FB by 3 list of face indices into VB + // Outputs: + // d hausdorff distance + // //pair 2 by 3 list of "determiner points" so that pair(1,:) is from A + // // and pair(2,:) is from B + // + template < + typename DerivedVA, + typename DerivedFA, + typename DerivedVB, + typename DerivedFB, + typename Scalar> + IGL_INLINE void hausdorff( + const Eigen::PlainObjectBase & VA, + const Eigen::PlainObjectBase & FA, + const Eigen::PlainObjectBase & VB, + const Eigen::PlainObjectBase & FB, + Scalar & d); + // Compute lower and upper bounds (l,u) on the Hausdorff distance between a triangle + // (V) and a pointset (e.g., mesh, triangle soup) given by a distance function + // handle (dist_to_B). + // + // Inputs: + // V 3 by 3 list of corner positions so that V.row(i) is the position of the + // ith corner + // dist_to_B function taking the x,y,z coordinate of a query position and + // outputting the closest-point distance to some point-set B + // Outputs: + // l lower bound on Hausdorff distance + // u upper bound on Hausdorff distance + // + template < + typename DerivedV, + typename Scalar> + IGL_INLINE void hausdorff( + const Eigen::MatrixBase& V, + const std::function< + Scalar(const Scalar &,const Scalar &, const Scalar &)> & dist_to_B, + Scalar & l, + Scalar & u); +} + +#ifndef IGL_STATIC_LIBRARY +# include "hausdorff.cpp" +#endif + +#endif + diff --git a/src/igl/hessian.cpp b/src/igl/hessian.cpp new file mode 100644 index 0000000000..ebf81cd7d9 --- /dev/null +++ b/src/igl/hessian.cpp @@ -0,0 +1,60 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// and Oded Stein +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "hessian.h" +#include + +#include "grad.h" +#include "igl/doublearea.h" +#include "igl/repdiag.h" + + + +template +IGL_INLINE void igl::hessian( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::SparseMatrix& H) +{ + typedef typename DerivedV::Scalar denseScalar; + typedef typename Eigen::Matrix VecXd; + typedef typename Eigen::SparseMatrix SparseMat; + typedef typename Eigen::DiagonalMatrix + DiagMat; + + int dim = V.cols(); + assert((dim==2 || dim==3) && + "The dimension of the vertices should be 2 or 3"); + + //Construct the combined gradient matric + SparseMat G; + igl::grad(DerivedV(V), + DerivedF(F), + G, false); + SparseMat GG(F.rows(), dim*V.rows()); + GG.reserve(G.nonZeros()); + for(int i=0; i, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/hessian.h b/src/igl/hessian.h new file mode 100644 index 0000000000..360750d2cb --- /dev/null +++ b/src/igl/hessian.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// and Oded Stein +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_FEM_HESSIAN_H +#define IGL_FEM_HESSIAN_H +#include "igl_inline.h" + +#include +#include + + +namespace igl +{ + // Constructs the finite element Hessian matrix + // as described in https://arxiv.org/abs/1707.04348, + // Natural Boundary Conditions for Smoothing in Geometry Processing + // (Oded Stein, Eitan Grinspun, Max Wardetzky, Alec Jacobson) + // The interior vertices are NOT set to zero yet. + // + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by 3 list of mesh faces (must be triangles) + // Outputs: + // H #V by #V Hessian energy matrix, each column i + // corresponding to V(i,:) + // + // + // + template + IGL_INLINE void hessian( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::SparseMatrix& H); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "hessian.cpp" +#endif + +#endif diff --git a/src/igl/hessian_energy.cpp b/src/igl/hessian_energy.cpp new file mode 100644 index 0000000000..fa0b10a7d7 --- /dev/null +++ b/src/igl/hessian_energy.cpp @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// and Oded Stein +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "hessian_energy.h" +#include + +#include "hessian.h" +#include "massmatrix.h" +#include "boundary_loop.h" + + +template +IGL_INLINE void igl::hessian_energy( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::SparseMatrix& Q) +{ + typedef typename DerivedV::Scalar denseScalar; + typedef typename Eigen::Matrix VecXd; + typedef typename Eigen::SparseMatrix SparseMat; + typedef typename Eigen::DiagonalMatrix + DiagMat; + + int dim = V.cols(); + assert((dim==2 || dim==3) && + "The dimension of the vertices should be 2 or 3"); + + SparseMat M; + igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_VORONOI,M); + + //Kill non-interior DOFs + VecXd Mint = M.diagonal(); + std::vector > bdryLoop; + igl::boundary_loop(DerivedF(F),bdryLoop); + for(const std::vector& loop : bdryLoop) + for(const int& bdryVert : loop) + Mint(bdryVert) = 0.; + + //Invert Mint + for(int i=0; i 0) + Mint(i) = 1./Mint(i); + + //Repeat Mint to form diaginal matrix + DiagMat stackedMinv = Mint.replicate(dim*dim,1).asDiagonal(); + + //Compute squared Hessian + SparseMat H; + igl::hessian(V,F,H); + Q = H.transpose()*stackedMinv*H; + +} + + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::hessian_energy, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/hessian_energy.h b/src/igl/hessian_energy.h new file mode 100644 index 0000000000..e53373580f --- /dev/null +++ b/src/igl/hessian_energy.h @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// and Oded Stein +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_HESSIAN_ENERGY_H +#define IGL_HESSIAN_ENERGY_H +#include "igl_inline.h" + +#include +#include + + +namespace igl +{ + // Constructs the Hessian energy matrix using mixed FEM + // as described in https://arxiv.org/abs/1707.04348 + // Natural Boundary Conditions for Smoothing in Geometry Processing + // (Oded Stein, Eitan Grinspun, Max Wardetzky, Alec Jacobson) + // + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by 3 list of mesh faces (must be triangles) + // Outputs: + // Q #V by #V Hessian energy matrix, each row/column i + // corresponding to V(i,:) + // + // + // + template + IGL_INLINE void hessian_energy( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + Eigen::SparseMatrix& Q); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "hessian_energy.cpp" +#endif + +#endif diff --git a/src/igl/histc.cpp b/src/igl/histc.cpp new file mode 100644 index 0000000000..3f967b3bee --- /dev/null +++ b/src/igl/histc.cpp @@ -0,0 +1,113 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "histc.h" +#include +#include + +template +IGL_INLINE void igl::histc( + const Eigen::MatrixBase & X, + const Eigen::MatrixBase & E, + Eigen::PlainObjectBase & N, + Eigen::PlainObjectBase & B) +{ + histc(X,E,B); + const int n = E.size(); + const int m = X.size(); + assert(m == B.size()); + N.resize(n,1); + N.setConstant(0); +#pragma omp parallel for + for(int j = 0;j= 0) + { +#pragma omp atomic + N(B(j))++; + } + } +} + +template +IGL_INLINE void igl::histc( + const Eigen::MatrixBase & X, + const Eigen::MatrixBase & E, + Eigen::PlainObjectBase & B) +{ + const int m = X.size(); + using namespace std; + assert( + (E.bottomRightCorner(E.size()-1,1) - + E.topLeftCorner(E.size()-1,1)).maxCoeff() >= 0 && + "E should be monotonically increasing"); + B.resize(m,1); +#pragma omp parallel for + for(int j = 0;j E(E.size()-1)) + { + B(j) = -1; + continue; + } + // Find x in E + int l = 0; + int h = E.size()-1; + int k = l; + while((h-l)>1) + { + assert(x >= E(l)); + assert(x <= E(h)); + k = (h+l)/2; + if(x < E(k)) + { + h = k; + }else + { + l = k; + } + } + if(x == E(h)) + { + k = h; + }else + { + k = l; + } + B(j) = k; + } +} + +template +IGL_INLINE void igl::histc( + const typename DerivedE::Scalar & x, + const Eigen::MatrixBase & E, + typename DerivedE::Index & b) +{ + Eigen::Matrix X; + X(0) = x; + Eigen::Matrix B; + hist(X,E,B); + b = B(0); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::histc, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::histc, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::histc, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::histc, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::histc, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#if EIGEN_VERSION_AT_LEAST(3,3,0) +#else +template void igl::histc, Eigen::Matrix >, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase, Eigen::Matrix > > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif + +#endif diff --git a/src/igl/histc.h b/src/igl/histc.h new file mode 100644 index 0000000000..2f0ed9135d --- /dev/null +++ b/src/igl/histc.h @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_HISTC_H +#define IGL_HISTC_H + +#include "igl_inline.h" +#include + +namespace igl +{ + // Like matlab's histc. Count occurrences of values in X between consecutive + // entries in E + // + // Inputs: + // X m-long Vector of values + // E n-long Monotonically increasing vector of edges + // Outputs: + // N n-long vector where N(k) reveals how many values in X fall between + // E(k) <= X < E(k+1) + // B m-long vector of bin ids so that B(j) = k if E(k) <= X(j) < E(k+1). + // B(j) = -1 if X(j) is outside of E. + // + // O(n+m*log(n)) + template + IGL_INLINE void histc( + const Eigen::MatrixBase & X, + const Eigen::MatrixBase & E, + Eigen::PlainObjectBase & N, + Eigen::PlainObjectBase & B); + // Truly O(m*log(n)) + template + IGL_INLINE void histc( + const Eigen::MatrixBase & X, + const Eigen::MatrixBase & E, + Eigen::PlainObjectBase & B); + // Scalar search wrapper + template + IGL_INLINE void histc( + const typename DerivedE::Scalar & x, + const Eigen::MatrixBase & E, + typename DerivedE::Index & b); +} + +#ifndef IGL_STATIC_LIBRARY +# include "histc.cpp" +#endif + +#endif + + + diff --git a/src/igl/hsv_to_rgb.cpp b/src/igl/hsv_to_rgb.cpp new file mode 100644 index 0000000000..a1727ff50b --- /dev/null +++ b/src/igl/hsv_to_rgb.cpp @@ -0,0 +1,72 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "hsv_to_rgb.h" +#include + + +template +IGL_INLINE void igl::hsv_to_rgb(const T * hsv, T * rgb) +{ + igl::hsv_to_rgb( + hsv[0],hsv[1],hsv[2], + rgb[0],rgb[1],rgb[2]); +} + +template +IGL_INLINE void igl::hsv_to_rgb( + const T & h, const T & s, const T & v, + T & r, T & g, T & b) +{ + // From medit + double f,p,q,t,hh; + int i; + hh = ((int)h % 360) / 60.; + i = (int)std::floor(hh); /* largest int <= h */ + f = hh - i; /* fractional part of h */ + p = v * (1.0 - s); + q = v * (1.0 - (s * f)); + t = v * (1.0 - (s * (1.0 - f))); + + switch(i) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } +} + +template +void igl::hsv_to_rgb( + const Eigen::PlainObjectBase & H, + Eigen::PlainObjectBase & R) +{ + assert(H.cols() == 3); + R.resizeLike(H); + for(typename DerivedH::Index r = 0;r, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::hsv_to_rgb, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::hsv_to_rgb, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::hsv_to_rgb, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::hsv_to_rgb(double const*, double*); +#endif diff --git a/src/igl/hsv_to_rgb.h b/src/igl/hsv_to_rgb.h new file mode 100644 index 0000000000..aeb64d9d34 --- /dev/null +++ b/src/igl/hsv_to_rgb.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_HSV_TO_RGB_H +#define IGL_HSV_TO_RGB_H +#include "igl_inline.h" +#include +namespace igl +{ + // Convert RGB to HSV + // + // Inputs: + // h hue value (degrees: [0,360]) + // s saturation value ([0,1]) + // v value value ([0,1]) + // Outputs: + // r red value ([0,1]) + // g green value ([0,1]) + // b blue value ([0,1]) + template + IGL_INLINE void hsv_to_rgb(const T * hsv, T * rgb); + template + IGL_INLINE void hsv_to_rgb( + const T & h, const T & s, const T & v, + T & r, T & g, T & b); + template + void hsv_to_rgb( + const Eigen::PlainObjectBase & H, + Eigen::PlainObjectBase & R); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "hsv_to_rgb.cpp" +#endif + +#endif + diff --git a/src/igl/igl_inline.h b/src/igl/igl_inline.h new file mode 100644 index 0000000000..20c9630e4f --- /dev/null +++ b/src/igl/igl_inline.h @@ -0,0 +1,18 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// This should *NOT* be contained in a IGL_*_H ifdef, since it may be defined +// differently based on when it is included +#ifdef IGL_INLINE +#undef IGL_INLINE +#endif + +#ifndef IGL_STATIC_LIBRARY +# define IGL_INLINE inline +#else +# define IGL_INLINE +#endif diff --git a/src/igl/in_element.cpp b/src/igl/in_element.cpp new file mode 100644 index 0000000000..dcdfd8a89c --- /dev/null +++ b/src/igl/in_element.cpp @@ -0,0 +1,65 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "in_element.h" + +template +IGL_INLINE void igl::in_element( + const Eigen::PlainObjectBase & V, + const Eigen::MatrixXi & Ele, + const Eigen::PlainObjectBase & Q, + const AABB & aabb, + Eigen::VectorXi & I) +{ + using namespace std; + using namespace Eigen; + const int Qr = Q.rows(); + I.setConstant(Qr,1,-1); +#pragma omp parallel for if (Qr>10000) + for(int e = 0;e +IGL_INLINE void igl::in_element( + const Eigen::PlainObjectBase & V, + const Eigen::MatrixXi & Ele, + const Eigen::PlainObjectBase & Q, + const AABB & aabb, + Eigen::SparseMatrix & I) +{ + using namespace std; + using namespace Eigen; + const int Qr = Q.rows(); + std::vector > IJV; + IJV.reserve(Qr); +#pragma omp parallel for if (Qr>10000) + for(int e = 0;e(e,r,1)); + } + } + I.resize(Qr,Ele.rows()); + I.setFromTriplets(IJV.begin(),IJV.end()); +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::in_element, Eigen::Matrix, 2>(Eigen::PlainObjectBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase > const&, igl::AABB, 2> const&, Eigen::Matrix&); +template void igl::in_element, Eigen::Matrix, 3>(Eigen::PlainObjectBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase > const&, igl::AABB, 3> const&, Eigen::Matrix&); +#endif diff --git a/src/igl/in_element.h b/src/igl/in_element.h new file mode 100644 index 0000000000..dcbc03aa64 --- /dev/null +++ b/src/igl/in_element.h @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IN_ELEMENT_H +#define IGL_IN_ELEMENT_H + +#include "igl_inline.h" +#include "AABB.h" +#include +#include + +namespace igl +{ + // Determine whether each point in a list of points is in the elements of a + // mesh. + // + // templates: + // DIM dimension of vertices in V (# of columns) + // Inputs: + // V #V by dim list of mesh vertex positions. + // Ele #Ele by dim+1 list of mesh indices into #V. + // Q #Q by dim list of query point positions + // aabb axis-aligned bounding box tree object (see AABB.h) + // Outputs: + // I #Q list of indices into Ele of first containing element (-1 means no + // containing element) + template + IGL_INLINE void in_element( + const Eigen::PlainObjectBase & V, + const Eigen::MatrixXi & Ele, + const Eigen::PlainObjectBase & Q, + const AABB & aabb, + Eigen::VectorXi & I); + // Outputs: + // I #Q by #Ele sparse matrix revealing whether each element contains each + // point: I(q,e) means point q is in element e + template + IGL_INLINE void in_element( + const Eigen::PlainObjectBase & V, + const Eigen::MatrixXi & Ele, + const Eigen::PlainObjectBase & Q, + const AABB & aabb, + Eigen::SparseMatrix & I); +}; + +#ifndef IGL_STATIC_LIBRARY +#include "in_element.cpp" +#endif + +#endif diff --git a/src/igl/infinite_cost_stopping_condition.cpp b/src/igl/infinite_cost_stopping_condition.cpp new file mode 100644 index 0000000000..033f2b6780 --- /dev/null +++ b/src/igl/infinite_cost_stopping_condition.cpp @@ -0,0 +1,108 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "infinite_cost_stopping_condition.h" + +IGL_INLINE void igl::infinite_cost_stopping_condition( + const std::function & cost_and_placement, + std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> & stopping_condition) +{ + stopping_condition = + [&cost_and_placement] + ( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI, + const std::set > & Q, + const std::vector >::iterator > & Qit, + const Eigen::MatrixXd & C, + const int e, + const int /*e1*/, + const int /*e2*/, + const int /*f1*/, + const int /*f2*/)->bool + { + Eigen::RowVectorXd p; + double cost; + cost_and_placement(e,V,F,E,EMAP,EF,EI,cost,p); + return std::isinf(cost); + }; +} + +IGL_INLINE + std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> + igl::infinite_cost_stopping_condition( + const std::function & cost_and_placement) +{ + std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> stopping_condition; + infinite_cost_stopping_condition(cost_and_placement,stopping_condition); + return stopping_condition; +} + diff --git a/src/igl/infinite_cost_stopping_condition.h b/src/igl/infinite_cost_stopping_condition.h new file mode 100644 index 0000000000..d37e980254 --- /dev/null +++ b/src/igl/infinite_cost_stopping_condition.h @@ -0,0 +1,85 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_INFINITE_COST_STOPPING_CONDITION_H +#define IGL_INFINITE_COST_STOPPING_CONDITION_H +#include "igl_inline.h" +#include +#include +#include +#include +namespace igl +{ + // Stopping condition function compatible with igl::decimate. The output + // function handle will return true if cost of next edge is infinite. + // + // Inputs: + // cost_and_placement handle being used by igl::collapse_edge + // Outputs: + // stopping_condition + // + IGL_INLINE void infinite_cost_stopping_condition( + const std::function & cost_and_placement, + std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> & stopping_condition); + IGL_INLINE + std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> + infinite_cost_stopping_condition( + const std::function & cost_and_placement); +} + +#ifndef IGL_STATIC_LIBRARY +# include "infinite_cost_stopping_condition.cpp" +#endif +#endif + + diff --git a/src/igl/inradius.cpp b/src/igl/inradius.cpp new file mode 100644 index 0000000000..d5a06ab50f --- /dev/null +++ b/src/igl/inradius.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "inradius.h" +#include "edge_lengths.h" +#include "doublearea.h" + +template < + typename DerivedV, + typename DerivedF, + typename DerivedR> +IGL_INLINE void igl::inradius( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & r) +{ + Eigen::Matrix l; + Eigen::Matrix R; + igl::edge_lengths(V,F,l); + // If R is the circumradius, + // R*r = (abc)/(2*(a+b+c)) + // R = abc/(4*area) + // r(abc/(4*area)) = (abc)/(2*(a+b+c)) + // r/(4*area) = 1/(2*(a+b+c)) + // r = (2*area)/(a+b+c) + DerivedR A; + igl::doublearea(l,0.,A); + r = A.array() /l.array().rowwise().sum(); +} diff --git a/src/igl/inradius.h b/src/igl/inradius.h new file mode 100644 index 0000000000..07166b4b9d --- /dev/null +++ b/src/igl/inradius.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_INRADIUS_H +#define IGL_INRADIUS_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute the inradius of each triangle in a mesh (V,F) + // + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by 3 list of triangle indices into V + // Outputs: + // R #F list of inradii + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedR> + IGL_INLINE void inradius( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & R); +} +#ifndef IGL_STATIC_LIBRARY +# include "inradius.cpp" +#endif +#endif + diff --git a/src/igl/internal_angles.cpp b/src/igl/internal_angles.cpp new file mode 100644 index 0000000000..5c6e8dfbeb --- /dev/null +++ b/src/igl/internal_angles.cpp @@ -0,0 +1,130 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// Copyright (C) 2015 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "internal_angles.h" +#include "squared_edge_lengths.h" +#include "parallel_for.h" +#include "get_seconds.h" + +template +IGL_INLINE void igl::internal_angles( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase & K) +{ + using namespace Eigen; + using namespace std; + typedef typename DerivedV::Scalar Scalar; + if(F.cols() == 3) + { + // Edge lengths + Matrix< + Scalar, + DerivedF::RowsAtCompileTime, + DerivedF::ColsAtCompileTime> L_sq; + igl::squared_edge_lengths(V,F,L_sq); + + assert(F.cols() == 3 && "F should contain triangles"); + igl::internal_angles_using_squared_edge_lengths(L_sq,K); + }else + { + assert(V.cols() == 3 && "If F contains non-triangle facets, V must be 3D"); + K.resizeLike(F); + auto corner = []( + const typename DerivedV::ConstRowXpr & x, + const typename DerivedV::ConstRowXpr & y, + const typename DerivedV::ConstRowXpr & z) + { + typedef Eigen::Matrix RowVector3S; + RowVector3S v1 = (x-y).normalized(); + RowVector3S v2 = (z-y).normalized(); + // http://stackoverflow.com/questions/10133957/signed-angle-between-two-vectors-without-a-reference-plane + Scalar s = v1.cross(v2).norm(); + Scalar c = v1.dot(v2); + return atan2(s, c); + }; + for(unsigned i=0; i +IGL_INLINE void igl::internal_angles_using_squared_edge_lengths( + const Eigen::MatrixBase& L_sq, + Eigen::PlainObjectBase & K) +{ + typedef typename DerivedL::Index Index; + assert(L_sq.cols() == 3 && "Edge-lengths should come from triangles"); + const Index m = L_sq.rows(); + K.resize(m,3); + parallel_for( + m, + [&L_sq,&K](const Index f) + { + for(size_t d = 0;d<3;d++) + { + const auto & s1 = L_sq(f,d); + const auto & s2 = L_sq(f,(d+1)%3); + const auto & s3 = L_sq(f,(d+2)%3); + K(f,d) = acos((s3 + s2 - s1)/(2.*sqrt(s3*s2))); + } + }, + 1000l); +} + +template +IGL_INLINE void igl::internal_angles_using_edge_lengths( + const Eigen::MatrixBase& L, + Eigen::PlainObjectBase & K) +{ + // Note: + // Usage of internal_angles_using_squared_edge_lengths() is preferred to internal_angles_using_squared_edge_lengths() + // This function is deprecated and probably will be removed in future versions + typedef typename DerivedL::Index Index; + assert(L.cols() == 3 && "Edge-lengths should come from triangles"); + const Index m = L.rows(); + K.resize(m,3); + parallel_for( + m, + [&L,&K](const Index f) + { + for(size_t d = 0;d<3;d++) + { + const auto & s1 = L(f,d); + const auto & s2 = L(f,(d+1)%3); + const auto & s3 = L(f,(d+2)%3); + K(f,d) = acos((s3*s3 + s2*s2 - s1*s1)/(2.*s3*s2)); + } + }, + 1000l); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::internal_angles, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::internal_angles, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::internal_angles, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::internal_angles, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::internal_angles, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::internal_angles, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::internal_angles, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::internal_angles, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::internal_angles, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/internal_angles.h b/src/igl/internal_angles.h new file mode 100644 index 0000000000..1dd990ed49 --- /dev/null +++ b/src/igl/internal_angles.h @@ -0,0 +1,62 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_INTERNAL_ANGLES_H +#define IGL_INTERNAL_ANGLES_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute internal angles for a triangle mesh + // + // Inputs: + // V #V by dim eigen Matrix of mesh vertex nD positions + // F #F by poly-size eigen Matrix of face (triangle) indices + // Output: + // K #F by poly-size eigen Matrix of internal angles + // for triangles, columns correspond to edges [1,2],[2,0],[0,1] + // + // Known Issues: + // if poly-size ≠ 3 then dim must equal 3. + template + IGL_INLINE void internal_angles( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase & K); + // Inputs: + // L_sq #F by 3 list of squared edge lengths + // Output: + // K #F by poly-size eigen Matrix of internal angles + // for triangles, columns correspond to edges [1,2],[2,0],[0,1] + // + // Note: + // Usage of internal_angles_using_squared_edge_lengths is preferred to internal_angles_using_squared_edge_lengths + template + IGL_INLINE void internal_angles_using_squared_edge_lengths( + const Eigen::MatrixBase& L_sq, + Eigen::PlainObjectBase & K); + // Inputs: + // L #F by 3 list of edge lengths + // Output: + // K #F by poly-size eigen Matrix of internal angles + // for triangles, columns correspond to edges [1,2],[2,0],[0,1] + // + // Note: + // Usage of internal_angles_using_squared_edge_lengths is preferred to internal_angles_using_squared_edge_lengths + // This function is deprecated and probably will be removed in future versions + template + IGL_INLINE void internal_angles_using_edge_lengths( + const Eigen::MatrixBase& L, + Eigen::PlainObjectBase & K);} + +#ifndef IGL_STATIC_LIBRARY +# include "internal_angles.cpp" +#endif + +#endif + + diff --git a/src/igl/intersect.cpp b/src/igl/intersect.cpp new file mode 100644 index 0000000000..f930fc0be0 --- /dev/null +++ b/src/igl/intersect.cpp @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "intersect.h" +template +IGL_INLINE void igl::intersect(const M & A, const M & B, M & C) +{ + // Stupid O(size(A) * size(B)) to do it + // Alec: This should be implemented by using unique and sort like `setdiff` + M dyn_C(A.size() > B.size() ? A.size() : B.size(),1); + // count of intersects + int c = 0; + // Loop over A + for(int i = 0;i +IGL_INLINE M igl::intersect(const M & A, const M & B) +{ + M C; + intersect(A,B,C); + return C; +} +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template Eigen::Matrix igl::intersect >(Eigen::Matrix const&, Eigen::Matrix const&); +#endif diff --git a/src/igl/intersect.h b/src/igl/intersect.h new file mode 100644 index 0000000000..be4da91e5a --- /dev/null +++ b/src/igl/intersect.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_INTERSECT_H +#define IGL_INTERSECT_H +#include "igl_inline.h" +#include +namespace igl +{ + // Determine the intersect between two sets of coefficients using == + // Templates: + // M matrix type that implements indexing by global index M(i) + // Inputs: + // A matrix of coefficients + // B matrix of coefficients + // Output: + // C matrix of elements appearing in both A and B, C is always resized to + // have a single column + template + IGL_INLINE void intersect(const M & A, const M & B, M & C); + // Last argument as return + template + IGL_INLINE M intersect(const M & A, const M & B); +} +#ifndef IGL_STATIC_LIBRARY +#include "intersect.cpp" +#endif +#endif diff --git a/src/igl/invert_diag.cpp b/src/igl/invert_diag.cpp new file mode 100644 index 0000000000..b87a8d5b41 --- /dev/null +++ b/src/igl/invert_diag.cpp @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "invert_diag.h" + +template +IGL_INLINE void igl::invert_diag( + const Eigen::SparseMatrix& X, + Eigen::SparseMatrix& Y) +{ +#ifndef NDEBUG + typename Eigen::SparseVector dX = X.diagonal().sparseView(); + // Check that there are no zeros along the diagonal + assert(dX.nonZeros() == dX.size()); +#endif + // http://www.alecjacobson.com/weblog/?p=2552 + if(&Y != &X) + { + Y = X; + } + // Iterate over outside + for(int k=0; k::InnerIterator it (Y,k); it; ++it) + { + if(it.col() == it.row()) + { + T v = it.value(); + assert(v != 0); + v = ((T)1.0)/v; + Y.coeffRef(it.row(),it.col()) = v; + } + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::invert_diag(Eigen::SparseMatrix const&, Eigen::SparseMatrix&); +template void igl::invert_diag(Eigen::SparseMatrix const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/invert_diag.h b/src/igl/invert_diag.h new file mode 100644 index 0000000000..2194b3f14d --- /dev/null +++ b/src/igl/invert_diag.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_INVERT_DIAG_H +#define IGL_INVERT_DIAG_H +#include "igl_inline.h" +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include + +namespace igl +{ + // Invert the diagonal entries of a matrix (if the matrix is a diagonal + // matrix then this amounts to inverting the matrix) + + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Inputs: + // X an m by n sparse matrix + // Outputs: + // Y an m by n sparse matrix + template + IGL_INLINE void invert_diag( + const Eigen::SparseMatrix& X, + Eigen::SparseMatrix& Y); +} + +#ifndef IGL_STATIC_LIBRARY +# include "invert_diag.cpp" +#endif + +#endif + diff --git a/src/igl/is_border_vertex.cpp b/src/igl/is_border_vertex.cpp new file mode 100644 index 0000000000..a337609aed --- /dev/null +++ b/src/igl/is_border_vertex.cpp @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_border_vertex.h" +#include + +#include "triangle_triangle_adjacency.h" + +template +IGL_INLINE std::vector igl::is_border_vertex( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F) +{ + DerivedF FF; + igl::triangle_triangle_adjacency(F,FF); + std::vector ret(V.rows()); + for(unsigned i=0; i > igl::is_border_vertex, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template std::vector > igl::is_border_vertex, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template std::vector > igl::is_border_vertex, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +#endif diff --git a/src/igl/is_border_vertex.h b/src/igl/is_border_vertex.h new file mode 100644 index 0000000000..2e611ac11c --- /dev/null +++ b/src/igl/is_border_vertex.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_BORDER_VERTEX_H +#define IGL_IS_BORDER_VERTEX_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Determine vertices on open boundary of a (manifold) mesh with triangle + // faces F + // + // Inputs: + // V #V by dim list of vertex positions + // F #F by 3 list of triangle indices + // Returns #V vector of bools revealing whether vertices are on boundary + // + // Known Bugs: - does not depend on V + // - assumes mesh is edge manifold + // + template + IGL_INLINE std::vector is_border_vertex( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F); +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_border_vertex.cpp" +#endif + +#endif diff --git a/src/igl/is_boundary_edge.cpp b/src/igl/is_boundary_edge.cpp new file mode 100644 index 0000000000..679214bbce --- /dev/null +++ b/src/igl/is_boundary_edge.cpp @@ -0,0 +1,122 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_boundary_edge.h" +#include "unique_rows.h" +#include "sort.h" + +template < + typename DerivedF, + typename DerivedE, + typename DerivedB> +void igl::is_boundary_edge( + const Eigen::PlainObjectBase & E, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & B) +{ + using namespace Eigen; + using namespace std; + // Should be triangles + assert(F.cols() == 3); + // Should be edges + assert(E.cols() == 2); + // number of faces + const int m = F.rows(); + // Collect all directed edges after E + MatrixXi EallE(E.rows()+3*m,2); + EallE.block(0,0,E.rows(),E.cols()) = E; + for(int e = 0;e<3;e++) + { + for(int f = 0;f +void igl::is_boundary_edge( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & B, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP) +{ + using namespace Eigen; + using namespace std; + // Should be triangles + assert(F.cols() == 3); + // number of faces + const int m = F.rows(); + // Collect all directed edges after E + MatrixXi allE(3*m,2); + for(int e = 0;e<3;e++) + { + for(int f = 0;f, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::is_boundary_edge, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::is_boundary_edge, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::is_boundary_edge, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::is_boundary_edge, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::is_boundary_edge, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/is_boundary_edge.h b/src/igl/is_boundary_edge.h new file mode 100644 index 0000000000..f68ebb7e49 --- /dev/null +++ b/src/igl/is_boundary_edge.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IS_BOUNDARY_EDGE_H +#define IS_BOUNDARY_EDGE_H +#include + +namespace igl +{ + // IS_BOUNDARY_EDGE Determine for each edge E if it is a "boundary edge" in F. + // Boundary edges are undirected edges which occur only once. + // + // Inputs: + // E #E by 2 list of edges + // F #F by 3 list of triangles + // Outputs: + // B #E list bools. true iff unoriented edge occurs exactly once in F + // (non-manifold and non-existant edges will be false) + // + template < + typename DerivedF, + typename DerivedE, + typename DerivedB> + void is_boundary_edge( + const Eigen::PlainObjectBase & E, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & B); + // Wrapper where Edges should also be computed from F + // E #E by 2 list of edges + // EMAP #F*3 list of indices mapping allE to E + template < + typename DerivedF, + typename DerivedE, + typename DerivedB, + typename DerivedEMAP> + void is_boundary_edge( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & B, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP); +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_boundary_edge.cpp" +#endif + +#endif diff --git a/src/igl/is_dir.cpp b/src/igl/is_dir.cpp new file mode 100644 index 0000000000..e7346c1dda --- /dev/null +++ b/src/igl/is_dir.cpp @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_dir.h" + +#include + +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif + +IGL_INLINE bool igl::is_dir(const char * filename) +{ + struct stat status; + if(stat(filename,&status)!=0) + { + // path does not exist + return false; + } + // Tests whether existing path is a directory + return S_ISDIR(status.st_mode); +} diff --git a/src/igl/is_dir.h b/src/igl/is_dir.h new file mode 100644 index 0000000000..68e30a5640 --- /dev/null +++ b/src/igl/is_dir.h @@ -0,0 +1,29 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_DIR_H +#define IGL_IS_DIR_H +#include "igl_inline.h" +namespace igl +{ + // Act like php's is_dir function + // http://php.net/manual/en/function.is-dir.php + // Tells whether the given filename is a directory. + // Input: + // filename Path to the file. If filename is a relative filename, it will + // be checked relative to the current working directory. + // Returns TRUE if the filename exists and is a directory, FALSE + // otherwise. + IGL_INLINE bool is_dir(const char * filename); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_dir.cpp" +#endif + +#endif diff --git a/src/igl/is_edge_manifold.cpp b/src/igl/is_edge_manifold.cpp new file mode 100644 index 0000000000..1b106056e9 --- /dev/null +++ b/src/igl/is_edge_manifold.cpp @@ -0,0 +1,104 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_edge_manifold.h" +#include "oriented_facets.h" +#include "unique_simplices.h" + +#include +#include + +template < + typename DerivedF, + typename DerivedBF, + typename DerivedE, + typename DerivedEMAP, + typename DerivedBE> +IGL_INLINE bool igl::is_edge_manifold( + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& BF, + Eigen::PlainObjectBase& E, + Eigen::PlainObjectBase& EMAP, + Eigen::PlainObjectBase& BE) +{ + using namespace Eigen; + typedef typename DerivedF::Index Index; + typedef Matrix VectorXF; + typedef Matrix MatrixXF2; + MatrixXF2 allE; + oriented_facets(F,allE); + // Find unique undirected edges and mapping + VectorXF _; + unique_simplices(allE,E,_,EMAP); + std::vector count(E.rows(),0); + for(Index e = 0;e +IGL_INLINE bool igl::is_edge_manifold( + const Eigen::MatrixBase& F) +{ + // TODO: It's bothersome that this is not calling/reusing the code from the + // overload above. This could result in disagreement. + + // List of edges (i,j,f,c) where edge i > TTT; + for(int f=0;f v2) std::swap(v1,v2); + std::vector r(4); + r[0] = v1; r[1] = v2; + r[2] = f; r[3] = i; + TTT.push_back(r); + } + // Sort lexicographically + std::sort(TTT.begin(),TTT.end()); + + for(int i=2;i<(int)TTT.size();++i) + { + // Check any edges occur 3 times + std::vector& r1 = TTT[i-2]; + std::vector& r2 = TTT[i-1]; + std::vector& r3 = TTT[i]; + if ( (r1[0] == r2[0] && r2[0] == r3[0]) + && + (r1[1] == r2[1] && r2[1] == r3[1]) ) + { + return false; + } + } + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::is_edge_manifold >(Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +//template bool igl::is_edge_manifold(Eigen::Matrix const&, Eigen::Matrix const&); +template bool igl::is_edge_manifold >(Eigen::MatrixBase > const&); +template bool igl::is_edge_manifold >(Eigen::MatrixBase > const&); +#endif diff --git a/src/igl/is_edge_manifold.h b/src/igl/is_edge_manifold.h new file mode 100644 index 0000000000..5832806acf --- /dev/null +++ b/src/igl/is_edge_manifold.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_EDGE_MANIFOLD_H +#define IGL_IS_EDGE_MANIFOLD_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // check if the mesh is edge-manifold + // + // Inputs: + // V #V by dim list of mesh vertex positions **unneeded** + // F #F by 3 list of triangle indices + // Returns whether mesh is edge manifold. + // + // Known Bugs: + // Does not check for non-manifold vertices + // + // See also: is_vertex_manifold + template < + typename DerivedF, + typename DerivedBF, + typename DerivedE, + typename DerivedEMAP, + typename DerivedBE> + IGL_INLINE bool is_edge_manifold( + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& BF, + Eigen::PlainObjectBase& E, + Eigen::PlainObjectBase& EMAP, + Eigen::PlainObjectBase& BE); + template + IGL_INLINE bool is_edge_manifold( + const Eigen::MatrixBase& F); +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_edge_manifold.cpp" +#endif + +#endif diff --git a/src/igl/is_file.cpp b/src/igl/is_file.cpp new file mode 100644 index 0000000000..6c040b86c6 --- /dev/null +++ b/src/igl/is_file.cpp @@ -0,0 +1,26 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_file.h" + +#include +#ifdef _WIN32 +# ifndef S_ISREG +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +# endif +#endif +IGL_INLINE bool igl::is_file(const char * filename) +{ + struct stat status; + if(stat(filename,&status)!=0) + { + // path does not exist + return false; + } + // Tests whether existing path is a regular file + return S_ISREG(status.st_mode); +} diff --git a/src/igl/is_file.h b/src/igl/is_file.h new file mode 100644 index 0000000000..5610c7ff33 --- /dev/null +++ b/src/igl/is_file.h @@ -0,0 +1,29 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_FILE_H +#define IGL_IS_FILE_H +#include "igl_inline.h" +namespace igl +{ + // Act like php's is_file function + // http://php.net/manual/en/function.is-file.php + // Tells whether the given filename is a regular file. + // Input: + // filename Path to the file. If filename is a relative filename, it will + // be checked relative to the current working directory. + // Returns TRUE if the filename exists and is a regular file, FALSE + // otherwise. + IGL_INLINE bool is_file(const char * filename); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_file.cpp" +#endif + +#endif diff --git a/src/igl/is_irregular_vertex.cpp b/src/igl/is_irregular_vertex.cpp new file mode 100644 index 0000000000..34665d66f4 --- /dev/null +++ b/src/igl/is_irregular_vertex.cpp @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_irregular_vertex.h" +#include + +#include "is_border_vertex.h" + +template +IGL_INLINE std::vector igl::is_irregular_vertex(const Eigen::PlainObjectBase &V, const Eigen::PlainObjectBase &F) +{ + Eigen::VectorXi count = Eigen::VectorXi::Zero(F.maxCoeff()); + + for(unsigned i=0; i border = is_border_vertex(V,F); + + std::vector res(count.size()); + + for (unsigned i=0; i > igl::is_irregular_vertex, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template std::vector > igl::is_irregular_vertex, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +#endif diff --git a/src/igl/is_irregular_vertex.h b/src/igl/is_irregular_vertex.h new file mode 100644 index 0000000000..e07e5ab70a --- /dev/null +++ b/src/igl/is_irregular_vertex.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_IRREGULAR_VERTEX_H +#define IGL_IS_IRREGULAR_VERTEX_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Determine if a vertex is irregular, i.e. it has more than 6 (triangles) + // or 4 (quads) incident edges. Vertices on the boundary are ignored. + // + // Inputs: + // V #V by dim list of vertex positions + // F #F by 3[4] list of triangle[quads] indices + // Returns #V vector of bools revealing whether vertices are singular + // + template + IGL_INLINE std::vector is_irregular_vertex(const Eigen::PlainObjectBase &V, const Eigen::PlainObjectBase &F); +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_irregular_vertex.cpp" +#endif + +#endif diff --git a/src/igl/is_planar.cpp b/src/igl/is_planar.cpp new file mode 100644 index 0000000000..6058a03d4e --- /dev/null +++ b/src/igl/is_planar.cpp @@ -0,0 +1,18 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_planar.h" +IGL_INLINE bool igl::is_planar(const Eigen::MatrixXd & V) +{ + if(V.size() == 0) return false; + if(V.cols() == 2) return true; + for(int i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_PLANAR_H +#define IGL_IS_PLANAR_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Determine if a set of points lies on the XY plane + // + // Inputs: + // V #V by dim list of vertex positions + // Return true if a mesh has constant value of 0 in z coordinate + // + // Known bugs: Doesn't determine if vertex is flat if it doesn't lie on the + // XY plane. + IGL_INLINE bool is_planar(const Eigen::MatrixXd & V); +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_planar.cpp" +#endif +#endif diff --git a/src/igl/is_readable.cpp b/src/igl/is_readable.cpp new file mode 100644 index 0000000000..eb5e953471 --- /dev/null +++ b/src/igl/is_readable.cpp @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_readable.h" + +#ifdef _WIN32 +# include +IGL_INLINE bool igl::is_readable(const char* filename) +{ + FILE * f = fopen(filename,"r"); + if(f == NULL) + { + return false; + } + fclose(f); + return true; +} +#else +# include +# include +# include +IGL_INLINE bool igl::is_readable(const char* filename) +{ + // Check if file already exists + struct stat status; + if(stat(filename,&status)!=0) + { + return false; + } + + // Get current users uid and gid + uid_t this_uid = getuid(); + gid_t this_gid = getgid(); + + // Dealing with owner + if( this_uid == status.st_uid ) + { + return S_IRUSR & status.st_mode; + } + + // Dealing with group member + if( this_gid == status.st_gid ) + { + return S_IRGRP & status.st_mode; + } + + // Dealing with other + return S_IROTH & status.st_mode; + +} +#endif diff --git a/src/igl/is_readable.h b/src/igl/is_readable.h new file mode 100644 index 0000000000..6897012853 --- /dev/null +++ b/src/igl/is_readable.h @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_READABLE_H +#define IGL_IS_READABLE_H +#include "igl_inline.h" +namespace igl +{ + // Check if a file is reabable like PHP's is_readable function: + // http://www.php.net/manual/en/function.is-readable.php + // Input: + // filename path to file + // Returns true if file exists and is readable and false if file doesn't + // exist or *is not readable* + // + // Note: Windows version will not check user or group ids + IGL_INLINE bool is_readable(const char * filename); +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_readable.cpp" +#endif + +#endif diff --git a/src/igl/is_sparse.cpp b/src/igl/is_sparse.cpp new file mode 100644 index 0000000000..507393d853 --- /dev/null +++ b/src/igl/is_sparse.cpp @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_sparse.h" +template +IGL_INLINE bool igl::is_sparse( + const Eigen::SparseMatrix & A) +{ + return true; +} +template +IGL_INLINE bool igl::is_sparse( + const Eigen::PlainObjectBase& A) +{ + return false; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::is_sparse(Eigen::SparseMatrix const&); +// generated by autoexplicit.sh +template bool igl::is_sparse >(Eigen::PlainObjectBase > const&); +#endif diff --git a/src/igl/is_sparse.h b/src/igl/is_sparse.h new file mode 100644 index 0000000000..9547bb8e80 --- /dev/null +++ b/src/igl/is_sparse.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_SPARSE_H +#define IGL_IS_SPARSE_H +#include "igl_inline.h" +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include +namespace igl +{ + // Determine if a matrix A is sparse + // + // Template: + // T,DerivedA defines scalar type + // Inputs: + // A matrix in question + // Returns true if A is represented with a sparse matrix + template + IGL_INLINE bool is_sparse( + const Eigen::SparseMatrix & A); + template + IGL_INLINE bool is_sparse( + const Eigen::PlainObjectBase& A); +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_sparse.cpp" +#endif + +#endif + diff --git a/src/igl/is_stl.cpp b/src/igl/is_stl.cpp new file mode 100644 index 0000000000..240703616b --- /dev/null +++ b/src/igl/is_stl.cpp @@ -0,0 +1,64 @@ +#include "is_stl.h" +#include +IGL_INLINE bool igl::is_stl(FILE * stl_file, bool & is_ascii) +{ + + // solid? + // YES NO + // / if .stl, definitely binary + // / + // perfect size? + // YES NO + // + const auto perfect_size = [](FILE * stl_file)->bool + { + //stl_file = freopen(NULL,"rb",stl_file); + // Read 80 header + char header[80]; + if(fread(header,sizeof(char),80,stl_file)!=80) + { + return false; + } + // Read number of triangles + unsigned int num_tri; + if(fread(&num_tri,sizeof(unsigned int),1,stl_file)!=1) + { + return false; + } + fseek(stl_file,0,SEEK_END); + int file_size = ftell(stl_file); + fseek(stl_file,0,SEEK_SET); + //stl_file = freopen(NULL,"r",stl_file); + return (file_size == 80 + 4 + (4*12 + 2) * num_tri); + }; + // Specifically 80 character header + char header[80]; + char solid[80]; + is_ascii = true; + bool f = true; + if(fread(header,1,80,stl_file) != 80) + { + f = false; + goto finish; + } + + sscanf(header,"%s",solid); + if(std::string("solid") == solid) + { + f = true; + is_ascii = !perfect_size(stl_file); + }else + { + is_ascii = false; + f = perfect_size(stl_file); + } +finish: + rewind(stl_file); + return f; +} + +IGL_INLINE bool igl::is_stl(FILE * stl_file) +{ + bool is_ascii; + return is_stl(stl_file,is_ascii); +} diff --git a/src/igl/is_stl.h b/src/igl/is_stl.h new file mode 100644 index 0000000000..e8f78df1a9 --- /dev/null +++ b/src/igl/is_stl.h @@ -0,0 +1,21 @@ +#ifndef IGL_IS_STL_H +#define IGL_IS_STL_H +#include "igl_inline.h" +#include +namespace igl +{ + // Given a file pointer, determine if it contains an .stl file and then + // rewind it. + // + // Inputs: + // stl_file pointer to file + // Outputs: + // is_ascii flag whether stl is ascii + // Returns whether stl_file is an .stl file + IGL_INLINE bool is_stl(FILE * stl_file, bool & is_ascii); + IGL_INLINE bool is_stl(FILE * stl_file); +}; +#ifndef IGL_STATIC_LIBRARY +# include "is_stl.cpp" +#endif +#endif diff --git a/src/igl/is_symmetric.cpp b/src/igl/is_symmetric.cpp new file mode 100644 index 0000000000..f40473b66c --- /dev/null +++ b/src/igl/is_symmetric.cpp @@ -0,0 +1,73 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_symmetric.h" +#include "find.h" + +template +IGL_INLINE bool igl::is_symmetric(const Eigen::SparseMatrix& A) +{ + if(A.rows() != A.cols()) + { + return false; + } + assert(A.size() != 0); + Eigen::SparseMatrix AT = A.transpose(); + Eigen::SparseMatrix AmAT = A-AT; + //// Eigen screws up something with LLT if you try to do + //SparseMatrix AmAT = A-A.transpose(); + //// Eigen crashes at runtime if you try to do + // return (A-A.transpose()).nonZeros() == 0; + return AmAT.nonZeros() == 0; +} + +template +IGL_INLINE bool igl::is_symmetric( + const Eigen::PlainObjectBase& A) +{ + if(A.rows() != A.cols()) + { + return false; + } + assert(A.size() != 0); + return (A-A.transpose()).eval().nonZeros() == 0; +} + +template +IGL_INLINE bool igl::is_symmetric( + const Eigen::SparseMatrix& A, + const epsilonT epsilon) +{ + using namespace Eigen; + using namespace std; + if(A.rows() != A.cols()) + { + return false; + } + assert(A.size() != 0); + SparseMatrix AT = A.transpose(); + SparseMatrix AmAT = A-AT; + VectorXi AmATI,AmATJ; + Matrix AmATV; + find(AmAT,AmATI,AmATJ,AmATV); + if(AmATI.size() == 0) + { + return true; + } + + return AmATV.maxCoeff() < epsilon && AmATV.minCoeff() > -epsilon; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::is_symmetric >(Eigen::PlainObjectBase > const&); +// generated by autoexplicit.sh +template bool igl::is_symmetric(Eigen::SparseMatrix const&); +template bool igl::is_symmetric(Eigen::SparseMatrix const&, double); +template bool igl::is_symmetric(Eigen::SparseMatrix const&, int); +#endif diff --git a/src/igl/is_symmetric.h b/src/igl/is_symmetric.h new file mode 100644 index 0000000000..31725298e6 --- /dev/null +++ b/src/igl/is_symmetric.h @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_SYMMETRIC_H +#define IGL_IS_SYMMETRIC_H +#include "igl_inline.h" +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +namespace igl +{ + // Returns true if the given matrix is symmetric + // Inputs: + // A m by m matrix + // Returns true if the matrix is square and symmetric + template + IGL_INLINE bool is_symmetric(const Eigen::SparseMatrix& A); + // Inputs: + // epsilon threshold on L1 difference between A and A' + template + IGL_INLINE bool is_symmetric(const Eigen::SparseMatrix& A, const epsilonT epsilon); + template + IGL_INLINE bool is_symmetric( + const Eigen::PlainObjectBase& A); +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_symmetric.cpp" +#endif + +#endif diff --git a/src/igl/is_vertex_manifold.cpp b/src/igl/is_vertex_manifold.cpp new file mode 100644 index 0000000000..d266ebab7c --- /dev/null +++ b/src/igl/is_vertex_manifold.cpp @@ -0,0 +1,101 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_vertex_manifold.h" +#include "triangle_triangle_adjacency.h" +#include "vertex_triangle_adjacency.h" +#include "unique.h" +#include +#include +#include +#include +#include + +template +IGL_INLINE bool igl::is_vertex_manifold( + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& B) +{ + using namespace std; + using namespace Eigen; + assert(F.cols() == 3 && "F must contain triangles"); + typedef typename DerivedF::Scalar Index; + typedef typename DerivedF::Index FIndex; + const FIndex m = F.rows(); + const Index n = F.maxCoeff()+1; + vector > > TT; + vector > > TTi; + triangle_triangle_adjacency(F,TT,TTi); + + vector > V2F,_1; + vertex_triangle_adjacency(n,F,V2F,_1); + + const auto & check_vertex = [&](const Index v)->bool + { + vector uV2Fv; + { + vector _1,_2; + unique(V2F[v],uV2Fv,_1,_2); + } + const FIndex one_ring_size = uV2Fv.size(); + if(one_ring_size == 0) + { + return false; + } + const FIndex g = uV2Fv[0]; + queue Q; + Q.push(g); + map seen; + while(!Q.empty()) + { + const FIndex f = Q.front(); + Q.pop(); + if(seen.count(f)==1) + { + continue; + } + seen[f] = true; + // Face f's neighbor lists opposite opposite each corner + for(const auto & c : TT[f]) + { + // Each neighbor + for(const auto & n : c) + { + bool contains_v = false; + for(Index nc = 0;nc, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template bool igl::is_vertex_manifold, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/is_vertex_manifold.h b/src/igl/is_vertex_manifold.h new file mode 100644 index 0000000000..bf9caded6b --- /dev/null +++ b/src/igl/is_vertex_manifold.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_VERTEX_MANIFOLD_H +#define IGL_IS_VERTEX_MANIFOLD_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Check if a mesh is vertex-manifold. This only checks whether the faces + // incident on each vertex form exactly one connected component. Vertices + // incident on non-manifold edges are not consider non-manifold by this + // function (see is_edge_manifold.h). Unreferenced verties are considered + // non-manifold (zero components). + // + // Inputs: + // F #F by 3 list of triangle indices + // Outputs: + // B #V list indicate whether each vertex is locally manifold. + // Returns whether mesh is vertex manifold. + // + // See also: is_edge_manifold + template + IGL_INLINE bool is_vertex_manifold( + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& B); +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_vertex_manifold.cpp" +#endif + +#endif diff --git a/src/igl/is_writable.cpp b/src/igl/is_writable.cpp new file mode 100644 index 0000000000..f266b00783 --- /dev/null +++ b/src/igl/is_writable.cpp @@ -0,0 +1,58 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "is_writable.h" + +#ifdef _WIN32 +#include +#ifndef S_IWUSR +# define S_IWUSR S_IWRITE +#endif +IGL_INLINE bool is_writable(const char* filename) +{ + // Check if file already exists + struct stat status; + if(stat(filename,&status)!=0) + { + return false; + } + + return S_IWUSR & status.st_mode; +} +#else +#include +#include + +IGL_INLINE bool igl::is_writable(const char* filename) +{ + // Check if file already exists + struct stat status; + if(stat(filename,&status)!=0) + { + return false; + } + + // Get current users uid and gid + uid_t this_uid = getuid(); + gid_t this_gid = getgid(); + + // Dealing with owner + if( this_uid == status.st_uid ) + { + return S_IWUSR & status.st_mode; + } + + // Dealing with group member + if( this_gid == status.st_gid ) + { + return S_IWGRP & status.st_mode; + } + + // Dealing with other + return S_IWOTH & status.st_mode; +} +#endif diff --git a/src/igl/is_writable.h b/src/igl/is_writable.h new file mode 100644 index 0000000000..8834e8afbb --- /dev/null +++ b/src/igl/is_writable.h @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_IS_WRITABLE_H +#define IGL_IS_WRITABLE_H +#include "igl_inline.h" +namespace igl +{ + // Check if a file exists *and* is writable like PHP's is_writable function: + // http://www.php.net/manual/en/function.is-writable.php + // Input: + // filename path to file + // Returns true if file exists and is writable and false if file doesn't + // exist or *is not writable* + // + // Note: Windows version will not test group and user id + IGL_INLINE bool is_writable(const char * filename); +} + +#ifndef IGL_STATIC_LIBRARY +# include "is_writable.cpp" +#endif + +#endif diff --git a/src/igl/isdiag.cpp b/src/igl/isdiag.cpp new file mode 100644 index 0000000000..452341bf80 --- /dev/null +++ b/src/igl/isdiag.cpp @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "isdiag.h" + +template +IGL_INLINE bool igl::isdiag(const Eigen::SparseMatrix & A) +{ + // Iterate over outside of A + for(int k=0; k::InnerIterator it (A,k); it; ++it) + { + if(it.row() != it.col() && it.value()!=0) + { + return false; + } + } + } + return true; +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::isdiag(class Eigen::SparseMatrix const &); +#endif diff --git a/src/igl/isdiag.h b/src/igl/isdiag.h new file mode 100644 index 0000000000..2bd555fa35 --- /dev/null +++ b/src/igl/isdiag.h @@ -0,0 +1,26 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ISDIAG_H +#define IGL_ISDIAG_H +#include "igl_inline.h" +#include +namespace igl +{ + // Determine if a given matrix is diagonal: all non-zeros lie on the + // main diagonal. + // + // Inputs: + // A m by n sparse matrix + // Returns true iff and only if the matrix is diagonal. + template + IGL_INLINE bool isdiag(const Eigen::SparseMatrix & A); +}; +#ifndef IGL_STATIC_LIBRARY +# include "isdiag.cpp" +#endif +#endif diff --git a/src/igl/ismember.cpp b/src/igl/ismember.cpp new file mode 100644 index 0000000000..e3c3fd4924 --- /dev/null +++ b/src/igl/ismember.cpp @@ -0,0 +1,185 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "ismember.h" +#include "colon.h" +#include "list_to_matrix.h" +#include "sort.h" +#include "sortrows.h" +#include "unique.h" +#include "unique_rows.h" +#include + +template < + typename DerivedA, + typename DerivedB, + typename DerivedIA, + typename DerivedLOCB> +IGL_INLINE void igl::ismember( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + Eigen::PlainObjectBase & IA, + Eigen::PlainObjectBase & LOCB) +{ + using namespace Eigen; + using namespace std; + IA.resizeLike(A); + IA.setConstant(false); + LOCB.resizeLike(A); + LOCB.setConstant(-1); + // boring base cases + if(A.size() == 0) + { + return; + } + if(B.size() == 0) + { + return; + } + + // Get rid of any duplicates + typedef Matrix VectorA; + typedef Matrix VectorB; + const VectorA vA(Eigen::Map(DerivedA(A).data(), A.cols()*A.rows(),1)); + const VectorB vB(Eigen::Map(DerivedB(B).data(), B.cols()*B.rows(),1)); + VectorA uA; + VectorB uB; + Eigen::Matrix uIA,uIuA,uIB,uIuB; + unique(vA,uA,uIA,uIuA); + unique(vB,uB,uIB,uIuB); + // Sort both + VectorA sA; + VectorB sB; + Eigen::Matrix sIA,sIB; + sort(uA,1,true,sA,sIA); + sort(uB,1,true,sB,sIB); + + Eigen::Matrix uF = + Eigen::Matrix::Zero(sA.size(),1); + Eigen::Matrix uLOCB = + Eigen::Matrix:: + Constant(sA.size(),1,-1); + { + int bi = 0; + // loop over sA + bool past = false; + for(int a = 0;asB(bi)) + { + bi++; + past = bi>=sB.size(); + } + if(!past && sA(a)==sB(bi)) + { + uF(sIA(a)) = true; + uLOCB(sIA(a)) = uIB(sIB(bi)); + } + } + } + + Map< Matrix > + vIA(IA.data(),IA.cols()*IA.rows(),1); + Map< Matrix > + vLOCB(LOCB.data(),LOCB.cols()*LOCB.rows(),1); + for(int a = 0;a +IGL_INLINE void igl::ismember_rows( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + Eigen::PlainObjectBase & IA, + Eigen::PlainObjectBase & LOCB) +{ + using namespace Eigen; + using namespace std; + assert(A.cols() == B.cols() && "number of columns must match"); + IA.resize(A.rows(),1); + IA.setConstant(false); + LOCB.resize(A.rows(),1); + LOCB.setConstant(-1); + // boring base cases + if(A.size() == 0) + { + return; + } + if(B.size() == 0) + { + return; + } + + // Get rid of any duplicates + DerivedA uA; + DerivedB uB; + Eigen::Matrix uIA,uIuA,uIB,uIuB; + unique_rows(A,uA,uIA,uIuA); + unique_rows(B,uB,uIB,uIuB); + // Sort both + DerivedA sA; + DerivedB sB; + Eigen::Matrix sIA,sIB; + sortrows(uA,true,sA,sIA); + sortrows(uB,true,sB,sIB); + + Eigen::Matrix uF = + Eigen::Matrix::Zero(sA.size(),1); + Eigen::Matrix uLOCB = + Eigen::Matrix:: + Constant(sA.size(),1,-1); + const auto & row_greater_than = [&sA,&sB](const int a, const int b) + { + for(int c = 0;c sB(b,c)) return true; + if(sA(a,c) < sB(b,c)) return false; + } + return false; + }; + { + int bi = 0; + // loop over sA + bool past = false; + for(int a = 0;a=sB.rows(); + } + if(!past && (sA.row(a).array()==sB.row(bi).array()).all() ) + { + uF(sIA(a)) = true; + uLOCB(sIA(a)) = uIB(sIB(bi)); + } + } + } + + for(int a = 0;a, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::ismember_rows, Eigen::Matrix, Eigen::Array, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::ismember_rows, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::ismember_rows, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::ismember_rows, Eigen::Matrix, Eigen::Array, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/ismember.h b/src/igl/ismember.h new file mode 100644 index 0000000000..72be6006d7 --- /dev/null +++ b/src/igl/ismember.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ISMEMBER_H +#define IGL_ISMEMBER_H +#include "igl_inline.h" +#include +namespace igl +{ + // Determine if elements of A exist in elements of B + // + // Inputs: + // A ma by na matrix + // B mb by nb matrix + // Outputs: + // IA ma by na matrix of flags whether corresponding element of A exists in + // B + // LOCB ma by na matrix of indices in B locating matching element (-1 if + // not found), indices assume column major ordering + // + template < + typename DerivedA, + typename DerivedB, + typename DerivedIA, + typename DerivedLOCB> + IGL_INLINE void ismember( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + Eigen::PlainObjectBase & IA, + Eigen::PlainObjectBase & LOCB); + template < + typename DerivedA, + typename DerivedB, + typename DerivedIA, + typename DerivedLOCB> + IGL_INLINE void ismember_rows( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + Eigen::PlainObjectBase & IA, + Eigen::PlainObjectBase & LOCB); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "ismember.cpp" +#endif +#endif + diff --git a/src/igl/isolines.cpp b/src/igl/isolines.cpp new file mode 100644 index 0000000000..6d4f87e32b --- /dev/null +++ b/src/igl/isolines.cpp @@ -0,0 +1,116 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Oded Stein +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + + +#include "isolines.h" + +#include +#include +#include + +#include "remove_duplicate_vertices.h" + + +template +IGL_INLINE void igl::isolines( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const Eigen::MatrixBase& z, + const int n, + Eigen::PlainObjectBase& isoV, + Eigen::PlainObjectBase& isoE) +{ + //Constants + const int dim = V.cols(); + assert(dim==2 || dim==3); + const int nVerts = V.rows(); + assert(z.rows() == nVerts && + "There must be as many function entries as vertices"); + const int nFaces = F.rows(); + const int np1 = n+1; + const double min = z.minCoeff(), max = z.maxCoeff(); + + + //Following http://www.alecjacobson.com/weblog/?p=2529 + typedef typename DerivedZ::Scalar Scalar; + typedef Eigen::Matrix Vec; + Vec iso(np1); + for(int i=0; i Matrix; + std::array t{{Matrix(nFaces, np1), + Matrix(nFaces, np1), Matrix(nFaces, np1)}}; + for(int i=0; i1) + t[k](i,j) = std::numeric_limits::quiet_NaN(); + } + } + } + + std::array,3> Fij, Iij; + for(int i=0; i LIVec; + typedef Eigen::Matrix LMat; + typedef Eigen::Matrix LIMat; + LIVec dummy1, dummy2; + igl::remove_duplicate_vertices(LMat(isoV), LIMat(isoE), + 2.2204e-15, isoV, dummy1, dummy2, isoE); + +} + + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::isolines, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int const, Eigen::PlainObjectBase > &, Eigen::PlainObjectBase > &); +#endif + diff --git a/src/igl/isolines.h b/src/igl/isolines.h new file mode 100644 index 0000000000..3b5199b6ad --- /dev/null +++ b/src/igl/isolines.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Oded Stein +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + + +#ifndef IGL_ISOLINES_H +#define IGL_ISOLINES_H +#include "igl_inline.h" + +#include +#include + + +namespace igl +{ + // Constructs isolines for a function z given on a mesh (V,F) + // + // + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by 3 list of mesh faces (must be triangles) + // z #V by 1 list of function values evaluated at vertices + // n the number of desired isolines + // Outputs: + // isoV #isoV by dim list of isoline vertex positions + // isoE #isoE by 2 list of isoline edge positions + // + // + + template + IGL_INLINE void isolines( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const Eigen::MatrixBase& z, + const int n, + Eigen::PlainObjectBase& isoV, + Eigen::PlainObjectBase& isoE); +} + +#ifndef IGL_STATIC_LIBRARY +# include "isolines.cpp" +#endif + +#endif diff --git a/src/igl/jet.cpp b/src/igl/jet.cpp new file mode 100644 index 0000000000..373f23bcbf --- /dev/null +++ b/src/igl/jet.cpp @@ -0,0 +1,93 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "jet.h" +#include "colormap.h" + +template +IGL_INLINE void igl::jet(const T x, T * rgb) +{ + igl::colormap(igl::COLOR_MAP_TYPE_JET, x, rgb); +} + +template +IGL_INLINE void igl::jet(const T x_in, T & r, T & g, T & b) +{ + // Only important if the number of colors is small. In which case the rest is + // still wrong anyway + // x = linspace(0,1,jj)' * (1-1/jj) + 1/jj; + // + const double rone = 0.8; + const double gone = 1.0; + const double bone = 1.0; + T x = x_in; + x = (x_in<0 ? 0 : (x>1 ? 1 : x)); + + if (x<1. / 8.) + { + r = 0; + g = 0; + b = bone*(0.5 + (x) / (1. / 8.)*0.5); + } else if (x<3. / 8.) + { + r = 0; + g = gone*(x - 1. / 8.) / (3. / 8. - 1. / 8.); + b = bone; + } else if (x<5. / 8.) + { + r = rone*(x - 3. / 8.) / (5. / 8. - 3. / 8.); + g = gone; + b = (bone - (x - 3. / 8.) / (5. / 8. - 3. / 8.)); + } else if (x<7. / 8.) + { + r = rone; + g = (gone - (x - 5. / 8.) / (7. / 8. - 5. / 8.)); + b = 0; + } else + { + r = (rone - (x - 7. / 8.) / (1. - 7. / 8.)*0.5); + g = 0; + b = 0; + } +} + +template +IGL_INLINE void igl::jet( + const Eigen::MatrixBase & Z, + const bool normalize, + Eigen::PlainObjectBase & C) +{ + igl::colormap(igl::COLOR_MAP_TYPE_JET,Z, normalize, C); +} + +template +IGL_INLINE void igl::jet( + const Eigen::MatrixBase & Z, + const double min_z, + const double max_z, + Eigen::PlainObjectBase & C) +{ + igl::colormap(igl::COLOR_MAP_TYPE_JET, Z, min_z, max_z, C); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::jet, Eigen::Matrix >(Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +template void igl::jet(double, double*); +template void igl::jet(double, double&, double&, double&); +template void igl::jet(float, float*); +template void igl::jet, Eigen::Matrix >(Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +template void igl::jet, Eigen::Matrix >(Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +template void igl::jet, Eigen::Matrix >(Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +template void igl::jet(float, float&, float&, float&); +template void igl::jet, Eigen::Matrix >(Eigen::MatrixBase > const&, double, double, Eigen::PlainObjectBase >&); +template void igl::jet, Eigen::Matrix >(Eigen::MatrixBase > const&, double, double, Eigen::PlainObjectBase >&); +template void igl::jet, Eigen::Matrix >(Eigen::MatrixBase > const&, double, double, Eigen::PlainObjectBase >&); + +template void igl::jet, Eigen::Matrix >(Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/jet.h b/src/igl/jet.h new file mode 100644 index 0000000000..411b53af3a --- /dev/null +++ b/src/igl/jet.h @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_JET_H +#define IGL_JET_H +#include "igl_inline.h" +//#ifndef IGL_NO_EIGEN +# include +//#endif +namespace igl +{ + // JET like MATLAB's jet + // + // Inputs: + // m number of colors + // Outputs: + // J m by list of RGB colors between 0 and 1 + // +//#ifndef IGL_NO_EIGEN +// void jet(const int m, Eigen::MatrixXd & J); +//#endif + // Wrapper for directly computing [r,g,b] values for a given factor f between + // 0 and 1 + // + // Inputs: + // f factor determining color value as if 0 was min and 1 was max + // Outputs: + // r red value + // g green value + // b blue value + template + IGL_INLINE void jet(const T f, T * rgb); + template + IGL_INLINE void jet(const T f, T & r, T & g, T & b); + // Inputs: + // Z #Z list of factors + // normalize whether to normalize Z to be tightly between [0,1] + // Outputs: + // C #C by 3 list of rgb colors + template + IGL_INLINE void jet( + const Eigen::MatrixBase & Z, + const bool normalize, + Eigen::PlainObjectBase & C); + // Inputs: + // min_z value at blue + // max_z value at red + template + IGL_INLINE void jet( + const Eigen::MatrixBase & Z, + const double min_Z, + const double max_Z, + Eigen::PlainObjectBase & C); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "jet.cpp" +#endif + +#endif diff --git a/src/igl/knn.cpp b/src/igl/knn.cpp new file mode 100644 index 0000000000..b2e355df19 --- /dev/null +++ b/src/igl/knn.cpp @@ -0,0 +1,105 @@ +#include "knn.h" +#include "parallel_for.h" + +#include +#include + +namespace igl { + template + IGL_INLINE void knn(const Eigen::MatrixBase& P, + const KType & k, + const std::vector > & point_indices, + const Eigen::MatrixBase& CH, + const Eigen::MatrixBase& CN, + const Eigen::MatrixBase& W, + Eigen::PlainObjectBase & I) + { + typedef typename DerivedCN::Scalar CentersType; + typedef typename DerivedW::Scalar WidthsType; + + typedef Eigen::Matrix RowVector3PType; + + int n = P.rows(); + const KType real_k = std::min(n,k); + + auto distance_to_width_one_cube = [](RowVector3PType point){ + return std::sqrt(std::pow(std::max(std::abs(point(0))-1,0.0),2) + + std::pow(std::max(std::abs(point(1))-1,0.0),2) + + std::pow(std::max(std::abs(point(2))-1,0.0),2)); + }; + + auto distance_to_cube = [&distance_to_width_one_cube] + (RowVector3PType point, + Eigen::Matrix cube_center, + WidthsType cube_width){ + RowVector3PType transformed_point = (point-cube_center)/cube_width; + return cube_width*distance_to_width_one_cube(transformed_point); + }; + + I.resize(n,real_k); + + igl::parallel_for(n,[&](int i) + { + int points_found = 0; + RowVector3PType point_of_interest = P.row(i); + + //To make my priority queue take both points and octree cells, + //I use the indices 0 to n-1 for the n points, + // and the indices n to n+m-1 for the m octree cells + + // Using lambda to compare elements. + auto cmp = [&point_of_interest, &P, &CN, &W, + &n, &distance_to_cube](int left, int right) { + double leftdistance, rightdistance; + if(left < n){ //left is a point index + leftdistance = (P.row(left) - point_of_interest).norm(); + } else { //left is an octree cell + leftdistance = distance_to_cube(point_of_interest, + CN.row(left-n), + W(left-n)); + } + + if(right < n){ //left is a point index + rightdistance = (P.row(right) - point_of_interest).norm(); + } else { //left is an octree cell + rightdistance = distance_to_cube(point_of_interest, + CN.row(right-n), + W(right-n)); + } + return leftdistance >= rightdistance; + }; + + std::priority_queue, + decltype(cmp)> queue(cmp); + + queue.push(n); //This is the 0th octree cell (ie the root) + while(points_found < real_k){ + IndexType curr_cell_or_point = queue.top(); + queue.pop(); + if(curr_cell_or_point < n){ //current index is for is a point + I(i,points_found) = curr_cell_or_point; + points_found++; + } else { + IndexType curr_cell = curr_cell_or_point - n; + if(CH(curr_cell,0) == -1){ //In the case of a leaf + if(point_indices.at(curr_cell).size() > 0){ + //Assumption: Leaves either have one point, or none + queue.push(point_indices.at(curr_cell).at(0)); + } + } else { //Not a leaf + for(int j = 0; j < 8; j++){ + //+n to adjust for the octree cells + queue.push(CH(curr_cell,j)+n); + } + } + } + } + },1000); + } +} + + + + diff --git a/src/igl/knn.h b/src/igl/knn.h new file mode 100644 index 0000000000..0411fc3ee5 --- /dev/null +++ b/src/igl/knn.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Gavin Barill +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/ + +#ifndef IGL_KNN +#define IGL_KNN +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Given a 3D set of points P, an whole number k, and an octree + // find the indicies of the k nearest neighbors for each point in P. + // Note that each point is its own neighbor. + // + // The octree data structures used in this function are intended to be the + // same ones output from igl::octree + // + // Inputs: + // P #P by 3 list of point locations + // k number of neighbors to find + // point_indices a vector of vectors, where the ith entry is a vector of + // the indices into P that are the ith octree cell's points + // CH #OctreeCells by 8, where the ith row is the indices of + // the ith octree cell's children + // CN #OctreeCells by 3, where the ith row is a 3d row vector + // representing the position of the ith cell's center + // W #OctreeCells, a vector where the ith entry is the width + // of the ith octree cell + // Outputs: + // I #P by k list of k-nearest-neighbor indices into P + template + IGL_INLINE void knn(const Eigen::MatrixBase& P, + const KType & k, + const std::vector > & point_indices, + const Eigen::MatrixBase& CH, + const Eigen::MatrixBase& CN, + const Eigen::MatrixBase& W, + Eigen::PlainObjectBase & I); +} +#ifndef IGL_STATIC_LIBRARY +# include "knn.cpp" +#endif +#endif + diff --git a/src/igl/launch_medit.cpp b/src/igl/launch_medit.cpp new file mode 100644 index 0000000000..151af8e5b9 --- /dev/null +++ b/src/igl/launch_medit.cpp @@ -0,0 +1,69 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "launch_medit.h" +#include "writeMESH.h" +#include +#include +#include +#include + +#define MEDIT_PATH "/opt/local/bin/medit" +#define TEMP_MESH_FILE "/var/tmp/temp.mesh" +#define TEMP_MEDIT_FILE "/var/tmp/temp.medit" + +template +IGL_INLINE int igl::launch_medit( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & T, + const Eigen::PlainObjectBase & F, + const bool wait) +{ + using namespace std; + // Build medit command, end with & so command returns without waiting + stringstream command; + command<, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool); +#endif + diff --git a/src/igl/launch_medit.h b/src/igl/launch_medit.h new file mode 100644 index 0000000000..8a324bb228 --- /dev/null +++ b/src/igl/launch_medit.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LAUNCH_MEDIT_H +#define IGL_LAUNCH_MEDIT_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Writes the tetmesh in (V,T,F) to a temporary file, opens it with medit + // (forking with a system call) and returns + // + // + // Templates: + // DerivedV real-value: i.e. from MatrixXd + // DerivedT integer-value: i.e. from MatrixXi + // DerivedF integer-value: i.e. from MatrixXi + // Inputs: + // V double matrix of vertex positions #V by 3 + // T #T list of tet indices into vertex positions + // F #F list of face indices into vertex positions + // wait whether to wait for medit process to finish before returning + // Returns returned value of system call (probably not useful if wait=false + // because of the fork) + template + IGL_INLINE int launch_medit( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & T, + const Eigen::PlainObjectBase & F, + const bool wait); +} + +#ifndef IGL_STATIC_LIBRARY +# include "launch_medit.cpp" +#endif + +#endif + diff --git a/src/igl/lbs_matrix.cpp b/src/igl/lbs_matrix.cpp new file mode 100644 index 0000000000..a80f74be0d --- /dev/null +++ b/src/igl/lbs_matrix.cpp @@ -0,0 +1,184 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "lbs_matrix.h" + +IGL_INLINE void igl::lbs_matrix( + const Eigen::MatrixXd & V, + const Eigen::MatrixXd & W, + Eigen::MatrixXd & M) +{ + using namespace Eigen; + // Number of dimensions + const int dim = V.cols(); + // Number of model points + const int n = V.rows(); + // Number of skinning transformations/weights + const int m = W.cols(); + + // Assumes that first n rows of weights correspond to V + assert(W.rows() >= n); + + M.resize(n,(dim+1)*m); + for(int j = 0;j& M) +{ + // number of mesh vertices + int n = V.rows(); + assert(n == W.rows()); + // dimension of mesh + int dim = V.cols(); + // number of handles + int m = W.cols(); + + M.resize(n*dim,m*dim*(dim+1)); + + // loop over coordinates of mesh vertices + for(int x = 0; x < dim; x++) + { + // loop over mesh vertices + for(int j = 0; j < n; j++) + { + // loop over handles + for(int i = 0; i < m; i++) + { + // loop over cols of affine transformations + for(int c = 0; c < (dim+1); c++) + { + double value = W(j,i); + if(c& M) +{ + // number of mesh vertices + int n = V.rows(); + assert(n == W.rows()); + assert(n == WI.rows()); + // dimension of mesh + int dim = V.cols(); + // number of handles + int m = WI.maxCoeff()+1; + // max number of influencing handles + int k = W.cols(); + assert(k == WI.cols()); + + M.resize(n*dim,m*dim*(dim+1)); + + // loop over coordinates of mesh vertices + for(int x = 0; x < dim; x++) + { + // loop over mesh vertices + for(int j = 0; j < n; j++) + { + // loop over handles + for(int i = 0; i < k; i++) + { + // loop over cols of affine transformations + for(int c = 0; c < (dim+1); c++) + { + double value = W(j,i); + if(c sM; + lbs_matrix_column(V,W,WI,sM); + M = MatrixXd(sM); +} diff --git a/src/igl/lbs_matrix.h b/src/igl/lbs_matrix.h new file mode 100644 index 0000000000..307273d209 --- /dev/null +++ b/src/igl/lbs_matrix.h @@ -0,0 +1,94 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LBS_MATRIX_H +#define IGL_LBS_MATRIX_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // LBS_MATRIX Linear blend skinning can be expressed by V' = M * T where V' is + // a #V by dim matrix of deformed vertex positions (one vertex per row), M is a + // #V by (dim+1)*#T (composed of weights and rest positions) and T is a + // #T*(dim+1) by dim matrix of #T stacked transposed transformation matrices. + // See equations (1) and (2) in "Fast Automatic Skinning Transformations" + // [Jacobson et al 2012] + // + // Inputs: + // V #V by dim list of rest positions + // W #V+ by #T list of weights + // Outputs: + // M #V by #T*(dim+1) + // + // In MATLAB: + // kron(ones(1,size(W,2)),[V ones(size(V,1),1)]).*kron(W,ones(1,size(V,2)+1)) + IGL_INLINE void lbs_matrix( + const Eigen::MatrixXd & V, + const Eigen::MatrixXd & W, + Eigen::MatrixXd & M); + // LBS_MATRIX construct a matrix that when multiplied against a column of + // affine transformation entries computes new coordinates of the vertices + // + // I'm not sure it makes since that the result is stored as a sparse matrix. + // The number of non-zeros per row *is* dependent on the number of mesh + // vertices and handles. + // + // Inputs: + // V #V by dim list of vertex rest positions + // W #V by #handles list of correspondence weights + // Output: + // M #V * dim by #handles * dim * (dim+1) matrix such that + // new_V(:) = LBS(V,W,A) = reshape(M * A,size(V)), where A is a column + // vectors formed by the entries in each handle's dim by dim+1 + // transformation matrix. Specifcally, A = + // reshape(permute(Astack,[3 1 2]),n*dim*(dim+1),1) + // or A = [Lxx;Lyx;Lxy;Lyy;tx;ty], and likewise for other dim + // if Astack(:,:,i) is the dim by (dim+1) transformation at handle i + IGL_INLINE void lbs_matrix_column( + const Eigen::MatrixXd & V, + const Eigen::MatrixXd & W, + Eigen::SparseMatrix& M); + IGL_INLINE void lbs_matrix_column( + const Eigen::MatrixXd & V, + const Eigen::MatrixXd & W, + Eigen::MatrixXd & M); + // Same as LBS_MATRIX above but instead of giving W as a full matrix of weights + // (each vertex has #handles weights), a constant number of weights are given + // for each vertex. + // + // Inputs: + // V #V by dim list of vertex rest positions + // W #V by k list of k correspondence weights per vertex + // WI #V by k list of k correspondence weight indices per vertex. Such that + // W(j,WI(i)) gives the ith most significant correspondence weight on vertex j + // Output: + // M #V * dim by #handles * dim * (dim+1) matrix such that + // new_V(:) = LBS(V,W,A) = reshape(M * A,size(V)), where A is a column + // vectors formed by the entries in each handle's dim by dim+1 + // transformation matrix. Specifcally, A = + // reshape(permute(Astack,[3 1 2]),n*dim*(dim+1),1) + // or A = [Lxx;Lyx;Lxy;Lyy;tx;ty], and likewise for other dim + // if Astack(:,:,i) is the dim by (dim+1) transformation at handle i + // + IGL_INLINE void lbs_matrix_column( + const Eigen::MatrixXd & V, + const Eigen::MatrixXd & W, + const Eigen::MatrixXi & WI, + Eigen::SparseMatrix& M); + IGL_INLINE void lbs_matrix_column( + const Eigen::MatrixXd & V, + const Eigen::MatrixXd & W, + const Eigen::MatrixXi & WI, + Eigen::MatrixXd & M); +} +#ifndef IGL_STATIC_LIBRARY +#include "lbs_matrix.cpp" +#endif +#endif diff --git a/src/igl/lexicographic_triangulation.cpp b/src/igl/lexicographic_triangulation.cpp new file mode 100644 index 0000000000..ce9e796ae0 --- /dev/null +++ b/src/igl/lexicographic_triangulation.cpp @@ -0,0 +1,132 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "lexicographic_triangulation.h" +#include "sortrows.h" + +#include +#include + +template< + typename DerivedP, + typename Orient2D, + typename DerivedF + > +IGL_INLINE void igl::lexicographic_triangulation( + const Eigen::PlainObjectBase& P, + Orient2D orient2D, + Eigen::PlainObjectBase& F) +{ + typedef typename DerivedP::Scalar Scalar; + const size_t num_pts = P.rows(); + if (num_pts < 3) { + throw "At least 3 points are required for triangulation!"; + } + + // Sort points in lexicographic order. + DerivedP ordered_P; + Eigen::VectorXi order; + igl::sortrows(P, true, ordered_P, order); + + std::vector faces; + std::list boundary; + const Scalar p0[] = {ordered_P(0, 0), ordered_P(0, 1)}; + const Scalar p1[] = {ordered_P(1, 0), ordered_P(1, 1)}; + for (size_t i=2; i 0) { + for (size_t j=0; j<=i-2; j++) { + faces.push_back({order[j], order[j+1], order[i]}); + } + } else if (orientation < 0) { + for (size_t j=0; j<=i-2; j++) { + faces.push_back({order[j+1], order[j], order[i]}); + } + } + // Initialize current boundary. + boundary.insert(boundary.end(), order.data(), order.data()+i+1); + if (orientation < 0) { + boundary.reverse(); + } + } + } else { + const size_t bd_size = boundary.size(); + assert(bd_size >= 3); + std::vector orientations; + for (auto itr=boundary.begin(); itr!=boundary.end(); itr++) { + auto next_itr = std::next(itr, 1); + if (next_itr == boundary.end()) { + next_itr = boundary.begin(); + } + const Scalar bd_p0[] = {P(*itr, 0), P(*itr, 1)}; + const Scalar bd_p1[] = {P(*next_itr, 0), P(*next_itr, 1)}; + auto orientation = orient2D(bd_p0, bd_p1, curr_p); + if (orientation < 0) { + faces.push_back({*next_itr, *itr, order[i]}); + } + orientations.push_back(orientation); + } + + auto left_itr = boundary.begin(); + auto right_itr = boundary.begin(); + auto curr_itr = boundary.begin(); + for (size_t j=0; j= 0 && orientations[prev] < 0) { + right_itr = curr_itr; + } else if (orientations[j] < 0 && orientations[prev] >= 0) { + left_itr = curr_itr; + } + } + assert(left_itr != right_itr); + + for (auto itr=left_itr; itr!=right_itr; itr++) { + if (itr == boundary.end()) itr = boundary.begin(); + if (itr == right_itr) break; + if (itr == left_itr || itr == right_itr) continue; + itr = boundary.erase(itr); + if (itr == boundary.begin()) { + itr = boundary.end(); + } else { + itr--; + } + } + + if (right_itr == boundary.begin()) { + assert(std::next(left_itr, 1) == boundary.end()); + boundary.insert(boundary.end(), order[i]); + } else { + assert(std::next(left_itr, 1) == right_itr); + boundary.insert(right_itr, order[i]); + } + } + } + + const size_t num_faces = faces.size(); + if (num_faces == 0) { + // All input points are collinear. + // Do nothing here. + } else { + F.resize(num_faces, 3); + for (size_t i=0; i, short (*)(double const*, double const*, double const*), Eigen::Matrix >(Eigen::PlainObjectBase > const&, short (*)(double const*, double const*, double const*), Eigen::PlainObjectBase >&); +#endif \ No newline at end of file diff --git a/src/igl/lexicographic_triangulation.h b/src/igl/lexicographic_triangulation.h new file mode 100644 index 0000000000..4e0b81eb52 --- /dev/null +++ b/src/igl/lexicographic_triangulation.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_LEXICOGRAPHIC_TRIANGULATION_H +#define IGL_LEXICOGRAPHIC_TRIANGULATION_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Given a set of points in 2D, return a lexicographic triangulation of these + // points. + // + // Inputs: + // P #P by 2 list of vertex positions + // orient2D A functor such that orient2D(pa, pb, pc) returns + // 1 if pa,pb,pc forms a conterclockwise triangle. + // -1 if pa,pb,pc forms a clockwise triangle. + // 0 if pa,pb,pc are collinear. + // where the argument pa,pb,pc are of type Scalar[2]. + // + // Outputs: + // F #F by 3 of faces in lexicographic triangulation. + template< + typename DerivedP, + typename Orient2D, + typename DerivedF + > + IGL_INLINE void lexicographic_triangulation( + const Eigen::PlainObjectBase& P, + Orient2D orient2D, + Eigen::PlainObjectBase& F); +} + + + + +#ifndef IGL_STATIC_LIBRARY +# include "lexicographic_triangulation.cpp" +#endif +#endif diff --git a/src/igl/lim/lim.cpp b/src/igl/lim/lim.cpp new file mode 100644 index 0000000000..1f46590628 --- /dev/null +++ b/src/igl/lim/lim.cpp @@ -0,0 +1,137 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Christian Schüller +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "lim.h" +#include + + +IGL_INLINE igl::lim::State igl::lim::lim( + Eigen::Matrix& vertices, + const Eigen::Matrix& initialVertices, + const Eigen::Matrix& elements, + const Eigen::SparseMatrix& constraintMatrix, + const Eigen::Matrix& constraintTargets, + Energy energyType, + double tolerance, + int maxIteration, + bool findLocalMinima) +{ + return (State)ComputeLIM( + vertices, + initialVertices, + elements, + constraintMatrix, + constraintTargets, + (EnergyType)energyType, + tolerance, + maxIteration, + findLocalMinima + ); +} + +IGL_INLINE igl::lim::State igl::lim::lim( + Eigen::Matrix& vertices, + const Eigen::Matrix& initialVertices, + const Eigen::Matrix& elements, + const Eigen::SparseMatrix& constraintMatrix, + const Eigen::Matrix& constraintTargets, + Energy energyType, + double tolerance, + int maxIteration, + bool findLocalMinima, + bool enableOuput, + bool enableBarriers, + bool enableAlphaUpdate, + double beta, + double eps) +{ + return (State)ComputeLIM( + vertices, + initialVertices, + elements, + constraintMatrix, + constraintTargets, + (EnergyType)energyType, + tolerance, + maxIteration, + findLocalMinima, + enableOuput, + enableBarriers, + enableAlphaUpdate, + beta, + eps + ); +} + +IGL_INLINE igl::lim::State igl::lim::lim( + Eigen::Matrix& vertices, + const Eigen::Matrix& initialVertices, + const Eigen::Matrix& elements, + const std::vector& borderVertices, + const Eigen::Matrix& gradients, + const Eigen::SparseMatrix& constraintMatrix, + const Eigen::Matrix& constraintTargets, + Energy energyType, + double tolerance, + int maxIteration, + bool findLocalMinima) +{ + return (State)ComputeLIM( + vertices, + initialVertices, + elements, + borderVertices, + gradients, + constraintMatrix, + constraintTargets, + (EnergyType)energyType, + tolerance, + maxIteration, + findLocalMinima + ); +} + +IGL_INLINE igl::lim::State igl::lim::lim( + Eigen::Matrix& vertices, + const Eigen::Matrix& initialVertices, + const Eigen::Matrix& elements, + const std::vector& borderVertices, + const Eigen::Matrix& gradients, + const Eigen::SparseMatrix& constraintMatrix, + const Eigen::Matrix& constraintTargets, + Energy energyType, + double tolerance, + int maxIteration, + bool findLocalMinima, + bool enableOuput, + bool enableBarriers, + bool enableAlphaUpdate, + double beta, + double eps) +{ + return (State)ComputeLIM( + vertices, + initialVertices, + elements, + borderVertices, + gradients, + constraintMatrix, + constraintTargets, + (EnergyType)energyType, + tolerance, + maxIteration, + findLocalMinima, + enableOuput, + enableBarriers, + enableAlphaUpdate, + beta, + eps); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/lim/lim.h b/src/igl/lim/lim.h new file mode 100644 index 0000000000..c4d3f4ad47 --- /dev/null +++ b/src/igl/lim/lim.h @@ -0,0 +1,133 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Christian Schüller +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LIM_LIM_H +#define IGL_LIM_LIM_H +#include +#include +#include + +namespace igl +{ + namespace lim + { + // Computes a locally injective mapping of a triangle or tet-mesh based on + // a deformation energy subject to some provided linear positional + // constraints Cv-d. + // + // Inputs: + // vertices vx3 matrix containing vertex position of the mesh + // initialVertices vx3 matrix containing vertex position of initial + // rest pose mesh + // elements exd matrix containing vertex indices of all elements + // borderVertices (only needed for 2D LSCM) vector containing indices + // of border vertices + // gradients (only needed for 2D Poisson) vector containing + // partial derivatives of target element gradients + // (structure is: [xx_0, xy_0, xx_1, xy_1, ..., xx_v, + // xy_v, yx_0, yy_0, yx_1, yy_1, ..., yx_v, yy_v]') + // constraintMatrix C: (c)x(v*(d-1)) sparse linear positional constraint + // matrix. X an Y-coordinates are alternatingly stacked + // per row (structure for triangles: [x_1, y_1, x_2, + // y_2, ..., x_v,y_v]) + // constraintTargets d: c vector target positions + // energyType type of used energy: + // Dirichlet, Laplacian, Green, ARAP, LSCM, Poisson (only 2D), UniformLaplacian, Identity + // tolerance max squared positional constraints error + // maxIteration max number of iterations + // findLocalMinima iterating until a local minima is found. If not + // enabled only tolerance must be fulfilled. + // enableOutput (optional) enables the output (#iteration / hessian correction / step size / positional constraints / barrier constraints / deformation energy) (default : true) + // enableBarriers (optional) enables the non-flip constraints (default = true) + // enableAlphaUpdate (optional) enables dynamic alpha weight adjustment (default = true) + // beta (optional) steepness factor of barrier slopes (default: ARAP/LSCM = 0.01, Green = 1) + // eps (optional) smallest valid triangle area (default: 1e-5 * smallest triangle) + // + // where: + // v : # vertices + // c : # linear constraints + // e : # elements of mesh + // d : # vertices per element (triangle = 3, tet = 4) + //-------------------------------------------------------------------------- + // Output: + // vertices vx3 matrix containing resulting vertex position of the + // mesh + //-------------------------------------------------------------------------- + // Return values: + // Succeeded : Successful optimization with fulfilled tolerance + // LocalMinima : Convergenged to a local minima / tolerance not fulfilled + // IterationLimit : Max iteration reached before tolerance was fulfilled + // Infeasible : not feasible -> has inverted elements (decrease eps?) + + enum Energy { Dirichlet = 0, Laplacian=1, Green=2, ARAP=3, LSCM=4, Poisson=5, UniformLaplacian=6, Identity=7 }; + enum State { Uninitialized = -4, Infeasible = -3, IterationLimit = -2, LocalMinima = -1, Running = 0, Succeeded = 1 }; + + State lim( + Eigen::Matrix& vertices, + const Eigen::Matrix& initialVertices, + const Eigen::Matrix& elements, + const Eigen::SparseMatrix& constraintMatrix, + const Eigen::Matrix& constraintTargets, + Energy energyType, + double tolerance, + int maxIteration, + bool findLocalMinima); + + State lim( + Eigen::Matrix& vertices, + const Eigen::Matrix& initialVertices, + const Eigen::Matrix& elements, + const Eigen::SparseMatrix& constraintMatrix, + const Eigen::Matrix& constraintTargets, + Energy energyType, + double tolerance, + int maxIteration, + bool findLocalMinima, + bool enableOuput, + bool enableBarriers, + bool enableAlphaUpdate, + double beta, + double eps); + + State lim( + Eigen::Matrix& vertices, + const Eigen::Matrix& initialVertices, + const Eigen::Matrix& elements, + const std::vector& borderVertices, + const Eigen::Matrix& gradients, + const Eigen::SparseMatrix& constraintMatrix, + const Eigen::Matrix& constraintTargets, + Energy energyType, + double tolerance, + int maxIteration, + bool findLocalMinima); + + State lim( + Eigen::Matrix& vertices, + const Eigen::Matrix& initialVertices, + const Eigen::Matrix& elements, + const std::vector& borderVertices, + const Eigen::Matrix& gradients, + const Eigen::SparseMatrix& constraintMatrix, + const Eigen::Matrix& constraintTargets, + Energy energyType, + double tolerance, + int maxIteration, + bool findLocalMinima, + bool enableOuput, + bool enableBarriers, + bool enableAlphaUpdate, + double beta, + double eps); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "lim.cpp" +#endif + +#endif diff --git a/src/igl/limit_faces.cpp b/src/igl/limit_faces.cpp new file mode 100644 index 0000000000..e54c40b455 --- /dev/null +++ b/src/igl/limit_faces.cpp @@ -0,0 +1,63 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "limit_faces.h" + +#include +#include + +template +IGL_INLINE void igl::limit_faces( + const MatF & F, + const VecL & L, + const bool exclusive, + MatF & LF) +{ + using namespace std; + using namespace Eigen; + vector in(F.rows(),false); + int num_in = 0; + // loop over faces + for(int i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LIMIT_FACES_H +#define IGL_LIMIT_FACES_H +#include "igl_inline.h" +namespace igl +{ + // LIMIT_FACES limit given faces F to those which contain (only) indices found + // in L. + // + // [LF] = limit_faces(F,L,exclusive); + // [LF,in] = limit_faces(F,L,exclusive); + // + // Templates: + // MatF matrix type of faces, matrixXi + // VecL matrix type of vertex indices, VectorXi + // Inputs: + // F #F by 3 list of face indices + // L #L by 1 list of allowed indices + // exclusive flag specifying whether a face is included only if all its + // indices are in L, default is false + // Outputs: + // LF #LF by 3 list of remaining faces after limiting + // in #F list of whether given face was included + // + template + IGL_INLINE void limit_faces( + const MatF & F, + const VecL & L, + const bool exclusive, + MatF & LF); +} + +#ifndef IGL_STATIC_LIBRARY +# include "limit_faces.cpp" +#endif + +#endif diff --git a/src/igl/line_field_missmatch.cpp b/src/igl/line_field_missmatch.cpp new file mode 100644 index 0000000000..21c45b7101 --- /dev/null +++ b/src/igl/line_field_missmatch.cpp @@ -0,0 +1,144 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Nico Pietroni +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "line_field_missmatch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace igl { +template +class MissMatchCalculatorLine +{ +public: + + const Eigen::PlainObjectBase &V; + const Eigen::PlainObjectBase &F; + const Eigen::PlainObjectBase &PD1; + const Eigen::PlainObjectBase &PD2; + DerivedV N; + +private: + // internal + std::vector V_border; // bool + std::vector > VF; + std::vector > VFi; + DerivedF TT; + DerivedF TTi; + + +private: + + //compute the mismatch between 2 faces + inline int MissMatchByLine(const int f0, + const int f1) + { + Eigen::Matrix dir0 = PD1.row(f0); + Eigen::Matrix dir1 = PD1.row(f1); + Eigen::Matrix n0 = N.row(f0); + Eigen::Matrix n1 = N.row(f1); + + Eigen::Matrix dir1Rot = igl::rotation_matrix_from_directions(n1,n0)*dir1; + dir1Rot.normalize(); + + // TODO: this should be equivalent to the other code below, to check! + // Compute the angle between the two vectors + // double a0 = atan2(dir0.dot(B2.row(f0)),dir0.dot(B1.row(f0))); + // double a1 = atan2(dir1Rot.dot(B2.row(f0)),dir1Rot.dot(B1.row(f0))); + // + // double angle_diff = a1-a0; //VectToAngle(f0,dir1Rot); + + double angle_diff = atan2(dir1Rot.dot(PD2.row(f0)),dir1Rot.dot(PD1.row(f0))); + + double step=igl::PI; + int i=(int)std::floor((angle_diff/step)+0.5); + assert((i>=-2)&&(i<=2)); + int k=0; + if (i>=0) + k=i%2; + else + k=(2+i)%2; + + assert((k==0)||(k==1)); + return (k*2); + } + +public: + + inline MissMatchCalculatorLine(const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const Eigen::PlainObjectBase &_PD1, + const Eigen::PlainObjectBase &_PD2 + ): + V(_V), + F(_F), + PD1(_PD1), + PD2(_PD2) + { + igl::per_face_normals(V,F,N); + V_border = igl::is_border_vertex(V,F); + igl::vertex_triangle_adjacency(V,F,VF,VFi); + igl::triangle_triangle_adjacency(F,TT,TTi); + } + + inline void calculateMissmatchLine(Eigen::PlainObjectBase &Handle_MMatch) + { + Handle_MMatch.setConstant(F.rows(),3,-1); + for (unsigned int i=0;i +IGL_INLINE void igl::line_field_missmatch(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const bool isCombed, + Eigen::PlainObjectBase &missmatch) +{ + DerivedV PD1_combed; + DerivedV PD2_combed; + + if (!isCombed) + igl::comb_line_field(V,F,PD1,PD1_combed); + else + { + PD1_combed = PD1; + } + Eigen::MatrixXd B1,B2,B3; + igl::local_basis(V,F,B1,B2,B3); + PD2_combed = igl::rotate_vectors(PD1_combed, Eigen::VectorXd::Constant(1,igl::PI/2), B1, B2); + igl::MissMatchCalculatorLine sf(V, F, PD1_combed, PD2_combed); + sf.calculateMissmatchLine(missmatch); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/line_field_missmatch.h b/src/igl/line_field_missmatch.h new file mode 100644 index 0000000000..a939685cb1 --- /dev/null +++ b/src/igl/line_field_missmatch.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Nico Pietroni +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_LINE_FIELD_MISSMATCH_H +#define IGL_LINE_FIELD_MISSMATCH_H +#include "igl_inline.h" +#include +namespace igl +{ + // Calculates the missmatch (integer), at each face edge, of a cross field defined on the mesh faces. + // The integer missmatch is a multiple of pi/2 that transforms the cross on one side of the edge to + // the cross on the other side. It represents the deviation from a Lie connection across the edge. + + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (quad) indices + // PD1 #F by 3 eigen Matrix of the first per face cross field vector + // PD2 #F by 3 eigen Matrix of the second per face cross field vector + // isCombed boolean, specifying whether the field is combed (i.e. matching has been precomputed. + // If not, the field is combed first. + // Output: + // Handle_MMatch #F by 3 eigen Matrix containing the integer missmatch of the cross field + // across all face edges + // + + template + IGL_INLINE void line_field_missmatch(const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + const Eigen::PlainObjectBase &PD1, + const bool isCombed, + Eigen::PlainObjectBase &missmatch); +} +#ifndef IGL_STATIC_LIBRARY +#include "line_field_missmatch.cpp" +#endif + +#endif diff --git a/src/igl/line_search.cpp b/src/igl/line_search.cpp new file mode 100644 index 0000000000..c94eeaa529 --- /dev/null +++ b/src/igl/line_search.cpp @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Michael Rabinovich +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "line_search.h" + +IGL_INLINE double igl::line_search( + Eigen::MatrixXd& x, + const Eigen::MatrixXd& d, + double step_size, + std::function energy, + double cur_energy) +{ + double old_energy; + if (cur_energy > 0) + { + old_energy = cur_energy; + } + else + { + old_energy = energy(x); // no energy was given -> need to compute the current energy + } + double new_energy = old_energy; + int cur_iter = 0; int MAX_STEP_SIZE_ITER = 12; + + while (new_energy >= old_energy && cur_iter < MAX_STEP_SIZE_ITER) + { + Eigen::MatrixXd new_x = x + step_size * d; + + double cur_e = energy(new_x); + if ( cur_e >= old_energy) + { + step_size /= 2; + } + else + { + x = new_x; + new_energy = cur_e; + } + cur_iter++; + } + return new_energy; +} + + +#ifdef IGL_STATIC_LIBRARY +#endif diff --git a/src/igl/line_search.h b/src/igl/line_search.h new file mode 100644 index 0000000000..057c6a43f2 --- /dev/null +++ b/src/igl/line_search.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Michael Rabinovich +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LINE_SEARCH_H +#define IGL_LINE_SEARCH_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Implement a bisection linesearch to minimize a mesh-based energy on vertices given at 'x' at a search direction 'd', + // with initial step size. Stops when a point with lower energy is found, or after maximal iterations have been reached. + // + // Inputs: + // x #X by dim list of variables + // d #X by dim list of a given search direction + // i_step_size initial step size + // energy A function to compute the mesh-based energy (return an energy that is bigger than 0) + // cur_energy(OPTIONAL) The energy at the given point. Helps save redundant computations. + // This is optional. If not specified, the function will compute it. + // Outputs: + // x #X by dim list of variables at the new location + // Returns the energy at the new point 'x' + IGL_INLINE double line_search( + Eigen::MatrixXd& x, + const Eigen::MatrixXd& d, + double i_step_size, + std::function energy, + double cur_energy = -1); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "line_search.cpp" +#endif + +#endif diff --git a/src/igl/line_segment_in_rectangle.cpp b/src/igl/line_segment_in_rectangle.cpp new file mode 100644 index 0000000000..6f47c7d4c7 --- /dev/null +++ b/src/igl/line_segment_in_rectangle.cpp @@ -0,0 +1,103 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "line_segment_in_rectangle.h" + +IGL_INLINE bool igl::line_segment_in_rectangle( + const Eigen::Vector2d & s, + const Eigen::Vector2d & d, + const Eigen::Vector2d & A, + const Eigen::Vector2d & B) +{ + using namespace std; + using namespace Eigen; + // http://stackoverflow.com/a/100165/148668 + const auto SegmentIntersectRectangle = [](double a_rectangleMinX, + double a_rectangleMinY, + double a_rectangleMaxX, + double a_rectangleMaxY, + double a_p1x, + double a_p1y, + double a_p2x, + double a_p2y)->bool + { + // Find min and max X for the segment + + double minX = a_p1x; + double maxX = a_p2x; + + if(a_p1x > a_p2x) + { + minX = a_p2x; + maxX = a_p1x; + } + + // Find the intersection of the segment's and rectangle's x-projections + + if(maxX > a_rectangleMaxX) + { + maxX = a_rectangleMaxX; + } + + if(minX < a_rectangleMinX) + { + minX = a_rectangleMinX; + } + + if(minX > maxX) // If their projections do not intersect return false + { + return false; + } + + // Find corresponding min and max Y for min and max X we found before + + double minY = a_p1y; + double maxY = a_p2y; + + double dx = a_p2x - a_p1x; + + if(fabs(dx) > 0.0000001) + { + double a = (a_p2y - a_p1y) / dx; + double b = a_p1y - a * a_p1x; + minY = a * minX + b; + maxY = a * maxX + b; + } + + if(minY > maxY) + { + double tmp = maxY; + maxY = minY; + minY = tmp; + } + + // Find the intersection of the segment's and rectangle's y-projections + + if(maxY > a_rectangleMaxY) + { + maxY = a_rectangleMaxY; + } + + if(minY < a_rectangleMinY) + { + minY = a_rectangleMinY; + } + + if(minY > maxY) // If Y-projections do not intersect return false + { + return false; + } + + return true; + }; + const double minX = std::min(A(0),B(0)); + const double minY = std::min(A(1),B(1)); + const double maxX = std::max(A(0),B(0)); + const double maxY = std::max(A(1),B(1)); + bool ret = SegmentIntersectRectangle(minX,minY,maxX,maxY,s(0),s(1),d(0),d(1)); + return ret; +} diff --git a/src/igl/line_segment_in_rectangle.h b/src/igl/line_segment_in_rectangle.h new file mode 100644 index 0000000000..6d7eaf4044 --- /dev/null +++ b/src/igl/line_segment_in_rectangle.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LINE_SEGMENT_IN_RECTANGLE_H +#define IGL_LINE_SEGMENT_IN_RECTANGLE_H +#include "igl_inline.h" +#include +namespace igl +{ + // Determine whether a line segment overlaps with a rectangle. + // + // Inputs: + // s source point of line segment + // d dest point of line segment + // A first corner of rectangle + // B opposite corner of rectangle + // Returns true if line segment is at all inside rectangle + IGL_INLINE bool line_segment_in_rectangle( + const Eigen::Vector2d & s, + const Eigen::Vector2d & d, + const Eigen::Vector2d & A, + const Eigen::Vector2d & B); +} + +#ifndef IGL_STATIC_LIBRARY +# include "line_segment_in_rectangle.cpp" +#endif + +#endif diff --git a/src/igl/linprog.cpp b/src/igl/linprog.cpp new file mode 100644 index 0000000000..b73ffc5139 --- /dev/null +++ b/src/igl/linprog.cpp @@ -0,0 +1,302 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "linprog.h" +#include "slice.h" +#include "slice_into.h" +#include "find.h" +#include "colon.h" +#include + +//#define IGL_LINPROG_VERBOSE +IGL_INLINE bool igl::linprog( + const Eigen::VectorXd & c, + const Eigen::MatrixXd & _A, + const Eigen::VectorXd & b, + const int k, + Eigen::VectorXd & x) +{ + // This is a very literal translation of + // http://www.mathworks.com/matlabcentral/fileexchange/2166-introduction-to-linear-algebra/content/strang/linprog.m + using namespace Eigen; + using namespace std; + bool success = true; + // number of constraints + const int m = _A.rows(); + // number of original variables + const int n = _A.cols(); + // number of iterations + int it = 0; + // maximum number of iterations + //const int MAXIT = 10*m; + const int MAXIT = 100*m; + // residual tolerance + const double tol = 1e-10; + const auto & sign = [](const Eigen::VectorXd & B) -> Eigen::VectorXd + { + Eigen::VectorXd Bsign(B.size()); + for(int i = 0;i0?1:(B(i)<0?-1:0); + } + return Bsign; + }; + // initial (inverse) basis matrix + VectorXd Dv = sign(sign(b).array()+0.5); + Dv.head(k).setConstant(1.); + MatrixXd D = Dv.asDiagonal(); + // Incorporate slack variables + MatrixXd A(_A.rows(),_A.cols()+D.cols()); + A<<_A,D; + // Initial basis + VectorXi B = igl::colon(n,n+m-1); + // non-basis, may turn out that vector<> would be better here + VectorXi N = igl::colon(0,n-1); + int j; + double bmin = b.minCoeff(&j); + int phase; + VectorXd xb; + VectorXd s; + VectorXi J; + if(k>0 && bmin<0) + { + phase = 1; + xb = VectorXd::Ones(m); + // super cost + s.resize(n+m+1); + s<(0,n-1),B(j); + J.resize(B.size()-1); + // [0 1 2 3 4] + // ^ + // [0 1] + // [3 4] + J.head(j) = B.head(j); + J.tail(B.size()-j-1) = B.tail(B.size()-j-1); + B(j) = n+m; + MatrixXd AJ; + igl::slice(A,J,2,AJ); + const VectorXd a = b - AJ.rowwise().sum(); + { + MatrixXd old_A = A; + A.resize(A.rows(),A.cols()+a.cols()); + A<=0 + { + phase = 1; + xb = b.array().abs(); + s.resize(n+m); + // super cost + s<::max(); + // Lagrange mutipliers fro Ax=b + VectorXd yb = D.transpose() * igl::slice(s,B); + while(true) + { + if(MAXIT>0 && it>=MAXIT) + { +#ifdef IGL_LINPROG_VERBOSE + cerr<<"linprog: warning! maximum iterations without convergence."<=-tol*(sN.array().abs().maxCoeff()+1)) + { + break; + } + // increment iteration count + it++; + // apply Bland's rule to avoid cycling + if(df>=0) + { + if(MAXIT == -1) + { +#ifdef IGL_LINPROG_VERBOSE + cerr<<"linprog: warning! degenerate vertex"<().maxCoeff(&q); + } + VectorXd d = D*A.col(N(q)); + VectorXi I; + igl::find((d.array()>tol).eval(),I); + if(I.size() == 0) + { +#ifdef IGL_LINPROG_VERBOSE + cerr<<"linprog: warning! solution is unbounded"<=0) + { + igl::find((xbd.array()==r).eval(),J); + double Bp = igl::slice(B,igl::slice(I,J)).minCoeff(); + // idiotic way of finding index in B of Bp + // code down the line seems to assume p is a scalar though the matlab + // code could find a vector of matches) + (B.array()==Bp).cast().maxCoeff(&p); + } + // update x + xb -= r*d; + xb(p) = r; + // change in f + df = r*rmin; + } + // row vector + RowVectorXd v = D.row(p)/d(p); + yb += v.transpose() * (s(N(q)) - d.transpose()*igl::slice(s,B)); + d(p)-=1; + // update inverse basis matrix + D = D - d*v; + t = B(p); + B(p) = N(q); + if(t>(n+k-1)) + { + // remove qth entry from N + VectorXi old_N = N; + N.resize(N.size()-1); + N.head(q) = old_N.head(q); + N.head(q) = old_N.head(q); + N.tail(old_N.size()-q-1) = old_N.tail(old_N.size()-q-1); + }else + { + N(q) = t; + } + } + // iterative refinement + xb = (xb+D*(b-igl::slice(A,B,2)*xb)).eval(); + // must be due to rounding + VectorXi I; + igl::find((xb.array()<0).eval(),I); + if(I.size()>0) + { + // so correct + VectorXd Z = VectorXd::Zero(I.size(),1); + igl::slice_into(Z,I,xb); + } + // B, xb,n,m,res=A(:,B)*xb-b + if(phase == 2 || it<0) + { + break; + } + if(xb.transpose()*igl::slice(s,B) > tol) + { + it = -it; +#ifdef IGL_LINPROG_VERBOSE + cerr<<"linprog: warning, no feasible solution"<double + { + return (x<0?-1:(x>0?1:0)); + }; + AS.row(i) *= sign(b(i)); + } + MatrixXd In = MatrixXd::Identity(n,n); + MatrixXd P(n+m,2*n+m); + P<< In, -In, MatrixXd::Zero(n,m), + MatrixXd::Zero(m,2*n), Im; + MatrixXd ASP = AS*P; + MatrixXd BSP(0,2*n+m); + if(p>0) + { + MatrixXd BS(p,2*n); + BS< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LINPROG_H +#define IGL_LINPROG_H +#include "igl_inline.h" +#include +namespace igl +{ + // Solve a linear program given in "standard form" + // + // min f'x + // s.t. A( 1:k,:) x <= b(1:k) + // A(k+1:end,:) x = b(k+1:end) + // ** x >= 0 ** + // + // In contrast to other APIs the entries in b may be negative. + // + // Inputs: + // c #x list of linear coefficients + // A #A by #x matrix of linear constraint coefficients + // b #A list of linear constraint right-hand sides + // k number of inequality constraints as first rows of A,b + // Outputs: + // x #x solution vector + // + IGL_INLINE bool linprog( + const Eigen::VectorXd & c, + const Eigen::MatrixXd & A, + const Eigen::VectorXd & b, + const int k, + Eigen::VectorXd & f); + + // Wrapper in friendlier general form (no implicit bounds on x) + // + // min f'x + // s.t. A x <= b + // B x = c + // + // Inputs: + // f #x list of linear coefficients + // A #A by #x matrix of linear inequality constraint coefficients + // b #A list of linear constraint right-hand sides + // B #B by #x matrix of linear equality constraint coefficients + // c #B list of linear constraint right-hand sides + // Outputs: + // x #x solution vector + // + IGL_INLINE bool linprog( + const Eigen::VectorXd & f, + const Eigen::MatrixXd & A, + const Eigen::VectorXd & b, + const Eigen::MatrixXd & B, + const Eigen::VectorXd & c, + Eigen::VectorXd & x); +} + +#ifndef IGL_STATIC_LIBRARY +# include "linprog.cpp" +#endif +#endif diff --git a/src/igl/list_to_matrix.cpp b/src/igl/list_to_matrix.cpp new file mode 100644 index 0000000000..a1ca1926b7 --- /dev/null +++ b/src/igl/list_to_matrix.cpp @@ -0,0 +1,175 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "list_to_matrix.h" + +#include +#include + +#include + +#include "max_size.h" +#include "min_size.h" + +template +IGL_INLINE bool igl::list_to_matrix(const std::vector > & V,Eigen::PlainObjectBase& M) +{ + // number of rows + int m = V.size(); + if(m == 0) + { + M.resize(0,0); + return true; + } + // number of columns + int n = igl::min_size(V); + if(n != igl::max_size(V)) + { + return false; + } + assert(n != -1); + // Resize output + M.resize(m,n); + + // Loop over rows + for(int i = 0;i +IGL_INLINE bool igl::list_to_matrix( + const std::vector > & V, + const int n, + const T & padding, + Eigen::PlainObjectBase& M) +{ + const int m = V.size(); + M.resize(m,n); + for(int i = 0;in) + { + return false; + } + int j = 0; + for(;j +IGL_INLINE bool igl::list_to_matrix(const std::vector & V,Eigen::PlainObjectBase& M) +{ + // number of rows + int m = V.size(); + if(m == 0) + { + //fprintf(stderr,"Error: list_to_matrix() list is empty()\n"); + //return false; + if(Derived::ColsAtCompileTime == 1) + { + M.resize(0,1); + }else if(Derived::RowsAtCompileTime == 1) + { + M.resize(1,0); + }else + { + M.resize(0,0); + } + return true; + } + // Resize output + if(Derived::RowsAtCompileTime == 1) + { + M.resize(1,m); + }else + { + M.resize(m,1); + } + + // Loop over rows + for(int i = 0;i >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +// generated by autoexplicit.sh +// generated by autoexplicit.sh +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); + +#ifdef WIN32 +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +template bool igl::list_to_matrix >(class std::vector > const &, class Eigen::PlainObjectBase > &); +template bool igl::list_to_matrix >(class std::vector > const &,class Eigen::PlainObjectBase > &); +template bool igl::list_to_matrix >(class std::vector > const &,class Eigen::PlainObjectBase > &); +template bool igl::list_to_matrix >(class std::vector > const &,class Eigen::PlainObjectBase > &); +template bool igl::list_to_matrix >(std::vector > const&, Eigen::PlainObjectBase >&); +#endif +#endif diff --git a/src/igl/list_to_matrix.h b/src/igl/list_to_matrix.h new file mode 100644 index 0000000000..1a5881dc15 --- /dev/null +++ b/src/igl/list_to_matrix.h @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LIST_TO_MATRIX_H +#define IGL_LIST_TO_MATRIX_H +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Convert a list (std::vector) of row vectors of the same length to a matrix + // Template: + // T type that can be safely cast to type in Mat via '=' + // Mat Matrix type, must implement: + // .resize(m,n) + // .row(i) = Row + // Inputs: + // V a m-long list of vectors of size n + // Outputs: + // M an m by n matrix + // Returns true on success, false on errors + template + IGL_INLINE bool list_to_matrix( + const std::vector > & V, + Eigen::PlainObjectBase& M); + // Convert a list of row vectors of `n` or less to a matrix and pad on + // the right with `padding`: + // + // Inputs: + // V a m-long list of vectors of size <=n + // n number of columns + // padding value to fill in from right for short rows + // Outputs: + // M an m by n matrix + template + IGL_INLINE bool list_to_matrix( + const std::vector > & V, + const int n, + const T & padding, + Eigen::PlainObjectBase& M); + // Vector wrapper + template + IGL_INLINE bool list_to_matrix(const std::vector & V,Eigen::PlainObjectBase& M); +} + +#ifndef IGL_STATIC_LIBRARY +# include "list_to_matrix.cpp" +#endif + +#endif diff --git a/src/igl/local_basis.cpp b/src/igl/local_basis.cpp new file mode 100644 index 0000000000..599eca739f --- /dev/null +++ b/src/igl/local_basis.cpp @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "local_basis.h" + +#include +#include +#include + +#include +#include + + +template +IGL_INLINE void igl::local_basis( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& B1, + Eigen::PlainObjectBase& B2, + Eigen::PlainObjectBase& B3 + ) +{ + using namespace Eigen; + using namespace std; + B1.resize(F.rows(),3); + B2.resize(F.rows(),3); + B3.resize(F.rows(),3); + + for (unsigned i=0;i v1 = (V.row(F(i,1)) - V.row(F(i,0))).normalized(); + Eigen::Matrix t = V.row(F(i,2)) - V.row(F(i,0)); + Eigen::Matrix v3 = v1.cross(t).normalized(); + Eigen::Matrix v2 = v1.cross(v3).normalized(); + + B1.row(i) = v1; + B2.row(i) = -v2; + B3.row(i) = v3; + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::local_basis, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::local_basis, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/local_basis.h b/src/igl/local_basis.h new file mode 100644 index 0000000000..392796c340 --- /dev/null +++ b/src/igl/local_basis.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LOCALBASIS_H +#define IGL_LOCALBASIS_H + +#include "igl_inline.h" +#include +#include +#include + +namespace igl +{ + // Compute a local orthogonal reference system for each triangle in the given mesh + // Templates: + // DerivedV derived from vertex positions matrix type: i.e. MatrixXd + // DerivedF derived from face indices matrix type: i.e. MatrixXi + // Inputs: + // V eigen matrix #V by 3 + // F #F by 3 list of mesh faces (must be triangles) + // Outputs: + // B1 eigen matrix #F by 3, each vector is tangent to the triangle + // B2 eigen matrix #F by 3, each vector is tangent to the triangle and perpendicular to B1 + // B3 eigen matrix #F by 3, normal of the triangle + // + // See also: adjacency_matrix + template + IGL_INLINE void local_basis( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& B1, + Eigen::PlainObjectBase& B2, + Eigen::PlainObjectBase& B3 + ); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "local_basis.cpp" +#endif + +#endif diff --git a/src/igl/look_at.cpp b/src/igl/look_at.cpp new file mode 100644 index 0000000000..c55c9b2ec5 --- /dev/null +++ b/src/igl/look_at.cpp @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "look_at.h" + +template < + typename Derivedeye, + typename Derivedcenter, + typename Derivedup, + typename DerivedR> +IGL_INLINE void igl::look_at( + const Eigen::PlainObjectBase & eye, + const Eigen::PlainObjectBase & center, + const Eigen::PlainObjectBase & up, + Eigen::PlainObjectBase & R) +{ + typedef Eigen::Matrix Vector3S; + Vector3S f = (center - eye).normalized(); + Vector3S s = f.cross(up).normalized(); + Vector3S u = s.cross(f); + R = Eigen::Matrix::Identity(); + R(0,0) = s(0); + R(0,1) = s(1); + R(0,2) = s(2); + R(1,0) = u(0); + R(1,1) = u(1); + R(1,2) = u(2); + R(2,0) =-f(0); + R(2,1) =-f(1); + R(2,2) =-f(2); + R(0,3) =-s.transpose() * eye; + R(1,3) =-u.transpose() * eye; + R(2,3) = f.transpose() * eye; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::look_at, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/look_at.h b/src/igl/look_at.h new file mode 100644 index 0000000000..883cbf94e9 --- /dev/null +++ b/src/igl/look_at.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LOOK_AT_H +#define IGL_LOOK_AT_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Implementation of the deprecated gluLookAt function. + // + // Inputs: + // eye 3-vector of eye position + // center 3-vector of center reference point + // up 3-vector of up vector + // Outputs: + // R 4x4 rotation matrix + // + template < + typename Derivedeye, + typename Derivedcenter, + typename Derivedup, + typename DerivedR > + IGL_INLINE void look_at( + const Eigen::PlainObjectBase & eye, + const Eigen::PlainObjectBase & center, + const Eigen::PlainObjectBase & up, + Eigen::PlainObjectBase & R); +} + +#ifndef IGL_STATIC_LIBRARY +# include "look_at.cpp" +#endif + +#endif + diff --git a/src/igl/loop.cpp b/src/igl/loop.cpp new file mode 100644 index 0000000000..b4233ca679 --- /dev/null +++ b/src/igl/loop.cpp @@ -0,0 +1,173 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Oded Stein +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "loop.h" + +#include +#include +#include + +#include + +template < + typename DerivedF, + typename SType, + typename DerivedNF> +IGL_INLINE void igl::loop( + const int n_verts, + const Eigen::PlainObjectBase & F, + Eigen::SparseMatrix& S, + Eigen::PlainObjectBase & NF) +{ + typedef Eigen::SparseMatrix SparseMat; + typedef Eigen::Triplet Triplet_t; + + //Ref. https://graphics.stanford.edu/~mdfisher/subdivision.html + //Heavily borrowing from igl::upsample + + DerivedF FF, FFi; + triangle_triangle_adjacency(F, FF, FFi); + std::vector> adjacencyList; + adjacency_list(F, adjacencyList, true); + + //Compute the number and positions of the vertices to insert (on edges) + Eigen::MatrixXi NI = Eigen::MatrixXi::Constant(FF.rows(), FF.cols(), -1); + Eigen::MatrixXi NIdoubles = Eigen::MatrixXi::Zero(FF.rows(), FF.cols()); + Eigen::VectorXi vertIsOnBdry = Eigen::VectorXi::Zero(n_verts); + int counter = 0; + for(int i=0; i tripletList; + for(int i=0; i& localAdjList = adjacencyList[i]; + if(vertIsOnBdry(i)==1) + { + //Boundary vertex + tripletList.emplace_back(i, localAdjList.front(), 1./8.); + tripletList.emplace_back(i, localAdjList.back(), 1./8.); + tripletList.emplace_back(i, i, 3./4.); + } else + { + const int n = localAdjList.size(); + const SType dn = n; + SType beta; + if(n==3) + { + beta = 3./16.; + } else + { + beta = 3./8./dn; + } + for(int j=0; j +IGL_INLINE void igl::loop( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& NV, + Eigen::PlainObjectBase& NF, + const int number_of_subdivs) +{ + NV = V; + NF = F; + for(int i=0; i S; + loop(NV.rows(), tempF, S, NF); + // This .eval is super important + NV = (S*NV).eval(); + } +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::loop, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, int); +#endif diff --git a/src/igl/loop.h b/src/igl/loop.h new file mode 100644 index 0000000000..bc1616891a --- /dev/null +++ b/src/igl/loop.h @@ -0,0 +1,63 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Oded Stein +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_LOOP_H +#define IGL_LOOP_H + +#include +#include +#include + +namespace igl +{ + // LOOP Given the triangle mesh [V, F], where n_verts = V.rows(), computes + // newV and a sparse matrix S s.t. [newV, newF] is the subdivided mesh where + // newV = S*V. + // + // Inputs: + // n_verts an integer (number of mesh vertices) + // F an m by 3 matrix of integers of triangle faces + // Outputs: + // S a sparse matrix (will become the subdivision matrix) + // newF a matrix containing the new faces + template < + typename DerivedF, + typename SType, + typename DerivedNF> + IGL_INLINE void loop( + const int n_verts, + const Eigen::PlainObjectBase & F, + Eigen::SparseMatrix& S, + Eigen::PlainObjectBase & NF); + // LOOP Given the triangle mesh [V, F], computes number_of_subdivs steps of loop subdivision and outputs the new mesh [newV, newF] + // + // Inputs: + // V an n by 3 matrix of vertices + // F an m by 3 matrix of integers of triangle faces + // number_of_subdivs an integer that specifies how many subdivision steps to do + // Outputs: + // NV a matrix containing the new vertices + // NF a matrix containing the new faces + template < + typename DerivedV, + typename DerivedF, + typename DerivedNV, + typename DerivedNF> + IGL_INLINE void loop( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& NV, + Eigen::PlainObjectBase& NF, + const int number_of_subdivs = 1); +} + +#ifndef IGL_STATIC_LIBRARY +# include "loop.cpp" +#endif + +#endif diff --git a/src/igl/lscm.cpp b/src/igl/lscm.cpp new file mode 100644 index 0000000000..6932f94d41 --- /dev/null +++ b/src/igl/lscm.cpp @@ -0,0 +1,72 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "lscm.h" + +#include "vector_area_matrix.h" +#include "cotmatrix.h" +#include "repdiag.h" +#include "min_quad_with_fixed.h" +#include + +IGL_INLINE bool igl::lscm( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::VectorXi& b, + const Eigen::MatrixXd& bc, + Eigen::MatrixXd & V_uv) +{ + using namespace Eigen; + using namespace std; + + // Assemble the area matrix (note that A is #Vx2 by #Vx2) + SparseMatrix A; + igl::vector_area_matrix(F,A); + + // Assemble the cotan laplacian matrix + SparseMatrix L; + igl::cotmatrix(V,F,L); + + SparseMatrix L_flat; + repdiag(L,2,L_flat); + + VectorXi b_flat(b.size()*bc.cols(),1); + VectorXd bc_flat(bc.size(),1); + for(int c = 0;c Q = -L_flat + 2.*A; + const VectorXd B_flat = VectorXd::Zero(V.rows()*2); + igl::min_quad_with_fixed_data data; + if(!igl::min_quad_with_fixed_precompute(Q,b_flat,SparseMatrix(),true,data)) + { + return false; + } + + MatrixXd W_flat; + if(!min_quad_with_fixed_solve(data,B_flat,bc_flat,VectorXd(),W_flat)) + { + return false; + } + + + assert(W_flat.rows() == V.rows()*2); + V_uv.resize(V.rows(),2); + for (unsigned i=0;i +// 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_LSCM_H +#define IGL_LSCM_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Compute a Least-squares conformal map parametrization (equivalently + // derived in "Intrinsic Parameterizations of Surface Meshes" [Desbrun et al. + // 2002] and "Least Squares Conformal Maps for Automatic Texture Atlas + // Generation" [Lévy et al. 2002]), though this implementation follows the + // derivation in: "Spectral Conformal Parameterization" [Mullen et al. 2008] + // (note, this does **not** implement the Eigen-decomposition based method in + // [Mullen et al. 2008], which is not equivalent). Input should be a manifold + // mesh (also no unreferenced vertices) and "boundary" (fixed vertices) `b` + // should contain at least two vertices per connected component. + // + // Inputs: + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of mesh faces (must be triangles) + // b #b boundary indices into V + // bc #b by 3 list of boundary values + // Outputs: + // UV #V by 2 list of 2D mesh vertex positions in UV space + // Returns true only on solver success. + // + IGL_INLINE bool lscm( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::VectorXi& b, + const Eigen::MatrixXd& bc, + Eigen::MatrixXd& V_uv); +} + +#ifndef IGL_STATIC_LIBRARY +# include "lscm.cpp" +#endif + +#endif diff --git a/src/igl/map_vertices_to_circle.cpp b/src/igl/map_vertices_to_circle.cpp new file mode 100755 index 0000000000..2336139369 --- /dev/null +++ b/src/igl/map_vertices_to_circle.cpp @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Stefan Brugger +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "map_vertices_to_circle.h" +#include "PI.h" + +IGL_INLINE void igl::map_vertices_to_circle( + const Eigen::MatrixXd& V, + const Eigen::VectorXi& bnd, + Eigen::MatrixXd& UV) +{ + // Get sorted list of boundary vertices + std::vector interior,map_ij; + map_ij.resize(V.rows()); + + std::vector isOnBnd(V.rows(),false); + for (int i = 0; i < bnd.size(); i++) + { + isOnBnd[bnd[i]] = true; + map_ij[bnd[i]] = i; + } + + for (int i = 0; i < (int)isOnBnd.size(); i++) + { + if (!isOnBnd[i]) + { + map_ij[i] = interior.size(); + interior.push_back(i); + } + } + + // Map boundary to unit circle + std::vector len(bnd.size()); + len[0] = 0.; + + for (int i = 1; i < bnd.size(); i++) + { + len[i] = len[i-1] + (V.row(bnd[i-1]) - V.row(bnd[i])).norm(); + } + double total_len = len[len.size()-1] + (V.row(bnd[0]) - V.row(bnd[bnd.size()-1])).norm(); + + UV.resize(bnd.size(),2); + for (int i = 0; i < bnd.size(); i++) + { + double frac = len[i] * 2. * igl::PI / total_len; + UV.row(map_ij[bnd[i]]) << cos(frac), sin(frac); + } + +} diff --git a/src/igl/map_vertices_to_circle.h b/src/igl/map_vertices_to_circle.h new file mode 100755 index 0000000000..54743717f3 --- /dev/null +++ b/src/igl/map_vertices_to_circle.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Stefan Brugger +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MAP_VERTICES_TO_CIRCLE_H +#define IGL_MAP_VERTICES_TO_CIRCLE_H +#include "igl_inline.h" +#include "PI.h" + +#include +#include + +namespace igl +{ + + // Map the vertices whose indices are in a given boundary loop (bnd) on the + // unit circle with spacing proportional to the original boundary edge + // lengths. + // + // Inputs: + // V #V by dim list of mesh vertex positions + // b #W list of vertex ids + // Outputs: + // UV #W by 2 list of 2D position on the unit circle for the vertices in b + IGL_INLINE void map_vertices_to_circle( + const Eigen::MatrixXd& V, + const Eigen::VectorXi& bnd, + Eigen::MatrixXd& UV); +} + +#ifndef IGL_STATIC_LIBRARY +# include "map_vertices_to_circle.cpp" +#endif + +#endif diff --git a/src/igl/massmatrix.cpp b/src/igl/massmatrix.cpp new file mode 100644 index 0000000000..b51e110613 --- /dev/null +++ b/src/igl/massmatrix.cpp @@ -0,0 +1,166 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "massmatrix.h" +#include "normalize_row_sums.h" +#include "sparse.h" +#include "doublearea.h" +#include "repmat.h" +#include +#include + +template +IGL_INLINE void igl::massmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const MassMatrixType type, + Eigen::SparseMatrix& M) +{ + using namespace Eigen; + using namespace std; + + const int n = V.rows(); + const int m = F.rows(); + const int simplex_size = F.cols(); + + MassMatrixType eff_type = type; + // Use voronoi of for triangles by default, otherwise barycentric + if(type == MASSMATRIX_TYPE_DEFAULT) + { + eff_type = (simplex_size == 3?MASSMATRIX_TYPE_VORONOI:MASSMATRIX_TYPE_BARYCENTRIC); + } + + // Not yet supported + assert(type!=MASSMATRIX_TYPE_FULL); + + Matrix MI; + Matrix MJ; + Matrix MV; + if(simplex_size == 3) + { + // Triangles + // edge lengths numbered same as opposite vertices + Matrix l(m,3); + // loop over faces + for(int i = 0;i dblA; + doublearea(l,0.,dblA); + + switch(eff_type) + { + case MASSMATRIX_TYPE_BARYCENTRIC: + // diagonal entries for each face corner + MI.resize(m*3,1); MJ.resize(m*3,1); MV.resize(m*3,1); + MI.block(0*m,0,m,1) = F.col(0); + MI.block(1*m,0,m,1) = F.col(1); + MI.block(2*m,0,m,1) = F.col(2); + MJ = MI; + repmat(dblA,3,1,MV); + MV.array() /= 6.0; + break; + case MASSMATRIX_TYPE_VORONOI: + { + // diagonal entries for each face corner + // http://www.alecjacobson.com/weblog/?p=874 + MI.resize(m*3,1); MJ.resize(m*3,1); MV.resize(m*3,1); + MI.block(0*m,0,m,1) = F.col(0); + MI.block(1*m,0,m,1) = F.col(1); + MI.block(2*m,0,m,1) = F.col(2); + MJ = MI; + + // Holy shit this needs to be cleaned up and optimized + Matrix cosines(m,3); + cosines.col(0) = + (l.col(2).array().pow(2)+l.col(1).array().pow(2)-l.col(0).array().pow(2))/(l.col(1).array()*l.col(2).array()*2.0); + cosines.col(1) = + (l.col(0).array().pow(2)+l.col(2).array().pow(2)-l.col(1).array().pow(2))/(l.col(2).array()*l.col(0).array()*2.0); + cosines.col(2) = + (l.col(1).array().pow(2)+l.col(0).array().pow(2)-l.col(2).array().pow(2))/(l.col(0).array()*l.col(1).array()*2.0); + Matrix barycentric = cosines.array() * l.array(); + normalize_row_sums(barycentric,barycentric); + Matrix partial = barycentric; + partial.col(0).array() *= dblA.array() * 0.5; + partial.col(1).array() *= dblA.array() * 0.5; + partial.col(2).array() *= dblA.array() * 0.5; + Matrix quads(partial.rows(),partial.cols()); + quads.col(0) = (partial.col(1)+partial.col(2))*0.5; + quads.col(1) = (partial.col(2)+partial.col(0))*0.5; + quads.col(2) = (partial.col(0)+partial.col(1))*0.5; + + quads.col(0) = (cosines.col(0).array()<0).select( 0.25*dblA,quads.col(0)); + quads.col(1) = (cosines.col(0).array()<0).select(0.125*dblA,quads.col(1)); + quads.col(2) = (cosines.col(0).array()<0).select(0.125*dblA,quads.col(2)); + + quads.col(0) = (cosines.col(1).array()<0).select(0.125*dblA,quads.col(0)); + quads.col(1) = (cosines.col(1).array()<0).select(0.25*dblA,quads.col(1)); + quads.col(2) = (cosines.col(1).array()<0).select(0.125*dblA,quads.col(2)); + + quads.col(0) = (cosines.col(2).array()<0).select(0.125*dblA,quads.col(0)); + quads.col(1) = (cosines.col(2).array()<0).select(0.125*dblA,quads.col(1)); + quads.col(2) = (cosines.col(2).array()<0).select( 0.25*dblA,quads.col(2)); + + MV.block(0*m,0,m,1) = quads.col(0); + MV.block(1*m,0,m,1) = quads.col(1); + MV.block(2*m,0,m,1) = quads.col(2); + + break; + } + case MASSMATRIX_TYPE_FULL: + assert(false && "Implementation incomplete"); + break; + default: + assert(false && "Unknown Mass matrix eff_type"); + } + + }else if(simplex_size == 4) + { + assert(V.cols() == 3); + assert(eff_type == MASSMATRIX_TYPE_BARYCENTRIC); + MI.resize(m*4,1); MJ.resize(m*4,1); MV.resize(m*4,1); + MI.block(0*m,0,m,1) = F.col(0); + MI.block(1*m,0,m,1) = F.col(1); + MI.block(2*m,0,m,1) = F.col(2); + MI.block(3*m,0,m,1) = F.col(3); + MJ = MI; + // loop over tets + for(int i = 0;i v0m3,v1m3,v2m3; + v0m3.head(V.cols()) = V.row(F(i,0)) - V.row(F(i,3)); + v1m3.head(V.cols()) = V.row(F(i,1)) - V.row(F(i,3)); + v2m3.head(V.cols()) = V.row(F(i,2)) - V.row(F(i,3)); + Scalar v = fabs(v0m3.dot(v1m3.cross(v2m3)))/6.0; + MV(i+0*m) = v/4.0; + MV(i+1*m) = v/4.0; + MV(i+2*m) = v/4.0; + MV(i+3*m) = v/4.0; + } + }else + { + // Unsupported simplex size + assert(false && "Unsupported simplex size"); + } + sparse(MI,MJ,MV,n,n,M); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::massmatrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MassMatrixType, Eigen::SparseMatrix&); +// generated by autoexplicit.sh +template void igl::massmatrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MassMatrixType, Eigen::SparseMatrix&); +// generated by autoexplicit.sh +template void igl::massmatrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MassMatrixType, Eigen::SparseMatrix&); +template void igl::massmatrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MassMatrixType, Eigen::SparseMatrix&); +template void igl::massmatrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::MassMatrixType, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/massmatrix.h b/src/igl/massmatrix.h new file mode 100644 index 0000000000..69d8dc9605 --- /dev/null +++ b/src/igl/massmatrix.h @@ -0,0 +1,61 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MASSMATRIX_TYPE_H +#define IGL_MASSMATRIX_TYPE_H +#include "igl_inline.h" + +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include + +namespace igl +{ + + enum MassMatrixType + { + MASSMATRIX_TYPE_BARYCENTRIC = 0, + MASSMATRIX_TYPE_VORONOI = 1, + MASSMATRIX_TYPE_FULL = 2, + MASSMATRIX_TYPE_DEFAULT = 3, + NUM_MASSMATRIX_TYPE = 4 + }; + + // Constructs the mass (area) matrix for a given mesh (V,F). + // + // Templates: + // DerivedV derived type of eigen matrix for V (e.g. derived from + // MatrixXd) + // DerivedF derived type of eigen matrix for F (e.g. derived from + // MatrixXi) + // Scalar scalar type for eigen sparse matrix (e.g. double) + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by simplex_size list of mesh faces (must be triangles) + // type one of the following ints: + // MASSMATRIX_TYPE_BARYCENTRIC barycentric + // MASSMATRIX_TYPE_VORONOI voronoi-hybrid {default} + // MASSMATRIX_TYPE_FULL full {not implemented} + // Outputs: + // M #V by #V mass matrix + // + // See also: adjacency_matrix + // + template + IGL_INLINE void massmatrix( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const MassMatrixType type, + Eigen::SparseMatrix& M); +} + +#ifndef IGL_STATIC_LIBRARY +# include "massmatrix.cpp" +#endif + +#endif + diff --git a/src/igl/mat_max.cpp b/src/igl/mat_max.cpp new file mode 100644 index 0000000000..c5da975697 --- /dev/null +++ b/src/igl/mat_max.cpp @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mat_max.h" + +template +IGL_INLINE void igl::mat_max( + const Eigen::DenseBase & X, + const int dim, + Eigen::PlainObjectBase & Y, + Eigen::PlainObjectBase & I) +{ + assert(dim==1||dim==2); + + // output size + int n = (dim==1?X.cols():X.rows()); + // resize output + Y.resize(n); + I.resize(n); + + // loop over dimension opposite of dim + for(int j = 0;j, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/mat_max.h b/src/igl/mat_max.h new file mode 100644 index 0000000000..ad3ec6e61d --- /dev/null +++ b/src/igl/mat_max.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MAT_MAX_H +#define IGL_MAT_MAX_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Ideally this becomes a super overloaded function supporting everything + // that matlab's max supports + + // Max function for matrices to act like matlab's max function. Specifically + // like [Y,I] = max(X,[],dim); + // + // Templates: + // T should be a eigen matrix primitive type like int or double + // Inputs: + // X m by n matrix + // dim dimension along which to take max + // Outputs: + // Y n-long vector (if dim == 1) + // or + // Y m-long vector (if dim == 2) + // I vector the same size as Y containing the indices along dim of maximum + // entries + template + IGL_INLINE void mat_max( + const Eigen::DenseBase & X, + const int dim, + Eigen::PlainObjectBase & Y, + Eigen::PlainObjectBase & I); +} + +#ifndef IGL_STATIC_LIBRARY +# include "mat_max.cpp" +#endif + +#endif diff --git a/src/igl/mat_min.cpp b/src/igl/mat_min.cpp new file mode 100644 index 0000000000..ad50c7014a --- /dev/null +++ b/src/igl/mat_min.cpp @@ -0,0 +1,59 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mat_min.h" + +template +IGL_INLINE void igl::mat_min( + const Eigen::DenseBase & X, + const int dim, + Eigen::PlainObjectBase & Y, + Eigen::PlainObjectBase & I) +{ + assert(dim==1||dim==2); + + // output size + int n = (dim==1?X.cols():X.rows()); + // resize output + Y.resize(n); + I.resize(n); + + // loop over dimension opposite of dim + for(int j = 0;j +//IGL_INLINE Eigen::Matrix igl::mat_min( +// const Eigen::Matrix & X, +// const int dim) +//{ +// Eigen::Matrix Y; +// Eigen::Matrix I; +// mat_min(X,dim,Y,I); +// return Y; +//} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::mat_min, Eigen::Array, Eigen::Matrix >(Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::mat_min, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/mat_min.h b/src/igl/mat_min.h new file mode 100644 index 0000000000..3673aeded8 --- /dev/null +++ b/src/igl/mat_min.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MAT_MIN_H +#define IGL_MAT_MIN_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Ideally this becomes a super overloaded function supporting everything + // that matlab's min supports + + // Min function for matrices to act like matlab's min function. Specifically + // like [Y,I] = min(X,[],dim); + // + // Templates: + // T should be a eigen matrix primitive type like int or double + // Inputs: + // X m by n matrix + // dim dimension along which to take min + // Outputs: + // Y n-long sparse vector (if dim == 1) + // or + // Y m-long sparse vector (if dim == 2) + // I vector the same size as Y containing the indices along dim of minimum + // entries + // + // See also: mat_max + template + IGL_INLINE void mat_min( + const Eigen::DenseBase & X, + const int dim, + Eigen::PlainObjectBase & Y, + Eigen::PlainObjectBase & I); + // Use Y = X.colwise().minCoeff() instead + //// In-line wrapper + //template + //IGL_INLINE Eigen::Matrix mat_min( + // const Eigen::Matrix & X, + // const int dim); +} + +#ifndef IGL_STATIC_LIBRARY +# include "mat_min.cpp" +#endif + +#endif diff --git a/src/igl/mat_to_quat.cpp b/src/igl/mat_to_quat.cpp new file mode 100644 index 0000000000..5c8e04b1fa --- /dev/null +++ b/src/igl/mat_to_quat.cpp @@ -0,0 +1,141 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mat_to_quat.h" +#include + +// This could be replaced by something fast +template +static inline Q_type ReciprocalSqrt( const Q_type x ) +{ + return 1.0/sqrt(x); +} + +//// Converts row major order matrix to quat +//// http://software.intel.com/sites/default/files/m/d/4/1/d/8/293748.pdf +//template +//IGL_INLINE void igl::mat4_to_quat(const Q_type * m, Q_type * q) +//{ +// Q_type t = + m[0 * 4 + 0] + m[1 * 4 + 1] + m[2 * 4 + 2] + 1.0f; +// Q_type s = ReciprocalSqrt( t ) * 0.5f; +// q[3] = s * t; +// q[2] = ( m[0 * 4 + 1] - m[1 * 4 + 0] ) * s; +// q[1] = ( m[2 * 4 + 0] - m[0 * 4 + 2] ) * s; +// q[0] = ( m[1 * 4 + 2] - m[2 * 4 + 1] ) * s; +//} + +// https://bmgame.googlecode.com/svn/idlib/math/Simd_AltiVec.cpp +template +IGL_INLINE void igl::mat4_to_quat(const Q_type * mat, Q_type * q) +{ + Q_type trace; + Q_type s; + Q_type t; + int i; + int j; + int k; + + static int next[3] = { 1, 2, 0 }; + + trace = mat[0 * 4 + 0] + mat[1 * 4 + 1] + mat[2 * 4 + 2]; + + if ( trace > 0.0f ) { + + t = trace + 1.0f; + s = ReciprocalSqrt( t ) * 0.5f; + + q[3] = s * t; + q[0] = ( mat[1 * 4 + 2] - mat[2 * 4 + 1] ) * s; + q[1] = ( mat[2 * 4 + 0] - mat[0 * 4 + 2] ) * s; + q[2] = ( mat[0 * 4 + 1] - mat[1 * 4 + 0] ) * s; + + } else { + + i = 0; + if ( mat[1 * 4 + 1] > mat[0 * 4 + 0] ) { + i = 1; + } + if ( mat[2 * 4 + 2] > mat[i * 4 + i] ) { + i = 2; + } + j = next[i]; + k = next[j]; + + t = ( mat[i * 4 + i] - ( mat[j * 4 + j] + mat[k * 4 + k] ) ) + 1.0f; + s = ReciprocalSqrt( t ) * 0.5f; + + q[i] = s * t; + q[3] = ( mat[j * 4 + k] - mat[k * 4 + j] ) * s; + q[j] = ( mat[i * 4 + j] + mat[j * 4 + i] ) * s; + q[k] = ( mat[i * 4 + k] + mat[k * 4 + i] ) * s; + } + + //// Unused translation + //jq.t[0] = mat[0 * 4 + 3]; + //jq.t[1] = mat[1 * 4 + 3]; + //jq.t[2] = mat[2 * 4 + 3]; +} + +template +IGL_INLINE void igl::mat3_to_quat(const Q_type * mat, Q_type * q) +{ + Q_type trace; + Q_type s; + Q_type t; + int i; + int j; + int k; + + static int next[3] = { 1, 2, 0 }; + + trace = mat[0 * 3 + 0] + mat[1 * 3 + 1] + mat[2 * 3 + 2]; + + if ( trace > 0.0f ) { + + t = trace + 1.0f; + s = ReciprocalSqrt( t ) * 0.5f; + + q[3] = s * t; + q[0] = ( mat[1 * 3 + 2] - mat[2 * 3 + 1] ) * s; + q[1] = ( mat[2 * 3 + 0] - mat[0 * 3 + 2] ) * s; + q[2] = ( mat[0 * 3 + 1] - mat[1 * 3 + 0] ) * s; + + } else { + + i = 0; + if ( mat[1 * 3 + 1] > mat[0 * 3 + 0] ) { + i = 1; + } + if ( mat[2 * 3 + 2] > mat[i * 3 + i] ) { + i = 2; + } + j = next[i]; + k = next[j]; + + t = ( mat[i * 3 + i] - ( mat[j * 3 + j] + mat[k * 3 + k] ) ) + 1.0f; + s = ReciprocalSqrt( t ) * 0.5f; + + q[i] = s * t; + q[3] = ( mat[j * 3 + k] - mat[k * 3 + j] ) * s; + q[j] = ( mat[i * 3 + j] + mat[j * 3 + i] ) * s; + q[k] = ( mat[i * 3 + k] + mat[k * 3 + i] ) * s; + } + + //// Unused translation + //jq.t[0] = mat[0 * 4 + 3]; + //jq.t[1] = mat[1 * 4 + 3]; + //jq.t[2] = mat[2 * 4 + 3]; +} + + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::mat4_to_quat(double const*, double*); +template void igl::mat4_to_quat(float const*, float*); +template void igl::mat3_to_quat(double const*, double*); +#endif diff --git a/src/igl/mat_to_quat.h b/src/igl/mat_to_quat.h new file mode 100644 index 0000000000..acea683f77 --- /dev/null +++ b/src/igl/mat_to_quat.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MAT_TO_QUAT_H +#define IGL_MAT_TO_QUAT_H +#include "igl_inline.h" +namespace igl +{ + // Convert a OpenGL (rotation) matrix to a quaternion + // + // Input: + // m 16-element opengl rotation matrix + // Output: + // q 4-element quaternion (not normalized) + template + IGL_INLINE void mat4_to_quat(const Q_type * m, Q_type * q); + // Input: + // m 9-element opengl rotation matrix + template + IGL_INLINE void mat3_to_quat(const Q_type * m, Q_type * q); +} + +#ifndef IGL_STATIC_LIBRARY +# include "mat_to_quat.cpp" +#endif + +#endif + diff --git a/src/igl/material_colors.h b/src/igl/material_colors.h new file mode 100644 index 0000000000..1927fa42db --- /dev/null +++ b/src/igl/material_colors.h @@ -0,0 +1,57 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MATERIAL_COLORS_H +#define IGL_MATERIAL_COLORS_H +#include +// Define constant material colors for use with opengl glMaterialfv +// Most of these colors come from IGL publications +namespace igl +{ + // Gold/Silver used in BBW/MONO/STBS/FAST + const float GOLD_AMBIENT[4] = { 51.0/255.0, 43.0/255.0,33.3/255.0,1.0f }; + const float GOLD_DIFFUSE[4] = { 255.0/255.0,228.0/255.0,58.0/255.0,1.0f }; + const float GOLD_SPECULAR[4] = { 255.0/255.0,235.0/255.0,80.0/255.0,1.0f }; + const float SILVER_AMBIENT[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; + const float SILVER_DIFFUSE[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + const float SILVER_SPECULAR[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + // Blue/Cyan more similar to Jovan Popovic's blue than to Mario Botsch's blue + const float CYAN_AMBIENT[4] = { 59.0/255.0, 68.0/255.0,255.0/255.0,1.0f }; + const float CYAN_DIFFUSE[4] = { 94.0/255.0,185.0/255.0,238.0/255.0,1.0f }; + const float CYAN_SPECULAR[4] = { 163.0/255.0,221.0/255.0,255.0/255.0,1.0f }; + const float DENIS_PURPLE_DIFFUSE[4] = { 80.0/255.0,64.0/255.0,255.0/255.0,1.0f }; + const float LADISLAV_ORANGE_DIFFUSE[4] = {1.0f, 125.0f / 255.0f, 19.0f / 255.0f, 0.0f}; + // FAST armadillos colors + const float FAST_GREEN_DIFFUSE[4] = { 113.0f/255.0f, 239.0f/255.0f, 46.0f/255.0f, 1.0f}; + const float FAST_RED_DIFFUSE[4] = { 255.0f/255.0f, 65.0f/255.0f, 46.0f/255.0f, 1.0f}; + const float FAST_BLUE_DIFFUSE[4] = { 106.0f/255.0f, 106.0f/255.0f, 255.0f/255.0f, 1.0f}; + const float FAST_GRAY_DIFFUSE[4] = { 150.0f/255.0f, 150.0f/255.0f, 150.0f/255.0f, 1.0f}; + // Basic colors + const float WHITE[4] = { 255.0/255.0,255.0/255.0,255.0/255.0,1.0f }; + const float BLACK[4] = { 0.0/255.0,0.0/255.0,0.0/255.0,1.0f }; + const float WHITE_AMBIENT[4] = { 255.0/255.0,255.0/255.0,255.0/255.0,1.0f }; + const float WHITE_DIFFUSE[4] = { 255.0/255.0,255.0/255.0,255.0/255.0,1.0f }; + const float WHITE_SPECULAR[4] = { 255.0/255.0,255.0/255.0,255.0/255.0,1.0f }; + const float BBW_POINT_COLOR[4] = {239./255.,213./255.,46./255.,255.0/255.0}; + const float BBW_LINE_COLOR[4] = {106./255.,106./255.,255./255.,255./255.}; + const float MIDNIGHT_BLUE_DIFFUSE[4] = { 21.0f/255.0f, 27.0f/255.0f, 84.0f/255.0f, 1.0f}; + // Winding number colors + const float EASTER_RED_DIFFUSE[4] = {0.603922,0.494118f,0.603922f,1.0f}; + const float WN_OPEN_BOUNDARY_COLOR[4] = {154./255.,0./255.,0./255.,1.0f}; + const float WN_NON_MANIFOLD_EDGE_COLOR[4] = {201./255., 51./255.,255./255.,1.0f}; + const Eigen::Vector4f + MAYA_GREEN(128./255.,242./255.,0./255.,1.), + MAYA_YELLOW(255./255.,247./255.,50./255.,1.), + MAYA_RED(234./255.,63./255.,52./255.,1.), + MAYA_BLUE(0./255.,73./255.,252./255.,1.), + MAYA_PURPLE(180./255.,73./255.,200./255.,1.), + MAYA_VIOLET(31./255.,15./255.,66./255.,1.), + MAYA_GREY(0.5,0.5,0.5,1.0), + MAYA_CYAN(131./255.,219./255.,252./255.,1.), + MAYA_SEA_GREEN(70./255.,252./255.,167./255.,1.); +} +#endif diff --git a/src/igl/matlab/MatlabWorkspace.h b/src/igl/matlab/MatlabWorkspace.h new file mode 100644 index 0000000000..c8dcaf9ed7 --- /dev/null +++ b/src/igl/matlab/MatlabWorkspace.h @@ -0,0 +1,579 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MATLAB_MATLAB_WORKSPACE_H +#define IGL_MATLAB_MATLAB_WORKSPACE_H + +#include +#include + +#include + +#include +#include + +namespace igl +{ + namespace matlab + { + // It would be really great to replicate this for a simple XML-based + // workspace. + // + // Class which contains data of a matlab workspace which can be written to a + // .mat file and loaded from matlab + // + // This depends on matlab at compile time (though it shouldn't necessarily + // have to) but it does not depend on running the matlab engine at run-time. + // + // Known bugs: Treats all matrices as doubles (this may actually be desired + // for some "index" matrices since matlab's sparse command takes doubles + // rather than int class matrices). It is of course not desired when dealing + // with logicals or uint's for images. + class MatlabWorkspace + { + private: + // KNOWN BUG: Why not use a map? Any reason to allow duplicate names? + // + // List of names + std::vector names; + // List of data pointers + std::vector data; + public: + MatlabWorkspace(); + ~MatlabWorkspace(); + // Clear names and data of variables in workspace + inline void clear(); + // Save current list of variables + // + // Inputs: + // path path to .mat file + // Returns true on success, false on failure + inline bool write(const std::string & path) const; + // Load list of variables from .mat file + // + // Inputs: + // path path to .mat file + // Returns true on success, false on failure + inline bool read(const std::string & path); + // Assign data to a variable name in the workspace + // + // Template: + // DerivedM eigen matrix (e.g. MatrixXd) + // Inputs: + // M data (usually a matrix) + // name variable name to save into work space + // Returns true on success, false on failure + // + // Known Bugs: Assumes Eigen is using column major ordering + template + inline MatlabWorkspace& save( + const Eigen::PlainObjectBase& M, + const std::string & name); + // Template: + // MT sparse matrix type (e.g. double) + template + inline MatlabWorkspace& save( + const Eigen::SparseMatrix& M, + const std::string & name); + // Templates: + // ScalarM scalar type, e.g. double + template + inline MatlabWorkspace& save( + const std::vector > & vM, + const std::string & name); + // Templates: + // ScalarV scalar type, e.g. double + template + inline MatlabWorkspace& save( + const std::vector & vV, + const std::string & name); + // NOTE: Eigen stores quaternions coefficients as (i,j,k,1), but most of + // our matlab code stores them as (1,i,j,k) This takes a quaternion and + // saves it as a (1,i,j,k) row vector + // + // Templates: + // Q quaternion type + template + inline MatlabWorkspace& save( + const Eigen::Quaternion & q, + const std::string & name); + inline MatlabWorkspace& save( + const double d, + const std::string & name); + // Same as save() but adds 1 to each element, useful for saving "index" + // matrices like lists of faces or elements + template + inline MatlabWorkspace& save_index( + const Eigen::DenseBase& M, + const std::string & name); + template + inline MatlabWorkspace& save_index( + const std::vector > & vM, + const std::string & name); + template + inline MatlabWorkspace& save_index( + const std::vector & vV, + const std::string & name); + // Find a certain matrix by name. + // + // KNOWN BUG: Outputs the first found (not necessarily unique lists). + // + // Template: + // DerivedM eigen matrix (e.g. MatrixXd) + // Inputs: + // name exact name of matrix as string + // Outputs: + // M matrix + // Returns true only if found. + template + inline bool find( + const std::string & name, + Eigen::PlainObjectBase& M); + template + inline bool find( + const std::string & name, + Eigen::SparseMatrix& M); + inline bool find( + const std::string & name, + double & d); + inline bool find( + const std::string & name, + int & v); + // Subtracts 1 from all entries + template + inline bool find_index( + const std::string & name, + Eigen::PlainObjectBase& M); + }; + } +} + +// Implementation + +// Be sure that this is not compiled into libigl.a +// http://stackoverflow.com/a/3318993/148668 + +// IGL +#include "igl/list_to_matrix.h" + +// MATLAB +#include "mat.h" + +// STL +#include +#include +#include + +inline igl::matlab::MatlabWorkspace::MatlabWorkspace(): + names(), + data() +{ +} + +inline igl::matlab::MatlabWorkspace::~MatlabWorkspace() +{ + // clean up data + clear(); +} + +inline void igl::matlab::MatlabWorkspace::clear() +{ + for_each(data.begin(),data.end(),&mxDestroyArray); + data.clear(); + names.clear(); +} + +inline bool igl::matlab::MatlabWorkspace::write(const std::string & path) const +{ + using namespace std; + MATFile * mat_file = matOpen(path.c_str(), "w"); + if(mat_file == NULL) + { + fprintf(stderr,"Error opening file %s\n",path.c_str()); + return false; + } + assert(names.size() == data.size()); + // loop over names and data + for(int i = 0;i < (int)names.size(); i++) + { + // Put variable as LOCAL variable + int status = matPutVariable(mat_file,names[i].c_str(), data[i]); + if(status != 0) + { + cerr<<"^MatlabWorkspace::save Error: matPutVariable ("< +inline igl::matlab::MatlabWorkspace& igl::matlab::MatlabWorkspace::save( + const Eigen::PlainObjectBase& M, + const std::string & name) +{ + using namespace std; + const int m = M.rows(); + const int n = M.cols(); + mxArray * mx_data = mxCreateDoubleMatrix(m,n,mxREAL); + data.push_back(mx_data); + names.push_back(name); + // Copy data immediately + // Use Eigen's map and cast to copy + Eigen::Map< Eigen::Matrix > + map(mxGetPr(mx_data),m,n); + map = M.template cast(); + return *this; +} + +// Treat everything as a double +template +inline igl::matlab::MatlabWorkspace& igl::matlab::MatlabWorkspace::save( + const Eigen::SparseMatrix& M, + const std::string & name) +{ + using namespace std; + const int m = M.rows(); + const int n = M.cols(); + // THIS WILL NOT WORK FOR ROW-MAJOR + assert(n==M.outerSize()); + const int nzmax = M.nonZeros(); + mxArray * mx_data = mxCreateSparse(m, n, nzmax, mxREAL); + data.push_back(mx_data); + names.push_back(name); + // Copy data immediately + double * pr = mxGetPr(mx_data); + mwIndex * ir = mxGetIr(mx_data); + mwIndex * jc = mxGetJc(mx_data); + + // Iterate over outside + int k = 0; + for(int j=0; j::InnerIterator it (M,j); it; ++it) + { + pr[k] = it.value(); + ir[k] = it.row(); + k++; + } + } + jc[M.outerSize()] = k; + + return *this; +} + +template +inline igl::matlab::MatlabWorkspace& igl::matlab::MatlabWorkspace::save( + const std::vector > & vM, + const std::string & name) +{ + Eigen::MatrixXd M; + list_to_matrix(vM,M); + return this->save(M,name); +} + +template +inline igl::matlab::MatlabWorkspace& igl::matlab::MatlabWorkspace::save( + const std::vector & vV, + const std::string & name) +{ + Eigen::MatrixXd V; + list_to_matrix(vV,V); + return this->save(V,name); +} + +template +inline igl::matlab::MatlabWorkspace& igl::matlab::MatlabWorkspace::save( + const Eigen::Quaternion & q, + const std::string & name) +{ + Eigen::Matrix qm; + qm(0,0) = q.w(); + qm(0,1) = q.x(); + qm(0,2) = q.y(); + qm(0,3) = q.z(); + return save(qm,name); +} + +inline igl::matlab::MatlabWorkspace& igl::matlab::MatlabWorkspace::save( + const double d, + const std::string & name) +{ + Eigen::VectorXd v(1); + v(0) = d; + return save(v,name); +} + +template +inline igl::matlab::MatlabWorkspace& + igl::matlab::MatlabWorkspace::save_index( + const Eigen::DenseBase& M, + const std::string & name) +{ + DerivedM Mp1 = M; + Mp1.array() += 1; + return this->save(Mp1,name); +} + +template +inline igl::matlab::MatlabWorkspace& igl::matlab::MatlabWorkspace::save_index( + const std::vector > & vM, + const std::string & name) +{ + Eigen::MatrixXd M; + list_to_matrix(vM,M); + return this->save_index(M,name); +} + +template +inline igl::matlab::MatlabWorkspace& igl::matlab::MatlabWorkspace::save_index( + const std::vector & vV, + const std::string & name) +{ + Eigen::MatrixXd V; + list_to_matrix(vV,V); + return this->save_index(V,name); +} + +template +inline bool igl::matlab::MatlabWorkspace::find( + const std::string & name, + Eigen::PlainObjectBase& M) +{ + using namespace std; + const int i = std::find(names.begin(), names.end(), name)-names.begin(); + if(i>=(int)names.size()) + { + return false; + } + assert(i<=(int)data.size()); + mxArray * mx_data = data[i]; + assert(!mxIsSparse(mx_data)); + assert(mxGetNumberOfDimensions(mx_data) == 2); + //cout< > + (mxGetPr(mx_data),M.rows(),M.cols()).cast(); + return true; +} + +template +inline bool igl::matlab::MatlabWorkspace::find( + const std::string & name, + Eigen::SparseMatrix& M) +{ + using namespace std; + using namespace Eigen; + const int i = std::find(names.begin(), names.end(), name)-names.begin(); + if(i>=(int)names.size()) + { + return false; + } + assert(i<=(int)data.size()); + mxArray * mx_data = data[i]; + // Handle boring case where matrix is actually an empty dense matrix + if(mxGetNumberOfElements(mx_data) == 0) + { + M.resize(0,0); + return true; + } + assert(mxIsSparse(mx_data)); + assert(mxGetNumberOfDimensions(mx_data) == 2); + //cout< > MIJV; + const int nnz = mxGetNzmax(mx_data); + MIJV.reserve(nnz); + // Iterate over outside + int k = 0; + for(int j=0; j(ir[k],j,pr[k])); + k++; + } + } + M.resize(m,n); + M.setFromTriplets(MIJV.begin(),MIJV.end()); + + return true; +} + +inline bool igl::matlab::MatlabWorkspace::find( + const std::string & name, + int & v) +{ + using namespace std; + const int i = std::find(names.begin(), names.end(), name)-names.begin(); + if(i>=(int)names.size()) + { + return false; + } + assert(i<=(int)data.size()); + mxArray * mx_data = data[i]; + assert(!mxIsSparse(mx_data)); + assert(mxGetNumberOfDimensions(mx_data) == 2); + //cout<=(int)names.size()) + { + return false; + } + assert(i<=(int)data.size()); + mxArray * mx_data = data[i]; + assert(!mxIsSparse(mx_data)); + assert(mxGetNumberOfDimensions(mx_data) == 2); + //cout< +inline bool igl::matlab::MatlabWorkspace::find_index( + const std::string & name, + Eigen::PlainObjectBase& M) +{ + if(!find(name,M)) + { + return false; + } + M.array() -= 1; + return true; +} + + +//template +//bool igl::matlab::MatlabWorkspace::save(const Data & M, const std::string & name) +//{ +// using namespace std; +// // If I don't know the type then I can't save it +// cerr<<"^MatlabWorkspace::save Error: Unknown data type. "<< +// name<<" not saved."< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MATLAB_MEX_STREAM_H +#define IGL_MATLAB_MEX_STREAM_H +#include +namespace igl +{ + namespace matlab + { + // http://stackoverflow.com/a/249008/148668 + + // Class to implement "cout" for mex files to print to the matlab terminal + // window. + // + // Insert at the beginning of mexFunction(): + // MexStream mout; + // std::streambuf *outbuf = std::cout.rdbuf(&mout); + // ... + // ALWAYS restore original buffer to avoid memory leak problems in matlab + // std::cout.rdbuf(outbuf); + // + class MexStream : public std::streambuf + { + public: + protected: + inline virtual std::streamsize xsputn(const char *s, std::streamsize n); + inline virtual int overflow(int c = EOF); + }; + } +} + +// Implementation +#include +inline std::streamsize igl::matlab::MexStream::xsputn( + const char *s, + std::streamsize n) +{ + mexPrintf("%.*s",n,s); + mexEvalString("drawnow;"); // to dump string. + return n; +} + +inline int igl::matlab::MexStream::overflow(int c) +{ + if (c != EOF) { + mexPrintf("%.1s",&c); + mexEvalString("drawnow;"); // to dump string. + } + return 1; +} +#endif diff --git a/src/igl/matlab/matlabinterface.cpp b/src/igl/matlab/matlabinterface.cpp new file mode 100644 index 0000000000..6c03485de9 --- /dev/null +++ b/src/igl/matlab/matlabinterface.cpp @@ -0,0 +1,330 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include + +// Implementation + +// Init the MATLAB engine +// (no need to call it directly since it is automatically invoked by any other command) +IGL_INLINE void igl::matlab::mlinit(Engine** mlengine) +{ + *mlengine = engOpen("\0"); +} + +// Closes the MATLAB engine +IGL_INLINE void igl::matlab::mlclose(Engine** mlengine) +{ + engClose(*mlengine); + *mlengine = 0; +} + +// Send a matrix to MATLAB +IGL_INLINE void igl::matlab::mlsetmatrix(Engine** mlengine, std::string name, const Eigen::MatrixXd& M) +{ + if (*mlengine == 0) + mlinit(mlengine); + + mxArray *A = mxCreateDoubleMatrix(M.rows(), M.cols(), mxREAL); + double *pM = mxGetPr(A); + + int c = 0; + for(int j=0; j& M) +{ + if (*mlengine == 0) + mlinit(mlengine); + + mxArray *A = mxCreateDoubleMatrix(M.rows(), M.cols(), mxREAL); + double *pM = mxGetPr(A); + + int c = 0; + for(int j=0; j t; + + mxArray *ary = engGetVariable(*mlengine, name.c_str()); + if (ary == NULL) + { + m = 0; + n = 0; + M = Eigen::MatrixXd(0,0); + } + else + { + m = mxGetM(ary); + n = mxGetN(ary); + M = Eigen::MatrixXd(m,n); + + double *pM = mxGetPr(ary); + + int c = 0; + for(int j=0; j t; + + mxArray *ary = engGetVariable(*mlengine, name.c_str()); + if (ary == NULL) + { + m = 0; + n = 0; + M = Eigen::MatrixXf(0,0); + } + else + { + m = mxGetM(ary); + n = mxGetN(ary); + M = Eigen::MatrixXf(m,n); + + double *pM = mxGetPr(ary); + + int c = 0; + for(int j=0; j t; + + mxArray *ary = engGetVariable(*mlengine, name.c_str()); + if (ary == NULL) + { + m = 0; + n = 0; + M = Eigen::MatrixXi(0,0); + } + else + { + m = mxGetM(ary); + n = mxGetN(ary); + M = Eigen::MatrixXi(m,n); + + double *pM = mxGetPr(ary); + + int c = 0; + for(int j=0; j& M) +{ + if (*mlengine == 0) + mlinit(mlengine); + + unsigned long m = 0; + unsigned long n = 0; + std::vector t; + + mxArray *ary = engGetVariable(*mlengine, name.c_str()); + if (ary == NULL) + { + m = 0; + n = 0; + M = Eigen::Matrix(0,0); + } + else + { + m = mxGetM(ary); + n = mxGetN(ary); + M = Eigen::Matrix(m,n); + + double *pM = mxGetPr(ary); + + int c = 0; + for(int j=0; j' && buf[1] == '>' && buf[2] == ' ') + buf += 3; + if (buf[0] == '\n') ++buf; + + return std::string(buf); +} + +// Send a sparse matrix +IGL_INLINE void igl::matlab::mlsetmatrix(Engine** mlengine, std::string name, const Eigen::SparseMatrix& M) +{ + int count = 0; +// // Count non-zero +// for (unsigned k=0; k::InnerIterator it(M,k); it; ++it) +// if (it.value() != 0) +// ++count; + + Eigen::MatrixXd T(M.nonZeros(),3); + for (unsigned k=0; k<(unsigned)M.outerSize(); ++k) + { + for (Eigen::SparseMatrix::InnerIterator it(M,k); it; ++it) + { + T(count,0) = it.row(); + T(count,1) = it.col(); + T(count,2) = it.value(); + ++count; + } + } + + T.col(0) = T.col(0).array()+1; + T.col(1) = T.col(1).array()+1; + + mlsetmatrix(mlengine,"temp93765",T); + + std::string temp = name + " = sparse(temp93765(:,1),temp93765(:,2),temp93765(:,3)," + + std::to_string(M.rows()) + "," + + std::to_string(M.cols()) + ");"; + + mleval(mlengine,temp); + mleval(mlengine,"clear temp93765"); +} diff --git a/src/igl/matlab/matlabinterface.h b/src/igl/matlab/matlabinterface.h new file mode 100644 index 0000000000..63d72c74e2 --- /dev/null +++ b/src/igl/matlab/matlabinterface.h @@ -0,0 +1,90 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MATLAB_MATLAB_INTERFACE_H +#define IGL_MATLAB_MATLAB_INTERFACE_H +#include "../igl_inline.h" +// WARNING: These functions require matlab installed +// Additional header folder required: +// /Applications/MATLAB_R2011a.app/extern/include +// Additional binary lib to be linked with: +// /Applications/MATLAB_R2011a.app/bin/maci64/libeng.dylib +// /Applications/MATLAB_R2011a.app/bin/maci64/libmx.dylib + +// MAC ONLY: +// Add to the environment variables: +// DYLD_LIBRARY_PATH = /Applications/MATLAB_R2011a.app/bin/maci64/ +// PATH = /opt/local/bin:/opt/local/sbin:/Applications/MATLAB_R2011a.app/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/texbin:/usr/X11/bin + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include // Matlab engine header + +namespace igl +{ + namespace matlab + { + // Init the MATLAB engine + // (no need to call it directly since it is automatically invoked by any other command) + IGL_INLINE void mlinit(Engine** engine); + + // Closes the MATLAB engine + IGL_INLINE void mlclose(Engine** engine); + + // Send a matrix to MATLAB + IGL_INLINE void mlsetmatrix(Engine** engine, std::string name, const Eigen::MatrixXd& M); + + // Send a matrix to MATLAB + IGL_INLINE void mlsetmatrix(Engine** engine, std::string name, const Eigen::MatrixXf& M); + + // Send a matrix to MATLAB + IGL_INLINE void mlsetmatrix(Engine** engine, std::string name, const Eigen::MatrixXi& M); + + // Send a matrix to MATLAB + IGL_INLINE void mlsetmatrix(Engine** mlengine, std::string name, const Eigen::Matrix& M); + + // Receive a matrix from MATLAB + IGL_INLINE void mlgetmatrix(Engine** engine, std::string name, Eigen::MatrixXd& M); + + // Receive a matrix from MATLAB + IGL_INLINE void mlgetmatrix(Engine** engine, std::string name, Eigen::MatrixXf& M); + + // Receive a matrix from MATLAB + IGL_INLINE void mlgetmatrix(Engine** engine, std::string name, Eigen::MatrixXi& M); + + // Receive a matrix from MATLAB + IGL_INLINE void mlgetmatrix(Engine** mlengine, std::string name, Eigen::Matrix& M); + + // Send a single scalar to MATLAB + IGL_INLINE void mlsetscalar(Engine** engine, std::string name, double s); + + // Receive a single scalar from MATLAB + IGL_INLINE double mlgetscalar(Engine** engine, std::string name); + + // Execute arbitrary MATLAB code and return the MATLAB output + IGL_INLINE std::string mleval(Engine** engine, std::string code); + + // Send a sparse matrix to MATLAB + IGL_INLINE void mlsetmatrix(Engine** mlengine, std::string name, const Eigen::SparseMatrix& M); + + } +} + +// Be sure that this is not compiled into libigl.a +#ifndef IGL_STATIC_LIBRARY +# include "matlabinterface.cpp" +#endif + +#endif diff --git a/src/igl/matlab/mexErrMsgTxt.cpp b/src/igl/matlab/mexErrMsgTxt.cpp new file mode 100644 index 0000000000..89dda1fa37 --- /dev/null +++ b/src/igl/matlab/mexErrMsgTxt.cpp @@ -0,0 +1,17 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mexErrMsgTxt.h" + +IGL_INLINE void igl::matlab::mexErrMsgTxt(bool assertion, const char * text) +{ + if(!assertion) + { + ::mexErrMsgTxt(text); + } +} + diff --git a/src/igl/matlab/mexErrMsgTxt.h b/src/igl/matlab/mexErrMsgTxt.h new file mode 100644 index 0000000000..61b838e6e8 --- /dev/null +++ b/src/igl/matlab/mexErrMsgTxt.h @@ -0,0 +1,25 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MATLAB_MEXERRMSGTXT_H +#define IGL_MATLAB_MEXERRMSGTXT_H +#include "../igl_inline.h" +// Overload mexErrMsgTxt to check an assertion then print text only if +// assertion fails +#include "mex.h" +namespace igl +{ + namespace matlab + { + // Wrapper for mexErrMsgTxt that only calls error if test fails + IGL_INLINE void mexErrMsgTxt(bool test, const char * message); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "mexErrMsgTxt.cpp" +#endif +#endif diff --git a/src/igl/matlab/parse_rhs.cpp b/src/igl/matlab/parse_rhs.cpp new file mode 100644 index 0000000000..dafc8ba484 --- /dev/null +++ b/src/igl/matlab/parse_rhs.cpp @@ -0,0 +1,83 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "parse_rhs.h" +#include + +template +IGL_INLINE void igl::matlab::parse_rhs_double( + const mxArray *prhs[], + Eigen::PlainObjectBase & V) +{ + using namespace Eigen; + // Use Eigen's map and cast to copy + V = Map< Matrix > + (mxGetPr(prhs[0]),mxGetM(prhs[0]),mxGetN(prhs[0])) + .cast(); +} + +template +IGL_INLINE void igl::matlab::parse_rhs_index( + const mxArray *prhs[], + Eigen::PlainObjectBase & V) +{ + parse_rhs_double(prhs,V); + V.array() -= 1; +} + +template +IGL_INLINE void igl::matlab::parse_rhs( + const mxArray *prhs[], + Eigen::SparseMatrix & M) +{ + using namespace Eigen; + using namespace std; + const mxArray * mx_data = prhs[0]; + // Handle boring case where matrix is actually an empty dense matrix + if(mxGetNumberOfElements(mx_data) == 0) + { + M.resize(0,0); + return; + } + assert(mxIsSparse(mx_data)); + assert(mxGetNumberOfDimensions(mx_data) == 2); + //cout< > MIJV; + MIJV.reserve(mxGetNumberOfElements(mx_data)); + // Iterate over outside + int k = 0; + for(int j=0; j(ir[k],j,pr[k])); + k++; + } + } + M.resize(m,n); + M.setFromTriplets(MIJV.begin(),MIJV.end()); +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::matlab::parse_rhs_index >(mxArray_tag const**, Eigen::PlainObjectBase >&); +template void igl::matlab::parse_rhs_index >(mxArray_tag const**, Eigen::PlainObjectBase >&); +template void igl::matlab::parse_rhs_double >(mxArray_tag const**, Eigen::PlainObjectBase >&); +template void igl::matlab::parse_rhs_index >(mxArray_tag const**, Eigen::PlainObjectBase >&); +template void igl::matlab::parse_rhs_double >(mxArray_tag const**, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/matlab/parse_rhs.h b/src/igl/matlab/parse_rhs.h new file mode 100644 index 0000000000..3cabec7349 --- /dev/null +++ b/src/igl/matlab/parse_rhs.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MATLAB_PARSE_RHS_H +#define IGL_MATLAB_PARSE_RHS_H +#include +#include +#include +#include +namespace igl +{ + namespace matlab + { + // Reads in a matrix as a double + // + // Inputs: + // prhs points to rhs argument + // Outputs: + // V M by N matrix + template + IGL_INLINE void parse_rhs_double( + const mxArray *prhs[], + Eigen::PlainObjectBase & V); + // Reads in a matrix and subtracts 1 + template + IGL_INLINE void parse_rhs_index( + const mxArray *prhs[], + Eigen::PlainObjectBase & V); + template + IGL_INLINE void parse_rhs( + const mxArray *prhs[], + Eigen::SparseMatrix & M); + } +}; +#ifndef IGL_STATIC_LIBRARY +# include "parse_rhs.cpp" +#endif +#endif diff --git a/src/igl/matlab/prepare_lhs.cpp b/src/igl/matlab/prepare_lhs.cpp new file mode 100644 index 0000000000..93593685e8 --- /dev/null +++ b/src/igl/matlab/prepare_lhs.cpp @@ -0,0 +1,99 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "prepare_lhs.h" +#include +template +IGL_INLINE void igl::matlab::prepare_lhs_double( + const Eigen::PlainObjectBase & V, + mxArray *plhs[]) +{ + using namespace std; + using namespace Eigen; + const int m = V.rows(); + const int n = V.cols(); + plhs[0] = mxCreateDoubleMatrix(m,n, mxREAL); + Eigen::Map< Eigen::Matrix > + map(mxGetPr(plhs[0]),m,n); + map = V.template cast(); +} + +template +IGL_INLINE void igl::matlab::prepare_lhs_logical( + const Eigen::PlainObjectBase & V, + mxArray *plhs[]) +{ + using namespace std; + using namespace Eigen; + const int m = V.rows(); + const int n = V.cols(); + plhs[0] = mxCreateLogicalMatrix(m,n); + mxLogical * Vp = static_cast(mxGetData(plhs[0])); + Eigen::Map< Eigen::Matrix > + map(static_cast(mxGetData(plhs[0])),m,n); + map = V.template cast(); +} + +template +IGL_INLINE void igl::matlab::prepare_lhs_index( + const Eigen::PlainObjectBase & V, + mxArray *plhs[]) +{ + // Treat indices as reals + const auto Vd = (V.template cast().array()+1).eval(); + return prepare_lhs_double(Vd,plhs); +} + +template +IGL_INLINE void igl::matlab::prepare_lhs_double( + const Eigen::SparseMatrix & M, + mxArray *plhs[]) +{ + using namespace std; + const int m = M.rows(); + const int n = M.cols(); + // THIS WILL NOT WORK FOR ROW-MAJOR + assert(n==M.outerSize()); + const int nzmax = M.nonZeros(); + plhs[0] = mxCreateSparse(m, n, nzmax, mxREAL); + mxArray * mx_data = plhs[0]; + // Copy data immediately + double * pr = mxGetPr(mx_data); + mwIndex * ir = mxGetIr(mx_data); + mwIndex * jc = mxGetJc(mx_data); + + // Iterate over outside + int k = 0; + for(int j=0; j::InnerIterator it (M,j); it; ++it) + { + // copy (cast to double) + pr[k] = it.value(); + ir[k] = it.row(); + k++; + } + } + jc[M.outerSize()] = k; + +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::matlab::prepare_lhs_index >(Eigen::PlainObjectBase > const&, mxArray_tag**); +template void igl::matlab::prepare_lhs_index >(Eigen::PlainObjectBase > const&, mxArray_tag**); +template void igl::matlab::prepare_lhs_double >(Eigen::PlainObjectBase > const&, mxArray_tag**); +template void igl::matlab::prepare_lhs_index >(Eigen::PlainObjectBase > const&, mxArray_tag**); +template void igl::matlab::prepare_lhs_logical >(Eigen::PlainObjectBase > const&, mxArray_tag**); +template void igl::matlab::prepare_lhs_double >(Eigen::PlainObjectBase > const&, mxArray_tag**); +template void igl::matlab::prepare_lhs_logical >(Eigen::PlainObjectBase > const&, mxArray_tag**); +template void igl::matlab::prepare_lhs_index >(Eigen::PlainObjectBase > const&, mxArray_tag**); +template void igl::matlab::prepare_lhs_double >(Eigen::PlainObjectBase > const&, mxArray_tag**); +template void igl::matlab::prepare_lhs_double >(Eigen::PlainObjectBase > const&, mxArray_tag**); +template void igl::matlab::prepare_lhs_double >(Eigen::PlainObjectBase > const&, mxArray_tag**); +#endif diff --git a/src/igl/matlab/prepare_lhs.h b/src/igl/matlab/prepare_lhs.h new file mode 100644 index 0000000000..0ac0ca6ce5 --- /dev/null +++ b/src/igl/matlab/prepare_lhs.h @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MATLAB_PREPARE_LHS_H +#define IGL_MATLAB_PREPARE_LHS_H +#include +#include +#include +#include +namespace igl +{ + namespace matlab + { + // Writes out a matrix as a double + // + // Inputs: + // prhs points to rhs argument + // Outputs: + // V M by N matrix + template + IGL_INLINE void prepare_lhs_double( + const Eigen::PlainObjectBase & V, + mxArray *plhs[]); + // Casts to logical + template + IGL_INLINE void prepare_lhs_logical( + const Eigen::PlainObjectBase & V, + mxArray *plhs[]); + // Writes out a matrix and adds 1 + template + IGL_INLINE void prepare_lhs_index( + const Eigen::PlainObjectBase & V, + mxArray *plhs[]); + // SparseMatrix + template + IGL_INLINE void prepare_lhs_double( + const Eigen::SparseMatrix & V, + mxArray *plhs[]); + }; +} +#ifndef IGL_STATIC_LIBRARY +# include "prepare_lhs.cpp" +#endif +#endif + diff --git a/src/igl/matlab/requires_arg.cpp b/src/igl/matlab/requires_arg.cpp new file mode 100644 index 0000000000..a034691836 --- /dev/null +++ b/src/igl/matlab/requires_arg.cpp @@ -0,0 +1,16 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "requires_arg.h" +#include "mexErrMsgTxt.h" +#include "../C_STR.h" + +IGL_INLINE void igl::matlab::requires_arg(const int i, const int nrhs, const char *name) +{ + mexErrMsgTxt((i+1) +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_REQUIRES_ARG_H +#define IGL_REQUIRES_ARG_H +#include "../igl_inline.h" +#include +namespace igl +{ + namespace matlab + { + // Simply throw an error if (i+1) +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "validate_arg.h" +#include "requires_arg.h" +#include "mexErrMsgTxt.h" +#include "../C_STR.h" + +IGL_INLINE void igl::matlab::validate_arg_scalar( + const int i, const int nrhs, const mxArray * prhs[], const char * name) +{ + requires_arg(i,nrhs,name); + mexErrMsgTxt(mxGetN(prhs[i+1])==1 && mxGetM(prhs[i+1])==1, + C_STR("Parameter '"< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_VALIDATE_ARG_H +#define IGL_VALIDATE_ARG_H +#include "../igl_inline.h" +#include +namespace igl +{ + namespace matlab + { + // Throw an error if arg i+1 is not a scalar + // + // Inputs: + // i index of current argument + // nrhs total number of arguments + // prhs pointer to arguments array + // name name of current argument + IGL_INLINE void validate_arg_scalar( + const int i, const int nrhs, const mxArray * prhs[], const char * name); + IGL_INLINE void validate_arg_logical( + const int i, const int nrhs, const mxArray * prhs[], const char * name); + IGL_INLINE void validate_arg_char( + const int i, const int nrhs, const mxArray * prhs[], const char * name); + IGL_INLINE void validate_arg_double( + const int i, const int nrhs, const mxArray * prhs[], const char * name); + IGL_INLINE void validate_arg_function_handle( + const int i, const int nrhs, const mxArray * prhs[], const char * name); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "validate_arg.cpp" +#endif +#endif diff --git a/src/igl/matlab_format.cpp b/src/igl/matlab_format.cpp new file mode 100644 index 0000000000..ceb5d6db7d --- /dev/null +++ b/src/igl/matlab_format.cpp @@ -0,0 +1,155 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "matlab_format.h" +#include "STR.h" +#include "find.h" + +template +IGL_INLINE const Eigen::WithFormat< DerivedM > igl::matlab_format( + const Eigen::DenseBase & M, + const std::string name) +{ + using namespace std; + string prefix = ""; + if(!name.empty()) + { + prefix = name + " = "; + } + + return M.format(Eigen::IOFormat( + Eigen::FullPrecision, + 0, + " ", + "\n", + "", + "", + // This seems like a bit of a hack since I would expect the rows to align + // with out this extra spacing on the first line + prefix + "[\n ", + "\n];")); +} + +template +IGL_INLINE const std::string +igl::matlab_format( + const Eigen::SparseMatrix & S, + const std::string name) +{ + using namespace Eigen; + using namespace std; + Matrix::Scalar,Dynamic,1> I,J,V; + Matrix SIJV; + find(S,I,J,V); + I.array() += 1; + J.array() += 1; + SIJV.resize(V.rows(),3); + SIJV << I,J,V; + string prefix = ""; + string suffix = ""; + if(!name.empty()) + { + prefix = name + "IJV = "; + suffix = "\n"+name + " = sparse("+name+"IJV(:,1),"+name+"IJV(:,2),"+name+"IJV(:,3),"+std::to_string(S.rows())+","+std::to_string(S.cols())+" );"; + } + return STR(""<< + SIJV.format(Eigen::IOFormat( + Eigen::FullPrecision, + 0, + " ", + "\n", + "", + "", + // This seems like a bit of a hack since I would expect the rows to align + // with out this extra spacing on the first line + prefix + "[\n ", + "\n];"))< > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +// generated by autoexplicit.sh +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template std::basic_string, std::allocator > const igl::matlab_format(Eigen::SparseMatrix const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::string); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +template Eigen::WithFormat > const igl::matlab_format >(Eigen::DenseBase > const&, std::basic_string, std::allocator >); +#endif diff --git a/src/igl/matlab_format.h b/src/igl/matlab_format.h new file mode 100644 index 0000000000..e1f3ff8307 --- /dev/null +++ b/src/igl/matlab_format.h @@ -0,0 +1,89 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MATLAB_FORMAT_H +#define IGL_MATLAB_FORMAT_H + +#include "igl_inline.h" + +#include +#include +#include + +namespace igl +{ + // This is a routine to print a matrix using format suitable for pasting into + // the matlab IDE + // + // Templates: + // DerivedM e.g. derived from MatrixXd + // Input: + // input some matrix to be formatted + // name name of matrix + // Returns Formatted matrix + // + // Example: + // // M := [1 2 3;4 5 6]; + // cout< + IGL_INLINE const Eigen::WithFormat< DerivedM > matlab_format( + const Eigen::DenseBase & M, + const std::string name = ""); + // Same but for sparse matrices. Print IJV format into an auxiliary variable + // and then print a call to sparse which will construct the sparse matrix + // Example: + // // S := [0 2 3;4 5 0]; + // cout< + IGL_INLINE const std::string matlab_format( + const Eigen::SparseMatrix & S, + const std::string name = ""); + IGL_INLINE const std::string matlab_format( + const double v, + const std::string name = ""); + IGL_INLINE const std::string matlab_format( + const float v, + const std::string name = ""); + // Return just IOFormat + // + // Example: + // // M := [1 2 3;4 5 6]; + // cout< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "matrix_to_list.h" + +#include + +template +IGL_INLINE void igl::matrix_to_list( + const Eigen::DenseBase & M, + std::vector > & V) +{ + using namespace std; + V.resize(M.rows(),vector(M.cols())); + // loop over rows + for(int i = 0;i +IGL_INLINE void igl::matrix_to_list( + const Eigen::DenseBase & M, + std::vector & V) +{ + using namespace std; + V.resize(M.size()); + // loop over cols then rows + for(int j = 0;j +IGL_INLINE std::vector igl::matrix_to_list( + const Eigen::DenseBase & M) +{ + std::vector V; + matrix_to_list(M,V); + return V; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::matrix_to_list, -1, 1, true> >(Eigen::DenseBase, -1, 1, true> > const&, std::vector, -1, 1, true>::Scalar, std::allocator, -1, 1, true>::Scalar> >&); +// generated by autoexplicit.sh +template void igl::matrix_to_list, -1, 1, true> >(Eigen::DenseBase, -1, 1, true> > const&, std::vector, -1, 1, true>::Scalar, std::allocator, -1, 1, true>::Scalar> >&); +// generated by autoexplicit.sh +template void igl::matrix_to_list >(Eigen::DenseBase > const&, std::vector::Scalar, std::allocator::Scalar> >&); +// generated by autoexplicit.sh +template std::vector::Scalar, std::allocator::Scalar> > igl::matrix_to_list >(Eigen::DenseBase > const&); +// generated by autoexplicit.sh +template std::vector::Scalar, std::allocator::Scalar> > igl::matrix_to_list >(Eigen::DenseBase > const&); +//template void igl::matrix_to_list >, double>(Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > >&); +//template void igl::matrix_to_list >, int>(Eigen::PlainObjectBase > const&, std::vector >, std::allocator > > >&); +template void igl::matrix_to_list >(Eigen::DenseBase > const&, std::vector::Scalar, std::allocator::Scalar> >, std::allocator::Scalar, std::allocator::Scalar> > > >&); +template void igl::matrix_to_list >(Eigen::DenseBase > const&, std::vector::Scalar, std::allocator::Scalar> >, std::allocator::Scalar, std::allocator::Scalar> > > >&); +template void igl::matrix_to_list >(Eigen::DenseBase > const&, std::vector::Scalar, std::allocator::Scalar> >&); +template void igl::matrix_to_list >(Eigen::DenseBase > const&, std::vector::Scalar, std::allocator::Scalar> >&); +template void igl::matrix_to_list >(Eigen::DenseBase > const&, std::vector::Scalar, std::allocator::Scalar> >&); +template void igl::matrix_to_list >(Eigen::DenseBase > const&, std::vector::Scalar, std::allocator::Scalar> >, std::allocator::Scalar, std::allocator::Scalar> > > >&); +template void igl::matrix_to_list >(Eigen::DenseBase > const&, std::vector::Scalar, std::allocator::Scalar> >&); +template void igl::matrix_to_list >(Eigen::DenseBase > const&, std::vector::Scalar, std::allocator::Scalar> >&); +template std::vector::Scalar, std::allocator::Scalar> > igl::matrix_to_list >(Eigen::DenseBase > const&); +#endif diff --git a/src/igl/matrix_to_list.h b/src/igl/matrix_to_list.h new file mode 100644 index 0000000000..a842811278 --- /dev/null +++ b/src/igl/matrix_to_list.h @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MATRIX_TO_LIST_H +#define IGL_MATRIX_TO_LIST_H +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Convert a matrix to a list (std::vector) of row vectors of the same size + // + // Template: + // Mat Matrix type, must implement: + // .resize(m,n) + // .row(i) = Row + // T type that can be safely cast to type in Mat via '=' + // Inputs: + // M an m by n matrix + // Outputs: + // V a m-long list of vectors of size n + // + // See also: list_to_matrix + template + IGL_INLINE void matrix_to_list( + const Eigen::DenseBase & M, + std::vector > & V); + // Convert a matrix to a list (std::vector) of elements in column-major + // ordering. + // + // Inputs: + // M an m by n matrix + // Outputs: + // V an m*n list of elements + template + IGL_INLINE void matrix_to_list( + const Eigen::DenseBase & M, + std::vector & V); + // Return wrapper + template + IGL_INLINE std::vector matrix_to_list( + const Eigen::DenseBase & M); +} + +#ifndef IGL_STATIC_LIBRARY +# include "matrix_to_list.cpp" +#endif + +#endif + diff --git a/src/igl/max.cpp b/src/igl/max.cpp new file mode 100644 index 0000000000..e451c3b15f --- /dev/null +++ b/src/igl/max.cpp @@ -0,0 +1,46 @@ +#include "max.h" +#include "for_each.h" +#include "find_zero.h" + +template +IGL_INLINE void igl::max( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase & B, + Eigen::PlainObjectBase & I) +{ + const int n = A.cols(); + const int m = A.rows(); + B.resize(dim==1?n:m); + B.setConstant(std::numeric_limits::lowest()); + I.resize(dim==1?n:m); + for_each(A,[&B,&I,&dim](int i, int j,const typename DerivedB::Scalar v) + { + if(dim == 2) + { + std::swap(i,j); + } + // Coded as if dim == 1, assuming swap for dim == 2 + if(v > B(j)) + { + B(j) = v; + I(j) = i; + } + }); + Eigen::VectorXi Z; + find_zero(A,dim,Z); + for(int j = 0;j B(j)) + { + B(j) = 0; + I(j) = Z(j); + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::max, Eigen::Matrix >(Eigen::SparseMatrix const&, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/max.h b/src/igl/max.h new file mode 100644 index 0000000000..554a19b132 --- /dev/null +++ b/src/igl/max.h @@ -0,0 +1,27 @@ +#ifndef IGL_MAX_H +#define IGL_MAX_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Inputs: + // X m by n matrix + // dim dimension along which to take max + // Outputs: + // Y n-long vector (if dim == 1) + // or + // Y m-long vector (if dim == 2) + // I vector the same size as Y containing the indices along dim of maximum + // entries + template + IGL_INLINE void max( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase & B, + Eigen::PlainObjectBase & I); +} +#ifndef IGL_STATIC_LIBRARY +# include "max.cpp" +#endif +#endif diff --git a/src/igl/max_faces_stopping_condition.cpp b/src/igl/max_faces_stopping_condition.cpp new file mode 100644 index 0000000000..adc4fc96ee --- /dev/null +++ b/src/igl/max_faces_stopping_condition.cpp @@ -0,0 +1,93 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "max_faces_stopping_condition.h" + +IGL_INLINE void igl::max_faces_stopping_condition( + int & m, + const int orig_m, + const int max_m, + std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> & stopping_condition) +{ + stopping_condition = + [orig_m,max_m,&m]( + const Eigen::MatrixXd &, + const Eigen::MatrixXi &, + const Eigen::MatrixXi &, + const Eigen::VectorXi &, + const Eigen::MatrixXi &, + const Eigen::MatrixXi &, + const std::set > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int f1, + const int f2)->bool + { + // Only subtract if we're collapsing a real face + if(f1 < orig_m) m-=1; + if(f2 < orig_m) m-=1; + return m<=(int)max_m; + }; +} + +IGL_INLINE + std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> + igl::max_faces_stopping_condition( + int & m, + const int orig_m, + const int max_m) +{ + std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> stopping_condition; + max_faces_stopping_condition( + m,orig_m,max_m,stopping_condition); + return stopping_condition; +} diff --git a/src/igl/max_faces_stopping_condition.h b/src/igl/max_faces_stopping_condition.h new file mode 100644 index 0000000000..e886a1f5c5 --- /dev/null +++ b/src/igl/max_faces_stopping_condition.h @@ -0,0 +1,75 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MAX_FACES_STOPPING_CONDITION_H +#define IGL_MAX_FACES_STOPPING_CONDITION_H +#include "igl_inline.h" +#include +#include +#include +#include +namespace igl +{ + // Stopping condition function compatible with igl::decimate. The outpute + // function handle will return true if number of faces is less than max_m + // + // Inputs: + // m reference to working variable initially should be set to current + // number of faces. + // orig_m number (size) of original face list _**not**_ including any + // faces added to handle phony boundary faces connecting to point at + // infinity. For closed meshes it's safe to set this to F.rows() + // max_m maximum number of faces + // Outputs: + // stopping_condition + // + IGL_INLINE void max_faces_stopping_condition( + int & m, + const int orig_m, + const int max_m, + std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> & stopping_condition); + IGL_INLINE + std::function > &, + const std::vector >::iterator > &, + const Eigen::MatrixXd &, + const int, + const int, + const int, + const int, + const int)> + max_faces_stopping_condition( + int & m, + const int orign_m, + const int max_m); +} + +#ifndef IGL_STATIC_LIBRARY +# include "max_faces_stopping_condition.cpp" +#endif +#endif + diff --git a/src/igl/max_size.cpp b/src/igl/max_size.cpp new file mode 100644 index 0000000000..f3b9a07f22 --- /dev/null +++ b/src/igl/max_size.cpp @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "max_size.h" + + +template +IGL_INLINE int igl::max_size(const std::vector & V) +{ + int max_size = -1; + for( + typename std::vector::const_iterator iter = V.begin(); + iter != V.end(); + iter++) + { + int size = (int)iter->size(); + max_size = (max_size > size ? max_size : size); + } + return max_size; +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template int igl::max_size > >(std::vector >, std::allocator > > > const&); +// generated by autoexplicit.sh +template int igl::max_size > >(std::vector >, std::allocator > > > const&); +// generated by autoexplicit.sh +template int igl::max_size > >(std::vector >, std::allocator > > > const&); +// generated by autoexplicit.sh +template int igl::max_size > >(std::vector >, std::allocator > > > const&); +template int igl::max_size > >(std::vector >, std::allocator > > > const&); +template int igl::max_size > >(std::vector >, std::allocator > > > const&); +#endif diff --git a/src/igl/max_size.h b/src/igl/max_size.h new file mode 100644 index 0000000000..58035d4cee --- /dev/null +++ b/src/igl/max_size.h @@ -0,0 +1,29 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MAX_SIZE_H +#define IGL_MAX_SIZE_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Determine max size of lists in a vector + // Template: + // T some list type object that implements .size() + // Inputs: + // V vector of list types T + // Returns max .size() found in V, returns -1 if V is empty + template + IGL_INLINE int max_size(const std::vector & V); +} + +#ifndef IGL_STATIC_LIBRARY +# include "max_size.cpp" +#endif + +#endif diff --git a/src/igl/median.cpp b/src/igl/median.cpp new file mode 100644 index 0000000000..718b9acc91 --- /dev/null +++ b/src/igl/median.cpp @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "median.h" +#include "matrix_to_list.h" + +#include +#include + +template +IGL_INLINE bool igl::median( + const Eigen::MatrixBase & V, mType & m) +{ + using namespace std; + if(V.size() == 0) + { + return false; + } + vector vV; + matrix_to_list(V,vV); + // http://stackoverflow.com/a/1719155/148668 + size_t n = vV.size()/2; + nth_element(vV.begin(),vV.begin()+n,vV.end()); + if(vV.size()%2==0) + { + nth_element(vV.begin(),vV.begin()+n-1,vV.end()); + m = 0.5*(vV[n]+vV[n-1]); + }else + { + m = vV[n]; + } + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::median, -1, 1, true>, float>(Eigen::MatrixBase, -1, 1, true> > const&, float&); +// generated by autoexplicit.sh +template bool igl::median, -1, 1, true>, double>(Eigen::MatrixBase, -1, 1, true> > const&, double&); +#endif diff --git a/src/igl/median.h b/src/igl/median.h new file mode 100644 index 0000000000..7986dfda41 --- /dev/null +++ b/src/igl/median.h @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MEDIAN_H +#define IGL_MEDIAN_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute the median of an eigen vector + // + // Inputs: + // V #V list of unsorted values + // Outputs: + // m median of those values + // Returns true on success, false on failure + template + IGL_INLINE bool median( + const Eigen::MatrixBase & V, mType & m); +} + +#ifndef IGL_STATIC_LIBRARY +# include "median.cpp" +#endif + +#endif diff --git a/src/igl/min.cpp b/src/igl/min.cpp new file mode 100644 index 0000000000..ce8ba328e6 --- /dev/null +++ b/src/igl/min.cpp @@ -0,0 +1,41 @@ +#include "min.h" +#include "for_each.h" +#include "find_zero.h" + +template +IGL_INLINE void igl::min( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase & B, + Eigen::PlainObjectBase & I) +{ + const int n = A.cols(); + const int m = A.rows(); + B.resize(dim==1?n:m); + B.setConstant(std::numeric_limits::max()); + I.resize(dim==1?n:m); + for_each(A,[&B,&I,&dim](int i, int j,const typename DerivedB::Scalar v) + { + if(dim == 2) + { + std::swap(i,j); + } + // Coded as if dim == 1, assuming swap for dim == 2 + if(v < B(j)) + { + B(j) = v; + I(j) = i; + } + }); + Eigen::VectorXi Z; + find_zero(A,dim,Z); + for(int j = 0;j +#include +namespace igl +{ + // Inputs: + // X m by n matrix + // dim dimension along which to take min + // Outputs: + // Y n-long vector (if dim == 1) + // or + // Y m-long vector (if dim == 2) + // I vector the same size as Y containing the indices along dim of minimum + // entries + template + IGL_INLINE void min( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase & B, + Eigen::PlainObjectBase & I); +} +#ifndef IGL_STATIC_LIBRARY +# include "min.cpp" +#endif +#endif + diff --git a/src/igl/min_quad_dense.cpp b/src/igl/min_quad_dense.cpp new file mode 100644 index 0000000000..f489d996c2 --- /dev/null +++ b/src/igl/min_quad_dense.cpp @@ -0,0 +1,97 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "min_quad_dense.h" + +#include +#include +#include "EPS.h" +#include + +template +IGL_INLINE void igl::min_quad_dense_precompute( + const Eigen::Matrix& A, + const Eigen::Matrix& Aeq, + const bool use_lu_decomposition, + Eigen::Matrix& S) +{ + typedef Eigen::Matrix Mat; + // This threshold seems to matter a lot but I'm not sure how to + // set it + const T treshold = igl::FLOAT_EPS; + //const T treshold = igl::DOUBLE_EPS; + + const int n = A.rows(); + assert(A.cols() == n); + const int m = Aeq.rows(); + assert(Aeq.cols() == n); + + // Lagrange multipliers method: + Eigen::Matrix LM(n + m, n + m); + LM.block(0, 0, n, n) = A; + LM.block(0, n, n, m) = Aeq.transpose(); + LM.block(n, 0, m, n) = Aeq; + LM.block(n, n, m, m).setZero(); + + Mat LMpinv; + if(use_lu_decomposition) + { + // if LM is close to singular, use at your own risk :) + LMpinv = LM.inverse(); + }else + { + // use SVD + typedef Eigen::Matrix Vec; + Vec singValues; + Eigen::JacobiSVD svd; + svd.compute(LM, Eigen::ComputeFullU | Eigen::ComputeFullV ); + const Mat& u = svd.matrixU(); + const Mat& v = svd.matrixV(); + const Vec& singVals = svd.singularValues(); + + Vec pi_singVals(n + m); + int zeroed = 0; + for (int i=0; i= 0); + // printf("sv: %lg ? %lg\n",(double) sv,(double)treshold); + if (sv > treshold) pi_singVals(i, 0) = T(1) / sv; + else + { + pi_singVals(i, 0) = T(0); + zeroed++; + } + } + + printf("min_quad_dense_precompute: %i singular values zeroed (threshold = %e)\n", zeroed, treshold); + Eigen::DiagonalMatrix pi_diag(pi_singVals); + + LMpinv = v * pi_diag * u.transpose(); + } + S = LMpinv.block(0, 0, n, n + m); + + //// debug: + //mlinit(&g_pEngine); + // + //mlsetmatrix(&g_pEngine, "A", A); + //mlsetmatrix(&g_pEngine, "Aeq", Aeq); + //mlsetmatrix(&g_pEngine, "LM", LM); + //mlsetmatrix(&g_pEngine, "u", u); + //mlsetmatrix(&g_pEngine, "v", v); + //MatrixXd svMat = singVals; + //mlsetmatrix(&g_pEngine, "singVals", svMat); + //mlsetmatrix(&g_pEngine, "LMpinv", LMpinv); + //mlsetmatrix(&g_pEngine, "S", S); + + //int hu = 1; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::min_quad_dense_precompute(Eigen::Matrix const&, Eigen::Matrix const&, bool, Eigen::Matrix&); +#endif diff --git a/src/igl/min_quad_dense.h b/src/igl/min_quad_dense.h new file mode 100644 index 0000000000..3d115168ad --- /dev/null +++ b/src/igl/min_quad_dense.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MIN_QUAD_DENSE_H +#define IGL_MIN_QUAD_DENSE_H +#include "igl_inline.h" + +#include + +//// debug +//#include +//Engine *g_pEngine; + + +namespace igl +{ + // MIN_QUAD_WITH_FIXED Minimize quadratic energy Z'*A*Z + Z'*B + C + // subject to linear constraints Aeq*Z = Beq + // + // Templates: + // T should be a eigen matrix primitive type like float or double + // Inputs: + // A n by n matrix of quadratic coefficients + // B n by 1 column of linear coefficients + // Aeq m by n list of linear equality constraint coefficients + // Beq m by 1 list of linear equality constraint constant values + // use_lu_decomposition use lu rather than SVD + // Outputs: + // S n by (n + m) "solve" matrix, such that S*[B', Beq'] is a solution + // Returns true on success, false on error + template + IGL_INLINE void min_quad_dense_precompute( + const Eigen::Matrix& A, + const Eigen::Matrix& Aeq, + const bool use_lu_decomposition, + Eigen::Matrix& S); +} + +#ifndef IGL_STATIC_LIBRARY +# include "min_quad_dense.cpp" +#endif + +#endif diff --git a/src/igl/min_quad_with_fixed.cpp b/src/igl/min_quad_with_fixed.cpp new file mode 100644 index 0000000000..ffb1322c76 --- /dev/null +++ b/src/igl/min_quad_with_fixed.cpp @@ -0,0 +1,597 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "min_quad_with_fixed.h" + +#include "slice.h" +#include "is_symmetric.h" +#include "find.h" +#include "sparse.h" +#include "repmat.h" +#include "matlab_format.h" +#include "EPS.h" +#include "cat.h" + +//#include +// Bug in unsupported/Eigen/SparseExtra needs iostream first +#include +#include +#include +#include +#include + +template +IGL_INLINE bool igl::min_quad_with_fixed_precompute( + const Eigen::SparseMatrix& A2, + const Eigen::MatrixBase & known, + const Eigen::SparseMatrix& Aeq, + const bool pd, + min_quad_with_fixed_data & data + ) +{ +//#define MIN_QUAD_WITH_FIXED_CPP_DEBUG + using namespace Eigen; + using namespace std; + const Eigen::SparseMatrix A = 0.5*A2; +#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG + cout<<" pre"<= 0)&& "known indices should be in [0,n)"); + assert((kr == 0 || known.maxCoeff() < n) && "known indices should be in [0,n)"); + assert(neq <= n && "Number of equality constraints should be less than DOFs"); + + + // cache known + data.known = known; + // get list of unknown indices + data.unknown.resize(n-kr); + std::vector unknown_mask; + unknown_mask.resize(n,true); + for(int i = 0;i 0) + { + data.unknown_lagrange.head(data.unknown.size()) = data.unknown; + } + if(data.lagrange.size() > 0) + { + data.unknown_lagrange.tail(data.lagrange.size()) = data.lagrange; + } + + SparseMatrix Auu; + slice(A,data.unknown,data.unknown,Auu); + assert(Auu.size() != 0 && Auu.rows() > 0 && "There should be at least one unknown."); + + // Positive definiteness is *not* determined, rather it is given as a + // parameter + data.Auu_pd = pd; + if(data.Auu_pd) + { + // PD implies symmetric + data.Auu_sym = true; + // This is an annoying assertion unless EPS can be chosen in a nicer way. + //assert(is_symmetric(Auu,EPS())); + assert(is_symmetric(Auu,1.0) && + "Auu should be symmetric if positive definite"); + }else + { + // determine if A(unknown,unknown) is symmetric and/or positive definite + VectorXi AuuI,AuuJ; + MatrixXd AuuV; + find(Auu,AuuI,AuuJ,AuuV); + data.Auu_sym = is_symmetric(Auu,EPS()*AuuV.maxCoeff()); + } + + // Determine number of linearly independent constraints + int nc = 0; + if(neq>0) + { +#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG + cout<<" qr"<(data.Aequ.transpose().eval()),"AeqT")< new_A; + SparseMatrix AeqT = Aeq.transpose(); + SparseMatrix Z(neq,neq); + // This is a bit slower. But why isn't cat fast? + new_A = cat(1, cat(2, A, AeqT ), + cat(2, Aeq, Z )); + + // precompute RHS builders + if(kr > 0) + { + SparseMatrix Aulk,Akul; + // Slow + slice(new_A,data.unknown_lagrange,data.known,Aulk); + //// This doesn't work!!! + //data.preY = Aulk + Akul.transpose(); + // Slow + if(data.Auu_sym) + { + data.preY = Aulk*2; + }else + { + slice(new_A,data.known,data.unknown_lagrange,Akul); + SparseMatrix AkulT = Akul.transpose(); + data.preY = Aulk + AkulT; + } + }else + { + data.preY.resize(data.unknown_lagrange.size(),0); + } + + // Positive definite and no equality constraints (Positive definiteness + // implies symmetric) +#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG + cout<<" factorize"<::LLT; + }else + { +#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG + cout<<" ldlt"< NA; + slice(new_A,data.unknown_lagrange,data.unknown_lagrange,NA); + data.NA = NA; + // Ideally we'd use LDLT but Eigen doesn't support positive semi-definite + // matrices: + // http://forum.kde.org/viewtopic.php?f=74&t=106962&p=291990#p291990 + if(data.Auu_sym && false) + { + data.ldlt.compute(NA); + switch(data.ldlt.info()) + { + case Eigen::Success: + break; + case Eigen::NumericalIssue: + cerr<<"Error: Numerical issue."<::LDLT; + }else + { +#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG + cout<<" lu"<1/2 + data.lu.compute(NA); + //std::cout<<"NA=["<::LU; + } + } + }else + { +#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG + cout<<" Aeq_li=false"< AeqTR,AeqTQ; + AeqTR = data.AeqTQR.matrixR(); + // This shouldn't be necessary + AeqTR.prune(0.0); +#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG + cout<<" matrixQ"< I(neq,neq); + I.setIdentity(); + data.AeqTE = data.AeqTQR.colsPermutation() * I; + data.AeqTET = data.AeqTQR.colsPermutation().transpose() * I; + assert(AeqTR.rows() == nu && "#rows in AeqTR should match #unknowns"); + assert(AeqTR.cols() == neq && "#cols in AeqTR should match #constraints"); + assert(AeqTQ.rows() == nu && "#rows in AeqTQ should match #unknowns"); + assert(AeqTQ.cols() == nu && "#cols in AeqTQ should match #unknowns"); + //cout<<" slice"< QRAuu = data.AeqTQ2T * Auu * data.AeqTQ2; + { +#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG + cout<<" factorize"<::QR_LLT; + } +#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG + cout<<" smash"< Auk; + slice(A,data.unknown,data.known,Auk); + SparseMatrix Aku; + slice(A,data.known,data.unknown,Aku); + SparseMatrix AkuT = Aku.transpose(); + data.preY = Auk + AkuT; + // Needed during solve + data.Auu = Auu; + slice(Aeq,data.known,2,data.Aeqk); + assert(data.Aeqk.rows() == neq); + assert(data.Aeqk.cols() == data.known.size()); + } + return true; +} + + +template < + typename T, + typename DerivedB, + typename DerivedY, + typename DerivedBeq, + typename DerivedZ, + typename Derivedsol> +IGL_INLINE bool igl::min_quad_with_fixed_solve( + const min_quad_with_fixed_data & data, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & Y, + const Eigen::MatrixBase & Beq, + Eigen::PlainObjectBase & Z, + Eigen::PlainObjectBase & sol) +{ + using namespace std; + using namespace Eigen; + typedef Matrix VectorXT; + typedef Matrix MatrixXT; + // number of known rows + int kr = data.known.size(); + if(kr!=0) + { + assert(kr == Y.rows()); + } + // number of columns to solve + int cols = Y.cols(); + assert(B.cols() == 1 || B.cols() == cols); + assert(Beq.size() == 0 || Beq.cols() == 1 || Beq.cols() == cols); + + // resize output + Z.resize(data.n,cols); + // Set known values + for(int i = 0;i < kr;i++) + { + for(int j = 0;j < cols;j++) + { + Z(data.known(i),j) = Y(i,j); + } + } + + if(data.Aeq_li) + { + // number of lagrange multipliers aka linear equality constraints + int neq = data.lagrange.size(); + // append lagrange multiplier rhs's + MatrixXT BBeq(B.rows() + Beq.rows(),cols); + if(B.size() > 0) + { + BBeq.topLeftCorner(B.rows(),cols) = B.replicate(1,B.cols()==cols?1:cols); + } + if(Beq.size() > 0) + { + BBeq.bottomLeftCorner(Beq.rows(),cols) = -2.0*Beq.replicate(1,Beq.cols()==cols?1:cols); + } + + // Build right hand side + MatrixXT BBequlcols; + igl::slice(BBeq,data.unknown_lagrange,1,BBequlcols); + MatrixXT NB; + if(kr == 0) + { + NB = BBequlcols; + }else + { + NB = data.preY * Y + BBequlcols; + } + + //std::cout<<"NB=["<::LLT: + sol = data.llt.solve(NB); + break; + case igl::min_quad_with_fixed_data::LDLT: + sol = data.ldlt.solve(NB); + break; + case igl::min_quad_with_fixed_data::LU: + // Not a bottleneck + sol = data.lu.solve(NB); + break; + default: + cerr<<"Error: invalid solver type"<::QR_LLT); + MatrixXT eff_Beq; + // Adjust Aeq rhs to include known parts + eff_Beq = + //data.AeqTQR.colsPermutation().transpose() * (-data.Aeqk * Y + Beq); + data.AeqTET * (-data.Aeqk * Y + Beq.replicate(1,Beq.cols()==cols?1:cols)); + // Where did this -0.5 come from? Probably the same place as above. + MatrixXT Bu; + slice(B,data.unknown,1,Bu); + MatrixXT NB; + NB = -0.5*(Bu.replicate(1,B.cols()==cols?1:cols) + data.preY * Y); + // Trim eff_Beq + const int nc = data.AeqTQR.rank(); + const int neq = Beq.rows(); + eff_Beq = eff_Beq.topLeftCorner(nc,cols).eval(); + data.AeqTR1T.template triangularView().solveInPlace(eff_Beq); + // Now eff_Beq = (data.AeqTR1T \ (data.AeqTET * (-data.Aeqk * Y + Beq))) + MatrixXT lambda_0; + lambda_0 = data.AeqTQ1 * eff_Beq; + //cout<().solveInPlace(temp1); + //cout< +IGL_INLINE bool igl::min_quad_with_fixed_solve( + const min_quad_with_fixed_data & data, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & Y, + const Eigen::MatrixBase & Beq, + Eigen::PlainObjectBase & Z) +{ + Eigen::Matrix sol; + return min_quad_with_fixed_solve(data,B,Y,Beq,Z,sol); +} + +template < + typename T, + typename Derivedknown, + typename DerivedB, + typename DerivedY, + typename DerivedBeq, + typename DerivedZ> +IGL_INLINE bool igl::min_quad_with_fixed( + const Eigen::SparseMatrix& A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & known, + const Eigen::MatrixBase & Y, + const Eigen::SparseMatrix& Aeq, + const Eigen::MatrixBase & Beq, + const bool pd, + Eigen::PlainObjectBase & Z) +{ + min_quad_with_fixed_data data; + if(!min_quad_with_fixed_precompute(A,known,Aeq,pd,data)) + { + return false; + } + return min_quad_with_fixed_solve(data,B,Y,Beq,Z); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::min_quad_with_fixed, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::SparseMatrix const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix const&, Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +template bool igl::min_quad_with_fixed_solve, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(igl::min_quad_with_fixed_data const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template bool igl::min_quad_with_fixed_precompute >(Eigen::SparseMatrix const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix const&, bool, igl::min_quad_with_fixed_data&); +template bool igl::min_quad_with_fixed_solve, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(igl::min_quad_with_fixed_data const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::min_quad_with_fixed_solve, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(igl::min_quad_with_fixed_data const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::min_quad_with_fixed_precompute >(Eigen::SparseMatrix const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix const&, bool, igl::min_quad_with_fixed_data&); +template bool igl::min_quad_with_fixed_solve, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(igl::min_quad_with_fixed_data const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template bool igl::min_quad_with_fixed_solve, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(igl::min_quad_with_fixed_data const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template bool igl::min_quad_with_fixed_solve, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(igl::min_quad_with_fixed_data const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::min_quad_with_fixed_solve, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(igl::min_quad_with_fixed_data const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template bool igl::min_quad_with_fixed, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::SparseMatrix const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix const&, Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/min_quad_with_fixed.h b/src/igl/min_quad_with_fixed.h new file mode 100644 index 0000000000..269623313e --- /dev/null +++ b/src/igl/min_quad_with_fixed.h @@ -0,0 +1,179 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MIN_QUAD_WITH_FIXED_H +#define IGL_MIN_QUAD_WITH_FIXED_H +#include "igl_inline.h" + +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include +#include +// Bug in unsupported/Eigen/SparseExtra needs iostream first +#include +#include + +namespace igl +{ + template + struct min_quad_with_fixed_data; + // Known Bugs: rows of Aeq **should probably** be linearly independent. + // During precomputation, the rows of a Aeq are checked via QR. But in case + // they're not then resulting probably will no longer be sparse: it will be + // slow. + // + // MIN_QUAD_WITH_FIXED Minimize a quadratic energy of the form + // + // trace( 0.5*Z'*A*Z + Z'*B + constant ) + // + // subject to + // + // Z(known,:) = Y, and + // Aeq*Z = Beq + // + // Templates: + // T should be a eigen matrix primitive type like int or double + // Inputs: + // A n by n matrix of quadratic coefficients + // known list of indices to known rows in Z + // Y list of fixed values corresponding to known rows in Z + // Aeq m by n list of linear equality constraint coefficients + // pd flag specifying whether A(unknown,unknown) is positive definite + // Outputs: + // data factorization struct with all necessary information to solve + // using min_quad_with_fixed_solve + // Returns true on success, false on error + // + // Benchmark: For a harmonic solve on a mesh with 325K facets, matlab 2.2 + // secs, igl/min_quad_with_fixed.h 7.1 secs + // + template + IGL_INLINE bool min_quad_with_fixed_precompute( + const Eigen::SparseMatrix& A, + const Eigen::MatrixBase & known, + const Eigen::SparseMatrix& Aeq, + const bool pd, + min_quad_with_fixed_data & data + ); + // Solves a system previously factored using min_quad_with_fixed_precompute + // + // Template: + // T type of sparse matrix (e.g. double) + // DerivedY type of Y (e.g. derived from VectorXd or MatrixXd) + // DerivedZ type of Z (e.g. derived from VectorXd or MatrixXd) + // Inputs: + // data factorization struct with all necessary precomputation to solve + // B n by k column of linear coefficients + // Y b by k list of constant fixed values + // Beq m by k list of linear equality constraint constant values + // Outputs: + // Z n by k solution + // sol #unknowns+#lagrange by k solution to linear system + // Returns true on success, false on error + template < + typename T, + typename DerivedB, + typename DerivedY, + typename DerivedBeq, + typename DerivedZ, + typename Derivedsol> + IGL_INLINE bool min_quad_with_fixed_solve( + const min_quad_with_fixed_data & data, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & Y, + const Eigen::MatrixBase & Beq, + Eigen::PlainObjectBase & Z, + Eigen::PlainObjectBase & sol); + // Wrapper without sol + template < + typename T, + typename DerivedB, + typename DerivedY, + typename DerivedBeq, + typename DerivedZ> + IGL_INLINE bool min_quad_with_fixed_solve( + const min_quad_with_fixed_data & data, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & Y, + const Eigen::MatrixBase & Beq, + Eigen::PlainObjectBase & Z); + template < + typename T, + typename Derivedknown, + typename DerivedB, + typename DerivedY, + typename DerivedBeq, + typename DerivedZ> + IGL_INLINE bool min_quad_with_fixed( + const Eigen::SparseMatrix& A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & known, + const Eigen::MatrixBase & Y, + const Eigen::SparseMatrix& Aeq, + const Eigen::MatrixBase & Beq, + const bool pd, + Eigen::PlainObjectBase & Z); +} + +template +struct igl::min_quad_with_fixed_data +{ + // Size of original system: number of unknowns + number of knowns + int n; + // Whether A(unknown,unknown) is positive definite + bool Auu_pd; + // Whether A(unknown,unknown) is symmetric + bool Auu_sym; + // Indices of known variables + Eigen::VectorXi known; + // Indices of unknown variables + Eigen::VectorXi unknown; + // Indices of lagrange variables + Eigen::VectorXi lagrange; + // Indices of unknown variable followed by Indices of lagrange variables + Eigen::VectorXi unknown_lagrange; + // Matrix multiplied against Y when constructing right hand side + Eigen::SparseMatrix preY; + enum SolverType + { + LLT = 0, + LDLT = 1, + LU = 2, + QR_LLT = 3, + NUM_SOLVER_TYPES = 4 + } solver_type; + // Solvers + Eigen::SimplicialLLT > llt; + Eigen::SimplicialLDLT > ldlt; + Eigen::SparseLU, Eigen::COLAMDOrdering > lu; + // QR factorization + // Are rows of Aeq linearly independent? + bool Aeq_li; + // Columns of Aeq corresponding to unknowns + int neq; + Eigen::SparseQR, Eigen::COLAMDOrdering > AeqTQR; + Eigen::SparseMatrix Aeqk; + Eigen::SparseMatrix Aequ; + Eigen::SparseMatrix Auu; + Eigen::SparseMatrix AeqTQ1; + Eigen::SparseMatrix AeqTQ1T; + Eigen::SparseMatrix AeqTQ2; + Eigen::SparseMatrix AeqTQ2T; + Eigen::SparseMatrix AeqTR1; + Eigen::SparseMatrix AeqTR1T; + Eigen::SparseMatrix AeqTE; + Eigen::SparseMatrix AeqTET; + // Debug + Eigen::SparseMatrix NA; + Eigen::Matrix NB; +}; + +#ifndef IGL_STATIC_LIBRARY +# include "min_quad_with_fixed.cpp" +#endif + +#endif diff --git a/src/igl/min_size.cpp b/src/igl/min_size.cpp new file mode 100644 index 0000000000..0b0cfa9e2f --- /dev/null +++ b/src/igl/min_size.cpp @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "min_size.h" + +template +IGL_INLINE int igl::min_size(const std::vector & V) +{ + int min_size = -1; + for( + typename std::vector::const_iterator iter = V.begin(); + iter != V.end(); + iter++) + { + int size = (int)iter->size(); + // have to handle base case + if(min_size == -1) + { + min_size = size; + }else{ + min_size = (min_size < size ? min_size : size); + } + } + return min_size; +} + + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template int igl::min_size > >(std::vector >, std::allocator > > > const&); +// generated by autoexplicit.sh +template int igl::min_size > >(std::vector >, std::allocator > > > const&); +// generated by autoexplicit.sh +template int igl::min_size > >(std::vector >, std::allocator > > > const&); +// generated by autoexplicit.sh +template int igl::min_size > >(std::vector >, std::allocator > > > const&); +template int igl::min_size > >(std::vector >, std::allocator > > > const&); +template int igl::min_size > >(std::vector >, std::allocator > > > const&); +#endif diff --git a/src/igl/min_size.h b/src/igl/min_size.h new file mode 100644 index 0000000000..e440362790 --- /dev/null +++ b/src/igl/min_size.h @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MIN_SIZE_H +#define IGL_MIN_SIZE_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Determine min size of lists in a vector + // Template: + // T some list type object that implements .size() + // Inputs: + // V vector of list types T + // Returns min .size() found in V, returns -1 if V is empty + template + IGL_INLINE int min_size(const std::vector & V); +} + + +#ifndef IGL_STATIC_LIBRARY +# include "min_size.cpp" +#endif + +#endif diff --git a/src/igl/mod.cpp b/src/igl/mod.cpp new file mode 100644 index 0000000000..2064fe6a4d --- /dev/null +++ b/src/igl/mod.cpp @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mod.h" + +template +IGL_INLINE void igl::mod( + const Eigen::PlainObjectBase & A, + const int base, + Eigen::PlainObjectBase & B) +{ + B.resizeLike(A); + for(int i = 0;i +IGL_INLINE DerivedA igl::mod( + const Eigen::PlainObjectBase & A, const int base) +{ + DerivedA B; + mod(A,base,B); + return B; +} +#ifdef IGL_STATIC_LIBRARY +#endif diff --git a/src/igl/mod.h b/src/igl/mod.h new file mode 100644 index 0000000000..3ff58a231c --- /dev/null +++ b/src/igl/mod.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MOD_H +#define IGL_MOD_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute elementwise mod: B = A % base + // + // Inputs: + // A m by n matrix + // base number to mod against + // Outputs: + // B m by n matrix + template + IGL_INLINE void mod( + const Eigen::PlainObjectBase & A, + const int base, + Eigen::PlainObjectBase & B); + template + IGL_INLINE DerivedA mod( + const Eigen::PlainObjectBase & A, const int base); +} +#ifndef IGL_STATIC_LIBRARY +#include "mod.cpp" +#endif +#endif diff --git a/src/igl/mode.cpp b/src/igl/mode.cpp new file mode 100644 index 0000000000..89df6be66d --- /dev/null +++ b/src/igl/mode.cpp @@ -0,0 +1,62 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mode.h" + +// Implementation +#include + +template +IGL_INLINE void igl::mode( + const Eigen::Matrix & X, + const int d, + Eigen::Matrix & M) +{ + assert(d==1 || d==2); + using namespace std; + int m = X.rows(); + int n = X.cols(); + M.resize((d==1)?n:m,1); + for(int i = 0;i<((d==2)?m:n);i++) + { + vector counts(((d==2)?n:m),0); + for(int j = 0;j<((d==2)?n:m);j++) + { + T v = (d==2)?X(i,j):X(j,i); + for(int k = 0;k<((d==2)?n:m);k++) + { + T u = (d==2)?X(i,k):X(k,i); + if(v == u) + { + counts[k]++; + } + } + } + assert(counts.size() > 0); + int max_count = -1; + int max_count_j = -1; + int j =0; + for(vector::iterator it = counts.begin();it(Eigen::Matrix const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::mode(Eigen::Matrix const&, int, Eigen::Matrix&); +#endif diff --git a/src/igl/mode.h b/src/igl/mode.h new file mode 100644 index 0000000000..4d09438812 --- /dev/null +++ b/src/igl/mode.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MODE_H +#define IGL_MODE_H +#include "igl_inline.h" +#include +namespace igl +{ + // Takes mode of coefficients in a matrix along a given dension + // + // Templates: + // T should be a eigen matrix primitive type like int or double + // Inputs: + // X m by n original matrix + // d dension along which to take mode, m or n + // Outputs: + // M vector containing mode along dension d, if d==1 then this will be a + // n-long vector if d==2 then this will be a m-long vector + template + IGL_INLINE void mode( + const Eigen::Matrix & X, + const int d, + Eigen::Matrix & M); +} + +#ifndef IGL_STATIC_LIBRARY +# include "mode.cpp" +#endif + +#endif diff --git a/src/igl/mosek/bbw.cpp b/src/igl/mosek/bbw.cpp new file mode 100644 index 0000000000..99d28ab152 --- /dev/null +++ b/src/igl/mosek/bbw.cpp @@ -0,0 +1,88 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "bbw.h" +#include "mosek_quadprog.h" +#include "../harmonic.h" +#include "../slice_into.h" +#include +#include +#include + + +template < + typename DerivedV, + typename DerivedEle, + typename Derivedb, + typename Derivedbc, + typename DerivedW> +IGL_INLINE bool igl::mosek::bbw( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & Ele, + const Eigen::PlainObjectBase & b, + const Eigen::PlainObjectBase & bc, + igl::BBWData & data, + igl::mosek::MosekData & mosek_data, + Eigen::PlainObjectBase & W + ) +{ + using namespace std; + using namespace Eigen; + assert(!data.partition_unity && "partition_unity not implemented yet"); + // number of domain vertices + int n = V.rows(); + // number of handles + int m = bc.cols(); + // Build biharmonic operator + Eigen::SparseMatrix Q; + harmonic(V,Ele,2,Q); + W.derived().resize(n,m); + // No linear terms + VectorXd c = VectorXd::Zero(n); + // No linear constraints + SparseMatrix A(0,n); + VectorXd uc(0,1),lc(0,1); + // Upper and lower box constraints (Constant bounds) + VectorXd ux = VectorXd::Ones(n); + VectorXd lx = VectorXd::Zero(n); + // Loop over handles + for(int i = 0;i= 1) + { + cout<<"BBW: Computing weight for handle "<, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, igl::BBWData&, igl::mosek::MosekData&, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/mosek/bbw.h b/src/igl/mosek/bbw.h new file mode 100644 index 0000000000..5dcae0fedc --- /dev/null +++ b/src/igl/mosek/bbw.h @@ -0,0 +1,60 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MOSEK_BBW_H +#define IGL_MOSEK_BBW_H +#include "../igl_inline.h" +#include "mosek_quadprog.h" +#include "../bbw.h" +#include + +namespace igl +{ + namespace mosek + { + // Compute Bounded Biharmonic Weights on a given domain (V,Ele) with a given + // set of boundary conditions + // + // Templates + // DerivedV derived type of eigen matrix for V (e.g. MatrixXd) + // DerivedF derived type of eigen matrix for F (e.g. MatrixXi) + // Derivedb derived type of eigen matrix for b (e.g. VectorXi) + // Derivedbc derived type of eigen matrix for bc (e.g. MatrixXd) + // DerivedW derived type of eigen matrix for W (e.g. MatrixXd) + // Inputs: + // V #V by dim vertex positions + // Ele #Elements by simplex-size list of element indices + // b #b boundary indices into V + // bc #b by #W list of boundary values + // data object containing options, initial guess --> solution and results + // mosek_data object containing mosek options + // Outputs: + // W #V by #W list of *unnormalized* weights to normalize use + // igl::normalize_row_sums(W,W); + // Returns true on success, false on failure + template < + typename DerivedV, + typename DerivedEle, + typename Derivedb, + typename Derivedbc, + typename DerivedW> + IGL_INLINE bool bbw( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & Ele, + const Eigen::PlainObjectBase & b, + const Eigen::PlainObjectBase & bc, + igl::BBWData & data, + igl::mosek::MosekData & mosek_data, + Eigen::PlainObjectBase & W); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "bbw.cpp" +#endif + +#endif diff --git a/src/igl/mosek/mosek_guarded.cpp b/src/igl/mosek/mosek_guarded.cpp new file mode 100644 index 0000000000..242d928f6f --- /dev/null +++ b/src/igl/mosek/mosek_guarded.cpp @@ -0,0 +1,24 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mosek_guarded.h" +#include + +IGL_INLINE MSKrescodee igl::mosek::mosek_guarded(const MSKrescodee r) +{ + using namespace std; + if(r != MSK_RES_OK) + { + /* In case of an error print error code and description. */ + char symname[MSK_MAX_STR_LEN]; + char desc[MSK_MAX_STR_LEN]; + MSK_getcodedesc(r,symname,desc); + cerr<<"MOSEK ERROR ("< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MOSEK_MOSEK_GUARDED_H +#define IGL_MOSEK_MOSEK_GUARDED_H +#include "../igl_inline.h" + +#include "mosek.h" +namespace igl +{ + namespace mosek + { + // Little function to wrap around mosek call to handle errors + // + // Inputs: + // r mosek error code returned from mosek call + // Returns r untouched + IGL_INLINE MSKrescodee mosek_guarded(const MSKrescodee r); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "mosek_guarded.cpp" +#endif + +#endif + diff --git a/src/igl/mosek/mosek_linprog.cpp b/src/igl/mosek/mosek_linprog.cpp new file mode 100644 index 0000000000..fe09d5adb5 --- /dev/null +++ b/src/igl/mosek/mosek_linprog.cpp @@ -0,0 +1,164 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mosek_linprog.h" +#include "../mosek/mosek_guarded.h" +#include "../harwell_boeing.h" +#include +#include +#include + +IGL_INLINE bool igl::mosek::mosek_linprog( + const Eigen::VectorXd & c, + const Eigen::SparseMatrix & A, + const Eigen::VectorXd & lc, + const Eigen::VectorXd & uc, + const Eigen::VectorXd & lx, + const Eigen::VectorXd & ux, + Eigen::VectorXd & x) +{ + // variables for mosek task, env and result code + MSKenv_t env; + // Create the MOSEK environment + mosek_guarded(MSK_makeenv(&env,NULL)); + // initialize mosek environment +#if MSK_VERSION_MAJOR <= 7 + mosek_guarded(MSK_initenv(env)); +#endif + const bool ret = mosek_linprog(c,A,lc,uc,lx,ux,env,x); + MSK_deleteenv(&env); + return ret; +} + +IGL_INLINE bool igl::mosek::mosek_linprog( + const Eigen::VectorXd & c, + const Eigen::SparseMatrix & A, + const Eigen::VectorXd & lc, + const Eigen::VectorXd & uc, + const Eigen::VectorXd & lx, + const Eigen::VectorXd & ux, + const MSKenv_t & env, + Eigen::VectorXd & x) +{ + // following http://docs.mosek.com/7.1/capi/Linear_optimization.html + using namespace std; + // number of constraints + const int m = A.rows(); + // number of variables + const int n = A.cols(); + + + vector vAv; + vector vAri,vAcp; + int nr; + harwell_boeing(A,nr,vAv,vAri,vAcp); + + MSKtask_t task; + // Create the optimization task + mosek_guarded(MSK_maketask(env,m,n,&task)); + // no threads + mosek_guarded(MSK_putintparam(task,MSK_IPAR_NUM_THREADS,1)); + if(m>0) + { + // Append 'm' empty constraints, the constrainst will initially have no + // bounds + mosek_guarded(MSK_appendcons(task,m)); + } + mosek_guarded(MSK_appendvars(task,n)); + + + const auto & key = [](const double lxj, const double uxj) -> + MSKboundkeye + { + MSKboundkeye k = MSK_BK_FR; + if(isfinite(lxj) && isfinite(uxj)) + { + if(lxj == uxj) + { + k = MSK_BK_FX; + }else{ + k = MSK_BK_RA; + } + }else if(isfinite(lxj)) + { + k = MSK_BK_LO; + }else if(isfinite(uxj)) + { + k = MSK_BK_UP; + } + return k; + }; + + // loop over variables + for(int j = 0;j 0) + { + // Set linear term c_j in the objective + mosek_guarded(MSK_putcj(task,j,c(j))); + } + + // Set constant bounds on variable j + const double lxj = lx.size()>0?lx[j]:-numeric_limits::infinity(); + const double uxj = ux.size()>0?ux[j]: numeric_limits::infinity(); + mosek_guarded(MSK_putvarbound(task,j,key(lxj,uxj),lxj,uxj)); + + if(m>0) + { + // Input column j of A + mosek_guarded( + MSK_putacol( + task, + j, + vAcp[j+1]-vAcp[j], + &vAri[vAcp[j]], + &vAv[vAcp[j]]) + ); + } + } + // loop over constraints + for(int i = 0;i0?lc[i]:-numeric_limits::infinity(); + const double uci = uc.size()>0?uc[i]: numeric_limits::infinity(); + mosek_guarded(MSK_putconbound(task,i,key(lci,uci),lci,uci)); + } + + // Now the optimizer has been prepared + MSKrescodee trmcode; + // run the optimizer + mosek_guarded(MSK_optimizetrm(task,&trmcode)); + // Get status + MSKsolstae solsta; + MSK_getsolsta (task,MSK_SOL_ITR,&solsta); + bool success = false; + switch(solsta) + { + case MSK_SOL_STA_OPTIMAL: + case MSK_SOL_STA_NEAR_OPTIMAL: + x.resize(n); + /* Request the basic solution. */ + MSK_getxx(task,MSK_SOL_BAS,x.data()); + success = true; + break; + case MSK_SOL_STA_DUAL_INFEAS_CER: + case MSK_SOL_STA_PRIM_INFEAS_CER: + case MSK_SOL_STA_NEAR_DUAL_INFEAS_CER: + case MSK_SOL_STA_NEAR_PRIM_INFEAS_CER: + //printf("Primal or dual infeasibility certificate found.\n"); + break; + case MSK_SOL_STA_UNKNOWN: + //printf("The status of the solution could not be determined.\n"); + break; + default: + //printf("Other solution status."); + break; + } + MSK_deletetask(&task); + return success; +} diff --git a/src/igl/mosek/mosek_linprog.h b/src/igl/mosek/mosek_linprog.h new file mode 100644 index 0000000000..57791cd609 --- /dev/null +++ b/src/igl/mosek/mosek_linprog.h @@ -0,0 +1,59 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MOSEK_MOSEK_LINPROG_H +#define IGL_MOSEK_MOSEK_LINPROG_H +#include "../igl_inline.h" +#include +#include +#include +namespace igl +{ + namespace mosek + { + // Solve a linear program using mosek: + // + // min c'x + // s.t. lc <= A x <= uc + // lx <= x <= ux + // + // Inputs: + // c #x list of linear objective coefficients + // A #A by #x matrix of linear inequality constraint coefficients + // lc #A list of lower constraint bounds + // uc #A list of upper constraint bounds + // lx #x list of lower variable bounds + // ux #x list of upper variable bounds + // Outputs: + // x #x list of solution values + // Returns true iff success. + IGL_INLINE bool mosek_linprog( + const Eigen::VectorXd & c, + const Eigen::SparseMatrix & A, + const Eigen::VectorXd & lc, + const Eigen::VectorXd & uc, + const Eigen::VectorXd & lx, + const Eigen::VectorXd & ux, + Eigen::VectorXd & x); + // Wrapper that keeps mosek environment alive (if licence checking is + // becoming a bottleneck) + IGL_INLINE bool mosek_linprog( + const Eigen::VectorXd & c, + const Eigen::SparseMatrix & A, + const Eigen::VectorXd & lc, + const Eigen::VectorXd & uc, + const Eigen::VectorXd & lx, + const Eigen::VectorXd & ux, + const MSKenv_t & env, + Eigen::VectorXd & x); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "mosek_linprog.cpp" +#endif +#endif diff --git a/src/igl/mosek/mosek_quadprog.cpp b/src/igl/mosek/mosek_quadprog.cpp new file mode 100644 index 0000000000..36d9e873b5 --- /dev/null +++ b/src/igl/mosek/mosek_quadprog.cpp @@ -0,0 +1,343 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mosek_quadprog.h" +#include "mosek_guarded.h" +#include +#include "../find.h" +#include "../verbose.h" +#include "../speye.h" +#include "../matrix_to_list.h" +#include "../list_to_matrix.h" +#include "../harwell_boeing.h" +#include "../EPS.h" + + +igl::mosek::MosekData::MosekData() +{ + // These are the default settings that worked well for BBW. Your miles may + // very well be kilometers. + + // >1e0 NONSOLUTION + // 1e-1 artifacts in deformation + // 1e-3 artifacts in isolines + // 1e-4 seems safe + // 1e-8 MOSEK DEFAULT SOLUTION + douparam[MSK_DPAR_INTPNT_TOL_REL_GAP]=1e-8; +#if MSK_VERSION_MAJOR >= 8 + douparam[MSK_DPAR_INTPNT_QO_TOL_REL_GAP]=1e-12; +#endif + // Force using multiple threads, not sure if MOSEK is properly destroying + //extra threads... +#if MSK_VERSION_MAJOR >= 7 + intparam[MSK_IPAR_NUM_THREADS] = 6; +#elif MSK_VERSION_MAJOR == 6 + intparam[MSK_IPAR_INTPNT_NUM_THREADS] = 6; +#endif +#if MSK_VERSION_MAJOR == 6 + // Force turn off data check + intparam[MSK_IPAR_DATA_CHECK]=MSK_OFF; +#endif + // Turn off presolving + // intparam[MSK_IPAR_PRESOLVE_USE] = MSK_PRESOLVE_MODE_OFF; + // Force particular matrix reordering method + // MSK_ORDER_METHOD_NONE cuts time in half roughly, since half the time is + // usually spent reordering the matrix + // !! WARNING Setting this parameter to anything but MSK_ORDER_METHOD_FREE + // seems to have the effect of setting it to MSK_ORDER_METHOD_NONE + // *Or maybe Mosek is spending a bunch of time analyzing the matrix to + // choose the right ordering method when really any of them are + // instantaneous + intparam[MSK_IPAR_INTPNT_ORDER_METHOD] = MSK_ORDER_METHOD_NONE; + // 1.0 means optimizer is very lenient about declaring model infeasible + douparam[MSK_DPAR_INTPNT_TOL_INFEAS] = 1e-8; + // Hard to say if this is doing anything, probably nothing dramatic + douparam[MSK_DPAR_INTPNT_TOL_PSAFE]= 1e2; + // Turn off convexity check + intparam[MSK_IPAR_CHECK_CONVEXITY] = MSK_CHECK_CONVEXITY_NONE; +} + +template +IGL_INLINE bool igl::mosek::mosek_quadprog( + const Index n, + std::vector & Qi, + std::vector & Qj, + std::vector & Qv, + const std::vector & c, + const Scalar cf, + const Index m, + std::vector & Av, + std::vector & Ari, + const std::vector & Acp, + const std::vector & lc, + const std::vector & uc, + const std::vector & lx, + const std::vector & ux, + MosekData & mosek_data, + std::vector & x) +{ + // I J V vectors of Q should all be same length + assert(Qv.size() == Qi.size()); + assert(Qv.size() == Qj.size()); + // number of columns in linear constraint matrix must be ≤ number of + // variables + assert( (int)Acp.size() == (n+1)); + // linear bound vectors must be size of number of constraints or empty + assert( ((int)lc.size() == m) || ((int)lc.size() == 0)); + assert( ((int)uc.size() == m) || ((int)uc.size() == 0)); + // constant bound vectors must be size of number of variables or empty + assert( ((int)lx.size() == n) || ((int)lx.size() == 0)); + assert( ((int)ux.size() == n) || ((int)ux.size() == 0)); + + // allocate space for solution in x + x.resize(n); + + // variables for mosek task, env and result code + MSKenv_t env; + MSKtask_t task; + + // Create the MOSEK environment +#if MSK_VERSION_MAJOR >= 7 + mosek_guarded(MSK_makeenv(&env,NULL)); +#elif MSK_VERSION_MAJOR == 6 + mosek_guarded(MSK_makeenv(&env,NULL,NULL,NULL,NULL)); +#endif + ///* Directs the log stream to the 'printstr' function. */ + //// Little function mosek needs in order to know how to print to std out + //const auto & printstr = [](void *handle, char str[]) + //{ + // printf("%s",str); + //} + //mosek_guarded(MSK_linkfunctoenvstream(env,MSK_STREAM_LOG,NULL,printstr)); + // initialize mosek environment +#if MSK_VERSION_MAJOR <= 7 + mosek_guarded(MSK_initenv(env)); +#endif + // Create the optimization task + mosek_guarded(MSK_maketask(env,m,n,&task)); + verbose("Creating task with %ld linear constraints and %ld variables...\n",m,n); + //// Tell mosek how to print to std out + //mosek_guarded(MSK_linkfunctotaskstream(task,MSK_STREAM_LOG,NULL,printstr)); + // Give estimate of number of variables + mosek_guarded(MSK_putmaxnumvar(task,n)); + if(m>0) + { + // Give estimate of number of constraints + mosek_guarded(MSK_putmaxnumcon(task,m)); + // Give estimate of number of non zeros in A + mosek_guarded(MSK_putmaxnumanz(task,Av.size())); + } + // Give estimate of number of non zeros in Q + mosek_guarded(MSK_putmaxnumqnz(task,Qv.size())); + if(m>0) + { + // Append 'm' empty constraints, the constrainst will initially have no + // bounds +#if MSK_VERSION_MAJOR >= 7 + mosek_guarded(MSK_appendcons(task,m)); +#elif MSK_VERSION_MAJOR == 6 + mosek_guarded(MSK_append(task,MSK_ACC_CON,m)); +#endif + } + // Append 'n' variables +#if MSK_VERSION_MAJOR >= 7 + mosek_guarded(MSK_appendvars(task,n)); +#elif MSK_VERSION_MAJOR == 6 + mosek_guarded(MSK_append(task,MSK_ACC_VAR,n)); +#endif + // add a contant term to the objective + mosek_guarded(MSK_putcfix(task,cf)); + + // loop over variables + for(int j = 0;j 0) + { + // Set linear term c_j in the objective + mosek_guarded(MSK_putcj(task,j,c[j])); + } + + // Set constant bounds on variable j + if(lx[j] == ux[j]) + { + mosek_guarded(MSK_putbound(task,MSK_ACC_VAR,j,MSK_BK_FX,lx[j],ux[j])); + }else + { + mosek_guarded(MSK_putbound(task,MSK_ACC_VAR,j,MSK_BK_RA,lx[j],ux[j])); + } + + if(m>0) + { + // Input column j of A +#if MSK_VERSION_MAJOR >= 7 + mosek_guarded( + MSK_putacol( + task, + j, + Acp[j+1]-Acp[j], + &Ari[Acp[j]], + &Av[Acp[j]]) + ); +#elif MSK_VERSION_MAJOR == 6 + mosek_guarded( + MSK_putavec( + task, + MSK_ACC_VAR, + j, + Acp[j+1]-Acp[j], + &Ari[Acp[j]], + &Av[Acp[j]]) + ); +#endif + } + } + + // loop over constraints + for(int i = 0;i::iterator pit = mosek_data.intparam.begin(); + pit != mosek_data.intparam.end(); + pit++) + { + mosek_guarded(MSK_putintparam(task,pit->first,pit->second)); + } + for( + std::map::iterator pit = mosek_data.douparam.begin(); + pit != mosek_data.douparam.end(); + pit++) + { + mosek_guarded(MSK_putdouparam(task,pit->first,pit->second)); + } + + // Now the optimizer has been prepared + MSKrescodee trmcode; + // run the optimizer + mosek_guarded(MSK_optimizetrm(task,&trmcode)); + + //// Print a summary containing information about the solution for debugging + //// purposes + //MSK_solutionsummary(task,MSK_STREAM_LOG); + + // Get status of solution + MSKsolstae solsta; +#if MSK_VERSION_MAJOR >= 7 + MSK_getsolsta (task,MSK_SOL_ITR,&solsta); +#elif MSK_VERSION_MAJOR == 6 + MSK_getsolutionstatus(task,MSK_SOL_ITR,NULL,&solsta); +#endif + + bool success = false; + switch(solsta) + { + case MSK_SOL_STA_OPTIMAL: + case MSK_SOL_STA_NEAR_OPTIMAL: + MSK_getsolutionslice(task,MSK_SOL_ITR,MSK_SOL_ITEM_XX,0,n,&x[0]); + //printf("Optimal primal solution\n"); + //for(size_t j=0; j & Q, + const Eigen::VectorXd & c, + const double cf, + const Eigen::SparseMatrix & A, + const Eigen::VectorXd & lc, + const Eigen::VectorXd & uc, + const Eigen::VectorXd & lx, + const Eigen::VectorXd & ux, + MosekData & mosek_data, + Eigen::VectorXd & x) +{ + using namespace Eigen; + using namespace std; + + typedef int Index; + typedef double Scalar; + // Q should be square + assert(Q.rows() == Q.cols()); + // Q should be symmetric +#ifdef EIGEN_HAS_A_BUG_AND_FAILS_TO_LET_ME_COMPUTE_Q_MINUS_Q_TRANSPOSE + assert( (Q-Q.transpose()).sum() < FLOAT_EPS); +#endif + // Only keep lower triangular part of Q + SparseMatrix QL; + //QL = Q.template triangularView(); + QL = Q.triangularView(); + VectorXi Qi,Qj; + VectorXd Qv; + find(QL,Qi,Qj,Qv); + vector vQi = matrix_to_list(Qi); + vector vQj = matrix_to_list(Qj); + vector vQv = matrix_to_list(Qv); + + // Convert linear term + vector vc = matrix_to_list(c); + + assert(lc.size() == A.rows()); + assert(uc.size() == A.rows()); + // Convert A to harwell boeing format + vector vAv; + vector vAr,vAc; + Index nr; + harwell_boeing(A,nr,vAv,vAr,vAc); + + assert(lx.size() == Q.rows()); + assert(ux.size() == Q.rows()); + vector vlc = matrix_to_list(lc); + vector vuc = matrix_to_list(uc); + vector vlx = matrix_to_list(lx); + vector vux = matrix_to_list(ux); + + vector vx; + bool ret = mosek_quadprog( + Q.rows(),vQi,vQj,vQv, + vc, + cf, + nr, + vAv, vAr, vAc, + vlc,vuc, + vlx,vux, + mosek_data, + vx); + list_to_matrix(vx,x); + return ret; +} +#ifdef IGL_STATIC_LIBRARY +// Explicit template declarations +#endif diff --git a/src/igl/mosek/mosek_quadprog.h b/src/igl/mosek/mosek_quadprog.h new file mode 100644 index 0000000000..38343318ac --- /dev/null +++ b/src/igl/mosek/mosek_quadprog.h @@ -0,0 +1,145 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MOSEK_MOSEK_QUADPROG_H +#define IGL_MOSEK_MOSEK_QUADPROG_H +#include "../igl_inline.h" +#include +#include +#include + + +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include + +namespace igl +{ + namespace mosek + { + struct MosekData + { + // Integer parameters + std::map intparam; + // Double parameters + std::map douparam; + // Default values + IGL_INLINE MosekData(); + }; + // Solve a convex quadratic optimization problem with linear and constant + // bounds, that is: + // + // Minimize: ½ * xT * Qâ° * x + cT * x + cf + // + // Subject to: lc ≤ Ax ≤ uc + // lx ≤ x ≤ ux + // + // where we are trying to find the optimal vector of values x. + // + // Note: Qâ° must be symmetric and the ½ is a convention of MOSEK + // + // Note: Because of how MOSEK accepts different parts of the system, Q + // should be stored in IJV (aka Coordinate) format and should only include + // entries in the lower triangle. A should be stored in Column compressed + // (aka Harwell Boeing) format. As described: + // http://netlib.org/linalg/html_templates/node92.html + // or + // http://en.wikipedia.org/wiki/Sparse_matrix + // #Compressed_sparse_column_.28CSC_or_CCS.29 + // + // + // Templates: + // Index type for index variables + // Scalar type for floating point variables (gets cast to double?) + // Input: + // n number of variables, i.e. size of x + // Qi vector of qnnz row indices of non-zeros in LOWER TRIANGLE ONLY of + // Qâ° + // Qj vector of qnnz column indices of non-zeros in LOWER TRIANGLE ONLY + // of Qâ° + // Qv vector of qnnz values of non-zeros in LOWER TRIANGLE ONLY of Qâ°, + // such that: + // + // Qâ°(Qi[k],Qj[k]) = Qv[k] for k ∈ [0,Qnnz-1], where Qnnz is the + // + // number of non-zeros in Qâ° + // c (optional) vector of n values of c, transpose of coefficient row + // vector of linear terms, EMPTY means c == 0 + // cf (ignored) value of constant term in objective, 0 means cf == 0, so + // optional only in the sense that it is mandatory + // m number of constraints, therefore also number of rows in linear + // constraint coefficient matrix A, and in linear constraint bound + // vectors lc and uc + // Av vector of non-zero values of A, in column compressed order + // Ari vector of row indices corresponding to non-zero values of A, + // Acp vector of indices into Ari and Av of the first entry for each + // column of A, size(Acp) = (# columns of A) + 1 = n + 1 + // lc vector of m linear constraint lower bounds + // uc vector of m linear constraint upper bounds + // lx vector of n constant lower bounds + // ux vector of n constant upper bounds + // Output: + // x vector of size n to hold output of optimization + // Return: + // true only if optimization was successful with no errors + // + // Note: All indices are 0-based + // + template + IGL_INLINE bool mosek_quadprog( + const Index n, + /* mosek won't allow this to be const*/ std::vector & Qi, + /* mosek won't allow this to be const*/ std::vector & Qj, + /* mosek won't allow this to be const*/ std::vector & Qv, + const std::vector & c, + const Scalar cf, + const Index m, + /* mosek won't allow this to be const*/ std::vector & Av, + /* mosek won't allow this to be const*/ std::vector & Ari, + const std::vector & Acp, + const std::vector & lc, + const std::vector & uc, + const std::vector & lx, + const std::vector & ux, + MosekData & mosek_data, + std::vector & x); + // Wrapper with Eigen elements + // + // Inputs: + // Q n by n square quadratic coefficients matrix **only lower triangle + // is used**. + // c n-long vector of linear coefficients + // cf constant coefficient + // A m by n square linear coefficienst matrix of inequality constraints + // lc m-long vector of lower bounds for linear inequality constraints + // uc m-long vector of upper bounds for linear inequality constraints + // lx n-long vector of lower bounds + // ux n-long vector of upper bounds + // mosek_data parameters struct + // Outputs: + // x n-long solution vector + // Returns true only if optimization finishes without error + // + IGL_INLINE bool mosek_quadprog( + const Eigen::SparseMatrix & Q, + const Eigen::VectorXd & c, + const double cf, + const Eigen::SparseMatrix & A, + const Eigen::VectorXd & lc, + const Eigen::VectorXd & uc, + const Eigen::VectorXd & lx, + const Eigen::VectorXd & ux, + MosekData & mosek_data, + Eigen::VectorXd & x); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "mosek_quadprog.cpp" +#endif + +#endif diff --git a/src/igl/mvc.cpp b/src/igl/mvc.cpp new file mode 100644 index 0000000000..a6fed49d8c --- /dev/null +++ b/src/igl/mvc.cpp @@ -0,0 +1,196 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "mvc.h" +#include +#include +#include + +// Broken Implementation +IGL_INLINE void igl::mvc(const Eigen::MatrixXd &V, const Eigen::MatrixXd &C, Eigen::MatrixXd &W) +{ + + // at least three control points + assert(C.rows()>2); + + // dimension of points + assert(C.cols() == 3 || C.cols() == 2); + assert(V.cols() == 3 || V.cols() == 2); + + // number of polygon points + int num = C.rows(); + + Eigen::MatrixXd V1,C1; + int i_prev, i_next; + + // check if either are 3D but really all z's are 0 + bool V_flat = (V.cols() == 3) && (std::sqrt( (V.col(3)).dot(V.col(3)) ) < 1e-10); + bool C_flat = (C.cols() == 3) && (std::sqrt( (C.col(3)).dot(C.col(3)) ) < 1e-10); + + // if both are essentially 2D then ignore z-coords + if((C.cols() == 2 || C_flat) && (V.cols() == 2 || V_flat)) + { + // ignore z coordinate + V1 = V.block(0,0,V.rows(),2); + C1 = C.block(0,0,C.rows(),2); + } + else + { + // give dummy z coordinate to either mesh or poly + if(V.rows() == 2) + { + V1 = Eigen::MatrixXd(V.rows(),3); + V1.block(0,0,V.rows(),2) = V; + } + else + V1 = V; + + if(C.rows() == 2) + { + C1 = Eigen::MatrixXd(C.rows(),3); + C1.block(0,0,C.rows(),2) = C; + } + else + C1 = C; + + // check that C is planar + // average normal around poly corners + + Eigen::Vector3d n = Eigen::Vector3d::Zero(); + // take centroid as point on plane + Eigen::Vector3d p = Eigen::Vector3d::Zero(); + for (int i = 0; i0)?(i-1):(num-1); + i_next = (i1e-10) + std::cerr<<"Distance from V to plane of C is large..."< solver = basis.colPivHouseholderQr(); + // Throw away coordinates in normal direction + V1 = solver.solve(V1.transpose()).transpose().block(0,0,V1.rows(),2); + C1 = solver.solve(C1.transpose()).transpose().block(0,0,C1.rows(),2); + + } + + // vectors from V to every C, where CmV(i,j,:) is the vector from domain + // vertex j to handle i + double EPS = 1e-10; + Eigen::MatrixXd WW = Eigen::MatrixXd(C1.rows(), V1.rows()); + Eigen::MatrixXd dist_C_V (C1.rows(), V1.rows()); + std::vector< std::pair > on_corner(0); + std::vector< std::pair > on_segment(0); + for (int i = 0; i0)?(i-1):(num-1); + i_next = (i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_MVC_H +#define IGL_MVC_H + +#include "igl_inline.h" +#include + +namespace igl +{ + // MVC - MEAN VALUE COORDINATES + // + // mvc(V,C,W) + // + // Inputs: + // V #V x dim list of vertex positions (dim = 2 or dim = 3) + // C #C x dim list of polygon vertex positions in counter-clockwise order + // (dim = 2 or dim = 3) + // + // Outputs: + // W weights, #V by #C matrix of weights + // + // Known Bugs: implementation is listed as "Broken" + IGL_INLINE void mvc( + const Eigen::MatrixXd &V, + const Eigen::MatrixXd &C, + Eigen::MatrixXd &W); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "mvc.cpp" +#endif + +#endif diff --git a/src/igl/nchoosek.cpp b/src/igl/nchoosek.cpp new file mode 100644 index 0000000000..88ab5449dd --- /dev/null +++ b/src/igl/nchoosek.cpp @@ -0,0 +1,77 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Olga Diamanti, Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "nchoosek.h" +#include +#include + +IGL_INLINE double igl::nchoosek(const int n, const int k) +{ + if(k>n/2) + { + return nchoosek(n,n-k); + }else if(k==1) + { + return n; + }else + { + double c = 1; + for(int i = 1;i<=k;i++) + { + c *= (((double)n-k+i)/((double)i)); + } + return std::round(c); + } +} + +template < typename DerivedV, typename DerivedU> +IGL_INLINE void igl::nchoosek( + const Eigen::MatrixBase & V, + const int k, + Eigen::PlainObjectBase & U) +{ + using namespace Eigen; + if(V.size() == 0) + { + U.resize(0,k); + return; + } + assert((V.cols() == 1 || V.rows() == 1) && "V must be a vector"); + U.resize(nchoosek(V.size(),k),k); + int running_i = 0; + int running_j = 0; + Matrix running(1,k); + int N = V.size(); + const std::function doCombs = + [&running,&N,&doCombs,&running_i,&running_j,&U,&V](int offset, int k) + { + if(k==0) + { + U.row(running_i) = running; + running_i++; + return; + } + for (int i = offset; i <= N - k; ++i) + { + running(running_j) = V(i); + running_j++; + doCombs(i+1,k-1); + running_j--; + } + }; + doCombs(0,k); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::nchoosek, Eigen::Matrix >(Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&); +#if EIGEN_VERSION_AT_LEAST(3,3,0) +#else +template void igl::nchoosek, Eigen::Matrix >, Eigen::Matrix >(Eigen::MatrixBase, Eigen::Matrix > > const&, int, Eigen::PlainObjectBase >&); +#endif + +#endif diff --git a/src/igl/nchoosek.h b/src/igl/nchoosek.h new file mode 100644 index 0000000000..ec882e6c63 --- /dev/null +++ b/src/igl/nchoosek.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Olga Diamanti, Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_NCHOOSEK +#define IGL_NCHOOSEK +#include "igl_inline.h" +#include "deprecated.h" +#include + +#include + +namespace igl +{ + // NCHOOSEK Like matlab's nchoosek. + // + // Inputs: + // n total number elements + // k size of sub-set to consider + // Returns number of k-size combinations out of the set [1,...,n] + IGL_INLINE double nchoosek(const int n, const int k); + // + // Inputs: + // V n-long vector of elements + // k size of sub-set to consider + // Outputs: + // U nchoosek by k long matrix where each row is a unique k-size + // combination + template < typename DerivedV, typename DerivedU> + IGL_INLINE void nchoosek( + const Eigen::MatrixBase & V, + const int k, + Eigen::PlainObjectBase & U); +} + + +#ifndef IGL_STATIC_LIBRARY +#include "nchoosek.cpp" +#endif + + +#endif /* defined(IGL_NCHOOSEK) */ diff --git a/src/igl/next_filename.cpp b/src/igl/next_filename.cpp new file mode 100644 index 0000000000..b453a149af --- /dev/null +++ b/src/igl/next_filename.cpp @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "next_filename.h" +#include "STR.h" +#include "file_exists.h" +#include +#include + +bool igl::next_filename( + const std::string & prefix, + const int zeros, + const std::string & suffix, + std::string & next) +{ + using namespace std; + // O(n), for huge lists could at least find bounds with exponential search + // and then narrow with binary search O(log(n)) + int i = 0; + while(true) + { + next = STR(prefix << setfill('0') << setw(zeros)< 0 && i >= pow(10,zeros)) + { + return false; + } + } +} + diff --git a/src/igl/next_filename.h b/src/igl/next_filename.h new file mode 100644 index 0000000000..2aa3094da7 --- /dev/null +++ b/src/igl/next_filename.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_NEXT_FILENAME_H +#define IGL_NEXT_FILENAME_H +#include "igl_inline.h" +#include +namespace igl +{ + // Find the file with the first filename of the form + // "prefix%0[zeros]dsuffix" + // + // Inputs: + // prefix path to containing dir and filename prefix + // zeros number of leading zeros as if digit printed with printf + // suffix suffix of filename and extension (should included dot) + // Outputs: + // next path to next file + // Returns true if found, false if exceeding range in zeros + IGL_INLINE bool next_filename( + const std::string & prefix, + const int zeros, + const std::string & suffix, + std::string & next); +} + +#ifndef IGL_STATIC_LIBRARY +# include "next_filename.cpp" +#endif + +#endif + diff --git a/src/igl/normal_derivative.cpp b/src/igl/normal_derivative.cpp new file mode 100644 index 0000000000..d943fe0e23 --- /dev/null +++ b/src/igl/normal_derivative.cpp @@ -0,0 +1,118 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "LinSpaced.h" +#include "normal_derivative.h" +#include "cotmatrix_entries.h" +#include "slice.h" +#include + +template < + typename DerivedV, + typename DerivedEle, + typename Scalar> +IGL_INLINE void igl::normal_derivative( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & Ele, + Eigen::SparseMatrix& DD) +{ + using namespace Eigen; + using namespace std; + // Element simplex-size + const size_t ss = Ele.cols(); + assert( ((ss==3) || (ss==4)) && "Only triangles or tets"); + // cotangents + Matrix C; + cotmatrix_entries(V,Ele,C); + vector > IJV; + // Number of elements + const size_t m = Ele.rows(); + // Number of vertices + const size_t n = V.rows(); + switch(ss) + { + default: + assert(false); + return; + case 4: + { + const MatrixXi DDJ = + slice( + Ele, + (VectorXi(24)<< + 1,0,2,0,3,0,2,1,3,1,0,1,3,2,0,2,1,2,0,3,1,3,2,3).finished(), + 2); + MatrixXi DDI(m,24); + for(size_t f = 0;f<4;f++) + { + const auto & I = (igl::LinSpaced(m,0,m-1).array()+f*m).eval(); + for(size_t r = 0;r<6;r++) + { + DDI.col(f*6+r) = I; + } + } + const DiagonalMatrix S = + (Matrix(1,-1).template replicate<12,1>()).asDiagonal(); + Matrix DDV = + slice( + C, + (VectorXi(24)<< + 2,2,1,1,3,3,0,0,4,4,2,2,5,5,1,1,0,0,3,3,4,4,5,5).finished(), + 2); + DDV *= S; + + IJV.reserve(DDV.size()); + for(size_t f = 0;f<6*4;f++) + { + for(size_t e = 0;e(DDI(e,f),DDJ(e,f),DDV(e,f))); + } + } + DD.resize(m*4,n); + DD.setFromTriplets(IJV.begin(),IJV.end()); + break; + } + case 3: + { + const MatrixXi DDJ = + slice(Ele,(VectorXi(12)<<2,0,1,0,0,1,2,1,1,2,0,2).finished(),2); + MatrixXi DDI(m,12); + for(size_t f = 0;f<3;f++) + { + const auto & I = (igl::LinSpaced(m,0,m-1).array()+f*m).eval(); + for(size_t r = 0;r<4;r++) + { + DDI.col(f*4+r) = I; + } + } + const DiagonalMatrix S = + (Matrix(1,-1).template replicate<6,1>()).asDiagonal(); + Matrix DDV = + slice(C,(VectorXi(12)<<1,1,2,2,2,2,0,0,0,0,1,1).finished(),2); + DDV *= S; + + IJV.reserve(DDV.size()); + for(size_t f = 0;f<12;f++) + { + for(size_t e = 0;e(DDI(e,f),DDJ(e,f),DDV(e,f))); + } + } + DD.resize(m*3,n); + DD.setFromTriplets(IJV.begin(),IJV.end()); + break; + } + } + +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::normal_derivative, Eigen::Matrix, double>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/normal_derivative.h b/src/igl/normal_derivative.h new file mode 100644 index 0000000000..861b555555 --- /dev/null +++ b/src/igl/normal_derivative.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_NORMAL_DERIVATIVE_H +#define IGL_NORMAL_DERIVATIVE_H +#include "igl_inline.h" + +#include +#include +namespace igl +{ + // NORMAL_DERIVATIVE Computes the directional derivative **normal** to + // **all** (half-)edges of a triangle mesh (not just boundary edges). These + // are integrated along the edge: they're the per-face constant gradient dot + // the rotated edge vector (unit rotated edge vector for direction then + // magnitude for integration). + // + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by 3|4 list of triangle|tetrahedron indices into V + // Outputs: + // DD #F*3|4 by #V sparse matrix representing operator to compute + // directional derivative with respect to each facet of each element. + // + template < + typename DerivedV, + typename DerivedEle, + typename Scalar> + IGL_INLINE void normal_derivative( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & Ele, + Eigen::SparseMatrix& DD); +} + +#ifndef IGL_STATIC_LIBRARY +# include "normal_derivative.cpp" +#endif + +#endif + diff --git a/src/igl/normalize_quat.cpp b/src/igl/normalize_quat.cpp new file mode 100644 index 0000000000..f110d6b754 --- /dev/null +++ b/src/igl/normalize_quat.cpp @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "normalize_quat.h" + +#include "EPS.h" +#include + +template +IGL_INLINE bool igl::normalize_quat( + const Q_type *q, + Q_type *out) +{ + // Get length + Q_type len = sqrt( + q[0]*q[0]+ + q[1]*q[1]+ + q[2]*q[2]+ + q[3]*q[3]); + + // Noramlize each coordinate + out[0] = q[0]/len; + out[1] = q[1]/len; + out[2] = q[2]/len; + out[3] = q[3]/len; + + // Test whether length was below Epsilon + return (len > igl::EPS()); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::normalize_quat(double const*, double*); +// generated by autoexplicit.sh +template bool igl::normalize_quat(float const*, float*); +#endif diff --git a/src/igl/normalize_quat.h b/src/igl/normalize_quat.h new file mode 100644 index 0000000000..e97759b178 --- /dev/null +++ b/src/igl/normalize_quat.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_NORMALIZE_QUAT_H +#define IGL_NORMALIZE_QUAT_H +#include "igl_inline.h" + +namespace igl +{ + // Normalize a quaternion + // A Quaternion, q, is defined here as an arrays of four scalars (x,y,z,w), + // such that q = x*i + y*j + z*k + w + // Inputs: + // q input quaternion + // Outputs: + // out result of normalization, allowed to be same as q + // Returns true on success, false if len(q) < EPS + template + IGL_INLINE bool normalize_quat( + const Q_type *q, + Q_type *out); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "normalize_quat.cpp" +#endif + +#endif diff --git a/src/igl/normalize_row_lengths.cpp b/src/igl/normalize_row_lengths.cpp new file mode 100644 index 0000000000..3d7e566e88 --- /dev/null +++ b/src/igl/normalize_row_lengths.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "normalize_row_lengths.h" + +template +IGL_INLINE void igl::normalize_row_lengths( + const Eigen::PlainObjectBase& A, + Eigen::PlainObjectBase & B) +{ + // Resize output + B.resizeLike(A); + + // loop over rows + for(int i = 0; i < A.rows();i++) + { + B.row(i) = A.row(i).normalized(); + } + //// Or just: + //B = A; + //B.rowwise().normalize(); +} +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::normalize_row_lengths >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::normalize_row_lengths >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::normalize_row_lengths >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/normalize_row_lengths.h b/src/igl/normalize_row_lengths.h new file mode 100644 index 0000000000..c305c0f65b --- /dev/null +++ b/src/igl/normalize_row_lengths.h @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_NORMALIZE_ROW_LENGTHS_H +#define IGL_NORMALIZE_ROW_LENGTHS_H +#include "igl_inline.h" +#include + +// History: +// March 24, 2012: Alec changed function name from normalize_rows to +// normalize_row_lengths to avoid confusion with normalize_row_sums + +namespace igl +{ + // Obsolete: just use A.rowwise().normalize() or B=A.rowwise().normalized(); + // + // Normalize the rows in A so that their lengths are each 1 and place the new + // entries in B + // Inputs: + // A #rows by k input matrix + // Outputs: + // B #rows by k input matrix, can be the same as A + template + IGL_INLINE void normalize_row_lengths( + const Eigen::PlainObjectBase& A, + Eigen::PlainObjectBase & B); +} + +#ifndef IGL_STATIC_LIBRARY +# include "normalize_row_lengths.cpp" +#endif + +#endif diff --git a/src/igl/normalize_row_sums.cpp b/src/igl/normalize_row_sums.cpp new file mode 100644 index 0000000000..1204e073df --- /dev/null +++ b/src/igl/normalize_row_sums.cpp @@ -0,0 +1,29 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "normalize_row_sums.h" + +template +IGL_INLINE void igl::normalize_row_sums( + const Eigen::MatrixBase& A, + Eigen::MatrixBase & B) +{ +#ifndef NDEBUG + // loop over rows + for(int i = 0; i < A.rows();i++) + { + typename DerivedB::Scalar sum = A.row(i).sum(); + assert(sum != 0); + } +#endif + B = (A.array().colwise() / A.rowwise().sum().array()).eval(); +} +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::normalize_row_sums, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase >&); +template void igl::normalize_row_sums, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase >&); +#endif diff --git a/src/igl/normalize_row_sums.h b/src/igl/normalize_row_sums.h new file mode 100644 index 0000000000..a832e82a60 --- /dev/null +++ b/src/igl/normalize_row_sums.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_NORMALIZE_ROW_SUMS_H +#define IGL_NORMALIZE_ROW_SUMS_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Normalize the rows in A so that their sums are each 1 and place the new + // entries in B + // Inputs: + // A #rows by k input matrix + // Outputs: + // B #rows by k input matrix, can be the same as A + // + // Note: This is just calling an Eigen one-liner. + template + IGL_INLINE void normalize_row_sums( + const Eigen::MatrixBase& A, + Eigen::MatrixBase & B); +} + +#ifndef IGL_STATIC_LIBRARY +# include "normalize_row_sums.cpp" +#endif + +#endif diff --git a/src/igl/null.cpp b/src/igl/null.cpp new file mode 100644 index 0000000000..b2291f454d --- /dev/null +++ b/src/igl/null.cpp @@ -0,0 +1,21 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "null.h" +#include "EPS.h" + +template +IGL_INLINE void igl::null( + const Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & N) +{ + using namespace Eigen; + typedef typename DerivedA::Scalar Scalar; + JacobiSVD svd(A, ComputeFullV); + svd.setThreshold(A.cols() * svd.singularValues().maxCoeff() * EPS()); + N = svd.matrixV().rightCols(A.cols()-svd.rank()); +} diff --git a/src/igl/null.h b/src/igl/null.h new file mode 100644 index 0000000000..2349217fb2 --- /dev/null +++ b/src/igl/null.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_NULL_H +#define IGL_NULL_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Like MATLAB's null + // + // Compute a basis for the null space for the given matrix A: the columns of + // the output N form a basis for the space orthogonal to that spanned by the + // rows of A. + // + // Inputs: + // A m by n matrix + // Outputs: + // N n by r matrix, where r is the row rank of A + template + IGL_INLINE void null( + const Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & N); +} + +#ifndef IGL_STATIC_LIBRARY +# include "null.cpp" +#endif + +#endif diff --git a/src/igl/octree.cpp b/src/igl/octree.cpp new file mode 100644 index 0000000000..6ed95d8462 --- /dev/null +++ b/src/igl/octree.cpp @@ -0,0 +1,176 @@ +#include "octree.h" +#include +#include + +namespace igl { + template + IGL_INLINE void octree(const Eigen::MatrixBase& P, + std::vector > & point_indices, + Eigen::PlainObjectBase& CH, + Eigen::PlainObjectBase& CN, + Eigen::PlainObjectBase& W) + { + + + + const int MAX_DEPTH = 30000; + + typedef typename DerivedCH::Scalar ChildrenType; + typedef typename DerivedCN::Scalar CentersType; + typedef typename DerivedW::Scalar WidthsType; + typedef Eigen::Matrix Vector8i; + typedef Eigen::Matrix RowVector3PType; + typedef Eigen::Matrix RowVector3CentersType; + + std::vector, + Eigen::aligned_allocator > > children; + std::vector, + Eigen::aligned_allocator > > centers; + std::vector widths; + + auto get_octant = [](RowVector3PType location, + RowVector3CentersType center){ + // We use a binary numbering of children. Treating the parent cell's + // center as the origin, we number the octants in the following manner: + // The first bit is 1 iff the octant's x coordinate is positive + // The second bit is 1 iff the octant's y coordinate is positive + // The third bit is 1 iff the octant's z coordinate is positive + // + // For example, the octant with negative x, positive y, positive z is: + // 110 binary = 6 decimal + IndexType index = 0; + if( location(0) >= center(0)){ + index = index + 1; + } + if( location(1) >= center(1)){ + index = index + 2; + } + if( location(2) >= center(2)){ + index = index + 4; + } + return index; + }; + + + std::function< RowVector3CentersType(const RowVector3CentersType, + const CentersType, + const ChildrenType) > + translate_center = + [](const RowVector3CentersType & parent_center, + const CentersType h, + const ChildrenType child_index){ + RowVector3CentersType change_vector; + change_vector << -h,-h,-h; + + //positive x chilren are 1,3,4,7 + if(child_index % 2){ + change_vector(0) = h; + } + //positive y children are 2,3,6,7 + if(child_index == 2 || child_index == 3 || + child_index == 6 || child_index == 7){ + change_vector(1) = h; + } + //positive z children are 4,5,6,7 + if(child_index > 3){ + change_vector(2) = h; + } + RowVector3CentersType output = parent_center + change_vector; + return output; + }; + + // How many cells do we have so far? + IndexType m = 0; + + // Useful list of number 0..7 + const Vector8i zero_to_seven = (Vector8i()<<0,1,2,3,4,5,6,7).finished(); + const Vector8i neg_ones = (Vector8i()<<-1,-1,-1,-1,-1,-1,-1,-1).finished(); + + std::function< void(const ChildrenType, const int) > helper; + helper = [&helper,&translate_center,&get_octant,&m, + &zero_to_seven,&neg_ones,&P, + &point_indices,&children,¢ers,&widths] + (const ChildrenType index, const int depth)-> void + { + if(point_indices.at(index).size() > 1 && depth < MAX_DEPTH){ + //give the parent access to the children + children.at(index) = zero_to_seven.array() + m; + //make the children's data in our arrays + + //Add the children to the lists, as default children + CentersType h = widths.at(index)/2; + RowVector3CentersType curr_center = centers.at(index); + + + for(ChildrenType i = 0; i < 8; i++){ + children.emplace_back(neg_ones); + point_indices.emplace_back(std::vector()); + centers.emplace_back(translate_center(curr_center,h,i)); + widths.emplace_back(h); + } + + + //Split up the points into the corresponding children + for(int j = 0; j < point_indices.at(index).size(); j++){ + IndexType curr_point_index = point_indices.at(index).at(j); + IndexType cell_of_curr_point = + get_octant(P.row(curr_point_index),curr_center)+m; + point_indices.at(cell_of_curr_point).emplace_back(curr_point_index); + } + + //Now increase m + m += 8; + + + // Look ma, I'm calling myself. + for(int i = 0; i < 8; i++){ + helper(children.at(index)(i),depth+1); + } + } + }; + + { + std::vector all(P.rows()); + for(IndexType i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/ + +#ifndef IGL_OCTREE +#define IGL_OCTREE +#include "igl_inline.h" +#include +#include + + + + +namespace igl +{ + // Given a set of 3D points P, generate data structures for a pointerless + // octree. Each cell stores its points, children, center location and width. + // Our octree is not dense. We use the following rule: if the current cell + // has any number of points, it will have all 8 children. A leaf cell will + // have -1's as its list of child indices. + // + // We use a binary numbering of children. Treating the parent cell's center + // as the origin, we number the octants in the following manner: + // The first bit is 1 iff the octant's x coordinate is positive + // The second bit is 1 iff the octant's y coordinate is positive + // The third bit is 1 iff the octant's z coordinate is positive + // + // For example, the octant with negative x, positive y, positive z is: + // 110 binary = 6 decimal + // + // Inputs: + // P #P by 3 list of point locations + // + // Outputs: + // point_indices a vector of vectors, where the ith entry is a vector of + // the indices into P that are the ith octree cell's points + // CH #OctreeCells by 8, where the ith row is the indices of + // the ith octree cell's children + // CN #OctreeCells by 3, where the ith row is a 3d row vector + // representing the position of the ith cell's center + // W #OctreeCells, a vector where the ith entry is the width + // of the ith octree cell + // + template + IGL_INLINE void octree(const Eigen::MatrixBase& P, + std::vector > & point_indices, + Eigen::PlainObjectBase& CH, + Eigen::PlainObjectBase& CN, + Eigen::PlainObjectBase& W); +} + +#ifndef IGL_STATIC_LIBRARY +# include "octree.cpp" +#endif + +#endif + diff --git a/src/igl/on_boundary.cpp b/src/igl/on_boundary.cpp new file mode 100644 index 0000000000..3fb70ec593 --- /dev/null +++ b/src/igl/on_boundary.cpp @@ -0,0 +1,141 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "on_boundary.h" + +// IGL includes +#include "sort.h" +#include "face_occurrences.h" + +// STL includes + +template +IGL_INLINE void igl::on_boundary( + const std::vector > & T, + std::vector & I, + std::vector > & C) +{ + using namespace std; + if(T.empty()) + { + I.clear(); + C.clear(); + return; + } + + switch(T[0].size()) + { + case 3: + { + // Get a list of all faces + vector > F(T.size()*3,vector(2)); + // Gather faces, loop over tets + for(int i = 0; i< (int)T.size();i++) + { + assert(T[i].size() == 3); + // get face in correct order + F[i*3+0][0] = T[i][1]; + F[i*3+0][1] = T[i][2]; + F[i*3+1][0] = T[i][2]; + F[i*3+1][1] = T[i][0]; + F[i*3+2][0] = T[i][0]; + F[i*3+2][1] = T[i][1]; + } + // Counts + vector FC; + face_occurrences(F,FC); + C.resize(T.size(),vector(3)); + I.resize(T.size(),false); + for(int i = 0; i< (int)T.size();i++) + { + for(int j = 0;j<3;j++) + { + assert(FC[i*3+j] == 2 || FC[i*3+j] == 1); + C[i][j] = FC[i*3+j]==1; + // if any are on boundary set to true + I[i] = I[i] || C[i][j]; + } + } + return; + } + case 4: + { + // Get a list of all faces + vector > F(T.size()*4,vector(3)); + // Gather faces, loop over tets + for(int i = 0; i< (int)T.size();i++) + { + assert(T[i].size() == 4); + // get face in correct order + F[i*4+0][0] = T[i][1]; + F[i*4+0][1] = T[i][3]; + F[i*4+0][2] = T[i][2]; + // get face in correct order + F[i*4+1][0] = T[i][0]; + F[i*4+1][1] = T[i][2]; + F[i*4+1][2] = T[i][3]; + // get face in correct order + F[i*4+2][0] = T[i][0]; + F[i*4+2][1] = T[i][3]; + F[i*4+2][2] = T[i][1]; + // get face in correct order + F[i*4+3][0] = T[i][0]; + F[i*4+3][1] = T[i][1]; + F[i*4+3][2] = T[i][2]; + } + // Counts + vector FC; + face_occurrences(F,FC); + C.resize(T.size(),vector(4)); + I.resize(T.size(),false); + for(int i = 0; i< (int)T.size();i++) + { + for(int j = 0;j<4;j++) + { + assert(FC[i*4+j] == 2 || FC[i*4+j] == 1); + C[i][j] = FC[i*4+j]==1; + // if any are on boundary set to true + I[i] = I[i] || C[i][j]; + } + } + return; + } + } + + +} + +#include "list_to_matrix.h" +#include "matrix_to_list.h" + +template +IGL_INLINE void igl::on_boundary( + const Eigen::MatrixBase& T, + Eigen::PlainObjectBase& I, + Eigen::PlainObjectBase& C) +{ + assert(T.cols() == 0 || T.cols() == 4 || T.cols() == 3); + using namespace std; + using namespace Eigen; + // Cop out: use vector of vectors version + vector > vT; + matrix_to_list(T,vT); + vector vI; + vector > vC; + on_boundary(vT,vI,vC); + list_to_matrix(vI,I); + list_to_matrix(vC,C); +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::on_boundary, Eigen::Array, Eigen::Array >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::on_boundary, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::on_boundary, Eigen::Array, Eigen::Array >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/on_boundary.h b/src/igl/on_boundary.h new file mode 100644 index 0000000000..c4dab17064 --- /dev/null +++ b/src/igl/on_boundary.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ON_BOUNDARY_H +#define IGL_ON_BOUNDARY_H +#include "igl_inline.h" +#include + +#include + +namespace igl +{ + // ON_BOUNDARY Determine boundary facets of mesh elements stored in T + // + // Templates: + // IntegerT integer-value: i.e. int + // IntegerF integer-value: i.e. int + // Input: + // T triangle|tetrahedron index list, m by 3|4, where m is the number of + // elements + // Output: + // I m long list of bools whether tet is on boundary + // C m by 3|4 list of bools whether opposite facet is on boundary + // + template + IGL_INLINE void on_boundary( + const std::vector > & T, + std::vector & I, + std::vector > & C); + // Templates: + // DerivedT integer-value: i.e. from MatrixXi + // DerivedI bool-value: i.e. from MatrixXi + // DerivedC bool-value: i.e. from MatrixXi + template + IGL_INLINE void on_boundary( + const Eigen::MatrixBase& T, + Eigen::PlainObjectBase& I, + Eigen::PlainObjectBase& C); +} + +#ifndef IGL_STATIC_LIBRARY +# include "on_boundary.cpp" +#endif + +#endif + + diff --git a/src/igl/opengl/MeshGL.cpp b/src/igl/opengl/MeshGL.cpp new file mode 100644 index 0000000000..f499243240 --- /dev/null +++ b/src/igl/opengl/MeshGL.cpp @@ -0,0 +1,313 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "MeshGL.h" +#include "bind_vertex_attrib_array.h" +#include "create_shader_program.h" +#include "destroy_shader_program.h" +#include + +IGL_INLINE void igl::opengl::MeshGL::init_buffers() +{ + // Mesh: Vertex Array Object & Buffer objects + glGenVertexArrays(1, &vao_mesh); + glBindVertexArray(vao_mesh); + glGenBuffers(1, &vbo_V); + glGenBuffers(1, &vbo_V_normals); + glGenBuffers(1, &vbo_V_ambient); + glGenBuffers(1, &vbo_V_diffuse); + glGenBuffers(1, &vbo_V_specular); + glGenBuffers(1, &vbo_V_uv); + glGenBuffers(1, &vbo_F); + glGenTextures(1, &vbo_tex); + + // Line overlay + glGenVertexArrays(1, &vao_overlay_lines); + glBindVertexArray(vao_overlay_lines); + glGenBuffers(1, &vbo_lines_F); + glGenBuffers(1, &vbo_lines_V); + glGenBuffers(1, &vbo_lines_V_colors); + + // Point overlay + glGenVertexArrays(1, &vao_overlay_points); + glBindVertexArray(vao_overlay_points); + glGenBuffers(1, &vbo_points_F); + glGenBuffers(1, &vbo_points_V); + glGenBuffers(1, &vbo_points_V_colors); + + dirty = MeshGL::DIRTY_ALL; +} + +IGL_INLINE void igl::opengl::MeshGL::free_buffers() +{ + if (is_initialized) + { + glDeleteVertexArrays(1, &vao_mesh); + glDeleteVertexArrays(1, &vao_overlay_lines); + glDeleteVertexArrays(1, &vao_overlay_points); + + glDeleteBuffers(1, &vbo_V); + glDeleteBuffers(1, &vbo_V_normals); + glDeleteBuffers(1, &vbo_V_ambient); + glDeleteBuffers(1, &vbo_V_diffuse); + glDeleteBuffers(1, &vbo_V_specular); + glDeleteBuffers(1, &vbo_V_uv); + glDeleteBuffers(1, &vbo_F); + glDeleteBuffers(1, &vbo_lines_F); + glDeleteBuffers(1, &vbo_lines_V); + glDeleteBuffers(1, &vbo_lines_V_colors); + glDeleteBuffers(1, &vbo_points_F); + glDeleteBuffers(1, &vbo_points_V); + glDeleteBuffers(1, &vbo_points_V_colors); + + glDeleteTextures(1, &vbo_tex); + } +} + +IGL_INLINE void igl::opengl::MeshGL::bind_mesh() +{ + glBindVertexArray(vao_mesh); + glUseProgram(shader_mesh); + bind_vertex_attrib_array(shader_mesh,"position", vbo_V, V_vbo, dirty & MeshGL::DIRTY_POSITION); + bind_vertex_attrib_array(shader_mesh,"normal", vbo_V_normals, V_normals_vbo, dirty & MeshGL::DIRTY_NORMAL); + bind_vertex_attrib_array(shader_mesh,"Ka", vbo_V_ambient, V_ambient_vbo, dirty & MeshGL::DIRTY_AMBIENT); + bind_vertex_attrib_array(shader_mesh,"Kd", vbo_V_diffuse, V_diffuse_vbo, dirty & MeshGL::DIRTY_DIFFUSE); + bind_vertex_attrib_array(shader_mesh,"Ks", vbo_V_specular, V_specular_vbo, dirty & MeshGL::DIRTY_SPECULAR); + bind_vertex_attrib_array(shader_mesh,"texcoord", vbo_V_uv, V_uv_vbo, dirty & MeshGL::DIRTY_UV); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_F); + if (dirty & MeshGL::DIRTY_FACE) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*F_vbo.size(), F_vbo.data(), GL_DYNAMIC_DRAW); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, vbo_tex); + if (dirty & MeshGL::DIRTY_TEXTURE) + { + 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_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_u, tex_v, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.data()); + } + glUniform1i(glGetUniformLocation(shader_mesh,"tex"), 0); + dirty &= ~MeshGL::DIRTY_MESH; +} + +IGL_INLINE void igl::opengl::MeshGL::bind_overlay_lines() +{ + bool is_dirty = dirty & MeshGL::DIRTY_OVERLAY_LINES; + + glBindVertexArray(vao_overlay_lines); + glUseProgram(shader_overlay_lines); + bind_vertex_attrib_array(shader_overlay_lines,"position", vbo_lines_V, lines_V_vbo, is_dirty); + bind_vertex_attrib_array(shader_overlay_lines,"color", vbo_lines_V_colors, lines_V_colors_vbo, is_dirty); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_lines_F); + if (is_dirty) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*lines_F_vbo.size(), lines_F_vbo.data(), GL_DYNAMIC_DRAW); + + dirty &= ~MeshGL::DIRTY_OVERLAY_LINES; +} + +IGL_INLINE void igl::opengl::MeshGL::bind_overlay_points() +{ + bool is_dirty = dirty & MeshGL::DIRTY_OVERLAY_POINTS; + + glBindVertexArray(vao_overlay_points); + glUseProgram(shader_overlay_points); + bind_vertex_attrib_array(shader_overlay_points,"position", vbo_points_V, points_V_vbo, is_dirty); + bind_vertex_attrib_array(shader_overlay_points,"color", vbo_points_V_colors, points_V_colors_vbo, is_dirty); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_points_F); + if (is_dirty) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*points_F_vbo.size(), points_F_vbo.data(), GL_DYNAMIC_DRAW); + + dirty &= ~MeshGL::DIRTY_OVERLAY_POINTS; +} + +IGL_INLINE void igl::opengl::MeshGL::draw_mesh(bool solid) +{ + glPolygonMode(GL_FRONT_AND_BACK, solid ? GL_FILL : GL_LINE); + + /* Avoid Z-buffer fighting between filled triangles & wireframe lines */ + if (solid) + { + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0, 1.0); + } + glDrawElements(GL_TRIANGLES, 3*F_vbo.rows(), GL_UNSIGNED_INT, 0); + + glDisable(GL_POLYGON_OFFSET_FILL); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +} + +IGL_INLINE void igl::opengl::MeshGL::draw_overlay_lines() +{ + glDrawElements(GL_LINES, lines_F_vbo.rows(), GL_UNSIGNED_INT, 0); +} + +IGL_INLINE void igl::opengl::MeshGL::draw_overlay_points() +{ + glDrawElements(GL_POINTS, points_F_vbo.rows(), GL_UNSIGNED_INT, 0); +} + +IGL_INLINE void igl::opengl::MeshGL::init() +{ + if(is_initialized) + { + return; + } + is_initialized = true; + std::string mesh_vertex_shader_string = +R"(#version 150 + uniform mat4 view; + uniform mat4 proj; + uniform mat4 normal_matrix; + in vec3 position; + in vec3 normal; + out vec3 position_eye; + out vec3 normal_eye; + in vec4 Ka; + in vec4 Kd; + in vec4 Ks; + in vec2 texcoord; + out vec2 texcoordi; + out vec4 Kai; + out vec4 Kdi; + out vec4 Ksi; + + void main() + { + position_eye = vec3 (view * vec4 (position, 1.0)); + normal_eye = vec3 (normal_matrix * vec4 (normal, 0.0)); + normal_eye = normalize(normal_eye); + gl_Position = proj * vec4 (position_eye, 1.0); //proj * view * vec4(position, 1.0);" + Kai = Ka; + Kdi = Kd; + Ksi = Ks; + texcoordi = texcoord; + } +)"; + + std::string mesh_fragment_shader_string = +R"(#version 150 + uniform mat4 view; + uniform mat4 proj; + uniform vec4 fixed_color; + in vec3 position_eye; + in vec3 normal_eye; + uniform vec3 light_position_eye; + vec3 Ls = vec3 (1, 1, 1); + vec3 Ld = vec3 (1, 1, 1); + vec3 La = vec3 (1, 1, 1); + in vec4 Ksi; + in vec4 Kdi; + in vec4 Kai; + in vec2 texcoordi; + uniform sampler2D tex; + uniform float specular_exponent; + uniform float lighting_factor; + uniform float texture_factor; + out vec4 outColor; + void main() + { + vec3 Ia = La * vec3(Kai); // ambient intensity + + vec3 vector_to_light_eye = light_position_eye - position_eye; + vec3 direction_to_light_eye = normalize (vector_to_light_eye); + float dot_prod = dot (direction_to_light_eye, normalize(normal_eye)); + float clamped_dot_prod = max (dot_prod, 0.0); + vec3 Id = Ld * vec3(Kdi) * clamped_dot_prod; // Diffuse intensity + + vec3 reflection_eye = reflect (-direction_to_light_eye, normalize(normal_eye)); + vec3 surface_to_viewer_eye = normalize (-position_eye); + float dot_prod_specular = dot (reflection_eye, surface_to_viewer_eye); + dot_prod_specular = float(abs(dot_prod)==dot_prod) * max (dot_prod_specular, 0.0); + float specular_factor = pow (dot_prod_specular, specular_exponent); + vec3 Is = Ls * vec3(Ksi) * specular_factor; // specular intensity + vec4 color = vec4(lighting_factor * (Is + Id) + Ia + (1.0-lighting_factor) * vec3(Kdi),(Kai.a+Ksi.a+Kdi.a)/3); + outColor = mix(vec4(1,1,1,1), texture(tex, texcoordi), texture_factor) * color; + if (fixed_color != vec4(0.0)) outColor = fixed_color; + } +)"; + + std::string overlay_vertex_shader_string = +R"(#version 150 + uniform mat4 view; + uniform mat4 proj; + in vec3 position; + in vec3 color; + out vec3 color_frag; + + void main() + { + gl_Position = proj * view * vec4 (position, 1.0); + color_frag = color; + } +)"; + + std::string overlay_fragment_shader_string = +R"(#version 150 + in vec3 color_frag; + out vec4 outColor; + void main() + { + outColor = vec4(color_frag, 1.0); + } +)"; + + std::string overlay_point_fragment_shader_string = +R"(#version 150 + in vec3 color_frag; + out vec4 outColor; + void main() + { + if (length(gl_PointCoord - vec2(0.5)) > 0.5) + discard; + outColor = vec4(color_frag, 1.0); + } +)"; + + init_buffers(); + create_shader_program( + mesh_vertex_shader_string, + mesh_fragment_shader_string, + {}, + shader_mesh); + create_shader_program( + overlay_vertex_shader_string, + overlay_fragment_shader_string, + {}, + shader_overlay_lines); + create_shader_program( + overlay_vertex_shader_string, + overlay_point_fragment_shader_string, + {}, + shader_overlay_points); +} + +IGL_INLINE void igl::opengl::MeshGL::free() +{ + const auto free = [](GLuint & id) + { + if(id) + { + destroy_shader_program(id); + id = 0; + } + }; + + if (is_initialized) + { + free(shader_mesh); + free(shader_overlay_lines); + free(shader_overlay_points); + free_buffers(); + } +} diff --git a/src/igl/opengl/MeshGL.h b/src/igl/opengl/MeshGL.h new file mode 100644 index 0000000000..74c3589030 --- /dev/null +++ b/src/igl/opengl/MeshGL.h @@ -0,0 +1,133 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_MESHGL_H +#define IGL_OPENGL_MESHGL_H + +// Coverts mesh data inside a igl::ViewerData class in an OpenGL +// compatible format The class includes a shader and the opengl calls to plot +// the data + +#include +#include + +namespace igl +{ +namespace opengl +{ + +class MeshGL +{ +public: + typedef unsigned int GLuint; + + enum DirtyFlags + { + DIRTY_NONE = 0x0000, + DIRTY_POSITION = 0x0001, + DIRTY_UV = 0x0002, + DIRTY_NORMAL = 0x0004, + DIRTY_AMBIENT = 0x0008, + DIRTY_DIFFUSE = 0x0010, + DIRTY_SPECULAR = 0x0020, + DIRTY_TEXTURE = 0x0040, + DIRTY_FACE = 0x0080, + DIRTY_MESH = 0x00FF, + DIRTY_OVERLAY_LINES = 0x0100, + DIRTY_OVERLAY_POINTS = 0x0200, + DIRTY_ALL = 0x03FF + }; + + bool is_initialized = false; + GLuint vao_mesh; + GLuint vao_overlay_lines; + GLuint vao_overlay_points; + GLuint shader_mesh; + GLuint shader_overlay_lines; + GLuint shader_overlay_points; + + GLuint vbo_V; // Vertices of the current mesh (#V x 3) + GLuint vbo_V_uv; // UV coordinates for the current mesh (#V x 2) + GLuint vbo_V_normals; // Vertices of the current mesh (#V x 3) + GLuint vbo_V_ambient; // Ambient material (#V x 3) + GLuint vbo_V_diffuse; // Diffuse material (#V x 3) + GLuint vbo_V_specular; // Specular material (#V x 3) + + GLuint vbo_F; // Faces of the mesh (#F x 3) + GLuint vbo_tex; // Texture + + GLuint vbo_lines_F; // Indices of the line overlay + GLuint vbo_lines_V; // Vertices of the line overlay + GLuint vbo_lines_V_colors; // Color values of the line overlay + GLuint vbo_points_F; // Indices of the point overlay + GLuint vbo_points_V; // Vertices of the point overlay + GLuint vbo_points_V_colors; // Color values of the point overlay + + // Temporary copy of the content of each VBO + typedef Eigen::Matrix RowMatrixXf; + RowMatrixXf V_vbo; + RowMatrixXf V_normals_vbo; + RowMatrixXf V_ambient_vbo; + RowMatrixXf V_diffuse_vbo; + RowMatrixXf V_specular_vbo; + RowMatrixXf V_uv_vbo; + RowMatrixXf lines_V_vbo; + RowMatrixXf lines_V_colors_vbo; + RowMatrixXf points_V_vbo; + RowMatrixXf points_V_colors_vbo; + + int tex_u; + int tex_v; + Eigen::Matrix tex; + + Eigen::Matrix F_vbo; + Eigen::Matrix lines_F_vbo; + Eigen::Matrix points_F_vbo; + + // Marks dirty buffers that need to be uploaded to OpenGL + uint32_t dirty; + + // Initialize shaders and buffers + IGL_INLINE void init(); + + // Release all resources + IGL_INLINE void free(); + + // Create a new set of OpenGL buffer objects + IGL_INLINE void init_buffers(); + + // Bind the underlying OpenGL buffer objects for subsequent mesh draw calls + IGL_INLINE void bind_mesh(); + + /// Draw the currently buffered mesh (either solid or wireframe) + IGL_INLINE void draw_mesh(bool solid); + + // Bind the underlying OpenGL buffer objects for subsequent line overlay draw calls + IGL_INLINE void bind_overlay_lines(); + + /// Draw the currently buffered line overlay + IGL_INLINE void draw_overlay_lines(); + + // Bind the underlying OpenGL buffer objects for subsequent point overlay draw calls + IGL_INLINE void bind_overlay_points(); + + /// Draw the currently buffered point overlay + IGL_INLINE void draw_overlay_points(); + + // Release the OpenGL buffer objects + IGL_INLINE void free_buffers(); + +}; + +} +} + +#ifndef IGL_STATIC_LIBRARY +# include "MeshGL.cpp" +#endif + +#endif diff --git a/src/igl/opengl/ViewerCore.cpp b/src/igl/opengl/ViewerCore.cpp new file mode 100644 index 0000000000..fc5f4664b2 --- /dev/null +++ b/src/igl/opengl/ViewerCore.cpp @@ -0,0 +1,391 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "ViewerCore.h" +#include "gl.h" +#include "../quat_to_mat.h" +#include "../snap_to_fixed_up.h" +#include "../look_at.h" +#include "../frustum.h" +#include "../ortho.h" +#include "../massmatrix.h" +#include "../barycenter.h" +#include "../PI.h" +#include +#include + +IGL_INLINE void igl::opengl::ViewerCore::align_camera_center( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F) +{ + if(V.rows() == 0) + return; + + get_scale_and_shift_to_fit_mesh(V,F,camera_base_zoom,camera_base_translation); + // Rather than crash on empty mesh... + if(V.size() > 0) + { + object_scale = (V.colwise().maxCoeff() - V.colwise().minCoeff()).norm(); + } +} + +IGL_INLINE void igl::opengl::ViewerCore::get_scale_and_shift_to_fit_mesh( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + float& zoom, + Eigen::Vector3f& shift) +{ + if (V.rows() == 0) + return; + + Eigen::MatrixXd BC; + if (F.rows() <= 1) + { + BC = V; + } else + { + igl::barycenter(V,F,BC); + } + return get_scale_and_shift_to_fit_mesh(BC,zoom,shift); +} + +IGL_INLINE void igl::opengl::ViewerCore::align_camera_center( + const Eigen::MatrixXd& V) +{ + if(V.rows() == 0) + return; + + get_scale_and_shift_to_fit_mesh(V,camera_base_zoom,camera_base_translation); + // Rather than crash on empty mesh... + if(V.size() > 0) + { + object_scale = (V.colwise().maxCoeff() - V.colwise().minCoeff()).norm(); + } +} + +IGL_INLINE void igl::opengl::ViewerCore::get_scale_and_shift_to_fit_mesh( + const Eigen::MatrixXd& V, + float& zoom, + Eigen::Vector3f& shift) +{ + if (V.rows() == 0) + return; + + auto min_point = V.colwise().minCoeff(); + auto max_point = V.colwise().maxCoeff(); + auto centroid = (0.5*(min_point + max_point)).eval(); + shift.setConstant(0); + shift.head(centroid.size()) = -centroid.cast(); + zoom = 2.0 / (max_point-min_point).array().abs().maxCoeff(); +} + + +IGL_INLINE void igl::opengl::ViewerCore::clear_framebuffers() +{ + glClearColor(background_color[0], + background_color[1], + background_color[2], + background_color[3]); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +IGL_INLINE void igl::opengl::ViewerCore::draw( + ViewerData& data, + bool update_matrices) +{ + using namespace std; + using namespace Eigen; + + if (depth_test) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* Bind and potentially refresh mesh/line/point data */ + if (data.dirty) + { + data.updateGL(data, data.invert_normals,data.meshgl); + data.dirty = MeshGL::DIRTY_NONE; + } + data.meshgl.bind_mesh(); + + // Initialize uniform + glViewport(viewport(0), viewport(1), viewport(2), viewport(3)); + + if(update_matrices) + { + view = Eigen::Matrix4f::Identity(); + proj = Eigen::Matrix4f::Identity(); + norm = Eigen::Matrix4f::Identity(); + + float width = viewport(2); + float height = viewport(3); + + // Set view + look_at( camera_eye, camera_center, camera_up, view); + view = view + * (trackball_angle * Eigen::Scaling(camera_zoom * camera_base_zoom) + * Eigen::Translation3f(camera_translation + camera_base_translation)).matrix(); + + norm = view.inverse().transpose(); + + // Set projection + if (orthographic) + { + float length = (camera_eye - camera_center).norm(); + float h = tan(camera_view_angle/360.0 * igl::PI) * (length); + ortho(-h*width/height, h*width/height, -h, h, camera_dnear, camera_dfar,proj); + } + else + { + float fH = tan(camera_view_angle / 360.0 * igl::PI) * camera_dnear; + float fW = fH * (double)width/(double)height; + frustum(-fW, fW, -fH, fH, camera_dnear, camera_dfar,proj); + } + } + + // Send transformations to the GPU + GLint viewi = glGetUniformLocation(data.meshgl.shader_mesh,"view"); + GLint proji = glGetUniformLocation(data.meshgl.shader_mesh,"proj"); + GLint normi = glGetUniformLocation(data.meshgl.shader_mesh,"normal_matrix"); + glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data()); + glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data()); + glUniformMatrix4fv(normi, 1, GL_FALSE, norm.data()); + + // Light parameters + GLint specular_exponenti = glGetUniformLocation(data.meshgl.shader_mesh,"specular_exponent"); + GLint light_position_eyei = glGetUniformLocation(data.meshgl.shader_mesh,"light_position_eye"); + GLint lighting_factori = glGetUniformLocation(data.meshgl.shader_mesh,"lighting_factor"); + GLint fixed_colori = glGetUniformLocation(data.meshgl.shader_mesh,"fixed_color"); + GLint texture_factori = glGetUniformLocation(data.meshgl.shader_mesh,"texture_factor"); + + glUniform1f(specular_exponenti, data.shininess); + glUniform3fv(light_position_eyei, 1, light_position.data()); + glUniform1f(lighting_factori, lighting_factor); // enables lighting + glUniform4f(fixed_colori, 0.0, 0.0, 0.0, 0.0); + + if (data.V.rows()>0) + { + // Render fill + if (data.show_faces) + { + // Texture + glUniform1f(texture_factori, data.show_texture ? 1.0f : 0.0f); + data.meshgl.draw_mesh(true); + glUniform1f(texture_factori, 0.0f); + } + + // Render wireframe + if (data.show_lines) + { + glLineWidth(data.line_width); + glUniform4f(fixed_colori, + data.line_color[0], + data.line_color[1], + data.line_color[2], 1.0f); + data.meshgl.draw_mesh(false); + glUniform4f(fixed_colori, 0.0f, 0.0f, 0.0f, 0.0f); + } + } + + if (data.show_overlay) + { + if (data.show_overlay_depth) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + + if (data.lines.rows() > 0) + { + data.meshgl.bind_overlay_lines(); + viewi = glGetUniformLocation(data.meshgl.shader_overlay_lines,"view"); + proji = glGetUniformLocation(data.meshgl.shader_overlay_lines,"proj"); + + glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data()); + glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data()); + // This must be enabled, otherwise glLineWidth has no effect + glEnable(GL_LINE_SMOOTH); + glLineWidth(data.line_width); + + data.meshgl.draw_overlay_lines(); + } + + if (data.points.rows() > 0) + { + data.meshgl.bind_overlay_points(); + viewi = glGetUniformLocation(data.meshgl.shader_overlay_points,"view"); + proji = glGetUniformLocation(data.meshgl.shader_overlay_points,"proj"); + + glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data()); + glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data()); + glPointSize(data.point_size); + + data.meshgl.draw_overlay_points(); + } + + glEnable(GL_DEPTH_TEST); + } + +} + +IGL_INLINE void igl::opengl::ViewerCore::draw_buffer(ViewerData& data, + bool update_matrices, + Eigen::Matrix& R, + Eigen::Matrix& G, + Eigen::Matrix& B, + Eigen::Matrix& A) +{ + assert(R.rows() == G.rows() && G.rows() == B.rows() && B.rows() == A.rows()); + assert(R.cols() == G.cols() && G.cols() == B.cols() && B.cols() == A.cols()); + + unsigned width = R.rows(); + unsigned height = R.cols(); + + // https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing + unsigned int framebuffer; + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + // create a multisampled color attachment texture + unsigned int textureColorBufferMultiSampled; + glGenTextures(1, &textureColorBufferMultiSampled); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled); + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA, width, height, GL_TRUE); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0); + // create a (also multisampled) renderbuffer object for depth and stencil attachments + unsigned int rbo; + glGenRenderbuffers(1, &rbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); + assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // configure second post-processing framebuffer + unsigned int intermediateFBO; + glGenFramebuffers(1, &intermediateFBO); + glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO); + // create a color attachment texture + unsigned int screenTexture; + glGenTextures(1, &screenTexture); + glBindTexture(GL_TEXTURE_2D, screenTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0); // we only need a color buffer + assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + + // Clear the buffer + glClearColor(background_color(0), background_color(1), background_color(2), 0.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // Save old viewport + Eigen::Vector4f viewport_ori = viewport; + viewport << 0,0,width,height; + // Draw + draw(data,update_matrices); + // Restore viewport + viewport = viewport_ori; + + glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediateFBO); + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO); + // Copy back in the given Eigen matrices + GLubyte* pixels = (GLubyte*)calloc(width*height*4,sizeof(GLubyte)); + glReadPixels(0, 0,width, height,GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Clean up + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteTextures(1, &screenTexture); + glDeleteTextures(1, &textureColorBufferMultiSampled); + glDeleteFramebuffers(1, &framebuffer); + glDeleteFramebuffers(1, &intermediateFBO); + glDeleteRenderbuffers(1, &rbo); + + int count = 0; + for (unsigned j=0; j +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_VIEWERCORE_H +#define IGL_OPENGL_VIEWERCORE_H + +#include +#include + +#include +#include +#include + +namespace igl +{ +namespace opengl +{ + +// Basic class of the 3D mesh viewer +// TODO: write documentation + +class ViewerCore +{ +public: + IGL_INLINE ViewerCore(); + + // Initialization + IGL_INLINE void init(); + + // Shutdown + IGL_INLINE void shut(); + + // Serialization code + IGL_INLINE void InitSerialization(); + + + // ------------------- Camera control functions + + // Adjust the view to see the entire model + IGL_INLINE void align_camera_center( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F); + + // Determines how much to zoom and shift such that the mesh fills the unit + // box (centered at the origin) + IGL_INLINE void get_scale_and_shift_to_fit_mesh( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + float & zoom, + Eigen::Vector3f& shift); + + // Adjust the view to see the entire model + IGL_INLINE void align_camera_center( + const Eigen::MatrixXd& V); + + // Determines how much to zoom and shift such that the mesh fills the unit + // box (centered at the origin) + IGL_INLINE void get_scale_and_shift_to_fit_mesh( + const Eigen::MatrixXd& V, + float & zoom, + Eigen::Vector3f& shift); + + // ------------------- Drawing functions + + // Clear the frame buffers + IGL_INLINE void clear_framebuffers(); + + // Draw everything + // + // data cannot be const because it is being set to "clean" + IGL_INLINE void draw(ViewerData& data, bool update_matrices = true); + IGL_INLINE void draw_buffer( + ViewerData& data, + bool update_matrices, + Eigen::Matrix& R, + Eigen::Matrix& G, + Eigen::Matrix& B, + Eigen::Matrix& A); + + // Trackball angle (quaternion) + enum RotationType + { + ROTATION_TYPE_TRACKBALL = 0, + ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP = 1, + ROTATION_TYPE_NO_ROTATION = 2, + NUM_ROTATION_TYPES = 3 + }; + IGL_INLINE void set_rotation_type(const RotationType & value); + + // ------------------- Properties + + // Colors + Eigen::Vector4f background_color; + + // Lighting + Eigen::Vector3f light_position; + float lighting_factor; + + RotationType rotation_type; + Eigen::Quaternionf trackball_angle; + + // Camera parameters + float camera_base_zoom; + float camera_zoom; + bool orthographic; + Eigen::Vector3f camera_base_translation; + Eigen::Vector3f camera_translation; + Eigen::Vector3f camera_eye; + Eigen::Vector3f camera_up; + Eigen::Vector3f camera_center; + float camera_view_angle; + float camera_dnear; + float camera_dfar; + + bool depth_test; + + // Animation + bool is_animating; + double animation_max_fps; + + // Caches the two-norm between the min/max point of the bounding box + float object_scale; + + // Viewport size + Eigen::Vector4f viewport; + + // Save the OpenGL transformation matrices used for the previous rendering pass + Eigen::Matrix4f view; + Eigen::Matrix4f proj; + Eigen::Matrix4f norm; + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW +}; + +} +} + +#include +namespace igl { + namespace serialization { + + inline void serialization(bool s, igl::opengl::ViewerCore& obj, std::vector& buffer) + { + + SERIALIZE_MEMBER(background_color); + + SERIALIZE_MEMBER(light_position); + SERIALIZE_MEMBER(lighting_factor); + + SERIALIZE_MEMBER(trackball_angle); + SERIALIZE_MEMBER(rotation_type); + + SERIALIZE_MEMBER(camera_base_zoom); + SERIALIZE_MEMBER(camera_zoom); + SERIALIZE_MEMBER(orthographic); + SERIALIZE_MEMBER(camera_base_translation); + SERIALIZE_MEMBER(camera_translation); + SERIALIZE_MEMBER(camera_view_angle); + SERIALIZE_MEMBER(camera_dnear); + SERIALIZE_MEMBER(camera_dfar); + SERIALIZE_MEMBER(camera_eye); + SERIALIZE_MEMBER(camera_center); + SERIALIZE_MEMBER(camera_up); + + SERIALIZE_MEMBER(depth_test); + SERIALIZE_MEMBER(is_animating); + SERIALIZE_MEMBER(animation_max_fps); + + SERIALIZE_MEMBER(object_scale); + + SERIALIZE_MEMBER(viewport); + SERIALIZE_MEMBER(view); + SERIALIZE_MEMBER(proj); + SERIALIZE_MEMBER(norm); + } + + template<> + inline void serialize(const igl::opengl::ViewerCore& obj, std::vector& buffer) + { + serialization(true, const_cast(obj), buffer); + } + + template<> + inline void deserialize(igl::opengl::ViewerCore& obj, const std::vector& buffer) + { + serialization(false, obj, const_cast&>(buffer)); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "ViewerCore.cpp" +#endif + +#endif diff --git a/src/igl/opengl/ViewerData.cpp b/src/igl/opengl/ViewerData.cpp new file mode 100644 index 0000000000..0a7f2c44b5 --- /dev/null +++ b/src/igl/opengl/ViewerData.cpp @@ -0,0 +1,691 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "ViewerData.h" + +#include "../per_face_normals.h" +#include "../material_colors.h" +#include "../parula.h" +#include "../per_vertex_normals.h" + +#include + + +IGL_INLINE igl::opengl::ViewerData::ViewerData() +: dirty(MeshGL::DIRTY_ALL), + show_faces(true), + show_lines(true), + invert_normals(false), + show_overlay(true), + show_overlay_depth(true), + show_vertid(false), + show_faceid(false), + show_texture(false), + point_size(30), + line_width(0.5f), + line_color(0,0,0,1), + shininess(35.0f), + id(-1) +{ + clear(); +}; + +IGL_INLINE void igl::opengl::ViewerData::set_face_based(bool newvalue) +{ + if (face_based != newvalue) + { + face_based = newvalue; + dirty = MeshGL::DIRTY_ALL; + } +} + +// Helpers that draws the most common meshes +IGL_INLINE void igl::opengl::ViewerData::set_mesh( + const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F) +{ + using namespace std; + + Eigen::MatrixXd V_temp; + + // If V only has two columns, pad with a column of zeros + if (_V.cols() == 2) + { + V_temp = Eigen::MatrixXd::Zero(_V.rows(),3); + V_temp.block(0,0,_V.rows(),2) = _V; + } + else + V_temp = _V; + + if (V.rows() == 0 && F.rows() == 0) + { + V = V_temp; + F = _F; + + compute_normals(); + uniform_colors( + Eigen::Vector3d(GOLD_AMBIENT[0], GOLD_AMBIENT[1], GOLD_AMBIENT[2]), + Eigen::Vector3d(GOLD_DIFFUSE[0], GOLD_DIFFUSE[1], GOLD_DIFFUSE[2]), + Eigen::Vector3d(GOLD_SPECULAR[0], GOLD_SPECULAR[1], GOLD_SPECULAR[2])); + + grid_texture(); + } + else + { + if (_V.rows() == V.rows() && _F.rows() == F.rows()) + { + V = V_temp; + F = _F; + } + else + cerr << "ERROR (set_mesh): The new mesh has a different number of vertices/faces. Please clear the mesh before plotting."<0 && C.cols() == 1) + { + Eigen::MatrixXd C3; + igl::parula(C,true,C3); + return set_colors(C3); + } + // Ambient color should be darker color + const auto ambient = [](const MatrixXd & C)->MatrixXd + { + MatrixXd T = 0.1*C; + T.col(3) = C.col(3); + return T; + }; + // Specular color should be a less saturated and darker color: dampened + // highlights + const auto specular = [](const MatrixXd & C)->MatrixXd + { + const double grey = 0.3; + MatrixXd T = grey+0.1*(C.array()-grey); + T.col(3) = C.col(3); + return T; + }; + if (C.rows() == 1) + { + for (unsigned i=0;i& R, + const Eigen::Matrix& G, + const Eigen::Matrix& B) +{ + texture_R = R; + texture_G = G; + texture_B = B; + texture_A = Eigen::Matrix::Constant(R.rows(),R.cols(),255); + dirty |= MeshGL::DIRTY_TEXTURE; +} + +IGL_INLINE void igl::opengl::ViewerData::set_texture( + const Eigen::Matrix& R, + const Eigen::Matrix& G, + const Eigen::Matrix& B, + const Eigen::Matrix& A) +{ + texture_R = R; + texture_G = G; + texture_B = B; + texture_A = A; + dirty |= MeshGL::DIRTY_TEXTURE; +} + +IGL_INLINE void igl::opengl::ViewerData::set_points( + const Eigen::MatrixXd& P, + const Eigen::MatrixXd& C) +{ + // clear existing points + points.resize(0,0); + add_points(P,C); +} + +IGL_INLINE void igl::opengl::ViewerData::add_points(const Eigen::MatrixXd& P, const Eigen::MatrixXd& C) +{ + Eigen::MatrixXd P_temp; + + // If P only has two columns, pad with a column of zeros + if (P.cols() == 2) + { + P_temp = Eigen::MatrixXd::Zero(P.rows(),3); + P_temp.block(0,0,P.rows(),2) = P; + } + else + P_temp = P; + + int lastid = points.rows(); + points.conservativeResize(points.rows() + P_temp.rows(),6); + for (unsigned i=0; i=size2 && j>=size2)) + texture_R(i,j) = 255; + } + } + + texture_G = texture_R; + texture_B = texture_R; + texture_A = Eigen::Matrix::Constant(texture_R.rows(),texture_R.cols(),255); + dirty |= MeshGL::DIRTY_TEXTURE; +} + +IGL_INLINE void igl::opengl::ViewerData::updateGL( + const igl::opengl::ViewerData& data, + const bool invert_normals, + igl::opengl::MeshGL& meshgl + ) +{ + if (!meshgl.is_initialized) + { + meshgl.init(); + } + + bool per_corner_uv = (data.F_uv.rows() == data.F.rows()); + bool per_corner_normals = (data.F_normals.rows() == 3 * data.F.rows()); + + meshgl.dirty |= data.dirty; + + // Input: + // X #F by dim quantity + // Output: + // X_vbo #F*3 by dim scattering per corner + const auto per_face = [&data]( + const Eigen::MatrixXd & X, + MeshGL::RowMatrixXf & X_vbo) + { + assert(X.cols() == 4); + X_vbo.resize(data.F.rows()*3,4); + for (unsigned i=0; i(); + }; + + // Input: + // X #V by dim quantity + // Output: + // X_vbo #F*3 by dim scattering per corner + const auto per_corner = [&data]( + const Eigen::MatrixXd & X, + MeshGL::RowMatrixXf & X_vbo) + { + X_vbo.resize(data.F.rows()*3,X.cols()); + for (unsigned i=0; i(); + }; + + if (!data.face_based) + { + if (!(per_corner_uv || per_corner_normals)) + { + // Vertex positions + if (meshgl.dirty & MeshGL::DIRTY_POSITION) + meshgl.V_vbo = data.V.cast(); + + // Vertex normals + if (meshgl.dirty & MeshGL::DIRTY_NORMAL) + { + meshgl.V_normals_vbo = data.V_normals.cast(); + if (invert_normals) + meshgl.V_normals_vbo = -meshgl.V_normals_vbo; + } + + // Per-vertex material settings + if (meshgl.dirty & MeshGL::DIRTY_AMBIENT) + meshgl.V_ambient_vbo = data.V_material_ambient.cast(); + if (meshgl.dirty & MeshGL::DIRTY_DIFFUSE) + meshgl.V_diffuse_vbo = data.V_material_diffuse.cast(); + if (meshgl.dirty & MeshGL::DIRTY_SPECULAR) + meshgl.V_specular_vbo = data.V_material_specular.cast(); + + // Face indices + if (meshgl.dirty & MeshGL::DIRTY_FACE) + meshgl.F_vbo = data.F.cast(); + + // Texture coordinates + if (meshgl.dirty & MeshGL::DIRTY_UV) + { + meshgl.V_uv_vbo = data.V_uv.cast(); + } + } + else + { + + // Per vertex properties with per corner UVs + if (meshgl.dirty & MeshGL::DIRTY_POSITION) + { + per_corner(data.V,meshgl.V_vbo); + } + + if (meshgl.dirty & MeshGL::DIRTY_AMBIENT) + { + meshgl.V_ambient_vbo.resize(data.F.rows()*3,4); + for (unsigned i=0; i(); + } + if (meshgl.dirty & MeshGL::DIRTY_DIFFUSE) + { + meshgl.V_diffuse_vbo.resize(data.F.rows()*3,4); + for (unsigned i=0; i(); + } + if (meshgl.dirty & MeshGL::DIRTY_SPECULAR) + { + meshgl.V_specular_vbo.resize(data.F.rows()*3,4); + for (unsigned i=0; i(); + } + + if (meshgl.dirty & MeshGL::DIRTY_NORMAL) + { + meshgl.V_normals_vbo.resize(data.F.rows()*3,3); + for (unsigned i=0; i() : + data.V_normals.row(data.F(i,j)).cast(); + + + if (invert_normals) + meshgl.V_normals_vbo = -meshgl.V_normals_vbo; + } + + if (meshgl.dirty & MeshGL::DIRTY_FACE) + { + meshgl.F_vbo.resize(data.F.rows(),3); + for (unsigned i=0; i(); + } + } + } + else + { + if (meshgl.dirty & MeshGL::DIRTY_POSITION) + { + per_corner(data.V,meshgl.V_vbo); + } + if (meshgl.dirty & MeshGL::DIRTY_AMBIENT) + { + per_face(data.F_material_ambient,meshgl.V_ambient_vbo); + } + if (meshgl.dirty & MeshGL::DIRTY_DIFFUSE) + { + per_face(data.F_material_diffuse,meshgl.V_diffuse_vbo); + } + if (meshgl.dirty & MeshGL::DIRTY_SPECULAR) + { + per_face(data.F_material_specular,meshgl.V_specular_vbo); + } + + if (meshgl.dirty & MeshGL::DIRTY_NORMAL) + { + meshgl.V_normals_vbo.resize(data.F.rows()*3,3); + for (unsigned i=0; i() : + data.F_normals.row(i).cast(); + + if (invert_normals) + meshgl.V_normals_vbo = -meshgl.V_normals_vbo; + } + + if (meshgl.dirty & MeshGL::DIRTY_FACE) + { + meshgl.F_vbo.resize(data.F.rows(),3); + for (unsigned i=0; i(); + } + } + + if (meshgl.dirty & MeshGL::DIRTY_TEXTURE) + { + meshgl.tex_u = data.texture_R.rows(); + meshgl.tex_v = data.texture_R.cols(); + meshgl.tex.resize(data.texture_R.size()*4); + for (unsigned i=0;i(i, 0).cast(); + meshgl.lines_V_vbo.row(2*i+1) = data.lines.block<1, 3>(i, 3).cast(); + meshgl.lines_V_colors_vbo.row(2*i+0) = data.lines.block<1, 3>(i, 6).cast(); + meshgl.lines_V_colors_vbo.row(2*i+1) = data.lines.block<1, 3>(i, 6).cast(); + meshgl.lines_F_vbo(2*i+0) = 2*i+0; + meshgl.lines_F_vbo(2*i+1) = 2*i+1; + } + } + + if (meshgl.dirty & MeshGL::DIRTY_OVERLAY_POINTS) + { + meshgl.points_V_vbo.resize(data.points.rows(),3); + meshgl.points_V_colors_vbo.resize(data.points.rows(),3); + meshgl.points_F_vbo.resize(data.points.rows(),1); + for (unsigned i=0; i(i, 0).cast(); + meshgl.points_V_colors_vbo.row(i) = data.points.block<1, 3>(i, 3).cast(); + meshgl.points_F_vbo(i) = i; + } + } +} diff --git a/src/igl/opengl/ViewerData.h b/src/igl/opengl/ViewerData.h new file mode 100644 index 0000000000..b3ec9178c4 --- /dev/null +++ b/src/igl/opengl/ViewerData.h @@ -0,0 +1,276 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_VIEWERDATA_H +#define IGL_VIEWERDATA_H + +#include "../igl_inline.h" +#include "MeshGL.h" +#include +#include +#include +#include +#include + +// Alec: This is a mesh class containing a variety of data types (normals, +// overlays, material colors, etc.) +// +namespace igl +{ + +// TODO: write documentation +namespace opengl +{ + +class ViewerData +{ +public: + ViewerData(); + + // Empty all fields + IGL_INLINE void clear(); + + // Change the visualization mode, invalidating the cache if necessary + IGL_INLINE void set_face_based(bool newvalue); + + // Helpers that can draw the most common meshes + IGL_INLINE void set_mesh(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F); + IGL_INLINE void set_vertices(const Eigen::MatrixXd& V); + IGL_INLINE void set_normals(const Eigen::MatrixXd& N); + + // Set the color of the mesh + // + // Inputs: + // C #V|#F|1 by 3 list of colors + IGL_INLINE void set_colors(const Eigen::MatrixXd &C); + // Set per-vertex UV coordinates + // + // Inputs: + // UV #V by 2 list of UV coordinates (indexed by F) + IGL_INLINE void set_uv(const Eigen::MatrixXd& UV); + // Set per-corner UV coordinates + // + // Inputs: + // UV_V #UV by 2 list of UV coordinates + // UV_F #F by 3 list of UV indices into UV_V + IGL_INLINE void set_uv(const Eigen::MatrixXd& UV_V, const Eigen::MatrixXi& UV_F); + // Set the texture associated with the mesh. + // + // Inputs: + // R width by height image matrix of red channel + // G width by height image matrix of green channel + // B width by height image matrix of blue channel + // + IGL_INLINE void set_texture( + const Eigen::Matrix& R, + const Eigen::Matrix& G, + const Eigen::Matrix& B); + + // Set the texture associated with the mesh. + // + // Inputs: + // R width by height image matrix of red channel + // G width by height image matrix of green channel + // B width by height image matrix of blue channel + // A width by height image matrix of alpha channel + // + IGL_INLINE void set_texture( + const Eigen::Matrix& R, + const Eigen::Matrix& G, + const Eigen::Matrix& B, + const Eigen::Matrix& A); + + // Sets points given a list of point vertices. In constrast to `set_points` + // this will (purposefully) clober existing points. + // + // Inputs: + // P #P by 3 list of vertex positions + // C #P|1 by 3 color(s) + IGL_INLINE void set_points( + const Eigen::MatrixXd& P, + const Eigen::MatrixXd& C); + IGL_INLINE void add_points(const Eigen::MatrixXd& P, const Eigen::MatrixXd& C); + // Sets edges given a list of edge vertices and edge indices. In constrast + // to `add_edges` this will (purposefully) clober existing edges. + // + // Inputs: + // P #P by 3 list of vertex positions + // E #E by 2 list of edge indices into P + // C #E|1 by 3 color(s) + IGL_INLINE void set_edges (const Eigen::MatrixXd& P, const Eigen::MatrixXi& E, const Eigen::MatrixXd& C); + // Alec: This is very confusing. Why does add_edges have a different API from + // set_edges? + IGL_INLINE void add_edges (const Eigen::MatrixXd& P1, const Eigen::MatrixXd& P2, const Eigen::MatrixXd& C); + IGL_INLINE void add_label (const Eigen::VectorXd& P, const std::string& str); + + // Computes the normals of the mesh + IGL_INLINE void compute_normals(); + + // Assigns uniform colors to all faces/vertices + IGL_INLINE void uniform_colors( + const Eigen::Vector3d& diffuse, + const Eigen::Vector3d& ambient, + const Eigen::Vector3d& specular); + + // Assigns uniform colors to all faces/vertices + IGL_INLINE void uniform_colors( + const Eigen::Vector4d& ambient, + const Eigen::Vector4d& diffuse, + const Eigen::Vector4d& specular); + + // Generates a default grid texture + IGL_INLINE void grid_texture(); + + Eigen::MatrixXd V; // Vertices of the current mesh (#V x 3) + Eigen::MatrixXi F; // Faces of the mesh (#F x 3) + + // Per face attributes + Eigen::MatrixXd F_normals; // One normal per face + + Eigen::MatrixXd F_material_ambient; // Per face ambient color + Eigen::MatrixXd F_material_diffuse; // Per face diffuse color + Eigen::MatrixXd F_material_specular; // Per face specular color + + // Per vertex attributes + Eigen::MatrixXd V_normals; // One normal per vertex + + Eigen::MatrixXd V_material_ambient; // Per vertex ambient color + Eigen::MatrixXd V_material_diffuse; // Per vertex diffuse color + Eigen::MatrixXd V_material_specular; // Per vertex specular color + + // UV parametrization + Eigen::MatrixXd V_uv; // UV vertices + Eigen::MatrixXi F_uv; // optional faces for UVs + + // Texture + Eigen::Matrix texture_R; + Eigen::Matrix texture_G; + Eigen::Matrix texture_B; + Eigen::Matrix texture_A; + + // Overlays + + // Lines plotted over the scene + // (Every row contains 9 doubles in the following format S_x, S_y, S_z, T_x, T_y, T_z, C_r, C_g, C_b), + // with S and T the coordinates of the two vertices of the line in global coordinates, and C the color in floating point rgb format + Eigen::MatrixXd lines; + + // Points plotted over the scene + // (Every row contains 6 doubles in the following format P_x, P_y, P_z, C_r, C_g, C_b), + // with P the position in global coordinates of the center of the point, and C the color in floating point rgb format + Eigen::MatrixXd points; + + // Text labels plotted over the scene + // Textp contains, in the i-th row, the position in global coordinates where the i-th label should be anchored + // Texts contains in the i-th position the text of the i-th label + Eigen::MatrixXd labels_positions; + std::vector labels_strings; + + // Marks dirty buffers that need to be uploaded to OpenGL + uint32_t dirty; + + // Enable per-face or per-vertex properties + bool face_based; + + // Visualization options + bool show_overlay; + bool show_overlay_depth; + bool show_texture; + bool show_faces; + bool show_lines; + bool show_vertid; + bool show_faceid; + bool invert_normals; + + // Point size / line width + float point_size; + float line_width; + Eigen::Vector4f line_color; + + // Shape material + float shininess; + + // Unique identifier + int id; + + // OpenGL representation of the mesh + igl::opengl::MeshGL meshgl; + + // Update contents from a 'Data' instance + IGL_INLINE void updateGL( + const igl::opengl::ViewerData& data, + const bool invert_normals, + igl::opengl::MeshGL& meshgl); +}; + +} // namespace opengl +} // namespace igl + +//////////////////////////////////////////////////////////////////////////////// + +#include +namespace igl +{ + namespace serialization + { + inline void serialization(bool s, igl::opengl::ViewerData& obj, std::vector& buffer) + { + SERIALIZE_MEMBER(V); + SERIALIZE_MEMBER(F); + SERIALIZE_MEMBER(F_normals); + SERIALIZE_MEMBER(F_material_ambient); + SERIALIZE_MEMBER(F_material_diffuse); + SERIALIZE_MEMBER(F_material_specular); + SERIALIZE_MEMBER(V_normals); + SERIALIZE_MEMBER(V_material_ambient); + SERIALIZE_MEMBER(V_material_diffuse); + SERIALIZE_MEMBER(V_material_specular); + SERIALIZE_MEMBER(V_uv); + SERIALIZE_MEMBER(F_uv); + SERIALIZE_MEMBER(texture_R); + SERIALIZE_MEMBER(texture_G); + SERIALIZE_MEMBER(texture_B); + SERIALIZE_MEMBER(texture_A); + SERIALIZE_MEMBER(lines); + SERIALIZE_MEMBER(points); + SERIALIZE_MEMBER(labels_positions); + SERIALIZE_MEMBER(labels_strings); + SERIALIZE_MEMBER(dirty); + SERIALIZE_MEMBER(face_based); + SERIALIZE_MEMBER(show_faces); + SERIALIZE_MEMBER(show_lines); + SERIALIZE_MEMBER(invert_normals); + SERIALIZE_MEMBER(show_overlay); + SERIALIZE_MEMBER(show_overlay_depth); + SERIALIZE_MEMBER(show_vertid); + SERIALIZE_MEMBER(show_faceid); + SERIALIZE_MEMBER(show_texture); + SERIALIZE_MEMBER(point_size); + SERIALIZE_MEMBER(line_width); + SERIALIZE_MEMBER(line_color); + SERIALIZE_MEMBER(shininess); + SERIALIZE_MEMBER(id); + } + template<> + inline void serialize(const igl::opengl::ViewerData& obj, std::vector& buffer) + { + serialization(true, const_cast(obj), buffer); + } + template<> + inline void deserialize(igl::opengl::ViewerData& obj, const std::vector& buffer) + { + serialization(false, obj, const_cast&>(buffer)); + obj.dirty = igl::opengl::MeshGL::DIRTY_ALL; + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "ViewerData.cpp" +#endif + +#endif diff --git a/src/igl/opengl/bind_vertex_attrib_array.cpp b/src/igl/opengl/bind_vertex_attrib_array.cpp new file mode 100644 index 0000000000..4aa807c18d --- /dev/null +++ b/src/igl/opengl/bind_vertex_attrib_array.cpp @@ -0,0 +1,24 @@ +#include "bind_vertex_attrib_array.h" + +IGL_INLINE GLint igl::opengl::bind_vertex_attrib_array( + const GLuint program_shader, + const std::string &name, + GLuint bufferID, + const Eigen::Matrix &M, + bool refresh) +{ + GLint id = glGetAttribLocation(program_shader, name.c_str()); + if (id < 0) + return id; + if (M.size() == 0) + { + glDisableVertexAttribArray(id); + return id; + } + glBindBuffer(GL_ARRAY_BUFFER, bufferID); + if (refresh) + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*M.size(), M.data(), GL_DYNAMIC_DRAW); + glVertexAttribPointer(id, M.cols(), GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(id); + return id; +} diff --git a/src/igl/opengl/bind_vertex_attrib_array.h b/src/igl/opengl/bind_vertex_attrib_array.h new file mode 100644 index 0000000000..4e94431369 --- /dev/null +++ b/src/igl/opengl/bind_vertex_attrib_array.h @@ -0,0 +1,32 @@ +#ifndef IGL_OPENGL_BIND_VERTEX_ATTRIB_ARRAY_H +#define IGL_OPENGL_BIND_VERTEX_ATTRIB_ARRAY_H +#include "gl.h" +#include "../igl_inline.h" +#include +#include +namespace igl +{ + namespace opengl + { + // Bind a per-vertex array attribute and refresh its contents from an Eigen + // matrix + // + // Inputs: + // program_shader id of shader program + // name name of attribute in vertex shader + // bufferID id of buffer to bind to + // M #V by dim matrix of per-vertex data + // refresh whether to actually call glBufferData or just bind the buffer + // Returns id of named attribute in shader + IGL_INLINE GLint bind_vertex_attrib_array( + const GLuint program_shader, + const std::string &name, + GLuint bufferID, + const Eigen::Matrix &M, + bool refresh); + } +} +#ifndef IGL_STATIC_LIBRARY +#include "bind_vertex_attrib_array.cpp" +#endif +#endif diff --git a/src/igl/opengl/create_index_vbo.cpp b/src/igl/opengl/create_index_vbo.cpp new file mode 100644 index 0000000000..76f6248c0a --- /dev/null +++ b/src/igl/opengl/create_index_vbo.cpp @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "create_index_vbo.h" + +// http://www.songho.ca/opengl/gl_vbo.html#create +IGL_INLINE void igl::opengl::create_index_vbo( + const Eigen::MatrixXi & F, + GLuint & F_vbo_id) +{ + // Generate Buffers + glGenBuffers(1,&F_vbo_id); + // Bind Buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,F_vbo_id); + // Copy data to buffers + // We expect a matrix with each vertex position on a row, we then want to + // pass this data to OpenGL reading across rows (row-major) + if(F.Options & Eigen::RowMajor) + { + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + sizeof(int)*F.size(), + F.data(), + GL_STATIC_DRAW); + }else + { + // Create temporary copy of transpose + Eigen::MatrixXi FT = F.transpose(); + // If its column major then we need to temporarily store a transpose + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + sizeof(int)*F.size(), + FT.data(), + GL_STATIC_DRAW); + } + // bind with 0, so, switch back to normal pointer operation + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/opengl/create_index_vbo.h b/src/igl/opengl/create_index_vbo.h new file mode 100644 index 0000000000..e48f3a4890 --- /dev/null +++ b/src/igl/opengl/create_index_vbo.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_CREATE_INDEX_VBO_H +#define IGL_OPENGL_CREATE_INDEX_VBO_H +#include "../igl_inline.h" +#include "gl.h" +#include + +// Create a VBO (Vertex Buffer Object) for a list of indices: +// GL_ELEMENT_ARRAY_BUFFER_ARB for the triangle indices (F) +namespace igl +{ + namespace opengl + { + // Inputs: + // F #F by 3 eigen Matrix of face (triangle) indices + // Outputs: + // F_vbo_id buffer id for face indices + // + IGL_INLINE void create_index_vbo( + const Eigen::MatrixXi & F, + GLuint & F_vbo_id); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "create_index_vbo.cpp" +#endif + +#endif diff --git a/src/igl/opengl/create_mesh_vbo.cpp b/src/igl/opengl/create_mesh_vbo.cpp new file mode 100644 index 0000000000..3876e1dfaa --- /dev/null +++ b/src/igl/opengl/create_mesh_vbo.cpp @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "create_mesh_vbo.h" + +#include "create_vector_vbo.h" +#include "create_index_vbo.h" + +// http://www.songho.ca/opengl/gl_vbo.html#create +IGL_INLINE void igl::opengl::create_mesh_vbo( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + GLuint & V_vbo_id, + GLuint & F_vbo_id) +{ + // Create VBO for vertex position vectors + create_vector_vbo(V,V_vbo_id); + // Create VBO for face index lists + create_index_vbo(F,F_vbo_id); +} + +// http://www.songho.ca/opengl/gl_vbo.html#create +IGL_INLINE void igl::opengl::create_mesh_vbo( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + GLuint & V_vbo_id, + GLuint & F_vbo_id, + GLuint & N_vbo_id) +{ + // Create VBOs for faces and vertices + create_mesh_vbo(V,F,V_vbo_id,F_vbo_id); + // Create VBO for normal vectors + create_vector_vbo(N,N_vbo_id); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/opengl/create_mesh_vbo.h b/src/igl/opengl/create_mesh_vbo.h new file mode 100644 index 0000000000..ecb303b090 --- /dev/null +++ b/src/igl/opengl/create_mesh_vbo.h @@ -0,0 +1,61 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_CREATE_MESH_VBO_H +#define IGL_OPENGL_CREATE_MESH_VBO_H +#include "../igl_inline.h" +#include "gl.h" +#include + +// Create a VBO (Vertex Buffer Object) for a mesh. Actually two VBOs: one +// GL_ARRAY_BUFFER for the vertex positions (V) and one +// GL_ELEMENT_ARRAY_BUFFER for the triangle indices (F) +namespace igl +{ + namespace opengl + { + + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (triangle) indices + // Outputs: + // V_vbo_id buffer id for vertex positions + // F_vbo_id buffer id for face indices + // + // NOTE: when using glDrawElements VBOs for V and F using MatrixXd and + // MatrixXi will have types GL_DOUBLE and GL_UNSIGNED_INT respectively + // + IGL_INLINE void create_mesh_vbo( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + GLuint & V_vbo_id, + GLuint & F_vbo_id); + + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (triangle) indices + // N #V by 3 eigen Matrix of mesh vertex 3D normals + // Outputs: + // V_vbo_id buffer id for vertex positions + // F_vbo_id buffer id for face indices + // N_vbo_id buffer id for vertex positions + IGL_INLINE void create_mesh_vbo( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + GLuint & V_vbo_id, + GLuint & F_vbo_id, + GLuint & N_vbo_id); + } + +} + +#ifndef IGL_STATIC_LIBRARY +# include "create_mesh_vbo.cpp" +#endif + +#endif diff --git a/src/igl/opengl/create_shader_program.cpp b/src/igl/opengl/create_shader_program.cpp new file mode 100644 index 0000000000..6c4b31a2f7 --- /dev/null +++ b/src/igl/opengl/create_shader_program.cpp @@ -0,0 +1,141 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "create_shader_program.h" + +#include "load_shader.h" +#include "print_program_info_log.h" +#include +#include + +IGL_INLINE bool igl::opengl::create_shader_program( + const std::string & geom_source, + const std::string & vert_source, + const std::string & frag_source, + const std::map & attrib, + GLuint & id) +{ + using namespace std; + if(vert_source == "" && frag_source == "") + { + cerr<< + "create_shader_program() could not create shader program," + " both .vert and .frag source given were empty"<::const_iterator ait = attrib.begin(); + ait != attrib.end(); + ait++) + { + glBindAttribLocation( + id, + (*ait).second, + (*ait).first.c_str()); + } + // Link program + glLinkProgram(id); + const auto & detach = [&id](const GLuint shader) + { + if(shader) + { + glDetachShader(id,shader); + glDeleteShader(shader); + } + }; + detach(g); + detach(f); + detach(v); + + // print log if any + igl::opengl::print_program_info_log(id); + + return true; +} + +IGL_INLINE bool igl::opengl::create_shader_program( + const std::string & vert_source, + const std::string & frag_source, + const std::map & attrib, + GLuint & prog_id) +{ + return create_shader_program("",vert_source,frag_source,attrib,prog_id); +} + + +IGL_INLINE GLuint igl::opengl::create_shader_program( + const std::string & geom_source, + const std::string & vert_source, + const std::string & frag_source, + const std::map & attrib) +{ + GLuint prog_id = 0; + create_shader_program(geom_source,vert_source,frag_source,attrib,prog_id); + return prog_id; +} + +IGL_INLINE GLuint igl::opengl::create_shader_program( + const std::string & vert_source, + const std::string & frag_source, + const std::map & attrib) +{ + GLuint prog_id = 0; + create_shader_program(vert_source,frag_source,attrib,prog_id); + return prog_id; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif + diff --git a/src/igl/opengl/create_shader_program.h b/src/igl/opengl/create_shader_program.h new file mode 100644 index 0000000000..5dfdc701dc --- /dev/null +++ b/src/igl/opengl/create_shader_program.h @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_CREATE_SHADER_PROGRAM_H +#define IGL_OPENGL_CREATE_SHADER_PROGRAM_H +#include "../igl_inline.h" +#include "gl.h" +#include +#include + +namespace igl +{ + namespace opengl + { + // Create a shader program with a vertex and fragments shader loading from + // source strings and vertex attributes assigned from a map before linking the + // shaders to the program, making it ready to use with glUseProgram(id) + // Inputs: + // geom_source string containing source code of geometry shader (can be + // "" to mean use default pass-through) + // vert_source string containing source code of vertex shader + // frag_source string containing source code of fragment shader + // attrib map containing table of vertex attribute strings add their + // correspondingly ids (generated previously using glBindAttribLocation) + // Outputs: + // id index id of created shader, set to 0 on error + // Returns true on success, false on error + // + // Note: Caller is responsible for making sure that current value of id is not + // leaking a shader (since it will be overwritten) + // + // See also: destroy_shader_program + IGL_INLINE bool create_shader_program( + const std::string &geom_source, + const std::string &vert_source, + const std::string &frag_source, + const std::map &attrib, + GLuint & id); + IGL_INLINE bool create_shader_program( + const std::string &vert_source, + const std::string &frag_source, + const std::map &attrib, + GLuint & id); + IGL_INLINE GLuint create_shader_program( + const std::string & geom_source, + const std::string & vert_source, + const std::string & frag_source, + const std::map &attrib); + IGL_INLINE GLuint create_shader_program( + const std::string & vert_source, + const std::string & frag_source, + const std::map &attrib); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "create_shader_program.cpp" +#endif + +#endif diff --git a/src/igl/opengl/create_vector_vbo.cpp b/src/igl/opengl/create_vector_vbo.cpp new file mode 100644 index 0000000000..8690359b74 --- /dev/null +++ b/src/igl/opengl/create_vector_vbo.cpp @@ -0,0 +1,57 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "create_vector_vbo.h" + +#include + +// http://www.songho.ca/opengl/gl_vbo.html#create +template +IGL_INLINE void igl::opengl::create_vector_vbo( + const Eigen::Matrix & V, + GLuint & V_vbo_id) +{ + //// Expects that input is list of 3D vectors along rows + //assert(V.cols() == 3); + + // Generate Buffers + glGenBuffers(1,&V_vbo_id); + // Bind Buffers + glBindBuffer(GL_ARRAY_BUFFER,V_vbo_id); + // Copy data to buffers + // We expect a matrix with each vertex position on a row, we then want to + // pass this data to OpenGL reading across rows (row-major) + if(V.Options & Eigen::RowMajor) + { + glBufferData( + GL_ARRAY_BUFFER, + sizeof(T)*V.size(), + V.data(), + GL_STATIC_DRAW); + }else + { + // Create temporary copy of transpose + Eigen::Matrix VT = V.transpose(); + // If its column major then we need to temporarily store a transpose + glBufferData( + GL_ARRAY_BUFFER, + sizeof(T)*V.size(), + VT.data(), + GL_STATIC_DRAW); + } + // bind with 0, so, switch back to normal pointer operation + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::opengl::create_vector_vbo(Eigen::Matrix const&, unsigned int&); +// generated by autoexplicit.sh +template void igl::opengl::create_vector_vbo(Eigen::Matrix const&, unsigned int&); +#endif + diff --git a/src/igl/opengl/create_vector_vbo.h b/src/igl/opengl/create_vector_vbo.h new file mode 100644 index 0000000000..757c1a1999 --- /dev/null +++ b/src/igl/opengl/create_vector_vbo.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_CREATE_VECTOR_VBO_H +#define IGL_OPENGL_CREATE_VECTOR_VBO_H +#include "../igl_inline.h" +#include "gl.h" +#include + +// Create a VBO (Vertex Buffer Object) for a list of vectors: +// GL_ARRAY_BUFFER for the vectors (V) +namespace igl +{ + namespace opengl + { + // Templates: + // T should be a eigen matrix primitive type like int or double + // Inputs: + // V m by n eigen Matrix of type T values + // Outputs: + // V_vbo_id buffer id for vectors + // + template + IGL_INLINE void create_vector_vbo( + const Eigen::Matrix & V, + GLuint & V_vbo_id); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "create_vector_vbo.cpp" +#endif + +#endif diff --git a/src/igl/opengl/destroy_shader_program.cpp b/src/igl/opengl/destroy_shader_program.cpp new file mode 100644 index 0000000000..2ceace7c22 --- /dev/null +++ b/src/igl/opengl/destroy_shader_program.cpp @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "destroy_shader_program.h" +#include "report_gl_error.h" +#include + +IGL_INLINE bool igl::opengl::destroy_shader_program(const GLuint id) +{ + // Don't try to destroy id == 0 (no shader program) + if(id == 0) + { + fprintf(stderr,"Error: destroy_shader_program() id = %d" + " but must should be positive\n",id); + return false; + } + // Get each attached shader one by one and detach and delete it + GLsizei count; + // shader id + GLuint s; + do + { + // Try to get at most *1* attached shader + glGetAttachedShaders(id,1,&count,&s); + GLenum err = igl::opengl::report_gl_error(); + if (GL_NO_ERROR != err) + { + return false; + } + // Check that we actually got *1* + if(count == 1) + { + // Detach and delete this shader + glDetachShader(id,s); + glDeleteShader(s); + } + }while(count > 0); + // Now that all of the shaders are gone we can just delete the program + glDeleteProgram(id); + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif + diff --git a/src/igl/opengl/destroy_shader_program.h b/src/igl/opengl/destroy_shader_program.h new file mode 100644 index 0000000000..5d53d8c3e4 --- /dev/null +++ b/src/igl/opengl/destroy_shader_program.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_DESTROY_SHADER_PROGRAM_H +#define IGL_OPENGL_DESTROY_SHADER_PROGRAM_H +#include "../igl_inline.h" +#include "gl.h" + +namespace igl +{ + namespace opengl + { + // Properly destroy a shader program. Detach and delete each of its shaders + // and delete it + // Inputs: + // id index id of created shader, set to 0 on error + // Returns true on success, false on error + // + // Note: caller is responsible for making sure he doesn't foolishly continue + // to use id as if it still contains a program + // + // See also: create_shader_program + IGL_INLINE bool destroy_shader_program(const GLuint id); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "destroy_shader_program.cpp" +#endif + +#endif diff --git a/src/igl/opengl/gl.h b/src/igl/opengl/gl.h new file mode 100644 index 0000000000..fc037c198b --- /dev/null +++ b/src/igl/opengl/gl.h @@ -0,0 +1,25 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013, 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GL_H +#define IGL_OPENGL_GL_H + +#ifdef IGL_OPENGL2_GL_H +# error "igl/opengl2/gl.h already included" +#endif + +// Always use this: +// #include "gl.h" +// Instead of: +// #include +// or +// #include +// + +#include + +#endif diff --git a/src/igl/opengl/gl_type_size.cpp b/src/igl/opengl/gl_type_size.cpp new file mode 100644 index 0000000000..0fd02bf057 --- /dev/null +++ b/src/igl/opengl/gl_type_size.cpp @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "gl_type_size.h" +#include + +IGL_INLINE int igl::opengl::gl_type_size(const GLenum type) +{ + switch(type) + { + case GL_DOUBLE: + return 8; + break; + case GL_FLOAT: + return 4; + break; + case GL_INT: + return 4; + break; + default: + // should handle all other GL_[types] + assert(false && "Implementation incomplete."); + break; + } + return -1; +} diff --git a/src/igl/opengl/gl_type_size.h b/src/igl/opengl/gl_type_size.h new file mode 100644 index 0000000000..a741195491 --- /dev/null +++ b/src/igl/opengl/gl_type_size.h @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GL_TYPE_SIZE_H +#define IGL_OPENGL_GL_TYPE_SIZE_H +#include "../igl_inline.h" +#include "gl.h" + +namespace igl +{ + namespace opengl + { + // Return the number of bytes for a given OpenGL type // Inputs: + // type enum value of opengl type + // Returns size in bytes of type + IGL_INLINE int gl_type_size(const GLenum type); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "gl_type_size.cpp" +#endif + +#endif diff --git a/src/igl/opengl/glfw/Viewer.cpp b/src/igl/opengl/glfw/Viewer.cpp new file mode 100644 index 0000000000..76d1604a07 --- /dev/null +++ b/src/igl/opengl/glfw/Viewer.cpp @@ -0,0 +1,950 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "Viewer.h" + +#include +#include + +#include + +#include "../gl.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Internal global variables used for glfw event handling +static igl::opengl::glfw::Viewer * __viewer; +static double highdpi = 1; +static double scroll_x = 0; +static double scroll_y = 0; + +static void glfw_mouse_press(GLFWwindow* window, int button, int action, int modifier) +{ + + igl::opengl::glfw::Viewer::MouseButton mb; + + if (button == GLFW_MOUSE_BUTTON_1) + mb = igl::opengl::glfw::Viewer::MouseButton::Left; + else if (button == GLFW_MOUSE_BUTTON_2) + mb = igl::opengl::glfw::Viewer::MouseButton::Right; + else //if (button == GLFW_MOUSE_BUTTON_3) + mb = igl::opengl::glfw::Viewer::MouseButton::Middle; + + if (action == GLFW_PRESS) + __viewer->mouse_down(mb,modifier); + else + __viewer->mouse_up(mb,modifier); +} + +static void glfw_error_callback(int error, const char* description) +{ + fputs(description, stderr); +} + +static void glfw_char_mods_callback(GLFWwindow* window, unsigned int codepoint, int modifier) +{ + __viewer->key_pressed(codepoint, modifier); +} + +static void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int modifier) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GL_TRUE); + + if (action == GLFW_PRESS) + __viewer->key_down(key, modifier); + else if(action == GLFW_RELEASE) + __viewer->key_up(key, modifier); +} + +static void glfw_window_size(GLFWwindow* window, int width, int height) +{ + int w = width*highdpi; + int h = height*highdpi; + + __viewer->post_resize(w, h); + +} + +static void glfw_mouse_move(GLFWwindow* window, double x, double y) +{ + __viewer->mouse_move(x*highdpi, y*highdpi); +} + +static void glfw_mouse_scroll(GLFWwindow* window, double x, double y) +{ + using namespace std; + scroll_x += x; + scroll_y += y; + + __viewer->mouse_scroll(y); +} + +static void glfw_drop_callback(GLFWwindow *window,int count,const char **filenames) +{ +} + +namespace igl +{ +namespace opengl +{ +namespace glfw +{ + + IGL_INLINE int Viewer::launch(bool resizable,bool fullscreen) + { + // TODO return values are being ignored... + launch_init(resizable,fullscreen); + launch_rendering(true); + launch_shut(); + return EXIT_SUCCESS; + } + + IGL_INLINE int Viewer::launch_init(bool resizable,bool fullscreen) + { + glfwSetErrorCallback(glfw_error_callback); + if (!glfwInit()) + { + return EXIT_FAILURE; + } + glfwWindowHint(GLFW_SAMPLES, 8); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + #ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + #endif + if(fullscreen) + { + GLFWmonitor *monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + window = glfwCreateWindow(mode->width,mode->height,"libigl viewer",monitor,nullptr); + } + else + { + if (core.viewport.tail<2>().any()) { + window = glfwCreateWindow(core.viewport(2),core.viewport(3),"libigl viewer",nullptr,nullptr); + } else { + window = glfwCreateWindow(1280,800,"libigl viewer",nullptr,nullptr); + } + } + if (!window) + { + glfwTerminate(); + return EXIT_FAILURE; + } + glfwMakeContextCurrent(window); + // Load OpenGL and its extensions + if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) + { + printf("Failed to load OpenGL and its extensions\n"); + return(-1); + } + #if defined(DEBUG) || defined(_DEBUG) + printf("OpenGL Version %d.%d loaded\n", GLVersion.major, GLVersion.minor); + int major, minor, rev; + major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR); + minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR); + rev = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION); + printf("OpenGL version received: %d.%d.%d\n", major, minor, rev); + printf("Supported OpenGL is %s\n", (const char*)glGetString(GL_VERSION)); + printf("Supported GLSL is %s\n", (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)); + #endif + glfwSetInputMode(window,GLFW_CURSOR,GLFW_CURSOR_NORMAL); + // Initialize FormScreen + __viewer = this; + // Register callbacks + glfwSetKeyCallback(window, glfw_key_callback); + glfwSetCursorPosCallback(window,glfw_mouse_move); + glfwSetWindowSizeCallback(window,glfw_window_size); + glfwSetMouseButtonCallback(window,glfw_mouse_press); + glfwSetScrollCallback(window,glfw_mouse_scroll); + glfwSetCharModsCallback(window,glfw_char_mods_callback); + glfwSetDropCallback(window,glfw_drop_callback); + // Handle retina displays (windows and mac) + int width, height; + glfwGetFramebufferSize(window, &width, &height); + int width_window, height_window; + glfwGetWindowSize(window, &width_window, &height_window); + highdpi = width/width_window; + glfw_window_size(window,width_window,height_window); + //opengl.init(); + core.align_camera_center(data().V,data().F); + // Initialize IGL viewer + init(); + return EXIT_SUCCESS; + } + + + + IGL_INLINE bool Viewer::launch_rendering(bool loop) + { + // glfwMakeContextCurrent(window); + // Rendering loop + const int num_extra_frames = 5; + int frame_counter = 0; + while (!glfwWindowShouldClose(window)) + { + double tic = get_seconds(); + draw(); + glfwSwapBuffers(window); + if(core.is_animating || frame_counter++ < num_extra_frames) + { + glfwPollEvents(); + // In microseconds + double duration = 1000000.*(get_seconds()-tic); + const double min_duration = 1000000./core.animation_max_fps; + if(durationinit(this); + } + } + + IGL_INLINE void Viewer::shutdown_plugins() + { + for (unsigned int i = 0; ishutdown(); + } + } + + IGL_INLINE Viewer::Viewer(): + data_list(1), + selected_data_index(0), + next_data_id(1) + { + window = nullptr; + data_list.front().id = 0; + + // Temporary variables initialization + down = false; + hack_never_moved = true; + scroll_position = 0.0f; + + // Per face + data().set_face_based(false); + + // C-style callbacks + callback_init = nullptr; + callback_pre_draw = nullptr; + callback_post_draw = nullptr; + callback_mouse_down = nullptr; + callback_mouse_up = nullptr; + callback_mouse_move = nullptr; + callback_mouse_scroll = nullptr; + callback_key_down = nullptr; + callback_key_up = nullptr; + + callback_init_data = nullptr; + callback_pre_draw_data = nullptr; + callback_post_draw_data = nullptr; + callback_mouse_down_data = nullptr; + callback_mouse_up_data = nullptr; + callback_mouse_move_data = nullptr; + callback_mouse_scroll_data = nullptr; + callback_key_down_data = nullptr; + callback_key_up_data = nullptr; + +#ifndef IGL_VIEWER_VIEWER_QUIET + const std::string usage(R"(igl::opengl::glfw::Viewer usage: + [drag] Rotate scene + A,a Toggle animation (tight draw loop) + F,f Toggle face based + I,i Toggle invert normals + L,l Toggle wireframe + O,o Toggle orthographic/perspective projection + T,t Toggle filled faces + Z Snap to canonical view + [,] Toggle between rotation control types (trackball, two-axis + valuator with fixed up, 2D mode with no rotation)) + <,> Toggle between models + ; Toggle vertex labels + : Toggle face labels)" +); + std::cout<load(mesh_file_name_string)) + { + return true; + } + } + + // Create new data slot and set to selected + if(!(data().F.rows() == 0 && data().V.rows() == 0)) + { + append_mesh(); + } + data().clear(); + + size_t last_dot = mesh_file_name_string.rfind('.'); + if (last_dot == std::string::npos) + { + std::cerr<<"Error: No file extension found in "<< + mesh_file_name_string<post_load()) + return true; + + return true; + } + + IGL_INLINE bool Viewer::save_mesh_to_file( + const std::string & mesh_file_name_string) + { + // first try to load it with a plugin + for (unsigned int i = 0; isave(mesh_file_name_string)) + return true; + + size_t last_dot = mesh_file_name_string.rfind('.'); + if (last_dot == std::string::npos) + { + // No file type determined + std::cerr<<"Error: No file extension found in "<< + mesh_file_name_string<key_pressed(unicode_key, modifiers)) + { + return true; + } + } + + switch(unicode_key) + { + case 'A': + case 'a': + { + core.is_animating = !core.is_animating; + return true; + } + case 'F': + case 'f': + { + data().set_face_based(!data().face_based); + return true; + } + case 'I': + case 'i': + { + data().dirty |= MeshGL::DIRTY_NORMAL; + data().invert_normals = !data().invert_normals; + return true; + } + case 'L': + case 'l': + { + data().show_lines = !data().show_lines; + return true; + } + case 'O': + case 'o': + { + core.orthographic = !core.orthographic; + return true; + } + case 'T': + case 't': + { + data().show_faces = !data().show_faces; + return true; + } + case 'Z': + { + snap_to_canonical_quaternion(); + return true; + } + case '[': + case ']': + { + if(core.rotation_type == ViewerCore::ROTATION_TYPE_TRACKBALL) + core.set_rotation_type(ViewerCore::ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP); + else + core.set_rotation_type(ViewerCore::ROTATION_TYPE_TRACKBALL); + + return true; + } + case '<': + case '>': + { + selected_data_index = + (selected_data_index + data_list.size() + (unicode_key=='>'?1:-1))%data_list.size(); + return true; + } + case ';': + data().show_vertid = !data().show_vertid; + return true; + case ':': + data().show_faceid = !data().show_faceid; + return true; + default: break;//do nothing + } + return false; + } + + IGL_INLINE bool Viewer::key_down(int key,int modifiers) + { + if (callback_key_down) + if (callback_key_down(*this,key,modifiers)) + return true; + for (unsigned int i = 0; ikey_down(key, modifiers)) + return true; + return false; + } + + IGL_INLINE bool Viewer::key_up(int key,int modifiers) + { + if (callback_key_up) + if (callback_key_up(*this,key,modifiers)) + return true; + + for (unsigned int i = 0; ikey_up(key, modifiers)) + return true; + + return false; + } + + IGL_INLINE bool Viewer::mouse_down(MouseButton button,int modifier) + { + // Remember mouse location at down even if used by callback/plugin + down_mouse_x = current_mouse_x; + down_mouse_y = current_mouse_y; + + if (callback_mouse_down) + if (callback_mouse_down(*this,static_cast(button),modifier)) + return true; + + for (unsigned int i = 0; imouse_down(static_cast(button),modifier)) + return true; + + down = true; + + down_translation = core.camera_translation; + + + // Initialization code for the trackball + Eigen::RowVector3d center; + if (data().V.rows() == 0) + { + center << 0,0,0; + }else + { + center = data().V.colwise().sum()/data().V.rows(); + } + + Eigen::Vector3f coord = + igl::project( + Eigen::Vector3f(center(0),center(1),center(2)), + core.view, + core.proj, + core.viewport); + down_mouse_z = coord[2]; + down_rotation = core.trackball_angle; + + mouse_mode = MouseMode::Rotation; + + switch (button) + { + case MouseButton::Left: + if (core.rotation_type == ViewerCore::ROTATION_TYPE_NO_ROTATION) { + mouse_mode = MouseMode::Translation; + } else { + mouse_mode = MouseMode::Rotation; + } + break; + + case MouseButton::Right: + mouse_mode = MouseMode::Translation; + break; + + default: + mouse_mode = MouseMode::None; + break; + } + + return true; + } + + IGL_INLINE bool Viewer::mouse_up(MouseButton button,int modifier) + { + down = false; + + if (callback_mouse_up) + if (callback_mouse_up(*this,static_cast(button),modifier)) + return true; + + for (unsigned int i = 0; imouse_up(static_cast(button),modifier)) + return true; + + mouse_mode = MouseMode::None; + + return true; + } + + IGL_INLINE bool Viewer::mouse_move(int mouse_x,int mouse_y) + { + if(hack_never_moved) + { + down_mouse_x = mouse_x; + down_mouse_y = mouse_y; + hack_never_moved = false; + } + current_mouse_x = mouse_x; + current_mouse_y = mouse_y; + + if (callback_mouse_move) + if (callback_mouse_move(*this,mouse_x,mouse_y)) + return true; + + for (unsigned int i = 0; imouse_move(mouse_x, mouse_y)) + return true; + + if (down) + { + switch (mouse_mode) + { + case MouseMode::Rotation: + { + switch(core.rotation_type) + { + default: + assert(false && "Unknown rotation type"); + case ViewerCore::ROTATION_TYPE_NO_ROTATION: + break; + case ViewerCore::ROTATION_TYPE_TRACKBALL: + igl::trackball( + core.viewport(2), + core.viewport(3), + 2.0f, + down_rotation, + down_mouse_x, + down_mouse_y, + mouse_x, + mouse_y, + core.trackball_angle); + break; + case ViewerCore::ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP: + igl::two_axis_valuator_fixed_up( + core.viewport(2),core.viewport(3), + 2.0, + down_rotation, + down_mouse_x, down_mouse_y, mouse_x, mouse_y, + core.trackball_angle); + break; + } + //Eigen::Vector4f snapq = core.trackball_angle; + + break; + } + + case MouseMode::Translation: + { + //translation + Eigen::Vector3f pos1 = igl::unproject(Eigen::Vector3f(mouse_x, core.viewport[3] - mouse_y, down_mouse_z), core.view, core.proj, core.viewport); + Eigen::Vector3f pos0 = igl::unproject(Eigen::Vector3f(down_mouse_x, core.viewport[3] - down_mouse_y, down_mouse_z), core.view, core.proj, core.viewport); + + Eigen::Vector3f diff = pos1 - pos0; + core.camera_translation = down_translation + Eigen::Vector3f(diff[0],diff[1],diff[2]); + + break; + } + case MouseMode::Zoom: + { + float delta = 0.001f * (mouse_x - down_mouse_x + mouse_y - down_mouse_y); + core.camera_zoom *= 1 + delta; + down_mouse_x = mouse_x; + down_mouse_y = mouse_y; + break; + } + + default: + break; + } + } + return true; + } + + IGL_INLINE bool Viewer::mouse_scroll(float delta_y) + { + scroll_position += delta_y; + + if (callback_mouse_scroll) + if (callback_mouse_scroll(*this,delta_y)) + return true; + + for (unsigned int i = 0; imouse_scroll(delta_y)) + return true; + + // Only zoom if there's actually a change + if(delta_y != 0) + { + float mult = (1.0+((delta_y>0)?1.:-1.)*0.05); + const float min_zoom = 0.1f; + core.camera_zoom = (core.camera_zoom * mult > min_zoom ? core.camera_zoom * mult : min_zoom); + } + return true; + } + + IGL_INLINE bool Viewer::load_scene() + { + std::string fname = igl::file_dialog_open(); + if(fname.length() == 0) + return false; + return load_scene(fname); + } + + IGL_INLINE bool Viewer::load_scene(std::string fname) + { + igl::deserialize(core,"Core",fname.c_str()); + igl::deserialize(data(),"Data",fname.c_str()); + return true; + } + + IGL_INLINE bool Viewer::save_scene() + { + std::string fname = igl::file_dialog_save(); + if (fname.length() == 0) + return false; + return save_scene(fname); + } + + IGL_INLINE bool Viewer::save_scene(std::string fname) + { + igl::serialize(core,"Core",fname.c_str(),true); + igl::serialize(data(),"Data",fname.c_str()); + + return true; + } + + IGL_INLINE void Viewer::draw() + { + using namespace std; + using namespace Eigen; + + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + int width_window, height_window; + glfwGetWindowSize(window, &width_window, &height_window); + + auto highdpi_tmp = width/width_window; + + if(fabs(highdpi_tmp-highdpi)>1e-8) + { + post_resize(width, height); + highdpi=highdpi_tmp; + } + + core.clear_framebuffers(); + if (callback_pre_draw) + { + if (callback_pre_draw(*this)) + { + return; + } + } + for (unsigned int i = 0; ipre_draw()) + { + return; + } + } + for(int i = 0;ipost_draw()) + { + break; + } + } + } + + IGL_INLINE void Viewer::resize(int w,int h) + { + if (window) { + glfwSetWindowSize(window, w/highdpi, h/highdpi); + } + post_resize(w, h); + } + + IGL_INLINE void Viewer::post_resize(int w,int h) + { + core.viewport = Eigen::Vector4f(0,0,w,h); + for (unsigned int i = 0; ipost_resize(w, h); + } + } + + IGL_INLINE void Viewer::snap_to_canonical_quaternion() + { + Eigen::Quaternionf snapq = this->core.trackball_angle; + igl::snap_to_canonical_view_quat(snapq,1.0f,this->core.trackball_angle); + } + + IGL_INLINE void Viewer::open_dialog_load_mesh() + { + std::string fname = igl::file_dialog_open(); + + if (fname.length() == 0) + return; + + this->load_mesh_from_file(fname.c_str()); + } + + IGL_INLINE void Viewer::open_dialog_save_mesh() + { + std::string fname = igl::file_dialog_save(); + + if(fname.length() == 0) + return; + + this->save_mesh_to_file(fname.c_str()); + } + + IGL_INLINE ViewerData& Viewer::data() + { + assert(!data_list.empty() && "data_list should never be empty"); + assert( + (selected_data_index >= 0 && selected_data_index < data_list.size()) && + "selected_data_index should be in bounds"); + return data_list[selected_data_index]; + } + + IGL_INLINE int Viewer::append_mesh() + { + assert(data_list.size() >= 1); + + data_list.emplace_back(); + selected_data_index = data_list.size()-1; + data_list.back().id = next_data_id++; + return data_list.back().id; + } + + IGL_INLINE bool Viewer::erase_mesh(const size_t index) + { + assert((index >= 0 && index < data_list.size()) && "index should be in bounds"); + assert(data_list.size() >= 1); + if(data_list.size() == 1) + { + // Cannot remove last mesh + return false; + } + data_list[index].meshgl.free(); + data_list.erase(data_list.begin() + index); + if(selected_data_index >= index && selected_data_index>0) + { + selected_data_index--; + } + return true; + } + + IGL_INLINE size_t Viewer::mesh_index(const int id) const { + for (size_t i = 0; i < data_list.size(); ++i) + { + if (data_list[i].id == id) + return i; + } + return 0; + } + + +} // end namespace +} // end namespace +} diff --git a/src/igl/opengl/glfw/Viewer.h b/src/igl/opengl/glfw/Viewer.h new file mode 100644 index 0000000000..35aa7dfa29 --- /dev/null +++ b/src/igl/opengl/glfw/Viewer.h @@ -0,0 +1,178 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GLFW_VIEWER_H +#define IGL_OPENGL_GLFW_VIEWER_H + +#ifndef IGL_OPENGL_4 +#define IGL_OPENGL_4 +#endif + +#include "../../igl_inline.h" +#include "../MeshGL.h" +#include "../ViewerCore.h" +#include "../ViewerData.h" +#include "ViewerPlugin.h" + +#include +#include + +#include +#include +#include + +#define IGL_MOD_SHIFT 0x0001 +#define IGL_MOD_CONTROL 0x0002 +#define IGL_MOD_ALT 0x0004 +#define IGL_MOD_SUPER 0x0008 + +struct GLFWwindow; + +namespace igl +{ +namespace opengl +{ +namespace glfw +{ + // GLFW-based mesh viewer + class Viewer + { + public: + // UI Enumerations + enum class MouseButton {Left, Middle, Right}; + enum class MouseMode { None, Rotation, Zoom, Pan, Translation} mouse_mode; + IGL_INLINE int launch(bool resizable = true,bool fullscreen = false); + IGL_INLINE int launch_init(bool resizable = true,bool fullscreen = false); + IGL_INLINE bool launch_rendering(bool loop = true); + IGL_INLINE void launch_shut(); + IGL_INLINE void init(); + IGL_INLINE void init_plugins(); + IGL_INLINE void shutdown_plugins(); + Viewer(); + ~Viewer(); + // Mesh IO + IGL_INLINE bool load_mesh_from_file(const std::string & mesh_file_name); + IGL_INLINE bool save_mesh_to_file(const std::string & mesh_file_name); + // Callbacks + IGL_INLINE bool key_pressed(unsigned int unicode_key,int modifier); + IGL_INLINE bool key_down(int key,int modifier); + IGL_INLINE bool key_up(int key,int modifier); + IGL_INLINE bool mouse_down(MouseButton button,int modifier); + IGL_INLINE bool mouse_up(MouseButton button,int modifier); + IGL_INLINE bool mouse_move(int mouse_x,int mouse_y); + IGL_INLINE bool mouse_scroll(float delta_y); + // Scene IO + IGL_INLINE bool load_scene(); + IGL_INLINE bool load_scene(std::string fname); + IGL_INLINE bool save_scene(); + IGL_INLINE bool save_scene(std::string fname); + // Draw everything + IGL_INLINE void draw(); + // OpenGL context resize + IGL_INLINE void resize(int w,int h); // explicitly set window size + IGL_INLINE void post_resize(int w,int h); // external resize due to user interaction + // Helper functions + IGL_INLINE void snap_to_canonical_quaternion(); + IGL_INLINE void open_dialog_load_mesh(); + IGL_INLINE void open_dialog_save_mesh(); + IGL_INLINE ViewerData& data(); + + // Append a new "slot" for a mesh (i.e., create empty entires at the end of + // the data_list and opengl_state_list. + // + // Returns the id of the last appended mesh + // + // Side Effects: + // selected_data_index is set this newly created, last entry (i.e., + // #meshes-1) + IGL_INLINE int append_mesh(); + + // Erase a mesh (i.e., its corresponding data and state entires in data_list + // and opengl_state_list) + // + // Inputs: + // index index of mesh to erase + // Returns whether erasure was successful <=> cannot erase last mesh + // + // Side Effects: + // If selected_data_index is greater than or equal to index then it is + // decremented + // Example: + // // Erase all mesh slots except first and clear remaining mesh + // viewer.selected_data_index = viewer.data_list.size()-1; + // while(viewer.erase_mesh(viewer.selected_data_index)){}; + // viewer.data().clear(); + // + IGL_INLINE bool erase_mesh(const size_t index); + + // Retrieve mesh index from its unique identifier + // Returns 0 if not found + IGL_INLINE size_t mesh_index(const int id) const; + + // Alec: I call this data_list instead of just data to avoid confusion with + // old "data" variable. + // Stores all the data that should be visualized + std::vector data_list; + + size_t selected_data_index; + int next_data_id; + GLFWwindow* window; + // Stores all the viewing options + ViewerCore core; + // List of registered plugins + std::vector plugins; + // Temporary data stored when the mouse button is pressed + Eigen::Quaternionf down_rotation; + int current_mouse_x; + int current_mouse_y; + int down_mouse_x; + int down_mouse_y; + float down_mouse_z; + Eigen::Vector3f down_translation; + bool down; + bool hack_never_moved; + // Keep track of the global position of the scrollwheel + float scroll_position; + // C++-style functions + // + // Returns **true** if action should be cancelled. + std::function callback_init; + std::function callback_pre_draw; + std::function callback_post_draw; + std::function callback_mouse_down; + std::function callback_mouse_up; + std::function callback_mouse_move; + std::function callback_mouse_scroll; + std::function callback_key_pressed; + // THESE SHOULD BE DEPRECATED: + std::function callback_key_down; + std::function callback_key_up; + // Pointers to per-callback data + void* callback_init_data; + void* callback_pre_draw_data; + void* callback_post_draw_data; + void* callback_mouse_down_data; + void* callback_mouse_up_data; + void* callback_mouse_move_data; + void* callback_mouse_scroll_data; + void* callback_key_pressed_data; + void* callback_key_down_data; + void* callback_key_up_data; + + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + }; + +} // end namespace +} // end namespace +} // end namespace + +#ifndef IGL_STATIC_LIBRARY +# include "Viewer.cpp" +#endif + +#endif diff --git a/src/igl/opengl/glfw/ViewerPlugin.h b/src/igl/opengl/glfw/ViewerPlugin.h new file mode 100644 index 0000000000..9ba7903a89 --- /dev/null +++ b/src/igl/opengl/glfw/ViewerPlugin.h @@ -0,0 +1,182 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GLFW_VIEWERPLUGIN_H +#define IGL_OPENGL_GLFW_VIEWERPLUGIN_H + +// TODO: +// * create plugins/skeleton.h +// * pass time in draw function +// * remove Preview3D from comments +// * clean comments +#include +#include +#include + +namespace igl +{ +namespace opengl +{ +namespace glfw +{ + +// Abstract class for plugins +// All plugins MUST have this class as their parent and may implement any/all +// the callbacks marked `virtual` here. +// +// /////For an example of a basic plugins see plugins/skeleton.h +// +// Return value of callbacks: returning true to any of the callbacks tells +// Viewer that the event has been handled and that it should not be passed to +// other plugins or to other internal functions of Viewer + +// Forward declaration of the viewer +class Viewer; + +class ViewerPlugin +{ +public: + IGL_INLINE ViewerPlugin() + {plugin_name = "dummy";} + + virtual ~ViewerPlugin(){} + + // This function is called when the viewer is initialized (no mesh will be loaded at this stage) + IGL_INLINE virtual void init(Viewer *_viewer) + { + viewer = _viewer; + } + + // This function is called before shutdown + IGL_INLINE virtual void shutdown() + { + } + + // This function is called before a mesh is loaded + IGL_INLINE virtual bool load(std::string filename) + { + return false; + } + + // This function is called before a mesh is saved + IGL_INLINE virtual bool save(std::string filename) + { + return false; + } + + // This function is called when the scene is serialized + IGL_INLINE virtual bool serialize(std::vector& buffer) const + { + return false; + } + + // This function is called when the scene is deserialized + IGL_INLINE virtual bool deserialize(const std::vector& buffer) + { + return false; + } + + // Runs immediately after a new mesh has been loaded. + IGL_INLINE virtual bool post_load() + { + return false; + } + + // This function is called before the draw procedure of Preview3D + IGL_INLINE virtual bool pre_draw() + { + return false; + } + + // This function is called after the draw procedure of Preview3D + IGL_INLINE virtual bool post_draw() + { + return false; + } + + // This function is called after the window has been resized + IGL_INLINE virtual void post_resize(int w, int h) + { + } + + // This function is called when the mouse button is pressed + // - button can be GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON or GLUT_RIGHT_BUTTON + // - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT; + IGL_INLINE virtual bool mouse_down(int button, int modifier) + { + return false; + } + + // This function is called when the mouse button is released + // - button can be GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON or GLUT_RIGHT_BUTTON + // - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT; + IGL_INLINE virtual bool mouse_up(int button, int modifier) + { + return false; + } + + // This function is called every time the mouse is moved + // - mouse_x and mouse_y are the new coordinates of the mouse pointer in screen coordinates + IGL_INLINE virtual bool mouse_move(int mouse_x, int mouse_y) + { + return false; + } + + // This function is called every time the scroll wheel is moved + // Note: this callback is not working with every glut implementation + IGL_INLINE virtual bool mouse_scroll(float delta_y) + { + return false; + } + + // This function is called when a keyboard key is pressed. Unlike key_down + // this will reveal the actual character being sent (not just the physical + // key) + // - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT; + IGL_INLINE virtual bool key_pressed(unsigned int key, int modifiers) + { + return false; + } + + // This function is called when a keyboard key is down + // - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT; + IGL_INLINE virtual bool key_down(int key, int modifiers) + { + return false; + } + + // This function is called when a keyboard key is release + // - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT; + IGL_INLINE virtual bool key_up(int key, int modifiers) + { + return false; + } + + std::string plugin_name; +protected: + // Pointer to the main Viewer class + Viewer *viewer; +}; + +namespace serialization +{ + inline void serialize(const ViewerPlugin& obj,std::vector& buffer) + { + obj.serialize(buffer); + } + + inline void deserialize(ViewerPlugin& obj,const std::vector& buffer) + { + obj.deserialize(buffer); + } +} + +} +} +} + +#endif diff --git a/src/igl/opengl/glfw/background_window.cpp b/src/igl/opengl/glfw/background_window.cpp new file mode 100644 index 0000000000..964202cc29 --- /dev/null +++ b/src/igl/opengl/glfw/background_window.cpp @@ -0,0 +1,30 @@ +#include "background_window.h" + +#include + +IGL_INLINE bool igl::opengl::glfw::background_window(GLFWwindow* & window) +{ + if(!glfwInit()) return false; + glfwSetErrorCallback([](int id,const char* m){std::cerr< + +namespace igl +{ + namespace opengl + { + namespace glfw + { + // Create a background window with a valid core profile opengl context + // set to current. + // + // After you're finished with this window you may call + // `glfwDestroyWindow(window)` + // + // After you're finished with glfw you should call `glfwTerminate()` + // + // Outputs: + // window pointer to glfw window + // Returns true iff success + IGL_INLINE bool background_window(GLFWwindow* & window); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "background_window.cpp" +#endif + +#endif diff --git a/src/igl/opengl/glfw/imgui/ImGuiHelpers.h b/src/igl/opengl/glfw/imgui/ImGuiHelpers.h new file mode 100644 index 0000000000..892274b0fc --- /dev/null +++ b/src/igl/opengl/glfw/imgui/ImGuiHelpers.h @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Jérémie Dumas +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H +#define IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H + +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// + +// Extend ImGui by populating its namespace directly +// +// Code snippets taken from there: +// https://eliasdaler.github.io/using-imgui-with-sfml-pt2/ +namespace ImGui +{ + +static auto vector_getter = [](void* vec, int idx, const char** out_text) +{ + auto& vector = *static_cast*>(vec); + if (idx < 0 || idx >= static_cast(vector.size())) { return false; } + *out_text = vector.at(idx).c_str(); + return true; +}; + +inline bool Combo(const char* label, int* idx, std::vector& values) +{ + if (values.empty()) { return false; } + return Combo(label, idx, vector_getter, + static_cast(&values), values.size()); +} + +inline bool Combo(const char* label, int* idx, std::function getter, int items_count) +{ + auto func = [](void* data, int i, const char** out_text) { + auto &getter = *reinterpret_cast *>(data); + const char *s = getter(i); + if (s) { *out_text = s; return true; } + else { return false; } + }; + return Combo(label, idx, func, reinterpret_cast(&getter), items_count); +} + +inline bool ListBox(const char* label, int* idx, std::vector& values) +{ + if (values.empty()) { return false; } + return ListBox(label, idx, vector_getter, + static_cast(&values), values.size()); +} + +inline bool InputText(const char* label, std::string &str, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL) +{ + char buf[1024]; + std::fill_n(buf, 1024, 0); + std::copy_n(str.begin(), std::min(1024, (int) str.size()), buf); + if (ImGui::InputText(label, buf, 1024, flags, callback, user_data)) + { + str = std::string(buf); + return true; + } + return false; +} + +} // namespace ImGui + +#endif // IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H diff --git a/src/igl/opengl/glfw/imgui/ImGuiMenu.cpp b/src/igl/opengl/glfw/imgui/ImGuiMenu.cpp new file mode 100644 index 0000000000..627e055cb0 --- /dev/null +++ b/src/igl/opengl/glfw/imgui/ImGuiMenu.cpp @@ -0,0 +1,385 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Jérémie Dumas +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +//////////////////////////////////////////////////////////////////////////////// +#include "ImGuiMenu.h" +#include +#include +#include +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// + +namespace igl +{ +namespace opengl +{ +namespace glfw +{ +namespace imgui +{ + +IGL_INLINE void ImGuiMenu::init(igl::opengl::glfw::Viewer *_viewer) +{ + ViewerPlugin::init(_viewer); + // Setup ImGui binding + if (_viewer) + { + if (context_ == nullptr) + { + context_ = ImGui::CreateContext(); + } + ImGui_ImplGlfwGL3_Init(viewer->window, false); + ImGui::GetIO().IniFilename = nullptr; + ImGui::StyleColorsDark(); + ImGuiStyle& style = ImGui::GetStyle(); + style.FrameRounding = 5.0f; + reload_font(); + } +} + +IGL_INLINE void ImGuiMenu::reload_font(int font_size) +{ + hidpi_scaling_ = hidpi_scaling(); + pixel_ratio_ = pixel_ratio(); + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->Clear(); + io.Fonts->AddFontFromMemoryCompressedTTF(droid_sans_compressed_data, + droid_sans_compressed_size, font_size * hidpi_scaling_); + io.FontGlobalScale = 1.0 / pixel_ratio_; +} + +IGL_INLINE void ImGuiMenu::shutdown() +{ + // Cleanup + ImGui_ImplGlfwGL3_Shutdown(); + ImGui::DestroyContext(context_); + context_ = nullptr; +} + +IGL_INLINE bool ImGuiMenu::pre_draw() +{ + glfwPollEvents(); + + // Check whether window dpi has changed + float scaling = hidpi_scaling(); + if (std::abs(scaling - hidpi_scaling_) > 1e-5) + { + reload_font(); + ImGui_ImplGlfwGL3_InvalidateDeviceObjects(); + } + + ImGui_ImplGlfwGL3_NewFrame(); + return false; +} + +IGL_INLINE bool ImGuiMenu::post_draw() +{ + draw_menu(); + ImGui::Render(); + return false; +} + +IGL_INLINE void ImGuiMenu::post_resize(int width, int height) +{ + if (context_) + { + ImGui::GetIO().DisplaySize.x = float(width); + ImGui::GetIO().DisplaySize.y = float(height); + } +} + +// Mouse IO +IGL_INLINE bool ImGuiMenu::mouse_down(int button, int modifier) +{ + ImGui_ImplGlfwGL3_MouseButtonCallback(viewer->window, button, GLFW_PRESS, modifier); + return ImGui::GetIO().WantCaptureMouse; +} + +IGL_INLINE bool ImGuiMenu::mouse_up(int button, int modifier) +{ + return ImGui::GetIO().WantCaptureMouse; +} + +IGL_INLINE bool ImGuiMenu::mouse_move(int mouse_x, int mouse_y) +{ + return ImGui::GetIO().WantCaptureMouse; +} + +IGL_INLINE bool ImGuiMenu::mouse_scroll(float delta_y) +{ + ImGui_ImplGlfwGL3_ScrollCallback(viewer->window, 0.f, delta_y); + return ImGui::GetIO().WantCaptureMouse; +} + +// Keyboard IO +IGL_INLINE bool ImGuiMenu::key_pressed(unsigned int key, int modifiers) +{ + ImGui_ImplGlfwGL3_CharCallback(nullptr, key); + return ImGui::GetIO().WantCaptureKeyboard; +} + +IGL_INLINE bool ImGuiMenu::key_down(int key, int modifiers) +{ + ImGui_ImplGlfwGL3_KeyCallback(viewer->window, key, 0, GLFW_PRESS, modifiers); + return ImGui::GetIO().WantCaptureKeyboard; +} + +IGL_INLINE bool ImGuiMenu::key_up(int key, int modifiers) +{ + ImGui_ImplGlfwGL3_KeyCallback(viewer->window, key, 0, GLFW_RELEASE, modifiers); + return ImGui::GetIO().WantCaptureKeyboard; +} + +// Draw menu +IGL_INLINE void ImGuiMenu::draw_menu() +{ + // Text labels + draw_labels_window(); + + // Viewer settings + if (callback_draw_viewer_window) { callback_draw_viewer_window(); } + else { draw_viewer_window(); } + + // Other windows + if (callback_draw_custom_window) { callback_draw_custom_window(); } + else { draw_custom_window(); } +} + +IGL_INLINE void ImGuiMenu::draw_viewer_window() +{ + float menu_width = 180.f * menu_scaling(); + ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiSetCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f), ImGuiSetCond_FirstUseEver); + ImGui::SetNextWindowSizeConstraints(ImVec2(menu_width, -1.0f), ImVec2(menu_width, -1.0f)); + bool _viewer_menu_visible = true; + ImGui::Begin( + "Viewer", &_viewer_menu_visible, + ImGuiWindowFlags_NoSavedSettings + | ImGuiWindowFlags_AlwaysAutoResize + ); + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.4f); + if (callback_draw_viewer_menu) { callback_draw_viewer_menu(); } + else { draw_viewer_menu(); } + ImGui::PopItemWidth(); + ImGui::End(); +} + +IGL_INLINE void ImGuiMenu::draw_viewer_menu() +{ + // Workspace + if (ImGui::CollapsingHeader("Workspace", ImGuiTreeNodeFlags_DefaultOpen)) + { + float w = ImGui::GetContentRegionAvailWidth(); + float p = ImGui::GetStyle().FramePadding.x; + if (ImGui::Button("Load##Workspace", ImVec2((w-p)/2.f, 0))) + { + viewer->load_scene(); + } + ImGui::SameLine(0, p); + if (ImGui::Button("Save##Workspace", ImVec2((w-p)/2.f, 0))) + { + viewer->save_scene(); + } + } + + // Mesh + if (ImGui::CollapsingHeader("Mesh", ImGuiTreeNodeFlags_DefaultOpen)) + { + float w = ImGui::GetContentRegionAvailWidth(); + float p = ImGui::GetStyle().FramePadding.x; + if (ImGui::Button("Load##Mesh", ImVec2((w-p)/2.f, 0))) + { + viewer->open_dialog_load_mesh(); + } + ImGui::SameLine(0, p); + if (ImGui::Button("Save##Mesh", ImVec2((w-p)/2.f, 0))) + { + viewer->open_dialog_save_mesh(); + } + } + + // Viewing options + if (ImGui::CollapsingHeader("Viewing Options", ImGuiTreeNodeFlags_DefaultOpen)) + { + if (ImGui::Button("Center object", ImVec2(-1, 0))) + { + viewer->core.align_camera_center(viewer->data().V, viewer->data().F); + } + if (ImGui::Button("Snap canonical view", ImVec2(-1, 0))) + { + viewer->snap_to_canonical_quaternion(); + } + + // Zoom + ImGui::PushItemWidth(80 * menu_scaling()); + ImGui::DragFloat("Zoom", &(viewer->core.camera_zoom), 0.05f, 0.1f, 20.0f); + + // Select rotation type + int rotation_type = static_cast(viewer->core.rotation_type); + static Eigen::Quaternionf trackball_angle = Eigen::Quaternionf::Identity(); + static bool orthographic = true; + if (ImGui::Combo("Camera Type", &rotation_type, "Trackball\0Two Axes\0002D Mode\0\0")) + { + using RT = igl::opengl::ViewerCore::RotationType; + auto new_type = static_cast(rotation_type); + if (new_type != viewer->core.rotation_type) + { + if (new_type == RT::ROTATION_TYPE_NO_ROTATION) + { + trackball_angle = viewer->core.trackball_angle; + orthographic = viewer->core.orthographic; + viewer->core.trackball_angle = Eigen::Quaternionf::Identity(); + viewer->core.orthographic = true; + } + else if (viewer->core.rotation_type == RT::ROTATION_TYPE_NO_ROTATION) + { + viewer->core.trackball_angle = trackball_angle; + viewer->core.orthographic = orthographic; + } + viewer->core.set_rotation_type(new_type); + } + } + + // Orthographic view + ImGui::Checkbox("Orthographic view", &(viewer->core.orthographic)); + ImGui::PopItemWidth(); + } + + // Draw options + if (ImGui::CollapsingHeader("Draw Options", ImGuiTreeNodeFlags_DefaultOpen)) + { + if (ImGui::Checkbox("Face-based", &(viewer->data().face_based))) + { + viewer->data().set_face_based(viewer->data().face_based); + } + ImGui::Checkbox("Show texture", &(viewer->data().show_texture)); + if (ImGui::Checkbox("Invert normals", &(viewer->data().invert_normals))) + { + viewer->data().dirty |= igl::opengl::MeshGL::DIRTY_NORMAL; + } + ImGui::Checkbox("Show overlay", &(viewer->data().show_overlay)); + ImGui::Checkbox("Show overlay depth", &(viewer->data().show_overlay_depth)); + ImGui::ColorEdit4("Background", viewer->core.background_color.data(), + ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_PickerHueWheel); + ImGui::ColorEdit4("Line color", viewer->data().line_color.data(), + ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_PickerHueWheel); + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.3f); + ImGui::DragFloat("Shininess", &(viewer->data().shininess), 0.05f, 0.0f, 100.0f); + ImGui::PopItemWidth(); + } + + // Overlays + if (ImGui::CollapsingHeader("Overlays", ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::Checkbox("Wireframe", &(viewer->data().show_lines)); + ImGui::Checkbox("Fill", &(viewer->data().show_faces)); + ImGui::Checkbox("Show vertex labels", &(viewer->data().show_vertid)); + ImGui::Checkbox("Show faces labels", &(viewer->data().show_faceid)); + } +} + +IGL_INLINE void ImGuiMenu::draw_labels_window() +{ + // Text labels + ImGui::SetNextWindowPos(ImVec2(0,0), ImGuiSetCond_Always); + ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize, ImGuiSetCond_Always); + bool visible = true; + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0,0,0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0); + ImGui::Begin("ViewerLabels", &visible, + ImGuiWindowFlags_NoTitleBar + | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoMove + | ImGuiWindowFlags_NoScrollbar + | ImGuiWindowFlags_NoScrollWithMouse + | ImGuiWindowFlags_NoCollapse + | ImGuiWindowFlags_NoSavedSettings + | ImGuiWindowFlags_NoInputs); + for (const auto & data : viewer->data_list) + { + draw_labels(data); + } + ImGui::End(); + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); +} + +IGL_INLINE void ImGuiMenu::draw_labels(const igl::opengl::ViewerData &data) +{ + if (data.show_vertid) + { + for (int i = 0; i < data.V.rows(); ++i) + { + draw_text(data.V.row(i), data.V_normals.row(i), std::to_string(i)); + } + } + + if (data.show_faceid) + { + for (int i = 0; i < data.F.rows(); ++i) + { + Eigen::RowVector3d p = Eigen::RowVector3d::Zero(); + for (int j = 0; j < data.F.cols(); ++j) + { + p += data.V.row(data.F(i,j)); + } + p /= (double) data.F.cols(); + + draw_text(p, data.F_normals.row(i), std::to_string(i)); + } + } + + if (data.labels_positions.rows() > 0) + { + for (int i = 0; i < data.labels_positions.rows(); ++i) + { + draw_text(data.labels_positions.row(i), Eigen::Vector3d(0.0,0.0,0.0), + data.labels_strings[i]); + } + } +} + +IGL_INLINE void ImGuiMenu::draw_text(Eigen::Vector3d pos, Eigen::Vector3d normal, const std::string &text) +{ + pos += normal * 0.005f * viewer->core.object_scale; + Eigen::Vector3f coord = igl::project(Eigen::Vector3f(pos.cast()), + viewer->core.view, viewer->core.proj, viewer->core.viewport); + + // Draw text labels slightly bigger than normal text + ImDrawList* drawList = ImGui::GetWindowDrawList(); + drawList->AddText(ImGui::GetFont(), ImGui::GetFontSize() * 1.2, + ImVec2(coord[0]/pixel_ratio_, (viewer->core.viewport[3] - coord[1])/pixel_ratio_), + ImGui::GetColorU32(ImVec4(0, 0, 10, 255)), + &text[0], &text[0] + text.size()); +} + +IGL_INLINE float ImGuiMenu::pixel_ratio() +{ + // Computes pixel ratio for hidpi devices + int buf_size[2]; + int win_size[2]; + GLFWwindow* window = glfwGetCurrentContext(); + glfwGetFramebufferSize(window, &buf_size[0], &buf_size[1]); + glfwGetWindowSize(window, &win_size[0], &win_size[1]); + return (float) buf_size[0] / (float) win_size[0]; +} + +IGL_INLINE float ImGuiMenu::hidpi_scaling() +{ + // Computes scaling factor for hidpi devices + float xscale, yscale; + GLFWwindow* window = glfwGetCurrentContext(); + glfwGetWindowContentScale(window, &xscale, &yscale); + return 0.5 * (xscale + yscale); +} + +} // end namespace +} // end namespace +} // end namespace +} // end namespace diff --git a/src/igl/opengl/glfw/imgui/ImGuiMenu.h b/src/igl/opengl/glfw/imgui/ImGuiMenu.h new file mode 100644 index 0000000000..e7144a675c --- /dev/null +++ b/src/igl/opengl/glfw/imgui/ImGuiMenu.h @@ -0,0 +1,110 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Jérémie Dumas +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GLFW_IMGUI_IMGUIMENU_H +#define IGL_OPENGL_GLFW_IMGUI_IMGUIMENU_H + +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// + +// Forward declarations +struct ImGuiContext; + +namespace igl +{ +namespace opengl +{ +namespace glfw +{ +namespace imgui +{ + +class ImGuiMenu : public igl::opengl::glfw::ViewerPlugin +{ +protected: + // Hidpi scaling to be used for text rendering. + float hidpi_scaling_; + + // Ratio between the framebuffer size and the window size. + // May be different from the hipdi scaling! + float pixel_ratio_; + + // ImGui Context + ImGuiContext * context_ = nullptr; + +public: + IGL_INLINE virtual void init(igl::opengl::glfw::Viewer *_viewer) override; + + IGL_INLINE virtual void reload_font(int font_size = 13); + + IGL_INLINE virtual void shutdown() override; + + IGL_INLINE virtual bool pre_draw() override; + + IGL_INLINE virtual bool post_draw() override; + + IGL_INLINE virtual void post_resize(int width, int height) override; + + // Mouse IO + IGL_INLINE virtual bool mouse_down(int button, int modifier) override; + + IGL_INLINE virtual bool mouse_up(int button, int modifier) override; + + IGL_INLINE virtual bool mouse_move(int mouse_x, int mouse_y) override; + + IGL_INLINE virtual bool mouse_scroll(float delta_y) override; + + // Keyboard IO + IGL_INLINE virtual bool key_pressed(unsigned int key, int modifiers) override; + + IGL_INLINE virtual bool key_down(int key, int modifiers) override; + + IGL_INLINE virtual bool key_up(int key, int modifiers) override; + + // Draw menu + IGL_INLINE virtual void draw_menu(); + + // Can be overwritten by `callback_draw_viewer_window` + IGL_INLINE virtual void draw_viewer_window(); + + // Can be overwritten by `callback_draw_viewer_menu` + IGL_INLINE virtual void draw_viewer_menu(); + + // Can be overwritten by `callback_draw_custom_window` + IGL_INLINE virtual void draw_custom_window() { } + + // Easy-to-customize callbacks + std::function callback_draw_viewer_window; + std::function callback_draw_viewer_menu; + std::function callback_draw_custom_window; + + IGL_INLINE void draw_labels_window(); + + IGL_INLINE void draw_labels(const igl::opengl::ViewerData &data); + + IGL_INLINE void draw_text(Eigen::Vector3d pos, Eigen::Vector3d normal, const std::string &text); + + IGL_INLINE float pixel_ratio(); + + IGL_INLINE float hidpi_scaling(); + + float menu_scaling() { return hidpi_scaling_ / pixel_ratio_; } +}; + +} // end namespace +} // end namespace +} // end namespace +} // end namespace + +#ifndef IGL_STATIC_LIBRARY +# include "ImGuiMenu.cpp" +#endif + +#endif // IGL_OPENGL_GLFW_IMGUI_IMGUIMENU_H diff --git a/src/igl/opengl/glfw/map_texture.cpp b/src/igl/opengl/glfw/map_texture.cpp new file mode 100644 index 0000000000..7975d87d47 --- /dev/null +++ b/src/igl/opengl/glfw/map_texture.cpp @@ -0,0 +1,211 @@ +#ifdef IGL_OPENGL_4 + +#include "map_texture.h" +#include "background_window.h" +#include "../create_shader_program.h" + +#include "../gl.h" +#include + +#include +#include + +template +IGL_INLINE bool igl::opengl::glfw::map_texture( + const Eigen::MatrixBase & _V, + const Eigen::MatrixBase & _F, + const Eigen::MatrixBase & _U, + const unsigned char * in_data, + const int w, + const int h, + const int nc, + std::vector & out_data) +{ + int out_w = w; + int out_h = h; + int out_nc = nc; + return map_texture(_V,_F,_U,in_data,w,h,nc,out_data,out_w,out_h,out_nc); +} + + +template +IGL_INLINE bool igl::opengl::glfw::map_texture( + const Eigen::MatrixBase & _V, + const Eigen::MatrixBase & _F, + const Eigen::MatrixBase & _U, + const unsigned char * in_data, + const int w, + const int h, + const int nc, + std::vector & out_data, + int & out_w, + int & out_h, + int & out_nc) +{ + const auto fail = [](const std::string msg) + { + std::cerr< V = _V.template cast(); + Eigen::Matrix< + double, + DerivedU::RowsAtCompileTime, + DerivedU::ColsAtCompileTime, + Eigen::RowMajor> U = _U.template cast(); + Eigen::Matrix< + int, + DerivedF::RowsAtCompileTime, + DerivedF::ColsAtCompileTime, + Eigen::RowMajor> F = _F.template cast(); + const int dim = U.cols(); + GLFWwindow * window; + if(!background_window(window)) + { + fail("Could not initialize glfw window"); + } + + // Compile each shader + std::string vertex_shader = dim == 2 ? + R"( +#version 330 core +layout(location = 0) in vec2 position; +layout(location = 1) in vec2 tex_coord_v; +out vec2 tex_coord_f; +void main() +{ + tex_coord_f = vec2(tex_coord_v.x,1.-tex_coord_v.y); + gl_Position = vec4( 2.*position.x-1., 2.*(1.-position.y)-1., 0.,1.); +} +)" + : + R"( +#version 330 core +layout(location = 0) in vec3 position; +layout(location = 1) in vec2 tex_coord_v; +out vec2 tex_coord_f; +void main() +{ + tex_coord_f = vec2(tex_coord_v.x,1.-tex_coord_v.y); + gl_Position = vec4( 2.*position.x-1., 2.*(1.-position.y)-1., position.z,1.); +} +)" + ; + std::string fragment_shader = R"( +#version 330 core +layout(location = 0) out vec3 color; +uniform sampler2D tex; +in vec2 tex_coord_f; +void main() +{ + color = texture(tex,tex_coord_f).rgb; +} +)"; + GLuint prog_id = + igl::opengl::create_shader_program(vertex_shader,fragment_shader,{}); + glUniform1i(glGetUniformLocation(prog_id, "tex"),0); + // Generate and attach buffers to vertex array + glDisable(GL_CULL_FACE); + GLuint VAO = 0; + glGenVertexArrays(1,&VAO); + glBindVertexArray(VAO); + GLuint ibo,vbo,tbo; + glGenBuffers(1,&ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*F.size(), F.data(), GL_STATIC_DRAW); + glGenBuffers(1,&vbo); + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER,vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(double)*U.size(), U.data(), GL_STATIC_DRAW); + glVertexAttribLPointer(0, U.cols(), GL_DOUBLE, U.cols() * sizeof(GLdouble), (GLvoid*)0); + glGenBuffers(1,&tbo); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER,tbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(double)*V.size(), V.data(), GL_STATIC_DRAW); + glVertexAttribLPointer(1, V.cols(), GL_DOUBLE, V.cols() * sizeof(GLdouble), (GLvoid*)0); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + // Prepare texture + GLuint in_tex; + GLenum format; + { + format = nc==1 ? GL_RED : (nc==3 ? GL_RGB : (nc == 4 ? GL_RGBA : GL_FALSE)); + glGenTextures(1, &in_tex); + glBindTexture(GL_TEXTURE_2D, in_tex); + 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_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w, h, 0,format, GL_UNSIGNED_BYTE, in_data); + } + // Prepare framebuffer + GLuint fb = 0; + glGenFramebuffers(1, &fb); + glBindFramebuffer(GL_FRAMEBUFFER, fb); + GLuint out_tex; + glGenTextures(1, &out_tex); + glBindTexture(GL_TEXTURE_2D, out_tex); + // always use float for internal storage + assert(out_nc == 3); + glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, out_w, out_h, 0,GL_RGB, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, out_tex, 0); + { + GLenum bufs[1] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, bufs); + } + if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + fail("framebuffer setup failed."); + } + glBindFramebuffer(GL_FRAMEBUFFER, fb); + // clear screen and set viewport + glClearColor(0.0,1.0,0.0,0.); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0,0,out_w,out_h); + // Attach shader program + glUseProgram(prog_id); + glActiveTexture(GL_TEXTURE0 + 0); + glBindTexture(GL_TEXTURE_2D, in_tex); + // Draw mesh as wireframe + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glBindVertexArray(VAO); + glDrawElements(GL_TRIANGLES, F.size(), GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + // Write into memory + assert(out_nc == 3); + out_data.resize(out_nc*out_w*out_h); + glBindTexture(GL_TEXTURE_2D, out_tex); + glGetTexImage(GL_TEXTURE_2D, 0, format, GL_UNSIGNED_BYTE, &out_data[0]); + // OpenGL cleanup + glDeleteBuffers(1,&fb); + glDeleteBuffers(1,&ibo); + glDeleteBuffers(1,&vbo); + glDeleteBuffers(1,&tbo); + glDeleteTextures(1,&in_tex); + glDeleteTextures(1,&out_tex); + glDeleteVertexArrays(1,&VAO); + glUseProgram(0); + glDeleteProgram(prog_id); + // GLFW cleanup + glfwDestroyWindow(window); + glfwTerminate(); + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::opengl::glfw::map_texture, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, unsigned char const*, int, int, int, std::vector >&); +#endif + +#endif // IGL_OPENGL_4 diff --git a/src/igl/opengl/glfw/map_texture.h b/src/igl/opengl/glfw/map_texture.h new file mode 100644 index 0000000000..ee3b30a450 --- /dev/null +++ b/src/igl/opengl/glfw/map_texture.h @@ -0,0 +1,63 @@ +#ifndef IGL_OPENGL_GLFW_MAP_TEXTURE_H +#define IGL_OPENGL_GLFW_MAP_TEXTURE_H + +#ifdef IGL_OPENGL_4 + +#include "../../igl_inline.h" +#include +#include + +namespace igl +{ + namespace opengl + { + namespace glfw + { + // Given a mesh (V,F) in [0,1]² and new positions (U) and a texture image + // (in_data), _render_ a new image (out_data) of the same size. + // Inputs: + // V #V by 2 list of undeformed mesh vertex positions (matching texture) + // F #F by 3 list of mesh triangle indices into V + // U #U by 2 list of deformed vertex positions + // in_data w*h*nc array of color values, channels, then columns, then + // rows (e.g., what stbi_image returns and expects) + // w width + // h height + // nc number of channels + // Outputs: + // out_data h*w*nc list of output colors in same order as input + // + template + IGL_INLINE bool map_texture( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & U, + const unsigned char * in_data, + const int w, + const int h, + const int nc, + std::vector & out_data); + template + IGL_INLINE bool map_texture( + const Eigen::MatrixBase & _V, + const Eigen::MatrixBase & _F, + const Eigen::MatrixBase & _U, + const unsigned char * in_data, + const int w, + const int h, + const int nc, + std::vector & out_data, + int & out_w, + int & out_h, + int & out_nc); + } + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "map_texture.cpp" +#endif + +#endif // IGL_OPENGL_4 + +#endif diff --git a/src/igl/opengl/init_render_to_texture.cpp b/src/igl/opengl/init_render_to_texture.cpp new file mode 100644 index 0000000000..7fbcfea10b --- /dev/null +++ b/src/igl/opengl/init_render_to_texture.cpp @@ -0,0 +1,86 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "init_render_to_texture.h" +#include "gl.h" +#include + +IGL_INLINE void igl::opengl::init_render_to_texture( + const size_t width, + const size_t height, + const bool depth_texture, + GLuint & tex_id, + GLuint & fbo_id, + GLuint & d_id) +{ + using namespace std; + // http://www.opengl.org/wiki/Framebuffer_Object_Examples#Quick_example.2C_render_to_texture_.282D.29 + const auto & gen_tex = [](GLuint & tex_id) + { + glGenTextures(1, &tex_id); + glBindTexture(GL_TEXTURE_2D, tex_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + }; + gen_tex(tex_id); + //NULL means reserve texture memory, but texels are undefined + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_BGRA, GL_FLOAT, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenFramebuffers(1, &fbo_id); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_id); + //Attach 2D texture to this FBO + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_id, 0); + if(depth_texture) + { + // Generate a depth texture + gen_tex(d_id); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_DEPTH_COMPONENT32, + width, + height, + 0, + GL_DEPTH_COMPONENT, + GL_FLOAT, + NULL); + glFramebufferTexture2D( + GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,d_id,0); + }else + { + // Attach a depth buffer + glGenRenderbuffers(1, &d_id); + glBindRenderbuffer(GL_RENDERBUFFER, d_id); + glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT24,width,height); + glFramebufferRenderbuffer( + GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,d_id); + } + + //Does the GPU support current FBO configuration? + GLenum status; + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + assert(status == GL_FRAMEBUFFER_COMPLETE); + // Unbind to clean up + if(!depth_texture) + { + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +IGL_INLINE void igl::opengl::init_render_to_texture( + const size_t width, + const size_t height, + GLuint & tex_id, + GLuint & fbo_id, + GLuint & dfbo_id) +{ + return init_render_to_texture(width,height,false,tex_id,fbo_id,dfbo_id); +} diff --git a/src/igl/opengl/init_render_to_texture.h b/src/igl/opengl/init_render_to_texture.h new file mode 100644 index 0000000000..ae854e770a --- /dev/null +++ b/src/igl/opengl/init_render_to_texture.h @@ -0,0 +1,77 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_INIT_RENDER_TO_TEXTURE_H +#define IGL_OPENGL_INIT_RENDER_TO_TEXTURE_H +#include "../igl_inline.h" +#include "gl.h" +#include +namespace igl +{ + namespace opengl + { + // Create a frame buffer that renders color to a RGBA texture a depth to a + // "render buffer". + // + // After calling this, you can use with something like: + // + // glBindFramebuffer(GL_FRAMEBUFFER, fbo_id); + // if(!depth_texture) + // { + // glBindRenderbuffer(GL_RENDERBUFFER, d_id); + // } + // // + // // draw scene ... + // // + // // clean up + // glBindFramebuffer(GL_FRAMEBUFFER,0); + // if(!depth_texture) + // { + // glBindRenderbuffer(GL_RENDERBUFFER, 0); + // } + // // Later ... + // glActiveTexture(GL_TEXTURE0+0); + // glBindTexture(GL_TEXTURE_2D,tex_id); + // if(depth_texture) + // { + // glActiveTexture(GL_TEXTURE0+1); + // glBindTexture(GL_TEXTURE_2D,d_id); + // } + // // draw textures + // + // + // + // Inputs: + // width image width + // height image height + // depth_texture whether to create a texture for depth or to create a + // render buffer for depth + // Outputs: + // tex_id id of the texture + // fbo_id id of the frame buffer object + // d_id id of the depth texture or frame buffer object + // + IGL_INLINE void init_render_to_texture( + const size_t width, + const size_t height, + const bool depth_texture, + GLuint & tex_id, + GLuint & fbo_id, + GLuint & d_id); + // Wrapper with depth_texture = false for legacy reasons + IGL_INLINE void init_render_to_texture( + const size_t width, + const size_t height, + GLuint & tex_id, + GLuint & fbo_id, + GLuint & dfbo_id); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "init_render_to_texture.cpp" +#endif +#endif diff --git a/src/igl/opengl/load_shader.cpp b/src/igl/opengl/load_shader.cpp new file mode 100644 index 0000000000..0ee0a978f4 --- /dev/null +++ b/src/igl/opengl/load_shader.cpp @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "load_shader.h" + +// Copyright Denis Kovacs 4/10/08 +#include "print_shader_info_log.h" +#include +IGL_INLINE GLuint igl::opengl::load_shader( + const std::string & src,const GLenum type) +{ + if(src.empty()) + { + return (GLuint) 0; + } + + GLuint s = glCreateShader(type); + if(s == 0) + { + fprintf(stderr,"Error: load_shader() failed to create shader.\n"); + return 0; + } + // Pass shader source string + const char *c = src.c_str(); + glShaderSource(s, 1, &c, NULL); + glCompileShader(s); + // Print info log (if any) + igl::opengl::print_shader_info_log(s); + return s; +} diff --git a/src/igl/opengl/load_shader.h b/src/igl/opengl/load_shader.h new file mode 100644 index 0000000000..6c234d8fcd --- /dev/null +++ b/src/igl/opengl/load_shader.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_LOAD_SHADER_H +#define IGL_OPENGL_LOAD_SHADER_H +#include "../igl_inline.h" +#include "gl.h" +#include + +namespace igl +{ + namespace opengl + { + // Creates and compiles a shader from a given string + // + // Inputs: + // src string containing GLSL shader code + // type GLSL type of shader, one of: + // GL_VERTEX_SHADER + // GL_FRAGMENT_SHADER + // GL_GEOMETRY_SHADER + // Returns index id of the newly created shader, 0 on error + // + // Will immediately return 0 if src is empty. + IGL_INLINE GLuint load_shader( + const std::string & src,const GLenum type); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "load_shader.cpp" +#endif + +#endif diff --git a/src/igl/opengl/print_program_info_log.cpp b/src/igl/opengl/print_program_info_log.cpp new file mode 100644 index 0000000000..41b37b7fbe --- /dev/null +++ b/src/igl/opengl/print_program_info_log.cpp @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "print_program_info_log.h" + +#include +#include +// Copyright Denis Kovacs 4/10/08 +IGL_INLINE void igl::opengl::print_program_info_log(const GLuint obj) +{ + GLint infologLength = 0; + GLint charsWritten = 0; + char *infoLog; + + glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength); + + if (infologLength > 0) + { + infoLog = (char *)malloc(infologLength); + glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog); + printf("%s\n",infoLog); + free(infoLog); + } +} diff --git a/src/igl/opengl/print_program_info_log.h b/src/igl/opengl/print_program_info_log.h new file mode 100644 index 0000000000..43bcc09744 --- /dev/null +++ b/src/igl/opengl/print_program_info_log.h @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_PRINT_PROGRAM_INFO_LOG_H +#define IGL_OPENGL_PRINT_PROGRAM_INFO_LOG_H +#include "../igl_inline.h" +#include "gl.h" + +namespace igl +{ + namespace opengl + { + // Inputs: + // obj OpenGL index of program to print info log about + IGL_INLINE void print_program_info_log(const GLuint obj); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "print_program_info_log.cpp" +#endif + +#endif diff --git a/src/igl/opengl/print_shader_info_log.cpp b/src/igl/opengl/print_shader_info_log.cpp new file mode 100644 index 0000000000..2d3554387c --- /dev/null +++ b/src/igl/opengl/print_shader_info_log.cpp @@ -0,0 +1,29 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "print_shader_info_log.h" + +#include +#include +// Copyright Denis Kovacs 4/10/08 +IGL_INLINE void igl::opengl::print_shader_info_log(const GLuint obj) +{ + GLint infologLength = 0; + GLint charsWritten = 0; + char *infoLog; + + // Get shader info log from opengl + glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength); + // Only print if there is something in the log + if (infologLength > 0) + { + infoLog = (char *)malloc(infologLength); + glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog); + printf("%s\n",infoLog); + free(infoLog); + } +} diff --git a/src/igl/opengl/print_shader_info_log.h b/src/igl/opengl/print_shader_info_log.h new file mode 100644 index 0000000000..5a571fb116 --- /dev/null +++ b/src/igl/opengl/print_shader_info_log.h @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_PRINT_SHADER_INFO_LOG_H +#define IGL_OPENGL_PRINT_SHADER_INFO_LOG_H +#include "../igl_inline.h" +#include "gl.h" + +namespace igl +{ + namespace opengl + { + // Inputs: + // obj OpenGL index of shader to print info log about + IGL_INLINE void print_shader_info_log(const GLuint obj); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "print_shader_info_log.cpp" +#endif + +#endif diff --git a/src/igl/opengl/report_gl_error.cpp b/src/igl/opengl/report_gl_error.cpp new file mode 100644 index 0000000000..2dcc0b6323 --- /dev/null +++ b/src/igl/opengl/report_gl_error.cpp @@ -0,0 +1,61 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "report_gl_error.h" +#include "../verbose.h" +#include + +IGL_INLINE GLenum igl::opengl::report_gl_error(const std::string id) +{ + // http://stackoverflow.com/q/28485180/148668 + + // gluErrorString was deprecated + const auto gluErrorString = [](GLenum errorCode)->const char * + { + switch(errorCode) + { + default: + return "unknown error code"; + case GL_NO_ERROR: + return "no error"; + case GL_INVALID_ENUM: + return "invalid enumerant"; + case GL_INVALID_VALUE: + return "invalid value"; + case GL_INVALID_OPERATION: + return "invalid operation"; +#ifndef GL_VERSION_3_0 + case GL_STACK_OVERFLOW: + return "stack overflow"; + case GL_STACK_UNDERFLOW: + return "stack underflow"; + case GL_TABLE_TOO_LARGE: + return "table too large"; +#endif + case GL_OUT_OF_MEMORY: + return "out of memory"; +#ifdef GL_EXT_framebuffer_object + case GL_INVALID_FRAMEBUFFER_OPERATION_EXT: + return "invalid framebuffer operation"; +#endif + } + }; + + GLenum err = glGetError(); + if(GL_NO_ERROR != err) + { + verbose("GL_ERROR: "); + fprintf(stderr,"%s%s\n",id.c_str(),gluErrorString(err)); + } + return err; +} + +IGL_INLINE GLenum igl::opengl::report_gl_error() +{ + return igl::opengl::report_gl_error(std::string("")); +} + diff --git a/src/igl/opengl/report_gl_error.h b/src/igl/opengl/report_gl_error.h new file mode 100644 index 0000000000..70e5c7d74f --- /dev/null +++ b/src/igl/opengl/report_gl_error.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_REPORT_GL_ERROR_H +#define IGL_OPENGL_REPORT_GL_ERROR_H +#include "../igl_inline.h" + +// Hack to allow both opengl/ and opengl2 to use this (we shouldn't allow this) +#ifndef __gl_h_ +# include "gl.h" +#endif +#include + +namespace igl +{ + namespace opengl + { + // Print last OpenGL error to stderr prefixed by specified id string + // Inputs: + // id string to appear before any error msgs + // Returns result of glGetError() + IGL_INLINE GLenum report_gl_error(const std::string id); + // No prefix + IGL_INLINE GLenum report_gl_error(); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "report_gl_error.cpp" +#endif + +#endif diff --git a/src/igl/opengl/uniform_type_to_string.cpp b/src/igl/opengl/uniform_type_to_string.cpp new file mode 100644 index 0000000000..b2f9a2fdb4 --- /dev/null +++ b/src/igl/opengl/uniform_type_to_string.cpp @@ -0,0 +1,71 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "uniform_type_to_string.h" + +IGL_INLINE std::string igl::opengl::uniform_type_to_string(const GLenum type) +{ + switch(type) + { + case GL_FLOAT: + return "GL_FLOAT"; + case GL_FLOAT_VEC2: + return "GL_FLOAT_VEC2"; + case GL_FLOAT_VEC3: + return "GL_FLOAT_VEC3"; + case GL_FLOAT_VEC4: + return "GL_FLOAT_VEC4"; + case GL_INT: + return "GL_INT"; + case GL_INT_VEC2: + return "GL_INT_VEC2"; + case GL_INT_VEC3: + return "GL_INT_VEC3"; + case GL_INT_VEC4: + return "GL_INT_VEC4"; + case GL_BOOL: + return "GL_BOOL"; + case GL_BOOL_VEC2: + return "GL_BOOL_VEC2"; + case GL_BOOL_VEC3: + return "GL_BOOL_VEC3"; + case GL_BOOL_VEC4: + return "GL_BOOL_VEC4"; + case GL_FLOAT_MAT2: + return "GL_FLOAT_MAT2"; + case GL_FLOAT_MAT3: + return "GL_FLOAT_MAT3"; + case GL_FLOAT_MAT4: + return "GL_FLOAT_MAT4"; + case GL_FLOAT_MAT2x3: + return "GL_FLOAT_MAT2x3"; + case GL_FLOAT_MAT2x4: + return "GL_FLOAT_MAT2x4"; + case GL_FLOAT_MAT3x2: + return "GL_FLOAT_MAT3x2"; + case GL_FLOAT_MAT3x4: + return "GL_FLOAT_MAT3x4"; + case GL_FLOAT_MAT4x2: + return "GL_FLOAT_MAT4x2"; + case GL_FLOAT_MAT4x3: + return "GL_FLOAT_MAT4x3"; + case GL_SAMPLER_1D: + return "GL_SAMPLER_1D"; + case GL_SAMPLER_2D: + return "GL_SAMPLER_2D"; + case GL_SAMPLER_3D: + return "GL_SAMPLER_3D"; + case GL_SAMPLER_CUBE: + return "GL_SAMPLER_CUBE"; + case GL_SAMPLER_1D_SHADOW: + return "GL_SAMPLER_1D_SHADOW"; + case GL_SAMPLER_2D_SHADOW: + return "GL_SAMPLER_2D_SHADOW"; + default: + return "UNKNOWN_TYPE"; + } +} diff --git a/src/igl/opengl/uniform_type_to_string.h b/src/igl/opengl/uniform_type_to_string.h new file mode 100644 index 0000000000..ed0a93e906 --- /dev/null +++ b/src/igl/opengl/uniform_type_to_string.h @@ -0,0 +1,31 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_UNIFORM_TYPE_TO_STRING_H +#define IGL_OPENGL_UNIFORM_TYPE_TO_STRING_H +#include "../igl_inline.h" +#include "gl.h" +#include + +namespace igl +{ + namespace opengl + { + // Convert a GL uniform variable type (say, returned from + // glGetActiveUniform) and output a string naming that type + // Inputs: + // type enum for given type + // Returns string name of that type + IGL_INLINE std::string uniform_type_to_string(const GLenum type); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "uniform_type_to_string.cpp" +#endif + +#endif diff --git a/src/igl/opengl/vertex_array.cpp b/src/igl/opengl/vertex_array.cpp new file mode 100644 index 0000000000..c07113ad3f --- /dev/null +++ b/src/igl/opengl/vertex_array.cpp @@ -0,0 +1,61 @@ +#include "vertex_array.h" +#include + +template < + typename DerivedV, + typename DerivedF> +IGL_INLINE void igl::opengl::vertex_array( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + GLuint & va_id, + GLuint & ab_id, + GLuint & eab_id) +{ + // Inputs should be in RowMajor storage. If not, we have no choice but to + // create a copy. + if(!(V.Options & Eigen::RowMajor)) + { + Eigen::Matrix< + typename DerivedV::Scalar, + DerivedV::RowsAtCompileTime, + DerivedV::ColsAtCompileTime, + Eigen::RowMajor> VR = V; + return vertex_array(VR,F,va_id,ab_id,eab_id); + } + if(!(F.Options & Eigen::RowMajor)) + { + Eigen::Matrix< + typename DerivedF::Scalar, + DerivedF::RowsAtCompileTime, + DerivedF::ColsAtCompileTime, + Eigen::RowMajor> FR = F; + return vertex_array(V,FR,va_id,ab_id,eab_id); + } + // Generate and attach buffers to vertex array + glGenVertexArrays(1, &va_id); + glGenBuffers(1, &ab_id); + glGenBuffers(1, &eab_id); + glBindVertexArray(va_id); + glBindBuffer(GL_ARRAY_BUFFER, ab_id); + const auto size_VScalar = sizeof(typename DerivedV::Scalar); + const auto size_FScalar = sizeof(typename DerivedF::Scalar); + glBufferData(GL_ARRAY_BUFFER,size_VScalar*V.size(),V.data(),GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eab_id); + assert(sizeof(GLuint) == size_FScalar && "F type does not match GLuint"); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*F.size(), F.data(), GL_STATIC_DRAW); + glVertexAttribPointer( + 0, + V.cols(), + size_VScalar==sizeof(float)?GL_FLOAT:GL_DOUBLE, + GL_FALSE, + V.cols()*size_VScalar, + (GLvoid*)0); + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::opengl::vertex_array, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned int&, unsigned int&, unsigned int&); +#endif diff --git a/src/igl/opengl/vertex_array.h b/src/igl/opengl/vertex_array.h new file mode 100644 index 0000000000..1342a9c90c --- /dev/null +++ b/src/igl/opengl/vertex_array.h @@ -0,0 +1,38 @@ +#ifndef IGL_OPENGL_VERTEX_ARRAY_H +#define IGL_OPENGL_VERTEX_ARRAY_H +#include +#include +#include +namespace igl +{ + namespace opengl + { + // Create a GL_VERTEX_ARRAY for a given mesh (V,F) + // + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by 3 list of mesh triangle indices into V + // Outputs: + // va_id id of vertex array + // ab_id id of array buffer (vertex buffer object) + // eab_id id of element array buffer (element/face buffer object) + // + template < + typename DerivedV, + typename DerivedF> + IGL_INLINE void vertex_array( + // Note: Unlike most libigl functions, the **input** Eigen matrices must + // be `Eigen::PlainObjectBase` because we want to directly access it's + // underlying storage. It cannot be `Eigen::MatrixBase` (see + // http://stackoverflow.com/questions/25094948/) + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + GLuint & va_id, + GLuint & ab_id, + GLuint & eab_id); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "vertex_array.cpp" +#endif +#endif diff --git a/src/igl/opengl2/MouseController.h b/src/igl/opengl2/MouseController.h new file mode 100644 index 0000000000..eb38802ad6 --- /dev/null +++ b/src/igl/opengl2/MouseController.h @@ -0,0 +1,691 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_MOUSECONTROLLER_H +#define IGL_OPENGL2_MOUSECONTROLLER_H +// Needs to be included before others +#include +#include "RotateWidget.h" +#include "TranslateWidget.h" +#include +#include +#include + +// Class for control a skeletal FK rig with the mouse. +namespace igl +{ + namespace opengl2 + { + class MouseController + { + public: + typedef Eigen::VectorXi VectorXb; + // Propagate selection to descendants so that selected bones and their + // subtrees are all selected. + // + // Input: + // S #S list of whether selected + // P #S list of bone parents + // Output: + // T #S list of whether selected + static inline void propogate_to_descendants_if( + const VectorXb & S, + const Eigen::VectorXi & P, + VectorXb & T); + // Create a matrix of colors for the selection and their descendants. + // + // Inputs: + // selection #S list of whether a bone is selected + // selected_color color for selected bones + // unselected_color color for unselected bones + // Outputs: + // C #P by 4 list of colors + static inline void color_if( + const VectorXb & S, + const Eigen::Vector4f & selected_color, + const Eigen::Vector4f & unselected_color, + Eigen::MatrixXf & C); + enum WidgetMode + { + WIDGET_MODE_ROTATE = 0, + WIDGET_MODE_TRANSLATE = 1, + NUM_WIDGET_MODES = 2, + }; + private: + // m_is_selecting whether currently selecting + // m_selection #m_rotations list of whether a bone is selected + // m_down_x x-coordinate of mouse location at down + // m_down_y y-coordinate 〃 + // m_drag_x x-coordinate of mouse location at drag + // m_drag_y y-coordinate 〃 + // m_widget rotation widget for selected bone + // m_width width of containing window + // m_height height 〃 + // m_rotations list of rotations for each bone + // m_rotations_at_selection list of rotations for each bone at time of + // selection + // m_translations list of translations for each bone + // m_fk_rotations_at_selection list of rotations for each bone at time of + // selection + // m_root_enabled Whether root is enabled + bool m_is_selecting; + VectorXb m_selection; + int m_down_x,m_down_y,m_drag_x,m_drag_y; + int m_width,m_height; + igl::opengl2::RotateWidget m_widget; + igl::opengl2::TranslateWidget m_trans_widget; + Eigen::Quaterniond m_widget_rot_at_selection; + //Eigen::Vector3d m_trans_widget_trans_at_selection; + typedef std::vector< + Eigen::Quaterniond, + Eigen::aligned_allocator > RotationList; + typedef std::vector< Eigen::Vector3d > TranslationList; + RotationList + m_rotations, + m_rotations_at_selection, + m_fk_rotations_at_selection, + m_parent_rotations_at_selection; + TranslationList + m_translations, + m_translations_at_selection, + m_fk_translations_at_selection; + bool m_root_enabled; + WidgetMode m_widget_mode; + public: + MouseController(); + // Returns const reference to m_selection + inline const VectorXb & selection() const{return m_selection;}; + // 〃 m_is_selecting + inline const bool & is_selecting() const{return m_is_selecting;} + inline bool is_widget_down() const{return m_widget.is_down();} + inline bool is_trans_widget_down() const{return m_trans_widget.is_down();} + // 〃 m_rotations + inline const RotationList & rotations() const{return m_rotations;} + inline const TranslationList & translations() const{return m_translations;} + // Returns non-const reference to m_root_enabled + inline bool & root_enabled(){ return m_root_enabled;} + inline void reshape(const int w, const int h); + // Process down, drag, up mouse events + // + // Inputs: + // x x-coordinate of mouse click with respect to container + // y y-coordinate 〃 + // Returns true if accepted (action taken). + inline bool down(const int x, const int y); + inline bool drag(const int x, const int y); + inline bool up(const int x, const int y); + // Draw selection box and widget + inline void draw() const; + // Set `m_selection` based on the last drag selection and initialize + // widget. + // + // Inputs: + // C #C by dim list of joint positions at rest + // BE #BE by 2 list of bone indices at rest + // P #P list of bone parents + inline void set_selection_from_last_drag( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const Eigen::VectorXi & P, + const Eigen::VectorXi & RP); + // Set from explicit selection + inline void set_selection( + const Eigen::VectorXi & S, + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const Eigen::VectorXi & P, + const Eigen::VectorXi & RP); + // Set size of skeleton + // + // Inputs: + // n number of bones + inline void set_size(const int n); + // Resets m_rotation elements to identity + inline void reset(); + inline void reset_selected(); + inline void reset_rotations(); + inline void reset_selected_rotations(); + inline void reset_translations(); + inline void reset_selected_translations(); + inline bool set_rotations(const RotationList & vQ); + inline bool set_translations(const TranslationList & vT); + // Sets all entries in m_selection to false + inline void clear_selection(); + // Returns true iff some element in m_selection is true + inline bool any_selection() const; + inline void set_widget_mode(const WidgetMode & mode); + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + }; + } +} + +// Implementation +#include "../line_segment_in_rectangle.h" +#include "draw_rectangular_marquee.h" +#include "project.h" +#include "../forward_kinematics.h" +#include +#include +#include + +inline void igl::opengl2::MouseController::propogate_to_descendants_if( + const VectorXb & S, + const Eigen::VectorXi & P, + VectorXb & T) +{ + using namespace std; + const int n = S.rows(); + assert(P.rows() == n); + // dynamic programming + T = S; + vector seen(n,false); + // Recursively look up chain and see if ancestor is selected + const function look_up = [&](int e) -> bool + { + if(e==-1) + { + return false; + } + if(!seen[e]) + { + seen[e] = true; + T(e) |= look_up(P(e)); + } + return T(e); + }; + for(int e = 0;e > vQ; + vector vT; + forward_kinematics(C,BE,P,m_rotations,m_translations,vQ,vT); + // Loop over deformed bones + for(int e = 0;e > & vQ = + m_fk_rotations_at_selection; + vector & vT = m_fk_translations_at_selection; + forward_kinematics(C,BE,P,m_rotations,m_translations,vQ,vT); + m_parent_rotations_at_selection.resize( + m_rotations.size(),Quaterniond::Identity()); + for(size_t r = 0;r=0) + { + m_parent_rotations_at_selection[r] = vQ[P(r)]; + } + } + + + if(&m_selection != &S) + { + m_selection = S; + } + assert(m_selection.rows() == BE.rows()); + assert(BE.rows() == P.rows()); + assert(BE.rows() == RP.rows()); + // Zero-out S up a path of ones from e + auto propagate = [&](const int e, const VectorXb & S, VectorXb & N) + { + if(S(e)) + { + int f = e; + while(true) + { + int p = P(f); + if(p==-1||!S(p)) + { + break; + } + N(f) = false; + f = p; + } + } + }; + VectorXb prev_selection = m_selection; + // Combine upward, group rigid parts, repeat + while(true) + { + // Spread selection across rigid pieces + VectorXb SRP(VectorXb::Zero(RP.maxCoeff()+1)); + for(int e = 0;e +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_ROTATE_WIDGET_H +#define IGL_OPENGL2_ROTATE_WIDGET_H +#include "../material_colors.h" +#include +#include +#include + +namespace igl +{ + namespace opengl2 + { + // 3D Rotate tool widget similar to Maya's. Works best if field of view angle + // is less than ~25. + class RotateWidget + { + // If a is true then use A else use desaturated A + static inline void glColor4fv(const bool a, const Eigen::Vector4f & A); + public: + inline static Eigen::Quaterniond axis_q(const int a); + inline static Eigen::Vector3d view_direction(const int x, const int y); + inline static Eigen::Vector3d view_direction(const Eigen::Vector3d & pos); + Eigen::Vector3d pos; + Eigen::Quaterniond rot,down_rot; + Eigen::Vector2d down_xy,drag_xy,down_dir; + Eigen::Vector3d udown,udrag; + double outer_radius_on_screen; + double outer_over_inner; + bool m_is_enabled; + enum DownType + { + DOWN_TYPE_X = 0, + DOWN_TYPE_Y = 1, + DOWN_TYPE_Z = 2, + DOWN_TYPE_OUTLINE = 3, + DOWN_TYPE_TRACKBALL = 4, + DOWN_TYPE_NONE = 5, + NUM_DOWN_TYPES = 6 + } down_type, selected_type; + inline RotateWidget(); + // Vector from origin to mouse click "Unprojected" onto plane with depth of + // origin and scale to so that outer radius is 1 + // + // Inputs: + // x mouse x position + // y mouse y position + // Returns vector + inline Eigen::Vector3d unproject_onto(const int x, const int y) const; + // Shoot ray from mouse click to sphere + // + // Inputs: + // x mouse x position + // y mouse y position + // Outputs: + // hit position of hit + // Returns true only if there was a hit + inline bool intersect( + const int x, + const int y, + Eigen::Vector3d & hit) const; + inline double unprojected_inner_radius() const; + inline bool down(const int x, const int y); + inline bool drag(const int x, const int y); + inline bool up(const int x, const int y); + inline bool is_down() const; + inline void draw() const; + inline void draw_guide() const; + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + }; + } +} + +// Implementation +#include "../PI.h" +#include "../EPS.h" +#include "../ray_sphere_intersect.h" +#include "../mat_to_quat.h" +#include "../trackball.h" +#include "gl.h" +#include "project.h" +#include "unproject.h" +#include +#include + +inline void igl::opengl2::RotateWidget::glColor4fv( + const bool a, + const Eigen::Vector4f & A) +{ + if(a) + { + ::glColor4fv(A.data()); + }else + { + Eigen::Vector4f B; + const double f = 0.95; // desaturate by 95% + const double L = 0.3*A(0) + 0.6*A(1) + 0.1*A(2); + B.head(3) = A.head(3).array() + f*(L-A.head(3).array()); + B(3) = A(3); + ::glColor4fv(B.data()); + } +} + +inline Eigen::Quaterniond igl::opengl2::RotateWidget::axis_q(const int a) +{ + assert(a<3 && a>=0); + const Eigen::Quaterniond axes[3] = { + Eigen::Quaterniond(Eigen::AngleAxisd(igl::PI*0.5,Eigen::Vector3d(0,1,0))), + Eigen::Quaterniond(Eigen::AngleAxisd(igl::PI*0.5,Eigen::Vector3d(1,0,0))), + Eigen::Quaterniond::Identity()}; + return axes[a]; +} + +inline Eigen::Vector3d igl::opengl2::RotateWidget::view_direction(const int x, const int y) +{ + using namespace Eigen; + const Vector3d win_s(x,y,0), win_d(x,y,1); + const Vector3d s = unproject(win_s); + const Vector3d d = unproject(win_d); + return d-s; +} + +inline Eigen::Vector3d igl::opengl2::RotateWidget::view_direction(const Eigen::Vector3d & pos) +{ + using namespace Eigen; + const Vector3d ppos = project(pos); + return view_direction(ppos(0),ppos(1)); +} + +inline igl::opengl2::RotateWidget::RotateWidget(): + pos(0,0,0), + rot(Eigen::Quaterniond::Identity()), + down_rot(rot), + down_xy(-1,-1),drag_xy(-1,-1), + outer_radius_on_screen(91.), + outer_over_inner(1.13684210526), + m_is_enabled(true), + down_type(DOWN_TYPE_NONE), + selected_type(DOWN_TYPE_NONE) +{ +} + +inline Eigen::Vector3d igl::opengl2::RotateWidget::unproject_onto( + const int x, + const int y) const +{ + using namespace Eigen; + // KNOWN BUG: This projects to same depths as pos. I think what we actually + // want is The intersection with the plane perpendicular to the view + // direction at pos. If the field of view angle is small then this difference + // is negligible. + //const Vector3d ppos = project(pos); + //const Vector3d uxy = unproject( Vector3d(x,y,ppos(2))); + // http://en.wikipedia.org/wiki/Line-plane_intersection + // + // Hrrmmm. There's still something wrong here if the ball's in the corner of + // the screen. Am I somehow not accounting for perspective correctly? + // + // Q: What about just projecting the circle's equation and solving for the + // distance? + const Vector3d l0 = unproject(Vector3d(x,y,0)); + const Vector3d l = unproject(Vector3d(x,y,1))-l0; + const Vector3d n = view_direction(pos); + const double t = (pos-l0).dot(n)/l.dot(n); + const Vector3d uxy = l0+t*l; + return (uxy-pos)/unprojected_inner_radius()*outer_over_inner*outer_over_inner; +} + +inline bool igl::opengl2::RotateWidget::intersect( + const int x, + const int y, + Eigen::Vector3d & hit) const +{ + using namespace Eigen; + Vector3d view = view_direction(x,y); + const Vector3d ppos = project(pos); + Vector3d uxy = unproject(Vector3d(x,y,ppos(2))); + double t0,t1; + if(!ray_sphere_intersect(uxy,view,pos,unprojected_inner_radius(),t0,t1)) + { + return false; + } + hit = uxy+t0*view; + return true; +} + + +inline double igl::opengl2::RotateWidget::unprojected_inner_radius() const +{ + using namespace Eigen; + Vector3d off,ppos,ppos_off,pos_off; + project(pos,ppos); + ppos_off = ppos; + ppos_off(0) += outer_radius_on_screen/outer_over_inner; + unproject(ppos_off,pos_off); + return (pos-pos_off).norm(); +} +inline bool igl::opengl2::RotateWidget::down(const int x, const int y) +{ + using namespace Eigen; + using namespace std; + if(!m_is_enabled) + { + return false; + } + down_type = DOWN_TYPE_NONE; + selected_type = DOWN_TYPE_NONE; + down_xy = Vector2d(x,y); + drag_xy = down_xy; + down_rot = rot; + Vector3d ppos = project(pos); + const double r = (ppos.head(2) - down_xy).norm(); + const double thresh = 3; + if(fabs(r - outer_radius_on_screen) bool + { + // project onto rotate plane + pl_hit = hit-pos; + pl_hit = (m.conjugate()*rot.conjugate()*pl_hit).eval(); + pl_hit(2) = 0; + pl_hit = (rot*m*pl_hit).eval(); + pl_hit.normalize(); + pl_hit *= unprojected_inner_radius(); + pl_hit += pos; + return (project(pl_hit).head(2)-project(hit).head(2)).norm()<2*thresh; + }; + udown = (hit-pos).normalized()/outer_radius_on_screen; + udrag = udown; + for(int a = 0;a<3;a++) + { + Vector3d pl_hit; + if(on_meridian(hit,rot,Quaterniond(axis_q(a)),pl_hit)) + { + udown = (pl_hit-pos).normalized()/outer_radius_on_screen; + udrag = udown; + down_type = DownType(DOWN_TYPE_X+a); + selected_type = down_type; + { + Vector3d dir3 = axis_q(a).conjugate()*down_rot.conjugate()*(hit-pos); + dir3 = AngleAxisd(-PI*0.5,Vector3d(0,0,1))*dir3; + dir3 = (rot*axis_q(a)*dir3).eval(); + down_dir = (project((hit+dir3).eval())-project(hit)).head(2); + down_dir.normalize(); + //// flip y because y coordinate is going to be given backwards in + //// drag() + //down_dir(1) *= -1; + } + return true; + } + } + //assert(is_hit); + down_type = DOWN_TYPE_TRACKBALL; + selected_type = DOWN_TYPE_TRACKBALL; + return true; + }else + { + return false; + } +} + +inline bool igl::opengl2::RotateWidget::drag(const int x, const int y) +{ + using namespace std; + using namespace Eigen; + if(!m_is_enabled) + { + return false; + } + drag_xy = Vector2d(x,y); + switch(down_type) + { + case DOWN_TYPE_NONE: + return false; + default: + { + const Quaterniond & q = axis_q(down_type-DOWN_TYPE_X); + const double dtheta = -(drag_xy - down_xy).dot(down_dir)/ + outer_radius_on_screen/outer_over_inner*PI/2.; + Quaterniond dq(AngleAxisd(dtheta,down_rot*q*Vector3d(0,0,1))); + rot = dq * down_rot; + udrag = dq * udown; + return true; + } + case DOWN_TYPE_OUTLINE: + { + Vector3d ppos = project(pos); + // project mouse to same depth as pos + udrag = unproject_onto(x,y); + const Vector2d A = down_xy - ppos.head(2); + const Vector2d B = drag_xy - ppos.head(2); + const double dtheta = atan2(A(0)*B(1)-A(1)*B(0),A(0)*B(0)+A(1)*B(1)); + Vector3d n = view_direction(pos).normalized(); + Quaterniond dq(AngleAxisd(dtheta,-n)); + //Vector3d n = udrag.cross(udown).normalized(); + //Quaterniond dq(AngleAxisd(fabs(dtheta),-n)); + rot = dq * down_rot; + } + return true; + case DOWN_TYPE_TRACKBALL: + { + Vector3d ppos = project(pos); + const double r = (double)outer_radius_on_screen/outer_over_inner*2.0; + //const int h = w; + Vector4i vp; + glGetIntegerv(GL_VIEWPORT,vp.data()); + const int h = vp(3); + Quaterniond dq; + trackball( + r,r, + 1, + Quaterniond::Identity(), + double( down_xy(0)-ppos(0) )+r/2., + double((h-down_xy(1))-(h-ppos(1)))+r/2., + double( x-ppos(0) )+r/2., + double( (h-y)-(h-ppos(1)))+r/2., + dq); + // We've computed change in rotation according to this view: + // R = mv * r, R' = rot * (mv * r) + // But we only want new value for r: + // R' = mv * r' + // mv * r' = rot * (mv * r) + // r' = mv* * rot * mv * r + Matrix4d mv; + glGetDoublev(GL_MODELVIEW_MATRIX,mv.data()); + Quaterniond scene_rot; + // Convert modelview matrix to quaternion + mat4_to_quat(mv.data(),scene_rot.coeffs().data()); + scene_rot.normalize(); + rot = scene_rot.conjugate() * dq * scene_rot * down_rot; + } + return true; + } +} + +inline bool igl::opengl2::RotateWidget::up(const int /*x*/, const int /*y*/) +{ + // even if disabled process up + down_type = DOWN_TYPE_NONE; + return false; +} + +inline bool igl::opengl2::RotateWidget::is_down() const +{ + return down_type != DOWN_TYPE_NONE; +} + +inline void igl::opengl2::RotateWidget::draw() const +{ + using namespace Eigen; + using namespace std; + glPushAttrib(GL_ENABLE_BIT | GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT | GL_LINE_BIT); + glDisable(GL_CLIP_PLANE0); + + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glLineWidth(2.0); + + double r = unprojected_inner_radius(); + Vector3d view = view_direction(pos).normalized(); + + auto draw_circle = [&](const bool cull) + { + Vector3d view = view_direction(pos).normalized(); + glBegin(GL_LINES); + const double th_step = (2.0*igl::PI/100.0); + for(double th = 0;th<2.0*igl::PI+th_step;th+=th_step) + { + Vector3d a(cos(th),sin(th),0.0); + Vector3d b(cos(th+th_step),sin(th+th_step),0.0); + if(!cull || (0.5*(a+b)).dot(view) +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_TRANSLATE_WIDGET_H +#define IGL_OPENGL2_TRANSLATE_WIDGET_H +#include "../material_colors.h" +#include +#include +#include + +namespace igl +{ + namespace opengl2 + { + class TranslateWidget + { +public: + // m_pos position of center + // m_trans translation vector + // m_down_xy mouse position on down + // m_drag_xy mouse position on drag + // m_is_enabled whether enabled + Eigen::Vector3d m_pos,m_trans,m_down_trans; + Eigen::Vector2d m_down_xy, m_drag_xy; + bool m_is_enabled; + double m_len; + enum DownType + { + DOWN_TYPE_X = 0, + DOWN_TYPE_Y = 1, + DOWN_TYPE_Z = 2, + DOWN_TYPE_CENTER = 3, + DOWN_TYPE_NONE = 4, + NUM_DOWN_TYPES = 5 + } m_down_type, m_selected_type; + inline TranslateWidget(const Eigen::Vector3d & pos = Eigen::Vector3d(0,0,0)); + inline bool down(const int x, const int y); + inline bool drag(const int x, const int y); + inline bool up(const int x, const int y); + inline bool is_down() const; + inline void draw() const; +public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW; + }; + } +} + +// Implementation +#include "project.h" +#include "unproject.h" + +inline igl::opengl2::TranslateWidget::TranslateWidget( + const Eigen::Vector3d & pos): + m_pos(pos), + m_trans(0,0,0), + m_down_xy(-1,-1), + m_drag_xy(-1,-1), + m_is_enabled(true), + m_len(50), + m_down_type(DOWN_TYPE_NONE), + m_selected_type(DOWN_TYPE_NONE) +{ +} + +inline bool igl::opengl2::TranslateWidget::down(const int x, const int y) +{ + using namespace Eigen; + using namespace std; + if(!m_is_enabled) + { + return false; + } + m_down_trans = m_trans; + m_down_xy = Vector2d(x,y); + m_drag_xy = m_down_xy; + m_down_type = DOWN_TYPE_NONE; + m_selected_type = DOWN_TYPE_NONE; + Vector3d ppos = project((m_pos+m_trans).eval()); + const double r = (ppos.head(2) - m_down_xy).norm(); + const double center_thresh = 10; + if(r < center_thresh) + { + m_down_type = DOWN_TYPE_CENTER; + m_selected_type = m_down_type; + return true; + }else if(r < m_len) + { + // Might be hit on lines + } + return false; +} + +inline bool igl::opengl2::TranslateWidget::drag(const int x, const int y) +{ + using namespace std; + using namespace Eigen; + if(!m_is_enabled) + { + return false; + } + m_drag_xy = Vector2d(x,y); + switch(m_down_type) + { + case DOWN_TYPE_NONE: + return false; + default: + { + Vector3d ppos = project((m_pos+m_trans).eval()); + Vector3d drag3(m_drag_xy(0),m_drag_xy(1),ppos(2)); + Vector3d down3(m_down_xy(0),m_down_xy(1),ppos(2)); + m_trans = m_down_trans + unproject(drag3)-unproject(down3); + return true; + } + } +} + +inline bool igl::opengl2::TranslateWidget::up(const int /*x*/, const int /*y*/) +{ + // even if disabled process up + m_down_type = DOWN_TYPE_NONE; + return false; +} + +inline bool igl::opengl2::TranslateWidget::is_down() const +{ + return m_down_type != DOWN_TYPE_NONE; +} + +inline void igl::opengl2::TranslateWidget::draw() const +{ + using namespace Eigen; + using namespace std; + glPushAttrib(GL_ENABLE_BIT | GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT | GL_LINE_BIT); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glLineWidth(2.0); + auto draw_axes = [&]() + { + glBegin(GL_LINES); + glColor3f(1,0,0); + glVertex3f(0,0,0); + glVertex3f(1,0,0); + glColor3f(0,1,0); + glVertex3f(0,0,0); + glVertex3f(0,1,0); + glColor3f(0,0,1); + glVertex3f(0,0,0); + glVertex3f(0,0,1); + glEnd(); + }; + auto draw_cube = [] + { + glBegin(GL_LINES); + glVertex3f(-1.0f, 1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glVertex3f(1.0f, -1.0f, 1.0f); + glVertex3f(1.0f, -1.0f, 1.0f); + glVertex3f(-1.0f, -1.0f, 1.0f); + glVertex3f(-1.0f, -1.0f, 1.0f); + glVertex3f(-1.0f, 1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, -1.0f); + glVertex3f(1.0f, 1.0f, -1.0f); + glVertex3f(1.0f, -1.0f, -1.0f); + glVertex3f(1.0f, -1.0f, -1.0f); + glVertex3f(1.0f, -1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, -1.0f); + glVertex3f(-1.0f, 1.0f, -1.0f); + glVertex3f(-1.0f, -1.0f, -1.0f); + glVertex3f(1.0f, -1.0f, -1.0f); + glVertex3f(-1.0f, -1.0f, -1.0f); + glVertex3f(-1.0f, 1.0f, -1.0f); + glVertex3f(-1.0f, 1.0f, -1.0f); + glVertex3f(-1.0f, 1.0f, 1.0f); + glVertex3f(-1.0f, -1.0f, 1.0f); + glVertex3f(-1.0f, -1.0f, -1.0f); + glEnd(); + }; + glPushMatrix(); + glTranslated( m_pos(0)+m_trans(0), m_pos(1)+m_trans(1), m_pos(2)+m_trans(2)); + + { + Vector3d off,ppos,ppos_off,pos_off; + project((m_pos+m_trans).eval(),ppos); + ppos_off = ppos; + ppos_off(0) += m_len; + unproject(ppos_off,pos_off); + const double r = (m_pos+m_trans-pos_off).norm(); + glScaled(r,r,r); + } + + draw_axes(); + glScaled(0.05,0.05,0.05); + if(m_selected_type == DOWN_TYPE_CENTER) + { + glColor3fv(MAYA_YELLOW.data()); + }else + { + glColor3fv(MAYA_GREY.data()); + } + draw_cube(); + glPopMatrix(); + glPopAttrib(); +} + +#endif diff --git a/src/igl/opengl2/draw_beach_ball.cpp b/src/igl/opengl2/draw_beach_ball.cpp new file mode 100644 index 0000000000..a661ce4ae5 --- /dev/null +++ b/src/igl/opengl2/draw_beach_ball.cpp @@ -0,0 +1,283 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "draw_beach_ball.h" +#include "gl.h" + +// I'm not sure why windows would need it this way: +// http://lists.cairographics.org/archives/cairo/2008-January/012722.html +#ifdef _MSC_VER +#define SAFE_INLINE __inline +#else +#define SAFE_INLINE inline +#endif + +#include +#include +#include + +// Most of this implementation comes from the AntTweakBar source code: +// TwMgr.cpp, TwMgr.h, TwColor.h, TwColor.cpp, TwOpenGL.h and TwOpenGL.cpp + +//////////////////////////////////////////////////////////////////////////// +// Begin Copied Straight from AntTweakBar +//////////////////////////////////////////////////////////////////////////// +enum EArrowParts { ARROW_CONE, ARROW_CONE_CAP, ARROW_CYL, ARROW_CYL_CAP }; + +template SAFE_INLINE const T& TClamp(const T& X, const T& Limit1, const T& Limit2) +{ + if( Limit1=Limit2) ? Limit2 : X ); + else + return (X<=Limit2) ? Limit2 : ( (X>=Limit1) ? Limit1 : X ); +} + +typedef unsigned int color32; +static SAFE_INLINE color32 Color32FromARGBi(int A, int R, int G, int B) +{ + return (((color32)TClamp(A, 0, 255))<<24) | (((color32)TClamp(R, 0, 255))<<16) | (((color32)TClamp(G, 0, 255))<<8) | ((color32)TClamp(B, 0, 255)); +} + +static SAFE_INLINE color32 Color32FromARGBf(float A, float R, float G, float B) +{ + return (((color32)TClamp(A*256.0f, 0.0f, 255.0f))<<24) | (((color32)TClamp(R*256.0f, 0.0f, 255.0f))<<16) | (((color32)TClamp(G*256.0f, 0.0f, 255.0f))<<8) | ((color32)TClamp(B*256.0f, 0.0f, 255.0f)); +} + +static SAFE_INLINE void Color32ToARGBi(color32 Color, int *A, int *R, int *G, int *B) +{ + if(A) *A = (Color>>24)&0xff; + if(R) *R = (Color>>16)&0xff; + if(G) *G = (Color>>8)&0xff; + if(B) *B = Color&0xff; +} + +static SAFE_INLINE void Color32ToARGBf(color32 Color, float *A, float *R, float *G, float *B) +{ + if(A) *A = (1.0f/255.0f)*float((Color>>24)&0xff); + if(R) *R = (1.0f/255.0f)*float((Color>>16)&0xff); + if(G) *G = (1.0f/255.0f)*float((Color>>8)&0xff); + if(B) *B = (1.0f/255.0f)*float(Color&0xff); +} + +static color32 ColorBlend(color32 Color1, color32 Color2, float S) +{ + float a1, r1, g1, b1, a2, r2, g2, b2; + Color32ToARGBf(Color1, &a1, &r1, &g1, &b1); + Color32ToARGBf(Color2, &a2, &r2, &g2, &b2); + float t = 1.0f-S; + return Color32FromARGBf(t*a1+S*a2, t*r1+S*r2, t*g1+S*g2, t*b1+S*b2); +} + +static std::vector s_SphTri; +static std::vector s_SphCol; +static void CreateSphere() +{ + const int SUBDIV = 7; + s_SphTri.clear(); + s_SphCol.clear(); + + const float A[8*3] = { 1,0,0, 0,0,-1, -1,0,0, 0,0,1, 0,0,1, 1,0,0, 0,0,-1, -1,0,0 }; + const float B[8*3] = { 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,-1,0, 0,-1,0, 0,-1,0, 0,-1,0 }; + const float C[8*3] = { 0,0,1, 1,0,0, 0,0,-1, -1,0,0, 1,0,0, 0,0,-1, -1,0,0, 0,0,1 }; + //const color32 COL_A[8] = { 0xffff8080, 0xff000080, 0xff800000, 0xff8080ff, 0xff8080ff, 0xffff8080, 0xff000080, 0xff800000 }; + //const color32 COL_B[8] = { 0xff80ff80, 0xff80ff80, 0xff80ff80, 0xff80ff80, 0xff008000, 0xff008000, 0xff008000, 0xff008000 }; + //const color32 COL_C[8] = { 0xff8080ff, 0xffff8080, 0xff000080, 0xff800000, 0xffff8080, 0xff000080, 0xff800000, 0xff8080ff }; + const color32 COL_A[8] = { 0xffffffff, 0xffffff40, 0xff40ff40, 0xff40ffff, 0xffff40ff, 0xffff4040, 0xff404040, 0xff4040ff }; + const color32 COL_B[8] = { 0xffffffff, 0xffffff40, 0xff40ff40, 0xff40ffff, 0xffff40ff, 0xffff4040, 0xff404040, 0xff4040ff }; + const color32 COL_C[8] = { 0xffffffff, 0xffffff40, 0xff40ff40, 0xff40ffff, 0xffff40ff, 0xffff4040, 0xff404040, 0xff4040ff }; + + int i, j, k, l; + float xa, ya, za, xb, yb, zb, xc, yc, zc, x, y, z, norm, u[3], v[3]; + color32 col; + for( i=0; i<8; ++i ) + { + xa = A[3*i+0]; ya = A[3*i+1]; za = A[3*i+2]; + xb = B[3*i+0]; yb = B[3*i+1]; zb = B[3*i+2]; + xc = C[3*i+0]; yc = C[3*i+1]; zc = C[3*i+2]; + for( j=0; j<=SUBDIV; ++j ) + for( k=0; k<=2*(SUBDIV-j); ++k ) + { + if( k%2==0 ) + { + u[0] = ((float)j)/(SUBDIV+1); + v[0] = ((float)(k/2))/(SUBDIV+1); + u[1] = ((float)(j+1))/(SUBDIV+1); + v[1] = ((float)(k/2))/(SUBDIV+1); + u[2] = ((float)j)/(SUBDIV+1); + v[2] = ((float)(k/2+1))/(SUBDIV+1); + } + else + { + u[0] = ((float)j)/(SUBDIV+1); + v[0] = ((float)(k/2+1))/(SUBDIV+1); + u[1] = ((float)(j+1))/(SUBDIV+1); + v[1] = ((float)(k/2))/(SUBDIV+1); + u[2] = ((float)(j+1))/(SUBDIV+1); + v[2] = ((float)(k/2+1))/(SUBDIV+1); + } + + for( l=0; l<3; ++l ) + { + x = (1.0f-u[l]-v[l])*xa + u[l]*xb + v[l]*xc; + y = (1.0f-u[l]-v[l])*ya + u[l]*yb + v[l]*yc; + z = (1.0f-u[l]-v[l])*za + u[l]*zb + v[l]*zc; + norm = sqrtf(x*x+y*y+z*z); + x /= norm; y /= norm; z /= norm; + s_SphTri.push_back(x); s_SphTri.push_back(y); s_SphTri.push_back(z); +static const float FLOAT_EPS = 1.0e-7f; + if( u[l]+v[l]>FLOAT_EPS ) + col = ColorBlend(COL_A[i], ColorBlend(COL_B[i], COL_C[i], v[l]/(u[l]+v[l])), u[l]+v[l]); + else + col = COL_A[i]; + //if( (j==0 && k==0) || (j==0 && k==2*SUBDIV) || (j==SUBDIV && k==0) ) + // col = 0xffff0000; + s_SphCol.push_back(col); + } + } + } + //s_SphTriProj.clear(); + //s_SphTriProj.resize(2*s_SphCol.size(), 0); + //s_SphColLight.clear(); + //s_SphColLight.resize(s_SphCol.size(), 0); +} + +static std::vector s_ArrowTri[4]; +static std::vector s_ArrowNorm[4]; +static void CreateArrow() +{ + const int SUBDIV = 15; + const float CYL_RADIUS = 0.08f; + const float CONE_RADIUS = 0.16f; + const float CONE_LENGTH = 0.25f; + const float ARROW_BGN = -1.1f; + const float ARROW_END = 1.15f; + int i; + for(i=0; i<4; ++i) + { + s_ArrowTri[i].clear(); + s_ArrowNorm[i].clear(); + } + + float x0, x1, y0, y1, z0, z1, a0, a1, nx, nn; + for(i=0; i>16), GLubyte(s_SphCol[i]>>8), GLubyte(s_SphCol[i]), GLubyte(s_SphCol[i]>>24)); + glVertex3fv(&s_SphTri[i*3]); + } + glEnd(); + glPopMatrix(); + + CreateArrow(); + for(int k = 0;k<3;k++) + { + glPushMatrix(); + glColor3f(k==0,k==1,k==2); + glRotatef((k==2?-1.0:1.0)*90,k==0,k==2,k==1); + glBegin(GL_TRIANGLES); + for(int j = 0;j<4;j++) + { + for(int i = 0;i<(int)s_ArrowTri[j].size();i+=3) + { + glNormal3fv(&s_ArrowNorm[j][i]); + glVertex3fv(&s_ArrowTri[j][i]); + } + } + glEnd(); + glPopMatrix(); + } + + (cm ? glEnable(GL_COLOR_MATERIAL):glDisable(GL_COLOR_MATERIAL)); +} diff --git a/src/igl/opengl2/draw_beach_ball.h b/src/igl/opengl2/draw_beach_ball.h new file mode 100644 index 0000000000..d430dc5ede --- /dev/null +++ b/src/igl/opengl2/draw_beach_ball.h @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_DRAW_BEACH_BALL_H +#define IGL_OPENGL2_DRAW_BEACH_BALL_H +#include "../igl_inline.h" + +namespace igl +{ + namespace opengl2 + { + // Draw a beach ball icon/glyph (from AntTweakBar) at the current origin + // according to the current orientation: ball has radius 0.75 and axis have + // length 1.15 + IGL_INLINE void draw_beach_ball(); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "draw_beach_ball.cpp" +#endif + +#endif diff --git a/src/igl/opengl2/draw_floor.cpp b/src/igl/opengl2/draw_floor.cpp new file mode 100644 index 0000000000..61fa474916 --- /dev/null +++ b/src/igl/opengl2/draw_floor.cpp @@ -0,0 +1,161 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "draw_floor.h" +#include "gl.h" + +IGL_INLINE void igl::opengl2::draw_floor(const float * colorA, const float * colorB, + const int GridSizeX, + const int GridSizeY) +{ + const float SizeX = 0.5f*100./(double)GridSizeX; + const float SizeY = 0.5f*100./(double)GridSizeY; + // old settings + int old_lighting=0,old_color_material=0; + glGetIntegerv(GL_LIGHTING,&old_lighting); + glGetIntegerv(GL_COLOR_MATERIAL,&old_color_material); + glDisable(GL_LIGHTING); + glColorMaterial( GL_FRONT, GL_EMISSION); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE); + // Set material + const float black[] = {0.,0.,0.,1.}; + glMaterialfv(GL_FRONT, GL_AMBIENT, black); + glMaterialfv(GL_FRONT, GL_DIFFUSE, black); + glMaterialfv(GL_FRONT, GL_SPECULAR, black); + glMaterialfv(GL_FRONT, GL_EMISSION, black); + glMaterialf(GL_FRONT, GL_SHININESS,0); + const bool use_lighting = false; + if(use_lighting) + { + glEnable(GL_LIGHTING); + }else + { + glDisable(GL_LIGHTING); + } + + + glBegin(GL_QUADS); + glNormal3f(0,1,0); + for (int x =-GridSizeX/2;x +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_DRAW_FLOOR_H +#define IGL_OPENGL2_DRAW_FLOOR_H +#include "../igl_inline.h" +namespace igl +{ + namespace opengl2 + { + + // Draw a checkerboard floor aligned with current (X,Z) plane using ../opengl/OpenGL_ + // calls. side=50 centered at (0,0): + // (-25,-25)-->(-25,25)-->(25,25)-->(25,-25) + // + // Use glPushMatrix(), glScaled(), glTranslated() to arrange the floor. + // + // Inputs: + // colorA float4 color + // colorB float4 color + // + // Example: + // // Draw a nice floor + // glPushMatrix(); + // glCullFace(GL_BACK); + // glEnable(GL_CULL_FACE); + // glEnable(GL_LIGHTING); + // glTranslated(0,-1,0); + // if(project(Vector3d(0,0,0))(2) - project(Vector3d(0,1,0))(2) > -FLOAT_EPS) + // { + // draw_floor_outline(); + // } + // draw_floor(); + // glPopMatrix(); + // glDisable(GL_CULL_FACE); + // + IGL_INLINE void draw_floor( + const float * colorA, + const float * colorB, + const int GridSizeX=100, + const int GridSizeY=100); + // Wrapper with default colors + IGL_INLINE void draw_floor(); + IGL_INLINE void draw_floor_outline( + const float * colorA, + const float * colorB, + const int GridSizeX=100, + const int GridSizeY=100); + // Wrapper with default colors + IGL_INLINE void draw_floor_outline(); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "draw_floor.cpp" +#endif +#endif diff --git a/src/igl/opengl2/draw_mesh.cpp b/src/igl/opengl2/draw_mesh.cpp new file mode 100644 index 0000000000..aeaed12f02 --- /dev/null +++ b/src/igl/opengl2/draw_mesh.cpp @@ -0,0 +1,278 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "draw_mesh.h" + +IGL_INLINE void igl::opengl2::draw_mesh( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N) +{ + using namespace Eigen; + MatrixXd _d; + MatrixXi _i; + return draw_mesh(V,F,N,_i,_d,_d,_i,_d,0,_i,0); +} + +IGL_INLINE void igl::opengl2::draw_mesh( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + const Eigen::MatrixXd & C) +{ + using namespace Eigen; + MatrixXd _d; + MatrixXi _i; + return draw_mesh(V,F,N,_i,C,_d,_i,_d,0,_i,0); +} + +IGL_INLINE void igl::opengl2::draw_mesh( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + const Eigen::MatrixXd & C, + const Eigen::MatrixXd & TC) +{ + using namespace Eigen; + MatrixXd _d; + MatrixXi _i; + return draw_mesh(V,F,N,_i,C,TC,_i,_d,0,_i,0); +} + +IGL_INLINE void igl::opengl2::draw_mesh( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + const Eigen::MatrixXd & C, + const Eigen::MatrixXd & TC, + const Eigen::MatrixXd & W, + const GLuint W_index, + const Eigen::MatrixXi & WI, + const GLuint WI_index) +{ + using namespace Eigen; + return draw_mesh(V,F,N,MatrixXi(),C,TC,MatrixXi(),W,W_index,WI,WI_index); +} + +IGL_INLINE void igl::opengl2::draw_mesh( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + const Eigen::MatrixXi & NF, + const Eigen::MatrixXd & C, + const Eigen::MatrixXd & TC, + const Eigen::MatrixXi & TF, + const Eigen::MatrixXd & W, + const GLuint W_index, + const Eigen::MatrixXi & WI, + const GLuint WI_index) +{ + using namespace std; + using namespace Eigen; + const int rF = F.rows(); + const int cF = F.cols(); + const int cC = C.cols(); + const int rC = C.rows(); + const int cW = W.cols(); + const int rW = W.rows(); + const int rV = V.rows(); + const int rTC = TC.rows(); + const int rTF = TF.rows(); + const int rNF = NF.rows(); + const int rN = N.rows(); + + if(F.size() > 0) + { + assert(F.maxCoeff() < V.rows()); + assert(V.cols() == 3); + assert(rC == rV || rC == rF || rC == rF*3 || rC==1 || C.size() == 0); + assert(C.cols() >= 3 || C.size() == 0); + assert(N.cols() == 3 || N.size() == 0); + assert(TC.cols() == 2 || TC.size() == 0); + assert(cF == 3 || cF == 4); + assert(TF.size() == 0 || TF.cols() == F.cols()); + assert(NF.size() == 0 || NF.cols() == NF.cols()); + } + if(W.size()>0) + { + assert(W.rows() == V.rows()); + assert(WI.rows() == V.rows()); + assert(W.cols() == WI.cols()); + } + + switch(F.cols()) + { + default: + case 3: + glBegin(GL_TRIANGLES); + break; + case 4: + glBegin(GL_QUADS); + break; + } + // loop over faces + for(int i = 0; i color; + if(rC == 1) + { + color = C.row(0); + }else if(rC == rV) + { + color = C.row(F(i,j)); + }else if(rC == rF*cF) + { + color = C.row(i*cF+j); + }else if(rC == rF) + { + color = C.row(i); + }else + { + assert(C.size() == 0); + } + + int n = -1; + if(rNF != 0) + { + n = NF(i,j); // indexed normals + } else if(rN == 1) + { + n = 0; // uniform normals + }else if(rN == rF) + { + n = i; // face normals + }else if(rN == rV) + { + n = F(i,j); // vertex normals + }else if(rN == rF*cF) + { + n = i*cF + j; // corner normals + }else + { + assert(N.size() == 0); + } + + { + if(rW>0 && W_index !=0 && WI_index != 0) + { + int weights_left = cW; + while(weights_left != 0) + { + int pass_size = std::min(4,weights_left); + int weights_already_passed = cW-weights_left; + // Get attribute location of next 4 weights + int pass_W_index = W_index + weights_already_passed/4; + int pass_WI_index = WI_index + weights_already_passed/4; + switch(pass_size) + { + case 1: + glVertexAttrib1d( + pass_W_index, + W(F(i,j),0+weights_already_passed)); + glVertexAttrib1d( + pass_WI_index, + WI(F(i,j),0+weights_already_passed)); + break; + case 2: + glVertexAttrib2d( + pass_W_index, + W(F(i,j),0+weights_already_passed), + W(F(i,j),1+weights_already_passed)); + glVertexAttrib2d( + pass_WI_index, + WI(F(i,j),0+weights_already_passed), + WI(F(i,j),1+weights_already_passed)); + break; + case 3: + glVertexAttrib3d( + pass_W_index, + W(F(i,j),0+weights_already_passed), + W(F(i,j),1+weights_already_passed), + W(F(i,j),2+weights_already_passed)); + glVertexAttrib3d( + pass_WI_index, + WI(F(i,j),0+weights_already_passed), + WI(F(i,j),1+weights_already_passed), + WI(F(i,j),2+weights_already_passed)); + break; + default: + glVertexAttrib4d( + pass_W_index, + W(F(i,j),0+weights_already_passed), + W(F(i,j),1+weights_already_passed), + W(F(i,j),2+weights_already_passed), + W(F(i,j),3+weights_already_passed)); + glVertexAttrib4d( + pass_WI_index, + WI(F(i,j),0+weights_already_passed), + WI(F(i,j),1+weights_already_passed), + WI(F(i,j),2+weights_already_passed), + WI(F(i,j),3+weights_already_passed)); + break; + } + weights_left -= pass_size; + } + } + if(tc != -1) + { + glTexCoord2d(TC(tc,0),TC(tc,1)); + } + if(rC>0) + { + switch(cC) + { + case 3: + glColor3dv(color.data()); + break; + case 4: + glColor4dv(color.data()); + break; + default: + break; + } + } + if(n != -1) + { + glNormal3d(N(n,0),N(n,1),N(n,2)); + } + glVertex3d(V(F(i,j),0),V(F(i,j),1),V(F(i,j),2)); + } + } + } + glEnd(); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif + diff --git a/src/igl/opengl2/draw_mesh.h b/src/igl/opengl2/draw_mesh.h new file mode 100644 index 0000000000..ea1559c20e --- /dev/null +++ b/src/igl/opengl2/draw_mesh.h @@ -0,0 +1,122 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_DRAW_MESH_H +#define IGL_OPENGL2_DRAW_MESH_H +#include "../igl_inline.h" +#include "gl.h" +#include + + +namespace igl +{ + namespace opengl2 + { + + // Draw ../opengl/OpenGL_ commands needed to display a mesh with normals + // + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3|4 eigen Matrix of face (triangle/quad) indices + // N #V|#F by 3 eigen Matrix of 3D normals + IGL_INLINE void draw_mesh( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N); + + // Draw ../opengl/OpenGL_ commands needed to display a mesh with normals and per-vertex + // colors + // + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3|4 eigen Matrix of face (triangle/quad) indices + // N #V|#F by 3 eigen Matrix of 3D normals + // C #V|#F|1 by 3 eigen Matrix of RGB colors + IGL_INLINE void draw_mesh( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + const Eigen::MatrixXd & C); + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3|4 eigen Matrix of face (triangle/quad) indices + // N #V|#F by 3 eigen Matrix of 3D normals + // C #V|#F|1 by 3 eigen Matrix of RGB colors + // TC #V|#F|1 by 3 eigen Matrix of Texture Coordinates + IGL_INLINE void draw_mesh( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + const Eigen::MatrixXd & C, + const Eigen::MatrixXd & TC); + + // Draw ../opengl/OpenGL_ commands needed to display a mesh with normals, per-vertex + // colors and LBS weights + // + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3|4 eigen Matrix of face (triangle/quad) indices + // N #V by 3 eigen Matrix of mesh vertex 3D normals + // C #V by 3 eigen Matrix of mesh vertex RGB colors + // TC #V by 3 eigen Matrix of mesh vertex UC coorindates between 0 and 1 + // W #V by #H eigen Matrix of per mesh vertex, per handle weights + // W_index Specifies the index of the "weight" vertex attribute: see + // glBindAttribLocation, if W_index is 0 then weights are ignored + // WI #V by #H eigen Matrix of per mesh vertex, per handle weight ids + // WI_index Specifies the index of the "weight" vertex attribute: see + // glBindAttribLocation, if WI_index is 0 then weight indices are ignored + IGL_INLINE void draw_mesh( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + const Eigen::MatrixXd & C, + const Eigen::MatrixXd & TC, + const Eigen::MatrixXd & W, + const GLuint W_index, + const Eigen::MatrixXi & WI, + const GLuint WI_index); + + // Draw ../opengl/OpenGL_ commands needed to display a mesh with normals, per-vertex + // colors and LBS weights + // + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3|4 eigen Matrix of face (triangle/quad) indices + // N #V by 3 eigen Matrix of mesh vertex 3D normals + // NF #F by 3 eigen Matrix of face (triangle/quad) normal indices, <0 + // means no normal + // C #V by 3 eigen Matrix of mesh vertex RGB colors + // TC #V by 3 eigen Matrix of mesh vertex UC coorindates between 0 and 1 + // TF #F by 3 eigen Matrix of face (triangle/quad) texture indices, <0 + // means no texture + // W #V by #H eigen Matrix of per mesh vertex, per handle weights + // W_index Specifies the index of the "weight" vertex attribute: see + // glBindAttribLocation, if W_index is 0 then weights are ignored + // WI #V by #H eigen Matrix of per mesh vertex, per handle weight ids + // WI_index Specifies the index of the "weight" vertex attribute: see + // glBindAttribLocation, if WI_index is 0 then weight indices are ignored + IGL_INLINE void draw_mesh( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & N, + const Eigen::MatrixXi & NF, + const Eigen::MatrixXd & C, + const Eigen::MatrixXd & TC, + const Eigen::MatrixXi & TF, + const Eigen::MatrixXd & W, + const GLuint W_index, + const Eigen::MatrixXi & WI, + const GLuint WI_index); + + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "draw_mesh.cpp" +#endif + +#endif diff --git a/src/igl/opengl2/draw_point.cpp b/src/igl/opengl2/draw_point.cpp new file mode 100644 index 0000000000..910fcc716e --- /dev/null +++ b/src/igl/opengl2/draw_point.cpp @@ -0,0 +1,100 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "draw_point.h" + +// Implementation +#include "gl.h" + +#include +#include + +IGL_INLINE void igl::opengl2::draw_point( + const double x, + const double y, + const double z, + const double requested_r, + const bool selected) +{ + // Push GL settings + glPushAttrib(GL_ENABLE_BIT | GL_LIGHTING_BIT); + + float f; + glGetFloatv(GL_POINT_SIZE_MAX,&f); + // THIS IS OVERZEALOUS on Mac OS X: OpenGL reports a smaller point size than + // possible. + //assert(requested_r<=0.5*f); + double r = (requested_r<0.5*f?requested_r:0.5*f); + + //glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + // get current color + float color[4]; + glGetFloatv(GL_CURRENT_COLOR,color); + + double outline_size = (r>7 ? sqrt(r/7.0) : 1.0); + + // White outline + glColor4f(1,1,1,color[3]); + glPointSize(2*r); + glBegin(GL_POINTS); + glVertex3d(x,y,z); + glEnd(); + // Black outline + glColor4f(0,0,0,color[3]); + glPointSize(2*r-2*outline_size); + glBegin(GL_POINTS); + glVertex3d(x,y,z); + glEnd(); + + // Foreground + glColor4fv(color); + glPointSize(2*r-4*outline_size); + glBegin(GL_POINTS); + glVertex3d(x,y,z); + glEnd(); + // Selection inner circle + if(selected) + { + glColor4f(0,0,0,color[3]); + double selected_size = 2*r-7*outline_size; + selected_size = (selected_size>3?selected_size:3); + glPointSize(selected_size); + glBegin(GL_POINTS); + glVertex3d(x,y,z); + glEnd(); + } + + // reset color + glColor4fv(color); + + // Pop GL settings + glPopAttrib(); +} + +template +IGL_INLINE void igl::opengl2::draw_point( + const Eigen::PlainObjectBase & P, + const double requested_r, + const bool selected) +{ + switch(P.size()) + { + case 2: + return draw_point(P(0),P(1),0,requested_r,selected); + default: + return draw_point(P(0),P(1),P(2),requested_r,selected); + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::opengl2::draw_point >(Eigen::PlainObjectBase > const&, double, bool); +template void igl::opengl2::draw_point >(Eigen::PlainObjectBase > const&, double, bool); +#endif + diff --git a/src/igl/opengl2/draw_point.h b/src/igl/opengl2/draw_point.h new file mode 100644 index 0000000000..2c7cc1c7df --- /dev/null +++ b/src/igl/opengl2/draw_point.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_DRAW_POINT_H +#define IGL_OPENGL2_DRAW_POINT_H +#include "../igl_inline.h" +#include +namespace igl +{ + namespace opengl2 + { + + //double POINT_COLOR[3] = {239./255.,213./255.,46./255.}; + // Draw a nice looking, colored dot at a given point in 3d. + // + // Note: expects that GL_CURRENT_COLOR is set with the desired foreground color + // + // Inputs: + // x x-coordinate of point, modelview coordinates + // y y-coordinate of point, modelview coordinates + // z z-coordinate of point, modelview coordinates + // requested_r outer-most radius of dot {7}, measured in screen space pixels + // selected fills inner circle with black {false} + // Asserts that requested_r does not exceed 0.5*GL_POINT_SIZE_MAX + IGL_INLINE void draw_point( + const double x, + const double y, + const double z, + const double requested_r = 7, + const bool selected = false); + template + IGL_INLINE void draw_point( + const Eigen::PlainObjectBase & P, + const double requested_r = 7, + const bool selected = false); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "draw_point.cpp" +#endif + +#endif diff --git a/src/igl/opengl2/draw_rectangular_marquee.cpp b/src/igl/opengl2/draw_rectangular_marquee.cpp new file mode 100644 index 0000000000..73f97c8442 --- /dev/null +++ b/src/igl/opengl2/draw_rectangular_marquee.cpp @@ -0,0 +1,62 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + + +#include "draw_rectangular_marquee.h" +#include "gl.h" +#include "glu.h" +#include "../material_colors.h" + +IGL_INLINE void igl::opengl2::draw_rectangular_marquee( + const int from_x, + const int from_y, + const int to_x, + const int to_y) +{ + using namespace std; + int l; + glGetIntegerv(GL_LIGHTING,&l); + int s; + glGetIntegerv(GL_LINE_STIPPLE,&s); + double lw; + glGetDoublev(GL_LINE_WIDTH,&lw); + glDisable(GL_LIGHTING); + // Screen space for this viewport + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT,viewport); + const int width = viewport[2]; + const int height = viewport[3]; + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D(0,width,0,height); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glEnable(GL_LINE_STIPPLE); + glLineStipple(3,0xAAAA); + glLineWidth(1); + glColor4f(0.2,0.2,0.2,1); + glBegin(GL_LINE_STRIP); + glVertex2d(from_x,from_y); + glVertex2d(to_x,from_y); + glVertex2d(to_x,to_y); + glVertex2d(from_x,to_y); + glVertex2d(from_x,from_y); + glEnd(); + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glLineWidth(lw); + (s ? glEnable(GL_LINE_STIPPLE):glDisable(GL_LINE_STIPPLE)); + (l ? glEnable(GL_LIGHTING):glDisable(GL_LIGHTING)); +} + diff --git a/src/igl/opengl2/draw_rectangular_marquee.h b/src/igl/opengl2/draw_rectangular_marquee.h new file mode 100644 index 0000000000..bbb2e3f18a --- /dev/null +++ b/src/igl/opengl2/draw_rectangular_marquee.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_DRAW_RECTANGULAR_MARQUEE_H +#define IGL_OPENGL2_DRAW_RECTANGULAR_MARQUEE_H +#include "../igl_inline.h" +namespace igl +{ + namespace opengl2 + { + // Draw a rectangular marquee (selection box) in screen space. This sets up + // screen space using current viewport. + // + // Inputs: + // from_x x coordinate of from point + // from_y y coordinate of from point + // to_x x coordinate of to point + // to_y y coordinate of to point + IGL_INLINE void draw_rectangular_marquee( + const int from_x, + const int from_y, + const int to_x, + const int to_y); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "draw_rectangular_marquee.cpp" +#endif + +#endif diff --git a/src/igl/opengl2/draw_skeleton_3d.cpp b/src/igl/opengl2/draw_skeleton_3d.cpp new file mode 100644 index 0000000000..a0ca6139ab --- /dev/null +++ b/src/igl/opengl2/draw_skeleton_3d.cpp @@ -0,0 +1,166 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "draw_skeleton_3d.h" +#include "../PI.h" +#include "../material_colors.h" +#include "gl.h" +#include +#include + + +template < + typename DerivedC, + typename DerivedBE, + typename DerivedT, + typename Derivedcolor> +IGL_INLINE void igl::opengl2::draw_skeleton_3d( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & BE, + const Eigen::PlainObjectBase & T, + const Eigen::PlainObjectBase & color, + const double half_bbd) +{ + // Note: Maya's skeleton *does* scale with the mesh suggesting a scale + // parameter. Further, its joint balls are not rotated with the bones. + using namespace Eigen; + using namespace std; + if(color.size() == 0) + { + return draw_skeleton_3d(C,BE,T,MAYA_SEA_GREEN,half_bbd); + } + assert(color.cols() == 4 || color.size() == 4); + if(T.size() == 0) + { + typedef Eigen::Matrix Mat; + Mat I = Mat::Identity(C.cols()+1,C.cols()); + Mat T = I.replicate(BE.rows(),1); + // insane base case + if(T.size() == 0) + { + return; + } + return draw_skeleton_3d(C,BE,T,color,half_bbd); + } + assert(T.rows() == BE.rows()*(C.cols()+1)); + assert(T.cols() == C.cols()); + // push old settings + glPushAttrib(GL_LIGHTING_BIT); + glPushAttrib(GL_LINE_BIT); + glDisable(GL_LIGHTING); + glLineWidth(1.0); + + auto draw_sphere = [](const double r) + { + auto draw_circle = []() + { + glBegin(GL_LINE_STRIP); + for(double th = 0;th<2.0*igl::PI;th+=(2.0*igl::PI/30.0)) + { + glVertex3d(cos(th),sin(th),0.0); + } + glVertex3d(cos(0),sin(0),0.0); + glEnd(); + }; + glPushMatrix(); + glScaled(r,r,r); + draw_circle(); + glRotated(90.0,1.0,0.0,0.0); + draw_circle(); + glRotated(90.0,0.0,1.0,0.0); + draw_circle(); + glPopMatrix(); + }; + auto draw_pyramid = []() + { + glBegin(GL_LINE_STRIP); + glVertex3d(0, 1,-1); + glVertex3d(0,-1,-1); + glVertex3d(0,-1, 1); + glVertex3d(0, 1, 1); + glVertex3d(0, 1,-1); + glEnd(); + glBegin(GL_LINES); + glVertex3d(0, 1,-1); + glVertex3d(1,0,0); + glVertex3d(0,-1,-1); + glVertex3d(1,0,0); + glVertex3d(0,-1, 1); + glVertex3d(1,0,0); + glVertex3d(0, 1, 1); + glVertex3d(1,0,0); + glEnd(); + }; + + // Loop over bones + for(int e = 0;e < BE.rows();e++) + { + // Draw a sphere + auto s = C.row(BE(e,0)); + auto d = C.row(BE(e,1)); + auto b = (d-s).transpose().eval(); + double r = 0.02*half_bbd; + Matrix4d Te = Matrix4d::Identity(); + Te.block(0,0,3,4) = T.block(e*4,0,4,3).transpose(); + Quaterniond q; + q.setFromTwoVectors(Vector3d(1,0,0),b); + glPushMatrix(); + glMultMatrixd(Te.data()); + glTranslated(s(0),s(1),s(2)); + if(color.size() == 4) + { + glColor4d( color(0), color(1), color(2), color(3)); + }else + { + glColor4d( color(e,0), color(e,1), color(e,2), color(e,3)); + } + draw_sphere(r); + const double len = b.norm()-2.*r; + if(len>=0) + { + auto u = b.normalized()*r; + glPushMatrix(); + glTranslated(u(0),u(1),u(2)); + glMultMatrixd(Affine3d(q).matrix().data()); + glScaled(b.norm()-2.*r,r,r); + draw_pyramid(); + glPopMatrix(); + } + glTranslated(b(0),b(1),b(2)); + draw_sphere(r); + glPopMatrix(); + } + // Reset settings + glPopAttrib(); + glPopAttrib(); +} + +template +IGL_INLINE void igl::opengl2::draw_skeleton_3d( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & BE, + const Eigen::PlainObjectBase & T) +{ + return draw_skeleton_3d(C,BE,T,MAYA_SEA_GREEN); +} + +template +IGL_INLINE void igl::opengl2::draw_skeleton_3d( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & BE) +{ + return draw_skeleton_3d(C,BE,Eigen::MatrixXd(),MAYA_SEA_GREEN); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::opengl2::draw_skeleton_3d, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template void igl::opengl2::draw_skeleton_3d, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template void igl::opengl2::draw_skeleton_3d, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, double); +template void igl::opengl2::draw_skeleton_3d, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, double); +#endif diff --git a/src/igl/opengl2/draw_skeleton_3d.h b/src/igl/opengl2/draw_skeleton_3d.h new file mode 100644 index 0000000000..7bbb59717a --- /dev/null +++ b/src/igl/opengl2/draw_skeleton_3d.h @@ -0,0 +1,53 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_DRAW_SKELETON_3D_H +#define IGL_OPENGL2_DRAW_SKELETON_3D_H +#include "../igl_inline.h" +#include "../material_colors.h" +#include +namespace igl +{ + namespace opengl2 + { + + // Draw a skeleton + // + // Inputs: + // C #C by dim List of joint rest positions + // BE #BE by 2 list of bone edge indices into C + // T #BE*(dim+1) by dim matrix of stacked transposed bone transformations + // color #BE|1 by 4 list of color + // half_bbd half bounding box diagonal to determine scaling {1.0} + template < + typename DerivedC, + typename DerivedBE, + typename DerivedT, + typename Derivedcolor> + IGL_INLINE void draw_skeleton_3d( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & BE, + const Eigen::PlainObjectBase & T, + const Eigen::PlainObjectBase & color, + const double half_bbd=0.5); + // Default color + template + IGL_INLINE void draw_skeleton_3d( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & BE, + const Eigen::PlainObjectBase & T); + template + IGL_INLINE void draw_skeleton_3d( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & BE); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "draw_skeleton_3d.cpp" +#endif +#endif diff --git a/src/igl/opengl2/draw_skeleton_vector_graphics.cpp b/src/igl/opengl2/draw_skeleton_vector_graphics.cpp new file mode 100644 index 0000000000..e45706120c --- /dev/null +++ b/src/igl/opengl2/draw_skeleton_vector_graphics.cpp @@ -0,0 +1,122 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "draw_skeleton_vector_graphics.h" +#include "draw_point.h" +#include "gl.h" +#include "../material_colors.h" + +IGL_INLINE void igl::opengl2::draw_skeleton_vector_graphics( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE) +{ + return draw_skeleton_vector_graphics(C,BE,BBW_POINT_COLOR,BBW_LINE_COLOR); +} + +IGL_INLINE void igl::opengl2::draw_skeleton_vector_graphics( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const float * point_color, + const float * line_color) +{ + using namespace Eigen; + + int old_lighting=0; + double old_line_width=1; + glGetIntegerv(GL_LIGHTING,&old_lighting); + glGetDoublev(GL_LINE_WIDTH,&old_line_width); + int cm; + glGetIntegerv(GL_COLOR_MATERIAL,&cm); + glDisable(GL_LIGHTING); + glDisable(GL_LINE_STIPPLE); + //glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_COLOR_MATERIAL); + glLineWidth(10.0); + glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE); + float mat_ambient[4] = {0.1,0.1,0.1,1.0}; + float mat_specular[4] = {0.0,0.0,0.0,1.0}; + float mat_shininess = 1; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular); + glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess); + + for(int i = 0;i<3;i++) + { + switch(i) + { + case 0: glColor3fv(WHITE); glLineWidth(10); break; + case 1: glColor3fv(BLACK); glLineWidth( 6); break; + case 2: glColor3fv(line_color); glLineWidth( 4); break; + } + // Loop over bone edges + glBegin(GL_LINES); + for(int e = 0;e +IGL_INLINE void igl::opengl2::draw_skeleton_vector_graphics( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & BE, + const Eigen::PlainObjectBase & T) +{ + return draw_skeleton_vector_graphics(C,BE,T,BBW_POINT_COLOR,BBW_LINE_COLOR); +} + +template +IGL_INLINE void igl::opengl2::draw_skeleton_vector_graphics( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & BE, + const Eigen::PlainObjectBase & T, + const float * point_color, + const float * line_color) +{ + DerivedC CT; + DerivedBE BET; + const int dim = T.cols(); + assert(dim == C.cols()); + CT.resize(2*BE.rows(),C.cols()); + BET.resize(BE.rows(),2); + for(int e = 0;e, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +#endif diff --git a/src/igl/opengl2/draw_skeleton_vector_graphics.h b/src/igl/opengl2/draw_skeleton_vector_graphics.h new file mode 100644 index 0000000000..8091cbb784 --- /dev/null +++ b/src/igl/opengl2/draw_skeleton_vector_graphics.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_DRAW_SKELETON_VECTOR_GRAPHICS_H +#define IGL_OPENGL2_DRAW_SKELETON_VECTOR_GRAPHICS_H +#include "../igl_inline.h" +#include + +namespace igl +{ + namespace opengl2 + { + // Draw a skeleton with a 2D vector graphcis style à la BBW, STBS, Monotonic, + // FAST papers. + // + // Inputs: + // C #C by dim list of joint positions + // BE #BE by 2 list of bone edge indices into C + // point_color color of points + // line_color color of lines + IGL_INLINE void draw_skeleton_vector_graphics( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const float * point_color, + const float * line_color); + // Use default colors (originally from BBW paper) + IGL_INLINE void draw_skeleton_vector_graphics( + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE); + // T #BE*(dim+1) by dim matrix of stacked transposed bone transformations + template + IGL_INLINE void draw_skeleton_vector_graphics( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & BE, + const Eigen::PlainObjectBase & T, + const float * point_color, + const float * line_color); + template + IGL_INLINE void draw_skeleton_vector_graphics( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & BE, + const Eigen::PlainObjectBase & T); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "draw_skeleton_vector_graphics.cpp" +#endif +#endif diff --git a/src/igl/opengl2/flare_textures.h b/src/igl/opengl2/flare_textures.h new file mode 100644 index 0000000000..0455195ebe --- /dev/null +++ b/src/igl/opengl2/flare_textures.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_FLARE_TEXTURES_H +#define IGL_OPENGL2_FLARE_TEXTURES_H +#include +namespace igl +{ + namespace opengl2 + { + + const uint8_t FLARE_TEXTURE_0[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,12,12,12,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,12,12,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,15,15,15,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,15,15,12,12,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,15,20,20,20,20,20,20,20,20,23,23,23,23,23,23,23,23,23,23,23,23,23,23,20,20,20,20,20,20,20,20,15,15,12,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,15,20,20,20,20,20,20,23,23,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,23,23,23,20,20,20,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,20,20,23,23,28,28,28,28,28,28,28,31,31,31,31,31,31,31,31,31,31,31,31,31,31,28,28,28,28,28,28,28,28,23,23,20,20,20,20,15,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,20,23,28,28,28,28,28,28,31,31,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,31,31,31,28,28,28,28,28,23,23,20,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,15,20,20,20,23,23,28,28,28,28,31,31,36,36,36,36,36,36,36,36,39,39,39,39,39,39,39,39,39,39,39,39,39,36,36,36,36,36,36,36,36,31,28,28,28,28,28,23,20,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,20,23,28,28,28,28,31,31,36,36,36,36,36,39,39,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,39,39,36,36,36,36,36,36,31,28,28,28,28,23,20,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,20,23,28,28,28,28,31,36,36,36,36,39,39,44,44,44,44,44,44,44,44,47,47,47,47,47,47,47,47,47,47,47,47,47,44,44,44,44,44,44,44,39,39,36,36,36,36,31,31,28,28,28,23,23,20,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,20,23,28,28,28,31,31,36,36,36,36,39,44,44,44,44,44,47,47,47,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,47,47,44,44,44,44,44,39,39,36,36,36,36,31,28,28,28,23,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,23,28,28,28,28,31,36,36,36,39,44,44,44,44,44,47,47,52,52,52,52,52,52,52,55,55,55,55,55,55,55,55,55,55,55,55,55,52,52,52,52,52,52,52,47,47,44,44,44,44,39,36,36,36,36,31,28,28,28,23,20,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,20,23,28,28,28,31,36,36,36,39,44,44,44,44,47,52,52,52,52,52,52,55,55,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,55,55,52,52,52,52,52,47,47,44,44,44,39,39,36,36,36,31,28,28,28,23,20,20,15,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,23,28,28,28,31,36,36,36,39,44,44,44,44,47,52,52,52,52,55,55,60,60,60,60,60,60,60,63,63,63,63,63,63,63,63,63,63,63,63,60,60,60,60,60,60,60,60,55,52,52,52,52,52,47,44,44,44,39,36,36,36,31,28,28,28,23,20,20,20,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,15,20,20,23,28,28,28,36,36,36,39,44,44,44,47,47,52,52,52,55,55,60,60,60,60,60,63,63,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,63,63,63,60,60,60,60,60,55,52,52,52,52,47,44,44,44,39,36,36,36,31,28,28,28,20,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,20,20,20,23,28,28,31,36,36,36,39,44,44,44,47,52,52,52,55,60,60,60,60,63,63,68,68,68,68,68,68,68,71,71,71,71,71,71,71,71,71,71,71,71,68,68,68,68,68,68,68,63,63,60,60,60,60,55,52,52,52,52,47,44,44,44,39,36,36,36,28,28,28,23,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,20,28,28,28,31,36,36,39,44,44,44,47,52,52,52,55,60,60,60,60,63,68,68,68,68,68,71,71,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,71,71,68,68,68,68,68,63,63,60,60,60,55,55,52,52,52,47,44,44,39,36,36,36,31,28,28,23,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,23,28,28,31,36,36,36,39,44,44,47,52,52,52,55,60,60,60,63,63,68,68,68,68,71,76,76,76,76,76,76,76,79,79,79,79,79,79,79,79,79,79,79,79,76,76,76,76,76,76,76,71,71,68,68,68,68,63,60,60,60,55,52,52,52,47,44,44,44,39,36,36,31,28,28,23,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,23,28,28,31,36,36,39,44,44,44,47,52,52,55,60,60,60,63,63,68,68,68,71,71,76,76,76,76,79,79,79,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,79,79,76,76,76,76,76,71,68,68,68,68,63,60,60,60,55,52,52,52,47,44,44,39,36,36,31,28,28,23,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,23,28,28,31,36,36,39,44,44,47,52,52,52,55,60,60,60,63,68,68,68,71,76,76,76,76,79,79,84,84,84,84,84,84,84,87,87,87,87,87,87,87,87,87,87,87,84,84,84,84,84,84,84,79,76,76,76,76,71,71,68,68,68,63,60,60,60,55,52,52,47,44,44,44,36,36,36,28,28,28,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,23,28,28,31,36,36,39,44,44,47,52,52,55,60,60,60,63,68,68,68,71,76,76,76,76,79,84,84,84,84,84,87,87,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,87,87,84,84,84,84,84,79,76,76,76,71,71,68,68,68,60,60,60,55,52,52,52,44,44,44,36,36,36,28,28,28,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,20,20,23,28,28,31,36,36,39,44,44,47,52,52,55,60,60,63,68,68,68,71,76,76,76,79,79,84,84,84,84,87,92,92,92,92,92,92,92,95,95,95,95,95,95,95,95,95,95,95,92,92,92,92,92,92,87,87,84,84,84,84,79,76,76,76,71,68,68,68,63,60,60,60,52,52,52,47,44,44,39,36,36,28,28,28,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,20,20,23,28,28,31,36,36,39,44,44,47,52,52,55,60,60,63,68,68,68,71,76,76,79,79,84,84,84,87,92,92,92,92,92,95,95,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,95,95,92,92,92,92,92,87,84,84,84,84,79,76,76,76,71,68,68,68,60,60,60,55,52,52,47,44,44,39,36,36,28,28,23,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,28,28,31,36,36,39,44,44,52,52,52,60,60,60,63,68,68,71,76,76,76,79,84,84,84,87,92,92,92,92,95,100,100,100,100,100,100,100,103,103,103,103,103,103,103,103,103,103,100,100,100,100,100,100,100,95,95,92,92,92,87,87,84,84,84,79,76,76,71,68,68,68,63,60,60,55,52,52,47,44,44,39,36,36,28,28,23,20,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,28,28,31,36,36,39,44,44,52,52,52,60,60,60,68,68,68,71,76,76,79,84,84,84,87,92,92,92,95,95,100,100,100,100,103,103,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,103,103,100,100,100,100,100,95,92,92,92,87,84,84,84,79,76,76,76,71,68,68,63,60,60,55,52,52,47,44,44,39,36,36,28,28,23,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,20,23,28,31,36,36,39,44,44,52,52,52,60,60,63,68,68,68,76,76,76,79,84,84,87,92,92,92,95,95,100,100,100,103,103,108,108,108,108,108,108,111,111,111,111,111,111,111,111,111,111,108,108,108,108,108,108,108,103,100,100,100,100,95,92,92,92,87,84,84,84,79,76,76,71,68,68,63,60,60,55,52,52,47,44,44,36,36,31,28,28,23,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,20,20,23,28,28,36,36,39,44,44,47,52,52,60,60,63,68,68,71,76,76,79,84,84,84,87,92,92,92,95,100,100,100,103,108,108,108,108,108,111,111,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,111,111,108,108,108,108,103,103,100,100,100,95,92,92,92,87,84,84,79,76,76,71,68,68,63,60,60,55,52,52,47,44,44,36,36,31,28,28,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,23,28,28,31,36,39,44,44,47,52,52,60,60,63,68,68,71,76,76,79,84,84,87,92,92,92,95,100,100,100,103,108,108,108,108,111,116,116,116,116,116,116,119,119,119,119,119,119,119,119,119,119,116,116,116,116,116,116,111,111,108,108,108,103,103,100,100,100,95,92,92,87,84,84,79,76,76,71,68,68,63,60,60,55,52,52,44,44,39,36,36,31,28,23,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,20,28,28,31,36,36,44,44,47,52,52,60,60,63,68,68,71,76,76,79,84,84,87,92,92,92,100,100,100,103,108,108,108,111,111,116,116,116,116,119,119,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,119,119,116,116,116,116,111,108,108,108,103,100,100,100,95,92,92,87,84,84,84,76,76,76,68,68,63,60,60,55,52,52,44,44,39,36,36,28,28,23,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,20,20,23,28,28,36,36,39,44,47,52,52,55,60,60,68,68,71,76,76,79,84,84,87,92,92,95,100,100,100,108,108,108,111,116,116,116,116,119,119,124,124,124,124,124,124,127,127,127,127,127,127,127,127,127,124,124,124,124,124,124,119,116,116,116,116,111,108,108,108,103,100,100,100,92,92,92,84,84,84,76,76,76,68,68,63,60,60,55,52,47,44,44,39,36,31,28,28,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,23,28,28,36,36,39,44,44,52,52,55,60,60,68,68,71,76,76,79,84,84,87,92,92,95,100,100,103,108,108,108,111,116,116,116,119,124,124,124,124,127,127,132,132,132,132,132,132,132,132,132,132,132,132,132,132,127,127,124,124,124,124,119,119,116,116,116,111,108,108,103,100,100,100,95,92,92,87,84,84,76,76,71,68,68,63,60,60,52,52,47,44,44,36,36,31,28,23,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,20,20,28,28,31,36,36,44,44,47,52,55,60,60,63,68,68,76,76,79,84,84,87,92,92,95,100,100,103,108,108,111,116,116,116,119,124,124,124,124,127,132,132,132,132,132,132,135,135,135,135,135,135,135,135,135,132,132,132,132,132,132,127,124,124,124,119,119,116,116,111,108,108,108,103,100,100,95,92,92,87,84,84,76,76,71,68,68,63,60,55,52,52,44,44,39,36,36,28,28,23,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,23,28,28,36,36,39,44,47,52,52,60,60,63,68,68,76,76,79,84,84,87,92,92,100,100,100,108,108,108,111,116,116,119,124,124,124,127,127,132,132,132,132,135,140,140,140,140,140,140,140,140,140,140,140,140,140,140,135,135,132,132,132,132,127,124,124,124,119,116,116,116,111,108,108,103,100,100,95,92,92,87,84,84,76,76,71,68,68,60,60,55,52,52,44,44,39,36,31,28,28,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,20,28,28,31,36,39,44,44,52,52,55,60,63,68,68,71,76,79,84,84,87,92,92,100,100,100,108,108,108,116,116,116,119,124,124,124,127,132,132,132,135,140,140,140,140,140,140,143,143,143,143,143,143,143,143,140,140,140,140,140,140,135,135,132,132,132,127,124,124,124,119,116,116,111,108,108,103,100,100,95,92,92,84,84,79,76,76,71,68,63,60,60,52,52,47,44,44,36,36,28,28,23,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,23,28,28,36,36,44,44,47,52,55,60,60,68,68,71,76,76,84,84,87,92,92,95,100,100,108,108,111,116,116,116,124,124,124,127,132,132,132,135,140,140,140,140,143,143,148,148,148,148,148,148,148,148,148,148,148,148,148,143,143,140,140,140,140,135,132,132,132,127,124,124,119,116,116,111,108,108,103,100,100,95,92,92,84,84,79,76,76,68,68,63,60,55,52,52,44,44,39,36,31,28,28,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,20,20,28,28,31,36,39,44,44,52,52,60,60,63,68,68,76,76,79,84,87,92,92,95,100,100,108,108,111,116,116,119,124,124,124,132,132,132,135,140,140,140,143,143,148,148,148,148,148,151,151,151,151,151,151,151,151,148,148,148,148,148,148,143,140,140,140,135,132,132,132,127,124,124,119,116,116,111,108,108,103,100,100,92,92,87,84,84,79,76,71,68,68,60,60,55,52,47,44,44,36,36,31,28,23,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,23,28,31,36,36,44,44,47,52,55,60,60,68,68,71,76,79,84,84,92,92,95,100,100,108,108,111,116,116,119,124,124,127,132,132,132,140,140,140,143,148,148,148,148,151,151,156,156,156,156,156,156,156,156,156,156,156,156,151,151,148,148,148,148,143,140,140,140,135,132,132,127,124,124,119,116,116,111,108,108,103,100,100,92,92,87,84,84,76,76,71,68,63,60,60,52,52,47,44,39,36,31,28,28,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,20,20,28,28,31,36,39,44,47,52,52,60,60,63,68,71,76,76,84,84,87,92,92,100,100,103,108,108,116,116,119,124,124,127,132,132,135,140,140,140,143,148,148,148,151,156,156,156,156,156,156,159,159,159,159,159,159,159,156,156,156,156,156,151,151,148,148,148,143,140,140,135,132,132,132,124,124,119,116,116,111,108,108,100,100,95,92,92,84,84,79,76,76,68,68,60,60,55,52,47,44,44,36,36,28,28,23,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,23,28,28,36,36,44,44,47,52,55,60,63,68,68,76,76,79,84,87,92,92,100,100,103,108,108,116,116,119,124,124,127,132,132,135,140,140,143,148,148,148,151,156,156,156,156,159,164,164,164,164,164,164,164,164,164,164,164,164,159,159,156,156,156,156,151,148,148,143,140,140,140,132,132,132,124,124,119,116,116,111,108,108,100,100,95,92,87,84,84,76,76,71,68,63,60,60,52,52,47,44,39,36,31,28,28,20,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,20,23,28,31,36,39,44,44,52,52,60,60,63,68,71,76,79,84,84,92,92,95,100,100,108,108,111,116,116,124,124,127,132,132,135,140,140,143,148,148,151,156,156,156,159,159,164,164,164,164,164,167,167,167,167,167,167,167,164,164,164,164,164,159,156,156,156,151,148,148,148,143,140,140,135,132,132,124,124,119,116,116,108,108,103,100,100,92,92,87,84,79,76,76,68,68,63,60,55,52,47,44,44,36,36,28,28,23,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,20,28,28,36,36,44,44,47,52,55,60,63,68,68,76,76,79,84,87,92,92,100,100,108,108,111,116,116,124,124,127,132,132,135,140,140,143,148,148,151,156,156,159,164,164,164,164,167,167,172,172,172,172,172,172,172,172,172,172,172,167,164,164,164,164,159,156,156,156,151,148,148,143,140,140,135,132,132,124,124,119,116,116,108,108,103,100,95,92,92,84,84,79,76,71,68,63,60,60,52,52,44,44,39,36,31,28,23,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,23,28,31,36,36,44,44,52,52,60,60,63,68,71,76,79,84,84,92,92,95,100,103,108,108,116,116,119,124,124,132,132,135,140,140,143,148,148,156,156,156,159,164,164,164,167,172,172,172,172,172,175,175,175,175,175,175,175,172,172,172,172,172,167,164,164,164,159,156,156,151,148,148,143,140,140,132,132,127,124,124,116,116,111,108,108,100,100,95,92,87,84,79,76,76,68,68,60,60,55,52,47,44,39,36,36,28,28,20,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,20,28,28,31,36,39,44,47,52,55,60,60,68,68,76,76,79,84,87,92,95,100,100,108,108,111,116,119,124,124,132,132,135,140,140,143,148,148,156,156,156,164,164,164,167,172,172,172,175,175,180,180,180,180,180,180,180,180,180,180,180,175,172,172,172,167,164,164,164,159,156,156,151,148,148,143,140,140,132,132,127,124,124,116,116,108,108,103,100,95,92,92,84,84,79,76,71,68,63,60,60,52,52,44,44,36,36,28,28,23,20,15,12,7,0,0,0,0,0,0,0,0,0,0,7,12,15,20,20,28,28,36,36,44,44,52,52,55,60,63,68,71,76,76,84,84,92,92,95,100,103,108,111,116,116,124,124,127,132,132,140,140,143,148,148,156,156,156,164,164,164,172,172,172,175,180,180,180,180,180,183,183,183,183,183,183,180,180,180,180,180,175,172,172,172,167,164,164,159,156,156,151,148,148,140,140,135,132,132,124,124,119,116,111,108,108,100,100,95,92,87,84,79,76,76,68,68,60,60,52,52,47,44,39,36,31,28,23,20,15,12,12,0,0,0,0,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,44,52,52,60,60,68,68,71,76,79,84,87,92,95,100,100,108,108,116,116,119,124,124,132,132,140,140,143,148,148,156,156,156,164,164,167,172,172,172,175,180,180,180,183,188,188,188,188,188,188,188,188,188,188,183,183,180,180,180,175,172,172,167,164,164,159,156,156,151,148,148,140,140,135,132,127,124,124,116,116,111,108,103,100,95,92,92,84,84,76,76,71,68,63,60,55,52,47,44,39,36,36,28,28,20,20,12,12,0,0,0,0,0,0,0,0,0,0,12,12,20,20,23,28,31,36,39,44,47,52,55,60,63,68,68,76,76,84,84,92,92,95,100,103,108,111,116,116,124,124,127,132,135,140,140,148,148,151,156,156,164,164,167,172,172,175,180,180,180,183,188,188,188,188,191,191,191,191,191,191,188,188,188,188,188,183,180,180,175,172,172,167,164,164,159,156,156,148,148,143,140,140,132,132,127,124,119,116,111,108,108,100,100,92,92,87,84,79,76,71,68,63,60,60,52,52,44,44,36,36,28,28,20,20,15,12,7,0,0,0,0,0,0,0,0,0,12,12,20,20,28,28,36,36,44,44,47,52,55,60,63,68,71,76,79,84,84,92,92,100,100,108,108,111,116,119,124,127,132,132,140,140,143,148,151,156,156,164,164,167,172,172,175,180,180,183,188,188,188,191,191,196,196,196,196,196,196,196,196,196,191,188,188,188,183,180,180,180,172,172,167,164,164,159,156,156,148,148,143,140,135,132,132,124,124,116,116,111,108,103,100,95,92,87,84,84,76,76,68,68,60,60,52,52,47,44,39,36,31,28,23,20,15,12,7,0,0,0,0,0,0,0,0,7,12,15,20,20,28,28,36,36,44,44,52,52,60,60,68,68,76,76,79,84,87,92,95,100,103,108,108,116,116,124,124,127,132,135,140,143,148,148,156,156,159,164,164,172,172,175,180,180,183,188,188,191,196,196,196,196,196,199,199,199,199,199,196,196,196,196,191,188,188,188,180,180,180,172,172,167,164,164,156,156,151,148,143,140,140,132,132,127,124,119,116,111,108,108,100,100,92,92,84,84,76,76,71,68,63,60,55,52,47,44,39,36,31,28,23,20,20,12,12,0,0,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,52,60,60,68,68,76,76,84,84,92,92,100,100,103,108,111,116,119,124,124,132,132,140,140,148,148,151,156,159,164,164,172,172,175,180,180,183,188,188,191,196,196,196,199,204,204,204,204,204,204,204,204,199,199,196,196,196,191,188,188,180,180,180,172,172,167,164,159,156,156,148,148,143,140,135,132,127,124,124,116,116,108,108,100,100,95,92,87,84,79,76,71,68,63,60,55,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,84,92,92,100,100,108,108,116,116,119,124,127,132,135,140,143,148,148,156,156,164,164,167,172,172,180,180,183,188,188,191,196,196,199,204,204,204,204,207,207,207,207,207,204,204,204,204,196,196,196,191,188,188,180,180,175,172,172,164,164,159,156,151,148,143,140,140,132,132,124,124,119,116,111,108,103,100,95,92,87,84,79,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,0,0,12,12,20,20,28,28,36,36,44,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,108,116,116,124,124,132,132,140,140,143,148,151,156,159,164,164,172,172,175,180,183,188,188,191,196,196,199,204,204,207,212,212,212,212,212,212,212,212,207,204,204,204,199,196,196,191,188,188,180,180,175,172,167,164,164,156,156,148,148,140,140,135,132,127,124,119,116,111,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,39,36,31,28,23,20,15,12,7,0,0,0,0,0,0,0,12,12,20,20,28,28,36,36,44,44,52,52,60,60,68,68,76,76,79,84,87,92,95,100,103,108,111,116,119,124,127,132,132,140,140,148,148,156,156,159,164,167,172,175,180,180,188,188,191,196,196,204,204,204,207,212,212,212,215,215,215,215,212,212,212,212,207,204,204,199,196,196,188,188,183,180,180,172,172,164,164,159,156,151,148,143,140,135,132,132,124,124,116,116,108,108,100,100,92,92,84,84,76,76,71,68,63,60,55,52,47,44,39,36,31,28,23,20,15,12,7,0,0,0,0,0,0,0,12,12,20,20,28,28,36,36,44,44,52,52,60,60,68,68,76,76,84,84,92,92,100,100,108,108,111,116,119,124,127,132,135,140,143,148,151,156,156,164,164,172,172,180,180,183,188,191,196,196,199,204,204,212,212,212,215,220,220,220,220,220,220,220,215,212,212,207,204,204,199,196,191,188,188,180,180,175,172,167,164,159,156,156,148,148,140,140,132,132,124,124,116,116,108,108,103,100,95,92,87,84,79,76,71,68,63,60,55,52,47,44,39,36,31,28,23,20,15,12,7,0,0,0,0,0,0,7,12,15,20,23,28,31,36,36,44,44,52,52,60,60,68,68,76,76,84,84,92,92,100,100,108,108,116,116,124,124,132,132,140,140,143,148,151,156,159,164,167,172,175,180,180,188,188,196,196,199,204,204,212,212,215,220,220,220,220,223,223,223,220,220,220,215,212,212,207,204,204,196,196,191,188,183,180,175,172,172,164,164,156,156,148,148,140,140,135,132,127,124,119,116,111,108,103,100,95,92,87,84,79,76,71,68,63,60,55,52,47,44,39,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,84,92,92,100,100,108,108,116,116,124,124,132,132,140,140,148,148,156,156,164,164,167,172,175,180,183,188,191,196,196,204,204,207,212,215,220,220,223,228,228,228,228,228,228,223,220,220,215,212,212,207,204,199,196,196,188,188,180,180,172,172,164,164,159,156,151,148,143,140,135,132,127,124,119,116,111,108,103,100,95,92,87,84,79,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,124,132,132,140,140,148,148,156,156,164,164,172,172,180,180,188,188,191,196,199,204,207,212,212,220,220,223,228,228,228,231,231,231,228,228,228,220,220,215,212,212,204,204,196,196,188,188,183,180,175,172,167,164,159,156,151,148,143,140,135,132,127,124,119,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,127,132,135,140,143,148,151,156,156,164,164,172,172,180,180,188,188,196,196,204,204,212,212,215,220,223,228,228,231,236,236,236,236,236,228,228,223,220,220,212,212,207,204,199,196,191,188,183,180,175,172,167,164,159,156,156,148,148,140,140,132,132,124,124,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,127,132,135,140,143,148,151,156,159,164,167,172,175,180,183,188,191,196,196,204,204,212,212,220,220,228,228,231,236,236,239,239,236,236,236,228,228,223,220,215,212,207,204,199,196,196,188,188,180,180,172,172,164,164,156,156,148,148,140,140,132,132,124,124,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,127,132,135,140,143,148,151,156,159,164,167,172,175,180,183,188,191,196,199,204,207,212,215,220,220,228,228,236,236,239,244,244,244,239,236,231,228,223,220,220,212,212,204,204,196,196,188,188,180,180,172,172,164,164,156,156,148,148,140,140,132,132,124,124,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,127,132,135,140,143,148,151,156,159,164,167,172,175,180,183,188,191,196,199,204,207,212,215,220,223,228,231,236,239,244,244,247,244,244,236,236,228,228,220,220,212,212,204,204,196,196,188,188,180,180,172,172,164,164,156,156,148,148,140,140,132,132,124,124,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,127,132,135,140,143,148,151,156,159,164,167,172,175,180,183,188,191,196,199,204,207,212,215,220,223,228,231,236,239,244,247,252,244,244,236,236,228,228,220,220,212,212,204,204,196,196,188,188,180,180,172,172,164,164,156,156,148,148,140,140,132,132,124,124,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,127,132,135,140,143,148,151,156,159,164,167,172,175,180,183,188,191,196,199,204,207,212,215,220,223,228,231,236,236,244,244,244,244,239,236,236,228,228,220,220,212,212,204,204,196,196,188,188,180,180,172,172,164,164,156,156,148,148,140,140,132,132,124,124,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,127,132,135,140,143,148,151,156,159,164,167,172,175,180,183,188,191,196,199,204,207,212,212,220,220,228,228,236,236,239,244,244,239,236,236,231,228,223,220,215,212,207,204,204,196,196,188,188,180,180,172,172,164,164,156,156,148,148,140,140,132,132,124,124,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,127,132,135,140,143,148,151,156,159,164,167,172,175,180,180,188,188,196,196,204,204,212,212,220,220,223,228,228,236,236,236,236,236,236,231,228,228,220,220,215,212,207,204,199,196,191,188,183,180,175,172,172,164,164,156,156,148,148,140,140,132,132,124,124,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,127,132,135,140,140,148,148,156,156,164,164,172,172,180,180,188,188,196,196,199,204,207,212,215,220,220,228,228,228,231,236,236,236,231,228,228,223,220,220,212,212,204,204,199,196,191,188,183,180,175,172,167,164,159,156,151,148,143,140,140,132,132,124,124,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,95,100,100,108,108,116,116,124,124,132,132,140,140,148,148,156,156,164,164,172,172,180,180,183,188,191,196,199,204,204,212,212,215,220,220,223,228,228,228,228,228,228,228,223,220,220,212,212,207,204,204,196,196,188,188,180,180,175,172,167,164,159,156,151,148,143,140,135,132,127,124,119,116,111,108,103,100,100,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,60,68,68,76,76,84,84,92,92,100,100,108,108,116,116,124,124,132,132,140,140,148,148,151,156,159,164,167,172,175,180,183,188,188,196,196,204,204,207,212,212,215,220,220,223,223,228,228,228,223,220,220,220,215,212,212,204,204,199,196,191,188,188,180,180,172,172,164,164,156,156,151,148,143,140,135,132,127,124,119,116,111,108,103,100,95,92,87,84,79,76,71,68,63,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,7,12,15,20,20,28,28,36,36,44,44,52,52,60,60,68,68,76,76,84,84,92,92,100,100,108,108,116,116,124,124,127,132,135,140,143,148,151,156,159,164,164,172,172,180,180,188,188,191,196,196,204,204,207,212,212,215,220,220,220,220,220,220,220,220,220,212,212,212,204,204,199,196,196,188,188,183,180,175,172,167,164,164,156,156,148,148,140,140,132,132,124,124,119,116,111,108,103,100,95,92,87,84,79,76,71,68,63,60,55,52,47,44,39,36,31,28,23,20,20,12,12,0,0,0,0,0,0,0,12,12,20,20,28,28,36,36,44,44,52,52,60,60,68,68,76,76,84,84,92,92,95,100,103,108,111,116,119,124,127,132,135,140,143,148,148,156,156,164,164,172,172,175,180,183,188,188,196,196,199,204,204,207,212,212,212,215,220,220,220,220,215,215,212,212,212,204,204,204,196,196,191,188,188,180,180,172,172,167,164,159,156,151,148,143,140,140,132,132,124,124,116,116,108,108,100,100,92,92,87,84,79,76,71,68,63,60,55,52,47,44,39,36,31,28,23,20,15,12,7,0,0,0,0,0,0,0,12,12,20,20,28,28,36,36,44,44,52,52,60,60,63,68,71,76,79,84,87,92,95,100,103,108,111,116,119,124,124,132,132,140,140,148,148,151,156,159,164,167,172,172,180,180,183,188,191,196,196,199,204,204,207,212,212,212,212,212,212,212,212,212,212,207,204,204,204,196,196,191,188,188,180,180,175,172,172,164,164,156,156,151,148,143,140,135,132,127,124,119,116,116,108,108,100,100,92,92,84,84,76,76,68,68,60,60,55,52,47,44,39,36,31,28,23,20,15,12,7,0,0,0,0,0,0,0,12,12,20,20,23,28,31,36,39,44,47,52,55,60,63,68,71,76,79,84,87,92,92,100,100,108,108,116,116,124,124,132,132,135,140,143,148,151,156,156,164,164,167,172,175,180,180,188,188,191,196,196,199,204,204,204,207,207,212,212,212,212,207,207,204,204,204,199,196,196,191,188,188,183,180,180,172,172,167,164,159,156,156,148,148,140,140,132,132,127,124,119,116,111,108,103,100,95,92,92,84,84,76,76,68,68,60,60,52,52,44,44,36,36,28,28,23,20,15,12,7,0,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,55,60,63,68,68,76,76,84,84,92,92,100,100,108,108,111,116,119,124,127,132,135,140,140,148,148,156,156,159,164,164,172,172,175,180,180,188,188,191,196,196,196,199,204,204,204,204,204,204,204,204,204,204,204,199,196,196,191,188,188,183,180,180,175,172,167,164,164,156,156,151,148,143,140,135,132,132,124,124,116,116,108,108,103,100,95,92,87,84,79,76,71,68,68,60,60,52,52,44,44,36,36,28,28,20,20,12,12,0,0,0,0,0,0,0,0,7,12,15,20,23,28,31,36,36,44,44,52,52,60,60,68,68,76,76,84,84,87,92,95,100,103,108,111,116,116,124,124,132,132,140,140,143,148,151,156,156,164,164,167,172,172,180,180,180,188,188,188,191,196,196,196,199,199,204,204,204,204,204,199,199,196,196,196,191,188,188,183,180,180,175,172,172,164,164,159,156,151,148,148,140,140,135,132,127,124,119,116,116,108,108,100,100,92,92,84,84,79,76,71,68,63,60,55,52,47,44,39,36,36,28,28,20,20,12,12,0,0,0,0,0,0,0,0,0,12,12,20,20,28,28,36,36,44,44,52,52,60,60,63,68,71,76,79,84,87,92,95,100,100,108,108,116,116,119,124,127,132,135,140,140,148,148,151,156,159,164,164,167,172,172,180,180,180,188,188,188,191,196,196,196,196,196,196,196,196,196,196,196,196,191,188,188,188,183,180,180,175,172,172,167,164,159,156,156,148,148,143,140,140,132,132,124,124,119,116,111,108,103,100,95,92,92,84,84,76,76,68,68,60,60,55,52,47,44,39,36,31,28,23,20,15,12,7,0,0,0,0,0,0,0,0,0,12,12,20,20,28,28,36,36,39,44,47,52,55,60,63,68,71,76,76,84,84,92,92,100,100,103,108,111,116,119,124,124,132,132,135,140,143,148,148,156,156,159,164,164,167,172,172,180,180,180,183,188,188,188,188,191,196,196,196,196,196,196,191,191,188,188,188,188,180,180,180,175,172,172,167,164,164,156,156,151,148,148,140,140,135,132,127,124,124,116,116,108,108,100,100,95,92,87,84,79,76,76,68,68,60,60,52,52,44,44,36,36,31,28,23,20,15,12,7,0,0,0,0,0,0,0,0,0,7,12,15,20,23,28,31,36,39,44,47,52,52,60,60,68,68,76,76,84,84,87,92,95,100,103,108,108,116,116,119,124,127,132,132,140,140,143,148,151,156,156,159,164,164,167,172,172,175,180,180,180,183,188,188,188,188,188,188,188,188,188,188,188,188,188,183,180,180,180,175,172,172,167,164,164,156,156,151,148,148,143,140,135,132,132,124,124,119,116,111,108,103,100,100,92,92,84,84,79,76,71,68,63,60,55,52,52,44,44,36,36,28,28,20,20,12,12,7,0,0,0,0,0,0,0,0,0,7,12,15,20,23,28,28,36,36,44,44,52,52,60,60,63,68,71,76,79,84,84,92,92,100,100,103,108,111,116,119,124,124,132,132,135,140,140,148,148,151,156,156,159,164,164,167,172,172,175,180,180,180,180,183,183,188,188,188,188,188,188,183,183,180,180,180,180,175,172,172,172,167,164,164,159,156,156,148,148,143,140,140,132,132,127,124,119,116,116,108,108,103,100,95,92,87,84,84,76,76,68,68,60,60,55,52,47,44,39,36,31,28,28,20,20,12,12,0,0,0,0,0,0,0,0,0,0,0,12,12,20,20,28,28,36,36,39,44,47,52,55,60,63,68,68,76,76,84,84,87,92,95,100,103,108,108,116,116,119,124,127,132,132,135,140,143,148,148,151,156,156,159,164,164,167,172,172,172,175,175,180,180,180,180,180,180,180,180,180,180,180,180,180,175,172,172,172,167,164,164,164,156,156,156,148,148,143,140,140,135,132,127,124,124,116,116,111,108,103,100,100,92,92,87,84,79,76,71,68,68,60,60,52,52,44,44,39,36,31,28,23,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,12,12,20,20,23,28,31,36,39,44,47,52,52,60,60,68,68,71,76,79,84,87,92,92,100,100,103,108,111,116,116,124,124,127,132,132,140,140,143,148,148,151,156,156,159,164,164,164,167,172,172,172,172,175,175,180,180,180,180,180,180,175,175,175,172,172,172,172,167,164,164,159,156,156,156,148,148,143,140,140,135,132,132,124,124,119,116,116,108,108,100,100,95,92,87,84,84,76,76,68,68,63,60,55,52,47,44,44,36,36,28,28,20,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,23,28,28,36,36,44,44,52,52,55,60,63,68,71,76,76,84,84,87,92,95,100,100,108,108,111,116,119,124,124,127,132,132,140,140,143,148,148,151,156,156,156,159,164,164,164,167,172,172,172,172,172,172,172,172,172,172,172,172,172,172,167,167,164,164,164,159,156,156,151,148,148,143,140,140,135,132,132,127,124,119,116,116,111,108,103,100,100,92,92,87,84,79,76,71,68,68,60,60,52,52,47,44,39,36,31,28,28,20,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,20,28,28,31,36,39,44,47,52,52,60,60,68,68,71,76,79,84,84,92,92,100,100,103,108,108,116,116,119,124,124,132,132,135,140,140,143,148,148,148,156,156,156,159,164,164,164,164,164,167,167,172,172,172,172,172,172,172,167,167,164,164,164,164,159,156,156,156,151,148,148,143,140,140,135,132,132,127,124,124,116,116,111,108,108,100,100,95,92,87,84,84,76,76,71,68,63,60,55,52,52,44,44,36,36,31,28,23,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,23,28,31,36,36,44,44,52,52,55,60,63,68,71,76,76,84,84,87,92,95,100,100,103,108,111,116,116,119,124,124,132,132,135,140,140,140,148,148,148,151,156,156,156,159,159,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,159,156,156,156,151,148,148,148,143,140,140,135,132,132,127,124,124,119,116,111,108,108,103,100,95,92,92,84,84,79,76,71,68,68,60,60,55,52,47,44,39,36,36,28,28,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,20,28,28,36,36,39,44,47,52,55,60,60,68,68,71,76,79,84,84,92,92,95,100,100,108,108,111,116,116,119,124,124,132,132,132,140,140,140,143,148,148,148,151,156,156,156,156,159,159,159,164,164,164,164,164,164,164,159,159,156,156,156,156,156,151,148,148,148,143,140,140,135,132,132,127,124,124,119,116,116,108,108,103,100,100,92,92,87,84,84,76,76,68,68,63,60,55,52,52,44,44,36,36,31,28,23,20,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,20,23,28,31,36,39,44,44,52,52,55,60,63,68,68,76,76,79,84,87,92,92,100,100,103,108,108,111,116,116,119,124,124,132,132,132,135,140,140,143,143,148,148,148,151,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,151,151,148,148,148,143,140,140,140,135,132,132,127,124,124,119,116,116,108,108,103,100,100,95,92,87,84,84,79,76,71,68,68,60,60,55,52,47,44,39,36,36,28,28,20,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,20,28,28,36,36,39,44,47,52,52,60,60,68,68,71,76,76,84,84,87,92,92,100,100,103,108,108,111,116,116,119,124,124,127,132,132,135,140,140,140,143,143,148,148,148,148,151,151,156,156,156,156,156,156,156,156,151,151,151,148,148,148,148,143,140,140,140,135,132,132,132,127,124,124,119,116,116,111,108,108,100,100,95,92,92,84,84,79,76,76,68,68,63,60,55,52,52,44,44,39,36,31,28,23,20,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,20,23,28,31,36,36,44,44,52,52,55,60,60,68,68,71,76,79,84,84,87,92,95,100,100,103,108,108,111,116,116,119,124,124,127,132,132,132,135,140,140,140,140,143,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,143,143,140,140,140,140,135,132,132,127,124,124,124,119,116,116,111,108,108,100,100,95,92,92,87,84,84,76,76,71,68,63,60,60,52,52,47,44,39,36,36,28,28,23,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,23,28,28,36,36,39,44,47,52,52,60,60,63,68,68,76,76,79,84,84,92,92,95,100,100,103,108,108,111,116,116,119,124,124,124,127,132,132,132,135,140,140,140,140,140,143,143,148,148,148,148,148,148,148,148,143,143,143,140,140,140,140,135,135,132,132,132,127,124,124,119,116,116,116,108,108,108,100,100,95,92,92,87,84,84,76,76,71,68,68,60,60,55,52,52,44,44,36,36,31,28,23,20,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,20,23,28,31,36,36,44,44,47,52,55,60,60,68,68,71,76,76,79,84,84,92,92,95,100,100,103,108,108,111,116,116,116,124,124,124,127,132,132,132,132,135,135,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,135,132,132,132,132,127,124,124,124,119,116,116,111,108,108,108,100,100,100,92,92,87,84,84,79,76,76,68,68,63,60,55,52,52,44,44,39,36,36,28,28,20,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,20,28,28,31,36,39,44,44,52,52,55,60,60,68,68,71,76,76,84,84,87,92,92,95,100,100,103,108,108,108,116,116,116,119,124,124,124,127,127,132,132,132,132,135,135,135,140,140,140,140,140,140,140,140,140,135,135,132,132,132,132,132,127,124,124,124,119,116,116,116,111,108,108,103,100,100,95,92,92,87,84,84,79,76,76,68,68,63,60,60,52,52,47,44,44,36,36,31,28,23,20,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,20,23,28,28,36,36,39,44,47,52,52,60,60,63,68,68,71,76,76,84,84,87,92,92,95,100,100,100,108,108,108,111,116,116,116,119,124,124,124,124,127,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,127,127,124,124,124,124,119,116,116,116,111,108,108,103,100,100,95,92,92,87,84,84,79,76,76,71,68,68,60,60,55,52,52,44,44,39,36,31,28,28,20,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,20,28,28,31,36,36,44,44,47,52,52,60,60,63,68,68,71,76,76,84,84,87,92,92,92,100,100,100,103,108,108,108,111,116,116,116,119,124,124,124,124,124,127,127,127,132,132,132,132,132,132,132,132,132,127,127,124,124,124,124,124,119,119,116,116,116,111,108,108,108,103,100,100,95,92,92,87,84,84,79,76,76,71,68,68,60,60,55,52,52,44,44,39,36,36,28,28,23,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,23,28,28,36,36,39,44,44,52,52,55,60,60,63,68,68,76,76,76,84,84,84,92,92,92,95,100,100,103,108,108,108,111,111,116,116,116,119,119,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,119,119,116,116,116,116,111,108,108,108,103,100,100,100,95,92,92,87,84,84,79,76,76,71,68,68,60,60,55,52,52,47,44,44,36,36,31,28,23,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,20,20,23,28,28,36,36,39,44,44,52,52,55,60,60,63,68,68,76,76,76,84,84,84,87,92,92,95,100,100,100,103,108,108,108,111,111,116,116,116,116,116,119,119,119,124,124,124,124,124,124,124,124,124,119,119,119,116,116,116,116,116,111,108,108,108,103,100,100,100,95,92,92,92,87,84,84,79,76,76,71,68,68,63,60,60,52,52,47,44,44,36,36,31,28,28,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,20,28,28,31,36,36,44,44,47,52,52,55,60,60,63,68,68,71,76,76,79,84,84,87,92,92,92,95,100,100,100,103,108,108,108,108,111,111,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,111,108,108,108,108,103,103,100,100,100,95,92,92,87,84,84,84,79,76,76,71,68,68,63,60,60,52,52,47,44,44,39,36,36,28,28,23,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,28,28,31,36,36,44,44,47,52,52,55,60,60,63,68,68,71,76,76,79,84,84,84,87,92,92,95,95,100,100,100,103,108,108,108,108,108,108,111,111,116,116,116,116,116,116,116,116,116,116,111,111,111,108,108,108,108,108,103,100,100,100,100,95,92,92,92,87,84,84,84,76,76,76,71,68,68,63,60,60,52,52,47,44,44,39,36,36,28,28,23,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,20,20,23,28,28,31,36,36,44,44,47,52,52,55,60,60,63,68,68,71,76,76,79,84,84,84,87,92,92,92,95,95,100,100,100,100,103,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,103,103,100,100,100,100,95,92,92,92,87,84,84,84,79,76,76,76,68,68,68,60,60,60,52,52,52,44,44,39,36,36,31,28,28,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,20,20,23,28,28,36,36,39,44,44,47,52,52,55,60,60,63,68,68,71,76,76,76,79,84,84,84,87,92,92,92,92,95,100,100,100,100,100,103,103,103,108,108,108,108,108,108,108,108,108,108,103,103,103,100,100,100,100,100,95,95,92,92,92,87,87,84,84,84,79,76,76,71,68,68,68,60,60,60,52,52,52,44,44,39,36,36,31,28,28,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,23,28,28,36,36,39,44,44,47,52,52,55,60,60,63,68,68,68,71,76,76,76,79,84,84,84,87,92,92,92,92,95,95,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,95,95,92,92,92,92,87,87,84,84,84,79,76,76,76,71,68,68,63,60,60,55,52,52,47,44,44,39,36,36,31,28,28,23,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,23,28,28,36,36,39,44,44,47,52,52,55,60,60,60,63,68,68,71,76,76,76,79,79,84,84,84,87,87,92,92,92,92,92,95,95,95,100,100,100,100,100,100,100,100,100,100,100,95,95,92,92,92,92,92,92,87,84,84,84,84,79,76,76,76,71,68,68,68,63,60,60,55,52,52,47,44,44,39,36,36,31,28,28,23,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,28,28,28,36,36,39,44,44,44,52,52,52,55,60,60,63,68,68,68,71,76,76,76,79,79,84,84,84,84,87,87,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,87,84,84,84,84,84,79,76,76,76,71,68,68,68,63,60,60,60,55,52,52,47,44,44,39,36,36,31,28,28,23,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,28,28,28,36,36,36,44,44,44,47,52,52,55,60,60,60,63,68,68,68,71,76,76,76,76,79,84,84,84,84,84,84,87,87,87,92,92,92,92,92,92,92,92,92,92,92,87,87,87,84,84,84,84,84,79,79,76,76,76,71,71,68,68,68,63,60,60,55,52,52,52,47,44,44,39,36,36,31,28,28,23,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,28,28,28,36,36,36,39,44,44,47,52,52,52,55,60,60,60,63,68,68,68,71,76,76,76,76,76,79,79,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,79,79,76,76,76,76,71,68,68,68,68,63,60,60,60,55,52,52,52,44,44,44,39,36,36,31,28,28,23,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,23,28,28,31,36,36,39,44,44,44,52,52,52,55,60,60,60,63,63,68,68,68,71,71,76,76,76,76,76,76,79,79,79,84,84,84,84,84,84,84,84,84,84,84,79,79,79,76,76,76,76,76,76,71,68,68,68,68,63,60,60,60,55,52,52,52,47,44,44,44,36,36,36,31,28,28,23,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,23,28,28,31,36,36,39,44,44,44,47,52,52,52,55,60,60,60,60,63,68,68,68,68,71,71,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,71,71,68,68,68,68,68,63,60,60,60,55,55,52,52,52,44,44,44,39,36,36,36,28,28,28,23,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,23,28,28,31,36,36,36,39,44,44,44,47,52,52,52,55,60,60,60,60,63,63,68,68,68,68,68,71,71,71,76,76,76,76,76,76,76,76,76,76,76,76,71,71,71,68,68,68,68,68,68,63,60,60,60,60,55,55,52,52,52,47,44,44,44,39,36,36,31,28,28,28,20,20,20,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,23,28,28,28,31,36,36,39,44,44,44,47,47,52,52,52,55,60,60,60,60,60,63,63,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,63,60,60,60,60,60,55,52,52,52,52,47,44,44,44,39,36,36,36,31,28,28,23,20,20,20,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,20,23,28,28,31,36,36,36,39,44,44,44,47,47,52,52,52,52,55,60,60,60,60,60,60,63,63,63,68,68,68,68,68,68,68,68,68,68,68,68,63,63,63,60,60,60,60,60,60,55,55,52,52,52,52,47,44,44,44,39,36,36,36,31,28,28,28,23,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,23,28,28,28,31,36,36,36,39,44,44,44,44,47,52,52,52,52,52,55,55,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,55,55,52,52,52,52,47,47,44,44,44,39,39,36,36,36,31,28,28,23,20,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,20,23,28,28,28,31,36,36,36,39,44,44,44,44,47,47,52,52,52,52,52,52,55,55,55,60,60,60,60,60,60,60,60,60,60,60,60,60,55,55,55,52,52,52,52,52,52,47,44,44,44,44,39,36,36,36,36,31,28,28,28,23,20,20,20,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,20,20,20,23,28,28,28,31,31,36,36,36,39,39,44,44,44,44,47,47,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,47,47,44,44,44,44,44,39,36,36,36,36,31,28,28,28,23,20,20,20,15,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,20,23,28,28,28,28,31,36,36,36,36,39,39,44,44,44,44,44,44,47,47,47,52,52,52,52,52,52,52,52,52,52,52,52,52,47,47,47,44,44,44,44,44,44,39,39,36,36,36,36,31,28,28,28,23,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,20,23,28,28,28,28,31,36,36,36,36,36,39,39,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,39,39,36,36,36,36,36,31,31,28,28,28,23,23,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,23,28,28,28,28,28,31,36,36,36,36,36,36,39,39,39,39,44,44,44,44,44,44,44,44,44,44,44,44,44,39,39,39,36,36,36,36,36,36,31,31,28,28,28,28,23,20,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,23,23,28,28,28,28,28,31,31,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,31,31,28,28,28,28,28,23,20,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,20,23,23,28,28,28,28,28,28,31,31,31,36,36,36,36,36,36,36,36,36,36,36,36,36,36,31,31,31,28,28,28,28,28,28,28,23,20,20,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,15,20,20,20,20,20,23,23,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,23,23,20,20,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,15,20,20,20,20,20,20,23,23,23,28,28,28,28,28,28,28,28,28,28,28,28,28,28,23,23,23,23,20,20,20,20,20,20,15,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,12,15,15,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,15,15,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,12,12,12,15,15,15,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,15,15,15,12,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t FLARE_TEXTURE_1[16384] ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,12,12,12,12,12,15,15,15,15,15,15,15,15,15,15,15,15,12,12,12,12,12,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,12,12,12,15,15,15,15,20,20,20,20,20,20,20,20,20,20,20,20,20,15,15,15,15,12,12,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,12,12,12,15,15,15,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,15,15,15,12,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,12,15,15,20,20,20,20,20,20,20,20,20,20,20,23,23,23,23,23,20,20,20,20,20,20,20,20,20,20,20,15,15,12,12,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,12,12,15,15,20,20,20,20,20,20,20,23,23,23,23,28,28,28,28,28,28,28,28,28,23,23,23,23,20,20,20,20,20,20,20,15,15,12,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,12,15,15,20,20,20,20,20,20,23,23,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,23,23,23,20,20,20,20,20,20,15,15,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,15,15,20,20,20,20,20,23,23,28,28,28,28,28,28,28,28,31,31,31,31,31,31,31,31,28,28,28,28,28,28,28,28,23,23,20,20,20,20,20,15,15,12,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,15,15,20,20,20,20,23,23,28,28,28,28,28,28,31,31,36,36,36,36,36,36,36,36,36,36,36,36,31,31,31,28,28,28,28,28,28,23,20,20,20,20,20,15,15,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,15,20,20,20,20,20,23,28,28,28,28,28,31,31,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,31,31,28,28,28,28,23,23,20,20,20,20,15,15,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,15,20,20,20,20,23,28,28,28,28,28,31,36,36,36,36,36,39,39,39,44,44,44,44,44,44,44,44,44,39,39,36,36,36,36,36,36,31,28,28,28,28,23,20,20,20,20,20,15,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,15,20,20,20,20,23,28,28,28,28,31,36,36,36,36,39,39,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,39,36,36,36,36,36,31,28,28,28,28,23,20,20,20,20,15,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,15,20,20,20,23,23,28,28,28,31,36,36,36,36,39,44,44,44,44,44,47,47,52,52,52,52,52,52,52,52,47,47,47,44,44,44,44,39,39,36,36,36,31,28,28,28,28,23,20,20,20,20,15,12,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,15,20,20,20,23,28,28,28,28,31,36,36,36,39,44,44,44,44,47,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,47,47,44,44,44,39,39,36,36,36,31,28,28,28,23,20,20,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,12,15,20,20,20,23,28,28,28,31,36,36,36,39,44,44,44,44,47,52,52,52,52,55,55,60,60,60,60,60,60,60,60,60,55,55,52,52,52,52,47,44,44,44,39,36,36,36,31,28,28,28,23,20,20,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,20,23,28,28,28,31,36,36,36,39,44,44,44,47,52,52,52,55,60,60,60,60,60,63,63,63,63,63,63,60,60,60,60,60,55,55,52,52,52,47,44,44,44,39,36,36,31,28,28,28,23,20,20,20,15,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,20,23,28,28,28,31,36,36,36,39,44,44,47,52,52,52,55,60,60,60,63,68,68,68,68,68,68,68,68,68,68,68,68,63,60,60,60,60,55,52,52,47,44,44,44,39,36,36,31,28,28,28,23,20,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,15,20,20,20,23,28,28,31,36,36,36,44,44,44,47,52,52,55,60,60,60,63,68,68,68,71,71,76,76,76,76,76,76,71,71,68,68,68,68,63,60,60,60,52,52,52,47,44,44,39,36,36,31,28,28,28,23,20,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,20,23,28,28,31,36,36,36,44,44,44,52,52,52,60,60,60,63,68,68,71,76,76,76,76,76,79,79,79,79,76,76,76,76,76,71,68,68,68,63,60,60,55,52,52,47,44,44,39,36,36,31,28,28,28,23,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,23,28,28,28,36,36,36,44,44,44,52,52,55,60,60,63,68,68,71,76,76,76,79,84,84,84,84,84,84,84,84,84,84,84,79,76,76,76,68,68,68,60,60,55,52,52,47,44,44,39,36,36,31,28,28,23,20,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,20,28,28,28,31,36,36,39,44,44,52,52,55,60,60,68,68,68,76,76,79,84,84,84,87,87,92,92,92,92,92,92,92,87,84,84,84,79,76,76,71,68,68,63,60,60,52,52,47,44,44,39,36,36,31,28,28,23,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,23,28,28,31,36,36,39,44,44,52,52,55,60,60,68,68,71,76,76,84,84,84,92,92,92,95,95,100,100,100,100,95,95,92,92,92,87,84,84,79,76,76,68,68,63,60,60,52,52,47,44,44,36,36,36,28,28,28,23,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,23,28,28,28,36,36,39,44,44,47,52,55,60,60,68,68,71,76,79,84,84,92,92,95,100,100,100,100,103,103,103,103,103,100,100,100,95,92,92,87,84,84,76,76,71,68,63,60,60,52,52,47,44,39,36,36,31,28,28,23,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,23,28,28,31,36,36,44,44,47,52,52,60,60,68,68,76,76,79,84,87,92,92,100,100,103,108,108,108,108,111,111,108,108,108,108,103,100,100,95,92,92,84,84,76,76,71,68,63,60,55,52,52,44,44,39,36,36,28,28,28,23,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,23,28,28,28,36,36,39,44,44,52,52,60,60,68,68,71,76,84,84,87,92,95,100,103,108,108,111,116,116,116,116,116,116,116,116,116,111,108,108,100,100,92,92,87,84,76,76,68,68,63,60,55,52,47,44,44,36,36,31,28,28,23,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,23,28,28,31,36,36,44,44,47,52,55,60,63,68,71,76,79,84,92,92,100,100,108,108,116,116,119,124,124,124,124,124,124,124,124,119,116,116,111,108,103,100,95,92,87,84,76,76,68,68,60,60,52,52,44,44,39,36,36,28,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,28,28,28,36,36,39,44,44,52,52,60,60,68,68,76,79,84,87,92,100,100,108,111,116,119,124,124,127,132,132,132,132,132,132,132,127,124,124,116,116,108,103,100,95,92,84,84,76,76,68,63,60,55,52,47,44,44,36,36,31,28,28,23,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,23,28,28,31,36,36,44,44,47,52,55,60,63,68,76,76,84,87,92,100,100,108,111,116,124,124,132,132,135,140,140,140,140,140,140,140,132,132,127,124,119,116,108,108,100,95,92,84,79,76,71,68,60,60,52,52,44,44,39,36,36,28,28,23,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,23,28,28,31,36,39,44,44,52,52,60,60,68,71,76,84,84,92,95,100,108,111,116,124,127,132,135,140,143,148,148,148,148,148,148,148,140,140,132,132,124,119,116,108,103,100,92,87,84,76,76,68,63,60,55,52,47,44,44,36,36,31,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,28,28,28,36,36,39,44,47,52,55,60,63,68,76,79,84,92,92,100,108,111,116,124,127,132,140,143,148,151,156,156,156,156,156,156,156,148,148,140,135,132,124,119,116,108,103,100,92,84,84,76,71,68,60,60,52,52,44,44,36,36,31,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,23,28,28,31,36,36,44,44,52,52,60,60,68,71,76,84,84,92,100,103,108,116,124,127,132,140,143,148,156,159,164,164,167,167,164,164,164,156,151,148,140,135,132,124,116,111,108,100,95,92,84,76,76,68,63,60,55,52,47,44,39,36,36,28,28,23,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,23,28,28,31,36,39,44,44,52,52,60,63,68,76,76,84,92,95,100,108,116,119,124,132,140,143,151,156,164,167,172,172,175,175,175,172,172,164,159,156,148,140,135,127,124,116,108,103,100,92,84,84,76,68,68,60,55,52,47,44,39,36,36,31,28,28,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,20,23,28,28,36,36,39,44,47,52,55,60,68,68,76,79,84,92,100,103,108,116,124,132,135,143,148,156,164,172,175,180,183,188,188,183,180,180,172,164,159,156,148,140,132,124,119,116,108,100,92,87,84,76,71,68,60,60,52,52,44,44,36,36,31,28,28,23,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,20,23,28,28,36,36,39,44,47,52,55,60,68,71,76,84,87,92,100,108,111,119,124,132,140,148,156,164,172,175,180,188,191,196,196,196,188,188,180,172,164,156,151,143,135,132,124,116,108,100,95,92,84,79,76,68,63,60,52,52,44,44,36,36,31,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,28,28,31,36,36,44,44,52,52,60,60,68,71,76,84,87,95,100,108,116,124,127,135,143,151,159,167,175,180,188,196,204,204,204,204,199,196,188,180,172,164,156,148,140,132,124,116,111,103,100,92,84,79,76,68,63,60,55,52,47,44,39,36,31,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,28,28,31,36,36,44,44,52,52,60,63,68,76,76,84,92,95,100,108,116,124,132,140,148,156,164,172,180,188,196,204,212,215,220,212,207,199,191,183,175,167,156,148,140,132,127,119,111,108,100,92,87,84,76,68,68,60,55,52,47,44,39,36,36,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,23,28,28,31,36,36,44,44,52,52,60,63,68,76,79,84,92,100,103,108,116,124,132,140,148,156,164,172,183,191,204,212,220,228,228,223,215,204,196,188,180,172,164,151,143,135,127,124,116,108,100,92,87,84,76,71,68,60,55,52,47,44,39,36,36,28,28,23,20,20,20,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,23,28,28,31,36,36,44,44,52,52,60,63,68,76,79,84,92,100,103,111,116,124,132,140,148,156,167,175,188,196,204,215,228,236,236,231,220,212,199,188,180,172,164,156,148,140,132,124,116,108,100,92,87,84,76,71,68,60,55,52,47,44,39,36,36,28,28,23,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,23,28,28,31,36,36,44,44,52,52,60,63,68,76,79,84,92,100,103,111,116,124,132,140,148,156,167,175,188,196,204,220,228,236,244,236,220,212,204,191,180,172,164,156,148,140,132,124,116,108,100,92,87,84,76,71,68,60,55,52,47,44,39,36,36,28,28,23,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,23,28,28,31,36,36,44,44,52,52,60,63,68,76,79,84,92,100,103,108,116,124,132,140,148,156,164,175,183,196,204,212,223,231,236,228,220,212,199,188,180,172,164,156,148,135,132,124,116,108,100,92,87,84,76,71,68,60,55,52,47,44,39,36,36,28,28,23,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,23,28,28,31,36,36,44,44,52,52,60,63,68,76,76,84,92,95,103,108,116,124,132,140,148,156,164,172,180,188,199,207,215,220,220,220,212,204,196,188,180,167,159,151,143,135,127,119,116,108,100,92,87,84,76,68,68,60,55,52,47,44,39,36,36,28,28,23,20,20,20,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,28,28,31,36,36,44,44,52,52,60,60,68,71,76,84,92,95,100,108,116,124,132,140,148,156,164,172,180,188,196,199,204,212,212,212,204,196,188,180,172,164,156,148,140,132,124,116,111,108,100,92,84,79,76,68,68,60,55,52,47,44,39,36,31,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,28,28,28,36,36,44,44,47,52,60,60,68,71,76,84,87,92,100,108,116,119,127,132,140,148,156,164,172,180,188,191,196,199,204,199,196,188,183,175,167,164,156,148,140,132,124,116,108,103,100,92,84,79,76,68,63,60,55,52,44,44,39,36,31,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,20,23,28,28,36,36,39,44,47,52,55,60,68,68,76,84,84,92,100,103,111,116,124,132,140,148,151,159,164,172,180,183,188,188,191,188,188,180,175,172,164,156,148,140,132,127,124,116,108,100,95,92,84,76,76,68,63,60,52,52,44,44,36,36,31,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,20,20,20,23,28,28,31,36,39,44,47,52,55,60,63,68,76,79,84,92,95,100,108,116,124,127,132,140,148,156,159,164,172,175,180,180,180,180,180,172,167,164,156,148,143,140,132,124,116,111,108,100,92,87,84,76,71,68,60,60,52,52,44,44,36,36,31,28,28,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,23,28,28,31,36,36,44,44,52,52,60,60,68,71,76,84,87,92,100,108,111,116,124,132,135,140,148,156,156,164,167,172,172,172,172,167,164,164,156,148,148,140,132,124,119,116,108,100,95,92,84,79,76,68,68,60,55,52,47,44,39,36,36,28,28,23,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,23,28,28,31,36,36,44,44,47,52,55,60,68,68,76,79,84,92,95,100,108,116,119,124,132,135,140,148,151,156,156,164,164,164,164,159,156,156,148,143,140,132,127,124,116,108,103,100,92,87,84,76,71,68,63,60,52,52,44,44,39,36,31,28,28,23,20,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,23,28,28,36,36,39,44,47,52,55,60,63,68,76,76,84,87,92,100,103,108,116,119,124,132,135,140,143,148,148,151,156,156,156,151,148,148,140,140,132,127,124,116,111,108,100,95,92,84,79,76,68,68,60,60,52,52,44,44,36,36,31,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,20,23,28,28,31,36,36,44,44,52,52,60,60,68,68,76,79,84,92,92,100,103,108,116,119,124,127,132,135,140,140,143,148,148,148,143,140,140,132,132,124,124,116,111,108,100,100,92,87,84,76,71,68,63,60,55,52,47,44,39,36,36,28,28,28,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,23,28,28,31,36,36,39,44,47,52,55,60,63,68,71,76,84,84,92,95,100,108,108,116,116,124,124,132,132,132,135,140,140,135,135,132,132,127,124,119,116,111,108,100,100,92,87,84,79,76,68,68,60,60,52,52,44,44,39,36,31,28,28,23,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,23,28,28,36,36,39,44,44,52,52,60,60,68,68,76,76,84,87,92,95,100,103,108,111,116,119,124,124,127,127,132,132,132,127,124,124,124,116,116,108,108,100,100,92,92,84,79,76,71,68,63,60,55,52,47,44,39,36,36,31,28,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,23,28,28,31,36,36,39,44,47,52,52,60,60,68,68,76,76,84,87,92,95,100,103,108,108,116,116,116,119,124,124,124,124,119,116,116,116,111,108,103,100,100,92,92,84,79,76,71,68,63,60,55,52,52,44,44,39,36,36,28,28,23,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,28,28,28,36,36,39,44,44,47,52,55,60,63,68,71,76,76,84,84,92,92,100,100,103,108,108,111,111,116,116,116,116,116,111,108,108,108,100,100,95,92,87,84,79,76,76,68,68,60,60,52,52,47,44,39,36,36,31,28,28,23,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,23,28,28,31,36,36,39,44,44,52,52,55,60,63,68,71,76,76,84,84,87,92,95,100,100,100,103,108,108,108,108,108,108,108,103,100,100,95,92,92,87,84,79,76,76,68,68,60,60,55,52,47,44,44,36,36,31,28,28,28,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,23,28,28,31,36,36,44,44,47,52,52,60,60,63,68,68,76,76,79,84,84,92,92,92,95,100,100,100,100,100,100,100,100,100,95,92,92,87,84,84,79,76,71,68,68,60,60,55,52,52,44,44,39,36,36,31,28,28,23,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,23,28,28,28,36,36,39,44,44,47,52,52,60,60,63,68,68,76,76,76,84,84,84,87,92,92,92,92,92,92,92,92,92,92,92,87,84,84,79,76,76,71,68,68,60,60,55,52,52,44,44,39,36,36,31,28,28,23,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,23,28,28,31,36,36,39,44,44,47,52,52,60,60,63,68,68,71,76,76,76,84,84,84,84,87,87,87,87,87,87,84,84,84,84,79,76,76,71,68,68,63,60,60,55,52,52,44,44,44,36,36,31,28,28,28,23,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,15,20,20,20,28,28,28,31,36,36,39,44,44,47,52,52,55,60,60,63,68,68,71,76,76,76,79,79,84,84,84,84,84,84,79,79,76,76,76,71,68,68,68,63,60,60,55,52,52,44,44,44,36,36,36,28,28,28,23,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,23,28,28,28,31,36,36,39,44,44,47,52,52,55,60,60,60,63,68,68,68,71,76,76,76,76,76,76,76,76,76,76,76,71,68,68,68,63,60,60,55,52,52,52,44,44,44,36,36,36,31,28,28,23,20,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,20,23,28,28,28,31,36,36,39,44,44,47,52,52,52,55,60,60,60,63,68,68,68,68,68,71,71,71,71,68,68,68,68,68,68,63,60,60,60,55,52,52,47,44,44,44,36,36,36,31,28,28,23,20,20,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,15,20,20,20,23,28,28,28,31,36,36,39,44,44,44,47,52,52,52,55,60,60,60,60,63,63,68,68,68,68,68,68,68,63,63,60,60,60,60,55,52,52,52,47,44,44,39,36,36,36,31,28,28,28,23,20,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,20,20,23,28,28,28,31,36,36,36,39,44,44,44,47,52,52,52,55,55,60,60,60,60,60,60,60,60,60,60,60,60,60,55,52,52,52,52,47,44,44,44,39,36,36,36,31,28,28,28,23,20,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,20,20,23,28,28,28,31,36,36,36,39,44,44,44,44,47,52,52,52,52,52,55,55,55,55,55,55,55,55,55,52,52,52,52,52,47,44,44,44,39,36,36,36,31,28,28,28,28,23,20,20,20,15,12,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,20,20,23,28,28,28,31,36,36,36,36,39,44,44,44,44,47,47,52,52,52,52,52,52,52,52,52,52,52,52,52,47,44,44,44,44,39,39,36,36,36,31,28,28,28,23,23,20,20,20,15,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,12,15,20,20,20,20,23,28,28,28,28,31,36,36,36,36,39,44,44,44,44,44,44,47,47,47,47,47,47,47,47,44,44,44,44,44,44,39,39,36,36,36,31,31,28,28,28,23,20,20,20,20,15,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,15,20,20,20,20,23,23,28,28,28,28,31,36,36,36,36,36,39,39,44,44,44,44,44,44,44,44,44,44,44,44,44,39,39,36,36,36,36,36,31,28,28,28,28,23,20,20,20,20,15,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,15,15,20,20,20,20,23,28,28,28,28,28,31,36,36,36,36,36,36,36,39,39,39,39,39,39,39,39,39,36,36,36,36,36,36,31,31,28,28,28,28,23,23,20,20,20,20,15,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,15,15,20,20,20,20,23,23,28,28,28,28,28,31,31,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,31,31,28,28,28,28,28,28,23,20,20,20,20,20,15,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,12,15,20,20,20,20,20,23,23,28,28,28,28,28,28,31,31,31,31,36,36,36,36,36,36,31,31,31,31,28,28,28,28,28,28,23,23,20,20,20,20,20,15,15,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,12,15,15,20,20,20,20,20,23,23,23,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,23,23,20,20,20,20,20,20,15,12,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,15,15,20,20,20,20,20,20,23,23,23,28,28,28,28,28,28,28,28,28,28,28,28,28,28,23,23,23,20,20,20,20,20,20,20,15,15,12,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,12,12,15,15,20,20,20,20,20,20,20,20,23,23,23,23,23,23,23,23,23,23,23,23,20,20,20,20,20,20,20,20,20,15,15,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,12,15,15,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,15,15,15,12,12,12,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,12,12,12,15,15,15,15,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,15,15,15,12,12,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,12,12,12,12,15,15,15,15,15,15,20,20,20,20,20,15,15,15,15,15,15,12,12,12,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15,15,15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,12,12,12,12,12,12,12,12,12,12,12,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t FLARE_TEXTURE_2[16384] ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,20,23,28,28,28,28,28,28,28,28,28,28,23,23,20,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,28,28,31,36,36,36,36,39,39,44,44,44,44,44,44,39,39,39,36,36,36,36,28,28,23,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,23,28,31,36,36,39,44,44,44,47,47,52,52,52,52,52,52,52,52,52,52,52,52,47,47,44,44,44,44,36,36,36,28,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,28,36,36,39,44,44,47,52,52,52,55,55,60,60,60,60,60,60,60,60,60,60,60,60,60,60,55,55,52,52,52,52,47,44,44,39,36,31,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,36,36,44,44,47,52,52,55,60,60,60,60,63,63,68,68,68,68,68,68,68,68,68,68,68,68,68,68,63,63,60,60,60,60,55,52,52,52,44,44,39,36,31,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,31,36,44,44,47,52,52,60,60,60,63,68,68,68,68,68,71,71,76,76,76,76,76,76,76,76,76,76,76,76,71,71,68,68,68,68,68,63,60,60,60,55,52,52,47,44,39,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,39,44,47,52,52,60,60,60,63,68,68,68,71,76,76,76,76,76,76,79,79,79,79,84,84,84,84,84,79,79,79,79,76,76,76,76,76,71,71,68,68,68,63,60,60,55,52,52,44,44,36,31,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,39,44,52,52,55,60,60,63,68,68,71,76,76,76,76,79,79,84,84,84,84,84,84,84,84,84,87,87,87,84,84,84,84,84,84,84,84,84,79,79,76,76,76,71,68,68,68,63,60,60,52,52,47,44,36,31,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,36,39,44,52,52,60,60,63,68,68,71,76,76,76,79,84,84,84,84,84,87,87,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,87,87,84,84,84,84,79,76,76,76,71,68,68,68,60,60,55,52,47,44,36,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,31,36,44,52,52,60,60,63,68,68,76,76,76,79,84,84,84,84,87,92,92,92,92,92,92,95,95,95,100,100,100,100,100,100,100,100,95,95,95,95,92,92,92,92,92,92,87,84,84,84,79,76,76,76,71,68,68,60,60,55,52,47,44,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,44,47,52,60,60,63,68,71,76,76,76,84,84,84,87,92,92,92,92,92,95,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,95,95,92,92,92,92,87,84,84,84,79,76,76,71,68,68,63,60,55,52,44,39,36,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,36,39,44,52,55,60,63,68,71,76,76,79,84,84,84,87,92,92,92,95,100,100,100,100,100,100,103,103,108,108,108,108,108,108,108,108,108,108,108,108,108,108,103,103,103,100,100,100,100,100,95,92,92,92,92,87,84,84,79,76,76,71,68,68,60,60,52,52,44,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,44,52,52,60,63,68,68,76,76,79,84,84,87,92,92,92,95,100,100,100,100,103,103,108,108,108,108,108,108,108,108,111,111,111,111,111,111,111,111,111,108,108,108,108,108,108,108,108,103,100,100,100,100,95,92,92,92,87,84,84,84,76,76,71,68,68,60,55,52,44,39,36,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,39,44,52,55,60,68,68,76,76,79,84,84,87,92,92,92,95,100,100,100,103,108,108,108,108,108,111,111,111,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,111,111,108,108,108,108,108,103,100,100,100,100,95,92,92,87,84,84,79,76,76,71,68,63,60,52,52,44,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,23,36,44,47,52,60,63,68,71,76,76,84,84,87,92,92,92,100,100,100,103,103,108,108,108,108,111,116,116,116,116,116,116,116,116,119,119,119,119,119,119,119,119,119,119,119,116,116,116,116,116,116,116,111,111,108,108,108,108,103,100,100,100,95,92,92,87,84,84,79,76,76,68,68,60,55,52,44,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,44,52,55,60,68,68,76,76,79,84,84,92,92,92,100,100,100,103,108,108,108,108,111,116,116,116,116,116,119,119,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,119,119,116,116,116,116,116,111,111,108,108,108,103,100,100,100,95,92,92,87,84,84,76,76,71,68,63,60,52,47,39,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,44,52,60,60,68,71,76,76,84,84,87,92,92,95,100,100,103,108,108,108,111,116,116,116,116,119,119,124,124,124,124,124,124,124,124,127,127,127,127,127,127,127,127,127,127,127,124,124,124,124,124,124,124,119,119,116,116,116,116,111,108,108,108,103,100,100,100,95,92,92,84,84,79,76,76,68,63,60,52,47,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,36,44,52,60,63,68,71,76,79,84,84,92,92,95,100,100,103,108,108,108,111,116,116,116,116,119,124,124,124,124,124,127,127,127,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,127,127,124,124,124,124,124,119,119,116,116,116,111,111,108,108,103,100,100,100,92,92,87,84,84,76,76,68,68,60,55,52,44,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,39,44,52,60,63,68,76,76,84,84,87,92,92,100,100,100,108,108,108,111,116,116,116,119,124,124,124,124,124,127,132,132,132,132,132,132,132,132,132,135,135,135,135,135,135,135,135,135,132,132,132,132,132,132,132,132,127,127,124,124,124,124,119,116,116,116,111,108,108,108,103,100,100,95,92,92,84,84,79,76,71,68,60,55,52,44,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,39,47,52,60,63,68,76,76,84,84,92,92,95,100,100,103,108,108,111,116,116,116,119,124,124,124,124,127,132,132,132,132,132,132,135,135,135,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,135,135,135,132,132,132,132,132,127,127,124,124,124,119,116,116,116,111,108,108,108,100,100,100,92,92,87,84,79,76,71,68,60,55,52,44,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,39,47,52,60,68,68,76,76,84,84,92,92,95,100,100,108,108,108,111,116,116,119,124,124,124,124,127,132,132,132,132,135,135,140,140,140,140,140,140,140,140,140,140,140,143,143,140,140,140,140,140,140,140,140,140,140,140,135,135,132,132,132,132,132,127,124,124,124,119,116,116,116,111,108,108,103,100,100,95,92,87,84,84,76,71,68,60,55,52,44,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,44,52,60,68,68,76,79,84,87,92,92,100,100,103,108,108,111,116,116,116,119,124,124,124,127,132,132,132,132,135,140,140,140,140,140,140,143,143,143,148,148,148,148,148,148,148,148,148,148,148,148,143,143,143,140,140,140,140,140,140,135,135,132,132,132,132,127,124,124,124,119,116,116,111,108,108,108,100,100,95,92,92,84,84,76,71,68,60,55,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,36,44,52,60,68,68,76,79,84,87,92,92,100,100,103,108,108,111,116,116,119,124,124,124,127,132,132,132,135,135,140,140,140,140,143,143,143,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,143,143,140,140,140,140,140,135,132,132,132,132,127,124,124,124,116,116,116,111,108,108,100,100,95,92,92,84,84,76,71,68,60,55,52,44,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,44,52,60,63,68,76,79,84,87,92,95,100,100,108,108,108,116,116,116,124,124,124,127,132,132,132,135,135,140,140,140,140,143,148,148,148,148,148,148,148,151,151,151,151,151,151,156,156,156,151,151,151,151,151,148,148,148,148,148,148,148,143,143,140,140,140,140,135,132,132,132,127,124,124,124,119,116,116,111,108,108,103,100,100,92,92,84,84,76,71,68,60,55,52,39,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,60,63,68,76,79,84,87,92,95,100,100,108,108,111,116,116,119,124,124,124,127,132,132,132,135,140,140,140,143,143,148,148,148,148,148,151,151,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,151,151,148,148,148,148,148,148,143,140,140,140,140,135,132,132,132,127,124,124,119,116,116,111,108,108,103,100,100,92,92,84,84,76,71,68,60,52,47,39,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,44,52,60,63,68,76,76,84,87,92,95,100,100,108,108,111,116,116,119,124,124,127,132,132,132,135,140,140,140,143,143,148,148,148,148,151,151,156,156,156,156,156,156,156,156,156,159,159,159,159,159,159,159,156,156,156,156,156,156,156,156,156,151,148,148,148,148,148,143,140,140,140,140,135,132,132,127,124,124,124,116,116,116,108,108,103,100,100,92,92,84,84,76,71,68,60,52,44,36,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,39,47,55,60,68,76,76,84,87,92,95,100,100,108,108,111,116,116,119,124,124,127,132,132,135,140,140,140,140,143,148,148,148,148,151,156,156,156,156,156,156,159,159,159,164,164,164,164,164,164,164,164,164,164,164,164,159,159,159,156,156,156,156,156,156,151,151,148,148,148,148,143,140,140,140,135,132,132,132,127,124,124,119,116,116,108,108,103,100,100,92,92,84,79,76,68,68,60,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,44,52,60,68,71,76,84,84,92,92,100,100,108,108,111,116,116,124,124,124,127,132,132,135,140,140,140,143,148,148,148,148,151,156,156,156,156,156,159,159,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,159,159,156,156,156,156,156,151,148,148,148,143,143,140,140,140,132,132,132,127,124,124,119,116,116,108,108,103,100,95,92,87,84,79,76,68,63,60,52,44,31,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,60,68,71,76,84,84,92,92,100,100,108,108,111,116,116,124,124,124,132,132,132,135,140,140,143,148,148,148,148,151,156,156,156,156,159,159,164,164,164,164,164,164,164,167,167,167,167,167,167,167,167,167,167,167,167,164,164,164,164,164,164,164,164,159,156,156,156,156,151,151,148,148,148,143,140,140,140,135,132,132,127,124,124,119,116,116,108,108,103,100,95,92,87,84,76,76,68,60,55,47,36,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,39,52,55,63,68,76,79,84,92,92,100,100,108,108,111,116,116,124,124,124,132,132,132,140,140,140,143,148,148,148,151,156,156,156,156,159,159,164,164,164,164,164,167,167,167,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,167,167,164,164,164,164,164,164,159,156,156,156,156,151,148,148,148,143,140,140,140,135,132,132,127,124,124,119,116,116,108,108,103,100,95,92,87,84,76,71,68,60,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,44,52,60,68,76,76,84,87,92,95,100,103,108,111,116,116,124,124,124,132,132,132,140,140,140,143,148,148,148,151,156,156,156,159,159,164,164,164,164,167,167,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,167,167,164,164,164,164,164,159,156,156,156,156,151,148,148,148,143,140,140,135,132,132,127,124,124,119,116,116,108,108,100,100,92,92,84,84,76,68,63,60,52,44,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,31,44,52,60,68,71,76,84,84,92,95,100,103,108,108,116,116,119,124,124,132,132,135,140,140,140,143,148,148,151,156,156,156,156,159,164,164,164,164,167,167,172,172,172,172,172,172,175,175,175,175,175,180,180,180,180,180,175,175,175,175,172,172,172,172,172,172,172,167,164,164,164,164,164,159,156,156,156,151,148,148,148,143,140,140,135,132,132,127,124,124,119,116,111,108,108,100,100,92,92,84,79,76,68,60,55,47,36,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,36,47,55,63,68,76,79,84,92,92,100,100,108,108,116,116,119,124,124,132,132,132,140,140,140,143,148,148,151,156,156,156,159,164,164,164,164,167,167,172,172,172,172,172,175,175,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,175,175,175,172,172,172,172,172,167,164,164,164,164,159,156,156,156,151,148,148,148,143,140,140,135,132,132,127,124,124,116,116,111,108,103,100,95,92,87,84,76,71,68,60,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,60,68,76,76,84,87,92,100,100,108,108,111,116,119,124,124,127,132,132,140,140,140,148,148,148,151,156,156,156,159,164,164,164,167,167,172,172,172,172,175,175,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,175,175,172,172,172,172,172,167,164,164,164,164,159,156,156,156,148,148,148,143,140,140,135,132,132,127,124,124,116,116,111,108,103,100,95,92,84,84,76,68,63,55,52,39,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,39,52,60,63,68,76,84,84,92,95,100,103,108,111,116,116,124,124,127,132,132,140,140,140,143,148,148,151,156,156,156,159,164,164,164,167,172,172,172,172,175,175,180,180,180,180,180,180,183,183,183,183,188,188,188,188,188,188,188,183,183,183,180,180,180,180,180,180,180,175,172,172,172,172,167,167,164,164,164,159,156,156,156,148,148,148,143,140,140,135,132,132,124,124,119,116,116,108,108,100,100,92,92,84,79,76,68,60,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,60,68,76,79,84,92,92,100,100,108,108,116,116,124,124,127,132,132,135,140,140,143,148,148,151,156,156,156,159,164,164,164,167,172,172,172,175,175,180,180,180,180,180,183,183,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,183,183,183,180,180,180,180,180,175,172,172,172,172,167,164,164,164,159,156,156,156,148,148,148,143,140,140,135,132,132,124,124,119,116,111,108,103,100,95,92,87,84,76,68,63,60,52,39,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,39,52,60,63,71,76,84,87,92,95,100,108,108,111,116,119,124,124,132,132,135,140,140,143,148,148,151,156,156,156,164,164,164,167,172,172,172,172,175,180,180,180,180,180,183,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,183,183,180,180,180,180,175,175,172,172,172,167,164,164,164,159,156,156,156,148,148,148,140,140,140,132,132,127,124,124,116,116,111,108,103,100,92,92,84,79,76,68,60,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,60,68,76,79,84,92,92,100,103,108,111,116,116,124,124,127,132,135,140,140,143,148,148,151,156,156,156,164,164,164,167,172,172,172,175,175,180,180,180,180,183,188,188,188,188,188,188,191,191,191,191,196,196,196,196,196,196,191,191,191,191,188,188,188,188,188,188,183,183,180,180,180,180,175,172,172,172,167,164,164,164,159,156,156,151,148,148,148,140,140,135,132,132,127,124,119,116,116,108,108,100,100,92,87,84,76,71,63,60,52,39,28,0,0,0,0,0,0,0,0,0,0,0,0,28,39,52,60,63,71,76,84,87,92,100,100,108,108,116,116,119,124,127,132,132,140,140,143,148,148,151,156,156,156,159,164,164,167,172,172,172,175,180,180,180,180,183,188,188,188,188,188,191,191,191,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,191,191,188,188,188,188,188,183,180,180,180,180,175,172,172,172,167,164,164,164,159,156,156,151,148,148,143,140,140,135,132,132,124,124,119,116,111,108,103,100,92,92,84,79,76,68,60,52,44,36,15,0,0,0,0,0,0,0,0,0,0,12,31,44,52,60,68,76,79,84,92,92,100,103,108,111,116,119,124,124,132,132,135,140,140,148,148,148,156,156,156,159,164,164,167,172,172,172,175,180,180,180,180,183,188,188,188,188,191,191,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,191,191,188,188,188,188,188,183,180,180,180,175,172,172,172,167,164,164,164,159,156,156,151,148,148,143,140,140,132,132,127,124,124,116,116,108,108,100,100,92,87,84,76,68,63,55,47,36,23,0,0,0,0,0,0,0,0,0,0,23,36,47,55,63,68,76,84,87,92,100,100,108,108,116,116,124,124,127,132,132,140,140,143,148,148,151,156,156,159,164,164,167,172,172,172,175,180,180,180,183,188,188,188,188,191,191,196,196,196,196,196,196,199,199,199,199,199,204,204,199,199,199,199,199,196,196,196,196,196,196,196,191,188,188,188,188,183,180,180,180,175,172,172,172,167,164,164,164,156,156,156,151,148,148,140,140,135,132,132,124,124,119,116,111,108,103,100,92,92,84,79,76,68,60,52,44,31,12,0,0,0,0,0,0,0,0,0,28,44,52,60,68,76,76,84,92,92,100,103,108,111,116,119,124,124,132,132,135,140,140,148,148,151,156,156,159,164,164,164,172,172,172,175,180,180,180,183,188,188,188,188,191,196,196,196,196,196,199,199,199,204,204,204,204,204,204,204,204,204,204,204,204,204,199,199,196,196,196,196,196,191,191,188,188,188,183,180,180,180,175,172,172,172,167,164,164,159,156,156,151,148,148,143,140,140,135,132,127,124,124,116,116,108,108,100,95,92,87,84,76,68,63,55,44,36,20,0,0,0,0,0,0,0,0,20,36,44,52,60,68,76,84,84,92,95,100,108,108,116,116,124,124,127,132,135,140,140,143,148,148,156,156,156,164,164,164,167,172,172,175,180,180,180,183,188,188,188,188,191,196,196,196,196,199,199,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,199,199,196,196,196,196,196,191,188,188,188,183,180,180,180,175,172,172,172,167,164,164,159,156,156,151,148,148,143,140,135,132,132,124,124,119,116,111,108,103,100,92,92,84,76,71,68,60,52,39,28,0,0,0,0,0,0,0,0,23,36,47,60,63,71,76,84,87,92,100,103,108,111,116,119,124,124,132,132,135,140,143,148,148,151,156,156,159,164,164,167,172,172,175,180,180,180,183,188,188,188,191,191,196,196,196,196,199,204,204,204,204,204,204,204,207,207,207,207,207,207,207,207,207,204,204,204,204,204,204,199,199,196,196,196,196,191,188,188,188,183,180,180,180,175,172,172,172,164,164,164,159,156,156,148,148,143,140,140,135,132,127,124,124,116,116,108,108,100,95,92,84,79,76,68,60,52,44,31,12,0,0,0,0,0,0,7,28,44,52,60,68,76,79,84,92,95,100,103,108,116,116,124,124,127,132,135,140,140,143,148,148,156,156,159,164,164,167,172,172,172,175,180,180,183,188,188,188,191,196,196,196,196,199,204,204,204,204,204,204,207,207,212,212,212,212,212,212,212,212,212,212,207,207,207,204,204,204,204,204,199,196,196,196,196,191,188,188,188,183,180,180,180,175,172,172,167,164,164,159,156,156,151,148,148,143,140,135,132,132,124,124,119,116,111,108,100,100,92,87,84,76,68,63,55,47,36,20,0,0,0,0,0,0,20,36,44,52,60,68,76,84,87,92,100,100,108,108,116,116,124,124,132,132,135,140,143,148,148,151,156,156,159,164,164,167,172,172,175,180,180,180,188,188,188,191,196,196,196,196,199,204,204,204,204,207,207,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,207,207,204,204,204,204,199,199,196,196,196,191,188,188,188,183,180,180,180,172,172,172,167,164,164,159,156,156,148,148,143,140,140,135,132,127,124,119,116,111,108,103,100,92,92,84,76,71,68,60,52,39,28,0,0,0,0,0,0,23,36,47,60,63,71,76,84,92,92,100,103,108,111,116,119,124,127,132,132,140,140,143,148,148,156,156,159,164,164,167,172,172,175,180,180,180,183,188,188,188,191,196,196,196,199,204,204,204,204,207,212,212,212,212,212,212,212,212,215,215,215,215,215,212,212,212,212,212,212,212,207,207,204,204,204,204,199,196,196,196,191,188,188,188,183,180,180,175,172,172,167,164,164,159,156,156,151,148,148,143,140,135,132,132,124,124,116,116,108,108,100,95,92,84,79,76,68,60,52,44,31,12,0,0,0,0,0,28,39,52,60,68,76,79,84,92,95,100,108,108,116,116,124,124,132,132,135,140,140,148,148,151,156,156,159,164,164,167,172,172,175,180,180,183,188,188,188,191,196,196,196,199,204,204,204,204,207,212,212,212,212,212,215,215,215,220,220,220,220,220,220,215,215,215,212,212,212,212,212,212,207,204,204,204,204,199,196,196,196,191,188,188,183,180,180,180,175,172,172,167,164,164,159,156,156,148,148,143,140,140,132,132,127,124,119,116,111,108,100,100,92,87,84,76,68,63,55,44,36,20,0,0,0,0,12,31,44,52,60,68,76,84,84,92,100,100,108,108,116,119,124,124,132,132,140,140,143,148,148,156,156,159,164,164,167,172,172,175,180,180,180,188,188,188,191,196,196,196,199,204,204,204,207,207,212,212,212,212,215,215,220,220,220,220,220,220,220,220,220,220,220,220,220,215,212,212,212,212,212,207,204,204,204,199,199,196,196,191,188,188,188,183,180,180,175,172,172,167,164,164,159,156,156,151,148,148,140,140,135,132,127,124,124,116,111,108,103,100,92,92,84,76,71,68,60,47,36,23,0,0,0,0,20,36,44,55,63,68,76,84,87,92,100,103,108,111,116,119,124,127,132,135,140,140,148,148,151,156,156,159,164,164,167,172,172,175,180,180,183,188,188,191,196,196,196,199,204,204,204,207,207,212,212,212,215,215,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,215,215,212,212,212,212,207,204,204,204,199,196,196,196,191,188,188,188,180,180,180,175,172,172,167,164,164,159,156,156,148,148,143,140,135,132,132,124,124,116,116,108,108,100,95,92,84,79,76,68,60,52,39,28,0,0,0,0,20,36,47,60,68,71,76,84,92,92,100,103,108,116,116,124,124,132,132,135,140,143,148,148,151,156,156,164,164,167,172,172,175,180,180,180,188,188,188,191,196,196,196,204,204,204,204,207,212,212,212,215,215,220,220,220,220,220,223,223,223,223,223,223,223,223,220,220,220,220,220,220,215,212,212,212,212,207,204,204,204,199,196,196,196,191,188,188,183,180,180,175,172,172,167,164,164,159,156,156,151,148,143,140,140,132,132,127,124,119,116,111,108,100,100,92,84,84,76,68,60,52,44,31,12,0,0,0,28,39,52,60,68,76,79,84,92,95,100,108,108,116,116,124,124,132,132,140,140,143,148,148,156,156,159,164,164,167,172,172,175,180,180,183,188,188,191,196,196,196,199,204,204,204,207,212,212,212,215,220,220,220,220,220,223,223,228,228,228,228,228,228,228,228,228,223,223,220,220,220,220,215,212,212,212,212,207,204,204,204,199,196,196,191,188,188,188,180,180,180,175,172,172,164,164,164,156,156,151,148,148,140,140,135,132,127,124,119,116,111,108,103,100,92,87,84,76,68,60,52,44,36,15,0,0,0,28,44,52,60,68,76,79,84,92,100,100,108,111,116,119,124,127,132,132,140,140,143,148,151,156,156,159,164,164,172,172,172,180,180,180,188,188,188,191,196,196,199,204,204,204,207,212,212,212,215,220,220,220,220,223,223,228,228,228,228,228,228,228,228,228,228,228,228,228,223,220,220,220,220,215,212,212,212,207,204,204,204,199,196,196,196,191,188,188,183,180,180,175,172,172,167,164,164,159,156,156,148,148,143,140,135,132,132,124,124,116,116,108,103,100,92,92,84,76,71,63,55,47,36,20,0,0,12,31,44,52,60,68,76,84,87,92,100,100,108,111,116,119,124,127,132,135,140,140,148,148,151,156,156,164,164,167,172,172,175,180,180,183,188,188,191,196,196,196,199,204,204,207,212,212,212,215,215,220,220,220,223,228,228,228,228,228,228,228,231,231,228,228,228,228,228,228,228,223,220,220,220,220,215,212,212,212,207,204,204,204,199,196,196,191,188,188,183,180,180,180,172,172,167,164,164,159,156,156,148,148,143,140,140,132,132,124,124,116,116,108,108,100,95,92,84,79,71,68,60,52,36,23,0,0,15,36,44,55,63,68,76,84,87,92,100,103,108,111,116,124,124,127,132,135,140,143,148,148,156,156,159,164,164,167,172,172,175,180,180,183,188,188,191,196,196,199,204,204,204,207,212,212,212,215,220,220,220,223,228,228,228,228,228,231,231,231,236,236,236,231,231,231,228,228,228,228,223,220,220,220,220,215,212,212,212,207,204,204,199,196,196,196,188,188,188,183,180,180,175,172,172,164,164,159,156,156,151,148,143,140,140,132,132,127,124,119,116,108,108,100,95,92,84,79,76,68,60,52,39,28,0,0,20,36,47,55,63,71,76,84,92,92,100,103,108,116,116,124,124,132,132,135,140,143,148,148,156,156,159,164,164,167,172,172,180,180,180,188,188,188,191,196,196,199,204,204,204,212,212,212,215,220,220,220,223,228,228,228,228,231,231,236,236,236,236,236,236,236,236,236,231,228,228,228,228,223,220,220,220,215,212,212,212,207,204,204,204,196,196,196,191,188,188,183,180,180,175,172,172,167,164,164,156,156,151,148,148,140,140,135,132,127,124,119,116,111,108,100,100,92,84,79,76,68,60,52,44,28,0,0,20,36,47,60,68,71,76,84,92,95,100,108,108,116,116,124,124,132,132,140,140,143,148,151,156,156,159,164,164,172,172,175,180,180,183,188,188,191,196,196,196,199,204,204,207,212,212,212,215,220,220,220,223,228,228,228,231,231,236,236,236,236,236,236,236,236,236,236,236,231,228,228,228,228,223,220,220,220,215,212,212,207,204,204,204,199,196,196,191,188,188,183,180,180,175,172,172,167,164,164,159,156,151,148,148,140,140,135,132,127,124,119,116,111,108,100,100,92,87,84,76,68,60,52,44,28,7,0,23,36,52,60,68,76,79,84,92,95,100,108,108,116,116,124,124,132,132,140,140,148,148,151,156,156,164,164,167,172,172,175,180,180,183,188,188,191,196,196,199,204,204,204,207,212,212,215,220,220,220,223,228,228,228,231,231,236,236,236,236,239,239,239,239,236,236,236,236,236,231,228,228,228,223,220,220,220,215,212,212,212,207,204,204,199,196,196,191,188,188,188,180,180,180,172,172,167,164,164,159,156,156,148,148,143,140,135,132,127,124,119,116,111,108,103,100,92,87,84,76,68,60,52,44,31,12,0,28,39,52,60,68,76,79,84,92,95,100,108,108,116,119,124,127,132,132,140,140,148,148,151,156,156,164,164,167,172,172,175,180,180,183,188,188,191,196,196,199,204,204,204,212,212,212,215,220,220,220,223,228,228,228,231,236,236,236,236,239,244,244,244,244,239,239,236,236,236,236,231,228,228,228,223,220,220,220,212,212,212,207,204,204,199,196,196,196,188,188,188,183,180,180,172,172,172,164,164,159,156,156,148,148,143,140,135,132,132,124,124,116,116,108,103,100,92,87,84,76,68,63,52,44,36,12,0,28,39,52,60,68,76,79,84,92,100,100,108,111,116,119,124,127,132,135,140,140,148,148,151,156,156,164,164,167,172,172,175,180,180,183,188,188,191,196,196,199,204,204,207,212,212,212,215,220,220,223,228,228,228,231,236,236,236,236,239,244,244,244,244,244,244,244,239,236,236,236,231,228,228,228,223,220,220,220,215,212,212,207,204,204,204,199,196,196,191,188,188,183,180,180,175,172,172,164,164,159,156,156,148,148,143,140,140,132,132,124,124,116,116,108,103,100,92,92,84,76,68,63,55,44,36,15,0,28,44,52,60,68,76,79,84,92,100,100,108,111,116,119,124,127,132,135,140,140,148,148,151,156,159,164,164,167,172,172,175,180,180,188,188,188,196,196,196,199,204,204,207,212,212,212,220,220,220,223,228,228,228,231,236,236,236,239,244,244,244,244,244,244,244,244,239,239,236,236,236,231,228,228,223,220,220,220,215,212,212,212,204,204,204,199,196,196,191,188,188,183,180,180,175,172,172,164,164,159,156,156,151,148,143,140,140,132,132,124,124,116,116,108,103,100,92,92,84,76,71,63,55,44,36,20,0,28,44,52,60,68,76,84,84,92,100,100,108,111,116,119,124,127,132,135,140,140,148,148,151,156,159,164,164,167,172,172,180,180,180,188,188,188,196,196,196,199,204,204,207,212,212,215,220,220,220,223,228,228,228,231,236,236,239,244,244,244,244,247,247,244,244,244,244,239,236,236,236,231,228,228,228,223,220,220,215,212,212,212,204,204,204,199,196,196,191,188,188,183,180,180,175,172,172,164,164,159,156,156,151,148,143,140,140,132,132,124,124,116,116,108,103,100,95,92,84,76,71,63,55,47,36,20,0,28,44,52,60,68,76,84,87,92,100,100,108,111,116,119,124,127,132,135,140,143,148,148,156,156,159,164,164,167,172,172,180,180,180,188,188,188,196,196,196,204,204,204,207,212,212,215,220,220,220,223,228,228,231,236,236,236,239,244,244,244,247,252,252,247,244,244,244,239,236,236,236,231,228,228,228,223,220,220,215,212,212,212,207,204,204,199,196,196,191,188,188,183,180,180,175,172,172,167,164,164,156,156,151,148,143,140,140,132,132,124,124,116,116,108,108,100,95,92,84,76,71,63,55,47,36,20,0,28,44,52,60,68,76,84,87,92,100,100,108,111,116,119,124,127,132,135,140,143,148,148,156,156,159,164,164,167,172,172,180,180,180,188,188,188,196,196,196,204,204,204,207,212,212,215,220,220,220,223,228,228,231,236,236,236,239,244,244,244,247,252,252,247,244,244,244,239,236,236,236,231,228,228,228,223,220,220,215,212,212,212,207,204,204,199,196,196,191,188,188,183,180,180,175,172,172,167,164,164,156,156,151,148,143,140,140,132,132,124,124,116,116,108,108,100,95,92,84,76,71,63,55,47,36,20,0,28,44,52,60,68,76,84,87,92,100,100,108,111,116,119,124,127,132,135,140,140,148,148,156,156,159,164,164,167,172,172,180,180,180,188,188,188,196,196,196,199,204,204,207,212,212,215,220,220,220,223,228,228,228,236,236,236,239,244,244,244,244,247,247,247,244,244,244,239,236,236,236,231,228,228,228,223,220,220,215,212,212,212,207,204,204,199,196,196,191,188,188,183,180,180,175,172,172,167,164,159,156,156,151,148,143,140,140,132,132,124,124,116,116,108,103,100,95,92,84,76,71,63,55,47,36,20,0,28,44,52,60,68,76,84,84,92,100,100,108,111,116,119,124,127,132,135,140,140,148,148,151,156,159,164,164,167,172,172,180,180,180,188,188,188,196,196,196,199,204,204,207,212,212,215,220,220,220,223,228,228,228,231,236,236,236,239,244,244,244,244,244,244,244,244,244,239,236,236,236,231,228,228,228,220,220,220,215,212,212,212,204,204,204,199,196,196,191,188,188,183,180,180,175,172,172,164,164,159,156,156,151,148,143,140,140,132,132,124,124,116,116,108,103,100,92,92,84,76,71,63,55,44,36,20,0,28,39,52,60,68,76,79,84,92,100,100,108,111,116,119,124,127,132,135,140,140,148,148,151,156,159,164,164,167,172,172,175,180,180,188,188,188,191,196,196,199,204,204,207,212,212,212,215,220,220,223,228,228,228,231,236,236,236,239,244,244,244,244,244,244,244,244,239,236,236,236,231,228,228,228,223,220,220,220,215,212,212,207,204,204,204,199,196,196,191,188,188,183,180,180,175,172,172,164,164,159,156,156,151,148,143,140,140,132,132,124,124,116,116,108,103,100,92,92,84,76,71,63,55,44,36,20,0,28,39,52,60,68,76,79,84,92,95,100,108,111,116,119,124,127,132,135,140,140,148,148,151,156,156,164,164,167,172,172,175,180,180,183,188,188,191,196,196,199,204,204,207,212,212,212,215,220,220,220,228,228,228,231,236,236,236,236,239,239,244,244,244,244,244,239,236,236,236,236,231,228,228,228,223,220,220,220,215,212,212,207,204,204,204,196,196,196,191,188,188,183,180,180,175,172,172,164,164,159,156,156,148,148,143,140,135,132,132,124,124,116,116,108,103,100,92,87,84,76,68,63,55,44,36,15,0,23,39,52,60,68,76,79,84,92,95,100,108,108,116,119,124,127,132,132,140,140,148,148,151,156,156,164,164,167,172,172,175,180,180,183,188,188,191,196,196,199,204,204,204,207,212,212,215,220,220,220,223,228,228,228,231,236,236,236,236,239,239,239,239,239,239,236,236,236,236,231,228,228,228,228,223,220,220,215,212,212,212,207,204,204,199,196,196,196,188,188,188,180,180,180,172,172,167,164,164,159,156,156,148,148,143,140,135,132,132,124,124,116,111,108,103,100,92,87,84,76,68,60,52,44,31,12,0,23,36,47,60,68,71,79,84,92,95,100,108,108,116,116,124,124,132,132,140,140,143,148,151,156,156,159,164,164,172,172,175,180,180,183,188,188,191,196,196,196,204,204,204,207,212,212,212,220,220,220,223,228,228,228,228,231,236,236,236,236,236,236,236,236,236,236,236,236,231,231,228,228,228,223,220,220,220,215,212,212,212,207,204,204,199,196,196,191,188,188,183,180,180,175,172,172,167,164,164,159,156,156,148,148,143,140,135,132,127,124,119,116,111,108,103,100,92,87,84,76,68,60,52,44,31,12,0,20,36,47,55,63,71,76,84,92,95,100,103,108,116,116,124,124,132,132,140,140,143,148,148,156,156,159,164,164,172,172,172,180,180,180,188,188,188,196,196,196,199,204,204,207,212,212,212,215,220,220,220,223,228,228,228,228,231,236,236,236,236,236,236,236,236,236,236,231,231,228,228,228,223,220,220,220,220,215,212,212,207,204,204,204,199,196,196,191,188,188,183,180,180,175,172,172,167,164,164,156,156,151,148,148,140,140,135,132,127,124,119,116,111,108,100,100,92,87,84,76,68,60,52,44,28,0,0,20,36,44,55,63,68,76,84,92,92,100,103,108,116,116,124,124,132,132,135,140,143,148,148,156,156,159,164,164,167,172,172,175,180,180,183,188,188,191,196,196,199,204,204,204,207,212,212,212,215,220,220,220,223,228,228,228,228,231,231,236,236,236,236,236,236,231,231,228,228,228,228,228,223,220,220,220,215,212,212,212,207,204,204,199,196,196,196,191,188,188,183,180,180,175,172,172,167,164,164,156,156,151,148,148,140,140,132,132,127,124,119,116,108,108,100,95,92,84,79,76,68,60,52,44,28,0,0,12,36,44,52,60,68,76,84,87,92,100,103,108,111,116,119,124,127,132,135,140,140,148,148,151,156,156,164,164,167,172,172,175,180,180,183,188,188,191,196,196,196,199,204,204,207,212,212,212,215,220,220,220,220,223,228,228,228,228,228,231,231,231,231,231,231,228,228,228,228,228,228,223,220,220,220,215,212,212,212,207,204,204,204,199,196,196,191,188,188,188,180,180,180,172,172,172,164,164,159,156,156,151,148,143,140,140,132,132,124,124,116,116,108,108,100,95,92,84,79,76,68,60,52,39,28,0,0,7,28,44,52,60,68,76,84,87,92,100,100,108,111,116,119,124,127,132,135,140,140,148,148,151,156,156,164,164,164,172,172,175,180,180,183,188,188,188,191,196,196,199,204,204,204,207,212,212,212,215,220,220,220,220,223,228,228,228,228,228,228,228,228,228,228,228,228,228,228,223,223,220,220,220,220,215,212,212,212,207,204,204,204,196,196,196,191,188,188,183,180,180,175,172,172,167,164,164,159,156,156,148,148,143,140,135,132,132,124,124,116,116,108,103,100,95,92,84,76,71,68,60,47,36,20,0,0,0,28,44,52,60,68,76,79,84,92,95,100,108,108,116,116,124,124,132,132,140,140,143,148,148,156,156,159,164,164,167,172,172,175,180,180,183,188,188,191,196,196,196,199,204,204,207,212,212,212,212,215,220,220,220,220,223,223,228,228,228,228,228,228,228,228,228,228,228,223,220,220,220,220,220,215,212,212,212,207,204,204,204,199,196,196,196,188,188,188,183,180,180,175,172,172,167,164,164,156,156,151,148,148,140,140,135,132,127,124,119,116,111,108,103,100,92,87,84,76,68,63,55,44,36,20,0,0,0,23,36,52,60,68,71,79,84,92,95,100,108,108,116,116,124,124,132,132,135,140,143,148,148,156,156,159,164,164,167,172,172,175,180,180,183,188,188,188,196,196,196,199,204,204,204,207,212,212,212,212,215,220,220,220,220,220,223,223,223,228,228,228,228,228,223,223,223,220,220,220,220,220,215,212,212,212,207,204,204,204,199,196,196,196,191,188,188,183,180,180,180,172,172,172,164,164,159,156,156,151,148,148,140,140,132,132,127,124,119,116,111,108,100,100,92,87,84,76,68,60,52,44,31,12,0,0,0,20,36,47,55,63,71,76,84,92,92,100,103,108,111,116,119,124,127,132,135,140,140,148,148,151,156,156,164,164,164,172,172,172,180,180,180,183,188,188,191,196,196,196,199,204,204,204,207,212,212,212,212,215,220,220,220,220,220,220,220,223,223,223,223,220,220,220,220,220,220,220,215,215,212,212,212,207,207,204,204,204,199,196,196,191,188,188,188,183,180,180,175,172,172,167,164,164,159,156,156,148,148,143,140,140,132,132,124,124,116,116,108,108,100,95,92,84,79,76,68,60,52,44,28,0,0,0,0,15,36,44,52,60,68,76,84,87,92,100,100,108,111,116,119,124,127,132,132,140,140,143,148,148,156,156,159,164,164,167,172,172,175,180,180,183,188,188,188,191,196,196,196,199,204,204,204,207,212,212,212,212,215,215,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,215,212,212,212,212,207,207,204,204,204,199,196,196,196,191,188,188,183,180,180,180,172,172,172,164,164,164,156,156,151,148,148,143,140,135,132,132,124,124,116,116,108,103,100,95,92,84,79,71,68,60,52,39,28,0,0,0,0,0,28,44,52,60,68,76,79,84,92,95,100,108,108,116,116,124,124,132,132,135,140,143,148,148,151,156,156,164,164,164,172,172,172,180,180,180,183,188,188,191,196,196,196,199,204,204,204,204,207,212,212,212,212,212,215,215,220,220,220,220,220,220,220,220,220,220,215,215,215,212,212,212,212,207,207,204,204,204,199,196,196,196,191,188,188,188,183,180,180,175,172,172,167,164,164,159,156,156,151,148,148,140,140,135,132,127,124,119,116,111,108,103,100,92,87,84,76,71,63,55,47,36,20,0,0,0,0,0,28,39,52,60,68,71,76,84,92,92,100,103,108,111,116,119,124,127,132,135,140,140,148,148,151,156,156,159,164,164,167,172,172,175,180,180,180,188,188,188,191,196,196,196,199,204,204,204,204,207,207,212,212,212,212,212,212,215,215,215,215,215,215,215,215,215,212,212,212,212,212,212,207,204,204,204,204,199,196,196,196,191,188,188,188,183,180,180,180,172,172,172,164,164,164,156,156,151,148,148,143,140,140,132,132,124,124,119,116,108,108,100,100,92,87,84,76,68,60,52,44,36,15,0,0,0,0,0,20,36,44,55,63,68,76,84,87,92,100,100,108,111,116,119,124,127,132,132,140,140,143,148,148,156,156,156,164,164,164,172,172,172,175,180,180,183,188,188,188,191,196,196,196,199,199,204,204,204,204,207,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,207,207,204,204,204,204,199,196,196,196,191,191,188,188,183,180,180,180,175,172,172,167,164,164,159,156,156,151,148,148,140,140,135,132,127,124,124,116,116,108,108,100,95,92,84,79,76,68,60,52,44,28,0,0,0,0,0,0,12,31,44,52,60,68,76,79,84,92,95,100,108,108,116,116,124,124,132,132,135,140,140,148,148,151,156,156,159,164,164,167,172,172,175,180,180,180,183,188,188,188,191,196,196,196,199,199,204,204,204,204,207,207,207,212,212,212,212,212,212,212,212,212,212,212,212,212,207,207,204,204,204,204,204,199,196,196,196,196,191,188,188,188,183,180,180,175,172,172,172,164,164,164,156,156,156,148,148,143,140,140,132,132,127,124,119,116,111,108,103,100,92,92,84,76,71,63,60,47,36,23,0,0,0,0,0,0,0,28,39,52,60,68,71,76,84,92,92,100,103,108,111,116,119,124,127,132,132,140,140,143,148,148,151,156,156,164,164,164,167,172,172,175,180,180,180,183,188,188,188,191,196,196,196,196,199,204,204,204,204,204,204,207,207,207,212,212,212,212,212,212,207,207,207,207,204,204,204,204,204,199,199,196,196,196,191,191,188,188,188,183,180,180,180,172,172,172,167,164,164,159,156,156,151,148,148,140,140,135,132,132,124,124,116,116,108,108,100,100,92,87,84,76,68,60,52,44,36,20,0,0,0,0,0,0,0,20,36,47,55,63,68,76,84,87,92,100,100,108,108,116,116,124,124,132,132,135,140,140,148,148,151,156,156,159,164,164,167,172,172,172,175,180,180,180,183,188,188,188,191,196,196,196,196,199,199,204,204,204,204,204,204,204,204,204,207,207,207,204,204,204,204,204,204,204,204,204,199,196,196,196,196,191,191,188,188,188,183,180,180,180,175,172,172,167,164,164,159,156,156,151,148,148,143,140,140,132,132,127,124,119,116,111,108,103,100,95,92,84,79,76,68,60,52,44,28,7,0,0,0,0,0,0,0,12,31,44,52,60,68,76,79,84,92,95,100,103,108,111,116,119,124,127,132,132,140,140,143,148,148,151,156,156,159,164,164,167,172,172,172,175,180,180,180,183,188,188,188,191,191,196,196,196,196,199,199,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,199,199,196,196,196,196,196,191,188,188,188,188,183,180,180,180,175,172,172,167,164,164,164,159,156,156,151,148,148,140,140,135,132,132,124,124,116,116,108,108,100,100,92,87,84,76,71,63,60,47,36,23,0,0,0,0,0,0,0,0,0,28,39,52,60,68,71,76,84,87,92,100,100,108,111,116,116,124,124,132,132,135,140,140,143,148,148,156,156,156,164,164,164,167,172,172,172,175,180,180,180,183,188,188,188,188,191,196,196,196,196,196,196,199,199,199,204,204,204,204,204,204,204,204,204,199,199,199,196,196,196,196,196,191,191,188,188,188,188,183,180,180,180,175,172,172,172,167,164,164,159,156,156,151,148,148,143,140,140,132,132,127,124,119,116,111,108,103,100,95,92,84,79,76,68,60,52,44,36,15,0,0,0,0,0,0,0,0,0,20,36,44,52,60,68,76,79,84,92,95,100,103,108,111,116,119,124,127,132,132,140,140,143,148,148,151,156,156,159,164,164,164,167,172,172,172,175,180,180,180,183,188,188,188,188,191,191,196,196,196,196,196,196,196,199,199,199,199,199,199,199,199,196,196,196,196,196,196,196,196,191,188,188,188,188,183,183,180,180,180,175,172,172,172,167,164,164,159,156,156,151,148,148,143,140,140,135,132,132,124,124,116,116,108,108,100,100,92,87,84,76,71,68,60,52,39,28,0,0,0,0,0,0,0,0,0,0,7,28,44,52,60,68,71,76,84,87,92,100,100,108,108,116,116,124,124,127,132,135,140,140,143,148,148,151,156,156,159,164,164,164,167,172,172,172,175,180,180,180,183,183,188,188,188,188,191,191,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,191,191,188,188,188,188,188,183,180,180,180,180,175,172,172,172,167,164,164,159,156,156,156,148,148,148,140,140,135,132,132,127,124,119,116,111,108,103,100,95,92,84,84,76,68,60,55,44,36,20,0,0,0,0,0,0,0,0,0,0,0,20,36,47,55,60,68,76,84,84,92,95,100,103,108,111,116,119,124,124,132,132,135,140,140,143,148,148,151,156,156,159,164,164,164,167,172,172,172,175,180,180,180,180,183,188,188,188,188,188,188,191,191,191,196,196,196,196,196,196,196,196,196,196,196,191,191,191,188,188,188,188,188,183,183,180,180,180,180,175,172,172,172,167,164,164,164,156,156,156,151,148,148,143,140,140,132,132,127,124,124,116,116,108,108,100,100,92,87,84,76,71,68,60,52,44,28,12,0,0,0,0,0,0,0,0,0,0,0,12,31,44,52,60,68,71,76,84,87,92,100,100,108,108,116,116,124,124,127,132,132,140,140,140,148,148,148,156,156,156,159,164,164,164,167,172,172,172,175,180,180,180,180,180,183,188,188,188,188,188,188,188,188,191,191,191,191,191,191,191,191,191,188,188,188,188,188,188,188,183,183,180,180,180,180,175,172,172,172,172,167,164,164,164,156,156,156,151,148,148,143,140,140,135,132,132,124,124,119,116,111,108,103,100,95,92,84,84,76,68,63,55,47,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,47,55,63,68,76,79,84,92,95,100,103,108,111,116,116,124,124,127,132,132,140,140,143,148,148,148,156,156,156,159,164,164,164,167,172,172,172,172,175,180,180,180,180,180,183,183,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,183,183,180,180,180,180,180,175,172,172,172,167,167,164,164,164,156,156,156,151,148,148,143,140,140,135,132,132,127,124,119,116,116,108,108,100,100,92,87,84,76,71,68,60,52,44,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,52,60,68,71,76,84,87,92,100,100,108,108,111,116,119,124,124,132,132,135,140,140,143,148,148,148,156,156,156,159,164,164,164,167,172,172,172,172,175,175,180,180,180,180,180,183,183,183,188,188,188,188,188,188,188,188,188,188,188,188,183,183,183,180,180,180,180,180,180,175,172,172,172,172,167,164,164,164,159,156,156,156,151,148,148,143,140,140,135,132,132,127,124,124,116,116,111,108,103,100,95,92,84,79,76,68,60,55,47,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,47,55,60,68,76,79,84,92,92,100,100,108,108,116,116,119,124,127,132,132,135,140,140,143,148,148,148,156,156,156,159,164,164,164,164,167,172,172,172,172,175,175,180,180,180,180,180,180,180,183,183,183,183,183,183,183,183,183,183,180,180,180,180,180,180,180,180,175,172,172,172,172,172,167,164,164,164,159,156,156,156,151,148,148,148,140,140,140,132,132,127,124,124,119,116,111,108,103,100,95,92,87,84,76,71,68,60,52,44,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,52,60,68,71,76,84,84,92,95,100,103,108,111,116,116,124,124,127,132,132,135,140,140,143,148,148,148,151,156,156,156,159,164,164,164,167,167,172,172,172,172,175,175,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,175,175,172,172,172,172,172,167,164,164,164,164,159,156,156,156,151,148,148,148,140,140,140,132,132,132,124,124,119,116,116,108,108,100,100,92,92,84,79,76,68,60,52,44,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,60,68,76,76,84,87,92,100,100,108,108,111,116,116,124,124,127,132,132,135,140,140,143,148,148,148,151,156,156,156,159,164,164,164,164,167,167,172,172,172,172,172,175,175,175,180,180,180,180,180,180,180,180,180,180,180,180,175,175,175,172,172,172,172,172,172,167,164,164,164,164,159,159,156,156,156,151,148,148,143,140,140,140,132,132,132,124,124,119,116,116,108,108,103,100,95,92,84,84,76,68,63,60,52,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,39,52,55,63,68,76,79,84,92,92,100,100,108,108,111,116,119,124,124,127,132,132,135,140,140,143,148,148,148,151,156,156,156,159,159,164,164,164,164,167,167,172,172,172,172,172,172,172,172,175,175,175,175,175,175,175,175,175,172,172,172,172,172,172,172,172,167,164,164,164,164,164,159,156,156,156,151,148,148,148,143,140,140,140,132,132,132,124,124,119,116,116,111,108,103,100,95,92,87,84,76,71,68,60,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,60,68,71,76,84,84,92,95,100,100,108,108,116,116,119,124,124,127,132,132,135,140,140,140,148,148,148,151,151,156,156,156,159,159,164,164,164,164,164,167,167,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,167,167,164,164,164,164,164,159,156,156,156,156,151,148,148,148,143,140,140,140,132,132,132,124,124,124,116,116,111,108,103,100,100,92,87,84,79,76,68,60,55,47,36,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,44,52,60,68,76,76,84,87,92,95,100,103,108,108,116,116,119,124,124,127,132,132,135,140,140,140,143,148,148,148,151,156,156,156,156,159,159,164,164,164,164,164,164,167,167,167,172,172,172,172,172,172,172,172,172,172,167,167,167,167,164,164,164,164,164,164,159,156,156,156,156,151,151,148,148,148,143,140,140,135,132,132,132,124,124,124,116,116,111,108,108,100,100,92,92,84,79,76,68,63,60,52,44,31,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,39,52,55,63,68,76,79,84,87,92,95,100,103,108,108,116,116,119,124,124,127,132,132,135,140,140,140,143,148,148,148,148,151,156,156,156,156,159,159,164,164,164,164,164,164,164,164,164,164,164,167,167,167,164,164,164,164,164,164,164,164,164,164,159,159,156,156,156,156,156,151,148,148,148,143,140,140,140,135,132,132,132,124,124,124,116,116,111,108,108,100,100,92,92,84,84,76,71,68,60,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,60,63,68,76,79,84,92,92,100,100,103,108,108,116,116,119,124,124,127,132,132,132,135,140,140,140,143,148,148,148,148,151,156,156,156,156,156,159,159,159,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,159,159,156,156,156,156,156,151,151,148,148,148,148,143,140,140,140,135,132,132,127,124,124,119,116,116,111,108,108,100,100,95,92,87,84,76,71,68,60,52,47,36,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,44,52,60,68,71,76,84,84,92,92,100,100,103,108,108,116,116,119,124,124,124,132,132,132,135,140,140,140,143,143,148,148,148,148,151,156,156,156,156,156,156,156,159,159,159,159,159,159,164,164,159,159,159,159,159,159,156,156,156,156,156,156,156,151,151,148,148,148,148,143,140,140,140,135,132,132,132,127,124,124,119,116,116,111,108,108,100,100,95,92,87,84,76,76,68,60,55,52,39,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,36,47,52,60,68,71,76,84,84,92,92,100,100,103,108,108,116,116,116,124,124,124,127,132,132,132,135,140,140,140,143,143,148,148,148,148,151,151,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,151,151,148,148,148,148,148,143,140,140,140,140,135,132,132,132,127,124,124,119,116,116,111,108,108,100,100,95,92,87,84,79,76,68,63,60,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,39,47,55,60,68,71,76,84,84,92,92,100,100,103,108,108,111,116,116,119,124,124,127,132,132,132,135,135,140,140,140,143,143,148,148,148,148,148,148,151,151,151,156,156,156,156,156,156,156,156,156,156,156,156,156,151,151,151,148,148,148,148,148,148,143,140,140,140,140,135,132,132,132,127,124,124,124,119,116,116,111,108,108,100,100,95,92,87,84,79,76,68,63,60,52,44,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,44,52,55,60,68,71,76,84,84,92,92,95,100,103,108,108,111,116,116,119,124,124,124,127,132,132,132,135,135,140,140,140,140,143,143,148,148,148,148,148,148,148,148,148,151,151,151,151,151,151,151,148,148,148,148,148,148,148,148,148,143,143,140,140,140,140,135,132,132,132,132,127,124,124,119,116,116,116,108,108,103,100,100,95,92,87,84,79,76,68,68,60,52,44,36,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,55,60,68,71,76,84,84,92,92,95,100,100,108,108,111,116,116,116,119,124,124,124,127,132,132,132,135,135,140,140,140,140,140,143,143,143,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,143,143,140,140,140,140,140,140,135,132,132,132,132,127,124,124,124,119,116,116,111,108,108,103,100,100,92,92,87,84,79,76,68,68,60,52,44,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,55,60,68,71,76,84,84,87,92,95,100,100,103,108,108,111,116,116,119,124,124,124,124,127,132,132,132,132,135,135,140,140,140,140,140,140,140,143,143,143,143,143,143,143,143,143,143,143,143,143,140,140,140,140,140,140,140,135,135,132,132,132,132,127,124,124,124,119,116,116,116,111,108,108,103,100,100,92,92,87,84,79,76,68,68,60,52,47,39,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,44,52,55,60,68,71,76,79,84,87,92,92,100,100,103,108,108,111,116,116,116,119,124,124,124,124,127,132,132,132,132,132,135,135,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,135,135,132,132,132,132,132,127,127,124,124,124,119,116,116,116,111,108,108,103,100,100,95,92,92,84,84,76,76,68,68,60,52,47,39,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,44,52,55,60,68,71,76,79,84,87,92,92,95,100,100,103,108,108,111,116,116,116,119,124,124,124,124,127,127,132,132,132,132,132,132,135,135,135,135,140,140,140,140,140,140,140,140,135,135,135,135,132,132,132,132,132,132,132,127,124,124,124,124,119,116,116,116,111,108,108,108,103,100,100,95,92,87,84,84,76,76,68,63,60,52,47,39,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,44,52,55,60,68,68,76,76,84,84,92,92,95,100,100,103,108,108,108,111,116,116,116,119,119,124,124,124,124,127,127,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,127,127,124,124,124,124,124,119,116,116,116,111,108,108,108,103,100,100,95,92,92,87,84,79,76,71,68,63,60,52,44,39,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,44,52,52,60,68,68,76,76,84,84,87,92,92,95,100,100,103,108,108,108,111,116,116,116,116,119,124,124,124,124,124,124,127,127,127,127,132,132,132,132,132,132,132,132,132,132,132,127,127,127,124,124,124,124,124,124,119,119,116,116,116,111,108,108,108,103,100,100,100,95,92,92,84,84,79,76,71,68,60,60,52,44,36,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,39,47,52,60,63,68,71,76,79,84,84,92,92,92,100,100,100,103,108,108,108,111,111,116,116,116,116,119,119,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,119,119,116,116,116,116,116,111,108,108,108,103,100,100,100,95,92,92,87,84,84,76,76,68,68,60,55,52,44,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,39,44,52,60,60,68,68,76,76,84,84,87,92,92,92,100,100,100,103,108,108,108,108,111,111,116,116,116,116,116,119,119,119,119,124,124,124,124,124,124,124,124,124,124,124,119,119,119,116,116,116,116,116,116,111,108,108,108,108,103,100,100,100,95,92,92,87,84,84,79,76,71,68,63,60,52,52,44,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,36,44,52,55,60,63,68,71,76,79,84,84,87,92,92,92,95,100,100,100,103,108,108,108,108,111,111,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,111,111,108,108,108,108,108,103,100,100,100,95,92,92,87,84,84,79,76,76,68,68,60,60,52,47,39,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,44,47,52,60,60,68,68,76,76,79,84,84,87,92,92,92,95,100,100,100,100,103,108,108,108,108,108,108,111,111,111,116,116,116,116,116,116,116,116,116,116,111,111,111,108,108,108,108,108,108,103,103,100,100,100,100,95,92,92,87,84,84,84,76,76,71,68,63,60,55,52,44,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,36,44,52,55,60,63,68,68,76,76,79,84,84,87,92,92,92,92,95,100,100,100,100,103,103,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,103,103,100,100,100,100,100,95,92,92,92,87,84,84,84,76,76,71,68,68,60,60,52,47,44,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,36,44,47,52,55,60,63,68,71,76,76,79,84,84,84,87,92,92,92,92,95,100,100,100,100,100,100,100,103,103,103,103,103,108,108,103,103,103,103,103,103,100,100,100,100,100,100,95,95,92,92,92,92,87,84,84,79,76,76,71,68,68,60,60,52,52,44,36,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,36,44,52,52,60,60,63,68,68,76,76,76,79,84,84,84,87,92,92,92,92,92,95,95,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,95,95,95,92,92,92,92,87,87,84,84,84,79,76,76,71,68,68,63,60,55,52,44,44,36,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,39,44,52,52,60,60,63,68,68,71,76,76,76,79,84,84,84,84,87,92,92,92,92,92,92,92,92,92,95,95,95,95,92,92,92,92,92,92,92,92,92,87,87,84,84,84,84,79,76,76,76,71,68,68,60,60,55,52,47,44,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,39,44,52,52,55,60,63,68,68,68,71,76,76,76,79,84,84,84,84,84,84,87,87,87,92,92,92,92,92,92,92,92,87,87,87,87,84,84,84,84,84,79,79,76,76,76,71,68,68,63,60,60,55,52,47,44,36,31,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,39,44,47,52,55,60,60,63,68,68,68,71,76,76,76,76,79,79,79,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,79,79,76,76,76,76,71,71,68,68,63,60,60,60,52,52,44,44,36,31,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,36,44,44,52,52,55,60,60,63,68,68,68,68,71,71,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,71,68,68,68,68,63,60,60,60,52,52,47,44,39,36,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,23,31,36,39,44,47,52,52,55,60,60,60,60,63,68,68,68,68,68,68,68,71,71,71,71,71,71,71,68,68,68,68,68,68,68,63,60,60,60,55,52,52,47,44,44,36,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,31,36,39,44,44,47,52,52,52,55,60,60,60,60,60,63,63,63,63,63,63,63,63,63,63,60,60,60,60,60,60,55,52,52,52,47,44,44,36,36,28,23,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,31,36,36,39,44,44,47,52,52,52,52,52,52,55,55,55,55,55,55,55,55,55,52,52,52,52,52,47,44,44,44,39,36,36,28,23,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,23,28,31,36,36,36,39,44,44,44,44,44,44,47,47,47,47,44,44,44,44,44,44,44,39,36,36,31,28,28,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,23,28,28,28,31,36,36,36,36,36,36,36,36,36,36,31,31,28,28,28,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,20,20,20,20,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t FLARE_TEXTURE_3[16384] ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,36,39,44,52,55,60,60,60,60,60,60,60,52,52,44,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,36,44,60,68,76,84,92,95,100,108,108,108,111,111,111,108,108,103,100,92,84,79,71,60,52,44,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,60,71,84,95,108,116,124,132,140,148,148,156,156,159,164,164,164,159,156,151,148,140,135,127,119,111,100,92,76,68,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,44,60,76,92,108,119,132,143,156,164,172,180,188,196,199,204,207,212,212,212,212,212,204,204,196,191,188,180,172,159,148,140,124,116,100,84,68,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,55,76,92,108,124,140,156,167,180,191,204,212,220,228,236,244,252,252,244,244,244,244,244,244,247,252,247,244,236,228,220,207,196,188,172,164,148,132,116,100,84,63,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,60,84,100,119,140,156,172,188,204,215,228,244,252,244,231,223,215,212,204,199,196,196,191,191,196,196,196,204,207,212,220,228,236,244,244,236,220,212,196,180,164,148,132,108,92,71,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,39,60,84,108,124,148,164,183,204,220,236,252,239,228,212,204,191,183,172,167,159,156,148,148,143,140,140,143,148,148,151,156,164,172,180,188,196,207,220,236,247,244,228,212,196,175,156,135,116,95,76,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,36,60,84,108,132,151,172,191,212,228,247,236,220,204,191,180,164,156,143,132,124,116,111,108,100,100,92,92,92,92,95,100,103,108,116,124,132,140,148,159,172,188,199,212,228,244,239,220,204,180,164,140,116,92,71,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,52,76,100,124,148,172,196,220,236,244,228,207,191,172,159,143,132,116,108,95,84,76,68,60,55,52,47,44,44,44,44,44,52,52,60,68,71,79,92,100,111,124,140,151,164,180,199,220,236,247,228,204,183,164,140,116,92,63,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,39,68,92,119,148,172,196,220,239,244,220,199,180,164,143,127,111,95,84,68,60,44,36,28,20,12,0,0,0,0,0,0,0,0,0,0,12,15,23,31,44,52,63,76,92,103,119,135,156,172,188,212,228,252,228,204,180,156,132,108,79,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,79,108,135,164,188,212,236,244,220,196,175,156,132,116,100,79,63,52,36,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,44,55,71,92,108,124,143,164,188,207,228,252,228,204,175,148,124,92,68,36,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,60,92,124,148,180,204,228,247,220,196,172,151,132,108,87,68,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,60,79,100,119,140,164,188,212,236,244,220,191,164,135,108,76,47,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,39,71,100,132,164,188,220,244,228,204,180,156,132,108,84,63,44,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,52,76,95,119,140,164,191,220,244,231,204,175,148,116,87,55,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,79,108,140,172,204,231,244,215,188,164,135,111,84,63,39,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,52,76,100,124,148,175,204,228,244,215,188,156,124,92,63,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,84,116,148,180,212,244,231,204,172,148,119,92,68,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,31,55,79,108,132,159,188,220,247,228,196,164,132,100,68,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,87,124,156,188,220,252,220,191,164,132,108,76,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,63,92,119,148,180,207,236,236,204,172,140,108,71,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,55,92,124,156,191,228,244,212,183,151,124,92,63,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,76,108,140,167,196,228,244,207,175,140,108,71,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,92,124,159,196,228,244,207,175,143,116,84,52,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,68,100,127,159,191,228,244,212,180,143,108,71,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,87,124,159,196,228,236,204,172,140,108,76,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,92,124,156,188,220,247,212,180,140,108,68,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,44,84,124,156,196,228,236,204,167,132,100,68,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,84,116,151,188,220,247,212,175,140,103,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,79,116,156,191,228,236,204,167,132,100,63,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,47,79,116,148,183,220,247,212,172,135,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,71,108,148,188,228,244,204,167,132,95,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,79,116,148,188,223,244,204,167,132,92,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,100,140,180,220,244,207,172,132,100,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,44,79,116,151,188,228,236,199,164,124,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,92,132,172,212,252,212,175,140,100,63,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,84,119,156,196,236,231,191,151,111,71,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,79,124,164,204,244,220,183,143,108,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,87,124,164,204,244,220,180,140,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,68,108,148,188,231,231,191,151,116,76,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,55,92,132,172,212,252,212,172,132,87,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,52,92,135,180,220,244,204,164,124,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,60,100,140,180,223,239,196,156,116,71,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,76,119,164,204,244,215,172,132,92,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,71,116,156,196,236,228,183,140,100,55,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,60,100,148,188,228,228,188,148,108,63,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,84,124,167,212,252,212,164,124,79,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,84,124,172,212,247,204,164,119,76,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,60,100,140,183,228,236,191,148,108,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,108,148,196,236,220,180,135,92,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,71,116,156,199,244,215,172,127,84,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,84,132,172,220,244,196,156,111,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,132,175,220,239,196,151,108,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,60,108,151,196,239,220,172,132,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,108,151,196,244,220,172,127,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,124,172,220,244,196,151,108,63,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,84,132,172,220,239,196,148,103,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,55,100,148,191,236,220,175,132,84,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,108,151,196,244,215,172,124,76,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,119,164,212,244,199,156,108,63,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,84,132,180,223,236,188,143,95,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,183,228,228,180,132,87,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,111,156,204,252,207,164,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,156,204,247,207,164,116,68,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,188,231,228,180,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,172,220,236,191,143,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,167,212,244,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,188,236,220,172,127,79,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,103,151,196,244,212,164,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,156,204,252,204,159,111,63,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,87,135,180,228,228,180,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,71,119,167,215,239,191,143,95,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,71,119,167,215,239,191,143,95,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,228,228,180,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,156,204,252,204,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,95,143,191,244,212,164,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,188,236,215,167,119,71,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,60,108,156,204,252,204,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,228,228,180,132,84,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,164,212,244,191,143,95,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,119,167,220,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,172,220,231,183,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,156,207,247,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,228,223,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,199,247,204,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,92,140,188,236,215,167,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,191,244,212,164,116,68,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,95,148,196,244,212,159,111,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,135,188,236,220,172,119,71,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,199,252,204,156,108,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,79,132,180,228,223,175,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,108,156,204,252,199,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,175,228,228,180,132,79,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,60,108,156,207,244,196,148,100,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,71,124,172,220,231,180,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,159,212,244,196,143,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,119,172,220,236,188,135,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,164,212,244,191,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,167,220,236,188,135,87,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,164,212,244,191,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,167,220,236,188,140,87,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,164,212,244,196,143,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,167,220,236,188,135,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,159,212,244,196,148,95,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,119,172,220,236,183,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,60,108,156,204,247,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,76,124,172,223,231,180,132,84,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,103,151,204,252,204,151,103,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,127,180,228,228,180,127,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,247,207,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,231,220,172,124,76,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,191,244,212,164,116,68,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,92,140,188,236,215,167,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,135,188,236,220,172,124,71,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,100,148,196,244,212,159,111,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,79,127,180,228,228,180,132,79,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,55,108,156,204,252,204,156,103,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,71,119,172,220,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,68,116,164,212,244,196,143,95,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,159,207,244,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,172,220,236,183,135,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,244,207,159,111,63,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,87,135,183,231,220,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,188,236,220,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,244,212,164,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,172,220,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,164,212,244,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,164,212,247,199,151,103,55,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,79,127,175,223,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,244,212,164,119,71,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,95,143,188,236,220,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,228,228,180,135,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,111,159,204,252,204,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,164,212,244,199,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,175,223,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,239,220,172,124,79,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,55,100,148,196,239,215,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,175,220,236,188,143,100,52,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,167,212,244,196,151,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,63,108,156,204,247,212,164,119,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,52,100,140,188,231,228,180,132,87,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,135,180,228,228,188,140,95,52,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,116,164,207,252,204,159,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,71,116,164,204,252,207,164,119,76,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,100,140,188,228,228,183,140,92,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,95,140,183,228,228,188,140,100,55,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,76,119,164,207,252,207,164,116,71,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,116,164,204,252,212,164,124,79,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,60,100,148,188,231,228,183,140,95,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,92,140,180,228,236,191,148,108,63,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,84,127,172,212,247,204,159,116,71,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,71,116,156,204,244,220,175,132,92,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,68,111,156,196,236,220,180,135,92,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,132,175,220,244,204,159,119,76,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,100,140,180,223,236,196,156,111,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,63,108,148,191,231,228,188,148,108,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,47,87,127,167,212,252,212,172,127,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,79,124,164,204,244,220,180,140,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,76,116,156,196,236,228,183,140,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,92,135,175,215,247,207,167,127,92,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,68,108,148,188,228,236,196,156,116,76,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,68,108,148,188,228,236,196,159,124,84,47,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,68,103,140,180,220,244,207,167,127,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,76,116,156,196,236,228,191,156,116,79,44,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,100,135,172,212,252,215,175,140,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,47,87,124,164,204,244,228,188,151,116,79,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,100,132,172,204,244,220,183,148,108,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,55,92,132,172,207,244,220,188,148,116,79,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,100,132,167,204,239,228,188,151,116,76,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,63,100,140,175,212,247,220,183,148,116,84,52,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,68,100,132,167,204,236,228,196,156,119,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,68,108,140,180,212,247,220,188,151,119,87,55,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,39,68,103,135,172,204,236,231,196,159,124,84,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,71,108,143,180,212,247,223,188,156,124,92,60,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,47,76,108,140,172,204,239,228,196,159,124,92,52,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,71,108,140,175,212,244,228,196,164,132,100,71,44,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,87,116,148,180,212,244,228,196,159,124,92,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,71,108,140,172,204,236,236,204,172,140,116,84,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,44,68,100,127,156,188,220,252,220,188,156,124,92,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,68,103,135,167,199,231,244,212,180,156,124,100,71,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,60,84,111,140,167,196,228,244,215,183,151,119,84,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,68,100,132,164,191,220,252,223,196,167,140,116,92,68,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,52,76,100,127,156,180,212,236,236,207,175,148,116,84,52,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,92,124,151,180,212,239,236,212,183,156,132,108,84,60,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,52,76,100,119,148,172,196,223,252,228,196,167,140,108,76,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,84,111,140,172,196,228,252,228,199,175,151,132,108,84,68,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,55,76,100,116,140,164,188,212,236,236,212,183,156,127,100,68,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,71,100,132,156,183,212,236,244,220,196,172,151,132,111,92,76,60,39,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,47,68,84,100,124,140,164,188,207,231,247,220,196,172,140,116,84,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,60,87,116,140,164,191,215,239,244,220,196,180,156,140,124,103,87,71,60,44,36,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,28,39,52,68,79,95,111,132,148,167,188,207,228,252,228,204,180,156,127,100,76,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,71,100,124,148,172,196,220,239,244,223,204,188,167,151,135,119,108,92,84,68,60,52,44,36,31,28,20,20,20,20,20,20,23,28,36,39,47,55,68,76,87,100,116,127,143,159,175,196,212,231,252,228,207,183,159,135,111,84,60,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,55,79,108,127,151,172,196,215,236,252,231,212,196,180,167,156,140,132,119,108,100,92,84,79,76,71,68,68,68,68,68,76,76,84,92,100,108,116,124,135,148,164,175,188,204,223,239,244,228,204,183,164,140,116,92,68,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,60,84,108,127,148,172,188,207,228,244,244,228,215,204,188,180,167,156,148,140,135,132,124,124,119,116,116,116,119,124,127,132,140,148,156,164,172,183,196,212,223,236,252,236,215,196,180,159,140,116,95,71,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,60,84,103,124,143,164,180,196,212,228,239,252,236,228,220,207,199,191,188,180,175,172,172,167,167,167,172,172,180,180,188,196,204,212,220,231,244,244,236,220,204,188,172,151,132,116,92,71,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,60,76,95,116,132,148,164,180,191,204,215,228,236,247,247,244,236,228,228,220,220,220,220,220,220,223,228,231,236,244,252,244,236,220,212,196,188,172,156,140,124,108,87,68,47,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,52,68,84,100,116,132,143,156,167,180,188,196,204,212,220,223,228,231,236,236,236,236,236,231,228,220,215,212,204,196,183,172,164,148,140,124,108,92,76,60,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,52,68,84,95,108,119,132,140,148,156,164,172,175,180,180,188,188,188,188,183,180,180,172,167,159,156,143,135,124,116,100,92,76,60,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,47,60,71,84,92,100,108,116,119,124,132,132,135,135,140,135,132,132,127,124,116,111,103,95,84,76,68,52,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,31,44,52,60,68,71,76,79,84,84,87,87,84,84,84,76,76,68,60,52,44,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,28,28,36,36,36,36,36,36,31,28,23,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t FLARE_TEXTURE_4[16384] ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,36,39,44,52,55,60,60,60,60,60,60,60,52,52,44,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,36,44,60,68,76,84,92,95,100,108,108,108,111,111,111,108,108,103,100,92,84,79,71,60,52,44,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,60,71,84,95,108,116,124,132,140,148,148,156,156,159,164,164,164,159,156,151,148,140,135,127,119,111,100,92,76,68,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,44,60,76,92,108,119,132,143,156,164,172,180,188,196,199,204,207,212,212,212,212,212,204,204,196,191,188,180,172,159,148,140,124,116,100,84,68,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,55,76,92,108,124,140,156,167,180,191,204,212,220,228,236,244,252,252,252,252,252,252,252,252,252,252,247,244,236,228,220,207,196,188,172,164,148,132,116,100,84,63,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,60,84,100,119,140,156,172,188,204,215,228,244,252,252,252,252,247,247,247,244,244,244,244,244,244,244,244,247,247,247,247,252,252,252,244,236,220,212,196,180,164,148,132,108,92,71,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,39,60,84,108,124,148,164,183,204,220,236,252,252,252,247,247,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,247,247,252,252,244,228,212,196,175,156,135,116,95,76,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,36,60,84,108,132,151,172,191,212,228,247,252,247,247,244,244,244,244,244,239,239,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,239,239,244,244,244,244,244,247,252,252,239,220,204,180,164,140,116,92,71,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,52,76,100,124,148,172,196,220,236,252,252,247,244,244,244,244,239,236,236,236,236,236,236,231,231,231,231,231,231,231,231,231,231,231,231,236,236,236,236,236,236,239,239,244,244,244,244,247,252,247,228,204,183,164,140,116,92,63,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,39,68,92,119,148,172,196,220,239,252,247,244,244,244,244,239,236,236,236,236,231,231,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,231,236,236,236,236,236,239,244,244,244,247,252,252,228,204,180,156,132,108,79,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,79,108,135,164,188,212,236,252,247,244,244,244,239,236,236,236,236,231,228,228,228,228,228,223,223,223,220,220,220,220,220,220,220,220,220,220,220,223,223,228,228,228,228,228,228,231,236,236,236,239,244,244,244,247,252,252,228,204,175,148,124,92,68,36,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,60,92,124,148,180,204,228,252,252,244,244,244,239,236,236,236,231,228,228,228,228,223,220,220,220,220,220,220,220,220,215,215,215,215,215,215,215,220,220,220,220,220,220,220,223,223,228,228,228,231,231,236,236,236,239,244,244,247,252,244,220,191,164,135,108,76,47,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,39,71,100,132,164,188,220,244,252,247,244,244,239,236,236,236,231,228,228,228,223,220,220,220,220,215,215,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,215,215,220,220,220,220,223,223,228,228,228,231,236,236,236,244,244,244,247,252,231,204,175,148,116,87,55,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,79,108,140,172,204,231,252,247,244,244,239,236,236,236,228,228,228,223,220,220,220,220,215,212,212,212,212,212,212,207,207,207,204,204,204,204,204,204,204,207,207,207,212,212,212,212,212,215,215,220,220,220,223,228,228,228,231,236,236,239,244,244,247,252,244,215,188,156,124,92,63,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,84,116,148,180,212,244,252,247,244,244,236,236,236,231,228,228,223,220,220,220,215,212,212,212,212,207,207,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,207,212,212,212,212,215,220,220,220,223,228,228,228,231,236,236,239,244,244,247,252,228,196,164,132,100,68,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,87,124,156,188,220,252,252,244,244,239,236,236,231,228,228,223,220,220,220,215,212,212,212,207,204,204,204,204,204,199,199,196,196,196,196,196,196,196,196,196,196,196,196,196,199,199,204,204,204,204,207,207,212,212,212,215,220,220,223,228,228,228,236,236,236,244,244,247,252,236,204,172,140,108,71,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,55,92,124,156,191,228,252,247,244,244,239,236,236,228,228,228,220,220,220,215,212,212,207,204,204,204,204,199,196,196,196,196,196,196,191,191,191,191,191,191,191,191,191,191,196,196,196,196,196,196,199,199,204,204,204,207,212,212,212,215,220,220,223,228,228,231,236,236,239,244,244,252,244,207,175,140,108,71,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,92,124,159,196,228,252,247,244,244,236,236,231,228,228,223,220,220,215,212,212,207,204,204,204,199,196,196,196,196,191,191,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,191,196,196,196,196,199,204,204,204,207,212,212,212,215,220,220,223,228,228,236,236,239,244,244,252,244,212,180,143,108,71,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,87,124,159,196,228,252,247,244,239,236,236,231,228,228,220,220,215,212,212,207,204,204,204,199,196,196,196,191,188,188,188,188,188,183,183,180,180,180,180,180,180,180,180,180,180,183,183,188,188,188,188,188,191,196,196,196,196,199,204,204,207,212,212,215,220,220,223,228,228,231,236,239,244,244,247,247,212,180,140,108,68,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,44,84,124,156,196,228,252,247,244,239,236,236,228,228,223,220,220,215,212,212,207,204,204,199,196,196,196,191,188,188,188,183,183,180,180,180,180,180,180,175,175,175,175,175,175,180,180,180,180,180,180,180,183,188,188,188,188,191,196,196,196,199,204,204,207,212,212,215,220,220,228,228,231,236,236,244,244,247,247,212,175,140,103,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,79,116,156,191,228,252,247,244,239,236,236,228,228,223,220,220,212,212,207,204,204,199,196,196,196,188,188,188,183,180,180,180,180,175,175,172,172,172,172,172,172,172,172,172,172,172,172,172,172,175,175,180,180,180,180,183,188,188,188,191,196,196,199,204,204,204,212,212,215,220,220,223,228,231,236,236,244,244,247,247,212,172,135,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,71,108,148,188,228,252,247,244,239,236,231,228,228,220,220,215,212,212,207,204,204,196,196,196,188,188,188,183,180,180,180,175,172,172,172,172,172,167,167,167,164,164,164,164,164,164,167,167,167,172,172,172,172,172,175,180,180,180,183,188,188,188,191,196,196,199,204,204,207,212,212,220,220,223,228,231,236,236,244,244,252,244,204,167,132,92,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,100,140,180,220,252,247,244,239,236,231,228,228,220,220,215,212,212,204,204,199,196,196,191,188,188,183,180,180,180,175,172,172,172,167,167,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,167,172,172,172,172,175,180,180,183,188,188,188,196,196,196,204,204,207,212,212,220,220,223,228,231,236,236,244,244,252,236,199,164,124,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,92,132,172,212,252,247,244,239,236,236,228,228,220,220,215,212,207,204,204,199,196,196,188,188,183,180,180,180,175,172,172,172,167,164,164,164,164,159,159,156,156,156,156,156,156,156,156,156,156,156,159,159,164,164,164,164,167,172,172,172,175,180,180,183,188,188,191,196,196,199,204,207,212,212,220,220,223,228,231,236,236,244,244,252,231,191,151,111,71,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,79,124,164,204,244,252,244,244,236,236,228,228,220,220,215,212,207,204,204,196,196,191,188,188,183,180,180,175,172,172,167,164,164,164,159,159,156,156,156,156,156,151,151,151,151,151,151,151,151,156,156,156,156,156,156,159,164,164,164,167,172,172,172,175,180,180,183,188,188,196,196,199,204,204,212,212,220,220,223,228,231,236,239,244,247,252,220,180,140,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,68,108,148,188,231,252,244,244,236,236,228,228,220,220,215,212,207,204,204,196,196,191,188,188,180,180,175,172,172,167,164,164,164,159,156,156,156,151,151,148,148,148,148,148,148,148,148,148,148,148,148,148,148,151,151,156,156,156,156,159,164,164,167,172,172,175,180,180,183,188,188,196,196,199,204,204,212,212,220,220,223,228,231,236,239,244,247,252,212,172,132,87,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,52,92,135,180,220,252,247,244,239,236,231,228,223,220,215,212,207,204,204,196,196,188,188,183,180,180,175,172,172,164,164,164,159,156,156,156,151,148,148,148,148,143,143,140,140,140,140,140,140,140,140,143,143,148,148,148,148,148,151,156,156,156,159,164,164,167,172,172,175,180,180,188,188,191,196,199,204,204,212,212,220,220,228,228,236,236,244,244,252,239,196,156,116,71,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,76,119,164,204,244,247,244,239,236,231,228,223,220,215,212,207,204,204,196,196,188,188,183,180,180,172,172,167,164,164,159,156,156,151,148,148,148,148,143,140,140,140,140,140,140,135,135,135,135,140,140,140,140,140,140,140,143,148,148,148,151,156,156,156,164,164,164,172,172,175,180,180,188,188,191,196,199,204,204,212,212,220,220,228,228,236,236,244,244,252,228,183,140,100,55,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,60,100,148,188,228,252,244,244,236,236,228,228,220,220,212,212,204,204,196,196,188,188,183,180,175,172,172,167,164,164,156,156,156,148,148,148,143,140,140,140,140,135,132,132,132,132,132,132,132,132,132,132,132,132,135,135,140,140,140,143,148,148,148,151,156,156,159,164,164,167,172,175,180,180,188,188,191,196,199,204,207,212,215,220,223,228,231,236,239,244,247,252,212,164,124,79,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,84,124,172,212,252,247,244,236,236,228,228,220,220,212,212,204,204,196,196,188,188,183,180,175,172,172,164,164,159,156,156,151,148,148,143,140,140,140,135,132,132,132,132,127,127,127,124,124,124,124,127,127,127,132,132,132,132,132,135,140,140,140,148,148,148,156,156,156,164,164,167,172,172,180,180,188,188,191,196,199,204,207,212,215,220,223,228,231,236,239,244,252,236,191,148,108,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,108,148,196,236,252,244,239,236,231,228,223,220,215,212,207,204,199,196,191,188,183,180,175,172,172,164,164,159,156,156,148,148,143,140,140,140,135,132,132,132,127,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,127,132,132,132,135,140,140,143,148,148,151,156,156,164,164,167,172,172,180,180,188,188,196,196,204,204,212,212,220,220,228,228,236,236,244,244,252,215,172,127,84,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,84,132,172,220,252,244,244,236,236,228,228,220,215,212,207,204,199,196,191,188,183,180,175,172,172,164,164,159,156,151,148,148,143,140,140,135,132,132,127,124,124,124,124,119,119,116,116,116,116,116,116,116,116,116,119,119,124,124,124,127,132,132,132,135,140,140,143,148,151,156,156,159,164,167,172,175,180,180,188,188,196,196,204,204,212,212,220,220,228,231,236,239,244,247,239,196,151,108,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,60,108,151,196,239,247,244,239,236,231,228,220,220,212,212,204,204,196,196,188,188,180,180,172,172,164,164,156,156,151,148,148,140,140,135,132,132,127,124,124,124,119,116,116,116,116,116,111,111,111,111,111,111,111,116,116,116,116,119,119,124,124,127,132,132,135,140,140,143,148,148,156,156,159,164,167,172,175,180,183,188,191,196,199,204,207,212,215,220,223,228,236,236,244,244,252,220,172,127,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,124,172,220,252,244,244,236,236,228,223,220,215,212,207,204,196,196,188,188,180,180,172,172,164,164,159,156,151,148,148,140,140,135,132,132,124,124,124,119,116,116,116,111,108,108,108,108,108,108,108,108,108,108,108,108,111,111,116,116,116,119,124,124,127,132,132,140,140,143,148,148,156,156,159,164,167,172,175,180,183,188,191,196,199,204,212,212,220,220,228,228,236,239,244,247,239,196,148,103,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,55,100,148,191,236,247,244,239,236,228,228,220,220,212,207,204,199,196,191,188,183,180,175,172,167,164,159,156,151,148,143,140,140,132,132,127,124,124,119,116,116,111,108,108,108,108,103,103,100,100,100,100,100,100,103,103,108,108,108,108,111,116,116,116,124,124,124,132,132,135,140,143,148,148,156,156,164,164,172,172,180,180,188,188,196,196,204,204,212,215,220,223,228,236,236,244,244,252,215,172,124,76,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,119,164,212,252,244,244,236,236,228,223,220,215,212,204,204,196,196,188,183,180,175,172,167,164,159,156,151,148,148,140,140,132,132,127,124,124,116,116,116,108,108,108,103,100,100,100,100,100,100,95,95,95,100,100,100,100,100,103,108,108,108,111,116,116,119,124,124,132,132,135,140,143,148,148,156,156,164,164,172,172,180,180,188,191,196,199,204,207,212,220,220,228,228,236,239,244,252,236,188,143,95,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,183,228,252,244,239,236,231,228,220,220,212,207,204,199,196,188,188,180,180,172,172,164,164,156,156,148,148,140,140,132,132,127,124,119,116,116,111,108,108,103,100,100,100,95,92,92,92,92,92,92,92,92,92,92,95,100,100,100,100,108,108,108,116,116,119,124,124,132,132,135,140,143,148,151,156,159,164,167,172,175,180,183,188,196,196,204,204,212,215,220,223,228,236,236,244,247,252,207,164,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,156,204,247,247,244,236,236,228,223,220,215,212,204,204,196,196,188,183,180,175,172,164,164,156,156,148,148,140,140,132,132,127,124,119,116,116,108,108,103,100,100,100,95,92,92,92,87,87,87,84,84,84,87,87,92,92,92,92,95,100,100,103,108,108,111,116,116,124,124,132,132,135,140,143,148,151,156,159,164,172,172,180,180,188,188,196,199,204,207,212,220,220,228,231,236,239,244,252,228,180,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,172,220,252,244,244,236,231,228,220,220,212,207,204,199,196,188,188,180,180,172,167,164,159,156,151,148,143,140,135,132,127,124,119,116,116,108,108,103,100,100,95,92,92,87,84,84,84,84,84,84,84,84,84,84,84,84,87,92,92,92,95,100,100,108,108,111,116,116,124,124,132,132,140,140,148,148,156,156,164,164,172,175,180,183,188,196,196,204,204,212,215,220,223,228,236,236,244,247,244,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,188,236,247,244,239,236,228,228,220,215,212,204,204,196,196,188,183,180,175,172,164,164,156,156,148,143,140,135,132,127,124,119,116,116,108,108,100,100,95,92,92,87,84,84,84,79,79,76,76,76,76,76,76,76,79,84,84,84,87,92,92,95,100,100,103,108,111,116,116,124,124,132,132,140,140,148,151,156,159,164,167,172,180,180,188,188,196,199,204,212,212,220,223,228,231,236,244,244,252,212,164,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,156,204,252,247,244,236,236,228,223,220,212,212,204,199,196,191,188,180,180,172,167,164,159,156,148,148,140,140,132,132,124,124,116,116,108,108,100,100,95,92,92,84,84,84,79,76,76,76,76,71,71,71,71,71,76,76,76,76,79,84,84,87,92,92,100,100,103,108,111,116,119,124,127,132,135,140,143,148,156,156,164,164,172,175,180,183,188,196,196,204,207,212,215,220,228,228,236,239,244,252,228,180,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,71,119,167,215,252,244,244,236,231,228,220,220,212,207,204,196,196,188,183,180,175,172,164,164,156,151,148,143,140,135,132,124,124,116,116,108,108,100,100,95,92,87,84,84,79,76,76,71,71,68,68,68,68,68,68,68,68,68,71,76,76,76,84,84,87,92,92,100,100,103,108,111,116,119,124,127,132,140,140,148,148,156,159,164,167,172,180,180,188,191,196,204,204,212,215,220,223,228,236,236,244,247,239,191,143,95,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,228,252,244,239,236,228,228,220,215,212,204,204,196,191,188,180,180,172,172,164,159,156,148,148,140,140,132,127,124,119,116,111,108,103,100,95,92,87,84,84,76,76,76,68,68,68,63,63,60,60,60,60,60,63,68,68,68,71,76,76,79,84,84,92,92,100,100,108,108,116,116,124,124,132,135,140,143,148,156,156,164,164,172,175,180,188,188,196,199,204,207,212,220,223,228,231,236,244,247,252,204,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,95,143,191,244,247,244,236,236,228,223,220,212,212,204,199,196,188,188,180,175,172,167,164,156,156,148,143,140,135,132,124,124,116,116,108,103,100,95,92,87,84,84,76,76,71,68,68,63,60,60,60,60,55,55,60,60,60,60,60,63,68,68,76,76,79,84,84,92,92,100,100,108,111,116,119,124,127,132,140,140,148,151,156,159,164,172,172,180,183,188,196,196,204,207,212,215,220,228,231,236,244,244,252,215,167,119,71,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,60,108,156,204,252,247,244,236,231,228,220,220,212,207,204,196,196,188,183,180,172,172,164,159,156,151,148,140,140,132,127,124,119,116,108,108,100,100,92,92,84,84,76,76,71,68,63,60,60,60,55,52,52,52,52,52,52,52,55,60,60,63,68,68,71,76,79,84,87,92,95,100,103,108,111,116,124,124,132,135,140,143,148,156,156,164,167,172,180,180,188,191,196,204,204,212,215,220,228,228,236,239,244,252,228,180,132,84,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,164,212,252,244,244,236,231,228,220,215,212,207,204,196,191,188,183,180,172,167,164,159,156,148,148,140,135,132,124,124,116,111,108,103,100,95,92,84,84,76,76,71,68,63,60,60,55,52,52,47,47,47,44,47,47,52,52,52,55,60,60,68,68,71,76,79,84,87,92,100,100,108,108,116,119,124,127,132,140,140,148,151,156,164,164,172,175,180,188,188,196,199,204,212,212,220,223,228,236,236,244,247,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,172,220,252,244,239,236,228,228,220,215,212,204,204,196,191,188,180,175,172,167,164,156,151,148,143,140,132,132,124,119,116,108,108,100,100,92,87,84,79,76,71,68,63,60,55,52,52,47,44,44,44,44,44,44,44,44,47,52,52,55,60,60,68,68,76,76,84,84,92,95,100,103,108,116,116,124,124,132,135,140,148,148,156,159,164,172,172,180,183,188,196,196,204,207,212,220,223,228,231,236,244,247,247,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,228,252,244,239,236,228,223,220,212,212,204,199,196,188,188,180,175,172,164,164,156,151,148,140,140,132,127,124,116,116,108,103,100,95,92,84,84,76,76,68,63,60,55,52,52,44,44,44,39,36,36,36,36,36,39,44,44,47,52,55,60,60,68,68,76,79,84,87,92,100,100,108,111,116,119,124,132,132,140,143,148,156,156,164,167,172,180,183,188,191,196,204,207,212,220,220,228,231,236,244,244,252,204,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,92,140,188,236,247,244,236,236,228,223,220,212,212,204,199,196,188,183,180,172,172,164,159,156,148,148,140,135,132,124,124,116,111,108,100,100,92,87,84,79,76,68,68,60,60,52,52,44,44,39,36,36,36,31,31,31,36,36,36,44,44,47,52,55,60,63,68,71,76,84,84,92,95,100,103,108,116,119,124,127,132,140,143,148,151,156,164,167,172,180,180,188,191,196,204,204,212,215,220,228,231,236,244,244,252,212,164,116,68,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,95,148,196,244,247,244,236,231,228,223,220,212,207,204,196,196,188,183,180,172,167,164,159,156,148,143,140,132,132,124,119,116,108,108,100,95,92,84,84,76,71,68,63,60,55,52,44,44,39,36,31,28,28,28,28,28,28,31,36,36,44,44,47,52,60,60,68,68,76,79,84,92,92,100,103,108,111,116,124,127,132,140,140,148,151,156,164,164,172,175,180,188,191,196,199,204,212,215,220,228,228,236,239,244,252,220,172,119,71,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,199,252,247,244,236,231,228,220,220,212,207,204,196,191,188,180,180,172,167,164,156,156,148,143,140,132,127,124,119,116,108,103,100,92,92,84,79,76,71,68,60,60,52,47,44,39,36,31,28,28,23,20,20,20,23,28,28,36,36,44,44,52,52,60,63,68,76,76,84,87,92,100,100,108,111,116,124,124,132,135,140,148,148,156,159,164,172,175,180,188,188,196,199,204,212,215,220,223,228,236,239,244,252,223,175,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,108,156,204,252,244,244,236,231,228,220,220,212,207,204,196,191,188,180,180,172,167,164,156,151,148,140,140,132,127,124,116,116,108,103,100,92,87,84,79,76,68,63,60,55,52,44,44,36,31,28,28,20,20,20,20,20,20,23,28,28,36,39,44,47,52,60,60,68,71,76,84,84,92,95,100,108,108,116,119,124,132,135,140,148,148,156,159,164,172,172,180,183,188,196,199,204,212,212,220,223,228,236,239,244,252,228,180,132,79,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,60,108,156,207,252,244,244,236,231,228,220,215,212,204,204,196,191,188,180,175,172,164,164,156,151,148,140,140,132,127,124,116,111,108,100,100,92,87,84,76,76,68,63,60,52,47,44,39,36,28,28,20,20,12,12,12,12,15,20,23,28,31,36,44,44,52,55,60,68,68,76,79,84,92,95,100,108,108,116,119,124,132,132,140,143,148,156,159,164,172,172,180,183,188,196,199,204,212,212,220,223,228,236,239,244,247,231,180,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,159,212,252,244,244,236,231,228,220,215,212,204,204,196,191,188,180,175,172,164,164,156,151,148,140,135,132,124,124,116,111,108,100,100,92,87,84,76,71,68,60,60,52,47,44,36,36,28,23,20,12,12,7,7,12,12,20,20,28,28,36,39,44,52,55,60,63,68,76,79,84,92,92,100,103,108,116,119,124,132,132,140,143,148,156,159,164,172,172,180,183,188,196,196,204,207,212,220,223,228,236,236,244,247,236,188,135,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,164,212,252,244,244,236,231,228,220,215,212,204,204,196,191,188,180,175,172,164,164,156,151,148,140,135,132,124,124,116,111,108,100,95,92,84,84,76,71,68,60,55,52,47,44,36,31,28,20,20,12,7,0,0,0,12,15,20,23,28,36,39,44,52,52,60,63,68,76,79,84,92,92,100,103,108,116,119,124,132,132,140,143,148,156,156,164,167,172,180,183,188,196,196,204,207,212,220,223,228,236,236,244,247,236,188,135,87,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,164,212,252,244,244,236,231,228,220,215,212,204,204,196,191,188,180,175,172,164,164,156,151,148,140,135,132,124,124,116,111,108,100,95,92,84,84,76,71,68,60,55,52,44,44,36,31,28,20,20,12,7,0,0,0,12,12,20,23,28,36,39,44,52,52,60,63,68,76,79,84,92,92,100,103,108,116,119,124,132,132,140,143,148,156,156,164,167,172,180,183,188,196,196,204,207,212,220,223,228,236,236,244,247,236,188,140,87,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,164,212,252,244,244,236,231,228,220,215,212,204,204,196,191,188,180,175,172,164,164,156,151,148,140,135,132,124,124,116,111,108,100,95,92,84,84,76,71,68,60,60,52,47,44,36,31,28,20,20,12,12,0,0,7,12,15,20,28,28,36,39,44,52,52,60,63,68,76,79,84,92,92,100,103,108,116,119,124,132,132,140,143,148,156,159,164,167,172,180,183,188,196,196,204,207,212,220,223,228,236,236,244,247,236,188,135,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,159,212,252,244,244,236,231,228,220,215,212,204,204,196,191,188,180,175,172,164,164,156,151,148,140,140,132,127,124,116,111,108,100,100,92,87,84,76,71,68,60,60,52,47,44,36,36,28,23,20,15,12,12,12,12,12,20,20,28,31,36,44,44,52,55,60,68,68,76,79,84,92,92,100,103,108,116,119,124,132,132,140,143,148,156,159,164,172,172,180,183,188,196,199,204,212,212,220,223,228,236,236,244,247,236,183,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,60,108,156,204,252,244,244,236,231,228,220,215,212,204,204,196,191,188,180,180,172,167,164,156,151,148,140,140,132,127,124,116,111,108,103,100,92,87,84,76,76,68,63,60,52,52,44,39,36,31,28,23,20,20,15,12,15,20,20,28,28,36,36,44,47,52,55,60,68,71,76,84,84,92,95,100,108,108,116,119,124,132,135,140,143,148,156,159,164,172,172,180,183,188,196,199,204,212,212,220,223,228,236,239,244,252,231,180,132,84,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,103,151,204,252,247,244,236,231,228,220,220,212,207,204,196,191,188,180,180,172,167,164,156,156,148,143,140,132,127,124,116,116,108,103,100,92,92,84,79,76,68,68,60,55,52,47,44,36,36,28,28,23,20,20,20,20,20,28,28,31,36,39,44,52,52,60,63,68,71,76,84,84,92,95,100,108,111,116,124,124,132,135,140,148,148,156,159,164,172,175,180,188,188,196,199,204,212,212,220,223,228,236,239,244,252,228,180,127,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,247,247,244,236,231,228,220,220,212,207,204,196,196,188,183,180,172,167,164,156,156,148,143,140,132,132,124,119,116,108,108,100,95,92,84,84,76,71,68,60,60,52,52,44,44,36,36,28,28,28,23,23,28,28,28,31,36,39,44,47,52,55,60,68,68,76,76,84,87,92,100,100,108,111,116,124,124,132,135,140,148,151,156,164,164,172,175,180,188,188,196,199,204,212,215,220,228,228,236,239,244,252,220,172,124,76,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,191,244,247,244,236,236,228,223,220,212,207,204,196,196,188,183,180,172,172,164,159,156,148,148,140,135,132,124,119,116,111,108,100,100,92,87,84,76,76,68,63,60,55,52,47,44,44,36,36,31,28,28,28,28,31,36,36,39,44,44,52,52,60,60,68,71,76,79,84,92,92,100,103,108,116,116,124,127,132,140,140,148,151,156,164,164,172,175,180,188,191,196,204,204,212,215,220,228,228,236,239,244,252,215,167,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,135,188,236,247,244,236,236,228,223,220,212,212,204,199,196,188,188,180,175,172,164,159,156,151,148,140,135,132,124,124,116,111,108,103,100,92,92,84,79,76,71,68,63,60,55,52,47,44,44,39,36,36,36,36,36,36,36,39,44,44,52,52,60,60,68,68,76,76,84,87,92,95,100,108,108,116,119,124,132,132,140,143,148,156,156,164,167,172,180,180,188,191,196,204,207,212,215,220,228,231,236,244,244,252,212,159,111,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,79,127,180,228,252,244,239,236,228,228,220,215,212,204,199,196,188,188,180,175,172,164,164,156,151,148,140,140,132,127,124,119,116,108,108,100,95,92,87,84,76,76,68,68,60,60,55,52,47,44,44,44,39,39,39,39,44,44,44,47,52,52,55,60,63,68,71,76,79,84,92,92,100,100,108,111,116,124,124,132,135,140,148,148,156,159,164,172,172,180,183,188,196,196,204,207,212,220,220,228,231,236,244,247,252,204,156,103,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,71,119,172,220,252,244,239,236,228,228,220,215,212,204,204,196,191,188,180,180,172,167,164,156,156,148,143,140,132,132,124,119,116,111,108,100,100,92,92,84,84,76,76,68,68,60,60,55,52,52,47,44,44,44,44,44,44,47,52,52,52,60,60,63,68,71,76,79,84,87,92,95,100,108,108,116,116,124,127,132,140,140,148,151,156,159,164,172,175,180,183,188,196,199,204,212,212,220,223,228,236,236,244,247,244,196,143,95,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,159,207,252,244,244,236,231,228,220,220,212,207,204,196,196,188,183,180,172,172,164,159,156,148,148,140,135,132,127,124,116,116,108,108,100,95,92,87,84,79,76,71,68,68,60,60,60,52,52,52,52,52,52,52,52,52,52,55,60,60,63,68,68,76,76,84,84,92,92,100,100,108,111,116,119,124,132,132,140,143,148,151,156,164,167,172,175,180,188,188,196,199,204,212,215,220,223,228,236,239,244,252,236,183,135,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,244,247,244,236,236,228,223,220,212,212,204,199,196,188,188,180,175,172,164,164,156,151,148,143,140,132,132,124,119,116,111,108,103,100,95,92,87,84,79,76,71,68,68,63,60,60,60,55,55,52,52,52,55,55,60,60,60,68,68,71,76,76,84,84,92,92,100,100,108,108,116,116,124,127,132,135,140,148,148,156,159,164,167,172,180,180,188,191,196,204,204,212,215,220,228,228,236,239,244,252,220,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,188,236,247,244,239,236,228,223,220,215,212,204,199,196,191,188,180,180,172,167,164,156,156,148,148,140,135,132,127,124,116,116,108,108,100,100,92,92,84,84,79,76,76,68,68,68,63,60,60,60,60,60,60,60,60,63,68,68,68,71,76,76,84,84,87,92,95,100,103,108,111,116,119,124,132,132,140,140,148,151,156,164,164,172,175,180,183,188,196,196,204,207,212,220,220,228,231,236,244,244,252,212,164,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,172,220,252,244,239,236,228,228,220,215,212,207,204,196,196,188,183,180,172,172,164,159,156,151,148,140,140,132,132,124,124,116,116,108,108,100,100,92,92,84,84,79,76,76,71,68,68,68,68,63,63,63,63,68,68,68,68,71,76,76,79,84,84,87,92,95,100,103,108,111,116,119,124,127,132,135,140,148,148,156,156,164,167,172,175,180,188,188,196,199,204,212,212,220,223,228,236,236,244,247,244,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,164,212,252,244,244,236,231,228,223,220,212,207,204,199,196,188,188,180,175,172,167,164,156,156,148,148,140,135,132,127,124,119,116,111,108,103,100,100,92,92,87,84,84,79,76,76,76,71,68,68,68,68,68,68,71,71,76,76,76,79,84,84,92,92,95,100,100,108,108,116,116,124,124,132,132,140,143,148,151,156,159,164,172,172,180,183,188,191,196,204,204,212,215,220,228,228,236,239,244,252,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,244,247,244,236,236,228,223,220,215,212,204,204,196,191,188,183,180,172,172,164,159,156,151,148,143,140,135,132,124,124,119,116,111,108,103,100,100,92,92,87,84,84,84,79,76,76,76,76,76,76,76,76,76,76,76,79,84,84,87,92,92,95,100,100,108,108,116,116,124,124,132,132,140,140,148,148,156,156,164,167,172,175,180,188,188,196,199,204,207,212,220,220,228,231,236,244,244,252,220,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,228,252,244,239,236,231,228,220,220,212,207,204,196,196,188,188,180,175,172,167,164,156,156,148,148,140,140,132,132,124,124,116,116,111,108,103,100,100,95,92,92,87,84,84,84,84,79,79,79,79,79,79,84,84,84,84,87,92,92,92,100,100,103,108,108,116,116,119,124,127,132,135,140,143,148,151,156,164,164,172,172,180,180,188,191,196,204,204,212,212,220,223,228,236,236,244,247,252,204,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,164,212,252,244,244,236,231,228,223,220,212,212,204,199,196,191,188,183,180,172,172,164,164,156,156,148,143,140,140,132,132,124,124,116,116,111,108,108,100,100,100,95,92,92,92,87,84,84,84,84,84,84,84,84,84,87,92,92,92,95,100,100,103,108,108,116,116,119,124,127,132,135,140,140,148,151,156,159,164,167,172,175,180,188,188,196,196,204,207,212,215,220,228,228,236,239,244,252,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,239,247,244,239,236,228,228,220,215,212,207,204,196,196,188,188,180,175,172,167,164,159,156,151,148,143,140,135,132,132,124,124,116,116,111,108,108,103,100,100,100,95,92,92,92,92,92,92,92,92,92,92,92,92,92,95,100,100,100,108,108,111,116,116,119,124,127,132,132,140,140,148,148,156,156,164,164,172,172,180,183,188,191,196,199,204,212,212,220,223,228,231,236,244,244,252,215,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,175,220,252,244,244,236,231,228,223,220,212,212,204,199,196,191,188,183,180,175,172,164,164,156,156,151,148,143,140,135,132,132,124,124,119,116,116,111,108,108,103,100,100,100,100,95,95,92,92,92,92,92,95,95,100,100,100,100,108,108,108,111,116,116,124,124,127,132,132,140,140,148,148,151,156,159,164,167,172,180,180,188,188,196,196,204,207,212,215,220,228,228,236,236,244,247,244,196,151,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,63,108,156,204,247,247,244,236,236,228,228,220,215,212,207,204,199,196,188,188,180,180,172,172,164,164,156,156,148,148,143,140,135,132,132,124,124,119,116,116,111,108,108,108,103,103,100,100,100,100,100,100,100,100,100,100,100,103,108,108,108,111,116,116,119,124,124,127,132,132,140,140,143,148,151,156,159,164,167,172,175,180,183,188,191,196,204,204,212,212,220,223,228,231,236,244,244,252,228,180,132,87,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,135,180,228,252,244,239,236,231,228,223,220,212,212,204,204,196,196,188,183,180,175,172,167,164,164,156,156,148,148,143,140,135,132,132,127,124,124,119,116,116,116,111,108,108,108,108,108,103,103,103,103,103,108,108,108,108,108,111,116,116,116,119,124,124,132,132,135,140,140,143,148,151,156,159,164,164,172,172,180,180,188,191,196,199,204,207,212,215,220,228,228,236,236,244,247,252,204,159,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,71,116,164,204,252,247,244,236,236,228,228,220,215,212,207,204,199,196,191,188,183,180,175,172,167,164,159,156,156,148,148,143,140,140,132,132,127,124,124,124,119,116,116,116,111,111,108,108,108,108,108,108,108,108,111,111,116,116,116,116,119,124,124,127,132,132,135,140,140,148,148,151,156,159,164,164,172,172,180,180,188,188,196,196,204,204,212,212,220,223,228,231,236,239,244,252,228,183,140,92,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,95,140,183,228,252,244,244,236,231,228,223,220,215,212,204,204,196,196,188,188,180,180,175,172,167,164,159,156,156,148,148,143,140,140,135,132,132,127,124,124,124,119,119,116,116,116,116,116,116,116,116,116,116,116,116,116,119,124,124,124,127,132,132,132,140,140,140,148,148,151,156,159,164,164,172,172,180,180,183,188,191,196,199,204,212,212,220,220,228,228,236,236,244,247,252,207,164,116,71,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,116,164,204,252,247,244,239,236,228,228,220,220,212,212,204,204,196,196,188,188,180,180,172,172,167,164,159,156,156,151,148,148,140,140,140,135,132,132,127,124,124,124,124,124,119,119,119,119,119,119,119,119,124,124,124,124,124,127,132,132,132,135,140,140,143,148,148,151,156,159,164,164,172,172,175,180,183,188,191,196,199,204,207,212,215,220,223,228,231,236,244,244,252,228,183,140,95,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,92,140,180,228,252,244,244,236,236,228,223,220,215,212,207,204,199,196,196,188,188,180,180,172,172,167,164,164,156,156,151,148,148,143,140,140,140,135,132,132,132,127,127,124,124,124,124,124,124,124,124,124,124,124,127,132,132,132,132,135,140,140,143,148,148,151,156,156,159,164,164,172,172,175,180,183,188,191,196,196,204,204,212,212,220,220,228,231,236,239,244,247,247,204,159,116,71,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,71,116,156,204,244,247,244,239,236,231,228,223,220,215,212,207,204,199,196,191,188,188,180,180,175,172,167,164,164,159,156,156,151,148,148,143,140,140,140,135,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,135,140,140,140,140,148,148,148,151,156,156,159,164,164,172,172,175,180,183,188,188,196,196,204,204,212,212,220,220,228,228,236,236,244,244,252,220,180,135,92,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,132,175,220,252,247,244,236,236,228,228,220,220,212,212,207,204,199,196,191,188,188,180,180,175,172,172,164,164,159,156,156,156,148,148,148,143,140,140,140,140,140,135,135,132,132,132,132,132,132,135,135,135,140,140,140,140,143,148,148,148,151,156,156,159,164,164,167,172,172,180,180,183,188,188,196,196,204,204,212,212,215,220,223,228,231,236,239,244,252,236,196,156,111,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,63,108,148,191,231,252,244,244,236,236,228,228,220,220,212,212,204,204,199,196,191,188,188,180,180,175,172,172,167,164,164,159,156,156,156,151,148,148,148,143,143,140,140,140,140,140,140,140,140,140,140,140,140,140,143,148,148,148,148,151,156,156,156,164,164,164,167,172,172,180,180,183,188,188,196,196,204,204,207,212,215,220,223,228,231,236,239,244,247,252,212,172,127,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,79,124,164,204,244,247,244,239,236,231,228,223,220,220,212,212,204,204,199,196,191,188,188,183,180,180,172,172,172,164,164,164,159,156,156,156,151,148,148,148,148,148,148,143,143,143,143,143,143,143,148,148,148,148,148,151,151,156,156,156,159,164,164,167,172,172,175,180,180,183,188,191,196,196,204,204,207,212,215,220,223,228,228,236,236,244,244,252,228,183,140,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,92,135,175,215,252,247,244,239,236,231,228,223,220,220,212,212,204,204,199,196,196,188,188,183,180,180,175,172,172,167,164,164,164,159,156,156,156,156,151,151,148,148,148,148,148,148,148,148,148,148,151,151,156,156,156,156,159,164,164,164,167,172,172,172,180,180,180,188,188,191,196,196,204,204,207,212,215,220,220,228,228,236,236,244,244,252,236,196,156,116,76,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,68,108,148,188,228,252,244,244,239,236,231,228,223,220,220,212,212,204,204,199,196,196,191,188,188,180,180,180,175,172,172,167,164,164,164,164,159,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,159,159,164,164,164,167,172,172,172,175,180,180,183,188,188,191,196,196,204,204,207,212,215,220,220,228,228,236,236,239,244,247,244,207,167,127,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,76,116,156,196,236,252,244,244,236,236,231,228,223,220,220,212,212,207,204,204,196,196,191,188,188,183,180,180,180,175,172,172,172,167,164,164,164,164,164,159,159,159,159,156,156,159,159,159,159,164,164,164,164,164,167,167,172,172,172,175,180,180,183,188,188,191,196,196,199,204,204,212,212,215,220,220,228,228,236,236,239,244,247,252,215,175,140,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,47,87,124,164,204,244,252,244,244,236,236,231,228,223,220,220,212,212,207,204,204,199,196,196,191,188,188,183,180,180,180,175,172,172,172,172,167,167,164,164,164,164,164,164,164,164,164,164,164,164,164,167,172,172,172,172,175,175,180,180,180,188,188,188,191,196,196,199,204,204,212,212,215,220,220,228,228,231,236,239,244,247,252,220,183,148,108,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,55,92,132,172,207,244,247,244,244,236,236,231,228,223,220,220,215,212,212,204,204,199,196,196,196,188,188,188,183,180,180,180,180,175,172,172,172,172,172,172,172,172,167,167,167,172,172,172,172,172,172,172,175,175,180,180,180,183,188,188,188,191,196,196,199,204,204,207,212,212,215,220,223,228,228,236,236,239,244,247,252,228,188,151,116,76,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,63,100,140,175,212,247,247,244,244,236,236,231,228,228,220,220,215,212,212,207,204,204,199,196,196,196,188,188,188,188,183,180,180,180,180,180,175,175,172,172,172,172,172,172,172,172,175,175,175,180,180,180,180,180,183,188,188,188,191,196,196,196,204,204,204,212,212,212,220,220,223,228,228,236,236,239,244,247,252,228,196,156,119,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,68,108,140,180,212,247,247,244,244,236,236,231,228,228,223,220,220,212,212,212,204,204,204,199,196,196,196,191,188,188,188,188,183,183,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,183,183,188,188,188,188,191,196,196,196,199,204,204,207,212,212,215,220,220,223,228,228,236,236,239,244,247,252,231,196,159,124,84,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,71,108,143,180,212,247,252,244,244,239,236,236,228,228,223,220,220,215,212,212,207,204,204,204,199,196,196,196,196,191,188,188,188,188,188,188,183,183,183,183,183,183,183,183,188,188,188,188,188,188,188,191,196,196,196,199,204,204,204,207,212,212,212,220,220,220,228,228,231,236,236,239,244,247,252,228,196,159,124,92,52,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,71,108,140,175,212,244,252,244,244,239,236,236,231,228,228,220,220,220,215,212,212,207,204,204,204,204,199,196,196,196,196,191,191,191,188,188,188,188,188,188,188,188,188,188,188,191,191,196,196,196,196,196,199,204,204,204,207,212,212,212,215,220,220,223,228,228,231,236,236,244,244,247,252,228,196,159,124,92,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,71,108,140,172,204,236,252,247,244,244,236,236,231,228,228,223,220,220,220,215,212,212,212,207,204,204,204,204,199,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,199,199,204,204,204,204,207,212,212,212,215,220,220,223,228,228,231,236,236,239,244,244,247,252,220,188,156,124,92,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,68,103,135,167,199,231,252,247,244,244,239,236,236,231,228,228,223,220,220,220,215,212,212,212,207,207,204,204,204,204,204,199,199,199,199,196,196,196,196,199,199,199,199,204,204,204,204,204,204,207,212,212,212,212,215,220,220,223,228,228,228,231,236,236,239,244,244,252,244,215,183,151,119,84,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,68,100,132,164,191,220,252,252,244,244,239,236,236,236,228,228,228,223,220,220,220,215,215,212,212,212,212,207,207,204,204,204,204,204,204,204,204,204,204,204,204,204,204,207,207,212,212,212,212,212,215,220,220,220,223,228,228,228,231,236,236,239,244,244,247,252,236,207,175,148,116,84,52,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,92,124,151,180,212,239,252,247,244,244,239,236,236,236,228,228,228,223,223,220,220,220,215,215,212,212,212,212,212,212,212,212,207,207,207,207,212,212,212,212,212,212,212,212,215,215,220,220,220,220,223,228,228,228,231,236,236,236,244,244,244,252,252,228,196,167,140,108,76,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,84,111,140,172,196,228,252,252,244,244,244,239,236,236,236,231,228,228,228,223,223,220,220,220,220,220,215,215,215,212,212,212,212,212,212,212,212,212,215,215,215,220,220,220,220,220,223,228,228,228,228,231,236,236,236,239,244,244,247,252,236,212,183,156,127,100,68,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,71,100,132,156,183,212,236,252,247,244,244,244,239,236,236,236,231,228,228,228,228,228,223,223,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,223,223,228,228,228,228,231,236,236,236,236,244,244,244,247,252,247,220,196,172,140,116,84,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,60,87,116,140,164,191,215,239,252,247,244,244,244,239,236,236,236,236,231,231,228,228,228,228,228,228,223,223,223,223,223,223,223,223,223,223,228,228,228,228,228,228,228,231,236,236,236,236,239,244,244,244,247,252,252,228,204,180,156,127,100,76,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,71,100,124,148,172,196,220,239,252,252,247,244,244,244,239,236,236,236,236,236,231,231,231,228,228,228,228,228,228,228,228,228,228,228,228,228,231,231,236,236,236,236,236,239,244,244,244,244,247,252,252,228,207,183,159,135,111,84,60,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,55,79,108,127,151,172,196,215,236,252,252,247,244,244,244,244,244,239,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,239,239,244,244,244,244,247,252,252,244,228,204,183,164,140,116,92,68,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,60,84,108,127,148,172,188,207,228,244,252,252,247,247,244,244,244,244,244,244,239,239,239,239,236,236,236,236,236,239,239,239,239,244,244,244,244,244,244,247,252,252,252,236,215,196,180,159,140,116,95,71,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,60,84,103,124,143,164,180,196,212,228,239,252,252,252,247,247,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,247,247,252,252,252,244,236,220,204,188,172,151,132,116,92,71,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,60,76,95,116,132,148,164,180,191,204,215,228,236,247,252,252,252,252,252,247,247,247,247,247,247,252,252,252,252,252,252,244,236,220,212,196,188,172,156,140,124,108,87,68,47,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,52,68,84,100,116,132,143,156,167,180,188,196,204,212,220,223,228,231,236,236,236,236,236,231,228,220,215,212,204,196,183,172,164,148,140,124,108,92,76,60,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,52,68,84,95,108,119,132,140,148,156,164,172,175,180,180,188,188,188,188,183,180,180,172,167,159,156,143,135,124,116,100,92,76,60,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,47,60,71,84,92,100,108,116,119,124,132,132,135,135,140,135,132,132,127,124,116,111,103,95,84,76,68,52,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,31,44,52,60,68,71,76,79,84,84,87,87,84,84,84,76,76,68,60,52,44,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,28,28,36,36,36,36,36,36,31,28,23,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t FLARE_TEXTURE_5[16384] ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,36,39,44,52,55,60,60,60,60,60,60,60,52,52,44,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,36,44,60,68,76,84,92,95,100,108,108,108,111,111,111,108,108,103,100,92,84,79,71,60,52,44,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,60,71,84,95,108,116,124,132,140,148,148,156,156,159,164,164,164,159,156,151,148,140,135,127,119,111,100,92,76,68,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,44,60,76,92,108,119,132,143,156,164,172,180,188,196,199,204,207,212,212,212,212,212,204,204,196,191,188,180,172,159,148,140,124,116,100,84,68,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,55,76,92,108,124,140,156,167,180,191,204,212,220,228,236,244,252,252,252,252,252,252,252,252,252,252,247,244,236,228,220,207,196,188,172,164,148,132,116,100,84,63,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,60,84,100,119,140,156,172,188,204,215,228,244,252,247,244,244,244,239,236,236,236,236,236,236,236,236,236,236,236,239,244,244,247,252,244,236,220,212,196,180,164,148,132,108,92,71,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,39,60,84,108,124,148,164,183,204,220,236,252,247,244,239,236,236,231,228,228,228,223,220,220,220,220,220,220,220,220,223,223,228,228,228,236,236,239,244,244,252,244,228,212,196,175,156,135,116,95,76,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,36,60,84,108,132,151,172,191,212,228,247,247,244,236,236,228,228,223,220,220,215,212,212,212,212,207,207,207,207,207,207,207,212,212,212,212,220,220,220,228,228,231,236,239,244,252,239,220,204,180,164,140,116,92,71,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,52,76,100,124,148,172,196,220,236,252,244,239,236,228,228,220,220,212,212,207,204,204,199,199,196,196,196,196,196,196,196,196,196,196,196,199,204,204,204,212,212,215,220,223,228,231,236,244,247,247,228,204,183,164,140,116,92,63,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,39,68,92,119,148,172,196,220,239,247,244,236,231,228,220,215,212,207,204,204,196,196,191,188,188,188,188,183,183,180,180,180,180,180,183,183,188,188,188,191,196,196,199,204,204,212,212,220,223,228,236,239,244,252,228,204,180,156,132,108,79,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,79,108,135,164,188,212,236,252,244,236,228,223,220,212,207,204,199,196,191,188,188,183,180,180,175,172,172,172,172,172,172,172,172,172,172,172,172,175,180,180,180,183,188,188,196,196,204,204,212,215,220,228,231,236,244,252,228,204,175,148,124,92,68,36,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,60,92,124,148,180,204,228,252,244,236,228,223,220,212,204,204,196,191,188,183,180,180,172,172,172,164,164,164,164,159,159,159,156,156,159,159,159,164,164,164,164,167,172,172,175,180,180,188,188,196,196,204,207,212,220,228,231,239,244,244,220,191,164,135,108,76,47,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,39,71,100,132,164,188,220,244,244,236,228,223,220,212,204,199,196,188,183,180,175,172,172,164,164,159,156,156,156,151,151,148,148,148,148,148,148,148,148,148,151,156,156,156,159,164,164,167,172,172,180,180,188,191,196,204,207,212,220,228,236,244,252,231,204,175,148,116,87,55,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,79,108,140,172,204,231,252,244,236,228,220,212,204,199,196,188,183,180,172,172,164,164,156,156,151,148,148,148,143,140,140,140,140,140,140,140,140,140,140,140,140,140,143,148,148,151,156,156,159,164,167,172,175,180,188,188,196,204,207,215,220,228,236,244,244,215,188,156,124,92,63,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,84,116,148,180,212,244,244,236,228,220,212,207,199,196,188,180,180,172,167,164,156,156,151,148,148,140,140,140,135,132,132,132,132,127,127,127,127,127,127,132,132,132,132,132,135,140,140,143,148,148,156,156,164,164,172,172,180,188,191,196,204,212,220,228,236,244,252,228,196,164,132,100,68,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,87,124,156,188,220,252,244,236,228,220,212,204,196,188,183,180,172,164,164,156,151,148,148,140,140,135,132,132,127,124,124,124,124,119,119,119,116,116,116,119,119,119,124,124,124,124,132,132,132,135,140,143,148,148,156,159,164,172,172,180,188,196,199,204,212,220,228,236,247,236,204,172,140,108,71,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,55,92,124,156,191,228,252,239,231,223,215,207,199,191,188,180,172,167,164,156,151,148,143,140,135,132,127,124,124,119,116,116,116,116,111,111,108,108,108,108,108,108,111,111,116,116,116,116,119,124,124,127,132,132,140,140,148,148,156,159,164,172,175,180,188,196,204,212,220,228,236,244,244,207,175,140,108,71,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,92,124,159,196,228,247,239,228,220,212,204,196,188,180,175,172,164,156,151,148,140,140,132,132,124,124,119,116,116,111,108,108,108,108,103,100,100,100,100,100,100,100,100,103,103,108,108,108,111,116,116,116,124,124,127,132,135,140,148,148,156,159,164,172,180,188,196,199,207,215,228,236,244,244,212,180,143,108,71,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,87,124,159,196,228,247,236,228,220,212,204,196,188,180,172,164,159,156,148,143,140,132,132,124,124,116,116,111,108,108,103,100,100,100,100,95,92,92,92,92,92,92,92,92,95,95,100,100,100,100,108,108,108,116,116,119,124,127,132,135,140,148,151,156,164,172,175,183,188,196,204,212,223,236,244,247,212,180,140,108,68,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,44,84,124,156,196,228,247,236,228,220,212,199,191,183,180,172,164,156,148,148,140,132,132,124,119,116,116,108,108,103,100,100,95,92,92,92,92,87,87,84,84,84,84,84,84,84,87,87,92,92,92,95,100,100,100,103,108,111,116,116,124,127,132,140,140,148,156,159,164,172,180,188,196,204,212,220,231,244,247,212,175,140,103,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,79,116,156,191,228,247,236,228,220,207,199,191,183,175,167,164,156,148,140,135,132,124,124,116,111,108,108,100,100,95,92,92,87,84,84,84,84,79,79,79,76,76,76,76,79,79,79,84,84,84,84,87,92,92,92,100,100,103,108,108,116,119,124,127,132,140,148,151,156,164,172,180,188,196,204,212,220,231,244,247,212,172,135,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,71,108,148,188,228,247,236,228,220,207,199,188,180,172,164,159,151,148,140,132,127,124,116,116,108,108,100,100,92,92,87,84,84,84,79,76,76,76,76,76,71,71,71,71,71,71,71,76,76,76,76,76,79,84,84,87,92,92,95,100,103,108,111,116,119,124,132,135,140,148,156,164,172,180,188,196,204,212,220,231,244,244,204,167,132,92,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,100,140,180,220,252,239,228,220,207,199,188,180,172,164,156,151,143,140,132,124,119,116,108,108,100,100,92,92,87,84,84,79,76,76,76,71,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,71,76,76,76,79,84,84,92,92,95,100,103,108,116,116,124,127,132,140,148,156,164,172,180,188,196,204,212,223,236,244,236,199,164,124,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,92,132,172,212,252,239,228,220,212,199,188,180,172,164,156,148,143,135,132,124,116,116,108,103,100,92,92,87,84,79,76,76,71,68,68,68,68,63,60,60,60,60,60,60,60,60,60,60,60,60,63,63,68,68,68,71,76,76,79,84,84,92,92,95,100,108,108,116,124,127,132,140,148,156,164,172,180,188,196,204,212,223,236,244,231,191,151,111,71,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,79,124,164,204,244,244,231,220,212,199,191,180,172,164,156,148,140,135,127,124,116,111,108,100,95,92,87,84,79,76,76,71,68,68,63,60,60,60,60,55,55,55,52,52,52,52,52,52,52,55,55,60,60,60,60,63,68,68,68,76,76,79,84,84,92,92,100,103,108,116,119,124,132,140,148,156,164,172,180,188,196,204,215,228,236,252,220,180,140,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,68,108,148,188,231,244,236,223,212,204,191,183,172,164,156,148,140,135,127,124,116,108,103,100,92,92,84,84,76,76,71,68,68,63,60,60,60,55,52,52,52,52,52,52,47,47,47,47,47,52,52,52,52,52,52,55,60,60,60,63,68,68,76,76,79,84,87,92,100,100,108,111,116,124,132,140,148,156,164,172,180,188,196,207,220,228,239,252,212,172,132,87,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,52,92,135,180,220,252,236,228,215,204,196,183,175,164,156,148,140,135,127,124,116,108,103,100,92,87,84,79,76,71,68,68,63,60,60,55,52,52,52,47,47,44,44,44,44,44,44,44,44,44,44,44,44,44,47,52,52,52,52,55,60,60,63,68,68,76,76,84,84,92,95,100,108,111,116,124,132,140,148,156,164,172,180,188,199,212,220,231,244,239,196,156,116,71,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,76,119,164,204,244,244,228,220,207,196,188,180,167,159,151,143,135,127,124,116,108,100,100,92,87,84,76,76,68,68,63,60,60,55,52,52,47,44,44,44,44,44,39,39,39,39,36,36,36,39,39,39,44,44,44,44,44,47,52,52,52,55,60,60,68,68,71,76,79,84,92,92,100,108,111,116,124,132,140,148,156,164,172,180,191,204,212,223,236,247,228,183,140,100,55,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,60,100,148,188,228,244,236,220,212,199,188,180,172,164,151,143,135,127,124,116,108,100,100,92,84,84,76,76,68,68,60,60,55,52,52,47,44,44,44,39,39,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,39,44,44,44,47,52,52,52,60,60,63,68,71,76,79,84,92,92,100,108,111,116,124,132,140,148,156,164,172,183,196,204,215,228,239,252,212,164,124,79,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,84,124,172,212,252,236,228,212,204,191,180,172,164,156,148,140,132,124,116,108,100,100,92,84,84,76,71,68,63,60,60,52,52,47,44,44,44,39,36,36,36,36,36,31,31,31,28,28,28,28,28,31,31,31,36,36,36,36,36,39,44,44,47,52,52,55,60,60,68,68,76,76,84,87,92,100,108,111,119,124,132,140,148,156,167,180,188,196,207,220,231,244,236,191,148,108,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,108,148,196,236,244,228,220,207,196,188,175,164,156,148,140,132,124,116,108,103,100,92,84,79,76,71,68,63,60,55,52,52,44,44,44,39,36,36,36,31,31,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,31,36,36,36,36,39,44,44,47,52,52,60,60,68,68,76,76,84,87,92,100,108,116,119,127,135,143,151,164,172,180,191,204,212,223,236,252,215,172,127,84,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,84,132,172,220,252,236,223,212,199,188,180,172,159,148,140,132,124,116,111,103,100,92,84,84,76,71,68,60,60,52,52,47,44,44,39,36,36,36,31,28,28,28,28,28,23,23,23,23,23,23,23,23,23,23,28,28,28,28,28,31,31,36,36,36,44,44,44,52,52,55,60,63,68,76,76,84,92,95,100,108,116,124,132,140,148,156,164,172,183,196,204,220,228,244,239,196,151,108,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,60,108,151,196,239,244,228,220,204,196,183,172,164,156,148,135,127,119,116,108,100,92,87,84,76,71,68,60,60,52,52,47,44,44,36,36,36,31,28,28,28,28,23,23,20,20,20,20,20,20,20,20,20,20,20,20,20,23,28,28,28,28,31,36,36,36,39,44,44,52,52,55,60,63,68,76,79,84,92,95,100,108,116,124,132,140,148,156,167,180,188,199,212,223,236,247,220,172,127,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,124,172,220,247,236,223,212,199,188,180,167,156,148,140,132,124,116,108,100,92,87,84,76,71,68,60,60,52,52,44,44,39,36,36,31,28,28,28,28,23,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,23,23,28,28,28,31,36,36,39,44,44,47,52,55,60,63,68,76,79,84,92,100,103,111,119,127,135,143,156,164,172,183,196,204,220,228,244,239,196,148,103,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,55,100,148,191,236,244,228,220,204,196,180,172,164,151,143,132,124,116,108,103,95,92,84,76,76,68,63,60,52,52,44,44,39,36,36,31,28,28,28,23,20,20,20,20,20,15,15,15,15,12,12,12,12,12,15,15,15,20,20,20,20,20,23,23,28,28,28,36,36,36,44,44,47,52,55,60,68,68,76,84,87,92,100,108,116,124,132,140,148,156,167,180,188,199,212,223,236,252,215,172,124,76,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,119,164,212,252,236,223,212,199,188,180,164,156,148,140,132,124,116,108,100,92,84,79,76,68,63,60,52,52,44,44,39,36,36,31,28,28,23,20,20,20,20,20,15,15,12,12,12,12,12,12,12,12,12,12,12,12,12,15,15,20,20,20,20,23,28,28,28,31,36,36,44,44,47,52,60,60,68,71,76,84,92,95,100,108,116,124,132,140,151,164,172,180,196,204,220,228,244,236,188,143,95,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,183,228,244,231,220,204,196,183,172,164,151,140,132,124,116,108,100,92,87,84,76,68,68,60,55,52,47,44,39,36,36,31,28,28,23,20,20,20,20,15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15,15,20,20,20,20,28,28,28,31,36,36,44,44,52,52,60,60,68,76,79,84,92,100,108,116,124,132,140,148,156,167,180,188,199,212,223,236,252,207,164,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,156,204,247,239,228,212,204,188,180,167,156,148,140,132,119,111,108,100,92,84,76,71,68,60,60,52,47,44,39,36,36,31,28,28,23,20,20,20,15,12,12,12,12,12,12,12,7,7,7,7,7,7,7,7,12,12,12,12,12,12,12,15,15,20,20,20,23,28,28,31,36,36,44,44,52,52,60,63,68,76,84,87,92,100,108,116,124,132,140,151,164,172,183,196,207,220,231,244,228,180,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,172,220,247,236,220,207,196,183,172,164,151,143,132,124,116,108,100,92,87,79,76,68,63,60,52,52,44,44,36,36,31,28,28,23,20,20,20,15,12,12,12,12,12,7,7,7,7,0,0,0,0,0,0,0,7,7,7,12,12,12,12,12,12,15,20,20,20,23,28,28,31,36,39,44,47,52,55,60,68,71,76,84,92,100,108,111,124,132,140,148,156,167,180,188,204,212,228,239,244,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,188,236,244,228,215,204,191,180,172,156,148,140,132,124,116,108,100,92,84,76,71,68,60,55,52,44,44,36,36,31,28,28,23,20,20,20,15,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,15,20,20,20,23,28,28,36,36,39,44,47,52,60,63,68,76,79,87,92,100,108,116,124,132,143,156,164,172,188,196,212,220,236,252,212,164,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,156,204,252,236,228,212,199,188,175,164,156,148,135,124,116,108,100,92,87,79,76,68,63,60,52,47,44,39,36,31,28,28,23,20,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,20,28,28,31,36,36,44,44,52,55,60,68,71,76,84,92,100,108,116,124,132,140,148,159,172,180,196,204,220,231,244,228,180,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,71,119,167,215,247,236,220,207,196,183,172,164,151,140,132,124,116,108,100,92,84,76,71,68,60,55,52,44,44,36,36,28,28,23,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,23,28,28,31,36,39,44,47,52,60,63,68,76,84,87,95,100,108,116,127,135,148,156,167,180,188,204,212,228,244,239,191,143,95,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,228,244,228,220,204,191,180,172,156,148,140,127,119,111,103,95,87,84,76,68,63,60,52,47,44,39,36,31,28,28,20,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,23,28,28,36,36,44,44,52,55,60,68,71,76,84,92,100,108,116,124,132,143,156,164,172,188,196,212,223,236,252,204,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,95,143,191,244,239,228,212,204,188,180,164,156,148,135,124,116,108,100,92,84,79,71,68,60,55,52,44,44,36,36,28,28,23,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,20,28,28,31,36,39,44,47,52,60,63,68,76,84,92,95,103,111,124,132,140,148,159,172,183,196,207,220,236,247,215,167,119,71,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,60,108,156,204,252,236,223,212,196,188,172,164,151,140,132,124,116,108,100,92,84,76,68,63,60,52,47,44,39,36,31,28,28,20,20,20,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,23,28,28,36,36,44,44,52,55,60,68,76,79,87,92,100,108,116,127,140,148,156,167,180,191,204,220,228,244,228,180,132,84,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,164,212,247,236,220,207,196,183,172,159,148,140,132,119,111,103,95,87,84,76,68,60,60,52,44,44,36,36,28,28,23,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,28,28,31,36,39,44,52,52,60,68,71,76,84,92,100,108,116,124,132,143,156,164,180,188,204,212,228,244,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,172,220,244,231,220,204,191,180,172,156,148,140,127,116,108,100,92,84,79,76,68,60,55,52,44,39,36,31,28,28,20,20,20,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,23,28,28,36,36,44,47,52,60,63,68,76,84,92,100,108,116,124,132,140,151,164,172,188,196,212,228,236,247,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,228,244,228,215,204,188,180,164,156,148,135,124,116,108,100,92,84,76,71,68,60,52,47,44,39,36,31,28,23,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,20,20,20,28,28,36,36,44,44,52,55,60,68,76,84,87,95,103,111,124,132,140,148,164,172,183,196,212,220,236,252,204,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,92,140,188,236,244,228,212,199,188,175,164,156,143,132,124,116,108,100,92,84,76,68,63,60,52,47,44,36,36,28,28,23,20,20,15,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,28,28,31,36,39,44,52,55,60,68,76,79,87,92,100,108,119,127,140,148,159,172,180,196,207,220,236,247,212,164,116,68,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,95,148,196,244,239,228,212,199,188,172,164,151,140,132,124,116,108,100,92,84,76,68,60,55,52,44,44,36,36,28,28,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,23,28,31,36,39,44,47,52,60,68,71,76,84,92,100,108,116,127,140,148,156,172,180,191,204,220,231,244,220,172,119,71,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,199,252,236,223,212,196,188,172,164,151,140,132,124,111,103,95,87,79,76,68,60,55,52,44,39,36,31,28,23,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,20,20,23,28,28,36,36,44,47,52,60,63,68,76,84,92,100,108,116,124,135,148,156,167,180,191,204,220,228,244,223,175,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,108,156,204,252,236,220,212,196,183,172,159,148,140,132,119,111,100,92,87,79,76,68,60,55,52,44,39,36,31,28,23,20,20,15,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,28,28,36,36,44,47,52,60,63,68,76,84,92,100,108,116,124,132,143,156,164,180,188,204,215,228,244,228,180,132,79,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,60,108,156,207,252,236,220,207,196,183,172,159,148,140,127,119,108,100,92,84,79,71,68,60,52,52,44,39,36,31,28,23,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,28,28,36,36,44,44,52,60,63,68,76,84,92,100,108,116,124,132,143,156,164,180,188,204,212,228,244,231,180,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,159,212,252,236,220,207,196,180,172,159,148,140,127,119,108,100,92,84,76,71,68,60,52,47,44,39,36,28,28,23,20,20,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,28,28,31,36,44,44,52,55,60,68,76,84,92,100,108,116,124,132,143,156,164,175,188,204,212,228,244,236,188,135,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,164,212,252,236,220,207,196,180,172,156,148,140,127,116,108,100,92,84,76,71,68,60,52,47,44,36,36,28,28,23,20,20,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,28,28,31,36,44,44,52,55,60,68,76,84,92,100,108,116,124,132,140,156,164,175,188,199,212,228,244,236,188,135,87,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,164,212,252,236,220,207,196,180,172,156,148,140,127,116,108,100,92,84,76,71,68,60,52,47,44,36,36,28,28,23,20,20,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,23,28,31,36,44,44,52,55,60,68,76,84,92,100,108,116,124,132,140,156,164,175,188,199,212,228,244,236,188,140,87,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,164,212,252,236,220,207,196,180,172,159,148,140,127,116,108,100,92,84,76,71,68,60,52,47,44,36,36,28,28,23,20,20,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,28,28,31,36,44,44,52,55,60,68,76,84,92,100,108,116,124,132,143,156,164,175,188,199,212,228,244,236,188,135,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,159,212,252,236,220,207,196,180,172,159,148,140,127,119,108,100,92,84,79,71,68,60,52,47,44,39,36,28,28,23,20,20,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,28,28,31,36,44,44,52,55,60,68,76,84,92,100,108,116,124,132,143,156,164,175,188,204,212,228,244,236,183,132,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,60,108,156,204,252,236,220,207,196,183,172,159,148,140,132,119,111,100,92,84,79,71,68,60,52,52,44,39,36,31,28,23,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,28,28,36,36,44,44,52,60,63,68,76,84,92,100,108,116,124,132,143,156,164,180,188,204,215,228,244,231,180,132,84,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,103,151,204,252,236,223,212,196,183,172,164,148,140,132,119,111,103,95,87,79,76,68,60,55,52,44,39,36,31,28,23,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,20,20,23,28,28,36,36,44,47,52,60,63,68,76,84,92,100,108,116,124,135,148,156,164,180,188,204,215,228,244,228,180,127,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,247,236,223,212,196,188,172,164,151,140,132,124,116,103,95,87,84,76,68,60,55,52,44,44,36,31,28,28,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,20,23,28,28,36,36,44,47,52,60,68,71,76,84,92,100,108,116,124,135,148,156,167,180,191,204,220,231,244,220,172,124,76,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,191,244,239,228,212,199,188,175,164,156,140,132,124,116,108,100,92,84,76,68,63,60,52,44,44,36,36,28,28,20,20,20,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,23,28,31,36,39,44,52,52,60,68,71,79,84,92,100,108,116,127,140,148,156,172,180,196,204,220,236,247,215,167,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,135,188,236,244,228,212,204,188,180,164,156,143,132,124,116,108,100,92,84,76,68,63,60,52,47,44,36,36,28,28,23,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,28,28,31,36,44,44,52,55,60,68,76,79,87,95,103,111,119,132,140,148,159,172,183,196,207,220,236,252,212,159,111,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,79,127,180,228,244,228,220,204,191,180,167,156,148,135,124,116,108,100,92,84,76,71,68,60,52,52,44,39,36,31,28,28,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,23,28,28,36,36,44,44,52,60,60,68,76,84,92,100,108,116,124,132,140,151,164,172,188,196,212,223,236,252,204,156,103,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,71,119,172,220,247,236,220,204,196,180,172,159,148,140,132,119,111,100,95,87,79,76,68,60,55,52,44,44,36,36,28,28,23,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,23,28,31,36,39,44,47,52,60,63,68,76,84,92,100,108,116,124,132,143,156,164,175,188,199,212,228,239,244,196,143,95,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,111,159,207,252,236,220,212,196,183,172,164,151,140,132,124,116,108,100,92,84,76,68,63,60,52,47,44,36,36,31,28,23,20,20,15,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,15,20,20,23,28,28,31,36,44,44,52,55,60,68,71,79,84,92,100,108,116,124,135,148,156,167,180,188,204,215,228,244,236,183,135,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,244,239,228,212,199,188,175,164,156,143,132,124,116,108,100,92,84,76,71,68,60,52,52,44,39,36,31,28,28,23,20,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,23,28,31,36,36,44,47,52,60,60,68,76,84,87,95,103,111,119,132,140,148,159,172,180,196,204,220,231,244,220,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,140,188,236,244,228,215,204,188,180,167,156,148,135,127,116,108,100,92,87,79,76,68,60,55,52,47,44,36,36,31,28,23,20,20,15,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,23,28,28,31,36,39,44,52,52,60,68,68,76,84,92,100,108,116,124,132,140,151,164,172,183,196,212,220,236,252,212,164,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,124,172,220,244,231,220,204,196,180,172,159,148,140,132,124,116,103,100,92,84,76,68,63,60,52,52,44,39,36,36,28,28,23,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,15,20,20,20,28,28,31,36,36,44,44,52,55,60,68,76,79,84,92,100,108,116,124,135,143,156,164,175,188,199,212,228,239,244,196,148,100,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,164,212,252,236,223,212,196,188,172,164,156,143,132,124,116,108,100,92,84,79,76,68,60,55,52,47,44,36,36,31,28,28,20,20,20,15,12,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,15,20,20,20,23,28,28,36,36,39,44,52,52,60,63,68,76,84,92,95,103,111,119,132,140,148,156,172,180,191,204,215,228,244,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,244,239,228,212,204,188,180,167,156,148,140,127,119,111,103,95,92,84,76,68,63,60,52,52,44,44,36,36,28,28,28,20,20,20,15,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,15,20,20,20,23,28,28,31,36,39,44,47,52,55,60,68,76,79,84,92,100,108,116,124,132,140,151,164,172,183,196,207,220,236,247,220,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,180,228,244,231,220,204,196,180,172,164,148,140,132,124,116,108,100,92,84,79,76,68,60,60,52,47,44,39,36,36,28,28,23,20,20,20,15,12,12,12,12,12,7,7,7,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,12,15,20,20,20,23,28,28,31,36,36,44,44,52,52,60,63,68,76,84,87,95,103,108,119,127,135,148,156,164,180,188,199,212,228,236,252,204,156,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,68,116,164,212,252,236,223,212,196,188,175,164,156,148,135,127,116,108,103,95,92,84,76,68,68,60,55,52,44,44,39,36,31,28,28,23,20,20,20,15,15,12,12,12,12,12,12,7,7,7,7,7,7,7,7,7,7,7,12,12,12,12,12,12,15,20,20,20,23,28,28,31,36,36,44,44,47,52,60,60,68,76,79,84,92,100,108,116,124,132,140,148,159,172,180,191,204,220,228,244,236,188,140,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,100,148,196,239,244,228,215,204,191,180,172,159,148,140,132,124,116,108,100,92,84,79,76,68,63,60,52,52,44,44,36,36,31,28,28,23,20,20,20,20,15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15,15,20,20,20,23,28,28,31,36,36,39,44,47,52,55,60,68,71,76,84,92,95,103,108,116,127,135,148,156,164,175,188,196,212,220,236,247,215,172,124,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,84,132,175,220,247,236,220,207,196,188,172,164,156,148,135,127,119,111,103,95,92,84,76,71,68,60,60,52,52,44,44,36,36,31,28,28,28,23,20,20,20,15,15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15,15,20,20,20,20,23,28,28,31,36,36,39,44,47,52,55,60,63,68,76,84,87,92,100,108,116,124,132,140,148,159,172,180,191,204,212,228,239,244,196,151,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,63,108,156,204,247,239,228,212,204,188,180,172,159,148,140,132,124,116,108,100,92,87,84,76,71,68,60,55,52,47,44,44,36,36,31,28,28,28,23,20,20,20,20,20,15,15,12,12,12,12,12,12,12,12,12,12,15,15,15,20,20,20,20,23,28,28,28,31,36,36,39,44,44,52,52,60,63,68,76,79,84,92,100,103,111,119,127,135,148,156,164,172,188,196,207,220,236,244,228,180,132,87,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,135,180,228,244,231,220,207,196,188,172,164,156,148,140,127,119,116,108,100,92,84,79,76,68,68,60,55,52,47,44,44,36,36,36,31,28,28,28,23,20,20,20,20,20,20,15,15,15,15,15,15,15,15,20,20,20,20,20,20,23,23,28,28,28,31,36,36,39,44,44,52,52,60,60,68,71,76,84,92,95,100,108,116,124,132,140,148,159,172,180,188,204,212,228,239,252,204,159,116,68,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,71,116,164,204,252,236,228,212,204,191,180,172,159,151,140,132,124,116,108,103,100,92,84,79,76,68,63,60,55,52,47,44,44,39,36,36,31,28,28,28,28,23,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,23,23,28,28,28,31,36,36,36,44,44,47,52,52,60,60,68,71,76,84,87,92,100,108,116,124,132,140,148,156,164,175,188,196,207,220,231,244,228,183,140,92,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,95,140,183,228,244,231,220,207,196,188,175,164,156,148,140,132,124,116,108,100,95,92,84,76,76,68,63,60,55,52,52,44,44,39,36,36,36,31,28,28,28,28,28,23,23,20,20,20,20,20,20,20,20,23,23,23,28,28,28,28,31,31,36,36,39,44,44,47,52,52,60,60,68,71,76,84,84,92,100,108,111,119,127,135,143,151,164,172,180,191,204,212,228,239,252,207,164,116,71,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,116,164,204,252,239,228,215,204,196,180,172,164,156,148,135,127,124,116,108,100,92,92,84,76,76,68,63,60,60,52,52,47,44,44,39,36,36,36,31,28,28,28,28,28,28,28,28,28,23,28,28,28,28,28,28,28,28,31,31,36,36,36,39,44,44,47,52,55,60,60,68,71,76,84,84,92,100,103,108,116,124,132,140,148,156,167,180,188,196,212,220,236,244,228,183,140,95,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,92,140,180,228,244,236,220,212,199,188,180,172,159,151,140,132,127,119,111,108,100,92,87,84,76,76,68,68,60,60,52,52,47,44,44,44,39,36,36,36,36,31,31,28,28,28,28,28,28,28,28,28,28,28,31,31,36,36,36,36,39,44,44,47,52,52,55,60,63,68,71,76,84,84,92,100,103,108,116,124,132,140,148,156,164,172,183,196,204,215,228,239,247,204,159,116,71,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,71,116,156,204,244,244,228,220,204,196,188,175,164,156,148,140,132,124,116,111,108,100,92,87,84,79,76,68,68,60,60,55,52,52,47,44,44,44,39,36,36,36,36,36,36,36,31,31,31,31,31,36,36,36,36,36,36,39,44,44,44,44,52,52,52,60,60,63,68,71,76,84,84,92,100,100,108,116,124,132,140,148,156,164,172,180,188,204,212,223,236,247,220,180,135,92,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,92,132,175,220,252,236,228,212,204,196,183,172,164,156,148,140,132,124,116,111,108,100,92,92,84,79,76,71,68,63,60,60,55,52,52,47,44,44,44,44,39,39,36,36,36,36,36,36,36,36,36,36,36,39,44,44,44,44,47,52,52,52,55,60,60,68,68,76,76,84,84,92,100,100,108,116,124,127,135,143,151,159,172,180,188,196,207,220,231,244,236,196,156,111,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,63,108,148,191,231,244,236,220,212,199,188,180,172,164,156,148,140,132,124,116,111,108,100,95,92,84,84,76,76,68,68,63,60,60,55,52,52,52,47,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,47,52,52,52,55,60,60,63,68,71,76,79,84,87,92,100,103,108,116,124,127,135,140,148,156,167,175,188,196,204,215,228,239,252,212,172,127,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,79,124,164,204,244,244,228,220,207,196,188,180,172,164,156,148,140,132,124,116,111,108,100,95,92,87,84,79,76,71,68,68,63,60,60,55,52,52,52,52,47,47,47,44,44,44,44,44,44,44,47,47,52,52,52,52,55,60,60,60,63,68,68,76,76,84,84,92,92,100,103,108,116,124,127,132,140,148,156,164,172,183,196,204,212,223,236,247,228,183,140,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,92,135,175,215,252,236,228,215,204,196,188,180,172,164,156,148,140,132,124,119,116,108,100,100,92,92,84,84,76,76,71,68,68,63,60,60,60,55,55,52,52,52,52,52,52,52,52,52,52,52,52,52,55,60,60,60,60,68,68,68,76,76,79,84,87,92,95,100,108,108,116,124,127,135,140,148,156,164,172,180,191,204,212,220,236,244,236,196,156,116,76,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,68,108,148,188,228,247,236,228,212,204,196,188,180,172,164,156,148,140,132,124,119,116,108,103,100,95,92,87,84,79,76,76,71,68,68,68,63,60,60,60,60,60,60,55,55,55,55,55,60,60,60,60,60,60,63,68,68,68,76,76,79,84,84,92,92,100,100,108,111,116,124,132,135,140,148,156,164,172,180,188,199,212,220,228,244,244,207,167,127,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,76,116,156,196,236,244,236,223,212,204,196,188,180,172,164,156,148,140,132,127,124,116,111,108,100,100,92,92,87,84,84,76,76,76,71,68,68,68,68,63,63,63,60,60,60,60,60,63,63,68,68,68,68,68,71,76,76,79,84,84,87,92,95,100,103,108,116,119,124,132,140,143,148,156,164,172,180,188,199,207,220,228,239,252,215,175,140,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,47,87,124,164,204,244,244,236,220,212,204,196,188,180,172,164,156,148,140,135,132,124,119,116,108,108,100,100,92,92,87,84,84,79,76,76,76,76,71,68,68,68,68,68,68,68,68,68,68,71,71,76,76,76,79,84,84,84,92,92,95,100,103,108,111,116,124,127,132,140,148,151,156,164,172,180,188,199,207,220,228,236,252,220,183,148,108,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,55,92,132,172,207,244,244,231,220,212,204,196,188,180,172,164,156,148,143,140,132,127,124,116,116,108,108,100,100,95,92,92,87,84,84,84,79,76,76,76,76,76,76,76,76,76,76,76,76,79,79,84,84,84,87,92,92,95,100,103,108,108,116,119,124,132,135,140,148,156,159,167,172,180,188,199,207,220,228,236,247,228,188,151,116,76,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,63,100,140,175,212,247,244,231,220,212,204,196,188,180,172,164,156,151,148,140,135,132,124,124,116,111,108,108,100,100,95,92,92,92,87,87,84,84,84,84,84,84,84,84,84,84,84,84,84,87,92,92,92,95,100,100,103,108,108,116,116,124,127,132,140,143,148,156,164,172,175,183,191,199,207,220,228,236,247,228,196,156,119,84,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,68,108,140,180,212,247,244,231,223,212,204,196,188,180,172,167,164,156,148,143,140,132,132,124,124,116,116,108,108,103,100,100,100,95,92,92,92,92,92,92,92,92,92,92,92,92,92,92,95,100,100,100,103,108,108,111,116,119,124,127,132,135,140,148,151,156,164,172,180,188,196,204,212,220,228,236,247,231,196,159,124,84,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,71,108,143,180,212,247,244,236,223,215,207,199,191,183,180,172,164,156,156,148,140,140,132,132,124,124,116,116,111,108,108,108,103,100,100,100,100,100,100,100,100,100,100,100,100,100,100,103,108,108,108,111,116,116,119,124,127,132,135,140,148,148,156,164,167,172,180,188,196,204,212,220,228,236,247,228,196,159,124,92,52,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,71,108,140,175,212,244,244,236,228,220,212,204,196,188,180,172,167,164,156,151,148,140,140,132,132,127,124,124,116,116,116,111,108,108,108,108,108,108,108,108,108,108,108,108,108,108,111,116,116,116,119,124,124,132,132,135,140,148,148,156,159,164,172,180,183,188,196,204,212,220,228,239,252,228,196,159,124,92,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,71,108,140,172,204,236,244,236,228,220,212,204,196,191,183,180,172,167,164,156,151,148,143,140,135,132,132,127,124,124,124,119,116,116,116,116,116,116,116,116,116,116,116,116,116,119,124,124,124,132,132,135,140,140,148,148,156,159,164,172,175,180,188,196,204,207,215,223,236,244,252,220,188,156,124,92,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,68,103,135,167,199,231,252,239,231,223,215,207,204,196,188,183,180,172,167,164,156,156,148,148,143,140,140,132,132,132,127,127,124,124,124,124,124,124,124,124,124,124,124,127,132,132,132,135,140,140,143,148,151,156,159,164,172,172,180,188,191,196,204,212,220,228,236,244,244,215,183,151,119,84,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,68,100,132,164,191,220,252,244,236,228,220,212,204,199,196,188,180,180,172,167,164,159,156,156,148,148,143,140,140,140,140,135,132,132,132,132,132,132,132,132,135,135,140,140,140,143,148,148,151,156,156,164,164,172,175,180,188,188,196,204,212,215,223,231,239,247,236,207,175,148,116,84,52,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,92,124,151,180,212,239,247,239,231,223,220,212,204,199,196,188,183,180,172,172,167,164,159,156,156,151,148,148,148,148,143,143,143,140,140,143,143,143,148,148,148,148,151,156,156,159,164,164,172,172,180,180,188,191,196,204,207,212,220,228,236,244,252,228,196,167,140,108,76,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,84,111,140,172,196,228,252,244,236,228,223,220,212,204,199,196,188,188,180,180,172,172,167,164,164,164,159,156,156,156,156,156,156,156,156,156,156,156,156,156,159,164,164,167,172,172,175,180,183,188,191,196,204,207,212,220,228,236,239,247,236,212,183,156,127,100,68,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,71,100,132,156,183,212,236,252,244,236,228,223,220,212,207,204,196,196,188,188,183,180,180,172,172,172,172,167,164,164,164,164,164,164,164,164,164,167,172,172,172,175,180,180,183,188,191,196,199,204,212,212,220,228,231,239,244,247,220,196,172,140,116,84,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,60,87,116,140,164,191,215,239,247,244,236,228,223,220,212,212,204,204,196,196,191,188,188,183,180,180,180,180,180,175,175,175,175,175,180,180,180,180,183,188,188,188,196,196,199,204,207,212,220,220,228,236,239,244,252,228,204,180,156,127,100,76,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,71,100,124,148,172,196,220,239,252,244,236,231,228,220,220,212,212,207,204,204,196,196,196,191,191,188,188,188,188,188,188,188,188,188,191,196,196,196,199,204,204,212,212,215,220,228,228,236,239,244,252,228,207,183,159,135,111,84,60,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,55,79,108,127,151,172,196,215,236,252,244,239,236,231,228,223,220,220,212,212,212,207,204,204,204,204,204,199,199,199,204,204,204,204,204,207,212,212,215,220,220,228,228,236,236,244,247,244,228,204,183,164,140,116,92,68,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,60,84,108,127,148,172,188,207,228,244,252,244,244,236,236,228,228,228,220,220,220,220,215,212,212,212,212,212,212,215,215,220,220,220,223,228,228,231,236,239,244,247,252,236,215,196,180,159,140,116,95,71,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,60,84,103,124,143,164,180,196,212,228,239,252,247,244,244,236,236,236,231,228,228,228,228,228,228,228,228,228,228,231,236,236,236,239,244,244,252,244,236,220,204,188,172,151,132,116,92,71,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,60,76,95,116,132,148,164,180,191,204,215,228,236,247,252,247,244,244,244,244,244,244,244,244,244,244,244,244,247,252,252,244,236,220,212,196,188,172,156,140,124,108,87,68,47,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,52,68,84,100,116,132,143,156,167,180,188,196,204,212,220,223,228,231,236,236,236,236,236,231,228,220,215,212,204,196,183,172,164,148,140,124,108,92,76,60,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,52,68,84,95,108,119,132,140,148,156,164,172,175,180,180,188,188,188,188,183,180,180,172,167,159,156,143,135,124,116,100,92,76,60,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,47,60,71,84,92,100,108,116,119,124,132,132,135,135,140,135,132,132,127,124,116,111,103,95,84,76,68,52,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,31,44,52,60,68,71,76,79,84,84,87,87,84,84,84,76,76,68,60,52,44,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,28,28,36,36,36,36,36,36,31,28,23,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t * FLARE_TEXTURES[6] ={ + FLARE_TEXTURE_0, + FLARE_TEXTURE_1, + FLARE_TEXTURE_2, + FLARE_TEXTURE_3, + FLARE_TEXTURE_4, + FLARE_TEXTURE_5, + }; + const int FLARE_TEXTURE_WIDTHS[10] = { + 128, + 128, + 128, + 128, + 128, + 128, + }; + const int FLARE_TEXTURE_HEIGHTS[10] = { + 128, + 128, + 128, + 128, + 128, + 128, + }; + } +} +#endif diff --git a/src/igl/opengl2/gl.h b/src/igl/opengl2/gl.h new file mode 100644 index 0000000000..7147c92898 --- /dev/null +++ b/src/igl/opengl2/gl.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_GL_H +#define IGL_OPENGL2_GL_H + +#ifdef IGL_OPENGL_GL_H +# error "igl/opengl/gl.h already included" +#endif + +// Always use this: +// #include "gl.h" +// Instead of: +// #include +// or +// #include +// + +// For now this includes glu, glew and glext (perhaps these should be +// separated) +#ifdef _WIN32 +# define NOMINMAX +# include +# undef DrawText +# undef NOMINMAX +#endif + +#ifndef __APPLE__ +# define GLEW_STATIC +# include +#endif + +#ifdef __APPLE__ +# include +# define __gl_h_ /* Prevent inclusion of the old gl.h */ +#else +# include +#endif + +#endif diff --git a/src/igl/opengl2/glext.h b/src/igl/opengl2/glext.h new file mode 100644 index 0000000000..78ff45feac --- /dev/null +++ b/src/igl/opengl2/glext.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL_GLEXT_H +#define IGL_OPENGL_GLEXT_H + +#ifdef IGL_OPENGL2_GLEXT_H +# error "igl/opengl2/glext.h already included" +#endif + +// Always use this: +// #include "gl.h" +// Instead of: +// #include +// or +// #include +// + +#ifdef __APPLE__ +# include +#elif _WIN32 +// do nothing(?) +#else +# include +#endif + +#endif + diff --git a/src/igl/opengl2/glu.h b/src/igl/opengl2/glu.h new file mode 100644 index 0000000000..d4ab5e16c3 --- /dev/null +++ b/src/igl/opengl2/glu.h @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_GLU_H +#define IGL_OPENGL2_GLU_H + +#ifdef IGL_OPENGL_GLU_H +# error "igl/opengl/glu.h already included" +#endif + +// Always use this: +// #include "glu.h" +// Instead of: +// #include +// or +// #include +// + +#ifdef __APPLE__ +# include +#else +# include +#endif + +#endif + diff --git a/src/igl/opengl2/lens_flare.cpp b/src/igl/opengl2/lens_flare.cpp new file mode 100644 index 0000000000..c99f634c8e --- /dev/null +++ b/src/igl/opengl2/lens_flare.cpp @@ -0,0 +1,195 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "lens_flare.h" + +#include "../C_STR.h" +#include "unproject.h" +#include "project.h" +#include "shine_textures.h" +#include "flare_textures.h" + +#include +#include + +// http://www.opengl.org/archives/resources/features/KilgardTechniques/LensFlare/glflare.c + +IGL_INLINE void igl::opengl2::lens_flare_load_textures( + std::vector & shine_id, + std::vector & flare_id) +{ + + const auto setup_texture =[]( + const uint8_t * texture, + const int width, + const int height, + GLuint texobj, + GLenum minFilter, GLenum maxFilter) + { + glBindTexture(GL_TEXTURE_2D, texobj); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, maxFilter); + glTexImage2D(GL_TEXTURE_2D, 0, 1, width, height, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, texture); + }; + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + shine_id.resize(10); + glGenTextures(10,&shine_id[0]); + for (int i = 0; i < (int)shine_id.size(); i++) { + setup_texture( + SHINE_TEXTURES[i], + SHINE_TEXTURE_WIDTHS[i], + SHINE_TEXTURE_HEIGHTS[i], + shine_id[i], GL_LINEAR, GL_LINEAR); + } + flare_id.resize(6); + glGenTextures(6,&flare_id[0]); + for (int i = 0; i < (int)flare_id.size(); i++) { + setup_texture( + FLARE_TEXTURES[i], + FLARE_TEXTURE_WIDTHS[i], + FLARE_TEXTURE_HEIGHTS[i], + flare_id[i], GL_LINEAR, GL_LINEAR); + } +} + +IGL_INLINE void igl::opengl2::lens_flare_create( + const float * A, + const float * B, + const float * C, + std::vector & flares) +{ + using namespace std; + flares.resize(12); + /* Shines */ + flares[0] = Flare(-1, 1.0f, 0.1f, C, 1.0); + flares[1] = Flare(-1, 1.0f, 0.15f, B, 1.0); + flares[2] = Flare(-1, 1.0f, 0.35f, A, 1.0); + + /* Flares */ + flares[3] = Flare(2, 1.3f, 0.04f, A, 0.6); + flares[4] = Flare(3, 1.0f, 0.1f, A, 0.4); + flares[5] = Flare(1, 0.5f, 0.2f, A, 0.3); + flares[6] = Flare(3, 0.2f, 0.05f, A, 0.3); + flares[7] = Flare(0, 0.0f, 0.04f, A, 0.3); + flares[8] = Flare(5, -0.25f, 0.07f, A, 0.5); + flares[9] = Flare(5, -0.4f, 0.02f, A, 0.6); + flares[10] = Flare(5, -0.6f, 0.04f, A, 0.4); + flares[11] = Flare(5, -1.0f, 0.03f, A, 0.2); +} + +IGL_INLINE void igl::opengl2::lens_flare_draw( + const std::vector & flares, + const std::vector & shine_ids, + const std::vector & flare_ids, + const Eigen::Vector3f & light, + const float near_clip, + int & shine_tic) +{ + bool ot2 = glIsEnabled(GL_TEXTURE_2D); + bool ob = glIsEnabled(GL_BLEND); + bool odt = glIsEnabled(GL_DEPTH_TEST); + bool ocm = glIsEnabled(GL_COLOR_MATERIAL); + bool ol = glIsEnabled(GL_LIGHTING); + int obsa,obda,odf,odwm; + glGetIntegerv(GL_BLEND_SRC_ALPHA,&obsa); + glGetIntegerv(GL_BLEND_DST_ALPHA,&obda); + glGetIntegerv(GL_DEPTH_FUNC,&odf); + glGetIntegerv(GL_DEPTH_WRITEMASK,&odwm); + + glDisable(GL_COLOR_MATERIAL); + glEnable(GL_DEPTH_TEST); + //glDepthFunc(GL_LEQUAL); + glDepthMask(GL_FALSE); + glEnable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + + using namespace Eigen; + using namespace std; + + //// view_dir direction from eye to position it is looking at + //const Vector3f view_dir = (at - from).normalized(); + + //// near_clip distance from eye to near clipping plane along view_dir + //// center position on near clipping plane along viewdir from eye + //const Vector3f center = from + near_clip*view_dir; + + Vector3f plight = project(light); + // Orthogonal vectors to view direction at light + Vector3f psx = plight; + psx(0) += 1; + Vector3f psy = plight; + psy(1) += 1; + + // axis toward center + int vp[4]; + glGetIntegerv(GL_VIEWPORT,vp); + Vector3f center = unproject(Vector3f(0.5*vp[2],0.5*vp[3],plight[2]-1e-3)); + //Vector3f center(0,0,1); + Vector3f axis = light-center; + //glLineWidth(4.); + //glColor3f(1,0,0); + //glBegin(GL_LINES); + //glVertex3fv(center.data()); + //glVertex3fv(light.data()); + //glEnd(); + + const Vector3f SX = unproject(psx).normalized(); + const Vector3f SY = unproject(psy).normalized(); + + for(int i = 0; i < (int)flares.size(); i++) + { + const Vector3f sx = flares[i].scale * SX; + const Vector3f sy = flares[i].scale * SY; + glColor3fv(flares[i].color); + if (flares[i].type < 0) { + glBindTexture(GL_TEXTURE_2D, shine_ids[shine_tic]); + shine_tic = (shine_tic + 1) % shine_ids.size(); + } else + { + glBindTexture(GL_TEXTURE_2D, flare_ids[flares[i].type]); + } + + /* position = center + flare[i].loc * axis */ + const Vector3f position = center + flares[i].loc * axis; + Vector3f tmp; + + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); + tmp = position + sx; + tmp = tmp + sy; + glVertex3fv(tmp.data()); + + glTexCoord2f(1.0, 0.0); + tmp = position - sx; + tmp = tmp + sy; + glVertex3fv(tmp.data()); + + glTexCoord2f(1.0, 1.0); + tmp = position - sx; + tmp = tmp - sy; + glVertex3fv(tmp.data()); + + glTexCoord2f(0.0, 1.0); + tmp = position + sx; + tmp = tmp - sy; + glVertex3fv(tmp.data()); + glEnd(); + } + ot2?glEnable(GL_TEXTURE_2D):glDisable(GL_TEXTURE_2D); + ob?glEnable(GL_BLEND):glDisable(GL_BLEND); + odt?glEnable(GL_DEPTH_TEST):glDisable(GL_DEPTH_TEST); + ocm?glEnable(GL_COLOR_MATERIAL):glDisable(GL_COLOR_MATERIAL); + ol?glEnable(GL_LIGHTING):glDisable(GL_LIGHTING); + glBlendFunc(obsa,obda); + glDepthFunc(odf); + glDepthMask(odwm); +} diff --git a/src/igl/opengl2/lens_flare.h b/src/igl/opengl2/lens_flare.h new file mode 100644 index 0000000000..280c27b693 --- /dev/null +++ b/src/igl/opengl2/lens_flare.h @@ -0,0 +1,93 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_LENS_FLARE_H +#define IGL_OPENGL2_LENS_FLARE_H + +#include "../igl_inline.h" +#include "gl.h" +#include +#include + +namespace igl +{ + namespace opengl2 + { + + struct Flare{ + int type; /* flare texture index, 0..5 */ + float scale; + float loc; /* position on axis */ + float color[3]; + Flare(): + type(-1), + scale(0), + loc(0) + {} + Flare(int type, float location, float scale, const float color[3], float colorScale) : + type(type), + scale(scale), + loc(location) + { + this->color[0] = color[0] * colorScale; + this->color[1] = color[1] * colorScale; + this->color[2] = color[2] * colorScale; + } + }; + + + // Initialize shared data for lens flates + // + // Inputs: + // start_id starting texture id location (should have at least id:id+16 free) + // Outputs: + // shine list of texture ids for shines + // flare list of texture ids for flares + IGL_INLINE void lens_flare_load_textures( + std::vector & shine_ids, + std::vector & flare_ids); + + // Create a set of lens flares + // + // Inputs: + // A primary color + // B secondary color + // C secondary color + // Outputs: + // flares list of flare objects + IGL_INLINE void lens_flare_create( + const float * A, + const float * B, + const float * C, + std::vector & flares); + + // Draw lens flares + // + // Inputs: + // flares list of Flare objects + // shine_ids list of shine textures + // flare_ids list of flare texture ids + // light position of light + // near_clip near clipping plane + // shine_tic current "tic" in shine textures + // Outputs: + // shine_tic current "tic" in shine textures + IGL_INLINE void lens_flare_draw( + const std::vector & flares, + const std::vector & shine_ids, + const std::vector & flare_ids, + const Eigen::Vector3f & light, + const float near_clip, + int & shine_tic); + } +}; + +#ifndef IGL_STATIC_LIBRARY +# include "lens_flare.cpp" +#endif + +#endif diff --git a/src/igl/opengl2/model_proj_viewport.cpp b/src/igl/opengl2/model_proj_viewport.cpp new file mode 100644 index 0000000000..9fec32a49b --- /dev/null +++ b/src/igl/opengl2/model_proj_viewport.cpp @@ -0,0 +1,31 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "model_proj_viewport.h" +#include "gl.h" + +template +IGL_INLINE void igl::opengl2::model_proj_viewport( + Eigen::PlainObjectBase & model, + Eigen::PlainObjectBase & proj, + Eigen::PlainObjectBase & viewport) +{ + Eigen::Matrix4d MV,P; + Eigen::Vector4i VPi; + glGetDoublev(GL_MODELVIEW_MATRIX,MV.data()); + glGetDoublev(GL_PROJECTION_MATRIX,P.data()); + glGetIntegerv(GL_VIEWPORT,VPi.data()); + viewport = VPi.cast(); + model = MV.cast(); + proj = P.cast(); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::opengl2::model_proj_viewport, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::opengl2::model_proj_viewport, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/opengl2/model_proj_viewport.h b/src/igl/opengl2/model_proj_viewport.h new file mode 100644 index 0000000000..025247ae71 --- /dev/null +++ b/src/igl/opengl2/model_proj_viewport.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_MODEL_PROJ_VIEW_H +#define IGL_OPENGL2_MODEL_PROJ_VIEW_H +#include "../igl_inline.h" +#include +namespace igl +{ + namespace opengl2 + { + // Collect the model-view, projection and viewport matrices + // + // Outputs: + // model 4x4 modelview matrix + // proj 4x4 projection matrix + // viewport 4x1 viewport vector + // + template + IGL_INLINE void model_proj_viewport( + Eigen::PlainObjectBase & model, + Eigen::PlainObjectBase & proj, + Eigen::PlainObjectBase & viewport); + } +} +#ifndef IGL_STATIC_LIBRARY +# include "model_proj_viewport.cpp" +#endif +#endif diff --git a/src/igl/opengl2/print_gl_get.cpp b/src/igl/opengl2/print_gl_get.cpp new file mode 100644 index 0000000000..7182dc5582 --- /dev/null +++ b/src/igl/opengl2/print_gl_get.cpp @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "print_gl_get.h" + +#include +IGL_INLINE void igl::opengl2::print_gl_get(GLenum pname) +{ + double dM[16]; + + int rows = 4; + int cols = 4; + switch(pname) + { + case GL_MODELVIEW_MATRIX: + case GL_PROJECTION_MATRIX: + { + rows = 4; + cols = 4; + glGetDoublev(pname,dM); + for(int i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_PRINT_GL_GET_H +#define IGL_OPENGL2_PRINT_GL_GET_H +#include "gl.h" +#include "../igl_inline.h" + +namespace igl +{ + namespace opengl2 + { + // Prints the value of pname found by issuing glGet*(pname,*) + // Inputs: + // pname enum key to gl parameter + IGL_INLINE void print_gl_get(GLenum pname); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "print_gl_get.cpp" +#endif + +#endif diff --git a/src/igl/opengl2/project.cpp b/src/igl/opengl2/project.cpp new file mode 100644 index 0000000000..a953197b7d --- /dev/null +++ b/src/igl/opengl2/project.cpp @@ -0,0 +1,73 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "project.h" +#include "../project.h" +#include "gl.h" +#include "glu.h" +#include + +IGL_INLINE int igl::opengl2::project( + const double objX, + const double objY, + const double objZ, + double* winX, + double* winY, + double* winZ) +{ + using namespace std; + // Put model, projection, and viewport matrices into double arrays + double MV[16]; + double P[16]; + int VP[4]; + glGetDoublev(GL_MODELVIEW_MATRIX, MV); + glGetDoublev(GL_PROJECTION_MATRIX, P); + glGetIntegerv(GL_VIEWPORT, VP); + int ret = gluProject(objX,objY,objZ,MV,P,VP,winX,winY,winZ); + return ret; +} + +template +IGL_INLINE int igl::opengl2::project( + const Eigen::PlainObjectBase & obj, + Eigen::PlainObjectBase & win) +{ + assert(obj.size() >= 3); + Eigen::Vector3d dobj(obj(0),obj(1),obj(2)); + Eigen::Vector3d dwin; + int ret = igl::opengl2::project(dobj(0),dobj(1),dobj(2), + &dwin.data()[0], + &dwin.data()[1], + &dwin.data()[2]); + win(0) = dwin(0); + win(1) = dwin(1); + win(2) = dwin(2); + return ret; +} + +template +IGL_INLINE Derivedobj igl::opengl2::project( + const Eigen::PlainObjectBase & obj) +{ + Derivedobj win; + igl::opengl2::project(obj,win); + return win; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template int igl::opengl2::project, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template int igl::opengl2::project, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template int igl::opengl2::project, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template Eigen::Matrix igl::opengl2::project >(Eigen::PlainObjectBase > const&); +template Eigen::Matrix igl::opengl2::project >(Eigen::PlainObjectBase > const&); +template Eigen::Matrix igl::opengl2::project >(Eigen::PlainObjectBase > const&); +template Eigen::Matrix igl::opengl2::project >(Eigen::PlainObjectBase > const&); +template Eigen::Matrix igl::opengl2::project >(Eigen::PlainObjectBase > const&); +template Eigen::Matrix igl::opengl2::project >(Eigen::PlainObjectBase > const&); +#endif + diff --git a/src/igl/opengl2/project.h b/src/igl/opengl2/project.h new file mode 100644 index 0000000000..befd090070 --- /dev/null +++ b/src/igl/opengl2/project.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_PROJECT_H +#define IGL_OPENGL2_PROJECT_H +#include "../igl_inline.h" +#include +namespace igl +{ + namespace opengl2 + { + // Wrapper for gluProject that uses the current GL_MODELVIEW_MATRIX, + // GL_PROJECTION_MATRIX, and GL_VIEWPORT + // Inputs: + // obj* 3D objects' x, y, and z coordinates respectively + // Outputs: + // win* pointers to screen space x, y, and z coordinates respectively + // Returns return value of gluProject call + IGL_INLINE int project( + const double objX, + const double objY, + const double objZ, + double* winX, + double* winY, + double* winZ); + // Eigen wrapper + template + IGL_INLINE int project( + const Eigen::PlainObjectBase & obj, + Eigen::PlainObjectBase & win); + // Eigen wrapper with return + template + IGL_INLINE Derivedobj project( + const Eigen::PlainObjectBase & obj); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "project.cpp" +#endif + +#endif + diff --git a/src/igl/opengl2/right_axis.cpp b/src/igl/opengl2/right_axis.cpp new file mode 100644 index 0000000000..a0b9051760 --- /dev/null +++ b/src/igl/opengl2/right_axis.cpp @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "right_axis.h" +#include "gl.h" + +IGL_INLINE void igl::opengl2::right_axis(double * x, double * y, double * z) +{ + double mv[16]; + glGetDoublev(GL_MODELVIEW_MATRIX, mv); + igl::opengl2::right_axis(mv,x,y,z); +} + +IGL_INLINE void igl::opengl2::right_axis(const double * mv,double * x, double * y, double * z) +{ + *x = -mv[0*4+0]; + *y = -mv[1*4+0]; + *z = -mv[2*4+0]; +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/opengl2/right_axis.h b/src/igl/opengl2/right_axis.h new file mode 100644 index 0000000000..4e6787cdda --- /dev/null +++ b/src/igl/opengl2/right_axis.h @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_RIGHT_AXIS_H +#define IGL_OPENGL2_RIGHT_AXIS_H +#include "../igl_inline.h" +namespace igl +{ + namespace opengl2 + { + // Determines the right axis or depth axis of the current gl matrix + // Outputs: + // x pointer to x-coordinate in scene coordinates of the un-normalized + // right axis + // y pointer to y-coordinate in scene coordinates of the un-normalized + // right axis + // z pointer to z-coordinate in scene coordinates of the un-normalized + // right axis + // mv pointer to modelview matrix + // + // Note: Right axis is returned *UN-normalized* + IGL_INLINE void right_axis(double * x, double * y, double * z); + IGL_INLINE void right_axis(const double * mv, double * x, double * y, double * z); + } +}; + +#ifndef IGL_STATIC_LIBRARY +# include "right_axis.cpp" +#endif +#endif diff --git a/src/igl/opengl2/shine_textures.h b/src/igl/opengl2/shine_textures.h new file mode 100644 index 0000000000..05001989d8 --- /dev/null +++ b/src/igl/opengl2/shine_textures.h @@ -0,0 +1,66 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_SHINE_TEXTURES_H +#define IGL_OPENGL2_SHINE_TEXTURES_H + +#include + +namespace igl +{ + namespace opengl2 + { + + const uint8_t SHINE_TEXTURE_0[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,36,0,0,0,0,0,0,0,0,0,0,0,0,7,20,15,7,0,0,0,0,0,0,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,20,36,36,0,0,0,0,0,0,0,0,0,0,0,0,20,20,15,12,20,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,36,28,36,36,0,0,0,0,0,0,0,0,0,0,0,12,28,20,12,20,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,39,28,31,28,0,0,0,0,0,0,0,0,0,0,0,28,28,20,15,28,28,12,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,44,31,36,28,0,0,0,0,0,0,0,0,0,0,15,31,28,20,36,36,20,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,36,20,0,0,0,0,0,0,0,0,12,12,7,12,12,47,47,44,44,36,0,0,0,0,0,0,0,0,0,0,31,36,28,28,44,28,12,0,0,0,12,23,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,36,0,0,0,0,0,0,0,0,15,15,15,20,20,47,47,44,44,23,0,0,0,0,0,0,7,12,7,20,36,36,28,44,39,20,0,0,0,0,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,39,23,0,0,0,0,0,0,0,12,15,12,12,12,47,47,44,44,15,0,0,0,0,0,0,12,12,12,36,36,36,44,52,28,7,0,0,7,20,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,39,7,0,0,0,0,0,0,12,20,20,0,0,47,47,47,47,7,0,0,0,0,0,12,15,15,23,36,44,44,60,44,20,0,0,0,28,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,28,0,0,0,0,0,0,12,23,23,12,12,52,52,52,52,0,0,0,0,0,0,20,23,23,39,47,47,60,60,28,0,0,0,28,44,23,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,44,12,0,0,0,0,0,0,28,31,31,23,52,60,52,52,0,0,0,0,0,0,28,28,36,44,60,55,68,44,12,0,0,28,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,52,36,0,0,0,0,0,0,31,39,52,36,60,68,52,52,0,0,0,0,0,20,28,36,44,60,63,76,60,23,0,0,23,47,36,12,0,0,0,0,0,0,0,0,0,0,0,12,23,23,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,52,20,0,0,15,15,0,36,44,60,36,71,76,55,52,0,0,0,0,0,31,36,44,52,71,76,76,36,0,0,23,47,39,12,0,7,12,0,0,0,0,0,0,0,7,20,28,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,52,39,0,0,20,20,12,36,55,92,39,79,100,68,60,0,0,0,0,20,36,47,52,76,84,92,60,20,0,20,47,44,20,0,12,20,15,0,0,0,0,0,0,20,28,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,55,20,0,20,23,20,31,79,111,44,84,127,84,55,0,0,0,0,36,39,60,68,92,92,79,28,0,20,47,52,23,0,0,12,20,7,0,0,0,0,20,36,36,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,7,0,0,0,0,0,0,0,31,60,47,0,7,28,28,20,84,116,52,100,164,100,52,0,0,0,12,44,60,63,92,103,100,52,7,20,47,60,28,0,12,15,7,7,0,0,0,15,36,47,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,12,12,7,0,0,0,0,0,7,52,60,28,0,28,28,20,84,119,79,119,188,116,44,0,0,0,36,44,76,76,111,108,76,20,12,44,60,31,12,20,28,12,0,0,0,12,36,52,52,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,7,0,0,0,0,20,20,12,0,0,0,12,20,7,0,0,0,0,0,28,63,52,0,28,36,28,76,116,100,124,188,116,36,0,0,7,47,68,79,108,119,100,36,12,44,68,36,20,28,39,23,0,0,7,31,60,68,52,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,20,20,12,0,0,12,15,23,20,12,0,0,20,23,12,0,0,0,0,0,55,68,31,12,39,39,76,119,132,127,180,100,28,0,0,28,55,92,95,140,124,68,20,44,68,44,28,31,44,28,7,0,20,52,76,71,52,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,28,20,12,0,0,20,36,31,15,0,7,23,28,12,0,0,0,0,28,68,60,12,44,44,71,116,151,140,175,100,20,0,7,55,84,108,140,148,100,36,44,71,52,36,36,47,28,0,12,39,76,87,71,39,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,31,23,12,0,20,36,39,28,7,12,28,36,20,0,0,0,0,55,71,39,36,44,76,116,167,156,180,100,15,12,39,76,111,127,167,132,60,44,76,60,44,44,52,28,7,28,68,92,92,63,28,0,0,7,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,36,36,20,7,12,31,44,36,15,12,28,36,20,0,0,0,28,71,68,36,52,76,119,172,180,188,108,20,28,76,108,140,172,164,103,60,76,68,52,52,52,28,20,52,95,108,84,47,12,0,12,20,20,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,39,39,23,15,28,47,44,28,20,36,44,23,12,12,12,55,76,52,60,76,124,164,188,180,100,20,60,103,143,164,183,132,92,87,84,68,60,52,31,36,84,116,108,76,28,7,15,28,23,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,44,39,28,28,44,52,36,28,36,52,36,28,20,36,76,76,84,84,148,175,207,172,100,36,108,140,175,191,164,124,108,100,76,68,52,44,60,100,124,100,52,20,20,31,28,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,52,52,44,39,44,60,52,44,44,60,47,39,28,68,84,108,100,172,196,228,167,108,76,156,188,204,196,156,132,124,92,76,60,60,84,116,116,84,52,31,36,36,31,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,44,55,55,52,55,60,60,55,60,71,68,52,52,84,108,127,188,220,239,164,116,119,188,228,212,188,156,140,111,84,71,79,100,124,111,84,63,52,44,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,31,52,60,68,68,76,71,76,84,87,79,63,84,100,148,188,236,252,172,140,164,220,236,212,180,164,132,100,87,100,116,132,124,100,84,68,52,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,0,0,0,0,0,12,39,60,68,84,92,87,92,100,108,95,87,108,148,191,244,252,183,172,212,244,236,212,188,148,124,108,116,135,151,140,111,87,68,44,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,20,28,28,20,0,0,0,20,52,68,84,108,111,116,116,132,116,124,148,204,239,252,204,199,244,252,236,215,172,148,135,143,156,164,140,108,87,68,44,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,28,36,36,39,31,12,0,28,63,95,127,140,143,143,164,156,164,212,244,252,228,228,252,252,244,212,188,167,180,180,156,124,108,92,60,36,28,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,36,39,44,47,52,44,44,63,100,140,167,191,204,204,212,223,252,252,244,244,252,252,244,228,212,204,183,156,132,108,68,44,36,36,31,23,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,47,52,55,60,76,100,132,156,188,228,244,239,244,252,252,252,252,252,252,252,244,228,204,164,116,79,68,79,76,60,44,31,20,20,12,7,0,0,0,0,7,15,15,7,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,55,60,68,87,132,180,228,244,252,252,252,252,252,252,252,252,252,244,212,172,143,135,127,108,87,76,68,68,68,68,68,60,55,52,68,68,60,55,52,47,39,28,36,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,0,15,28,28,28,28,28,28,28,23,20,15,15,15,15,15,15,15,15,12,20,44,68,79,103,164,220,244,252,252,252,252,252,252,252,252,244,223,204,191,180,172,180,180,172,151,135,132,132,127,124,116,108,124,119,108,95,92,76,60,44,39,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,31,68,84,84,84,84,84,84,92,92,92,84,84,84,84,84,92,95,100,92,87,84,87,103,140,188,228,244,252,252,252,252,252,252,252,252,239,212,191,191,180,159,140,124,124,124,124,132,132,124,111,100,84,87,76,60,52,52,47,47,39,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,47,52,52,52,52,52,52,60,63,68,68,68,71,84,95,116,132,148,159,172,188,207,223,231,244,247,252,252,252,252,252,252,252,252,252,244,220,172,132,108,92,84,76,60,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,31,44,63,71,76,76,76,84,103,132,156,164,172,167,172,196,228,244,252,252,252,252,252,252,252,252,252,252,244,220,188,159,132,100,76,55,47,44,39,36,36,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,28,44,44,52,52,71,84,103,127,140,148,143,140,132,119,132,148,172,196,228,244,252,252,244,244,252,252,252,239,220,223,244,244,223,204,183,164,140,127,108,84,60,39,36,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,28,47,55,84,100,111,100,92,79,71,68,68,71,84,87,100,140,196,228,236,236,252,228,220,228,247,247,252,239,212,183,188,212,228,228,228,223,196,159,124,92,68,63,60,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,36,52,52,52,52,60,60,60,60,55,39,28,36,52,71,108,164,204,215,207,212,236,244,188,180,212,239,236,236,247,212,167,132,140,164,188,191,188,188,180,167,164,148,116,84,63,60,52,44,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,44,44,47,52,52,52,52,44,28,7,0,12,28,44,60,92,124,151,188,188,172,175,204,228,215,148,156,220,236,212,212,236,220,180,127,92,92,103,132,151,148,140,151,148,132,132,148,140,111,76,60,52,52,47,39,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,36,36,39,44,44,44,44,36,15,0,0,0,0,12,28,36,44,71,92,108,140,172,164,140,148,180,204,215,164,108,148,239,244,188,188,204,204,188,156,108,68,63,68,87,111,119,108,100,124,140,132,108,111,124,124,103,71,52,44,44,44,39,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,23,28,31,36,36,36,36,20,0,0,0,0,0,0,0,12,23,28,36,60,71,76,100,148,164,135,108,111,156,180,196,183,108,84,164,252,252,183,164,175,196,180,172,140,92,60,39,44,68,79,95,100,84,68,87,116,124,116,103,103,103,100,84,63,44,39,36,36,36,31,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,12,20,23,28,23,12,0,0,0,0,0,0,0,0,0,0,0,15,23,44,52,52,71,108,135,140,116,84,84,132,156,164,188,148,52,84,180,252,252,196,140,148,188,164,172,164,108,76,60,31,20,44,68,76,79,76,68,52,60,79,100,108,103,108,103,95,84,63,52,39,36,31,31,23,20,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,36,39,52,76,100,116,116,87,55,68,116,127,127,172,175,95,20,100,204,252,252,220,132,116,172,156,140,156,140,84,63,55,31,12,12,44,68,68,68,60,52,44,44,52,68,87,95,100,100,92,84,68,47,31,28,23,20,12,20,12,15,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,36,60,71,92,100,92,52,39,52,100,116,108,143,188,140,44,20,119,220,252,252,236,140,92,132,167,124,116,143,119,68,52,52,31,12,0,20,44,63,63,55,44,36,36,36,36,44,55,76,84,87,84,76,71,63,44,20,7,12,12,15,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,36,52,71,84,84,63,28,28,44,84,103,87,108,167,167,84,7,36,140,236,252,244,228,148,76,87,156,132,100,111,135,103,52,39,44,31,12,0,0,20,47,60,60,47,31,23,31,28,28,28,28,36,52,68,71,68,68,60,52,36,12,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,36,52,60,71,60,36,12,20,36,76,92,79,76,127,172,116,28,0,60,151,236,231,220,204,143,68,52,116,156,100,79,116,124,84,39,28,39,28,12,0,0,0,28,52,60,55,39,20,12,20,28,20,20,12,0,12,36,55,60,55,52,44,36,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,36,52,63,55,39,12,0,12,36,60,84,76,60,87,151,132,60,0,0,68,148,220,212,204,188,148,68,31,79,140,119,79,68,116,108,68,28,20,28,23,12,0,0,0,0,28,52,52,52,36,12,0,12,20,12,0,0,0,0,7,28,44,44,39,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,52,52,39,20,0,0,12,28,52,71,76,44,55,116,143,87,20,0,0,76,143,196,196,183,183,143,76,28,52,100,132,87,52,68,116,95,52,20,12,20,12,0,0,0,0,0,7,28,47,47,44,28,7,0,0,0,0,0,0,0,0,0,0,20,28,28,20,15,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,44,52,39,20,0,0,0,7,23,39,60,71,44,28,76,132,103,44,0,0,12,71,132,183,188,156,167,140,84,44,23,76,116,95,60,44,79,108,84,44,15,0,0,12,12,0,0,0,0,0,12,28,44,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,7,12,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,44,23,0,0,0,0,7,20,28,52,68,44,12,44,100,116,68,12,0,0,20,71,132,167,172,132,140,116,100,52,0,55,79,100,71,36,39,84,100,68,36,12,0,12,12,0,0,0,0,0,0,0,12,31,44,39,31,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,31,12,0,0,0,0,0,12,20,44,63,44,12,20,63,108,84,36,0,0,0,12,60,124,148,164,124,119,100,108,52,0,28,68,92,76,60,20,52,92,84,52,20,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,36,36,20,0,0,0,0,0,0,0,12,36,60,47,15,0,44,87,92,52,7,0,0,0,20,60,124,140,156,116,100,92,108,47,15,0,55,68,76,60,36,15,60,92,68,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,31,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,23,12,0,0,0,0,0,0,0,0,23,52,47,20,0,28,63,95,68,28,0,0,0,0,20,52,135,135,148,111,84,100,108,47,36,0,36,60,68,52,47,20,20,68,84,52,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,28,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,12,0,0,0,0,0,0,0,0,0,20,47,52,20,0,12,44,84,76,44,0,0,0,0,0,23,44,124,116,108,108,71,108,92,52,44,0,7,55,60,47,44,36,0,28,76,79,52,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,23,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,7,0,0,0,0,0,0,0,0,0,0,15,44,52,23,0,0,36,60,84,55,20,0,0,0,0,0,28,44,108,87,76,103,68,108,71,60,44,0,0,36,55,52,36,44,20,0,39,79,68,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,7,0,0,0,0,0,0,0,0,0,0,12,36,52,28,0,0,20,44,76,63,36,0,0,0,0,0,0,28,52,76,68,55,100,68,108,60,71,44,7,0,12,52,52,31,36,36,0,0,52,84,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,7,28,47,28,0,0,12,36,60,68,52,12,0,0,0,0,0,0,15,52,63,55,44,92,63,100,52,84,36,23,0,0,36,52,36,28,36,20,0,12,60,79,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,44,28,0,0,0,28,44,63,52,28,0,0,0,0,0,0,0,12,60,52,47,39,79,60,84,52,76,36,36,0,0,12,52,47,20,28,28,7,0,23,68,76,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,28,7,0,0,12,36,52,55,44,7,0,0,0,0,0,0,7,20,68,47,44,36,68,60,68,47,60,31,31,0,0,0,36,47,28,15,28,20,0,0,31,68,63,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,7,0,0,0,28,36,52,44,20,0,0,0,0,0,0,0,0,12,60,47,36,28,52,52,44,47,52,28,28,0,0,0,15,44,44,12,20,20,0,0,0,36,76,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,0,0,15,28,44,44,36,0,0,0,0,0,0,0,0,0,0,52,47,23,28,52,47,31,47,44,28,20,7,0,0,0,36,44,20,7,20,12,0,0,7,47,76,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,12,0,0,0,0,20,28,44,39,15,0,0,0,0,0,0,0,0,0,0,44,44,12,28,47,44,28,47,44,36,20,15,0,0,0,20,39,36,0,12,12,12,0,0,15,52,68,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,12,20,31,36,28,0,0,0,0,0,0,0,0,0,0,0,44,39,0,28,47,52,36,52,44,36,12,12,0,0,0,0,36,36,15,0,12,12,0,0,0,20,60,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,12,15,20,36,36,12,0,0,0,0,0,0,0,0,0,0,0,39,39,0,23,44,47,28,52,39,39,12,12,0,0,0,0,20,36,28,0,0,0,0,0,0,0,28,55,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,15,20,28,31,20,0,0,0,0,0,0,0,0,0,0,0,12,36,36,0,20,36,44,20,44,36,36,7,7,0,0,0,0,0,31,36,12,0,0,0,0,0,0,0,31,52,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,12,0,0,0,0,0,0,12,20,28,28,0,0,0,0,0,0,0,0,0,0,0,0,23,36,36,0,15,36,28,12,20,28,28,0,7,0,0,0,0,0,20,31,23,0,0,0,0,0,0,0,7,31,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,7,7,23,28,15,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,0,15,36,15,0,0,28,28,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,0,12,31,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,15,0,15,36,23,0,0,23,23,7,0,0,0,0,0,0,0,20,28,20,0,0,0,0,0,0,0,0,12,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,15,0,12,28,28,12,12,31,31,20,0,0,0,0,0,0,0,7,28,23,0,0,0,0,0,0,0,0,0,7,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,12,0,0,28,28,20,20,23,23,20,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,0,0,0,12,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,20,20,7,7,15,20,20,0,0,0,0,0,0,0,0,7,20,20,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,23,20,0,0,12,28,28,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,28,20,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,31,28,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,20,12,0,0,0,15,15,7,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t SHINE_TEXTURE_1[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,20,20,7,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,12,12,0,0,0,0,12,12,12,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,20,20,15,0,0,0,0,12,12,0,0,0,0,12,12,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,12,20,20,0,0,0,0,12,15,15,0,0,12,20,20,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,0,20,20,0,0,0,0,28,31,28,0,0,0,12,12,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,20,20,0,0,0,12,36,44,28,0,0,0,0,0,12,28,28,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,0,28,28,20,0,0,20,47,60,28,0,0,7,7,0,20,20,20,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,28,31,28,0,0,15,44,68,36,0,0,20,20,12,20,20,12,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,15,36,36,0,0,12,44,68,36,0,0,28,28,12,23,23,12,0,0,0,12,12,0,0,0,0,12,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,7,36,36,0,0,0,44,68,36,0,0,23,23,0,28,28,0,0,0,0,12,7,0,0,0,0,20,23,12,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,28,31,15,36,36,12,0,0,28,60,36,0,0,20,20,12,28,28,0,0,0,12,12,0,0,0,0,12,28,23,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,28,7,0,0,0,0,0,12,12,0,0,0,0,0,0,0,23,36,28,36,36,28,0,0,28,60,36,0,0,20,20,28,36,36,0,0,0,20,20,0,0,0,0,28,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,36,23,12,0,0,0,0,0,15,12,0,0,0,0,0,0,12,36,36,36,44,39,0,0,44,52,20,0,12,36,36,36,36,36,0,0,15,20,20,0,0,0,15,28,23,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,39,28,12,0,0,0,0,15,15,0,0,0,0,0,0,0,36,36,31,44,44,0,12,52,60,28,0,28,36,36,39,39,20,0,0,28,28,12,0,0,0,28,31,12,0,0,0,0,0,0,0,12,20,15,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,36,44,44,28,7,0,0,0,7,20,12,0,0,0,0,0,0,36,39,36,44,44,0,0,52,84,55,12,44,39,39,44,44,7,0,0,28,28,0,0,0,20,36,28,0,0,0,0,0,0,0,12,31,28,12,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,36,44,39,31,20,0,0,0,12,20,12,0,0,0,0,0,20,44,44,47,44,20,0,52,92,71,15,47,31,39,44,44,0,0,20,31,28,0,0,7,36,36,12,0,0,0,0,0,0,12,36,44,20,7,20,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,28,52,52,36,20,7,0,12,28,28,0,0,0,0,0,0,44,44,55,52,36,12,60,100,84,12,52,36,55,44,44,0,0,31,36,12,0,0,28,39,28,0,0,0,0,0,0,7,28,52,28,12,20,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,44,63,52,44,28,0,0,20,36,23,0,0,0,0,0,44,44,63,52,52,23,76,108,87,0,52,44,76,47,44,0,12,36,36,0,0,12,44,44,12,0,0,0,0,0,7,36,52,36,20,20,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,60,76,68,52,28,7,0,36,36,12,0,0,0,0,31,47,63,52,52,20,68,124,108,0,44,44,76,52,36,0,28,44,36,0,0,28,44,28,0,0,0,0,0,0,36,60,44,20,20,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,68,79,71,52,28,7,20,44,36,0,0,0,0,12,52,55,60,52,28,63,140,116,0,47,47,68,52,20,0,44,44,20,0,12,44,44,12,0,0,0,0,0,36,68,52,28,23,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,76,87,84,60,28,12,44,52,28,0,0,0,0,52,52,79,55,44,60,143,124,0,52,55,60,52,0,12,44,44,0,0,36,52,28,0,0,0,0,0,36,76,71,44,36,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,68,92,95,92,60,20,28,60,52,12,0,0,0,44,52,92,55,71,71,159,124,0,52,71,60,52,0,36,47,39,0,20,52,47,12,0,0,0,0,31,76,84,55,36,44,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,79,103,108,100,60,20,52,68,39,0,0,0,20,55,92,60,71,68,164,119,20,60,92,60,55,0,47,52,20,0,44,55,28,0,0,0,0,28,79,92,68,44,47,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,84,116,116,108,60,36,68,68,20,0,0,0,60,76,76,68,68,164,124,47,76,108,60,47,15,52,52,0,23,60,52,7,0,0,0,23,76,100,76,52,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,52,100,132,140,108,52,55,76,52,0,0,0,52,63,103,63,92,172,140,76,92,119,60,36,39,52,44,0,52,60,28,0,0,0,20,71,103,87,63,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,63,108,148,156,108,60,71,76,28,0,0,36,60,119,63,116,180,159,92,92,124,60,23,55,60,20,28,63,52,0,0,0,20,68,108,92,68,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,76,124,172,167,103,68,76,60,12,0,12,63,116,76,127,172,172,111,108,116,63,28,60,60,12,55,68,28,0,0,15,63,108,103,76,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,84,148,196,167,100,84,79,44,0,0,60,100,103,124,156,180,127,127,116,68,47,60,44,36,68,52,0,0,12,60,108,111,84,55,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,100,180,212,164,100,92,76,20,0,44,84,132,124,156,188,140,148,108,68,60,63,36,60,68,28,0,20,68,108,119,95,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,132,212,220,151,108,92,55,0,20,76,140,124,172,196,148,159,100,76,68,63,47,71,52,0,20,76,119,132,108,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,148,220,212,156,124,87,31,0,68,148,148,183,199,175,180,100,87,68,68,68,71,28,20,76,124,143,119,76,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,68,159,223,212,167,124,68,12,60,135,164,191,199,204,196,108,87,76,76,76,52,20,71,132,159,132,76,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,71,167,228,212,172,111,44,36,124,183,207,212,228,191,127,79,92,79,76,44,71,140,167,140,84,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,15,84,180,223,212,164,92,36,100,180,220,220,231,183,148,84,116,92,84,79,156,196,156,84,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,23,12,0,20,92,188,220,220,151,68,84,172,239,236,244,204,164,108,124,116,108,156,212,180,95,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,15,31,36,23,7,28,100,191,236,212,127,84,148,236,244,252,228,172,143,132,151,164,212,191,108,31,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,20,20,20,12,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,20,28,36,44,39,23,36,111,199,239,188,111,135,223,252,252,236,183,172,180,188,207,188,108,31,0,0,0,0,15,20,15,7,0,0,0,0,0,0,0,0,12,20,20,12,7,20,28,44,44,39,36,28,23,20,20,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,31,20,0,0,0,0,0,12,20,28,28,39,44,52,52,39,52,124,204,228,156,143,196,252,252,236,212,204,220,215,188,108,31,0,0,12,28,28,23,20,12,0,0,0,0,0,20,31,36,36,44,60,63,60,52,52,44,44,36,36,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,36,39,44,44,44,28,7,0,0,20,28,36,44,68,76,68,60,76,140,212,204,172,188,247,252,236,236,231,236,188,108,31,0,20,39,44,36,28,15,0,0,7,28,44,52,60,76,84,92,87,84,76,63,52,44,44,44,36,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,31,39,44,47,60,71,76,76,60,36,23,28,39,47,68,92,100,92,111,172,212,207,212,244,252,247,247,236,188,103,44,36,52,52,44,39,28,20,36,60,79,100,116,124,108,103,100,92,76,60,52,47,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,68,76,84,84,84,84,84,76,76,76,76,100,132,143,164,204,223,236,244,252,252,236,188,116,84,76,76,68,68,71,76,92,111,124,132,132,132,124,116,84,68,60,52,36,23,20,15,12,0,12,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,68,84,84,84,84,87,100,127,143,156,172,196,215,236,252,252,252,244,212,159,132,140,148,148,132,119,119,124,132,140,148,143,135,119,108,92,71,52,44,31,36,44,44,39,28,23,39,52,47,44,44,44,55,60,52,55,52,36,23,28,47,39,28,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,68,84,92,100,108,132,172,212,244,247,252,252,252,244,215,196,207,228,212,196,196,204,204,196,188,175,164,148,143,143,148,148,140,140,140,116,108,108,103,100,84,76,76,79,68,60,60,60,60,60,55,55,52,36,23,28,47,39,28,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,12,12,12,20,20,20,20,20,44,60,76,76,76,76,76,76,76,76,76,76,76,71,68,68,76,76,84,92,92,92,92,92,92,92,92,92,92,92,100,111,132,156,180,199,223,244,252,252,252,252,247,244,244,252,247,247,252,247,231,204,175,156,156,156,159,164,167,164,151,132,116,108,84,76,68,68,71,71,68,63,63,60,60,60,55,52,52,44,44,36,23,12,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,12,12,12,20,23,23,20,20,20,39,60,68,68,68,76,84,92,92,100,103,108,135,159,167,172,172,164,156,164,180,180,175,172,164,164,156,151,151,156,172,188,204,220,236,244,247,252,252,252,252,252,252,252,252,252,247,244,239,220,188,164,124,92,63,44,28,28,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,23,28,36,39,68,92,100,100,100,84,68,68,84,84,79,76,84,100,116,124,127,124,127,148,175,204,220,228,236,252,252,252,252,252,252,252,252,247,244,228,199,164,135,124,108,92,68,52,44,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,7,0,0,0,12,31,52,60,68,68,68,68,76,100,132,164,172,164,164,183,215,236,244,244,244,252,252,252,252,252,239,212,196,188,188,172,140,108,79,60,52,47,44,36,36,28,20,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,52,55,60,60,60,63,68,71,92,116,132,132,116,108,111,135,156,183,215,236,220,215,239,252,252,252,252,252,239,220,191,164,148,148,135,108,68,28,12,20,28,36,36,28,20,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,44,47,52,52,52,52,55,60,60,60,60,76,92,100,100,100,100,92,95,111,132,164,212,236,196,175,204,244,252,252,252,252,252,252,228,196,164,140,132,143,156,143,116,76,36,12,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,36,36,39,44,44,44,47,52,52,44,28,15,23,44,68,84,100,100,100,87,63,52,76,108,140,164,199,220,188,140,156,215,228,236,252,252,252,252,252,231,215,196,151,103,92,116,143,164,159,140,100,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,20,23,28,28,36,36,39,44,36,20,0,0,0,20,36,52,60,68,76,92,84,63,31,20,47,100,127,140,156,180,204,183,116,116,167,220,188,212,252,252,252,252,252,236,204,196,188,156,92,52,60,92,135,164,172,156,124,84,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,12,15,20,20,23,23,20,12,0,0,0,0,0,20,36,44,47,52,55,60,52,39,23,7,0,36,84,124,132,127,132,164,196,175,100,71,135,183,188,143,188,244,236,236,236,236,252,223,188,164,172,156,100,39,20,39,79,124,156,172,159,132,95,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,12,0,7,0,0,0,0,0,0,0,12,28,36,36,44,44,44,39,20,12,0,0,0,23,68,116,140,124,100,103,148,191,167,95,44,87,180,172,148,127,167,228,220,220,199,220,247,247,212,164,143,156,156,108,44,0,0,28,63,100,140,159,156,116,84,55,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,28,31,36,36,28,12,0,0,0,0,0,12,47,95,132,132,100,68,87,140,172,148,92,28,44,127,180,148,108,132,156,212,204,204,164,191,228,252,247,207,148,116,148,148,108,44,0,0,0,15,52,79,108,132,132,108,76,52,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,28,28,28,20,12,0,0,0,0,0,0,0,31,79,116,124,108,68,44,79,132,159,127,84,28,20,76,156,140,127,84,116,132,180,196,188,151,148,191,244,252,244,204,132,100,124,132,100,47,15,0,0,0,12,31,60,79,100,108,95,68,44,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,23,23,20,0,0,0,0,0,0,0,0,0,20,55,92,119,108,68,28,20,68,132,148,116,68,28,0,44,108,159,108,100,76,84,100,140,180,180,148,100,156,220,252,252,244,196,119,76,95,116,103,60,20,0,0,0,0,0,28,44,63,76,84,76,52,36,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,7,0,0,0,0,0,0,0,0,0,0,12,39,71,100,100,79,36,12,15,68,116,119,84,44,20,0,20,76,140,132,87,84,76,52,76,103,148,172,143,71,108,172,244,252,252,244,196,108,60,76,108,100,68,28,0,0,0,0,0,0,15,36,52,60,68,68,47,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,60,79,92,76,44,15,0,15,60,108,111,68,28,0,0,0,52,95,135,100,68,71,71,44,76,84,127,172,151,79,55,127,204,236,244,252,244,188,100,52,55,92,92,71,36,12,0,0,0,0,0,0,12,28,44,52,55,55,36,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,68,84,76,52,20,0,0,20,55,92,92,68,36,7,0,0,28,68,116,103,63,60,68,60,44,76,76,100,151,159,92,23,79,156,215,228,244,244,228,164,84,36,44,84,87,71,39,20,0,0,0,0,0,0,0,0,20,36,36,39,44,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,52,68,68,52,28,12,0,0,20,52,79,71,47,36,20,0,0,0,52,84,108,76,28,63,68,44,44,76,68,63,108,151,103,20,36,108,180,212,223,231,212,188,135,63,28,36,68,76,68,44,23,0,0,0,0,0,0,0,0,0,12,28,31,31,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,39,60,68,52,31,12,0,0,0,23,55,68,52,28,20,12,0,0,0,28,63,100,84,55,12,68,68,20,44,68,55,39,71,148,119,28,7,60,132,180,196,212,199,172,151,116,52,20,28,52,68,68,47,28,7,0,0,0,0,0,0,0,0,0,12,20,28,28,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,44,55,52,36,20,0,0,0,0,28,52,60,36,12,12,7,0,0,0,12,52,76,92,68,28,20,63,63,0,44,60,31,20,47,132,127,44,0,20,84,140,172,196,204,172,143,127,100,44,12,20,44,60,60,47,28,12,0,0,0,0,0,0,0,0,0,0,0,12,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,31,44,44,36,20,7,0,0,0,0,28,52,52,28,0,0,0,0,0,0,0,36,60,79,68,52,0,44,63,55,0,44,60,15,0,20,103,124,60,0,0,39,100,148,172,191,180,140,116,108,92,44,12,12,36,47,44,39,23,7,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,31,28,28,20,12,0,0,0,0,0,28,52,44,20,0,0,0,0,0,0,0,12,52,60,76,63,28,0,55,60,36,0,47,60,20,7,0,76,116,76,7,0,12,68,124,143,172,183,148,111,100,100,84,36,0,7,20,31,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,28,20,12,0,0,0,0,0,0,7,28,52,36,15,0,0,0,0,0,0,0,0,36,52,68,63,55,0,0,60,60,12,0,52,60,23,15,0,60,103,84,23,0,0,36,92,124,143,172,164,116,84,87,87,76,28,0,0,12,20,15,20,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,12,7,0,0,0,0,0,0,12,31,52,31,12,0,0,0,0,0,0,0,0,20,52,52,68,60,31,0,20,60,60,0,0,52,60,23,20,0,44,84,92,31,0,0,7,60,103,116,148,164,140,87,68,76,76,63,23,0,0,0,0,7,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,28,7,0,0,0,0,0,0,0,0,0,36,47,52,60,52,0,0,36,55,52,0,0,52,52,12,12,0,20,68,84,28,0,0,0,28,76,108,111,148,156,108,68,60,68,71,60,23,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,39,23,0,0,0,0,0,0,0,0,0,0,20,44,39,52,55,36,0,0,52,52,36,0,0,52,52,0,0,0,0,55,76,28,0,0,0,0,47,92,92,116,140,132,76,52,52,68,68,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,31,36,20,0,0,0,0,0,0,0,0,0,0,0,36,44,36,52,52,12,0,0,52,52,12,0,0,52,52,0,0,0,0,52,68,44,7,0,0,0,28,68,92,84,119,132,100,60,44,44,63,47,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,12,0,0,0,0,0,0,0,0,0,0,0,20,39,28,44,52,36,0,0,12,52,52,0,0,0,52,52,0,0,0,0,44,60,52,0,0,0,0,0,44,76,76,92,116,111,68,36,28,36,44,28,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,23,20,12,0,0,0,0,0,0,0,0,0,0,0,7,36,36,28,52,52,12,0,0,36,47,44,0,0,0,47,47,0,0,0,0,28,52,52,0,0,0,0,0,20,52,79,68,87,108,87,44,28,28,36,44,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,20,31,47,36,0,0,0,44,47,28,0,0,0,44,44,0,0,0,0,7,47,47,0,0,0,0,0,0,36,63,68,68,84,92,63,36,20,20,36,52,44,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,31,15,44,44,15,0,0,0,44,44,12,0,0,0,44,44,0,0,0,0,0,44,44,12,0,0,0,0,0,15,44,68,52,63,76,76,39,20,20,28,31,44,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,31,15,23,44,36,0,0,0,12,44,44,0,0,0,0,44,44,0,0,0,0,0,44,44,28,0,0,0,0,0,0,36,52,60,52,60,71,55,28,20,12,20,28,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,23,7,39,44,20,0,0,0,28,39,36,0,0,0,0,44,44,0,0,0,0,0,28,44,39,0,0,0,0,0,0,12,36,60,44,52,60,68,44,20,12,12,15,15,12,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,12,20,39,36,0,0,0,0,36,39,28,0,0,0,0,39,39,0,0,0,0,0,12,39,39,0,0,0,0,0,0,0,28,44,52,36,44,52,52,20,7,0,0,0,12,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,20,0,31,36,20,0,0,0,0,36,36,12,0,0,0,0,36,36,0,0,0,0,0,0,36,36,0,0,0,0,0,0,0,12,36,52,39,36,39,44,31,12,0,0,0,15,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,12,36,31,0,0,0,0,12,36,36,0,0,0,0,0,28,28,0,0,0,0,0,0,36,36,20,0,0,0,0,0,0,0,20,36,44,28,36,36,36,15,0,0,0,0,12,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,0,28,36,20,0,0,0,0,15,28,28,0,0,0,0,0,12,12,0,0,0,0,0,0,28,36,28,0,0,0,0,0,0,0,12,28,44,36,31,31,36,28,7,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,12,31,31,0,0,0,0,0,23,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,31,0,0,0,0,0,0,0,0,20,28,36,23,28,28,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,20,28,20,0,0,0,0,0,23,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,0,0,23,31,28,20,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,0,0,12,23,28,15,15,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,20,0,0,0,0,0,12,20,20,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,0,0,0,20,20,20,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,12,20,20,0,0,0,0,0,0,0,0,0,12,20,20,15,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,7,20,20,0,0,0,0,0,0,0,0,0,0,12,15,12,7,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,7,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,7,12,12,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,12,20,20,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,15,20,20,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t SHINE_TEXTURE_2[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,0,0,0,0,0,0,7,7,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,7,23,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,7,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,20,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,15,0,0,0,0,0,0,12,12,0,0,0,0,28,28,12,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,12,0,0,0,0,0,12,12,0,0,0,0,20,23,20,0,0,0,0,12,12,0,0,7,7,0,0,0,0,0,0,0,15,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,0,0,0,12,15,12,0,0,0,15,23,23,0,0,0,0,28,28,0,0,20,20,0,0,0,0,0,0,0,28,31,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,15,0,0,0,0,0,20,20,0,0,0,12,23,23,0,0,0,0,36,36,0,20,28,28,0,0,0,0,12,12,12,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,28,7,0,0,0,0,28,28,12,0,0,0,28,28,0,0,0,0,36,36,0,20,20,20,0,0,0,7,12,12,28,36,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,20,0,0,0,0,20,28,23,0,0,0,28,28,12,0,0,0,36,36,0,23,28,20,0,0,0,20,20,20,36,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,36,12,0,0,0,0,28,28,0,0,0,36,36,28,0,0,0,36,36,0,36,36,23,0,0,0,15,15,28,39,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,28,0,0,0,0,28,36,20,0,0,28,44,44,0,0,0,36,36,0,36,36,12,0,0,0,12,12,36,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,28,39,20,0,0,0,20,36,31,0,0,20,44,52,12,0,0,36,36,0,36,36,0,0,0,12,20,28,44,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,12,0,0,0,12,44,39,12,0,0,0,36,36,12,0,0,44,52,12,0,0,36,36,0,39,39,0,0,0,20,20,39,44,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,15,0,0,0,0,28,44,28,0,0,0,36,39,28,0,0,44,60,28,0,0,36,36,7,44,44,0,0,7,31,36,52,47,20,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,0,12,44,44,20,0,0,20,44,39,0,0,44,71,55,12,0,44,44,20,44,44,0,0,20,36,55,52,47,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,12,0,0,0,0,0,0,0,0,0,0,20,36,20,0,0,0,28,52,39,0,0,0,44,44,15,0,39,68,68,15,0,52,52,31,44,44,0,0,36,36,68,52,31,0,0,12,12,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,12,0,0,0,0,0,0,0,0,0,28,39,20,0,0,0,44,52,28,0,0,31,44,36,0,28,68,76,20,0,60,60,44,52,52,0,0,36,52,60,52,12,0,12,20,12,0,0,0,0,0,0,0,0,0,20,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,15,0,0,0,0,0,0,0,0,7,31,39,20,0,0,20,52,47,12,0,12,52,47,0,12,76,92,44,0,60,60,47,52,47,0,23,44,76,52,44,0,0,28,28,7,0,0,0,0,0,0,0,0,20,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,23,12,0,0,0,0,0,0,0,12,36,44,20,0,0,36,55,36,0,0,47,52,23,0,68,100,60,0,60,60,52,60,36,7,44,52,84,55,23,0,20,28,20,0,0,0,0,0,0,0,7,28,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,31,20,0,0,0,0,0,0,0,20,44,44,15,0,15,52,55,20,0,31,52,44,0,60,108,84,0,60,60,52,68,36,20,52,76,71,55,0,12,36,31,0,0,0,0,0,0,0,12,28,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,36,28,7,0,0,0,0,0,0,31,52,39,12,0,36,60,44,0,12,55,55,12,52,116,108,23,60,63,55,76,31,39,60,100,60,44,0,28,36,20,0,0,0,0,0,0,12,36,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,36,15,0,0,0,0,12,28,52,60,36,7,12,52,60,28,0,52,60,36,36,124,127,44,60,76,60,84,36,60,79,108,60,15,20,44,36,0,0,0,0,0,0,20,39,44,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,44,44,28,0,0,0,12,23,44,55,60,36,0,28,68,55,12,28,60,55,20,116,140,68,60,87,68,92,52,79,103,92,55,12,39,44,15,0,0,0,0,0,20,44,44,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,52,36,12,0,0,12,28,52,68,60,31,7,52,68,39,12,60,60,28,100,135,84,68,108,87,108,87,100,124,76,36,36,52,31,0,0,0,0,0,28,52,47,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,52,44,23,0,0,12,36,63,76,60,28,23,68,68,20,52,63,47,84,135,100,79,124,108,132,116,124,124,68,39,60,52,12,0,0,0,0,28,55,52,20,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,52,60,36,12,0,15,44,76,87,60,28,44,76,52,31,68,63,76,132,116,92,132,116,148,135,156,108,63,60,76,36,0,0,0,0,36,60,52,20,0,0,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,52,60,47,20,0,20,52,87,100,60,36,68,76,39,68,68,76,132,124,108,143,124,164,143,164,87,68,84,60,7,0,0,12,39,68,52,20,0,7,12,20,12,0,7,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,20,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,44,63,60,31,7,20,60,100,108,52,52,76,63,60,71,87,124,127,124,156,135,188,175,156,95,84,84,36,0,0,12,44,71,52,20,0,20,31,28,15,12,12,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,28,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,60,68,44,20,28,76,111,108,60,71,79,68,76,84,124,124,140,172,167,212,204,140,111,108,76,12,0,15,52,76,52,20,12,28,36,36,28,23,23,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,20,23,36,36,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,28,55,76,60,36,36,87,124,111,76,84,79,100,84,140,124,156,188,196,236,199,140,124,108,44,0,20,55,79,60,28,28,44,44,39,36,31,28,23,12,0,0,0,7,20,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,36,36,36,39,44,36,15,0,0,0,0,0,0,0,0,0,0,15,47,76,71,44,47,100,140,116,100,92,124,103,156,132,172,199,223,244,188,156,132,92,20,28,60,100,68,52,47,52,52,52,44,36,31,20,0,0,12,23,28,23,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,12,0,7,20,36,39,44,44,44,47,39,20,0,0,0,0,0,0,0,0,0,36,68,84,63,68,119,156,132,116,132,148,172,172,204,220,236,228,188,164,140,60,36,76,108,100,68,63,68,68,60,44,36,28,12,12,28,36,36,31,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,23,20,20,20,28,28,28,36,44,47,52,52,52,47,28,7,0,0,0,0,0,0,28,60,84,79,92,140,159,148,135,172,196,220,236,244,247,220,204,172,116,68,92,124,124,100,92,92,87,63,47,36,28,31,44,44,36,36,36,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,28,28,36,44,52,52,47,39,44,52,60,60,55,55,60,60,55,36,12,0,0,0,0,12,52,84,92,111,156,172,164,183,215,244,252,252,252,228,212,156,108,119,159,156,124,124,124,100,68,55,52,52,52,47,44,44,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,12,20,20,15,28,44,55,60,68,68,84,79,84,84,92,92,79,68,63,68,60,44,20,0,0,0,36,76,103,132,172,188,204,228,252,252,252,252,236,196,148,148,196,196,172,156,132,116,103,92,68,52,52,52,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,0,7,20,20,20,28,52,52,52,60,76,92,92,100,108,108,111,124,132,132,119,100,84,76,68,52,28,7,28,63,108,156,196,223,239,252,252,252,252,231,191,180,212,236,212,180,156,148,124,84,63,60,52,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,7,20,20,15,20,36,28,28,36,52,76,84,108,127,148,156,156,140,132,132,151,167,164,143,124,108,92,68,52,68,108,172,228,247,252,252,252,252,228,215,228,252,252,228,204,172,124,84,76,55,36,23,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,28,44,44,44,47,52,63,84,108,132,156,172,180,188,196,204,212,212,196,167,135,111,116,156,207,244,252,252,252,252,244,244,252,252,244,215,180,159,132,84,55,36,28,23,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,20,28,44,52,44,20,20,44,60,60,60,60,60,68,84,95,100,100,100,108,116,132,156,180,196,204,223,244,247,236,204,180,180,212,244,252,252,252,252,252,252,252,252,239,207,156,111,68,47,44,36,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,20,28,44,52,44,20,20,44,60,60,68,68,60,68,68,71,68,68,76,76,92,103,116,132,159,196,231,244,252,252,252,252,244,244,247,252,252,252,252,252,252,252,252,236,204,172,140,103,76,68,60,60,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,52,68,76,100,111,132,140,140,143,156,180,204,215,236,252,252,252,252,252,252,252,252,252,252,252,252,252,252,239,215,188,159,148,132,116,100,87,68,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,28,44,55,76,79,95,103,116,140,159,172,172,172,199,228,239,239,244,252,252,252,252,252,252,252,252,244,236,231,223,196,156,127,100,92,87,84,84,84,76,63,47,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,20,44,68,84,84,92,92,87,84,100,132,164,183,172,180,207,239,252,252,252,252,252,252,252,252,244,231,212,196,188,188,188,172,156,140,111,92,84,84,84,84,84,79,79,76,71,60,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,23,28,36,44,52,63,84,111,132,140,127,116,116,132,156,196,236,252,252,252,252,252,252,252,252,247,236,207,183,164,156,159,156,148,116,100,95,95,92,79,76,79,79,79,79,79,79,76,76,76,71,55,52,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,20,20,23,28,36,36,47,71,95,108,108,108,100,100,100,116,164,220,247,252,252,252,252,252,239,239,244,252,252,239,196,143,124,116,111,124,135,140,124,95,71,63,60,47,36,28,36,52,68,76,76,76,71,60,60,55,36,28,31,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,12,12,0,0,20,52,79,100,108,100,79,60,60,76,111,140,188,223,247,239,239,252,252,252,228,215,220,236,236,239,228,196,148,100,84,79,71,76,92,116,124,108,84,63,60,55,47,28,12,0,0,12,23,36,47,52,36,28,31,20,12,20,12,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,47,63,76,84,84,60,28,31,60,79,108,124,156,196,236,228,220,228,252,252,252,228,188,172,199,204,204,220,204,188,156,108,79,60,44,47,60,63,71,92,108,92,68,55,52,52,47,36,20,0,0,0,0,0,0,12,12,12,20,12,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,52,60,60,52,28,7,12,44,68,79,100,108,132,172,220,231,196,196,223,244,252,252,244,180,143,143,156,164,172,196,183,164,140,116,84,68,47,23,23,44,55,55,60,68,84,76,60,52,44,44,44,39,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,44,47,47,36,15,0,0,28,52,68,71,84,92,108,148,204,244,204,172,180,220,220,239,252,244,188,124,100,132,127,140,140,164,164,148,124,108,92,71,63,36,12,0,28,44,52,52,47,52,60,60,52,44,44,39,36,36,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,36,36,44,39,20,0,0,0,12,39,60,63,60,68,84,95,116,180,236,228,156,156,180,191,172,220,244,236,196,116,84,100,116,108,116,124,140,135,127,108,95,84,71,68,55,31,0,0,7,28,44,44,44,39,39,39,39,36,36,36,36,36,31,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,31,36,36,23,12,0,0,0,0,23,52,60,52,47,60,84,84,92,140,204,228,175,127,164,183,156,132,199,231,212,188,116,71,60,95,95,92,100,108,116,108,108,100,84,71,63,63,60,52,23,0,0,0,12,28,39,36,36,36,31,23,20,28,31,31,28,28,20,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,23,28,28,28,12,0,0,0,0,0,12,36,52,55,36,36,52,84,60,60,100,172,212,196,124,116,183,159,119,116,196,212,180,172,116,60,39,68,87,84,76,92,92,100,84,84,76,84,63,52,52,60,60,44,20,0,0,0,0,12,28,36,31,28,28,20,7,7,15,23,20,20,20,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,23,15,0,0,0,0,0,0,0,23,44,52,44,23,28,55,76,52,31,60,124,175,196,140,76,127,188,124,84,108,188,204,172,172,116,63,44,28,68,76,76,63,84,84,84,68,60,60,84,68,44,39,52,55,52,36,12,0,0,0,0,0,12,28,28,20,12,15,12,7,0,0,15,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,12,28,44,44,28,12,28,60,76,44,20,39,92,135,180,164,87,68,156,172,84,63,103,172,188,164,180,108,79,44,20,36,63,68,60,52,71,68,76,63,44,47,71,76,52,28,31,52,52,44,28,0,0,0,0,0,0,0,0,12,15,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,36,15,0,31,63,68,36,12,31,79,108,156,175,116,39,84,172,143,52,55,100,156,175,164,172,103,87,36,15,12,36,63,63,47,52,60,63,68,60,36,31,60,76,60,28,15,36,44,44,36,20,0,0,0,0,0,0,0,0,12,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,36,20,0,0,36,68,60,31,0,28,60,84,124,172,148,60,28,116,175,116,28,52,100,151,164,164,156,108,84,36,20,0,0,36,68,52,36,52,52,52,60,60,31,20,44,68,60,28,0,12,36,44,39,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,31,23,12,0,12,36,68,55,28,0,20,52,68,87,143,164,92,12,44,140,159,76,20,52,100,148,156,156,148,119,60,44,28,0,0,0,44,60,36,28,52,44,44,52,60,31,12,28,52,52,28,12,0,15,36,36,36,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,12,0,0,12,39,63,47,20,0,12,44,60,63,103,156,116,36,0,79,164,140,44,31,47,100,148,148,148,143,132,44,60,28,0,0,0,15,52,55,28,23,44,36,39,47,60,36,7,15,36,44,31,12,0,0,20,31,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,7,0,0,0,15,44,60,39,12,0,12,36,52,52,76,140,132,60,0,20,108,164,100,15,39,44,92,143,140,132,140,127,47,60,23,15,0,0,0,28,52,44,15,20,44,36,28,44,60,36,7,0,20,36,28,15,0,0,0,20,28,28,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,15,0,0,0,0,20,44,55,36,7,0,0,28,52,36,44,108,140,87,20,0,55,132,148,60,0,44,44,79,140,140,111,135,119,60,44,20,15,0,0,0,0,31,52,31,7,23,44,28,23,36,55,36,12,0,12,28,28,20,0,0,0,0,20,28,23,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,20,44,52,28,0,0,0,23,44,31,36,79,132,108,44,0,0,84,148,116,36,0,44,44,68,140,140,100,132,119,76,36,20,7,0,0,0,0,12,39,47,20,0,20,36,20,20,36,52,36,12,0,0,20,28,20,0,0,0,0,7,20,20,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,47,44,23,0,0,0,20,39,31,15,39,103,124,63,7,0,20,100,143,76,15,0,28,28,52,132,135,92,119,124,84,28,28,0,0,0,0,0,0,20,44,39,12,0,20,36,20,12,28,47,36,12,0,0,12,15,12,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,39,20,0,0,0,12,36,31,15,20,71,108,84,23,0,0,52,108,124,44,0,0,28,28,39,124,132,84,100,124,79,20,20,0,0,0,0,0,0,0,23,44,28,0,0,20,23,7,0,28,44,36,12,0,0,0,7,0,0,0,0,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,44,36,12,0,0,0,0,20,28,15,12,44,92,92,44,0,0,0,71,116,92,28,0,12,36,36,36,116,124,68,76,116,68,20,20,0,0,0,0,0,0,0,0,28,39,20,0,0,12,7,0,0,20,39,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,28,12,0,0,0,0,12,23,12,0,20,68,100,68,12,0,0,20,84,116,55,12,0,15,23,23,36,116,119,52,60,111,60,20,12,0,0,0,0,0,0,0,0,12,36,36,12,0,0,0,0,0,0,20,36,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,20,0,0,0,0,0,15,20,7,0,0,39,84,79,28,0,0,0,44,84,100,36,0,0,20,20,20,36,108,116,44,47,108,63,36,12,12,0,0,0,0,0,0,0,0,20,36,28,0,0,0,0,7,0,0,15,28,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,20,0,0,0,0,0,0,20,12,0,0,12,63,84,52,0,0,0,0,60,92,68,23,0,0,20,20,12,20,84,103,39,44,95,68,36,7,7,0,0,0,0,0,0,0,0,0,20,28,12,0,0,0,12,7,0,0,12,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,12,0,0,0,0,0,0,0,0,0,0,0,44,84,68,15,0,0,0,15,60,84,39,12,0,0,20,20,0,0,52,92,39,44,76,68,36,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,0,0,0,0,0,0,0,0,0,0,0,20,68,79,36,0,0,0,0,31,55,68,20,0,0,0,0,0,0,0,39,79,36,31,52,60,28,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,44,76,52,0,0,0,0,0,39,55,44,12,0,0,0,0,0,0,0,36,76,36,28,36,55,23,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,7,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,23,68,63,20,0,0,0,0,7,36,52,15,0,0,0,0,7,7,0,0,36,68,28,20,36,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,52,63,36,0,0,0,0,0,20,36,39,7,0,0,0,0,0,0,0,12,47,68,28,7,36,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,44,0,0,0,0,0,0,31,39,28,7,0,0,0,0,0,0,0,15,52,68,28,0,20,36,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,47,52,20,0,0,0,0,0,12,31,36,0,0,0,0,0,0,0,0,0,12,52,63,28,0,15,28,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,47,28,0,0,0,0,0,0,20,28,23,0,0,0,0,0,0,0,0,0,12,47,47,12,0,20,23,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,36,7,0,0,0,0,0,0,28,28,7,0,0,0,0,0,0,0,0,0,0,44,36,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,28,12,0,0,0,0,0,0,12,28,28,0,0,0,0,0,0,0,0,0,0,0,36,36,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,15,0,0,0,0,0,0,0,15,20,12,0,0,0,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t SHINE_TEXTURE_3[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,15,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,20,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,20,0,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,28,0,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,20,7,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,0,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,31,20,20,15,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,31,0,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,28,31,28,28,20,20,12,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,0,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,31,44,28,23,15,7,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,28,36,36,36,44,36,31,20,0,0,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,15,0,12,12,0,0,0,0,0,0,0,0,0,7,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,52,39,52,44,39,28,12,0,12,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,28,12,12,20,12,12,12,0,0,0,0,0,0,12,0,0,0,39,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,52,55,52,52,44,36,20,0,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,28,12,20,20,20,20,12,0,0,0,0,0,12,12,0,0,39,39,12,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,68,55,60,52,44,28,7,12,23,15,0,0,0,0,0,0,0,7,12,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,23,12,0,0,0,0,0,0,0,0,0,0,7,7,0,0,28,36,20,15,28,28,20,20,0,0,0,0,12,15,20,12,0,44,44,23,28,23,0,0,0,0,0,0,0,0,0,0,0,44,68,71,63,60,55,36,12,15,28,20,0,0,0,0,0,0,0,7,20,15,20,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,28,15,0,0,0,0,0,0,0,0,0,7,12,12,0,15,36,36,20,28,28,31,23,12,0,0,0,0,12,28,12,0,44,44,36,44,44,0,0,0,0,0,0,0,0,0,0,20,60,87,71,71,60,47,28,20,36,36,12,0,0,0,0,0,0,0,12,20,23,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,20,36,39,23,0,0,0,0,0,0,0,0,0,7,20,12,7,20,44,31,23,31,36,28,28,0,0,0,0,20,36,15,0,44,44,44,47,44,0,0,0,0,0,0,0,0,0,0,47,79,92,79,76,60,36,28,44,44,20,0,0,0,0,0,0,15,15,15,23,31,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,7,0,0,0,0,0,15,36,44,28,12,0,0,0,0,0,0,0,0,15,28,28,12,36,44,31,31,36,44,28,12,0,0,0,20,44,31,12,44,47,52,52,39,0,0,0,0,0,0,0,0,0,28,68,100,84,84,68,52,36,44,52,23,0,0,0,0,0,0,20,31,28,31,36,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,12,31,47,36,20,0,0,0,0,0,0,0,12,36,44,31,28,44,44,36,36,47,36,28,0,0,0,20,44,44,23,44,52,60,52,36,0,0,0,0,0,0,0,0,0,60,100,108,92,87,60,44,44,60,31,0,0,0,0,0,0,20,28,39,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,15,0,0,0,0,0,28,47,44,23,0,0,0,0,0,0,0,15,47,60,36,36,52,47,47,52,55,36,12,0,0,12,44,60,31,36,52,68,47,28,0,0,0,0,0,0,0,0,28,92,124,108,100,79,52,55,60,44,12,0,0,0,0,12,23,36,36,44,36,20,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,28,12,0,0,0,0,23,47,52,31,7,0,0,0,0,0,0,23,60,60,36,47,60,60,52,60,44,31,0,0,7,39,71,36,36,52,71,47,28,0,0,0,0,0,0,0,0,68,116,132,116,100,76,68,68,52,20,0,0,0,0,20,36,47,47,44,31,15,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,15,28,28,23,12,0,0,0,20,44,55,39,20,0,0,0,0,0,0,36,68,60,44,55,68,68,60,68,44,15,0,0,36,76,47,36,52,76,52,28,0,0,0,0,0,0,0,31,100,148,132,116,92,76,79,68,28,0,0,0,0,23,44,60,60,47,28,12,0,0,7,15,12,7,12,12,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,20,7,0,0,0,12,20,31,36,20,7,0,0,12,36,60,52,23,0,0,0,0,0,12,44,76,63,52,60,79,76,84,55,36,0,0,39,84,68,47,60,84,60,28,0,0,0,0,0,0,0,71,132,151,132,108,87,92,84,36,0,0,0,7,28,52,63,63,47,28,0,0,0,15,20,20,20,20,23,28,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,20,20,12,0,0,0,15,31,36,36,20,0,0,0,31,60,60,36,0,0,0,0,0,15,60,87,68,63,79,95,84,79,47,12,0,39,92,84,52,60,92,60,28,0,0,0,0,0,0,36,108,167,159,132,108,103,92,52,7,0,0,12,36,63,68,68,44,20,0,0,20,28,28,28,28,28,36,39,31,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,23,28,28,15,0,0,7,23,36,44,36,15,0,0,28,52,68,44,23,20,15,0,0,23,76,95,84,79,103,103,100,68,36,0,28,87,95,60,60,92,60,28,0,0,0,7,12,15,76,148,180,156,124,116,108,63,15,0,0,15,39,76,76,68,44,20,0,12,28,36,36,36,36,39,47,44,28,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,28,28,20,0,0,12,31,44,44,28,12,0,20,47,68,60,44,28,20,7,0,36,92,100,87,92,119,108,100,55,12,20,79,108,68,63,100,63,28,0,0,0,12,15,39,119,191,188,148,135,124,84,23,0,0,20,52,84,84,68,36,12,7,28,44,44,52,44,44,55,52,36,20,12,7,12,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,31,36,36,20,0,0,20,44,47,44,23,0,12,44,68,68,52,31,20,0,0,52,108,111,103,119,132,124,79,36,20,84,124,84,76,108,63,20,0,0,12,20,31,84,172,220,188,156,148,108,36,0,0,28,63,92,92,68,31,12,20,39,47,60,55,60,63,63,44,28,20,12,20,20,20,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,39,39,28,7,12,31,47,52,44,20,7,36,68,76,60,44,23,15,12,68,119,124,116,148,148,124,60,28,76,148,108,95,108,60,20,0,0,28,28,60,140,220,215,183,159,124,55,0,0,36,76,100,100,60,28,20,36,52,60,68,71,71,76,55,36,23,20,23,23,20,20,12,0,0,7,12,15,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,44,44,28,12,20,44,52,52,36,15,28,60,76,71,60,39,20,20,84,132,132,143,172,164,92,44,68,156,132,116,116,63,28,7,12,31,44,100,191,239,212,180,140,71,12,12,44,92,111,100,52,28,28,52,68,76,84,84,87,68,44,31,31,31,28,28,23,12,0,12,20,20,20,15,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,44,52,36,23,28,52,60,52,31,28,52,84,87,76,52,23,36,95,140,148,188,199,148,68,60,148,148,140,124,68,36,20,31,36,79,143,228,239,212,164,87,20,12,52,108,119,95,47,36,44,68,84,100,100,100,76,60,44,44,36,36,31,28,12,12,23,28,28,23,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,12,36,52,52,52,39,36,44,60,63,52,36,47,76,100,92,63,36,52,108,148,180,204,196,108,76,132,172,159,140,84,39,20,44,55,119,196,244,236,188,108,31,20,68,124,124,92,52,47,68,92,111,116,108,92,71,60,52,39,36,36,28,23,28,36,36,28,28,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,20,23,20,0,0,0,12,36,52,52,60,52,52,55,68,63,52,52,71,103,103,84,44,68,119,167,204,215,167,95,124,172,180,164,108,44,44,47,95,156,236,244,212,124,47,31,92,140,132,87,60,63,92,124,135,132,116,100,76,60,44,44,39,36,36,36,36,36,36,31,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,20,28,28,28,28,20,7,0,12,36,55,60,60,60,63,68,68,68,68,76,108,124,108,63,84,140,196,223,220,132,124,167,196,196,140,60,76,68,140,196,252,228,156,68,47,108,156,140,92,76,92,132,159,159,140,132,108,84,55,47,52,55,52,44,44,36,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,36,39,44,39,28,12,12,36,60,60,63,76,84,79,76,76,84,119,148,135,92,100,172,220,239,183,148,164,212,215,156,63,100,108,180,236,239,180,87,63,119,164,148,108,100,132,172,188,188,172,140,100,68,68,71,68,52,52,44,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,47,55,60,52,47,31,20,36,60,68,76,95,100,87,87,100,132,164,156,116,132,196,239,228,167,172,212,228,159,84,116,164,212,244,196,116,84,132,172,164,132,140,175,196,207,204,172,119,92,92,87,68,52,52,52,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,52,60,76,79,84,60,44,52,68,71,84,111,116,100,116,148,175,164,140,164,215,247,196,188,199,236,172,124,140,204,236,212,140,108,140,180,175,172,188,212,231,220,188,148,124,116,92,68,55,52,47,28,0,0,0,0,0,12,12,15,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,23,52,76,95,95,100,100,84,79,84,92,111,132,132,132,156,188,172,164,191,231,223,212,212,247,196,172,188,236,223,172,135,159,196,196,196,220,244,239,220,188,156,124,92,68,60,52,36,12,0,15,28,39,44,39,39,36,36,28,20,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,7,7,0,0,12,12,12,20,44,63,84,100,108,108,108,108,124,132,127,143,159,164,175,196,196,196,228,247,231,231,252,228,223,228,236,199,172,188,220,215,220,236,252,244,228,188,132,92,68,60,44,36,36,55,71,84,87,84,76,68,52,44,36,28,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,12,20,20,20,44,39,36,39,36,28,39,60,92,124,132,124,119,132,156,180,196,204,207,212,228,228,236,252,247,247,252,244,244,247,231,204,212,236,239,239,252,247,228,188,140,103,84,84,76,84,92,100,100,100,95,92,92,84,68,52,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,15,20,31,28,28,47,60,68,84,87,92,92,95,111,135,156,167,175,199,220,236,236,244,247,252,252,252,252,252,252,252,252,236,236,244,252,252,236,212,188,164,148,132,116,103,100,100,100,100,100,100,87,68,47,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,28,36,44,44,44,52,71,92,103,108,116,140,175,204,220,236,244,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,239,220,196,164,132,108,100,100,100,100,87,71,52,31,12,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,12,0,0,0,0,0,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,15,15,15,15,15,15,15,15,15,15,15,12,0,0,0,0,0,0,0,7,20,36,52,60,60,68,79,108,132,148,156,188,228,252,252,252,252,252,252,252,252,252,252,252,252,252,252,236,212,172,143,132,135,132,116,100,84,68,60,60,63,68,63,68,68,63,63,63,60,31,28,36,36,36,36,36,28,28,20,12,0,0,0,0,0,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,20,23,28,36,36,36,36,36,44,44,44,36,36,39,44,47,47,47,52,55,60,60,60,63,71,84,100,116,132,156,183,212,231,236,244,252,252,252,252,252,252,252,252,252,252,252,236,220,204,188,164,151,148,132,124,124,119,100,84,76,68,68,68,68,63,63,63,60,31,28,36,36,36,36,36,28,28,20,12,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,15,15,20,23,28,28,28,28,36,39,44,44,44,44,52,52,52,52,60,68,84,100,116,132,156,175,196,212,228,244,252,252,252,252,252,252,252,252,252,236,223,220,228,244,228,191,156,148,132,124,116,116,100,76,68,63,60,52,52,52,44,44,44,36,15,12,28,36,28,28,28,28,23,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,36,52,60,76,92,111,127,148,164,175,180,180,199,231,252,252,252,252,252,252,252,252,247,231,199,183,180,172,159,140,116,95,100,100,92,84,76,60,52,55,60,52,52,52,52,44,44,44,36,15,12,28,36,28,28,28,28,23,20,7,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,39,44,47,52,52,68,87,108,124,156,183,204,207,199,183,175,183,204,220,244,252,244,239,239,252,252,252,252,247,231,196,140,108,103,116,116,92,68,60,52,52,47,44,44,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,15,12,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,36,36,36,39,44,52,71,92,108,111,124,143,159,159,164,159,143,132,132,148,156,172,196,228,247,244,207,204,228,236,236,252,252,252,247,220,172,132,100,76,71,68,63,52,44,39,44,44,44,36,28,23,20,15,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,12,12,20,20,20,20,44,60,76,87,92,92,92,95,100,100,103,108,103,92,84,84,92,100,100,124,164,196,220,244,236,188,156,180,220,204,204,247,244,244,252,252,231,180,116,68,60,60,63,60,60,52,28,0,0,0,12,20,20,15,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,15,12,20,36,52,52,52,60,47,47,52,68,68,76,76,76,60,47,44,44,47,60,68,76,100,140,156,164,188,215,220,172,116,140,204,212,172,180,244,220,212,228,231,239,236,196,135,68,28,28,47,60,55,52,52,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,28,28,23,28,36,36,44,52,55,60,47,39,20,23,28,31,20,12,36,55,63,68,84,108,116,108,124,156,196,199,156,87,84,164,223,167,140,172,236,188,172,180,196,215,228,236,196,148,92,44,0,7,28,47,52,44,44,39,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,15,20,20,20,20,23,28,36,31,20,15,12,20,12,20,7,0,0,20,44,60,60,63,76,84,84,76,95,116,132,164,172,132,63,47,116,196,204,116,108,180,204,156,135,132,167,172,204,228,223,183,148,103,60,15,0,0,12,31,44,44,36,36,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,15,20,20,12,12,0,0,0,0,0,12,0,0,0,0,0,28,44,52,55,60,60,63,60,60,76,84,100,116,148,140,100,44,28,76,156,220,164,63,92,196,180,140,103,108,140,140,156,196,228,212,164,132,100,68,28,0,0,0,0,15,31,36,31,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,47,52,52,52,44,44,52,55,63,68,68,84,108,127,116,76,28,7,36,108,191,212,119,28,100,196,156,124,84,108,100,127,124,148,196,231,188,132,100,92,68,36,12,0,0,0,0,0,15,28,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,44,47,44,28,28,39,47,52,55,52,52,76,100,108,100,60,20,0,12,60,148,220,175,76,15,108,196,140,92,79,92,71,108,108,116,135,188,199,132,79,68,68,60,44,20,0,0,0,0,0,0,0,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,39,44,44,36,15,15,31,44,44,44,36,28,52,68,92,92,84,44,12,0,0,28,92,172,212,127,31,20,116,180,108,60,76,68,63,76,100,100,103,124,164,156,100,60,44,47,52,44,20,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,36,36,36,36,20,0,12,28,36,36,39,28,20,20,52,63,79,76,68,28,7,0,0,7,52,119,183,164,84,0,31,116,180,84,28,63,55,55,60,79,87,100,87,108,140,132,84,44,20,28,36,31,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,28,31,36,28,12,0,7,20,31,36,36,20,12,0,20,39,60,68,63,47,23,0,0,0,0,20,76,140,172,119,44,0,44,119,172,71,20,52,52,47,52,60,76,92,87,71,108,132,124,76,36,0,7,20,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,28,28,15,0,0,0,15,28,28,31,20,0,0,0,20,36,47,55,52,36,15,0,0,0,0,0,47,108,148,143,84,12,0,52,116,164,60,7,28,52,47,28,60,60,68,100,76,60,108,124,116,60,20,0,0,12,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,23,20,7,0,0,0,12,20,23,23,15,0,0,0,0,20,36,47,36,28,15,12,0,0,0,0,0,28,76,116,143,116,52,0,0,60,100,140,60,0,0,44,44,15,52,55,44,79,92,55,55,108,116,100,52,12,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,0,0,0,0,0,7,12,15,20,15,0,0,0,0,0,20,36,44,36,20,0,0,0,0,0,0,0,7,52,92,108,124,84,15,0,0,63,76,100,55,0,0,44,44,23,28,52,44,44,92,76,39,60,108,108,92,47,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,20,36,31,20,12,12,0,0,0,0,0,0,0,36,68,84,103,100,60,0,0,0,60,60,68,52,0,0,28,39,36,0,44,44,28,60,92,60,28,63,103,100,84,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,7,20,31,23,7,0,0,0,0,0,0,0,0,0,15,52,76,68,100,84,23,0,0,0,60,60,52,47,0,0,12,36,36,0,28,44,36,20,76,87,39,28,60,92,84,68,36,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,15,0,0,0,0,0,0,0,0,0,0,0,36,60,52,68,87,63,0,0,0,7,60,60,44,47,0,0,0,31,31,7,0,39,39,12,39,84,71,20,23,60,84,68,52,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,12,0,0,0,0,0,0,0,0,0,0,0,20,52,47,28,76,79,36,0,0,0,20,60,60,44,44,0,0,0,28,28,20,0,23,36,28,0,52,76,47,7,28,60,68,52,44,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,39,52,23,36,76,68,0,0,0,0,28,55,60,36,39,0,0,0,15,20,20,0,7,36,36,7,15,60,68,31,0,23,52,55,47,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,47,36,7,63,76,44,0,0,0,0,39,52,52,28,36,0,0,0,0,20,20,0,0,20,28,20,0,28,60,52,15,0,20,44,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,44,15,28,76,68,12,0,0,0,0,44,52,44,28,36,0,0,0,0,12,12,0,0,0,28,28,0,0,28,52,36,0,0,20,44,39,36,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,28,0,55,76,47,0,0,0,0,0,47,47,31,28,36,0,0,0,0,12,12,12,0,0,15,20,12,0,0,36,44,20,0,0,20,39,36,28,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,36,7,20,68,68,20,0,0,0,0,0,44,44,20,23,28,0,0,0,0,0,0,0,0,0,0,20,20,0,0,12,39,39,12,0,0,20,36,28,28,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,36,20,0,44,68,52,0,0,0,0,0,0,44,44,7,20,23,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,20,44,28,0,0,0,20,36,28,23,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,28,0,12,68,68,28,0,0,0,0,0,0,44,44,0,15,20,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,28,36,20,0,0,0,20,23,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,12,0,36,68,60,0,0,0,0,0,0,0,39,39,0,15,20,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,12,31,31,12,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,20,0,0,60,68,31,0,0,0,0,0,0,15,36,36,0,12,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,31,20,0,0,0,0,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,28,63,60,0,0,0,0,0,0,0,28,36,36,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,12,0,0,0,7,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,7,0,0,44,60,36,0,0,0,0,0,0,0,28,28,28,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,28,7,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,0,0,12,47,47,12,0,0,0,0,0,0,0,28,28,20,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,31,44,28,0,0,0,0,0,0,0,0,31,31,20,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,7,36,36,7,0,0,0,0,0,0,0,0,28,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,23,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,7,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,7,0,0,0,0,0,0,0,0,0,7,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,7,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t SHINE_TEXTURE_4[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,12,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,20,23,12,0,0,0,12,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,15,0,31,31,7,0,0,0,23,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,7,36,36,0,0,0,12,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,31,20,15,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,12,23,36,36,0,0,0,28,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,28,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,12,36,36,28,0,0,7,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,36,28,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,0,39,39,12,0,0,23,36,28,0,0,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,36,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,12,44,44,0,0,0,36,36,12,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,39,31,28,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,12,36,36,28,44,44,0,0,20,44,36,0,0,0,0,7,12,12,0,0,0,7,12,12,0,0,0,0,0,0,0,0,0,0,20,36,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,44,44,36,20,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,20,28,28,44,44,31,0,0,39,44,20,0,0,0,0,20,20,7,0,0,7,20,20,12,0,0,0,0,0,0,0,0,12,28,36,31,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,47,52,47,36,7,0,0,0,0,0,0,15,20,20,12,12,0,0,0,0,28,31,28,47,47,12,0,20,44,44,0,0,0,0,20,28,12,0,0,0,15,20,15,12,0,0,0,0,0,0,0,20,36,44,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,36,52,60,44,28,0,0,0,0,0,0,7,20,28,12,12,0,0,0,0,36,36,36,52,52,0,0,36,44,28,0,0,0,12,28,23,0,0,0,12,15,20,15,12,0,0,0,0,0,7,28,44,47,31,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,63,60,44,15,0,0,0,0,0,0,28,28,12,7,0,0,0,0,44,44,52,52,47,0,12,52,52,12,0,0,7,28,36,12,0,0,20,20,12,20,15,0,0,0,0,0,15,36,60,52,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,39,60,71,60,36,0,0,0,0,0,0,31,31,28,15,0,0,0,0,44,44,55,52,36,0,36,52,36,0,0,0,28,36,20,0,0,28,44,31,23,15,0,0,0,0,0,23,52,63,55,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,52,68,76,52,23,0,0,0,0,0,36,36,60,28,12,0,0,0,47,52,55,55,20,0,52,52,20,0,0,20,39,36,7,0,28,44,44,28,20,0,0,0,0,12,36,63,68,52,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,39,68,79,76,47,12,0,0,0,0,28,36,76,36,28,0,0,20,52,68,60,60,0,28,55,47,0,0,12,36,39,20,0,28,47,44,36,23,12,0,0,0,20,52,76,68,44,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,60,76,87,68,36,0,0,0,0,12,44,76,36,36,0,0,52,68,92,60,55,0,52,60,28,0,0,36,52,28,12,28,52,52,44,28,12,0,0,7,36,68,84,68,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,44,68,92,95,60,20,0,0,0,0,44,71,47,39,0,0,68,71,100,60,44,28,60,55,0,0,28,52,52,23,31,52,60,52,28,12,0,0,20,52,84,87,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,60,84,100,92,52,12,0,0,0,44,63,68,44,0,0,68,76,100,60,23,52,60,36,0,20,52,60,39,39,60,68,60,36,12,0,0,31,76,100,84,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,36,76,100,116,87,44,12,0,0,44,55,92,44,0,0,68,84,92,68,28,63,63,12,7,44,76,52,44,68,76,68,36,12,0,12,55,100,111,79,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,52,92,124,124,92,44,7,0,28,52,100,52,7,28,84,124,79,63,47,68,52,0,36,76,76,68,76,87,76,36,12,0,28,79,124,111,76,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,23,68,111,140,124,76,28,0,7,52,108,60,20,60,108,156,71,63,68,68,23,23,71,87,84,84,92,84,44,15,12,52,100,127,108,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,23,20,12,0,0,12,7,0,0,0,0,0,0,0,0,31,95,140,156,127,60,12,0,60,116,76,36,71,111,156,71,68,71,60,20,60,95,100,92,108,92,44,20,28,76,124,132,92,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,36,31,28,20,12,15,12,0,0,0,0,0,0,12,52,127,175,172,116,39,0,52,116,100,52,100,140,143,84,76,76,44,47,100,119,108,124,103,52,23,44,100,140,124,76,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,20,15,0,0,0,12,20,31,36,39,44,31,28,28,23,12,0,0,0,0,0,20,84,164,207,172,87,20,44,108,124,63,132,180,135,108,76,79,52,100,132,132,135,111,60,36,68,119,140,108,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,20,36,28,31,28,12,7,7,20,47,68,60,60,44,36,36,23,7,0,0,0,0,36,124,207,220,148,60,28,100,135,84,148,212,140,132,84,92,108,148,164,159,124,68,52,87,132,127,84,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,44,63,68,55,36,12,20,47,68,79,76,76,60,47,39,20,0,0,0,0,68,164,228,212,116,36,92,143,108,164,228,164,135,111,124,172,191,188,148,87,76,108,132,108,60,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,15,20,36,60,68,71,76,76,63,47,44,60,84,92,100,92,76,60,36,15,0,0,20,103,199,244,175,76,87,148,140,172,231,191,140,151,183,220,220,175,124,103,124,127,92,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,20,20,20,15,12,12,15,12,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,20,20,28,36,60,84,84,84,84,84,79,76,71,76,92,108,124,124,92,63,36,12,0,44,140,223,223,132,95,140,164,188,231,199,164,188,236,244,204,151,127,132,116,68,20,0,0,0,0,0,0,0,0,0,0,0,12,28,36,36,36,31,28,28,39,52,52,44,31,31,28,20,20,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,20,28,31,44,47,63,103,124,124,108,92,84,92,100,108,116,132,148,140,100,63,28,12,68,164,228,188,124,143,191,212,239,212,204,223,244,220,180,151,132,95,44,0,0,0,0,0,0,0,0,0,20,36,44,44,44,44,52,63,84,92,92,84,79,68,60,47,36,31,23,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,44,52,76,76,87,116,143,148,132,108,108,124,148,148,156,164,148,108,60,36,84,180,220,159,164,204,236,252,236,236,244,228,196,164,119,71,20,0,0,0,0,0,0,20,39,52,52,55,68,84,100,116,124,116,95,87,84,79,79,79,63,47,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,52,71,79,79,92,119,151,164,156,148,164,183,183,188,188,156,108,76,116,196,196,196,212,252,252,252,252,247,220,172,108,52,12,0,0,0,28,44,68,76,92,100,116,135,140,140,119,100,92,92,92,84,79,68,47,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,60,79,84,100,124,164,188,196,204,212,228,228,204,148,124,156,196,228,228,252,252,252,252,228,180,100,39,12,28,52,76,100,124,143,159,164,156,143,124,108,92,92,87,79,68,44,28,12,0,0,0,0,0,12,20,15,7,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,7,0,12,23,31,36,47,60,76,111,143,164,183,191,204,220,236,252,231,191,180,207,239,247,252,252,244,212,164,116,87,84,108,132,148,156,164,167,164,148,132,124,116,111,100,87,71,60,52,52,60,68,76,76,52,39,47,60,52,39,20,12,0,15,31,36,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,7,0,12,23,31,36,44,52,52,76,100,116,143,172,204,228,239,252,252,247,228,228,244,252,252,252,236,204,164,148,159,183,207,223,228,207,196,183,172,164,148,140,132,135,140,148,151,148,148,140,148,135,119,108,76,60,68,103,108,76,44,36,20,28,44,39,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,36,44,47,63,84,92,100,100,116,140,164,196,220,239,252,252,252,252,252,252,236,220,215,236,247,252,252,244,228,207,199,196,204,204,204,204,204,207,204,199,196,191,175,156,156,135,108,84,60,44,44,71,76,44,20,20,20,20,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,31,44,52,63,76,84,92,92,92,108,127,143,156,172,180,196,220,244,252,252,252,252,252,252,252,252,252,252,252,239,236,236,236,228,212,207,204,204,191,172,156,140,127,124,108,92,84,76,68,52,36,28,23,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,39,52,60,71,84,87,92,92,92,92,92,92,92,95,108,132,148,167,188,191,188,172,164,180,204,228,244,252,252,252,252,252,252,252,252,252,252,252,236,215,196,172,156,140,143,148,148,132,116,92,84,79,76,76,71,76,76,63,63,60,52,44,36,44,36,28,28,15,0,0,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,12,44,60,52,36,55,84,92,92,92,92,92,92,92,92,92,92,92,103,124,140,148,143,135,124,116,108,108,103,108,119,148,175,204,228,247,252,252,252,252,247,247,252,252,252,252,252,228,196,164,140,116,100,87,76,60,44,44,52,52,52,60,60,60,60,52,44,44,44,31,23,20,36,36,28,28,15,0,0,12,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,28,20,28,60,79,60,39,55,84,92,92,87,84,76,68,68,71,84,92,87,79,76,76,76,76,76,76,68,52,52,68,100,132,172,196,215,236,252,244,236,236,252,228,228,231,252,252,252,252,244,220,188,148,116,92,76,55,52,44,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,12,0,0,12,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,28,28,28,20,23,47,60,44,28,23,28,20,12,20,36,52,60,68,68,68,68,68,71,71,68,52,31,12,0,20,60,103,132,159,172,172,172,204,236,252,212,204,212,236,220,191,196,236,252,252,247,231,207,191,167,127,100,87,84,71,52,28,20,15,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,12,7,0,0,0,0,0,12,28,36,36,44,44,52,60,60,68,68,63,52,31,12,0,0,0,0,28,60,92,124,148,156,148,124,132,167,212,244,244,172,167,199,220,204,172,148,188,244,247,236,228,220,196,164,148,132,92,60,44,44,44,39,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,20,23,28,36,36,36,44,44,52,44,28,12,0,0,0,0,0,0,0,28,60,84,108,124,132,124,100,84,108,143,180,215,244,212,132,140,212,196,180,180,116,124,199,244,231,212,204,196,188,159,132,108,87,60,44,28,20,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,20,20,20,20,23,28,36,28,12,0,0,0,0,0,0,0,0,0,0,7,31,55,76,100,108,100,92,84,68,63,95,140,164,175,212,236,164,95,132,220,175,156,188,108,92,124,212,236,204,183,188,188,180,156,124,95,76,55,31,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,20,20,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,52,68,84,92,76,68,63,55,47,52,87,143,156,148,180,220,204,116,60,132,212,148,148,180,132,84,63,143,220,220,188,151,164,180,172,135,108,84,68,52,39,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,47,60,71,76,60,52,47,47,44,36,39,87,148,151,132,143,183,212,156,63,31,124,180,103,127,156,140,84,52,63,164,220,204,143,108,116,156,167,148,108,76,52,39,36,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,52,60,60,39,28,31,36,36,36,20,31,92,156,148,116,119,156,180,172,108,23,28,127,156,60,95,140,127,92,68,20,79,172,207,164,111,71,76,124,156,156,124,76,36,23,28,28,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,39,44,44,31,20,15,12,20,23,28,20,12,31,95,156,140,95,100,140,151,172,140,63,0,28,116,140,44,68,132,108,100,84,31,20,100,180,196,140,92,60,55,92,140,156,140,103,52,20,12,20,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,36,36,36,20,0,0,0,7,7,15,20,12,0,36,100,148,135,76,84,124,132,148,156,100,20,0,31,100,127,31,52,119,108,100,79,60,0,31,116,183,180,124,84,52,39,68,108,140,143,116,76,28,0,12,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,28,31,28,15,0,0,0,0,0,0,7,0,0,0,39,103,148,132,68,68,108,127,116,148,124,60,0,0,36,87,116,28,44,95,116,84,76,76,23,0,44,132,180,156,103,79,39,28,44,79,116,132,124,92,47,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,23,28,20,12,0,0,0,0,0,0,0,0,0,0,0,44,108,148,124,55,52,95,119,108,124,135,92,20,0,0,44,84,108,20,28,63,116,71,68,76,55,0,0,60,140,164,124,92,76,36,20,28,60,84,108,116,100,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,47,108,140,116,52,36,84,108,103,92,132,116,60,0,0,0,52,84,92,15,12,44,108,68,52,76,71,20,0,12,68,148,156,108,68,68,39,12,20,39,60,84,100,100,76,39,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,111,132,108,44,20,60,100,100,79,108,124,92,20,0,0,0,60,84,79,12,0,36,92,76,55,55,76,47,0,0,20,84,148,132,76,55,68,39,12,12,28,44,60,76,84,76,60,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,55,108,124,100,36,7,31,79,87,76,79,124,116,60,0,0,0,0,60,84,68,0,0,28,60,76,63,28,71,68,12,0,0,28,92,132,95,52,44,60,36,12,0,20,36,39,52,76,79,63,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,60,108,111,87,36,0,15,55,68,68,60,100,119,95,20,0,0,0,0,60,87,55,0,0,20,31,84,63,20,60,68,39,0,0,0,28,92,111,76,39,36,60,36,12,0,12,20,28,36,55,68,68,52,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,60,108,108,76,28,0,7,44,63,60,60,68,111,116,60,0,0,0,0,0,55,76,31,0,0,12,20,76,60,36,28,68,60,7,0,0,0,36,92,103,63,31,36,55,36,12,0,0,12,20,28,36,52,63,52,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,92,92,68,28,0,0,31,60,52,52,52,84,116,92,20,0,0,0,0,0,52,60,7,0,0,0,12,52,60,52,0,60,68,31,0,0,0,7,44,92,95,52,23,31,52,36,12,0,0,0,12,12,20,39,52,52,39,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,44,79,76,60,20,0,0,20,52,52,44,52,60,100,108,55,0,0,0,0,0,12,52,52,0,0,0,0,12,28,60,60,0,31,63,55,0,0,0,0,12,52,92,92,44,20,28,47,31,12,0,0,0,0,0,12,20,36,44,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,63,60,52,20,0,0,12,44,52,39,44,47,71,108,92,20,0,0,0,0,0,23,52,52,0,0,0,0,0,12,60,60,23,0,52,60,23,0,0,0,0,20,55,87,79,36,12,23,44,36,12,0,0,0,0,0,0,7,20,28,36,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,52,47,36,20,0,0,0,36,52,36,36,44,44,84,100,52,0,0,0,0,0,0,36,52,52,0,0,0,0,0,0,52,55,44,0,28,52,39,0,0,0,0,0,20,60,79,68,28,7,20,44,31,12,0,0,0,0,0,0,0,0,20,15,20,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,39,44,36,20,0,0,0,28,52,36,28,39,36,60,95,76,15,0,0,0,0,0,0,44,47,44,0,0,0,0,0,0,28,52,52,0,0,39,44,15,0,0,0,0,0,28,63,76,60,23,0,20,36,28,12,0,0,0,0,0,0,0,0,0,12,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,20,20,15,0,0,0,20,44,44,20,31,36,36,76,92,44,0,0,0,0,0,0,0,44,44,36,0,0,0,0,0,0,7,52,52,12,0,20,36,28,0,0,0,0,0,0,28,63,68,47,20,0,15,28,28,12,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,0,0,0,0,0,12,36,44,20,20,36,28,44,84,68,15,0,0,0,0,0,0,0,44,44,28,0,0,0,0,0,0,0,47,52,28,0,0,36,36,12,0,0,0,0,0,12,31,60,63,39,15,0,12,28,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,28,44,23,12,28,28,28,68,79,39,0,0,0,0,0,0,0,0,36,36,12,0,0,0,0,0,0,0,31,47,44,0,0,20,31,23,0,0,0,0,0,0,12,36,60,55,36,12,0,12,28,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,7,0,0,0,0,0,0,0,20,39,28,7,20,28,20,36,76,60,12,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,0,12,44,44,0,0,0,28,28,7,0,0,0,0,0,0,15,28,44,47,28,7,0,12,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,12,0,0,0,0,0,0,0,12,36,31,12,12,28,20,15,52,68,36,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,44,44,15,0,0,20,28,20,0,0,0,0,0,0,0,12,28,44,44,23,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,7,28,36,12,0,15,20,12,31,68,52,12,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,36,44,36,0,0,0,23,23,0,0,0,0,0,0,0,0,20,31,36,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,20,0,0,12,0,12,52,60,31,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,20,44,44,0,0,0,15,20,12,0,0,0,0,0,0,0,0,12,28,28,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,20,0,0,12,12,0,28,60,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,39,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,12,23,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,15,0,0,12,12,0,12,44,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,36,20,0,0,0,12,15,7,0,0,0,0,0,0,0,0,7,12,20,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,7,0,0,23,47,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,31,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,7,15,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,0,0,0,0,0,0,7,31,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,36,36,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,15,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,15,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t SHINE_TEXTURE_5[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,15,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,15,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,12,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,7,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,31,0,0,0,0,12,20,20,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,36,0,0,0,0,20,20,20,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,20,20,0,0,0,0,23,23,7,0,0,0,0,0,0,0,0,7,15,12,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,0,36,36,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,12,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,15,12,7,0,0,0,0,0,0,7,36,36,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,12,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,28,28,0,0,0,23,36,36,0,12,12,0,0,0,0,0,20,20,0,0,0,0,12,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,44,44,0,0,0,36,36,28,0,12,12,0,0,0,0,15,28,20,0,0,0,0,28,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,12,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,12,23,15,15,0,39,39,0,0,0,39,39,20,12,12,12,0,0,0,0,28,28,7,0,0,0,20,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,15,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,7,20,44,28,28,0,44,44,0,0,0,44,44,0,12,12,0,0,0,0,20,28,20,0,0,0,15,36,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,12,28,44,36,12,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,12,28,31,31,0,44,44,0,0,12,44,47,0,12,12,0,0,0,7,31,36,7,0,0,12,36,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,47,31,12,0,0,12,28,20,0,0,0,0,0,0,0,0,0,15,20,36,36,0,52,52,0,0,31,52,52,20,20,15,0,0,0,23,36,23,0,0,0,28,44,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,52,28,0,0,0,23,28,12,0,0,0,0,0,0,0,0,23,28,39,28,0,52,52,0,0,44,60,47,28,23,0,0,0,12,36,36,12,0,0,23,44,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,60,52,20,0,0,12,28,23,0,0,0,0,0,0,0,0,23,23,47,28,0,52,52,0,0,60,76,55,31,31,0,0,0,28,44,28,0,0,20,44,39,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,20,52,60,52,20,0,0,28,36,15,0,0,0,0,0,0,0,23,28,68,39,0,52,52,0,0,76,84,63,36,31,0,0,12,44,44,12,0,12,39,52,36,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,7,0,0,0,0,0,0,0,0,7,23,60,68,44,12,0,15,36,31,0,0,0,0,0,0,0,20,28,76,44,12,55,55,0,20,92,92,60,36,20,0,0,36,44,28,0,0,36,52,44,31,28,12,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,0,0,0,0,0,0,15,20,15,31,68,71,36,7,0,28,36,20,0,0,0,0,0,0,12,36,84,44,23,60,60,0,44,92,92,52,39,0,0,15,52,47,12,0,28,60,60,47,36,12,12,20,12,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,0,0,0,0,12,23,28,20,0,0,0,0,15,31,23,23,44,79,76,28,0,15,44,39,7,0,0,0,0,0,0,39,92,47,36,55,52,0,76,108,100,44,39,0,0,36,52,31,0,20,52,71,63,47,20,15,31,31,12,0,0,0,0,0,0,12,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,7,0,0,0,12,31,44,31,12,0,0,0,20,39,28,28,60,87,68,23,0,31,44,28,0,0,0,0,0,0,44,92,55,44,55,52,20,108,119,100,44,28,0,20,52,52,12,7,44,76,79,60,36,20,36,44,20,0,0,0,0,0,7,20,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,15,0,0,0,12,36,55,44,20,0,0,0,28,44,31,36,76,95,60,15,12,44,44,12,0,0,0,0,0,44,92,71,52,60,52,36,124,132,95,44,12,0,44,60,36,0,36,76,92,76,39,28,44,47,20,0,0,0,0,0,15,28,36,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,23,23,12,0,0,12,36,60,60,31,0,0,0,28,44,36,52,92,100,47,7,36,52,36,0,0,0,0,0,44,87,87,63,63,52,60,135,148,76,47,0,20,60,55,12,28,76,108,100,60,39,52,63,31,0,0,0,0,7,23,36,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,28,12,0,0,31,60,68,44,12,0,7,28,52,52,68,108,95,36,20,52,55,36,12,0,0,0,44,84,103,71,63,47,79,140,148,60,39,0,47,60,36,15,60,111,119,84,60,63,84,47,12,0,0,0,15,36,44,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,31,36,23,12,0,23,52,71,52,20,0,12,39,68,68,87,116,92,28,36,55,63,28,12,0,0,36,76,108,76,60,52,100,140,140,55,20,23,68,60,20,52,108,132,108,84,84,92,60,20,0,0,0,28,44,44,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,36,20,0,15,47,76,60,28,0,12,47,76,84,111,132,87,31,52,71,52,28,0,0,20,76,116,84,68,63,116,156,119,55,7,52,68,47,44,108,143,132,100,92,92,60,20,0,0,20,36,52,44,20,0,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,36,20,12,44,71,71,44,12,15,55,92,100,135,132,68,44,63,84,47,28,0,7,84,132,108,71,84,127,172,100,52,28,68,68,60,111,164,159,124,100,100,52,12,0,0,28,52,52,36,12,0,0,0,0,0,0,0,12,20,12,12,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,47,36,28,36,68,76,52,20,20,63,100,119,148,124,60,60,84,79,44,12,0,76,140,124,68,100,132,180,84,36,52,68,68,100,172,188,156,124,116,71,20,0,15,44,60,52,28,0,0,0,0,0,12,20,28,36,28,23,23,20,15,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,52,52,39,44,68,84,63,28,28,68,108,140,156,108,68,76,108,68,31,0,68,140,164,92,116,148,164,68,44,68,84,100,180,212,180,156,140,95,36,12,28,52,60,44,20,0,0,0,12,20,28,44,44,36,39,44,36,23,20,28,20,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,55,63,60,60,68,92,76,44,36,71,119,156,148,100,79,100,108,60,12,60,140,199,116,132,172,148,68,60,84,108,164,223,212,180,156,108,44,23,44,63,60,36,12,0,0,15,28,44,55,52,47,52,55,44,36,44,44,28,20,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,63,76,76,92,95,87,52,52,76,132,164,148,103,100,140,108,47,52,140,228,143,148,196,132,79,76,116,156,223,244,220,172,116,52,39,60,68,52,28,0,7,20,39,60,71,68,68,68,63,52,55,60,52,36,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,84,100,108,111,103,68,60,84,148,180,140,111,135,148,84,52,140,236,164,167,196,124,92,108,151,207,252,244,204,124,68,60,68,68,44,15,7,23,52,71,84,84,84,84,76,71,76,68,52,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,79,103,124,132,124,87,79,108,180,196,151,140,175,127,68,127,228,180,183,196,140,116,156,196,244,247,212,143,92,79,84,68,36,20,36,68,84,100,103,108,100,95,92,84,71,52,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,63,100,140,159,148,111,95,135,196,196,164,183,164,92,119,220,196,191,204,167,164,196,231,247,212,156,108,100,92,60,39,44,84,108,124,132,132,124,124,116,100,76,47,36,20,0,0,0,0,0,0,0,0,12,15,20,20,20,15,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,39,92,132,172,175,148,124,164,204,196,188,196,127,127,204,228,220,228,204,212,223,247,212,164,124,103,84,68,68,100,132,156,167,180,167,159,140,116,84,60,36,7,0,0,0,0,15,28,36,36,39,44,47,39,31,28,20,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,12,20,23,28,44,76,116,164,196,180,159,183,204,212,212,159,156,191,244,239,244,236,239,239,220,180,140,108,95,92,111,151,188,204,212,188,172,143,119,87,60,28,7,0,20,36,60,68,79,84,76,60,52,52,47,47,28,23,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,15,12,20,12,12,15,23,36,47,68,92,119,156,196,199,188,204,220,236,204,183,196,252,252,252,252,247,228,191,148,124,124,132,164,207,236,223,196,164,140,108,71,52,36,44,60,79,92,100,100,100,103,100,95,84,60,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,15,12,20,23,36,36,44,52,55,60,76,100,127,164,191,204,212,228,244,236,220,220,252,252,252,252,244,212,175,156,159,172,212,231,215,180,148,124,108,92,79,84,92,116,124,132,140,148,140,119,108,92,60,36,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,23,36,36,44,44,52,76,108,132,140,156,180,212,223,236,247,252,244,244,252,252,252,252,244,220,196,196,212,212,196,180,164,148,132,124,127,143,164,175,180,183,175,159,135,116,84,39,23,28,20,7,0,0,0,0,0,0,0,12,12,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,47,52,63,92,143,188,204,215,236,247,252,252,252,252,252,252,252,252,247,236,228,236,228,207,196,180,167,172,188,204,215,228,236,223,199,172,132,116,116,108,84,52,44,52,44,44,36,28,12,0,20,28,28,23,20,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,55,76,108,140,172,196,223,244,252,252,252,252,252,252,252,252,252,252,244,228,212,212,228,244,247,244,236,215,204,196,180,159,124,87,68,79,76,60,52,44,47,44,39,36,28,12,0,20,28,28,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,92,132,167,212,244,252,252,252,252,252,252,252,252,252,252,252,244,239,231,228,220,204,196,183,172,156,148,135,116,100,92,92,87,84,79,68,52,36,28,20,15,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,23,39,60,76,92,116,143,172,180,196,220,244,252,252,252,252,252,239,228,223,239,252,244,220,183,151,132,108,92,76,68,60,52,39,36,36,36,36,36,36,36,31,28,20,12,12,12,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,60,76,92,108,124,140,159,175,191,212,236,252,252,252,252,252,252,252,252,252,252,239,215,188,164,164,180,172,156,132,108,87,84,84,76,76,60,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,60,79,92,100,116,132,151,164,180,191,220,236,252,252,247,236,231,236,244,252,252,252,252,252,252,252,252,244,212,172,148,127,103,92,103,116,116,116,111,100,87,84,76,71,47,44,44,28,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,23,52,60,68,92,100,100,108,132,148,156,172,180,188,196,212,212,231,244,236,212,199,188,188,204,231,239,236,236,252,252,252,252,252,247,236,204,164,132,108,71,44,36,36,36,36,36,47,60,63,68,47,44,44,28,20,20,20,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,36,47,55,63,84,84,92,127,148,143,140,124,124,132,140,140,140,148,172,191,199,180,172,159,140,132,156,188,204,204,204,228,252,252,244,236,236,247,223,196,180,164,140,119,84,36,7,0,12,12,15,12,0,0,0,0,12,12,15,20,20,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,28,36,36,39,44,55,76,92,108,103,87,79,71,76,92,95,100,92,84,100,127,148,148,132,127,124,108,92,100,124,151,180,172,156,188,228,244,236,236,212,212,236,228,196,172,156,140,124,111,95,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,20,15,20,28,36,44,60,60,55,52,44,36,52,68,76,68,60,52,47,63,84,100,103,95,87,87,84,76,60,68,76,95,124,148,148,124,151,212,223,223,220,228,196,188,207,239,207,172,156,132,108,92,92,92,76,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,23,28,28,23,20,20,28,36,44,52,44,28,28,31,36,39,52,60,68,68,68,68,68,60,52,44,52,44,52,76,108,124,124,100,116,180,220,204,204,196,204,188,148,159,228,228,188,143,127,119,92,71,68,76,76,68,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,20,20,12,15,15,23,20,20,28,23,28,44,55,55,60,55,47,36,36,36,39,28,20,28,63,84,100,111,84,76,127,212,196,180,196,188,180,196,119,111,196,223,196,156,100,87,92,84,52,47,60,76,76,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,12,12,36,47,52,52,52,44,28,20,20,28,31,23,12,0,28,52,68,79,100,84,55,84,172,220,164,164,204,175,164,212,124,76,148,199,188,164,132,68,55,76,92,60,31,36,60,71,68,52,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,44,47,44,28,12,0,12,20,23,20,7,0,0,23,44,47,55,76,84,44,52,124,196,204,140,148,223,164,124,196,124,60,100,180,180,156,143,108,52,36,68,84,63,28,12,36,60,68,68,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,39,44,44,36,15,0,0,0,0,12,12,0,0,0,0,28,44,39,36,52,68,44,36,84,148,199,172,124,140,231,156,84,164,116,68,52,148,188,148,140,132,92,44,20,47,76,68,36,7,12,36,60,63,60,39,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,31,36,36,36,20,0,0,0,0,0,0,0,0,0,0,0,7,23,39,28,28,36,52,28,12,60,108,156,188,132,103,143,228,143,55,119,108,87,20,100,172,164,119,124,124,76,28,0,31,63,71,44,15,0,12,39,60,60,55,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,28,28,31,23,12,0,0,0,0,0,0,0,0,0,0,0,0,12,23,36,20,20,31,47,28,0,36,84,124,159,164,95,92,151,212,140,52,92,111,100,12,47,132,167,132,100,111,111,68,23,0,20,52,68,52,20,0,0,15,44,60,55,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,23,28,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,15,12,28,44,28,0,20,68,100,116,172,140,68,95,164,188,132,44,68,116,100,31,12,84,148,132,100,92,103,100,60,20,0,12,44,68,60,28,0,0,0,20,44,52,52,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,12,12,0,20,39,28,0,0,44,76,100,124,156,108,52,100,172,172,124,44,47,108,100,52,0,36,95,132,95,84,84,95,87,52,12,0,0,31,60,60,36,12,0,0,0,20,44,52,44,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,15,31,20,0,0,28,68,84,92,148,135,68,52,100,180,164,116,36,36,92,84,55,0,12,63,108,103,79,68,84,84,76,44,0,0,0,20,47,60,44,15,0,0,0,0,20,39,39,36,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,12,52,71,76,100,140,108,36,68,100,180,159,111,20,20,60,76,68,0,0,36,76,108,76,68,63,76,68,63,31,0,0,0,12,36,55,44,20,0,0,0,0,0,20,31,31,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,0,0,0,36,60,71,68,116,124,76,12,84,108,151,148,92,12,12,39,68,68,12,0,12,52,84,84,68,55,60,60,52,52,23,0,0,0,0,28,52,52,28,0,0,0,0,0,0,20,28,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,55,60,60,79,116,100,44,12,92,111,127,140,76,12,15,28,52,52,12,0,0,28,52,79,68,55,47,60,44,39,44,20,0,0,0,0,20,44,52,28,12,0,0,0,0,0,0,20,23,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,55,52,47,92,108,71,20,15,79,108,108,127,68,15,28,28,44,47,15,0,0,7,44,63,76,60,44,52,52,36,36,36,12,0,0,0,0,12,31,44,36,12,0,0,0,0,0,0,0,12,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,52,44,60,92,92,44,0,31,76,108,100,124,68,0,20,20,28,31,20,0,0,0,28,44,68,60,47,36,52,39,28,31,28,7,0,0,0,0,0,28,44,36,20,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,52,36,36,76,87,71,28,0,44,68,108,95,116,68,0,0,0,20,28,28,0,0,0,7,44,52,60,55,36,31,47,28,20,20,7,0,0,0,0,0,0,20,36,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,36,28,44,71,87,44,7,0,52,63,100,100,108,63,0,0,0,7,20,20,0,0,0,0,28,39,52,52,52,23,36,44,20,0,12,7,0,0,0,0,0,0,12,28,36,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,44,44,28,28,60,71,68,28,0,0,44,47,84,108,92,60,0,0,0,0,20,20,0,0,0,0,7,36,39,52,52,36,15,36,36,12,7,20,12,0,0,0,0,0,0,0,20,31,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,44,28,20,36,60,76,44,15,0,12,47,44,63,108,76,52,0,0,0,0,20,20,0,0,0,0,0,20,36,39,44,47,20,20,36,28,12,12,15,0,0,0,0,0,0,0,0,12,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,28,15,20,44,52,60,20,0,0,7,31,31,52,108,68,44,0,0,0,0,12,12,0,0,0,0,0,0,31,31,36,47,36,7,20,36,20,0,0,0,0,0,0,0,0,0,0,0,12,15,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,36,15,20,23,52,55,36,12,0,0,12,28,36,44,108,60,36,0,0,0,0,0,12,12,0,0,0,0,0,20,28,28,36,44,20,0,28,31,12,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,36,20,7,12,31,44,52,15,0,0,0,7,20,36,44,103,60,28,0,0,0,0,0,7,7,0,0,0,0,0,0,28,28,28,44,36,7,12,28,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,28,0,12,12,39,44,36,12,0,0,0,12,12,39,39,100,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,20,28,39,28,0,12,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,12,0,0,20,36,44,12,0,0,0,0,12,12,44,36,95,55,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,15,36,36,12,0,12,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,20,0,0,0,28,36,28,0,0,0,0,0,0,0,39,36,92,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,20,36,28,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,23,0,0,0,7,31,36,12,0,0,0,0,0,0,0,36,44,76,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,12,28,36,12,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,12,0,0,0,20,28,28,0,0,0,0,0,0,0,0,28,39,68,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,12,28,28,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,15,0,0,0,0,28,28,12,0,0,0,0,0,0,0,0,15,36,55,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,12,28,23,0,0,0,0,0,0,0,0,0,12,39,52,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,20,23,12,0,0,0,0,0,0,0,0,7,28,60,52,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,12,20,60,47,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,0,0,0,0,0,0,0,0,0,0,7,52,47,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,52,47,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,12,12,55,44,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,12,12,52,44,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,31,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t SHINE_TEXTURE_6[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,23,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,44,0,0,0,0,0,0,0,7,12,7,0,7,7,0,0,0,0,7,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,44,44,0,0,0,0,0,0,0,12,12,0,0,7,7,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,36,36,0,0,0,0,0,0,0,12,12,0,12,12,7,0,0,0,12,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,31,0,0,12,12,0,0,0,12,12,0,12,12,0,0,0,0,20,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,47,20,0,28,28,0,0,12,20,20,0,20,20,0,0,0,12,23,20,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,76,44,0,44,39,7,0,20,20,20,20,28,23,0,0,0,23,28,12,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,7,0,0,0,68,84,60,0,39,36,0,0,23,23,15,20,20,12,0,0,12,28,28,0,0,0,12,12,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,60,76,63,0,36,28,0,0,28,28,20,28,28,0,0,0,28,31,15,0,0,15,20,7,0,0,0,0,0,0,0,0,0,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,12,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,52,76,68,12,44,28,0,0,28,28,28,36,28,0,0,12,36,36,0,0,12,23,15,0,0,0,0,0,0,0,0,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,28,7,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,44,84,76,20,52,36,0,12,31,28,44,44,28,0,0,28,36,20,0,7,28,28,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,28,0,0,0,0,0,0,0,0,0,0,0,12,20,20,0,0,39,92,92,20,68,44,12,31,44,44,47,47,12,0,12,36,36,0,0,23,31,15,0,0,0,0,0,0,0,20,28,12,0,0,0,0,7,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,28,0,0,0,0,0,0,0,0,0,0,0,28,28,0,0,36,95,95,28,79,60,20,44,47,55,52,52,0,0,31,44,28,0,20,36,28,0,0,0,0,0,0,0,20,31,15,0,0,0,0,7,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,31,44,23,0,0,0,0,0,0,0,0,0,0,31,31,12,0,28,100,100,44,84,60,20,60,60,63,52,44,0,12,44,44,12,12,36,36,12,0,0,0,0,0,12,28,36,20,0,0,0,0,20,23,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,39,44,20,0,0,0,0,0,0,0,0,0,28,36,28,0,15,100,100,60,87,60,23,60,63,60,52,23,0,36,47,36,0,28,39,20,0,0,0,0,0,12,31,39,20,0,0,0,12,20,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,44,20,0,0,0,0,0,0,0,0,12,36,36,0,7,100,100,68,84,52,36,68,79,60,55,0,12,47,47,12,20,44,36,0,0,0,0,0,12,36,44,23,0,0,0,20,36,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,0,0,0,0,0,0,0,0,0,23,52,44,20,0,0,0,0,0,0,0,0,39,39,12,0,95,100,84,84,52,52,68,100,60,55,0,36,52,36,12,39,44,20,0,0,0,0,12,36,44,23,0,0,12,28,39,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,7,0,0,0,0,0,0,0,0,28,60,44,15,0,0,0,0,0,0,0,36,44,28,0,87,100,95,95,60,68,76,111,71,44,12,52,55,20,31,52,28,0,0,0,0,15,36,44,20,0,0,20,36,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,36,68,44,20,0,0,0,0,0,0,23,44,44,0,79,100,111,111,68,68,92,116,84,36,36,60,44,28,52,44,12,0,0,0,20,44,44,20,0,7,28,44,52,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,12,0,0,0,0,0,0,12,44,68,55,28,7,0,0,0,0,7,52,52,0,68,100,124,132,100,76,119,124,92,28,55,60,36,47,55,23,0,0,0,28,47,52,28,0,15,36,60,52,39,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,20,31,36,20,7,0,0,0,0,0,20,52,79,63,28,0,0,0,0,0,52,55,28,60,100,132,124,108,84,143,124,87,52,60,52,44,60,39,0,0,0,28,52,52,28,0,28,52,68,60,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,12,0,0,0,0,0,12,28,36,36,15,0,0,0,0,0,23,68,87,68,23,0,0,0,0,47,63,52,47,100,143,124,124,108,188,135,84,63,68,52,60,55,20,12,12,28,55,60,28,12,36,68,76,63,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,23,23,15,0,0,0,0,7,28,44,44,28,7,0,0,0,0,31,76,92,68,20,0,0,0,36,76,71,44,100,151,124,135,135,204,132,84,68,71,60,63,31,12,28,52,68,60,31,23,52,84,84,60,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,28,31,20,7,0,0,0,28,52,55,36,20,7,0,0,0,39,92,100,60,12,0,0,15,84,87,60,100,156,124,148,175,212,127,84,71,76,71,52,28,36,68,79,68,36,36,68,92,84,55,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,31,36,36,28,12,0,0,28,52,68,55,31,7,0,12,20,52,108,108,52,7,0,0,79,100,84,108,156,124,164,199,188,132,79,92,76,76,47,52,76,84,63,52,52,92,108,84,44,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,39,39,31,20,12,23,52,76,76,44,28,20,28,31,68,124,111,47,0,0,60,100,108,116,159,140,180,220,172,127,92,100,87,76,60,92,95,71,60,76,116,116,76,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,39,44,47,39,28,20,52,84,92,71,52,44,36,44,87,140,111,36,0,28,95,124,140,175,167,196,220,164,116,116,108,108,87,108,108,84,76,103,132,116,68,23,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,44,52,52,47,44,47,76,100,92,76,60,52,63,116,148,100,23,0,84,140,180,199,196,212,204,164,116,135,124,124,135,132,108,100,127,143,108,55,20,12,15,7,0,12,12,12,12,0,0,0,0,7,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,52,52,60,60,60,76,103,119,108,79,68,84,140,148,79,12,63,140,212,228,228,223,204,164,151,151,164,164,167,148,135,140,140,92,44,23,20,20,28,31,31,23,20,12,12,20,28,20,20,15,7,12,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,60,68,76,92,116,135,132,108,92,116,164,140,60,44,127,220,244,244,236,212,180,191,196,207,196,188,172,151,124,84,47,36,36,47,52,44,36,28,36,39,39,36,28,28,20,20,20,15,20,12,20,20,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,15,20,7,0,0,0,0,0,0,31,60,76,84,108,132,143,148,140,140,164,191,132,60,108,207,252,252,244,231,220,228,244,239,223,188,151,116,76,55,60,76,79,68,60,68,71,60,44,36,36,39,52,55,60,60,55,44,44,36,31,23,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,23,28,36,44,52,52,44,28,12,0,0,12,44,76,100,116,148,164,167,172,196,220,204,116,119,188,252,252,252,247,244,244,252,236,196,156,124,92,87,111,124,116,116,116,92,68,60,68,84,95,100,95,92,84,84,79,60,44,39,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,31,44,60,63,76,79,92,84,68,44,28,28,60,92,124,159,191,204,212,236,244,191,172,196,252,252,252,252,252,252,236,212,175,148,140,156,172,175,172,151,140,127,132,132,132,119,103,100,95,95,92,84,76,55,31,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,12,0,12,20,20,12,0,0,0,0,0,12,28,44,68,76,92,92,95,95,95,92,87,87,108,124,148,188,228,247,252,236,220,223,252,252,252,252,252,247,231,204,188,196,207,220,228,228,212,188,172,148,124,108,100,100,95,84,68,47,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,28,20,0,0,12,28,44,44,28,31,44,52,44,44,36,36,44,44,47,47,47,52,52,68,84,100,103,103,116,116,116,127,148,164,183,204,231,247,252,244,244,252,252,252,252,252,247,236,231,236,244,244,231,212,180,143,119,108,103,92,68,52,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,20,15,0,0,0,12,36,44,39,47,68,76,79,84,79,92,124,140,135,132,140,143,140,132,124,119,119,132,164,175,196,212,212,196,191,207,231,247,252,252,252,252,252,252,252,252,252,252,236,220,188,164,140,116,100,84,68,47,28,20,15,15,15,15,15,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,0,0,0,0,20,31,36,36,36,39,39,44,44,63,92,116,124,132,151,164,164,164,156,156,151,148,151,164,204,231,252,252,252,244,244,247,252,252,252,252,252,252,252,252,252,252,228,191,156,124,92,71,60,60,60,52,47,44,44,36,36,28,23,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,23,20,44,76,87,71,76,100,119,132,156,172,180,183,199,220,236,244,247,247,252,252,252,252,252,252,252,252,252,236,212,164,124,84,60,52,47,44,36,28,28,20,15,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,20,47,60,39,44,60,84,108,132,148,164,180,199,204,196,191,207,236,252,252,252,252,252,252,252,252,252,252,236,196,156,108,76,60,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,12,20,31,44,60,79,100,124,148,167,180,172,164,148,148,164,204,228,244,244,252,252,252,247,228,223,236,252,252,239,220,183,148,108,76,60,52,47,39,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,52,71,92,108,140,172,191,204,191,167,135,108,92,92,116,164,220,220,220,236,252,244,244,247,220,188,180,212,236,228,215,204,204,188,164,135,100,71,52,44,44,36,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,60,68,84,108,140,180,204,204,188,156,132,100,68,44,47,68,108,164,212,199,180,196,244,252,228,228,252,231,175,124,132,167,191,188,188,188,188,175,151,116,103,103,92,68,44,36,36,23,20,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,28,60,100,132,164,175,172,156,143,116,92,60,28,7,20,47,76,108,156,204,196,148,151,207,252,252,196,199,244,236,196,119,71,76,116,140,143,140,148,148,148,143,132,100,68,60,68,71,60,36,20,20,20,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,52,84,108,124,116,116,116,108,84,55,23,0,0,0,31,60,76,108,148,196,204,140,108,164,236,247,236,164,172,212,231,196,156,79,36,36,76,108,124,111,100,108,103,87,92,108,103,76,52,47,44,44,36,23,15,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,52,71,84,87,92,87,76,60,36,12,0,0,0,0,20,44,60,71,92,143,204,212,148,76,108,188,231,228,220,148,148,172,220,196,172,124,60,12,15,47,76,100,103,84,68,84,87,76,68,76,92,84,60,44,39,36,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,23,36,55,68,71,76,68,55,39,23,0,0,0,0,0,0,12,28,52,55,60,79,140,212,220,148,60,68,127,188,212,204,204,148,119,148,196,191,172,148,100,52,7,0,28,60,76,84,84,60,52,60,68,68,60,60,68,68,60,44,36,36,31,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,23,39,36,44,60,47,36,28,15,0,0,0,0,0,0,0,0,20,39,47,47,44,63,132,204,223,156,63,36,95,148,156,196,183,188,156,95,116,180,172,164,159,116,87,44,0,0,7,36,60,68,76,63,44,36,44,52,60,55,52,52,52,47,39,31,28,28,23,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,23,28,28,36,20,20,20,12,0,0,0,0,0,0,0,0,0,12,28,39,44,31,36,60,124,188,212,164,68,15,60,116,140,124,188,175,172,167,87,79,148,156,156,148,132,95,76,36,0,0,0,20,44,60,60,55,44,36,31,28,28,39,47,47,44,44,36,28,23,23,20,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,20,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,15,31,36,36,20,23,60,111,164,196,164,76,12,28,92,111,100,119,183,172,159,172,92,47,111,148,124,132,135,95,84,71,28,0,0,0,0,28,52,55,52,44,31,28,23,20,20,28,39,44,44,44,36,23,12,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,31,20,12,20,63,100,143,172,156,79,20,0,60,92,84,60,124,188,164,140,180,100,20,84,148,108,116,127,108,76,76,63,23,0,0,0,0,12,36,52,52,36,28,20,20,20,12,0,12,28,39,39,36,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,20,12,0,20,60,92,132,156,143,79,20,0,28,76,76,44,44,132,188,140,124,183,108,12,68,116,116,100,100,124,79,60,76,55,20,0,0,0,0,0,20,36,47,44,28,15,12,12,12,0,0,0,15,31,36,36,31,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,12,0,0,20,60,84,116,140,140,84,23,0,0,60,76,44,28,52,127,183,111,103,188,116,12,44,84,132,84,79,108,103,52,52,76,47,12,0,0,0,0,0,0,20,39,44,31,15,0,7,0,0,0,0,0,0,20,31,31,28,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,7,12,44,68,95,124,132,84,28,0,0,28,68,60,12,20,68,127,183,92,87,183,116,20,15,68,124,84,76,76,108,76,39,47,71,44,7,0,0,0,0,0,0,12,28,36,36,23,7,0,0,0,0,0,0,0,0,12,20,28,28,20,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,55,79,116,119,84,28,0,0,0,55,68,36,0,12,79,132,167,76,76,180,108,31,0,60,87,108,68,55,84,95,52,31,47,68,36,0,0,0,0,0,0,0,0,15,28,36,28,12,0,0,0,0,0,0,0,0,0,0,12,20,15,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,36,55,71,100,108,84,31,0,0,0,28,63,55,0,0,7,92,135,140,60,68,167,100,39,0,39,68,108,68,52,60,92,76,36,28,52,60,28,0,0,0,0,0,0,0,0,0,20,28,28,20,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,60,92,103,84,36,0,0,0,7,52,60,28,0,0,15,92,135,108,60,68,159,92,47,0,12,60,92,76,60,36,68,84,52,20,20,52,55,23,0,0,0,0,0,0,0,0,0,7,20,23,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,63,92,87,36,0,0,0,0,31,60,52,0,0,0,28,95,140,84,55,68,156,92,52,0,0,55,68,92,63,36,44,76,71,36,12,23,52,52,20,0,0,0,0,0,0,0,0,0,0,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,31,68,84,36,0,0,0,0,12,52,60,23,0,0,0,52,84,132,63,44,60,140,95,55,0,0,36,60,87,60,52,20,55,76,52,23,0,28,52,44,12,0,0,0,0,0,0,0,0,0,0,0,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,20,52,76,39,0,0,0,0,0,36,55,44,0,0,0,0,60,76,108,52,28,44,103,84,55,0,0,12,55,68,68,60,20,36,60,60,28,12,0,28,52,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,15,15,28,52,36,0,0,0,0,0,12,52,52,20,0,0,0,0,68,68,68,44,12,36,68,60,52,0,0,0,52,55,68,55,44,12,47,60,36,12,0,0,28,47,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,12,15,36,47,20,7,0,0,0,0,0,36,52,36,0,0,0,0,20,63,68,52,39,0,36,47,52,52,0,0,0,36,52,60,55,52,12,28,52,52,23,12,0,0,28,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,52,36,0,0,0,0,0,0,12,47,47,12,0,0,0,0,36,60,71,44,36,0,36,39,52,52,0,0,0,12,47,52,52,52,31,0,39,52,36,7,0,0,7,28,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,44,28,7,0,0,0,0,0,0,31,44,28,0,0,0,0,0,52,60,68,36,36,0,36,44,52,47,0,0,0,0,44,47,47,52,47,0,20,44,47,20,7,0,0,12,28,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,31,28,0,0,0,0,0,0,0,12,44,44,12,0,0,0,0,0,52,55,44,28,23,0,36,52,60,44,12,0,0,0,31,44,44,44,52,23,0,28,44,36,0,0,0,0,12,28,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,7,0,0,0,0,0,0,0,31,44,28,0,0,0,0,0,7,52,52,28,20,20,0,28,52,71,44,20,0,0,0,12,44,44,31,47,44,0,12,39,44,15,0,0,0,0,12,28,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,39,36,0,0,0,0,0,0,28,52,52,28,28,15,0,12,36,68,44,28,0,0,0,0,39,44,28,44,44,15,0,20,39,31,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,36,20,0,0,0,0,0,0,44,52,44,20,20,0,0,0,15,52,31,28,0,0,0,0,28,39,36,28,44,31,0,0,28,36,20,0,0,0,0,0,0,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,28,0,0,0,0,0,0,0,47,52,28,12,12,0,0,0,12,44,31,28,0,0,0,0,7,36,36,12,44,39,7,0,12,36,31,0,0,0,0,0,0,12,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,36,12,0,0,0,0,0,0,0,47,47,12,12,12,0,0,0,7,31,28,28,0,0,0,0,0,36,36,15,31,36,23,0,0,20,31,20,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,0,0,0,0,0,0,0,12,47,47,0,12,12,0,0,0,0,28,36,36,0,0,0,0,0,23,36,28,12,36,36,0,0,7,28,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,12,0,0,0,0,0,0,0,31,44,44,0,0,0,0,0,0,0,12,36,36,0,0,0,0,0,7,31,31,0,31,36,15,0,0,15,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,0,0,0,0,0,0,44,44,31,0,0,0,0,0,0,0,0,23,23,0,0,0,0,0,0,28,28,12,15,36,28,0,0,0,20,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,7,0,0,0,0,0,0,0,0,44,44,12,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,20,28,23,0,31,31,12,0,0,12,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,15,0,0,0,0,0,0,0,0,0,44,44,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,28,28,0,20,28,20,0,0,0,15,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,20,39,39,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,23,23,12,0,28,28,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,36,31,0,0,0,0,0,0,0,0,0,28,28,12,0,0,0,0,0,0,15,20,20,0,20,28,15,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,36,15,0,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,0,20,20,0,7,23,23,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,15,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,12,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,31,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,20,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t SHINE_TEXTURE_7[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,12,12,0,0,0,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,23,7,0,0,0,0,0,0,0,0,0,0,0,28,28,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,12,15,7,0,0,0,0,0,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,28,12,0,0,0,0,0,0,0,0,0,0,28,28,7,0,0,0,0,0,0,0,0,7,15,15,0,0,0,0,0,20,20,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,47,28,0,0,0,0,0,0,0,0,0,0,36,36,20,0,0,0,0,0,0,0,0,12,20,20,0,0,0,0,12,28,20,0,0,0,0,0,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,44,12,0,0,0,0,0,0,0,0,0,36,36,36,0,0,0,0,0,0,0,0,20,20,12,0,0,0,0,31,28,12,0,0,0,0,12,23,20,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,60,31,0,0,0,0,0,0,0,0,0,31,39,39,0,0,0,0,0,0,0,0,20,20,12,0,0,0,20,36,28,0,0,0,0,0,23,28,12,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,52,15,0,0,0,0,0,0,0,0,20,44,44,0,0,0,0,0,0,0,0,28,23,0,0,0,12,36,36,20,0,0,0,0,20,28,20,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,52,68,36,0,0,0,0,0,0,0,0,7,44,44,0,0,0,0,0,0,0,15,28,28,7,7,0,20,47,36,7,0,0,0,12,31,28,12,20,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,28,68,60,15,0,0,0,0,0,0,0,0,44,44,0,0,0,0,0,0,0,23,31,28,7,7,12,36,52,31,0,0,0,0,28,36,20,20,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,0,0,15,12,0,0,0,0,0,0,12,55,76,44,0,0,0,0,0,0,0,0,47,47,12,0,0,0,0,0,0,44,44,36,12,12,12,52,44,20,0,0,0,20,36,28,20,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,7,0,7,12,7,0,0,0,0,0,0,31,76,68,20,0,0,0,0,0,0,0,47,47,28,0,0,0,0,0,7,60,60,28,12,12,36,60,44,0,0,0,12,36,36,28,28,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,7,0,0,15,20,0,0,0,0,0,12,63,92,55,0,0,0,0,0,0,0,47,52,44,0,0,0,0,0,23,63,68,20,15,23,60,63,28,0,0,0,28,44,36,28,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,12,0,20,36,20,0,0,0,0,0,36,92,84,23,0,0,0,0,0,0,39,52,52,0,0,0,0,0,52,76,84,20,23,36,76,55,12,0,0,20,44,44,36,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,12,7,20,28,12,0,0,0,0,12,68,100,60,0,0,0,0,0,0,28,52,52,0,0,0,0,0,68,84,84,28,39,60,79,44,0,0,7,39,44,44,39,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,12,7,20,28,20,7,0,0,0,36,92,92,28,0,0,0,0,0,12,55,55,0,0,0,0,0,79,84,71,31,44,84,71,23,0,0,28,52,52,44,39,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,15,15,36,39,28,12,0,0,12,76,111,76,0,0,0,0,0,0,60,60,0,0,0,0,12,84,100,60,44,60,92,60,0,0,20,52,60,52,52,31,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,20,20,44,39,28,15,7,0,36,108,108,36,0,0,0,0,0,60,60,20,0,0,0,31,84,119,52,60,84,92,36,0,0,44,60,60,52,52,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,39,23,28,47,44,28,12,0,7,84,119,76,0,0,0,0,0,60,60,36,0,0,0,52,92,132,52,79,100,79,20,0,31,60,68,60,52,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,44,28,36,52,44,36,20,0,36,108,108,36,0,0,0,0,55,60,47,0,0,0,68,108,127,76,92,108,60,12,28,60,76,71,76,52,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,15,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,44,36,44,60,60,39,20,7,76,119,84,12,12,12,0,47,63,60,0,0,0,84,132,116,108,111,108,44,23,60,71,84,87,76,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,31,28,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,23,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,52,44,52,68,63,39,15,31,108,111,39,15,15,12,36,68,63,0,0,12,87,156,108,132,124,92,31,52,76,92,100,100,63,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,36,31,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,28,31,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,60,52,60,76,71,36,20,76,116,84,28,28,20,20,68,68,0,0,28,95,164,111,140,124,76,44,79,100,124,124,84,28,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,36,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,36,44,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,47,68,63,71,84,76,31,36,103,108,52,28,28,12,68,68,12,0,44,116,164,140,132,116,60,76,100,124,140,124,44,0,0,0,0,0,0,0,0,0,0,0,0,7,28,44,44,44,28,7,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,52,52,44,23,0,0,0,0,0,0,0,0,0,0,0,0,0,28,55,76,76,84,92,71,36,71,116,87,44,36,20,68,68,23,0,68,148,167,164,124,103,68,103,132,156,148,76,12,0,0,0,0,0,0,0,0,0,0,0,28,44,47,44,28,12,0,0,0,0,0,7,12,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,52,63,68,60,36,12,0,0,0,0,0,0,0,0,0,12,15,36,60,84,84,100,100,68,52,103,111,71,36,36,68,71,39,0,84,172,180,164,132,92,108,132,172,156,92,23,0,0,0,0,0,0,0,7,7,12,28,47,52,52,36,15,0,0,0,0,7,20,28,23,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,39,60,71,76,76,52,20,0,0,0,0,0,0,0,7,12,20,36,68,87,92,116,108,63,84,116,100,60,44,76,76,52,0,103,180,196,148,132,103,140,172,180,124,39,0,0,0,0,0,0,12,20,20,36,52,60,52,39,20,0,0,0,0,20,28,28,28,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,39,63,84,92,87,68,28,0,0,0,0,0,0,0,12,28,52,76,100,111,135,116,84,108,108,92,52,92,84,68,20,124,191,199,148,140,143,180,196,156,71,7,0,0,0,0,12,28,39,60,68,63,60,47,23,0,0,0,20,36,36,36,28,28,20,7,0,0,0,0,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,71,92,95,92,76,44,12,0,0,0,0,0,20,39,63,84,116,135,156,124,108,116,124,71,100,92,76,44,140,215,196,164,156,188,212,180,108,28,0,0,0,12,28,52,76,87,84,68,52,28,0,0,20,36,44,39,36,36,28,12,0,0,0,12,20,20,20,20,15,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,68,92,95,100,84,52,20,0,0,0,0,20,44,76,100,132,164,172,132,132,124,108,116,119,92,60,164,236,199,191,204,228,207,140,44,0,0,0,28,60,95,116,108,79,60,31,7,12,36,47,44,44,44,36,20,0,0,20,36,36,31,23,20,20,20,20,12,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,84,100,100,92,68,28,0,0,0,20,44,84,108,148,180,172,148,132,148,135,159,108,79,180,236,212,220,244,231,164,68,7,0,20,60,116,148,140,100,71,36,23,36,52,52,52,47,44,31,20,20,36,44,39,36,36,36,36,36,23,23,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,79,95,100,95,76,39,12,0,15,44,92,127,180,204,180,164,164,164,183,124,100,204,239,236,236,236,180,84,15,12,47,108,156,164,140,92,55,44,55,68,71,63,52,52,44,44,44,44,44,44,44,52,63,63,47,36,28,15,12,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,71,92,100,100,84,52,23,28,60,100,148,212,212,199,180,196,212,140,124,220,244,244,236,188,92,28,36,87,140,172,156,116,84,79,87,100,92,92,92,84,63,55,52,52,63,76,84,79,60,44,36,36,36,31,28,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,20,20,20,12,12,0,0,0,0,0,0,0,0,0,0,0,28,60,92,100,100,95,84,76,84,124,180,231,228,220,223,236,159,151,236,252,244,196,108,52,68,124,167,172,156,127,111,108,116,132,148,132,100,84,84,92,103,108,95,76,55,44,44,44,39,36,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,20,20,20,23,36,36,36,36,39,36,23,12,0,0,0,0,0,0,15,52,84,100,116,132,132,135,164,204,236,244,244,252,188,188,247,247,207,135,87,108,164,204,212,183,156,148,156,172,175,159,140,132,124,124,111,92,68,52,52,47,44,36,20,0,0,0,0,0,0,12,12,0,0,12,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,23,36,36,36,36,39,44,44,47,52,52,52,52,36,20,7,0,12,44,84,116,140,164,183,204,228,244,252,252,220,220,252,223,172,135,151,204,236,231,220,212,220,220,212,180,156,140,124,108,79,63,60,55,47,36,28,28,36,39,39,36,36,28,12,12,20,28,20,7,0,12,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,44,47,52,52,55,60,60,60,60,68,68,68,63,84,119,164,196,215,231,244,252,252,244,244,247,220,188,196,231,252,252,244,236,220,196,175,164,151,132,124,116,108,92,68,60,52,52,52,47,44,44,44,39,36,36,28,12,12,20,28,20,7,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,52,60,60,60,68,68,68,76,87,108,140,188,228,252,252,252,252,252,252,247,236,236,247,252,252,252,244,228,196,175,156,127,108,84,68,60,60,60,55,52,52,52,52,47,44,44,39,36,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,52,63,71,76,100,132,175,215,239,252,252,252,252,252,252,252,244,228,207,188,159,127,100,84,71,68,68,68,60,60,52,39,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,44,52,63,76,92,100,111,124,124,124,119,116,132,183,228,252,252,252,252,252,252,252,244,223,196,156,116,68,31,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,47,52,52,55,60,68,84,100,124,140,140,140,140,135,132,132,124,124,132,151,180,220,244,252,252,252,252,252,252,252,247,236,199,156,127,108,95,92,87,76,52,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,28,36,36,39,44,44,44,47,60,71,87,100,108,111,116,119,124,132,135,124,108,100,84,68,60,68,92,124,156,196,236,252,252,252,252,252,252,252,236,220,212,199,175,148,119,100,92,92,92,92,92,87,79,63,44,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,7,12,20,28,31,28,28,36,44,60,76,84,84,92,92,92,84,76,68,63,55,55,55,52,44,28,12,0,0,36,76,119,164,204,236,239,239,247,252,252,252,252,252,252,236,212,180,172,180,167,132,100,76,68,71,84,92,87,87,87,84,84,84,76,52,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,15,20,7,12,28,36,44,44,44,52,44,44,36,36,36,39,44,44,44,36,28,12,0,0,0,0,0,0,12,44,84,127,159,191,207,220,212,212,228,247,252,252,252,244,244,247,244,228,188,148,140,172,196,180,132,87,52,23,15,31,52,68,84,84,84,84,60,60,44,44,36,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,12,12,0,0,12,12,12,20,20,20,23,36,36,28,15,0,0,0,0,0,0,0,0,0,0,0,20,52,84,124,156,180,183,180,172,164,172,204,236,252,252,244,244,220,220,236,231,236,228,180,116,100,127,180,196,172,124,84,55,28,0,0,0,12,28,47,44,52,44,44,39,36,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,84,116,148,156,156,148,140,132,124,132,172,220,244,247,252,220,220,196,196,228,207,212,236,223,180,108,68,84,124,164,180,156,111,76,60,36,12,0,0,0,0,0,0,7,20,23,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,36,68,84,108,132,132,116,116,108,108,103,100,100,156,204,228,231,247,252,188,188,172,172,220,196,180,196,220,212,188,108,52,44,84,124,148,156,140,100,71,55,39,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,68,84,100,124,116,84,84,84,79,84,84,71,84,140,204,220,212,220,244,244,167,167,156,148,199,207,164,156,183,196,199,180,119,52,20,39,84,116,132,127,116,87,68,52,44,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,44,68,76,95,108,95,68,60,63,63,68,71,68,52,68,124,199,215,188,188,204,220,223,148,148,156,119,167,228,164,135,143,156,156,180,180,132,60,12,12,44,84,108,111,100,92,76,60,52,44,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,55,68,84,92,76,52,44,44,52,52,60,52,47,36,52,116,196,220,180,159,172,180,188,196,119,124,159,92,124,215,196,124,108,124,116,132,167,175,140,68,12,0,15,44,76,92,92,76,68,63,52,44,44,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,52,68,71,60,44,36,36,36,44,47,39,31,28,28,44,100,180,204,164,132,148,156,156,167,164,92,111,164,68,76,175,228,143,92,92,100,87,124,156,172,148,79,20,0,0,20,44,68,76,71,52,44,44,44,39,36,31,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,23,28,36,47,52,44,36,31,23,28,36,44,39,28,15,20,12,31,100,167,196,156,116,116,148,132,140,156,135,71,108,172,71,36,132,220,180,100,71,87,76,71,116,148,172,156,92,28,0,0,0,15,36,52,60,52,28,12,28,36,36,31,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,36,36,31,28,28,20,15,23,36,39,28,12,0,12,0,20,92,164,180,140,84,84,119,132,108,124,156,108,60,108,180,76,12,100,183,204,132,60,71,71,52,68,108,132,164,156,92,36,0,0,0,0,12,28,36,36,28,12,0,15,28,28,28,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,7,12,23,15,0,12,28,36,31,15,0,0,0,0,20,76,151,180,135,79,63,87,132,108,92,111,148,76,36,108,183,84,0,68,140,212,159,79,44,76,60,44,68,100,116,148,148,100,39,0,0,0,0,0,12,28,28,28,20,7,0,7,20,28,23,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,23,20,0,0,0,0,0,12,63,135,164,132,84,44,60,108,108,79,84,103,135,60,20,108,164,60,0,36,108,191,180,124,36,52,71,44,36,71,87,95,127,140,100,44,0,0,0,0,0,0,12,28,23,20,12,0,0,0,12,15,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,12,60,119,140,116,84,36,44,76,116,79,47,87,108,108,55,0,108,143,44,0,12,92,156,183,148,63,28,63,68,28,36,71,84,79,108,132,100,44,7,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,12,52,108,124,103,84,44,28,60,100,92,63,31,92,124,76,52,0,108,140,36,0,0,71,119,172,156,100,20,36,68,52,15,36,76,71,68,100,116,92,39,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,44,100,108,92,71,44,20,44,68,100,68,36,44,84,124,60,44,0,108,148,47,0,0,44,100,156,148,127,55,7,47,63,36,0,36,71,68,60,84,100,79,39,12,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,92,100,79,68,47,20,20,60,84,84,60,12,68,84,108,52,28,0,108,148,44,7,0,12,92,132,132,143,84,15,20,55,55,20,0,44,68,60,47,76,87,76,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,84,92,76,55,47,28,0,44,63,84,60,36,12,84,87,79,47,7,0,108,140,39,12,0,0,76,108,132,127,108,44,0,28,60,44,7,12,44,68,52,36,60,76,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,71,84,68,52,44,28,0,23,55,71,68,52,7,31,84,95,55,44,0,0,108,132,28,0,0,0,44,100,132,100,119,68,12,0,39,60,31,0,12,44,68,44,31,52,68,55,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,68,76,63,44,44,31,12,0,44,55,68,52,31,0,60,84,95,44,44,0,0,92,108,20,0,0,0,15,92,116,95,111,84,36,0,15,47,52,20,0,12,44,60,36,28,44,52,52,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,76,63,28,28,28,12,0,28,52,52,44,39,12,0,76,84,79,39,36,0,0,76,92,15,0,0,0,0,76,100,108,84,95,55,0,0,28,52,44,7,0,15,44,60,31,20,36,47,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,68,60,31,23,28,12,0,12,44,47,39,31,23,0,20,84,84,52,36,20,0,0,68,84,15,0,0,0,0,44,100,116,63,92,60,28,0,0,36,52,28,0,0,20,44,55,28,15,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,52,44,23,20,20,12,0,0,28,47,36,28,23,7,0,47,84,76,36,36,0,0,0,68,87,20,0,0,0,0,15,92,100,71,68,68,44,0,0,12,44,44,15,0,0,20,47,52,20,12,20,23,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,47,39,12,20,20,12,0,0,15,44,44,20,15,12,0,0,68,84,60,36,36,0,0,0,68,87,20,0,0,0,0,0,71,87,84,44,76,44,20,0,0,20,44,36,0,0,0,20,52,44,20,7,20,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,52,36,12,0,12,12,0,0,0,31,44,23,0,0,0,0,12,79,84,44,31,31,0,0,0,68,87,20,0,0,0,0,0,44,84,84,44,60,52,36,0,0,0,28,39,20,0,0,0,23,47,39,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,44,39,20,12,0,12,0,0,0,15,39,36,0,0,0,0,0,36,79,76,36,28,23,0,0,0,68,84,15,0,0,0,0,0,15,76,76,47,36,60,44,20,0,0,12,36,36,12,0,0,0,23,47,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,15,7,0,0,0,0,0,0,31,36,15,0,0,0,0,0,60,76,60,20,20,12,0,0,0,68,84,15,0,0,0,0,0,0,60,76,60,28,52,44,36,0,0,0,15,36,28,0,0,0,0,23,44,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,0,0,0,0,0,0,0,0,0,20,36,28,0,0,0,0,0,0,71,76,28,12,12,0,0,0,0,68,92,20,0,0,0,0,0,0,39,76,68,36,36,52,36,12,0,0,0,20,36,20,0,0,0,0,28,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,12,0,0,0,0,0,0,0,0,7,31,31,12,0,0,0,0,0,20,68,68,12,12,12,0,0,0,0,60,87,28,0,0,0,0,0,0,15,60,60,36,20,39,36,28,0,0,0,7,28,28,12,0,0,0,0,28,44,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,23,12,0,0,0,0,0,0,0,0,20,28,20,0,0,0,0,0,0,44,68,52,0,12,12,0,0,0,0,44,60,15,0,0,0,0,0,0,0,44,55,36,20,28,39,36,12,0,0,0,12,28,20,0,0,0,0,0,23,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,12,7,0,0,0,0,0,0,0,0,12,28,28,0,0,0,0,0,0,0,60,68,36,7,7,0,0,0,0,0,23,28,0,0,0,0,0,0,0,0,20,36,36,15,15,36,28,20,0,0,0,0,15,23,12,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,12,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,12,52,52,12,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,7,28,28,15,12,20,28,28,0,0,0,0,0,20,20,7,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,12,0,0,0,0,0,0,0,0,0,0,0,20,15,0,0,0,0,0,0,0,28,47,44,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,23,28,12,12,12,28,28,15,0,0,0,0,7,20,15,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,28,31,20,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,12,28,20,12,12,20,23,23,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,12,0,0,0,0,0,0,0,0,36,36,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,0,0,0,15,20,12,0,0,0,0,0,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,12,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,7,20,20,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,20,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,12,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,15,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t SHINE_TEXTURE_8[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,12,23,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,12,39,39,0,0,0,0,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,7,7,0,0,0,0,7,44,44,7,0,0,0,15,20,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,12,7,0,0,0,7,7,44,44,20,0,0,0,15,20,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,23,12,7,0,0,7,7,47,47,36,0,0,0,15,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,28,12,12,0,0,12,12,60,63,60,0,0,0,15,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,20,12,0,0,0,12,44,68,68,0,0,0,15,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,20,12,0,0,20,31,68,68,0,0,0,15,36,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,12,31,36,23,20,0,0,20,23,84,71,20,0,0,12,28,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,28,36,47,28,12,0,20,20,84,71,36,0,0,0,28,23,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,7,7,0,0,0,0,0,0,0,23,36,55,28,23,0,12,20,84,71,55,0,0,0,20,28,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,15,12,0,0,0,0,0,12,15,44,47,44,31,0,7,31,84,76,68,0,0,0,28,39,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,7,12,12,0,0,0,0,12,12,39,36,63,36,15,0,31,68,84,76,0,0,0,28,52,28,28,0,0,0,0,0,0,0,0,0,0,0,0,7,36,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,0,0,20,20,12,0,0,0,0,20,36,44,68,44,31,0,36,52,100,76,12,0,0,36,52,28,28,0,7,7,0,0,0,0,0,0,0,0,0,28,39,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,7,0,12,28,23,0,0,0,0,20,20,52,52,63,36,0,28,44,111,76,28,0,0,36,44,12,12,0,12,12,0,0,0,0,0,0,0,0,12,39,39,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,12,0,20,31,20,0,0,0,12,23,47,47,84,44,20,20,36,116,76,44,0,0,36,44,12,12,0,12,12,0,0,0,0,0,0,0,0,31,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,12,0,28,36,15,0,0,0,20,36,60,76,60,36,0,39,108,84,60,0,0,36,63,28,28,12,12,12,0,0,0,0,0,0,0,15,44,44,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,28,12,12,36,31,7,0,0,12,28,63,60,84,44,7,44,92,95,71,0,0,44,79,44,28,20,20,12,0,0,0,0,0,0,0,36,47,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,28,12,23,44,28,0,0,0,28,52,68,95,55,28,39,76,116,76,0,0,44,84,44,23,20,20,0,0,0,0,0,0,0,20,52,44,0,0,0,0,0,0,0,0,0,0,12,20,23,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,44,28,15,36,44,20,0,0,15,36,76,84,76,44,28,60,124,76,12,0,47,92,44,23,28,28,0,0,0,0,0,0,0,44,52,23,0,0,0,0,0,0,0,0,0,7,28,36,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,47,28,20,44,44,12,0,0,36,68,84,100,52,23,52,132,84,31,0,47,92,47,28,36,36,0,0,0,0,0,0,28,55,44,0,0,0,0,0,0,0,0,0,12,39,52,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,47,28,31,52,36,0,0,23,52,92,108,71,36,52,127,95,47,0,47,92,47,39,36,36,0,0,0,0,0,12,52,55,23,0,0,0,0,0,0,0,0,20,52,63,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,44,47,36,44,55,28,0,0,44,87,103,100,52,52,116,116,68,0,52,100,52,44,36,31,0,0,0,0,0,36,60,44,0,0,0,0,0,0,0,0,20,60,76,60,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,44,44,52,52,20,0,28,68,108,119,68,60,100,132,76,0,55,108,52,44,44,20,0,0,0,0,15,60,60,20,0,0,0,0,0,0,0,28,63,76,60,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,55,47,52,60,44,7,12,52,108,132,95,63,92,148,87,12,60,111,60,44,44,12,0,0,0,0,44,68,44,0,0,0,0,0,0,0,36,87,108,68,20,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,63,52,60,63,36,0,36,87,135,124,71,92,156,108,23,60,116,68,47,47,15,15,12,0,20,68,63,20,0,0,0,0,0,0,36,92,116,84,31,0,0,0,0,0,0,0,0,0,0,12,20,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,44,68,60,68,60,23,15,68,132,156,95,100,159,127,44,60,116,84,52,52,12,12,0,0,52,68,44,0,0,0,0,0,7,47,100,108,79,36,7,0,0,0,0,0,0,0,0,15,28,28,23,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,52,68,71,76,52,12,44,124,183,132,108,148,140,60,60,119,103,52,60,20,20,0,28,71,68,12,0,0,0,0,12,60,116,127,84,31,0,0,0,0,0,0,0,0,15,31,36,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,60,76,84,76,44,28,103,196,183,132,151,156,76,68,124,116,55,68,31,28,0,60,76,44,0,0,0,0,12,68,135,148,100,36,0,0,0,0,0,0,0,20,36,36,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,7,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,28,68,84,92,68,36,68,172,220,156,164,156,95,79,140,124,60,63,36,15,36,76,68,15,0,0,0,20,76,156,164,108,36,0,0,0,0,0,0,20,39,44,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,20,23,20,7,0,0,0,12,12,0,0,12,7,12,12,12,7,0,0,0,0,0,36,76,100,95,60,47,124,220,196,188,164,116,103,156,124,71,52,39,15,68,84,47,7,0,0,23,79,156,172,116,44,7,0,7,0,0,20,44,52,47,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,28,28,28,15,0,0,0,0,0,20,28,28,15,20,15,20,20,0,0,0,0,44,92,108,95,55,87,180,228,207,188,132,132,180,124,92,52,44,44,92,92,36,12,0,23,87,159,164,100,39,15,15,12,12,28,52,63,60,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,31,31,31,36,28,12,0,0,20,28,39,39,36,28,36,36,28,15,0,0,12,68,119,132,92,76,140,228,220,212,143,148,191,124,116,52,44,84,111,84,44,28,39,95,164,159,92,36,23,28,20,31,60,79,84,68,36,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,39,44,39,39,39,28,12,12,28,36,52,60,55,55,55,44,28,7,0,31,92,148,135,87,116,191,236,236,164,167,204,132,116,55,68,116,124,71,52,68,116,164,151,87,44,36,36,44,68,92,100,84,60,28,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,44,55,52,52,44,39,28,28,36,52,68,71,76,71,63,39,20,0,52,116,159,132,108,156,231,247,180,183,212,148,108,76,103,148,116,76,92,135,167,143,87,52,44,55,76,92,100,100,71,31,0,0,0,0,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,44,71,79,71,60,52,44,44,44,60,84,95,100,87,68,31,20,60,132,172,140,148,204,252,207,207,220,172,103,111,148,159,116,116,148,167,140,92,63,68,92,108,100,95,76,39,7,0,12,28,31,23,20,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,31,44,68,92,95,84,68,63,60,68,84,111,124,124,92,52,36,71,156,180,167,188,239,236,236,236,191,124,143,180,172,156,172,172,148,100,84,100,116,111,100,79,39,12,20,36,39,36,36,31,23,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,68,92,111,116,95,92,92,100,116,151,159,135,92,60,95,172,188,196,223,252,252,244,196,164,180,215,204,196,196,151,124,119,127,116,100,84,52,36,44,47,44,44,39,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,68,108,140,148,140,140,140,148,167,188,188,132,100,124,180,220,228,252,252,247,204,199,220,236,228,212,180,164,151,140,108,92,76,63,55,52,52,47,44,31,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,47,79,116,148,164,172,175,188,204,223,212,172,140,156,207,244,252,252,247,228,228,244,244,231,212,191,164,140,124,116,103,92,76,68,52,36,15,12,12,20,20,12,0,0,0,0,0,0,0,0,0,7,20,20,15,15,28,28,15,0,0,12,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,36,68,100,132,156,167,188,228,244,239,204,180,199,236,252,252,252,247,247,252,247,228,212,191,172,156,140,124,124,108,92,68,47,44,36,36,28,23,28,31,36,44,52,47,47,44,44,36,31,36,36,28,20,15,28,28,15,0,0,12,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,0,0,0,0,0,0,0,0,15,23,20,12,12,20,20,12,7,0,12,36,68,100,140,180,228,244,247,228,220,236,252,252,252,252,252,252,252,239,220,196,180,175,188,183,167,140,116,103,100,100,103,100,87,79,68,63,52,52,52,47,44,44,44,36,31,36,36,28,20,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,23,23,28,28,28,15,0,15,31,52,60,76,68,60,76,103,119,116,108,100,92,87,79,84,108,148,188,212,236,247,247,247,252,252,252,252,252,252,252,239,236,236,236,220,204,196,196,180,159,132,108,84,68,60,60,60,55,55,52,52,47,39,31,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,23,23,28,28,28,12,0,15,31,44,52,63,68,60,76,103,124,119,116,116,116,116,116,124,132,156,183,212,236,252,252,252,252,252,252,252,252,252,247,244,244,244,244,236,215,188,164,132,100,76,60,52,39,28,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,36,44,52,60,68,76,84,84,92,92,100,100,108,116,132,156,191,228,244,252,252,252,252,252,252,252,252,252,252,252,239,220,188,151,116,100,63,31,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,7,0,20,44,60,71,76,76,76,76,84,84,84,84,84,84,84,84,84,84,84,87,95,116,132,156,164,167,180,207,236,252,252,252,252,252,252,252,252,252,252,252,247,239,212,164,108,71,68,52,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,23,47,47,23,0,20,52,76,84,79,76,76,76,84,84,84,84,84,76,76,68,76,92,100,100,92,92,87,100,124,151,183,212,236,252,252,252,252,252,252,236,236,252,252,252,247,228,204,191,172,132,87,63,52,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,23,47,47,23,0,20,44,60,55,47,36,31,20,20,12,7,20,36,52,55,60,60,60,63,68,92,116,132,135,127,140,180,212,220,231,239,244,252,252,228,204,204,252,244,244,244,228,204,172,151,140,116,92,71,60,44,44,36,36,31,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,47,52,52,52,55,60,60,68,84,100,92,76,68,68,95,132,151,167,183,196,204,220,239,252,244,183,159,188,244,223,223,228,228,212,180,148,132,124,108,71,31,20,31,36,36,31,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,36,44,44,44,44,47,52,52,52,52,55,55,52,52,55,60,60,76,84,92,95,124,148,148,156,188,220,239,247,220,127,127,188,228,188,196,191,204,220,220,172,124,100,92,95,84,52,12,0,0,12,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,20,23,23,28,36,36,36,44,44,44,36,20,20,28,44,44,47,52,52,52,44,44,60,68,76,100,116,108,92,116,156,196,223,244,236,167,79,103,172,188,172,172,164,172,172,212,231,188,119,76,68,76,79,71,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,20,20,20,23,23,28,36,28,15,0,0,12,28,36,36,44,44,44,44,36,20,28,52,68,71,84,92,92,76,63,84,124,180,196,212,223,204,108,52,95,143,140,172,156,143,151,135,148,215,244,204,132,68,44,60,76,76,68,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,12,20,20,20,20,15,12,0,0,0,0,12,23,23,28,36,36,36,36,23,0,0,28,52,63,68,71,76,71,63,60,44,55,108,164,188,172,191,212,156,52,44,95,108,84,164,164,116,132,127,100,148,220,244,212,156,84,31,31,60,76,71,68,39,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,0,0,0,0,0,0,0,0,0,15,20,28,28,23,28,28,15,0,0,0,28,52,60,63,60,60,52,52,55,44,28,44,103,143,164,148,156,199,188,95,12,55,87,84,60,148,180,108,84,124,84,76,148,191,207,199,159,92,36,7,28,55,68,68,60,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,15,20,23,20,12,0,0,0,0,28,52,60,60,55,44,36,44,52,44,23,20,47,103,124,140,124,132,172,204,140,44,0,60,71,44,31,124,196,116,44,76,87,60,63,124,156,180,188,151,108,52,12,0,23,52,63,60,60,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,12,7,0,0,0,0,0,12,28,47,52,55,52,31,28,36,44,44,28,7,15,52,100,116,116,103,108,148,204,180,84,7,0,55,55,12,12,100,188,140,52,28,71,68,39,52,108,132,148,164,148,116,60,20,0,0,20,44,60,60,60,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,47,52,44,20,12,23,36,36,28,12,0,20,52,100,100,100,92,92,116,180,204,124,28,0,12,52,52,0,0,79,167,156,76,0,36,68,52,23,52,95,116,124,143,140,124,76,31,0,0,0,20,44,60,55,52,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,44,44,36,15,0,12,28,31,28,15,0,0,28,60,92,92,84,79,84,92,148,204,156,68,0,0,28,44,44,0,0,60,132,164,103,12,7,52,68,31,15,47,87,100,108,124,127,124,84,44,7,0,0,0,20,44,52,47,44,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,39,44,44,31,12,0,7,20,28,28,15,0,0,0,28,60,84,76,68,68,76,76,116,180,180,100,20,0,0,36,44,44,0,0,47,100,175,119,36,0,23,60,60,15,20,44,76,84,92,103,116,119,95,60,20,0,0,0,0,15,36,47,47,44,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,36,36,28,7,0,0,7,20,20,20,0,0,0,0,31,60,76,68,52,60,68,60,84,140,172,119,44,0,0,0,39,39,28,0,0,28,71,164,124,68,0,0,36,60,39,0,20,44,68,68,76,87,103,111,103,68,28,0,0,0,0,0,15,36,47,44,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,31,31,31,20,0,0,0,0,0,7,15,7,0,0,0,7,36,60,68,60,44,52,60,52,68,116,156,135,76,12,0,0,0,36,36,12,0,0,12,60,132,124,84,0,0,12,52,60,23,0,20,44,60,55,68,76,92,100,103,76,39,0,0,0,0,0,0,15,36,44,36,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,28,28,28,15,0,0,0,0,0,0,0,0,0,0,0,0,12,36,60,60,44,31,44,55,52,52,92,135,140,92,36,0,0,0,0,36,36,0,0,0,0,52,103,127,100,23,0,0,28,52,44,7,0,20,39,52,44,60,68,79,92,100,84,52,12,0,0,0,0,0,0,12,28,36,36,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,23,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,60,44,36,20,31,47,52,36,68,116,132,103,60,7,0,0,0,12,28,28,0,0,0,0,52,76,124,103,44,0,0,0,36,52,28,0,0,20,36,36,36,52,60,68,76,92,87,60,23,0,0,0,0,0,0,0,12,28,36,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,15,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,52,36,20,12,23,39,52,28,47,100,119,111,76,28,0,0,0,0,15,20,20,0,0,0,0,44,55,103,92,52,0,0,0,12,44,44,15,0,0,12,20,20,28,44,52,60,68,84,87,68,36,0,0,0,0,0,0,0,0,12,23,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,44,28,12,7,12,36,52,28,36,84,108,108,84,44,0,0,0,0,0,15,15,12,0,0,0,0,28,44,87,84,68,15,0,0,0,28,44,36,0,0,0,0,15,28,28,36,39,52,60,71,84,68,36,0,0,0,0,0,0,0,0,0,7,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,39,20,0,0,12,28,47,28,20,63,100,100,87,63,20,0,0,0,0,0,15,15,0,0,0,0,0,12,44,76,76,76,36,0,0,0,0,36,44,20,0,0,0,15,28,20,20,20,31,44,52,60,68,63,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,36,15,0,0,0,20,44,28,12,44,92,92,84,68,36,0,0,0,0,0,0,7,7,0,0,0,0,0,0,36,55,60,76,39,0,0,0,0,15,36,36,7,0,0,0,12,23,20,12,12,23,36,44,44,52,60,44,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,36,28,12,0,0,0,20,36,28,12,31,76,92,71,71,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,44,55,71,44,12,0,0,0,0,23,36,20,0,0,0,0,12,12,0,0,0,15,28,39,36,44,52,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,23,7,0,0,0,12,36,28,12,15,63,92,68,63,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,36,60,63,55,23,0,0,0,0,7,28,31,12,0,0,0,0,0,0,0,0,0,7,23,28,23,28,44,39,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,23,20,0,0,0,0,12,28,28,12,0,47,84,68,52,60,44,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,52,47,60,31,0,0,0,0,0,12,28,20,0,0,0,0,0,0,0,0,0,0,0,12,23,20,28,28,31,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,7,0,0,0,0,0,12,20,12,0,36,76,71,39,52,52,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,23,36,23,52,28,0,0,0,0,0,0,20,28,12,0,0,0,0,0,0,0,0,0,0,0,12,20,23,20,20,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,0,0,0,7,12,0,0,20,63,76,44,31,52,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,15,36,28,7,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,7,7,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,12,20,7,0,0,47,76,52,20,44,44,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,20,28,28,20,0,0,0,0,0,0,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,7,7,12,0,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,15,12,0,0,36,71,60,15,28,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,36,20,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,20,60,68,28,12,36,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,23,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,68,44,0,28,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,52,12,12,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,44,52,20,0,28,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,28,0,12,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,28,0,0,23,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,31,31,12,0,12,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,15,0,0,23,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,12,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t SHINE_TEXTURE_9[16384] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,7,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,23,20,0,0,0,7,0,0,0,0,0,0,0,0,0,0,12,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,23,0,0,0,28,28,20,0,0,0,0,0,0,0,0,20,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,12,0,0,36,36,28,0,0,0,0,0,0,0,0,20,23,12,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,36,15,0,0,36,31,23,0,0,0,0,0,0,0,0,20,20,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,28,0,12,23,20,12,0,0,0,0,0,0,0,0,36,36,0,0,0,7,20,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,36,0,12,20,36,28,0,0,0,0,0,0,0,12,52,52,7,0,12,12,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,12,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,39,20,0,20,71,60,12,0,0,0,0,0,0,20,52,52,7,0,12,12,23,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,12,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,52,28,0,23,84,68,12,0,0,0,0,0,0,36,60,52,0,0,20,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,7,0,0,0,12,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,60,47,12,23,55,31,7,0,0,0,0,0,0,63,79,60,7,15,12,28,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,12,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,76,71,12,15,52,36,20,0,0,0,0,0,0,60,68,44,12,28,28,39,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,20,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,15,76,79,28,20,76,68,44,0,0,0,0,0,0,68,68,36,15,36,39,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,7,28,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,76,84,52,31,100,100,68,0,0,0,0,0,20,92,92,39,28,36,60,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,12,0,15,20,0,0,0,12,36,31,12,0,0,0,0,0,0,0,0,0,0,0,0,60,92,68,28,84,108,76,0,0,0,0,0,36,92,92,36,39,36,60,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,20,36,23,0,0,0,20,44,28,0,0,0,0,0,0,0,0,0,7,7,0,39,92,87,36,68,111,79,0,0,0,0,0,55,108,103,31,52,44,52,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,23,31,36,20,0,0,0,28,44,20,0,0,0,0,0,0,0,0,12,12,7,20,92,95,52,68,124,84,0,0,0,0,0,76,116,108,28,60,68,44,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,28,31,39,28,0,0,12,39,44,15,0,0,0,0,0,0,0,12,20,15,0,92,100,63,60,127,84,12,0,0,0,0,92,124,108,44,60,76,39,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,36,39,44,23,0,0,20,44,36,12,0,0,0,0,0,0,0,23,23,0,68,100,79,60,140,100,23,0,0,0,12,108,132,103,68,76,76,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,36,36,44,23,0,0,28,52,31,0,0,0,0,0,0,0,20,20,7,44,100,92,68,140,108,36,0,0,0,23,124,140,92,79,84,63,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,28,23,36,44,28,0,7,39,52,28,0,0,0,0,0,0,12,23,20,20,100,100,76,132,116,47,0,0,0,39,127,156,84,92,92,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,28,39,52,47,23,0,20,52,52,20,0,0,0,0,0,0,31,31,12,92,100,95,132,140,60,0,0,0,60,127,156,92,108,92,44,0,0,0,0,0,0,0,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,44,52,52,55,52,23,0,28,60,44,12,0,0,0,0,0,28,36,23,76,100,111,132,151,71,0,0,0,76,127,148,95,116,84,36,0,0,0,0,0,0,7,20,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,28,60,63,60,68,52,20,0,36,60,36,7,15,15,0,0,15,39,39,52,100,116,124,151,79,0,0,12,100,143,140,103,108,68,20,0,0,0,0,0,12,23,23,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,63,71,71,71,52,28,12,52,60,28,20,28,15,0,0,44,44,44,100,108,124,148,92,0,0,28,127,164,140,127,108,60,0,0,0,0,0,15,28,28,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,76,84,84,76,52,20,23,60,60,28,28,28,12,0,44,55,52,92,100,140,151,108,0,0,39,140,188,148,148,108,47,0,0,0,0,12,28,44,39,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,76,92,92,84,52,23,36,68,52,31,36,28,0,28,63,60,84,100,156,151,119,20,0,44,132,180,151,148,103,36,7,0,0,7,28,52,52,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,84,100,108,92,52,28,44,71,52,44,44,20,0,68,68,84,100,156,156,132,28,0,71,156,188,164,140,92,23,0,0,0,28,52,60,39,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,23,20,15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,7,0,0,0,0,0,12,36,87,111,116,100,55,39,60,68,60,52,44,7,52,76,87,100,151,164,140,39,0,100,183,204,191,148,76,23,0,0,20,52,71,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,28,23,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,20,20,15,12,12,0,0,0,36,92,124,127,108,60,52,68,76,76,60,36,23,76,84,108,140,180,140,52,20,119,204,220,196,148,60,12,0,12,44,76,71,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,36,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,20,28,36,36,36,28,12,0,0,36,92,132,135,111,60,60,76,84,84,68,20,68,79,124,124,196,140,63,31,132,228,236,199,132,44,0,0,36,76,84,44,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,44,55,52,44,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,52,60,47,28,7,0,36,92,132,148,116,71,76,87,108,92,52,47,79,127,116,199,140,76,44,148,244,244,196,108,28,0,28,76,95,68,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,60,60,52,31,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,63,76,76,52,23,0,31,87,148,180,148,92,84,108,116,84,44,76,116,127,196,156,87,60,164,252,244,188,87,12,15,68,103,92,44,12,0,0,0,0,0,0,0,0,0,0,0,0,28,52,68,68,60,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,60,92,100,84,44,20,28,95,175,204,156,108,103,132,116,68,76,92,151,183,180,100,76,180,252,244,164,68,7,55,111,116,68,28,7,0,0,0,0,0,0,0,0,0,0,36,68,84,76,68,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,52,87,116,116,84,36,36,100,180,207,164,124,132,143,100,79,84,164,188,204,111,87,188,252,228,132,36,36,108,140,92,39,12,0,0,0,0,0,0,0,0,7,39,76,92,92,79,47,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,36,76,124,148,124,68,55,100,180,204,172,140,156,143,95,100,148,196,220,124,100,204,252,199,100,36,100,159,132,60,12,0,0,0,0,0,0,0,12,44,84,100,100,87,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,20,60,116,156,151,108,76,108,180,196,180,164,172,132,116,132,212,228,132,116,220,244,164,71,76,156,156,92,28,0,0,0,0,0,0,12,47,84,103,108,92,63,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,15,12,0,36,92,140,164,135,116,132,180,204,196,196,191,148,140,204,231,148,132,231,223,127,76,140,180,124,39,0,0,0,0,0,12,52,87,103,103,92,60,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,20,20,20,31,36,31,44,76,124,151,159,151,156,196,212,223,236,188,172,196,244,159,156,244,188,116,119,188,156,60,12,0,0,0,20,55,92,103,103,92,55,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,20,36,39,36,44,60,71,84,116,148,164,172,196,228,244,252,228,196,199,239,180,180,236,164,135,172,172,84,20,0,0,20,60,92,103,103,87,52,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,20,20,12,12,12,0,0,0,0,0,0,0,0,0,12,12,12,20,23,47,68,76,76,84,100,127,159,180,196,228,244,252,252,228,228,244,204,204,220,172,167,172,100,23,0,23,63,92,108,108,87,47,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,20,20,12,15,28,28,28,28,36,39,36,28,12,0,0,0,0,12,20,31,36,52,76,103,124,124,140,172,207,228,244,252,252,244,244,244,228,228,212,199,172,116,39,28,68,100,124,124,100,52,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,28,28,28,28,36,39,44,44,44,47,52,52,52,47,36,31,36,44,44,52,76,108,151,175,196,220,244,252,252,252,252,252,244,244,231,196,132,76,79,119,140,140,116,63,28,0,0,0,0,0,0,0,7,20,20,28,28,31,23,15,12,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,0,0,0,7,20,36,44,44,44,47,52,52,52,55,60,60,68,76,84,95,108,103,111,132,164,204,231,252,252,252,252,252,252,252,231,188,140,132,148,148,124,76,44,31,28,31,44,52,52,52,52,52,52,52,47,52,52,52,28,15,20,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,20,28,15,0,0,0,20,36,44,44,55,71,92,103,108,116,124,127,124,132,127,127,143,164,180,204,228,244,252,252,252,252,252,252,236,207,188,188,180,156,124,108,103,116,124,132,132,124,116,108,103,100,92,87,76,68,60,39,12,0,7,15,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,23,28,39,36,20,15,28,47,68,71,76,84,87,95,95,100,124,159,180,188,207,220,223,223,220,212,212,228,244,252,252,252,252,252,252,244,244,236,212,188,175,180,196,204,215,220,228,223,196,167,148,132,124,108,100,95,92,92,76,60,36,20,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,20,28,28,36,36,44,52,60,68,76,84,100,108,124,127,140,172,207,228,228,236,244,239,244,236,236,247,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,236,212,175,156,124,100,87,84,84,84,84,84,84,84,84,68,44,36,23,0,0,0,0,0,0,0,0,12,20,20,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,68,84,60,31,47,84,100,100,92,92,92,92,92,92,100,108,124,143,164,164,172,172,172,196,207,220,223,236,244,244,252,252,252,252,252,252,252,252,252,236,212,204,215,236,252,236,215,180,159,140,116,87,76,76,71,60,44,36,28,28,28,28,31,20,0,0,0,0,0,0,0,0,12,20,20,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,28,60,76,44,12,28,60,84,84,84,84,84,87,84,76,76,68,63,60,68,92,127,148,143,140,148,172,188,196,215,244,252,252,252,252,252,252,252,252,252,236,212,183,164,156,164,188,212,223,223,191,156,119,103,92,79,60,52,52,47,44,44,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,20,44,52,31,7,12,20,20,12,0,0,0,7,7,12,20,28,52,68,79,79,84,95,119,140,140,124,140,167,204,236,247,236,236,231,236,252,252,247,247,244,228,191,156,140,119,108,116,135,148,151,143,124,111,100,92,76,76,71,68,60,52,44,39,36,28,28,31,23,15,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,52,60,60,63,68,79,100,108,92,79,100,124,140,159,204,228,231,207,212,207,212,244,244,236,220,212,220,212,196,180,148,111,76,60,60,60,63,60,68,76,76,60,44,28,28,39,52,47,44,39,28,28,31,23,15,12,23,12,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,39,52,52,52,55,60,60,60,68,68,63,68,76,92,95,116,140,167,188,228,196,172,196,188,180,223,228,220,191,156,167,172,172,180,188,180,156,116,68,28,15,28,39,39,36,36,36,39,36,31,20,12,0,0,0,0,12,12,12,12,23,12,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,39,44,44,47,52,52,47,36,31,44,52,60,60,60,60,68,84,108,124,140,156,212,215,148,148,204,172,148,204,196,188,180,124,100,132,140,140,148,164,180,188,159,111,68,31,0,0,12,28,31,28,20,15,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,28,36,36,39,44,44,44,36,20,7,20,39,52,52,55,47,36,44,68,84,100,108,116,132,180,215,172,108,143,228,156,124,188,164,156,167,124,76,68,111,116,111,111,132,148,175,180,148,100,68,36,7,0,0,0,0,12,12,12,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,20,28,36,36,36,36,23,0,0,0,20,39,44,47,52,36,15,23,52,76,79,84,92,100,116,159,204,188,108,92,164,244,143,100,156,140,124,124,143,84,44,55,95,103,100,87,95,111,127,156,156,132,92,68,36,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,28,20,20,23,23,12,0,0,0,7,20,36,39,44,44,28,0,0,36,68,76,68,68,79,84,95,140,188,191,132,60,92,188,215,116,76,119,143,92,95,132,116,60,20,52,84,92,84,76,76,76,87,108,132,140,116,84,60,39,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,15,15,20,20,12,0,0,0,0,0,12,20,28,36,36,36,20,0,0,20,52,68,68,52,60,76,76,76,124,175,188,143,76,36,108,196,180,84,44,100,143,68,84,92,124,84,31,7,44,76,76,71,68,68,60,60,76,95,116,119,108,76,55,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,0,0,0,0,0,0,0,0,15,23,28,28,23,12,0,0,0,36,60,68,52,39,47,76,68,63,103,164,180,148,92,28,52,124,180,151,55,20,92,124,76,68,68,100,92,60,12,0,36,68,68,60,55,63,52,44,44,63,84,95,100,87,63,44,31,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,12,20,23,15,0,0,0,0,20,44,63,60,36,28,47,76,60,47,76,148,164,148,108,52,0,68,140,148,116,23,0,87,100,92,52,52,68,92,68,36,0,0,28,60,68,52,44,60,60,36,28,36,60,68,71,76,68,52,36,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,0,0,0,0,0,0,31,52,60,47,20,20,52,76,52,36,60,132,143,132,116,76,12,15,76,156,124,92,12,0,79,92,100,47,44,52,76,76,60,23,0,0,23,52,68,44,36,44,52,44,23,15,28,44,55,52,52,52,44,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,20,44,55,52,31,7,20,52,71,47,28,47,116,132,124,116,92,36,0,44,79,164,116,63,7,0,63,92,100,44,39,31,52,68,60,47,12,0,0,20,47,63,39,28,31,52,47,28,12,12,28,28,36,36,44,44,28,20,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,52,44,20,0,23,52,68,39,12,36,95,124,108,108,100,60,0,0,60,92,151,111,39,0,0,44,92,92,44,39,20,44,55,60,55,36,0,0,0,12,39,60,44,20,20,44,44,31,12,0,0,12,20,36,28,28,28,23,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,36,52,44,28,0,0,28,55,60,36,0,20,68,116,108,92,100,76,20,0,12,68,111,127,108,12,0,0,23,87,87,44,36,20,23,44,52,47,52,20,0,0,0,12,36,60,44,15,12,28,44,36,20,0,0,0,12,15,20,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,44,44,36,12,0,0,28,60,55,28,0,12,44,92,100,76,92,92,44,0,0,36,68,124,108,92,0,0,0,7,87,87,47,31,28,0,36,39,39,47,39,12,0,0,0,0,28,52,44,20,0,20,36,36,23,7,0,0,0,0,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,44,39,20,0,0,0,31,55,52,23,0,0,28,76,92,68,79,92,63,7,0,0,52,63,119,100,76,0,0,0,0,84,87,52,28,28,0,20,36,36,31,44,28,0,0,0,0,0,28,44,44,20,0,12,28,36,28,12,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,39,28,12,0,0,12,36,55,44,20,0,0,20,60,84,68,60,84,79,28,0,0,0,60,63,100,87,52,0,0,0,0,68,87,68,20,23,12,0,28,36,28,36,44,20,0,0,0,0,0,20,39,39,20,0,0,15,23,20,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,36,31,20,0,0,0,12,36,52,36,12,0,0,12,36,68,71,47,68,84,44,0,0,0,28,60,63,84,79,36,0,0,0,0,47,87,79,12,15,15,0,12,28,28,20,36,31,7,0,0,0,0,0,12,36,39,20,0,0,0,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,31,20,7,0,0,0,12,36,52,31,12,0,0,12,28,52,68,39,44,76,63,12,0,0,0,44,60,52,71,71,15,0,0,0,0,28,84,84,12,12,12,0,0,20,23,12,20,36,20,0,0,0,0,0,0,12,28,36,20,0,0,0,7,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,23,12,0,0,0,0,15,36,44,28,0,0,0,7,23,52,63,44,28,55,63,28,0,0,0,0,52,55,36,60,60,0,0,0,0,0,12,84,84,20,0,0,0,0,7,20,20,12,28,31,12,0,0,0,0,0,0,7,23,36,20,0,0,0,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,23,15,0,0,0,0,0,15,36,44,20,0,0,0,0,7,31,60,47,20,31,55,39,0,0,0,0,20,52,52,20,44,44,0,0,0,0,0,0,84,84,44,0,0,0,0,0,12,20,12,12,28,28,0,0,0,0,0,0,0,0,20,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,20,7,0,0,0,0,0,12,31,36,20,0,0,0,0,0,15,44,52,28,15,44,44,12,0,0,0,0,36,52,44,20,36,31,0,0,0,0,0,0,68,84,60,0,0,0,0,0,0,12,12,0,12,20,12,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,7,0,0,0,0,0,0,12,28,20,12,0,0,0,0,0,12,31,47,28,0,28,44,28,0,0,0,0,0,47,52,20,23,28,20,0,0,0,0,0,0,52,84,76,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,20,28,20,0,0,0,0,0,0,7,28,47,36,12,15,39,36,0,0,0,0,0,12,47,47,0,20,20,7,0,0,0,0,0,0,31,84,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,31,20,7,0,0,0,0,0,0,15,39,36,12,0,31,36,15,0,0,0,0,0,28,44,44,0,12,12,0,0,0,0,0,0,0,12,76,76,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,0,0,0,0,0,0,0,0,28,36,20,0,20,36,23,0,0,0,0,0,0,44,44,23,0,12,12,0,0,0,0,0,0,0,0,60,60,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,20,36,20,0,7,28,28,7,0,0,0,0,0,0,44,44,0,0,7,7,0,0,0,0,0,0,0,0,44,55,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,12,0,0,0,0,0,0,0,0,0,12,31,23,0,0,20,28,12,0,0,0,0,0,0,20,39,36,0,0,0,0,0,0,0,0,0,0,0,0,28,44,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,0,0,0,0,0,0,0,0,0,12,28,28,7,0,12,28,20,0,0,0,0,0,0,0,36,36,23,0,0,0,0,0,0,0,0,0,0,0,0,12,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,12,0,0,20,23,7,0,0,0,0,0,0,0,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,7,0,0,12,20,12,0,0,0,0,0,0,0,15,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,12,0,0,0,12,12,0,0,0,0,0,0,0,0,28,36,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,23,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,7,12,0,0,0,0,0,0,0,0,0,28,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,15,20,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,15,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + const uint8_t * SHINE_TEXTURES[10] = { + SHINE_TEXTURE_0, + SHINE_TEXTURE_1, + SHINE_TEXTURE_2, + SHINE_TEXTURE_3, + SHINE_TEXTURE_4, + SHINE_TEXTURE_5, + SHINE_TEXTURE_6, + SHINE_TEXTURE_7, + SHINE_TEXTURE_8, + SHINE_TEXTURE_9, + }; + const int SHINE_TEXTURE_WIDTHS[10] = { + 128, + 128, + 128, + 128, + 128, + 128, + 128, + 128, + 128, + 128, + }; + const int SHINE_TEXTURE_HEIGHTS[10] = { + 128, + 128, + 128, + 128, + 128, + 128, + 128, + 128, + 128, + 128, + }; + } +} +#endif diff --git a/src/igl/opengl2/sort_triangles.cpp b/src/igl/opengl2/sort_triangles.cpp new file mode 100644 index 0000000000..ae9671099c --- /dev/null +++ b/src/igl/opengl2/sort_triangles.cpp @@ -0,0 +1,80 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "sort_triangles.h" +#include "project.h" +#include "../sort_triangles.h" +#include "gl.h" +#include "../sort.h" +#include "../slice.h" +#include "../barycenter.h" +#include +template < + typename DerivedV, + typename DerivedF, + typename DerivedFF, + typename DerivedI> +void igl::opengl2::sort_triangles( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & I) +{ + using namespace Eigen; + using namespace std; + // Put model, projection, and viewport matrices into double arrays + Matrix4d MV; + Matrix4d P; + glGetDoublev(GL_MODELVIEW_MATRIX, MV.data()); + glGetDoublev(GL_PROJECTION_MATRIX, P.data()); + if(V.cols() == 3) + { + Matrix hV; + hV.resize(V.rows(),4); + hV.block(0,0,V.rows(),V.cols()) = V; + hV.col(3).setConstant(1); + return igl::sort_triangles(hV,F,MV,P,FF,I); + }else + { + return igl::sort_triangles(V,F,MV,P,FF,I); + } +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedFF, + typename DerivedI> +void igl::opengl2::sort_triangles_slow( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & I) +{ + using namespace Eigen; + using namespace std; + // Barycenter, centroid + Eigen::Matrix D,sD; + Eigen::Matrix BC; + D.resize(F.rows(),3); + barycenter(V,F,BC); + for(int f = 0;f bc,pbc; + bc = BC.row(f); + project(bc,pbc); + D(f) = pbc(2); + } + sort(D,1,false,sD,I); + slice(F,I,1,FF); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::opengl2::sort_triangles, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::opengl2::sort_triangles_slow, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/opengl2/sort_triangles.h b/src/igl/opengl2/sort_triangles.h new file mode 100644 index 0000000000..53fd3e8099 --- /dev/null +++ b/src/igl/opengl2/sort_triangles.h @@ -0,0 +1,70 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_SORT_TRIANGLES_H +#define IGL_OPENGL2_SORT_TRIANGLES_H + +#include "../igl_inline.h" +#include + +namespace igl +{ + namespace opengl2 + { + template < + typename DerivedV, + typename DerivedF, + typename DerivedFF, + typename DerivedI> + IGL_INLINE void sort_triangles( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & I); + template < + typename DerivedV, + typename DerivedF, + typename DerivedFF, + typename DerivedI> + IGL_INLINE void sort_triangles_slow( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & I); + //template < + // typename DerivedV, + // typename DerivedF, + // typename DerivedMV, + // typename DerivedP, + // typename DerivedFF, + // typename DerivedI> + //IGL_INLINE void sort_triangles_robust( + // const Eigen::PlainObjectBase & V, + // const Eigen::PlainObjectBase & F, + // const Eigen::PlainObjectBase & MV, + // const Eigen::PlainObjectBase & P, + // Eigen::PlainObjectBase & FF, + // Eigen::PlainObjectBase & I); + //template < + // typename DerivedV, + // typename DerivedF, + // typename DerivedFF, + // typename DerivedI> + //IGL_INLINE void sort_triangles_robust( + // const Eigen::PlainObjectBase & V, + // const Eigen::PlainObjectBase & F, + // Eigen::PlainObjectBase & FF, + // Eigen::PlainObjectBase & I); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "sort_triangles.cpp" +#endif + +#endif + diff --git a/src/igl/opengl2/unproject.cpp b/src/igl/opengl2/unproject.cpp new file mode 100644 index 0000000000..f6b0013fe0 --- /dev/null +++ b/src/igl/opengl2/unproject.cpp @@ -0,0 +1,63 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unproject.h" +#include "model_proj_viewport.h" +#include "../unproject.h" +#include "gl.h" + +#include +#include + +IGL_INLINE void igl::opengl2::unproject( + const double winX, + const double winY, + const double winZ, + double* objX, + double* objY, + double* objZ) +{ + Eigen::Vector3d obj; + igl::opengl2::unproject(Eigen::Vector3d(winX,winY,winZ),obj); + *objX = obj(0); + *objY = obj(1); + *objZ = obj(2); +} + +template +IGL_INLINE void igl::opengl2::unproject( + const Eigen::PlainObjectBase & win, + Eigen::PlainObjectBase & obj) +{ + const auto ret = igl::opengl2::unproject(win); + obj = ret.template cast(); +} + + +template +IGL_INLINE Derivedwin igl::opengl2::unproject( + const Eigen::PlainObjectBase & win) +{ + using namespace Eigen; + typedef typename Derivedwin::Scalar Scalar; + Matrix4d MV,P; + Vector4d VPd; + model_proj_viewport(MV,P,VPd); + Vector3d wind = win.template cast(); + Vector3d objd = igl::unproject(wind,MV,P,VPd); + return objd.template cast(); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::opengl2::unproject, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::opengl2::unproject, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template Eigen::Matrix igl::opengl2::unproject >(Eigen::PlainObjectBase > const&); +template Eigen::Matrix igl::opengl2::unproject >(Eigen::PlainObjectBase > const&); +template void igl::opengl2::unproject, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/opengl2/unproject.h b/src/igl/opengl2/unproject.h new file mode 100644 index 0000000000..00de130773 --- /dev/null +++ b/src/igl/opengl2/unproject.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_UNPROJECT_H +#define IGL_OPENGL2_UNPROJECT_H +#include "../igl_inline.h" +#include +namespace igl +{ + namespace opengl2 + { + // Wrapper for gluUnproject that uses the current GL_MODELVIEW_MATRIX, + // GL_PROJECTION_MATRIX, and GL_VIEWPORT + // Inputs: + // win* screen space x, y, and z coordinates respectively + // Outputs: + // obj* pointers to 3D objects' x, y, and z coordinates respectively + // Returns return value of gluUnProject call + IGL_INLINE void unproject( + const double winX, + const double winY, + const double winZ, + double* objX, + double* objY, + double* objZ); + template + IGL_INLINE void unproject( + const Eigen::PlainObjectBase & win, + Eigen::PlainObjectBase & obj); + template + IGL_INLINE Derivedwin unproject( + const Eigen::PlainObjectBase & win); + } +} + + +#ifndef IGL_STATIC_LIBRARY +# include "unproject.cpp" +#endif + +#endif + diff --git a/src/igl/opengl2/unproject_to_zero_plane.cpp b/src/igl/opengl2/unproject_to_zero_plane.cpp new file mode 100644 index 0000000000..1c2945886d --- /dev/null +++ b/src/igl/opengl2/unproject_to_zero_plane.cpp @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unproject_to_zero_plane.h" + +#include "gl.h" + +#include "project.h" +#include "unproject.h" + +IGL_INLINE void igl::opengl2::unproject_to_zero_plane( + const double winX, + const double winY, + double* objX, + double* objY, + double* objZ) +{ + double winOrigin[3]; + igl::opengl2::project(0,0,0,&winOrigin[0],&winOrigin[1],&winOrigin[2]); + return igl::opengl2::unproject(winX, winY, winOrigin[2], objX, objY, objZ); +} + +template +IGL_INLINE void igl::opengl2::unproject_to_zero_plane( + const Eigen::PlainObjectBase & win, + Eigen::PlainObjectBase & obj) +{ + return unproject_to_zero_plane(win(0),win(1), + &obj.data()[0], + &obj.data()[1], + &obj.data()[2]); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::opengl2::unproject_to_zero_plane, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::opengl2::unproject_to_zero_plane, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::opengl2::unproject_to_zero_plane, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/opengl2/unproject_to_zero_plane.h b/src/igl/opengl2/unproject_to_zero_plane.h new file mode 100644 index 0000000000..d76337e20f --- /dev/null +++ b/src/igl/opengl2/unproject_to_zero_plane.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_UNPROJECT_TO_ZERO_PLANE_H +#define IGL_OPENGL2_UNPROJECT_TO_ZERO_PLANE_H +#include "../igl_inline.h" +#include +namespace igl +{ + namespace opengl2 + { + // Wrapper for gluUnproject that uses the current GL_MODELVIEW_MATRIX, + // GL_PROJECTION_MATRIX, and GL_VIEWPORT to unproject a screen position + // (winX,winY) to a 3d location at same depth as the current origin. + // Inputs: + // win* screen space x, y, and z coordinates respectively + // Outputs: + // obj* pointers to 3D objects' x, y, and z coordinates respectively + // Returns return value of gluUnProject call + IGL_INLINE void unproject_to_zero_plane( + const double winX, + const double winY, + double* objX, + double* objY, + double* objZ); + template + IGL_INLINE void unproject_to_zero_plane( + const Eigen::PlainObjectBase & win, + Eigen::PlainObjectBase & obj); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "unproject_to_zero_plane.cpp" +#endif + +#endif + diff --git a/src/igl/opengl2/up_axis.cpp b/src/igl/opengl2/up_axis.cpp new file mode 100644 index 0000000000..c6b3c02a0e --- /dev/null +++ b/src/igl/opengl2/up_axis.cpp @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "up_axis.h" +#include "gl.h" + +IGL_INLINE void igl::opengl2::up_axis(double * x, double * y, double * z) +{ + double mv[16]; + glGetDoublev(GL_MODELVIEW_MATRIX, mv); + igl::opengl2::up_axis(mv,x,y,z); +} + +IGL_INLINE void igl::opengl2::up_axis(const double *mv, double * x, double * y, double * z) +{ + *x = -mv[0*4+1]; + *y = -mv[1*4+1]; + *z = -mv[2*4+1]; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +#endif diff --git a/src/igl/opengl2/up_axis.h b/src/igl/opengl2/up_axis.h new file mode 100644 index 0000000000..2c042fb2c2 --- /dev/null +++ b/src/igl/opengl2/up_axis.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_UP_AXIS_H +#define IGL_OPENGL2_UP_AXIS_H +#include "../igl_inline.h" +namespace igl +{ + namespace opengl2 + { + // Determines the up axis or depth axis of the current gl matrix + // Outputs: + // x pointer to x-coordinate in scene coordinates of the un-normalized + // up axis + // y pointer to y-coordinate in scene coordinates of the un-normalized + // up axis + // z pointer to z-coordinate in scene coordinates of the un-normalized + // up axis + // mv pointer to modelview matrix + // + // Note: Up axis is returned *UN-normalized* + IGL_INLINE void up_axis(double * x, double * y, double * z); + IGL_INLINE void up_axis(const double * mv, double * x, double * y, double * z); + } +}; + +#ifndef IGL_STATIC_LIBRARY +# include "up_axis.cpp" +#endif +#endif + + diff --git a/src/igl/opengl2/view_axis.cpp b/src/igl/opengl2/view_axis.cpp new file mode 100644 index 0000000000..5145cd2077 --- /dev/null +++ b/src/igl/opengl2/view_axis.cpp @@ -0,0 +1,40 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "view_axis.h" +#include "gl.h" + +IGL_INLINE void igl::opengl2::view_axis(double * x, double * y, double * z) +{ + double mv[16]; + glGetDoublev(GL_MODELVIEW_MATRIX, mv); + igl::opengl2::view_axis(mv,x,y,z); +} + +IGL_INLINE void igl::opengl2::view_axis(const double * mv, double * x, double * y, double * z) +{ + *x = -mv[0*4+2]; + *y = -mv[1*4+2]; + *z = -mv[2*4+2]; +} + +template +IGL_INLINE void igl::opengl2::view_axis(Eigen::PlainObjectBase & V) +{ + double x,y,z; + view_axis(&x,&y,&z); + V(0) = x; + V(1) = y; + V(2) = z; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::opengl2::view_axis >(Eigen::PlainObjectBase >&); +template void igl::opengl2::view_axis >(Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/opengl2/view_axis.h b/src/igl/opengl2/view_axis.h new file mode 100644 index 0000000000..ab324cbad0 --- /dev/null +++ b/src/igl/opengl2/view_axis.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OPENGL2_VIEW_AXIS_H +#define IGL_OPENGL2_VIEW_AXIS_H +#include "../igl_inline.h" +#include + +namespace igl +{ + namespace opengl2 + { + // Determines the view axis or depth axis of the current gl matrix + // Inputs: + // mv pointer to modelview matrix + // Outputs: + // x pointer to x-coordinate in scene coordinates of the un-normalized + // viewing axis + // y pointer to y-coordinate in scene coordinates of the un-normalized + // viewing axis + // z pointer to z-coordinate in scene coordinates of the un-normalized + // viewing axis + // + // Note: View axis is returned *UN-normalized* + IGL_INLINE void view_axis(const double * mv, double * x, double * y, double * z); + // Extract mv from current GL state. + IGL_INLINE void view_axis(double * x, double * y, double * z); + template + IGL_INLINE void view_axis(Eigen::PlainObjectBase & V); + } +}; + + +#ifndef IGL_STATIC_LIBRARY +# include "view_axis.cpp" +#endif + +#endif + diff --git a/src/igl/orient_outward.cpp b/src/igl/orient_outward.cpp new file mode 100644 index 0000000000..522745bc44 --- /dev/null +++ b/src/igl/orient_outward.cpp @@ -0,0 +1,95 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "orient_outward.h" +#include "per_face_normals.h" +#include "barycenter.h" +#include "doublearea.h" +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedC, + typename DerivedFF, + typename DerivedI> +IGL_INLINE void igl::orient_outward( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & I) +{ + using namespace Eigen; + using namespace std; + assert(C.rows() == F.rows()); + assert(F.cols() == 3); + assert(V.cols() == 3); + + // number of faces + const int m = F.rows(); + // number of patches + const int num_cc = C.maxCoeff()+1; + I.resize(num_cc); + if(&FF != &F) + { + FF = F; + } + DerivedV N,BC,BCmean; + Matrix A; + VectorXd totA(num_cc), dot(num_cc); + Matrix Z(1,1,1); + per_face_normals(V,F,Z.normalized(),N); + barycenter(V,F,BC); + doublearea(V,F,A); + BCmean.setConstant(num_cc,3,0); + dot.setConstant(num_cc,1,0); + totA.setConstant(num_cc,1,0); + // loop over faces + for(int f = 0;f, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/orient_outward.h b/src/igl/orient_outward.h new file mode 100644 index 0000000000..197c8b9506 --- /dev/null +++ b/src/igl/orient_outward.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ORIENT_OUTWARD_H +#define IGL_ORIENT_OUTWARD_H +#include "igl_inline.h" +#include +namespace igl +{ + // Orient each component (identified by C) of a mesh (V,F) so the normals on + // average point away from the patch's centroid. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // C #F list of components (output of orientable_patches) + // Outputs: + // FF #F by 3 list of new triangle indices such that FF(~I,:) = F(~I,:) and + // FF(I,:) = fliplr(F(I,:)) (OK if &FF = &F) + // I max(C)+1 list of whether face has been flipped + template < + typename DerivedV, + typename DerivedF, + typename DerivedC, + typename DerivedFF, + typename DerivedI> + IGL_INLINE void orient_outward( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & I); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "orient_outward.cpp" +#endif + +#endif diff --git a/src/igl/orientable_patches.cpp b/src/igl/orientable_patches.cpp new file mode 100644 index 0000000000..fa2ea1bd49 --- /dev/null +++ b/src/igl/orientable_patches.cpp @@ -0,0 +1,105 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "orientable_patches.h" +#include "components.h" +#include "sort.h" +#include "unique_rows.h" +#include +#include + +template +IGL_INLINE void igl::orientable_patches( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & C, + Eigen::SparseMatrix & A) +{ + using namespace Eigen; + using namespace std; + + // simplex size + assert(F.cols() == 3); + + // List of all "half"-edges: 3*#F by 2 + Matrix allE,sortallE,uE; + allE.resize(F.rows()*3,2); + Matrix IX; + VectorXi IA,IC; + allE.block(0*F.rows(),0,F.rows(),1) = F.col(1); + allE.block(0*F.rows(),1,F.rows(),1) = F.col(2); + allE.block(1*F.rows(),0,F.rows(),1) = F.col(2); + allE.block(1*F.rows(),1,F.rows(),1) = F.col(0); + allE.block(2*F.rows(),0,F.rows(),1) = F.col(0); + allE.block(2*F.rows(),1,F.rows(),1) = F.col(1); + // Sort each row + sort(allE,2,true,sortallE,IX); + //IC(i) tells us where to find sortallE(i,:) in uE: + // so that sortallE(i,:) = uE(IC(i),:) + unique_rows(sortallE,uE,IA,IC); + // uE2FT(e,f) = 1 means face f is adjacent to unique edge e + vector > uE2FTijv(IC.rows()); + for(int e = 0;e(e%F.rows(),IC(e),1); + } + SparseMatrix uE2FT(F.rows(),uE.rows()); + uE2FT.setFromTriplets(uE2FTijv.begin(),uE2FTijv.end()); + // kill non-manifold edges + for(int j=0; j<(int)uE2FT.outerSize();j++) + { + int degree = 0; + for(typename SparseMatrix::InnerIterator it (uE2FT,j); it; ++it) + { + degree++; + } + // Iterate over inside + if(degree > 2) + { + for(typename SparseMatrix::InnerIterator it (uE2FT,j); it; ++it) + { + uE2FT.coeffRef(it.row(),it.col()) = 0; + } + } + } + // Face-face Adjacency matrix + SparseMatrix uE2F; + uE2F = uE2FT.transpose().eval(); + A = uE2FT*uE2F; + // All ones + for(int j=0; j::InnerIterator it (A,j); it; ++it) + { + if(it.value() > 1) + { + A.coeffRef(it.row(),it.col()) = 1; + } + } + } + //% Connected components are patches + //%C = components(A); % alternative to graphconncomp from matlab_bgl + //[~,C] = graphconncomp(A); + // graph connected components + components(A,C); + +} + +template +IGL_INLINE void igl::orientable_patches( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & C) +{ + Eigen::SparseMatrix A; + return orientable_patches(F,C,A); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::orientable_patches, Eigen::Matrix, int>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::SparseMatrix&); +template void igl::orientable_patches, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/orientable_patches.h b/src/igl/orientable_patches.h new file mode 100644 index 0000000000..a74284ac54 --- /dev/null +++ b/src/igl/orientable_patches.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ORIENTABLE_PATCHES_H +#define IGL_ORIENTABLE_PATCHES_H +#include +#include +#include +namespace igl +{ + // Compute connected components of facets connected by manifold edges. + // + // Known bugs: This will detect a moebius strip as a single patch (manifold, + // non-orientable) and also non-manfiold, yet orientable patches. + // + // Q: Does this find exactly (manifold || orientable) patches? + // + // Inputs: + // F #F by simplex-size list of facets + // Outputs: + // C #F list of component ids + // A #F by #F adjacency matrix + // + template + IGL_INLINE void orientable_patches( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & C, + Eigen::SparseMatrix & A); + template + IGL_INLINE void orientable_patches( + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & C); +}; +#ifndef IGL_STATIC_LIBRARY +# include "orientable_patches.cpp" +#endif +#endif diff --git a/src/igl/oriented_facets.cpp b/src/igl/oriented_facets.cpp new file mode 100644 index 0000000000..7226793352 --- /dev/null +++ b/src/igl/oriented_facets.cpp @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "oriented_facets.h" + +template +IGL_INLINE void igl::oriented_facets( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & E) +{ + E.resize(F.rows()*F.cols(),F.cols()-1); + typedef typename DerivedE::Scalar EScalar; + switch(F.cols()) + { + case 4: + E.block(0*F.rows(),0,F.rows(),1) = F.col(1).template cast(); + E.block(0*F.rows(),1,F.rows(),1) = F.col(3).template cast(); + E.block(0*F.rows(),2,F.rows(),1) = F.col(2).template cast(); + + E.block(1*F.rows(),0,F.rows(),1) = F.col(0).template cast(); + E.block(1*F.rows(),1,F.rows(),1) = F.col(2).template cast(); + E.block(1*F.rows(),2,F.rows(),1) = F.col(3).template cast(); + + E.block(2*F.rows(),0,F.rows(),1) = F.col(0).template cast(); + E.block(2*F.rows(),1,F.rows(),1) = F.col(3).template cast(); + E.block(2*F.rows(),2,F.rows(),1) = F.col(1).template cast(); + + E.block(3*F.rows(),0,F.rows(),1) = F.col(0).template cast(); + E.block(3*F.rows(),1,F.rows(),1) = F.col(1).template cast(); + E.block(3*F.rows(),2,F.rows(),1) = F.col(2).template cast(); + return; + case 3: + E.block(0*F.rows(),0,F.rows(),1) = F.col(1).template cast(); + E.block(0*F.rows(),1,F.rows(),1) = F.col(2).template cast(); + E.block(1*F.rows(),0,F.rows(),1) = F.col(2).template cast(); + E.block(1*F.rows(),1,F.rows(),1) = F.col(0).template cast(); + E.block(2*F.rows(),0,F.rows(),1) = F.col(0).template cast(); + E.block(2*F.rows(),1,F.rows(),1) = F.col(1).template cast(); + return; + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::oriented_facets, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::oriented_facets, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::oriented_facets, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::oriented_facets, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::oriented_facets, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::oriented_facets, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/oriented_facets.h b/src/igl/oriented_facets.h new file mode 100644 index 0000000000..b0a66ef087 --- /dev/null +++ b/src/igl/oriented_facets.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ORIENTED_FACETS_H +#define IGL_ORIENTED_FACETS_H +#include "igl_inline.h" +#include +namespace igl +{ + // ORIENTED_FACETS Determines all "directed + // [facets](https://en.wikipedia.org/wiki/Simplex#Elements)" of a given set + // of simplicial elements. For a manifold triangle mesh, this computes all + // half-edges. For a manifold tetrahedral mesh, this computes all half-faces. + // + // Inputs: + // F #F by simplex_size list of simplices + // Outputs: + // E #E by simplex_size-1 list of facets + // + // Note: this is not the same as igl::edges because this includes every + // directed edge including repeats (meaning interior edges on a surface will + // show up once for each direction and non-manifold edges may appear more than + // once for each direction). + // + // Note: This replaces the deprecated `all_edges` function + template + IGL_INLINE void oriented_facets( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & E); +} + +#ifndef IGL_STATIC_LIBRARY +# include "oriented_facets.cpp" +#endif + +#endif + diff --git a/src/igl/orth.cpp b/src/igl/orth.cpp new file mode 100644 index 0000000000..467a4993e0 --- /dev/null +++ b/src/igl/orth.cpp @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "orth.h" + +// Broken Implementation +IGL_INLINE void igl::orth(const Eigen::MatrixXd &A, Eigen::MatrixXd &Q) +{ + + //perform svd on A = U*S*V' (V is not computed and only the thin U is computed) + Eigen::JacobiSVD svd(A, Eigen::ComputeThinU ); + Eigen::MatrixXd U = svd.matrixU(); + const Eigen::VectorXd S = svd.singularValues(); + + //get rank of A + int m = A.rows(); + int n = A.cols(); + double tol = std::max(m,n) * S.maxCoeff() * 2.2204e-16; + int r = 0; + for (int i = 0; i < S.rows(); ++r,++i) + { + if (S[i] < tol) + break; + } + + //keep r first columns of U + Q = U.block(0,0,U.rows(),r); +} diff --git a/src/igl/orth.h b/src/igl/orth.h new file mode 100644 index 0000000000..a77cd6593c --- /dev/null +++ b/src/igl/orth.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ORTH_H +#define IGL_ORTH_H + +#include "igl_inline.h" +#include + +namespace igl +{ + // ORTH Orthogonalization. + // ORTH(A,Q) produces Q as an orthonormal basis for the range of A. + // That is, Q'*Q = I, the columns of Q span the same space as + // the columns of A, and the number of columns of Q is the + // rank of A. + // + // + // The algorithm uses singular value decomposition, SVD, instead of orthogonal + // factorization, QR. This doubles the computation time, but + // provides more reliable and consistent rank determination. + // Closely follows MATLAB implementation in orth.m + // + // Inputs: + // A m by n matrix + // Outputs: + // Q m by n matrix with orthonormal columns spanning same column space as + // A + // + // Known bugs: Implementation listed as "Broken" + IGL_INLINE void orth(const Eigen::MatrixXd &A, Eigen::MatrixXd &Q); +} + + +#ifndef IGL_STATIC_LIBRARY +# include "orth.cpp" +#endif +#endif + diff --git a/src/igl/ortho.cpp b/src/igl/ortho.cpp new file mode 100644 index 0000000000..e634eed746 --- /dev/null +++ b/src/igl/ortho.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "ortho.h" + +template < typename DerivedP> +IGL_INLINE void igl::ortho( + const typename DerivedP::Scalar left, + const typename DerivedP::Scalar right, + const typename DerivedP::Scalar bottom, + const typename DerivedP::Scalar top, + const typename DerivedP::Scalar nearVal, + const typename DerivedP::Scalar farVal, + Eigen::PlainObjectBase & P) +{ + P.setIdentity(); + P(0,0) = 2. / (right - left); + P(1,1) = 2. / (top - bottom); + P(2,2) = - 2./ (farVal - nearVal); + P(0,3) = - (right + left) / (right - left); + P(1,3) = - (top + bottom) / (top - bottom); + P(2,3) = - (farVal + nearVal) / (farVal - nearVal); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::ortho >(Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/ortho.h b/src/igl/ortho.h new file mode 100644 index 0000000000..70b9cab4dd --- /dev/null +++ b/src/igl/ortho.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ORTHO_H +#define IGL_ORTHO_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Implementation of the deprecated glOrtho function. + // + // Inputs: + // left coordinate of left vertical clipping plane + // right coordinate of right vertical clipping plane + // bottom coordinate of bottom vertical clipping plane + // top coordinate of top vertical clipping plane + // nearVal distance to near plane + // farVal distance to far plane + // Outputs: + // P 4x4 perspective matrix + template < typename DerivedP> + IGL_INLINE void ortho( + const typename DerivedP::Scalar left, + const typename DerivedP::Scalar right, + const typename DerivedP::Scalar bottom, + const typename DerivedP::Scalar top, + const typename DerivedP::Scalar nearVal, + const typename DerivedP::Scalar farVal, + Eigen::PlainObjectBase & P); +} + +#ifndef IGL_STATIC_LIBRARY +# include "ortho.cpp" +#endif + +#endif diff --git a/src/igl/outer_element.cpp b/src/igl/outer_element.cpp new file mode 100644 index 0000000000..2ce837780f --- /dev/null +++ b/src/igl/outer_element.cpp @@ -0,0 +1,280 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "outer_element.h" +#include +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename IndexType, + typename DerivedA + > +IGL_INLINE void igl::outer_vertex( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & I, + IndexType & v_index, + Eigen::PlainObjectBase & A) +{ + // Algorithm: + // Find an outer vertex (i.e. vertex reachable from infinity) + // Return the vertex with the largest X value. + // If there is a tie, pick the one with largest Y value. + // If there is still a tie, pick the one with the largest Z value. + // If there is still a tie, then there are duplicated vertices within the + // mesh, which violates the precondition. + typedef typename DerivedF::Scalar Index; + const Index INVALID = std::numeric_limits::max(); + const size_t num_selected_faces = I.rows(); + std::vector candidate_faces; + Index outer_vid = INVALID; + typename DerivedV::Scalar outer_val = 0; + for (size_t i=0; i outer_val) + { + outer_val = vx; + outer_vid = v; + candidate_faces = {f}; + } else if (v == outer_vid) + { + candidate_faces.push_back(f); + } else if (vx == outer_val) + { + // Break tie. + auto vy = V(v,1); + auto vz = V(v, 2); + auto outer_y = V(outer_vid, 1); + auto outer_z = V(outer_vid, 2); + assert(!(vy == outer_y && vz == outer_z)); + bool replace = (vy > outer_y) || + ((vy == outer_y) && (vz > outer_z)); + if (replace) + { + outer_val = vx; + outer_vid = v; + candidate_faces = {f}; + } + } + } + } + + assert(outer_vid != INVALID); + assert(candidate_faces.size() > 0); + v_index = outer_vid; + A.resize(candidate_faces.size()); + std::copy(candidate_faces.begin(), candidate_faces.end(), A.data()); +} + +template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename IndexType, + typename DerivedA + > +IGL_INLINE void igl::outer_edge( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & I, + IndexType & v1, + IndexType & v2, + Eigen::PlainObjectBase & A) { + // Algorithm: + // Find an outer vertex first. + // Find the incident edge with largest abs slope when projected onto XY plane. + // If there is a tie, check the signed slope and use the positive one. + // If there is still a tie, break it using the projected slope onto ZX plane. + // If there is still a tie, again check the signed slope and use the positive one. + // If there is still a tie, then there are multiple overlapping edges, + // which violates the precondition. + typedef typename DerivedV::Scalar Scalar; + typedef typename DerivedV::Index Index; + typedef typename Eigen::Matrix ScalarArray3; + typedef typename Eigen::Matrix IndexArray3; + const Index INVALID = std::numeric_limits::max(); + + Index outer_vid; + Eigen::Matrix candidate_faces; + outer_vertex(V, F, I, outer_vid, candidate_faces); + const ScalarArray3& outer_v = V.row(outer_vid); + assert(candidate_faces.size() > 0); + + auto get_vertex_index = [&](const IndexArray3& f, Index vid) -> Index + { + if (f[0] == vid) return 0; + if (f[1] == vid) return 1; + if (f[2] == vid) return 2; + assert(false); + return -1; + }; + + auto unsigned_value = [](Scalar v) -> Scalar { + if (v < 0) return v * -1; + else return v; + }; + + Scalar outer_slope_YX = 0; + Scalar outer_slope_ZX = 0; + Index outer_opp_vid = INVALID; + bool infinite_slope_detected = false; + std::vector incident_faces; + auto check_and_update_outer_edge = [&](Index opp_vid, Index fid) { + if (opp_vid == outer_opp_vid) + { + incident_faces.push_back(fid); + return; + } + + const ScalarArray3 opp_v = V.row(opp_vid); + if (!infinite_slope_detected && outer_v[0] != opp_v[0]) + { + // Finite slope + const ScalarArray3 diff = opp_v - outer_v; + const Scalar slope_YX = diff[1] / diff[0]; + const Scalar slope_ZX = diff[2] / diff[0]; + const Scalar u_slope_YX = unsigned_value(slope_YX); + const Scalar u_slope_ZX = unsigned_value(slope_ZX); + bool update = false; + if (outer_opp_vid == INVALID) { + update = true; + } else { + const Scalar u_outer_slope_YX = unsigned_value(outer_slope_YX); + if (u_slope_YX > u_outer_slope_YX) { + update = true; + } else if (u_slope_YX == u_outer_slope_YX && + slope_YX > outer_slope_YX) { + update = true; + } else if (slope_YX == outer_slope_YX) { + const Scalar u_outer_slope_ZX = + unsigned_value(outer_slope_ZX); + if (u_slope_ZX > u_outer_slope_ZX) { + update = true; + } else if (u_slope_ZX == u_outer_slope_ZX && + slope_ZX > outer_slope_ZX) { + update = true; + } else if (slope_ZX == u_outer_slope_ZX) { + assert(false); + } + } + } + + if (update) { + outer_opp_vid = opp_vid; + outer_slope_YX = slope_YX; + outer_slope_ZX = slope_ZX; + incident_faces = {fid}; + } + } else if (!infinite_slope_detected) + { + // Infinite slope + outer_opp_vid = opp_vid; + infinite_slope_detected = true; + incident_faces = {fid}; + } + }; + + const size_t num_candidate_faces = candidate_faces.size(); + for (size_t i=0; i +IGL_INLINE void igl::outer_facet( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & N, + const Eigen::PlainObjectBase & I, + IndexType & f, + bool & flipped) { + // Algorithm: + // Find an outer edge. + // Find the incident facet with the largest absolute X normal component. + // If there is a tie, keep the one with positive X component. + // If there is still a tie, pick the face with the larger signed index + // (flipped face has negative index). + typedef typename DerivedV::Scalar Scalar; + typedef typename DerivedV::Index Index; + const size_t INVALID = std::numeric_limits::max(); + + Index v1,v2; + Eigen::Matrix incident_faces; + outer_edge(V, F, I, v1, v2, incident_faces); + assert(incident_faces.size() > 0); + + auto generic_fabs = [&](const Scalar& val) -> const Scalar { + if (val >= 0) return val; + else return -val; + }; + + Scalar max_nx = 0; + size_t outer_fid = INVALID; + const size_t num_incident_faces = incident_faces.size(); + for (size_t i=0; i generic_fabs(max_nx)) { + max_nx = nx; + outer_fid = fid; + } else if (nx == -max_nx && nx > 0) { + max_nx = nx; + outer_fid = fid; + } else if (nx == max_nx) { + if ((max_nx >= 0 && outer_fid < fid) || + (max_nx < 0 && outer_fid > fid)) { + max_nx = nx; + outer_fid = fid; + } + } + } + } + + assert(outer_fid != INVALID); + f = outer_fid; + flipped = max_nx < 0; +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::outer_facet, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +template void igl::outer_facet, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, unsigned long>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, unsigned long&, bool&); +template void igl::outer_facet, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +template void igl::outer_facet, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, bool&); +#endif diff --git a/src/igl/outer_element.h b/src/igl/outer_element.h new file mode 100644 index 0000000000..6fbaf9c5df --- /dev/null +++ b/src/igl/outer_element.h @@ -0,0 +1,110 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_OUTER_ELEMENT_H +#define IGL_OUTER_ELEMENT_H +#include "igl_inline.h" +#include +namespace igl +{ + // Find a vertex that is reachable from infinite without crossing any faces. + // Such vertex is called "outer vertex." + // + // Precondition: The input mesh must have all self-intersection resolved and + // no duplicated vertices. See cgal::remesh_self_intersections.h for how to + // obtain such input. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // I #I list of facets to consider + // Outputs: + // v_index index of outer vertex + // A #A list of facets incident to the outer vertex + template < + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename IndexType, + typename DerivedA + > + IGL_INLINE void outer_vertex( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & I, + IndexType & v_index, + Eigen::PlainObjectBase & A); + + + // Find an edge that is reachable from infinity without crossing any faces. + // Such edge is called "outer edge." + // + // Precondition: The input mesh must have all self-intersection resolved and + // no duplicated vertices. The correctness of the output depends on the fact + // that there is no edge overlap. See cgal::remesh_self_intersections.h for + // how to obtain such input. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // I #I list of facets to consider + // Outputs: + // v1 index of the first end point of outer edge + // v2 index of the second end point of outer edge + // A #A list of facets incident to the outer edge + template< + typename DerivedV, + typename DerivedF, + typename DerivedI, + typename IndexType, + typename DerivedA + > + IGL_INLINE void outer_edge( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & I, + IndexType & v1, + IndexType & v2, + Eigen::PlainObjectBase & A); + + + // Find a facet that is reachable from infinity without crossing any faces. + // Such facet is called "outer facet." + // + // Precondition: The input mesh must have all self-intersection resolved. I.e + // there is no duplicated vertices, no overlapping edge and no intersecting + // faces (the only exception is there could be topologically duplicated faces). + // See cgal::remesh_self_intersections.h for how to obtain such input. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices into V + // N #N by 3 list of face normals + // I #I list of facets to consider + // Outputs: + // f Index of the outer facet. + // flipped true iff the normal of f points inwards. + template< + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DerivedI, + typename IndexType + > + IGL_INLINE void outer_facet( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & N, + const Eigen::PlainObjectBase & I, + IndexType & f, + bool & flipped); +} + +#ifndef IGL_STATIC_LIBRARY +# include "outer_element.cpp" +#endif +#endif diff --git a/src/igl/parallel_for.h b/src/igl/parallel_for.h new file mode 100644 index 0000000000..458adace52 --- /dev/null +++ b/src/igl/parallel_for.h @@ -0,0 +1,188 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PARALLEL_FOR_H +#define IGL_PARALLEL_FOR_H +#include "igl_inline.h" +#include + +//#warning "Defining IGL_PARALLEL_FOR_FORCE_SERIAL" +//#define IGL_PARALLEL_FOR_FORCE_SERIAL + +namespace igl +{ + // PARALLEL_FOR Functional implementation of a basic, open-mp style, parallel + // for loop. If the inner block of a for-loop can be rewritten/encapsulated in + // a single (anonymous/lambda) function call `func` so that the serial code + // looks like: + // + // for(int i = 0;i + inline bool parallel_for( + const Index loop_size, + const FunctionType & func, + const size_t min_parallel=0); + // PARALLEL_FOR Functional implementation of an open-mp style, parallel for + // loop with accumulation. For example, serial code separated into n chunks + // (each to be parallelized with a thread) might look like: + // + // Eigen::VectorXd S; + // const auto & prep_func = [&S](int n){ S = Eigen:VectorXd::Zero(n); }; + // const auto & func = [&X,&S](int i, int t){ S(t) += X(i); }; + // const auto & accum_func = [&S,&sum](int t){ sum += S(t); }; + // prep_func(n); + // for(int i = 0;i= number of threads as only + // argument + // func function handle taking iteration index i and thread id t as only + // arguments to compute inner block of for loop I.e. + // for(int i ...){ func(i,t); } + // accum_func function handle taking thread index as only argument, to be + // called after all calls of func, e.g., for serial accumulation across + // all n (potential) threads, see n in description of prep_func. + // min_parallel min size of loop_size such that parallel (non-serial) + // thread pooling should be attempted {0} + // Returns true iff thread pool was invoked + template< + typename Index, + typename PrepFunctionType, + typename FunctionType, + typename AccumFunctionType + > + inline bool parallel_for( + const Index loop_size, + const PrepFunctionType & prep_func, + const FunctionType & func, + const AccumFunctionType & accum_func, + const size_t min_parallel=0); +} + +// Implementation + +#include +#include +#include +#include +#include + +template +inline bool igl::parallel_for( + const Index loop_size, + const FunctionType & func, + const size_t min_parallel) +{ + using namespace std; + // no op preparation/accumulation + const auto & no_op = [](const size_t /*n/t*/){}; + // two-parameter wrapper ignoring thread id + const auto & wrapper = [&func](Index i,size_t /*t*/){ func(i); }; + return parallel_for(loop_size,no_op,wrapper,no_op,min_parallel); +} + +template< + typename Index, + typename PreFunctionType, + typename FunctionType, + typename AccumFunctionType> +inline bool igl::parallel_for( + const Index loop_size, + const PreFunctionType & prep_func, + const FunctionType & func, + const AccumFunctionType & accum_func, + const size_t min_parallel) +{ + assert(loop_size>=0); + if(loop_size==0) return false; + // Estimate number of threads in the pool + // http://ideone.com/Z7zldb + const static size_t sthc = std::thread::hardware_concurrency(); + const size_t nthreads = +#ifdef IGL_PARALLEL_FOR_FORCE_SERIAL + 0; +#else + loop_size(nthreads)),(Index)1); + + // [Helper] Inner loop + const auto & range = [&func](const Index k1, const Index k2, const size_t t) + { + for(Index k = k1; k < k2; k++) func(k,t); + }; + prep_func(nthreads); + // Create pool and launch jobs + std::vector pool; + pool.reserve(nthreads); + // Inner range extents + Index i1 = 0; + Index i2 = std::min(0 + slice, loop_size); + { + size_t t = 0; + for (; t+1 < nthreads && i1 < loop_size; ++t) + { + pool.emplace_back(range, i1, i2, t); + i1 = i2; + i2 = std::min(i2 + slice, loop_size); + } + if (i1 < loop_size) + { + pool.emplace_back(range, i1, loop_size, t); + } + } + // Wait for jobs to finish + for (std::thread &t : pool) if (t.joinable()) t.join(); + // Accumulate across threads + for(size_t t = 0;t +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include +#include + +template +IGL_INLINE void igl::parallel_transport_angles( +const Eigen::PlainObjectBase& V, +const Eigen::PlainObjectBase& F, +const Eigen::PlainObjectBase& FN, +const Eigen::MatrixXi &E2F, +const Eigen::MatrixXi &F2E, +Eigen::PlainObjectBase &K) +{ + int numE = E2F.rows(); + + Eigen::VectorXi isBorderEdge; + isBorderEdge.setZero(numE,1); + for(unsigned i=0; i N0 = FN.row(fid0); +// Eigen::Matrix N1 = FN.row(fid1); + + // find common edge on triangle 0 and 1 + int fid0_vc = -1; + int fid1_vc = -1; + for (unsigned i=0;i<3;++i) + { + if (F2E(fid0,i) == eid) + fid0_vc = i; + if (F2E(fid1,i) == eid) + fid1_vc = i; + } + assert(fid0_vc != -1); + assert(fid1_vc != -1); + + Eigen::Matrix common_edge = V.row(F(fid0,(fid0_vc+1)%3)) - V.row(F(fid0,fid0_vc)); + common_edge.normalize(); + + // Map the two triangles in a new space where the common edge is the x axis and the N0 the z axis + Eigen::Matrix P; + Eigen::Matrix o = V.row(F(fid0,fid0_vc)); + Eigen::Matrix tmp = -N0.cross(common_edge); + P << common_edge, tmp, N0; + // P.transposeInPlace(); + + + Eigen::Matrix V0; + V0.row(0) = V.row(F(fid0,0)) -o; + V0.row(1) = V.row(F(fid0,1)) -o; + V0.row(2) = V.row(F(fid0,2)) -o; + + V0 = (P*V0.transpose()).transpose(); + + // assert(V0(0,2) < 1e-10); + // assert(V0(1,2) < 1e-10); + // assert(V0(2,2) < 1e-10); + + Eigen::Matrix V1; + V1.row(0) = V.row(F(fid1,0)) -o; + V1.row(1) = V.row(F(fid1,1)) -o; + V1.row(2) = V.row(F(fid1,2)) -o; + V1 = (P*V1.transpose()).transpose(); + + // assert(V1(fid1_vc,2) < 10e-10); + // assert(V1((fid1_vc+1)%3,2) < 10e-10); + + // compute rotation R such that R * N1 = N0 + // i.e. map both triangles to the same plane + double alpha = -atan2(V1((fid1_vc+2)%3,2),V1((fid1_vc+2)%3,1)); + + Eigen::Matrix R; + R << 1, 0, 0, + 0, cos(alpha), -sin(alpha) , + 0, sin(alpha), cos(alpha); + V1 = (R*V1.transpose()).transpose(); + + // assert(V1(0,2) < 1e-10); + // assert(V1(1,2) < 1e-10); + // assert(V1(2,2) < 1e-10); + + // measure the angle between the reference frames + // k_ij is the angle between the triangle on the left and the one on the right + Eigen::Matrix ref0 = V0.row(1) - V0.row(0); + Eigen::Matrix ref1 = V1.row(1) - V1.row(0); + + ref0.normalize(); + ref1.normalize(); + + double ktemp = atan2(ref1(1),ref1(0)) - atan2(ref0(1),ref0(0)); + + // just to be sure, rotate ref0 using angle ktemp... + Eigen::Matrix R2; + R2 << cos(ktemp), -sin(ktemp), sin(ktemp), cos(ktemp); + +// Eigen::Matrix tmp1 = R2*(ref0.head(2)).transpose(); + + // assert(tmp1(0) - ref1(0) < 1e-10); + // assert(tmp1(1) - ref1(1) < 1e-10); + + K[eid] = ktemp; + } + } + +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::parallel_transport_angles, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/parallel_transport_angles.h b/src/igl/parallel_transport_angles.h new file mode 100644 index 0000000000..e37d7f3735 --- /dev/null +++ b/src/igl/parallel_transport_angles.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_PARALLEL_TRANSPORT_ANGLE +#define IGL_PARALLEL_TRANSPORT_ANGLE +#include "igl_inline.h" + +#include +#include + +namespace igl { + // Given the per-face local bases computed via igl::local_basis, this function + // computes the angle between the two reference frames across each edge. + // Any two vectors across the edge whose 2D representation only differs by + // this angle are considered to be parallel. + + // Inputs: + // V #V by 3 list of mesh vertex coordinates + // F #F by 3 list of mesh faces (must be triangles) + // FN #F by 3 list of face normals + // E2F #E by 2 list of the edge-to-face relation (e.g. computed + // via igl::edge_topology) + // F2E #F by 3 list of the face-to-edge relation (e.g. computed + // via igl::edge_topology) + // Output: + // K #E by 1 list of the parallel transport angles (zero + // for all boundary edges) + // +template +IGL_INLINE void parallel_transport_angles( +const Eigen::PlainObjectBase&V, +const Eigen::PlainObjectBase&F, +const Eigen::PlainObjectBase&FN, +const Eigen::MatrixXi &E2F, +const Eigen::MatrixXi &F2E, +Eigen::PlainObjectBase&K); + +}; + + +#ifndef IGL_STATIC_LIBRARY +#include "parallel_transport_angles.cpp" +#endif + + +#endif /* defined(IGL_PARALLEL_TRANSPORT_ANGLE) */ diff --git a/src/igl/partition.cpp b/src/igl/partition.cpp new file mode 100644 index 0000000000..d2ef34c94f --- /dev/null +++ b/src/igl/partition.cpp @@ -0,0 +1,59 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "partition.h" +#include "mat_min.h" + +IGL_INLINE void igl::partition( + const Eigen::MatrixXd & W, + const int k, + Eigen::Matrix & G, + Eigen::Matrix & S, + Eigen::Matrix & D) +{ + // number of mesh vertices + int n = W.rows(); + + // Resize output + G.resize(n); + S.resize(k); + + // "Randomly" choose first seed + // Pick a vertex farthest from 0 + int s; + (W.array().square().matrix()).rowwise().sum().maxCoeff(&s); + + S(0) = s; + // Initialize distance to closest seed + D = ((W.rowwise() - W.row(s)).array().square()).matrix().rowwise().sum(); + G.setZero(); + + // greedily choose the remaining k-1 seeds + for(int i = 1;i Ds = + ((W.rowwise() - W.row(s)).array().square()).matrix().rowwise().sum(); + // Concatenation of D and Ds: DDs = [D Ds]; + Eigen::Matrix DDs; + // Make space for two columns + DDs.resize(D.rows(),2); + DDs.col(0) = D; + DDs.col(1) = Ds; + // Update D + // get minimum of old D and distance to this seed, C == 1 if new distance + // was smaller + Eigen::Matrix C; + igl::mat_min(DDs,2,D,C); + G = (C.array() ==0).select(G,i); + } + + +} diff --git a/src/igl/partition.h b/src/igl/partition.h new file mode 100644 index 0000000000..167ebd0d7a --- /dev/null +++ b/src/igl/partition.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PARTITION_H +#define IGL_PARTITION_H +#include "igl_inline.h" +#include + +namespace igl +{ + // PARTITION partition vertices into groups based on each + // vertex's vector: vertices with similar coordinates (close in + // space) will be put in the same group. + // + // Inputs: + // W #W by dim coordinate matrix + // k desired number of groups default is dim + // Output: + // G #W list of group indices (1 to k) for each vertex, such that vertex i + // is assigned to group G(i) + // S k list of seed vertices + // D #W list of squared distances for each vertex to it's corresponding + // closest seed + IGL_INLINE void partition( + const Eigen::MatrixXd & W, + const int k, + Eigen::Matrix & G, + Eigen::Matrix & S, + Eigen::Matrix & D); +} + +#ifndef IGL_STATIC_LIBRARY +#include "partition.cpp" +#endif +#endif diff --git a/src/igl/parula.cpp b/src/igl/parula.cpp new file mode 100644 index 0000000000..b3daaa5e7f --- /dev/null +++ b/src/igl/parula.cpp @@ -0,0 +1,53 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "parula.h" +#include "colormap.h" + +template +IGL_INLINE void igl::parula(const T x, T * rgb) +{ + igl::colormap(igl::COLOR_MAP_TYPE_PARULA,x, rgb); +} + +template +IGL_INLINE void igl::parula(const T f, T & r, T & g, T & b) +{ + igl::colormap(igl::COLOR_MAP_TYPE_PARULA, f, r, g, b); +} + + +template +IGL_INLINE void igl::parula( + const Eigen::MatrixBase & Z, + const bool normalize, + Eigen::PlainObjectBase & C) +{ + igl::colormap(igl::COLOR_MAP_TYPE_PARULA, Z, normalize, C); +} +template +IGL_INLINE void igl::parula( + const Eigen::MatrixBase & Z, + const double min_z, + const double max_z, + Eigen::PlainObjectBase & C) +{ + igl::colormap(igl::COLOR_MAP_TYPE_PARULA, Z, min_z, max_z, C); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::parula, Eigen::Matrix >(Eigen::MatrixBase > const&, double, double, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::parula, Eigen::Matrix >(Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +template void igl::parula(double, double*); +template void igl::parula(double, double&, double&, double&); +template void igl::parula, Eigen::Matrix >(Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +template void igl::parula, Eigen::Matrix >(Eigen::MatrixBase > const&, bool, Eigen::PlainObjectBase >&); +template void igl::parula, Eigen::Matrix >(Eigen::MatrixBase > const&, double, double, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/parula.h b/src/igl/parula.h new file mode 100644 index 0000000000..55cbd447f6 --- /dev/null +++ b/src/igl/parula.h @@ -0,0 +1,62 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PARULA_H +#define IGL_PARULA_H +#include "igl_inline.h" +//#ifndef IGL_NO_EIGEN +# include +//#endif +namespace igl +{ + // PARULA like MATLAB's parula + // + // Inputs: + // m number of colors + // Outputs: + // J m by list of RGB colors between 0 and 1 + // + // Wrapper for directly computing [r,g,b] values for a given factor f between + // 0 and 1 + // + // Inputs: + // f factor determining color value as if 0 was min and 1 was max + // Outputs: + // r red value + // g green value + // b blue value + template + IGL_INLINE void parula(const T f, T * rgb); + template + IGL_INLINE void parula(const T f, T & r, T & g, T & b); + // Inputs: + // Z #Z list of factors + // normalize whether to normalize Z to be tightly between [0,1] + // Outputs: + // C #C by 3 list of rgb colors + template + IGL_INLINE void parula( + const Eigen::MatrixBase & Z, + const bool normalize, + Eigen::PlainObjectBase & C); + // Inputs: + // min_Z value at blue + // max_Z value at red + template + IGL_INLINE void parula( + const Eigen::MatrixBase & Z, + const double min_Z, + const double max_Z, + Eigen::PlainObjectBase & C); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "parula.cpp" +#endif + +#endif + diff --git a/src/igl/path_to_executable.cpp b/src/igl/path_to_executable.cpp new file mode 100644 index 0000000000..509a763d6a --- /dev/null +++ b/src/igl/path_to_executable.cpp @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "path_to_executable.h" +#ifdef __APPLE__ +# include +#endif +#if defined(_WIN32) +# include +#endif +#include +IGL_INLINE std::string igl::path_to_executable() +{ + // http://pastebin.com/ffzzxPzi + using namespace std; + std::string path; + char buffer[1024]; + uint32_t size = sizeof(buffer); +#if defined (WIN32) + GetModuleFileName(nullptr,buffer,size); + path = buffer; +#elif defined (__APPLE__) + if(_NSGetExecutablePath(buffer, &size) == 0) + { + path = buffer; + } +#elif defined(UNIX) + if (readlink("/proc/self/exe", buffer, sizeof(buffer)) == -1) + { + path = buffer; + } +#elif defined(__FreeBSD__) + int mib[4]; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = -1; + sysctl(mib, 4, buffer, sizeof(buffer), NULL, 0); + path = buffer; +#elif defined(SUNOS) + path = getexecname(); +#endif + return path; +} + diff --git a/src/igl/path_to_executable.h b/src/igl/path_to_executable.h new file mode 100644 index 0000000000..169c3400bc --- /dev/null +++ b/src/igl/path_to_executable.h @@ -0,0 +1,21 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PATH_TO_EXECUTABLE_H +#define IGL_PATH_TO_EXECUTABLE_H +#include "igl_inline.h" +#include +namespace igl +{ + // Return the path of the current executable. + // Note: Tested for Mac OS X + IGL_INLINE std::string path_to_executable(); +} +#ifndef IGL_STATIC_LIBRARY +# include "path_to_executable.cpp" +#endif +#endif diff --git a/src/igl/pathinfo.cpp b/src/igl/pathinfo.cpp new file mode 100644 index 0000000000..110535cd8b --- /dev/null +++ b/src/igl/pathinfo.cpp @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "pathinfo.h" + +#include "dirname.h" +#include "basename.h" +// Verbose should be removed once everything working correctly +#include "verbose.h" +#include + +IGL_INLINE void igl::pathinfo( + const std::string & path, + std::string & dirname, + std::string & basename, + std::string & extension, + std::string & filename) +{ + dirname = igl::dirname(path); + basename = igl::basename(path); + std::string::reverse_iterator last_dot = + std::find( + basename.rbegin(), + basename.rend(), '.'); + // Was a dot found? + if(last_dot == basename.rend()) + { + // filename is same as basename + filename = basename; + // no extension + extension = ""; + }else + { + // extension is substring of basename + extension = std::string(last_dot.base(),basename.end()); + // filename is substring of basename + filename = std::string(basename.begin(),last_dot.base()-1); + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/pathinfo.h b/src/igl/pathinfo.h new file mode 100644 index 0000000000..b59e481574 --- /dev/null +++ b/src/igl/pathinfo.h @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PATHINFO_H +#define IGL_PATHINFO_H +#include "igl_inline.h" + +#include + +namespace igl +{ + //// Decided not to use these + //const int PATHINFO_DIRNAME 01 + //const int PATHINFO_BASENAME 02 + //const int PATHINFO_EXTENSION 04 + //const int PATHINFO_FILENAME 08 + + // Function like PHP's pathinfo + // returns information about path + // Input: + // path string containing input path + // Outputs: + // dirname string containing dirname (see dirname.h) + // basename string containing basename (see basename.h) + // extension string containing extension (characters after last '.') + // filename string containing filename (characters of basename before last + // '.') + // + // + // Examples: + // + // input | dirname basename ext filename + // "/" | "/" "" "" "" + // "//" | "/" "" "" "" + // "/foo" | "/" "foo" "" "foo" + // "/foo/" | "/" "foo" "" "foo" + // "/foo//" | "/" "foo" "" "foo" + // "/foo/./" | "/foo" "." "" "" + // "/foo/bar" | "/foo" "bar" "" "bar" + // "/foo/bar." | "/foo" "bar." "" "bar" + // "/foo/bar.txt" | "/foo" "bar.txt" "txt" "bar" + // "/foo/bar.txt.zip" | "/foo" "bar.txt.zip" "zip" "bar.txt" + // "/foo/bar.dir/" | "/foo" "bar.dir" "dir" "bar" + // "/foo/bar.dir/file" | "/foo/bar.dir" "file" "" "file" + // "/foo/bar.dir/file.txt" | "/foo/bar.dir" "file.txt" "txt" "file" + // See also: basename, dirname + IGL_INLINE void pathinfo( + const std::string & path, + std::string & dirname, + std::string & basename, + std::string & extension, + std::string & filename); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "pathinfo.cpp" +#endif + +#endif diff --git a/src/igl/per_corner_normals.cpp b/src/igl/per_corner_normals.cpp new file mode 100644 index 0000000000..5d2aa6aff6 --- /dev/null +++ b/src/igl/per_corner_normals.cpp @@ -0,0 +1,111 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "per_corner_normals.h" + +#include "vertex_triangle_adjacency.h" +#include "per_face_normals.h" +#include "PI.h" + +template +IGL_INLINE void igl::per_corner_normals( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const double corner_threshold, + Eigen::PlainObjectBase & CN) +{ + using namespace Eigen; + using namespace std; + Eigen::Matrix FN; + per_face_normals(V,F,FN); + vector > VF,VFi; + vertex_triangle_adjacency(V,F,VF,VFi); + return per_corner_normals(V,F,FN,VF,corner_threshold,CN); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedCN> +IGL_INLINE void igl::per_corner_normals( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& FN, + const double corner_threshold, + Eigen::PlainObjectBase & CN) +{ + using namespace Eigen; + using namespace std; + vector > VF,VFi; + vertex_triangle_adjacency(V,F,VF,VFi); + return per_corner_normals(V,F,FN,VF,corner_threshold,CN); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename IndexType, + typename DerivedCN> +IGL_INLINE void igl::per_corner_normals( + const Eigen::PlainObjectBase& /*V*/, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& FN, + const std::vector >& VF, + const double corner_threshold, + Eigen::PlainObjectBase & CN) +{ + using namespace Eigen; + using namespace std; + + // number of faces + const int m = F.rows(); + // valence of faces + const int n = F.cols(); + + // initialize output to ***zero*** + CN.setZero(m*n,3); + + // loop over faces + for(size_t i = 0;int(i) fn = FN.row(i); + // loop over corners + for(size_t j = 0;int(j) &incident_faces = VF[F(i,j)]; + // loop over faces sharing vertex of this corner + for(int k = 0;k<(int)incident_faces.size();k++) + { + Eigen::Matrix ifn = FN.row(incident_faces[k]); + // dot product between face's normal and other face's normal + double dp = fn.dot(ifn); + // if difference in normal is slight then add to average + if(dp > cos(corner_threshold*PI/180)) + { + // add to running sum + CN.row(i*n+j) += ifn; + // else ignore + }else + { + } + } + // normalize to take average + CN.row(i*n+j).normalize(); + } + } +} +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::per_corner_normals, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, double, Eigen::PlainObjectBase >&); +template void igl::per_corner_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, double, Eigen::PlainObjectBase >&); +template void igl::per_corner_normals, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, double, Eigen::PlainObjectBase >&); +template void igl::per_corner_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, double, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/per_corner_normals.h b/src/igl/per_corner_normals.h new file mode 100644 index 0000000000..f93f7eb465 --- /dev/null +++ b/src/igl/per_corner_normals.h @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PER_CORNER_NORMALS_H +#define IGL_PER_CORNER_NORMALS_H +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Compute vertex normals via vertex position list, face list + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (triangle) indices + // corner_threshold threshold in degrees on sharp angles + // Output: + // CN #F*3 by 3 eigen Matrix of mesh vertex 3D normals, where the normal + // for corner F(i,j) is at CN(i*3+j,:) + template + IGL_INLINE void per_corner_normals( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const double corner_threshold, + Eigen::PlainObjectBase & CN); + // Other Inputs: + // FN #F by 3 eigen Matrix of face normals + template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedCN> + IGL_INLINE void per_corner_normals( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& FN, + const double corner_threshold, + Eigen::PlainObjectBase & CN); + // Other Inputs: + // VF map from vertices to list of incident faces + template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename IndexType, + typename DerivedCN> + IGL_INLINE void per_corner_normals( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& FN, + const std::vector >& VF, + const double corner_threshold, + Eigen::PlainObjectBase & CN); +} + +#ifndef IGL_STATIC_LIBRARY +# include "per_corner_normals.cpp" +#endif + +#endif diff --git a/src/igl/per_edge_normals.cpp b/src/igl/per_edge_normals.cpp new file mode 100644 index 0000000000..8aa71791d7 --- /dev/null +++ b/src/igl/per_edge_normals.cpp @@ -0,0 +1,130 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "oriented_facets.h" +#include "doublearea.h" +#include "per_edge_normals.h" +#include "get_seconds.h" +#include "per_face_normals.h" +#include "unique_simplices.h" +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedN, + typename DerivedE, + typename DerivedEMAP> +IGL_INLINE void igl::per_edge_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const PerEdgeNormalsWeightingType weighting, + const Eigen::MatrixBase& FN, + Eigen::PlainObjectBase & N, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP) + +{ + using namespace Eigen; + using namespace std; + assert(F.cols() == 3 && "Faces must be triangles"); + // number of faces + const int m = F.rows(); + // All occurrences of directed edges + MatrixXi allE; + oriented_facets(F,allE); + // Find unique undirected edges and mapping + VectorXi _; + unique_simplices(allE,E,_,EMAP); + // now sort(allE,2) == E(EMAP,:), that is, if EMAP(i) = j, then E.row(j) is + // the undirected edge corresponding to the directed edge allE.row(i). + + Eigen::VectorXd W; + switch(weighting) + { + case PER_EDGE_NORMALS_WEIGHTING_TYPE_UNIFORM: + // Do nothing + break; + default: + assert(false && "Unknown weighting type"); + case PER_EDGE_NORMALS_WEIGHTING_TYPE_DEFAULT: + case PER_EDGE_NORMALS_WEIGHTING_TYPE_AREA: + { + doublearea(V,F,W); + break; + } + } + + N.setZero(E.rows(),3); + for(int f = 0;f +IGL_INLINE void igl::per_edge_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const PerEdgeNormalsWeightingType weighting, + Eigen::PlainObjectBase & N, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP) +{ + Eigen::Matrix FN; + per_face_normals(V,F,FN); + return per_edge_normals(V,F,weighting,FN,N,E,EMAP); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DerivedE, + typename DerivedEMAP> +IGL_INLINE void igl::per_edge_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase & N, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP) +{ + return + per_edge_normals(V,F,PER_EDGE_NORMALS_WEIGHTING_TYPE_DEFAULT,N,E,EMAP); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::per_edge_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerEdgeNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::per_edge_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerEdgeNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::per_edge_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerEdgeNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::per_edge_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerEdgeNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::per_edge_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::per_edge_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerEdgeNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::per_edge_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerEdgeNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::per_edge_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerEdgeNormalsWeightingType, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::per_edge_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/per_edge_normals.h b/src/igl/per_edge_normals.h new file mode 100644 index 0000000000..b40d729768 --- /dev/null +++ b/src/igl/per_edge_normals.h @@ -0,0 +1,81 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PER_EDGE_NORMALS_H +#define IGL_PER_EDGE_NORMALS_H +#include "igl_inline.h" +#include +namespace igl +{ + enum PerEdgeNormalsWeightingType + { + // Incident face normals have uniform influence on edge normal + PER_EDGE_NORMALS_WEIGHTING_TYPE_UNIFORM = 0, + // Incident face normals are averaged weighted by area + PER_EDGE_NORMALS_WEIGHTING_TYPE_AREA = 1, + // Area weights + PER_EDGE_NORMALS_WEIGHTING_TYPE_DEFAULT = 2, + NUM_PER_EDGE_NORMALS_WEIGHTING_TYPE = 3 + }; + // Compute face normals via vertex position list, face list + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (triangle) indices + // weight weighting type + // FN #F by 3 matrix of 3D face normals per face + // Output: + // N #2 by 3 matrix of mesh edge 3D normals per row + // E #E by 2 matrix of edge indices per row + // EMAP #E by 1 matrix of indices from all edges to E + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedN, + typename DerivedE, + typename DerivedEMAP> + IGL_INLINE void per_edge_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const PerEdgeNormalsWeightingType weight, + const Eigen::MatrixBase& FN, + Eigen::PlainObjectBase & N, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP); + template < + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DerivedE, + typename DerivedEMAP> + IGL_INLINE void per_edge_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const PerEdgeNormalsWeightingType weight, + Eigen::PlainObjectBase & N, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP); + template < + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DerivedE, + typename DerivedEMAP> + IGL_INLINE void per_edge_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase & N, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & EMAP); +} + +#ifndef IGL_STATIC_LIBRARY +# include "per_edge_normals.cpp" +#endif + +#endif diff --git a/src/igl/per_face_normals.cpp b/src/igl/per_face_normals.cpp new file mode 100644 index 0000000000..d3de7ff950 --- /dev/null +++ b/src/igl/per_face_normals.cpp @@ -0,0 +1,128 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "per_face_normals.h" +#include + +#define SQRT_ONE_OVER_THREE 0.57735026918962573 +template +IGL_INLINE void igl::per_face_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const Eigen::MatrixBase & Z, + Eigen::PlainObjectBase & N) +{ + N.resize(F.rows(),3); + // loop over faces + int Frows = F.rows(); +#pragma omp parallel for if (Frows>10000) + for(int i = 0; i < Frows;i++) + { + const Eigen::Matrix v1 = V.row(F(i,1)) - V.row(F(i,0)); + const Eigen::Matrix v2 = V.row(F(i,2)) - V.row(F(i,0)); + N.row(i) = v1.cross(v2);//.normalized(); + typename DerivedV::Scalar r = N.row(i).norm(); + if(r == 0) + { + N.row(i) = Z; + }else + { + N.row(i) /= r; + } + } +} + +template +IGL_INLINE void igl::per_face_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase & N) +{ + using namespace Eigen; + Matrix Z(0,0,0); + return per_face_normals(V,F,Z,N); +} + +template +IGL_INLINE void igl::per_face_normals_stable( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase & N) +{ + using namespace Eigen; + typedef Matrix RowVectorV3; + typedef typename DerivedV::Scalar Scalar; + + const size_t m = F.rows(); + + N.resize(F.rows(),3); + // Grad all points + for(size_t f = 0;f sum3 = + [&sum3](Scalar a, Scalar b, Scalar c)->Scalar + { + if(fabs(c)>fabs(a)) + { + return sum3(c,b,a); + } + // c < a + if(fabs(c)>fabs(b)) + { + return sum3(a,c,b); + } + // c < a, c < b + if(fabs(b)>fabs(a)) + { + return sum3(b,a,c); + } + return (a+b)+c; + }; + + N(f,d) = sum3(n0(d),n1(d),n2(d)); + } + // sum better not be sure, or else NaN + N.row(f) /= N.row(f).norm(); + } + +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals,class Eigen::Matrix,class Eigen::Matrix >(class Eigen::MatrixBase > const &,class Eigen::MatrixBase > const &,class Eigen::PlainObjectBase > &); +template void igl::per_face_normals_stable, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals_stable, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_face_normals_stable, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/per_face_normals.h b/src/igl/per_face_normals.h new file mode 100644 index 0000000000..220e5e9a68 --- /dev/null +++ b/src/igl/per_face_normals.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PER_FACE_NORMALS_H +#define IGL_PER_FACE_NORMALS_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute face normals via vertex position list, face list + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigen Matrix of face (triangle) indices + // Z 3 vector normal given to faces with degenerate normal. + // Output: + // N #F by 3 eigen Matrix of mesh face (triangle) 3D normals + // + // Example: + // // Give degenerate faces (1/3,1/3,1/3)^0.5 + // per_face_normals(V,F,Vector3d(1,1,1).normalized(),N); + template + IGL_INLINE void per_face_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const Eigen::MatrixBase & Z, + Eigen::PlainObjectBase & N); + // Wrapper with Z = (0,0,0). Note that this means that row norms will be zero + // (i.e. not 1) for degenerate normals. + template + IGL_INLINE void per_face_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase & N); + // Special version where order of face indices is guaranteed not to effect + // output. + template + IGL_INLINE void per_face_normals_stable( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase & N); +} + +#ifndef IGL_STATIC_LIBRARY +# include "per_face_normals.cpp" +#endif + +#endif diff --git a/src/igl/per_vertex_attribute_smoothing.cpp b/src/igl/per_vertex_attribute_smoothing.cpp new file mode 100644 index 0000000000..ecf2b4e824 --- /dev/null +++ b/src/igl/per_vertex_attribute_smoothing.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "per_vertex_attribute_smoothing.h" +#include + +template +IGL_INLINE void igl::per_vertex_attribute_smoothing( + const Eigen::PlainObjectBase& Ain, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase & Aout) +{ + std::vector denominator(Ain.rows(), 0); + Aout = DerivedV::Zero(Ain.rows(), Ain.cols()); + for (int i = 0; i < F.rows(); ++i) { + for (int j = 0; j < 3; ++j) { + int j1 = (j + 1) % 3; + int j2 = (j + 2) % 3; + Aout.row(F(i, j)) += Ain.row(F(i, j1)) + Ain.row(F(i, j2)); + denominator[F(i, j)] += 2; + } + } + for (int i = 0; i < Ain.rows(); ++i) + Aout.row(i) /= denominator[i]; +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::per_vertex_attribute_smoothing, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/per_vertex_attribute_smoothing.h b/src/igl/per_vertex_attribute_smoothing.h new file mode 100644 index 0000000000..6628102366 --- /dev/null +++ b/src/igl/per_vertex_attribute_smoothing.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PER_VERTEX_ATTRIBUTE_SMOOTHING_H +#define IGL_PER_VERTEX_ATTRIBUTE_SMOOTHING_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Smooth vertex attributes using uniform Laplacian + // Inputs: + // Ain #V by #A eigen Matrix of mesh vertex attributes (each vertex has #A attributes) + // F #F by 3 eigne Matrix of face (triangle) indices + // Output: + // Aout #V by #A eigen Matrix of mesh vertex attributes + template + IGL_INLINE void per_vertex_attribute_smoothing( + const Eigen::PlainObjectBase& Ain, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase & Aout); +} + +#ifndef IGL_STATIC_LIBRARY +# include "per_vertex_attribute_smoothing.cpp" +#endif + +#endif diff --git a/src/igl/per_vertex_normals.cpp b/src/igl/per_vertex_normals.cpp new file mode 100644 index 0000000000..c3bbda193a --- /dev/null +++ b/src/igl/per_vertex_normals.cpp @@ -0,0 +1,133 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "per_vertex_normals.h" + +#include "get_seconds.h" +#include "per_face_normals.h" +#include "doublearea.h" +#include "parallel_for.h" +#include "internal_angles.h" + +template < + typename DerivedV, + typename DerivedF, + typename DerivedN> +IGL_INLINE void igl::per_vertex_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const igl::PerVertexNormalsWeightingType weighting, + Eigen::PlainObjectBase & N) +{ + Eigen::Matrix PFN; + igl::per_face_normals(V,F,PFN); + return per_vertex_normals(V,F,weighting,PFN,N); +} + +template +IGL_INLINE void igl::per_vertex_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase & N) +{ + return per_vertex_normals(V,F,PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT,N); +} + +template +IGL_INLINE void igl::per_vertex_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const igl::PerVertexNormalsWeightingType weighting, + const Eigen::MatrixBase& FN, + Eigen::PlainObjectBase & N) +{ + using namespace std; + // Resize for output + N.setZero(V.rows(),3); + + Eigen::Matrix + W(F.rows(),3); + switch(weighting) + { + case PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM: + W.setConstant(1.); + break; + default: + assert(false && "Unknown weighting type"); + case PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT: + case PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA: + { + Eigen::Matrix A; + doublearea(V,F,A); + W = A.replicate(1,3); + break; + } + case PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE: + internal_angles(V,F,W); + break; + } + + // loop over faces + for(int i = 0;i NN; + //parallel_for( + // F.rows(), + // [&NN,&N](const size_t n){ NN.resize(n,DerivedN::Zero(N.rows(),3));}, + // [&F,&W,&FN,&NN,&critical](const int i, const size_t t) + // { + // // throw normal at each corner + // for(int j = 0; j < 3;j++) + // { + // // Q: Does this need to be critical? + // // A: Yes. Different (i,j)'s could produce the same F(i,j) + // NN[t].row(F(i,j)) += W(i,j) * FN.row(i); + // } + // }, + // [&N,&NN](const size_t t){ N += NN[t]; }, + // 1000l); + + // take average via normalization + N.rowwise().normalize(); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedN> +IGL_INLINE void igl::per_vertex_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const Eigen::MatrixBase& FN, + Eigen::PlainObjectBase & N) +{ + return + per_vertex_normals(V,F,PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT,FN,N); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::per_vertex_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerVertexNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_vertex_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerVertexNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_vertex_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerVertexNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_vertex_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerVertexNormalsWeightingType, Eigen::PlainObjectBase >&); +template void igl::per_vertex_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_vertex_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerVertexNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_vertex_normals, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::PerVertexNormalsWeightingType, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::per_vertex_normals, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/per_vertex_normals.h b/src/igl/per_vertex_normals.h new file mode 100644 index 0000000000..be4ba04610 --- /dev/null +++ b/src/igl/per_vertex_normals.h @@ -0,0 +1,80 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PER_VERTEX_NORMALS_H +#define IGL_PER_VERTEX_NORMALS_H +#include "igl_inline.h" +#include +// Note: It would be nice to support more or all of the methods here: +// "A comparison of algorithms for vertex normal computation" +namespace igl +{ + enum PerVertexNormalsWeightingType + { + // Incident face normals have uniform influence on vertex normal + PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM = 0, + // Incident face normals are averaged weighted by area + PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA = 1, + // Incident face normals are averaged weighted by incident angle of vertex + PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE = 2, + // Area weights + PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT = 3, + NUM_PER_VERTEX_NORMALS_WEIGHTING_TYPE = 4 + }; + // Compute vertex normals via vertex position list, face list + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 3 eigne Matrix of face (triangle) indices + // weighting Weighting type + // Output: + // N #V by 3 eigen Matrix of mesh vertex 3D normals + template < + typename DerivedV, + typename DerivedF, + typename DerivedN> + IGL_INLINE void per_vertex_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const igl::PerVertexNormalsWeightingType weighting, + Eigen::PlainObjectBase & N); + // Without weighting + template < + typename DerivedV, + typename DerivedF, + typename DerivedN> + IGL_INLINE void per_vertex_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase & N); + // Inputs: + // FN #F by 3 matrix of face (triangle) normals + template + IGL_INLINE void per_vertex_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const PerVertexNormalsWeightingType weighting, + const Eigen::MatrixBase& FN, + Eigen::PlainObjectBase & N); + // Without weighting + template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedN> + IGL_INLINE void per_vertex_normals( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const Eigen::MatrixBase& FN, + Eigen::PlainObjectBase & N); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "per_vertex_normals.cpp" +#endif + +#endif diff --git a/src/igl/per_vertex_point_to_plane_quadrics.cpp b/src/igl/per_vertex_point_to_plane_quadrics.cpp new file mode 100644 index 0000000000..6d1be71b08 --- /dev/null +++ b/src/igl/per_vertex_point_to_plane_quadrics.cpp @@ -0,0 +1,157 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "per_vertex_point_to_plane_quadrics.h" +#include "quadric_binary_plus_operator.h" +#include +#include +#include + + +IGL_INLINE void igl::per_vertex_point_to_plane_quadrics( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI, + std::vector< + std::tuple > & quadrics) +{ + using namespace std; + typedef std::tuple Quadric; + const int dim = V.cols(); + //// Quadrics per face + //std::vector face_quadrics(F.rows()); + // Initialize each vertex quadric to zeros + quadrics.resize( + V.rows(), + // gcc <=4.8 can't handle initializer lists correctly + Quadric{Eigen::MatrixXd::Zero(dim,dim),Eigen::RowVectorXd::Zero(dim),0}); + Eigen::MatrixXd I = Eigen::MatrixXd::Identity(dim,dim); + // Rather initial with zeros, initial with a small amount of energy pull + // toward original vertex position + const double w = 1e-10; + for(int v = 0;v(quadrics[v]) = w*I; + Eigen::RowVectorXd Vv = V.row(v); + std::get<1>(quadrics[v]) = w*-Vv; + std::get<2>(quadrics[v]) = w*Vv.dot(Vv); + } + // Generic nD qslim from "Simplifying Surfaces with Color and Texture + // using Quadric Error Metric" (follow up to original QSlim) + for(int f = 0;fQuadric + { + // Dimension of subspace + const int m = S.rows(); + // Weight face's quadric (v'*A*v + 2*b'*v + c) by area + // e1 and e2 should be perpendicular + Eigen::MatrixXd A = I; + Eigen::RowVectorXd b = -p; + double c = p.dot(p); + for(int i = 0;i edge opposite cth corner is boundary + // Boundary edge vector + const Eigen::RowVectorXd p = V.row(F(f,(infinite_corner+1)%3)); + Eigen::RowVectorXd ev = V.row(F(f,(infinite_corner+2)%3)) - p; + const double length = ev.norm(); + ev /= length; + // Face neighbor across boundary edge + int e = EMAP(f+F.rows()*infinite_corner); + int opp = EF(e,0) == f ? 1 : 0; + int n = EF(e,opp); + int nc = EI(e,opp); + assert( + ((F(f,(infinite_corner+1)%3) == F(n,(nc+1)%3) && + F(f,(infinite_corner+2)%3) == F(n,(nc+2)%3)) || + (F(f,(infinite_corner+1)%3) == F(n,(nc+2)%3) + && F(f,(infinite_corner+2)%3) == F(n,(nc+1)%3))) && + "Edge flaps not agreeing on shared edge"); + // Edge vector on opposite face + const Eigen::RowVectorXd eu = V.row(F(n,nc)) - p; + assert(!std::isinf(eu(0))); + // Matrix with vectors spanning plane as columns + Eigen::MatrixXd A(ev.size(),2); + A< qr(A); + const Eigen::MatrixXd Q = qr.householderQ(); + const Eigen::MatrixXd N = + Q.topRightCorner(ev.size(),ev.size()-2).transpose(); + assert(N.cols() == ev.size()); + assert(N.rows() == ev.size()-2); + Eigen::MatrixXd S(N.rows()+1,ev.size()); + S< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PER_VERTEX_POINT_TO_PLANE_QUADRICS_H +#define IGL_PER_VERTEX_POINT_TO_PLANE_QUADRICS_H +#include "igl_inline.h" +#include +#include +#include +namespace igl +{ + // Compute quadrics per vertex of a "closed" triangle mesh (V,F). Rather than + // follow the qslim paper, this implements the lesser-known _follow up_ + // "Simplifying Surfaces with Color and Texture using Quadric Error Metrics". + // This allows V to be n-dimensional (where the extra coordiantes store + // texture UVs, color RGBs, etc. + // + // Inputs: + // V #V by n list of vertex positions. Assumes that vertices with + // infinite coordinates are "points at infinity" being used to close up + // boundary edges with faces. This allows special subspace quadrice for + // boundary edges: There should never be more than one "point at + // infinity" in a single triangle. + // F #F by 3 list of triangle indices into V + // E #E by 2 list of edge indices into V. + // EMAP #F*3 list of indices into E, mapping each directed edge to unique + // unique edge in E + // EF #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of + // F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) " + // e=(j->i) + // EI #E by 2 list of edge flap corners (see above). + // Outputs: + // quadrics #V list of quadrics, where a quadric is a tuple {A,b,c} such + // that the quadratic energy of moving this vertex to position x is + // given by x'Ax - 2b + c + // + IGL_INLINE void per_vertex_point_to_plane_quadrics( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI, + std::vector< + std::tuple > & quadrics); +} +#ifndef IGL_STATIC_LIBRARY +# include "per_vertex_point_to_plane_quadrics.cpp" +#endif +#endif diff --git a/src/igl/piecewise_constant_winding_number.cpp b/src/igl/piecewise_constant_winding_number.cpp new file mode 100644 index 0000000000..4bf7d76f8c --- /dev/null +++ b/src/igl/piecewise_constant_winding_number.cpp @@ -0,0 +1,82 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "piecewise_constant_winding_number.h" +#include "unique_edge_map.h" +#include "PI.h" + +template < + typename DerivedF, + typename DeriveduE, + typename uE2EType> +IGL_INLINE bool igl::piecewise_constant_winding_number( + const Eigen::MatrixBase& F, + const Eigen::MatrixBase& uE, + const std::vector >& uE2E) +{ + const size_t num_faces = F.rows(); + const size_t num_edges = uE.rows(); + const auto edge_index_to_face_index = [&](size_t ei) + { + return ei % num_faces; + }; + const auto is_consistent = [&](size_t fid, size_t s, size_t d) + { + if ((size_t)F(fid, 0) == s && (size_t)F(fid, 1) == d) return true; + if ((size_t)F(fid, 1) == s && (size_t)F(fid, 2) == d) return true; + if ((size_t)F(fid, 2) == s && (size_t)F(fid, 0) == d) return true; + + if ((size_t)F(fid, 0) == d && (size_t)F(fid, 1) == s) return false; + if ((size_t)F(fid, 1) == d && (size_t)F(fid, 2) == s) return false; + if ((size_t)F(fid, 2) == d && (size_t)F(fid, 0) == s) return false; + throw "Invalid face!!"; + }; + for (size_t i=0; i +IGL_INLINE bool igl::piecewise_constant_winding_number( + const Eigen::MatrixBase& F) +{ + Eigen::Matrix E, uE; + Eigen::Matrix EMAP; + std::vector > uE2E; + unique_edge_map(F, E, uE, EMAP, uE2E); + return piecewise_constant_winding_number(F,uE,uE2E); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::piecewise_constant_winding_number, Eigen::Matrix, unsigned long>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >, std::allocator > > > const&); +template bool igl::piecewise_constant_winding_number, Eigen::Matrix, unsigned long>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >, std::allocator > > > const&); +#ifdef WIN32 +template bool igl::piecewise_constant_winding_number, class Eigen::Matrix, unsigned __int64>(class Eigen::MatrixBase> const &, class Eigen::MatrixBase> const &, class std::vector>, class std::allocator>>> const &); +template bool igl::piecewise_constant_winding_number, class Eigen::Matrix, unsigned __int64>(class Eigen::MatrixBase> const &, class Eigen::MatrixBase> const &, class std::vector>, class std::allocator>>> const &); +#endif +#endif diff --git a/src/igl/piecewise_constant_winding_number.h b/src/igl/piecewise_constant_winding_number.h new file mode 100644 index 0000000000..ebbeb6e98b --- /dev/null +++ b/src/igl/piecewise_constant_winding_number.h @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PIECEWISE_CONSTANT_WINDING_NUMBER_H +#define IGL_PIECEWISE_CONSTANT_WINDING_NUMBER_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // PIECEWISE_CONSTANT_WINDING_NUMBER Determine if a given mesh induces a + // piecewise constant winding number field: Is this mesh valid input to solid + // set operations. **Assumes** that `(V,F)` contains no self-intersections + // (including degeneracies and co-incidences). If there are co-planar and + // co-incident vertex placements, a mesh could _fail_ this combinatorial test + // but still induce a piecewise-constant winding number _geometrically_. For + // example, consider a hemisphere with boundary and then pinch the boundary + // "shut" along a line segment. The **_bullet-proof_** check is to first + // resolve all self-intersections in `(V,F) -> (SV,SF)` (i.e. what the + // `igl::copyleft::cgal::piecewise_constant_winding_number` overload does). + // + // Inputs: + // F #F by 3 list of triangle indices into some (abstract) list of + // vertices V + // uE #uE by 2 list of unique edges indices into V + // uE2E #uE list of lists of indices into directed edges (#F * 3) + // Returns true if the mesh _combinatorially_ induces a piecewise constant + // winding number field. + // + template < + typename DerivedF, + typename DeriveduE, + typename uE2EType> + IGL_INLINE bool piecewise_constant_winding_number( + const Eigen::MatrixBase& F, + const Eigen::MatrixBase& uE, + const std::vector >& uE2E); + template + IGL_INLINE bool piecewise_constant_winding_number( + const Eigen::MatrixBase& F); +} +#ifndef IGL_STATIC_LIBRARY +# include "piecewise_constant_winding_number.cpp" +#endif +#endif diff --git a/src/igl/pinv.cpp b/src/igl/pinv.cpp new file mode 100644 index 0000000000..acf6a19139 --- /dev/null +++ b/src/igl/pinv.cpp @@ -0,0 +1,35 @@ +#include "pinv.h" +#include +#include + +template +void igl::pinv( + const Eigen::MatrixBase & A, + typename DerivedA::Scalar tol, + Eigen::PlainObjectBase & X) +{ + Eigen::JacobiSVD svd(A, Eigen::ComputeFullU | Eigen::ComputeFullV ); + typedef typename DerivedA::Scalar Scalar; + const Eigen::Matrix & U = svd.matrixU(); + const Eigen::Matrix & V = svd.matrixV(); + const Eigen::Matrix & S = svd.singularValues(); + if(tol < 0) + { + const Scalar smax = S.array().abs().maxCoeff(); + tol = + (Scalar)(std::max(A.rows(),A.cols())) * + (smax-std::nextafter(smax,std::numeric_limits::epsilon())); + } + const int rank = (S.array()>0).count(); + X = (V.leftCols(rank).array().rowwise() * + (1.0/S.head(rank).array()).transpose()).matrix()* + U.leftCols(rank).transpose(); +} + +template +void igl::pinv( + const Eigen::MatrixBase & A, + Eigen::PlainObjectBase & X) +{ + return pinv(A,-1,X); +} diff --git a/src/igl/pinv.h b/src/igl/pinv.h new file mode 100644 index 0000000000..045a17da2f --- /dev/null +++ b/src/igl/pinv.h @@ -0,0 +1,29 @@ +#ifndef IGL_PINV_H +#define IGL_PINV_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute the Moore-Penrose pseudoinverse + // + // Inputs: + // A m by n matrix + // tol tolerance (if negative then default is used) + // Outputs: + // X n by m matrix so that A*X*A = A and X*A*X = X and A*X = (A*X)' and + // (X*A) = (X*A)' + template + void pinv( + const Eigen::MatrixBase & A, + typename DerivedA::Scalar tol, + Eigen::PlainObjectBase & X); + // Wrapper using default tol + template + void pinv( + const Eigen::MatrixBase & A, + Eigen::PlainObjectBase & X); +} +#ifndef IGL_STATIC_LIBRARY +# include "pinv.cpp" +#endif +#endif diff --git a/src/igl/planarize_quad_mesh.cpp b/src/igl/planarize_quad_mesh.cpp new file mode 100644 index 0000000000..d3c06be620 --- /dev/null +++ b/src/igl/planarize_quad_mesh.cpp @@ -0,0 +1,245 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "planarize_quad_mesh.h" +#include "quad_planarity.h" +#include +#include +#include + +namespace igl +{ + template + class PlanarizerShapeUp + { + protected: + // number of faces, number of vertices + long numV, numF; + // references to the input faces and vertices + const Eigen::PlainObjectBase &Vin; + const Eigen::PlainObjectBase &Fin; + + // vector consisting of the vertex positions stacked: [x;y;z;x;y;z...] + // vector consisting of a weight per face (currently all set to 1) + // vector consisting of the projected face vertices (might be different for the same vertex belonging to different faces) + Eigen::Matrix Vv, weightsSqrt, P; + + // Matrices as in the paper + // Q: lhs matrix + // Ni: matrix that subtracts the mean of a face from the 4 vertices of a face + Eigen::SparseMatrix Q, Ni; + Eigen::SimplicialLDLT > solver; + + int maxIter; + double threshold; + const int ni = 4; + + // Matrix assemblers + inline void assembleQ(); + inline void assembleP(); + inline void assembleNi(); + + // Selects out of Vv the 4 vertices belonging to face fi + inline void assembleSelector(int fi, + Eigen::SparseMatrix &S); + + + public: + // Init - assemble stacked vector and lhs matrix, factorize + inline PlanarizerShapeUp(const Eigen::PlainObjectBase &V_, + const Eigen::PlainObjectBase &F_, + const int maxIter_, + const double &threshold_); + // Planarization - output to Vout + inline void planarize(Eigen::PlainObjectBase &Vout); + }; +} + +//Implementation + +template +inline igl::PlanarizerShapeUp::PlanarizerShapeUp(const Eigen::PlainObjectBase &V_, + const Eigen::PlainObjectBase &F_, + const int maxIter_, + const double &threshold_): +numV(V_.rows()), +numF(F_.rows()), +Vin(V_), +Fin(F_), +weightsSqrt(Eigen::Matrix::Ones(numF,1)), +maxIter(maxIter_), +threshold(threshold_) +{ + // assemble stacked vertex position vector + Vv.setZero(3*numV,1); + for (int i =0;i +inline void igl::PlanarizerShapeUp::assembleQ() +{ + std::vector > tripletList; + + // assemble the Ni matrix + assembleNi(); + + for (int fi = 0; fi< numF; fi++) + { + Eigen::SparseMatrix Sfi; + assembleSelector(fi, Sfi); + + // the final matrix per face + Eigen::SparseMatrix Qi = weightsSqrt(fi)*Ni*Sfi; + // put it in the correct block of Q + // todo: this can be made faster by omitting the selector matrix + for (int k=0; k::InnerIterator it(Qi,k); it; ++it) + { + typename DerivedV::Scalar val = it.value(); + int row = it.row(); + int col = it.col(); + tripletList.push_back(Eigen::Triplet(row+3*ni*fi,col,val)); + } + } + + Q.resize(3*ni*numF,3*numV); + Q.setFromTriplets(tripletList.begin(), tripletList.end()); + // the actual lhs matrix is Q'*Q + // prefactor that matrix + solver.compute(Q.transpose()*Q); + if(solver.info()!=Eigen::Success) + { + std::cerr << "Cholesky failed - PlanarizerShapeUp.cpp" << std::endl; + assert(0); + } +} + +template +inline void igl::PlanarizerShapeUp::assembleNi() +{ + std::vector> tripletList; + for (int ii = 0; ii< ni; ii++) + { + for (int jj = 0; jj< ni; jj++) + { + tripletList.push_back(Eigen::Triplet(3*ii+0,3*jj+0,-1./ni)); + tripletList.push_back(Eigen::Triplet(3*ii+1,3*jj+1,-1./ni)); + tripletList.push_back(Eigen::Triplet(3*ii+2,3*jj+2,-1./ni)); + } + tripletList.push_back(Eigen::Triplet(3*ii+0,3*ii+0,1.)); + tripletList.push_back(Eigen::Triplet(3*ii+1,3*ii+1,1.)); + tripletList.push_back(Eigen::Triplet(3*ii+2,3*ii+2,1.)); + } + Ni.resize(3*ni,3*ni); + Ni.setFromTriplets(tripletList.begin(), tripletList.end()); +} + +//assumes V stacked [x;y;z;x;y;z...]; +template +inline void igl::PlanarizerShapeUp::assembleSelector(int fi, + Eigen::SparseMatrix &S) +{ + + std::vector> tripletList; + for (int fvi = 0; fvi< ni; fvi++) + { + int vi = Fin(fi,fvi); + tripletList.push_back(Eigen::Triplet(3*fvi+0,3*vi+0,1.)); + tripletList.push_back(Eigen::Triplet(3*fvi+1,3*vi+1,1.)); + tripletList.push_back(Eigen::Triplet(3*fvi+2,3*vi+2,1.)); + } + + S.resize(3*ni,3*numV); + S.setFromTriplets(tripletList.begin(), tripletList.end()); + +} + +//project all faces to their closest planar face +template +inline void igl::PlanarizerShapeUp::assembleP() +{ + P.setZero(3*ni*numF); + for (int fi = 0; fi< numF; fi++) + { + // todo: this can be made faster by omitting the selector matrix + Eigen::SparseMatrix Sfi; + assembleSelector(fi, Sfi); + Eigen::SparseMatrix NSi = Ni*Sfi; + + Eigen::Matrix Vi = NSi*Vv; + Eigen::Matrix CC(3,ni); + for (int i = 0; i C = CC*CC.transpose(); + + // Alec: Doesn't compile + Eigen::EigenSolver> es(C); + // the real() is for compilation purposes + Eigen::Matrix lambda = es.eigenvalues().real(); + Eigen::Matrix U = es.eigenvectors().real(); + int min_i; + lambda.cwiseAbs().minCoeff(&min_i); + U.col(min_i).setZero(); + Eigen::Matrix PP = U*U.transpose()*CC; + for (int i = 0; i +inline void igl::PlanarizerShapeUp::planarize(Eigen::PlainObjectBase &Vout) +{ + Eigen::Matrix planarity; + Vout = Vin; + + for (int iter =0; iter oldMean, newMean; + oldMean = Vin.colwise().mean(); + newMean = Vout.colwise().mean(); + Vout.rowwise() += (oldMean - newMean); + +}; + + + +template +IGL_INLINE void igl::planarize_quad_mesh(const Eigen::PlainObjectBase &Vin, + const Eigen::PlainObjectBase &Fin, + const int maxIter, + const double &threshold, + Eigen::PlainObjectBase &Vout) +{ + PlanarizerShapeUp planarizer(Vin, Fin, maxIter, threshold); + planarizer.planarize(Vout); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::planarize_quad_mesh, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, double const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/planarize_quad_mesh.h b/src/igl/planarize_quad_mesh.h new file mode 100644 index 0000000000..0de7bf3895 --- /dev/null +++ b/src/igl/planarize_quad_mesh.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PLANARIZE_QUAD_MESH_H +#define IGL_PLANARIZE_QUAD_MESH_H +#include "igl_inline.h" +#include +namespace igl +{ + // Planarizes a given quad mesh using the algorithm described in the paper + // "Shape-Up: Shaping Discrete Geometry with Projections" by S. Bouaziz, + // M. Deuss, Y. Schwartzburg, T. Weise, M. Pauly, Computer Graphics Forum, + // Volume 31, Issue 5, August 2012, p. 1657-1667 + // (http://dl.acm.org/citation.cfm?id=2346802). + // The algorithm iterates between projecting each quad to its closest planar + // counterpart and stitching those quads together via a least squares + // optimization. It stops whenever all quads' non-planarity is less than a + // given threshold (suggested value: 0.01), or a maximum number of iterations + // is reached. + + + // Inputs: + // Vin #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 4 eigen Matrix of face (quad) indices + // maxIter maximum numbers of iterations + // threshold minimum allowed threshold for non-planarity + // Output: + // Vout #V by 3 eigen Matrix of planar mesh vertex 3D positions + // + + template + IGL_INLINE void planarize_quad_mesh(const Eigen::PlainObjectBase &Vin, + const Eigen::PlainObjectBase &F, + const int maxIter, + const double &threshold, + Eigen::PlainObjectBase &Vout); +} +#ifndef IGL_STATIC_LIBRARY +# include "planarize_quad_mesh.cpp" +#endif + +#endif diff --git a/src/igl/ply.h b/src/igl/ply.h new file mode 100644 index 0000000000..60f2b29de2 --- /dev/null +++ b/src/igl/ply.h @@ -0,0 +1,3168 @@ +#ifndef IGL_PLY_H +#define IGL_PLY_H +/* + +Header for PLY polygon files. + +- Greg Turk, March 1994 + +A PLY file contains a single polygonal _object_. + +An object is composed of lists of _elements_. Typical elements are +vertices, faces, edges and materials. + +Each type of element for a given object has one or more _properties_ +associated with the element type. For instance, a vertex element may +have as properties three floating-point values x,y,z and three unsigned +chars for red, green and blue. + +--------------------------------------------------------------- + +Copyright (c) 1994 The Board of Trustees of The Leland Stanford +Junior University. All rights reserved. + +Permission to use, copy, modify and distribute this software and its +documentation for any purpose is hereby granted without fee, provided +that the above copyright notice and this permission notice appear in +all copies of this software and that you do not sell the software. + +THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, +EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY +WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +*/ + +/* +-------------------------------------------------------------------------------- +Joao Fradinho Oliveira, July 2005 +Copyright (c) 2005 University College London +copyright conditions as above + +update for ply reading of multi OS ply files, in any OS (Unix, Macintosh, PC) +-------------------------------------------------------------------------------- + +ply_open_for_reading + +* was changed to always open files in binary mode, files written in ascii can also be +read with this binary mode. + +* allows opening of filenames that are alias files in macintosh + +* code tested on pc and mac + + +get_words + +* was changed to handle line breaks in UNIX, MACINTOSH, PC, it resets the file pointer +accordingly for the next read. + + +NOTES: +The ply file, has always an ascii part for the header, and a binary or ascii +part for the data. +The header part in ascii, dictates that linebreaks are used, this make models +operating system dependent, as a line break in unix is indicated with the escape character \n, +on a macintosh, with \r, and on a pc with \r\n <--2 unsigned chars, 2 bytes, instead of 1 byte. + +get_words allows reading of any OS, text editors such as BBEdit do not save the linebreaks +properly to target OSs with binary files. + +*/ + +#ifndef __PLY_H__ +#define __PLY_H__ + + + +#include +#include +#include +#include + +namespace igl { + namespace ply { + +#define PLY_ASCII 1 /* ascii PLY file */ +#define PLY_BINARY_BE 2 /* binary PLY file, big endian */ +#define PLY_BINARY_LE 3 /* binary PLY file, little endian */ +#define PLY_BINARY_NATIVE 4 /* binary PLY file, same endianness as + current architecture */ + +#define PLY_OKAY 0 /* ply routine worked okay */ +#define PLY_ERROR -1 /* error in ply routine */ + +/* scalar data types supported by PLY format */ + +#define PLY_START_TYPE 0 +#define PLY_CHAR 1 +#define PLY_SHORT 2 +#define PLY_INT 3 +#define PLY_UCHAR 4 +#define PLY_USHORT 5 +#define PLY_UINT 6 +#define PLY_FLOAT 7 +#define PLY_DOUBLE 8 +#define PLY_END_TYPE 9 + +#define PLY_SCALAR 0 +#define PLY_LIST 1 + + + + +typedef struct PlyProperty { /* description of a property */ + + const char *name; /* property name */ + int external_type; /* file's data type */ + int internal_type; /* program's data type */ + int offset; /* offset bytes of prop in a struct */ + + int is_list; /* 1 = list, 0 = scalar */ + int count_external; /* file's count type */ + int count_internal; /* program's count type */ + int count_offset; /* offset byte for list count */ + +} PlyProperty; + +typedef struct PlyElement { /* description of an element */ + const char *name; /* element name */ + int num; /* number of elements in this object */ + int size; /* size of element (bytes) or -1 if variable */ + int nprops; /* number of properties for this element */ + PlyProperty **props; /* list of properties in the file */ + char *store_prop; /* flags: property wanted by user? */ + int other_offset; /* offset to un-asked-for props, or -1 if none*/ + int other_size; /* size of other_props structure */ +} PlyElement; + +typedef struct PlyOtherProp { /* describes other properties in an element */ + const char *name; /* element name */ + int size; /* size of other_props */ + int nprops; /* number of properties in other_props */ + PlyProperty **props; /* list of properties in other_props */ +} PlyOtherProp; + +typedef struct OtherData { /* for storing other_props for an other element */ + void *other_props; +} OtherData; + +typedef struct OtherElem { /* data for one "other" element */ + char *elem_name; /* names of other elements */ + int elem_count; /* count of instances of each element */ + OtherData **other_data; /* actual property data for the elements */ + PlyOtherProp *other_props; /* description of the property data */ +} OtherElem; + +typedef struct PlyOtherElems { /* "other" elements, not interpreted by user */ + int num_elems; /* number of other elements */ + OtherElem *other_list; /* list of data for other elements */ +} PlyOtherElems; + +typedef struct PlyFile { /* description of PLY file */ + FILE *fp; /* file pointer */ + int file_type; /* ascii or binary */ + float version; /* version number of file */ + int nelems; /* number of elements of object */ + PlyElement **elems; /* list of elements */ + int num_comments; /* number of comments */ + char **comments; /* list of comments */ + int num_obj_info; /* number of items of object information */ + char **obj_info; /* list of object info items */ + PlyElement *which_elem; /* which element we're currently writing */ + PlyOtherElems *other_elems; /* "other" elements from a PLY file */ +} PlyFile; + +/* memory allocation */ +extern char *my_alloc(); +#define myalloc(mem_size) my_alloc((mem_size), __LINE__, __FILE__) + +#ifndef ALLOCN +#define REALLOCN(PTR,TYPE,OLD_N,NEW_N) \ + { \ + if ((OLD_N) == 0) \ + { ALLOCN((PTR),TYPE,(NEW_N));} \ + else \ + { \ + (PTR) = (TYPE *)realloc((PTR),(NEW_N)*sizeof(TYPE)); \ + if (((PTR) == NULL) && ((NEW_N) != 0)) \ + { \ + fprintf(stderr, "Memory reallocation failed on line %d in %s\n", \ + __LINE__, __FILE__); \ + fprintf(stderr, " tried to reallocate %d->%d\n", \ + (OLD_N), (NEW_N)); \ + exit(-1); \ + } \ + if ((NEW_N)>(OLD_N)) \ + memset((char *)(PTR)+(OLD_N)*sizeof(TYPE), 0, \ + ((NEW_N)-(OLD_N))*sizeof(TYPE)); \ + } \ + } + +#define ALLOCN(PTR,TYPE,N) \ + { (PTR) = (TYPE *) calloc(((unsigned)(N)),sizeof(TYPE));\ + if ((PTR) == NULL) { \ + fprintf(stderr, "Memory allocation failed on line %d in %s\n", \ + __LINE__, __FILE__); \ + exit(-1); \ + } \ + } + + +#define FREE(PTR) { free((PTR)); (PTR) = NULL; } +#endif + + +/*** delcaration of routines ***/ + +inline int get_native_binary_type2(); + +inline PlyFile *ply_write(FILE *, int,const char **, int); +inline PlyFile *ply_open_for_writing(char *, int,const char **, int, float *); +inline void ply_describe_element(PlyFile *, const char *, int, int, PlyProperty *); +inline void ply_describe_property(PlyFile *, const char *, PlyProperty *); +inline void ply_element_count(PlyFile *, const char *, int); +inline void ply_header_complete(PlyFile *); +inline void ply_put_element_setup(PlyFile *, const char *); +inline void ply_put_element(PlyFile *, void *, int*); +inline void ply_put_comment(PlyFile *, char *); +inline void ply_put_obj_info(PlyFile *, char *); +inline PlyFile *ply_read(FILE *, int *, char ***); +inline PlyFile *ply_open_for_reading( const char *, int *, char ***, int *, float *); +inline PlyProperty **ply_get_element_description(PlyFile *, const char *, int*, int*); +inline void ply_get_element_setup( PlyFile *, const char *, int, PlyProperty *); +inline void ply_get_property(PlyFile *, const char *, PlyProperty *); +inline PlyOtherProp *ply_get_other_properties(PlyFile *, const char *, int); +inline void ply_get_element(PlyFile *, void *, int *); +inline char **ply_get_comments(PlyFile *, int *); +inline char **ply_get_obj_info(PlyFile *, int *); +inline void ply_close(PlyFile *); +inline void ply_get_info(PlyFile *, float *, int *); +inline PlyOtherElems *ply_get_other_element (PlyFile *, const char *, int); +inline void ply_describe_other_elements ( PlyFile *, PlyOtherElems *); +inline void ply_put_other_elements (PlyFile *); +inline void ply_free_other_elements (PlyOtherElems *); +inline void ply_describe_other_properties(PlyFile *, PlyOtherProp *, int); + +inline int equal_strings(const char *, const char *); + + +} +} +#endif /* !__PLY_H__ */ +/* + +The interface routines for reading and writing PLY polygon files. + +Greg Turk, February 1994 + +--------------------------------------------------------------- + +A PLY file contains a single polygonal _object_. + +An object is composed of lists of _elements_. Typical elements are +vertices, faces, edges and materials. + +Each type of element for a given object has one or more _properties_ +associated with the element type. For instance, a vertex element may +have as properties the floating-point values x,y,z and the three unsigned +chars representing red, green and blue. + +--------------------------------------------------------------- + +Copyright (c) 1994 The Board of Trustees of The Leland Stanford +Junior University. All rights reserved. + +Permission to use, copy, modify and distribute this software and its +documentation for any purpose is hereby granted without fee, provided +that the above copyright notice and this permission notice appear in +all copies of this software and that you do not sell the software. + +THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, +EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY +WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +*/ + +/* +-------------------------------------------------------------------------------- +Joao Fradinho Oliveira, July 2005 +University College London + +update for ply reading of multi OS ply files, in any OS (Unix, Macintosh, PC) +-------------------------------------------------------------------------------- + +ply_open_for_reading + +* was changed to always open files in binary mode, files written in ascii can also be +read with this binary mode. + +* allows opening of filenames that are alias files in macintosh + +* code tested on pc and mac + + +get_words + +* was changed to handle line breaks in UNIX, MACINTOSH, PC, it resets the file pointer +accordingly for the next read. + + +NOTES: +The ply file, has always an ascii part for the header, and a binary or ascii +part for the data. +The header part in ascii, dictates that linebreaks are used, this make models +operating system dependent, as a line break in unix is indicated with the escape character \n, +on a macintosh, with \r, and on a pc with \r\n <--2 unsigned chars, 2 bytes, instead of 1 byte. + +get_words allows reading of any OS, text editors such as BBEdit do not save the linebreaks +properly to target OSs with binary files. + +*/ + +#include +#include +#include +#include +//#include "ply.h" + + +namespace igl { + namespace ply { + + +// Use unnamed namespace to avoid duplicate symbols +/* +namespace +{ +const char *type_names[] = { +"invalid", +"char", "short", "int", +"uchar", "ushort", "uint", +"float", "double", +}; + +// names of scalar types +const char *alt_type_names[] = { +"invalid", +"int8", "int16", "int32", "uint8", "uint16", "uint32", "float32", "float64", +}; + +int ply_type_size[] = { + 0, 1, 2, 4, 1, 2, 4, 4, 8 +}; +} + +typedef union +{ + int int_value; + char byte_values[sizeof(int)]; +} endian_test_type; + + +namespace +{ +static int native_binary_type = -1; +static int types_checked = 0; +} +*/ + +#define NO_OTHER_PROPS -1 + +#define DONT_STORE_PROP 0 +#define STORE_PROP 1 + +#define OTHER_PROP 0 +#define NAMED_PROP 1 + +/* returns 1 if strings are equal, 0 if not */ +inline int equal_strings(const char *, const char *); + +/* find an element in a plyfile's list */ +inline PlyElement *find_element(PlyFile *, const char *); + +/* find a property in an element's list */ +inline PlyProperty *find_property(PlyElement *, const char *, int *); + +/* write to a file the word describing a PLY file data type */ +inline void write_scalar_type (FILE *, int); + +/* read a line from a file and break it up into separate words */ +inline char **get_words(FILE *, int *, char **); +inline char **old_get_words(FILE *, int *); + +/* write an item to a file */ +inline void write_binary_item(FILE *, int, int, unsigned int, double, int, int*); +inline void write_ascii_item(FILE *, int, unsigned int, double, int); +inline double old_write_ascii_item(FILE *, char *, int); + +/* add information to a PLY file descriptor */ +inline void add_element(PlyFile *, char **); +inline void add_property(PlyFile *, char **); +inline void add_comment(PlyFile *, char *); +inline void add_obj_info(PlyFile *, char *); + +/* copy a property */ +inline void copy_property(PlyProperty *, PlyProperty *); + +/* store a value into where a pointer and a type specify */ +inline void store_item(char *, int, int, unsigned int, double); + +/* return the value of a stored item */ +inline void get_stored_item( void *, int, int *, unsigned int *, double *); + +/* return the value stored in an item, given ptr to it and its type */ +inline double get_item_value(char *, int); + +/* get binary or ascii item and store it according to ptr and type */ +inline void get_ascii_item(char *, int, int *, unsigned int *, double *); +inline void get_binary_item(FILE *, int, int, int *, unsigned int *, double *, int*); + +/* get a bunch of elements from a file */ +inline void ascii_get_element(PlyFile *, char *); +inline void binary_get_element(PlyFile *, char *, int*); + +/* memory allocation */ +inline char *my_alloc(int, int, const char *); + +/* byte ordering */ +inline void get_native_binary_type(int*); +inline void swap_bytes(char *, int); + +inline int check_types(); + + +/*************/ +/* Writing */ +/*************/ + + +/****************************************************************************** +Given a file pointer, get ready to write PLY data to the file. + +Entry: + fp - the given file pointer + nelems - number of elements in object + elem_names - list of element names + file_type - file type, either ascii or binary + +Exit: + returns a pointer to a PlyFile, used to refer to this file, or NULL if error +******************************************************************************/ + +inline PlyFile *ply_write( + FILE *fp, + int nelems, + const char **elem_names, + int file_type +) +{ + int i; + PlyFile *plyfile; + PlyElement *elem; + + /* check for NULL file pointer */ + if (fp == NULL) + return (NULL); + + int native_binary_type = -1; + int types_checked = 0; + if (native_binary_type == -1) + native_binary_type = get_native_binary_type2(); + if (!types_checked) + types_checked = check_types(); + + /* create a record for this object */ + + plyfile = (PlyFile *) myalloc (sizeof (PlyFile)); + if (file_type == PLY_BINARY_NATIVE) + plyfile->file_type = native_binary_type; + else + plyfile->file_type = file_type; + plyfile->num_comments = 0; + plyfile->num_obj_info = 0; + plyfile->nelems = nelems; + plyfile->version = 1.0; + plyfile->fp = fp; + plyfile->other_elems = NULL; + + /* tuck aside the names of the elements */ + + plyfile->elems = (PlyElement **) myalloc (sizeof (PlyElement *) * nelems); + for (i = 0; i < nelems; i++) { + elem = (PlyElement *) myalloc (sizeof (PlyElement)); + plyfile->elems[i] = elem; + elem->name = strdup (elem_names[i]); + elem->num = 0; + elem->nprops = 0; + } + + /* return pointer to the file descriptor */ + return (plyfile); +} + + +/****************************************************************************** +Open a polygon file for writing. + +Entry: + filename - name of file to read from + nelems - number of elements in object + elem_names - list of element names + file_type - file type, either ascii or binary + +Exit: + version - version number of PLY file + returns a file identifier, used to refer to this file, or NULL if error +******************************************************************************/ + +inline PlyFile *ply_open_for_writing( + const char *filename, + int nelems, + const char **elem_names, + int file_type, + float *version +) +{ + PlyFile *plyfile; + char *name; + FILE *fp; + + /* tack on the extension .ply, if necessary */ + + name = (char *) myalloc (sizeof (char) * (strlen (filename) + 5)); + strcpy (name, filename); + if (strlen (name) < 4 || + strcmp (name + strlen (name) - 4, ".ply") != 0) + strcat (name, ".ply"); + + /* open the file for writing */ + + fp = fopen (name, "w"); + if (fp == NULL) { + return (NULL); + } + + /* create the actual PlyFile structure */ + + plyfile = ply_write (fp, nelems, elem_names, file_type); + if (plyfile == NULL) + return (NULL); + + /* say what PLY file version number we're writing */ + *version = plyfile->version; + + /* return pointer to the file descriptor */ + return (plyfile); +} + + +/****************************************************************************** +Describe an element, including its properties and how many will be written +to the file. + +Entry: + plyfile - file identifier + elem_name - name of element that information is being specified about + nelems - number of elements of this type to be written + nprops - number of properties contained in the element + prop_list - list of properties +******************************************************************************/ + +inline void ply_describe_element( + PlyFile *plyfile, + const char *elem_name, + int nelems, + int nprops, + PlyProperty *prop_list +) +{ + int i; + PlyElement *elem; + PlyProperty *prop; + + /* look for appropriate element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf(stderr,"ply_describe_element: can't find element '%s'\n",elem_name); + exit (-1); + } + + elem->num = nelems; + + /* copy the list of properties */ + + elem->nprops = nprops; + elem->props = (PlyProperty **) myalloc (sizeof (PlyProperty *) * nprops); + elem->store_prop = (char *) myalloc (sizeof (char) * nprops); + + for (i = 0; i < nprops; i++) { + prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + elem->props[i] = prop; + elem->store_prop[i] = NAMED_PROP; + copy_property (prop, &prop_list[i]); + } +} + + +/****************************************************************************** +Describe a property of an element. + +Entry: + plyfile - file identifier + elem_name - name of element that information is being specified about + prop - the new property +******************************************************************************/ + +inline void ply_describe_property( + PlyFile *plyfile, + const char *elem_name, + PlyProperty *prop +) +{ + PlyElement *elem; + PlyProperty *elem_prop; + + /* look for appropriate element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf(stderr, "ply_describe_property: can't find element '%s'\n", + elem_name); + return; + } + + /* create room for new property */ + + if (elem->nprops == 0) { + elem->props = (PlyProperty **) myalloc (sizeof (PlyProperty *)); + elem->store_prop = (char *) myalloc (sizeof (char)); + elem->nprops = 1; + } + else { + elem->nprops++; + elem->props = (PlyProperty **) + realloc (elem->props, sizeof (PlyProperty *) * elem->nprops); + elem->store_prop = (char *) + realloc (elem->store_prop, sizeof (char) * elem->nprops); + } + + /* copy the new property */ + + elem_prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + elem->props[elem->nprops - 1] = elem_prop; + elem->store_prop[elem->nprops - 1] = NAMED_PROP; + copy_property (elem_prop, prop); +} + + +/****************************************************************************** +Describe what the "other" properties are that are to be stored, and where +they are in an element. +******************************************************************************/ + +inline void ply_describe_other_properties( + PlyFile *plyfile, + PlyOtherProp *other, + int offset +) +{ + int i; + PlyElement *elem; + PlyProperty *prop; + + /* look for appropriate element */ + elem = find_element (plyfile, other->name); + if (elem == NULL) { + fprintf(stderr, "ply_describe_other_properties: can't find element '%s'\n", + other->name); + return; + } + + /* create room for other properties */ + + if (elem->nprops == 0) { + elem->props = (PlyProperty **) + myalloc (sizeof (PlyProperty *) * other->nprops); + elem->store_prop = (char *) myalloc (sizeof (char) * other->nprops); + elem->nprops = 0; + } + else { + int newsize; + newsize = elem->nprops + other->nprops; + elem->props = (PlyProperty **) + realloc (elem->props, sizeof (PlyProperty *) * newsize); + elem->store_prop = (char *) + realloc (elem->store_prop, sizeof (char) * newsize); + } + + /* copy the other properties */ + + for (i = 0; i < other->nprops; i++) { + prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + copy_property (prop, other->props[i]); + elem->props[elem->nprops] = prop; + elem->store_prop[elem->nprops] = OTHER_PROP; + elem->nprops++; + } + + /* save other info about other properties */ + elem->other_size = other->size; + elem->other_offset = offset; +} + + +/****************************************************************************** +State how many of a given element will be written. + +Entry: + plyfile - file identifier + elem_name - name of element that information is being specified about + nelems - number of elements of this type to be written +******************************************************************************/ + +inline void ply_element_count( + PlyFile *plyfile, + const char *elem_name, + int nelems +) +{ + PlyElement *elem; + + /* look for appropriate element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf(stderr,"ply_element_count: can't find element '%s'\n",elem_name); + exit (-1); + } + + elem->num = nelems; +} + + +/****************************************************************************** +Signal that we've described everything a PLY file's header and that the +header should be written to the file. + +Entry: + plyfile - file identifier +******************************************************************************/ + +inline void ply_header_complete(PlyFile *plyfile) +{ + int i,j; + FILE *fp = plyfile->fp; + PlyElement *elem; + PlyProperty *prop; + + fprintf (fp, "ply\n"); + + switch (plyfile->file_type) { + case PLY_ASCII: + fprintf (fp, "format ascii 1.0\n"); + break; + case PLY_BINARY_BE: + fprintf (fp, "format binary_big_endian 1.0\n"); + break; + case PLY_BINARY_LE: + fprintf (fp, "format binary_little_endian 1.0\n"); + break; + default: + fprintf (stderr, "ply_header_complete: bad file type = %d\n", + plyfile->file_type); + exit (-1); + } + + /* write out the comments */ + + for (i = 0; i < plyfile->num_comments; i++) + fprintf (fp, "comment %s\n", plyfile->comments[i]); + + /* write out object information */ + + for (i = 0; i < plyfile->num_obj_info; i++) + fprintf (fp, "obj_info %s\n", plyfile->obj_info[i]); + + /* write out information about each element */ + + for (i = 0; i < plyfile->nelems; i++) { + + elem = plyfile->elems[i]; + fprintf (fp, "element %s %d\n", elem->name, elem->num); + + /* write out each property */ + for (j = 0; j < elem->nprops; j++) { + prop = elem->props[j]; + if (prop->is_list) { + fprintf (fp, "property list "); + write_scalar_type (fp, prop->count_external); + fprintf (fp, " "); + write_scalar_type (fp, prop->external_type); + fprintf (fp, " %s\n", prop->name); + } + else { + fprintf (fp, "property "); + write_scalar_type (fp, prop->external_type); + fprintf (fp, " %s\n", prop->name); + } + } + } + + fprintf (fp, "end_header\n"); +} + + +/****************************************************************************** +Specify which elements are going to be written. This should be called +before a call to the routine ply_put_element(). + +Entry: + plyfile - file identifier + elem_name - name of element we're talking about +******************************************************************************/ + +inline void ply_put_element_setup(PlyFile *plyfile, const char *elem_name) +{ + PlyElement *elem; + + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf(stderr, "ply_elements_setup: can't find element '%s'\n", elem_name); + exit (-1); + } + + plyfile->which_elem = elem; +} + + +/****************************************************************************** +Write an element to the file. This routine assumes that we're +writing the type of element specified in the last call to the routine +ply_put_element_setup(). + +Entry: + plyfile - file identifier + elem_ptr - pointer to the element +******************************************************************************/ + +inline void ply_put_element(PlyFile *plyfile, void *elem_ptr, int *native_binary_type) +{ + int j,k; + FILE *fp = plyfile->fp; + PlyElement *elem; + PlyProperty *prop; + char *elem_data,*item; + char **item_ptr; + int list_count; + int item_size; + int int_val; + unsigned int uint_val; + double double_val; + char **other_ptr; + + elem = plyfile->which_elem; + elem_data = (char *)elem_ptr; + other_ptr = (char **) (((char *) elem_ptr) + elem->other_offset); + + /* write out either to an ascii or binary file */ + int ply_type_size[] = { + 0, 1, 2, 4, 1, 2, 4, 4, 8 + }; + + if (plyfile->file_type == PLY_ASCII) { + + /* write an ascii file */ + + /* write out each property of the element */ + for (j = 0; j < elem->nprops; j++) { + prop = elem->props[j]; + if (elem->store_prop[j] == OTHER_PROP) + elem_data = *other_ptr; + else + elem_data = (char *)elem_ptr; + if (prop->is_list) { + item = elem_data + prop->count_offset; + get_stored_item ((void *) item, prop->count_internal, + &int_val, &uint_val, &double_val); + write_ascii_item (fp, int_val, uint_val, double_val, + prop->count_external); + list_count = uint_val; + item_ptr = (char **) (elem_data + prop->offset); + item = item_ptr[0]; + item_size = ply_type_size[prop->internal_type]; + for (k = 0; k < list_count; k++) { + get_stored_item ((void *) item, prop->internal_type, + &int_val, &uint_val, &double_val); + write_ascii_item (fp, int_val, uint_val, double_val, + prop->external_type); + item += item_size; + } + } + else { + item = elem_data + prop->offset; + get_stored_item ((void *) item, prop->internal_type, + &int_val, &uint_val, &double_val); + write_ascii_item (fp, int_val, uint_val, double_val, + prop->external_type); + } + } + + fprintf (fp, "\n"); + } + else { + + /* write a binary file */ + + /* write out each property of the element */ + for (j = 0; j < elem->nprops; j++) { + prop = elem->props[j]; + if (elem->store_prop[j] == OTHER_PROP) + elem_data = *other_ptr; + else + elem_data = (char *)elem_ptr; + if (prop->is_list) { + item = elem_data + prop->count_offset; + item_size = ply_type_size[prop->count_internal]; + get_stored_item ((void *) item, prop->count_internal, + &int_val, &uint_val, &double_val); + write_binary_item (fp, plyfile->file_type, int_val, uint_val, + double_val, prop->count_external, native_binary_type); + list_count = uint_val; + item_ptr = (char **) (elem_data + prop->offset); + item = item_ptr[0]; + item_size = ply_type_size[prop->internal_type]; + for (k = 0; k < list_count; k++) { + get_stored_item ((void *) item, prop->internal_type, + &int_val, &uint_val, &double_val); + write_binary_item (fp, plyfile->file_type, int_val, uint_val, + double_val, prop->external_type, native_binary_type); + item += item_size; + } + } + else { + item = elem_data + prop->offset; + item_size = ply_type_size[prop->internal_type]; + get_stored_item ((void *) item, prop->internal_type, + &int_val, &uint_val, &double_val); + write_binary_item (fp, plyfile->file_type, int_val, uint_val, + double_val, prop->external_type, native_binary_type); + } + } + + } +} + + +/****************************************************************************** +Specify a comment that will be written in the header. + +Entry: + plyfile - file identifier + comment - the comment to be written +******************************************************************************/ + +inline void ply_put_comment(PlyFile *plyfile, char *comment) +{ + /* (re)allocate space for new comment */ + if (plyfile->num_comments == 0) + plyfile->comments = (char **) myalloc (sizeof (char *)); + else + plyfile->comments = (char **) realloc (plyfile->comments, + sizeof (char *) * (plyfile->num_comments + 1)); + + /* add comment to list */ + plyfile->comments[plyfile->num_comments] = strdup (comment); + plyfile->num_comments++; +} + + +/****************************************************************************** +Specify a piece of object information (arbitrary text) that will be written +in the header. + +Entry: + plyfile - file identifier + obj_info - the text information to be written +******************************************************************************/ + +inline void ply_put_obj_info(PlyFile *plyfile, char *obj_info) +{ + /* (re)allocate space for new info */ + if (plyfile->num_obj_info == 0) + plyfile->obj_info = (char **) myalloc (sizeof (char *)); + else + plyfile->obj_info = (char **) realloc (plyfile->obj_info, + sizeof (char *) * (plyfile->num_obj_info + 1)); + + /* add info to list */ + plyfile->obj_info[plyfile->num_obj_info] = strdup (obj_info); + plyfile->num_obj_info++; +} + + + + + + + +/*************/ +/* Reading */ +/*************/ + + + +/****************************************************************************** +Given a file pointer, get ready to read PLY data from the file. + +Entry: + fp - the given file pointer + +Exit: + nelems - number of elements in object + elem_names - list of element names + returns a pointer to a PlyFile, used to refer to this file, or NULL if error +******************************************************************************/ + +inline PlyFile *ply_read(FILE *fp, int *nelems, char ***elem_names) +{ + int i,j; + PlyFile *plyfile; + int nwords; + char **words; + char **elist; + PlyElement *elem; + char *orig_line; + + /* check for NULL file pointer */ + if (fp == NULL) + return (NULL); + + int native_binary_type = -1; + int types_checked = 0; + + if (native_binary_type == -1) + native_binary_type = get_native_binary_type2(); + if (!types_checked) + types_checked = check_types(); + + /* create record for this object */ + + plyfile = (PlyFile *) myalloc (sizeof (PlyFile)); + plyfile->nelems = 0; + plyfile->comments = NULL; + plyfile->num_comments = 0; + plyfile->obj_info = NULL; + plyfile->num_obj_info = 0; + plyfile->fp = fp; + plyfile->other_elems = NULL; + + /* read and parse the file's header */ + + words = get_words (plyfile->fp, &nwords, &orig_line); + if (nwords == 0 || !words || !equal_strings (words[0], "ply")) + { + if (words) + free(words); + + + return (NULL); + } + + while (words) { + + /* parse words */ + + if (equal_strings (words[0], "format")) { + if (nwords != 3) { + free(words); + return (NULL); + } + if (equal_strings (words[1], "ascii")) + plyfile->file_type = PLY_ASCII; + else if (equal_strings (words[1], "binary_big_endian")) + plyfile->file_type = PLY_BINARY_BE; + else if (equal_strings (words[1], "binary_little_endian")) + plyfile->file_type = PLY_BINARY_LE; + else { + free(words); + return (NULL); + } + plyfile->version = atof (words[2]); + } + else if (equal_strings (words[0], "element")) + add_element (plyfile, words); + else if (equal_strings (words[0], "property")) + add_property (plyfile, words); + else if (equal_strings (words[0], "comment")) + add_comment (plyfile, orig_line); + else if (equal_strings (words[0], "obj_info")) + add_obj_info (plyfile, orig_line); + else if (equal_strings (words[0], "end_header")) { + free(words); + break; + } + + /* free up words space */ + free (words); + + words = get_words (plyfile->fp, &nwords, &orig_line); + } + + /* create tags for each property of each element, to be used */ + /* later to say whether or not to store each property for the user */ + + for (i = 0; i < plyfile->nelems; i++) { + elem = plyfile->elems[i]; + elem->store_prop = (char *) myalloc (sizeof (char) * elem->nprops); + for (j = 0; j < elem->nprops; j++) + elem->store_prop[j] = DONT_STORE_PROP; + elem->other_offset = NO_OTHER_PROPS; /* no "other" props by default */ + } + + /* set return values about the elements */ + + elist = (char **) myalloc (sizeof (char *) * plyfile->nelems); + for (i = 0; i < plyfile->nelems; i++) + elist[i] = strdup (plyfile->elems[i]->name); + + *elem_names = elist; + *nelems = plyfile->nelems; + + /* return a pointer to the file's information */ + + return (plyfile); +} + + +/****************************************************************************** +Open a polygon file for reading. + +Entry: + filename - name of file to read from + +Exit: + nelems - number of elements in object + elem_names - list of element names + file_type - file type, either ascii or binary + version - version number of PLY file + returns a file identifier, used to refer to this file, or NULL if error +******************************************************************************/ + +inline PlyFile *ply_open_for_reading( + char *filename, + int *nelems, + char ***elem_names, + int *file_type, + float *version +) +{ + FILE *fp; + PlyFile *plyfile; + //char *name; + + + + /* tack on the extension .ply, if necessary */ + + // removing below, to handle also macintosh alias filenames + //name = (char *) myalloc (sizeof (char) * (strlen (filename) + 5)); + //strcpy (name, filename); + //if (strlen (name) < 4 || + // strcmp (name + strlen (name) - 4, ".ply") != 0) + // strcat (name, ".ply"); + + /* open the file for reading */ + + //fp = fopen (name, "r"); + + //opening file in binary, ascii data can be read in binary with get_words + fp = fopen (filename, "rb"); + + if (fp == NULL) + return (NULL); + + /* create the PlyFile data structure */ + + plyfile = ply_read (fp, nelems, elem_names); + + /* determine the file type and version */ + + *file_type = plyfile->file_type; + *version = plyfile->version; + + /* return a pointer to the file's information */ + + return (plyfile); +} + + +/****************************************************************************** +Get information about a particular element. + +Entry: + plyfile - file identifier + elem_name - name of element to get information about + +Exit: + nelems - number of elements of this type in the file + nprops - number of properties + returns a list of properties, or NULL if the file doesn't contain that elem +******************************************************************************/ + +inline PlyProperty **ply_get_element_description( + PlyFile *plyfile, + const char *elem_name, + int *nelems, + int *nprops +) +{ + int i; + PlyElement *elem; + PlyProperty *prop; + PlyProperty **prop_list; + + /* find information about the element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) + return (NULL); + + *nelems = elem->num; + *nprops = elem->nprops; + + /* make a copy of the element's property list */ + prop_list = (PlyProperty **) myalloc (sizeof (PlyProperty *) * elem->nprops); + for (i = 0; i < elem->nprops; i++) { + prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + copy_property (prop, elem->props[i]); + prop_list[i] = prop; + } + + /* return this duplicate property list */ + return (prop_list); +} + + +/****************************************************************************** +Specify which properties of an element are to be returned. This should be +called before a call to the routine ply_get_element(). + +Entry: + plyfile - file identifier + elem_name - which element we're talking about + nprops - number of properties + prop_list - list of properties +******************************************************************************/ + +inline void ply_get_element_setup( + PlyFile *plyfile, + const char *elem_name, + int nprops, + PlyProperty *prop_list +) +{ + int i; + PlyElement *elem; + PlyProperty *prop; + int index; + + /* find information about the element */ + elem = find_element (plyfile, elem_name); + plyfile->which_elem = elem; + + /* deposit the property information into the element's description */ + for (i = 0; i < nprops; i++) { + + /* look for actual property */ + prop = find_property (elem, prop_list[i].name, &index); + if (prop == NULL) { + fprintf (stderr, "Warning: Can't find property '%s' in element '%s'\n", + prop_list[i].name, elem_name); + continue; + } + + /* store its description */ + prop->internal_type = prop_list[i].internal_type; + prop->offset = prop_list[i].offset; + prop->count_internal = prop_list[i].count_internal; + prop->count_offset = prop_list[i].count_offset; + + /* specify that the user wants this property */ + elem->store_prop[index] = STORE_PROP; + } +} + + +/****************************************************************************** +Specify a property of an element that is to be returned. This should be +called (usually multiple times) before a call to the routine ply_get_element(). +This routine should be used in preference to the less flexible old routine +called ply_get_element_setup(). + +Entry: + plyfile - file identifier + elem_name - which element we're talking about + prop - property to add to those that will be returned +******************************************************************************/ + +inline void ply_get_property( + PlyFile *plyfile, + const char *elem_name, + PlyProperty *prop +) +{ + PlyElement *elem; + PlyProperty *prop_ptr; + int index; + + /* find information about the element */ + elem = find_element (plyfile, elem_name); + plyfile->which_elem = elem; + + /* deposit the property information into the element's description */ + + prop_ptr = find_property (elem, prop->name, &index); + if (prop_ptr == NULL) { + fprintf (stderr, "Warning: Can't find property '%s' in element '%s'\n", + prop->name, elem_name); + return; + } + prop_ptr->internal_type = prop->internal_type; + prop_ptr->offset = prop->offset; + prop_ptr->count_internal = prop->count_internal; + prop_ptr->count_offset = prop->count_offset; + + /* specify that the user wants this property */ + elem->store_prop[index] = STORE_PROP; +} + + +/****************************************************************************** +Read one element from the file. This routine assumes that we're reading +the type of element specified in the last call to the routine +ply_get_element_setup(). + +Entry: + plyfile - file identifier + elem_ptr - pointer to location where the element information should be put +******************************************************************************/ + +inline void ply_get_element(PlyFile *plyfile, void *elem_ptr, int *native_binary_type) +{ + if (plyfile->file_type == PLY_ASCII) + ascii_get_element (plyfile, (char *) elem_ptr); + else + binary_get_element (plyfile, (char *) elem_ptr, native_binary_type); +} + + +/****************************************************************************** +Extract the comments from the header information of a PLY file. + +Entry: + plyfile - file identifier + +Exit: + num_comments - number of comments returned + returns a pointer to a list of comments +******************************************************************************/ + +inline char **ply_get_comments(PlyFile *plyfile, int *num_comments) +{ + *num_comments = plyfile->num_comments; + return (plyfile->comments); +} + + +/****************************************************************************** +Extract the object information (arbitrary text) from the header information +of a PLY file. + +Entry: + plyfile - file identifier + +Exit: + num_obj_info - number of lines of text information returned + returns a pointer to a list of object info lines +******************************************************************************/ + +inline char **ply_get_obj_info(PlyFile *plyfile, int *num_obj_info) +{ + *num_obj_info = plyfile->num_obj_info; + return (plyfile->obj_info); +} + + +/****************************************************************************** +Make ready for "other" properties of an element-- those properties that +the user has not explicitly asked for, but that are to be stashed away +in a special structure to be carried along with the element's other +information. + +Entry: + plyfile - file identifier + elem - element for which we want to save away other properties +******************************************************************************/ + +inline void setup_other_props(PlyElement *elem) +{ + int i; + PlyProperty *prop; + int size = 0; + int type_size; + + /* Examine each property in decreasing order of size. */ + /* We do this so that all data types will be aligned by */ + /* word, half-word, or whatever within the structure. */ + int ply_type_size[] = { + 0, 1, 2, 4, 1, 2, 4, 4, 8 + }; + + for (type_size = 8; type_size > 0; type_size /= 2) { + + /* add up the space taken by each property, and save this information */ + /* away in the property descriptor */ + + for (i = 0; i < elem->nprops; i++) { + + /* don't bother with properties we've been asked to store explicitly */ + if (elem->store_prop[i]) + continue; + + prop = elem->props[i]; + + /* internal types will be same as external */ + prop->internal_type = prop->external_type; + prop->count_internal = prop->count_external; + + /* check list case */ + if (prop->is_list) { + + /* pointer to list */ + if (type_size == sizeof (void *)) { + prop->offset = size; + size += sizeof (void *); /* always use size of a pointer here */ + } + + /* count of number of list elements */ + if (type_size == ply_type_size[prop->count_external]) { + prop->count_offset = size; + size += ply_type_size[prop->count_external]; + } + } + /* not list */ + else if (type_size == ply_type_size[prop->external_type]) { + prop->offset = size; + size += ply_type_size[prop->external_type]; + } + } + + } + + /* save the size for the other_props structure */ + elem->other_size = size; +} + + +/****************************************************************************** +Specify that we want the "other" properties of an element to be tucked +away within the user's structure. The user needn't be concerned for how +these properties are stored. + +Entry: + plyfile - file identifier + elem_name - name of element that we want to store other_props in + offset - offset to where other_props will be stored inside user's structure + +Exit: + returns pointer to structure containing description of other_props +******************************************************************************/ + +inline PlyOtherProp *ply_get_other_properties( + PlyFile *plyfile, + const char *elem_name, + int offset +) +{ + int i; + PlyElement *elem; + PlyOtherProp *other; + PlyProperty *prop; + int nprops; + + /* find information about the element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf (stderr, "ply_get_other_properties: Can't find element '%s'\n", + elem_name); + return (NULL); + } + + /* remember that this is the "current" element */ + plyfile->which_elem = elem; + + /* save the offset to where to store the other_props */ + elem->other_offset = offset; + + /* place the appropriate pointers, etc. in the element's property list */ + setup_other_props (elem); + + /* create structure for describing other_props */ + other = (PlyOtherProp *) myalloc (sizeof (PlyOtherProp)); + other->name = strdup (elem_name); +#if 0 + if (elem->other_offset == NO_OTHER_PROPS) { + other->size = 0; + other->props = NULL; + other->nprops = 0; + return (other); + } +#endif + other->size = elem->other_size; + other->props = (PlyProperty **) myalloc (sizeof(PlyProperty) * elem->nprops); + + /* save descriptions of each "other" property */ + nprops = 0; + for (i = 0; i < elem->nprops; i++) { + if (elem->store_prop[i]) + continue; + prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + copy_property (prop, elem->props[i]); + other->props[nprops] = prop; + nprops++; + } + other->nprops = nprops; + +#if 1 + /* set other_offset pointer appropriately if there are NO other properties */ + if (other->nprops == 0) { + elem->other_offset = NO_OTHER_PROPS; + } +#endif + + /* return structure */ + return (other); +} + + + + +/*************************/ +/* Other Element Stuff */ +/*************************/ + + + + +/****************************************************************************** +Grab all the data for an element that a user does not want to explicitly +read in. + +Entry: + plyfile - pointer to file + elem_name - name of element whose data is to be read in + elem_count - number of instances of this element stored in the file + +Exit: + returns pointer to ALL the "other" element data for this PLY file +******************************************************************************/ + +inline PlyOtherElems *ply_get_other_element ( + PlyFile *plyfile, + char *elem_name, + int elem_count +) +{ + int i; + PlyElement *elem; + PlyOtherElems *other_elems; + OtherElem *other; + + /* look for appropriate element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf (stderr, + "ply_get_other_element: can't find element '%s'\n", elem_name); + exit (-1); + } + + /* create room for the new "other" element, initializing the */ + /* other data structure if necessary */ + + if (plyfile->other_elems == NULL) { + plyfile->other_elems = (PlyOtherElems *) myalloc (sizeof (PlyOtherElems)); + other_elems = plyfile->other_elems; + other_elems->other_list = (OtherElem *) myalloc (sizeof (OtherElem)); + other = &(other_elems->other_list[0]); + other_elems->num_elems = 1; + } + else { + other_elems = plyfile->other_elems; + other_elems->other_list = (OtherElem *) realloc (other_elems->other_list, + sizeof (OtherElem) * (other_elems->num_elems + 1)); + other = &(other_elems->other_list[other_elems->num_elems]); + other_elems->num_elems++; + } + + /* count of element instances in file */ + other->elem_count = elem_count; + + /* save name of element */ + other->elem_name = strdup (elem_name); + + /* create a list to hold all the current elements */ + other->other_data = (OtherData **) + malloc (sizeof (OtherData *) * other->elem_count); + + /* set up for getting elements */ + other->other_props = ply_get_other_properties (plyfile, elem_name, + offsetof(OtherData,other_props)); + + /* grab all these elements */ + int native_binary_type = get_native_binary_type2(); + for (i = 0; i < other->elem_count; i++) { + /* grab and element from the file */ + other->other_data[i] = (OtherData *) malloc (sizeof (OtherData)); + ply_get_element (plyfile, (void *) other->other_data[i], &native_binary_type); + } + + /* return pointer to the other elements data */ + return (other_elems); +} + + +/****************************************************************************** +Pass along a pointer to "other" elements that we want to save in a given +PLY file. These other elements were presumably read from another PLY file. + +Entry: + plyfile - file pointer in which to store this other element info + other_elems - info about other elements that we want to store +******************************************************************************/ + +inline void ply_describe_other_elements ( + PlyFile *plyfile, + PlyOtherElems *other_elems +) +{ + int i; + OtherElem *other; + PlyElement *elem; + + /* ignore this call if there is no other element */ + if (other_elems == NULL) + return; + + /* save pointer to this information */ + plyfile->other_elems = other_elems; + + /* describe the other properties of this element */ + /* store them in the main element list as elements with + only other properties */ + + REALLOCN(plyfile->elems, PlyElement *, + plyfile->nelems, plyfile->nelems + other_elems->num_elems); + for (i = 0; i < other_elems->num_elems; i++) { + other = &(other_elems->other_list[i]); + elem = (PlyElement *) myalloc (sizeof (PlyElement)); + plyfile->elems[plyfile->nelems++] = elem; + elem->name = strdup (other->elem_name); + elem->num = other->elem_count; + elem->nprops = 0; + ply_describe_other_properties (plyfile, other->other_props, + offsetof(OtherData,other_props)); + } +} + + +/****************************************************************************** +Write out the "other" elements specified for this PLY file. + +Entry: + plyfile - pointer to PLY file to write out other elements for +******************************************************************************/ + +inline void ply_put_other_elements (PlyFile *plyfile, int *native_binary_type) +{ + int i,j; + OtherElem *other; + + /* make sure we have other elements to write */ + if (plyfile->other_elems == NULL) + return; + + /* write out the data for each "other" element */ + + for (i = 0; i < plyfile->other_elems->num_elems; i++) { + + other = &(plyfile->other_elems->other_list[i]); + ply_put_element_setup (plyfile, other->elem_name); + + /* write out each instance of the current element */ + for (j = 0; j < other->elem_count; j++) + ply_put_element (plyfile, (void *) other->other_data[j], native_binary_type); + } +} + + +/****************************************************************************** +Free up storage used by an "other" elements data structure. + +Entry: + other_elems - data structure to free up +******************************************************************************/ + +inline void ply_free_other_elements (PlyOtherElems *other_elems) +{ + // Alec: + //other_elems = other_elems; + delete(other_elems); +} + + + +/*******************/ +/* Miscellaneous */ +/*******************/ + + + +/****************************************************************************** +Close a PLY file. + +Entry: + plyfile - identifier of file to close +******************************************************************************/ + +inline void ply_close(PlyFile *plyfile) +{ + fclose (plyfile->fp); + // Alec: + plyfile->fp = NULL; + + /* free up memory associated with the PLY file */ + free (plyfile); +} + + +/****************************************************************************** +Get version number and file type of a PlyFile. + +Entry: + ply - pointer to PLY file + +Exit: + version - version of the file + file_type - PLY_ASCII, PLY_BINARY_BE, or PLY_BINARY_LE +******************************************************************************/ + +inline void ply_get_info(PlyFile *ply, float *version, int *file_type) +{ + if (ply == NULL) + return; + + *version = ply->version; + *file_type = ply->file_type; +} + + +/****************************************************************************** +Compare two strings. Returns 1 if they are the same, 0 if not. +******************************************************************************/ + +inline int equal_strings(const char *s1, const char *s2) +{ + + while (*s1 && *s2) + if (*s1++ != *s2++) + return (0); + + if (*s1 != *s2) + return (0); + else + return (1); +} + + +/****************************************************************************** +Find an element from the element list of a given PLY object. + +Entry: + plyfile - file id for PLY file + element - name of element we're looking for + +Exit: + returns the element, or NULL if not found +******************************************************************************/ + +inline PlyElement *find_element(PlyFile *plyfile, const char *element) +{ + int i; + + for (i = 0; i < plyfile->nelems; i++) + if (equal_strings (element, plyfile->elems[i]->name)) + return (plyfile->elems[i]); + + return (NULL); +} + + +/****************************************************************************** +Find a property in the list of properties of a given element. + +Entry: + elem - pointer to element in which we want to find the property + prop_name - name of property to find + +Exit: + index - index to position in list + returns a pointer to the property, or NULL if not found +******************************************************************************/ + +inline PlyProperty *find_property(PlyElement *elem, const char *prop_name, int *index) +{ + int i; + + for (i = 0; i < elem->nprops; i++) + if (equal_strings (prop_name, elem->props[i]->name)) { + *index = i; + return (elem->props[i]); + } + + *index = -1; + return (NULL); +} + + +/****************************************************************************** +Read an element from an ascii file. + +Entry: + plyfile - file identifier + elem_ptr - pointer to element +******************************************************************************/ + +inline void ascii_get_element(PlyFile *plyfile, char *elem_ptr) +{ + int ply_type_size[] = { + 0, 1, 2, 4, 1, 2, 4, 4, 8 + }; + + int j,k; + PlyElement *elem; + PlyProperty *prop; + char **words; + int nwords; + int which_word; + char *elem_data,*item=NULL; + char *item_ptr; + int item_size; + int int_val; + unsigned int uint_val; + double double_val; + int list_count; + int store_it; + char **store_array; + char *orig_line; + char *other_data=NULL; + int other_flag; + + /* the kind of element we're reading currently */ + elem = plyfile->which_elem; + + /* do we need to setup for other_props? */ + + if (elem->other_offset != NO_OTHER_PROPS) { + char **ptr; + other_flag = 1; + /* make room for other_props */ + other_data = (char *) myalloc (elem->other_size); + /* store pointer in user's structure to the other_props */ + ptr = (char **) (elem_ptr + elem->other_offset); + *ptr = other_data; + } + else + other_flag = 0; + + /* read in the element */ + + words = get_words (plyfile->fp, &nwords, &orig_line); + if (words == NULL) { + fprintf (stderr, "ply_get_element: unexpected end of file\n"); + exit (-1); + } + + which_word = 0; + + for (j = 0; j < elem->nprops; j++) { + + prop = elem->props[j]; + store_it = (elem->store_prop[j] | other_flag); + + /* store either in the user's structure or in other_props */ + // if (elem->store_prop[j]) + elem_data = elem_ptr; + //else + //elem_data = other_data; + + if (prop->is_list) { /* a list */ + + /* get and store the number of items in the list */ + get_ascii_item (words[which_word++], prop->count_external, + &int_val, &uint_val, &double_val); + if (store_it) { + item = elem_data + prop->count_offset; + store_item(item, prop->count_internal, int_val, uint_val, double_val); + } + + /* allocate space for an array of items and store a ptr to the array */ + list_count = int_val; + item_size = ply_type_size[prop->internal_type]; + store_array = (char **) (elem_data + prop->offset); + + if (list_count == 0) { + if (store_it) + *store_array = NULL; + } + else { + if (store_it) { + item_ptr = (char *) myalloc (sizeof (char) * item_size * list_count); + + item = item_ptr; + *store_array = item_ptr; + } + + /* read items and store them into the array */ + for (k = 0; k < list_count; k++) { + get_ascii_item (words[which_word++], prop->external_type, + &int_val, &uint_val, &double_val); + if (store_it) { + store_item (item, prop->internal_type, + int_val, uint_val, double_val); + item += item_size; + } + } + } + + } + else { /* not a list */ + get_ascii_item (words[which_word++], prop->external_type, + &int_val, &uint_val, &double_val); + if (store_it) { + item = elem_data + prop->offset; + store_item (item, prop->internal_type, int_val, uint_val, double_val); + } + } + + } + + free (words); +} + + +/****************************************************************************** +Read an element from a binary file. + +Entry: + plyfile - file identifier + elem_ptr - pointer to an element +******************************************************************************/ + +inline void binary_get_element(PlyFile *plyfile, char *elem_ptr, int *native_binary_type) +{ + int j,k; + PlyElement *elem; + PlyProperty *prop; + FILE *fp = plyfile->fp; + char *elem_data,*item=NULL; + char *item_ptr; + int item_size; + int int_val; + unsigned int uint_val; + double double_val; + int list_count; + int store_it; + char **store_array; + char *other_data=NULL; + int other_flag; + + int ply_type_size[] = { + 0, 1, 2, 4, 1, 2, 4, 4, 8 + }; + + + /* the kind of element we're reading currently */ + elem = plyfile->which_elem; + + /* do we need to setup for other_props? */ + + if (elem->other_offset != NO_OTHER_PROPS) { + char **ptr; + other_flag = 1; + /* make room for other_props */ + other_data = (char *) myalloc (elem->other_size); + /* store pointer in user's structure to the other_props */ + ptr = (char **) (elem_ptr + elem->other_offset); + *ptr = other_data; + } + else + other_flag = 0; + + /* read in a number of elements */ + + for (j = 0; j < elem->nprops; j++) { + + prop = elem->props[j]; + store_it = (elem->store_prop[j] | other_flag); + + /* store either in the user's structure or in other_props */ +// if (elem->store_prop[j]) + elem_data = elem_ptr; +// else +// elem_data = other_data; + + if (prop->is_list) { /* a list */ + + /* get and store the number of items in the list */ + get_binary_item (fp, plyfile->file_type, prop->count_external, + &int_val, &uint_val, &double_val, native_binary_type); + if (store_it) { + item = elem_data + prop->count_offset; + store_item(item, prop->count_internal, int_val, uint_val, double_val); + } + + /* allocate space for an array of items and store a ptr to the array */ + list_count = int_val; + + item_size = ply_type_size[prop->internal_type]; + store_array = (char **) (elem_data + prop->offset); + if (list_count == 0) { + if (store_it) + *store_array = NULL; + } + else { + if (store_it) { + item_ptr = (char *) myalloc (sizeof (char) * item_size * list_count); + + item = item_ptr; + *store_array = item_ptr; + } + + // read items and store them into the array + for (k = 0; k < list_count; k++) { + get_binary_item (fp, plyfile->file_type, prop->external_type, + &int_val, &uint_val, &double_val, native_binary_type); + if (store_it) { + store_item (item, prop->internal_type, + int_val, uint_val, double_val); + item += item_size; + } + } + + + + } + + } + else { /* not a list */ + get_binary_item (fp, plyfile->file_type, prop->external_type, + &int_val, &uint_val, &double_val, native_binary_type); + if (store_it) { + item = elem_data + prop->offset; + store_item (item, prop->internal_type, int_val, uint_val, double_val); + } + } + + } +} + + +/****************************************************************************** +Write to a file the word that represents a PLY data type. + +Entry: + fp - file pointer + code - code for type +******************************************************************************/ + +inline void write_scalar_type (FILE *fp, int code) +{ + /* make sure this is a valid code */ + + if (code <= PLY_START_TYPE || code >= PLY_END_TYPE) { + fprintf (stderr, "write_scalar_type: bad data code = %d\n", code); + exit (-1); + } + + /* write the code to a file */ + const char *type_names[] = { + "invalid", + "char", "short", "int", + "uchar", "ushort", "uint", + "float", "double", + }; + + + fprintf (fp, "%s", type_names[code]); +} + +/****************************************************************************** + Reverse the order in an array of bytes. This is the conversion from big + endian to little endian and vice versa + +Entry: + bytes - array of bytes to reverse (in place) + num_bytes - number of bytes in array +******************************************************************************/ + +inline void swap_bytes(char *bytes, int num_bytes) +{ + int i; + char temp; + + for (i=0; i < num_bytes/2; i++) + { + temp = bytes[i]; + bytes[i] = bytes[(num_bytes-1)-i]; + bytes[(num_bytes-1)-i] = temp; + } +} + +/****************************************************************************** + Find out if this machine is big endian or little endian + + Exit: + set global variable, native_binary_type = + either PLY_BINARY_BE or PLY_BINARY_LE + +******************************************************************************/ + +inline void get_native_binary_type(int *native_binary_type) +{ + typedef union + { + int int_value; + char byte_values[sizeof(int)]; + } endian_test_type; + + + endian_test_type test; + + test.int_value = 0; + test.int_value = 1; + if (test.byte_values[0] == 1) + *native_binary_type = PLY_BINARY_LE; + else if (test.byte_values[sizeof(int)-1] == 1) + *native_binary_type = PLY_BINARY_BE; + else + { + fprintf(stderr, "ply: Couldn't determine machine endianness.\n"); + fprintf(stderr, "ply: Exiting...\n"); + exit(1); + } +} + +inline int get_native_binary_type2() +{ + typedef union + { + int int_value; + char byte_values[sizeof(int)]; + } endian_test_type; + + + endian_test_type test; + + test.int_value = 0; + test.int_value = 1; + if (test.byte_values[0] == 1) + return PLY_BINARY_LE; + else if (test.byte_values[sizeof(int)-1] == 1) + return PLY_BINARY_BE; + else + { + fprintf(stderr, "ply: Couldn't determine machine endianness.\n"); + fprintf(stderr, "ply: Exiting...\n"); + exit(1); + } +} + +/****************************************************************************** + Verify that all the native types are the sizes we need + + +******************************************************************************/ + +inline int check_types() +{ + int ply_type_size[] = { + 0, 1, 2, 4, 1, 2, 4, 4, 8 + }; + + if ((ply_type_size[PLY_CHAR] != sizeof(char)) || + (ply_type_size[PLY_SHORT] != sizeof(short)) || + (ply_type_size[PLY_INT] != sizeof(int)) || + (ply_type_size[PLY_UCHAR] != sizeof(unsigned char)) || + (ply_type_size[PLY_USHORT] != sizeof(unsigned short)) || + (ply_type_size[PLY_UINT] != sizeof(unsigned int)) || + (ply_type_size[PLY_FLOAT] != sizeof(float)) || + (ply_type_size[PLY_DOUBLE] != sizeof(double))) + { + fprintf(stderr, "ply: Type sizes do not match built-in types\n"); + fprintf(stderr, "ply: Exiting...\n"); + exit(1); + } + + return 1; +} + +/****************************************************************************** +Get a text line from a file and break it up into words. + +IMPORTANT: The calling routine call "free" on the returned pointer once +finished with it. + +Entry: + fp - file to read from + +Exit: + nwords - number of words returned + orig_line - the original line of characters + returns a list of words from the line, or NULL if end-of-file +******************************************************************************/ + +inline char **get_words(FILE *fp, int *nwords, char **orig_line) +{ + #define BIG_STRING 4096 + char str[BIG_STRING]; + char str_copy[BIG_STRING]; + char **words; + int max_words = 10; + int num_words = 0; + char *ptr,*ptr2; + char *result; + + fpos_t pos; //keep track of file pointer + int nbytes; + int nonUNIX; + nonUNIX=0; + nbytes=0; + fgetpos(fp, &pos); + + words = (char **) myalloc (sizeof (char *) * max_words); + + /* read in a line */ + result = fgets (str, BIG_STRING, fp); + if (result == NULL) { + *nwords = 0; + *orig_line = NULL; + return (NULL); + } + + /* convert line-feed and tabs into spaces */ + /* (this guarantees that there will be a space before the */ + /* null character at the end of the string) */ + + str[BIG_STRING-2] = ' '; + str[BIG_STRING-1] = '\0'; + + for (ptr = str, ptr2 = str_copy; *ptr != '\0'; ptr++, ptr2++) { + *ptr2 = *ptr; + nbytes++; + if (*ptr == '\t') { + *ptr = ' '; + *ptr2 = ' '; + } + else if (*ptr == '\n') { + *ptr = ' '; //has to have a space, to be caught later when grouping words + *ptr2 = '\0'; + break; + } + else if (*ptr == '\r') + { //MAC line break + nonUNIX=1; + if(*(ptr+1)=='\n') //actuall PC line break + { + nbytes++; + } + + *ptr = ' '; + + *(ptr+1) = '\0'; //when reading mac, best end string here + *ptr2 = '\0'; //note a pc \r is followed by \n + + break; + } + } + + + /*check to see if a PC or MAC header was detected instead of UNIX*/ + if(nonUNIX==1) + { + fsetpos(fp, &pos); + fseek(fp, nbytes, SEEK_CUR); + } + + /* find the words in the line */ + + ptr = str; + while (*ptr != '\0') { + + /* jump over leading spaces */ + while (*ptr == ' ') + ptr++; + + /* break if we reach the end */ + if (*ptr == '\0') + break; + + /* save pointer to beginning of word */ + if (num_words >= max_words) { + max_words += 10; + words = (char **) realloc (words, sizeof (char *) * max_words); + } + words[num_words++] = ptr; + + /* jump over non-spaces */ + while (*ptr != ' ') + ptr++; + + /* place a null character here to mark the end of the word */ + *ptr++ = '\0'; + } + + /* return the list of words */ + *nwords = num_words; + *orig_line = str_copy; + return (words); +} + +/* +char **get_words(FILE *fp, int *nwords, char **orig_line) +{ +#define BIG_STRING 4096 + static char str[BIG_STRING]; + static char str_copy[BIG_STRING]; + char **words; + int max_words = 10; + int num_words = 0; + char *ptr,*ptr2; + char *result; + + words = (char **) myalloc (sizeof (char *) * max_words); + + // read in a line + result = fgets (str, BIG_STRING, fp); + if (result == NULL) { + *nwords = 0; + *orig_line = NULL; + return (NULL); + } + + // convert line-feed and tabs into spaces + // (this guarantees that there will be a space before the + // null character at the end of the string) + + str[BIG_STRING-2] = ' '; + str[BIG_STRING-1] = '\0'; + + for (ptr = str, ptr2 = str_copy; *ptr != '\0'; ptr++, ptr2++) { + *ptr2 = *ptr; + if (*ptr == '\t') { + *ptr = ' '; + *ptr2 = ' '; + } + else if (*ptr == '\n') { + *ptr = ' '; + *ptr2 = '\0'; + break; + } + else if (*ptr == '\r') { + *ptr = '\0'; + *ptr2 = '\0'; //note don't break yet, on a pc \r is followed by \n + } + } + + // find the words in the line + + ptr = str; + while (*ptr != '\0') { + + // jump over leading spaces + while (*ptr == ' ') + ptr++; + + // break if we reach the end + if (*ptr == '\0') + break; + + // save pointer to beginning of word + if (num_words >= max_words) { + max_words += 10; + words = (char **) realloc (words, sizeof (char *) * max_words); + } + words[num_words++] = ptr; + + // jump over non-spaces + while (*ptr != ' ') + ptr++; + + // place a null character here to mark the end of the word + *ptr++ = '\0'; + } + + // return the list of words + *nwords = num_words; + *orig_line = str_copy; + return (words); +}*/ + +/****************************************************************************** +Return the value of an item, given a pointer to it and its type. + +Entry: + item - pointer to item + type - data type that "item" points to + +Exit: + returns a double-precision float that contains the value of the item +******************************************************************************/ + +inline double get_item_value(char *item, int type) +{ + unsigned char *puchar; + char *pchar; + short int *pshort; + unsigned short int *pushort; + int *pint; + unsigned int *puint; + float *pfloat; + double *pdouble; + int int_value; + unsigned int uint_value; + double double_value; + + switch (type) { + case PLY_CHAR: + pchar = (char *) item; + int_value = *pchar; + return ((double) int_value); + case PLY_UCHAR: + puchar = (unsigned char *) item; + int_value = *puchar; + return ((double) int_value); + case PLY_SHORT: + pshort = (short int *) item; + int_value = *pshort; + return ((double) int_value); + case PLY_USHORT: + pushort = (unsigned short int *) item; + int_value = *pushort; + return ((double) int_value); + case PLY_INT: + pint = (int *) item; + int_value = *pint; + return ((double) int_value); + case PLY_UINT: + puint = (unsigned int *) item; + uint_value = *puint; + return ((double) uint_value); + case PLY_FLOAT: + pfloat = (float *) item; + double_value = *pfloat; + return (double_value); + case PLY_DOUBLE: + pdouble = (double *) item; + double_value = *pdouble; + return (double_value); + default: + fprintf (stderr, "get_item_value: bad type = %d\n", type); + exit (-1); + } +} + + +/****************************************************************************** +Write out an item to a file as raw binary bytes. + +Entry: + fp - file to write to + int_val - integer version of item + uint_val - unsigned integer version of item + double_val - double-precision float version of item + type - data type to write out +******************************************************************************/ + +inline void write_binary_item( + FILE *fp, + int file_type, + int int_val, + unsigned int uint_val, + double double_val, + int type, + int *native_binary_type +) +{ + unsigned char uchar_val; + char char_val; + unsigned short ushort_val; + short short_val; + float float_val; + void *value; + + switch (type) { + case PLY_CHAR: + char_val = int_val; + value = &char_val; + break; + case PLY_SHORT: + short_val = int_val; + value = &short_val; + break; + case PLY_INT: + value = &int_val; + break; + case PLY_UCHAR: + uchar_val = uint_val; + value = &uchar_val; + break; + case PLY_USHORT: + ushort_val = uint_val; + value = &ushort_val; + break; + case PLY_UINT: + value = &uint_val; + break; + case PLY_FLOAT: + float_val = double_val; + value = &float_val; + break; + case PLY_DOUBLE: + value = &double_val; + break; + default: + fprintf (stderr, "write_binary_item: bad type = %d\n", type); + exit (-1); + } + int ply_type_size[] = { + 0, 1, 2, 4, 1, 2, 4, 4, 8 + }; + + if ((file_type != *native_binary_type) && (ply_type_size[type] > 1)) + swap_bytes((char *)value, ply_type_size[type]); + + if (fwrite (value, ply_type_size[type], 1, fp) != 1) + { + fprintf(stderr, "PLY ERROR: fwrite() failed -- aborting.\n"); + exit(1); + } +} + + +/****************************************************************************** +Write out an item to a file as ascii characters. + +Entry: + fp - file to write to + int_val - integer version of item + uint_val - unsigned integer version of item + double_val - double-precision float version of item + type - data type to write out +******************************************************************************/ + +inline void write_ascii_item( + FILE *fp, + int int_val, + unsigned int uint_val, + double double_val, + int type +) +{ + switch (type) { + case PLY_CHAR: + case PLY_SHORT: + case PLY_INT: + if (fprintf (fp, "%d ", int_val) <= 0) + { + fprintf(stderr, "PLY ERROR: fprintf() failed -- aborting.\n"); + exit(1); + } + break; + case PLY_UCHAR: + case PLY_USHORT: + case PLY_UINT: + if (fprintf (fp, "%u ", uint_val) <= 0) + { + fprintf(stderr, "PLY ERROR: fprintf() failed -- aborting.\n"); + exit(1); + } + break; + case PLY_FLOAT: + case PLY_DOUBLE: + if (fprintf (fp, "%g ", double_val) <= 0) + { + fprintf(stderr, "PLY ERROR: fprintf() failed -- aborting.\n"); + exit(1); + } + break; + default: + fprintf (stderr, "write_ascii_item: bad type = %d\n", type); + exit (-1); + } +} + + +/****************************************************************************** +Write out an item to a file as ascii characters. + +Entry: + fp - file to write to + item - pointer to item to write + type - data type that "item" points to + +Exit: + returns a double-precision float that contains the value of the written item +******************************************************************************/ + +inline double old_write_ascii_item(FILE *fp, char *item, int type) +{ + unsigned char *puchar; + char *pchar; + short int *pshort; + unsigned short int *pushort; + int *pint; + unsigned int *puint; + float *pfloat; + double *pdouble; + int int_value; + unsigned int uint_value; + double double_value; + + switch (type) { + case PLY_CHAR: + pchar = (char *) item; + int_value = *pchar; + fprintf (fp, "%d ", int_value); + return ((double) int_value); + case PLY_UCHAR: + puchar = (unsigned char *) item; + int_value = *puchar; + fprintf (fp, "%d ", int_value); + return ((double) int_value); + case PLY_SHORT: + pshort = (short int *) item; + int_value = *pshort; + fprintf (fp, "%d ", int_value); + return ((double) int_value); + case PLY_USHORT: + pushort = (unsigned short int *) item; + int_value = *pushort; + fprintf (fp, "%d ", int_value); + return ((double) int_value); + case PLY_INT: + pint = (int *) item; + int_value = *pint; + fprintf (fp, "%d ", int_value); + return ((double) int_value); + case PLY_UINT: + puint = (unsigned int *) item; + uint_value = *puint; + fprintf (fp, "%u ", uint_value); + return ((double) uint_value); + case PLY_FLOAT: + pfloat = (float *) item; + double_value = *pfloat; + fprintf (fp, "%g ", double_value); + return (double_value); + case PLY_DOUBLE: + pdouble = (double *) item; + double_value = *pdouble; + fprintf (fp, "%g ", double_value); + return (double_value); + default: + fprintf (stderr, "old_write_ascii_item: bad type = %d\n", type); + exit (-1); + } +} + + +/****************************************************************************** +Get the value of an item that is in memory, and place the result +into an integer, an unsigned integer and a double. + +Entry: + ptr - pointer to the item + type - data type supposedly in the item + +Exit: + int_val - integer value + uint_val - unsigned integer value + double_val - double-precision floating point value +******************************************************************************/ + +inline void get_stored_item( + void *ptr, + int type, + int *int_val, + unsigned int *uint_val, + double *double_val +) +{ + switch (type) { + case PLY_CHAR: + *int_val = *((char *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_UCHAR: + *uint_val = *((unsigned char *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_SHORT: + *int_val = *((short int *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_USHORT: + *uint_val = *((unsigned short int *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_INT: + *int_val = *((int *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_UINT: + *uint_val = *((unsigned int *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_FLOAT: + *double_val = *((float *) ptr); + *int_val = (int) *double_val; + *uint_val = (unsigned int) *double_val; + break; + case PLY_DOUBLE: + *double_val = *((double *) ptr); + *int_val = (int) *double_val; + *uint_val = (unsigned int) *double_val; + break; + default: + fprintf (stderr, "get_stored_item: bad type = %d\n", type); + exit (-1); + } +} + + +/****************************************************************************** +Get the value of an item from a binary file, and place the result +into an integer, an unsigned integer and a double. + +Entry: + fp - file to get item from + type - data type supposedly in the word + +Exit: + int_val - integer value + uint_val - unsigned integer value + double_val - double-precision floating point value +******************************************************************************/ + +inline void get_binary_item( + FILE *fp, + int file_type, + int type, + int *int_val, + unsigned int *uint_val, + double *double_val, + int *native_binary_type +) +{ + char c[8]; + void *ptr; + + ptr = (void *) c; + int ply_type_size[] = { + 0, 1, 2, 4, 1, 2, 4, 4, 8 + }; + + if (fread (ptr, ply_type_size[type], 1, fp) != 1) + { + fprintf(stderr, "PLY ERROR: fread() failed -- aborting.\n"); + exit(1); + } + + + if ((file_type != *native_binary_type) && (ply_type_size[type] > 1)) + swap_bytes((char *)ptr, ply_type_size[type]); + + switch (type) { + case PLY_CHAR: + *int_val = *((char *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_UCHAR: + *uint_val = *((unsigned char *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_SHORT: + *int_val = *((short int *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_USHORT: + *uint_val = *((unsigned short int *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_INT: + *int_val = *((int *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_UINT: + *uint_val = *((unsigned int *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_FLOAT: + *double_val = *((float *) ptr); + *int_val = (int) *double_val; + *uint_val = (unsigned int) *double_val; + break; + case PLY_DOUBLE: + *double_val = *((double *) ptr); + *int_val = (int) *double_val; + *uint_val = (unsigned int) *double_val; + break; + default: + fprintf (stderr, "get_binary_item: bad type = %d\n", type); + exit (-1); + } +} + + +/****************************************************************************** +Extract the value of an item from an ascii word, and place the result +into an integer, an unsigned integer and a double. + +Entry: + word - word to extract value from + type - data type supposedly in the word + +Exit: + int_val - integer value + uint_val - unsigned integer value + double_val - double-precision floating point value +******************************************************************************/ + +inline void get_ascii_item( + char *word, + int type, + int *int_val, + unsigned int *uint_val, + double *double_val +) +{ + switch (type) { + case PLY_CHAR: + case PLY_UCHAR: + case PLY_SHORT: + case PLY_USHORT: + case PLY_INT: + *int_val = atoi (word); + *uint_val = (unsigned int) *int_val; + *double_val = (double) *int_val; + break; + + case PLY_UINT: + *uint_val = strtol (word, (char **) NULL, 10); + *int_val = (int) *uint_val; + *double_val = (double) *uint_val; + break; + + case PLY_FLOAT: + case PLY_DOUBLE: + *double_val = atof (word); + *int_val = (int) *double_val; + *uint_val = (unsigned int) *double_val; + break; + + default: + fprintf (stderr, "get_ascii_item: bad type = %d\n", type); + exit (-1); + } +} + + +/****************************************************************************** +Store a value into a place being pointed to, guided by a data type. + +Entry: + item - place to store value + type - data type + int_val - integer version of value + uint_val - unsigned integer version of value + double_val - double version of value + +Exit: + item - pointer to stored value +******************************************************************************/ + +inline void store_item ( + char *item, + int type, + int int_val, + unsigned int uint_val, + double double_val +) +{ + unsigned char *puchar; + short int *pshort; + unsigned short int *pushort; + int *pint; + unsigned int *puint; + float *pfloat; + double *pdouble; + + switch (type) { + case PLY_CHAR: + *item = int_val; + break; + case PLY_UCHAR: + puchar = (unsigned char *) item; + *puchar = uint_val; + break; + case PLY_SHORT: + pshort = (short *) item; + *pshort = int_val; + break; + case PLY_USHORT: + pushort = (unsigned short *) item; + *pushort = uint_val; + break; + case PLY_INT: + pint = (int *) item; + *pint = int_val; + break; + case PLY_UINT: + puint = (unsigned int *) item; + *puint = uint_val; + break; + case PLY_FLOAT: + pfloat = (float *) item; + *pfloat = double_val; + break; + case PLY_DOUBLE: + pdouble = (double *) item; + *pdouble = double_val; + break; + default: + fprintf (stderr, "store_item: bad type = %d\n", type); + exit (-1); + } +} + + +/****************************************************************************** +Add an element to a PLY file descriptor. + +Entry: + plyfile - PLY file descriptor + words - list of words describing the element + nwords - number of words in the list +******************************************************************************/ + +inline void add_element (PlyFile *plyfile, char **words) +{ + PlyElement *elem; + + /* create the new element */ + elem = (PlyElement *) myalloc (sizeof (PlyElement)); + elem->name = strdup (words[1]); + elem->num = atoi (words[2]); + elem->nprops = 0; + + /* make room for new element in the object's list of elements */ + if (plyfile->nelems == 0) + plyfile->elems = (PlyElement **) myalloc (sizeof (PlyElement *)); + else + plyfile->elems = (PlyElement **) realloc (plyfile->elems, + sizeof (PlyElement *) * (plyfile->nelems + 1)); + + /* add the new element to the object's list */ + plyfile->elems[plyfile->nelems] = elem; + plyfile->nelems++; +} + + +/****************************************************************************** +Return the type of a property, given the name of the property. + +Entry: + name - name of property type + +Exit: + returns integer code for property, or 0 if not found +******************************************************************************/ + +inline int get_prop_type(char *type_name) +{ + int i; + const char *type_names[] = { + "invalid", + "char", "short", "int", + "uchar", "ushort", "uint", + "float", "double", + }; + + const char *alt_type_names[] = { + "invalid", + "int8", "int16", "int32", "uint8", "uint16", "uint32", "float32", "float64", + }; + + + for (i = PLY_START_TYPE + 1; i < PLY_END_TYPE; i++) + if (equal_strings (type_name, type_names[i])) + return (i); + + for (i = PLY_START_TYPE + 1; i < PLY_END_TYPE; i++) + if (equal_strings (type_name, alt_type_names[i])) + return (i); + + /* if we get here, we didn't find the type */ + return (0); +} + + +/****************************************************************************** +Add a property to a PLY file descriptor. + +Entry: + plyfile - PLY file descriptor + words - list of words describing the property + nwords - number of words in the list +******************************************************************************/ + +inline void add_property (PlyFile *plyfile, char **words) +{ + PlyProperty *prop; + PlyElement *elem; + + /* create the new property */ + + prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + + if (equal_strings (words[1], "list")) { /* is a list */ + prop->count_external = get_prop_type (words[2]); + prop->external_type = get_prop_type (words[3]); + prop->name = strdup (words[4]); + prop->is_list = 1; + } + else { /* not a list */ + prop->external_type = get_prop_type (words[1]); + prop->name = strdup (words[2]); + prop->is_list = 0; + } + + /* add this property to the list of properties of the current element */ + + elem = plyfile->elems[plyfile->nelems - 1]; + + if (elem->nprops == 0) + elem->props = (PlyProperty **) myalloc (sizeof (PlyProperty *)); + else + elem->props = (PlyProperty **) realloc (elem->props, + sizeof (PlyProperty *) * (elem->nprops + 1)); + + elem->props[elem->nprops] = prop; + elem->nprops++; +} + + +/****************************************************************************** +Add a comment to a PLY file descriptor. + +Entry: + plyfile - PLY file descriptor + line - line containing comment +******************************************************************************/ + +inline void add_comment (PlyFile *plyfile, char *line) +{ + int i; + + /* skip over "comment" and leading spaces and tabs */ + i = 7; + while (line[i] == ' ' || line[i] == '\t') + i++; + + ply_put_comment (plyfile, &line[i]); +} + + +/****************************************************************************** +Add a some object information to a PLY file descriptor. + +Entry: + plyfile - PLY file descriptor + line - line containing text info +******************************************************************************/ + +inline void add_obj_info (PlyFile *plyfile, char *line) +{ + int i; + + /* skip over "obj_info" and leading spaces and tabs */ + i = 8; + while (line[i] == ' ' || line[i] == '\t') + i++; + + ply_put_obj_info (plyfile, &line[i]); +} + + +/****************************************************************************** +Copy a property. +******************************************************************************/ + +inline void copy_property(PlyProperty *dest, PlyProperty *src) +{ + dest->name = strdup (src->name); + dest->external_type = src->external_type; + dest->internal_type = src->internal_type; + dest->offset = src->offset; + + dest->is_list = src->is_list; + dest->count_external = src->count_external; + dest->count_internal = src->count_internal; + dest->count_offset = src->count_offset; +} + + +/****************************************************************************** +Allocate some memory. + +Entry: + size - amount of memory requested (in bytes) + lnum - line number from which memory was requested + fname - file name from which memory was requested +******************************************************************************/ + +inline char *my_alloc(int size, int lnum, const char *fe) +{ + char *ptr; + + ptr = (char *) malloc (size); + + if (ptr == 0) { + fprintf(stderr, "Memory allocation bombed on line %d in %s\n", lnum, fe); + } + + return (ptr); +} + +} +} +#endif diff --git a/src/igl/png/readPNG.cpp b/src/igl/png/readPNG.cpp new file mode 100644 index 0000000000..8d42ad8d92 --- /dev/null +++ b/src/igl/png/readPNG.cpp @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readPNG.h" +#include + +IGL_INLINE bool igl::png::readPNG( + const std::string png_file, + Eigen::Matrix& R, + Eigen::Matrix& G, + Eigen::Matrix& B, + Eigen::Matrix& A +) +{ + int cols,rows,n; + unsigned char *data = stbi_load(png_file.c_str(), &cols, &rows, &n, 4); + if(data == NULL) { + return false; + } + + R.resize(cols,rows); + G.resize(cols,rows); + B.resize(cols,rows); + A.resize(cols,rows); + + for (unsigned i=0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PNG_READ_PNG_H +#define IGL_PNG_READ_PNG_H +#include "../igl_inline.h" +#include +#include + +namespace igl +{ + namespace png + { + // Read an image from a .png file into 4 memory buffers + // + // Input: + // png_file path to .png file + // Output: + // R,G,B,A texture channels + // Returns true on success, false on failure + // + IGL_INLINE bool readPNG(const std::string png_file, + Eigen::Matrix& R, + Eigen::Matrix& G, + Eigen::Matrix& B, + Eigen::Matrix& A + ); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "readPNG.cpp" +#endif + +#endif diff --git a/src/igl/png/render_to_png.cpp b/src/igl/png/render_to_png.cpp new file mode 100644 index 0000000000..6533ad9ab4 --- /dev/null +++ b/src/igl/png/render_to_png.cpp @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "render_to_png.h" +#include + +#include "../opengl/gl.h" + +IGL_INLINE bool igl::png::render_to_png( + const std::string png_file, + const int width, + const int height, + const bool alpha, + const bool fast) +{ + unsigned char * data = new unsigned char[4*width*height]; + glReadPixels( + 0, + 0, + width, + height, + GL_RGBA, + GL_UNSIGNED_BYTE, + data); + //img->flip(); + if(!alpha) + { + for(int i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PNG_RENDER_TO_PNG_H +#define IGL_PNG_RENDER_TO_PNG_H +#include + +#include +namespace igl +{ + namespace png + { + // + // Render current open GL image to .png file + // Inputs: + // png_file path to output .png file + // width width of scene and resulting image + // height height of scene and resulting image + // alpha whether to include alpha channel + // fast sacrifice compression ratio for speed + // Returns true only if no errors occurred + // + // See also: igl/render_to_tga which is faster but writes .tga files + IGL_INLINE bool render_to_png( + const std::string png_file, + const int width, + const int height, + const bool alpha = true, + const bool fast = false); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "render_to_png.cpp" +#endif + +#endif diff --git a/src/igl/png/render_to_png_async.cpp b/src/igl/png/render_to_png_async.cpp new file mode 100644 index 0000000000..033701e630 --- /dev/null +++ b/src/igl/png/render_to_png_async.cpp @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "render_to_png_async.h" +#include "../opengl/gl.h" +#include + +static IGL_INLINE bool render_to_png_async_helper( + unsigned char * img, int width, int height, + const std::string png_file, + const bool alpha, + const bool fast) +{ + //img->flip(); + if(!alpha) + { + for(int i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PNG_RENDER_TO_PNG_ASYNC_H +#define IGL_PNG_RENDER_TO_PNG_ASYNC_H +#include +#include +//#include + +#include +namespace igl +{ + namespace png + { + // History: + // added multithreaded parameter and support, Alec Sept 3, 2012 + // + // Render current open GL image to .png file + // Inputs: + // png_file path to output .png file + // width width of scene and resulting image + // height height of scene and resulting image + // alpha whether to include alpha channel + // fast sacrifice compression ratio for speed + // Returns true only if no errors occurred + // + // See also: igl/render_to_tga which is faster but writes .tga files + IGL_INLINE std::thread render_to_png_async( + const std::string png_file, + const int width, + const int height, + const bool alpha = true, + const bool fast = false); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "render_to_png_async.cpp" +#endif + +#endif diff --git a/src/igl/png/texture_from_file.cpp b/src/igl/png/texture_from_file.cpp new file mode 100644 index 0000000000..60e8c795cf --- /dev/null +++ b/src/igl/png/texture_from_file.cpp @@ -0,0 +1,61 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "texture_from_file.h" + +#include "texture_from_png.h" +#include "../STR.h" +#include "../pathinfo.h" +#include "../opengl/report_gl_error.h" +//#include "../opengl2/texture_from_tga.h" +#include +#include +#include + +IGL_INLINE bool igl::png::texture_from_file(const std::string filename, GLuint & id) +{ + using namespace igl::opengl; + using namespace std; + // dirname, basename, extension and filename + string d,b,ext,f; + pathinfo(filename,d,b,ext,f); + // Convert extension to lower case + transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + //if(ext == "tga") + //{ + // return texture_from_tga(filename,id); + //}else + if(ext == "png") + { + return texture_from_png(filename,id); + }else + { +#ifdef __APPLE__ + // Convert to a temporary png file + string tmp = "/var/tmp/.texture_from_file.png"; +#define PATH_TO_CONVERT "/opt/local/bin/convert" + string command = STR(PATH_TO_CONVERT<<" \""< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PNG_TEXTURE_FROM_FILE_H +#define IGL_PNG_TEXTURE_FROM_FILE_H +#include "../igl_inline.h" +#include "../opengl/gl.h" + +#include + +namespace igl +{ + namespace png + { + // Read an image from an image file and use it as a texture. Officially, + // only .tga and .png are supported. Any filetype read by + // ImageMagick's `convert` will work via an unsafe system call. + // + // Input: + // filename path to image file + // Output: + // id of generated openGL texture + // Returns true on success, false on failure + IGL_INLINE bool texture_from_file(const std::string filename, GLuint & id); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "texture_from_file.cpp" +#endif + +#endif + + + diff --git a/src/igl/png/texture_from_png.cpp b/src/igl/png/texture_from_png.cpp new file mode 100644 index 0000000000..fde3c002c9 --- /dev/null +++ b/src/igl/png/texture_from_png.cpp @@ -0,0 +1,83 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "texture_from_png.h" + +#include "../opengl/report_gl_error.h" +#include + +IGL_INLINE bool igl::png::texture_from_png(const std::string png_file, const bool flip, GLuint & id) +{ + int width,height,n; + unsigned char *data = igl::stbi_load(png_file.c_str(), &width, &height, &n, 4); + if(data == NULL) { + return false; + } + + // Why do I need to flip? + /*if(flip) + { + yimg.flip(); + }*/ + + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGB, + width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glBindTexture(GL_TEXTURE_2D, 0); + + igl::stbi_image_free(data); + + return true; +} + +IGL_INLINE bool igl::png::texture_from_png(const std::string png_file, GLuint & id) +{ + return texture_from_png(png_file,false,id); +} + + +IGL_INLINE bool igl::png::texture_from_png( + const std::string png_file, + Eigen::Matrix& R, + Eigen::Matrix& G, + Eigen::Matrix& B, + Eigen::Matrix& A +) +{ + int width,height,n; + unsigned char *data = igl::stbi_load(png_file.c_str(), &width, &height, &n, 4); + if(data == NULL) { + return false; + } + + R.resize(height,width); + G.resize(height,width); + B.resize(height,width); + A.resize(height,width); + + for (unsigned j=0; j +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PNG_TEXTURE_FROM_PNG_H +#define IGL_PNG_TEXTURE_FROM_PNG_H +#include "../igl_inline.h" +#include +#include + +#include "../opengl/gl.h" + +namespace igl +{ + namespace png + { + // Read an image from a .png file and use it as a texture + // + // Input: + // png_file path to .png file + // flip whether to flip the image vertically (A --> ∀) + // Output: + // id of generated openGL texture + // Returns true on success, false on failure + IGL_INLINE bool texture_from_png(const std::string png_file, const bool flip, GLuint & id); + IGL_INLINE bool texture_from_png(const std::string png_file, GLuint & id); + + // Read an image from a .png file and use it as a texture + // + // Input: + // png_file path to .png file + // Output: + // R,G,B,A texture channels + // Returns true on success, false on failure + // + // Todo: this is an inappropriate function name. This is really just + // reading a png.... Not necessarily as a texture. + IGL_INLINE bool texture_from_png(const std::string png_file, + Eigen::Matrix& R, + Eigen::Matrix& G, + Eigen::Matrix& B, + Eigen::Matrix& A + ); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "texture_from_png.cpp" +#endif + +#endif diff --git a/src/igl/png/writePNG.cpp b/src/igl/png/writePNG.cpp new file mode 100644 index 0000000000..9ab68d775d --- /dev/null +++ b/src/igl/png/writePNG.cpp @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writePNG.h" +#include +#include + +IGL_INLINE bool igl::png::writePNG( + const Eigen::Matrix& R, + const Eigen::Matrix& G, + const Eigen::Matrix& B, + const Eigen::Matrix& A, + const std::string png_file +) +{ + assert((R.rows() == G.rows()) && (G.rows() == B.rows()) && (B.rows() == A.rows())); + assert((R.cols() == G.cols()) && (G.cols() == B.cols()) && (B.cols() == A.cols())); + + const int comp = 4; // 4 Channels Red, Green, Blue, Alpha + const int stride_in_bytes = R.rows()*comp; // Length of one row in bytes + std::vector data(R.size()*comp,0); // The image itself; + + for (unsigned i = 0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PNG_WRITE_PNG_H +#define IGL_PNG_WRITE_PNG_H +#include "../igl_inline.h" +#include +#include + +namespace igl +{ + namespace png + { + // Writes an image to a png file + // + // Input: + // R,G,B,A texture channels + // Output: + // png_file path to .png file + // Returns true on success, false on failure + // + IGL_INLINE bool writePNG + ( + const Eigen::Matrix& R, + const Eigen::Matrix& G, + const Eigen::Matrix& B, + const Eigen::Matrix& A, + const std::string png_file + ); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "writePNG.cpp" +#endif + +#endif diff --git a/src/igl/point_in_circle.cpp b/src/igl/point_in_circle.cpp new file mode 100644 index 0000000000..9c26d8c0dc --- /dev/null +++ b/src/igl/point_in_circle.cpp @@ -0,0 +1,18 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "point_in_circle.h" + +IGL_INLINE bool igl::point_in_circle( + const double qx, + const double qy, + const double cx, + const double cy, + const double r) +{ + return (qx-cx)*(qx-cx) + (qy-cy)*(qy-cy) - r*r < 0; +} diff --git a/src/igl/point_in_circle.h b/src/igl/point_in_circle.h new file mode 100644 index 0000000000..f19344c405 --- /dev/null +++ b/src/igl/point_in_circle.h @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_POINT_IN_CIRCLE_H +#define IGL_POINT_IN_CIRCLE_H +#include "igl_inline.h" + +namespace igl +{ + // Determine if 2d point is in a circle + // Inputs: + // qx x-coordinate of query point + // qy y-coordinate of query point + // cx x-coordinate of circle center + // cy y-coordinate of circle center + // r radius of circle + // Returns true if query point is in circle, false otherwise + IGL_INLINE bool point_in_circle( + const double qx, + const double qy, + const double cx, + const double cy, + const double r); +} + +#ifndef IGL_STATIC_LIBRARY +# include "point_in_circle.cpp" +#endif + +#endif diff --git a/src/igl/point_in_poly.cpp b/src/igl/point_in_poly.cpp new file mode 100644 index 0000000000..f0b07edf95 --- /dev/null +++ b/src/igl/point_in_poly.cpp @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "point_in_poly.h" + +bool IGL_INLINE igl::point_in_poly( const std::vector >&poly, + const unsigned int xt, + const unsigned int yt) +{ + int npoints= poly.size(); + unsigned int xnew,ynew; + unsigned int xold,yold; + unsigned int x1,y1; + unsigned int x2,y2; + int i; + int inside=0; + + if (npoints < 3) { + return(0); + } + xold=poly[npoints-1][0]; + yold=poly[npoints-1][1]; + for (i=0 ; i < npoints ; i++) { + xnew=poly[i][0]; + ynew=poly[i][1]; + if (xnew > xold) { + x1=xold; + x2=xnew; + y1=yold; + y2=ynew; + } + else { + x1=xnew; + x2=xold; + y1=ynew; + y2=yold; + } + if ((xnew < xt) == (xt <= xold) /* edge "open" at one end */ + && ((long)yt-(long)y1)*(long)(x2-x1) + < ((long)y2-(long)y1)*(long)(xt-x1)) { + inside=!inside; + } + xold=xnew; + yold=ynew; + } + return (inside != 0); +} diff --git a/src/igl/point_in_poly.h b/src/igl/point_in_poly.h new file mode 100644 index 0000000000..2b9c6e51b4 --- /dev/null +++ b/src/igl/point_in_poly.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_POINT_IN_POLY_H +#define IGL_POINT_IN_POLY_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Determine if 2d point is inside a 2D polygon + // Inputs: + // poly vector of polygon points, [0]=x, [1]=y. + // Polyline need not be closed (i.e. first point != last point), + // the line segment between last and first selected points is constructed + // within this function. + // x x-coordinate of query point + // y y-coordinate of query point + // Returns true if query point is in polygon, false otherwise + // from http://www.visibone.com/inpoly/ +bool IGL_INLINE point_in_poly( const std::vector >&poly, + const unsigned int xt, + const unsigned int yt); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "point_in_poly.cpp" +#endif + +#endif diff --git a/src/igl/point_mesh_squared_distance.cpp b/src/igl/point_mesh_squared_distance.cpp new file mode 100644 index 0000000000..57dd9aa145 --- /dev/null +++ b/src/igl/point_mesh_squared_distance.cpp @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "point_mesh_squared_distance.h" +#include "AABB.h" +#include + +template < + typename DerivedP, + typename DerivedV, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> +IGL_INLINE void igl::point_mesh_squared_distance( + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & V, + const Eigen::MatrixXi & Ele, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C) +{ + using namespace std; + const size_t dim = P.cols(); + assert((dim == 2 || dim == 3) && "P.cols() should be 2 or 3"); + assert(P.cols() == V.cols() && "P.cols() should equal V.cols()"); + switch(dim) + { + default: + assert(false && "Unsupported dim"); + // fall-through and pray + case 3: + { + AABB tree; + tree.init(V,Ele); + return tree.squared_distance(V,Ele,P,sqrD,I,C); + } + case 2: + { + AABB tree; + tree.init(V,Ele); + return tree.squared_distance(V,Ele,P,sqrD,I,C); + } + } +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::point_mesh_squared_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::point_mesh_squared_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::point_mesh_squared_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/point_mesh_squared_distance.h b/src/igl/point_mesh_squared_distance.h new file mode 100644 index 0000000000..0635288a3d --- /dev/null +++ b/src/igl/point_mesh_squared_distance.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_POINT_MESH_SQUARED_DISTANCE_H +#define IGL_POINT_MESH_SQUARED_DISTANCE_H +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Compute distances from a set of points P to a triangle mesh (V,F) + // + // Inputs: + // P #P by 3 list of query point positions + // V #V by 3 list of vertex positions + // Ele #Ele by (3|2|1) list of (triangle|edge|point) indices + // Outputs: + // sqrD #P list of smallest squared distances + // I #P list of primitive indices corresponding to smallest distances + // C #P by 3 list of closest points + // + // Known bugs: This only computes distances to given primitivess. So + // unreferenced vertices are ignored. However, degenerate primitives are + // handled correctly: triangle [1 2 2] is treated as a segment [1 2], and + // triangle [1 1 1] is treated as a point. So one _could_ add extra + // combinatorially degenerate rows to Ele for all unreferenced vertices to + // also get distances to points. + template < + typename DerivedP, + typename DerivedV, + typename DerivedsqrD, + typename DerivedI, + typename DerivedC> + IGL_INLINE void point_mesh_squared_distance( + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & V, + const Eigen::MatrixXi & Ele, + Eigen::PlainObjectBase & sqrD, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C); +} + +#ifndef IGL_STATIC_LIBRARY +# include "point_mesh_squared_distance.cpp" +#endif + +#endif diff --git a/src/igl/point_simplex_squared_distance.cpp b/src/igl/point_simplex_squared_distance.cpp new file mode 100644 index 0000000000..2b98bd2853 --- /dev/null +++ b/src/igl/point_simplex_squared_distance.cpp @@ -0,0 +1,181 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "point_simplex_squared_distance.h" +#include "project_to_line_segment.h" +#include "barycentric_coordinates.h" +#include +#include +#include + + + +template < + int DIM, + typename Derivedp, + typename DerivedV, + typename DerivedEle, + typename Derivedsqr_d, + typename Derivedc, + typename Derivedb> +IGL_INLINE void igl::point_simplex_squared_distance( + const Eigen::MatrixBase & p, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const typename DerivedEle::Index primitive, + Derivedsqr_d & sqr_d, + Eigen::MatrixBase & c, + Eigen::PlainObjectBase & bary) +{ + typedef typename Derivedp::Scalar Scalar; + typedef typename Eigen::Matrix Vector; + typedef Vector Point; + //typedef Derivedb BaryPoint; + typedef Eigen::Matrix BaryPoint; + + const auto & Dot = [](const Point & a, const Point & b)->Scalar + { + return a.dot(b); + }; + // Real-time collision detection, Ericson, Chapter 5 + const auto & ClosestBaryPtPointTriangle = + [&Dot](Point p, Point a, Point b, Point c, BaryPoint& bary_out )->Point + { + // Check if P in vertex region outside A + Vector ab = b - a; + Vector ac = c - a; + Vector ap = p - a; + Scalar d1 = Dot(ab, ap); + Scalar d2 = Dot(ac, ap); + if (d1 <= 0.0 && d2 <= 0.0) { + // barycentric coordinates (1,0,0) + bary_out << 1, 0, 0; + return a; + } + // Check if P in vertex region outside B + Vector bp = p - b; + Scalar d3 = Dot(ab, bp); + Scalar d4 = Dot(ac, bp); + if (d3 >= 0.0 && d4 <= d3) { + // barycentric coordinates (0,1,0) + bary_out << 0, 1, 0; + return b; + } + // Check if P in edge region of AB, if so return projection of P onto AB + Scalar vc = d1*d4 - d3*d2; + if( a != b) + { + if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) { + Scalar v = d1 / (d1 - d3); + // barycentric coordinates (1-v,v,0) + bary_out << 1-v, v, 0; + return a + v * ab; + } + } + // Check if P in vertex region outside C + Vector cp = p - c; + Scalar d5 = Dot(ab, cp); + Scalar d6 = Dot(ac, cp); + if (d6 >= 0.0 && d5 <= d6) { + // barycentric coordinates (0,0,1) + bary_out << 0, 0, 1; + return c; + } + // Check if P in edge region of AC, if so return projection of P onto AC + Scalar vb = d5*d2 - d1*d6; + if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) { + Scalar w = d2 / (d2 - d6); + // barycentric coordinates (1-w,0,w) + bary_out << 1-w, 0, w; + return a + w * ac; + } + // Check if P in edge region of BC, if so return projection of P onto BC + Scalar va = d3*d6 - d5*d4; + if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) { + Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + // barycentric coordinates (0,1-w,w) + bary_out << 0, 1-w, w; + return b + w * (c - b); + } + // P inside face region. Compute Q through its barycentric coordinates (u,v,w) + Scalar denom = 1.0 / (va + vb + vc); + Scalar v = vb * denom; + Scalar w = vc * denom; + bary_out << 1.0-v-w, v, w; + return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w + }; + + assert(p.size() == DIM); + assert(V.cols() == DIM); + assert(Ele.cols() <= DIM+1); + assert(Ele.cols() <= 3 && "Only simplices up to triangles are considered"); + + assert((Derivedb::RowsAtCompileTime == 1 || Derivedb::ColsAtCompileTime == 1) && "bary must be Eigen Vector or Eigen RowVector"); + assert( + ((Derivedb::RowsAtCompileTime == -1 || Derivedb::ColsAtCompileTime == -1) || + (Derivedb::RowsAtCompileTime == Ele.cols() || Derivedb::ColsAtCompileTime == Ele.cols()) + ) && "bary must be Dynamic or size of Ele.cols()"); + + BaryPoint tmp_bary; + c = (const Derivedc &)ClosestBaryPtPointTriangle( + p, + V.row(Ele(primitive,0)), + // modulo is a HACK to handle points, segments and triangles. Because of + // this, we need 3d buffer for bary + V.row(Ele(primitive,1%Ele.cols())), + V.row(Ele(primitive,2%Ele.cols())), + tmp_bary); + bary.resize( Derivedb::RowsAtCompileTime == 1 ? 1 : Ele.cols(), Derivedb::ColsAtCompileTime == 1 ? 1 : Ele.cols()); + bary.head(Ele.cols()) = tmp_bary.head(Ele.cols()); + sqr_d = (p-c).squaredNorm(); +} + +template < + int DIM, + typename Derivedp, + typename DerivedV, + typename DerivedEle, + typename Derivedsqr_d, + typename Derivedc> +IGL_INLINE void igl::point_simplex_squared_distance( + const Eigen::MatrixBase & p, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const typename DerivedEle::Index primitive, + Derivedsqr_d & sqr_d, + Eigen::MatrixBase & c) +{ + // Use Dynamic because we don't know Ele.cols() at compile time. + Eigen::Matrix b; + point_simplex_squared_distance( p, V, Ele, primitive, sqr_d, c, b ); +} + +namespace igl +{ + template <> IGL_INLINE void point_simplex_squared_distance<2>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, float&, Eigen::MatrixBase >&) {assert(false);}; + template <> IGL_INLINE void point_simplex_squared_distance<2>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&) {assert(false);}; + template <> IGL_INLINE void point_simplex_squared_distance<2>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&) {assert(false);}; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&); +// generated by autoexplicit.sh +template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, float, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, float&, Eigen::MatrixBase >&); +// generated by autoexplicit.sh +// generated by autoexplicit.sh +template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, float, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, float&, Eigen::MatrixBase >&); +// generated by autoexplicit.sh +template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, float, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, float&, Eigen::MatrixBase >&); +template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&); +template void igl::point_simplex_squared_distance<2, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&); +template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&, Eigen::PlainObjectBase >&); +template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&, Eigen::PlainObjectBase >&); +template void igl::point_simplex_squared_distance<2, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&, Eigen::PlainObjectBase >&); +template void igl::point_simplex_squared_distance<2, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/point_simplex_squared_distance.h b/src/igl/point_simplex_squared_distance.h new file mode 100644 index 0000000000..0cc871cf01 --- /dev/null +++ b/src/igl/point_simplex_squared_distance.h @@ -0,0 +1,72 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_POINT_SIMPLEX_SQUARED_DISTANCE_H +#define IGL_POINT_SIMPLEX_SQUARED_DISTANCE_H +#include "igl_inline.h" +#include +namespace igl +{ + // Determine squared distance from a point to linear simplex. + // + // Inputs: + // p d-long query point + // V #V by d list of vertices + // Ele #Ele by ss<=d+1 list of simplex indices into V + // i index into Ele of simplex + // Outputs: + // sqr_d squared distance of Ele(i) to p + // c closest point on Ele(i) + // + template < + int DIM, + typename Derivedp, + typename DerivedV, + typename DerivedEle, + typename Derivedsqr_d, + typename Derivedc> + IGL_INLINE void point_simplex_squared_distance( + const Eigen::MatrixBase & p, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const typename DerivedEle::Index i, + Derivedsqr_d & sqr_d, + Eigen::MatrixBase & c); + // Determine squared distance from a point to linear simplex. + // Also return barycentric coordinate of closest point. + // + // Inputs: + // p d-long query point + // V #V by d list of vertices + // Ele #Ele by ss<=d+1 list of simplex indices into V + // i index into Ele of simplex + // Outputs: + // sqr_d squared distance of Ele(i) to p + // c closest point on Ele(i) + // b barycentric coordinates of closest point on Ele(i) + // + template < + int DIM, + typename Derivedp, + typename DerivedV, + typename DerivedEle, + typename Derivedsqr_d, + typename Derivedc, + typename Derivedb> + IGL_INLINE void point_simplex_squared_distance( + const Eigen::MatrixBase & p, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & Ele, + const typename DerivedEle::Index i, + Derivedsqr_d & sqr_d, + Eigen::MatrixBase & c, + Eigen::PlainObjectBase & b); +} +#ifndef IGL_STATIC_LIBRARY +# include "point_simplex_squared_distance.cpp" +#endif +#endif diff --git a/src/igl/polar_dec.cpp b/src/igl/polar_dec.cpp new file mode 100644 index 0000000000..cdd358a074 --- /dev/null +++ b/src/igl/polar_dec.cpp @@ -0,0 +1,97 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "polar_dec.h" +#include "polar_svd.h" +#ifdef _WIN32 +#else +# include +#endif +#include +#include +#include +#include + +// From Olga's CGAL mentee's ARAP code +template < + typename DerivedA, + typename DerivedR, + typename DerivedT, + typename DerivedU, + typename DerivedS, + typename DerivedV> +IGL_INLINE void igl::polar_dec( + const Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & R, + Eigen::PlainObjectBase & T, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & V) +{ + using namespace std; + using namespace Eigen; + typedef typename DerivedA::Scalar Scalar; + + const Scalar th = std::sqrt(Eigen::NumTraits::dummy_precision()); + + Eigen::SelfAdjointEigenSolver eig; + feclearexcept(FE_UNDERFLOW); + eig.computeDirect(A.transpose()*A); + if(fetestexcept(FE_UNDERFLOW) || eig.eigenvalues()(0)/eig.eigenvalues()(2) th) + { + cout<<"resorting to svd 2..."< +IGL_INLINE void igl::polar_dec( + const Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & R, + Eigen::PlainObjectBase & T) +{ + DerivedA U; + DerivedA V; + Eigen::Matrix S; + return igl::polar_dec(A,R,T,U,S,V); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::polar_dec, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_dec, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/polar_dec.h b/src/igl/polar_dec.h new file mode 100644 index 0000000000..2b995eabe1 --- /dev/null +++ b/src/igl/polar_dec.h @@ -0,0 +1,53 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_POLAR_DEC +#define IGL_POLAR_DEC +#include "igl_inline.h" +#include + +namespace igl +{ + // Computes the polar decomposition (R,T) of a matrix A + // Inputs: + // A 3 by 3 matrix to be decomposed + // Outputs: + // R 3 by 3 orthonormal matrix part of decomposition + // T 3 by 3 stretch matrix part of decomposition + // U 3 by 3 left-singular vectors + // S 3 by 1 singular values + // V 3 by 3 right-singular vectors + // + // + template < + typename DerivedA, + typename DerivedR, + typename DerivedT, + typename DerivedU, + typename DerivedS, + typename DerivedV> + IGL_INLINE void polar_dec( + const Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & R, + Eigen::PlainObjectBase & T, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & V); + template < + typename DerivedA, + typename DerivedR, + typename DerivedT> + IGL_INLINE void polar_dec( + const Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & R, + Eigen::PlainObjectBase & T); +} +#ifndef IGL_STATIC_LIBRARY +# include "polar_dec.cpp" +#endif +#endif + diff --git a/src/igl/polar_svd.cpp b/src/igl/polar_svd.cpp new file mode 100644 index 0000000000..962a754fb9 --- /dev/null +++ b/src/igl/polar_svd.cpp @@ -0,0 +1,80 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "polar_svd.h" +#include +#include +#include + +// Adapted from Olga's CGAL mentee's ARAP code +template < + typename DerivedA, + typename DerivedR, + typename DerivedT> +IGL_INLINE void igl::polar_svd( + const Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & R, + Eigen::PlainObjectBase & T) +{ + DerivedA U; + DerivedA V; + Eigen::Matrix S; + return igl::polar_svd(A,R,T,U,S,V); +} + +template < + typename DerivedA, + typename DerivedR, + typename DerivedT, + typename DerivedU, + typename DerivedS, + typename DerivedV> +IGL_INLINE void igl::polar_svd( + const Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & R, + Eigen::PlainObjectBase & T, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & V) +{ + using namespace std; + Eigen::JacobiSVD svd; + svd.compute(A, Eigen::ComputeFullU | Eigen::ComputeFullV ); + U = svd.matrixU(); + V = svd.matrixV(); + S = svd.singularValues(); + R = U*V.transpose(); + const auto & SVT = S.asDiagonal() * V.adjoint(); + // Check for reflection + if(R.determinant() < 0) + { + // Annoyingly the .eval() is necessary + auto W = V.eval(); + W.col(V.cols()-1) *= -1.; + R = U*W.transpose(); + T = W*SVT; + }else + { + T = V*SVT; + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_svd,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::PlainObjectBase > const &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase > &,Eigen::PlainObjectBase >&); +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::polar_svd, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/polar_svd.h b/src/igl/polar_svd.h new file mode 100644 index 0000000000..13d3f38730 --- /dev/null +++ b/src/igl/polar_svd.h @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_POLAR_SVD +#define IGL_POLAR_SVD +#include "igl_inline.h" +#include + +namespace igl +{ + // Computes the polar decomposition (R,T) of a matrix A using SVD singular + // value decomposition + // + // Inputs: + // A 3 by 3 matrix to be decomposed + // Outputs: + // R 3 by 3 rotation matrix part of decomposition (**always rotataion**) + // T 3 by 3 stretch matrix part of decomposition + // U 3 by 3 left-singular vectors + // S 3 by 1 singular values + // V 3 by 3 right-singular vectors + // + // + template < + typename DerivedA, + typename DerivedR, + typename DerivedT, + typename DerivedU, + typename DerivedS, + typename DerivedV> + IGL_INLINE void polar_svd( + const Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & R, + Eigen::PlainObjectBase & T, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & V); + template < + typename DerivedA, + typename DerivedR, + typename DerivedT> + IGL_INLINE void polar_svd( + const Eigen::PlainObjectBase & A, + Eigen::PlainObjectBase & R, + Eigen::PlainObjectBase & T); +} +#ifndef IGL_STATIC_LIBRARY +# include "polar_svd.cpp" +#endif +#endif diff --git a/src/igl/polar_svd3x3.cpp b/src/igl/polar_svd3x3.cpp new file mode 100644 index 0000000000..c0d972acaf --- /dev/null +++ b/src/igl/polar_svd3x3.cpp @@ -0,0 +1,113 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "polar_svd3x3.h" +#include "svd3x3.h" +#ifdef __SSE__ +# include "svd3x3_sse.h" +#endif +#ifdef __AVX__ +# include "svd3x3_avx.h" +#endif + +template +IGL_INLINE void igl::polar_svd3x3(const Mat& A, Mat& R) +{ + // should be caught at compile time, but just to be 150% sure: + assert(A.rows() == 3 && A.cols() == 3); + + Eigen::Matrix U, Vt; + Eigen::Matrix S; + svd3x3(A, U, S, Vt); + R = U * Vt.transpose(); +} + +#ifdef __SSE__ +template +IGL_INLINE void igl::polar_svd3x3_sse(const Eigen::Matrix& A, Eigen::Matrix &R) +{ + // should be caught at compile time, but just to be 150% sure: + assert(A.rows() == 3*4 && A.cols() == 3); + + Eigen::Matrix U, Vt; + Eigen::Matrix S; + svd3x3_sse(A, U, S, Vt); + + for (int k=0; k<4; k++) + { + R.block(3*k, 0, 3, 3) = U.block(3*k, 0, 3, 3) * Vt.block(3*k, 0, 3, 3).transpose(); + } + + //// test: + //for (int k=0; k<4; k++) + //{ + // Eigen::Matrix3f Apart = A.block(3*k, 0, 3, 3); + // Eigen::Matrix3f Rpart; + // polar_svd3x3(Apart, Rpart); + + // Eigen::Matrix3f Rpart_SSE = R.block(3*k, 0, 3, 3); + // Eigen::Matrix3f diff = Rpart - Rpart_SSE; + // float diffNorm = diff.norm(); + + // int hu = 1; + //} + //// eof test +} +#endif + +#ifdef __AVX__ +template +IGL_INLINE void igl::polar_svd3x3_avx(const Eigen::Matrix& A, Eigen::Matrix &R) +{ + // should be caught at compile time, but just to be 150% sure: + assert(A.rows() == 3*8 && A.cols() == 3); + + Eigen::Matrix U, Vt; + Eigen::Matrix S; + svd3x3_avx(A, U, S, Vt); + + for (int k=0; k<8; k++) + { + R.block(3*k, 0, 3, 3) = U.block(3*k, 0, 3, 3) * Vt.block(3*k, 0, 3, 3).transpose(); + } + + // test: + for (int k=0; k<8; k++) + { + Eigen::Matrix3f Apart = A.block(3*k, 0, 3, 3); + Eigen::Matrix3f Rpart; + polar_svd3x3(Apart, Rpart); + + Eigen::Matrix3f Rpart_SSE = R.block(3*k, 0, 3, 3); + Eigen::Matrix3f diff = Rpart - Rpart_SSE; + float diffNorm = diff.norm(); + if (std::abs(diffNorm) > 0.001) + { + printf("Huh: diffNorm = %15f (k = %i)\n", diffNorm, k); + } + + // Unused + //int hu = 1; + } + // eof test +} +#endif + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::polar_svd3x3 >(Eigen::Matrix const&, Eigen::Matrix&); +template void igl::polar_svd3x3 >(Eigen::Matrix const &,Eigen::Matrix &); + +#ifdef __SSE__ +template void igl::polar_svd3x3_sse(Eigen::Matrix const&, Eigen::Matrix&); +#endif + +#ifdef __AVX__ +template void igl::polar_svd3x3_avx(Eigen::Matrix const&, Eigen::Matrix&); +#endif + +#endif diff --git a/src/igl/polar_svd3x3.h b/src/igl/polar_svd3x3.h new file mode 100644 index 0000000000..a579774e09 --- /dev/null +++ b/src/igl/polar_svd3x3.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_POLAR_SVD3X3_H +#define IGL_POLAR_SVD3X3_H +#include +#include "igl_inline.h" +namespace igl +{ + // Computes the closest rotation to input matrix A using specialized 3x3 SVD + // singular value decomposition (WunderSVD3x3) + // + // Inputs: + // A 3 by 3 matrix to be decomposed + // Outputs: + // R 3 by 3 closest element in SO(3) (closeness in terms of Frobenius + // metric) + // + // This means that det(R) = 1. Technically it's not polar decomposition + // which guarantees positive semidefinite stretch factor (at the cost of + // having det(R) = -1). "• The orthogonal factors U and V will be true + // rotation matrices..." [McAdams, Selle, Tamstorf, Teran, Sefakis 2011] + // + template + IGL_INLINE void polar_svd3x3(const Mat& A, Mat& R); + #ifdef __SSE__ + template + IGL_INLINE void polar_svd3x3_sse(const Eigen::Matrix& A, Eigen::Matrix &R); + #endif + #ifdef __AVX__ + template + IGL_INLINE void polar_svd3x3_avx(const Eigen::Matrix& A, Eigen::Matrix &R); + #endif +} +#ifndef IGL_STATIC_LIBRARY +# include "polar_svd3x3.cpp" +#endif +#endif + diff --git a/src/igl/polygon_mesh_to_triangle_mesh.cpp b/src/igl/polygon_mesh_to_triangle_mesh.cpp new file mode 100644 index 0000000000..2ea43238b5 --- /dev/null +++ b/src/igl/polygon_mesh_to_triangle_mesh.cpp @@ -0,0 +1,76 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "polygon_mesh_to_triangle_mesh.h" +#include "matrix_to_list.h" + +template +IGL_INLINE void igl::polygon_mesh_to_triangle_mesh( + const std::vector > & vF, + Eigen::PlainObjectBase& F) +{ + using namespace std; + using namespace Eigen; + int m = 0; + // estimate of size + for(typename vector >::const_iterator fit = vF.begin(); + fit!=vF.end(); + fit++) + { + if(fit->size() >= 3) + { + m += fit->size() - 2; + } + } + // Resize output + F.resize(m,3); + { + int k = 0; + for(typename vector >::const_iterator fit = vF.begin(); + fit!=vF.end(); + fit++) + { + if(fit->size() >= 3) + { + typename vector::const_iterator cit = fit->begin(); + cit++; + typename vector::const_iterator pit = cit++; + for(; + cit!=fit->end(); + cit++,pit++) + { + F(k,0) = *(fit->begin()); + F(k,1) = *pit; + F(k,2) = *cit; + k++; + } + } + } + assert(k==m); + } + +} + +template +IGL_INLINE void igl::polygon_mesh_to_triangle_mesh( + const Eigen::PlainObjectBase& P, + Eigen::PlainObjectBase& F) +{ + std::vector > vP; + matrix_to_list(P,vP); + return polygon_mesh_to_triangle_mesh(vP,F); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::polygon_mesh_to_triangle_mesh >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::polygon_mesh_to_triangle_mesh >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template void igl::polygon_mesh_to_triangle_mesh >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +template void igl::polygon_mesh_to_triangle_mesh >(std::vector >, std::allocator > > > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/polygon_mesh_to_triangle_mesh.h b/src/igl/polygon_mesh_to_triangle_mesh.h new file mode 100644 index 0000000000..7d8f16e82e --- /dev/null +++ b/src/igl/polygon_mesh_to_triangle_mesh.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_POLYGON_MESH_TO_TRIANGLE_MESH_H +#define IGL_POLYGON_MESH_TO_TRIANGLE_MESH_H +#include "igl_inline.h" + +#ifndef IGL_NO_EIGEN +# include +#endif +#include + +namespace igl +{ + // Triangulate a general polygonal mesh into a triangle mesh. + // + // Inputs: + // vF list of polygon index lists + // Outputs: + // F eigen int matrix #F by 3 + // + // Example: + // vector > vV; + // vector > vF; + // read_triangle_mesh("poly.obj",vV,vF); + // MatrixXd V; + // MatrixXi F; + // list_to_matrix(vV,V); + // triangulate(vF,F); + template + IGL_INLINE void polygon_mesh_to_triangle_mesh( + const std::vector > & vF, + Eigen::PlainObjectBase& F); + template + IGL_INLINE void polygon_mesh_to_triangle_mesh( + const Eigen::PlainObjectBase& P, + Eigen::PlainObjectBase& F); +} + +#ifndef IGL_STATIC_LIBRARY +# include "polygon_mesh_to_triangle_mesh.cpp" +#endif + +#endif diff --git a/src/igl/principal_curvature.cpp b/src/igl/principal_curvature.cpp new file mode 100644 index 0000000000..5712130a00 --- /dev/null +++ b/src/igl/principal_curvature.cpp @@ -0,0 +1,854 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "principal_curvature.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// Lib IGL includes +#include +#include +#include +#include +#include + +typedef enum +{ + SPHERE_SEARCH, + K_RING_SEARCH +} searchType; + +typedef enum +{ + AVERAGE, + PROJ_PLANE +} normalType; + +class CurvatureCalculator +{ +public: + /* Row number i represents the i-th vertex, whose columns are: + curv[i][0] : K2 + curv[i][1] : K1 + curvDir[i][0] : PD1 + curvDir[i][1] : PD2 + */ + std::vector< std::vector > curv; + std::vector< std::vector > curvDir; + bool curvatureComputed; + class Quadric + { + public: + + IGL_INLINE Quadric () + { + a() = b() = c() = d() = e() = 1.0; + } + + IGL_INLINE Quadric(double av, double bv, double cv, double dv, double ev) + { + a() = av; + b() = bv; + c() = cv; + d() = dv; + e() = ev; + } + + IGL_INLINE double& a() { return data[0];} + IGL_INLINE double& b() { return data[1];} + IGL_INLINE double& c() { return data[2];} + IGL_INLINE double& d() { return data[3];} + IGL_INLINE double& e() { return data[4];} + + double data[5]; + + IGL_INLINE double evaluate(double u, double v) + { + return a()*u*u + b()*u*v + c()*v*v + d()*u + e()*v; + } + + IGL_INLINE double du(double u, double v) + { + return 2.0*a()*u + b()*v + d(); + } + + IGL_INLINE double dv(double u, double v) + { + return 2.0*c()*v + b()*u + e(); + } + + IGL_INLINE double duv(double u, double v) + { + return b(); + } + + IGL_INLINE double duu(double u, double v) + { + return 2.0*a(); + } + + IGL_INLINE double dvv(double u, double v) + { + return 2.0*c(); + } + + + IGL_INLINE static Quadric fit(const std::vector &VV) + { + assert(VV.size() >= 5); + if (VV.size() < 5) + { + std::cerr << "ASSERT FAILED! fit function requires at least 5 points: Only " << VV.size() << " were given." << std::endl; + exit(0); + } + + Eigen::MatrixXd A(VV.size(),5); + Eigen::MatrixXd b(VV.size(),1); + Eigen::MatrixXd sol(5,1); + + for(unsigned int c=0; c < VV.size(); ++c) + { + double u = VV[c][0]; + double v = VV[c][1]; + double n = VV[c][2]; + + A(c,0) = u*u; + A(c,1) = u*v; + A(c,2) = v*v; + A(c,3) = u; + A(c,4) = v; + + b(c) = n; + } + + sol=A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b); + + return Quadric(sol(0),sol(1),sol(2),sol(3),sol(4)); + } + }; + +public: + + Eigen::MatrixXd vertices; + // Face list of current mesh (#F x 3) or (#F x 4) + // The i-th row contains the indices of the vertices that forms the i-th face in ccw order + Eigen::MatrixXi faces; + + std::vector > vertex_to_vertices; + std::vector > vertex_to_faces; + std::vector > vertex_to_faces_index; + Eigen::MatrixXd face_normals; + Eigen::MatrixXd vertex_normals; + + /* Size of the neighborhood */ + double sphereRadius; + int kRing; + + bool localMode; /* Use local mode */ + bool projectionPlaneCheck; /* Check collected vertices on tangent plane */ + bool montecarlo; + unsigned int montecarloN; + + searchType st; /* Use either a sphere search or a k-ring search */ + normalType nt; + + double lastRadius; + double scaledRadius; + std::string lastMeshName; + + /* Benchmark related variables */ + bool expStep; /* True if we want the radius to increase exponentially */ + int step; /* If expStep==false, by how much rhe radius increases on every step */ + int maxSize; /* The maximum limit of the radius in the benchmark */ + + IGL_INLINE CurvatureCalculator(); + IGL_INLINE void init(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F); + + IGL_INLINE void finalEigenStuff(int, const std::vector&, Quadric&); + IGL_INLINE void fitQuadric(const Eigen::Vector3d&, const std::vector& ref, const std::vector& , Quadric *); + IGL_INLINE void applyProjOnPlane(const Eigen::Vector3d&, const std::vector&, std::vector&); + IGL_INLINE void getSphere(const int, const double, std::vector&, int min); + IGL_INLINE void getKRing(const int, const double,std::vector&); + IGL_INLINE Eigen::Vector3d project(const Eigen::Vector3d&, const Eigen::Vector3d&, const Eigen::Vector3d&); + IGL_INLINE void computeReferenceFrame(int, const Eigen::Vector3d&, std::vector&); + IGL_INLINE void getAverageNormal(int, const std::vector&, Eigen::Vector3d&); + IGL_INLINE void getProjPlane(int, const std::vector&, Eigen::Vector3d&); + IGL_INLINE void applyMontecarlo(const std::vector&,std::vector*); + IGL_INLINE void computeCurvature(); + IGL_INLINE void printCurvature(const std::string& outpath); + IGL_INLINE double getAverageEdge(); + + IGL_INLINE static int rotateForward (double *v0, double *v1, double *v2) + { + double t; + + if (std::abs(*v2) >= std::abs(*v1) && std::abs(*v2) >= std::abs(*v0)) + return 0; + + t = *v0; + *v0 = *v2; + *v2 = *v1; + *v1 = t; + + return 1 + rotateForward (v0, v1, v2); + } + + IGL_INLINE static void rotateBackward (int nr, double *v0, double *v1, double *v2) + { + double t; + + if (nr == 0) + return; + + t = *v2; + *v2 = *v0; + *v0 = *v1; + *v1 = t; + + rotateBackward (nr - 1, v0, v1, v2); + } + + IGL_INLINE static Eigen::Vector3d chooseMax (Eigen::Vector3d n, Eigen::Vector3d abc, double ab) + { + int max_i; + double max_sp; + Eigen::Vector3d nt[8]; + + n.normalize (); + abc.normalize (); + + max_sp = - std::numeric_limits::max(); + + for (int i = 0; i < 4; ++i) + { + nt[i] = n; + if (ab > 0) + { + switch (i) + { + case 0: + break; + + case 1: + nt[i][2] = -n[2]; + break; + + case 2: + nt[i][0] = -n[0]; + nt[i][1] = -n[1]; + break; + + case 3: + nt[i][0] = -n[0]; + nt[i][1] = -n[1]; + nt[i][2] = -n[2]; + break; + } + } + else + { + switch (i) + { + case 0: + nt[i][0] = -n[0]; + break; + + case 1: + nt[i][1] = -n[1]; + break; + + case 2: + nt[i][0] = -n[0]; + nt[i][2] = -n[2]; + break; + + case 3: + nt[i][1] = -n[1]; + nt[i][2] = -n[2]; + break; + } + } + + if (nt[i].dot(abc) > max_sp) + { + max_sp = nt[i].dot(abc); + max_i = i; + } + } + return nt[max_i]; + } + +}; + +class comparer +{ +public: + IGL_INLINE bool operator() (const std::pair& lhs, const std::pair&rhs) const + { + return lhs.second>rhs.second; + } +}; + +IGL_INLINE CurvatureCalculator::CurvatureCalculator() +{ + this->localMode=true; + this->projectionPlaneCheck=true; + this->sphereRadius=5; + this->st=SPHERE_SEARCH; + this->nt=AVERAGE; + this->montecarlo=false; + this->montecarloN=0; + this->kRing=3; + this->curvatureComputed=false; + this->expStep=true; +} + +IGL_INLINE void CurvatureCalculator::init(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F) +{ + // Normalize vertices + vertices = V; + +// vertices = vertices.array() - vertices.minCoeff(); +// vertices = vertices.array() / vertices.maxCoeff(); +// vertices = vertices.array() * (1.0/igl::avg_edge_length(V,F)); + + faces = F; + igl::adjacency_list(F, vertex_to_vertices); + igl::vertex_triangle_adjacency(V, F, vertex_to_faces, vertex_to_faces_index); + igl::per_face_normals(V, F, face_normals); + igl::per_vertex_normals(V, F, face_normals, vertex_normals); +} + +IGL_INLINE void CurvatureCalculator::fitQuadric(const Eigen::Vector3d& v, const std::vector& ref, const std::vector& vv, Quadric *q) +{ + std::vector points; + points.reserve (vv.size()); + + for (unsigned int i = 0; i < vv.size(); ++i) { + + Eigen::Vector3d cp = vertices.row(vv[i]); + + // vtang non e` il v tangente!!! + Eigen::Vector3d vTang = cp - v; + + double x = vTang.dot(ref[0]); + double y = vTang.dot(ref[1]); + double z = vTang.dot(ref[2]); + points.push_back(Eigen::Vector3d (x,y,z)); + } + if (points.size() < 5) + { + std::cerr << "ASSERT FAILED! fit function requires at least 5 points: Only " << points.size() << " were given." << std::endl; + *q = Quadric(0,0,0,0,0); + } + else + { + *q = Quadric::fit (points); + } +} + +IGL_INLINE void CurvatureCalculator::finalEigenStuff(int i, const std::vector& ref, Quadric& q) +{ + + const double a = q.a(); + const double b = q.b(); + const double c = q.c(); + const double d = q.d(); + const double e = q.e(); + +// if (fabs(a) < 10e-8 || fabs(b) < 10e-8) +// { +// std::cout << "Degenerate quadric: " << i << std::endl; +// } + + double E = 1.0 + d*d; + double F = d*e; + double G = 1.0 + e*e; + + Eigen::Vector3d n = Eigen::Vector3d(-d,-e,1.0).normalized(); + + double L = 2.0 * a * n[2]; + double M = b * n[2]; + double N = 2 * c * n[2]; + + + // ----------------- Eigen stuff + Eigen::Matrix2d m; + m << L*G - M*F, M*E-L*F, M*E-L*F, N*E-M*F; + m = m / (E*G-F*F); + Eigen::SelfAdjointEigenSolver eig(m); + + Eigen::Vector2d c_val = eig.eigenvalues(); + Eigen::Matrix2d c_vec = eig.eigenvectors(); + + // std::cerr << "c_val:" << c_val << std::endl; + // std::cerr << "c_vec:" << c_vec << std::endl; + + // std::cerr << "c_vec:" << c_vec(0) << " " << c_vec(1) << std::endl; + + c_val = -c_val; + + Eigen::Vector3d v1, v2; + v1[0] = c_vec(0); + v1[1] = c_vec(1); + v1[2] = 0; //d * v1[0] + e * v1[1]; + + v2[0] = c_vec(2); + v2[1] = c_vec(3); + v2[2] = 0; //d * v2[0] + e * v2[1]; + + + // v1 = v1.normalized(); + // v2 = v2.normalized(); + + Eigen::Vector3d v1global = ref[0] * v1[0] + ref[1] * v1[1] + ref[2] * v1[2]; + Eigen::Vector3d v2global = ref[0] * v2[0] + ref[1] * v2[1] + ref[2] * v2[2]; + + v1global.normalize(); + v2global.normalize(); + + v1global *= c_val(0); + v2global *= c_val(1); + + if (c_val[0] > c_val[1]) + { + curv[i]=std::vector(2); + curv[i][0]=c_val(1); + curv[i][1]=c_val(0); + curvDir[i]=std::vector(2); + curvDir[i][0]=v2global; + curvDir[i][1]=v1global; + } + else + { + curv[i]=std::vector(2); + curv[i][0]=c_val(0); + curv[i][1]=c_val(1); + curvDir[i]=std::vector(2); + curvDir[i][0]=v1global; + curvDir[i][1]=v2global; + } + // ---- end Eigen stuff +} + +IGL_INLINE void CurvatureCalculator::getKRing(const int start, const double r, std::vector&vv) +{ + int bufsize=vertices.rows(); + vv.reserve(bufsize); + std::list > queue; + bool* visited = (bool*)calloc(bufsize,sizeof(bool)); + queue.push_back(std::pair(start,0)); + visited[start]=true; + while (!queue.empty()) + { + int toVisit=queue.front().first; + int distance=queue.front().second; + queue.pop_front(); + vv.push_back(toVisit); + if (distance<(int)r) + { + for (unsigned int i=0; i (neighbor,distance+1)); + visited[neighbor]=true; + } + } + } + } + free(visited); + return; +} + + +IGL_INLINE void CurvatureCalculator::getSphere(const int start, const double r, std::vector &vv, int min) +{ + int bufsize=vertices.rows(); + vv.reserve(bufsize); + std::list* queue= new std::list(); + bool* visited = (bool*)calloc(bufsize,sizeof(bool)); + queue->push_back(start); + visited[start]=true; + Eigen::Vector3d me=vertices.row(start); + std::priority_queue, std::vector >, comparer >* extra_candidates= new std::priority_queue, std::vector >, comparer >(); + while (!queue->empty()) + { + int toVisit=queue->front(); + queue->pop_front(); + vv.push_back(toVisit); + for (unsigned int i=0; ipush_back(neighbor); + else if ((int)vv.size()push(std::pair(neighbor,distance)); + visited[neighbor]=true; + } + } + } + while (!extra_candidates->empty() && (int)vv.size() cand=extra_candidates->top(); + extra_candidates->pop(); + vv.push_back(cand.first); + for (unsigned int i=0; ipush(std::pair(neighbor,distance)); + visited[neighbor]=true; + } + } + } + free(extra_candidates); + free(queue); + free(visited); +} + +IGL_INLINE Eigen::Vector3d CurvatureCalculator::project(const Eigen::Vector3d& v, const Eigen::Vector3d& vp, const Eigen::Vector3d& ppn) +{ + return (vp - (ppn * ((vp - v).dot(ppn)))); +} + +IGL_INLINE void CurvatureCalculator::computeReferenceFrame(int i, const Eigen::Vector3d& normal, std::vector& ref ) +{ + + Eigen::Vector3d longest_v=Eigen::Vector3d(vertices.row(vertex_to_vertices[i][0])); + + longest_v=(project(vertices.row(i),longest_v,normal)-Eigen::Vector3d(vertices.row(i))).normalized(); + + /* L'ultimo asse si ottiene come prodotto vettoriale tra i due + * calcolati */ + Eigen::Vector3d y_axis=(normal.cross(longest_v)).normalized(); + ref[0]=longest_v; + ref[1]=y_axis; + ref[2]=normal; +} + +IGL_INLINE void CurvatureCalculator::getAverageNormal(int j, const std::vector& vv, Eigen::Vector3d& normal) +{ + normal=(vertex_normals.row(j)).normalized(); + if (localMode) + return; + + for (unsigned int i=0; i& vv, Eigen::Vector3d& ppn) +{ + int nr; + double a, b, c; + double nx, ny, nz; + double abcq; + + a = b = c = 0; + + if (localMode) + { + for (unsigned int i=0; i& vin, std::vector &vout) +{ + for (std::vector::const_iterator vpi = vin.begin(); vpi != vin.end(); ++vpi) + if (vertex_normals.row(*vpi) * ppn > 0.0) + vout.push_back(*vpi); +} + +IGL_INLINE void CurvatureCalculator::applyMontecarlo(const std::vector& vin, std::vector *vout) +{ + if (montecarloN >= vin.size ()) + { + *vout = vin; + return; + } + + float p = ((float) montecarloN) / (float) vin.size(); + for (std::vector::const_iterator vpi = vin.begin(); vpi != vin.end(); ++vpi) + { + float r; + if ((r = ((float)rand () / RAND_MAX)) < p) + { + vout->push_back(*vpi); + } + } +} + +IGL_INLINE void CurvatureCalculator::computeCurvature() +{ + //CHECK che esista la mesh + const size_t vertices_count=vertices.rows(); + + if (vertices_count ==0) + return; + + curvDir=std::vector< std::vector >(vertices_count); + curv=std::vector >(vertices_count); + + + + scaledRadius=getAverageEdge()*sphereRadius; + + std::vector vv; + std::vector vvtmp; + Eigen::Vector3d normal; + + //double time_spent; + //double searchtime=0, ref_time=0, fit_time=0, final_time=0; + + for (size_t i=0; i= 6 && vvtmp.size() ref(3); + computeReferenceFrame(i,normal,ref); + + Quadric q; + fitQuadric (me, ref, vv, &q); + finalEigenStuff(i,ref,q); + } + + lastRadius=sphereRadius; + curvatureComputed=true; +} + +IGL_INLINE void CurvatureCalculator::printCurvature(const std::string& outpath) +{ + using namespace std; + if (!curvatureComputed) + return; + + std::ofstream of; + of.open(outpath.c_str()); + + if (!of) + { + fprintf(stderr, "Error: could not open output file %s\n", outpath.c_str()); + return; + } + + int vertices_count=vertices.rows(); + of << vertices_count << endl; + for (int i=0; i +IGL_INLINE void igl::principal_curvature( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& PD1, + Eigen::PlainObjectBase& PD2, + Eigen::PlainObjectBase& PV1, + Eigen::PlainObjectBase& PV2, + unsigned radius, + bool useKring) +{ + if (radius < 2) + { + radius = 2; + std::cout << "WARNING: igl::principal_curvature needs a radius >= 2, fixing it to 2." << std::endl; + } + + // Preallocate memory + PD1.resize(V.rows(),3); + PD2.resize(V.rows(),3); + + // Preallocate memory + PV1.resize(V.rows(),1); + PV2.resize(V.rows(),1); + + // Precomputation + CurvatureCalculator cc; + cc.init(V.template cast(),F.template cast()); + cc.sphereRadius = radius; + + if (useKring) + { + cc.kRing = radius; + cc.st = K_RING_SEARCH; + } + + // Compute + cc.computeCurvature(); + + // Copy it back + for (unsigned i=0; i 10e-6) + { + std::cerr << "PRINCIPAL_CURVATURE: Something is wrong with vertex: " << i << std::endl; + PD1.row(i) *= 0; + PD2.row(i) *= 0; + } + } + +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::principal_curvature, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, unsigned int, bool); +template void igl::principal_curvature, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, unsigned int, bool); +template void igl::principal_curvature, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, unsigned int, bool); +#endif diff --git a/src/igl/principal_curvature.h b/src/igl/principal_curvature.h new file mode 100644 index 0000000000..958e584185 --- /dev/null +++ b/src/igl/principal_curvature.h @@ -0,0 +1,68 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PRINCIPAL_CURVATURE_H +#define IGL_PRINCIPAL_CURVATURE_H + + +#include +#include + +#include "igl_inline.h" +//#include +//#include + + + +namespace igl +{ + + // Compute the principal curvature directions and magnitude of the given triangle mesh + // DerivedV derived from vertex positions matrix type: i.e. MatrixXd + // DerivedF derived from face indices matrix type: i.e. MatrixXi + // Inputs: + // V eigen matrix #V by 3 + // F #F by 3 list of mesh faces (must be triangles) + // radius controls the size of the neighbourhood used, 1 = average edge length + // + // Outputs: + // PD1 #V by 3 maximal curvature direction for each vertex. + // PD2 #V by 3 minimal curvature direction for each vertex. + // PV1 #V by 1 maximal curvature value for each vertex. + // PV2 #V by 1 minimal curvature value for each vertex. + // + // See also: average_onto_faces, average_onto_vertices + // + // This function has been developed by: Nikolas De Giorgis, Luigi Rocca and Enrico Puppo. + // The algorithm is based on: + // Efficient Multi-scale Curvature and Crease Estimation + // Daniele Panozzo, Enrico Puppo, Luigi Rocca + // GraVisMa, 2010 +template < + typename DerivedV, + typename DerivedF, + typename DerivedPD1, + typename DerivedPD2, + typename DerivedPV1, + typename DerivedPV2> +IGL_INLINE void principal_curvature( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& PD1, + Eigen::PlainObjectBase& PD2, + Eigen::PlainObjectBase& PV1, + Eigen::PlainObjectBase& PV2, + unsigned radius = 5, + bool useKring = true); +} + + +#ifndef IGL_STATIC_LIBRARY +#include "principal_curvature.cpp" +#endif + +#endif diff --git a/src/igl/print_ijv.cpp b/src/igl/print_ijv.cpp new file mode 100644 index 0000000000..38b1dd22a3 --- /dev/null +++ b/src/igl/print_ijv.cpp @@ -0,0 +1,40 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "print_ijv.h" + +#include "find.h" +#include + +template +IGL_INLINE void igl::print_ijv( + const Eigen::SparseMatrix& X, + const int offset) +{ + Eigen::Matrix I; + Eigen::Matrix J; + Eigen::Matrix V; + igl::find(X,I,J,V); + // Concatenate I,J,V + Eigen::Matrix IJV(I.size(),3); + IJV.col(0) = I.cast(); + IJV.col(1) = J.cast(); + IJV.col(2) = V; + // Offset + if(offset != 0) + { + IJV.col(0).array() += offset; + IJV.col(1).array() += offset; + } + std::cout<(Eigen::SparseMatrix const&, int); +#endif diff --git a/src/igl/print_ijv.h b/src/igl/print_ijv.h new file mode 100644 index 0000000000..e45c1a76ca --- /dev/null +++ b/src/igl/print_ijv.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PRINT_IJV_H +#define IGL_PRINT_IJV_H +#include "igl_inline.h" +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include +namespace igl +{ + // Prints a 3 column matrix representing [I,J,V] = find(X). That is, each + // row is the row index, column index and value for each non zero entry. Each + // row is printed on a new line + // + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Input: + // X m by n matrix whose entries are to be sorted + // offset optional offset for I and J indices {0} + template + IGL_INLINE void print_ijv( + const Eigen::SparseMatrix& X, + const int offset=0); +} + +#ifndef IGL_STATIC_LIBRARY +# include "print_ijv.cpp" +#endif + +#endif diff --git a/src/igl/print_vector.cpp b/src/igl/print_vector.cpp new file mode 100644 index 0000000000..b47753216f --- /dev/null +++ b/src/igl/print_vector.cpp @@ -0,0 +1,59 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "print_vector.h" +#include +#include + + +template +IGL_INLINE void igl::print_vector( std::vector& v) +{ + using namespace std; + for (int i=0; i +IGL_INLINE void igl::print_vector( std::vector< std::vector >& v) +{ + using namespace std; + for (int i=0; i +IGL_INLINE void igl::print_vector( std::vector< std::vector< std::vector > >& v) +{ + using namespace std; + for (int m=0; m +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PRINT_VECTOR_H +#define IGL_PRINT_VECTOR_H +#include "igl_inline.h" + +#include +namespace igl +{ + // Not clear what these are supposed to be doing. Currently they print + // vectors to standard error... + template + IGL_INLINE void print_vector( std::vector& v); + template + IGL_INLINE void print_vector( std::vector< std::vector >& v); + template + IGL_INLINE void print_vector(std::vector< std::vector< std::vector > >& v); +} + +#ifndef IGL_STATIC_LIBRARY +# include "print_vector.cpp" +#endif + +#endif diff --git a/src/igl/procrustes.cpp b/src/igl/procrustes.cpp new file mode 100644 index 0000000000..659eea340b --- /dev/null +++ b/src/igl/procrustes.cpp @@ -0,0 +1,140 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Stefan Brugger +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "procrustes.h" +#include "polar_svd.h" +#include "polar_dec.h" + +template < + typename DerivedX, + typename DerivedY, + typename Scalar, + typename DerivedR, + typename DerivedT> +IGL_INLINE void igl::procrustes( + const Eigen::PlainObjectBase& X, + const Eigen::PlainObjectBase& Y, + bool includeScaling, + bool includeReflections, + Scalar& scale, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& t) +{ + using namespace Eigen; + assert (X.rows() == Y.rows() && "Same number of points"); + assert(X.cols() == Y.cols() && "Points have same dimensions"); + + // Center data + const VectorXd Xmean = X.colwise().mean(); + const VectorXd Ymean = Y.colwise().mean(); + MatrixXd XC = X.rowwise() - Xmean.transpose(); + MatrixXd YC = Y.rowwise() - Ymean.transpose(); + + // Scale + scale = 1.; + if (includeScaling) + { + double scaleX = XC.norm() / XC.rows(); + double scaleY = YC.norm() / YC.rows(); + scale = scaleY/scaleX; + XC *= scale; + assert (std::abs(XC.norm() / XC.rows() - scaleY) < 1e-8); + } + + // Rotation + MatrixXd S = XC.transpose() * YC; + MatrixXd T; + if (includeReflections) + { + polar_dec(S,R,T); + }else + { + polar_svd(S,R,T); + } +// R.transposeInPlace(); + + // Translation + t = Ymean - scale*R.transpose()*Xmean; +} + + +template < + typename DerivedX, + typename DerivedY, + typename Scalar, + int DIM, + int TType> +IGL_INLINE void igl::procrustes( + const Eigen::PlainObjectBase& X, + const Eigen::PlainObjectBase& Y, + bool includeScaling, + bool includeReflections, + Eigen::Transform& T) +{ + using namespace Eigen; + double scale; + MatrixXd R; + VectorXd t; + procrustes(X,Y,includeScaling,includeReflections,scale,R,t); + + // Combine + T = Translation(t) * R * Scaling(scale); +} + +template < + typename DerivedX, + typename DerivedY, + typename DerivedR, + typename DerivedT> +IGL_INLINE void igl::procrustes( + const Eigen::PlainObjectBase& X, + const Eigen::PlainObjectBase& Y, + bool includeScaling, + bool includeReflections, + Eigen::PlainObjectBase& S, + Eigen::PlainObjectBase& t) +{ + double scale; + procrustes(X,Y,includeScaling,includeReflections,scale,S,t); + S *= scale; +} + +template < + typename DerivedX, + typename DerivedY, + typename DerivedR, + typename DerivedT> +IGL_INLINE void igl::procrustes( + const Eigen::PlainObjectBase& X, + const Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& t) +{ + procrustes(X,Y,false,false,R,t); +} + +template < + typename DerivedX, + typename DerivedY, + typename Scalar, + typename DerivedT> +IGL_INLINE void igl::procrustes( + const Eigen::PlainObjectBase& X, + const Eigen::PlainObjectBase& Y, + Eigen::Rotation2D& R, + Eigen::PlainObjectBase& t) +{ + using namespace Eigen; + assert (X.cols() == 2 && Y.cols() == 2 && "Points must have dimension 2"); + Matrix2d Rmat; + procrustes(X,Y,false,false,Rmat,t); + R.fromRotationMatrix(Rmat); +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::procrustes, Eigen::Matrix, double, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool, bool, double&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/procrustes.h b/src/igl/procrustes.h new file mode 100644 index 0000000000..5ce00db497 --- /dev/null +++ b/src/igl/procrustes.h @@ -0,0 +1,137 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Stefan Brugger +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PROCRUSTES_H +#define IGL_PROCRUSTES_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Solve Procrustes problem in d dimensions. Given two point sets X,Y in R^d + // find best scale s, orthogonal R and translation t s.t. |s*X*R + t - Y|^2 + // is minimized. + // + // Templates: + // DerivedV point type + // Scalar scalar type + // DerivedR type of R + // DerivedT type of t + // Inputs: + // X #V by DIM first list of points + // Y #V by DIM second list of points + // includeScaling if scaling should be allowed + // includeReflections if R is allowed to be a reflection + // Outputs: + // scale scaling + // R orthogonal matrix + // t translation + // + // Example: + // MatrixXd X, Y; (containing 3d points as rows) + // double scale; + // MatrixXd R; + // VectorXd t; + // igl::procrustes(X,Y,true,false,scale,R,t); + // R *= scale; + // MatrixXd Xprime = (X * R).rowwise() + t.transpose(); + // + template < + typename DerivedX, + typename DerivedY, + typename Scalar, + typename DerivedR, + typename DerivedT> + IGL_INLINE void procrustes( + const Eigen::PlainObjectBase& X, + const Eigen::PlainObjectBase& Y, + bool includeScaling, + bool includeReflections, + Scalar& scale, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& t); + // Same as above but returns Eigen transformation object. + // + // Templates: + // DerivedV point type + // Scalar scalar type + // DIM point dimension + // TType type of transformation + // (Isometry,Affine,AffineCompact,Projective) + // Inputs: + // X #V by DIM first list of points + // Y #V by DIM second list of points + // includeScaling if scaling should be allowed + // includeReflections if R is allowed to be a reflection + // Outputs: + // T transformation that minimizes error + // + // Example: + // MatrixXd X, Y; (containing 3d points as rows) + // AffineCompact3d T; + // igl::procrustes(X,Y,true,false,T); + // MatrixXd Xprime = (X * T.linear()).rowwise() + T.translation().transpose(); + template < + typename DerivedX, + typename DerivedY, + typename Scalar, + int DIM, + int TType> + IGL_INLINE void procrustes( + const Eigen::PlainObjectBase& X, + const Eigen::PlainObjectBase& Y, + bool includeScaling, + bool includeReflections, + Eigen::Transform& T); + + + // Convenient wrapper that returns S=scale*R instead of scale and R separately + template < + typename DerivedX, + typename DerivedY, + typename DerivedR, + typename DerivedT> + IGL_INLINE void procrustes( + const Eigen::PlainObjectBase& X, + const Eigen::PlainObjectBase& Y, + bool includeScaling, + bool includeReflections, + Eigen::PlainObjectBase& S, + Eigen::PlainObjectBase& t); + + // Convenient wrapper for rigid case (no scaling, no reflections) + template < + typename DerivedX, + typename DerivedY, + typename DerivedR, + typename DerivedT> + IGL_INLINE void procrustes( + const Eigen::PlainObjectBase& X, + const Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& R, + Eigen::PlainObjectBase& t); + + // Convenient wrapper for 2D case. + template < + typename DerivedX, + typename DerivedY, + typename Scalar, + typename DerivedT> + IGL_INLINE void procrustes( + const Eigen::PlainObjectBase& X, + const Eigen::PlainObjectBase& Y, + Eigen::Rotation2D& R, + Eigen::PlainObjectBase& t); +} + +#ifndef IGL_STATIC_LIBRARY + #include "procrustes.cpp" +#endif + +#endif diff --git a/src/igl/project.cpp b/src/igl/project.cpp new file mode 100644 index 0000000000..e57004f3c8 --- /dev/null +++ b/src/igl/project.cpp @@ -0,0 +1,59 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "project.h" + +template +Eigen::Matrix igl::project( + const Eigen::Matrix& obj, + const Eigen::Matrix& model, + const Eigen::Matrix& proj, + const Eigen::Matrix& viewport) +{ + Eigen::Matrix tmp; + tmp << obj,1; + + tmp = model * tmp; + + tmp = proj * tmp; + + tmp = tmp.array() / tmp(3); + tmp = tmp.array() * 0.5f + 0.5f; + tmp(0) = tmp(0) * viewport(2) + viewport(0); + tmp(1) = tmp(1) * viewport(3) + viewport(1); + + return tmp.head(3); +} + +template +IGL_INLINE void igl::project( + const Eigen::PlainObjectBase& V, + const Eigen::MatrixBase& model, + const Eigen::MatrixBase& proj, + const Eigen::MatrixBase& viewport, + Eigen::PlainObjectBase & P) +{ + typedef typename DerivedP::Scalar PScalar; + Eigen::Matrix HV(V.rows(),4); + HV.leftCols(3) = V.template cast(); + HV.col(3).setConstant(1); + HV = (HV*model.template cast().transpose()* + proj.template cast().transpose()).eval(); + HV = (HV.array().colwise()/HV.col(3).array()).eval(); + HV = (HV.array() * 0.5 + 0.5).eval(); + HV.col(0) = (HV.array().col(0) * viewport(2) + viewport(0)).eval(); + HV.col(1) = (HV.array().col(1) * viewport(3) + viewport(1)).eval(); + P = HV.leftCols(3); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template Eigen::Matrix igl::project(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); +template Eigen::Matrix igl::project(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); +template void igl::project, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix>(const Eigen::PlainObjectBase>&, const Eigen::MatrixBase>&, const Eigen::MatrixBase>&, const Eigen::MatrixBase>&, Eigen::PlainObjectBase>&); +template void igl::project, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix>(const Eigen::PlainObjectBase>&, const Eigen::MatrixBase>&, const Eigen::MatrixBase>&, const Eigen::MatrixBase>&, Eigen::PlainObjectBase>&); +#endif diff --git a/src/igl/project.h b/src/igl/project.h new file mode 100644 index 0000000000..e2ee27a151 --- /dev/null +++ b/src/igl/project.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PROJECT_H +#define IGL_PROJECT_H +#include "igl_inline.h" +#include +namespace igl +{ + // Eigen reimplementation of gluProject + // Inputs: + // obj* 3D objects' x, y, and z coordinates respectively + // model model matrix + // proj projection matrix + // viewport viewport vector + // Returns: + // screen space x, y, and z coordinates respectively + template + IGL_INLINE Eigen::Matrix project( + const Eigen::Matrix& obj, + const Eigen::Matrix& model, + const Eigen::Matrix& proj, + const Eigen::Matrix& viewport); + // Inputs: + // V #V by 3 list of object points + // model model matrix + // proj projection matrix + // viewport viewport vector + // Outputs: + // P #V by 3 list of screen space points + template + IGL_INLINE void project( + const Eigen::PlainObjectBase& V, + const Eigen::MatrixBase& model, + const Eigen::MatrixBase& proj, + const Eigen::MatrixBase& viewport, + Eigen::PlainObjectBase & P); +} + +#ifndef IGL_STATIC_LIBRARY +# include "project.cpp" +#endif + +#endif diff --git a/src/igl/project_isometrically_to_plane.cpp b/src/igl/project_isometrically_to_plane.cpp new file mode 100644 index 0000000000..537629caa4 --- /dev/null +++ b/src/igl/project_isometrically_to_plane.cpp @@ -0,0 +1,63 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "project_isometrically_to_plane.h" +#include "edge_lengths.h" + +template < + typename DerivedV, + typename DerivedF, + typename DerivedU, + typename DerivedUF, + typename Scalar> +IGL_INLINE void igl::project_isometrically_to_plane( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & UF, + Eigen::SparseMatrix& I) +{ + using namespace std; + using namespace Eigen; + assert(F.cols() == 3 && "F should contain triangles"); + typedef DerivedV MatrixX; + MatrixX l; + edge_lengths(V,F,l); + // Number of faces + const int m = F.rows(); + + // First corner at origin + U = DerivedU::Zero(m*3,2); + // Second corner along x-axis + U.block(m,0,m,1) = l.col(2); + // Third corner rotated onto plane + U.block(m*2,0,m,1) = + (-l.col(0).array().square() + + l.col(1).array().square() + + l.col(2).array().square())/(2.*l.col(2).array()); + U.block(m*2,1,m,1) = + (l.col(1).array().square()-U.block(m*2,0,m,1).array().square()).sqrt(); + + typedef Triplet IJV; + vector ijv; + ijv.reserve(3*m); + UF.resize(m,3); + for(int f = 0;f, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/project_isometrically_to_plane.h b/src/igl/project_isometrically_to_plane.h new file mode 100644 index 0000000000..388f6c3071 --- /dev/null +++ b/src/igl/project_isometrically_to_plane.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PROJECT_ISOMETRICALLY_TO_PLANE_H +#define IGL_PROJECT_ISOMETRICALLY_TO_PLANE_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Project each triangle to the plane + // + // [U,UF,I] = project_isometrically_to_plane(V,F) + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of mesh indices + // Outputs: + // U #F*3 by 2 list of triangle positions + // UF #F by 3 list of mesh indices into U + // I #V by #F such that I(i,j) = 1 implies U(j,:) corresponds to V(i,:) + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedU, + typename DerivedUF, + typename Scalar> + IGL_INLINE void project_isometrically_to_plane( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & UF, + Eigen::SparseMatrix& I); +} + +#ifndef IGL_STATIC_LIBRARY +# include "project_isometrically_to_plane.cpp" +#endif + +#endif + diff --git a/src/igl/project_to_line.cpp b/src/igl/project_to_line.cpp new file mode 100644 index 0000000000..a7f3a690ed --- /dev/null +++ b/src/igl/project_to_line.cpp @@ -0,0 +1,139 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "project_to_line.h" +#include +#include + +template < + typename DerivedP, + typename DerivedS, + typename DerivedD, + typename Derivedt, + typename DerivedsqrD> +IGL_INLINE void igl::project_to_line( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & S, + const Eigen::MatrixBase & D, + Eigen::PlainObjectBase & t, + Eigen::PlainObjectBase & sqrD) +{ + // http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html + + // number of dimensions +#ifndef NDEBUG + int dim = P.cols(); + assert(dim == S.size()); + assert(dim == D.size()); +#endif + // number of points + int np = P.rows(); + // vector from source to destination + DerivedD DmS = D-S; + double v_sqrlen = (double)(DmS.squaredNorm()); + assert(v_sqrlen != 0); + // resize output + t.resize(np,1); + sqrD.resize(np,1); + // loop over points +#pragma omp parallel for if (np>10000) + for(int i = 0;i +IGL_INLINE void igl::project_to_line( + const Scalar px, + const Scalar py, + const Scalar pz, + const Scalar sx, + const Scalar sy, + const Scalar sz, + const Scalar dx, + const Scalar dy, + const Scalar dz, + Scalar & projpx, + Scalar & projpy, + Scalar & projpz, + Scalar & t, + Scalar & sqrd) +{ + // vector from source to destination + Scalar dms[3]; + dms[0] = dx-sx; + dms[1] = dy-sy; + dms[2] = dz-sz; + Scalar v_sqrlen = dms[0]*dms[0] + dms[1]*dms[1] + dms[2]*dms[2]; + // line should have some length + assert(v_sqrlen != 0); + // vector from point to source + Scalar smp[3]; + smp[0] = sx-px; + smp[1] = sy-py; + smp[2] = sz-pz; + t = -(dms[0]*smp[0]+dms[1]*smp[1]+dms[2]*smp[2])/v_sqrlen; + // P projectred onto line + projpx = (1.0-t)*sx + t*dx; + projpy = (1.0-t)*sy + t*dy; + projpz = (1.0-t)*sz + t*dz; + // vector from projected point to p + Scalar pmprojp[3]; + pmprojp[0] = px-projpx; + pmprojp[1] = py-projpy; + pmprojp[2] = pz-projpz; + sqrd = pmprojp[0]*pmprojp[0] + pmprojp[1]*pmprojp[1] + pmprojp[2]*pmprojp[2]; +} + +template +IGL_INLINE void igl::project_to_line( + const Scalar px, + const Scalar py, + const Scalar pz, + const Scalar sx, + const Scalar sy, + const Scalar sz, + const Scalar dx, + const Scalar dy, + const Scalar dz, + Scalar & t, + Scalar & sqrd) +{ + Scalar projpx; + Scalar projpy; + Scalar projpz; + return igl::project_to_line( + px, py, pz, sx, sy, sz, dx, dy, dz, + projpx, projpy, projpz, t, sqrd); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::project_to_line, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::project_to_line, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::project_to_line, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::project_to_line const, -1, -1, false>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase const, -1, -1, false> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::project_to_line const, -1, -1, false>, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase const, -1, -1, false> > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::project_to_line, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::project_to_line(double, double, double, double, double, double, double, double, double, double&, double&); +template void igl::project_to_line(double, double, double, double, double, double, double, double, double, double&, double&,double&,double&, double&); +template void igl::project_to_line, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::project_to_line, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/project_to_line.h b/src/igl/project_to_line.h new file mode 100644 index 0000000000..d11399a76e --- /dev/null +++ b/src/igl/project_to_line.h @@ -0,0 +1,83 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PROJECT_TO_LINE_H +#define IGL_PROJECT_TO_LINE_H +#include "igl_inline.h" +#include + +namespace igl +{ + // PROJECT_TO_LINE project points onto vectors, that is find the parameter + // t for a point p such that proj_p = (y-x).*t, additionally compute the + // squared distance from p to the line of the vector, such that + // |p - proj_p|² = sqr_d + // + // [T,sqrD] = project_to_line(P,S,D) + // + // Inputs: + // P #P by dim list of points to be projected + // S size dim start position of line vector + // D size dim destination position of line vector + // Outputs: + // T #P by 1 list of parameters + // sqrD #P by 1 list of squared distances + // + // + template < + typename DerivedP, + typename DerivedS, + typename DerivedD, + typename Derivedt, + typename DerivedsqrD> + IGL_INLINE void project_to_line( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & S, + const Eigen::MatrixBase & D, + Eigen::PlainObjectBase & t, + Eigen::PlainObjectBase & sqrD); + + // Same as above but for a single query point + template + IGL_INLINE void project_to_line( + const Scalar px, + const Scalar py, + const Scalar pz, + const Scalar sx, + const Scalar sy, + const Scalar sz, + const Scalar dx, + const Scalar dy, + const Scalar dz, + Scalar & projpx, + Scalar & projpy, + Scalar & projpz, + Scalar & t, + Scalar & sqrd); + + // Same as above but for a single query point + template + IGL_INLINE void project_to_line( + const Scalar px, + const Scalar py, + const Scalar pz, + const Scalar sx, + const Scalar sy, + const Scalar sz, + const Scalar dx, + const Scalar dy, + const Scalar dz, + Scalar & t, + Scalar & sqrd); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "project_to_line.cpp" +#endif + +#endif diff --git a/src/igl/project_to_line_segment.cpp b/src/igl/project_to_line_segment.cpp new file mode 100644 index 0000000000..71866fa33e --- /dev/null +++ b/src/igl/project_to_line_segment.cpp @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "project_to_line_segment.h" +#include "project_to_line.h" +#include + +template < + typename DerivedP, + typename DerivedS, + typename DerivedD, + typename Derivedt, + typename DerivedsqrD> +IGL_INLINE void igl::project_to_line_segment( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & S, + const Eigen::MatrixBase & D, + Eigen::PlainObjectBase & t, + Eigen::PlainObjectBase & sqrD) +{ + project_to_line(P,S,D,t,sqrD); + const int np = P.rows(); + // loop over points and fix those that projected beyond endpoints +#pragma omp parallel for if (np>10000) + for(int p = 0;p1) + { + sqrD(p) = (Pp-D).squaredNorm(); + t(p) = 1; + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::project_to_line_segment, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::project_to_line_segment, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::project_to_line_segment, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::project_to_line_segment, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::project_to_line_segment, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/project_to_line_segment.h b/src/igl/project_to_line_segment.h new file mode 100644 index 0000000000..2fe33f2d40 --- /dev/null +++ b/src/igl/project_to_line_segment.h @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PROJECT_TO_LINE_SEGMENT_H +#define IGL_PROJECT_TO_LINE_SEGMENT_H +#include "igl_inline.h" +#include + +namespace igl +{ + // PROJECT_TO_LINE_SEGMENT project points onto vectors, that is find the parameter + // t for a point p such that proj_p = (y-x).*t, additionally compute the + // squared distance from p to the line of the vector, such that + // |p - proj_p|² = sqr_d + // + // [T,sqrD] = project_to_line_segment(P,S,D) + // + // Inputs: + // P #P by dim list of points to be projected + // S size dim start position of line vector + // D size dim destination position of line vector + // Outputs: + // T #P by 1 list of parameters + // sqrD #P by 1 list of squared distances + // + // + template < + typename DerivedP, + typename DerivedS, + typename DerivedD, + typename Derivedt, + typename DerivedsqrD> + IGL_INLINE void project_to_line_segment( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & S, + const Eigen::MatrixBase & D, + Eigen::PlainObjectBase & t, + Eigen::PlainObjectBase & sqrD); +} + +#ifndef IGL_STATIC_LIBRARY +# include "project_to_line_segment.cpp" +#endif + +#endif + diff --git a/src/igl/pseudonormal_test.cpp b/src/igl/pseudonormal_test.cpp new file mode 100644 index 0000000000..b56fdf7baf --- /dev/null +++ b/src/igl/pseudonormal_test.cpp @@ -0,0 +1,224 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "pseudonormal_test.h" +#include "barycentric_coordinates.h" +#include "doublearea.h" +#include "project_to_line_segment.h" +#include +template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedVN, + typename DerivedEN, + typename DerivedEMAP, + typename Derivedq, + typename Derivedc, + typename Scalar, + typename Derivedn> +IGL_INLINE void igl::pseudonormal_test( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & FN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & EMAP, + const Eigen::MatrixBase & q, + const int f, + Eigen::PlainObjectBase & c, + Scalar & s, + Eigen::PlainObjectBase & n) +{ + using namespace Eigen; + const auto & qc = q-c; + typedef Eigen::Matrix RowVector3S; + RowVector3S b; + // Using barycentric coorindates to determine whether close to a vertex/edge + // seems prone to error when dealing with nearly degenerate triangles: Even + // the barycenter (1/3,1/3,1/3) can be made arbitrarily close to an + // edge/vertex + // + const RowVector3S A = V.row(F(f,0)); + const RowVector3S B = V.row(F(f,1)); + const RowVector3S C = V.row(F(f,2)); + + const double area = [&A,&B,&C]() + { + Matrix area; + doublearea(A,B,C,area); + return area(0); + }(); + // These were chosen arbitrarily. In a floating point scenario, I'm not sure + // the best way to determine if c is on a vertex/edge or in the middle of the + // face: specifically, I'm worrying about degenerate triangles where + // barycentric coordinates are error-prone. + const double MIN_DOUBLE_AREA = 1e-4; + const double epsilon = 1e-12; + if(area>MIN_DOUBLE_AREA) + { + barycentric_coordinates( c,A,B,C,b); + // Determine which normal to use + const int type = (b.array()<=epsilon).template cast().sum(); + switch(type) + { + case 2: + // Find vertex + for(int x = 0;x<3;x++) + { + if(b(x)>epsilon) + { + n = VN.row(F(f,x)); + break; + } + } + break; + case 1: + // Find edge + for(int x = 0;x<3;x++) + { + if(b(x)<=epsilon) + { + n = EN.row(EMAP(F.rows()*x+f)); + break; + } + } + break; + default: + assert(false && "all barycentric coords zero."); + case 0: + n = FN.row(f); + break; + } + }else + { + // Check each vertex + bool found = false; + for(int v = 0;v<3 && !found;v++) + { + if( (c-V.row(F(f,v))).norm() < epsilon) + { + found = true; + n = VN.row(F(f,v)); + } + } + // Check each edge + for(int e = 0;e<3 && !found;e++) + { + const RowVector3S s = V.row(F(f,(e+1)%3)); + const RowVector3S d = V.row(F(f,(e+2)%3)); + Matrix sqr_d_j_x(1,1); + Matrix t(1,1); + project_to_line_segment(c,s,d,t,sqr_d_j_x); + if(sqrt(sqr_d_j_x(0)) < epsilon) + { + n = EN.row(EMAP(F.rows()*e+f)); + found = true; + } + } + // Finally just use face + if(!found) + { + n = FN.row(f); + } + } + s = (qc.dot(n) >= 0 ? 1. : -1.); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedEN, + typename DerivedVN, + typename Derivedq, + typename Derivedc, + typename Scalar, + typename Derivedn> +IGL_INLINE void igl::pseudonormal_test( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & q, + const int e, + Eigen::PlainObjectBase & c, + Scalar & s, + Eigen::PlainObjectBase & n) +{ + using namespace Eigen; + const auto & qc = q-c; + const double len = (V.row(E(e,1))-V.row(E(e,0))).norm(); + // barycentric coordinates + // this .head() nonsense is for "ridiculus" templates instantiations that AABB + // needs to compile + Eigen::Matrix + b((c-V.row(E(e,1))).norm()/len,(c-V.row(E(e,0))).norm()/len); + //b((c-V.row(E(e,1)).head(c.size())).norm()/len,(c-V.row(E(e,0)).head(c.size())).norm()/len); + // Determine which normal to use + const double epsilon = 1e-12; + const int type = (b.array()<=epsilon).template cast().sum(); + switch(type) + { + case 1: + // Find vertex + for(int x = 0;x<2;x++) + { + if(b(x)>epsilon) + { + n = VN.row(E(e,x)).head(2); + break; + } + } + break; + default: + assert(false && "all barycentric coords zero."); + case 0: + n = EN.row(e).head(2); + break; + } + s = (qc.dot(n) >= 0 ? 1. : -1.); +} + +// This is a bullshit template because AABB annoyingly needs templates for bad +// combinations of 3D V with DIM=2 AABB +// +// _Define_ as a no-op rather than monkeying around with the proper code above +namespace igl +{ + template <> IGL_INLINE void pseudonormal_test(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, float&, Eigen::PlainObjectBase >&){assert(false);}; + template <> IGL_INLINE void pseudonormal_test(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, float&, Eigen::PlainObjectBase >&){assert(false);}; + template <> IGL_INLINE void pseudonormal_test(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, double&, Eigen::PlainObjectBase >&){assert(false);}; + template <> IGL_INLINE void pseudonormal_test(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, double&, Eigen::PlainObjectBase >&){assert(false);}; + template <> IGL_INLINE void pseudonormal_test(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, float&, Eigen::PlainObjectBase >&) {assert(false);}; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, double&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, float, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, float&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +// generated by autoexplicit.sh +// generated by autoexplicit.sh +// generated by autoexplicit.sh +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, float, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, float&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, float, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, float&, Eigen::PlainObjectBase >&); +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, double&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, double&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, double&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Block, 1, -1, false>, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase, 1, -1, false> > const&, int, Eigen::PlainObjectBase >&, double&, Eigen::PlainObjectBase >&); +//template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, float, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, float&, Eigen::PlainObjectBase >&); +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, float, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, float&, Eigen::PlainObjectBase >&); +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, double&, Eigen::PlainObjectBase >&); +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, double&, Eigen::PlainObjectBase >&); +template void igl::pseudonormal_test, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, double&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/pseudonormal_test.h b/src/igl/pseudonormal_test.h new file mode 100644 index 0000000000..fc07946fbf --- /dev/null +++ b/src/igl/pseudonormal_test.h @@ -0,0 +1,78 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_PSEUDONORMAL_TEST_H +#define IGL_PSEUDONORMAL_TEST_H +#include "igl_inline.h" +#include +namespace igl +{ + // Given a mesh (V,F), a query point q, and a point on (V,F) c, determine + // whether q is inside (V,F) --> s=-1 or outside (V,F) s=1, based on the + // sign of the dot product between (q-c) and n, where n is the normal _at c_, + // carefully chosen according to [Bærentzen & Aanæs 2005] + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // FN #F by 3 list of triangle normals + // VN #V by 3 list of vertex normals (ANGLE WEIGHTING) + // EN #E by 3 list of edge normals (UNIFORM WEIGHTING) + // EMAP #F*3 mapping edges in F to E + // q Query point + // f index into F to face to which c belongs + // c Point on (V,F) + // Outputs: + // s sign + // n normal + template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedVN, + typename DerivedEN, + typename DerivedEMAP, + typename Derivedq, + typename Derivedc, + typename Scalar, + typename Derivedn> + IGL_INLINE void pseudonormal_test( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & FN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & EMAP, + const Eigen::MatrixBase & q, + const int f, + Eigen::PlainObjectBase & c, + Scalar & s, + Eigen::PlainObjectBase & n); + template < + typename DerivedV, + typename DerivedF, + typename DerivedEN, + typename DerivedVN, + typename Derivedq, + typename Derivedc, + typename Scalar, + typename Derivedn> + IGL_INLINE void pseudonormal_test( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & q, + const int e, + Eigen::PlainObjectBase & c, + Scalar & s, + Eigen::PlainObjectBase & n); +} +#ifndef IGL_STATIC_LIBRARY +# include "pseudonormal_test.cpp" +#endif +#endif diff --git a/src/igl/pso.cpp b/src/igl/pso.cpp new file mode 100644 index 0000000000..bf24a3a8c9 --- /dev/null +++ b/src/igl/pso.cpp @@ -0,0 +1,174 @@ +#include "pso.h" +#include +#include +#include +#include + +template < + typename Scalar, + typename DerivedX, + typename DerivedLB, + typename DerivedUB> +IGL_INLINE Scalar igl::pso( + const std::function< Scalar (DerivedX &) > f, + const Eigen::MatrixBase & LB, + const Eigen::MatrixBase & UB, + const int max_iters, + const int population, + DerivedX & X) +{ + const Eigen::Array P = + Eigen::Array::Zero(LB.size(),1); + return igl::pso(f,LB,UB,P,max_iters,population,X); +} + +template < + typename Scalar, + typename DerivedX, + typename DerivedLB, + typename DerivedUB, + typename DerivedP> +IGL_INLINE Scalar igl::pso( + const std::function< Scalar (DerivedX &) > f, + const Eigen::MatrixBase & LB, + const Eigen::MatrixBase & UB, + const Eigen::DenseBase & P, + const int max_iters, + const int population, + DerivedX & X) +{ + const int dim = LB.size(); + assert(UB.size() == dim && "UB should match LB size"); + assert(P.size() == dim && "P should match LB size"); + typedef std::vector > VectorList; + VectorList position(population); + VectorList best_position(population); + VectorList velocity(population); + Eigen::Matrix best_f(population); + // https://en.wikipedia.org/wiki/Particle_swarm_optimization#Algorithm + // + // g → X + // p_i → best[i] + // v_i → velocity[i] + // x_i → position[i] + Scalar min_f = std::numeric_limits::max(); + for(int p=0;p UB(d)) + { + position[p](d) = UB(d); + if(velocity[p](d) > 0.0) velocity[p](d) *= -1.0; + } +#else +//#warning "trying no bounds on periodic" +// // TODO: I'm not sure this is the right thing to do/enough. The +// // velocities could be weird. Suppose the current "best" value is ε and +// // the value is -ε and the "periodic bounds" [0,2π]. Moding will send +// // the value to 2π-ε but the "velocity" term will now be huge pointing +// // all the way from 2π-ε to ε. +// // +// // Q: Would it be enough to try (all combinations) of ±(UB-LB) before +// // computing velocities to "best"s? In the example above, instead of +// // +// // v += best - p = ε - (2π-ε) = -2π+2ε +// // +// // you'd use +// // +// // v += / argmin |b - p| \ - p = (ε+2π)-(2π-ε) = 2ε +// // | | +// // \ b∈{best, best+2π, best-2π} / +// // +// // Though, for multivariate b,p,v this would seem to explode +// // combinatorially. +// // +// // Maybe periodic things just shouldn't be bounded and we hope that the +// // forces toward the current minima "regularize" them away from insane +// // values. +// if(P(d)) +// { +// position[p](d) = std::fmod(position[p](d)-LB(d),UB(d)-LB(d))+LB(d); +// }else +// { +// position[p](d) = std::max(LB(d),std::min(UB(d),position[p](d))); +// } + position[p](d) = std::max(LB(d),std::min(UB(d),position[p](d))); +#endif + } + const Scalar fp = f(position[p]); + if(fp=max_iters) + { + break; + } + } + return min_f; +} + +#ifdef IGL_STATIC_LIBRARY +template float igl::pso, Eigen::Matrix, Eigen::Matrix >(std::function&)>, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, int, Eigen::Matrix&); +#endif diff --git a/src/igl/pso.h b/src/igl/pso.h new file mode 100644 index 0000000000..d2a2ccdffa --- /dev/null +++ b/src/igl/pso.h @@ -0,0 +1,59 @@ +#ifndef IGL_PSO_H +#define IGL_PSO_H +#include +#include +#include + +namespace igl +{ + // Solve the problem: + // + // minimize f(x) + // subject to lb ≤ x ≤ ub + // + // by particle swarm optimization (PSO). + // + // Inputs: + // f function that evaluates the objective for a given "particle" location + // LB #X vector of lower bounds + // UB #X vector of upper bounds + // max_iters maximum number of iterations + // population number of particles in swarm + // Outputs: + // X best particle seen so far + // Returns objective corresponding to best particle seen so far + template < + typename Scalar, + typename DerivedX, + typename DerivedLB, + typename DerivedUB> + IGL_INLINE Scalar pso( + const std::function< Scalar (DerivedX &) > f, + const Eigen::MatrixBase & LB, + const Eigen::MatrixBase & UB, + const int max_iters, + const int population, + DerivedX & X); + // Inputs: + // P whether each DOF is periodic + template < + typename Scalar, + typename DerivedX, + typename DerivedLB, + typename DerivedUB, + typename DerivedP> + IGL_INLINE Scalar pso( + const std::function< Scalar (DerivedX &) > f, + const Eigen::MatrixBase & LB, + const Eigen::MatrixBase & UB, + const Eigen::DenseBase & P, + const int max_iters, + const int population, + DerivedX & X); +} + +#ifndef IGL_STATIC_LIBRARY +# include "pso.cpp" +#endif + +#endif diff --git a/src/igl/qslim.cpp b/src/igl/qslim.cpp new file mode 100644 index 0000000000..e76db5f0b8 --- /dev/null +++ b/src/igl/qslim.cpp @@ -0,0 +1,120 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "qslim.h" + +#include "collapse_edge.h" +#include "connect_boundary_to_infinity.h" +#include "decimate.h" +#include "edge_flaps.h" +#include "is_edge_manifold.h" +#include "max_faces_stopping_condition.h" +#include "per_vertex_point_to_plane_quadrics.h" +#include "qslim_optimal_collapse_edge_callbacks.h" +#include "quadric_binary_plus_operator.h" +#include "remove_unreferenced.h" +#include "slice.h" +#include "slice_mask.h" + +IGL_INLINE bool igl::qslim( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const size_t max_m, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J, + Eigen::VectorXi & I) +{ + using namespace igl; + + // Original number of faces + const int orig_m = F.rows(); + // Tracking number of faces + int m = F.rows(); + typedef Eigen::MatrixXd DerivedV; + typedef Eigen::MatrixXi DerivedF; + DerivedV VO; + DerivedF FO; + igl::connect_boundary_to_infinity(V,F,VO,FO); + // decimate will not work correctly on non-edge-manifold meshes. By extension + // this includes meshes with non-manifold vertices on the boundary since these + // will create a non-manifold edge when connected to infinity. + if(!is_edge_manifold(FO)) + { + return false; + } + Eigen::VectorXi EMAP; + Eigen::MatrixXi E,EF,EI; + edge_flaps(FO,E,EMAP,EF,EI); + // Quadrics per vertex + typedef std::tuple Quadric; + std::vector quadrics; + per_vertex_point_to_plane_quadrics(VO,FO,EMAP,EF,EI,quadrics); + // State variables keeping track of edge we just collapsed + int v1 = -1; + int v2 = -1; + // Callbacks for computing and updating metric + std::function cost_and_placement; + std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> pre_collapse; + std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> post_collapse; + qslim_optimal_collapse_edge_callbacks( + E,quadrics,v1,v2, cost_and_placement, pre_collapse,post_collapse); + // Call to greedy decimator + bool ret = decimate( + VO, FO, + cost_and_placement, + max_faces_stopping_condition(m,orig_m,max_m), + pre_collapse, + post_collapse, + E, EMAP, EF, EI, + U, G, J, I); + // Remove phony boundary faces and clean up + const Eigen::Array keep = (J.array() +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_QSLIM_H +#define IGL_QSLIM_H +#include "igl_inline.h" +#include +namespace igl +{ + + // Decimate (simplify) a triangle mesh in nD according to the paper + // "Simplifying Surfaces with Color and Texture using Quadric Error Metrics" + // by [Garland and Heckbert, 1987] (technically a followup to qslim). The + // mesh can have open boundaries but should be edge-manifold. + // + // Inputs: + // V #V by dim list of vertex positions. Assumes that vertices w + // F #F by 3 list of triangle indices into V + // max_m desired number of output faces + // Outputs: + // U #U by dim list of output vertex posistions (can be same ref as V) + // G #G by 3 list of output face indices into U (can be same ref as G) + // J #G list of indices into F of birth face + // I #U list of indices into V of birth vertices + IGL_INLINE bool qslim( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const size_t max_m, + Eigen::MatrixXd & U, + Eigen::MatrixXi & G, + Eigen::VectorXi & J, + Eigen::VectorXi & I); +} +#ifndef IGL_STATIC_LIBRARY +# include "qslim.cpp" +#endif +#endif diff --git a/src/igl/qslim_optimal_collapse_edge_callbacks.cpp b/src/igl/qslim_optimal_collapse_edge_callbacks.cpp new file mode 100644 index 0000000000..ff3fbd0452 --- /dev/null +++ b/src/igl/qslim_optimal_collapse_edge_callbacks.cpp @@ -0,0 +1,130 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "qslim_optimal_collapse_edge_callbacks.h" +#include "quadric_binary_plus_operator.h" +#include + +IGL_INLINE void igl::qslim_optimal_collapse_edge_callbacks( + Eigen::MatrixXi & E, + std::vector > & + quadrics, + int & v1, + int & v2, + std::function & cost_and_placement, + std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> & pre_collapse, + std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> & post_collapse) +{ + typedef std::tuple Quadric; + cost_and_placement = [&quadrics,&v1,&v2]( + const int e, + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & /*F*/, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & /*EMAP*/, + const Eigen::MatrixXi & /*EF*/, + const Eigen::MatrixXi & /*EI*/, + double & cost, + Eigen::RowVectorXd & p) + { + // Combined quadric + Quadric quadric_p; + quadric_p = quadrics[E(e,0)] + quadrics[E(e,1)]; + // Quadric: p'Ap + 2b'p + c + // optimal point: Ap = -b, or rather because we have row vectors: pA=-b + const auto & A = std::get<0>(quadric_p); + const auto & b = std::get<1>(quadric_p); + const auto & c = std::get<2>(quadric_p); + p = -b*A.inverse(); + cost = p.dot(p*A) + 2*p.dot(b) + c; + // Force infs and nans to infinity + if(std::isinf(cost) || cost!=cost) + { + cost = std::numeric_limits::infinity(); + // Prevent NaNs. Actually NaNs might be useful for debugging. + p.setConstant(0); + } + }; + // Remember endpoints + pre_collapse = [&v1,&v2]( + const Eigen::MatrixXd & ,/*V*/ + const Eigen::MatrixXi & ,/*F*/ + const Eigen::MatrixXi & E, + const Eigen::VectorXi & ,/*EMAP*/ + const Eigen::MatrixXi & ,/*EF*/ + const Eigen::MatrixXi & ,/*EI*/ + const std::set > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int e)->bool + { + v1 = E(e,0); + v2 = E(e,1); + return true; + }; + // update quadric + post_collapse = [&v1,&v2,&quadrics]( + const Eigen::MatrixXd & , /*V*/ + const Eigen::MatrixXi & , /*F*/ + const Eigen::MatrixXi & , /*E*/ + const Eigen::VectorXi & ,/*EMAP*/ + const Eigen::MatrixXi & , /*EF*/ + const Eigen::MatrixXi & , /*EI*/ + const std::set > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool collapsed + )->void + { + if(collapsed) + { + quadrics[v1 +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_QSLIM_OPTIMAL_COLLAPSE_EDGE_CALLBACKS_H +#define IGL_QSLIM_OPTIMAL_COLLAPSE_EDGE_CALLBACKS_H +#include "igl_inline.h" +#include +#include +#include +#include +#include +namespace igl +{ + + // Prepare callbacks for decimating edges using the qslim optimal placement + // metric. + // + // Inputs: + // E #E by 2 list of working edges + // quadrics reference to list of working per vertex quadrics + // v1 working variable to maintain end point of collapsed edge + // v2 working variable to maintain end point of collapsed edge + // Outputs + // cost_and_placement callback for evaluating cost of edge collapse and + // determining placement of vertex (see collapse_edge) + // pre_collapse callback before edge collapse (see collapse_edge) + // post_collapse callback after edge collapse (see collapse_edge) + IGL_INLINE void qslim_optimal_collapse_edge_callbacks( + Eigen::MatrixXi & E, + std::vector > & + quadrics, + int & v1, + int & v2, + std::function & cost_and_placement, + std::function > & ,/*Q*/ + const std::vector >::iterator > &,/*Qit*/ + const Eigen::MatrixXd & ,/*C*/ + const int /*e*/ + )> & pre_collapse, + std::function > & , /*Q*/ + const std::vector >::iterator > &, /*Qit*/ + const Eigen::MatrixXd & , /*C*/ + const int , /*e*/ + const int , /*e1*/ + const int , /*e2*/ + const int , /*f1*/ + const int , /*f2*/ + const bool /*collapsed*/ + )> & post_collapse); +} +#ifndef IGL_STATIC_LIBRARY +# include "qslim_optimal_collapse_edge_callbacks.cpp" +#endif +#endif diff --git a/src/igl/quad_planarity.cpp b/src/igl/quad_planarity.cpp new file mode 100644 index 0000000000..2e0e4773e7 --- /dev/null +++ b/src/igl/quad_planarity.cpp @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "quad_planarity.h" +#include + +template +IGL_INLINE void igl::quad_planarity( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase & P) +{ + int nf = F.rows(); + P.setZero(nf,1); + for (int i =0; i &v1 = V.row(F(i,0)); + const Eigen::Matrix &v2 = V.row(F(i,1)); + const Eigen::Matrix &v3 = V.row(F(i,2)); + const Eigen::Matrix &v4 = V.row(F(i,3)); + Eigen::Matrix diagCross=(v3-v1).cross(v4-v2); + typename DerivedV::Scalar denom = + diagCross.norm()*(((v3-v1).norm()+(v4-v2).norm())/2); + if (fabs(denom)<1e-8) + //degenerate quad is still planar + P[i] = 0; + else + P[i] = (diagCross.dot(v2-v1)/denom); + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::quad_planarity, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/quad_planarity.h b/src/igl/quad_planarity.h new file mode 100644 index 0000000000..0d78b4e359 --- /dev/null +++ b/src/igl/quad_planarity.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_QUAD_PLANARITY_H +#define IGL_QUAD_PLANARITY_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute planarity of the faces of a quad mesh + // Inputs: + // V #V by 3 eigen Matrix of mesh vertex 3D positions + // F #F by 4 eigen Matrix of face (quad) indices + // Output: + // P #F by 1 eigen Matrix of mesh face (quad) planarities + // + template + IGL_INLINE void quad_planarity( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase & P); +} + +#ifndef IGL_STATIC_LIBRARY +# include "quad_planarity.cpp" +#endif + +#endif diff --git a/src/igl/quadric_binary_plus_operator.cpp b/src/igl/quadric_binary_plus_operator.cpp new file mode 100644 index 0000000000..eb3fd695e2 --- /dev/null +++ b/src/igl/quadric_binary_plus_operator.cpp @@ -0,0 +1,24 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "quadric_binary_plus_operator.h" + +IGL_INLINE std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double> + igl::operator+( + const std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double> & a, + const std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double> & b) +{ + std::tuple< + Eigen::MatrixXd, + Eigen::RowVectorXd, + double> c; + std::get<0>(c) = (std::get<0>(a) + std::get<0>(b)).eval(); + std::get<1>(c) = (std::get<1>(a) + std::get<1>(b)).eval(); + std::get<2>(c) = (std::get<2>(a) + std::get<2>(b)); + return c; +} + diff --git a/src/igl/quadric_binary_plus_operator.h b/src/igl/quadric_binary_plus_operator.h new file mode 100644 index 0000000000..3c6b845d80 --- /dev/null +++ b/src/igl/quadric_binary_plus_operator.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_QUADRIC_BINARY_PLUS_OPERATOR_H +#define IGL_QUADRIC_BINARY_PLUS_OPERATOR_H +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // A binary addition operator for Quadric tuples compatible with qslim, + // computing c = a+b + // + // Inputs: + // a QSlim quadric + // b QSlim quadric + // Output + // c QSlim quadric + // + IGL_INLINE std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double> + operator+( + const std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double> & a, + const std::tuple< Eigen::MatrixXd, Eigen::RowVectorXd, double> & b); +} + +#ifndef IGL_STATIC_LIBRARY +# include "quadric_binary_plus_operator.cpp" +#endif + +#endif diff --git a/src/igl/quat_conjugate.cpp b/src/igl/quat_conjugate.cpp new file mode 100644 index 0000000000..5856ea72d4 --- /dev/null +++ b/src/igl/quat_conjugate.cpp @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "quat_conjugate.h" + +template +IGL_INLINE void igl::quat_conjugate( + const Q_type *q1, + Q_type *out) +{ + out[0] = -q1[0]; + out[1] = -q1[1]; + out[2] = -q1[2]; + out[3] = q1[3]; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::quat_conjugate(double const*, double*); +// generated by autoexplicit.sh +template void igl::quat_conjugate(float const*, float*); +#endif diff --git a/src/igl/quat_conjugate.h b/src/igl/quat_conjugate.h new file mode 100644 index 0000000000..32ccb3b5b2 --- /dev/null +++ b/src/igl/quat_conjugate.h @@ -0,0 +1,32 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_QUAT_CONJUGATE_H +#define IGL_QUAT_CONJUGATE_H +#include "igl_inline.h" + +namespace igl +{ + // Compute conjugate of given quaternion + // http://en.wikipedia.org/wiki/Quaternion#Conjugation.2C_the_norm.2C_and_reciprocal + // A Quaternion, q, is defined here as an arrays of four scalars (x,y,z,w), + // such that q = x*i + y*j + z*k + w + // Inputs: + // q1 input quaternion + // Outputs: + // out result of conjugation, allowed to be same as input + template + IGL_INLINE void quat_conjugate( + const Q_type *q1, + Q_type *out); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "quat_conjugate.cpp" +#endif + +#endif diff --git a/src/igl/quat_mult.cpp b/src/igl/quat_mult.cpp new file mode 100644 index 0000000000..beb7baba5d --- /dev/null +++ b/src/igl/quat_mult.cpp @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "quat_mult.h" + +#include +// http://www.antisphere.com/Wiki/tools:anttweakbar +template +IGL_INLINE void igl::quat_mult( + const Q_type *q1, + const Q_type *q2, + Q_type *out) +{ + // output can't be either of the inputs + assert(q1 != out); + assert(q2 != out); + + out[0] = q1[3]*q2[0] + q1[0]*q2[3] + q1[1]*q2[2] - q1[2]*q2[1]; + out[1] = q1[3]*q2[1] + q1[1]*q2[3] + q1[2]*q2[0] - q1[0]*q2[2]; + out[2] = q1[3]*q2[2] + q1[2]*q2[3] + q1[0]*q2[1] - q1[1]*q2[0]; + out[3] = q1[3]*q2[3] - (q1[0]*q2[0] + q1[1]*q2[1] + q1[2]*q2[2]); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::quat_mult(double const*, double const*, double*); +// generated by autoexplicit.sh +template void igl::quat_mult(float const*, float const*, float*); +#endif diff --git a/src/igl/quat_mult.h b/src/igl/quat_mult.h new file mode 100644 index 0000000000..4a7ba654f4 --- /dev/null +++ b/src/igl/quat_mult.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_QUAT_MULT_H +#define IGL_QUAT_MULT_H +#include "igl_inline.h" + +namespace igl +{ + // Computes out = q1 * q2 with quaternion multiplication + // A Quaternion, q, is defined here as an arrays of four scalars (x,y,z,w), + // such that q = x*i + y*j + z*k + w + // Inputs: + // q1 left quaternion + // q2 right quaternion + // Outputs: + // out result of multiplication + template + IGL_INLINE void quat_mult( + const Q_type *q1, + const Q_type *q2, + Q_type *out); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "quat_mult.cpp" +#endif + +#endif diff --git a/src/igl/quat_to_axis_angle.cpp b/src/igl/quat_to_axis_angle.cpp new file mode 100644 index 0000000000..1aa59fad69 --- /dev/null +++ b/src/igl/quat_to_axis_angle.cpp @@ -0,0 +1,75 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "quat_to_axis_angle.h" +#include "EPS.h" +#include "PI.h" +#include +#include +// +// http://www.antisphere.com/Wiki/tools:anttweakbar +template +IGL_INLINE void igl::quat_to_axis_angle( + const Q_type *q, + Q_type *axis, + Q_type & angle) +{ + if( fabs(q[3])>(1.0 + igl::EPS()) ) + { + //axis[0] = axis[1] = axis[2] = 0; // no, keep the previous value + angle = 0; + } + else + { + double a; + if( q[3]>=1.0f ) + a = 0; // and keep V + else if( q[3]<=-1.0f ) + a = PI; // and keep V + else if( fabs(q[0]*q[0]+q[1]*q[1]+q[2]*q[2]+q[3]*q[3])()) + { + a = 0; + }else + { + a = acos(q[3]); + if( a*angle<0 ) // Preserve the sign of angle + a = -a; + double f = 1.0f / sin(a); + axis[0] = q[0] * f; + axis[1] = q[1] * f; + axis[2] = q[2] * f; + } + angle = 2.0*a; + } + + // if( angle>FLOAT_PI ) + // angle -= 2.0f*FLOAT_PI; + // else if( angle<-FLOAT_PI ) + // angle += 2.0f*FLOAT_PI; + //angle = RadToDeg(angle); + + if( fabs(angle)()&& fabs(axis[0]*axis[0]+axis[1]*axis[1]+axis[2]*axis[2])()) + { + axis[0] = 1.0e-7; // all components cannot be null + } +} + +template +IGL_INLINE void igl::quat_to_axis_angle_deg( + const Q_type *q, + Q_type *axis, + Q_type & angle) +{ + igl::quat_to_axis_angle(q,axis,angle); + angle = angle*(180.0/PI); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::quat_to_axis_angle(float const*, float*, float&); +template void igl::quat_to_axis_angle_deg(float const*, float*, float&); +#endif diff --git a/src/igl/quat_to_axis_angle.h b/src/igl/quat_to_axis_angle.h new file mode 100644 index 0000000000..77ea10e5c4 --- /dev/null +++ b/src/igl/quat_to_axis_angle.h @@ -0,0 +1,40 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_QUAT_TO_AXIS_ANGLE_H +#define IGL_QUAT_TO_AXIS_ANGLE_H +#include "igl_inline.h" + +namespace igl +{ + // Convert quat representation of a rotation to axis angle + // A Quaternion, q, is defined here as an arrays of four scalars (x,y,z,w), + // such that q = x*i + y*j + z*k + w + // Inputs: + // q quaternion + // Outputs: + // axis 3d vector + // angle scalar in radians + template + IGL_INLINE void quat_to_axis_angle( + const Q_type *q, + Q_type *axis, + Q_type & angle); + // Wrapper with angle in degrees + template + IGL_INLINE void quat_to_axis_angle_deg( + const Q_type *q, + Q_type *axis, + Q_type & angle); +} + +#ifndef IGL_STATIC_LIBRARY +# include "quat_to_axis_angle.cpp" +#endif + +#endif + diff --git a/src/igl/quat_to_mat.cpp b/src/igl/quat_to_mat.cpp new file mode 100644 index 0000000000..22ec07b941 --- /dev/null +++ b/src/igl/quat_to_mat.cpp @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "quat_to_mat.h" + +template +IGL_INLINE void igl::quat_to_mat(const Q_type * quat, Q_type * mat) +{ + Q_type yy2 = 2.0f * quat[1] * quat[1]; + Q_type xy2 = 2.0f * quat[0] * quat[1]; + Q_type xz2 = 2.0f * quat[0] * quat[2]; + Q_type yz2 = 2.0f * quat[1] * quat[2]; + Q_type zz2 = 2.0f * quat[2] * quat[2]; + Q_type wz2 = 2.0f * quat[3] * quat[2]; + Q_type wy2 = 2.0f * quat[3] * quat[1]; + Q_type wx2 = 2.0f * quat[3] * quat[0]; + Q_type xx2 = 2.0f * quat[0] * quat[0]; + mat[0*4+0] = - yy2 - zz2 + 1.0f; + mat[0*4+1] = xy2 + wz2; + mat[0*4+2] = xz2 - wy2; + mat[0*4+3] = 0; + mat[1*4+0] = xy2 - wz2; + mat[1*4+1] = - xx2 - zz2 + 1.0f; + mat[1*4+2] = yz2 + wx2; + mat[1*4+3] = 0; + mat[2*4+0] = xz2 + wy2; + mat[2*4+1] = yz2 - wx2; + mat[2*4+2] = - xx2 - yy2 + 1.0f; + mat[2*4+3] = 0; + mat[3*4+0] = mat[3*4+1] = mat[3*4+2] = 0; + mat[3*4+3] = 1; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::quat_to_mat(double const*, double*); +// generated by autoexplicit.sh +template void igl::quat_to_mat(float const*, float*); +#endif diff --git a/src/igl/quat_to_mat.h b/src/igl/quat_to_mat.h new file mode 100644 index 0000000000..4291b291ec --- /dev/null +++ b/src/igl/quat_to_mat.h @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_QUAT_TO_MAT_H +#define IGL_QUAT_TO_MAT_H +#include "igl_inline.h" +// Name history: +// quat2mat until 16 Sept 2011 +namespace igl +{ + // Convert a quaternion to a 4x4 matrix + // A Quaternion, q, is defined here as an arrays of four scalars (x,y,z,w), + // such that q = x*i + y*j + z*k + w + // Input: + // quat pointer to four elements of quaternion (x,y,z,w) + // Output: + // mat pointer to 16 elements of matrix + template + IGL_INLINE void quat_to_mat(const Q_type * quat, Q_type * mat); +} + +#ifndef IGL_STATIC_LIBRARY +# include "quat_to_mat.cpp" +#endif + +#endif diff --git a/src/igl/quats_to_column.cpp b/src/igl/quats_to_column.cpp new file mode 100644 index 0000000000..a4a88a666d --- /dev/null +++ b/src/igl/quats_to_column.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "quats_to_column.h" + +IGL_INLINE void igl::quats_to_column( + const std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > vQ, + Eigen::VectorXd & Q) +{ + Q.resize(vQ.size()*4); + for(int q = 0;q<(int)vQ.size();q++) + { + auto & xyzw = vQ[q].coeffs(); + for(int c = 0;c<4;c++) + { + Q(q*4+c) = xyzw(c); + } + } +} + +IGL_INLINE Eigen::VectorXd igl::quats_to_column( + const std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > vQ) +{ + Eigen::VectorXd Q; + quats_to_column(vQ,Q); + return Q; +} diff --git a/src/igl/quats_to_column.h b/src/igl/quats_to_column.h new file mode 100644 index 0000000000..4a7795ae14 --- /dev/null +++ b/src/igl/quats_to_column.h @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_QUATS_TO_COLUMN_H +#define IGL_QUATS_TO_COLUMN_H +#include "igl_inline.h" +#include +#include +#include +#include +namespace igl +{ + // "Columnize" a list of quaternions (q1x,q1y,q1z,q1w,q2x,q2y,q2z,q2w,...) + // + // Inputs: + // vQ n-long list of quaternions + // Outputs: + // Q n*4-long list of coefficients + IGL_INLINE void quats_to_column( + const std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > vQ, + Eigen::VectorXd & Q); + IGL_INLINE Eigen::VectorXd quats_to_column( + const std::vector< + Eigen::Quaterniond,Eigen::aligned_allocator > vQ); +} + +#ifndef IGL_STATIC_LIBRARY +# include "quats_to_column.cpp" +#endif + +#endif + diff --git a/src/igl/ramer_douglas_peucker.cpp b/src/igl/ramer_douglas_peucker.cpp new file mode 100644 index 0000000000..0dd1a40a3c --- /dev/null +++ b/src/igl/ramer_douglas_peucker.cpp @@ -0,0 +1,150 @@ +#include "ramer_douglas_peucker.h" + +#include "LinSpaced.h" +#include "find.h" +#include "cumsum.h" +#include "histc.h" +#include "slice.h" +#include "project_to_line.h" +#include "EPS.h" +#include "slice_mask.h" + +template +IGL_INLINE void igl::ramer_douglas_peucker( + const Eigen::MatrixBase & P, + const typename DerivedP::Scalar tol, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & J) +{ + typedef typename DerivedP::Scalar Scalar; + // number of vertices + const int n = P.rows(); + // Trivial base case + if(n <= 1) + { + J = DerivedJ::Zero(n); + S = P; + return; + } + // number of dimensions + const int m = P.cols(); + Eigen::Array I = + Eigen::Array::Constant(n,1,true); + const auto stol = tol*tol; + std::function simplify; + simplify = [&I,&P,&stol,&simplify](const int ixs, const int ixe)->void + { + assert(ixe>ixs); + Scalar sdmax = 0; + typename Eigen::Matrix::Index ixc = -1; + if((ixe-ixs)>1) + { + Scalar sdes = (P.row(ixe)-P.row(ixs)).squaredNorm(); + Eigen::Matrix sD; + const auto & Pblock = P.block(ixs+1,0,((ixe+1)-ixs)-2,P.cols()); + if(sdes<=EPS()) + { + sD = (Pblock.rowwise()-P.row(ixs)).rowwise().squaredNorm(); + }else + { + Eigen::Matrix T; + project_to_line(Pblock,P.row(ixs).eval(),P.row(ixe).eval(),T,sD); + } + sdmax = sD.maxCoeff(&ixc); + // Index full P + ixc = ixc+(ixs+1); + } + if(sdmax <= stol) + { + if(ixs != ixe-1) + { + I.block(ixs+1,0,((ixe+1)-ixs)-2,1).setConstant(false); + } + }else + { + simplify(ixs,ixc); + simplify(ixc,ixe); + } + }; + simplify(0,n-1); + slice_mask(P,I,1,S); + find(I,J); +} + +template < + typename DerivedP, + typename DerivedS, + typename DerivedJ, + typename DerivedQ> +IGL_INLINE void igl::ramer_douglas_peucker( + const Eigen::MatrixBase & P, + const typename DerivedP::Scalar tol, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & Q) +{ + typedef typename DerivedP::Scalar Scalar; + ramer_douglas_peucker(P,tol,S,J); + const int n = P.rows(); + assert(n>=2 && "Curve should be at least 2 points"); + typedef Eigen::Matrix VectorXS; + // distance traveled along high-res curve + VectorXS L(n); + L(0) = 0; + L.block(1,0,n-1,1) = (P.bottomRows(n-1)-P.topRows(n-1)).rowwise().norm(); + // Give extra on end + VectorXS T; + cumsum(L,1,T); + T.conservativeResize(T.size()+1); + T(T.size()-1) = T(T.size()-2); + // index of coarse point before each fine vertex + Eigen::VectorXi B; + { + Eigen::VectorXi N; + histc(igl::LinSpaced(n,0,n-1),J,N,B); + } + // Add extra point at end + J.conservativeResize(J.size()+1); + J(J.size()-1) = J(J.size()-2); + Eigen::VectorXi s,d; + // Find index in original list of "start" vertices + slice(J,B,s); + // Find index in original list of "destination" vertices + slice(J,(B.array()+1).eval(),d); + // Parameter between start and destination is linear in arc-length + VectorXS Ts,Td; + slice(T,s,Ts); + slice(T,d,Td); + T = ((T.head(T.size()-1)-Ts).array()/(Td-Ts).array()).eval(); + for(int t =0;t= S.rows()) + { + MB(b) = S.rows()-1; + } + } + DerivedS SMB; + slice(S,MB,1,SMB); + Q = SB.array() + ((SMB.array()-SB.array()).colwise()*T.array()); + + // Remove extra point at end + J.conservativeResize(J.size()-1); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::ramer_douglas_peucker, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::Matrix::Scalar, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::ramer_douglas_peucker, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::Matrix::Scalar, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/ramer_douglas_peucker.h b/src/igl/ramer_douglas_peucker.h new file mode 100644 index 0000000000..dcfe47e857 --- /dev/null +++ b/src/igl/ramer_douglas_peucker.h @@ -0,0 +1,49 @@ +#ifndef IGL_RAMER_DOUGLAS_PEUCKER_H +#define IGL_RAMER_DOUGLAS_PEUCKER_H +#include "igl_inline.h" +#include +namespace igl +{ + // Ramer-Douglas-Peucker piecewise-linear curve simplification. + // + // Inputs: + // P #P by dim ordered list of vertices along the curve + // tol tolerance (maximal euclidean distance allowed between the new line + // and a vertex) + // Outputs: + // S #S by dim ordered list of points along the curve + // J #S list of indices into P so that S = P(J,:) + template + IGL_INLINE void ramer_douglas_peucker( + const Eigen::MatrixBase & P, + const typename DerivedP::Scalar tol, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & J); + // Run (Ramer-)Duglass-Peucker curve simplification but keep track of where + // every point on the original curve maps to on the simplified curve. + // + // Inputs: + // P #P by dim list of points, (use P([1:end 1],:) for loops) + // tol DP tolerance + // Outputs: + // S #S by dim list of points along simplified curve + // J #S indices into P of simplified points + // Q #P by dim list of points mapping along simplified curve + // + template < + typename DerivedP, + typename DerivedS, + typename DerivedJ, + typename DerivedQ> + IGL_INLINE void ramer_douglas_peucker( + const Eigen::MatrixBase & P, + const typename DerivedP::Scalar tol, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & J, + Eigen::PlainObjectBase & Q); + +} +#ifndef IGL_STATIC_LIBRARY +# include "ramer_douglas_peucker.cpp" +#endif +#endif diff --git a/src/igl/random_dir.cpp b/src/igl/random_dir.cpp new file mode 100644 index 0000000000..f307834180 --- /dev/null +++ b/src/igl/random_dir.cpp @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "random_dir.h" +#include +#include + +IGL_INLINE Eigen::Vector3d igl::random_dir() +{ + using namespace Eigen; + double z = (double)rand() / (double)RAND_MAX*2.0 - 1.0; + double t = (double)rand() / (double)RAND_MAX*2.0*PI; + // http://www.altdevblogaday.com/2012/05/03/generating-uniformly-distributed-points-on-sphere/ + double r = sqrt(1.0-z*z); + double x = r * cos(t); + double y = r * sin(t); + return Vector3d(x,y,z); +} + +IGL_INLINE Eigen::MatrixXd igl::random_dir_stratified(const int n) +{ + using namespace Eigen; + using namespace std; + const double m = std::floor(sqrt(double(n))); + MatrixXd N(n,3); + int row = 0; + for(int i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_RANDOM_DIR_H +#define IGL_RANDOM_DIR_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Generate a uniformly random unit direction in 3D, return as vector + IGL_INLINE Eigen::Vector3d random_dir(); + // Generate n stratified uniformly random unit directions in 3d, return as rows + // of an n by 3 matrix + // + // Inputs: + // n number of directions + // Return n by 3 matrix of random directions + IGL_INLINE Eigen::MatrixXd random_dir_stratified(const int n); +} + +#ifndef IGL_STATIC_LIBRARY +# include "random_dir.cpp" +#endif + +#endif diff --git a/src/igl/random_points_on_mesh.cpp b/src/igl/random_points_on_mesh.cpp new file mode 100644 index 0000000000..b28135ad11 --- /dev/null +++ b/src/igl/random_points_on_mesh.cpp @@ -0,0 +1,83 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "random_points_on_mesh.h" +#include "doublearea.h" +#include "cumsum.h" +#include "histc.h" +#include +#include + +template +IGL_INLINE void igl::random_points_on_mesh( + const int n, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & B, + Eigen::PlainObjectBase & FI) +{ + using namespace Eigen; + using namespace std; + typedef typename DerivedV::Scalar Scalar; + typedef Matrix VectorXs; + VectorXs A; + doublearea(V,F,A); + A /= A.array().sum(); + // Should be traingle mesh. Although Turk's method 1 generalizes... + assert(F.cols() == 3); + VectorXs C; + VectorXs A0(A.size()+1); + A0(0) = 0; + A0.bottomRightCorner(A.size(),1) = A; + // Even faster would be to use the "Alias Table Method" + cumsum(A0,1,C); + const VectorXs R = (VectorXs::Random(n,1).array() + 1.)/2.; + assert(R.minCoeff() >= 0); + assert(R.maxCoeff() <= 1); + histc(R,C,FI); + const VectorXs S = (VectorXs::Random(n,1).array() + 1.)/2.; + const VectorXs T = (VectorXs::Random(n,1).array() + 1.)/2.; + B.resize(n,3); + B.col(0) = 1.-T.array().sqrt(); + B.col(1) = (1.-S.array()) * T.array().sqrt(); + B.col(2) = S.array() * T.array().sqrt(); +} + +template +IGL_INLINE void igl::random_points_on_mesh( + const int n, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::SparseMatrix & B, + Eigen::PlainObjectBase & FI) +{ + using namespace Eigen; + using namespace std; + Matrix BC; + random_points_on_mesh(n,V,F,BC,FI); + vector > BIJV; + BIJV.reserve(n*3); + for(int s = 0;s= 0); + const int v = F(FI(s),c); + BIJV.push_back(Triplet(s,v,BC(s,c))); + } + } + B.resize(n,V.rows()); + B.reserve(n*3); + B.setFromTriplets(BIJV.begin(),BIJV.end()); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::random_points_on_mesh, Eigen::Matrix, double, Eigen::Matrix >(int, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::SparseMatrix&, Eigen::PlainObjectBase >&); +template void igl::random_points_on_mesh, Eigen::Matrix, double, Eigen::Matrix >(int, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::SparseMatrix&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/random_points_on_mesh.h b/src/igl/random_points_on_mesh.h new file mode 100644 index 0000000000..7cde24f3c5 --- /dev/null +++ b/src/igl/random_points_on_mesh.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_RANDOM_POINTS_ON_MESH_H +#define IGL_RANDOM_POINTS_ON_MESH_H + +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // RANDOM_POINTS_ON_MESH Randomly sample a mesh (V,F) n times. + // + // Inputs: + // n number of samples + // V #V by dim list of mesh vertex positions + // F #F by 3 list of mesh triangle indices + // Outputs: + // B n by 3 list of barycentric coordinates, ith row are coordinates of + // ith sampled point in face FI(i) + // FI n list of indices into F + // + template + IGL_INLINE void random_points_on_mesh( + const int n, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & B, + Eigen::PlainObjectBase & FI); + // Outputs: + // B n by #V sparse matrix so that B*V produces a list of sample points + template + IGL_INLINE void random_points_on_mesh( + const int n, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::SparseMatrix & B, + Eigen::PlainObjectBase & FI); +} + +#ifndef IGL_STATIC_LIBRARY +# include "random_points_on_mesh.cpp" +#endif + +#endif + + diff --git a/src/igl/random_quaternion.cpp b/src/igl/random_quaternion.cpp new file mode 100644 index 0000000000..2d042e33fc --- /dev/null +++ b/src/igl/random_quaternion.cpp @@ -0,0 +1,84 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "random_quaternion.h" +#include "PI.h" + +template +IGL_INLINE Eigen::Quaternion igl::random_quaternion() +{ + const auto & unit_rand = []()->Scalar + { + return ((Scalar)rand() / (Scalar)RAND_MAX); + }; +#ifdef false + // http://mathproofs.blogspot.com/2005/05/uniformly-distributed-random-unit.html + const Scalar t0 = 2.*igl::PI*unit_rand(); + const Scalar t1 = acos(1.-2.*unit_rand()); + const Scalar t2 = 0.5*(igl::PI*unit_rand() + acos(unit_rand())); + return Eigen::Quaternion( + 1.*sin(t0)*sin(t1)*sin(t2), + 1.*cos(t0)*sin(t1)*sin(t2), + 1.*cos(t1)*sin(t2), + 1.*cos(t2)); +#elif false + // "Uniform Random Rotations" [Shoemake 1992] method 1 + const auto & uurand = [&unit_rand]()->Scalar + { + return unit_rand()*2.-1.; + }; + Scalar x = uurand(); + Scalar y = uurand(); + Scalar z = uurand(); + Scalar w = uurand(); + const auto & hype = [&uurand](Scalar & x, Scalar & y)->Scalar + { + Scalar s1; + while((s1 = x*x + y*y) > 1.0) + { + x = uurand(); + y = uurand(); + } + return s1; + }; + Scalar s1 = hype(x,y); + Scalar s2 = hype(z,w); + Scalar num1 = -2.*log(s1); + Scalar num2 = -2.*log(s2); + Scalar r = num1 + num2; + Scalar root1 = sqrt((num1/s1)/r); + Scalar root2 = sqrt((num2/s2)/r); + return Eigen::Quaternion( + x*root1, + y*root1, + z*root2, + w*root2); +#else + // Shoemake method 2 + const Scalar x0 = unit_rand(); + const Scalar x1 = unit_rand(); + const Scalar x2 = unit_rand(); + const Scalar r1 = sqrt(1.0 - x0); + const Scalar r2 = sqrt(x0); + const Scalar t1 = 2.*igl::PI*x1; + const Scalar t2 = 2.*igl::PI*x2; + const Scalar c1 = cos(t1); + const Scalar s1 = sin(t1); + const Scalar c2 = cos(t2); + const Scalar s2 = sin(t2); + return Eigen::Quaternion( + s1*r1, + c1*r1, + s2*r2, + c2*r2); +#endif +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template Eigen::Quaternion igl::random_quaternion(); +#endif diff --git a/src/igl/random_quaternion.h b/src/igl/random_quaternion.h new file mode 100644 index 0000000000..dfa36022be --- /dev/null +++ b/src/igl/random_quaternion.h @@ -0,0 +1,21 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_RANDOM_QUATERNION_H +#define IGL_RANDOM_QUATERNION_H +#include "igl_inline.h" +#include +namespace igl +{ + // Return a random quaternion via uniform sampling of the 4-sphere + template + IGL_INLINE Eigen::Quaternion random_quaternion(); +} +#ifndef IGL_STATIC_LIBRARY +#include "random_quaternion.cpp" +#endif +#endif diff --git a/src/igl/random_search.cpp b/src/igl/random_search.cpp new file mode 100644 index 0000000000..d248d73e68 --- /dev/null +++ b/src/igl/random_search.cpp @@ -0,0 +1,36 @@ +#include "random_search.h" +#include +#include + +template < + typename Scalar, + typename DerivedX, + typename DerivedLB, + typename DerivedUB> +IGL_INLINE Scalar igl::random_search( + const std::function< Scalar (DerivedX &) > f, + const Eigen::MatrixBase & LB, + const Eigen::MatrixBase & UB, + const int iters, + DerivedX & X) +{ + Scalar min_f = std::numeric_limits::max(); + const int dim = LB.size(); + assert(UB.size() == dim && "UB should match LB size"); + for(int iter = 0;iter, Eigen::Matrix, Eigen::Matrix >(std::function&)>, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, Eigen::Matrix&); +#endif diff --git a/src/igl/random_search.h b/src/igl/random_search.h new file mode 100644 index 0000000000..8573e88cf7 --- /dev/null +++ b/src/igl/random_search.h @@ -0,0 +1,42 @@ +#ifndef IGL_RANDOM_SEARCH_H +#define IGL_RANDOM_SEARCH_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Solve the problem: + // + // minimize f(x) + // subject to lb ≤ x ≤ ub + // + // by uniform random search. + // + // Inputs: + // f function to minimize + // LB #X vector of finite lower bounds + // UB #X vector of finite upper bounds + // iters number of iterations + // Outputs: + // X #X optimal parameter vector + // Returns f(X) + // + template < + typename Scalar, + typename DerivedX, + typename DerivedLB, + typename DerivedUB> + IGL_INLINE Scalar random_search( + const std::function< Scalar (DerivedX &) > f, + const Eigen::MatrixBase & LB, + const Eigen::MatrixBase & UB, + const int iters, + DerivedX & X); +} + +#ifndef IGL_STATIC_LIBRARY +# include "random_search.cpp" +#endif + +#endif + diff --git a/src/igl/randperm.cpp b/src/igl/randperm.cpp new file mode 100644 index 0000000000..d32af1760e --- /dev/null +++ b/src/igl/randperm.cpp @@ -0,0 +1,27 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "randperm.h" +#include "colon.h" +#include + +template +IGL_INLINE void igl::randperm( + const int n, + Eigen::PlainObjectBase & I) +{ + Eigen::VectorXi II; + igl::colon(0,1,n-1,II); + I = II; + std::random_shuffle(I.data(),I.data()+n); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::randperm >(int, Eigen::PlainObjectBase >&); +template void igl::randperm >(int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/randperm.h b/src/igl/randperm.h new file mode 100644 index 0000000000..0ba0671411 --- /dev/null +++ b/src/igl/randperm.h @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_RANDPERM_H +#define IGL_RANDPERM_H +#include "igl_inline.h" +#include +namespace igl +{ + // Like matlab's randperm(n) but minus 1 + // + // Inputs: + // n number of elements + // Outputs: + // I n list of rand permutation of 0:n-1 + template + IGL_INLINE void randperm( + const int n, + Eigen::PlainObjectBase & I); +} +#ifndef IGL_STATIC_LIBRARY +# include "randperm.cpp" +#endif +#endif diff --git a/src/igl/ray_box_intersect.cpp b/src/igl/ray_box_intersect.cpp new file mode 100644 index 0000000000..8c6346d86d --- /dev/null +++ b/src/igl/ray_box_intersect.cpp @@ -0,0 +1,149 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "ray_box_intersect.h" +#include + +template < + typename Derivedsource, + typename Deriveddir, + typename Scalar> +IGL_INLINE bool igl::ray_box_intersect( + const Eigen::MatrixBase & origin, + const Eigen::MatrixBase & dir, + const Eigen::AlignedBox & box, + const Scalar & t0, + const Scalar & t1, + Scalar & tmin, + Scalar & tmax) +{ +#ifdef false + // https://github.com/RMonica/basic_next_best_view/blob/master/src/RayTracer.cpp + const auto & intersectRayBox = []( + const Eigen::Vector3f& rayo, + const Eigen::Vector3f& rayd, + const Eigen::Vector3f& bmin, + const Eigen::Vector3f& bmax, + float & tnear, + float & tfar + )->bool + { + Eigen::Vector3f bnear; + Eigen::Vector3f bfar; + // Checks for intersection testing on each direction coordinate + // Computes + float t1, t2; + tnear = -1e+6f, tfar = 1e+6f; //, tCube; + bool intersectFlag = true; + for (int i = 0; i < 3; ++i) { + // std::cout << "coordinate " << i << ": bmin " << bmin(i) << ", bmax " << bmax(i) << std::endl; + assert(bmin(i) <= bmax(i)); + if (::fabs(rayd(i)) < 1e-6) { // Ray parallel to axis i-th + if (rayo(i) < bmin(i) || rayo(i) > bmax(i)) { + intersectFlag = false; + } + } + else { + // Finds the nearest and the farthest vertices of the box from the ray origin + if (::fabs(bmin(i) - rayo(i)) < ::fabs(bmax(i) - rayo(i))) { + bnear(i) = bmin(i); + bfar(i) = bmax(i); + } + else { + bnear(i) = bmax(i); + bfar(i) = bmin(i); + } + // std::cout << " bnear " << bnear(i) << ", bfar " << bfar(i) << std::endl; + // Finds the distance parameters t1 and t2 of the two ray-box intersections: + // t1 must be the closest to the ray origin rayo. + t1 = (bnear(i) - rayo(i)) / rayd(i); + t2 = (bfar(i) - rayo(i)) / rayd(i); + if (t1 > t2) { + std::swap(t1,t2); + } + // The two intersection values are used to saturate tnear and tfar + if (t1 > tnear) { + tnear = t1; + } + if (t2 < tfar) { + tfar = t2; + } + // std::cout << " t1 " << t1 << ", t2 " << t2 << ", tnear " << tnear << ", tfar " << tfar + // << " tnear > tfar? " << (tnear > tfar) << ", tfar < 0? " << (tfar < 0) << std::endl; + if(tnear > tfar) { + intersectFlag = false; + } + if(tfar < 0) { + intersectFlag = false; + } + } + } + // Checks whether intersection occurs or not + return intersectFlag; + }; + float tmin_f, tmax_f; + bool ret = intersectRayBox( + origin. template cast(), + dir. template cast(), + box.min().template cast(), + box.max().template cast(), + tmin_f, + tmax_f); + tmin = tmin_f; + tmax = tmax_f; + return ret; +#else + using namespace Eigen; + // This should be precomputed and provided as input + typedef Matrix RowVector3S; + const RowVector3S inv_dir( 1./dir(0),1./dir(1),1./dir(2)); + const std::vector sign = { inv_dir(0)<0, inv_dir(1)<0, inv_dir(2)<0}; + // http://people.csail.mit.edu/amy/papers/box-jgt.pdf + // "An Efficient and Robust Ray–Box Intersection Algorithm" + Scalar tymin, tymax, tzmin, tzmax; + std::vector bounds = {box.min(),box.max()}; + tmin = ( bounds[sign[0]](0) - origin(0)) * inv_dir(0); + tmax = ( bounds[1-sign[0]](0) - origin(0)) * inv_dir(0); + tymin = (bounds[sign[1]](1) - origin(1)) * inv_dir(1); + tymax = (bounds[1-sign[1]](1) - origin(1)) * inv_dir(1); + if ( (tmin > tymax) || (tymin > tmax) ) + { + return false; + } + if (tymin > tmin) + { + tmin = tymin; + } + if (tymax < tmax) + { + tmax = tymax; + } + tzmin = (bounds[sign[2]](2) - origin(2)) * inv_dir(2); + tzmax = (bounds[1-sign[2]](2) - origin(2)) * inv_dir(2); + if ( (tmin > tzmax) || (tzmin > tmax) ) + { + return false; + } + if (tzmin > tmin) + { + tmin = tzmin; + } + if (tzmax < tmax) + { + tmax = tzmax; + } + if(!( (tmin < t1) && (tmax > t0) )) + { + return false; + } + return true; +#endif +} + +#ifdef IGL_STATIC_LIBRARY +template bool igl::ray_box_intersect, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::AlignedBox const&, double const&, double const&, double&, double&); +#endif \ No newline at end of file diff --git a/src/igl/ray_box_intersect.h b/src/igl/ray_box_intersect.h new file mode 100644 index 0000000000..6b6e14aaa3 --- /dev/null +++ b/src/igl/ray_box_intersect.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_RAY_BOX_INTERSECT_H +#define IGL_RAY_BOX_INTERSECT_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Determine whether a ray origin+t*dir and box intersect within the ray's parameterized + // range (t0,t1) + // + // Inputs: + // source 3-vector origin of ray + // dir 3-vector direction of ray + // box axis aligned box + // t0 hit only if hit.t less than t0 + // t1 hit only if hit.t greater than t1 + // Outputs: + // tmin minimum of interval of overlap within [t0,t1] + // tmax maximum of interval of overlap within [t0,t1] + // Returns true if hit + template < + typename Derivedsource, + typename Deriveddir, + typename Scalar> + IGL_INLINE bool ray_box_intersect( + const Eigen::MatrixBase & source, + const Eigen::MatrixBase & dir, + const Eigen::AlignedBox & box, + const Scalar & t0, + const Scalar & t1, + Scalar & tmin, + Scalar & tmax); +} +#ifndef IGL_STATIC_LIBRARY +# include "ray_box_intersect.cpp" +#endif +#endif diff --git a/src/igl/ray_mesh_intersect.cpp b/src/igl/ray_mesh_intersect.cpp new file mode 100644 index 0000000000..512a35c461 --- /dev/null +++ b/src/igl/ray_mesh_intersect.cpp @@ -0,0 +1,86 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "ray_mesh_intersect.h" + +extern "C" +{ +#include "raytri.c" +} + +template < + typename Derivedsource, + typename Deriveddir, + typename DerivedV, + typename DerivedF> +IGL_INLINE bool igl::ray_mesh_intersect( + const Eigen::MatrixBase & s, + const Eigen::MatrixBase & dir, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + std::vector & hits) +{ + using namespace Eigen; + using namespace std; + // Should be but can't be const + Vector3d s_d = s.template cast(); + Vector3d dir_d = dir.template cast(); + hits.clear(); + // loop over all triangles + for(int f = 0;f(); + RowVector3d v1 = V.row(F(f,1)).template cast(); + RowVector3d v2 = V.row(F(f,2)).template cast(); + // shoot ray, record hit + double t,u,v; + if(intersect_triangle1( + s_d.data(), dir_d.data(), v0.data(), v1.data(), v2.data(), &t, &u, &v) && + t>0) + { + hits.push_back({(int)f,(int)-1,(float)u,(float)v,(float)t}); + } + } + // Sort hits based on distance + std::sort( + hits.begin(), + hits.end(), + [](const Hit & a, const Hit & b)->bool{ return a.t < b.t;}); + return hits.size() > 0; +} + +template < + typename Derivedsource, + typename Deriveddir, + typename DerivedV, + typename DerivedF> +IGL_INLINE bool igl::ray_mesh_intersect( + const Eigen::MatrixBase & source, + const Eigen::MatrixBase & dir, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + igl::Hit & hit) +{ + std::vector hits; + ray_mesh_intersect(source,dir,V,F,hits); + if(hits.size() > 0) + { + hit = hits.front(); + return true; + }else + { + return false; + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >&); +template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::Hit&); +template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Block const, 1, -1, false> >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase const, 1, -1, false> > const&, igl::Hit&); +#endif diff --git a/src/igl/ray_mesh_intersect.h b/src/igl/ray_mesh_intersect.h new file mode 100644 index 0000000000..e081bf3bbe --- /dev/null +++ b/src/igl/ray_mesh_intersect.h @@ -0,0 +1,56 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_RAY_MESH_INTERSECT_H +#define IGL_RAY_MESH_INTERSECT_H +#include "igl_inline.h" +#include "Hit.h" +#include +#include +namespace igl +{ + // Shoot a ray against a mesh (V,F) and collect all hits. + // + // Inputs: + // source 3-vector origin of ray + // dir 3-vector direction of ray + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of mesh face indices into V + // Outputs: + // hits **sorted** list of hits + // Returns true if there were any hits (hits.size() > 0) + // + template < + typename Derivedsource, + typename Deriveddir, + typename DerivedV, + typename DerivedF> + IGL_INLINE bool ray_mesh_intersect( + const Eigen::MatrixBase & source, + const Eigen::MatrixBase & dir, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + std::vector & hits); + // Outputs: + // hit first hit, set only if it exists + // Returns true if there was a hit + template < + typename Derivedsource, + typename Deriveddir, + typename DerivedV, + typename DerivedF> + IGL_INLINE bool ray_mesh_intersect( + const Eigen::MatrixBase & source, + const Eigen::MatrixBase & dir, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + igl::Hit & hit); +} +#ifndef IGL_STATIC_LIBRARY +# include "ray_mesh_intersect.cpp" +#endif +#endif diff --git a/src/igl/ray_sphere_intersect.cpp b/src/igl/ray_sphere_intersect.cpp new file mode 100644 index 0000000000..8a05c2a12c --- /dev/null +++ b/src/igl/ray_sphere_intersect.cpp @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "ray_sphere_intersect.h" + +template < + typename Derivedo, + typename Derivedd, + typename Derivedc, + typename r_type, + typename t_type> +IGL_INLINE int igl::ray_sphere_intersect( + const Eigen::PlainObjectBase & ao, + const Eigen::PlainObjectBase & d, + const Eigen::PlainObjectBase & ac, + r_type r, + t_type & t0, + t_type & t1) +{ + Eigen::Vector3d o = ao-ac; + // http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection + //Compute A, B and C coefficients + double a = d.dot(d); + double b = 2 * d.dot(o); + double c = o.dot(o) - (r * r); + + //Find discriminant + double disc = b * b - 4 * a * c; + + // if discriminant is negative there are no real roots, so return + // false as ray misses sphere + if (disc < 0) + { + return 0; + } + + // compute q as described above + double distSqrt = sqrt(disc); + double q; + if (b < 0) + { + q = (-b - distSqrt)/2.0; + } else + { + q = (-b + distSqrt)/2.0; + } + + // compute t0 and t1 + t0 = q / a; + double _t1 = c/q; + if(_t1 == t0) + { + return 1; + } + t1 = _t1; + // make sure t0 is smaller than t1 + if (t0 > t1) + { + // if t0 is bigger than t1 swap them around + double temp = t0; + t0 = t1; + t1 = temp; + } + return 2; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template int igl::ray_sphere_intersect, Eigen::Matrix, Eigen::Matrix, double, double>(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, double, double&, double&); +#endif diff --git a/src/igl/ray_sphere_intersect.h b/src/igl/ray_sphere_intersect.h new file mode 100644 index 0000000000..b4dfea3794 --- /dev/null +++ b/src/igl/ray_sphere_intersect.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_RAY_SPHERE_INTERSECT_H +#define IGL_RAY_SPHERE_INTERSECT_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute the intersection between a ray from O in direction D and a sphere + // centered at C with radius r + // + // Inputs: + // o origin of ray + // d direction of ray + // c center of sphere + // r radius of sphere + // Outputs: + // t0 parameterization of first hit (set only if exists) so that hit + // position = o + t0*d + // t1 parameterization of second hit (set only if exists) + // + // Returns the number of hits + template < + typename Derivedo, + typename Derivedd, + typename Derivedc, + typename r_type, + typename t_type> + IGL_INLINE int ray_sphere_intersect( + const Eigen::PlainObjectBase & o, + const Eigen::PlainObjectBase & d, + const Eigen::PlainObjectBase & c, + r_type r, + t_type & t0, + t_type & t1); +} +#ifndef IGL_STATIC_LIBRARY +#include "ray_sphere_intersect.cpp" +#endif +#endif + diff --git a/src/igl/raytri.c b/src/igl/raytri.c new file mode 100644 index 0000000000..b5e7f7b1f1 --- /dev/null +++ b/src/igl/raytri.c @@ -0,0 +1,267 @@ +/* Ray-Triangle Intersection Test Routines */ +/* Different optimizations of my and Ben Trumbore's */ +/* code from journals of graphics tools (JGT) */ +/* http://www.acm.org/jgt/ */ +/* by Tomas Moller, May 2000 */ + + +// Alec: this file is listed as "Public Domain" +// http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/code/ + +// Alec: I've added an include guard, made all functions inline and added +// IGL_RAY_TRI_ to #define macros +#ifndef IGL_RAY_TRI_C +#define IGL_RAY_TRI_C + +#include + +#define IGL_RAY_TRI_EPSILON 0.000001 +#define IGL_RAY_TRI_CROSS(dest,v1,v2) \ + dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \ + dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \ + dest[2]=v1[0]*v2[1]-v1[1]*v2[0]; +#define IGL_RAY_TRI_DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) +#define IGL_RAY_TRI_SUB(dest,v1,v2) \ + dest[0]=v1[0]-v2[0]; \ + dest[1]=v1[1]-v2[1]; \ + dest[2]=v1[2]-v2[2]; + +/* the original jgt code */ +inline int intersect_triangle(double orig[3], double dir[3], + double vert0[3], double vert1[3], double vert2[3], + double *t, double *u, double *v) +{ + double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3]; + double det,inv_det; + + /* find vectors for two edges sharing vert0 */ + IGL_RAY_TRI_SUB(edge1, vert1, vert0); + IGL_RAY_TRI_SUB(edge2, vert2, vert0); + + /* begin calculating determinant - also used to calculate U parameter */ + IGL_RAY_TRI_CROSS(pvec, dir, edge2); + + /* if determinant is near zero, ray lies in plane of triangle */ + det = IGL_RAY_TRI_DOT(edge1, pvec); + + if (det > -IGL_RAY_TRI_EPSILON && det < IGL_RAY_TRI_EPSILON) + return 0; + inv_det = 1.0 / det; + + /* calculate distance from vert0 to ray origin */ + IGL_RAY_TRI_SUB(tvec, orig, vert0); + + /* calculate U parameter and test bounds */ + *u = IGL_RAY_TRI_DOT(tvec, pvec) * inv_det; + if (*u < 0.0 || *u > 1.0) + return 0; + + /* prepare to test V parameter */ + IGL_RAY_TRI_CROSS(qvec, tvec, edge1); + + /* calculate V parameter and test bounds */ + *v = IGL_RAY_TRI_DOT(dir, qvec) * inv_det; + if (*v < 0.0 || *u + *v > 1.0) + return 0; + + /* calculate t, ray intersects triangle */ + *t = IGL_RAY_TRI_DOT(edge2, qvec) * inv_det; + + return 1; +} + + +/* code rewritten to do tests on the sign of the determinant */ +/* the division is at the end in the code */ +inline int intersect_triangle1(double orig[3], double dir[3], + double vert0[3], double vert1[3], double vert2[3], + double *t, double *u, double *v) +{ + double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3]; + double det,inv_det; + + /* find vectors for two edges sharing vert0 */ + IGL_RAY_TRI_SUB(edge1, vert1, vert0); + IGL_RAY_TRI_SUB(edge2, vert2, vert0); + + /* begin calculating determinant - also used to calculate U parameter */ + IGL_RAY_TRI_CROSS(pvec, dir, edge2); + + /* if determinant is near zero, ray lies in plane of triangle */ + det = IGL_RAY_TRI_DOT(edge1, pvec); + + if (det > IGL_RAY_TRI_EPSILON) + { + /* calculate distance from vert0 to ray origin */ + IGL_RAY_TRI_SUB(tvec, orig, vert0); + + /* calculate U parameter and test bounds */ + *u = IGL_RAY_TRI_DOT(tvec, pvec); + if (*u < 0.0 || *u > det) + return 0; + + /* prepare to test V parameter */ + IGL_RAY_TRI_CROSS(qvec, tvec, edge1); + + /* calculate V parameter and test bounds */ + *v = IGL_RAY_TRI_DOT(dir, qvec); + if (*v < 0.0 || *u + *v > det) + return 0; + + } + else if(det < -IGL_RAY_TRI_EPSILON) + { + /* calculate distance from vert0 to ray origin */ + IGL_RAY_TRI_SUB(tvec, orig, vert0); + + /* calculate U parameter and test bounds */ + *u = IGL_RAY_TRI_DOT(tvec, pvec); +/* printf("*u=%f\n",(float)*u); */ +/* printf("det=%f\n",det); */ + if (*u > 0.0 || *u < det) + return 0; + + /* prepare to test V parameter */ + IGL_RAY_TRI_CROSS(qvec, tvec, edge1); + + /* calculate V parameter and test bounds */ + *v = IGL_RAY_TRI_DOT(dir, qvec) ; + if (*v > 0.0 || *u + *v < det) + return 0; + } + else return 0; /* ray is parallel to the plane of the triangle */ + + + inv_det = 1.0 / det; + + /* calculate t, ray intersects triangle */ + *t = IGL_RAY_TRI_DOT(edge2, qvec) * inv_det; + (*u) *= inv_det; + (*v) *= inv_det; + + return 1; +} + +/* code rewritten to do tests on the sign of the determinant */ +/* the division is before the test of the sign of the det */ +inline int intersect_triangle2(double orig[3], double dir[3], + double vert0[3], double vert1[3], double vert2[3], + double *t, double *u, double *v) +{ + double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3]; + double det,inv_det; + + /* find vectors for two edges sharing vert0 */ + IGL_RAY_TRI_SUB(edge1, vert1, vert0); + IGL_RAY_TRI_SUB(edge2, vert2, vert0); + + /* begin calculating determinant - also used to calculate U parameter */ + IGL_RAY_TRI_CROSS(pvec, dir, edge2); + + /* if determinant is near zero, ray lies in plane of triangle */ + det = IGL_RAY_TRI_DOT(edge1, pvec); + + /* calculate distance from vert0 to ray origin */ + IGL_RAY_TRI_SUB(tvec, orig, vert0); + inv_det = 1.0 / det; + + if (det > IGL_RAY_TRI_EPSILON) + { + /* calculate U parameter and test bounds */ + *u = IGL_RAY_TRI_DOT(tvec, pvec); + if (*u < 0.0 || *u > det) + return 0; + + /* prepare to test V parameter */ + IGL_RAY_TRI_CROSS(qvec, tvec, edge1); + + /* calculate V parameter and test bounds */ + *v = IGL_RAY_TRI_DOT(dir, qvec); + if (*v < 0.0 || *u + *v > det) + return 0; + + } + else if(det < -IGL_RAY_TRI_EPSILON) + { + /* calculate U parameter and test bounds */ + *u = IGL_RAY_TRI_DOT(tvec, pvec); + if (*u > 0.0 || *u < det) + return 0; + + /* prepare to test V parameter */ + IGL_RAY_TRI_CROSS(qvec, tvec, edge1); + + /* calculate V parameter and test bounds */ + *v = IGL_RAY_TRI_DOT(dir, qvec) ; + if (*v > 0.0 || *u + *v < det) + return 0; + } + else return 0; /* ray is parallel to the plane of the triangle */ + + /* calculate t, ray intersects triangle */ + *t = IGL_RAY_TRI_DOT(edge2, qvec) * inv_det; + (*u) *= inv_det; + (*v) *= inv_det; + + return 1; +} + +/* code rewritten to do tests on the sign of the determinant */ +/* the division is before the test of the sign of the det */ +/* and one IGL_RAY_TRI_CROSS has been moved out from the if-else if-else */ +inline int intersect_triangle3(double orig[3], double dir[3], + double vert0[3], double vert1[3], double vert2[3], + double *t, double *u, double *v) +{ + double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3]; + double det,inv_det; + + /* find vectors for two edges sharing vert0 */ + IGL_RAY_TRI_SUB(edge1, vert1, vert0); + IGL_RAY_TRI_SUB(edge2, vert2, vert0); + + /* begin calculating determinant - also used to calculate U parameter */ + IGL_RAY_TRI_CROSS(pvec, dir, edge2); + + /* if determinant is near zero, ray lies in plane of triangle */ + det = IGL_RAY_TRI_DOT(edge1, pvec); + + /* calculate distance from vert0 to ray origin */ + IGL_RAY_TRI_SUB(tvec, orig, vert0); + inv_det = 1.0 / det; + + IGL_RAY_TRI_CROSS(qvec, tvec, edge1); + + if (det > IGL_RAY_TRI_EPSILON) + { + *u = IGL_RAY_TRI_DOT(tvec, pvec); + if (*u < 0.0 || *u > det) + return 0; + + /* calculate V parameter and test bounds */ + *v = IGL_RAY_TRI_DOT(dir, qvec); + if (*v < 0.0 || *u + *v > det) + return 0; + + } + else if(det < -IGL_RAY_TRI_EPSILON) + { + /* calculate U parameter and test bounds */ + *u = IGL_RAY_TRI_DOT(tvec, pvec); + if (*u > 0.0 || *u < det) + return 0; + + /* calculate V parameter and test bounds */ + *v = IGL_RAY_TRI_DOT(dir, qvec) ; + if (*v > 0.0 || *u + *v < det) + return 0; + } + else return 0; /* ray is parallel to the plane of the triangle */ + + *t = IGL_RAY_TRI_DOT(edge2, qvec) * inv_det; + (*u) *= inv_det; + (*v) *= inv_det; + + return 1; +} +#endif diff --git a/src/igl/readBF.cpp b/src/igl/readBF.cpp new file mode 100644 index 0000000000..03099ea7e1 --- /dev/null +++ b/src/igl/readBF.cpp @@ -0,0 +1,114 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readBF.h" +#include "list_to_matrix.h" +#include +#include +#include +#include +#include +template < + typename DerivedWI, + typename DerivedP, + typename DerivedO> +IGL_INLINE bool igl::readBF( + const std::string & filename, + Eigen::PlainObjectBase & WI, + Eigen::PlainObjectBase & P, + Eigen::PlainObjectBase & O) +{ + using namespace std; + ifstream is(filename); + if(!is.is_open()) + { + return false; + } + string line; + std::vector vWI; + std::vector vP; + std::vector > vO; + while(getline(is, line)) + { + int wi,p; + double cx,cy,cz; + if(sscanf(line.c_str(), "%d %d %lg %lg %lg",&wi,&p,&cx,&cy,&cz) != 5) + { + return false; + } + vWI.push_back(wi); + vP.push_back(p); + vO.push_back({cx,cy,cz}); + } + list_to_matrix(vWI,WI); + list_to_matrix(vP,P); + list_to_matrix(vO,O); + return true; +} + +template < + typename DerivedWI, + typename DerivedbfP, + typename DerivedO, + typename DerivedC, + typename DerivedBE, + typename DerivedP> +IGL_INLINE bool igl::readBF( + const std::string & filename, + Eigen::PlainObjectBase & WI, + Eigen::PlainObjectBase & bfP, + Eigen::PlainObjectBase & offsets, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & BE, + Eigen::PlainObjectBase & P) +{ + using namespace Eigen; + using namespace std; + if(!readBF(filename,WI,bfP,offsets)) + { + return false; + } + + C.resize(WI.rows(),3); + vector computed(C.rows(),false); + // better not be cycles in bfP + std::function locate_tip; + locate_tip = + [&offsets,&computed,&bfP,&locate_tip,&C](const int w)->Eigen::RowVector3d + { + if(w<0) return Eigen::RowVector3d(0,0,0); + if(computed[w]) return C.row(w); + computed[w] = true; + return C.row(w) = locate_tip(bfP(w)) + offsets.row(w); + }; + int num_roots = (bfP.array() == -1).count(); + BE.resize(WI.rows()-num_roots,2); + P.resize(BE.rows()); + for(int c = 0;c=0); + // weight associated with this bone + const int wi = WI(c); + if(wi >= 0) + { + // index into C + const int p = bfP(c); + assert(p >= 0 && "No weights for roots allowed"); + // index into BE + const int pwi = WI(p); + P(wi) = pwi; + BE(wi,0) = p; + BE(wi,1) = c; + } + } + return true; +} + +#ifdef IGL_STATIC_LIBRARY +template bool igl::readBF, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/readBF.h b/src/igl/readBF.h new file mode 100644 index 0000000000..a2b010c85a --- /dev/null +++ b/src/igl/readBF.h @@ -0,0 +1,67 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READBF_H +#define IGL_READBF_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Read a bones forest from a file, returns a list of bone roots + // Input: + // file_name path to .bf bones tree file + // Output: + // WI #B list of unique weight indices + // P #B list of parent indices into B, -1 for roots + // O #B by 3 list of tip offset vectors from parent (or position for roots) + // Returns true on success, false on errors + template < + typename DerivedWI, + typename DerivedP, + typename DerivedO> + IGL_INLINE bool readBF( + const std::string & filename, + Eigen::PlainObjectBase & WI, + Eigen::PlainObjectBase & P, + Eigen::PlainObjectBase & O); + // Read bone forest into pure bone-skeleton format, expects only bones (no + // point handles), and that a root in the .bf <---> no weight attachment. + // + // Input: + // file_name path to .bf bones tree file + // Output: + // WI #B list of unique weight indices + // P #B list of parent indices into B, -1 for roots + // O #B by 3 list of tip offset vectors from parent (or position for roots) + // C #C by 3 list of absolute joint locations + // BE #BE by 3 list of bone indices into C, in order of weight index + // P #BE list of parent bone indices into BE, -1 means root bone + // Returns true on success, false on errors + // + // See also: readTGF, bone_parents, forward_kinematics + template < + typename DerivedWI, + typename DerivedbfP, + typename DerivedO, + typename DerivedC, + typename DerivedBE, + typename DerivedP> + IGL_INLINE bool readBF( + const std::string & filename, + Eigen::PlainObjectBase & WI, + Eigen::PlainObjectBase & bfP, + Eigen::PlainObjectBase & O, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & BE, + Eigen::PlainObjectBase & P); +} + +#ifndef IGL_STATIC_LIBRARY +# include "readBF.cpp" +#endif +#endif diff --git a/src/igl/readCSV.cpp b/src/igl/readCSV.cpp new file mode 100644 index 0000000000..eb00b1fe4c --- /dev/null +++ b/src/igl/readCSV.cpp @@ -0,0 +1,68 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readCSV.h" + +#include +#include +#include +#include + +#include + +template +IGL_INLINE bool igl::readCSV( + const std::string str, + Eigen::Matrix& M) +{ + using namespace std; + + std::vector > Mt; + + std::ifstream infile(str.c_str()); + std::string line; + while (std::getline(infile, line)) + { + std::istringstream iss(line); + vector temp; + Scalar a; + while (iss >> a) + temp.push_back(a); + + if (temp.size() != 0) // skip empty lines + Mt.push_back(temp); + } + + if (Mt.size() != 0) + { + // Verify that it is indeed a matrix + for (unsigned i = 0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READ_CSV_H +#define IGL_READ_CSV_H + +#include "igl/igl_inline.h" +#include +#include +#include + +namespace igl +{ + // read a matrix from a csv file into a Eigen matrix + // Templates: + // Scalar type for the matrix + // Inputs: + // str path to .csv file + // Outputs: + // M eigen matrix + template + IGL_INLINE bool readCSV( + const std::string str, + Eigen::Matrix& M); +} + +#ifndef IGL_STATIC_LIBRARY +# include "readCSV.cpp" +#endif + +#endif diff --git a/src/igl/readDMAT.cpp b/src/igl/readDMAT.cpp new file mode 100644 index 0000000000..1ae9fdd65f --- /dev/null +++ b/src/igl/readDMAT.cpp @@ -0,0 +1,229 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readDMAT.h" + +#include "verbose.h" +#include +#include +#include + +// Static helper method reads the first to elements in the given file +// Inputs: +// fp file pointer of .dmat file that was just opened +// Outputs: +// num_rows number of rows +// num_cols number of columns +// Returns +// 0 success +// 1 did not find header +// 2 bad num_cols +// 3 bad num_rows +// 4 bad line ending +static inline int readDMAT_read_header(FILE * fp, int & num_rows, int & num_cols) +{ + // first line contains number of rows and number of columns + int res = fscanf(fp,"%d %d",&num_cols,&num_rows); + if(res != 2) + { + return 1; + } + // check that number of columns and rows are sane + if(num_cols < 0) + { + fprintf(stderr,"IOError: readDMAT() number of columns %d < 0\n",num_cols); + return 2; + } + if(num_rows < 0) + { + fprintf(stderr,"IOError: readDMAT() number of rows %d < 0\n",num_rows); + return 3; + } + // finish reading header + char lf; + + if(fread(&lf, sizeof(char), 1, fp)!=1 || !(lf == '\n' || lf == '\r')) + { + fprintf(stderr,"IOError: bad line ending in header\n"); + return 4; + } + + return 0; +} + +#ifndef IGL_NO_EIGEN +template +IGL_INLINE bool igl::readDMAT(const std::string file_name, + Eigen::PlainObjectBase & W) +{ + FILE * fp = fopen(file_name.c_str(),"rb"); + if(fp == NULL) + { + fprintf(stderr,"IOError: readDMAT() could not open %s...\n",file_name.c_str()); + return false; + } + int num_rows,num_cols; + int head_success = readDMAT_read_header(fp,num_rows,num_cols); + if(head_success != 0) + { + if(head_success == 1) + { + fprintf(stderr, + "IOError: readDMAT() first row should be [num cols] [num rows]...\n"); + } + fclose(fp); + return false; + } + + // Resize output to fit matrix, only if non-empty since this will trigger an + // error on fixed size matrices before reaching binary data. + bool empty = num_rows == 0 || num_cols == 0; + if(!empty) + { + W.resize(num_rows,num_cols); + } + + // Loop over columns slowly + for(int j = 0;j < num_cols;j++) + { + // loop over rows (down columns) quickly + for(int i = 0;i < num_rows;i++) + { + double d; + if(fscanf(fp," %lg",&d) != 1) + { + fclose(fp); + fprintf( + stderr, + "IOError: readDMAT() bad format after reading %d entries\n", + j*num_rows + i); + return false; + } + W(i,j) = d; + } + } + + // Try to read header for binary part + head_success = readDMAT_read_header(fp,num_rows,num_cols); + if(head_success == 0) + { + assert(W.size() == 0); + // Resize for output + W.resize(num_rows,num_cols); + double * Wraw = new double[num_rows*num_cols]; + fread(Wraw, sizeof(double), num_cols*num_rows, fp); + // Loop over columns slowly + for(int j = 0;j < num_cols;j++) + { + // loop over rows (down columns) quickly + for(int i = 0;i < num_rows;i++) + { + W(i,j) = Wraw[j*num_rows+i]; + } + } + }else + { + // we skipped resizing before in case there was binary data + if(empty) + { + // This could trigger an error if using fixed size matrices. + W.resize(num_rows,num_cols); + } + } + + fclose(fp); + return true; +} +#endif + +template +IGL_INLINE bool igl::readDMAT( + const std::string file_name, + std::vector > & W) +{ + FILE * fp = fopen(file_name.c_str(),"r"); + if(fp == NULL) + { + fprintf(stderr,"IOError: readDMAT() could not open %s...\n",file_name.c_str()); + return false; + } + int num_rows,num_cols; + bool head_success = readDMAT_read_header(fp,num_rows,num_cols); + if(head_success != 0) + { + if(head_success == 1) + { + fprintf(stderr, + "IOError: readDMAT() first row should be [num cols] [num rows]...\n"); + } + fclose(fp); + return false; + } + + // Resize for output + W.resize(num_rows,typename std::vector(num_cols)); + + // Loop over columns slowly + for(int j = 0;j < num_cols;j++) + { + // loop over rows (down columns) quickly + for(int i = 0;i < num_rows;i++) + { + double d; + if(fscanf(fp," %lg",&d) != 1) + { + fclose(fp); + fprintf( + stderr, + "IOError: readDMAT() bad format after reading %d entries\n", + j*num_rows + i); + return false; + } + W[i][j] = (Scalar)d; + } + } + + // Try to read header for binary part + head_success = readDMAT_read_header(fp,num_rows,num_cols); + if(head_success == 0) + { + assert(W.size() == 0); + // Resize for output + W.resize(num_rows,typename std::vector(num_cols)); + double * Wraw = new double[num_rows*num_cols]; + fread(Wraw, sizeof(double), num_cols*num_rows, fp); + // Loop over columns slowly + for(int j = 0;j < num_cols;j++) + { + // loop over rows (down columns) quickly + for(int i = 0;i < num_rows;i++) + { + W[i][j] = Wraw[j*num_rows+i]; + } + } + } + + fclose(fp); + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::readDMAT >(std::string, Eigen::PlainObjectBase >&); +template bool igl::readDMAT(std::string, std::vector >, std::allocator > > >&); +template bool igl::readDMAT >(std::string, Eigen::PlainObjectBase >&); +template bool igl::readDMAT >(std::string, Eigen::PlainObjectBase >&); +template bool igl::readDMAT >(std::string, Eigen::PlainObjectBase >&); +template bool igl::readDMAT >(std::string, Eigen::PlainObjectBase >&); +template bool igl::readDMAT >(std::string, Eigen::PlainObjectBase >&); +template bool igl::readDMAT >(std::string, Eigen::PlainObjectBase >&); +template bool igl::readDMAT >(std::string, Eigen::PlainObjectBase >&); +template bool igl::readDMAT >( std::string, Eigen::PlainObjectBase >&); +template bool igl::readDMAT >(std::string, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/readDMAT.h b/src/igl/readDMAT.h new file mode 100644 index 0000000000..e09c492c12 --- /dev/null +++ b/src/igl/readDMAT.h @@ -0,0 +1,53 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READDMAT_H +#define IGL_READDMAT_H +#include "igl_inline.h" +// .dmat is a simple ascii matrix file type, defined as follows. The first line +// is always: +// <#columns> <#rows> +// Then the coefficients of the matrix are given separated by whitespace with +// columns running fastest. +// +// Example: +// The matrix m = [1 2 3; 4 5 6]; +// corresponds to a .dmat file containing: +// 3 2 +// 1 4 2 5 3 6 +#include +#include +#ifndef IGL_NO_EIGEN +# include +#endif +namespace igl +{ + // Read a matrix from an ascii dmat file + // + // Inputs: + // file_name path to .dmat file + // Outputs: + // W eigen matrix containing read-in coefficients + // Returns true on success, false on error + // +#ifndef IGL_NO_EIGEN + template + IGL_INLINE bool readDMAT(const std::string file_name, + Eigen::PlainObjectBase & W); +#endif + // Wrapper for vector of vectors + template + IGL_INLINE bool readDMAT( + const std::string file_name, + std::vector > & W); +} + +#ifndef IGL_STATIC_LIBRARY +# include "readDMAT.cpp" +#endif + +#endif diff --git a/src/igl/readMESH.cpp b/src/igl/readMESH.cpp new file mode 100644 index 0000000000..bc912e11bc --- /dev/null +++ b/src/igl/readMESH.cpp @@ -0,0 +1,506 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readMESH.h" + +template +IGL_INLINE bool igl::readMESH( + const std::string mesh_file_name, + std::vector > & V, + std::vector > & T, + std::vector > & F) +{ + using namespace std; + FILE * mesh_file = fopen(mesh_file_name.c_str(),"r"); + if(NULL==mesh_file) + { + fprintf(stderr,"IOError: %s could not be opened...",mesh_file_name.c_str()); + return false; + } + return igl::readMESH(mesh_file,V,T,F); +} + +template +IGL_INLINE bool igl::readMESH( + FILE * mesh_file, + std::vector > & V, + std::vector > & T, + std::vector > & F) +{ + using namespace std; +#ifndef LINE_MAX +# define LINE_MAX 2048 +#endif + char line[LINE_MAX]; + bool still_comments; + V.clear(); + T.clear(); + F.clear(); + + // eat comments at beginning of file + still_comments= true; + while(still_comments) + { + if(fgets(line,LINE_MAX,mesh_file) == NULL) + { + fprintf(stderr, "Error: couldn't find start of .mesh file"); + fclose(mesh_file); + return false; + } + still_comments = (line[0] == '#' || line[0] == '\n'); + } + char str[LINE_MAX]; + sscanf(line," %s",str); + // check that first word is MeshVersionFormatted + if(0!=strcmp(str,"MeshVersionFormatted")) + { + fprintf(stderr, + "Error: first word should be MeshVersionFormatted not %s\n",str); + fclose(mesh_file); + return false; + } + + int one = -1; + if(2 != sscanf(line,"%s %d",str,&one)) + { + // 1 appears on next line? + fscanf(mesh_file," %d",&one); + } + if(one != 1) + { + fprintf(stderr,"Error: second word should be 1 not %d\n",one); + fclose(mesh_file); + return false; + } + + // eat comments + still_comments= true; + while(still_comments) + { + fgets(line,LINE_MAX,mesh_file); + still_comments = (line[0] == '#' || line[0] == '\n'); + } + + sscanf(line," %s",str); + // check that third word is Dimension + if(0!=strcmp(str,"Dimension")) + { + fprintf(stderr,"Error: third word should be Dimension not %s\n",str); + fclose(mesh_file); + return false; + } + int three = -1; + if(2 != sscanf(line,"%s %d",str,&three)) + { + // 1 appears on next line? + fscanf(mesh_file," %d",&three); + } + if(three != 3) + { + fprintf(stderr,"Error: only Dimension 3 supported not %d\n",three); + fclose(mesh_file); + return false; + } + + // eat comments + still_comments= true; + while(still_comments) + { + fgets(line,LINE_MAX,mesh_file); + still_comments = (line[0] == '#' || line[0] == '\n'); + } + + sscanf(line," %s",str); + // check that fifth word is Vertices + if(0!=strcmp(str,"Vertices")) + { + fprintf(stderr,"Error: fifth word should be Vertices not %s\n",str); + fclose(mesh_file); + return false; + } + + //fgets(line,LINE_MAX,mesh_file); + + int number_of_vertices; + if(1 != fscanf(mesh_file," %d",&number_of_vertices) || number_of_vertices > 1000000000) + { + fprintf(stderr,"Error: expecting number of vertices less than 10^9...\n"); + fclose(mesh_file); + return false; + } + // allocate space for vertices + V.resize(number_of_vertices,vector(3,0)); + int extra; + for(int i = 0;i(3)); + // triangle indices + int tri[3]; + for(int i = 0;i(4)); + // tet indices + int a,b,c,d; + for(int i = 0;i +#include "list_to_matrix.h" + + +template +IGL_INLINE bool igl::readMESH( + const std::string mesh_file_name, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& T, + Eigen::PlainObjectBase& F) +{ + using namespace std; + FILE * mesh_file = fopen(mesh_file_name.c_str(),"r"); + if(NULL==mesh_file) + { + fprintf(stderr,"IOError: %s could not be opened...",mesh_file_name.c_str()); + return false; + } + return readMESH(mesh_file,V,T,F); +} + +template +IGL_INLINE bool igl::readMESH( + FILE * mesh_file, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& T, + Eigen::PlainObjectBase& F) +{ + using namespace std; +#ifndef LINE_MAX +# define LINE_MAX 2048 +#endif + char line[LINE_MAX]; + bool still_comments; + + // eat comments at beginning of file + still_comments= true; + while(still_comments) + { + fgets(line,LINE_MAX,mesh_file); + still_comments = (line[0] == '#' || line[0] == '\n'); + } + + char str[LINE_MAX]; + sscanf(line," %s",str); + // check that first word is MeshVersionFormatted + if(0!=strcmp(str,"MeshVersionFormatted")) + { + fprintf(stderr, + "Error: first word should be MeshVersionFormatted not %s\n",str); + fclose(mesh_file); + return false; + } + int one = -1; + if(2 != sscanf(line,"%s %d",str,&one)) + { + // 1 appears on next line? + fscanf(mesh_file," %d",&one); + } + if(one != 1) + { + fprintf(stderr,"Error: second word should be 1 not %d\n",one); + fclose(mesh_file); + return false; + } + + // eat comments + still_comments= true; + while(still_comments) + { + fgets(line,LINE_MAX,mesh_file); + still_comments = (line[0] == '#' || line[0] == '\n'); + } + + sscanf(line," %s",str); + // check that third word is Dimension + if(0!=strcmp(str,"Dimension")) + { + fprintf(stderr,"Error: third word should be Dimension not %s\n",str); + fclose(mesh_file); + return false; + } + int three = -1; + if(2 != sscanf(line,"%s %d",str,&three)) + { + // 1 appears on next line? + fscanf(mesh_file," %d",&three); + } + if(three != 3) + { + fprintf(stderr,"Error: only Dimension 3 supported not %d\n",three); + fclose(mesh_file); + return false; + } + + // eat comments + still_comments= true; + while(still_comments) + { + fgets(line,LINE_MAX,mesh_file); + still_comments = (line[0] == '#' || line[0] == '\n'); + } + + sscanf(line," %s",str); + // check that fifth word is Vertices + if(0!=strcmp(str,"Vertices")) + { + fprintf(stderr,"Error: fifth word should be Vertices not %s\n",str); + fclose(mesh_file); + return false; + } + + //fgets(line,LINE_MAX,mesh_file); + + int number_of_vertices; + if(1 != fscanf(mesh_file," %d",&number_of_vertices) || number_of_vertices > 1000000000) + { + fprintf(stderr,"Error: expecting number of vertices less than 10^9...\n"); + fclose(mesh_file); + return false; + } + // allocate space for vertices + V.resize(number_of_vertices,3); + int extra; + for(int i = 0;i > vV,vT,vF; +// bool success = igl::readMESH(mesh_file_name,vV,vT,vF); +// if(!success) +// { +// // readMESH already printed error message to std err +// return false; +// } +// bool V_rect = igl::list_to_matrix(vV,V); +// if(!V_rect) +// { +// // igl::list_to_matrix(vV,V) already printed error message to std err +// return false; +// } +// bool T_rect = igl::list_to_matrix(vT,T); +// if(!T_rect) +// { +// // igl::list_to_matrix(vT,T) already printed error message to std err +// return false; +// } +// bool F_rect = igl::list_to_matrix(vF,F); +// if(!F_rect) +// { +// // igl::list_to_matrix(vF,F) already printed error message to std err +// return false; +// } +// assert(V.cols() == 3); +// assert(T.cols() == 4); +// assert(F.cols() == 3); +// return true; +//} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::readMESH, Eigen::Matrix, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readMESH, Eigen::Matrix, Eigen::Matrix >(FILE*, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::readMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::readMESH(std::basic_string, std::allocator >, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template bool igl::readMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/readMESH.h b/src/igl/readMESH.h new file mode 100644 index 0000000000..b9b7630351 --- /dev/null +++ b/src/igl/readMESH.h @@ -0,0 +1,78 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READMESH_H +#define IGL_READMESH_H +#include "igl_inline.h" + +#include +#include +#include +#include + +namespace igl +{ + // load a tetrahedral volume mesh from a .mesh file + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Index type for indices (will be read as int and cast to Index) + // Input: + // mesh_file_name path of .mesh file + // Outputs: + // V double matrix of vertex positions #V by 3 + // T #T list of tet indices into vertex positions + // F #F list of face indices into vertex positions + // + // Known bugs: Holes and regions are not supported + template + IGL_INLINE bool readMESH( + const std::string mesh_file_name, + std::vector > & V, + std::vector > & T, + std::vector > & F); + // Inputs: + // mesh_file pointer to already opened .mesh file + // Outputs: + // mesh_file closed file + template + IGL_INLINE bool readMESH( + FILE * mesh_file, + std::vector > & V, + std::vector > & T, + std::vector > & F); + + // Input: + // mesh_file_name path of .mesh file + // Outputs: + // V eigen double matrix #V by 3 + // T eigen int matrix #T by 4 + // F eigen int matrix #F by 3 + template + IGL_INLINE bool readMESH( + const std::string mesh_file_name, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& T, + Eigen::PlainObjectBase& F); + // Inputs: + // mesh_file pointer to already opened .mesh file + // Outputs: + // mesh_file closed file + template + IGL_INLINE bool readMESH( + FILE * mesh_file, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& T, + Eigen::PlainObjectBase& F); +} + +#ifndef IGL_STATIC_LIBRARY +# include "readMESH.cpp" +#endif + +#endif diff --git a/src/igl/readMSH.cpp b/src/igl/readMSH.cpp new file mode 100644 index 0000000000..2335fd1cca --- /dev/null +++ b/src/igl/readMSH.cpp @@ -0,0 +1,500 @@ + +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#include "readMSH.h" +#include +#include +#include +#include +#include + +template < + typename DerivedV, + typename DerivedT> +IGL_INLINE bool igl::readMSH( + const std::string & filename, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & T) +{ + // https://github.com/Yixin-Hu/TetWild/blob/master/pymesh/MshSaver.cpp + // Original copyright: /* This file is part of PyMesh. Copyright (c) 2015 by Qingnan Zhou */ + typedef typename DerivedV::Scalar Float; + typedef Eigen::Matrix VectorF; + typedef Eigen::Matrix VectorI; + typedef std::map FieldMap; + typedef std::vector FieldNames; + VectorF m_nodes; + VectorI m_elements; + FieldMap m_node_fields; + FieldMap m_element_fields; + + bool m_binary; + size_t m_data_size; + size_t m_nodes_per_element; + size_t m_element_type; + std::ifstream fin(filename.c_str(), std::ios::in | std::ios::binary); + if (!fin.is_open()) + { + std::stringstream err_msg; + err_msg << "failed to open file \"" << filename << "\""; + return false; + } + // Parse header + std::string buf; + double version; + int type; + fin >> buf; + const auto invalid_format = []()->bool + { + assert(false && "Invalid format"); + return false; + }; + const auto not_implemented = []()->bool + { + assert(false && "Not implemented"); + return false; + }; + if (buf != "$MeshFormat") { return invalid_format(); } + + fin >> version >> type >> m_data_size; + m_binary = (type == 1); + + // Some sanity check. + if (m_data_size != 8) { + std::cerr << "Error: data size must be 8 bytes." << std::endl; + return not_implemented(); + } + if (sizeof(int) != 4) { + std::cerr << "Error: code must be compiled with int size 4 bytes." << std::endl; + return not_implemented(); + } + const auto eat_white_space = [](std::ifstream& fin) + { + char next = fin.peek(); + while (next == '\n' || next == ' ' || next == '\t' || next == '\r') + { + fin.get(); + next = fin.peek(); + } + }; + + // Read in extra info from binary header. + if (m_binary) { + int one; + eat_white_space(fin); + fin.read(reinterpret_cast(&one), sizeof(int)); + if (one != 1) { + std::cerr << "Warning: binary msh file " << filename + << " is saved with different endianness than this machine." + << std::endl; + return not_implemented(); + } + } + + fin >> buf; + if (buf != "$EndMeshFormat") { return not_implemented(); } + + const auto num_nodes_per_elem_type = [](int elem_type)->int + { + size_t nodes_per_element = 0; + switch (elem_type) { + case 2: + nodes_per_element = 3; // Triangle + break; + case 3: + nodes_per_element = 4; // Quad + break; + case 4: + nodes_per_element = 4; // Tet + break; + case 5: + nodes_per_element = 8; // hexahedron + break; + default: + assert(false && "not implemented"); + nodes_per_element = -1; + break; + } + return nodes_per_element; + }; + + const auto parse_nodes = [&](std::ifstream& fin) + { + size_t num_nodes; + fin >> num_nodes; + m_nodes.resize(num_nodes*3); + + if (m_binary) { + size_t num_bytes = (4+3*m_data_size) * num_nodes; + char* data = new char[num_bytes]; + eat_white_space(fin); + fin.read(data, num_bytes); + + for (size_t i=0; i (&data[i*(4+3*m_data_size)]) - 1; + m_nodes[node_idx*3] = *reinterpret_cast(&data[i*(4+3*m_data_size) + 4]); + m_nodes[node_idx*3+1] = *reinterpret_cast(&data[i*(4+3*m_data_size) + 4 + m_data_size]); + m_nodes[node_idx*3+2] = *reinterpret_cast(&data[i*(4+3*m_data_size) + 4 + 2*m_data_size]); + } + + delete [] data; + } else { + int node_idx; + for (size_t i=0; i> node_idx; + node_idx -= 1; + fin >> m_nodes[node_idx*3] + >> m_nodes[node_idx*3+1] + >> m_nodes[node_idx*3+2]; + } + } + }; + + const auto parse_elements = [&](std::ifstream& fin) + { + size_t num_elements; + fin >> num_elements; + + // Tmp storage of elements; + std::vector triangle_element_idx; + std::vector triangle_elements; + std::vector quad_element_idx; + std::vector quad_elements; + std::vector tet_element_idx; + std::vector tet_elements; + std::vector hex_element_idx; + std::vector hex_elements; + + auto get_element_storage = [&](int elem_type) -> std::vector* { + switch (elem_type) { + default: + assert(false && "Unsupported element type encountered"); + case 2: + return &triangle_elements; + case 3: + return &quad_elements; + case 4: + return &tet_elements; + case 5: + return &hex_elements; + }; + }; + + auto get_element_idx_storage = [&](int elem_type) -> std::vector* { + switch (elem_type) { + default: + assert(false && "Unsupported element type encountered"); + case 2: + return &triangle_element_idx; + case 3: + return &quad_element_idx; + case 4: + return &tet_element_idx; + case 5: + return &hex_element_idx; + }; + }; + + size_t nodes_per_element; + int glob_elem_type = -1; + + + if (m_binary) + { + eat_white_space(fin); + int elem_read = 0; + while (elem_read < num_elements) { + // Parse element header. + int elem_type, num_elems, num_tags; + fin.read((char*)&elem_type, sizeof(int)); + fin.read((char*)&num_elems, sizeof(int)); + fin.read((char*)&num_tags, sizeof(int)); + nodes_per_element = num_nodes_per_elem_type(elem_type); + std::vector& elements = *get_element_storage(elem_type); + std::vector& element_idx = *get_element_idx_storage(elem_type); + + for (size_t i=0; i> elem_num >> elem_type >> num_tags; + for (size_t j=0; j> tag; + } + nodes_per_element = num_nodes_per_elem_type(elem_type); + std::vector& elements = *get_element_storage(elem_type); + std::vector& element_idx = *get_element_idx_storage(elem_type); + + elem_num -= 1; + element_idx.push_back(elem_num); + + // Parse node idx. + for (size_t j=0; j> idx; + elements.push_back(idx-1); // msh index starts from 1. + } + } + } + + auto copy_to_array = [&]( + const std::vector& elements, + const int nodes_per_element) { + const size_t num_elements = elements.size() / nodes_per_element; + if (elements.size() % nodes_per_element != 0) { + assert(false && "parsing element failed"); + return; + } + m_elements.resize(elements.size()); + std::copy(elements.begin(), elements.end(), m_elements.data()); + m_nodes_per_element = nodes_per_element; + }; + + if (!tet_elements.empty()) { + copy_to_array(tet_elements, 4); + m_element_type = 4; + } else if (!hex_elements.empty()) { + copy_to_array(hex_elements, 8); + m_element_type = 5; + } else if (!triangle_elements.empty()) { + copy_to_array(triangle_elements, 3); + m_element_type = 2; + } else if (!quad_elements.empty()) { + copy_to_array(quad_elements, 4); + m_element_type = 3; + } else { + // 0 elements, use triangle by default. + m_element_type = 2; + } + }; + const auto parse_element_field = [&](std::ifstream& fin) + { + size_t num_string_tags; + size_t num_real_tags; + size_t num_int_tags; + + fin >> num_string_tags; + std::string* str_tags = new std::string[num_string_tags]; + for (size_t i=0; i> str_tags[i]; + } + } + + fin >> num_real_tags; + Float* real_tags = new Float[num_real_tags]; + for (size_t i=0; i> real_tags[i]; + + fin >> num_int_tags; + int* int_tags = new int[num_int_tags]; + for (size_t i=0; i> int_tags[i]; + + if (num_string_tags <= 0 || num_int_tags <= 2) { assert(false && "invalid format"); return; } + std::string fieldname = str_tags[0]; + int num_components = int_tags[1]; + int num_entries = int_tags[2]; + VectorF field(num_entries * num_components); + + delete [] str_tags; + delete [] real_tags; + delete [] int_tags; + + if (m_binary) { + size_t num_bytes = (num_components * m_data_size + 4) * num_entries; + char* data = new char[num_bytes]; + eat_white_space(fin); + fin.read(data, num_bytes); + for (size_t i=0; i(&data[i*(4+num_components*m_data_size)]); + elem_idx -= 1; + size_t base_idx = i*(4+num_components*m_data_size) + 4; + for (size_t j=0; j(&data[base_idx+j*m_data_size]); + } + } + delete [] data; + } else { + int elem_idx; + for (size_t i=0; i> elem_idx; + elem_idx -= 1; + for (size_t j=0; j> field[elem_idx * num_components + j]; + } + } + } + + m_element_fields[fieldname] = field; + }; + + const auto parse_node_field = [&](std::ifstream& fin) + { + size_t num_string_tags; + size_t num_real_tags; + size_t num_int_tags; + + fin >> num_string_tags; + std::string* str_tags = new std::string[num_string_tags]; + for (size_t i=0; i> str_tags[i]; + } + } + + fin >> num_real_tags; + Float* real_tags = new Float[num_real_tags]; + for (size_t i=0; i> real_tags[i]; + + fin >> num_int_tags; + int* int_tags = new int[num_int_tags]; + for (size_t i=0; i> int_tags[i]; + + if (num_string_tags <= 0 || num_int_tags <= 2) { assert(false && "invalid format"); return; } + std::string fieldname = str_tags[0]; + int num_components = int_tags[1]; + int num_entries = int_tags[2]; + VectorF field(num_entries * num_components); + + delete [] str_tags; + delete [] real_tags; + delete [] int_tags; + + if (m_binary) { + size_t num_bytes = (num_components * m_data_size + 4) * num_entries; + char* data = new char[num_bytes]; + eat_white_space(fin); + fin.read(data, num_bytes); + for (size_t i=0; i(&data[i*(4+num_components*m_data_size)]); + node_idx -= 1; + size_t base_idx = i*(4+num_components*m_data_size) + 4; + for (size_t j=0; j(&data[base_idx+j*m_data_size]); + } + } + delete [] data; + } else { + int node_idx; + for (size_t i=0; i> node_idx; + node_idx -= 1; + for (size_t j=0; j> field[node_idx * num_components + j]; + } + } + } + + m_node_fields[fieldname] = field; + }; + const auto parse_unknown_field = [](std::ifstream& fin, + const std::string& fieldname) + { + std::cerr << "Warning: \"" << fieldname << "\" not supported yet. Ignored." << std::endl; + std::string endmark = fieldname.substr(0,1) + "End" + + fieldname.substr(1,fieldname.size()-1); + + std::string buf(""); + while (buf != endmark && !fin.eof()) { + fin >> buf; + } + }; + + + while (!fin.eof()) { + buf.clear(); + fin >> buf; + if (buf == "$Nodes") { + parse_nodes(fin); + fin >> buf; + if (buf != "$EndNodes") { return invalid_format(); } + } else if (buf == "$Elements") { + parse_elements(fin); + fin >> buf; + if (buf != "$EndElements") { return invalid_format(); } + } else if (buf == "$NodeData") { + parse_node_field(fin); + fin >> buf; + if (buf != "$EndNodeData") { return invalid_format(); } + } else if (buf == "$ElementData") { + parse_element_field(fin); + fin >> buf; + if (buf != "$EndElementData") { return invalid_format(); } + } else if (fin.eof()) { + break; + } else { + parse_unknown_field(fin, buf); + } + } + fin.close(); + V.resize(m_nodes.rows()/3,3); + for (int i = 0; i < m_nodes.rows() / 3; i++) + { + for (int j = 0; j < 3; j++) + { + V(i,j) = m_nodes(i * 3 + j); + } + } + int ss = num_nodes_per_elem_type(m_element_type); + T.resize(m_elements.rows()/ss,ss); + for (int i = 0; i < m_elements.rows() / ss; i++) + { + for (int j = 0; j < ss; j++) + { + T(i, j) = m_elements(i * ss + j); + } + } + return true; +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/readMSH.h b/src/igl/readMSH.h new file mode 100644 index 0000000000..1463411d85 --- /dev/null +++ b/src/igl/readMSH.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READMSH_H +#define IGL_READMSH_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Read a mesh (e.g., tet mesh) from a gmsh .msh file + // + // Inputs: + // filename path to .msh file + // Outputs: + // V #V by 3 list of 3D mesh vertex positions + // T #T by ss list of 3D ss-element indices into V (e.g., ss=4 for tets) + // Returns true on success + template < + typename DerivedV, + typename DerivedT> + IGL_INLINE bool readMSH( + const std::string & filename, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & T); +} + + +#ifndef IGL_STATIC_LIBRARY +# include "readMSH.cpp" +#endif +#endif diff --git a/src/igl/readNODE.cpp b/src/igl/readNODE.cpp new file mode 100644 index 0000000000..cb775e5992 --- /dev/null +++ b/src/igl/readNODE.cpp @@ -0,0 +1,161 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readNODE.h" +#include "matrix_to_list.h" +#include + +template +IGL_INLINE bool igl::readNODE( + const std::string node_file_name, + std::vector > & V, + std::vector > & I) +{ + // TODO: should be templated + Eigen::MatrixXd mV; + Eigen::MatrixXi mI; + if(igl::readNODE(node_file_name,mV,mI)) + { + matrix_to_list(mV,V); + matrix_to_list(mI,I); + return true; + }else + { + return false; + } +} + +template +IGL_INLINE bool igl::readNODE( + const std::string node_file_name, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& I) +{ + using namespace std; + FILE * node_file = fopen(node_file_name.c_str(),"r"); + if(NULL==node_file) + { + fprintf(stderr,"readNODE: IOError: %s could not be opened...\n", + node_file_name.c_str()); + return false; + } +#ifndef LINE_MAX +# define LINE_MAX 2048 +#endif + char line[LINE_MAX]; + bool still_comments; + + // eat comments at beginning of file + still_comments= true; + while(still_comments) + { + fgets(line,LINE_MAX,node_file); + still_comments = (line[0] == '#' || line[0] == '\n'); + } + + // Read header + // n number of points + // dim dimension + // num_attr number of attributes + // num_bm number of boundary markers + int n,dim,num_attr,num_bm; + int head_count = sscanf(line,"%d %d %d %d", &n, &dim, &num_attr, &num_bm); + if(head_count!=4) + { + fprintf(stderr,"readNODE: Error: incorrect header in %s...\n", + node_file_name.c_str()); + fclose(node_file); + return false; + } + if(num_attr) + { + fprintf(stderr,"readNODE: Error: %d attributes found in %s. " + "Attributes are not supported...\n", + num_attr, + node_file_name.c_str()); + fclose(node_file); + return false; + } + // Just quietly ignore boundary markers + //if(num_bm) + //{ + // fprintf(stderr,"readNODE: Warning: %d boundary markers found in %s. " + // "Boundary markers are ignored...\n", + // num_bm, + // node_file_name.c_str()); + //} + + // resize output + V.resize(n,dim); + I.resize(n,1); + + int line_no = 0; + int p = 0; + while (fgets(line, LINE_MAX, node_file) != NULL) + { + line_no++; + // Skip comments and blank lines + if(line[0] == '#' || line[0] == '\n') + { + continue; + } + char * l = line; + int offset; + + if(sscanf(l,"%d%n",&I(p),&offset) != 1) + { + fprintf(stderr,"readNODE Error: bad index (%d) in %s...\n", + line_no, + node_file_name.c_str()); + fclose(node_file); + return false; + } + // adjust offset + l += offset; + + // Read coordinates + for(int d = 0;d, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/readNODE.h b/src/igl/readNODE.h new file mode 100644 index 0000000000..a4bdb4774a --- /dev/null +++ b/src/igl/readNODE.h @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READNODE_H +#define IGL_READNODE_H +#include "igl_inline.h" + +#include +#include +#include + +namespace igl +{ + // load a list of points from a .node file + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Index type for indices (will be read as int and cast to Index) + // Input: + // node_file_name path of .node file + // Outputs: + // V double matrix of vertex positions #V by dim + // I list of indices (first tells whether 0 or 1 indexed) + template + IGL_INLINE bool readNODE( + const std::string node_file_name, + std::vector > & V, + std::vector > & I); + + // Input: + // node_file_name path of .node file + // Outputs: + // V eigen double matrix #V by dim + template + IGL_INLINE bool readNODE( + const std::string node_file_name, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& I); +} + +#ifndef IGL_STATIC_LIBRARY +# include "readNODE.cpp" +#endif + +#endif diff --git a/src/igl/readOBJ.cpp b/src/igl/readOBJ.cpp new file mode 100644 index 0000000000..42173fc84b --- /dev/null +++ b/src/igl/readOBJ.cpp @@ -0,0 +1,366 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readOBJ.h" + +#include "list_to_matrix.h" +#include "max_size.h" +#include "min_size.h" + +#include +#include +#include +#include +#include + +template +IGL_INLINE bool igl::readOBJ( + const std::string obj_file_name, + std::vector > & V, + std::vector > & TC, + std::vector > & N, + std::vector > & F, + std::vector > & FTC, + std::vector > & FN) +{ + // Open file, and check for error + FILE * obj_file = fopen(obj_file_name.c_str(),"r"); + if(NULL==obj_file) + { + fprintf(stderr,"IOError: %s could not be opened...\n", + obj_file_name.c_str()); + return false; + } + return igl::readOBJ(obj_file,V,TC,N,F,FTC,FN); +} + +template +IGL_INLINE bool igl::readOBJ( + FILE * obj_file, + std::vector > & V, + std::vector > & TC, + std::vector > & N, + std::vector > & F, + std::vector > & FTC, + std::vector > & FN) +{ + // File open was successful so clear outputs + V.clear(); + TC.clear(); + N.clear(); + F.clear(); + FTC.clear(); + FN.clear(); + + // variables and constants to assist parsing the .obj file + // Constant strings to compare against + std::string v("v"); + std::string vn("vn"); + std::string vt("vt"); + std::string f("f"); + std::string tic_tac_toe("#"); +#ifndef IGL_LINE_MAX +# define IGL_LINE_MAX 2048 +#endif + + char line[IGL_LINE_MAX]; + int line_no = 1; + while (fgets(line, IGL_LINE_MAX, obj_file) != NULL) + { + char type[IGL_LINE_MAX]; + // Read first word containing type + if(sscanf(line, "%s",type) == 1) + { + // Get pointer to rest of line right after type + char * l = &line[strlen(type)]; + if(type == v) + { + std::istringstream ls(&line[1]); + std::vector vertex{ std::istream_iterator(ls), std::istream_iterator() }; + + if (vertex.size() < 3) + { + fprintf(stderr, + "Error: readOBJ() vertex on line %d should have at least 3 coordinates", + line_no); + fclose(obj_file); + return false; + } + + V.push_back(vertex); + }else if(type == vn) + { + double x[3]; + int count = + sscanf(l,"%lf %lf %lf\n",&x[0],&x[1],&x[2]); + if(count != 3) + { + fprintf(stderr, + "Error: readOBJ() normal on line %d should have 3 coordinates", + line_no); + fclose(obj_file); + return false; + } + std::vector normal(count); + for(int i = 0;i tex(count); + for(int i = 0;iint + { + return i<0 ? i+V.size() : i-1; + }; + const auto & shift_t = [&TC](const int i)->int + { + return i<0 ? i+TC.size() : i-1; + }; + const auto & shift_n = [&N](const int i)->int + { + return i<0 ? i+N.size() : i-1; + }; + std::vector f; + std::vector ftc; + std::vector fn; + // Read each "word" after type + char word[IGL_LINE_MAX]; + int offset; + while(sscanf(l,"%s%n",word,&offset) == 1) + { + // adjust offset + l += offset; + // Process word + long int i,it,in; + if(sscanf(word,"%ld/%ld/%ld",&i,&it,&in) == 3) + { + f.push_back(shift(i)); + ftc.push_back(shift_t(it)); + fn.push_back(shift_n(in)); + }else if(sscanf(word,"%ld/%ld",&i,&it) == 2) + { + f.push_back(shift(i)); + ftc.push_back(shift_t(it)); + }else if(sscanf(word,"%ld//%ld",&i,&in) == 2) + { + f.push_back(shift(i)); + fn.push_back(shift_n(in)); + }else if(sscanf(word,"%ld",&i) == 1) + { + f.push_back(shift(i)); + }else + { + fprintf(stderr, + "Error: readOBJ() face on line %d has invalid element format\n", + line_no); + fclose(obj_file); + return false; + } + } + if( + (f.size()>0 && fn.size() == 0 && ftc.size() == 0) || + (f.size()>0 && fn.size() == f.size() && ftc.size() == 0) || + (f.size()>0 && fn.size() == 0 && ftc.size() == f.size()) || + (f.size()>0 && fn.size() == f.size() && ftc.size() == f.size())) + { + // No matter what add each type to lists so that lists are the + // correct lengths + F.push_back(f); + FTC.push_back(ftc); + FN.push_back(fn); + }else + { + fprintf(stderr, + "Error: readOBJ() face on line %d has invalid format\n", line_no); + fclose(obj_file); + return false; + } + }else if(strlen(type) >= 1 && (type[0] == '#' || + type[0] == 'g' || + type[0] == 's' || + strcmp("usemtl",type)==0 || + strcmp("mtllib",type)==0)) + { + //ignore comments or other shit + }else + { + //ignore any other lines + fprintf(stderr, + "Warning: readOBJ() ignored non-comment line %d:\n %s", + line_no, + line); + } + }else + { + // ignore empty line + } + line_no++; + } + fclose(obj_file); + + assert(F.size() == FN.size()); + assert(F.size() == FTC.size()); + + return true; +} + +template +IGL_INLINE bool igl::readOBJ( + const std::string obj_file_name, + std::vector > & V, + std::vector > & F) +{ + std::vector > TC,N; + std::vector > FTC,FN; + return readOBJ(obj_file_name,V,TC,N,F,FTC,FN); +} + +template < + typename DerivedV, + typename DerivedTC, + typename DerivedCN, + typename DerivedF, + typename DerivedFTC, + typename DerivedFN> +IGL_INLINE bool igl::readOBJ( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& TC, + Eigen::PlainObjectBase& CN, + Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& FTC, + Eigen::PlainObjectBase& FN) +{ + std::vector > vV,vTC,vN; + std::vector > vF,vFTC,vFN; + bool success = igl::readOBJ(str,vV,vTC,vN,vF,vFTC,vFN); + if(!success) + { + // readOBJ(str,vV,vTC,vN,vF,vFTC,vFN) should have already printed an error + // message to stderr + return false; + } + bool V_rect = igl::list_to_matrix(vV,V); + const char * format = "Failed to cast %s to matrix: min (%d) != max (%d)\n"; + if(!V_rect) + { + printf(format,"V",igl::min_size(vV),igl::max_size(vV)); + return false; + } + bool F_rect = igl::list_to_matrix(vF,F); + if(!F_rect) + { + printf(format,"F",igl::min_size(vF),igl::max_size(vF)); + return false; + } + if(!vN.empty()) + { + bool VN_rect = igl::list_to_matrix(vN,CN); + if(!VN_rect) + { + printf(format,"CN",igl::min_size(vN),igl::max_size(vN)); + return false; + } + } + + if(!vFN.empty() && !vFN[0].empty()) + { + bool FN_rect = igl::list_to_matrix(vFN,FN); + if(!FN_rect) + { + printf(format,"FN",igl::min_size(vFN),igl::max_size(vFN)); + return false; + } + } + + if(!vTC.empty()) + { + + bool T_rect = igl::list_to_matrix(vTC,TC); + if(!T_rect) + { + printf(format,"TC",igl::min_size(vTC),igl::max_size(vTC)); + return false; + } + } + if(!vFTC.empty()&& !vFTC[0].empty()) + { + + bool FTC_rect = igl::list_to_matrix(vFTC,FTC); + if(!FTC_rect) + { + printf(format,"FTC",igl::min_size(vFTC),igl::max_size(vFTC)); + return false; + } + } + return true; +} + +template +IGL_INLINE bool igl::readOBJ( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F) +{ + std::vector > vV,vTC,vN; + std::vector > vF,vFTC,vFN; + bool success = igl::readOBJ(str,vV,vTC,vN,vF,vFTC,vFN); + if(!success) + { + // readOBJ(str,vV,vTC,vN,vF,vFTC,vFN) should have already printed an error + // message to stderr + return false; + } + bool V_rect = igl::list_to_matrix(vV,V); + if(!V_rect) + { + // igl::list_to_matrix(vV,V) already printed error message to std err + return false; + } + bool F_rect = igl::list_to_matrix(vF,F); + if(!F_rect) + { + // igl::list_to_matrix(vF,F) already printed error message to std err + return false; + } + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::readOBJ, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readOBJ, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readOBJ, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readOBJ, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readOBJ(std::basic_string, std::allocator >, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +#endif diff --git a/src/igl/readOBJ.h b/src/igl/readOBJ.h new file mode 100644 index 0000000000..443b6fc8e5 --- /dev/null +++ b/src/igl/readOBJ.h @@ -0,0 +1,101 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READOBJ_H +#define IGL_READOBJ_H +#include "igl_inline.h" +#include "deprecated.h" +// History: +// return type changed from void to bool Alec 18 Sept 2011 +// added pure vector of vectors version that has much more support Alec 31 Oct +// 2011 + +#ifndef IGL_NO_EIGEN +# include +#endif +#include +#include +#include + +namespace igl +{ + // Read a mesh from an ascii obj file, filling in vertex positions, normals + // and texture coordinates. Mesh may have faces of any number of degree + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Index type for indices (will be read as int and cast to Index) + // Inputs: + // str path to .obj file + // Outputs: + // V double matrix of vertex positions #V by 3 + // TC double matrix of texture coordinats #TC by 2 + // N double matrix of corner normals #N by 3 + // F #F list of face indices into vertex positions + // FTC #F list of face indices into vertex texture coordinates + // FN #F list of face indices into vertex normals + // Returns true on success, false on errors + template + IGL_INLINE bool readOBJ( + const std::string obj_file_name, + std::vector > & V, + std::vector > & TC, + std::vector > & N, + std::vector > & F, + std::vector > & FTC, + std::vector > & FN); + // Inputs: + // obj_file pointer to already opened .obj file + // Outputs: + // obj_file closed file + template + IGL_INLINE bool readOBJ( + FILE * obj_file, + std::vector > & V, + std::vector > & TC, + std::vector > & N, + std::vector > & F, + std::vector > & FTC, + std::vector > & FN); + // Just V and F + template + IGL_INLINE bool readOBJ( + const std::string obj_file_name, + std::vector > & V, + std::vector > & F); + // Eigen Wrappers. These will return true only if the data is perfectly + // "rectangular": All faces are the same degree, all have the same number of + // textures/normals etc. + template < + typename DerivedV, + typename DerivedTC, + typename DerivedCN, + typename DerivedF, + typename DerivedFTC, + typename DerivedFN> + IGL_INLINE bool readOBJ( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& TC, + Eigen::PlainObjectBase& CN, + Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& FTC, + Eigen::PlainObjectBase& FN); + template + IGL_INLINE bool readOBJ( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "readOBJ.cpp" +#endif + +#endif diff --git a/src/igl/readOFF.cpp b/src/igl/readOFF.cpp new file mode 100644 index 0000000000..11d7e0efc8 --- /dev/null +++ b/src/igl/readOFF.cpp @@ -0,0 +1,268 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readOFF.h" +#include "list_to_matrix.h" + +template +IGL_INLINE bool igl::readOFF( + const std::string off_file_name, + std::vector > & V, + std::vector > & F, + std::vector > & N, + std::vector > & C) +{ + using namespace std; + FILE * off_file = fopen(off_file_name.c_str(),"r"); + if(NULL==off_file) + { + printf("IOError: %s could not be opened...\n",off_file_name.c_str()); + return false; + } + return readOFF(off_file,V,F,N,C); +} + +template +IGL_INLINE bool igl::readOFF( + FILE * off_file, + std::vector > & V, + std::vector > & F, + std::vector > & N, + std::vector > & C) +{ + using namespace std; + V.clear(); + F.clear(); + N.clear(); + C.clear(); + + // First line is always OFF + char header[1000]; + const std::string OFF("OFF"); + const std::string NOFF("NOFF"); + const std::string COFF("COFF"); + if(fscanf(off_file,"%s\n",header)!=1 + || !( + string(header).compare(0, OFF.length(), OFF)==0 || + string(header).compare(0, COFF.length(), COFF)==0 || + string(header).compare(0,NOFF.length(),NOFF)==0)) + { + printf("Error: readOFF() first line should be OFF or NOFF or COFF, not %s...",header); + fclose(off_file); + return false; + } + bool has_normals = string(header).compare(0,NOFF.length(),NOFF)==0; + bool has_vertexColors = string(header).compare(0,COFF.length(),COFF)==0; + // Second line is #vertices #faces #edges + int number_of_vertices; + int number_of_faces; + int number_of_edges; + char tic_tac_toe; + char line[1000]; + bool still_comments = true; + while(still_comments) + { + fgets(line,1000,off_file); + still_comments = (line[0] == '#' || line[0] == '\n'); + } + sscanf(line,"%d %d %d",&number_of_vertices,&number_of_faces,&number_of_edges); + V.resize(number_of_vertices); + if (has_normals) + N.resize(number_of_vertices); + if (has_vertexColors) + C.resize(number_of_vertices); + F.resize(number_of_faces); + //printf("%s %d %d %d\n",(has_normals ? "NOFF" : "OFF"),number_of_vertices,number_of_faces,number_of_edges); + // Read vertices + for(int i = 0;i= 3) + { + std::vector vertex; + vertex.resize(3); + vertex[0] = x; + vertex[1] = y; + vertex[2] = z; + V[i] = vertex; + + if (has_normals) + { + std::vector normal; + normal.resize(3); + normal[0] = nx; + normal[1] = ny; + normal[2] = nz; + N[i] = normal; + } + + if (has_vertexColors) + { + C[i].resize(3); + C[i][0] = nx / 255.0; + C[i][1] = ny / 255.0; + C[i][2] = nz / 255.0; + } + i++; + }else if( + fscanf(off_file,"%[#]",&tic_tac_toe)==1) + { + char comment[1000]; + fscanf(off_file,"%[^\n]",comment); + }else + { + printf("Error: bad line (%d)\n",i); + if(feof(off_file)) + { + fclose(off_file); + return false; + } + } + } + // Read faces + for(int i = 0;i face; + int valence; + if(fscanf(off_file,"%d",&valence)==1) + { + face.resize(valence); + for(int j = 0;j +IGL_INLINE bool igl::readOFF( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F) +{ + std::vector > vV; + std::vector > vN; + std::vector > vF; + std::vector > vC; + bool success = igl::readOFF(str,vV,vF,vN,vC); + if(!success) + { + // readOFF(str,vV,vF,vN,vC) should have already printed an error + // message to stderr + return false; + } + bool V_rect = igl::list_to_matrix(vV,V); + if(!V_rect) + { + // igl::list_to_matrix(vV,V) already printed error message to std err + return false; + } + bool F_rect = igl::list_to_matrix(vF,F); + if(!F_rect) + { + // igl::list_to_matrix(vF,F) already printed error message to std err + return false; + } + return true; +} + + +template +IGL_INLINE bool igl::readOFF( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& N) +{ + std::vector > vV; + std::vector > vN; + std::vector > vF; + std::vector > vC; + bool success = igl::readOFF(str,vV,vF,vN,vC); + if(!success) + { + // readOFF(str,vV,vF,vC) should have already printed an error + // message to stderr + return false; + } + bool V_rect = igl::list_to_matrix(vV,V); + if(!V_rect) + { + // igl::list_to_matrix(vV,V) already printed error message to std err + return false; + } + bool F_rect = igl::list_to_matrix(vF,F); + if(!F_rect) + { + // igl::list_to_matrix(vF,F) already printed error message to std err + return false; + } + + if (vN.size()) + { + bool N_rect = igl::list_to_matrix(vN,N); + if(!N_rect) + { + // igl::list_to_matrix(vN,N) already printed error message to std err + return false; + } + } + + //Warning: RGB colors will be returned in the N matrix + if (vC.size()) + { + bool C_rect = igl::list_to_matrix(vC,N); + if(!C_rect) + { + // igl::list_to_matrix(vC,N) already printed error message to std err + return false; + } + } + + return true; +} +#endif + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::readOFF(std::basic_string, std::allocator >, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +// generated by autoexplicit.sh +template bool igl::readOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::readOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::readOFF, Eigen::Matrix >(std::string, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::readOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/readOFF.h b/src/igl/readOFF.h new file mode 100644 index 0000000000..3d8c41fa52 --- /dev/null +++ b/src/igl/readOFF.h @@ -0,0 +1,86 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READOFF_H +#define IGL_READOFF_H +#include "igl_inline.h" +// History: +// return type changed from void to bool Alec 18 Sept 2011 + +#ifndef IGL_NO_EIGEN +# include +#endif +#include +#include +#include + +namespace igl +{ + + // Read a mesh from an ascii OFF file, filling in vertex positions, normals + // and texture coordinates. Mesh may have faces of any number of degree + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Index type for indices (will be read as int and cast to Index) + // Inputs: + // str path to .obj file + // Outputs: + // V double matrix of vertex positions #V by 3 + // F #F list of face indices into vertex positions + // N list of vertex normals #V by 3 + // C list of rgb color values per vertex #V by 3 + // Returns true on success, false on errors + template + IGL_INLINE bool readOFF( + const std::string off_file_name, + std::vector > & V, + std::vector > & F, + std::vector > & N, + std::vector > & C); + // Inputs: + // off_file pointer to already opened .off file + // Outputs: + // off_file closed file + template + IGL_INLINE bool readOFF( + FILE * off_file, + std::vector > & V, + std::vector > & F, + std::vector > & N, + std::vector > & C); + + +#ifndef IGL_NO_EIGEN + // read mesh from a ascii off file + // Inputs: + // str path to .off file + // Outputs: + // V eigen double matrix #V by 3 + // F eigen int matrix #F by 3 + template + IGL_INLINE bool readOFF( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F); + + template + IGL_INLINE bool readOFF( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& N); +#endif + +} + +#ifndef IGL_STATIC_LIBRARY +# include "readOFF.cpp" +#endif + +#endif diff --git a/src/igl/readPLY.cpp b/src/igl/readPLY.cpp new file mode 100644 index 0000000000..515b3307a3 --- /dev/null +++ b/src/igl/readPLY.cpp @@ -0,0 +1,224 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readPLY.h" +#include "list_to_matrix.h" +#include "ply.h" +#include + +template < + typename Vtype, + typename Ftype, + typename Ntype, + typename UVtype> +IGL_INLINE bool igl::readPLY( + const std::string filename, + std::vector > & V, + std::vector > & F, + std::vector > & N, + std::vector > & UV) +{ + using namespace std; + // Largely follows ply2iv.c + FILE * ply_file = fopen(filename.c_str(),"r"); + if(ply_file == NULL) + { + return false; + } + return readPLY(ply_file,V,F,N,UV); +} + +template < + typename Vtype, + typename Ftype, + typename Ntype, + typename UVtype> +IGL_INLINE bool igl::readPLY( + FILE * ply_file, + std::vector > & V, + std::vector > & F, + std::vector > & N, + std::vector > & UV) +{ + using namespace std; + typedef struct Vertex { + double x,y,z; /* position */ + double nx,ny,nz; /* surface normal */ + double s,t; /* texture coordinates */ + void *other_props; /* other properties */ + } Vertex; + + typedef struct Face { + unsigned char nverts; /* number of vertex indices in list */ + int *verts; /* vertex index list */ + void *other_props; /* other properties */ + } Face; + + igl::ply::PlyProperty vert_props[] = { /* list of property information for a vertex */ + {"x", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,x), 0, 0, 0, 0}, + {"y", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,y), 0, 0, 0, 0}, + {"z", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,z), 0, 0, 0, 0}, + {"nx", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,nx), 0, 0, 0, 0}, + {"ny", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,ny), 0, 0, 0, 0}, + {"nz", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,nz), 0, 0, 0, 0}, + {"s", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,s), 0, 0, 0, 0}, + {"t", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,t), 0, 0, 0, 0}, + }; + + igl::ply::PlyProperty face_props[] = { /* list of property information for a face */ + {"vertex_indices", PLY_INT, PLY_INT, offsetof(Face,verts), + 1, PLY_UCHAR, PLY_UCHAR, offsetof(Face,nverts)}, + }; + + int nelems; + char ** elem_names; + igl::ply::PlyFile * in_ply = igl::ply::ply_read(ply_file,&nelems,&elem_names); + if(in_ply==NULL) + { + return false; + } + + bool has_normals = false; + bool has_texture_coords = false; + igl::ply::PlyProperty **plist; + int nprops; + int elem_count; + plist = ply_get_element_description (in_ply,"vertex", &elem_count, &nprops); + int native_binary_type = igl::ply::get_native_binary_type2(); + if (plist != NULL) + { + /* set up for getting vertex elements */ + ply_get_property (in_ply,"vertex",&vert_props[0]); + ply_get_property (in_ply,"vertex",&vert_props[1]); + ply_get_property (in_ply,"vertex",&vert_props[2]); + for (int j = 0; j < nprops; j++) + { + igl::ply::PlyProperty * prop = plist[j]; + if (igl::ply::equal_strings ("nx", prop->name) + || igl::ply::equal_strings ("ny", prop->name) + || igl::ply::equal_strings ("nz", prop->name)) + { + ply_get_property (in_ply,"vertex",&vert_props[3]); + ply_get_property (in_ply,"vertex",&vert_props[4]); + ply_get_property (in_ply,"vertex",&vert_props[5]); + has_normals = true; + } + if (igl::ply::equal_strings ("s", prop->name) || + igl::ply::equal_strings ("t", prop->name)) + { + ply_get_property(in_ply,"vertex",&vert_props[6]); + ply_get_property(in_ply,"vertex",&vert_props[7]); + has_texture_coords = true; + } + } + // Is this call necessary? + ply_get_other_properties(in_ply,"vertex", + offsetof(Vertex,other_props)); + V.resize(elem_count,std::vector(3)); + if(has_normals) + { + N.resize(elem_count,std::vector(3)); + }else + { + N.resize(0); + } + if(has_texture_coords) + { + UV.resize(elem_count,std::vector(2)); + }else + { + UV.resize(0); + } + + for(int j = 0;j +IGL_INLINE bool igl::readPLY( + const std::string filename, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & N, + Eigen::PlainObjectBase & UV) +{ + std::vector > vV; + std::vector > vF; + std::vector > vN; + std::vector > vUV; + if(!readPLY(filename,vV,vF,vN,vUV)) + { + return false; + } + return + list_to_matrix(vV,V) && + list_to_matrix(vF,F) && + list_to_matrix(vN,N) && + list_to_matrix(vUV,UV); +} + +template < + typename DerivedV, + typename DerivedF> +IGL_INLINE bool igl::readPLY( + const std::string filename, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F) +{ + Eigen::MatrixXd N,UV; + return readPLY(filename,V,F,N,UV); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::readPLY(std::basic_string, std::allocator > const, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); + +template bool igl::readPLY, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const, Eigen::PlainObjectBase > &, Eigen::PlainObjectBase > &, Eigen::PlainObjectBase > &, Eigen::PlainObjectBase > &); + +template bool igl::readPLY, Eigen::Matrix >(std::basic_string, std::allocator > const, Eigen::PlainObjectBase > &, Eigen::PlainObjectBase > &); +template bool igl::readPLY, Eigen::Matrix >(std::string, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/readPLY.h b/src/igl/readPLY.h new file mode 100644 index 0000000000..bcb69eee23 --- /dev/null +++ b/src/igl/readPLY.h @@ -0,0 +1,77 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READPLY_H +#define IGL_READPLY_H +#include "igl_inline.h" +#include +#include +#include +#include + +namespace igl +{ + // Read a mesh from a .ply file. + // + // Inputs: + // filename path to .ply file + // Outputs: + // V #V by 3 list of vertex positions + // F #F list of lists of triangle indices + // N #V by 3 list of vertex normals + // UV #V by 2 list of vertex texture coordinates + // Returns true iff success + template < + typename Vtype, + typename Ftype, + typename Ntype, + typename UVtype> + IGL_INLINE bool readPLY( + const std::string filename, + std::vector > & V, + std::vector > & F, + std::vector > & N, + std::vector > & UV); + template < + typename Vtype, + typename Ftype, + typename Ntype, + typename UVtype> + // Inputs: + // ply_file pointer to already opened .ply file + // Outputs: + // ply_file closed file + IGL_INLINE bool readPLY( + FILE * ply_file, + std::vector > & V, + std::vector > & F, + std::vector > & N, + std::vector > & UV); + template < + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DerivedUV> + IGL_INLINE bool readPLY( + const std::string filename, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & N, + Eigen::PlainObjectBase & UV); + template < + typename DerivedV, + typename DerivedF> + IGL_INLINE bool readPLY( + const std::string filename, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F); +} +#ifndef IGL_STATIC_LIBRARY +# include "readPLY.cpp" +#endif +#endif + diff --git a/src/igl/readSTL.cpp b/src/igl/readSTL.cpp new file mode 100644 index 0000000000..fcdeddcdd6 --- /dev/null +++ b/src/igl/readSTL.cpp @@ -0,0 +1,290 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readSTL.h" +#include "list_to_matrix.h" + +#include +template +IGL_INLINE bool igl::readSTL( + const std::string & filename, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & N) +{ + using namespace std; + vector > vV; + vector > vN; + vector > vF; + if(!readSTL(filename,vV,vF,vN)) + { + return false; + } + + if(!list_to_matrix(vV,V)) + { + return false; + } + + if(!list_to_matrix(vF,F)) + { + return false; + } + + if(!list_to_matrix(vN,N)) + { + return false; + } + return true; +} + +template +IGL_INLINE bool igl::readSTL( + const std::string & filename, + std::vector > & V, + std::vector > & F, + std::vector > & N) +{ + using namespace std; + // Should test for ascii + + // Open file, and check for error + FILE * stl_file = fopen(filename.c_str(),"rb"); + if(NULL==stl_file) + { + fprintf(stderr,"IOError: %s could not be opened...\n", + filename.c_str()); + return false; + } + return readSTL(stl_file,V,F,N); +} + +template +IGL_INLINE bool igl::readSTL( + FILE * stl_file, + std::vector > & V, + std::vector > & F, + std::vector > & N) +{ + using namespace std; + //stl_file = freopen(NULL,"rb",stl_file); + if(NULL==stl_file) + { + fprintf(stderr,"IOError: stl file could not be reopened as binary (1) ...\n"); + return false; + } + + V.clear(); + F.clear(); + N.clear(); + + + // Specifically 80 character header + char header[80]; + char solid[80]; + bool is_ascii = true; + if(fread(header,1,80,stl_file) != 80) + { + cerr<<"IOError: too short (1)."<(buf); + fseek(stl_file,0,SEEK_END); + int file_size = ftell(stl_file); + if(file_size == 80 + 4 + (4*12 + 2) * num_faces) + { + is_ascii = false; + }else + { + is_ascii = true; + } + } + + if(is_ascii) + { + // Rewind to end of header + //stl_file = fopen(filename.c_str(),"r"); + //stl_file = freopen(NULL,"r",stl_file); + fseek(stl_file, 0, SEEK_SET); + if(NULL==stl_file) + { + fprintf(stderr,"IOError: stl file could not be reopened as ascii ...\n"); + return false; + } + // Read 80 header + // Eat file name +#ifndef IGL_LINE_MAX +# define IGL_LINE_MAX 2048 +#endif + char name[IGL_LINE_MAX]; + if(NULL==fgets(name,IGL_LINE_MAX,stl_file)) + { + cerr<<"IOError: ascii too short (2)."< n(3); + double nd[3]; + ret = fscanf(stl_file,"%s %s %lg %lg %lg",facet,normal,nd,nd+1,nd+2); + if(string("endsolid") == facet) + { + break; + } + if(ret != 5 || + !(string("facet") == facet || + string("faced") == facet) || + string("normal") != normal) + { + cout<<"facet: "< f; + while(true) + { + char word[IGL_LINE_MAX]; + int ret = fscanf(stl_file,"%s",word); + if(ret == 1 && string("endloop") == word) + { + break; + }else if(ret == 1 && string("vertex") == word) + { + vector v(3); + double vd[3]; + int ret = fscanf(stl_file,"%lg %lg %lg",vd,vd+1,vd+2); + if(ret != 3) + { + cerr<<"IOError: bad format (3)."<(3,0)); + N.resize(num_tri,vector(3,0)); + F.resize(num_tri,vector(3,0)); + for(int t = 0;t<(int)num_tri;t++) + { + // Read normal + float n[3]; + if(fread(n,sizeof(float),3,stl_file)!=3) + { + cerr<<"IOError: bad format (8)."<, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/readSTL.h b/src/igl/readSTL.h new file mode 100644 index 0000000000..ca1f7c9e5c --- /dev/null +++ b/src/igl/readSTL.h @@ -0,0 +1,68 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READSTL_H +#define IGL_READSTL_H +#include "igl_inline.h" + +#ifndef IGL_NO_EIGEN +# include +#endif +#include +#include +#include + +namespace igl +{ + // Read a mesh from an ascii/binary stl file. + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Inputs: + // filename path to .stl file + // Outputs: + // V double matrix of vertex positions #V*3 by 3 + // F index matrix of triangle indices #F by 3 + // N double matrix of surface normals #F by 3 + // Returns true on success, false on errors + // + // Example: + // bool success = readSTL(filename,temp_V,F,N); + // remove_duplicate_vertices(temp_V,0,V,SVI,SVJ); + // for_each(F.data(),F.data()+F.size(),[&SVJ](int & f){f=SVJ(f);}); + // writeOBJ("Downloads/cat.obj",V,F); + template + IGL_INLINE bool readSTL( + const std::string & filename, + Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & N); + // Inputs: + // stl_file pointer to already opened .stl file + // Outputs: + // stl_file closed file + template + IGL_INLINE bool readSTL( + FILE * stl_file, + std::vector > & V, + std::vector > & F, + std::vector > & N); + template + IGL_INLINE bool readSTL( + const std::string & filename, + std::vector > & V, + std::vector > & F, + std::vector > & N); +} + +#ifndef IGL_STATIC_LIBRARY +# include "readSTL.cpp" +#endif + +#endif + diff --git a/src/igl/readTGF.cpp b/src/igl/readTGF.cpp new file mode 100644 index 0000000000..814395b301 --- /dev/null +++ b/src/igl/readTGF.cpp @@ -0,0 +1,200 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readTGF.h" + +#include + +IGL_INLINE bool igl::readTGF( + const std::string tgf_filename, + std::vector > & C, + std::vector > & E, + std::vector & P, + std::vector > & BE, + std::vector > & CE, + std::vector > & PE) +{ + using namespace std; + // clear output + C.clear(); + E.clear(); + P.clear(); + BE.clear(); + CE.clear(); + PE.clear(); + + FILE * tgf_file = fopen(tgf_filename.c_str(),"r"); + if(NULL==tgf_file) + { + printf("IOError: %s could not be opened\n",tgf_filename.c_str()); + return false; + } + + bool reading_vertices = true; + bool reading_edges = true; + const int MAX_LINE_LENGTH = 500; + char line[MAX_LINE_LENGTH]; + // read until seeing end of file + while(fgets(line,MAX_LINE_LENGTH,tgf_file)!=NULL) + { + // comment signifies end of vertices, next line is start of edges + if(line[0] == '#') + { + if(reading_vertices) + { + reading_vertices = false; + reading_edges = true; + }else if(reading_edges) + { + reading_edges = false; + } + // process vertex line + }else if(reading_vertices) + { + int index; + vector position(3); + int count = + sscanf(line,"%d %lg %lg %lg", + &index, + &position[0], + &position[1], + &position[2]); + if(count != 4) + { + fprintf(stderr,"Error: readTGF.h: bad format in vertex line\n"); + fclose(tgf_file); + return false; + } + // index is ignored since vertices must already be in order + C.push_back(position); + }else if(reading_edges) + { + vector edge(2); + int is_BE = 0; + int is_PE = 0; + int is_CE = 0; + int count = sscanf(line,"%d %d %d %d %d\n", + &edge[0], + &edge[1], + &is_BE, + &is_PE, + &is_CE); + if(count<2) + { + fprintf(stderr,"Error: readTGF.h: bad format in edge line\n"); + fclose(tgf_file); + return false; + } + // .tgf is one indexed + edge[0]--; + edge[1]--; + E.push_back(edge); + if(is_BE == 1) + { + BE.push_back(edge); + } + if(is_PE == 1) + { + // PE should index P + fprintf(stderr, + "Warning: readTGF.h found pseudo edges but does not support " + "them\n"); + } + if(is_CE == 1) + { + // CE should index P + fprintf(stderr, + "Warning: readTGF.h found cage edges but does not support them\n"); + } + }else + { + // ignore faces + } + } + fclose(tgf_file); + // Construct P, indices not in BE + for(int i = 0;i<(int)C.size();i++) + { + bool in_edge = false; + for(int j = 0;j<(int)BE.size();j++) + { + if(i == BE[j][0] || i == BE[j][1]) + { + in_edge = true; + break; + } + } + if(!in_edge) + { + P.push_back(i); + } + } + return true; +} + +#ifndef IGL_NO_EIGEN +#include "list_to_matrix.h" + +IGL_INLINE bool igl::readTGF( + const std::string tgf_filename, + Eigen::MatrixXd & C, + Eigen::MatrixXi & E, + Eigen::VectorXi & P, + Eigen::MatrixXi & BE, + Eigen::MatrixXi & CE, + Eigen::MatrixXi & PE) +{ + std::vector > vC; + std::vector > vE; + std::vector vP; + std::vector > vBE; + std::vector > vCE; + std::vector > vPE; + bool success = readTGF(tgf_filename,vC,vE,vP,vBE,vCE,vPE); + if(!success) + { + return false; + } + + if(!list_to_matrix(vC,C)) + { + return false; + } + if(!list_to_matrix(vE,E)) + { + return false; + } + if(!list_to_matrix(vP,P)) + { + return false; + } + if(!list_to_matrix(vBE,BE)) + { + return false; + } + if(!list_to_matrix(vCE,CE)) + { + return false; + } + if(!list_to_matrix(vPE,PE)) + { + return false; + } + + return true; +} + +IGL_INLINE bool igl::readTGF( + const std::string tgf_filename, + Eigen::MatrixXd & C, + Eigen::MatrixXi & E) +{ + Eigen::VectorXi P; + Eigen::MatrixXi BE,CE,PE; + return readTGF(tgf_filename,C,E,P,BE,CE,PE); +} +#endif diff --git a/src/igl/readTGF.h b/src/igl/readTGF.h new file mode 100644 index 0000000000..68c5ddef8d --- /dev/null +++ b/src/igl/readTGF.h @@ -0,0 +1,66 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READTGF_H +#define IGL_READTGF_H +#include "igl_inline.h" + +#include +#include +#ifndef IGL_NO_EIGEN +#include +#endif + +namespace igl +{ + // READTGF + // + // [V,E,P,BE,CE,PE] = readTGF(filename) + // + // Read a graph from a .tgf file + // + // Input: + // filename .tgf file name + // Output: + // V # vertices by 3 list of vertex positions + // E # edges by 2 list of edge indices + // P # point-handles list of point handle indices + // BE # bone-edges by 2 list of bone-edge indices + // CE # cage-edges by 2 list of cage-edge indices + // PE # pseudo-edges by 2 list of pseudo-edge indices + // + // Assumes that graph vertices are 3 dimensional + IGL_INLINE bool readTGF( + const std::string tgf_filename, + std::vector > & C, + std::vector > & E, + std::vector & P, + std::vector > & BE, + std::vector > & CE, + std::vector > & PE); + + #ifndef IGL_NO_EIGEN + IGL_INLINE bool readTGF( + const std::string tgf_filename, + Eigen::MatrixXd & C, + Eigen::MatrixXi & E, + Eigen::VectorXi & P, + Eigen::MatrixXi & BE, + Eigen::MatrixXi & CE, + Eigen::MatrixXi & PE); + IGL_INLINE bool readTGF( + const std::string tgf_filename, + Eigen::MatrixXd & C, + Eigen::MatrixXi & E); + #endif +} + +#ifndef IGL_STATIC_LIBRARY +# include "readTGF.cpp" +#endif + +#endif diff --git a/src/igl/readWRL.cpp b/src/igl/readWRL.cpp new file mode 100644 index 0000000000..ce6de3e733 --- /dev/null +++ b/src/igl/readWRL.cpp @@ -0,0 +1,121 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "readWRL.h" +#include + +template +IGL_INLINE bool igl::readWRL( + const std::string wrl_file_name, + std::vector > & V, + std::vector > & F) +{ + using namespace std; + FILE * wrl_file = fopen(wrl_file_name.c_str(),"r"); + if(NULL==wrl_file) + { + printf("IOError: %s could not be opened...",wrl_file_name.c_str()); + return false; + } + return readWRL(wrl_file,V,F); +} + +template +IGL_INLINE bool igl::readWRL( + FILE * wrl_file, + std::vector > & V, + std::vector > & F) +{ + using namespace std; + + char line[1000]; + // Read lines until seeing "point [" + // treat other lines in file as "comments" + bool still_comments = true; + string needle("point ["); + string haystack; + while(still_comments) + { + if(fgets(line,1000,wrl_file) == NULL) + { + std::cerr<<"readWRL, reached EOF without finding \"point [\""< point; + point.resize(3); + point[0] = x; + point[1] = y; + point[2] = z; + V.push_back(point); + //printf("(%g, %g, %g)\n",x,y,z); + }else if(floats_read != 0) + { + printf("ERROR: unrecognized format...\n"); + return false; + } + } + // Read lines until seeing "coordIndex [" + // treat other lines in file as "comments" + still_comments = true; + needle = string("coordIndex ["); + while(still_comments) + { + fgets(line,1000,wrl_file); + haystack = string(line); + still_comments = string::npos == haystack.find(needle); + } + // read F + int ints_read = 1; + while(ints_read > 0) + { + // read new face indices (until hit -1) + vector face; + while(true) + { + // indices are 0-indexed + int i; + ints_read = fscanf(wrl_file," %d,",&i); + if(ints_read > 0) + { + if(i>=0) + { + face.push_back(i); + }else + { + F.push_back(face); + break; + } + }else + { + break; + } + } + } + + + + fclose(wrl_file); + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::readWRL(std::basic_string, std::allocator >, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +#endif diff --git a/src/igl/readWRL.h b/src/igl/readWRL.h new file mode 100644 index 0000000000..45c1fa0484 --- /dev/null +++ b/src/igl/readWRL.h @@ -0,0 +1,53 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READWRL_H +#define IGL_READWRL_H +#include "igl_inline.h" + +#include +#include +#include + +namespace igl +{ + // Read a mesh from an ascii wrl file, filling in vertex positions and face + // indices of the first model. Mesh may have faces of any number of degree + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Index type for indices (will be read as int and cast to Index) + // Inputs: + // str path to .wrl file + // Outputs: + // V double matrix of vertex positions #V by 3 + // F #F list of face indices into vertex positions + // Returns true on success, false on errors + template + IGL_INLINE bool readWRL( + const std::string wrl_file_name, + std::vector > & V, + std::vector > & F); + // Inputs: + // wrl_file pointer to already opened .wrl file + // Outputs: + // wrl_file closed file + template + IGL_INLINE bool readWRL( + FILE * wrl_file, + std::vector > & V, + std::vector > & F); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "readWRL.cpp" +#endif + +#endif + diff --git a/src/igl/read_triangle_mesh.cpp b/src/igl/read_triangle_mesh.cpp new file mode 100644 index 0000000000..b6e52539fb --- /dev/null +++ b/src/igl/read_triangle_mesh.cpp @@ -0,0 +1,183 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "read_triangle_mesh.h" + +#include "list_to_matrix.h" +#include "readMESH.h" +#include "readOBJ.h" +#include "readOFF.h" +#include "readSTL.h" +#include "readPLY.h" +#include "readWRL.h" +#include "pathinfo.h" +#include "boundary_facets.h" +#include "polygon_mesh_to_triangle_mesh.h" + +#include +#include + + +template +IGL_INLINE bool igl::read_triangle_mesh( + const std::string str, + std::vector > & V, + std::vector > & F) +{ + using namespace std; + // dirname, basename, extension and filename + string d,b,e,f; + pathinfo(str,d,b,e,f); + // Convert extension to lower case + std::transform(e.begin(), e.end(), e.begin(), ::tolower); + vector > TC, N, C; + vector > FTC, FN; + if(e == "obj") + { + // Annoyingly obj can store 4 coordinates, truncate to xyz for this generic + // read_triangle_mesh + bool success = readOBJ(str,V,TC,N,F,FTC,FN); + for(auto & v : V) + { + v.resize(std::min(v.size(),(size_t)3)); + } + return success; + }else if(e == "off") + { + return readOFF(str,V,F,N,C); + } + cerr<<"Error: "<<__FUNCTION__<<": "<< + str<<" is not a recognized mesh file format."< +IGL_INLINE bool igl::read_triangle_mesh( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F) +{ + std::string _1,_2,_3,_4; + return read_triangle_mesh(str,V,F,_1,_2,_3,_4); +} + +template +IGL_INLINE bool igl::read_triangle_mesh( + const std::string filename, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F, + std::string & dir, + std::string & base, + std::string & ext, + std::string & name) +{ + using namespace std; + using namespace Eigen; + + // dirname, basename, extension and filename + pathinfo(filename,dir,base,ext,name); + // Convert extension to lower case + transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + FILE * fp = fopen(filename.c_str(),"rb"); + return read_triangle_mesh(ext,fp,V,F); +} + +template +IGL_INLINE bool igl::read_triangle_mesh( + const std::string & ext, + FILE * fp, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F) +{ + using namespace std; + using namespace Eigen; + vector > vV,vN,vTC,vC; + vector > vF,vFTC,vFN; + if(ext == "mesh") + { + // Convert extension to lower case + MatrixXi T; + if(!readMESH(fp,V,T,F)) + { + return 1; + } + //if(F.size() > T.size() || F.size() == 0) + { + boundary_facets(T,F); + } + }else if(ext == "obj") + { + if(!readOBJ(fp,vV,vTC,vN,vF,vFTC,vFN)) + { + return false; + } + // Annoyingly obj can store 4 coordinates, truncate to xyz for this generic + // read_triangle_mesh + for(auto & v : vV) + { + v.resize(std::min(v.size(),(size_t)3)); + } + }else if(ext == "off") + { + if(!readOFF(fp,vV,vF,vN,vC)) + { + return false; + } + }else if(ext == "ply") + { + if(!readPLY(fp,vV,vF,vN,vTC)) + { + return false; + } + }else if(ext == "stl") + { + if(!readSTL(fp,vV,vF,vN)) + { + return false; + } + }else if(ext == "wrl") + { + if(!readWRL(fp,vV,vF)) + { + return false; + } + }else + { + cerr<<"Error: unknown extension: "< 0) + { + if(!list_to_matrix(vV,V)) + { + return false; + } + polygon_mesh_to_triangle_mesh(vF,F); + } + return true; +} + +#endif + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::read_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::read_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template bool igl::read_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::read_triangle_mesh(std::basic_string, std::allocator >, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template bool igl::read_triangle_mesh, Eigen::Matrix >(std::string, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::read_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::read_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::read_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::basic_string, std::allocator >&, std::basic_string, std::allocator >&, std::basic_string, std::allocator >&, std::basic_string, std::allocator >&); +template bool igl::read_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template bool igl::read_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::basic_string, std::allocator >&, std::basic_string, std::allocator >&, std::basic_string, std::allocator >&, std::basic_string, std::allocator >&); +#endif diff --git a/src/igl/read_triangle_mesh.h b/src/igl/read_triangle_mesh.h new file mode 100644 index 0000000000..26b84c5f1f --- /dev/null +++ b/src/igl/read_triangle_mesh.h @@ -0,0 +1,80 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_READ_TRIANGLE_MESH_H +#define IGL_READ_TRIANGLE_MESH_H +#include "igl_inline.h" + +#ifndef IGL_NO_EIGEN +# include +#endif +#include +#include +#include +// History: +// renamed read -> read_triangle_mesh Daniele 24 June 2014 +// return type changed from void to bool Alec 18 Sept 2011 + +namespace igl +{ + // read mesh from an ascii file with automatic detection of file format. + // supported: obj, off, stl, wrl, ply, mesh) + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Index type for indices (will be read as int and cast to Index) + // Inputs: + // str path to file + // Outputs: + // V eigen double matrix #V by 3 + // F eigen int matrix #F by 3 + // Returns true iff success + template + IGL_INLINE bool read_triangle_mesh( + const std::string str, + std::vector > & V, + std::vector > & F); +#ifndef IGL_NO_EIGEN + template + IGL_INLINE bool read_triangle_mesh( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F); + // Outputs: + // dir directory path (see pathinfo.h) + // base base name (see pathinfo.h) + // ext extension (see pathinfo.h) + // name filename (see pathinfo.h) + template + IGL_INLINE bool read_triangle_mesh( + const std::string str, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F, + std::string & dir, + std::string & base, + std::string & ext, + std::string & name); + // Inputs: + // ext file extension + // fp pointer to already opened .ext file + // Outputs: + // fp closed file + template + IGL_INLINE bool read_triangle_mesh( + const std::string & ext, + FILE * fp, + Eigen::PlainObjectBase& V, + Eigen::PlainObjectBase& F); +#endif +} + +#ifndef IGL_STATIC_LIBRARY +# include "read_triangle_mesh.cpp" +#endif + +#endif diff --git a/src/igl/redux.h b/src/igl/redux.h new file mode 100644 index 0000000000..38ce9395af --- /dev/null +++ b/src/igl/redux.h @@ -0,0 +1,70 @@ +#ifndef IGL_REDUX_H +#define IGL_REDUX_H +#include +#include +namespace igl +{ + // REDUX Perform reductions on the rows or columns of a SparseMatrix. This is + // _similar_ to DenseBase::redux, but different in two important ways: + // 1. (unstored) Zeros are **not** "visited", however if the first element + // in the column/row does not appear in the first row/column then the + // reduction is assumed to start with zero. In this way, "any", "all", + // "count"(non-zeros) work as expected. This means it is **not** possible + // to use this to count (implicit) zeros. + // 2. This redux is more powerful in the sense that A and B may have + // different types. This makes it possible to count the number of + // non-zeros in a SparseMatrix A into a VectorXi B. + // + // Inputs: + // A m by n sparse matrix + // dim dimension along which to sum (1 or 2) + // func function handle with the prototype `X(Y a, I i, J j, Z b)` where a + // is the running value, b is A(i,j) + // Output: + // S n-long sparse vector (if dim == 1) + // or + // S m-long sparse vector (if dim == 2) + template + inline void redux( + const Eigen::SparseMatrix & A, + const int dim, + const Func & func, + Eigen::PlainObjectBase & B); +} + +// Implementation + +#include "redux.h" +#include "for_each.h" + +template +inline void igl::redux( + const Eigen::SparseMatrix & A, + const int dim, + const Func & func, + Eigen::PlainObjectBase & B) +{ + assert((dim == 1 || dim == 2) && "dim must be 2 or 1"); + // Get size of input + int m = A.rows(); + int n = A.cols(); + // resize output + B = DerivedB::Zero(dim==1?n:m); + const auto func_wrap = [&func,&B,&dim](const int i, const int j, const int v) + { + if(dim == 1) + { + B(j) = i == 0? v : func(B(j),v); + }else + { + B(i) = j == 0? v : func(B(i),v); + } + }; + for_each(A,func_wrap); +} + + +//#ifndef IGL_STATIC_LIBRARY +//# include "redux.cpp" +//#endif +#endif diff --git a/src/igl/remesh_along_isoline.cpp b/src/igl/remesh_along_isoline.cpp new file mode 100644 index 0000000000..7164ba8e5d --- /dev/null +++ b/src/igl/remesh_along_isoline.cpp @@ -0,0 +1,160 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "remesh_along_isoline.h" +#include "list_to_matrix.h" + +template < + typename DerivedV, + typename DerivedF, + typename DerivedS, + typename DerivedU, + typename DerivedG, + typename DerivedJ, + typename BCtype, + typename DerivedSU, + typename DerivedL> + IGL_INLINE void igl::remesh_along_isoline( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & S, + const typename DerivedS::Scalar val, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & SU, + Eigen::PlainObjectBase & J, + Eigen::SparseMatrix & BC, + Eigen::PlainObjectBase & L) +{ + igl::remesh_along_isoline(V.rows(),F,S,val,G,SU,J,BC,L); + U = BC * V; +} + +template < + typename DerivedF, + typename DerivedS, + typename DerivedG, + typename DerivedJ, + typename BCtype, + typename DerivedSU, + typename DerivedL> + IGL_INLINE void igl::remesh_along_isoline( + const int num_vertices, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & S, + const typename DerivedS::Scalar val, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & SU, + Eigen::PlainObjectBase & J, + Eigen::SparseMatrix & BC, + Eigen::PlainObjectBase & L) +{ + // Lazy implementation using vectors + + //assert(val.size() == 1); + const int isoval_i = 0; + //auto isoval = val(isoval_i); + auto isoval = val; + std::vector > vG; + std::vector vJ; + std::vector vL; + std::vector > vBC; + int Ucount = 0; + for(int i = 0;i isoval; + // Find crossings + const int n = (p+1)%3; + const bool nsign = S(F(f,n)) > isoval; + if(psign != nsign) + { + P[count] = p; + Psign[count] = psign; + // record crossing + count++; + } + } + + assert(count == 0 || count == 2); + switch(count) + { + case 0: + { + // Easy case + std::vector row = {F(f,0),F(f,1),F(f,2)}; + vG.push_back(row); + vJ.push_back(f); + vL.push_back( S(F(f,0))>isoval ? isoval_i+1 : isoval_i ); + break; + } + case 2: + { + // Cut case + // flip so that P[1] is the one-off vertex + if(P[0] == 0 && P[1] == 2) + { + std::swap(P[0],P[1]); + std::swap(Psign[0],Psign[1]); + } + assert(Psign[0] != Psign[1]); + // Create two new vertices + for(int i = 0;i<2;i++) + { + const double bci = (isoval - S(F(f,(P[i]+1)%3)))/ + (S(F(f,P[i]))-S(F(f,(P[i]+1)%3))); + vBC.emplace_back(Ucount,F(f,P[i]),bci); + vBC.emplace_back(Ucount,F(f,(P[i]+1)%3),1.0-bci); + Ucount++; + } + const int v0 = F(f,P[0]); + const int v01 = Ucount-2; + assert(((P[0]+1)%3) == P[1]); + const int v1 = F(f,P[1]); + const int v12 = Ucount-1; + const int v2 = F(f,(P[1]+1)%3); + // v0 + // | \ + // | \ + // | \ + // v01 \ + // | \ + // | \ + // | \ + // v1--v12---v2 + typedef std::vector Row; + {Row row = {v01,v1,v12}; vG.push_back(row);vJ.push_back(f);vL.push_back(Psign[0]?isoval_i:isoval_i+1);} + {Row row = {v12,v2,v01}; vG.push_back(row);vJ.push_back(f);vL.push_back(Psign[1]?isoval_i:isoval_i+1);} + {Row row = {v2,v0,v01}; vG.push_back(row) ;vJ.push_back(f);vL.push_back(Psign[1]?isoval_i:isoval_i+1);} + break; + } + default: assert(false); + } + } + igl::list_to_matrix(vG,G); + igl::list_to_matrix(vJ,J); + igl::list_to_matrix(vL,L); + BC.resize(Ucount,num_vertices); + BC.setFromTriplets(vBC.begin(),vBC.end()); + SU = BC * S; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::remesh_along_isoline, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Scalar, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::SparseMatrix&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/remesh_along_isoline.h b/src/igl/remesh_along_isoline.h new file mode 100644 index 0000000000..9a7c553b2c --- /dev/null +++ b/src/igl/remesh_along_isoline.h @@ -0,0 +1,81 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_REMESH_ALONG_ISOLINE_H +#define IGL_REMESH_ALONG_ISOLINE_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Given a triangle mesh and a scalar field, remesh so that a given isovalue + // of the scalar field follows (new) edges of the output mesh. Effectively + // running "marching triangles" on mesh, but not in any coherent order. The + // output mesh should be as manifold as the input. + // + // Inputs: + // V #V by dim list of mesh vertex positions + // F #F by 3 list of mesh triangle indices into V + // S #V by 1 list of scalar field + // val value of S to remesh along + // Outputs: + // U #U by dim list of mesh vertex positions #U>=#V + // G #G by 3 list of mesh triangle indices into U, #G>=#F + // SU #U list of scalar field values over new mesh + // J #G list of indices into G revealing birth triangles + // BC #U by #V sparse matrix of barycentric coordinates so that U = BC*V + // L #G list of bools whether scalar field in triangle below or above val + template < + typename DerivedV, + typename DerivedF, + typename DerivedS, + typename DerivedU, + typename DerivedG, + typename DerivedJ, + typename BCtype, + typename DerivedSU, + typename DerivedL> + IGL_INLINE void remesh_along_isoline( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & S, + const typename DerivedS::Scalar val, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & SU, + Eigen::PlainObjectBase & J, + Eigen::SparseMatrix & BC, + Eigen::PlainObjectBase & L); + // Input: + // n number of vertices (#V) + template < + typename DerivedF, + typename DerivedS, + typename DerivedG, + typename DerivedJ, + typename BCtype, + typename DerivedSU, + typename DerivedL> + IGL_INLINE void remesh_along_isoline( + const int n, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & S, + const typename DerivedS::Scalar val, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & SU, + Eigen::PlainObjectBase & J, + Eigen::SparseMatrix & BC, + Eigen::PlainObjectBase & L); +} + +#ifndef IGL_STATIC_LIBRARY +# include "remesh_along_isoline.h" +#endif + +#endif diff --git a/src/igl/remove_duplicate_vertices.cpp b/src/igl/remove_duplicate_vertices.cpp new file mode 100644 index 0000000000..815ee51f8a --- /dev/null +++ b/src/igl/remove_duplicate_vertices.cpp @@ -0,0 +1,83 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "remove_duplicate_vertices.h" +#include "round.h" +#include "unique_rows.h" +#include "colon.h" +#include "slice.h" +#include + +template < + typename DerivedV, + typename DerivedSV, + typename DerivedSVI, + typename DerivedSVJ> +IGL_INLINE void igl::remove_duplicate_vertices( + const Eigen::MatrixBase& V, + const double epsilon, + Eigen::PlainObjectBase& SV, + Eigen::PlainObjectBase& SVI, + Eigen::PlainObjectBase& SVJ) +{ + if(epsilon > 0) + { + DerivedV rV,rSV; + round((V/(10.0*epsilon)).eval(),rV); + unique_rows(rV,rSV,SVI,SVJ); + slice(V,SVI,colon(0,V.cols()-1),SV); + }else + { + unique_rows(V,SV,SVI,SVJ); + } +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedSV, + typename DerivedSVI, + typename DerivedSVJ, + typename DerivedSF> +IGL_INLINE void igl::remove_duplicate_vertices( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const double epsilon, + Eigen::PlainObjectBase& SV, + Eigen::PlainObjectBase& SVI, + Eigen::PlainObjectBase& SVJ, + Eigen::PlainObjectBase& SF) +{ + using namespace Eigen; + using namespace std; + remove_duplicate_vertices(V,epsilon,SV,SVI,SVJ); + SF.resizeLike(F); + for(int f = 0;f, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::remove_duplicate_vertices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::remove_duplicate_vertices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::remove_duplicate_vertices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_duplicate_vertices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_duplicate_vertices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_duplicate_vertices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_duplicate_vertices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_duplicate_vertices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/remove_duplicate_vertices.h b/src/igl/remove_duplicate_vertices.h new file mode 100644 index 0000000000..9276ba729b --- /dev/null +++ b/src/igl/remove_duplicate_vertices.h @@ -0,0 +1,65 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_REMOVE_DUPLICATE_VERTICES_H +#define IGL_REMOVE_DUPLICATE_VERTICES_H +#include "igl_inline.h" +#include +namespace igl +{ + // REMOVE_DUPLICATE_VERTICES Remove duplicate vertices upto a uniqueness + // tolerance (epsilon) + // + // Inputs: + // V #V by dim list of vertex positions + // epsilon uniqueness tolerance (significant digit), can probably think of + // this as a tolerance on L1 distance + // Outputs: + // SV #SV by dim new list of vertex positions + // SVI #V by 1 list of indices so SV = V(SVI,:) + // SVJ #SV by 1 list of indices so V = SV(SVJ,:) + // + // Example: + // % Mesh in (V,F) + // [SV,SVI,SVJ] = remove_duplicate_vertices(V,1e-7); + // % remap faces + // SF = SVJ(F); + // + template < + typename DerivedV, + typename DerivedSV, + typename DerivedSVI, + typename DerivedSVJ> + IGL_INLINE void remove_duplicate_vertices( + const Eigen::MatrixBase& V, + const double epsilon, + Eigen::PlainObjectBase& SV, + Eigen::PlainObjectBase& SVI, + Eigen::PlainObjectBase& SVJ); + // Wrapper that also remaps given faces (F) --> (SF) so that SF index SV + template < + typename DerivedV, + typename DerivedF, + typename DerivedSV, + typename DerivedSVI, + typename DerivedSVJ, + typename DerivedSF> + IGL_INLINE void remove_duplicate_vertices( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const double epsilon, + Eigen::PlainObjectBase& SV, + Eigen::PlainObjectBase& SVI, + Eigen::PlainObjectBase& SVJ, + Eigen::PlainObjectBase& SF); +} + +#ifndef IGL_STATIC_LIBRARY +# include "remove_duplicate_vertices.cpp" +#endif + +#endif diff --git a/src/igl/remove_duplicates.cpp b/src/igl/remove_duplicates.cpp new file mode 100644 index 0000000000..3e97a39e1d --- /dev/null +++ b/src/igl/remove_duplicates.cpp @@ -0,0 +1,88 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "remove_duplicates.h" +#include + +//template +//IGL_INLINE void igl::remove_duplicates( +// const Eigen::Matrix &V, +// const Eigen::Matrix &F, +// Eigen::Matrix &NV, +// Eigen::Matrix &NF, +// Eigen::Matrix &I, +// const double epsilon) +template +IGL_INLINE void igl::remove_duplicates( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + Eigen::PlainObjectBase &NV, + Eigen::PlainObjectBase &NF, + Eigen::Matrix &I, + const double epsilon) +{ + using namespace std; + //// build collapse map + int n = V.rows(); + + I = Eigen::Matrix(n); + I[0] = 0; + + bool *VISITED = new bool[n]; + for (int i =0; i face; + NF.resizeLike(F); + for (int i =0; i , Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::Matrix::Scalar, -1, 1, 0, -1, 1>&, double); +template void igl::remove_duplicates, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::Matrix::Scalar, -1, 1, 0, -1, 1>&, double); +#endif diff --git a/src/igl/remove_duplicates.h b/src/igl/remove_duplicates.h new file mode 100644 index 0000000000..0e1249ce94 --- /dev/null +++ b/src/igl/remove_duplicates.h @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_REMOVE_DUPLICATES_H +#define IGL_REMOVE_DUPLICATES_H +#include "igl_inline.h" + +#include +namespace igl +{ + // [ NV, NF ] = remove_duplicates( V,F,epsilon ) + // Merge the duplicate vertices from V, fixing the topology accordingly + // + // Input: + // V,F: mesh description + // epsilon: minimal distance to consider two vertices identical + // + // Output: + // NV, NF: new mesh without duplicate vertices + +// template +// IGL_INLINE void remove_duplicates( +// const Eigen::Matrix &V, +// const Eigen::Matrix &F, +// Eigen::Matrix &NV, +// Eigen::Matrix &NF, +// Eigen::Matrix &I, +// const double epsilon = 2.2204e-15); + + template + IGL_INLINE void remove_duplicates( + const Eigen::PlainObjectBase &V, + const Eigen::PlainObjectBase &F, + Eigen::PlainObjectBase &NV, + Eigen::PlainObjectBase &NF, + Eigen::Matrix &I, + const double epsilon = 2.2204e-15); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "remove_duplicates.cpp" +#endif + +#endif diff --git a/src/igl/remove_unreferenced.cpp b/src/igl/remove_unreferenced.cpp new file mode 100644 index 0000000000..3ee8260aa8 --- /dev/null +++ b/src/igl/remove_unreferenced.cpp @@ -0,0 +1,122 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "remove_unreferenced.h" +#include "slice.h" +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedNV, + typename DerivedNF, + typename DerivedI> +IGL_INLINE void igl::remove_unreferenced( + const Eigen::MatrixBase &V, + const Eigen::MatrixBase &F, + Eigen::PlainObjectBase &NV, + Eigen::PlainObjectBase &NF, + Eigen::PlainObjectBase &I) +{ + Eigen::Matrix J; + remove_unreferenced(V,F,NV,NF,I,J); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedNV, + typename DerivedNF, + typename DerivedI, + typename DerivedJ> +IGL_INLINE void igl::remove_unreferenced( + const Eigen::MatrixBase &V, + const Eigen::MatrixBase &F, + Eigen::PlainObjectBase &NV, + Eigen::PlainObjectBase &NF, + Eigen::PlainObjectBase &I, + Eigen::PlainObjectBase &J) +{ + using namespace std; + const size_t n = V.rows(); + remove_unreferenced(n,F,I,J); + NF = F; + std::for_each(NF.data(),NF.data()+NF.size(), + [&I](typename DerivedNF::Scalar & a){a=I(a);}); + slice(V,J,1,NV); +} + +template < + typename DerivedF, + typename DerivedI, + typename DerivedJ> +IGL_INLINE void igl::remove_unreferenced( + const size_t n, + const Eigen::MatrixBase &F, + Eigen::PlainObjectBase &I, + Eigen::PlainObjectBase &J) +{ + // Mark referenced vertices + typedef Eigen::Matrix MatrixXb; + MatrixXb mark = MatrixXb::Zero(n,1); + for(int i=0; i, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +// generated by autoexplicit.sh +// generated by autoexplicit.sh +template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +//template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix >(unsigned long, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::remove_unreferenced, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/remove_unreferenced.h b/src/igl/remove_unreferenced.h new file mode 100644 index 0000000000..2284190ab5 --- /dev/null +++ b/src/igl/remove_unreferenced.h @@ -0,0 +1,82 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +// remove_unreferenced.h +// Preview3D +// +// Created by Daniele Panozzo on 17/11/11. + +#ifndef IGL_REMOVE_UNREFERENCED_H +#define IGL_REMOVE_UNREFERENCED_H +#include "igl_inline.h" + +#include +namespace igl +{ + // Remove unreferenced vertices from V, updating F accordingly + // + // Input: + // V #V by dim list of mesh vertex positions + // F #F by ss list of simplices (Values of -1 are quitely skipped) + // Outputs: + // NV #NV by dim list of mesh vertex positions + // NF #NF by ss list of simplices + // IM #V by 1 list of indices such that: NF = IM(F) and NT = IM(T) + // and V(find(IM<=size(NV,1)),:) = NV + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedNV, + typename DerivedNF, + typename DerivedI> + IGL_INLINE void remove_unreferenced( + const Eigen::MatrixBase &V, + const Eigen::MatrixBase &F, + Eigen::PlainObjectBase &NV, + Eigen::PlainObjectBase &NF, + Eigen::PlainObjectBase &I); + template < + typename DerivedV, + typename DerivedF, + typename DerivedNV, + typename DerivedNF, + typename DerivedI, + typename DerivedJ> + IGL_INLINE void remove_unreferenced( + const Eigen::MatrixBase &V, + const Eigen::MatrixBase &F, + Eigen::PlainObjectBase &NV, + Eigen::PlainObjectBase &NF, + Eigen::PlainObjectBase &I, + Eigen::PlainObjectBase &J); + // Inputs: + // n number of vertices (possibly greater than F.maxCoeff()+1) + // F #F by ss list of simplices + // Outputs: + // IM #V by 1 list of indices such that: NF = IM(F) and NT = IM(T) + // and V(find(IM<=size(NV,1)),:) = NV + // J #RV by 1 list, such that RV = V(J,:) + // + template < + typename DerivedF, + typename DerivedI, + typename DerivedJ> + IGL_INLINE void remove_unreferenced( + const size_t n, + const Eigen::MatrixBase &F, + Eigen::PlainObjectBase &I, + Eigen::PlainObjectBase &J); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "remove_unreferenced.cpp" +#endif + +#endif diff --git a/src/igl/reorder.cpp b/src/igl/reorder.cpp new file mode 100644 index 0000000000..ee74b20963 --- /dev/null +++ b/src/igl/reorder.cpp @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "reorder.h" +#include "SortableRow.h" +#ifndef IGL_NO_EIGEN +#include +#endif + +// This implementation is O(n), but also uses O(n) extra memory +template< class T > +IGL_INLINE void igl::reorder( + const std::vector & unordered, + std::vector const & index_map, + std::vector & ordered) +{ + // copy for the reorder according to index_map, because unsorted may also be + // sorted + std::vector copy = unordered; + ordered.resize(index_map.size()); + for(int i = 0; i<(int)index_map.size();i++) + { + ordered[i] = copy[index_map[i]]; + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::reorder(std::vector > const&, std::vector > const&, std::vector >&); +template void igl::reorder(std::vector > const&, std::vector > const&, std::vector >&); +template void igl::reorder(std::vector > const&, std::vector > const&, std::vector >&); +# ifndef IGL_NO_EIGEN + template void igl::reorder > >(std::vector >, std::allocator > > > const&, std::vector > const&, std::vector >, std::allocator > > >&); + template void igl::reorder > >(std::vector >, std::allocator > > > const&, std::vector > const&, std::vector >, std::allocator > > >&); +# endif +template void igl::reorder(std::vector > const&, std::vector > const&, std::vector >&); +#ifdef WIN32 +template void igl::reorder(class std::vector > const &,class std::vector > const &,class std::vector > &); +#endif +#endif diff --git a/src/igl/reorder.h b/src/igl/reorder.h new file mode 100644 index 0000000000..50f7d7b0fb --- /dev/null +++ b/src/igl/reorder.h @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_REORDER_H +#define IGL_REORDER_H +#include "igl_inline.h" +#include +// For size_t +#include +#include + +namespace igl +{ + // Act like matlab's Y = X(I) for std vectors + // where I contains a vector of indices so that after, + // Y[j] = X[I[j]] for index j + // this implies that Y.size() == I.size() + // X and Y are allowed to be the same reference + template< class T > + IGL_INLINE void reorder( + const std::vector & unordered, + std::vector const & index_map, + std::vector & ordered); +} + +#ifndef IGL_STATIC_LIBRARY +# include "reorder.cpp" +#endif + +#endif diff --git a/src/igl/repdiag.cpp b/src/igl/repdiag.cpp new file mode 100644 index 0000000000..283ae1e75f --- /dev/null +++ b/src/igl/repdiag.cpp @@ -0,0 +1,97 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "repdiag.h" +#include + +template +IGL_INLINE void igl::repdiag( + const Eigen::SparseMatrix& A, + const int d, + Eigen::SparseMatrix& B) +{ + using namespace std; + using namespace Eigen; + int m = A.rows(); + int n = A.cols(); +#if false + vector > IJV; + IJV.reserve(A.nonZeros()*d); + // Loop outer level + for (int k=0; k::InnerIterator it(A,k); it; ++it) + { + for(int i = 0;i(i*m+it.row(),i*n+it.col(),it.value())); + } + } + } + B.resize(m*d,n*d); + B.setFromTriplets(IJV.begin(),IJV.end()); +#else + // This will not work for RowMajor + B.resize(m*d,n*d); + Eigen::VectorXi per_col = Eigen::VectorXi::Zero(n*d); + for (int k=0; k::InnerIterator it(A,k); it; ++it) + { + for(int r = 0;r::InnerIterator it(A,k); it; ++it) + { + B.insert(it.row()+mr,k+nr) = it.value(); + } + } + } + B.makeCompressed(); +#endif +} + +template +IGL_INLINE void igl::repdiag( + const Eigen::Matrix & A, + const int d, + Eigen::Matrix & B) +{ + int m = A.rows(); + int n = A.cols(); + B.resize(m*d,n*d); + B.array() *= 0; + for(int i = 0;i +IGL_INLINE Mat igl::repdiag(const Mat & A, const int d) +{ + Mat B; + repdiag(A,d,B); + return B; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::repdiag(Eigen::SparseMatrix const&, int, Eigen::SparseMatrix&); +// generated by autoexplicit.sh +template Eigen::SparseMatrix igl::repdiag >(Eigen::SparseMatrix const&, int); +#endif diff --git a/src/igl/repdiag.h b/src/igl/repdiag.h new file mode 100644 index 0000000000..7794433745 --- /dev/null +++ b/src/igl/repdiag.h @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_REPDIAG_H +#define IGL_REPDIAG_H +#include "igl_inline.h" + +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include + +namespace igl +{ + // REPDIAG repeat a matrix along the diagonal a certain number of times, so + // that if A is a m by n matrix and we want to repeat along the diagonal d + // times, we get a m*d by n*d matrix B such that: + // B( (k*m+1):(k*m+1+m-1), (k*n+1):(k*n+1+n-1)) = A + // for k from 0 to d-1 + // + // Inputs: + // A m by n matrix we are repeating along the diagonal. May be dense or + // sparse + // d number of times to repeat A along the diagonal + // Outputs: + // B m*d by n*d matrix with A repeated d times along the diagonal, + // will be dense or sparse to match A + // + + // Sparse version + template + IGL_INLINE void repdiag( + const Eigen::SparseMatrix& A, + const int d, + Eigen::SparseMatrix& B); + // Dense version + template + IGL_INLINE void repdiag( + const Eigen::Matrix & A, + const int d, + Eigen::Matrix & B); + // Wrapper with B as output + template + IGL_INLINE Mat repdiag(const Mat & A, const int d); +} + +#ifndef IGL_STATIC_LIBRARY +# include "repdiag.cpp" +#endif + +#endif diff --git a/src/igl/repmat.cpp b/src/igl/repmat.cpp new file mode 100644 index 0000000000..f79840444f --- /dev/null +++ b/src/igl/repmat.cpp @@ -0,0 +1,67 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "repmat.h" + +template +IGL_INLINE void igl::repmat( + const Eigen::MatrixBase & A, + const int r, + const int c, + Eigen::PlainObjectBase & B) +{ + assert(r>0); + assert(c>0); + // Make room for output + B.resize(r*A.rows(),c*A.cols()); + + // copy tiled blocks + for(int i = 0;i +IGL_INLINE void igl::repmat( + const Eigen::SparseMatrix & A, + const int r, + const int c, + Eigen::SparseMatrix & B) +{ + assert(r>0); + assert(c>0); + B.resize(r*A.rows(),c*A.cols()); + B.reserve(r*c*A.nonZeros()); + for(int i = 0;i::InnerIterator it(A,k); it; ++it) + { + B.insert(i*A.rows()+it.row(),j*A.cols() + it.col()) = it.value(); + } + } + } + } + B.finalize(); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::repmat, Eigen::Matrix >(Eigen::MatrixBase > const&, int, int, Eigen::PlainObjectBase >&); +template void igl::repmat, Eigen::Matrix >(Eigen::MatrixBase > const&, int, int, Eigen::PlainObjectBase >&); +template void igl::repmat, Eigen::Matrix >(Eigen::MatrixBase > const&, int, int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/repmat.h b/src/igl/repmat.h new file mode 100644 index 0000000000..1509875aae --- /dev/null +++ b/src/igl/repmat.h @@ -0,0 +1,53 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_REPMAT_H +#define IGL_REPMAT_H +#include "igl_inline.h" + +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include + +namespace igl +{ + // At least for Dense matrices this is replaced by `replicate` e.g., dst = src.replicate(n,m); + // http://forum.kde.org/viewtopic.php?f=74&t=90876#p173517 + + // Ideally this is a super overloaded function that behaves just like + // matlab's repmat + + // Replicate and tile a matrix + // + // Templates: + // T should be a eigen matrix primitive type like int or double + // Inputs: + // A m by n input matrix + // r number of row-direction copies + // c number of col-direction copies + // Outputs: + // B r*m by c*n output matrix + // + template + IGL_INLINE void repmat( + const Eigen::MatrixBase & A, + const int r, + const int c, + Eigen::PlainObjectBase & B); + template + IGL_INLINE void repmat( + const Eigen::SparseMatrix & A, + const int r, + const int c, + Eigen::SparseMatrix & B); +} + +#ifndef IGL_STATIC_LIBRARY +# include "repmat.cpp" +#endif + +#endif diff --git a/src/igl/resolve_duplicated_faces.cpp b/src/igl/resolve_duplicated_faces.cpp new file mode 100644 index 0000000000..3b26b8ecc2 --- /dev/null +++ b/src/igl/resolve_duplicated_faces.cpp @@ -0,0 +1,96 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// + +#include "resolve_duplicated_faces.h" + +#include "slice.h" +#include "unique_simplices.h" + +template< + typename DerivedF1, + typename DerivedF2, + typename DerivedJ > +IGL_INLINE void igl::resolve_duplicated_faces( + const Eigen::PlainObjectBase& F1, + Eigen::PlainObjectBase& F2, + Eigen::PlainObjectBase& J) { + + //typedef typename DerivedF1::Scalar Index; + Eigen::VectorXi IA,IC; + DerivedF1 uF; + igl::unique_simplices(F1,uF,IA,IC); + + const size_t num_faces = F1.rows(); + const size_t num_unique_faces = uF.rows(); + assert((size_t) IA.rows() == num_unique_faces); + // faces on top of each unique face + std::vector > uF2F(num_unique_faces); + // signed counts + Eigen::VectorXi counts = Eigen::VectorXi::Zero(num_unique_faces); + Eigen::VectorXi ucounts = Eigen::VectorXi::Zero(num_unique_faces); + // loop over all faces + for (size_t i=0; i kept_faces; + for (size_t i=0; i 0) { + kept_faces.push_back(abs(fid)-1); + found = true; + break; + } + } + assert(found); + } else if (counts[i] == -1) { + bool found = false; + for (auto fid : uF2F[i]) { + if (fid < 0) { + kept_faces.push_back(abs(fid)-1); + found = true; + break; + } + } + assert(found); + } else { + assert(counts[i] == 0); + } + } + + const size_t num_kept = kept_faces.size(); + J.resize(num_kept, 1); + std::copy(kept_faces.begin(), kept_faces.end(), J.data()); + igl::slice(F1, J, 1, F2); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::resolve_duplicated_faces, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::resolve_duplicated_faces, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::resolve_duplicated_faces, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::resolve_duplicated_faces, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::resolve_duplicated_faces, class Eigen::Matrix, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>>(class Eigen::PlainObjectBase> const &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/resolve_duplicated_faces.h b/src/igl/resolve_duplicated_faces.h new file mode 100644 index 0000000000..03ea3d6e6c --- /dev/null +++ b/src/igl/resolve_duplicated_faces.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Qingnan Zhou +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +// +#ifndef IGL_COPYLEFT_RESOLVE_DUPLICATED_FACES +#define IGL_COPYLEFT_RESOLVE_DUPLICATED_FACES + +#include "igl_inline.h" +#include + +namespace igl { + + // Resolve duplicated faces according to the following rules per unique face: + // + // 1. If the number of positively oriented faces equals the number of + // negatively oriented faces, remove all duplicated faces at this triangle. + // 2. If the number of positively oriented faces equals the number of + // negatively oriented faces plus 1, keeps one of the positively oriented + // face. + // 3. If the number of positively oriented faces equals the number of + // negatively oriented faces minus 1, keeps one of the negatively oriented + // face. + // 4. If the number of postively oriented faces differ with the number of + // negativley oriented faces by more than 1, the mesh is not orientable. + // An exception will be thrown. + // + // Inputs: + // F1 #F1 by 3 array of input faces. + // + // Outputs: + // F2 #F2 by 3 array of output faces without duplicated faces. + // J #F2 list of indices into F1. + template< + typename DerivedF1, + typename DerivedF2, + typename DerivedJ > + IGL_INLINE void resolve_duplicated_faces( + const Eigen::PlainObjectBase& F1, + Eigen::PlainObjectBase& F2, + Eigen::PlainObjectBase& J); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "resolve_duplicated_faces.cpp" +#endif + +#endif diff --git a/src/igl/rgb_to_hsv.cpp b/src/igl/rgb_to_hsv.cpp new file mode 100644 index 0000000000..fbf32bfec3 --- /dev/null +++ b/src/igl/rgb_to_hsv.cpp @@ -0,0 +1,102 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "rgb_to_hsv.h" + +template +IGL_INLINE void igl::rgb_to_hsv(const R * rgb, H * hsv) +{ + // http://en.literateprograms.org/RGB_to_HSV_color_space_conversion_%28C%29 + R rgb_max = 0.0; + R rgb_min = 1.0; + rgb_max = (rgb[0]>rgb_max?rgb[0]:rgb_max); + rgb_max = (rgb[1]>rgb_max?rgb[1]:rgb_max); + rgb_max = (rgb[2]>rgb_max?rgb[2]:rgb_max); + rgb_min = (rgb[0]rgb_max?rgb_n[0]:rgb_max); + rgb_max = (rgb_n[1]>rgb_max?rgb_n[1]:rgb_max); + rgb_max = (rgb_n[2]>rgb_max?rgb_n[2]:rgb_max); + rgb_min = 1; + rgb_min = (rgb_n[0]rgb_max?rgb_n[0]:rgb_max); + rgb_max = (rgb_n[1]>rgb_max?rgb_n[1]:rgb_max); + rgb_max = (rgb_n[2]>rgb_max?rgb_n[2]:rgb_max); + rgb_min = 1; + rgb_min = (rgb_n[0] +IGL_INLINE void igl::rgb_to_hsv( + const Eigen::PlainObjectBase & R, + Eigen::PlainObjectBase & H) +{ + assert(R.cols() == 3); + H.resizeLike(R); + for(typename DerivedR::Index r = 0;r(float const*, double*); +template void igl::rgb_to_hsv(double const*, double*); +template void igl::rgb_to_hsv, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::rgb_to_hsv, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::rgb_to_hsv, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/rgb_to_hsv.h b/src/igl/rgb_to_hsv.h new file mode 100644 index 0000000000..ed695ad606 --- /dev/null +++ b/src/igl/rgb_to_hsv.h @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_RGB_TO_HSV_H +#define IGL_RGB_TO_HSV_H +#include "igl_inline.h" +#include +namespace igl +{ + // Convert RGB to HSV + // + // Inputs: + // r red value ([0,1]) + // g green value ([0,1]) + // b blue value ([0,1]) + // Outputs: + // h hue value (degrees: [0,360]) + // s saturation value ([0,1]) + // v value value ([0,1]) + template + IGL_INLINE void rgb_to_hsv(const R * rgb, H * hsv); + template + IGL_INLINE void rgb_to_hsv( + const Eigen::PlainObjectBase & R, + Eigen::PlainObjectBase & H); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "rgb_to_hsv.cpp" +#endif + +#endif + diff --git a/src/igl/rotate_by_quat.cpp b/src/igl/rotate_by_quat.cpp new file mode 100644 index 0000000000..b77c812ea3 --- /dev/null +++ b/src/igl/rotate_by_quat.cpp @@ -0,0 +1,55 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "rotate_by_quat.h" + +#include "quat_conjugate.h" +#include "quat_mult.h" +#include "normalize_quat.h" +#include + +template +IGL_INLINE void igl::rotate_by_quat( + const Q_type *v, + const Q_type *q, + Q_type *out) +{ + // Quaternion form of v, copy data in v, (as a result out can be same pointer + // as v) + Q_type quat_v[4] = {v[0],v[1],v[2],0}; + + // normalize input + Q_type normalized_q[4]; + +#ifndef NDEBUG + bool normalized = +#endif + igl::normalize_quat(q,normalized_q); +#ifndef NDEBUG + assert(normalized); +#endif + + // Conjugate of q + Q_type q_conj[4]; + igl::quat_conjugate(normalized_q,q_conj); + + // Rotate of vector v by quaternion q is: + // q*v*conj(q) + // Compute q*v + Q_type q_mult_quat_v[4]; + igl::quat_mult(normalized_q,quat_v,q_mult_quat_v); + // Compute (q*v) * conj(q) + igl::quat_mult(q_mult_quat_v,q_conj,out); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::rotate_by_quat(double const*, double const*, double*); +// generated by autoexplicit.sh +template void igl::rotate_by_quat(float const*, float const*, float*); +#endif diff --git a/src/igl/rotate_by_quat.h b/src/igl/rotate_by_quat.h new file mode 100644 index 0000000000..b452048399 --- /dev/null +++ b/src/igl/rotate_by_quat.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ROTATE_BY_QUAT_H +#define IGL_ROTATE_BY_QUAT_H +#include "igl_inline.h" + +namespace igl +{ + // Compute rotation of a given vector/point by a quaternion + // A Quaternion, q, is defined here as an arrays of four scalars (x,y,z,w), + // such that q = x*i + y*j + z*k + w + // Inputs: + // v input 3d point/vector + // q input quaternion + // Outputs: + // out result of rotation, allowed to be same as v + template + IGL_INLINE void rotate_by_quat( + const Q_type *v, + const Q_type *q, + Q_type *out); +}; + +#ifndef IGL_STATIC_LIBRARY +# include "rotate_by_quat.cpp" +#endif + +#endif diff --git a/src/igl/rotate_vectors.cpp b/src/igl/rotate_vectors.cpp new file mode 100644 index 0000000000..b236d79e69 --- /dev/null +++ b/src/igl/rotate_vectors.cpp @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "rotate_vectors.h" +IGL_INLINE Eigen::MatrixXd igl::rotate_vectors( + const Eigen::MatrixXd& V, + const Eigen::VectorXd& A, + const Eigen::MatrixXd& B1, + const Eigen::MatrixXd& B2) +{ + Eigen::MatrixXd RV(V.rows(),V.cols()); + + for (unsigned i=0; i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_ROTATE_VECTORS_H +#define IGL_ROTATE_VECTORS_H +#include "igl_inline.h" +#include +namespace igl +{ + // Rotate the vectors V by A radiants on the tangent plane spanned by B1 and + // B2 + // + // Inputs: + // V #V by 3 eigen Matrix of vectors + // A #V eigen vector of rotation angles or a single angle to be applied + // to all vectors + // B1 #V by 3 eigen Matrix of base vector 1 + // B2 #V by 3 eigen Matrix of base vector 2 + // + // Output: + // Returns the rotated vectors + // + IGL_INLINE Eigen::MatrixXd rotate_vectors( + const Eigen::MatrixXd& V, + const Eigen::VectorXd& A, + const Eigen::MatrixXd& B1, + const Eigen::MatrixXd& B2); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "rotate_vectors.cpp" +#endif + +#endif diff --git a/src/igl/rotation_matrix_from_directions.cpp b/src/igl/rotation_matrix_from_directions.cpp new file mode 100644 index 0000000000..4ecc1a5d9c --- /dev/null +++ b/src/igl/rotation_matrix_from_directions.cpp @@ -0,0 +1,63 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo , Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "rotation_matrix_from_directions.h" +#include +#include + +template +IGL_INLINE Eigen::Matrix igl::rotation_matrix_from_directions( + const Eigen::Matrix v0, + const Eigen::Matrix v1) +{ + Eigen::Matrix rotM; + const double epsilon=1e-8; + Scalar dot=v0.normalized().dot(v1.normalized()); + ///control if there is no rotation + if ((v0-v1).norm()::Identity(); + return rotM; + } + if ((v0+v1).norm()::Identity(); + rotM(0,0) = 1.; + std::cerr<<"igl::rotation_matrix_from_directions: rotating around x axis by 180o"< axis; + axis=v0.cross(v1); + axis.normalize(); + + ///construct rotation matrix + Scalar u=axis(0); + Scalar v=axis(1); + Scalar w=axis(2); + Scalar phi=acos(dot); + Scalar rcos = cos(phi); + Scalar rsin = sin(phi); + + rotM(0,0) = rcos + u*u*(1-rcos); + rotM(1,0) = w * rsin + v*u*(1-rcos); + rotM(2,0) = -v * rsin + w*u*(1-rcos); + rotM(0,1) = -w * rsin + u*v*(1-rcos); + rotM(1,1) = rcos + v*v*(1-rcos); + rotM(2,1) = u * rsin + w*v*(1-rcos); + rotM(0,2) = v * rsin + u*w*(1-rcos); + rotM(1,2) = -u * rsin + v*w*(1-rcos); + rotM(2,2) = rcos + w*w*(1-rcos); + + return rotM; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template Eigen::Matrix igl::rotation_matrix_from_directions(const Eigen::Matrix, const Eigen::Matrix); +#endif diff --git a/src/igl/rotation_matrix_from_directions.h b/src/igl/rotation_matrix_from_directions.h new file mode 100644 index 0000000000..ca13972729 --- /dev/null +++ b/src/igl/rotation_matrix_from_directions.h @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson, Daniele Panozzo, Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ROTATION_MATRIX_FROM_DIRECTIONS_H +#define IGL_ROTATION_MATRIX_FROM_DIRECTIONS_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Given 2 vectors centered on origin calculate the rotation matrix from + // first to the second + // + // Inputs: + // v0 3D column vector + // v1 3D column vector + // Output: + // 3 by 3 rotation matrix that takes v0 to v1 + // + template + IGL_INLINE Eigen::Matrix rotation_matrix_from_directions( + const Eigen::Matrix v0, + const Eigen::Matrix v1); +} + +#ifndef IGL_STATIC_LIBRARY +#include "rotation_matrix_from_directions.cpp" +#endif +#endif diff --git a/src/igl/round.cpp b/src/igl/round.cpp new file mode 100644 index 0000000000..acde47316b --- /dev/null +++ b/src/igl/round.cpp @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "round.h" +#include + + +// http://stackoverflow.com/a/485549 +template +IGL_INLINE DerivedX igl::round(const DerivedX r) +{ + return (r > 0.0) ? std::floor(r + 0.5) : std::ceil(r - 0.5); +} + +template < typename DerivedX, typename DerivedY> +IGL_INLINE void igl::round( + const Eigen::PlainObjectBase& X, + Eigen::PlainObjectBase& Y) +{ + Y.resizeLike(X); + // loop over rows + for(int i = 0;i, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::round, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::round, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::round, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::round, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::round, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::round, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/round.h b/src/igl/round.h new file mode 100644 index 0000000000..cdaac731bf --- /dev/null +++ b/src/igl/round.h @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ROUND_H +#define IGL_ROUND_H +#include "igl_inline.h" +#include +namespace igl +{ + // Round a scalar value + // + // Inputs: + // x number + // Returns x rounded to integer + template + DerivedX round(const DerivedX r); + // Round a given matrix to nearest integers + // + // Inputs: + // X m by n matrix of scalars + // Outputs: + // Y m by n matrix of rounded integers + template < typename DerivedX, typename DerivedY> + IGL_INLINE void round( + const Eigen::PlainObjectBase& X, + Eigen::PlainObjectBase& Y); +} + +#ifndef IGL_STATIC_LIBRARY +# include "round.cpp" +#endif + +#endif diff --git a/src/igl/rows_to_matrix.cpp b/src/igl/rows_to_matrix.cpp new file mode 100644 index 0000000000..14c369e40f --- /dev/null +++ b/src/igl/rows_to_matrix.cpp @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "rows_to_matrix.h" + +#include +#include + +#include "max_size.h" +#include "min_size.h" + +template +IGL_INLINE bool igl::rows_to_matrix(const std::vector & V,Mat & M) +{ + // number of columns + int m = V.size(); + if(m == 0) + { + fprintf(stderr,"Error: rows_to_matrix() list is empty()\n"); + return false; + } + // number of rows + int n = igl::min_size(V); + if(n != igl::max_size(V)) + { + fprintf(stderr,"Error: rows_to_matrix()" + " list elements are not all the same size\n"); + return false; + } + assert(n != -1); + // Resize output + M.resize(m,n); + + // Loop over rows + int i = 0; + typename std::vector::const_iterator iter = V.begin(); + while(iter != V.end()) + { + M.row(i) = V[i]; + // increment index and iterator + i++; + iter++; + } + + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/rows_to_matrix.h b/src/igl/rows_to_matrix.h new file mode 100644 index 0000000000..8f62b5a2d0 --- /dev/null +++ b/src/igl/rows_to_matrix.h @@ -0,0 +1,34 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_ROWS_TO_MATRIX_H +#define IGL_ROWS_TO_MATRIX_H +#include "igl_inline.h" +#include +namespace igl +{ + // Convert a list (std::vector) of row vectors of the same length to a matrix + // Template: + // Row row vector type, must implement: + // .size() + // Mat Matrix type, must implement: + // .resize(m,n) + // .row(i) = Row + // Inputs: + // V a m-long list of vectors of size n + // Outputs: + // M an m by n matrix + // Returns true on success, false on errors + template + IGL_INLINE bool rows_to_matrix(const std::vector & V,Mat & M); +} + +#ifndef IGL_STATIC_LIBRARY +# include "rows_to_matrix.cpp" +#endif + +#endif diff --git a/src/igl/sample_edges.cpp b/src/igl/sample_edges.cpp new file mode 100644 index 0000000000..a20df14261 --- /dev/null +++ b/src/igl/sample_edges.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "sample_edges.h" + +IGL_INLINE void igl::sample_edges( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & E, + const int k, + Eigen::MatrixXd & S) +{ + using namespace Eigen; + // Resize output + S.resize(V.rows() + k * E.rows(),V.cols()); + // Copy V at front of S + S.block(0,0,V.rows(),V.cols()) = V; + + // loop over edges + for(int i = 0;i +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SAMPLE_EDGES_H +#define IGL_SAMPLE_EDGES_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Compute samples_per_edge extra points along each edge in E defined over + // vertices of V. + // + // Inputs: + // V vertices over which edges are defined, # vertices by dim + // E edge list, # edges by 2 + // k number of extra samples to be computed along edge not + // including start and end points + // Output: + // S sampled vertices, size less than # edges * (2+k) by dim always begins + // with V so that E is also defined over S + IGL_INLINE void sample_edges( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & E, + const int k, + Eigen::MatrixXd & S); +} +#ifndef IGL_STATIC_LIBRARY +# include "sample_edges.cpp" +#endif + +#endif diff --git a/src/igl/seam_edges.cpp b/src/igl/seam_edges.cpp new file mode 100644 index 0000000000..8cbca143cd --- /dev/null +++ b/src/igl/seam_edges.cpp @@ -0,0 +1,211 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Yotam Gingold +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "seam_edges.h" +#include +#include +#include + +// Yotam has verified that this function produces the exact same output as +// `find_seam_fast.py` for `cow_triangled.obj`. +template < + typename DerivedV, + typename DerivedTC, + typename DerivedF, + typename DerivedFTC, + typename Derivedseams, + typename Derivedboundaries, + typename Derivedfoldovers> +IGL_INLINE void igl::seam_edges( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& TC, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& FTC, + Eigen::PlainObjectBase& seams, + Eigen::PlainObjectBase& boundaries, + Eigen::PlainObjectBase& foldovers) +{ + // Assume triangles. + assert( F.cols() == 3 ); + assert( F.cols() == FTC.cols() ); + assert( F.rows() == FTC.rows() ); + + // Assume 2D texture coordinates (foldovers tests). + assert( TC.cols() == 2 ); + typedef Eigen::Matrix< typename DerivedTC::Scalar, 2, 1 > Vector2S; + // Computes the orientation of `c` relative to the line between `a` and `b`. + // Assumes 2D vector input. + // Based on: https://www.cs.cmu.edu/~quake/robust.html + const auto& Orientation = []( + const Vector2S& a, + const Vector2S& b, + const Vector2S& c ) -> typename DerivedTC::Scalar + { + const Vector2S row0 = a - c; + const Vector2S row1 = b - c; + return row0(0)*row1(1) - row1(0)*row0(1); + }; + + seams .setZero( 3*F.rows(), 4 ); + boundaries.setZero( 3*F.rows(), 2 ); + foldovers .setZero( 3*F.rows(), 4 ); + + int num_seams = 0; + int num_boundaries = 0; + int num_foldovers = 0; + + // A map from a pair of vertex indices to the index (face and endpoints) + // into face_position_indices. + // The following should be true for every key, value pair: + // key == face_position_indices[ value ] + // This gives us a "reverse map" so that we can look up other face + // attributes based on position edges. + // The value are written in the format returned by numpy.where(), + // which stores multi-dimensional indices such as array[a0,b0], array[a1,b1] + // as ( (a0,a1), (b0,b1) ). + + // We need to make a hash function for our directed edges. + // We'll use i*V.rows() + j. + typedef std::pair< typename DerivedF::Scalar, typename DerivedF::Scalar > + directed_edge; + const int numV = V.rows(); + const int numF = F.rows(); + const auto& edge_hasher = + [numV]( directed_edge const& e ) { return e.first*numV + e.second; }; + // When we pass a hash function object, we also need to specify the number of + // buckets. The Euler characteristic says that the number of undirected edges + // is numV + numF -2*genus. + std::unordered_map,decltype(edge_hasher) > + directed_position_edge2face_position_index(2*( numV + numF ), edge_hasher); + for( int fi = 0; fi < F.rows(); ++fi ) + { + for( int i = 0; i < 3; ++i ) + { + const int j = ( i+1 ) % 3; + directed_position_edge2face_position_index[ + std::make_pair( F(fi,i), F(fi,j) ) ] = std::make_pair( fi, i ); + } + } + + // First find all undirected position edges (collect a canonical orientation + // of the directed edges). + std::unordered_set< directed_edge, decltype( edge_hasher ) > + undirected_position_edges( numV + numF, edge_hasher ); + for( const auto& el : directed_position_edge2face_position_index ) + { + // The canonical orientation is the one where the smaller of + // the two vertex indices is first. + undirected_position_edges.insert( std::make_pair( + std::min( el.first.first, el.first.second ), + std::max( el.first.first, el.first.second ) ) ); + } + + // Now we will iterate over all position edges. + // Seam edges are the edges whose two opposite directed edges have different + // texcoord indices (or one doesn't exist at all in the case of a mesh + // boundary). + for( const auto& vp_edge : undirected_position_edges ) + { + // We should only see canonical edges, + // where the first vertex index is smaller. + assert( vp_edge.first < vp_edge.second ); + + const auto vp_edge_reverse = std::make_pair(vp_edge.second, vp_edge.first); + // If it and its opposite exist as directed edges, check if their + // texture coordinate indices match. + if( directed_position_edge2face_position_index.count( vp_edge ) && + directed_position_edge2face_position_index.count( vp_edge_reverse ) ) + { + const auto forwards = + directed_position_edge2face_position_index[ vp_edge ]; + const auto backwards = + directed_position_edge2face_position_index[ vp_edge_reverse ]; + + // NOTE: They should never be equal. + assert( forwards != backwards ); + + // If the texcoord indices match (are similarly flipped), + // this edge is not a seam. It could be a foldover. + if( + std::make_pair( + FTC( forwards.first, forwards.second ), + FTC( forwards.first, ( forwards.second+1 ) % 3 ) ) + == + std::make_pair( + FTC( backwards.first, ( backwards.second+1 ) % 3 ), + FTC( backwards.first, backwards.second ) )) + { + // Check for foldovers in UV space. + // Get the edge (a,b) and the two opposite vertices's texture + // coordinates. + const Vector2S a = TC.row( FTC( forwards.first, forwards.second ) ); + const Vector2S b = + TC.row( FTC( forwards.first, (forwards.second+1) % 3 ) ); + const Vector2S c_forwards = + TC.row( FTC( forwards .first, (forwards .second+2) % 3 ) ); + const Vector2S c_backwards = + TC.row( FTC( backwards.first, (backwards.second+2) % 3 ) ); + // If the opposite vertices' texture coordinates fall on the same side + // of the edge, we have a UV-space foldover. + const auto orientation_forwards = Orientation( a, b, c_forwards ); + const auto orientation_backwards = Orientation( a, b, c_backwards ); + if( ( orientation_forwards > 0 && orientation_backwards > 0 ) || + ( orientation_forwards < 0 && orientation_backwards < 0 ) + ) { + foldovers( num_foldovers, 0 ) = forwards.first; + foldovers( num_foldovers, 1 ) = forwards.second; + foldovers( num_foldovers, 2 ) = backwards.first; + foldovers( num_foldovers, 3 ) = backwards.second; + num_foldovers += 1; + } + } + // Otherwise, we have a non-matching seam edge. + else + { + seams( num_seams, 0 ) = forwards.first; + seams( num_seams, 1 ) = forwards.second; + seams( num_seams, 2 ) = backwards.first; + seams( num_seams, 3 ) = backwards.second; + num_seams += 1; + } + } + // Otherwise, the edge and its opposite aren't both in the directed edges. + // One of them should be. + else if( directed_position_edge2face_position_index.count( vp_edge ) ) + { + const auto forwards = directed_position_edge2face_position_index[vp_edge]; + boundaries( num_boundaries, 0 ) = forwards.first; + boundaries( num_boundaries, 1 ) = forwards.second; + num_boundaries += 1; + } else if( + directed_position_edge2face_position_index.count( vp_edge_reverse ) ) + { + const auto backwards = + directed_position_edge2face_position_index[ vp_edge_reverse ]; + boundaries( num_boundaries, 0 ) = backwards.first; + boundaries( num_boundaries, 1 ) = backwards.second; + num_boundaries += 1; + } else { + // This should never happen! One of these two must have been seen. + assert( + directed_position_edge2face_position_index.count( vp_edge ) || + directed_position_edge2face_position_index.count( vp_edge_reverse ) + ); + } + } + + seams .conservativeResize( num_seams, Eigen::NoChange_t() ); + boundaries.conservativeResize( num_boundaries, Eigen::NoChange_t() ); + foldovers .conservativeResize( num_foldovers, Eigen::NoChange_t() ); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::seam_edges, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/seam_edges.h b/src/igl/seam_edges.h new file mode 100644 index 0000000000..15c82826cf --- /dev/null +++ b/src/igl/seam_edges.h @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Yotam Gingold +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SEAM_EDGES_H +#define IGL_SEAM_EDGES_H +#include "igl_inline.h" +#include +namespace igl +{ + // Finds all UV-space boundaries of a mesh. + // + // Inputs: + // V #V by dim list of positions of the input mesh. + // TC #TC by 2 list of 2D texture coordinates of the input mesh + // F #F by 3 list of triange indices into V representing a + // manifold-with-boundary triangle mesh + // FTC #F by 3 list of indices into TC for each corner + // Outputs: + // seams Edges where the forwards and backwards directions have different + // texture coordinates, as a #seams-by-4 matrix of indices. Each row is + // organized as [ forward_face_index, forward_face_vertex_index, + // backwards_face_index, backwards_face_vertex_index ] such that one side + // of the seam is the edge: + // F[ seams( i, 0 ), seams( i, 1 ) ], F[ seams( i, 0 ), (seams( i, 1 ) + 1) % 3 ] + // and the other side is the edge: + // F[ seams( i, 2 ), seams( i, 3 ) ], F[ seams( i, 2 ), (seams( i, 3 ) + 1) % 3 ] + // boundaries Edges with only one incident triangle, as a #boundaries-by-2 + // matrix of indices. Each row is organized as + // [ face_index, face_vertex_index ] + // such that the edge is: + // F[ boundaries( i, 0 ), boundaries( i, 1 ) ], F[ boundaries( i, 0 ), (boundaries( i, 1 ) + 1) % 3 ] + // foldovers Edges where the two incident triangles fold over each other + // in UV-space, as a #foldovers-by-4 matrix of indices. + // Each row is organized as [ forward_face_index, forward_face_vertex_index, + // backwards_face_index, backwards_face_vertex_index ] + // such that one side of the foldover is the edge: + // F[ foldovers( i, 0 ), foldovers( i, 1 ) ], F[ foldovers( i, 0 ), (foldovers( i, 1 ) + 1) % 3 ] + // and the other side is the edge: + // F[ foldovers( i, 2 ), foldovers( i, 3 ) ], F[ foldovers( i, 2 ), (foldovers( i, 3 ) + 1) % 3 ] + template < + typename DerivedV, + typename DerivedTC, + typename DerivedF, + typename DerivedFTC, + typename Derivedseams, + typename Derivedboundaries, + typename Derivedfoldovers> + IGL_INLINE void seam_edges( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& TC, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& FTC, + Eigen::PlainObjectBase& seams, + Eigen::PlainObjectBase& boundaries, + Eigen::PlainObjectBase& foldovers); +} +#ifndef IGL_STATIC_LIBRARY +# include "seam_edges.cpp" +#endif +#endif diff --git a/src/igl/segment_segment_intersect.cpp b/src/igl/segment_segment_intersect.cpp new file mode 100644 index 0000000000..8438c04963 --- /dev/null +++ b/src/igl/segment_segment_intersect.cpp @@ -0,0 +1,67 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Francisca Gil Ureta +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "segment_segment_intersect.h" + +#include + +template +IGL_INLINE bool igl::segments_intersect( + const Eigen::PlainObjectBase &p, + const Eigen::PlainObjectBase &r, + const Eigen::PlainObjectBase &q, + const Eigen::PlainObjectBase &s, + double &a_t, + double &a_u, + double eps +) +{ + // http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect + // Search intersection between two segments + // p + t*r : t \in [0,1] + // q + u*s : u \in [0,1] + + // p + t * r = q + u * s // x s + // t(r x s) = (q - p) x s + // t = (q - p) x s / (r x s) + + // (r x s) ~ 0 --> directions are parallel, they will never cross + Eigen::RowVector3d rxs = r.cross(s); + if (rxs.norm() <= eps) + return false; + + int sign; + + double u; + // u = (q − p) × r / (r × s) + Eigen::RowVector3d u1 = (q - p).cross(r); + sign = ((u1.dot(rxs)) > 0) ? 1 : -1; + u = u1.norm() / rxs.norm(); + u = u * sign; + + if ((u - 1.) > eps || u < -eps) + return false; + + double t; + // t = (q - p) x s / (r x s) + Eigen::RowVector3d t1 = (q - p).cross(s); + sign = ((t1.dot(rxs)) > 0) ? 1 : -1; + t = t1.norm() / rxs.norm(); + t = t * sign; + + if (t < -eps || fabs(t) < eps) + return false; + + a_t = t; + a_u = u; + + return true; +}; + +#ifdef IGL_STATIC_LIBRARY +template bool igl::segments_intersect, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, double&, double&, double); +#endif diff --git a/src/igl/segment_segment_intersect.h b/src/igl/segment_segment_intersect.h new file mode 100644 index 0000000000..714629215c --- /dev/null +++ b/src/igl/segment_segment_intersect.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Francisca Gil Ureta +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_SEGMENT_SEGMENT_INTERSECT_H +#define IGL_SEGMENT_SEGMENT_INTERSECT_H + + +#include "igl_inline.h" +#include +namespace igl +{ + + // Determine whether two line segments A,B intersect + // A: p + t*r : t \in [0,1] + // B: q + u*s : u \in [0,1] + // Inputs: + // p 3-vector origin of segment A + // r 3-vector direction of segment A + // q 3-vector origin of segment B + // s 3-vector direction of segment B + // eps precision + // Outputs: + // t scalar point of intersection along segment A, t \in [0,1] + // u scalar point of intersection along segment B, u \in [0,1] + // Returns true if intersection + template + IGL_INLINE bool segments_intersect( + const Eigen::PlainObjectBase &p, + const Eigen::PlainObjectBase &r, + const Eigen::PlainObjectBase &q, + const Eigen::PlainObjectBase &s, + double &t, + double &u, + double eps = 1e-6 + ); + +} +#ifndef IGL_STATIC_LIBRARY +# include "segment_segment_intersect.cpp" +#endif +#endif //IGL_SEGMENT_SEGMENT_INTERSECT_H diff --git a/src/igl/serialize.h b/src/igl/serialize.h new file mode 100644 index 0000000000..54967293b4 --- /dev/null +++ b/src/igl/serialize.h @@ -0,0 +1,1258 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Christian Schüller +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SERIALIZE_H +#define IGL_SERIALIZE_H + +// ----------------------------------------------------------------------------- +// Functions to save and load a serialization of fundamental c++ data types to +// and from a binary file. STL containers, Eigen matrix types and nested data +// structures are also supported. To serialize a user defined class implement +// the interface Serializable or SerializableBase. +// +// See also: xml/serialize_xml.h +// ----------------------------------------------------------------------------- +// TODOs: +// * arbitrary pointer graph structures +// ----------------------------------------------------------------------------- + +// Known issues: This is not written in libigl-style so it isn't (easily) +// "dualized" into the static library. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "igl_inline.h" + +// non-intrusive serialization helper macros + +#define SERIALIZE_TYPE(Type,Params) \ +namespace igl { namespace serialization { \ + void _serialization(bool s,Type& obj,std::vector& buffer) {Params} \ + template<> inline void serialize(const Type& obj,std::vector& buffer) { \ + _serialization(true,const_cast(obj),buffer); \ + } \ + template<> inline void deserialize(Type& obj,const std::vector& buffer) { \ + _serialization(false,obj,const_cast&>(buffer)); \ + } \ +}} + +#define SERIALIZE_TYPE_SOURCE(Type,Params) \ +namespace igl { namespace serialization { \ + void _serialization(bool s,Type& obj,std::vector& buffer) {Params} \ + void _serialize(const Type& obj,std::vector& buffer) { \ + _serialization(true,const_cast(obj),buffer); \ + } \ + void _deserialize(Type& obj,const std::vector& buffer) { \ + _serialization(false,obj,const_cast&>(buffer)); \ + } \ +}} + +#define SERIALIZE_MEMBER(Object) igl::serializer(s,obj.Object,std::string(#Object),buffer); +#define SERIALIZE_MEMBER_NAME(Object,Name) igl::serializer(s,obj.Object,std::string(Name),buffer); + + +namespace igl +{ + struct IndexedPointerBase; + + // Serializes the given object either to a file or to a provided buffer + // Templates: + // T type of the object to serialize + // Inputs: + // obj object to serialize + // objectName unique object name,used for the identification + // overwrite set to true to overwrite an existing file + // filename name of the file containing the serialization + // Outputs: + // buffer binary serialization + // + template + inline bool serialize(const T& obj,const std::string& filename); + template + inline bool serialize(const T& obj,const std::string& objectName,const std::string& filename,bool overwrite = false); + template + inline bool serialize(const T& obj,const std::string& objectName,std::vector& buffer); + template + inline bool serialize(const T& obj,const std::string& objectName,std::vector& buffer); + + // Deserializes the given data from a file or buffer back to the provided object + // + // Templates: + // T type of the object to serialize + // Inputs: + // buffer binary serialization + // objectName unique object name, used for the identification + // filename name of the file containing the serialization + // Outputs: + // obj object to load back serialization to + // + template + inline bool deserialize(T& obj,const std::string& filename); + template + inline bool deserialize(T& obj,const std::string& objectName,const std::string& filename); + template + inline bool deserialize(T& obj,const std::string& objectName,const std::vector& buffer); + + // Wrapper to expose both, the de- and serialization as one function + // + template + inline bool serializer(bool serialize,T& obj,const std::string& filename); + template + inline bool serializer(bool serialize,T& obj,const std::string& objectName,const std::string& filename,bool overwrite = false); + template + inline bool serializer(bool serialize,T& obj,const std::string& objectName,std::vector& buffer); + + // User defined types have to either overload the function igl::serialization::serialize() + // and igl::serialization::deserialize() for their type (non-intrusive serialization): + // + // namespace igl { namespace serialization + // { + // template<> + // inline void serialize(const UserType& obj,std::vector& buffer) { + // ::igl::serialize(obj.var,"var",buffer); + // } + // + // template<> + // inline void deserialize(UserType& obj,const std::vector& buffer) { + // ::igl::deserialize(obj.var,"var",buffer); + // } + // }} + // + // or use this macro for convenience: + // + // SERIALIZE_TYPE(UserType, + // SERIALIZE_MEMBER(var) + // ) + // + // or to derive from the class Serializable and add their the members + // in InitSerialization like the following: + // + // class UserType : public igl::Serializable { + // + // int var; + // + // void InitSerialization() { + // this->Add(var,"var"); + // } + // }; + + // Base interface for user defined types + struct SerializableBase + { + virtual void Serialize(std::vector& buffer) const = 0; + virtual void Deserialize(const std::vector& buffer) = 0; + }; + + // Convenient interface for user defined types + class Serializable: public SerializableBase + { + private: + + template + struct SerializationObject : public SerializableBase + { + bool Binary; + std::string Name; + std::unique_ptr Object; + + void Serialize(std::vector& buffer) const override { + igl::serialize(*Object,Name,buffer); + } + + void Deserialize(const std::vector& buffer) override { + igl::deserialize(*Object,Name,buffer); + } + }; + + mutable bool initialized; + mutable std::vector objects; + + public: + + // You **MUST** Override this function to add your member variables which + // should be serialized + // + // http://stackoverflow.com/a/6634382/148668 + virtual void InitSerialization() = 0; + + // Following functions can be overridden to handle the specific events. + // Return false to prevent the de-/serialization of an object. + inline virtual bool PreSerialization() const; + inline virtual void PostSerialization() const; + inline virtual bool PreDeserialization(); + inline virtual void PostDeserialization(); + + // Default implementation of SerializableBase interface + inline void Serialize(std::vector& buffer) const override final; + inline void Deserialize(const std::vector& buffer) override final; + + // Default constructor, destructor, assignment and copy constructor + inline Serializable(); + inline Serializable(const Serializable& obj); + inline ~Serializable(); + inline Serializable& operator=(const Serializable& obj); + + // Use this function to add your variables which should be serialized + template + inline void Add(T& obj,std::string name,bool binary = false); + }; + + // structure for pointer handling + struct IndexedPointerBase + { + enum { BEGIN,END } Type; + size_t Index; + }; + template + struct IndexedPointer: public IndexedPointerBase + { + const T* Object; + }; + + // internal functions + namespace serialization + { + // compile time type checks + template + struct is_stl_container { static const bool value = false; }; + template + struct is_stl_container > { static const bool value = true; }; + template + struct is_stl_container > { static const bool value = true; }; + template + struct is_stl_container > { static const bool value = true; }; + template + struct is_stl_container > { static const bool value = true; }; + template + struct is_stl_container > { static const bool value = true; }; + + template + struct is_eigen_type { static const bool value = false; }; + template + struct is_eigen_type > { static const bool value = true; }; + template + struct is_eigen_type > { static const bool value = true; }; + + template + struct is_smart_ptr { static const bool value = false; }; + template + struct is_smart_ptr > { static const bool value = true; }; + template + struct is_smart_ptr > { static const bool value = true; }; + template + struct is_smart_ptr > { static const bool value = true; }; + + template + struct is_serializable { + static const bool value = std::is_fundamental::value || std::is_same::value || std::is_enum::value || std::is_base_of::value + || is_stl_container::value || is_eigen_type::value || std::is_pointer::value || serialization::is_smart_ptr::value; + }; + + // non serializable types + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj); + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline typename std::enable_if::value>::type deserialize(T& obj,std::vector::const_iterator& iter); + + // fundamental types + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj); + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline typename std::enable_if::value>::type deserialize(T& obj,std::vector::const_iterator& iter); + + // std::string + inline size_t getByteSize(const std::string& obj); + inline void serialize(const std::string& obj,std::vector& buffer,std::vector::iterator& iter); + inline void deserialize(std::string& obj,std::vector::const_iterator& iter); + + // enum types + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj); + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline typename std::enable_if::value>::type deserialize(T& obj,std::vector::const_iterator& iter); + + // SerializableBase + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj); + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline typename std::enable_if::value>::type deserialize(T& obj,std::vector::const_iterator& iter); + + // stl containers + // std::pair + template + inline size_t getByteSize(const std::pair& obj); + template + inline void serialize(const std::pair& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline void deserialize(std::pair& obj,std::vector::const_iterator& iter); + + // std::vector + template + inline size_t getByteSize(const std::vector& obj); + template + inline void serialize(const std::vector& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline void deserialize(std::vector& obj,std::vector::const_iterator& iter); + template + inline void deserialize(std::vector& obj,std::vector::const_iterator& iter); + + // std::set + template + inline size_t getByteSize(const std::set& obj); + template + inline void serialize(const std::set& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline void deserialize(std::set& obj,std::vector::const_iterator& iter); + + // std::map + template + inline size_t getByteSize(const std::map& obj); + template + inline void serialize(const std::map& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline void deserialize(std::map& obj,std::vector::const_iterator& iter); + + // std::list + template + inline size_t getByteSize(const std::list& obj); + template + inline void serialize(const std::list& obj, std::vector& buffer, std::vector::iterator& iter); + template + inline void deserialize(std::list& obj, std::vector::const_iterator& iter); + + // Eigen types + template + inline size_t getByteSize(const Eigen::Matrix& obj); + template + inline void serialize(const Eigen::Matrix& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline void deserialize(Eigen::Matrix& obj,std::vector::const_iterator& iter); + + template + inline size_t getByteSize(const Eigen::SparseMatrix& obj); + template + inline void serialize(const Eigen::SparseMatrix& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline void deserialize(Eigen::SparseMatrix& obj,std::vector::const_iterator& iter); + + template + inline size_t getByteSize(const Eigen::Quaternion& obj); + template + inline void serialize(const Eigen::Quaternion& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline void deserialize(Eigen::Quaternion& obj,std::vector::const_iterator& iter); + + // raw pointers + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj); + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline typename std::enable_if::value>::type deserialize(T& obj,std::vector::const_iterator& iter); + + // std::shared_ptr and std::unique_ptr + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj); + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter); + template class T0, typename T1> + inline typename std::enable_if >::value>::type deserialize(T0& obj,std::vector::const_iterator& iter); + + // std::weak_ptr + template + inline size_t getByteSize(const std::weak_ptr& obj); + template + inline void serialize(const std::weak_ptr& obj,std::vector& buffer,std::vector::iterator& iter); + template + inline void deserialize(std::weak_ptr& obj,std::vector::const_iterator& iter); + + // functions to overload for non-intrusive serialization + template + inline void serialize(const T& obj,std::vector& buffer); + template + inline void deserialize(T& obj,const std::vector& buffer); + + // helper functions + template + inline void updateMemoryMap(T& obj,size_t size); + } +} + +// Always include inlines for these functions + +// IMPLEMENTATION + +namespace igl +{ + template + inline bool serialize(const T& obj,const std::string& filename) + { + return serialize(obj,"obj",filename,true); + } + + template + inline bool serialize(const T& obj,const std::string& objectName,const std::string& filename,bool overwrite) + { + bool success = false; + + std::vector buffer; + + std::ios_base::openmode mode = std::ios::out | std::ios::binary; + + if(overwrite) + mode |= std::ios::trunc; + else + mode |= std::ios::app; + + std::ofstream file(filename.c_str(),mode); + + if(file.is_open()) + { + serialize(obj,objectName,buffer); + + file.write(&buffer[0],buffer.size()); + + file.close(); + + success = true; + } + else + { + std::cerr << "serialization: file " << filename << " not found!" << std::endl; + } + + return success; + } + + template + inline bool serialize(const T& obj,const std::string& objectName,std::vector& buffer) + { + // serialize object data + size_t size = serialization::getByteSize(obj); + std::vector tmp(size); + auto it = tmp.begin(); + serialization::serialize(obj,tmp,it); + + std::string objectType(typeid(obj).name()); + size_t newObjectSize = tmp.size(); + size_t newHeaderSize = serialization::getByteSize(objectName) + serialization::getByteSize(objectType) + sizeof(size_t); + size_t curSize = buffer.size(); + size_t newSize = curSize + newHeaderSize + newObjectSize; + + buffer.resize(newSize); + + std::vector::iterator iter = buffer.begin()+curSize; + + // serialize object header (name/type/size) + serialization::serialize(objectName,buffer,iter); + serialization::serialize(objectType,buffer,iter); + serialization::serialize(newObjectSize,buffer,iter); + + // copy serialized data to buffer + iter = std::copy(tmp.begin(),tmp.end(),iter); + + return true; + } + + template + inline bool deserialize(T& obj,const std::string& filename) + { + return deserialize(obj,"obj",filename); + } + + template + inline bool deserialize(T& obj,const std::string& objectName,const std::string& filename) + { + bool success = false; + + std::ifstream file(filename.c_str(),std::ios::binary); + + if(file.is_open()) + { + file.seekg(0,std::ios::end); + std::streamoff size = file.tellg(); + file.seekg(0,std::ios::beg); + + std::vector buffer(size); + file.read(&buffer[0],size); + + deserialize(obj,objectName,buffer); + file.close(); + + success = true; + } + else + { + std::cerr << "serialization: file " << filename << " not found!" << std::endl; + } + + return success; + } + + template + inline bool deserialize(T& obj,const std::string& objectName,const std::vector& buffer) + { + bool success = false; + + // find suitable object header + auto objectIter = buffer.cend(); + auto iter = buffer.cbegin(); + while(iter != buffer.end()) + { + std::string name; + std::string type; + size_t size; + serialization::deserialize(name,iter); + serialization::deserialize(type,iter); + serialization::deserialize(size,iter); + + if(name == objectName && type == typeid(obj).name()) + { + objectIter = iter; + //break; // find first suitable object header + } + + iter+=size; + } + + if(objectIter != buffer.end()) + { + serialization::deserialize(obj,objectIter); + success = true; + } + else + { + obj = T(); + } + + return success; + } + + // Wrapper function which combines both, de- and serialization + template + inline bool serializer(bool s,T& obj,const std::string& filename) + { + return s ? serialize(obj,filename) : deserialize(obj,filename); + } + + template + inline bool serializer(bool s,T& obj,const std::string& objectName,const std::string& filename,bool overwrite) + { + return s ? serialize(obj,objectName,filename,overwrite) : deserialize(obj,objectName,filename); + } + + template + inline bool serializer(bool s,T& obj,const std::string& objectName,std::vector& buffer) + { + return s ? serialize(obj,objectName,buffer) : deserialize(obj,objectName,buffer); + } + + inline bool Serializable::PreSerialization() const + { + return true; + } + + inline void Serializable::PostSerialization() const + { + } + + inline bool Serializable::PreDeserialization() + { + return true; + } + + inline void Serializable::PostDeserialization() + { + } + + inline void Serializable::Serialize(std::vector& buffer) const + { + if(this->PreSerialization()) + { + if(initialized == false) + { + objects.clear(); + (const_cast(this))->InitSerialization(); + initialized = true; + } + + for(const auto& v : objects) + { + v->Serialize(buffer); + } + + this->PostSerialization(); + } + } + + inline void Serializable::Deserialize(const std::vector& buffer) + { + if(this->PreDeserialization()) + { + if(initialized == false) + { + objects.clear(); + (const_cast(this))->InitSerialization(); + initialized = true; + } + + for(auto& v : objects) + { + v->Deserialize(buffer); + } + + this->PostDeserialization(); + } + } + + inline Serializable::Serializable() + { + initialized = false; + } + + inline Serializable::Serializable(const Serializable& obj) + { + initialized = false; + objects.clear(); + } + + inline Serializable::~Serializable() + { + initialized = false; + objects.clear(); + } + + inline Serializable& Serializable::operator=(const Serializable& obj) + { + if(this != &obj) + { + if(initialized) + { + initialized = false; + objects.clear(); + } + } + return *this; + } + + template + inline void Serializable::Add(T& obj,const std::string name,bool binary) + { + auto object = new SerializationObject(); + object->Binary = binary; + object->Name = name; + object->Object = std::unique_ptr(&obj); + + objects.push_back(object); + } + + namespace serialization + { + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj) + { + return sizeof(std::vector::size_type); + } + + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter) + { + // data + std::vector tmp; + serialize<>(obj,tmp); + + // size + size_t size = buffer.size(); + serialization::serialize(tmp.size(),buffer,iter); + size_t cur = iter - buffer.begin(); + + buffer.resize(size+tmp.size()); + iter = buffer.begin()+cur; + iter = std::copy(tmp.begin(),tmp.end(),iter); + } + + template + inline typename std::enable_if::value>::type deserialize(T& obj,std::vector::const_iterator& iter) + { + std::vector::size_type size; + serialization::deserialize<>(size,iter); + + std::vector tmp; + tmp.resize(size); + std::copy(iter,iter+size,tmp.begin()); + + deserialize<>(obj,tmp); + iter += size; + } + + // fundamental types + + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj) + { + return sizeof(T); + } + + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter) + { + //serialization::updateMemoryMap(obj,sizeof(T)); + const uint8_t* ptr = reinterpret_cast(&obj); + iter = std::copy(ptr,ptr+sizeof(T),iter); + } + + template + inline typename std::enable_if::value>::type deserialize(T& obj,std::vector::const_iterator& iter) + { + uint8_t* ptr = reinterpret_cast(&obj); + std::copy(iter,iter+sizeof(T),ptr); + iter += sizeof(T); + } + + // std::string + + inline size_t getByteSize(const std::string& obj) + { + return getByteSize(obj.length())+obj.length()*sizeof(uint8_t); + } + + inline void serialize(const std::string& obj,std::vector& buffer,std::vector::iterator& iter) + { + serialization::serialize(obj.length(),buffer,iter); + for(const auto& cur : obj) + { + serialization::serialize(cur,buffer,iter); + } + } + + inline void deserialize(std::string& obj,std::vector::const_iterator& iter) + { + size_t size; + serialization::deserialize(size,iter); + + std::string str(size,'\0'); + for(size_t i=0; i + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj) + { + return sizeof(T); + } + + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter) + { + const uint8_t* ptr = reinterpret_cast(&obj); + iter = std::copy(ptr,ptr+sizeof(T),iter); + } + + template + inline typename std::enable_if::value>::type deserialize(T& obj,std::vector::const_iterator& iter) + { + uint8_t* ptr = reinterpret_cast(&obj); + std::copy(iter,iter+sizeof(T),ptr); + iter += sizeof(T); + } + + // SerializableBase + + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj) + { + return sizeof(std::vector::size_type); + } + + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter) + { + // data + std::vector tmp; + obj.Serialize(tmp); + + // size + size_t size = buffer.size(); + serialization::serialize(tmp.size(),buffer,iter); + size_t cur = iter - buffer.begin(); + + buffer.resize(size+tmp.size()); + iter = buffer.begin()+cur; + iter = std::copy(tmp.begin(),tmp.end(),iter); + } + + template + inline typename std::enable_if::value>::type deserialize(T& obj,std::vector::const_iterator& iter) + { + std::vector::size_type size; + serialization::deserialize(size,iter); + + std::vector tmp; + tmp.resize(size); + std::copy(iter,iter+size,tmp.begin()); + + obj.Deserialize(tmp); + iter += size; + } + + // STL containers + + // std::pair + + template + inline size_t getByteSize(const std::pair& obj) + { + return getByteSize(obj.first)+getByteSize(obj.second); + } + + template + inline void serialize(const std::pair& obj,std::vector& buffer,std::vector::iterator& iter) + { + serialization::serialize(obj.first,buffer,iter); + serialization::serialize(obj.second,buffer,iter); + } + + template + inline void deserialize(std::pair& obj,std::vector::const_iterator& iter) + { + serialization::deserialize(obj.first,iter); + serialization::deserialize(obj.second,iter); + } + + // std::vector + + template + inline size_t getByteSize(const std::vector& obj) + { + return std::accumulate(obj.begin(),obj.end(),sizeof(size_t),[](const size_t& acc,const T1& cur) { return acc+getByteSize(cur); }); + } + + template + inline void serialize(const std::vector& obj,std::vector& buffer,std::vector::iterator& iter) + { + size_t size = obj.size(); + serialization::serialize(size,buffer,iter); + for(const T1& cur : obj) + { + serialization::serialize(cur,buffer,iter); + } + } + + template + inline void deserialize(std::vector& obj,std::vector::const_iterator& iter) + { + size_t size; + serialization::deserialize(size,iter); + + obj.resize(size); + for(T1& v : obj) + { + serialization::deserialize(v,iter); + } + } + + template + inline void deserialize(std::vector& obj,std::vector::const_iterator& iter) + { + size_t size; + serialization::deserialize(size,iter); + + obj.resize(size); + for(int i=0;i + inline size_t getByteSize(const std::set& obj) + { + return std::accumulate(obj.begin(),obj.end(),getByteSize(obj.size()),[](const size_t& acc,const T& cur) { return acc+getByteSize(cur); }); + } + + template + inline void serialize(const std::set& obj,std::vector& buffer,std::vector::iterator& iter) + { + serialization::serialize(obj.size(),buffer,iter); + for(const T& cur : obj) + { + serialization::serialize(cur,buffer,iter); + } + } + + template + inline void deserialize(std::set& obj,std::vector::const_iterator& iter) + { + size_t size; + serialization::deserialize(size,iter); + + obj.clear(); + for(size_t i=0; i + inline size_t getByteSize(const std::map& obj) + { + return std::accumulate(obj.begin(),obj.end(),sizeof(size_t),[](const size_t& acc,const std::pair& cur) { return acc+getByteSize(cur); }); + } + + template + inline void serialize(const std::map& obj,std::vector& buffer,std::vector::iterator& iter) + { + serialization::serialize(obj.size(),buffer,iter); + for(const auto& cur : obj) + { + serialization::serialize(cur,buffer,iter); + } + } + + template + inline void deserialize(std::map& obj,std::vector::const_iterator& iter) + { + size_t size; + serialization::deserialize(size,iter); + + obj.clear(); + for(size_t i=0; i pair; + serialization::deserialize(pair,iter); + obj.insert(pair); + } + } + + //std::list + + template + inline size_t getByteSize(const std::list& obj) + { + return std::accumulate(obj.begin(), obj.end(), getByteSize(obj.size()), [](const size_t& acc, const T& cur) { return acc + getByteSize(cur); }); + } + + template + inline void serialize(const std::list& obj, std::vector& buffer, std::vector::iterator& iter) + { + serialization::serialize(obj.size(), buffer, iter); + for (const T& cur : obj) + { + serialization::serialize(cur, buffer, iter); + } + } + + template + inline void deserialize(std::list& obj, std::vector::const_iterator& iter) + { + size_t size; + serialization::deserialize(size, iter); + + obj.clear(); + for (size_t i = 0; i < size; ++i) + { + T val; + serialization::deserialize(val, iter); + obj.emplace_back(val); + } + } + + + // Eigen types + template + inline size_t getByteSize(const Eigen::Matrix& obj) + { + // space for numbers of rows,cols and data + return 2*sizeof(typename Eigen::Matrix::Index)+sizeof(T)*obj.rows()*obj.cols(); + } + + template + inline void serialize(const Eigen::Matrix& obj,std::vector& buffer,std::vector::iterator& iter) + { + serialization::serialize(obj.rows(),buffer,iter); + serialization::serialize(obj.cols(),buffer,iter); + size_t size = sizeof(T)*obj.rows()*obj.cols(); + auto ptr = reinterpret_cast(obj.data()); + iter = std::copy(ptr,ptr+size,iter); + } + + template + inline void deserialize(Eigen::Matrix& obj,std::vector::const_iterator& iter) + { + typename Eigen::Matrix::Index rows,cols; + serialization::deserialize(rows,iter); + serialization::deserialize(cols,iter); + size_t size = sizeof(T)*rows*cols; + obj.resize(rows,cols); + auto ptr = reinterpret_cast(obj.data()); + std::copy(iter,iter+size,ptr); + iter+=size; + } + + template + inline size_t getByteSize(const Eigen::SparseMatrix& obj) + { + // space for numbers of rows,cols,nonZeros and tripplets with data (rowIdx,colIdx,value) + size_t size = sizeof(typename Eigen::SparseMatrix::Index); + return 3*size+(sizeof(T)+2*size)*obj.nonZeros(); + } + + template + inline void serialize(const Eigen::SparseMatrix& obj,std::vector& buffer,std::vector::iterator& iter) + { + serialization::serialize(obj.rows(),buffer,iter); + serialization::serialize(obj.cols(),buffer,iter); + serialization::serialize(obj.nonZeros(),buffer,iter); + + for(int k=0;k::InnerIterator it(obj,k);it;++it) + { + serialization::serialize(it.row(),buffer,iter); + serialization::serialize(it.col(),buffer,iter); + serialization::serialize(it.value(),buffer,iter); + } + } + } + + template + inline void deserialize(Eigen::SparseMatrix& obj,std::vector::const_iterator& iter) + { + typename Eigen::SparseMatrix::Index rows,cols,nonZeros; + serialization::deserialize(rows,iter); + serialization::deserialize(cols,iter); + serialization::deserialize(nonZeros,iter); + + obj.resize(rows,cols); + obj.setZero(); + + std::vector > triplets; + for(int i=0;i::Index rowId,colId; + serialization::deserialize(rowId,iter); + serialization::deserialize(colId,iter); + T value; + serialization::deserialize(value,iter); + triplets.push_back(Eigen::Triplet(rowId,colId,value)); + } + obj.setFromTriplets(triplets.begin(),triplets.end()); + } + + template + inline size_t getByteSize(const Eigen::Quaternion& obj) + { + return sizeof(T)*4; + } + + template + inline void serialize(const Eigen::Quaternion& obj,std::vector& buffer,std::vector::iterator& iter) + { + serialization::serialize(obj.w(),buffer,iter); + serialization::serialize(obj.x(),buffer,iter); + serialization::serialize(obj.y(),buffer,iter); + serialization::serialize(obj.z(),buffer,iter); + } + + template + inline void deserialize(Eigen::Quaternion& obj,std::vector::const_iterator& iter) + { + serialization::deserialize(obj.w(),iter); + serialization::deserialize(obj.x(),iter); + serialization::deserialize(obj.y(),iter); + serialization::deserialize(obj.z(),iter); + } + + // pointers + + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj) + { + size_t size = sizeof(bool); + + if(obj) + size += getByteSize(*obj); + + return size; + } + + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter) + { + serialization::serialize(obj == nullptr,buffer,iter); + + if(obj) + serialization::serialize(*obj,buffer,iter); + } + + template + inline typename std::enable_if::value>::type deserialize(T& obj,std::vector::const_iterator& iter) + { + bool isNullPtr; + serialization::deserialize(isNullPtr,iter); + + if(isNullPtr) + { + if(obj) + { + std::cout << "serialization: possible memory leak in serialization for '" << typeid(obj).name() << "'" << std::endl; + obj = nullptr; + } + } + else + { + if(obj) + { + std::cout << "serialization: possible memory corruption in deserialization for '" << typeid(obj).name() << "'" << std::endl; + } + else + { + obj = new typename std::remove_pointer::type(); + } + serialization::deserialize(*obj,iter); + } + } + + // std::shared_ptr and std::unique_ptr + + template + inline typename std::enable_if::value,size_t>::type getByteSize(const T& obj) + { + return getByteSize(obj.get()); + } + + template + inline typename std::enable_if::value>::type serialize(const T& obj,std::vector& buffer,std::vector::iterator& iter) + { + serialize(obj.get(),buffer,iter); + } + + template class T0,typename T1> + inline typename std::enable_if >::value>::type deserialize(T0& obj,std::vector::const_iterator& iter) + { + bool isNullPtr; + serialization::deserialize(isNullPtr,iter); + + if(isNullPtr) + { + obj.reset(); + } + else + { + obj = T0(new T1()); + serialization::deserialize(*obj,iter); + } + } + + // std::weak_ptr + + template + inline size_t getByteSize(const std::weak_ptr& obj) + { + return sizeof(size_t); + } + + template + inline void serialize(const std::weak_ptr& obj,std::vector& buffer,std::vector::iterator& iter) + { + + } + + template + inline void deserialize(std::weak_ptr& obj,std::vector::const_iterator& iter) + { + + } + + // functions to overload for non-intrusive serialization + template + inline void serialize(const T& obj,std::vector& buffer) + { + std::cerr << typeid(obj).name() << " is not serializable: derive from igl::Serializable or spezialize the template function igl::serialization::serialize(const T& obj,std::vector& buffer)" << std::endl; + } + + template + inline void deserialize(T& obj,const std::vector& buffer) + { + std::cerr << typeid(obj).name() << " is not deserializable: derive from igl::Serializable or spezialize the template function igl::serialization::deserialize(T& obj, const std::vector& buffer)" << std::endl; + } + + // helper functions + + template + inline void updateMemoryMap(T& obj,size_t size,std::map& memoryMap) + { + // check if object is already serialized + auto startPtr = new IndexedPointer(); + startPtr->Object = &obj; + auto startBasePtr = static_cast(startPtr); + startBasePtr->Type = IndexedPointerBase::BEGIN; + auto startAddress = reinterpret_cast(&obj); + auto p = std::pair(startAddress,startBasePtr); + + auto el = memoryMap.insert(p); + auto iter = ++el.first; // next elememt + if(el.second && (iter == memoryMap.end() || iter->second->Type != IndexedPointerBase::END)) + { + // not yet serialized + auto endPtr = new IndexedPointer(); + auto endBasePtr = static_cast(endPtr); + endBasePtr->Type = IndexedPointerBase::END; + auto endAddress = reinterpret_cast(&obj) + size - 1; + auto p = std::pair(endAddress,endBasePtr); + + // insert end address + memoryMap.insert(el.first,p); + } + else + { + // already serialized + + // remove inserted address + memoryMap.erase(el.first); + } + } + } +} + +#endif diff --git a/src/igl/setdiff.cpp b/src/igl/setdiff.cpp new file mode 100644 index 0000000000..12404175ad --- /dev/null +++ b/src/igl/setdiff.cpp @@ -0,0 +1,83 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "setdiff.h" +#include "LinSpaced.h" +#include "list_to_matrix.h" +#include "sort.h" +#include "unique.h" + +template < + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedIA> +IGL_INLINE void igl::setdiff( + const Eigen::DenseBase & A, + const Eigen::DenseBase & B, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & IA) +{ + using namespace Eigen; + using namespace std; + // boring base cases + if(A.size() == 0) + { + C.resize(0,1); + IA.resize(0,1); + return; + } + + // Get rid of any duplicates + typedef Matrix VectorA; + typedef Matrix VectorB; + VectorA uA; + VectorB uB; + typedef DerivedIA IAType; + IAType uIA,uIuA,uIB,uIuB; + unique(A,uA,uIA,uIuA); + unique(B,uB,uIB,uIuB); + + // Sort both + VectorA sA; + VectorB sB; + IAType sIA,sIB; + sort(uA,1,true,sA,sIA); + sort(uB,1,true,sB,sIB); + + vector vC; + vector vIA; + int bi = 0; + // loop over sA + bool past = false; + bool sBempty = sB.size()==0; + for(int a = 0;asB(bi)) + { + bi++; + past = bi>=sB.size(); + } + if(sBempty || past || sA(a), Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::setdiff, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::setdiff, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::setdiff, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/setdiff.h b/src/igl/setdiff.h new file mode 100644 index 0000000000..20e8d64857 --- /dev/null +++ b/src/igl/setdiff.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SETDIFF_H +#define IGL_SETDIFF_H +#include "igl_inline.h" +#include +namespace igl +{ + // Set difference of elements of matrices + // + // Inputs: + // A m-long vector of indices + // B n-long vector of indices + // Outputs: + // C (k<=m)-long vector of unique elements appearing in A but not in B + // IA (k<=m)-long list of indices into A so that C = A(IA) + // + template < + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedIA> + IGL_INLINE void setdiff( + const Eigen::DenseBase & A, + const Eigen::DenseBase & B, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & IA); +} + +#ifndef IGL_STATIC_LIBRARY +# include "setdiff.cpp" +#endif +#endif diff --git a/src/igl/setunion.cpp b/src/igl/setunion.cpp new file mode 100644 index 0000000000..eb0e721d8e --- /dev/null +++ b/src/igl/setunion.cpp @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "setunion.h" +#include "unique.h" + +template < + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedIA, + typename DerivedIB> +IGL_INLINE void igl::setunion( + const Eigen::DenseBase & A, + const Eigen::DenseBase & B, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & IA, + Eigen::PlainObjectBase & IB) +{ + DerivedC CS(A.size()+B.size(),1); + { + int k = 0; + for(int j = 0;j, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/setunion.h b/src/igl/setunion.h new file mode 100644 index 0000000000..f28bff653a --- /dev/null +++ b/src/igl/setunion.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SETUNION_H +#define IGL_SETUNION_H +#include "igl_inline.h" +#include +namespace igl +{ + // Union of elements of matrices (like matlab's `union`) + // + // Inputs: + // A m-long vector of indices + // B n-long vector of indices + // Outputs: + // C (k>=m)-long vector of unique elements appearing in A and/or B + // IA (=m)-long list of indices into A so that C = sort([A(IA);B(IB)]) + // IB (=m)-long list of indices into B so that C = sort([A(IA);B(IB)]) + // + template < + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedIA, + typename DerivedIB> + IGL_INLINE void setunion( + const Eigen::DenseBase & A, + const Eigen::DenseBase & B, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & IA, + Eigen::PlainObjectBase & IB); +} + +#ifndef IGL_STATIC_LIBRARY +# include "setunion.cpp" +#endif +#endif + + diff --git a/src/igl/setxor.cpp b/src/igl/setxor.cpp new file mode 100644 index 0000000000..5429abdc20 --- /dev/null +++ b/src/igl/setxor.cpp @@ -0,0 +1,32 @@ +#include "setxor.h" +#include "setdiff.h" +#include "setunion.h" +#include "slice.h" + +template < + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedIA, + typename DerivedIB> +IGL_INLINE void igl::setxor( + const Eigen::DenseBase & A, + const Eigen::DenseBase & B, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & IA, + Eigen::PlainObjectBase & IB) +{ + DerivedC AB,BA; + DerivedIA IAB,IBA; + setdiff(A,B,AB,IAB); + setdiff(B,A,BA,IBA); + setunion(AB,BA,C,IA,IB); + slice(IAB,DerivedIA(IA),IA); + slice(IBA,DerivedIB(IB),IB); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::setxor, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/setxor.h b/src/igl/setxor.h new file mode 100644 index 0000000000..bbd1089fad --- /dev/null +++ b/src/igl/setxor.h @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SETXOR_H +#define IGL_SETXOR_H +#include "igl_inline.h" +#include +namespace igl +{ + // Set xor of elements of matrices + // + // Inputs: + // A m-long vector of indices + // B n-long vector of indices + // Outputs: + // C (k<=m)-long vector of unique elements appearing in A but not in B or + // B but not in A + // IA ( + IGL_INLINE void setxor( + const Eigen::DenseBase & A, + const Eigen::DenseBase & B, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & IA, + Eigen::PlainObjectBase & IB); +} + +#ifndef IGL_STATIC_LIBRARY +# include "setxor.cpp" +#endif +#endif + diff --git a/src/igl/shape_diameter_function.cpp b/src/igl/shape_diameter_function.cpp new file mode 100644 index 0000000000..db061fe394 --- /dev/null +++ b/src/igl/shape_diameter_function.cpp @@ -0,0 +1,182 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "shape_diameter_function.h" +#include "random_dir.h" +#include "barycenter.h" +#include "ray_mesh_intersect.h" +#include "per_vertex_normals.h" +#include "per_face_normals.h" +#include "EPS.h" +#include "Hit.h" +#include "parallel_for.h" +#include +#include +#include + +template < + typename DerivedP, + typename DerivedN, + typename DerivedS > +IGL_INLINE void igl::shape_diameter_function( + const std::function< + double( + const Eigen::Vector3f&, + const Eigen::Vector3f&) + > & shoot_ray, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + using namespace Eigen; + const int n = P.rows(); + // Resize output + S.resize(n,1); + // Embree seems to be parallel when constructing but not when tracing rays + const MatrixXf D = random_dir_stratified(num_samples).cast(); + + const auto & inner = [&P,&N,&num_samples,&D,&S,&shoot_ray](const int p) + { + const Vector3f origin = P.row(p).template cast(); + const Vector3f normal = N.row(p).template cast(); + int num_hits = 0; + double total_distance = 0; + for(int s = 0;s 0) + { + // reverse ray + d *= -1; + } + const double dist = shoot_ray(origin,d); + if(std::isfinite(dist)) + { + total_distance += dist; + num_hits++; + } + } + S(p) = total_distance/(double)num_hits; + }; + parallel_for(n,inner,1000); +} + +template < + typename DerivedV, + int DIM, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > +IGL_INLINE void igl::shape_diameter_function( + const igl::AABB & aabb, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + const auto & shoot_ray = [&aabb,&V,&F]( + const Eigen::Vector3f& _s, + const Eigen::Vector3f& dir)->double + { + Eigen::Vector3f s = _s+1e-4*dir; + igl::Hit hit; + if(aabb.intersect_ray( + V, + F, + s .cast().eval(), + dir.cast().eval(), + hit)) + { + return hit.t; + }else + { + return std::numeric_limits::infinity(); + } + }; + return shape_diameter_function(shoot_ray,P,N,num_samples,S); + +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > +IGL_INLINE void igl::shape_diameter_function( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + if(F.rows() < 100) + { + // Super naive + const auto & shoot_ray = [&V,&F]( + const Eigen::Vector3f& _s, + const Eigen::Vector3f& dir)->double + { + Eigen::Vector3f s = _s+1e-4*dir; + igl::Hit hit; + if(ray_mesh_intersect(s,dir,V,F,hit)) + { + return hit.t; + }else + { + return std::numeric_limits::infinity(); + } + }; + return shape_diameter_function(shoot_ray,P,N,num_samples,S); + } + AABB aabb; + aabb.init(V,F); + return shape_diameter_function(aabb,V,F,P,N,num_samples,S); +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedS> +IGL_INLINE void igl::shape_diameter_function( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const bool per_face, + const int num_samples, + Eigen::PlainObjectBase & S) +{ + if (per_face) + { + DerivedV N; + igl::per_face_normals(V, F, N); + DerivedV P; + igl::barycenter(V, F, P); + return igl::shape_diameter_function(V, F, P, N, num_samples, S); + } + else + { + DerivedV N; + igl::per_vertex_normals(V, F, N); + return igl::shape_diameter_function(V, F, V, N, num_samples, S); + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::shape_diameter_function, Eigen::Matrix, Eigen::Matrix >(std::function const&, Eigen::Matrix const&)> const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::shape_diameter_function, Eigen::Matrix, Eigen::Matrix >(std::function const&, Eigen::Matrix const&)> const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::shape_diameter_function, Eigen::Matrix, Eigen::Matrix >(std::function const&, Eigen::Matrix const&)> const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::shape_diameter_function, Eigen::Matrix, Eigen::Matrix >(std::function const&, Eigen::Matrix const&)> const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::shape_diameter_function, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool, int, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/shape_diameter_function.h b/src/igl/shape_diameter_function.h new file mode 100644 index 0000000000..5460cda7b1 --- /dev/null +++ b/src/igl/shape_diameter_function.h @@ -0,0 +1,95 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SHAPE_DIAMETER_FUNCTION_H +#define IGL_SHAPE_DIAMETER_FUNCTION_H +#include "igl_inline.h" +#include "AABB.h" +#include +#include +namespace igl +{ + // Compute shape diamater function per given point. In the parlence of the + // paper "Consistent Mesh Partitioning and Skeletonisation using the Shape + // Diameter Function" [Shapiro et al. 2008], this implementation uses a 180° + // cone and a _uniform_ average (_not_ a average weighted by inverse angles). + // + // Inputs: + // shoot_ray function handle that outputs hits of a given ray against a + // mesh (embedded in function handles as captured variable/data) + // P #P by 3 list of origin points + // N #P by 3 list of origin normals + // Outputs: + // S #P list of shape diamater function values between bounding box + // diagonal (perfect sphere) and 0 (perfect needle hook) + // + template < + typename DerivedP, + typename DerivedN, + typename DerivedS > + IGL_INLINE void shape_diameter_function( + const std::function< + double( + const Eigen::Vector3f&, + const Eigen::Vector3f&) + > & shoot_ray, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S); + // Inputs: + // AABB axis-aligned bounding box hierarchy around (V,F) + template < + typename DerivedV, + int DIM, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > + IGL_INLINE void shape_diameter_function( + const igl::AABB & aabb, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S); + // Inputs: + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of mesh face indices into V + template < + typename DerivedV, + typename DerivedF, + typename DerivedP, + typename DerivedN, + typename DerivedS > + IGL_INLINE void shape_diameter_function( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & N, + const int num_samples, + Eigen::PlainObjectBase & S); + // per_face whether to compute per face (S is #F by 1) or per vertex (S is + // #V by 1) + template < + typename DerivedV, + typename DerivedF, + typename DerivedS> + IGL_INLINE void shape_diameter_function( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const bool per_face, + const int num_samples, + Eigen::PlainObjectBase & S); +}; +#ifndef IGL_STATIC_LIBRARY +# include "shape_diameter_function.cpp" +#endif + +#endif + diff --git a/src/igl/shapeup.cpp b/src/igl/shapeup.cpp new file mode 100644 index 0000000000..f352d22bfd --- /dev/null +++ b/src/igl/shapeup.cpp @@ -0,0 +1,238 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Amir Vaxman +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace igl +{ + + //This projection does nothing but render points into projP. Mostly used for "echoing" the global step + IGL_INLINE bool shapeup_identity_projection(const Eigen::PlainObjectBase& P, const Eigen::PlainObjectBase& SC, const Eigen::PlainObjectBase& S, Eigen::PlainObjectBase& projP){ + projP.conservativeResize(SC.rows(), 3*SC.maxCoeff()); + for (int i=0;i& P, const Eigen::PlainObjectBase& SC, const Eigen::PlainObjectBase& S, Eigen::PlainObjectBase& projP){ + projP.conservativeResize(SC.rows(), 3*SC.maxCoeff()); + for (int currRow=0;currRow svd(corrMat, Eigen::ComputeFullU | Eigen::ComputeFullV); + Eigen::MatrixXd R=svd.matrixU()*svd.matrixV().transpose(); + //getting scale by edge length change average. TODO: by singular values + Eigen::VectorXd sourceEdgeLengths(N); + Eigen::VectorXd targetEdgeLengths(N); + for (int j=0;j + IGL_INLINE bool shapeup_precomputation(const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& SC, + const Eigen::PlainObjectBase& S, + const Eigen::PlainObjectBase& E, + const Eigen::PlainObjectBase& b, + const Eigen::PlainObjectBase& wShape, + const Eigen::PlainObjectBase& wSmooth, + ShapeupData & sudata) + { + using namespace std; + using namespace Eigen; + sudata.P=P; + sudata.SC=SC; + sudata.S=S; + sudata.b=b; + typedef typename DerivedP::Scalar Scalar; + + //checking for consistency of the input + assert(SC.rows()==S.rows()); + assert(SC.rows()==wShape.rows()); + assert(E.rows()==wSmooth.rows()); + assert(b.rows()!=0); //would lead to matrix becoming SPD + + sudata.DShape.conservativeResize(SC.sum(), P.rows()); //Shape matrix (integration); + sudata.DClose.conservativeResize(b.rows(), P.rows()); //Closeness matrix for positional constraints + sudata.DSmooth.conservativeResize(E.rows(), P.rows()); //smoothness matrix + + //Building shape matrix + std::vector > DShapeTriplets; + int currRow=0; + for (int i=0;i(currRow+j, S(i,k), (1.0-avgCoeff))); + else + DShapeTriplets.push_back(Triplet(currRow+j, S(i,k), (-avgCoeff))); + } + } + currRow+=SC(i); + + } + + sudata.DShape.setFromTriplets(DShapeTriplets.begin(), DShapeTriplets.end()); + + //Building closeness matrix + std::vector > DCloseTriplets; + for (int i=0;i(i,b(i), 1.0)); + + sudata.DClose.setFromTriplets(DCloseTriplets.begin(), DCloseTriplets.end()); + + //Building smoothness matrix + std::vector > DSmoothTriplets; + for (int i=0; i(i, E(i, 0), -1)); + DSmoothTriplets.push_back(Triplet(i, E(i, 1), 1)); + } + + SparseMatrix tempMat; + igl::cat(1, sudata.DShape, sudata.DClose, tempMat); + igl::cat(1, tempMat, sudata.DSmooth, sudata.A); + + //weight matrix + vector > WTriplets; + + //one weight per set in S. + currRow=0; + for (int i=0;i(currRow+j,currRow+j,sudata.shapeCoeff*wShape(i))); + currRow+=SC(i); + } + + for (int i=0;i(SC.sum()+i, SC.sum()+i, sudata.closeCoeff)); + + for (int i=0;i(SC.sum()+b.size()+i, SC.sum()+b.size()+i, sudata.smoothCoeff*wSmooth(i))); + + sudata.W.conservativeResize(SC.sum()+b.size()+E.rows(), SC.sum()+b.size()+E.rows()); + sudata.W.setFromTriplets(WTriplets.begin(), WTriplets.end()); + + sudata.At=sudata.A.transpose(); //for efficieny, as we use the transpose a lot in the iteration + sudata.Q=sudata.At*sudata.W*sudata.A; + + return min_quad_with_fixed_precompute(sudata.Q,VectorXi(),SparseMatrix(),true,sudata.solver_data); + } + + + template < + typename DerivedP, + typename DerivedSC, + typename DerivedS> + IGL_INLINE bool shapeup_solve(const Eigen::PlainObjectBase& bc, + const std::function&, const Eigen::PlainObjectBase&, const Eigen::PlainObjectBase&, Eigen::PlainObjectBase&)>& local_projection, + const Eigen::PlainObjectBase& P0, + const ShapeupData & sudata, + const bool quietIterations, + Eigen::PlainObjectBase& P) + { + using namespace Eigen; + using namespace std; + MatrixXd currP=P0; + MatrixXd prevP=P0; + MatrixXd projP; + + assert(bc.rows()==sudata.b.rows()); + + MatrixXd rhs(sudata.A.rows(), 3); rhs.setZero(); + rhs.block(sudata.DShape.rows(), 0, sudata.b.rows(),3)=bc; //this stays constant throughout the iterations + + if (!quietIterations){ + cout<<"Shapeup Iterations, "<(); + if (!quietIterations) + cout << "Iteration "<, typename Eigen::Matrix, typename Eigen::Matrix, typename Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, igl::ShapeupData&); + +template bool igl::shapeup_solve, typename Eigen::Matrix, typename Eigen::Matrix >(const Eigen::PlainObjectBase >& bc, const std::function >&, const Eigen::PlainObjectBase >&, const Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >& ) >& local_projection, const Eigen::PlainObjectBase >& P0, const igl::ShapeupData & sudata, const bool quietIterations, Eigen::PlainObjectBase >& P); +#endif diff --git a/src/igl/shapeup.h b/src/igl/shapeup.h new file mode 100644 index 0000000000..7230a8dc7c --- /dev/null +++ b/src/igl/shapeup.h @@ -0,0 +1,128 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Amir Vaxman +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SHAPEUP_H +#define IGL_SHAPEUP_H + +#include +#include +#include +#include +#include +#include +#include + + +//This file implements the following algorithm: + +//Boaziz et al. +//Shape-Up: Shaping Discrete Geometry with Projections +//Computer Graphics Forum (Proc. SGP) 31(5), 2012 + +namespace igl +{ + struct ShapeupData{ + //input data + Eigen::MatrixXd P; + Eigen::VectorXi SC; + Eigen::MatrixXi S; + Eigen::VectorXi b; + int maxIterations; //referring to number of local-global pairs. + double pTolerance; //algorithm stops when max(|P_k-P_{k-1}|) DShape, DClose, DSmooth, Q, A, At, W; + + min_quad_with_fixed_data solver_data; + + ShapeupData(): + maxIterations(50), + pTolerance(10e-6), + shapeCoeff(1.0), + closeCoeff(100.0), + smoothCoeff(0.0){} + }; + + //Every function here defines a local projection for ShapeUp, and must have the following structure to qualify: + //Input: + // P #P by 3 the set of points, either the initial solution, or from previous iteration. + // SC #Set by 1 cardinalities of sets in S + // S #Sets by max(SC) independent sets where the local projection applies. Values beyond column SC(i)-1 in row S(i,:) are "don't care" + //Output: + // projP #S by 3*max(SC) in format xyzxyzxyz, where the projected points correspond to each set in S in the same order. + typedef std::function&, const Eigen::PlainObjectBase&, const Eigen::PlainObjectBase&, Eigen::PlainObjectBase&)> shapeup_projection_function; + + + //This projection does nothing but render points into projP. Mostly used for "echoing" the global step + IGL_INLINE bool shapeup_identity_projection(const Eigen::PlainObjectBase& P, const Eigen::PlainObjectBase& SC, const Eigen::PlainObjectBase& S, Eigen::PlainObjectBase& projP); + + //the projection assumes that the sets are vertices of polygons in cyclic order + IGL_INLINE bool shapeup_regular_face_projection(const Eigen::PlainObjectBase& P, const Eigen::PlainObjectBase& SC, const Eigen::PlainObjectBase& S, Eigen::PlainObjectBase& projP); + + + //This function precomputation the necessary matrices for the ShapeUp process, and prefactorizes them. + + //input: + // P #P by 3 point positions + // SC #Set by 1 cardinalities of sets in S + // S #Sets by max(SC) independent sets where the local projection applies. Values beyond column SC(i)-1 in row S(i,:) are "don't care" + // E #E by 2 the "edges" of the set P; used for the smoothness energy. + // b #b by 1 boundary (fixed) vertices from P. + // wShape, #Set by 1 + // wSmooth #b by 1 weights for constraints from S and positional constraints (used in the global step) + + // Output: + // sudata struct ShapeupData the data necessary to solve the system in shapeup_solve + + template < + typename DerivedP, + typename DerivedSC, + typename DerivedS, + typename Derivedw> + IGL_INLINE bool shapeup_precomputation(const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& SC, + const Eigen::PlainObjectBase& S, + const Eigen::PlainObjectBase& E, + const Eigen::PlainObjectBase& b, + const Eigen::PlainObjectBase& wShape, + const Eigen::PlainObjectBase& wSmooth, + ShapeupData & sudata); + + + + //This function solve the shapeup project optimization. shapeup_precompute must be called before with the same sudata, or results are unpredictable + + //Input: + //bc #b by 3 fixed point values corresonding to "b" in sudata + //local_projection function pointer taking (P,SC,S,projP), + // where the first three parameters are as defined, and "projP" is the output, as a #S by 3*max(SC) function in format xyzxyzxyz, and where it returns the projected points corresponding to each set in S in the same order. + //NOTE: the input values in P0 don't need to correspond to prescribed values in bc; the iterations will project them automatically (by design). + //P0 #P by 3 initial solution (point positions) + //sudata the ShapeUpData structure computed in shapeup_precomputation() + //quietIterations flagging if to output iteration information. + + //Output: + //P the solution to the problem, indices corresponding to P0. + template < + typename DerivedP, + typename DerivedSC, + typename DerivedS> + IGL_INLINE bool shapeup_solve(const Eigen::PlainObjectBase& bc, + const std::function&, const Eigen::PlainObjectBase&, const Eigen::PlainObjectBase&, Eigen::PlainObjectBase&)>& local_projection, + const Eigen::PlainObjectBase& P0, + const ShapeupData & sudata, + const bool quietIterations, + Eigen::PlainObjectBase& P); + +} + +#ifndef IGL_STATIC_LIBRARY +#include "shapeup.cpp" +#endif + +#endif diff --git a/src/igl/shortest_edge_and_midpoint.cpp b/src/igl/shortest_edge_and_midpoint.cpp new file mode 100644 index 0000000000..db5338d0d7 --- /dev/null +++ b/src/igl/shortest_edge_and_midpoint.cpp @@ -0,0 +1,23 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "shortest_edge_and_midpoint.h" + +IGL_INLINE void igl::shortest_edge_and_midpoint( + const int e, + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & /*F*/, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & /*EMAP*/, + const Eigen::MatrixXi & /*EF*/, + const Eigen::MatrixXi & /*EI*/, + double & cost, + Eigen::RowVectorXd & p) +{ + cost = (V.row(E(e,0))-V.row(E(e,1))).norm(); + p = 0.5*(V.row(E(e,0))+V.row(E(e,1))); +} diff --git a/src/igl/shortest_edge_and_midpoint.h b/src/igl/shortest_edge_and_midpoint.h new file mode 100644 index 0000000000..45a4a2abfe --- /dev/null +++ b/src/igl/shortest_edge_and_midpoint.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SHORTEST_EDGE_AND_MIDPOINT_H +#define IGL_SHORTEST_EDGE_AND_MIDPOINT_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Cost and placement function compatible with igl::decimate. The cost of + // collapsing an edge is its length (prefer to collapse short edges) and the + // placement strategy for the new vertex is the midpoint of the collapsed + // edge. + // + // Inputs: + // e index into E of edge to be considered for collapse + // V #V by dim list of vertex positions + // F #F by 3 list of faces (ignored) + // E #E by 2 list of edge indices into V + // EMAP #F*3 list of half-edges indices into E (ignored) + // EF #E by 2 list of edge-face flaps into F (ignored) + // EI #E by 2 list of edge-face opposite corners (ignored) + // Outputs: + // cost set to edge length + // p placed point set to edge midpoint + IGL_INLINE void shortest_edge_and_midpoint( + const int e, + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & /*F*/, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & /*EMAP*/, + const Eigen::MatrixXi & /*EF*/, + const Eigen::MatrixXi & /*EI*/, + double & cost, + Eigen::RowVectorXd & p); +} + +#ifndef IGL_STATIC_LIBRARY +# include "shortest_edge_and_midpoint.cpp" +#endif +#endif + + diff --git a/src/igl/signed_angle.cpp b/src/igl/signed_angle.cpp new file mode 100644 index 0000000000..69f8aa6f5f --- /dev/null +++ b/src/igl/signed_angle.cpp @@ -0,0 +1,74 @@ +#include "signed_angle.h" +#include "PI.h" +#include + +template < + typename DerivedA, + typename DerivedB, + typename DerivedP> +IGL_INLINE typename DerivedA::Scalar igl::signed_angle( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & P) +{ + typedef typename DerivedA::Scalar SType; + // Gather vectors to source and destination + SType o2A[2]; + SType o2B[2]; + // and lengths + SType o2Al = 0; + SType o2Bl = 0; + for(int i = 0;i<2;i++) + { + o2A[i] = P(i) - A(i); + o2B[i] = P(i) - B(i); + o2Al += o2A[i]*o2A[i]; + o2Bl += o2B[i]*o2B[i]; + } + o2Al = sqrt(o2Al); + o2Bl = sqrt(o2Bl); + // Normalize + for(int i = 0;i<2;i++) + { + // Matlab crashes on NaN + if(o2Al!=0) + { + o2A[i] /= o2Al; + } + if(o2Bl!=0) + { + o2B[i] /= o2Bl; + } + } + return + -atan2(o2B[0]*o2A[1]-o2B[1]*o2A[0],o2B[0]*o2A[0]+o2B[1]*o2A[1])/ + (2.*igl::PI); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template Eigen::Block const, 1, 3, true>::Scalar igl::signed_angle const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, 3, true>::Scalar igl::signed_angle const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, -1, false>::Scalar igl::signed_angle const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, 3, true>::Scalar igl::signed_angle const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, 3, false>::Scalar igl::signed_angle const, 1, 3, false>, Eigen::Block const, 1, 3, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, false> > const&, Eigen::MatrixBase const, 1, 3, false> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, -1, false>::Scalar igl::signed_angle const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false> >(Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, -1, false>::Scalar igl::signed_angle const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, -1, false>::Scalar igl::signed_angle const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, -1, false>::Scalar igl::signed_angle const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase > const&); +template Eigen::Block const, 1, 3, false>::Scalar igl::signed_angle const, 1, 3, false>, Eigen::Block const, 1, 3, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, false> > const&, Eigen::MatrixBase const, 1, 3, false> > const&, Eigen::MatrixBase > const&); +template Eigen::Block const, 1, 3, true>::Scalar igl::signed_angle const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase > const&); +#ifdef WIN32 +template float igl::signed_angle const ,1,3,1>,class Eigen::Block const ,1,3,1>,class Eigen::Matrix >(class Eigen::MatrixBase const ,1,3,1> > const &,class Eigen::MatrixBase const ,1,3,1> > const &,class Eigen::MatrixBase > const &); +template float igl::signed_angle const ,1,3,0>,class Eigen::Block const ,1,3,0>,class Eigen::Matrix >(class Eigen::MatrixBase const ,1,3,0> > const &,class Eigen::MatrixBase const ,1,3,0> > const &,class Eigen::MatrixBase > const &); +#endif +#endif diff --git a/src/igl/signed_angle.h b/src/igl/signed_angle.h new file mode 100644 index 0000000000..547ba00a42 --- /dev/null +++ b/src/igl/signed_angle.h @@ -0,0 +1,27 @@ +#ifndef IGL_SIGNED_ANGLE_H +#define IGL_SIGNED_ANGLE_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute the signed angle subtended by the oriented 3d triangle (A,B,C) at some point P + // + // Inputs: + // A 2D position of corner + // B 2D position of corner + // P 2D position of query point + // returns signed angle + template < + typename DerivedA, + typename DerivedB, + typename DerivedP> + IGL_INLINE typename DerivedA::Scalar signed_angle( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & P); +} +#ifndef IGL_STATIC_LIBRARY +# include "signed_angle.cpp" +#endif +#endif + diff --git a/src/igl/signed_distance.cpp b/src/igl/signed_distance.cpp new file mode 100644 index 0000000000..f638b1f588 --- /dev/null +++ b/src/igl/signed_distance.cpp @@ -0,0 +1,463 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "signed_distance.h" +#include "get_seconds.h" +#include "per_edge_normals.h" +#include "parallel_for.h" +#include "per_face_normals.h" +#include "per_vertex_normals.h" +#include "point_mesh_squared_distance.h" +#include "pseudonormal_test.h" + + +template < + typename DerivedP, + typename DerivedV, + typename DerivedF, + typename DerivedS, + typename DerivedI, + typename DerivedC, + typename DerivedN> +IGL_INLINE void igl::signed_distance( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const SignedDistanceType sign_type, + const typename DerivedV::Scalar lower_bound, + const typename DerivedV::Scalar upper_bound, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & N) +{ + using namespace Eigen; + using namespace std; + const int dim = V.cols(); + assert((V.cols() == 3||V.cols() == 2) && "V should have 3d or 2d positions"); + assert((P.cols() == 3||P.cols() == 2) && "P should have 3d or 2d positions"); + assert(V.cols() == P.cols() && "V should have same dimension as P"); + // Only unsigned distance is supported for non-triangles + if(sign_type != SIGNED_DISTANCE_TYPE_UNSIGNED) + { + assert(F.cols() == dim && "F should have co-dimension 0 simplices"); + } + typedef Eigen::Matrix RowVector3S; + + // Prepare distance computation + AABB tree3; + AABB tree2; + switch(dim) + { + default: + case 3: + tree3.init(V,F); + break; + case 2: + tree2.init(V,F); + break; + } + + Eigen::Matrix FN,VN,EN; + Eigen::Matrix E; + Eigen::Matrix EMAP; + WindingNumberAABB hier3; + switch(sign_type) + { + default: + assert(false && "Unknown SignedDistanceType"); + case SIGNED_DISTANCE_TYPE_UNSIGNED: + // do nothing + break; + case SIGNED_DISTANCE_TYPE_DEFAULT: + case SIGNED_DISTANCE_TYPE_WINDING_NUMBER: + switch(dim) + { + default: + case 3: + hier3.set_mesh(V,F); + hier3.grow(); + break; + case 2: + // no precomp, no hierarchy + break; + } + break; + case SIGNED_DISTANCE_TYPE_PSEUDONORMAL: + switch(dim) + { + default: + case 3: + // "Signed Distance Computation Using the Angle Weighted Pseudonormal" + // [Bærentzen & Aanæs 2005] + per_face_normals(V,F,FN); + per_vertex_normals(V,F,PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE,FN,VN); + per_edge_normals( + V,F,PER_EDGE_NORMALS_WEIGHTING_TYPE_UNIFORM,FN,EN,E,EMAP); + break; + case 2: + FN.resize(F.rows(),2); + VN = DerivedV::Zero(V.rows(),2); + for(int e = 0;e q2; + switch(P.cols()) + { + default: + case 3: + q3.head(P.row(p).size()) = P.row(p); + break; + case 2: + q2 = P.row(p).head(2); + break; + } + typename DerivedV::Scalar s=1,sqrd=0; + Eigen::Matrix c; + RowVector3S c3; + Eigen::Matrix c2; + int i=-1; + // in all cases compute squared unsiged distances + sqrd = dim==3? + tree3.squared_distance(V,F,q3,low_sqr_d,up_sqr_d,i,c3): + tree2.squared_distance(V,F,q2,low_sqr_d,up_sqr_d,i,c2); + if(sqrd >= up_sqr_d || sqrd <= low_sqr_d) + { + // Out of bounds gets a nan (nans on grids can be flood filled later using + // igl::flood_fill) + S(p) = std::numeric_limits::quiet_NaN(); + I(p) = F.rows()+1; + C.row(p).setConstant(0); + }else + { + // Determine sign + switch(sign_type) + { + default: + assert(false && "Unknown SignedDistanceType"); + case SIGNED_DISTANCE_TYPE_UNSIGNED: + break; + case SIGNED_DISTANCE_TYPE_DEFAULT: + case SIGNED_DISTANCE_TYPE_WINDING_NUMBER: + { + Scalar w = 0; + if(dim == 3) + { + s = 1.-2.*hier3.winding_number(q3.transpose()); + }else + { + assert(!V.derived().IsRowMajor); + assert(!F.derived().IsRowMajor); + s = 1.-2.*winding_number(V,F,q2); + } + break; + } + case SIGNED_DISTANCE_TYPE_PSEUDONORMAL: + { + RowVector3S n3; + Eigen::Matrix n2; + dim==3 ? + pseudonormal_test(V,F,FN,VN,EN,EMAP,q3,i,c3,s,n3): + pseudonormal_test(V,E,EN,VN,q2,i,c2,s,n2); + Eigen::Matrix n; + (dim==3 ? n = n3 : n = n2); + N.row(p) = n; + break; + } + } + I(p) = i; + S(p) = s*sqrt(sqrd); + C.row(p) = (dim==3 ? c=c3 : c=c2); + } + } + ,10000); +} + +template < + typename DerivedP, + typename DerivedV, + typename DerivedF, + typename DerivedS, + typename DerivedI, + typename DerivedC, + typename DerivedN> +IGL_INLINE void igl::signed_distance( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const SignedDistanceType sign_type, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & N) +{ + typedef typename DerivedV::Scalar Scalar; + Scalar lower = std::numeric_limits::min(); + Scalar upper = std::numeric_limits::max(); + return signed_distance(P,V,F,sign_type,lower,upper,S,I,C,N); +} + + +template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedVN, + typename DerivedEN, + typename DerivedEMAP, + typename Derivedq> +IGL_INLINE typename DerivedV::Scalar igl::signed_distance_pseudonormal( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & FN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & EMAP, + const Eigen::MatrixBase & q) +{ + typename DerivedV::Scalar s,sqrd; + Eigen::Matrix n,c; + int i = -1; + signed_distance_pseudonormal(tree,V,F,FN,VN,EN,EMAP,q,s,sqrd,i,c,n); + return s*sqrt(sqrd); +} + +template < + typename DerivedP, + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedVN, + typename DerivedEN, + typename DerivedEMAP, + typename DerivedS, + typename DerivedI, + typename DerivedC, + typename DerivedN> +IGL_INLINE void igl::signed_distance_pseudonormal( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const AABB & tree, + const Eigen::MatrixBase & FN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & EMAP, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & N) +{ + using namespace Eigen; + const size_t np = P.rows(); + S.resize(np,1); + I.resize(np,1); + N.resize(np,3); + C.resize(np,3); + typedef typename AABB::RowVectorDIMS RowVector3S; +# pragma omp parallel for if(np>1000) + for(size_t p = 0;p +IGL_INLINE void igl::signed_distance_pseudonormal( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & FN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & EMAP, + const Eigen::MatrixBase & q, + Scalar & s, + Scalar & sqrd, + int & i, + Eigen::PlainObjectBase & c, + Eigen::PlainObjectBase & n) +{ + using namespace Eigen; + using namespace std; + //typedef Eigen::Matrix RowVector3S; + // Alec: Why was this constructor around q necessary? + //sqrd = tree.squared_distance(V,F,RowVector3S(q),i,(RowVector3S&)c); + // Alec: Why was this constructor around c necessary? + //sqrd = tree.squared_distance(V,F,q,i,(RowVector3S&)c); + sqrd = tree.squared_distance(V,F,q,i,c); + pseudonormal_test(V,F,FN,VN,EN,EMAP,q,i,c,s,n); +} + +template < + typename DerivedV, + typename DerivedE, + typename DerivedEN, + typename DerivedVN, + typename Derivedq, + typename Scalar, + typename Derivedc, + typename Derivedn> +IGL_INLINE void igl::signed_distance_pseudonormal( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & q, + Scalar & s, + Scalar & sqrd, + int & i, + Eigen::PlainObjectBase & c, + Eigen::PlainObjectBase & n) +{ + using namespace Eigen; + using namespace std; + typedef Eigen::Matrix RowVector2S; + sqrd = tree.squared_distance(V,E,RowVector2S(q),i,(RowVector2S&)c); + pseudonormal_test(V,E,EN,VN,q,i,c,s,n); +} + +template < + typename DerivedV, + typename DerivedF, + typename Derivedq> +IGL_INLINE typename DerivedV::Scalar igl::signed_distance_winding_number( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const igl::WindingNumberAABB & hier, + const Eigen::MatrixBase & q) +{ + typedef typename DerivedV::Scalar Scalar; + Scalar s,sqrd; + Eigen::Matrix c; + int i=-1; + signed_distance_winding_number(tree,V,F,hier,q,s,sqrd,i,c); + return s*sqrt(sqrd); +} + + +template < + typename DerivedV, + typename DerivedF, + typename Derivedq, + typename Scalar, + typename Derivedc> +IGL_INLINE void igl::signed_distance_winding_number( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const igl::WindingNumberAABB & hier, + const Eigen::MatrixBase & q, + Scalar & s, + Scalar & sqrd, + int & i, + Eigen::PlainObjectBase & c) +{ + using namespace Eigen; + using namespace std; + typedef Eigen::Matrix RowVector3S; + sqrd = tree.squared_distance(V,F,RowVector3S(q),i,(RowVector3S&)c); + const Scalar w = hier.winding_number(q.transpose()); + s = 1.-2.*w; +} + +template < + typename DerivedV, + typename DerivedF, + typename Derivedq, + typename Scalar, + typename Derivedc> +IGL_INLINE void igl::signed_distance_winding_number( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & q, + Scalar & s, + Scalar & sqrd, + int & i, + Eigen::PlainObjectBase & c) +{ + using namespace Eigen; + using namespace std; + typedef Eigen::Matrix RowVector2S; + sqrd = tree.squared_distance(V,F,RowVector2S(q),i,(RowVector2S&)c); + Scalar w; + // TODO: using .data() like this is very dangerous... This is assuming + // colmajor order + assert(!V.derived().IsRowMajor); + assert(!F.derived().IsRowMajor); + s = 1.-2.*winding_number(V,F,q); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::signed_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::SignedDistanceType, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::signed_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::SignedDistanceType, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::signed_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::SignedDistanceType, Eigen::Matrix::Scalar, Eigen::Matrix::Scalar, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::signed_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::SignedDistanceType, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::signed_distance_pseudonormal, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix, Eigen::Matrix >(igl::AABB, 3> const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double&, double&, int&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::signed_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::SignedDistanceType, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template Eigen::Matrix::Scalar igl::signed_distance_pseudonormal, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(igl::AABB, 3> const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +template void igl::signed_distance_pseudonormal, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::AABB, 3> const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::signed_distance, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::SignedDistanceType, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::signed_distance_winding_number, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(igl::AABB, 3> const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::WindingNumberAABB, Eigen::Matrix, Eigen::Matrix > const&, Eigen::MatrixBase > const&, double&, double&, int&, Eigen::PlainObjectBase >&); +template Eigen::Matrix::Scalar igl::signed_distance_winding_number, Eigen::Matrix, Eigen::Matrix >(igl::AABB, 3> const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::WindingNumberAABB, Eigen::Matrix, Eigen::Matrix > const&, Eigen::MatrixBase > const&); +#endif diff --git a/src/igl/signed_distance.h b/src/igl/signed_distance.h new file mode 100644 index 0000000000..3b178d304a --- /dev/null +++ b/src/igl/signed_distance.h @@ -0,0 +1,245 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SIGNED_DISTANCE_H +#define IGL_SIGNED_DISTANCE_H + +#include "igl_inline.h" +#include "AABB.h" +#include "WindingNumberAABB.h" +#include +#include +namespace igl +{ + enum SignedDistanceType + { + // Use fast pseudo-normal test [Bærentzen & Aanæs 2005] + SIGNED_DISTANCE_TYPE_PSEUDONORMAL = 0, + SIGNED_DISTANCE_TYPE_WINDING_NUMBER = 1, + SIGNED_DISTANCE_TYPE_DEFAULT = 2, + SIGNED_DISTANCE_TYPE_UNSIGNED = 3, + NUM_SIGNED_DISTANCE_TYPE = 4 + }; + // Computes signed distance to a mesh + // + // Inputs: + // P #P by 3 list of query point positions + // V #V by 3 list of vertex positions + // F #F by ss list of triangle indices, ss should be 3 unless sign_type == + // SIGNED_DISTANCE_TYPE_UNSIGNED + // sign_type method for computing distance _sign_ S + // lower_bound lower bound of distances needed {std::numeric_limits::min} + // upper_bound lower bound of distances needed {std::numeric_limits::max} + // Outputs: + // S #P list of smallest signed distances + // I #P list of facet indices corresponding to smallest distances + // C #P by 3 list of closest points + // N #P by 3 list of closest normals (only set if + // sign_type=SIGNED_DISTANCE_TYPE_PSEUDONORMAL) + // + // Known bugs: This only computes distances to triangles. So unreferenced + // vertices and degenerate triangles are ignored. + template < + typename DerivedP, + typename DerivedV, + typename DerivedF, + typename DerivedS, + typename DerivedI, + typename DerivedC, + typename DerivedN> + IGL_INLINE void signed_distance( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const SignedDistanceType sign_type, + const typename DerivedV::Scalar lower_bound, + const typename DerivedV::Scalar upper_bound, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & N); + // Default bounds + template < + typename DerivedP, + typename DerivedV, + typename DerivedF, + typename DerivedS, + typename DerivedI, + typename DerivedC, + typename DerivedN> + IGL_INLINE void signed_distance( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const SignedDistanceType sign_type, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & N); + // Computes signed distance to mesh + // + // Inputs: + // tree AABB acceleration tree (see AABB.h) + // F #F by 3 list of triangle indices + // FN #F by 3 list of triangle normals + // VN #V by 3 list of vertex normals (ANGLE WEIGHTING) + // EN #E by 3 list of edge normals (UNIFORM WEIGHTING) + // EMAP #F*3 mapping edges in F to E + // q Query point + // Returns signed distance to mesh + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedVN, + typename DerivedEN, + typename DerivedEMAP, + typename Derivedq> + IGL_INLINE typename DerivedV::Scalar signed_distance_pseudonormal( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & FN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & EMAP, + const Eigen::MatrixBase & q); + template < + typename DerivedP, + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedVN, + typename DerivedEN, + typename DerivedEMAP, + typename DerivedS, + typename DerivedI, + typename DerivedC, + typename DerivedN> + IGL_INLINE void signed_distance_pseudonormal( + const Eigen::MatrixBase & P, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const AABB & tree, + const Eigen::MatrixBase & FN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & EMAP, + Eigen::PlainObjectBase & S, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & N); + // Outputs: + // s sign + // sqrd squared distance + // i closest primitive + // c closest point + // n normal + template < + typename DerivedV, + typename DerivedF, + typename DerivedFN, + typename DerivedVN, + typename DerivedEN, + typename DerivedEMAP, + typename Derivedq, + typename Scalar, + typename Derivedc, + typename Derivedn> + IGL_INLINE void signed_distance_pseudonormal( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & FN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & EMAP, + const Eigen::MatrixBase & q, + Scalar & s, + Scalar & sqrd, + int & i, + Eigen::PlainObjectBase & c, + Eigen::PlainObjectBase & n); + template < + typename DerivedV, + typename DerivedE, + typename DerivedEN, + typename DerivedVN, + typename Derivedq, + typename Scalar, + typename Derivedc, + typename Derivedn> + IGL_INLINE void signed_distance_pseudonormal( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & EN, + const Eigen::MatrixBase & VN, + const Eigen::MatrixBase & q, + Scalar & s, + Scalar & sqrd, + int & i, + Eigen::PlainObjectBase & c, + Eigen::PlainObjectBase & n); + // Inputs: + // tree AABB acceleration tree (see cgal/point_mesh_squared_distance.h) + // hier Winding number evaluation hierarchy + // q Query point + // Returns signed distance to mesh + template < + typename DerivedV, + typename DerivedF, + typename Derivedq> + IGL_INLINE typename DerivedV::Scalar signed_distance_winding_number( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const igl::WindingNumberAABB & hier, + const Eigen::MatrixBase & q); + // Outputs: + // s sign + // sqrd squared distance + // pp closest point and primitve + template < + typename DerivedV, + typename DerivedF, + typename Derivedq, + typename Scalar, + typename Derivedc> + IGL_INLINE void signed_distance_winding_number( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const igl::WindingNumberAABB & hier, + const Eigen::MatrixBase & q, + Scalar & s, + Scalar & sqrd, + int & i, + Eigen::PlainObjectBase & c); + template < + typename DerivedV, + typename DerivedF, + typename Derivedq, + typename Scalar, + typename Derivedc> + IGL_INLINE void signed_distance_winding_number( + const AABB & tree, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & q, + Scalar & s, + Scalar & sqrd, + int & i, + Eigen::PlainObjectBase & c); +} + +#ifndef IGL_STATIC_LIBRARY +# include "signed_distance.cpp" +#endif + +#endif diff --git a/src/igl/simplify_polyhedron.cpp b/src/igl/simplify_polyhedron.cpp new file mode 100644 index 0000000000..38ba118e7a --- /dev/null +++ b/src/igl/simplify_polyhedron.cpp @@ -0,0 +1,107 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "simplify_polyhedron.h" +#include "decimate.h" +#include "circulation.h" +#include "per_face_normals.h" +#include "infinite_cost_stopping_condition.h" +#include + +IGL_INLINE void igl::simplify_polyhedron( + const Eigen::MatrixXd & OV, + const Eigen::MatrixXi & OF, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::VectorXi & J) +{ + // TODO: to generalize to open meshes, 0-cost should keep all incident + // boundary edges on their original lines. (for non-manifold meshes, + // igl::decimate needs to be generalized) + + Eigen::MatrixXd N; + // Function for computing cost of collapsing edge (0 if at least one + // direction doesn't change pointset, inf otherwise) and placement (in lowest + // cost direction). + const auto & perfect= [&N]( + const int e, + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI, + double & cost, + Eigen::RowVectorXd & p) + { + // Function for ocmputing cost (0 or inf) of collapsing edge by placing + // vertex at `positive` end of edge. + const auto & perfect_directed = [&N]( + const int e, + const bool positive, + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXi & E, + const Eigen::VectorXi & EMAP, + const Eigen::MatrixXi & EF, + const Eigen::MatrixXi & EI, + double & cost, + Eigen::RowVectorXd & p) + { + const auto vi = E(e,positive); + const auto vj = E(e,!positive); + p = V.row(vj); + std::vector faces = igl::circulation(e,positive,F,E,EMAP,EF,EI); + cost = 0; + for(auto f : faces) + { + // Skip the faces being collapsed + if(f == EF(e,0) || f == EF(e,1)) + { + continue; + } + const Eigen::RowVectorXd nbefore = N.row(f); + // Face with vi replaced with vj + const Eigen::RowVector3i fafter( + F(f,0) == vi ? vj : F(f,0), + F(f,1) == vi ? vj : F(f,1), + F(f,2) == vi ? vj : F(f,2)); + Eigen::RowVectorXd nafter; + igl::per_face_normals(V,fafter,nafter); + const double epsilon = 1e-10; + // if normal changed then not feasible, break + if((nbefore-nafter).norm() > epsilon) + { + cost = std::numeric_limits::infinity(); + break; + } + } + }; + p.resize(3); + double cost0, cost1; + Eigen::RowVectorXd p0, p1; + perfect_directed(e,false,V,F,E,EMAP,EF,EI,cost0,p0); + perfect_directed(e,true,V,F,E,EMAP,EF,EI,cost1,p1); + if(cost0 < cost1) + { + cost = cost0; + p = p0; + }else + { + cost = cost1; + p = p1; + } + }; + igl::per_face_normals(OV,OF,N); + Eigen::VectorXi I; + igl::decimate( + OV,OF, + perfect, + igl::infinite_cost_stopping_condition(perfect), + V,F,J,I); +} + diff --git a/src/igl/simplify_polyhedron.h b/src/igl/simplify_polyhedron.h new file mode 100644 index 0000000000..5b50181926 --- /dev/null +++ b/src/igl/simplify_polyhedron.h @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SIMPLIFY_POLYHEDRON_H +#define IGL_SIMPLIFY_POLYHEDRON_H +#include "igl_inline.h" +#include +namespace igl +{ + // Simplify a polyhedron represented as a triangle mesh (OV,OF) by collapsing + // any edge that doesn't contribute to defining surface's pointset. This + // _would_ also make sense for open and non-manifold meshes, but the current + // implementation only works with closed manifold surfaces with well defined + // triangle normals. + // + // Inputs: + // OV #OV by 3 list of input mesh vertex positions + // OF #OF by 3 list of input mesh triangle indices into OV + // Outputs: + // V #V by 3 list of output mesh vertex positions + // F #F by 3 list of input mesh triangle indices into V + // J #F list of indices into OF of birth parents + IGL_INLINE void simplify_polyhedron( + const Eigen::MatrixXd & OV, + const Eigen::MatrixXi & OF, + Eigen::MatrixXd & V, + Eigen::MatrixXi & F, + Eigen::VectorXi & J); +} +#ifndef IGL_STATIC_LIBRARY +# include "simplify_polyhedron.cpp" +#endif +#endif diff --git a/src/igl/slice.cpp b/src/igl/slice.cpp new file mode 100644 index 0000000000..df3a8e9ce1 --- /dev/null +++ b/src/igl/slice.cpp @@ -0,0 +1,362 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "slice.h" +#include "colon.h" + +#include +#include + +template +IGL_INLINE void igl::slice( + const Eigen::SparseMatrix& X, + const Eigen::Matrix & R, + const Eigen::Matrix & C, + Eigen::SparseMatrix& Y) +{ +#if 1 + int xm = X.rows(); + int xn = X.cols(); + int ym = R.size(); + int yn = C.size(); + + // special case when R or C is empty + if(ym == 0 || yn == 0) + { + Y.resize(ym,yn); + return; + } + + assert(R.minCoeff() >= 0); + assert(R.maxCoeff() < xm); + assert(C.minCoeff() >= 0); + assert(C.maxCoeff() < xn); + + // Build reindexing maps for columns and rows, -1 means not in map + std::vector > RI; + RI.resize(xm); + for(int i = 0;i > CI; + CI.resize(xn); + // initialize to -1 + for(int i = 0;i dyn_Y(ym,yn); + // Take a guess at the number of nonzeros (this assumes uniform distribution + // not banded or heavily diagonal) + dyn_Y.reserve((X.nonZeros()/(X.rows()*X.cols())) * (ym*yn)); + // Iterate over outside + for(int k=0; k::InnerIterator it (X,k); it; ++it) + { + std::vector::iterator rit, cit; + for(rit = RI[it.row()].begin();rit != RI[it.row()].end(); rit++) + { + for(cit = CI[it.col()].begin();cit != CI[it.col()].end(); cit++) + { + dyn_Y.coeffRef(*rit,*cit) = it.value(); + } + } + } + } + Y = Eigen::SparseMatrix(dyn_Y); +#else + + // Alec: This is _not_ valid for arbitrary R,C since they don't necessary + // representation a strict permutation of the rows and columns: rows or + // columns could be removed or replicated. The removal of rows seems to be + // handled here (although it's not clear if there is a performance gain when + // the #removals >> #remains). If this is sufficiently faster than the + // correct code above, one could test whether all entries in R and C are + // unique and apply the permutation version if appropriate. + // + + int xm = X.rows(); + int xn = X.cols(); + int ym = R.size(); + int yn = C.size(); + + // special case when R or C is empty + if(ym == 0 || yn == 0) + { + Y.resize(ym,yn); + return; + } + + assert(R.minCoeff() >= 0); + assert(R.maxCoeff() < xm); + assert(C.minCoeff() >= 0); + assert(C.maxCoeff() < xn); + + // initialize row and col permutation vectors + Eigen::VectorXi rowIndexVec = igl::LinSpaced(xm,0,xm-1); + Eigen::VectorXi rowPermVec = igl::LinSpaced(xm,0,xm-1); + for(int i=0;i rowPerm(rowIndexVec); + + Eigen::VectorXi colIndexVec = igl::LinSpaced(xn,0,xn-1); + Eigen::VectorXi colPermVec = igl::LinSpaced(xn,0,xn-1); + for(int i=0;i colPerm(colPermVec); + + Eigen::SparseMatrix M = (rowPerm * X); + Y = (M * colPerm).block(0,0,ym,yn); +#endif +} + +template +IGL_INLINE void igl::slice( + const MatX& X, + const Eigen::DenseBase & R, + const int dim, + MatY& Y) +{ + Eigen::Matrix C; + switch(dim) + { + case 1: + // boring base case + if(X.cols() == 0) + { + Y.resize(R.size(),0); + return; + } + igl::colon(0,X.cols()-1,C); + return slice(X,R,C,Y); + case 2: + // boring base case + if(X.rows() == 0) + { + Y.resize(0,R.size()); + return; + } + igl::colon(0,X.rows()-1,C); + return slice(X,C,R,Y); + default: + assert(false && "Unsupported dimension"); + return; + } +} + +template < + typename DerivedX, + typename DerivedR, + typename DerivedC, + typename DerivedY> +IGL_INLINE void igl::slice( + const Eigen::DenseBase & X, + const Eigen::DenseBase & R, + const Eigen::DenseBase & C, + Eigen::PlainObjectBase & Y) +{ +#ifndef NDEBUG + int xm = X.rows(); + int xn = X.cols(); +#endif + int ym = R.size(); + int yn = C.size(); + + // special case when R or C is empty + if(ym == 0 || yn == 0) + { + Y.resize(ym,yn); + return; + } + + assert(R.minCoeff() >= 0); + assert(R.maxCoeff() < xm); + assert(C.minCoeff() >= 0); + assert(C.maxCoeff() < xn); + + // Resize output + Y.resize(ym,yn); + // loop over output rows, then columns + for(int i = 0;i +IGL_INLINE void igl::slice( + const Eigen::DenseBase & X, + const Eigen::Matrix & R, + Eigen::PlainObjectBase & Y) +{ + // phony column indices + Eigen::Matrix C; + C.resize(1); + C(0) = 0; + return igl::slice(X,R,C,Y); +} + +template +IGL_INLINE DerivedX igl::slice( + const Eigen::DenseBase & X, + const Eigen::Matrix & R) +{ + DerivedX Y; + igl::slice(X,R,Y); + return Y; +} + +template +IGL_INLINE DerivedX igl::slice( + const Eigen::DenseBase& X, + const Eigen::Matrix & R, + const int dim) +{ + DerivedX Y; + igl::slice(X,R,dim,Y); + return Y; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template Eigen::Matrix igl::slice >(Eigen::DenseBase > const&, Eigen::Matrix const&, int); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Array >(Eigen::Array const&, Eigen::DenseBase > const&, int, Eigen::Array&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix >(Eigen::Array const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Array >(Eigen::Array const&, Eigen::DenseBase > const&, int, Eigen::Array&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::SparseMatrix >(Eigen::SparseMatrix const&, Eigen::DenseBase > const&, int, Eigen::SparseMatrix&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice >, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template Eigen::Matrix igl::slice >(Eigen::DenseBase > const&, Eigen::Matrix const&); +// generated by autoexplicit.sh +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::slice, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::slice, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::slice, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +template Eigen::Matrix igl::slice >(Eigen::DenseBase > const&, Eigen::Matrix const&, int); +template Eigen::Matrix igl::slice >(Eigen::DenseBase > const&, Eigen::Matrix const&); +template Eigen::Matrix igl::slice >(Eigen::DenseBase > const&, Eigen::Matrix const&, int); +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::slice >, Eigen::Matrix, Eigen::PlainObjectBase > >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::PlainObjectBase >&); +template void igl::slice, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +template void igl::slice, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +template void igl::slice, std::complex >(Eigen::SparseMatrix, 0, int> const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::SparseMatrix, 0, int>&); +template Eigen::Matrix igl::slice >(Eigen::DenseBase > const&, Eigen::Matrix const&, int); +template void igl::slice, Eigen::Matrix, Eigen::SparseMatrix >(Eigen::SparseMatrix const&, Eigen::DenseBase > const&, int, Eigen::SparseMatrix&); +template void igl::slice(Eigen::SparseMatrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::SparseMatrix&); +template void igl::slice, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +template void igl::slice, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +template void igl::slice, Eigen::Matrix, Eigen::SparseMatrix >(Eigen::SparseMatrix const&, Eigen::DenseBase > const&, int, Eigen::SparseMatrix&); +template void igl::slice, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::slice, Eigen::Matrix, Eigen::SparseMatrix >(Eigen::SparseMatrix const&, Eigen::DenseBase > const&, int, Eigen::SparseMatrix&); +template void igl::slice, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::slice(Eigen::SparseMatrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::SparseMatrix&); +template void igl::slice, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +template Eigen::Matrix igl::slice >(Eigen::DenseBase > const&, Eigen::Matrix const&); +template void igl::slice >, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::DenseBase > const&, int, Eigen::Matrix&); +template void igl::slice, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::slice(Eigen::SparseMatrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::SparseMatrix&); +#ifdef WIN32 +template void igl::slice, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, class Eigen::PlainObjectBase>>(class Eigen::Matrix<__int64, -1, 1, 0, -1, 1> const &, class Eigen::DenseBase> const &, int, class Eigen::PlainObjectBase> &); +template void igl::slice>, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, class Eigen::PlainObjectBase>>(class Eigen::PlainObjectBase> const &, class Eigen::DenseBase> const &, int, class Eigen::PlainObjectBase> &); +#endif +#endif diff --git a/src/igl/slice.h b/src/igl/slice.h new file mode 100644 index 0000000000..a25dfa4112 --- /dev/null +++ b/src/igl/slice.h @@ -0,0 +1,87 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SLICE_H +#define IGL_SLICE_H +#include "igl_inline.h" + +#include +namespace igl +{ + // Act like the matlab X(row_indices,col_indices) operator, where + // row_indices, col_indices are non-negative integer indices. + // + // Inputs: + // X m by n matrix + // R list of row indices + // C list of column indices + // Output: + // Y #R by #C matrix + // + // See also: slice_mask + template < + typename TX, + typename TY> + IGL_INLINE void slice( + const Eigen::SparseMatrix& X, + const Eigen::Matrix & R, + const Eigen::Matrix & C, + Eigen::SparseMatrix& Y); + // Wrapper to only slice in one direction + // + // Inputs: + // dim dimension to slice in 1 or 2, dim=1 --> X(R,:), dim=2 --> X(:,R) + // + // Note: For now this is just a cheap wrapper. + template < + typename MatX, + typename DerivedR, + typename MatY> + IGL_INLINE void slice( + const MatX& X, + const Eigen::DenseBase & R, + const int dim, + MatY& Y); + template < + typename DerivedX, + typename DerivedR, + typename DerivedC, + typename DerivedY> + IGL_INLINE void slice( + const Eigen::DenseBase & X, + const Eigen::DenseBase & R, + const Eigen::DenseBase & C, + Eigen::PlainObjectBase & Y); + + template + IGL_INLINE void slice( + const Eigen::DenseBase & X, + const Eigen::Matrix & R, + Eigen::PlainObjectBase & Y); + // VectorXi Y = slice(X,R); + // + // This templating is bad because the return type might not have the same + // size as `DerivedX`. This will probably only work if DerivedX has Dynamic + // as it's non-trivial sizes or if the number of rows in R happens to equal + // the number of rows in `DerivedX`. + template + IGL_INLINE DerivedX slice( + const Eigen::DenseBase & X, + const Eigen::Matrix & R); + template + IGL_INLINE DerivedX slice( + const Eigen::DenseBase& X, + const Eigen::Matrix & R, + const int dim); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "slice.cpp" +#endif + +#endif diff --git a/src/igl/slice_cached.cpp b/src/igl/slice_cached.cpp new file mode 100644 index 0000000000..e0f13d804f --- /dev/null +++ b/src/igl/slice_cached.cpp @@ -0,0 +1,57 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "slice_cached.h" + +#include +#include +#include +#include "slice.h" + +template +IGL_INLINE void igl::slice_cached_precompute( + const Eigen::SparseMatrix& X, + const Eigen::Matrix & R, + const Eigen::Matrix & C, + Eigen::MatrixBase& data, + Eigen::SparseMatrix& Y + ) +{ + // Create a sparse matrix whose entries are the ids + Eigen::SparseMatrix TS = X.template cast(); + + TS.makeCompressed(); + for (unsigned i=0;i TS_sliced; + igl::slice(TS,R,C,TS_sliced); + Y = TS_sliced.cast(); + + data.resize(TS_sliced.nonZeros()); + for (unsigned i=0;i +IGL_INLINE void igl::slice_cached( + const Eigen::SparseMatrix& X, + const Eigen::MatrixBase& data, + Eigen::SparseMatrix& Y + ) +{ + for (unsigned i=0; i >(Eigen::SparseMatrix const&, Eigen::MatrixBase > const&, Eigen::SparseMatrix&); +template void igl::slice_cached_precompute >(Eigen::SparseMatrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::MatrixBase >&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/slice_cached.h b/src/igl/slice_cached.h new file mode 100644 index 0000000000..84ced8dc93 --- /dev/null +++ b/src/igl/slice_cached.h @@ -0,0 +1,69 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SLICE_CACHED_H +#define IGL_SLICE_CACHED_H +#include "igl_inline.h" +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include +namespace igl +{ + + // Act like the matlab X(row_indices,col_indices) operator, where row_indices, + // col_indices are non-negative integer indices. This is a fast version of + // igl::slice that can analyze and store the sparsity structure. It is slower + // at the irst evaluation (slice_cached_precompute), but faster on the + // subsequent ones. + // + // Inputs: + // X m by n matrix + // R list of row indices + // C list of column indices + // + // Output: + // Y #R by #C matrix + // data Temporary data used by slice_cached to repeat this operation + // + // Usage: + // + // // Construct and slice up Laplacian + // SparseMatrix L,L_sliced; + // igl::cotmatrix(V,F,L); + + // // Normal igl::slice call + // igl::slice(L,in,in,L_in_in); + + // // Fast version + // static VectorXi data; // static or saved in a global state + // if (data.size() == 0) + // igl::slice_cached_precompute(L,in,in,data,L_sliced); + // else + // igl::slice_cached(L,data,L_sliced); + +template +IGL_INLINE void slice_cached_precompute( + const Eigen::SparseMatrix& X, + const Eigen::Matrix & R, + const Eigen::Matrix & C, + Eigen::MatrixBase& data, + Eigen::SparseMatrix& Y + ); + +template +IGL_INLINE void slice_cached( + const Eigen::SparseMatrix& X, + const Eigen::MatrixBase& data, + Eigen::SparseMatrix& Y + ); +} + +#ifndef IGL_STATIC_LIBRARY +# include "slice_cached.cpp" +#endif + +#endif diff --git a/src/igl/slice_into.cpp b/src/igl/slice_into.cpp new file mode 100644 index 0000000000..aa12d93032 --- /dev/null +++ b/src/igl/slice_into.cpp @@ -0,0 +1,153 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "slice_into.h" +#include "colon.h" + +// Bug in unsupported/Eigen/SparseExtra needs iostream first +#include +#include + +template +IGL_INLINE void igl::slice_into( + const Eigen::SparseMatrix& X, + const Eigen::Matrix & R, + const Eigen::Matrix & C, + Eigen::SparseMatrix& Y) +{ + +#ifndef NDEBUG + int xm = X.rows(); + int xn = X.cols(); + assert(R.size() == xm); + assert(C.size() == xn); + int ym = Y.size(); + int yn = Y.size(); + assert(R.minCoeff() >= 0); + assert(R.maxCoeff() < ym); + assert(C.minCoeff() >= 0); + assert(C.maxCoeff() < yn); +#endif + + // create temporary dynamic sparse matrix + Eigen::DynamicSparseMatrix dyn_Y(Y); + // Iterate over outside + for(int k=0; k::InnerIterator it (X,k); it; ++it) + { + dyn_Y.coeffRef(R(it.row()),C(it.col())) = it.value(); + } + } + Y = Eigen::SparseMatrix(dyn_Y); +} + +template +IGL_INLINE void igl::slice_into( + const Eigen::DenseBase & X, + const Eigen::Matrix & R, + const Eigen::Matrix & C, + Eigen::PlainObjectBase & Y) +{ + + int xm = X.rows(); + int xn = X.cols(); +#ifndef NDEBUG + assert(R.size() == xm); + assert(C.size() == xn); + int ym = Y.size(); + int yn = Y.size(); + assert(R.minCoeff() >= 0); + assert(R.maxCoeff() < ym); + assert(C.minCoeff() >= 0); + assert(C.maxCoeff() < yn); +#endif + + // Build reindexing maps for columns and rows, -1 means not in map + Eigen::Matrix RI; + RI.resize(xm); + for(int i = 0;i +IGL_INLINE void igl::slice_into( + const MatX& X, + const Eigen::Matrix & R, + const int dim, + MatY& Y) +{ + Eigen::VectorXi C; + switch(dim) + { + case 1: + assert(R.size() == X.rows()); + // boring base case + if(X.cols() == 0) + { + return; + } + igl::colon(0,X.cols()-1,C); + return slice_into(X,R,C,Y); + case 2: + assert(R.size() == X.cols()); + // boring base case + if(X.rows() == 0) + { + return; + } + igl::colon(0,X.rows()-1,C); + return slice_into(X,C,R,Y); + default: + assert(false && "Unsupported dimension"); + return; + } +} + +template +IGL_INLINE void igl::slice_into( + const Eigen::DenseBase & X, + const Eigen::Matrix & R, + Eigen::PlainObjectBase & Y) +{ + // phony column indices + Eigen::Matrix C; + C.resize(1); + C(0) = 0; + return igl::slice_into(X,R,C,Y); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::slice_into, Eigen::Matrix >(Eigen::Matrix const&, Eigen::Matrix const&, int, Eigen::Matrix&); +// generated by autoexplicit.sh +template void igl::slice_into, Eigen::PlainObjectBase > >(Eigen::Matrix const&, Eigen::Matrix const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice_into, -1, -1, true>, Eigen::PlainObjectBase > >(Eigen::Block, -1, -1, true> const&, Eigen::Matrix const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice_into, Eigen::PlainObjectBase > >(Eigen::Matrix const&, Eigen::Matrix const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::slice_into, Eigen::PlainObjectBase > >(Eigen::Matrix const&, Eigen::Matrix const&, int, Eigen::PlainObjectBase >&); +template void igl::slice_into(Eigen::SparseMatrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::SparseMatrix&); +template void igl::slice_into, Eigen::Matrix >(Eigen::Matrix const&, Eigen::Matrix const&, int, Eigen::Matrix&); +template void igl::slice_into, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +template void igl::slice_into, Eigen::Matrix >(Eigen::Matrix const&, Eigen::Matrix const&, int, Eigen::Matrix&); +template void igl::slice_into, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +template void igl::slice_into, Eigen::SparseMatrix >(Eigen::SparseMatrix const&, Eigen::Matrix const&, int, Eigen::SparseMatrix&); +template void igl::slice_into(Eigen::SparseMatrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::SparseMatrix&); +template void igl::slice_into, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +template void igl::slice_into, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +template void igl::slice_into, Eigen::Matrix >(Eigen::Matrix const&, Eigen::Matrix const&, int, Eigen::Matrix&); +template void igl::slice_into, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Matrix const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/slice_into.h b/src/igl/slice_into.h new file mode 100644 index 0000000000..83bd8f5230 --- /dev/null +++ b/src/igl/slice_into.h @@ -0,0 +1,63 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SLICE_INTO_H +#define IGL_SLICE_INTO_H +#include "igl_inline.h" + +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include +namespace igl +{ + // Act like the matlab Y(row_indices,col_indices) = X + // + // Inputs: + // X xm by xn rhs matrix + // R list of row indices + // C list of column indices + // Y ym by yn lhs matrix + // Output: + // Y ym by yn lhs matrix, same as input but Y(R,C) = X + template + IGL_INLINE void slice_into( + const Eigen::SparseMatrix& X, + const Eigen::Matrix & R, + const Eigen::Matrix & C, + Eigen::SparseMatrix& Y); + + template + IGL_INLINE void slice_into( + const Eigen::DenseBase & X, + const Eigen::Matrix & R, + const Eigen::Matrix & C, + Eigen::PlainObjectBase & Y); + // Wrapper to only slice in one direction + // + // Inputs: + // dim dimension to slice in 1 or 2, dim=1 --> X(R,:), dim=2 --> X(:,R) + // + // Note: For now this is just a cheap wrapper. + template + IGL_INLINE void slice_into( + const MatX & X, + const Eigen::Matrix & R, + const int dim, + MatY& Y); + + template + IGL_INLINE void slice_into( + const Eigen::DenseBase & X, + const Eigen::Matrix & R, + Eigen::PlainObjectBase & Y); +} + +#ifndef IGL_STATIC_LIBRARY +# include "slice_into.cpp" +#endif + +#endif diff --git a/src/igl/slice_mask.cpp b/src/igl/slice_mask.cpp new file mode 100644 index 0000000000..63cb82045e --- /dev/null +++ b/src/igl/slice_mask.cpp @@ -0,0 +1,168 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "slice_mask.h" +#include "slice.h" +#include "find.h" +#include + +template +IGL_INLINE void igl::slice_mask( + const Eigen::DenseBase & X, + const Eigen::Array & R, + const Eigen::Array & C, + Eigen::PlainObjectBase & Y) +{ + int xm = X.rows(); + int xn = X.cols(); + int ym = R.count(); + int yn = C.count(); + assert(R.size() == X.rows() && "R.size() should match X.rows()"); + assert(C.size() == X.cols() && "C.size() should match X.cols()"); + Y.resize(ym,yn); + { + int yi = 0; + for(int i = 0;i +IGL_INLINE void igl::slice_mask( + const Eigen::DenseBase & X, + const Eigen::Array & R, + const int dim, + Eigen::PlainObjectBase & Y) +{ + switch(dim) + { + case 1: + { + const int ym = R.count(); + assert(X.rows() == R.size() && "X.rows() should match R.size()"); + Y.resize(ym,X.cols()); + { + int yi = 0; + for(int i = 0;i +IGL_INLINE DerivedX igl::slice_mask( + const Eigen::DenseBase & X, + const Eigen::Array & R, + const Eigen::Array & C) +{ + DerivedX Y; + igl::slice_mask(X,R,C,Y); + return Y; +} + +template +IGL_INLINE DerivedX igl::slice_mask( + const Eigen::DenseBase& X, + const Eigen::Array & R, + const int dim) +{ + DerivedX Y; + igl::slice_mask(X,R,dim,Y); + return Y; +} + + +template +IGL_INLINE void igl::slice_mask( + const Eigen::SparseMatrix & X, + const Eigen::Array & R, + const int dim, + Eigen::SparseMatrix & Y) +{ + // Cheapskate solution + Eigen::VectorXi Ri; + find(R,Ri); + return slice(X,Ri,dim,Y); +} + +template +IGL_INLINE void igl::slice_mask( + const Eigen::SparseMatrix & X, + const Eigen::Array & R, + const Eigen::Array & C, + Eigen::SparseMatrix & Y) +{ + // Cheapskate solution + Eigen::VectorXi Ri; + find(R,Ri); + Eigen::VectorXi Ci; + find(C,Ci); + return slice(X,Ri,Ci,Y); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template Eigen::Matrix igl::slice_mask >(Eigen::DenseBase > const&, Eigen::Array const&, int); +// generated by autoexplicit.sh +template Eigen::Matrix igl::slice_mask >(Eigen::DenseBase > const&, Eigen::Array const&, int); +// generated by autoexplicit.sh +template Eigen::Array igl::slice_mask >(Eigen::DenseBase > const&, Eigen::Array const&, int); +// generated by autoexplicit.sh +template void igl::slice_mask(Eigen::SparseMatrix const&, Eigen::Array const&, int, Eigen::SparseMatrix&); +template void igl::slice_mask, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Array const&, int, Eigen::PlainObjectBase >&); +template void igl::slice_mask, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Array const&, int, Eigen::PlainObjectBase >&); +template void igl::slice_mask, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::Array const&, int, Eigen::PlainObjectBase >&); +template void igl::slice_mask, Eigen::Array >(Eigen::DenseBase > const&, Eigen::Array const&, int, Eigen::PlainObjectBase >&); +template void igl::slice_mask >(Eigen::DenseBase > const&, Eigen::Array const&, int, Eigen::PlainObjectBase >&); +template void igl::slice_mask >(Eigen::DenseBase > const&, Eigen::Array const&, Eigen::Array const&, Eigen::PlainObjectBase >&); +template void igl::slice_mask >(Eigen::DenseBase > const&, Eigen::Array const&, int, Eigen::PlainObjectBase >&); +template void igl::slice_mask >(Eigen::DenseBase > const&, Eigen::Array const&, Eigen::Array const&, Eigen::PlainObjectBase >&); +template void igl::slice_mask >(Eigen::DenseBase > const&, Eigen::Array const&, int, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/slice_mask.h b/src/igl/slice_mask.h new file mode 100644 index 0000000000..0a0dcda7c1 --- /dev/null +++ b/src/igl/slice_mask.h @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SLICE_MASK_H +#define IGL_SLICE_MASK_H +#include "igl_inline.h" + +#include +#include +namespace igl +{ + // Act like the matlab X(row_mask,col_mask) operator, where + // row_mask, col_mask are non-negative integer indices. + // + // Inputs: + // X m by n matrix + // R m list of row bools + // C n list of column bools + // Output: + // Y #trues-in-R by #trues-in-C matrix + // + // See also: slice_mask + + template + IGL_INLINE void slice_mask( + const Eigen::DenseBase & X, + const Eigen::Array & R, + const Eigen::Array & C, + Eigen::PlainObjectBase & Y); + template + IGL_INLINE void slice_mask( + const Eigen::DenseBase & X, + const Eigen::Array & R, + const int dim, + Eigen::PlainObjectBase & Y); + // + // This templating is bad because the return type might not have the same + // size as `DerivedX`. This will probably only work if DerivedX has Dynamic + // as it's non-trivial sizes or if the number of rows in R happens to equal + // the number of rows in `DerivedX`. + template + IGL_INLINE DerivedX slice_mask( + const Eigen::DenseBase & X, + const Eigen::Array & R, + const Eigen::Array & C); + template + IGL_INLINE DerivedX slice_mask( + const Eigen::DenseBase & X, + const Eigen::Array & R, + const int dim); + template + IGL_INLINE void slice_mask( + const Eigen::SparseMatrix & X, + const Eigen::Array & R, + const int dim, + Eigen::SparseMatrix & Y); + template + IGL_INLINE void slice_mask( + const Eigen::SparseMatrix & X, + const Eigen::Array & R, + const Eigen::Array & C, + Eigen::SparseMatrix & Y); +} + + +#ifndef IGL_STATIC_LIBRARY +# include "slice_mask.cpp" +#endif + +#endif diff --git a/src/igl/slice_tets.cpp b/src/igl/slice_tets.cpp new file mode 100644 index 0000000000..dbe042fa15 --- /dev/null +++ b/src/igl/slice_tets.cpp @@ -0,0 +1,356 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "slice_tets.h" +#include "LinSpaced.h" +#include "sort.h" +#include "edges.h" +#include "slice.h" +#include "cat.h" +#include "ismember.h" +#include "unique_rows.h" +#include +#include +#include + +template < + typename DerivedV, + typename DerivedT, + typename DerivedS, + typename DerivedSV, + typename DerivedSF, + typename DerivedJ, + typename BCType> +IGL_INLINE void igl::slice_tets( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& T, + const Eigen::MatrixBase & S, + Eigen::PlainObjectBase& SV, + Eigen::PlainObjectBase& SF, + Eigen::PlainObjectBase& J, + Eigen::SparseMatrix & BC) +{ + Eigen::MatrixXi sE; + Eigen::Matrix lambda; + igl::slice_tets(V,T,S,SV,SF,J,sE,lambda); + const int ns = SV.rows(); + std::vector > BCIJV(ns*2); + for(int i = 0;i(i,sE(i,0), lambda(i)); + BCIJV[2*i+1] = Eigen::Triplet(i,sE(i,1),1.0-lambda(i)); + } + BC.resize(SV.rows(),V.rows()); + BC.setFromTriplets(BCIJV.begin(),BCIJV.end()); +} + +template < + typename DerivedV, + typename DerivedT, + typename DerivedS, + typename DerivedSV, + typename DerivedSF, + typename DerivedJ> +IGL_INLINE void igl::slice_tets( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& T, + const Eigen::MatrixBase & S, + Eigen::PlainObjectBase& SV, + Eigen::PlainObjectBase& SF, + Eigen::PlainObjectBase& J) +{ + Eigen::MatrixXi sE; + Eigen::Matrix lambda; + igl::slice_tets(V,T,S,SV,SF,J,sE,lambda); +} + +template < + typename DerivedV, + typename DerivedT, + typename DerivedS, + typename DerivedSV, + typename DerivedSF, + typename DerivedJ, + typename DerivedsE, + typename Derivedlambda> +IGL_INLINE void igl::slice_tets( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& T, + const Eigen::MatrixBase & S, + Eigen::PlainObjectBase& SV, + Eigen::PlainObjectBase& SF, + Eigen::PlainObjectBase& J, + Eigen::PlainObjectBase& sE, + Eigen::PlainObjectBase& lambda) +{ + + using namespace Eigen; + using namespace std; + assert(V.cols() == 3 && "V should be #V by 3"); + assert(T.cols() == 4 && "T should be #T by 4"); + + static const Eigen::Matrix flipped_order = + (Eigen::Matrix(12,4)<< + 3,2,0,1, + 3,1,2,0, + 3,0,1,2, + 2,3,1,0, + 2,1,0,3, + 2,0,3,1, + 1,3,0,2, + 1,2,3,0, + 1,0,2,3, + 0,3,2,1, + 0,2,1,3, + 0,1,3,2 + ).finished(); + + // number of tets + const size_t m = T.rows(); + + typedef typename DerivedS::Scalar Scalar; + typedef typename DerivedT::Scalar Index; + typedef Matrix VectorXS; + typedef Matrix MatrixX4S; + typedef Matrix MatrixX3S; + typedef Matrix MatrixX2S; + typedef Matrix MatrixX4I; + typedef Matrix MatrixX3I; + typedef Matrix MatrixX2I; + typedef Matrix VectorXI; + typedef Array ArrayXb; + + MatrixX4S IT(m,4); + for(size_t t = 0;t & T, + const MatrixX4S & IT, + const ArrayXb & I, + MatrixX4I & TI, + MatrixX4S & ITI, + VectorXI & JI) + { + const Index num_I = std::count(I.data(),I.data()+I.size(),true); + TI.resize(num_I,4); + ITI.resize(num_I,4); + JI.resize(num_I,1); + { + size_t k = 0; + for(size_t t = 0;t<(size_t)T.rows();t++) + { + if(I(t)) + { + TI.row(k) = T.row(t); + ITI.row(k) = IT.row(t); + JI(k) = t; + k++; + } + } + assert(k == num_I); + } + }; + + ArrayXb I13 = (IT.array()<0).rowwise().count()==1; + ArrayXb I31 = (IT.array()>0).rowwise().count()==1; + ArrayXb I22 = (IT.array()<0).rowwise().count()==2; + MatrixX4I T13,T31,T22; + MatrixX4S IT13,IT31,IT22; + VectorXI J13,J31,J22; + extract_rows(T,IT,I13,T13,IT13,J13); + extract_rows(T,IT,I31,T31,IT31,J31); + extract_rows(T,IT,I22,T22,IT22,J22); + + const auto & apply_sort4 = [] ( + const MatrixX4I & T, + const MatrixX4I & sJ, + MatrixX4I & sT) + { + sT.resize(T.rows(),4); + for(size_t t = 0;t<(size_t)T.rows();t++) + { + for(size_t c = 0;c<4;c++) + { + sT(t,c) = T(t,sJ(t,c)); + } + } + }; + + const auto & apply_sort2 = [] ( + const MatrixX2I & E, + const MatrixX2I & sJ, + Eigen::PlainObjectBase& sE) + { + sE.resize(E.rows(),2); + for(size_t t = 0;t<(size_t)E.rows();t++) + { + for(size_t c = 0;c<2;c++) + { + sE(t,c) = E(t,sJ(t,c)); + } + } + }; + + const auto & one_below = [&apply_sort4]( + const MatrixX4I & T, + const MatrixX4S & IT, + MatrixX2I & U, + MatrixX3I & SF) + { + // Number of tets + const size_t m = T.rows(); + if(m == 0) + { + U.resize(0,2); + SF.resize(0,3); + return; + } + MatrixX4S sIT; + MatrixX4I sJ; + sort(IT,2,true,sIT,sJ); + MatrixX4I sT; + apply_sort4(T,sJ,sT); + U.resize(3*m,2); + U<< + sT.col(0),sT.col(1), + sT.col(0),sT.col(2), + sT.col(0),sT.col(3); + SF.resize(m,3); + for(size_t c = 0;c<3;c++) + { + SF.col(c) = + igl::LinSpaced< + Eigen::Matrix > + (m,0+c*m,(m-1)+c*m); + } + ArrayXb flip; + { + VectorXi _; + ismember_rows(sJ,flipped_order,flip,_); + } + for(int i = 0;i(m,0+0*m,(m-1)+0*m); + SF.block(0,1,m,1) = igl::LinSpaced(m,0+1*m,(m-1)+1*m); + SF.block(0,2,m,1) = igl::LinSpaced(m,0+3*m,(m-1)+3*m); + SF.block(m,0,m,1) = igl::LinSpaced(m,0+0*m,(m-1)+0*m); + SF.block(m,1,m,1) = igl::LinSpaced(m,0+3*m,(m-1)+3*m); + SF.block(m,2,m,1) = igl::LinSpaced(m,0+2*m,(m-1)+2*m); + ArrayXb flip; + { + VectorXi _; + ismember_rows(sJ,flipped_order,flip,_); + } + for(int i = 0;i()*lambda(e) + + V.row(sE(e,1)).template cast()*(1.0-lambda(e)); + } + SF.resize( SF13.rows()+SF31.rows()+SF22.rows(),3); + SF<< + SF13, + U13.rows()+ SF31.rowwise().reverse().array(), + U13.rows()+U31.rows()+SF22.array(); + + std::for_each( + SF.data(), + SF.data()+SF.size(), + [&uJ](typename DerivedSF::Scalar & i){i=uJ(i);}); + + J.resize(SF.rows()); + J<, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/slice_tets.h b/src/igl/slice_tets.h new file mode 100644 index 0000000000..349c5d7900 --- /dev/null +++ b/src/igl/slice_tets.h @@ -0,0 +1,96 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SLICE_TETS_H +#define IGL_SLICE_TETS_H +#include "igl_inline.h" + +#include +#include + +#include + +namespace igl +{ + // SLICE_TETS Slice through a tet mesh (V,T) along a given plane (via its + // implicit equation). + // + // Inputs: + // V #V by 3 list of tet mesh vertices + // T #T by 4 list of tet indices into V + //// plane list of 4 coefficients in the plane equation: [x y z 1]'*plane = 0 + // S #V list of values so that S = 0 is the desired isosurface + // Outputs: + // SV #SV by 3 list of triangle mesh vertices along slice + // SF #SF by 3 list of triangles indices into SV + // J #SF list of indices into T revealing from which tet each faces comes + // BC #SU by #V list of barycentric coordinates (or more generally: linear + // interpolation coordinates) so that SV = BC*V + // + template < + typename DerivedV, + typename DerivedT, + typename DerivedS, + typename DerivedSV, + typename DerivedSF, + typename DerivedJ, + typename BCType> + IGL_INLINE void slice_tets( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& T, + const Eigen::MatrixBase & S, + Eigen::PlainObjectBase& SV, + Eigen::PlainObjectBase& SF, + Eigen::PlainObjectBase& J, + Eigen::SparseMatrix & BC); + template < + typename DerivedV, + typename DerivedT, + typename DerivedS, + typename DerivedSV, + typename DerivedSF, + typename DerivedJ> + IGL_INLINE void slice_tets( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& T, + const Eigen::MatrixBase & S, + Eigen::PlainObjectBase& SV, + Eigen::PlainObjectBase& SF, + Eigen::PlainObjectBase& J); + // Outputs: + // sE #SV by 2 list of sorted edge indices into V + // lambda #SV by 1 list of parameters along each edge in sE so that: + // SV(i,:) = V(sE(i,1),:)*lambda(i) + V(sE(i,2),:)*(1-lambda(i)); + template < + typename DerivedV, + typename DerivedT, + typename DerivedS, + typename DerivedSV, + typename DerivedSF, + typename DerivedJ, + typename DerivedsE, + typename Derivedlambda + > + IGL_INLINE void slice_tets( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& T, + const Eigen::MatrixBase & S, + Eigen::PlainObjectBase& SV, + Eigen::PlainObjectBase& SF, + Eigen::PlainObjectBase& J, + Eigen::PlainObjectBase& sE, + Eigen::PlainObjectBase& lambda); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "slice_tets.cpp" +#endif + +#endif + + diff --git a/src/igl/slim.cpp b/src/igl/slim.cpp new file mode 100644 index 0000000000..a886742742 --- /dev/null +++ b/src/igl/slim.cpp @@ -0,0 +1,961 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Michael Rabinovich +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "slim.h" + +#include "boundary_loop.h" +#include "cotmatrix.h" +#include "edge_lengths.h" +#include "grad.h" +#include "local_basis.h" +#include "repdiag.h" +#include "vector_area_matrix.h" +#include "arap.h" +#include "cat.h" +#include "doublearea.h" +#include "grad.h" +#include "local_basis.h" +#include "per_face_normals.h" +#include "slice_into.h" +#include "volume.h" +#include "polar_svd.h" +#include "flip_avoiding_line_search.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "Timer.h" +#include "sparse_cached.h" +#include "AtA_cached.h" + +#ifdef CHOLMOD +#include +#endif + +namespace igl +{ + namespace slim + { + // Definitions of internal functions + IGL_INLINE void compute_surface_gradient_matrix(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, + const Eigen::MatrixXd &F1, const Eigen::MatrixXd &F2, + Eigen::SparseMatrix &D1, Eigen::SparseMatrix &D2); + IGL_INLINE void buildA(igl::SLIMData& s, std::vector > & IJV); + IGL_INLINE void buildRhs(igl::SLIMData& s, const Eigen::SparseMatrix &A); + IGL_INLINE void add_soft_constraints(igl::SLIMData& s, Eigen::SparseMatrix &L); + IGL_INLINE double compute_energy(igl::SLIMData& s, Eigen::MatrixXd &V_new); + IGL_INLINE double compute_soft_const_energy(igl::SLIMData& s, + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + Eigen::MatrixXd &V_o); + IGL_INLINE double compute_energy_with_jacobians(igl::SLIMData& s, + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, const Eigen::MatrixXd &Ji, + Eigen::MatrixXd &uv, Eigen::VectorXd &areas); + IGL_INLINE void solve_weighted_arap(igl::SLIMData& s, + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + Eigen::MatrixXd &uv, + Eigen::VectorXi &soft_b_p, + Eigen::MatrixXd &soft_bc_p); + IGL_INLINE void update_weights_and_closest_rotations( igl::SLIMData& s, + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + Eigen::MatrixXd &uv); + IGL_INLINE void compute_jacobians(igl::SLIMData& s, const Eigen::MatrixXd &uv); + IGL_INLINE void build_linear_system(igl::SLIMData& s, Eigen::SparseMatrix &L); + IGL_INLINE void pre_calc(igl::SLIMData& s); + + // Implementation + IGL_INLINE void compute_surface_gradient_matrix(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, + const Eigen::MatrixXd &F1, const Eigen::MatrixXd &F2, + Eigen::SparseMatrix &D1, Eigen::SparseMatrix &D2) + { + + Eigen::SparseMatrix G; + igl::grad(V, F, G); + Eigen::SparseMatrix Dx = G.block(0, 0, F.rows(), V.rows()); + Eigen::SparseMatrix Dy = G.block(F.rows(), 0, F.rows(), V.rows()); + Eigen::SparseMatrix Dz = G.block(2 * F.rows(), 0, F.rows(), V.rows()); + + D1 = F1.col(0).asDiagonal() * Dx + F1.col(1).asDiagonal() * Dy + F1.col(2).asDiagonal() * Dz; + D2 = F2.col(0).asDiagonal() * Dx + F2.col(1).asDiagonal() * Dy + F2.col(2).asDiagonal() * Dz; + } + + IGL_INLINE void compute_jacobians(igl::SLIMData& s, const Eigen::MatrixXd &uv) + { + if (s.F.cols() == 3) + { + // Ji=[D1*u,D2*u,D1*v,D2*v]; + s.Ji.col(0) = s.Dx * uv.col(0); + s.Ji.col(1) = s.Dy * uv.col(0); + s.Ji.col(2) = s.Dx * uv.col(1); + s.Ji.col(3) = s.Dy * uv.col(1); + } + else /*tet mesh*/{ + // Ji=[D1*u,D2*u,D3*u, D1*v,D2*v, D3*v, D1*w,D2*w,D3*w]; + s.Ji.col(0) = s.Dx * uv.col(0); + s.Ji.col(1) = s.Dy * uv.col(0); + s.Ji.col(2) = s.Dz * uv.col(0); + s.Ji.col(3) = s.Dx * uv.col(1); + s.Ji.col(4) = s.Dy * uv.col(1); + s.Ji.col(5) = s.Dz * uv.col(1); + s.Ji.col(6) = s.Dx * uv.col(2); + s.Ji.col(7) = s.Dy * uv.col(2); + s.Ji.col(8) = s.Dz * uv.col(2); + } + } + + IGL_INLINE void update_weights_and_closest_rotations(igl::SLIMData& s, + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + Eigen::MatrixXd &uv) + { + compute_jacobians(s, uv); + + const double eps = 1e-8; + double exp_f = s.exp_factor; + + if (s.dim == 2) + { + for (int i = 0; i < s.Ji.rows(); ++i) + { + typedef Eigen::Matrix Mat2; + typedef Eigen::Matrix Vec2; + Mat2 ji, ri, ti, ui, vi; + Vec2 sing; + Vec2 closest_sing_vec; + Mat2 mat_W; + Vec2 m_sing_new; + double s1, s2; + + ji(0, 0) = s.Ji(i, 0); + ji(0, 1) = s.Ji(i, 1); + ji(1, 0) = s.Ji(i, 2); + ji(1, 1) = s.Ji(i, 3); + + igl::polar_svd(ji, ri, ti, ui, sing, vi); + + s1 = sing(0); + s2 = sing(1); + + // Update Weights according to energy + switch (s.slim_energy) + { + case igl::SLIMData::ARAP: + { + m_sing_new << 1, 1; + break; + } + case igl::SLIMData::SYMMETRIC_DIRICHLET: + { + double s1_g = 2 * (s1 - pow(s1, -3)); + double s2_g = 2 * (s2 - pow(s2, -3)); + m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))); + break; + } + case igl::SLIMData::LOG_ARAP: + { + double s1_g = 2 * (log(s1) / s1); + double s2_g = 2 * (log(s2) / s2); + m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))); + break; + } + case igl::SLIMData::CONFORMAL: + { + double s1_g = 1 / (2 * s2) - s2 / (2 * pow(s1, 2)); + double s2_g = 1 / (2 * s1) - s1 / (2 * pow(s2, 2)); + + double geo_avg = sqrt(s1 * s2); + double s1_min = geo_avg; + double s2_min = geo_avg; + + m_sing_new << sqrt(s1_g / (2 * (s1 - s1_min))), sqrt(s2_g / (2 * (s2 - s2_min))); + + // change local step + closest_sing_vec << s1_min, s2_min; + ri = ui * closest_sing_vec.asDiagonal() * vi.transpose(); + break; + } + case igl::SLIMData::EXP_CONFORMAL: + { + double s1_g = 2 * (s1 - pow(s1, -3)); + double s2_g = 2 * (s2 - pow(s2, -3)); + + double geo_avg = sqrt(s1 * s2); + double s1_min = geo_avg; + double s2_min = geo_avg; + + double in_exp = exp_f * ((pow(s1, 2) + pow(s2, 2)) / (2 * s1 * s2)); + double exp_thing = exp(in_exp); + + s1_g *= exp_thing * exp_f; + s2_g *= exp_thing * exp_f; + + m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))); + break; + } + case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET: + { + double s1_g = 2 * (s1 - pow(s1, -3)); + double s2_g = 2 * (s2 - pow(s2, -3)); + + double in_exp = exp_f * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2)); + double exp_thing = exp(in_exp); + + s1_g *= exp_thing * exp_f; + s2_g *= exp_thing * exp_f; + + m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))); + break; + } + } + + if (std::abs(s1 - 1) < eps) m_sing_new(0) = 1; + if (std::abs(s2 - 1) < eps) m_sing_new(1) = 1; + mat_W = ui * m_sing_new.asDiagonal() * ui.transpose(); + + s.W_11(i) = mat_W(0, 0); + s.W_12(i) = mat_W(0, 1); + s.W_21(i) = mat_W(1, 0); + s.W_22(i) = mat_W(1, 1); + + // 2) Update local step (doesn't have to be a rotation, for instance in case of conformal energy) + s.Ri(i, 0) = ri(0, 0); + s.Ri(i, 1) = ri(1, 0); + s.Ri(i, 2) = ri(0, 1); + s.Ri(i, 3) = ri(1, 1); + } + } + else + { + typedef Eigen::Matrix Vec3; + typedef Eigen::Matrix Mat3; + Mat3 ji; + Vec3 m_sing_new; + Vec3 closest_sing_vec; + const double sqrt_2 = sqrt(2); + for (int i = 0; i < s.Ji.rows(); ++i) + { + ji(0, 0) = s.Ji(i, 0); + ji(0, 1) = s.Ji(i, 1); + ji(0, 2) = s.Ji(i, 2); + ji(1, 0) = s.Ji(i, 3); + ji(1, 1) = s.Ji(i, 4); + ji(1, 2) = s.Ji(i, 5); + ji(2, 0) = s.Ji(i, 6); + ji(2, 1) = s.Ji(i, 7); + ji(2, 2) = s.Ji(i, 8); + + Mat3 ri, ti, ui, vi; + Vec3 sing; + igl::polar_svd(ji, ri, ti, ui, sing, vi); + + double s1 = sing(0); + double s2 = sing(1); + double s3 = sing(2); + + // 1) Update Weights + switch (s.slim_energy) + { + case igl::SLIMData::ARAP: + { + m_sing_new << 1, 1, 1; + break; + } + case igl::SLIMData::LOG_ARAP: + { + double s1_g = 2 * (log(s1) / s1); + double s2_g = 2 * (log(s2) / s2); + double s3_g = 2 * (log(s3) / s3); + m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))), sqrt(s3_g / (2 * (s3 - 1))); + break; + } + case igl::SLIMData::SYMMETRIC_DIRICHLET: + { + double s1_g = 2 * (s1 - pow(s1, -3)); + double s2_g = 2 * (s2 - pow(s2, -3)); + double s3_g = 2 * (s3 - pow(s3, -3)); + m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))), sqrt(s3_g / (2 * (s3 - 1))); + break; + } + case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET: + { + double s1_g = 2 * (s1 - pow(s1, -3)); + double s2_g = 2 * (s2 - pow(s2, -3)); + double s3_g = 2 * (s3 - pow(s3, -3)); + m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))), sqrt(s3_g / (2 * (s3 - 1))); + + double in_exp = exp_f * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2) + pow(s3, 2) + pow(s3, -2)); + double exp_thing = exp(in_exp); + + s1_g *= exp_thing * exp_f; + s2_g *= exp_thing * exp_f; + s3_g *= exp_thing * exp_f; + + m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1))), sqrt(s3_g / (2 * (s3 - 1))); + + break; + } + case igl::SLIMData::CONFORMAL: + { + double common_div = 9 * (pow(s1 * s2 * s3, 5. / 3.)); + + double s1_g = (-2 * s2 * s3 * (pow(s2, 2) + pow(s3, 2) - 2 * pow(s1, 2))) / common_div; + double s2_g = (-2 * s1 * s3 * (pow(s1, 2) + pow(s3, 2) - 2 * pow(s2, 2))) / common_div; + double s3_g = (-2 * s1 * s2 * (pow(s1, 2) + pow(s2, 2) - 2 * pow(s3, 2))) / common_div; + + double closest_s = sqrt(pow(s1, 2) + pow(s3, 2)) / sqrt_2; + double s1_min = closest_s; + double s2_min = closest_s; + double s3_min = closest_s; + + m_sing_new << sqrt(s1_g / (2 * (s1 - s1_min))), sqrt(s2_g / (2 * (s2 - s2_min))), sqrt( + s3_g / (2 * (s3 - s3_min))); + + // change local step + closest_sing_vec << s1_min, s2_min, s3_min; + ri = ui * closest_sing_vec.asDiagonal() * vi.transpose(); + break; + } + case igl::SLIMData::EXP_CONFORMAL: + { + // E_conf = (s1^2 + s2^2 + s3^2)/(3*(s1*s2*s3)^(2/3) ) + // dE_conf/ds1 = (-2*(s2*s3)*(s2^2+s3^2 -2*s1^2) ) / (9*(s1*s2*s3)^(5/3)) + // Argmin E_conf(s1): s1 = sqrt(s1^2+s2^2)/sqrt(2) + double common_div = 9 * (pow(s1 * s2 * s3, 5. / 3.)); + + double s1_g = (-2 * s2 * s3 * (pow(s2, 2) + pow(s3, 2) - 2 * pow(s1, 2))) / common_div; + double s2_g = (-2 * s1 * s3 * (pow(s1, 2) + pow(s3, 2) - 2 * pow(s2, 2))) / common_div; + double s3_g = (-2 * s1 * s2 * (pow(s1, 2) + pow(s2, 2) - 2 * pow(s3, 2))) / common_div; + + double in_exp = exp_f * ((pow(s1, 2) + pow(s2, 2) + pow(s3, 2)) / (3 * pow((s1 * s2 * s3), 2. / 3)));; + double exp_thing = exp(in_exp); + + double closest_s = sqrt(pow(s1, 2) + pow(s3, 2)) / sqrt_2; + double s1_min = closest_s; + double s2_min = closest_s; + double s3_min = closest_s; + + s1_g *= exp_thing * exp_f; + s2_g *= exp_thing * exp_f; + s3_g *= exp_thing * exp_f; + + m_sing_new << sqrt(s1_g / (2 * (s1 - s1_min))), sqrt(s2_g / (2 * (s2 - s2_min))), sqrt( + s3_g / (2 * (s3 - s3_min))); + + // change local step + closest_sing_vec << s1_min, s2_min, s3_min; + ri = ui * closest_sing_vec.asDiagonal() * vi.transpose(); + } + } + if (std::abs(s1 - 1) < eps) m_sing_new(0) = 1; + if (std::abs(s2 - 1) < eps) m_sing_new(1) = 1; + if (std::abs(s3 - 1) < eps) m_sing_new(2) = 1; + Mat3 mat_W; + mat_W = ui * m_sing_new.asDiagonal() * ui.transpose(); + + s.W_11(i) = mat_W(0, 0); + s.W_12(i) = mat_W(0, 1); + s.W_13(i) = mat_W(0, 2); + s.W_21(i) = mat_W(1, 0); + s.W_22(i) = mat_W(1, 1); + s.W_23(i) = mat_W(1, 2); + s.W_31(i) = mat_W(2, 0); + s.W_32(i) = mat_W(2, 1); + s.W_33(i) = mat_W(2, 2); + + // 2) Update closest rotations (not rotations in case of conformal energy) + s.Ri(i, 0) = ri(0, 0); + s.Ri(i, 1) = ri(1, 0); + s.Ri(i, 2) = ri(2, 0); + s.Ri(i, 3) = ri(0, 1); + s.Ri(i, 4) = ri(1, 1); + s.Ri(i, 5) = ri(2, 1); + s.Ri(i, 6) = ri(0, 2); + s.Ri(i, 7) = ri(1, 2); + s.Ri(i, 8) = ri(2, 2); + } // for loop end + + } // if dim end + + } + + IGL_INLINE void solve_weighted_arap(igl::SLIMData& s, + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + Eigen::MatrixXd &uv, + Eigen::VectorXi &soft_b_p, + Eigen::MatrixXd &soft_bc_p) + { + using namespace Eigen; + + Eigen::SparseMatrix L; + build_linear_system(s,L); + + igl::Timer t; + + //t.start(); + // solve + Eigen::VectorXd Uc; +#ifndef CHOLMOD + if (s.dim == 2) + { + SimplicialLDLT > solver; + Uc = solver.compute(L).solve(s.rhs); + } + else + { // seems like CG performs much worse for 2D and way better for 3D + Eigen::VectorXd guess(uv.rows() * s.dim); + for (int i = 0; i < s.v_num; i++) for (int j = 0; j < s.dim; j++) guess(uv.rows() * j + i) = uv(i, j); // flatten vector + ConjugateGradient, Lower | Upper> cg; + cg.setTolerance(1e-8); + cg.compute(L); + Uc = cg.solveWithGuess(s.rhs, guess); + } +#else + CholmodSimplicialLDLT > solver; + Uc = solver.compute(L).solve(s.rhs); +#endif + for (int i = 0; i < s.dim; i++) + uv.col(i) = Uc.block(i * s.v_n, 0, s.v_n, 1); + + // t.stop(); + // std::cerr << "solve: " << t.getElapsedTime() << std::endl; + + } + + + IGL_INLINE void pre_calc(igl::SLIMData& s) + { + if (!s.has_pre_calc) + { + s.v_n = s.v_num; + s.f_n = s.f_num; + + if (s.F.cols() == 3) + { + s.dim = 2; + Eigen::MatrixXd F1, F2, F3; + igl::local_basis(s.V, s.F, F1, F2, F3); + compute_surface_gradient_matrix(s.V, s.F, F1, F2, s.Dx, s.Dy); + + s.W_11.resize(s.f_n); + s.W_12.resize(s.f_n); + s.W_21.resize(s.f_n); + s.W_22.resize(s.f_n); + } + else + { + s.dim = 3; + Eigen::SparseMatrix G; + igl::grad(s.V, s.F, G, + s.mesh_improvement_3d /*use normal gradient, or one from a "regular" tet*/); + s.Dx = G.block(0, 0, s.F.rows(), s.V.rows()); + s.Dy = G.block(s.F.rows(), 0, s.F.rows(), s.V.rows()); + s.Dz = G.block(2 * s.F.rows(), 0, s.F.rows(), s.V.rows()); + + + s.W_11.resize(s.f_n); + s.W_12.resize(s.f_n); + s.W_13.resize(s.f_n); + s.W_21.resize(s.f_n); + s.W_22.resize(s.f_n); + s.W_23.resize(s.f_n); + s.W_31.resize(s.f_n); + s.W_32.resize(s.f_n); + s.W_33.resize(s.f_n); + } + + s.Dx.makeCompressed(); + s.Dy.makeCompressed(); + s.Dz.makeCompressed(); + s.Ri.resize(s.f_n, s.dim * s.dim); + s.Ji.resize(s.f_n, s.dim * s.dim); + s.rhs.resize(s.dim * s.v_num); + + // flattened weight matrix + s.WGL_M.resize(s.dim * s.dim * s.f_n); + for (int i = 0; i < s.dim * s.dim; i++) + for (int j = 0; j < s.f_n; j++) + s.WGL_M(i * s.f_n + j) = s.M(j); + + s.first_solve = true; + s.has_pre_calc = true; + } + } + + IGL_INLINE void build_linear_system(igl::SLIMData& s, Eigen::SparseMatrix &L) + { + // formula (35) in paper + std::vector > IJV; + + #ifdef SLIM_CACHED + buildA(s,IJV); + if (s.A.rows() == 0) + { + s.A = Eigen::SparseMatrix(s.dim * s.dim * s.f_n, s.dim * s.v_n); + igl::sparse_cached_precompute(IJV,s.A_data,s.A); + } + else + igl::sparse_cached(IJV,s.A_data,s.A); + #else + Eigen::SparseMatrix A(s.dim * s.dim * s.f_n, s.dim * s.v_n); + buildA(s,IJV); + A.setFromTriplets(IJV.begin(),IJV.end()); + A.makeCompressed(); + #endif + + #ifdef SLIM_CACHED + #else + Eigen::SparseMatrix At = A.transpose(); + At.makeCompressed(); + #endif + + #ifdef SLIM_CACHED + Eigen::SparseMatrix id_m(s.A.cols(), s.A.cols()); + #else + Eigen::SparseMatrix id_m(A.cols(), A.cols()); + #endif + + id_m.setIdentity(); + + // add proximal penalty + #ifdef SLIM_CACHED + s.AtA_data.W = s.WGL_M; + if (s.AtA.rows() == 0) + igl::AtA_cached_precompute(s.A,s.AtA_data,s.AtA); + else + igl::AtA_cached(s.A,s.AtA_data,s.AtA); + + L = s.AtA + s.proximal_p * id_m; //add also a proximal + L.makeCompressed(); + + #else + L = At * s.WGL_M.asDiagonal() * A + s.proximal_p * id_m; //add also a proximal term + L.makeCompressed(); + #endif + + #ifdef SLIM_CACHED + buildRhs(s, s.A); + #else + buildRhs(s, A); + #endif + + Eigen::SparseMatrix OldL = L; + add_soft_constraints(s,L); + L.makeCompressed(); + } + + IGL_INLINE void add_soft_constraints(igl::SLIMData& s, Eigen::SparseMatrix &L) + { + int v_n = s.v_num; + for (int d = 0; d < s.dim; d++) + { + for (int i = 0; i < s.b.rows(); i++) + { + int v_idx = s.b(i); + s.rhs(d * v_n + v_idx) += s.soft_const_p * s.bc(i, d); // rhs + L.coeffRef(d * v_n + v_idx, d * v_n + v_idx) += s.soft_const_p; // diagonal of matrix + } + } + } + + IGL_INLINE double compute_energy(igl::SLIMData& s, Eigen::MatrixXd &V_new) + { + compute_jacobians(s,V_new); + return compute_energy_with_jacobians(s, s.V, s.F, s.Ji, V_new, s.M) + + compute_soft_const_energy(s, s.V, s.F, V_new); + } + + IGL_INLINE double compute_soft_const_energy(igl::SLIMData& s, + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + Eigen::MatrixXd &V_o) + { + double e = 0; + for (int i = 0; i < s.b.rows(); i++) + { + e += s.soft_const_p * (s.bc.row(i) - V_o.row(s.b(i))).squaredNorm(); + } + return e; + } + + IGL_INLINE double compute_energy_with_jacobians(igl::SLIMData& s, + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, const Eigen::MatrixXd &Ji, + Eigen::MatrixXd &uv, Eigen::VectorXd &areas) + { + + double energy = 0; + if (s.dim == 2) + { + Eigen::Matrix ji; + for (int i = 0; i < s.f_n; i++) + { + ji(0, 0) = Ji(i, 0); + ji(0, 1) = Ji(i, 1); + ji(1, 0) = Ji(i, 2); + ji(1, 1) = Ji(i, 3); + + typedef Eigen::Matrix Mat2; + typedef Eigen::Matrix Vec2; + Mat2 ri, ti, ui, vi; + Vec2 sing; + igl::polar_svd(ji, ri, ti, ui, sing, vi); + double s1 = sing(0); + double s2 = sing(1); + + switch (s.slim_energy) + { + case igl::SLIMData::ARAP: + { + energy += areas(i) * (pow(s1 - 1, 2) + pow(s2 - 1, 2)); + break; + } + case igl::SLIMData::SYMMETRIC_DIRICHLET: + { + energy += areas(i) * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2)); + break; + } + case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET: + { + energy += areas(i) * exp(s.exp_factor * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2))); + break; + } + case igl::SLIMData::LOG_ARAP: + { + energy += areas(i) * (pow(log(s1), 2) + pow(log(s2), 2)); + break; + } + case igl::SLIMData::CONFORMAL: + { + energy += areas(i) * ((pow(s1, 2) + pow(s2, 2)) / (2 * s1 * s2)); + break; + } + case igl::SLIMData::EXP_CONFORMAL: + { + energy += areas(i) * exp(s.exp_factor * ((pow(s1, 2) + pow(s2, 2)) / (2 * s1 * s2))); + break; + } + + } + + } + } + else + { + Eigen::Matrix ji; + for (int i = 0; i < s.f_n; i++) + { + ji(0, 0) = Ji(i, 0); + ji(0, 1) = Ji(i, 1); + ji(0, 2) = Ji(i, 2); + ji(1, 0) = Ji(i, 3); + ji(1, 1) = Ji(i, 4); + ji(1, 2) = Ji(i, 5); + ji(2, 0) = Ji(i, 6); + ji(2, 1) = Ji(i, 7); + ji(2, 2) = Ji(i, 8); + + typedef Eigen::Matrix Mat3; + typedef Eigen::Matrix Vec3; + Mat3 ri, ti, ui, vi; + Vec3 sing; + igl::polar_svd(ji, ri, ti, ui, sing, vi); + double s1 = sing(0); + double s2 = sing(1); + double s3 = sing(2); + + switch (s.slim_energy) + { + case igl::SLIMData::ARAP: + { + energy += areas(i) * (pow(s1 - 1, 2) + pow(s2 - 1, 2) + pow(s3 - 1, 2)); + break; + } + case igl::SLIMData::SYMMETRIC_DIRICHLET: + { + energy += areas(i) * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2) + pow(s3, 2) + pow(s3, -2)); + break; + } + case igl::SLIMData::EXP_SYMMETRIC_DIRICHLET: + { + energy += areas(i) * exp(s.exp_factor * + (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2) + pow(s3, 2) + pow(s3, -2))); + break; + } + case igl::SLIMData::LOG_ARAP: + { + energy += areas(i) * (pow(log(s1), 2) + pow(log(std::abs(s2)), 2) + pow(log(std::abs(s3)), 2)); + break; + } + case igl::SLIMData::CONFORMAL: + { + energy += areas(i) * ((pow(s1, 2) + pow(s2, 2) + pow(s3, 2)) / (3 * pow(s1 * s2 * s3, 2. / 3.))); + break; + } + case igl::SLIMData::EXP_CONFORMAL: + { + energy += areas(i) * exp((pow(s1, 2) + pow(s2, 2) + pow(s3, 2)) / (3 * pow(s1 * s2 * s3, 2. / 3.))); + break; + } + } + } + } + + return energy; + } + + IGL_INLINE void buildA(igl::SLIMData& s, std::vector > & IJV) + { + // formula (35) in paper + if (s.dim == 2) + { + IJV.reserve(4 * (s.Dx.outerSize() + s.Dy.outerSize())); + + /*A = [W11*Dx, W12*Dx; + W11*Dy, W12*Dy; + W21*Dx, W22*Dx; + W21*Dy, W22*Dy];*/ + for (int k = 0; k < s.Dx.outerSize(); ++k) + { + for (Eigen::SparseMatrix::InnerIterator it(s.Dx, k); it; ++it) + { + int dx_r = it.row(); + int dx_c = it.col(); + double val = it.value(); + + IJV.push_back(Eigen::Triplet(dx_r, dx_c, val * s.W_11(dx_r))); + IJV.push_back(Eigen::Triplet(dx_r, s.v_n + dx_c, val * s.W_12(dx_r))); + + IJV.push_back(Eigen::Triplet(2 * s.f_n + dx_r, dx_c, val * s.W_21(dx_r))); + IJV.push_back(Eigen::Triplet(2 * s.f_n + dx_r, s.v_n + dx_c, val * s.W_22(dx_r))); + } + } + + for (int k = 0; k < s.Dy.outerSize(); ++k) + { + for (Eigen::SparseMatrix::InnerIterator it(s.Dy, k); it; ++it) + { + int dy_r = it.row(); + int dy_c = it.col(); + double val = it.value(); + + IJV.push_back(Eigen::Triplet(s.f_n + dy_r, dy_c, val * s.W_11(dy_r))); + IJV.push_back(Eigen::Triplet(s.f_n + dy_r, s.v_n + dy_c, val * s.W_12(dy_r))); + + IJV.push_back(Eigen::Triplet(3 * s.f_n + dy_r, dy_c, val * s.W_21(dy_r))); + IJV.push_back(Eigen::Triplet(3 * s.f_n + dy_r, s.v_n + dy_c, val * s.W_22(dy_r))); + } + } + } + else + { + + /*A = [W11*Dx, W12*Dx, W13*Dx; + W11*Dy, W12*Dy, W13*Dy; + W11*Dz, W12*Dz, W13*Dz; + W21*Dx, W22*Dx, W23*Dx; + W21*Dy, W22*Dy, W23*Dy; + W21*Dz, W22*Dz, W23*Dz; + W31*Dx, W32*Dx, W33*Dx; + W31*Dy, W32*Dy, W33*Dy; + W31*Dz, W32*Dz, W33*Dz;];*/ + IJV.reserve(9 * (s.Dx.outerSize() + s.Dy.outerSize() + s.Dz.outerSize())); + for (int k = 0; k < s.Dx.outerSize(); k++) + { + for (Eigen::SparseMatrix::InnerIterator it(s.Dx, k); it; ++it) + { + int dx_r = it.row(); + int dx_c = it.col(); + double val = it.value(); + + IJV.push_back(Eigen::Triplet(dx_r, dx_c, val * s.W_11(dx_r))); + IJV.push_back(Eigen::Triplet(dx_r, s.v_n + dx_c, val * s.W_12(dx_r))); + IJV.push_back(Eigen::Triplet(dx_r, 2 * s.v_n + dx_c, val * s.W_13(dx_r))); + + IJV.push_back(Eigen::Triplet(3 * s.f_n + dx_r, dx_c, val * s.W_21(dx_r))); + IJV.push_back(Eigen::Triplet(3 * s.f_n + dx_r, s.v_n + dx_c, val * s.W_22(dx_r))); + IJV.push_back(Eigen::Triplet(3 * s.f_n + dx_r, 2 * s.v_n + dx_c, val * s.W_23(dx_r))); + + IJV.push_back(Eigen::Triplet(6 * s.f_n + dx_r, dx_c, val * s.W_31(dx_r))); + IJV.push_back(Eigen::Triplet(6 * s.f_n + dx_r, s.v_n + dx_c, val * s.W_32(dx_r))); + IJV.push_back(Eigen::Triplet(6 * s.f_n + dx_r, 2 * s.v_n + dx_c, val * s.W_33(dx_r))); + } + } + + for (int k = 0; k < s.Dy.outerSize(); k++) + { + for (Eigen::SparseMatrix::InnerIterator it(s.Dy, k); it; ++it) + { + int dy_r = it.row(); + int dy_c = it.col(); + double val = it.value(); + + IJV.push_back(Eigen::Triplet(s.f_n + dy_r, dy_c, val * s.W_11(dy_r))); + IJV.push_back(Eigen::Triplet(s.f_n + dy_r, s.v_n + dy_c, val * s.W_12(dy_r))); + IJV.push_back(Eigen::Triplet(s.f_n + dy_r, 2 * s.v_n + dy_c, val * s.W_13(dy_r))); + + IJV.push_back(Eigen::Triplet(4 * s.f_n + dy_r, dy_c, val * s.W_21(dy_r))); + IJV.push_back(Eigen::Triplet(4 * s.f_n + dy_r, s.v_n + dy_c, val * s.W_22(dy_r))); + IJV.push_back(Eigen::Triplet(4 * s.f_n + dy_r, 2 * s.v_n + dy_c, val * s.W_23(dy_r))); + + IJV.push_back(Eigen::Triplet(7 * s.f_n + dy_r, dy_c, val * s.W_31(dy_r))); + IJV.push_back(Eigen::Triplet(7 * s.f_n + dy_r, s.v_n + dy_c, val * s.W_32(dy_r))); + IJV.push_back(Eigen::Triplet(7 * s.f_n + dy_r, 2 * s.v_n + dy_c, val * s.W_33(dy_r))); + } + } + + for (int k = 0; k < s.Dz.outerSize(); k++) + { + for (Eigen::SparseMatrix::InnerIterator it(s.Dz, k); it; ++it) + { + int dz_r = it.row(); + int dz_c = it.col(); + double val = it.value(); + + IJV.push_back(Eigen::Triplet(2 * s.f_n + dz_r, dz_c, val * s.W_11(dz_r))); + IJV.push_back(Eigen::Triplet(2 * s.f_n + dz_r, s.v_n + dz_c, val * s.W_12(dz_r))); + IJV.push_back(Eigen::Triplet(2 * s.f_n + dz_r, 2 * s.v_n + dz_c, val * s.W_13(dz_r))); + + IJV.push_back(Eigen::Triplet(5 * s.f_n + dz_r, dz_c, val * s.W_21(dz_r))); + IJV.push_back(Eigen::Triplet(5 * s.f_n + dz_r, s.v_n + dz_c, val * s.W_22(dz_r))); + IJV.push_back(Eigen::Triplet(5 * s.f_n + dz_r, 2 * s.v_n + dz_c, val * s.W_23(dz_r))); + + IJV.push_back(Eigen::Triplet(8 * s.f_n + dz_r, dz_c, val * s.W_31(dz_r))); + IJV.push_back(Eigen::Triplet(8 * s.f_n + dz_r, s.v_n + dz_c, val * s.W_32(dz_r))); + IJV.push_back(Eigen::Triplet(8 * s.f_n + dz_r, 2 * s.v_n + dz_c, val * s.W_33(dz_r))); + } + } + } + } + + IGL_INLINE void buildRhs(igl::SLIMData& s, const Eigen::SparseMatrix &A) + { + Eigen::VectorXd f_rhs(s.dim * s.dim * s.f_n); + f_rhs.setZero(); + if (s.dim == 2) + { + /*b = [W11*R11 + W12*R21; (formula (36)) + W11*R12 + W12*R22; + W21*R11 + W22*R21; + W21*R12 + W22*R22];*/ + for (int i = 0; i < s.f_n; i++) + { + f_rhs(i + 0 * s.f_n) = s.W_11(i) * s.Ri(i, 0) + s.W_12(i) * s.Ri(i, 1); + f_rhs(i + 1 * s.f_n) = s.W_11(i) * s.Ri(i, 2) + s.W_12(i) * s.Ri(i, 3); + f_rhs(i + 2 * s.f_n) = s.W_21(i) * s.Ri(i, 0) + s.W_22(i) * s.Ri(i, 1); + f_rhs(i + 3 * s.f_n) = s.W_21(i) * s.Ri(i, 2) + s.W_22(i) * s.Ri(i, 3); + } + } + else + { + /*b = [W11*R11 + W12*R21 + W13*R31; + W11*R12 + W12*R22 + W13*R32; + W11*R13 + W12*R23 + W13*R33; + W21*R11 + W22*R21 + W23*R31; + W21*R12 + W22*R22 + W23*R32; + W21*R13 + W22*R23 + W23*R33; + W31*R11 + W32*R21 + W33*R31; + W31*R12 + W32*R22 + W33*R32; + W31*R13 + W32*R23 + W33*R33;];*/ + for (int i = 0; i < s.f_n; i++) + { + f_rhs(i + 0 * s.f_n) = s.W_11(i) * s.Ri(i, 0) + s.W_12(i) * s.Ri(i, 1) + s.W_13(i) * s.Ri(i, 2); + f_rhs(i + 1 * s.f_n) = s.W_11(i) * s.Ri(i, 3) + s.W_12(i) * s.Ri(i, 4) + s.W_13(i) * s.Ri(i, 5); + f_rhs(i + 2 * s.f_n) = s.W_11(i) * s.Ri(i, 6) + s.W_12(i) * s.Ri(i, 7) + s.W_13(i) * s.Ri(i, 8); + f_rhs(i + 3 * s.f_n) = s.W_21(i) * s.Ri(i, 0) + s.W_22(i) * s.Ri(i, 1) + s.W_23(i) * s.Ri(i, 2); + f_rhs(i + 4 * s.f_n) = s.W_21(i) * s.Ri(i, 3) + s.W_22(i) * s.Ri(i, 4) + s.W_23(i) * s.Ri(i, 5); + f_rhs(i + 5 * s.f_n) = s.W_21(i) * s.Ri(i, 6) + s.W_22(i) * s.Ri(i, 7) + s.W_23(i) * s.Ri(i, 8); + f_rhs(i + 6 * s.f_n) = s.W_31(i) * s.Ri(i, 0) + s.W_32(i) * s.Ri(i, 1) + s.W_33(i) * s.Ri(i, 2); + f_rhs(i + 7 * s.f_n) = s.W_31(i) * s.Ri(i, 3) + s.W_32(i) * s.Ri(i, 4) + s.W_33(i) * s.Ri(i, 5); + f_rhs(i + 8 * s.f_n) = s.W_31(i) * s.Ri(i, 6) + s.W_32(i) * s.Ri(i, 7) + s.W_33(i) * s.Ri(i, 8); + } + } + Eigen::VectorXd uv_flat(s.dim *s.v_n); + for (int i = 0; i < s.dim; i++) + for (int j = 0; j < s.v_n; j++) + uv_flat(s.v_n * i + j) = s.V_o(j, i); + + s.rhs = (f_rhs.transpose() * s.WGL_M.asDiagonal() * A).transpose() + s.proximal_p * uv_flat; + } + + } +} + +/// Slim Implementation + +IGL_INLINE void igl::slim_precompute( + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + const Eigen::MatrixXd &V_init, + SLIMData &data, + SLIMData::SLIM_ENERGY slim_energy, + Eigen::VectorXi &b, + Eigen::MatrixXd &bc, + double soft_p) +{ + + data.V = V; + data.F = F; + data.V_o = V_init; + + data.v_num = V.rows(); + data.f_num = F.rows(); + + data.slim_energy = slim_energy; + + data.b = b; + data.bc = bc; + data.soft_const_p = soft_p; + + data.proximal_p = 0.0001; + + igl::doublearea(V, F, data.M); + data.M /= 2.; + data.mesh_area = data.M.sum(); + data.mesh_improvement_3d = false; // whether to use a jacobian derived from a real mesh or an abstract regular mesh (used for mesh improvement) + data.exp_factor = 1.0; // param used only for exponential energies (e.g exponential symmetric dirichlet) + + assert (F.cols() == 3 || F.cols() == 4); + + igl::slim::pre_calc(data); + data.energy = igl::slim::compute_energy(data,data.V_o) / data.mesh_area; +} + +IGL_INLINE Eigen::MatrixXd igl::slim_solve(SLIMData &data, int iter_num) +{ + for (int i = 0; i < iter_num; i++) + { + Eigen::MatrixXd dest_res; + dest_res = data.V_o; + + // Solve Weighted Proxy + igl::slim::update_weights_and_closest_rotations(data,data.V, data.F, dest_res); + igl::slim::solve_weighted_arap(data,data.V, data.F, dest_res, data.b, data.bc); + + double old_energy = data.energy; + + std::function compute_energy = [&]( + Eigen::MatrixXd &aaa) { return igl::slim::compute_energy(data,aaa); }; + + data.energy = igl::flip_avoiding_line_search(data.F, data.V_o, dest_res, compute_energy, + data.energy * data.mesh_area) / data.mesh_area; + } + return data.V_o; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/slim.h b/src/igl/slim.h new file mode 100644 index 0000000000..1965e2bba1 --- /dev/null +++ b/src/igl/slim.h @@ -0,0 +1,113 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Michael Rabinovich +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef SLIM_H +#define SLIM_H + +#include "igl_inline.h" +#include +#include + +// This option makes the iterations faster (all except the first) by caching the +// sparsity pattern of the matrix involved in the assembly. It should be on if you plan to do many iterations, off if you have to change the matrix structure at every iteration. +#define SLIM_CACHED + +#ifdef SLIM_CACHED +#include +#endif + +namespace igl +{ + +// Compute a SLIM map as derived in "Scalable Locally Injective Maps" [Rabinovich et al. 2016]. +struct SLIMData +{ + // Input + Eigen::MatrixXd V; // #V by 3 list of mesh vertex positions + Eigen::MatrixXi F; // #F by 3/3 list of mesh faces (triangles/tets) + enum SLIM_ENERGY + { + ARAP, + LOG_ARAP, + SYMMETRIC_DIRICHLET, + CONFORMAL, + EXP_CONFORMAL, + EXP_SYMMETRIC_DIRICHLET + }; + SLIM_ENERGY slim_energy; + + // Optional Input + // soft constraints + Eigen::VectorXi b; + Eigen::MatrixXd bc; + double soft_const_p; + + double exp_factor; // used for exponential energies, ignored otherwise + bool mesh_improvement_3d; // only supported for 3d + + // Output + Eigen::MatrixXd V_o; // #V by dim list of mesh vertex positions (dim = 2 for parametrization, 3 otherwise) + double energy; // objective value + + // INTERNAL + Eigen::VectorXd M; + double mesh_area; + double avg_edge_length; + int v_num; + int f_num; + double proximal_p; + + Eigen::VectorXd WGL_M; + Eigen::VectorXd rhs; + Eigen::MatrixXd Ri,Ji; + Eigen::VectorXd W_11; Eigen::VectorXd W_12; Eigen::VectorXd W_13; + Eigen::VectorXd W_21; Eigen::VectorXd W_22; Eigen::VectorXd W_23; + Eigen::VectorXd W_31; Eigen::VectorXd W_32; Eigen::VectorXd W_33; + Eigen::SparseMatrix Dx,Dy,Dz; + int f_n,v_n; + bool first_solve; + bool has_pre_calc = false; + int dim; + + #ifdef SLIM_CACHED + Eigen::SparseMatrix A; + Eigen::VectorXi A_data; + Eigen::SparseMatrix AtA; + igl::AtA_cached_data AtA_data; + #endif +}; + +// Compute necessary information to start using SLIM +// Inputs: +// V #V by 3 list of mesh vertex positions +// F #F by 3/3 list of mesh faces (triangles/tets) +// b list of boundary indices into V +// bc #b by dim list of boundary conditions +// soft_p Soft penalty factor (can be zero) +// slim_energy Energy to minimize +IGL_INLINE void slim_precompute( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::MatrixXd& V_init, + SLIMData& data, + SLIMData::SLIM_ENERGY slim_energy, + Eigen::VectorXi& b, + Eigen::MatrixXd& bc, + double soft_p); + +// Run iter_num iterations of SLIM +// Outputs: +// V_o (in SLIMData): #V by dim list of mesh vertex positions +IGL_INLINE Eigen::MatrixXd slim_solve(SLIMData& data, int iter_num); + +} // END NAMESPACE + +#ifndef IGL_STATIC_LIBRARY +# include "slim.cpp" +#endif + +#endif // SLIM_H diff --git a/src/igl/snap_points.cpp b/src/igl/snap_points.cpp new file mode 100644 index 0000000000..1fc136f4c6 --- /dev/null +++ b/src/igl/snap_points.cpp @@ -0,0 +1,89 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "snap_points.h" +#include +#include + +template < + typename DerivedC, + typename DerivedV, + typename DerivedI, + typename DerivedminD, + typename DerivedVI> +IGL_INLINE void igl::snap_points( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & minD, + Eigen::PlainObjectBase & VI) +{ + snap_points(C,V,I,minD); + const int m = C.rows(); + VI.resize(m,V.cols()); + for(int c = 0;c +IGL_INLINE void igl::snap_points( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & minD) +{ + using namespace std; + const int n = V.rows(); + const int m = C.rows(); + assert(V.cols() == C.cols() && "Dimensions should match"); + // O(m*n) + // + // I believe there should be a way to do this in O(m*log(n) + n) assuming + // reasonably distubed points. + I.resize(m,1); + typedef typename DerivedV::Scalar Scalar; + minD.setConstant(m,1,numeric_limits::max()); + for(int v = 0;v +IGL_INLINE void igl::snap_points( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & I) +{ + Eigen::Matrix minD; + return igl::snap_points(C,V,I,minD); +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::snap_points, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::snap_points, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/snap_points.h b/src/igl/snap_points.h new file mode 100644 index 0000000000..dbb850a79c --- /dev/null +++ b/src/igl/snap_points.h @@ -0,0 +1,67 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SNAP_POINTS_H +#define IGL_SNAP_POINTS_H + +#include "igl_inline.h" +#include + +namespace igl +{ + // SNAP_POINTS snap list of points C to closest of another list of points V + // + // [I,minD,VI] = snap_points(C,V) + // + // Inputs: + // C #C by dim list of query point positions + // V #V by dim list of data point positions + // Outputs: + // I #C list of indices into V of closest points to C + // minD #C list of squared (^p) distances to closest points + // VI #C by dim list of new point positions, VI = V(I,:) + template < + typename DerivedC, + typename DerivedV, + typename DerivedI, + typename DerivedminD, + typename DerivedVI> + IGL_INLINE void snap_points( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & minD, + Eigen::PlainObjectBase & VI); + template < + typename DerivedC, + typename DerivedV, + typename DerivedI, + typename DerivedminD> + IGL_INLINE void snap_points( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & I, + Eigen::PlainObjectBase & minD); + template < + typename DerivedC, + typename DerivedV, + typename DerivedI > + IGL_INLINE void snap_points( + const Eigen::PlainObjectBase & C, + const Eigen::PlainObjectBase & V, + Eigen::PlainObjectBase & I); +} + +#ifndef IGL_STATIC_LIBRARY +# include "snap_points.cpp" +#endif + +#endif + + + + diff --git a/src/igl/snap_to_canonical_view_quat.cpp b/src/igl/snap_to_canonical_view_quat.cpp new file mode 100644 index 0000000000..9fb01724c5 --- /dev/null +++ b/src/igl/snap_to_canonical_view_quat.cpp @@ -0,0 +1,117 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "snap_to_canonical_view_quat.h" + +#include "canonical_quaternions.h" +#include "normalize_quat.h" + +#include +#include + +// Note: For the canonical view quaternions it should be completely possible to +// determine this anaylitcally. That is the max_distance should be a +// theoretical known value +// Also: I'm not sure it matters in this case, but. We are dealing with +// quaternions on the 4d unit sphere, but measuring distance in general 4d +// space (i.e. not geodesics on the sphere). Probably something with angles +// would be better. +template +IGL_INLINE bool igl::snap_to_canonical_view_quat( + const Q_type* q, + const Q_type threshold, + Q_type* s) +{ + // Copy input into output + // CANNOT use std::copy here according to: + // http://www.cplusplus.com/reference/algorithm/copy/ + s[0] = q[0]; + s[1] = q[1]; + s[2] = q[2]; + s[3] = q[3]; + + // Normalize input quaternion + Q_type qn[4]; + bool valid_len = + igl::normalize_quat(q,qn); + // If normalizing valid then don't bother + if(!valid_len) + { + return false; + } + + // 0.290019 + const Q_type MAX_DISTANCE = 0.4; + Q_type min_distance = 2*MAX_DISTANCE; + int min_index = -1; + double min_sign = 0; + // loop over canonical view quaternions + for(double sign = -1;sign<=1;sign+=2) + { + for(int i = 0; i(i,j))* + (qn[j]-sign*igl::CANONICAL_VIEW_QUAT(i,j)); + } + if(min_distance > distance) + { + min_distance = distance; + min_index = i; + min_sign = sign; + } + } + } + + if(MAX_DISTANCE < min_distance) + { + fprintf( + stderr, + "ERROR: found new max MIN_DISTANCE: %g\n" + "PLEASE update snap_to_canonical_quat()", + min_distance); + } + + assert(min_distance < MAX_DISTANCE); + assert(min_index >= 0); + + if( min_distance/MAX_DISTANCE <= threshold) + { + // loop over coordinates + for(int j = 0;j<4;j++) + { + s[j] = min_sign*igl::CANONICAL_VIEW_QUAT(min_index,j); + } + return true; + } + return false; +} + +template +IGL_INLINE bool igl::snap_to_canonical_view_quat( + const Eigen::Quaternion & q, + const double threshold, + Eigen::Quaternion & s) +{ + return snap_to_canonical_view_quat( + q.coeffs().data(),threshold,s.coeffs().data()); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::snap_to_canonical_view_quat(const double*, double, double*); +// generated by autoexplicit.sh +template bool igl::snap_to_canonical_view_quat(const float*, float, float*); +template bool igl::snap_to_canonical_view_quat(Eigen::Quaternion const&, double, Eigen::Quaternion&); +template bool igl::snap_to_canonical_view_quat(Eigen::Quaternion const&, double, Eigen::Quaternion&); +#endif diff --git a/src/igl/snap_to_canonical_view_quat.h b/src/igl/snap_to_canonical_view_quat.h new file mode 100644 index 0000000000..79bc3e0246 --- /dev/null +++ b/src/igl/snap_to_canonical_view_quat.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SNAP_TO_CANONICAL_VIEW_QUAT_H +#define IGL_SNAP_TO_CANONICAL_VIEW_QUAT_H +#include "igl_inline.h" +#include +// A Quaternion, q, is defined here as an arrays of four scalars (x,y,z,w), +// such that q = x*i + y*j + z*k + w +namespace igl +{ + // Snap the quaternion q to the nearest canonical view quaternion + // Input: + // q quaternion to be snapped (also see Outputs) + // threshold (optional) threshold: + // 1.0 --> snap any input + // 0.5 --> snap inputs somewhat close to canonical views + // 0.0 --> snap no input + // Output: + // q quaternion possibly set to nearest canonical view + // Return: + // true only if q was snapped to the nearest canonical view + template + IGL_INLINE bool snap_to_canonical_view_quat( + const Q_type* q, + const Q_type threshold, + Q_type* s); + + template + IGL_INLINE bool snap_to_canonical_view_quat( + const Eigen::Quaternion & q, + const double threshold, + Eigen::Quaternion & s); +} + +#ifndef IGL_STATIC_LIBRARY +# include "snap_to_canonical_view_quat.cpp" +#endif + +#endif diff --git a/src/igl/snap_to_fixed_up.cpp b/src/igl/snap_to_fixed_up.cpp new file mode 100644 index 0000000000..f0fdd7de88 --- /dev/null +++ b/src/igl/snap_to_fixed_up.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "snap_to_fixed_up.h" + +template +IGL_INLINE void igl::snap_to_fixed_up( + const Eigen::Quaternion & q, + Eigen::Quaternion & s) +{ + using namespace Eigen; + typedef Eigen::Matrix Vector3Q; + const Vector3Q up = q.matrix() * Vector3Q(0,1,0); + Vector3Q proj_up(0,up(1),up(2)); + if(proj_up.norm() == 0) + { + proj_up = Vector3Q(0,1,0); + } + proj_up.normalize(); + Quaternion dq; + dq = Quaternion::FromTwoVectors(up,proj_up); + s = dq * q; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiations +template void igl::snap_to_fixed_up(Eigen::Quaternion const&, Eigen::Quaternion&); +template void igl::snap_to_fixed_up(Eigen::Quaternion const&, Eigen::Quaternion&); +#endif diff --git a/src/igl/snap_to_fixed_up.h b/src/igl/snap_to_fixed_up.h new file mode 100644 index 0000000000..4276fa79ca --- /dev/null +++ b/src/igl/snap_to_fixed_up.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SNAP_TO_FIXED_UP_H +#define IGL_SNAP_TO_FIXED_UP_H + +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Snap an arbitrary rotation to a rotation resulting from a rotation about + // the y-axis then the x-axis (maintaining fixed up like + // two_axis_valuator_fixed_up.) + // + // Inputs: + // q General rotation as quaternion + // Outputs: + // s the resulting rotation (as quaternion) + // + // See also: two_axis_valuator_fixed_up + template + IGL_INLINE void snap_to_fixed_up( + const Eigen::Quaternion & q, + Eigen::Quaternion & s); +} + +#ifndef IGL_STATIC_LIBRARY +# include "snap_to_fixed_up.cpp" +#endif + +#endif + + diff --git a/src/igl/solid_angle.cpp b/src/igl/solid_angle.cpp new file mode 100644 index 0000000000..bbb478af3e --- /dev/null +++ b/src/igl/solid_angle.cpp @@ -0,0 +1,81 @@ +#include "solid_angle.h" +#include "PI.h" +#include + +template < + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedP> +IGL_INLINE typename DerivedA::Scalar igl::solid_angle( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C, + const Eigen::MatrixBase & P) +{ + typedef typename DerivedA::Scalar SType; + // Gather vectors to corners + Eigen::Matrix v; + // Don't use this since it will freak out for templates with != 3 size + //v<< (A-P),(B-P),(C-P); + for(int d = 0;d<3;d++) + { + v(0,d) = A(d)-P(d); + v(1,d) = B(d)-P(d); + v(2,d) = C(d)-P(d); + } + Eigen::Matrix vl = v.rowwise().norm(); + //printf("\n"); + // Compute determinant + SType detf = + v(0,0)*v(1,1)*v(2,2)+ + v(1,0)*v(2,1)*v(0,2)+ + v(2,0)*v(0,1)*v(1,2)- + v(2,0)*v(1,1)*v(0,2)- + v(1,0)*v(0,1)*v(2,2)- + v(0,0)*v(2,1)*v(1,2); + // Compute pairwise dotproducts + Eigen::Matrix dp; + dp(0) = v(1,0)*v(2,0); + dp(0) += v(1,1)*v(2,1); + dp(0) += v(1,2)*v(2,2); + dp(1) = v(2,0)*v(0,0); + dp(1) += v(2,1)*v(0,1); + dp(1) += v(2,2)*v(0,2); + dp(2) = v(0,0)*v(1,0); + dp(2) += v(0,1)*v(1,1); + dp(2) += v(0,2)*v(1,2); + // Compute winding number + // Only divide by TWO_PI instead of 4*pi because there was a 2 out front + return atan2(detf, + vl(0)*vl(1)*vl(2) + + dp(0)*vl(0) + + dp(1)*vl(1) + + dp(2)*vl(2)) / (2.*igl::PI); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template Eigen::Block const, 1, 3, true>::Scalar igl::solid_angle const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, 3, true>::Scalar igl::solid_angle const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, -1, false>::Scalar igl::solid_angle const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, 3, true>::Scalar igl::solid_angle const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, 3, true>::Scalar igl::solid_angle const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Block const, 1, 3, true>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase const, 1, 3, true> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, 3, false>::Scalar igl::solid_angle const, 1, 3, false>, Eigen::Block const, 1, 3, false>, Eigen::Block const, 1, 3, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, false> > const&, Eigen::MatrixBase const, 1, 3, false> > const&, Eigen::MatrixBase const, 1, 3, false> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, 3, false>::Scalar igl::solid_angle const, 1, 3, false>, Eigen::Block const, 1, 3, false>, Eigen::Block const, 1, 3, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, 3, false> > const&, Eigen::MatrixBase const, 1, 3, false> > const&, Eigen::MatrixBase const, 1, 3, false> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, -1, false>::Scalar igl::solid_angle const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false> >(Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, -1, false>::Scalar igl::solid_angle const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, -1, false>::Scalar igl::solid_angle const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Block const, 1, -1, false>::Scalar igl::solid_angle const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Block const, 1, -1, false>, Eigen::Matrix >(Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase const, 1, -1, false> > const&, Eigen::MatrixBase > const&); +#endif diff --git a/src/igl/solid_angle.h b/src/igl/solid_angle.h new file mode 100644 index 0000000000..2ee98a2474 --- /dev/null +++ b/src/igl/solid_angle.h @@ -0,0 +1,29 @@ +#ifndef IGL_SOLID_ANGLE_H +#define IGL_SOLID_ANGLE_H +#include "igl_inline.h" +#include +namespace igl +{ + // Compute the signed solid angle subtended by the oriented 3d triangle (A,B,C) at some point P + // + // Inputs: + // A 3D position of corner + // B 3D position of corner + // C 3D position of corner + // P 3D position of query point + // Returns signed solid angle + template < + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedP> + IGL_INLINE typename DerivedA::Scalar solid_angle( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C, + const Eigen::MatrixBase & P); +} +#ifndef IGL_STATIC_LIBRARY +# include "solid_angle.cpp" +#endif +#endif diff --git a/src/igl/sort.cpp b/src/igl/sort.cpp new file mode 100644 index 0000000000..251b3063ec --- /dev/null +++ b/src/igl/sort.cpp @@ -0,0 +1,351 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "sort.h" + +#include "SortableRow.h" +#include "reorder.h" +#include "IndexComparison.h" +#include "colon.h" +#include "parallel_for.h" + +#include +#include +#include + +template +IGL_INLINE void igl::sort( + const Eigen::DenseBase& X, + const int dim, + const bool ascending, + Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& IX) +{ + typedef typename DerivedX::Scalar Scalar; + // get number of rows (or columns) + int num_inner = (dim == 1 ? X.rows() : X.cols() ); + // Special case for swapping + switch(num_inner) + { + default: + break; + case 2: + return igl::sort2(X,dim,ascending,Y,IX); + case 3: + return igl::sort3(X,dim,ascending,Y,IX); + } + using namespace Eigen; + // get number of columns (or rows) + int num_outer = (dim == 1 ? X.cols() : X.rows() ); + // dim must be 2 or 1 + assert(dim == 1 || dim == 2); + // Resize output + Y.resizeLike(X); + IX.resizeLike(X); + // idea is to process each column (or row) as a std vector + // loop over columns (or rows) + for(int i = 0; i index_map(num_inner); + std::vector data(num_inner); + for(int j = 0;j +IGL_INLINE void igl::sort_new( + const Eigen::DenseBase& X, + const int dim, + const bool ascending, + Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& IX) +{ + // get number of rows (or columns) + int num_inner = (dim == 1 ? X.rows() : X.cols() ); + // Special case for swapping + switch(num_inner) + { + default: + break; + case 2: + return igl::sort2(X,dim,ascending,Y,IX); + case 3: + return igl::sort3(X,dim,ascending,Y,IX); + } + using namespace Eigen; + // get number of columns (or rows) + int num_outer = (dim == 1 ? X.cols() : X.rows() ); + // dim must be 2 or 1 + assert(dim == 1 || dim == 2); + // Resize output + Y.resizeLike(X); + IX.resizeLike(X); + // idea is to process each column (or row) as a std vector + // loop over columns (or rows) + for(int i = 0; i(X.col(i))); + }else + { + std::sort( + ix.data(), + ix.data()+ix.size(), + igl::IndexVectorLessThan(X.row(i))); + } + // if not ascending then reverse + if(!ascending) + { + std::reverse(ix.data(),ix.data()+ix.size()); + } + for(int j = 0;j +IGL_INLINE void igl::sort2( + const Eigen::DenseBase& X, + const int dim, + const bool ascending, + Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& IX) +{ + using namespace Eigen; + using namespace std; + typedef typename DerivedY::Scalar YScalar; + Y = X.derived().template cast(); + + + // get number of columns (or rows) + int num_outer = (dim == 1 ? X.cols() : X.rows() ); + // get number of rows (or columns) + int num_inner = (dim == 1 ? X.rows() : X.cols() ); + assert(num_inner == 2);(void)num_inner; + typedef typename DerivedIX::Scalar Index; + IX.resizeLike(X); + if(dim==1) + { + IX.row(0).setConstant(0);// = DerivedIX::Zero(1,IX.cols()); + IX.row(1).setConstant(1);// = DerivedIX::Ones (1,IX.cols()); + }else + { + IX.col(0).setConstant(0);// = DerivedIX::Zero(IX.rows(),1); + IX.col(1).setConstant(1);// = DerivedIX::Ones (IX.rows(),1); + } + // loop over columns (or rows) + for(int i = 0;ib) || (!ascending && a +IGL_INLINE void igl::sort3( + const Eigen::DenseBase& X, + const int dim, + const bool ascending, + Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& IX) +{ + using namespace Eigen; + using namespace std; + typedef typename DerivedY::Scalar YScalar; + Y = X.derived().template cast(); + Y.resizeLike(X); + for(int j=0;j b) + { + std::swap(a,b); + std::swap(ai,bi); + } + // 123 132 123 231 132 231 + if(b > c) + { + std::swap(b,c); + std::swap(bi,ci); + // 123 123 123 213 123 213 + if(a > b) + { + std::swap(a,b); + std::swap(ai,bi); + } + // 123 123 123 123 123 123 + } + }else + { + // 123 132 213 231 312 321 + if(a < b) + { + std::swap(a,b); + std::swap(ai,bi); + } + // 213 312 213 321 312 321 + if(b < c) + { + std::swap(b,c); + std::swap(bi,ci); + // 231 321 231 321 321 321 + if(a < b) + { + std::swap(a,b); + std::swap(ai,bi); + } + // 321 321 321 321 321 321 + } + } + }; + parallel_for(num_outer,inner,16000); +} + +template +IGL_INLINE void igl::sort( +const std::vector & unsorted, +const bool ascending, +std::vector & sorted, +std::vector & index_map) +{ +// Original unsorted index map +index_map.resize(unsorted.size()); +for(size_t i=0;i& >(unsorted)); + +// if not ascending then reverse +if(!ascending) +{ + std::reverse(index_map.begin(),index_map.end()); +} + // make space for output without clobbering + sorted.resize(unsorted.size()); + // reorder unsorted into sorted using index map + igl::reorder(unsorted,index_map,sorted); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort(std::vector > const&, bool, std::vector >&, std::vector > &); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort_new, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort(std::vector > const&, bool, std::vector >&, std::vector >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, int, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::sort,class Eigen::Matrix,class Eigen::Matrix<__int64,-1,1,0,-1,1> >(class Eigen::DenseBase > const &,int,bool,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &); +#endif +#endif diff --git a/src/igl/sort.h b/src/igl/sort.h new file mode 100644 index 0000000000..f89f54b13b --- /dev/null +++ b/src/igl/sort.h @@ -0,0 +1,88 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SORT_H +#define IGL_SORT_H +#include "igl_inline.h" + +#include +#include +namespace igl +{ + + // Sort the elements of a matrix X along a given dimension like matlabs sort + // function + // + // Templates: + // DerivedX derived scalar type, e.g. MatrixXi or MatrixXd + // DerivedIX derived integer type, e.g. MatrixXi + // Inputs: + // X m by n matrix whose entries are to be sorted + // dim dimensional along which to sort: + // 1 sort each column (matlab default) + // 2 sort each row + // ascending sort ascending (true, matlab default) or descending (false) + // Outputs: + // Y m by n matrix whose entries are sorted + // IX m by n matrix of indices so that if dim = 1, then in matlab notation + // for j = 1:n, Y(:,j) = X(I(:,j),j); end + template + IGL_INLINE void sort( + const Eigen::DenseBase& X, + const int dim, + const bool ascending, + Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& IX); + template + // Only better if size(X,dim) is small + IGL_INLINE void sort_new( + const Eigen::DenseBase& X, + const int dim, + const bool ascending, + Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& IX); + // Special case if size(X,dim) == 2 + template + IGL_INLINE void sort2( + const Eigen::DenseBase& X, + const int dim, + const bool ascending, + Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& IX); + // Special case if size(X,dim) == 3 + template + IGL_INLINE void sort3( + const Eigen::DenseBase& X, + const int dim, + const bool ascending, + Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& IX); + + + // Act like matlab's [Y,I] = SORT(X) for std library vectors + // Templates: + // T should be a class that implements the '<' comparator operator + // Input: + // unsorted unsorted vector + // ascending sort ascending (true, matlab default) or descending (false) + // Output: + // sorted sorted vector, allowed to be same as unsorted + // index_map an index map such that sorted[i] = unsorted[index_map[i]] + template + IGL_INLINE void sort( + const std::vector &unsorted, + const bool ascending, + std::vector &sorted, + std::vector &index_map); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "sort.cpp" +#endif + +#endif diff --git a/src/igl/sort_angles.cpp b/src/igl/sort_angles.cpp new file mode 100644 index 0000000000..486f621ea9 --- /dev/null +++ b/src/igl/sort_angles.cpp @@ -0,0 +1,114 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "sort_angles.h" +#include "LinSpaced.h" +#include + +template +IGL_INLINE void igl::sort_angles( + const Eigen::PlainObjectBase& M, + Eigen::PlainObjectBase& R) { + const size_t num_rows = M.rows(); + const size_t num_cols = M.cols(); + assert(num_cols >= 2); + + R.resize(num_rows); + // Have to use << instead of = because Eigen's PlainObjectBase is annoying + R << igl::LinSpaced< + Eigen::Matrix > + (num_rows, 0, num_rows-1); + + // | + // (pi/2, pi) | (0, pi/2) + // | + // -------------+-------------- + // | + // (-pi, -pi/2) | (-pi/2, 0) + // | + auto comp = [&](size_t i, size_t j) { + auto yi = M(i, 0); + auto xi = M(i, 1); + auto yj = M(j, 0); + auto xj = M(j, 1); + + if (xi == xj && yi == yj) { + for (size_t idx=2; idx= 0 && yi >= 0) { + if (xj >=0 && yj >= 0) { + if (xi != xj) { + return xi > xj; + } else { + return yi < yj; + } + } else if (xj < 0 && yj >= 0) { + return true; + } else if (xj < 0 && yj < 0) { + return false; + } else { + return false; + } + } else if (xi < 0 && yi >= 0) { + if (xj >= 0 && yj >= 0) { + return false; + } else if (xj < 0 && yj >= 0) { + if (xi != xj) { + return xi > xj; + } else { + return yi > yj; + } + } else if (xj < 0 && yj < 0) { + return false; + } else { + return false; + } + } else if (xi < 0 && yi < 0) { + if (xj >= 0 && yj >= 0) { + return true; + } else if (xj < 0 && yj >= 0) { + return true; + } else if (xj < 0 && yj < 0) { + if (xi != xj) { + return xi < xj; + } else { + return yi > yj; + } + } else { + return true; + } + } else { + if (xj >= 0 && yj >= 0) { + return true; + } else if (xj < 0 && yj >= 0) { + return true; + } else if (xj < 0 && yj < 0) { + return false; + } else { + if (xi != xj) { + return xi < xj; + } else { + return yi < yj; + } + } + } + }; + std::sort(R.data(), R.data() + num_rows, comp); +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::sort_angles, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/sort_angles.h b/src/igl/sort_angles.h new file mode 100644 index 0000000000..4763c981e9 --- /dev/null +++ b/src/igl/sort_angles.h @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef SORT_ANGLES_H +#define SORT_ANGLES_H + +#include "igl_inline.h" +#include + +namespace igl { + // Sort angles in ascending order in a numerically robust way. + // + // Instead of computing angles using atan2(y, x), sort directly on (y, x). + // + // Inputs: + // M: m by n matrix of scalars. (n >= 2). Assuming the first column of M + // contains values for y, and the second column is x. Using the rest + // of the columns as tie-breaker. + // R: an array of m indices. M.row(R[i]) contains the i-th smallest + // angle. + template + IGL_INLINE void sort_angles( + const Eigen::PlainObjectBase& M, + Eigen::PlainObjectBase& R); +} + +#ifndef IGL_STATIC_LIBRARY +#include "sort_angles.cpp" +#endif + +#endif diff --git a/src/igl/sort_triangles.cpp b/src/igl/sort_triangles.cpp new file mode 100644 index 0000000000..98fd8ab1a6 --- /dev/null +++ b/src/igl/sort_triangles.cpp @@ -0,0 +1,498 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "sort_triangles.h" +#include "barycenter.h" +#include "sort.h" +#include "sortrows.h" +#include "slice.h" +#include "round.h" +#include "colon.h" + +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedMV, + typename DerivedP, + typename DerivedFF, + typename DerivedI> +IGL_INLINE void igl::sort_triangles( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & MV, + const Eigen::PlainObjectBase & P, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & I) +{ + using namespace Eigen; + using namespace std; + + + // Barycenter, centroid + Eigen::Matrix D,sD; + Eigen::Matrix BC,PBC; + barycenter(V,F,BC); + D = BC*(MV.transpose()*P.transpose().eval().col(2)); + sort(D,1,false,sD,I); + + //// Closest corner + //Eigen::Matrix D,sD; + //D.setConstant(F.rows(),1,-1e26); + //for(int c = 0;c<3;c++) + //{ + // Eigen::Matrix C; + // Eigen::Matrix DC; + // C.resize(F.rows(),4); + // for(int f = 0;fD.array()).select(DC,D).eval(); + //} + //sort(D,1,false,sD,I); + + //// Closest corner with tie breaks + //Eigen::Matrix D,sD,ssD; + //D.resize(F.rows(),3); + //for(int c = 0;c<3;c++) + //{ + // Eigen::Matrix C; + // C.resize(F.rows(),4); + // for(int f = 0;f +//#include +// +//static int tough_count = 0; +//template +//class Triangle +//{ +// public: +// static inline bool z_comp(const Vec3 & A, const Vec3 & B) +// { +// return A(2) > B(2); +// } +// static typename Vec3::Scalar ZERO() +// { +// return igl::EPS(); +// return 0; +// } +// public: +// int id; +// // Sorted projected coners: c[0] has smallest z value +// Vec3 c[3]; +// Vec3 n; +// public: +// Triangle():id(-1) { }; +// Triangle(int id, const Vec3 c0, const Vec3 c1, const Vec3 c2): +// id(id) +// { +// using namespace std; +// c[0] = c0; +// c[1] = c1; +// c[2] = c2; +// sort(c,c+3,Triangle::z_comp); +// // normal pointed toward viewpoint +// n = (c0-c1).cross(c2-c0); +// if(n(2) < 0) +// { +// n *= -1.0; +// } +// // Avoid NaNs +// typename Vec3::Scalar len = n.norm(); +// if(len == 0) +// { +// cout<<"avoid NaN"<=0); +// return n.dot(r-c[closest]); +// } +// +// // Z-values of this are < z-values of that +// bool is_completely_behind(const Triangle & that) const +// { +// const typename Vec3::Scalar ac0 = that.c[0](2); +// const typename Vec3::Scalar ac1 = that.c[1](2); +// const typename Vec3::Scalar ac2 = that.c[2](2); +// const typename Vec3::Scalar ic0 = this->c[0](2); +// const typename Vec3::Scalar ic1 = this->c[1](2); +// const typename Vec3::Scalar ic2 = this->c[2](2); +// return +// (ic0 < ac2 && ic1 <= ac2 && ic2 <= ac2) || +// (ic0 <= ac2 && ic1 < ac2 && ic2 <= ac2) || +// (ic0 <= ac2 && ic1 <= ac2 && ic2 < ac2); +// } +// +// bool is_behind_plane(const Triangle &that) const +// { +// using namespace std; +// const typename Vec3::Scalar apc0 = that.project(this->c[0]); +// const typename Vec3::Scalar apc1 = that.project(this->c[1]); +// const typename Vec3::Scalar apc2 = that.project(this->c[2]); +// cout<<" "<< +// apc0<<", "<< +// apc1<<", "<< +// apc2<<", "<c[0]); +// const typename Vec3::Scalar apc1 = that.project(this->c[1]); +// const typename Vec3::Scalar apc2 = that.project(this->c[2]); +// cout<<" "<< +// apc0<<", "<< +// apc1<<", "<< +// apc2<<", "< ZERO() && apc1 > ZERO() && apc2 > ZERO()); +// } +// +// bool is_coplanar(const Triangle &that) const +// { +// using namespace std; +// const typename Vec3::Scalar apc0 = that.project(this->c[0]); +// const typename Vec3::Scalar apc1 = that.project(this->c[1]); +// const typename Vec3::Scalar apc2 = that.project(this->c[2]); +// return (fabs(apc0)<=ZERO() && fabs(apc1)<=ZERO() && fabs(apc2)<=ZERO()); +// } +// +// // http://stackoverflow.com/a/14561664/148668 +// // a1 is line1 start, a2 is line1 end, b1 is line2 start, b2 is line2 end +// static bool seg_seg_intersect(const Vec3 & a1, const Vec3 & a2, const Vec3 & b1, const Vec3 & b2) +// { +// Vec3 b = a2-a1; +// Vec3 d = b2-b1; +// typename Vec3::Scalar bDotDPerp = b(0) * d(1) - b(1) * d(0); +// +// // if b dot d == 0, it means the lines are parallel so have infinite intersection points +// if (bDotDPerp == 0) +// return false; +// +// Vec3 c = b1-a1; +// typename Vec3::Scalar t = (c(0) * d(1) - c(1) * d(0)) / bDotDPerp; +// if (t < 0 || t > 1) +// return false; +// +// typename Vec3::Scalar u = (c(0) * b(1) - c(1) * b(0)) / bDotDPerp; +// if (u < 0 || u > 1) +// return false; +// +// return true; +// } +// bool has_corner_inside(const Triangle & that) const +// { +// // http://www.blackpawn.com/texts/pointinpoly/ +// // Compute vectors +// Vec3 A = that.c[0]; +// Vec3 B = that.c[1]; +// Vec3 C = that.c[2]; +// A(2) = B(2) = C(2) = 0; +// for(int ci = 0;ci<3;ci++) +// { +// Vec3 P = this->c[ci]; +// P(2) = 0; +// +// Vec3 v0 = C - A; +// Vec3 v1 = B - A; +// Vec3 v2 = P - A; +// +// // Compute dot products +// typename Vec3::Scalar dot00 = v0.dot(v0); +// typename Vec3::Scalar dot01 = v0.dot(v1); +// typename Vec3::Scalar dot02 = v0.dot(v2); +// typename Vec3::Scalar dot11 = v1.dot(v1); +// typename Vec3::Scalar dot12 = v1.dot(v2); +// +// // Compute barycentric coordinates +// typename Vec3::Scalar invDenom = 1 / (dot00 * dot11 - dot01 * dot01); +// typename Vec3::Scalar u = (dot11 * dot02 - dot01 * dot12) * invDenom; +// typename Vec3::Scalar v = (dot00 * dot12 - dot01 * dot02) * invDenom; +// +// // Check if point is in triangle +// if((u >= 0) && (v >= 0) && (u + v < 1)) +// { +// return true; +// } +// } +// return false; +// } +// +// bool overlaps(const Triangle &that) const +// { +// // Edges cross +// for(int e = 0;e<3;e++) +// { +// for(int f = 0;f<3;f++) +// { +// if(seg_seg_intersect( +// this->c[e],this->c[(e+1)%3], +// that.c[e],that.c[(e+1)%3])) +// { +// return true; +// } +// } +// } +// // This could be entirely inside that +// if(this->has_corner_inside(that)) +// { +// return true; +// } +// // vice versa +// if(that.has_corner_inside(*this)) +// { +// return true; +// } +// return false; +// } +// +// +// bool operator< (const Triangle &that) const +// { +// // THIS < THAT if "depth" of THIS < "depth" of THAT +// // " if THIS should be draw before THAT +// using namespace std; +// bool ret = false; +// // Self compare +// if(that.id == this->id) +// { +// ret = false; +// } +// if(this->is_completely_behind(that)) +// { +// cout<<" "<id<<" completely behind "<id<overlaps(that)) +// { +// assert(!that.overlaps(*this)); +// cout<<" THIS does not overlap THAT"<c[0](2) + this->c[1](2) + this->c[2](2)) > +// 1./3.*(that.c[0](2) + that.c[1](2) + that.c[2](2)); +// }else +// { +// if(this->is_coplanar(that) || that.is_coplanar(*this)) +// { +// cout<<" coplanar"<c[0](2) + this->c[1](2) + this->c[2](2)) > +// 1./3.*(that.c[0](2) + that.c[1](2) + that.c[2](2)); +// }else if(this->is_behind_plane(that)) +// { +// cout<<" THIS behind plane of THAT"<is_in_front_of_plane(that)) +// { +// cout<<" THIS in front plane of THAT"<c[0](2) + this->c[1](2) + this->c[2](2)) > +// 1./3.*(that.c[0](2) + that.c[1](2) + that.c[2](2)); +// } +// } +// } +// if(ret) +// { +// // THIS < THAT so better not be THAT < THIS +// cout<id<<" < "<= THAT so could be THAT < THIS or THAT == THIS +// } +// return ret; +// } +//}; +//#include +// +//template < +// typename DerivedV, +// typename DerivedF, +// typename DerivedMV, +// typename DerivedP, +// typename DerivedFF, +// typename DerivedI> +//IGL_INLINE void igl::sort_triangles_robust( +// const Eigen::PlainObjectBase & V, +// const Eigen::PlainObjectBase & F, +// const Eigen::PlainObjectBase & MV, +// const Eigen::PlainObjectBase & P, +// Eigen::PlainObjectBase & FF, +// Eigen::PlainObjectBase & I) +//{ +// assert(false && +// "THIS WILL NEVER WORK because depth sorting is not a numerical sort where" +// "pairwise comparisons of triangles are transitive. Rather it is a" +// "topological sort on a dependency graph. Dependency encodes 'This triangle" +// "must be drawn before that one'"); +// using namespace std; +// using namespace Eigen; +// typedef Matrix Vec3; +// assert(V.cols() == 4); +// Matrix VMVP = +// V*(MV.transpose()*P.transpose().eval().block(0,0,4,3)); +// +// MatrixXd projV(V.rows(),3); +// for(int v = 0;v > vF(F.rows()); +// MatrixXd N(F.rows(),3); +// MatrixXd C(F.rows()*3,3); +// for(int f = 0;f(f,VMVP.row(F(f,0)),VMVP.row(F(f,1)),VMVP.row(F(f,2))); +// Triangle(f,projV.row(F(f,0)),projV.row(F(f,1)),projV.row(F(f,2))); +// N.row(f) = vF[f].n; +// for(int c = 0;c<3;c++) +// for(int d = 0;d<3;d++) +// C(f*3+c,d) = vF[f].c[c](d); +// } +// MatlabWorkspace mw; +// mw.save_index(F,"F"); +// mw.save(V,"V"); +// mw.save(MV,"MV"); +// mw.save(P,"P"); +// Vector4i VP; +// glGetIntegerv(GL_VIEWPORT, VP.data()); +// mw.save(projV,"projV"); +// mw.save(VP,"VP"); +// mw.save(VMVP,"VMVP"); +// mw.save(N,"N"); +// mw.save(C,"C"); +// mw.write("ao.mat"); +// sort(vF.begin(),vF.end()); +// +// // check +// for(int f = 0;f +//IGL_INLINE void igl::sort_triangles_robust( +// const Eigen::PlainObjectBase & V, +// const Eigen::PlainObjectBase & F, +// Eigen::PlainObjectBase & FF, +// Eigen::PlainObjectBase & I) +//{ +// using namespace Eigen; +// using namespace std; +// // Put model, projection, and viewport matrices into double arrays +// Matrix4d MV; +// Matrix4d P; +// glGetDoublev(GL_MODELVIEW_MATRIX, MV.data()); +// glGetDoublev(GL_PROJECTION_MATRIX, P.data()); +// if(V.cols() == 3) +// { +// Matrix hV; +// hV.resize(V.rows(),4); +// hV.block(0,0,V.rows(),V.cols()) = V; +// hV.col(3).setConstant(1); +// return sort_triangles_robust(hV,F,MV,P,FF,I); +// }else +// { +// return sort_triangles_robust(V,F,MV,P,FF,I); +// } +//} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::sort_triangles, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sort_triangles, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/sort_triangles.h b/src/igl/sort_triangles.h new file mode 100644 index 0000000000..b334e6530c --- /dev/null +++ b/src/igl/sort_triangles.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SORT_TRIANGLES_H +#define IGL_SORT_TRIANGLES_H + +#include "igl_inline.h" +#include + +namespace igl +{ + // Inputs: + // V #V by **4** list of homogeneous vertex positions + // F #F by 3 list of triangle indices + // MV 4 by 4 model view matrix + // P 4 by 4 projection matrix + // Outputs: + // FF #F by 3 list of sorted triangles indices + // I #F list of sorted indices + template < + typename DerivedV, + typename DerivedF, + typename DerivedMV, + typename DerivedP, + typename DerivedFF, + typename DerivedI> + IGL_INLINE void sort_triangles( + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & MV, + const Eigen::PlainObjectBase & P, + Eigen::PlainObjectBase & FF, + Eigen::PlainObjectBase & I); +} + +#ifndef IGL_STATIC_LIBRARY +# include "sort_triangles.cpp" +#endif + +#endif diff --git a/src/igl/sort_vectors_ccw.cpp b/src/igl/sort_vectors_ccw.cpp new file mode 100644 index 0000000000..ea613c51b7 --- /dev/null +++ b/src/igl/sort_vectors_ccw.cpp @@ -0,0 +1,103 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include +#include +#include + +template +IGL_INLINE void igl::sort_vectors_ccw( + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& N, + Eigen::PlainObjectBase &order) +{ + int half_degree = P.cols()/3; + //local frame + Eigen::Matrix e1 = P.head(3).normalized(); + Eigen::Matrix e3 = N.normalized(); + Eigen::Matrix e2 = e3.cross(e1); + + Eigen::Matrix F; F< angles(half_degree,1); + for (int i=0; i Pl = F.colPivHouseholderQr().solve(P.segment(i*3,3).transpose()).transpose(); +// assert(fabs(Pl(2))/Pl.cwiseAbs().maxCoeff() <1e-5); + angles[i] = atan2(Pl(1),Pl(0)); + } + + igl::sort( angles, 1, true, angles, order); + //make sure that the first element is always at the top + while (order[0] != 0) + { + //do a circshift + int temp = order[0]; + for (int i =0; i< half_degree-1; ++i) + order[i] = order[i+1]; + order(half_degree-1) = temp; + } +} + +template +IGL_INLINE void igl::sort_vectors_ccw( + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& N, + Eigen::PlainObjectBase &order, + Eigen::PlainObjectBase &sorted) + { + int half_degree = P.cols()/3; + igl::sort_vectors_ccw(P,N,order); + sorted.resize(1,half_degree*3); + for (int i=0; i +IGL_INLINE void igl::sort_vectors_ccw( + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& N, + Eigen::PlainObjectBase &order, + Eigen::PlainObjectBase &inv_order) + { + int half_degree = P.cols()/3; + igl::sort_vectors_ccw(P,N,order); + inv_order.resize(half_degree,1); + for (int i=0; i +IGL_INLINE void igl::sort_vectors_ccw( + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& N, + Eigen::PlainObjectBase &order, + Eigen::PlainObjectBase &sorted, + Eigen::PlainObjectBase &inv_order) +{ + int half_degree = P.cols()/3; + + igl::sort_vectors_ccw(P,N,order,inv_order); + + sorted.resize(1,half_degree*3); + for (int i=0; i, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +template void igl::sort_vectors_ccw, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/sort_vectors_ccw.h b/src/igl/sort_vectors_ccw.h new file mode 100644 index 0000000000..73c2f6fba6 --- /dev/null +++ b/src/igl/sort_vectors_ccw.h @@ -0,0 +1,68 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Olga Diamanti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef IGL_SORT_VECTORS_CCW +#define IGL_SORT_VECTORS_CCW +#include "igl_inline.h" + +#include + +namespace igl { + // Sorts a set of N coplanar vectors in a ccw order, and returns their order. + // Optionally it also returns a copy of the ordered vector set, or the indices, + // in the original unordered set, of the vectors in the ordered set (called here + // the "inverse" set of indices). + + // Inputs: + // P 1 by 3N row vector of the vectors to be sorted, stacked horizontally + // N #1 by 3 normal of the plane where the vectors lie + // Output: + // order N by 1 order of the vectors (indices of the unordered vectors into + // the ordered vector set) + // sorted 1 by 3N row vector of the ordered vectors, stacked horizontally + // inv_order N by 1 "inverse" order of the vectors (the indices of the ordered + // vectors into the unordered vector set) + // + template + IGL_INLINE void sort_vectors_ccw( + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& N, + Eigen::PlainObjectBase &order, + Eigen::PlainObjectBase &sorted, + Eigen::PlainObjectBase &inv_order); + + template + IGL_INLINE void sort_vectors_ccw( + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& N, + Eigen::PlainObjectBase &order, + Eigen::PlainObjectBase &sorted); + + template + IGL_INLINE void sort_vectors_ccw( + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& N, + Eigen::PlainObjectBase &order, + Eigen::PlainObjectBase &inv_order); + + + template + IGL_INLINE void sort_vectors_ccw( + const Eigen::PlainObjectBase& P, + const Eigen::PlainObjectBase& N, + Eigen::PlainObjectBase &order); + +}; + + +#ifndef IGL_STATIC_LIBRARY +#include "sort_vectors_ccw.cpp" +#endif + + +#endif /* defined(IGL_FIELD_LOCAL_GLOBAL_CONVERSIONS) */ diff --git a/src/igl/sortrows.cpp b/src/igl/sortrows.cpp new file mode 100644 index 0000000000..a5e084fd8e --- /dev/null +++ b/src/igl/sortrows.cpp @@ -0,0 +1,142 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "sortrows.h" +#include "get_seconds.h" + +#include "SortableRow.h" +#include "sort.h" +#include "colon.h" +#include "IndexComparison.h" + +#include + +// Obsolete slower version converst to vector +//template +//IGL_INLINE void igl::sortrows( +// const Eigen::DenseBase& X, +// const bool ascending, +// Eigen::PlainObjectBase& Y, +// Eigen::PlainObjectBase& IX) +//{ +// using namespace std; +// using namespace Eigen; +// typedef Eigen::Matrix RowVector; +// vector > rows; +// rows.resize(X.rows()); +// // Loop over rows +// for(int i = 0;i(ri); +// } +// vector > sorted; +// std::vector index_map; +// // Perform sort on rows +// igl::sort(rows,ascending,sorted,index_map); +// // Resize output +// Y.resizeLike(X); +// IX.resize(X.rows(),1); +// // Convert to eigen +// for(int i = 0;i +IGL_INLINE void igl::sortrows( + const Eigen::DenseBase& X, + const bool ascending, + Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& IX) +{ + // This is already 2x faster than matlab's builtin `sortrows`. I have tried + // implementing a "multiple-pass" sort on each column, but see no performance + // improvement. + using namespace std; + using namespace Eigen; + // Resize output + const size_t num_rows = X.rows(); + const size_t num_cols = X.cols(); + Y.resize(num_rows,num_cols); + IX.resize(num_rows,1); + for(int i = 0;i X.coeff(j, c)) return true; + else if (X.coeff(j,c) > X.coeff(i,c)) return false; + } + return false; + }; + std::sort( + IX.data(), + IX.data()+IX.size(), + index_greater_than + ); + } + for (size_t j=0; j, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::sortrows, Eigen::Matrix >(Eigen::DenseBase > const&, bool, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::sortrows,class Eigen::Matrix<__int64,-1,1,0,-1,1> >(class Eigen::DenseBase > const &,bool,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &); +template void igl::sortrows,class Eigen::Matrix<__int64,-1,1,0,-1,1> >(class Eigen::DenseBase > const &,bool,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &); +template void igl::sortrows,class Eigen::Matrix<__int64,-1,1,0,-1,1> >(class Eigen::DenseBase > const &,bool,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &); +template void igl::sortrows,class Eigen::Matrix<__int64,-1,1,0,-1,1> >(class Eigen::DenseBase > const &,bool,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &); +#endif +#endif diff --git a/src/igl/sortrows.h b/src/igl/sortrows.h new file mode 100644 index 0000000000..c0b15f29e1 --- /dev/null +++ b/src/igl/sortrows.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SORTROWS_H +#define IGL_SORTROWS_H +#include "igl_inline.h" + +#include +#include +namespace igl +{ + // Act like matlab's [Y,I] = sortrows(X) + // + // Templates: + // DerivedX derived scalar type, e.g. MatrixXi or MatrixXd + // DerivedI derived integer type, e.g. MatrixXi + // Inputs: + // X m by n matrix whose entries are to be sorted + // ascending sort ascending (true, matlab default) or descending (false) + // Outputs: + // Y m by n matrix whose entries are sorted (**should not** be same + // reference as X) + // I m list of indices so that + // Y = X(I,:); + template + IGL_INLINE void sortrows( + const Eigen::DenseBase& X, + const bool ascending, + Eigen::PlainObjectBase& Y, + Eigen::PlainObjectBase& I); +} + +#ifndef IGL_STATIC_LIBRARY +# include "sortrows.cpp" +#endif + +#endif + diff --git a/src/igl/sparse.cpp b/src/igl/sparse.cpp new file mode 100644 index 0000000000..ed9cd4a495 --- /dev/null +++ b/src/igl/sparse.cpp @@ -0,0 +1,123 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "sparse.h" + +#include +#include + +template +IGL_INLINE void igl::sparse( + const IndexVector & I, + const IndexVector & J, + const ValueVector & V, + Eigen::SparseMatrix& X) +{ + size_t m = (size_t)I.maxCoeff()+1; + size_t n = (size_t)J.maxCoeff()+1; + return igl::sparse(I,J,V,m,n,X); +} + +#include "verbose.h" +template < + class IndexVectorI, + class IndexVectorJ, + class ValueVector, + typename T> +IGL_INLINE void igl::sparse( + const IndexVectorI & I, + const IndexVectorJ & J, + const ValueVector & V, + const size_t m, + const size_t n, + Eigen::SparseMatrix& X) +{ + using namespace std; + using namespace Eigen; + assert((int)I.maxCoeff() < (int)m); + assert((int)I.minCoeff() >= 0); + assert((int)J.maxCoeff() < (int)n); + assert((int)J.minCoeff() >= 0); + assert(I.size() == J.size()); + assert(J.size() == V.size()); + // Really we just need .size() to be the same, but this is safer + assert(I.rows() == J.rows()); + assert(J.rows() == V.rows()); + assert(I.cols() == J.cols()); + assert(J.cols() == V.cols()); + //// number of values + //int nv = V.size(); + + //Eigen::DynamicSparseMatrix dyn_X(m,n); + //// over estimate the number of entries + //dyn_X.reserve(I.size()); + //for(int i = 0;i < nv;i++) + //{ + // dyn_X.coeffRef((int)I(i),(int)J(i)) += (T)V(i); + //} + //X = Eigen::SparseMatrix(dyn_X); + vector > IJV; + IJV.reserve(I.size()); + for(int x = 0;x(I(x),J(x),V(x))); + } + X.resize(m,n); + X.setFromTriplets(IJV.begin(),IJV.end()); +} + +template +IGL_INLINE void igl::sparse( + const Eigen::PlainObjectBase& D, + Eigen::SparseMatrix& X) +{ + assert(false && "Obsolete. Just call D.sparseView() directly"); + using namespace std; + using namespace Eigen; + vector > DIJV; + const int m = D.rows(); + const int n = D.cols(); + for(int i = 0;i(i,j,D(i,j))); + } + } + } + X.resize(m,n); + X.setFromTriplets(DIJV.begin(),DIJV.end()); +} + +template +IGL_INLINE Eigen::SparseMatrix igl::sparse( + const Eigen::PlainObjectBase& D) +{ + assert(false && "Obsolete. Just call D.sparseView() directly"); + Eigen::SparseMatrix X; + igl::sparse(D,X); + return X; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +#ifndef WIN32 +//template void igl::sparse >, Eigen::Matrix, Eigen::CwiseNullaryOp, Eigen::Array >, bool>(Eigen::PlainObjectBase > const&, Eigen::Matrix const&, Eigen::CwiseNullaryOp, Eigen::Array > const&, unsigned long, unsigned long, Eigen::SparseMatrix&); +//template void igl::sparse >, Eigen::MatrixBase >, Eigen::CwiseNullaryOp, Eigen::Array >, bool>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::CwiseNullaryOp, Eigen::Array > const&, unsigned long, unsigned long, Eigen::SparseMatrix&); +#if EIGEN_VERSION_AT_LEAST(3,3,0) +#else +//template void igl::sparse, Eigen::Matrix >, Eigen::Matrix, Eigen::CwiseNullaryOp, Eigen::Array >, bool>(Eigen::CwiseNullaryOp, Eigen::Matrix > const&, Eigen::Matrix const&, Eigen::CwiseNullaryOp, Eigen::Array > const&, unsigned long, unsigned long, Eigen::SparseMatrix&); +#endif +#endif + +template void igl::sparse, Eigen::Matrix, Eigen::Matrix, std::complex >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, size_t, size_t, Eigen::SparseMatrix, 0, int>&); +template void igl::sparse, Eigen::Matrix, double>(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::SparseMatrix&); +template void igl::sparse, Eigen::Matrix, Eigen::Matrix, double>(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, size_t, size_t, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/sparse.h b/src/igl/sparse.h new file mode 100644 index 0000000000..a947978ab1 --- /dev/null +++ b/src/igl/sparse.h @@ -0,0 +1,78 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SPARSE_H +#define IGL_SPARSE_H +#include "igl_inline.h" +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include +namespace igl +{ + // Build a sparse matrix from list of indices and values (I,J,V), functions + // like the sparse function in matlab + // + // Templates: + // IndexVector list of indices, value should be non-negative and should + // expect to be cast to an index. Must implement operator(i) to retrieve + // ith element + // ValueVector list of values, value should be expect to be cast to type + // T. Must implement operator(i) to retrieve ith element + // T should be a eigen sparse matrix primitive type like int or double + // Input: + // I nnz vector of row indices of non zeros entries in X + // J nnz vector of column indices of non zeros entries in X + // V nnz vector of non-zeros entries in X + // Optional: + // m number of rows + // n number of cols + // Outputs: + // X m by n matrix of type T whose entries are to be found + // + template + IGL_INLINE void sparse( + const IndexVector & I, + const IndexVector & J, + const ValueVector & V, + Eigen::SparseMatrix& X); + template < + class IndexVectorI, + class IndexVectorJ, + class ValueVector, + typename T> + IGL_INLINE void sparse( + const IndexVectorI & I, + const IndexVectorJ & J, + const ValueVector & V, + const size_t m, + const size_t n, + Eigen::SparseMatrix& X); + // THIS MAY BE SUPERSEDED BY EIGEN'S .sparseView Indeed it is. + // Convert a full, dense matrix to a sparse one + // + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Input: + // D m by n full, dense matrix + // Output: + // X m by n sparse matrix + template + IGL_INLINE void sparse( + const Eigen::PlainObjectBase& D, + Eigen::SparseMatrix& X); + // Wrapper with return + template + IGL_INLINE Eigen::SparseMatrix sparse( + const Eigen::PlainObjectBase& D); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "sparse.cpp" +#endif + +#endif diff --git a/src/igl/sparse_cached.cpp b/src/igl/sparse_cached.cpp new file mode 100644 index 0000000000..ddd65aadf5 --- /dev/null +++ b/src/igl/sparse_cached.cpp @@ -0,0 +1,127 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "sparse_cached.h" + +#include +#include +#include +#include +#include +#include + +template +IGL_INLINE void igl::sparse_cached_precompute( + const Eigen::MatrixBase & I, + const Eigen::MatrixBase & J, + Eigen::VectorXi& data, + Eigen::SparseMatrix& X) +{ + // Generates the triplets + std::vector > t(I.size()); + for (unsigned i = 0; i(I[i],J[i],1); + + // Call the triplets version + sparse_cached_precompute(t,X,data); +} + +template +IGL_INLINE void igl::sparse_cached_precompute( + const std::vector >& triplets, + Eigen::VectorXi& data, + Eigen::SparseMatrix& X) +{ + // Construct an empty sparse matrix + X.setFromTriplets(triplets.begin(),triplets.end()); + X.makeCompressed(); + + std::vector > T(triplets.size()); + for (unsigned i=0; i= 0); + assert(row < X.rows()); + assert(row >= 0); + assert(value_index >= 0); + assert(value_index < X.nonZeros()); + + std::pair p_m = std::make_pair(row,col); + + while (t +IGL_INLINE void igl::sparse_cached( + const std::vector >& triplets, + const Eigen::VectorXi& data, + Eigen::SparseMatrix& X) +{ + assert(triplets.size() == data.size()); + + // Clear it first + for (unsigned i = 0; i +IGL_INLINE void igl::sparse_cached( + const Eigen::MatrixBase& V, + const Eigen::VectorXi& data, + Eigen::SparseMatrix& X) +{ + assert(V.size() == data.size()); + + // Clear it first + for (unsigned i = 0; i(std::vector::StorageIndex>, std::allocator::StorageIndex> > > const&, Eigen::Matrix const&, Eigen::SparseMatrix&); + template void igl::sparse_cached_precompute(std::vector::StorageIndex>, std::allocator::StorageIndex> > > const&, Eigen::Matrix&, Eigen::SparseMatrix&); +#else + template void igl::sparse_cached(std::vector::Index>, std::allocator::Index> > > const&, Eigen::Matrix const&, Eigen::SparseMatrix&); + template void igl::sparse_cached_precompute(std::vector::Index>, std::allocator::Index> > > const&, Eigen::Matrix&, Eigen::SparseMatrix&); +#endif +#endif diff --git a/src/igl/sparse_cached.h b/src/igl/sparse_cached.h new file mode 100644 index 0000000000..40d2889626 --- /dev/null +++ b/src/igl/sparse_cached.h @@ -0,0 +1,85 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SPARSE_CACHED_H +#define IGL_SPARSE_CACHED_H +#include "igl_inline.h" +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include +#include +namespace igl +{ + // Build a sparse matrix from list of indices and values (I,J,V), similarly to + // the sparse function in matlab. Divides the construction in two phases, one + // for fixing the sparsity pattern, and one to populate it with values. Compared to + // igl::sparse, this version is slower for the first time (since it requires a + // precomputation), but faster to the subsequent evaluations. + // + // Templates: + // IndexVector list of indices, value should be non-negative and should + // expect to be cast to an index. Must implement operator(i) to retrieve + // ith element + // ValueVector list of values, value should be expect to be cast to type + // T. Must implement operator(i) to retrieve ith element + // T should be a eigen sparse matrix primitive type like int or double + // Input: + // I nnz vector of row indices of non zeros entries in X + // J nnz vector of column indices of non zeros entries in X + // V nnz vector of non-zeros entries in X + // Optional: + // m number of rows + // n number of cols + // Outputs: + // X m by n matrix of type T whose entries are to be found + // + // Example: + // Eigen::SparseMatrix A; + // std::vector > IJV; + // buildA(IJV); + // if (A.rows() == 0) + // { + // A = Eigen::SparseMatrix(rows,cols); + // igl::sparse_cached_precompute(IJV,A,A_data); + // } + // else + // igl::sparse_cached(IJV,s.A,s.A_data); + + template + IGL_INLINE void sparse_cached_precompute( + const Eigen::MatrixBase & I, + const Eigen::MatrixBase & J, + Eigen::VectorXi& data, + Eigen::SparseMatrix& X + ); + + template + IGL_INLINE void sparse_cached_precompute( + const std::vector >& triplets, + Eigen::VectorXi& data, + Eigen::SparseMatrix& X + ); + + template + IGL_INLINE void sparse_cached( + const std::vector >& triplets, + const Eigen::VectorXi& data, + Eigen::SparseMatrix& X); + + template + IGL_INLINE void sparse_cached( + const Eigen::MatrixBase& V, + const Eigen::VectorXi& data, + Eigen::SparseMatrix& X + ); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "sparse_cached.cpp" +#endif + +#endif diff --git a/src/igl/speye.cpp b/src/igl/speye.cpp new file mode 100644 index 0000000000..54af00c71b --- /dev/null +++ b/src/igl/speye.cpp @@ -0,0 +1,35 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "speye.h" + +template +IGL_INLINE void igl::speye(const int m, const int n, Eigen::SparseMatrix & I) +{ + // size of diagonal + int d = (m(m,n); + I.reserve(d); + for(int i = 0;i +IGL_INLINE void igl::speye(const int n, Eigen::SparseMatrix & I) +{ + return igl::speye(n,n,I); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::speye(int, Eigen::SparseMatrix&); +template void igl::speye >(int, int, Eigen::SparseMatrix, 0, int>&); +#endif diff --git a/src/igl/speye.h b/src/igl/speye.h new file mode 100644 index 0000000000..4c24255713 --- /dev/null +++ b/src/igl/speye.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SPEYE_H +#define IGL_SPEYE_H +#include "igl_inline.h" + +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET +#include + +namespace igl +{ + // Builds an m by n sparse identity matrix like matlab's speye function + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Inputs: + // m number of rows + // n number of cols + // Outputs: + // I m by n sparse matrix with 1's on the main diagonal + template + IGL_INLINE void speye(const int n,const int m, Eigen::SparseMatrix & I); + // Builds an n by n sparse identity matrix like matlab's speye function + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Inputs: + // n number of rows and cols + // Outputs: + // I n by n sparse matrix with 1's on the main diagonal + template + IGL_INLINE void speye(const int n, Eigen::SparseMatrix & I); +} + +#ifndef IGL_STATIC_LIBRARY +# include "speye.cpp" +#endif + +#endif diff --git a/src/igl/squared_edge_lengths.cpp b/src/igl/squared_edge_lengths.cpp new file mode 100644 index 0000000000..d15e6c13e5 --- /dev/null +++ b/src/igl/squared_edge_lengths.cpp @@ -0,0 +1,106 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "squared_edge_lengths.h" +#include "parallel_for.h" +#include + +template +IGL_INLINE void igl::squared_edge_lengths( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& L) +{ + using namespace std; + const int m = F.rows(); + switch(F.cols()) + { + case 2: + { + L.resize(F.rows(),1); + for(int i = 0;i, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::squared_edge_lengths, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/squared_edge_lengths.h b/src/igl/squared_edge_lengths.h new file mode 100644 index 0000000000..2f374d6c3a --- /dev/null +++ b/src/igl/squared_edge_lengths.h @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SQUARED_EDGE_LENGTHS_H +#define IGL_SQUARED_EDGE_LENGTHS_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Constructs a list of squared lengths of edges opposite each index in a face + // (triangle/tet) list + // + // Templates: + // DerivedV derived from vertex positions matrix type: i.e. MatrixXd + // DerivedF derived from face indices matrix type: i.e. MatrixXi + // DerivedL derived from edge lengths matrix type: i.e. MatrixXd + // Inputs: + // V eigen matrix #V by 3 + // F #F by 2 list of mesh edges + // or + // F #F by 3 list of mesh faces (must be triangles) + // or + // T #T by 4 list of mesh elements (must be tets) + // Outputs: + // L #F by {1|3|6} list of edge lengths squared + // for edges, column of lengths + // for triangles, columns correspond to edges [1,2],[2,0],[0,1] + // for tets, columns correspond to edges + // [3 0],[3 1],[3 2],[1 2],[2 0],[0 1] + // + template + IGL_INLINE void squared_edge_lengths( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& L); +} + +#ifndef IGL_STATIC_LIBRARY +# include "squared_edge_lengths.cpp" +#endif + +#endif + diff --git a/src/igl/stdin_to_temp.cpp b/src/igl/stdin_to_temp.cpp new file mode 100644 index 0000000000..a7a7fbd70a --- /dev/null +++ b/src/igl/stdin_to_temp.cpp @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "stdin_to_temp.h" + +#include + +IGL_INLINE bool igl::stdin_to_temp(FILE ** temp_file) +{ + // get a temporary file + *temp_file = tmpfile(); + if(*temp_file == NULL) + { + fprintf(stderr,"IOError: temp file could not be created.\n"); + return false; + } + char c; + // c++'s cin handles the stdind input in a reasonable way + while (std::cin.good()) + { + c = std::cin.get(); + if(std::cin.good()) + { + if(1 != fwrite(&c,sizeof(char),1,*temp_file)) + { + fprintf(stderr,"IOError: error writing to tempfile.\n"); + return false; + } + } + } + // rewind file getting it ready to read from + rewind(*temp_file); + return true; +} diff --git a/src/igl/stdin_to_temp.h b/src/igl/stdin_to_temp.h new file mode 100644 index 0000000000..ae35219ea5 --- /dev/null +++ b/src/igl/stdin_to_temp.h @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_STDIN_TO_TEMP_H +#define IGL_STDIN_TO_TEMP_H +#include "igl_inline.h" +#include +namespace igl +{ + // Write stdin/piped input to a temporary file which can than be preprocessed as it + // is (a normal file). This is often useful if you want to process stdin/piped + // with library functions that expect to be able to fseek(), rewind() etc.. + // + // If your application is not using fseek(), rewind(), etc. but just reading + // from stdin then this will likely cause a bottle neck as it defeats the whole + // purpose of piping. + // + // Outputs: + // temp_file pointer to temp file pointer, rewound to beginning of file so + // its ready to be read + // Return true only if no errors were found + // + // Note: Caller is responsible for closing the file (tmpfile() automatically + // unlinks the file so there is no need to remove/delete/unlink the file) + IGL_INLINE bool stdin_to_temp(FILE ** temp_file); +} + +#ifndef IGL_STATIC_LIBRARY +# include "stdin_to_temp.cpp" +#endif + +#endif diff --git a/src/igl/straighten_seams.cpp b/src/igl/straighten_seams.cpp new file mode 100644 index 0000000000..0aef2b316a --- /dev/null +++ b/src/igl/straighten_seams.cpp @@ -0,0 +1,370 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "straighten_seams.h" +#include "LinSpaced.h" +#include "on_boundary.h" +#include "sparse.h" +#include "max.h" +#include "count.h" +#include "any.h" +#include "slice_mask.h" +#include "slice_into.h" +#include "unique_simplices.h" +#include "adjacency_matrix.h" +#include "setxor.h" +#include "edges_to_path.h" +#include "ramer_douglas_peucker.h" +#include "components.h" +#include "list_to_matrix.h" +#include "ears.h" +#include "slice.h" +#include "sum.h" +#include "find.h" +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedVT, + typename DerivedFT, + typename Scalar, + typename DerivedUE, + typename DerivedUT, + typename DerivedOT> +IGL_INLINE void igl::straighten_seams( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & VT, + const Eigen::MatrixBase & FT, + const Scalar tol, + Eigen::PlainObjectBase & UE, + Eigen::PlainObjectBase & UT, + Eigen::PlainObjectBase & OT) +{ + using namespace Eigen; + // number of faces + assert(FT.rows() == F.rows() && "#FT must == #F"); + assert(F.cols() == 3 && "F should contain triangles"); + assert(FT.cols() == 3 && "FT should contain triangles"); + const int m = F.rows(); + // Boundary edges of the texture map and 3d meshes + Array _; + Array BT,BF; + on_boundary(FT,_,BT); + on_boundary(F,_,BF); + assert((!((BF && (BT!=true)).any())) && + "Not dealing with boundaries of mesh that get 'stitched' in texture mesh"); + typedef Matrix MatrixX2I; + const MatrixX2I ET = (MatrixX2I(FT.rows()*3,2) + <vBT = Map >(BT.data(),BT.size(),1); + ArrayvBF = Map >(BF.data(),BF.size(),1); + MatrixX2I OF; + slice_mask(ET,vBT,1,OT); + slice_mask(EF,vBT,1,OF); + VectorXi OFMAP; + slice_mask(EFMAP,vBT,1,OFMAP); + // Two boundary edges on the texture-mapping are "equivalent" to each other on + // the 3D-mesh if their 3D-mesh vertex indices match + SparseMatrix OEQ; + { + SparseMatrix OEQR; + sparse( + igl::LinSpaced(OT.rows(),0,OT.rows()-1), + OFMAP, + Array::Ones(OT.rows(),1), + OT.rows(), + m*3, + OEQR); + OEQ = OEQR * OEQR.transpose(); + // Remove diagonal + OEQ.prune([](const int r, const int c, const bool)->bool{return r!=c;}); + } + // For each edge in OT, for each endpoint, how many _other_ texture-vertices + // are images of all the 3d-mesh vertices in F who map from "corners" in F/FT + // mapping to this endpoint. + // + // Adjacency matrix between 3d-vertices and texture-vertices + SparseMatrix V2VT; + sparse( + F, + FT, + Array::Ones(F.rows(),F.cols()), + V.rows(), + VT.rows(), + V2VT); + // For each 3d-vertex count how many different texture-coordinates its getting + // from different incident corners + VectorXi DV; + count(V2VT,2,DV); + VectorXi M,I; + max(V2VT,1,M,I); + assert( (M.array() == 1).all() ); + VectorXi DT; + // Map counts onto texture-vertices + slice(DV,I,1,DT); + // Boundary in 3D && UV + Array BTF; + slice_mask(vBF, vBT, 1, BTF); + // Texture-vertex is "sharp" if incident on "half-"edge that is not a + // boundary in the 3D mesh but is a boundary in the texture-mesh AND is not + // "cut cleanly" (the vertex is mapped to exactly 2 locations) + Array SV = Array::Zero(VT.rows(),1); + //std::cout<<"#SV: "< CL = DT.array()==2; + SparseMatrix VTOT; + { + Eigen::MatrixXi I = + igl::LinSpaced(OT.rows(),0,OT.rows()-1).replicate(1,2); + sparse( + OT, + I, + Array::Ones(OT.rows(),OT.cols()), + VT.rows(), + OT.rows(), + VTOT); + Array cuts; + count( (VTOT*OEQ).eval(), 2, cuts); + CL = (CL && (cuts.array() == 2)).eval(); + } + //std::cout<<"#CL: "< earT = Array::Zero(VT.rows(),1); + for(int e = 0;e A; + adjacency_matrix(FT,A); + earT = (earT || (A*earT.matrix()).array()).eval(); + //std::cout<<"#earT: "< V2VTSV,V2VTC; + slice_mask(V2VT,SV,2,V2VTSV); + Array Cb; + any(V2VTSV,2,Cb); + slice_mask(V2VT,Cb,1,V2VTC); + any(V2VTC,1,SV); + } + //std::cout<<"#SV: "< OTVT = VTOT.transpose(); + int nc; + ArrayXi C; + { + // Doesn't Compile on older Eigen: + //SparseMatrix A = OTVT * (!SV).matrix().asDiagonal() * VTOT; + SparseMatrix A = OTVT * (SV!=true).matrix().asDiagonal() * VTOT; + components(A,C); + nc = C.maxCoeff()+1; + } + //std::cout<<"nc: "< > vUE; + // loop over each component + std::vector done(nc,false); + for(int c = 0;c OEQIc; + slice(OEQ,Ic,1,OEQIc); + Eigen::VectorXi N; + sum(OEQIc,2,N); + const int ncopies = N(0)+1; + assert((N.array() == ncopies-1).all()); + assert((ncopies == 1 || ncopies == 2) && + "Not dealing with non-manifold meshes"); + Eigen::VectorXi vpath,epath,eend; + typedef Eigen::Matrix MatrixX2S; + switch(ncopies) + { + case 1: + { + MatrixX2I OTIc; + slice(OT,Ic,1,OTIc); + edges_to_path(OTIc,vpath,epath,eend); + Array SVvpath; + slice(SV,vpath,1,SVvpath); + assert( + (vpath(0) != vpath(vpath.size()-1) || !SVvpath.any()) && + "Not dealing with 1-loops touching 'sharp' corners"); + // simple open boundary + MatrixX2S PI; + slice(VT,vpath,1,PI); + const Scalar bbd = + (PI.colwise().maxCoeff() - PI.colwise().minCoeff()).norm(); + // Do not collapse boundaries to fewer than 3 vertices + const bool allow_boundary_collapse = false; + assert(PI.size() >= 2); + const bool is_closed = PI(0) == PI(PI.size()-1); + assert(!is_closed || vpath.size() >= 4); + Scalar eff_tol = std::min(tol,2.); + VectorXi UIc; + while(true) + { + MatrixX2S UPI,UTvpath; + ramer_douglas_peucker(PI,eff_tol*bbd,UPI,UIc,UTvpath); + slice_into(UTvpath,vpath,1,UT); + if(!is_closed || allow_boundary_collapse) + { + break; + } + if(UPI.rows()>=4) + { + break; + } + eff_tol = eff_tol*0.5; + } + for(int i = 0;i IV; + SparseMatrix OEQIcT = OEQIc.transpose().eval(); + find(OEQIcT,Icc,II,IV); + assert(II.size() == Ic.size() && + (II.array() == + igl::LinSpaced(Ic.size(),0,Ic.size()-1).array()).all()); + assert(Icc.size() == Ic.size()); + const int cc = C(Icc(0)); + Eigen::VectorXi CIcc; + slice(C,Icc,1,CIcc); + assert((CIcc.array() == cc).all()); + assert(!done[cc]); + done[cc] = true; + } + Array flipped; + { + MatrixX2I OFIc,OFIcc; + slice(OF,Ic,1,OFIc); + slice(OF,Icc,1,OFIcc); + Eigen::VectorXi XOR,IA,IB; + setxor(OFIc,OFIcc,XOR,IA,IB); + assert(XOR.size() == 0); + flipped = OFIc.array().col(0) != OFIcc.array().col(0); + } + if(Ic.size() == 1) + { + // No change to UT + vUE.push_back({OT(Ic(0),0),OT(Ic(0),1)}); + assert(Icc.size() == 1); + vUE.push_back({OT(Icc(0),flipped(0)?1:0),OT(Icc(0),flipped(0)?0:1)}); + }else + { + MatrixX2I OTIc; + slice(OT,Ic,1,OTIc); + edges_to_path(OTIc,vpath,epath,eend); + // Flip endpoints if needed + for(int e = 0;e PI(vpath.size(),VT.cols()*2); + for(int p = 0;p UPI,SI; + VectorXi UIc; + ramer_douglas_peucker(PI,tol*bbd,UPI,UIc,SI); + slice_into(SI.leftCols (VT.cols()), vpath,1,UT); + slice_into(SI.rightCols(VT.cols()),vpathc,1,UT); + for(int i = 0;i, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, double, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/straighten_seams.h b/src/igl/straighten_seams.h new file mode 100644 index 0000000000..2eb0e9d469 --- /dev/null +++ b/src/igl/straighten_seams.h @@ -0,0 +1,57 @@ +#ifndef IGL_STRAIGHTEN_SEAMS_H +#define IGL_STRAIGHTEN_SEAMS_H + +#include "igl_inline.h" +#include + +namespace igl +{ + // STRAIGHTEN_SEAMS Given a obj-style mesh with (V,F) defining the geometric + // surface of the mesh and (VT,FT) defining the + // parameterization/texture-mapping of the mesh in the uv-domain, find all + // seams and boundaries in the texture-mapping and "straighten" them, + // remapping vertices along the boundary and in the interior. This will be + // careful to consistently straighten multiple seams in the texture-mesh + // corresponding to the same edge chains in the surface-mesh. + // + // [UT] = straighten_seams(V,F,VT,FT) + // + // Inputs: + // V #V by 3 list of vertices + // F #F by 3 list of triangle indices + // VT #VT by 2 list of texture coordinates + // FT #F by 3 list of triangle texture coordinates + // Optional: + // 'Tol' followed by Ramer-Douglas-Peucker tolerance as a fraction of the + // curves bounding box diagonal (see dpsimplify) + // Outputs: + // UE #UE by 2 list of indices into VT of coarse output polygon edges + // UT #VT by 3 list of new texture coordinates + // OT #OT by 2 list of indices into VT of boundary edges + // + // See also: simplify_curve, dpsimplify + template < + typename DerivedV, + typename DerivedF, + typename DerivedVT, + typename DerivedFT, + typename Scalar, + typename DerivedUE, + typename DerivedUT, + typename DerivedOT> + IGL_INLINE void straighten_seams( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & VT, + const Eigen::MatrixBase & FT, + const Scalar tol, + Eigen::PlainObjectBase & UE, + Eigen::PlainObjectBase & UT, + Eigen::PlainObjectBase & OT); +} + +#ifndef IGL_STATIC_LIBRARY +# include "straighten_seams.cpp" +#endif + +#endif diff --git a/src/igl/sum.cpp b/src/igl/sum.cpp new file mode 100644 index 0000000000..b994ce4051 --- /dev/null +++ b/src/igl/sum.cpp @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "sum.h" +#include "redux.h" + +template +IGL_INLINE void igl::sum( + const Eigen::SparseMatrix& X, + const int dim, + Eigen::SparseVector& S) +{ + assert((dim == 1 || dim == 2) && "dim must be 2 or 1"); + // Get size of input + int m = X.rows(); + int n = X.cols(); + // resize output + if(dim==1) + { + S = Eigen::SparseVector(n); + }else + { + S = Eigen::SparseVector(m); + } + + // Iterate over outside + for(int k=0; k::InnerIterator it (X,k); it; ++it) + { + if(dim == 1) + { + S.coeffRef(it.col()) += it.value(); + }else + { + S.coeffRef(it.row()) += it.value(); + } + } + } +} + +template +IGL_INLINE void igl::sum( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase& B) +{ + typedef typename DerivedB::Scalar Scalar; + igl::redux(A,dim,[](Scalar a, Scalar b){ return a+b;},B); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::sum >(Eigen::SparseMatrix const&, int, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::sum >(Eigen::SparseMatrix const&, int, Eigen::PlainObjectBase >&); +template void igl::sum(Eigen::SparseMatrix const&, int, Eigen::SparseVector&); +#endif diff --git a/src/igl/sum.h b/src/igl/sum.h new file mode 100644 index 0000000000..6caeac3447 --- /dev/null +++ b/src/igl/sum.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SUM_H +#define IGL_SUM_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Note: If your looking for dense matrix matlab like sum for eigen matrics + // just use: + // M.colwise().sum() or M.rowwise().sum() + // + + // Sum the columns or rows of a sparse matrix + // Templates: + // T should be a eigen sparse matrix primitive type like int or double + // Inputs: + // X m by n sparse matrix + // dim dimension along which to sum (1 or 2) + // Output: + // S n-long sparse vector (if dim == 1) + // or + // S m-long sparse vector (if dim == 2) + template + IGL_INLINE void sum( + const Eigen::SparseMatrix& X, + const int dim, + Eigen::SparseVector& S); + // Sum is "conducted" in the type of DerivedB::Scalar + template + IGL_INLINE void sum( + const Eigen::SparseMatrix & A, + const int dim, + Eigen::PlainObjectBase& B); +} + +#ifndef IGL_STATIC_LIBRARY +# include "sum.cpp" +#endif + +#endif diff --git a/src/igl/svd3x3.cpp b/src/igl/svd3x3.cpp new file mode 100644 index 0000000000..dd65e0cf3e --- /dev/null +++ b/src/igl/svd3x3.cpp @@ -0,0 +1,69 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "svd3x3.h" + +#include +#include + +#define USE_SCALAR_IMPLEMENTATION +#undef USE_SSE_IMPLEMENTATION +#undef USE_AVX_IMPLEMENTATION +#define COMPUTE_U_AS_MATRIX +#define COMPUTE_V_AS_MATRIX +#include "Singular_Value_Decomposition_Preamble.hpp" + +#pragma runtime_checks( "u", off ) // disable runtime asserts on xor eax,eax type of stuff (doesn't always work, disable explicitly in compiler settings) +template +IGL_INLINE void igl::svd3x3(const Eigen::Matrix& A, Eigen::Matrix &U, Eigen::Matrix &S, Eigen::Matrix&V) +{ + // this code only supports the scalar version (otherwise we'd need to pass arrays of matrices) + +#include "Singular_Value_Decomposition_Kernel_Declarations.hpp" + + ENABLE_SCALAR_IMPLEMENTATION(Sa11.f=A(0,0);) ENABLE_SSE_IMPLEMENTATION(Va11=_mm_loadu_ps(a11);) ENABLE_AVX_IMPLEMENTATION(Va11=_mm256_loadu_ps(a11);) + ENABLE_SCALAR_IMPLEMENTATION(Sa21.f=A(1,0);) ENABLE_SSE_IMPLEMENTATION(Va21=_mm_loadu_ps(a21);) ENABLE_AVX_IMPLEMENTATION(Va21=_mm256_loadu_ps(a21);) + ENABLE_SCALAR_IMPLEMENTATION(Sa31.f=A(2,0);) ENABLE_SSE_IMPLEMENTATION(Va31=_mm_loadu_ps(a31);) ENABLE_AVX_IMPLEMENTATION(Va31=_mm256_loadu_ps(a31);) + ENABLE_SCALAR_IMPLEMENTATION(Sa12.f=A(0,1);) ENABLE_SSE_IMPLEMENTATION(Va12=_mm_loadu_ps(a12);) ENABLE_AVX_IMPLEMENTATION(Va12=_mm256_loadu_ps(a12);) + ENABLE_SCALAR_IMPLEMENTATION(Sa22.f=A(1,1);) ENABLE_SSE_IMPLEMENTATION(Va22=_mm_loadu_ps(a22);) ENABLE_AVX_IMPLEMENTATION(Va22=_mm256_loadu_ps(a22);) + ENABLE_SCALAR_IMPLEMENTATION(Sa32.f=A(2,1);) ENABLE_SSE_IMPLEMENTATION(Va32=_mm_loadu_ps(a32);) ENABLE_AVX_IMPLEMENTATION(Va32=_mm256_loadu_ps(a32);) + ENABLE_SCALAR_IMPLEMENTATION(Sa13.f=A(0,2);) ENABLE_SSE_IMPLEMENTATION(Va13=_mm_loadu_ps(a13);) ENABLE_AVX_IMPLEMENTATION(Va13=_mm256_loadu_ps(a13);) + ENABLE_SCALAR_IMPLEMENTATION(Sa23.f=A(1,2);) ENABLE_SSE_IMPLEMENTATION(Va23=_mm_loadu_ps(a23);) ENABLE_AVX_IMPLEMENTATION(Va23=_mm256_loadu_ps(a23);) + ENABLE_SCALAR_IMPLEMENTATION(Sa33.f=A(2,2);) ENABLE_SSE_IMPLEMENTATION(Va33=_mm_loadu_ps(a33);) ENABLE_AVX_IMPLEMENTATION(Va33=_mm256_loadu_ps(a33);) + +#include "Singular_Value_Decomposition_Main_Kernel_Body.hpp" + + ENABLE_SCALAR_IMPLEMENTATION(U(0,0)=Su11.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(u11,Vu11);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(u11,Vu11);) + ENABLE_SCALAR_IMPLEMENTATION(U(1,0)=Su21.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(u21,Vu21);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(u21,Vu21);) + ENABLE_SCALAR_IMPLEMENTATION(U(2,0)=Su31.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(u31,Vu31);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(u31,Vu31);) + ENABLE_SCALAR_IMPLEMENTATION(U(0,1)=Su12.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(u12,Vu12);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(u12,Vu12);) + ENABLE_SCALAR_IMPLEMENTATION(U(1,1)=Su22.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(u22,Vu22);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(u22,Vu22);) + ENABLE_SCALAR_IMPLEMENTATION(U(2,1)=Su32.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(u32,Vu32);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(u32,Vu32);) + ENABLE_SCALAR_IMPLEMENTATION(U(0,2)=Su13.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(u13,Vu13);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(u13,Vu13);) + ENABLE_SCALAR_IMPLEMENTATION(U(1,2)=Su23.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(u23,Vu23);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(u23,Vu23);) + ENABLE_SCALAR_IMPLEMENTATION(U(2,2)=Su33.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(u33,Vu33);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(u33,Vu33);) + + ENABLE_SCALAR_IMPLEMENTATION(V(0,0)=Sv11.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(v11,Vv11);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(v11,Vv11);) + ENABLE_SCALAR_IMPLEMENTATION(V(1,0)=Sv21.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(v21,Vv21);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(v21,Vv21);) + ENABLE_SCALAR_IMPLEMENTATION(V(2,0)=Sv31.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(v31,Vv31);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(v31,Vv31);) + ENABLE_SCALAR_IMPLEMENTATION(V(0,1)=Sv12.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(v12,Vv12);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(v12,Vv12);) + ENABLE_SCALAR_IMPLEMENTATION(V(1,1)=Sv22.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(v22,Vv22);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(v22,Vv22);) + ENABLE_SCALAR_IMPLEMENTATION(V(2,1)=Sv32.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(v32,Vv32);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(v32,Vv32);) + ENABLE_SCALAR_IMPLEMENTATION(V(0,2)=Sv13.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(v13,Vv13);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(v13,Vv13);) + ENABLE_SCALAR_IMPLEMENTATION(V(1,2)=Sv23.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(v23,Vv23);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(v23,Vv23);) + ENABLE_SCALAR_IMPLEMENTATION(V(2,2)=Sv33.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(v33,Vv33);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(v33,Vv33);) + + ENABLE_SCALAR_IMPLEMENTATION(S(0,0)=Sa11.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(sigma1,Va11);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(sigma1,Va11);) + ENABLE_SCALAR_IMPLEMENTATION(S(1,0)=Sa22.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(sigma2,Va22);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(sigma2,Va22);) + ENABLE_SCALAR_IMPLEMENTATION(S(2,0)=Sa33.f;) ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(sigma3,Va33);) ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(sigma3,Va33);) +} +#pragma runtime_checks( "u", restore ) + +// forced instantiation +template void igl::svd3x3(const Eigen::Matrix& A, Eigen::Matrix &U, Eigen::Matrix &S, Eigen::Matrix&V); +template void igl::svd3x3(Eigen::Matrix const&, Eigen::Matrix&, Eigen::Matrix&, Eigen::Matrix&); +// doesn't even make sense with double because this SVD code is only single precision anyway... diff --git a/src/igl/svd3x3.h b/src/igl/svd3x3.h new file mode 100644 index 0000000000..900d459f47 --- /dev/null +++ b/src/igl/svd3x3.h @@ -0,0 +1,40 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SVD3X3_H +#define IGL_SVD3X3_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Super fast 3x3 SVD according to http://pages.cs.wisc.edu/~sifakis/project_pages/svd.html + // The resulting decomposition is A = U * diag(S[0], S[1], S[2]) * V' + // BEWARE: this SVD algorithm guarantees that det(U) = det(V) = 1, but this + // comes at the cost that 'sigma3' can be negative + // for computing polar decomposition it's great because all we need to do is U*V' + // and the result will automatically have positive determinant + // + // Inputs: + // A 3x3 matrix + // Outputs: + // U 3x3 left singular vectors + // S 3x1 singular values + // V 3x3 right singular vectors + // + // Known bugs: this will not work correctly for double precision. + template + IGL_INLINE void svd3x3( + const Eigen::Matrix& A, + Eigen::Matrix &U, + Eigen::Matrix &S, + Eigen::Matrix&V); +} +#ifndef IGL_STATIC_LIBRARY +# include "svd3x3.cpp" +#endif +#endif diff --git a/src/igl/svd3x3_avx.cpp b/src/igl/svd3x3_avx.cpp new file mode 100644 index 0000000000..db56ee30ab --- /dev/null +++ b/src/igl/svd3x3_avx.cpp @@ -0,0 +1,108 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifdef __AVX__ +#include "svd3x3_avx.h" + +#include +#include + +#undef USE_SCALAR_IMPLEMENTATION +#undef USE_SSE_IMPLEMENTATION +#define USE_AVX_IMPLEMENTATION +#define COMPUTE_U_AS_MATRIX +#define COMPUTE_V_AS_MATRIX +#include "Singular_Value_Decomposition_Preamble.hpp" + +#pragma runtime_checks( "u", off ) // disable runtime asserts on xor eax,eax type of stuff (doesn't always work, disable explicitly in compiler settings) +template +IGL_INLINE void igl::svd3x3_avx( + const Eigen::Matrix& A, + Eigen::Matrix &U, + Eigen::Matrix &S, + Eigen::Matrix&V) +{ + // this code assumes USE_AVX_IMPLEMENTATION is defined + float Ashuffle[9][8], Ushuffle[9][8], Vshuffle[9][8], Sshuffle[3][8]; + for (int i=0; i<3; i++) + { + for (int j=0; j<3; j++) + { + for (int k=0; k<8; k++) + { + Ashuffle[i + j*3][k] = A(i + 3*k, j); + } + } + } + +#include "Singular_Value_Decomposition_Kernel_Declarations.hpp" + + ENABLE_AVX_IMPLEMENTATION(Va11=_mm256_loadu_ps(Ashuffle[0]);) + ENABLE_AVX_IMPLEMENTATION(Va21=_mm256_loadu_ps(Ashuffle[1]);) + ENABLE_AVX_IMPLEMENTATION(Va31=_mm256_loadu_ps(Ashuffle[2]);) + ENABLE_AVX_IMPLEMENTATION(Va12=_mm256_loadu_ps(Ashuffle[3]);) + ENABLE_AVX_IMPLEMENTATION(Va22=_mm256_loadu_ps(Ashuffle[4]);) + ENABLE_AVX_IMPLEMENTATION(Va32=_mm256_loadu_ps(Ashuffle[5]);) + ENABLE_AVX_IMPLEMENTATION(Va13=_mm256_loadu_ps(Ashuffle[6]);) + ENABLE_AVX_IMPLEMENTATION(Va23=_mm256_loadu_ps(Ashuffle[7]);) + ENABLE_AVX_IMPLEMENTATION(Va33=_mm256_loadu_ps(Ashuffle[8]);) + +#include "Singular_Value_Decomposition_Main_Kernel_Body.hpp" + + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Ushuffle[0],Vu11);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Ushuffle[1],Vu21);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Ushuffle[2],Vu31);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Ushuffle[3],Vu12);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Ushuffle[4],Vu22);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Ushuffle[5],Vu32);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Ushuffle[6],Vu13);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Ushuffle[7],Vu23);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Ushuffle[8],Vu33);) + + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Vshuffle[0],Vv11);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Vshuffle[1],Vv21);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Vshuffle[2],Vv31);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Vshuffle[3],Vv12);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Vshuffle[4],Vv22);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Vshuffle[5],Vv32);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Vshuffle[6],Vv13);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Vshuffle[7],Vv23);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Vshuffle[8],Vv33);) + + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Sshuffle[0],Va11);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Sshuffle[1],Va22);) + ENABLE_AVX_IMPLEMENTATION(_mm256_storeu_ps(Sshuffle[2],Va33);) + + for (int i=0; i<3; i++) + { + for (int j=0; j<3; j++) + { + for (int k=0; k<8; k++) + { + U(i + 3*k, j) = Ushuffle[i + j*3][k]; + V(i + 3*k, j) = Vshuffle[i + j*3][k]; + } + } + } + + for (int i=0; i<3; i++) + { + for (int k=0; k<8; k++) + { + S(i + 3*k, 0) = Sshuffle[i][k]; + } + } +} +#pragma runtime_checks( "u", restore ) + +#ifdef IGL_STATIC_LIBRARY +// forced instantiation +//template void igl::svd3x3_avx(const Eigen::Matrix& A, Eigen::Matrix &U, Eigen::Matrix &S, Eigen::Matrix&V); +// doesn't even make sense with double because the wunder-SVD code is only single precision anyway... +template void igl::svd3x3_avx(Eigen::Matrix const&, Eigen::Matrix&, Eigen::Matrix&, Eigen::Matrix&); +#endif +#endif diff --git a/src/igl/svd3x3_avx.h b/src/igl/svd3x3_avx.h new file mode 100644 index 0000000000..b814acc956 --- /dev/null +++ b/src/igl/svd3x3_avx.h @@ -0,0 +1,40 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SVD3X3_AVX_H +#define IGL_SVD3X3_AVX_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Super fast 3x3 SVD according to + // http://pages.cs.wisc.edu/~sifakis/project_pages/svd.html This is AVX + // version of svd3x3 (see svd3x3.h) which works on 8 matrices at a time These + // eight matrices are simply stacked in columns, the rest is the same as for + // svd3x3 + // + // Inputs: + // A 12 by 3 stack of 3x3 matrices + // Outputs: + // U 12x3 left singular vectors stacked + // S 12x1 singular values stacked + // V 12x3 right singular vectors stacked + // + // Known bugs: this will not work correctly for double precision. + template + IGL_INLINE void svd3x3_avx( + const Eigen::Matrix& A, + Eigen::Matrix &U, + Eigen::Matrix &S, + Eigen::Matrix&V); +} +#ifndef IGL_STATIC_LIBRARY +# include "svd3x3_avx.cpp" +#endif +#endif + diff --git a/src/igl/svd3x3_sse.cpp b/src/igl/svd3x3_sse.cpp new file mode 100644 index 0000000000..2ef4e0fe8f --- /dev/null +++ b/src/igl/svd3x3_sse.cpp @@ -0,0 +1,108 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifdef __SSE__ +#include "svd3x3_sse.h" + +#include +#include + +#undef USE_SCALAR_IMPLEMENTATION +#define USE_SSE_IMPLEMENTATION +#undef USE_AVX_IMPLEMENTATION +#define COMPUTE_U_AS_MATRIX +#define COMPUTE_V_AS_MATRIX +#include "Singular_Value_Decomposition_Preamble.hpp" + +// disable runtime asserts on xor eax,eax type of stuff (doesn't always work, +// disable explicitly in compiler settings) +#pragma runtime_checks( "u", off ) +template +IGL_INLINE void igl::svd3x3_sse( + const Eigen::Matrix& A, + Eigen::Matrix &U, + Eigen::Matrix &S, + Eigen::Matrix&V) +{ + // this code assumes USE_SSE_IMPLEMENTATION is defined + float Ashuffle[9][4], Ushuffle[9][4], Vshuffle[9][4], Sshuffle[3][4]; + for (int i=0; i<3; i++) + { + for (int j=0; j<3; j++) + { + for (int k=0; k<4; k++) + { + Ashuffle[i + j*3][k] = A(i + 3*k, j); + } + } + } + +#include "Singular_Value_Decomposition_Kernel_Declarations.hpp" + + ENABLE_SSE_IMPLEMENTATION(Va11=_mm_loadu_ps(Ashuffle[0]);) + ENABLE_SSE_IMPLEMENTATION(Va21=_mm_loadu_ps(Ashuffle[1]);) + ENABLE_SSE_IMPLEMENTATION(Va31=_mm_loadu_ps(Ashuffle[2]);) + ENABLE_SSE_IMPLEMENTATION(Va12=_mm_loadu_ps(Ashuffle[3]);) + ENABLE_SSE_IMPLEMENTATION(Va22=_mm_loadu_ps(Ashuffle[4]);) + ENABLE_SSE_IMPLEMENTATION(Va32=_mm_loadu_ps(Ashuffle[5]);) + ENABLE_SSE_IMPLEMENTATION(Va13=_mm_loadu_ps(Ashuffle[6]);) + ENABLE_SSE_IMPLEMENTATION(Va23=_mm_loadu_ps(Ashuffle[7]);) + ENABLE_SSE_IMPLEMENTATION(Va33=_mm_loadu_ps(Ashuffle[8]);) + +#include "Singular_Value_Decomposition_Main_Kernel_Body.hpp" + + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Ushuffle[0],Vu11);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Ushuffle[1],Vu21);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Ushuffle[2],Vu31);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Ushuffle[3],Vu12);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Ushuffle[4],Vu22);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Ushuffle[5],Vu32);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Ushuffle[6],Vu13);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Ushuffle[7],Vu23);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Ushuffle[8],Vu33);) + + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Vshuffle[0],Vv11);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Vshuffle[1],Vv21);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Vshuffle[2],Vv31);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Vshuffle[3],Vv12);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Vshuffle[4],Vv22);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Vshuffle[5],Vv32);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Vshuffle[6],Vv13);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Vshuffle[7],Vv23);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Vshuffle[8],Vv33);) + + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Sshuffle[0],Va11);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Sshuffle[1],Va22);) + ENABLE_SSE_IMPLEMENTATION(_mm_storeu_ps(Sshuffle[2],Va33);) + + for (int i=0; i<3; i++) + { + for (int j=0; j<3; j++) + { + for (int k=0; k<4; k++) + { + U(i + 3*k, j) = Ushuffle[i + j*3][k]; + V(i + 3*k, j) = Vshuffle[i + j*3][k]; + } + } + } + + for (int i=0; i<3; i++) + { + for (int k=0; k<4; k++) + { + S(i + 3*k, 0) = Sshuffle[i][k]; + } + } +} +#pragma runtime_checks( "u", restore ) + +// forced instantiation +template void igl::svd3x3_sse(const Eigen::Matrix& A, Eigen::Matrix &U, Eigen::Matrix &S, Eigen::Matrix&V); +//// doesn't even make sense with double because the wunder-SVD code is only single precision anyway... +//template void wunderSVD3x3_SSE(Eigen::Matrix const&, Eigen::Matrix&, Eigen::Matrix&, Eigen::Matrix&); +#endif diff --git a/src/igl/svd3x3_sse.h b/src/igl/svd3x3_sse.h new file mode 100644 index 0000000000..1fcff988ae --- /dev/null +++ b/src/igl/svd3x3_sse.h @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SVD3X3_SSE_H +#define IGL_SVD3X3_SSE_H +#include "igl_inline.h" +#include + +namespace igl +{ + // Super fast 3x3 SVD according to http://pages.cs.wisc.edu/~sifakis/project_pages/svd.html + // This is SSE version of svd3x3 (see svd3x3.h) which works on 4 matrices at a time + // These four matrices are simply stacked in columns, the rest is the same as for svd3x3 + // + // Inputs: + // A 12 by 3 stack of 3x3 matrices + // Outputs: + // U 12x3 left singular vectors stacked + // S 12x1 singular values stacked + // V 12x3 right singular vectors stacked + // + // Known bugs: this will not work correctly for double precision. + template + IGL_INLINE void svd3x3_sse( + const Eigen::Matrix& A, + Eigen::Matrix &U, + Eigen::Matrix &S, + Eigen::Matrix&V); +} +#ifndef IGL_STATIC_LIBRARY +# include "svd3x3_sse.cpp" +#endif +#endif diff --git a/src/igl/swept_volume_bounding_box.cpp b/src/igl/swept_volume_bounding_box.cpp new file mode 100644 index 0000000000..7fc12e2f10 --- /dev/null +++ b/src/igl/swept_volume_bounding_box.cpp @@ -0,0 +1,28 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "swept_volume_bounding_box.h" +#include "LinSpaced.h" + +IGL_INLINE void igl::swept_volume_bounding_box( + const size_t & n, + const std::function & V, + const size_t & steps, + Eigen::AlignedBox3d & box) +{ + using namespace Eigen; + box.setEmpty(); + const VectorXd t = igl::LinSpaced(steps,0,1); + // Find extent over all time steps + for(int ti = 0;ti +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SWEPT_VOLUME_BOUNDING_BOX_H +#define IGL_SWEPT_VOLUME_BOUNDING_BOX_H +#include "igl_inline.h" +#include +#include +#include +namespace igl +{ + // Construct an axis-aligned bounding box containing a shape undergoing a + // motion sampled at `steps` discrete momements. + // + // Inputs: + // n number of mesh vertices + // V function handle so that V(i,t) returns the 3d position of vertex + // i at time t, for t∈[0,1] + // steps number of time steps: steps=3 --> t∈{0,0.5,1} + // Outputs: + // box box containing mesh under motion + IGL_INLINE void swept_volume_bounding_box( + const size_t & n, + const std::function< + Eigen::RowVector3d(const size_t vi, const double t)> & V, + const size_t & steps, + Eigen::AlignedBox3d & box); +} + +#ifndef IGL_STATIC_LIBRARY +# include "swept_volume_bounding_box.cpp" +#endif + +#endif diff --git a/src/igl/swept_volume_signed_distance.cpp b/src/igl/swept_volume_signed_distance.cpp new file mode 100644 index 0000000000..2571a7d120 --- /dev/null +++ b/src/igl/swept_volume_signed_distance.cpp @@ -0,0 +1,122 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "swept_volume_signed_distance.h" +#include "LinSpaced.h" +#include "flood_fill.h" +#include "signed_distance.h" +#include "AABB.h" +#include "pseudonormal_test.h" +#include "per_face_normals.h" +#include "per_vertex_normals.h" +#include "per_edge_normals.h" +#include +#include +#include + +IGL_INLINE void igl::swept_volume_signed_distance( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const std::function & transform, + const size_t & steps, + const Eigen::MatrixXd & GV, + const Eigen::RowVector3i & res, + const double h, + const double isolevel, + const Eigen::VectorXd & S0, + Eigen::VectorXd & S) +{ + using namespace std; + using namespace igl; + using namespace Eigen; + S = S0; + const VectorXd t = igl::LinSpaced(steps,0,1); + const bool finite_iso = isfinite(isolevel); + const double extension = (finite_iso ? isolevel : 0) + sqrt(3.0)*h; + Eigen::AlignedBox3d box( + V.colwise().minCoeff().array()-extension, + V.colwise().maxCoeff().array()+extension); + // Precomputation + Eigen::MatrixXd FN,VN,EN; + Eigen::MatrixXi E; + Eigen::VectorXi EMAP; + per_face_normals(V,F,FN); + per_vertex_normals(V,F,PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE,FN,VN); + per_edge_normals( + V,F,PER_EDGE_NORMALS_WEIGHTING_TYPE_UNIFORM,FN,EN,E,EMAP); + AABB tree; + tree.init(V,F); + for(int ti = 0;ti::infinity(); + sqrd = tree.squared_distance(V,F,gv,min_sqrd,i,c); + if(sqrd & transform, + const size_t & steps, + const Eigen::MatrixXd & GV, + const Eigen::RowVector3i & res, + const double h, + const double isolevel, + Eigen::VectorXd & S) +{ + using namespace std; + using namespace igl; + using namespace Eigen; + S = VectorXd::Constant(GV.rows(),1,numeric_limits::quiet_NaN()); + return + swept_volume_signed_distance(V,F,transform,steps,GV,res,h,isolevel,S,S); +} diff --git a/src/igl/swept_volume_signed_distance.h b/src/igl/swept_volume_signed_distance.h new file mode 100644 index 0000000000..86074fb674 --- /dev/null +++ b/src/igl/swept_volume_signed_distance.h @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SWEPT_VOLUME_SIGNED_DISTANCE_H +#define IGL_SWEPT_VOLUME_SIGNED_DISTANCE_H +#include "igl_inline.h" +#include +#include +#include +namespace igl +{ + // Compute the signed distance to a sweep surface of a mesh under-going + // an arbitrary motion V(t) discretely sampled at `steps`-many moments in + // time at a grid. + // + // Inputs: + // V #V by 3 list of mesh positions in reference pose + // F #F by 3 list of triangle indices [0,n) + // transform function handle so that transform(t) returns the rigid + // transformation at time t∈[0,1] + // steps number of time steps: steps=3 --> t∈{0,0.5,1} + // GV #GV by 3 list of evaluation point grid positions + // res 3-long resolution of GV grid + // h edge-length of grid + // isolevel isolevel to "focus" on; grid positions far enough away from + // isolevel (based on h) will get approximate values). Set + // isolevel=infinity to get good values everywhere (slow and + // unnecessary if just trying to extract isolevel-level set). + // S0 #GV initial values (will take minimum with these), can be same + // as S) + // Outputs: + // S #GV list of signed distances + IGL_INLINE void swept_volume_signed_distance( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const std::function & transform, + const size_t & steps, + const Eigen::MatrixXd & GV, + const Eigen::RowVector3i & res, + const double h, + const double isolevel, + const Eigen::VectorXd & S0, + Eigen::VectorXd & S); + IGL_INLINE void swept_volume_signed_distance( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const std::function & transform, + const size_t & steps, + const Eigen::MatrixXd & GV, + const Eigen::RowVector3i & res, + const double h, + const double isolevel, + Eigen::VectorXd & S); + } + +#ifndef IGL_STATIC_LIBRARY +# include "swept_volume_signed_distance.cpp" +#endif + +#endif diff --git a/src/igl/trackball.cpp b/src/igl/trackball.cpp new file mode 100644 index 0000000000..3a5fa2e9f8 --- /dev/null +++ b/src/igl/trackball.cpp @@ -0,0 +1,168 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "trackball.h" + +#include "EPS.h" +#include "dot.h" +#include "cross.h" +#include "axis_angle_to_quat.h" +#include "quat_mult.h" +#include +#include +#include +#include +#include + +// Utility IGL_INLINE functions +template +static IGL_INLINE Q_type _QuatD(double w, double h) +{ + using namespace std; + return (Q_type)(std::abs(w) < std::abs(h) ? std::abs(w) : std::abs(h)) - 4; +} +template +static IGL_INLINE Q_type _QuatIX(double x, double w, double h) +{ + return (2.0f*(Q_type)x - (Q_type)w - 1.0f)/_QuatD(w, h); +} +template +static IGL_INLINE Q_type _QuatIY(double y, double w, double h) +{ + return (-2.0f*(Q_type)y + (Q_type)h - 1.0f)/_QuatD(w, h); +} + +// This is largely the trackball as implemented in AntTweakbar. Much of the +// code is straight from its source in TwMgr.cpp +// http://www.antisphere.com/Wiki/tools:anttweakbar +template +IGL_INLINE void igl::trackball( + const double w, + const double h, + const Q_type speed_factor, + const double down_mouse_x, + const double down_mouse_y, + const double mouse_x, + const double mouse_y, + Q_type * quat) +{ + assert(speed_factor > 0); + + double original_x = + _QuatIX(speed_factor*(down_mouse_x-w/2)+w/2, w, h); + double original_y = + _QuatIY(speed_factor*(down_mouse_y-h/2)+h/2, w, h); + + double x = _QuatIX(speed_factor*(mouse_x-w/2)+w/2, w, h); + double y = _QuatIY(speed_factor*(mouse_y-h/2)+h/2, w, h); + + double z = 1; + double n0 = sqrt(original_x*original_x + original_y*original_y + z*z); + double n1 = sqrt(x*x + y*y + z*z); + if(n0>igl::DOUBLE_EPS && n1>igl::DOUBLE_EPS) + { + double v0[] = { original_x/n0, original_y/n0, z/n0 }; + double v1[] = { x/n1, y/n1, z/n1 }; + double axis[3]; + cross(v0,v1,axis); + double sa = sqrt(dot(axis, axis)); + double ca = dot(v0, v1); + double angle = atan2(sa, ca); + if( x*x+y*y>1.0 ) + { + angle *= 1.0 + 0.2f*(sqrt(x*x+y*y)-1.0); + } + double qrot[4]; + axis_angle_to_quat(axis,angle,qrot); + quat[0] = qrot[0]; + quat[1] = qrot[1]; + quat[2] = qrot[2]; + quat[3] = qrot[3]; + } +} + + +template +IGL_INLINE void igl::trackball( + const double w, + const double h, + const Q_type speed_factor, + const Q_type * down_quat, + const double down_mouse_x, + const double down_mouse_y, + const double mouse_x, + const double mouse_y, + Q_type * quat) +{ + double qrot[4], qres[4], qorig[4]; + igl::trackball( + w,h, + speed_factor, + down_mouse_x,down_mouse_y, + mouse_x,mouse_y, + qrot); + double nqorig = + sqrt(down_quat[0]*down_quat[0]+ + down_quat[1]*down_quat[1]+ + down_quat[2]*down_quat[2]+ + down_quat[3]*down_quat[3]); + + if( fabs(nqorig)>igl::DOUBLE_EPS_SQ ) + { + qorig[0] = down_quat[0]/nqorig; + qorig[1] = down_quat[1]/nqorig; + qorig[2] = down_quat[2]/nqorig; + qorig[3] = down_quat[3]/nqorig; + igl::quat_mult(qrot,qorig,qres); + quat[0] = qres[0]; + quat[1] = qres[1]; + quat[2] = qres[2]; + quat[3] = qres[3]; + } + else + { + quat[0] = qrot[0]; + quat[1] = qrot[1]; + quat[2] = qrot[2]; + quat[3] = qrot[3]; + } +} + +template +IGL_INLINE void igl::trackball( + const double w, + const double h, + const double speed_factor, + const Eigen::Quaternion & down_quat, + const double down_mouse_x, + const double down_mouse_y, + const double mouse_x, + const double mouse_y, + Eigen::Quaternion & quat) +{ + using namespace std; + return trackball( + w, + h, + (Scalarquat)speed_factor, + down_quat.coeffs().data(), + down_mouse_x, + down_mouse_y, + mouse_x, + mouse_y, + quat.coeffs().data()); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::trackball(double, double, double, double const*, double, double, double, double, double*); +// generated by autoexplicit.sh +template void igl::trackball(double, double, float, float const*, double, double, double, double, float*); +template void igl::trackball(double, double, double, Eigen::Quaternion const&, double, double, double, double, Eigen::Quaternion&); +template void igl::trackball(double, double, double, Eigen::Quaternion const&, double, double, double, double, Eigen::Quaternion&); +#endif diff --git a/src/igl/trackball.h b/src/igl/trackball.h new file mode 100644 index 0000000000..6bf79e0198 --- /dev/null +++ b/src/igl/trackball.h @@ -0,0 +1,80 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_TRACKBALL_H +#define IGL_TRACKBALL_H +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Applies a trackball drag to identity + // Inputs: + // w width of the trackball context + // h height of the trackball context + // speed_factor controls how fast the trackball feels, 1 is normal + // down_mouse_x x position of mouse down + // down_mouse_y y position of mouse down + // mouse_x current x position of mouse + // mouse_y current y position of mouse + // Outputs: + // quat the resulting rotation (as quaternion) + template + IGL_INLINE void trackball( + const double w, + const double h, + const Q_type speed_factor, + const double down_mouse_x, + const double down_mouse_y, + const double mouse_x, + const double mouse_y, + Q_type * quat); + + // Applies a trackball drag to a given rotation + // Inputs: + // w width of the trackball context + // h height of the trackball context + // speed_factor controls how fast the trackball feels, 1 is normal + // down_quat rotation at mouse down, i.e. the rotation we're applying the + // trackball motion to (as quaternion) + // down_mouse_x x position of mouse down + // down_mouse_y y position of mouse down + // mouse_x current x position of mouse + // mouse_y current y position of mouse + // Outputs: + // quat the resulting rotation (as quaternion) + template + IGL_INLINE void trackball( + const double w, + const double h, + const Q_type speed_factor, + const Q_type * down_quat, + const double down_mouse_x, + const double down_mouse_y, + const double mouse_x, + const double mouse_y, + Q_type * quat); + // Eigen wrapper. + template + IGL_INLINE void trackball( + const double w, + const double h, + const double speed_factor, + const Eigen::Quaternion & down_quat, + const double down_mouse_x, + const double down_mouse_y, + const double mouse_x, + const double mouse_y, + Eigen::Quaternion & quat); +} + +#ifndef IGL_STATIC_LIBRARY +# include "trackball.cpp" +#endif + +#endif diff --git a/src/igl/transpose_blocks.cpp b/src/igl/transpose_blocks.cpp new file mode 100644 index 0000000000..004edd7ac2 --- /dev/null +++ b/src/igl/transpose_blocks.cpp @@ -0,0 +1,63 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "transpose_blocks.h" + +#include + +template +IGL_INLINE void igl::transpose_blocks( + const Eigen::Matrix & A, + const size_t k, + const size_t dim, + Eigen::Matrix & B) +{ + // Eigen matrices must be 2d so dim must be only 1 or 2 + assert(dim == 1 || dim == 2); + // Output is not allowed to be input + assert(&A != &B); + + + // block height, width, and number of blocks + int m,n; + if(dim == 1) + { + m = A.rows()/k; + n = A.cols(); + }else// dim == 2 + { + m = A.rows(); + n = A.cols()/k; + } + + // resize output + if(dim == 1) + { + B.resize(n*k,m); + }else//dim ==2 + { + B.resize(n,m*k); + } + + // loop over blocks + for(int b = 0;b<(int)k;b++) + { + if(dim == 1) + { + B.block(b*n,0,n,m) = A.block(b*m,0,m,n).transpose(); + }else//dim ==2 + { + B.block(0,b*m,n,m) = A.block(0,b*n,m,n).transpose(); + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::transpose_blocks(Eigen::Matrix const&, size_t, size_t, Eigen::Matrix&); +#endif diff --git a/src/igl/transpose_blocks.h b/src/igl/transpose_blocks.h new file mode 100644 index 0000000000..65ce89fbd3 --- /dev/null +++ b/src/igl/transpose_blocks.h @@ -0,0 +1,61 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_TRANSPOSE_BLOCKS_H +#define IGL_TRANSPOSE_BLOCKS_H +#include "igl_inline.h" + +#include + +namespace igl +{ + // Templates: + // T should be a eigen matrix primitive type like int or double + // Inputs: + // A m*k by n (dim: 1) or m by n*k (dim: 2) eigen Matrix of type T values + // k number of blocks + // dim dimension in which to transpose + // Output + // B n*k by m (dim: 1) or n by m*k (dim: 2) eigen Matrix of type T values, + // NOT allowed to be the same as A + // + // Example: + // A = [ + // 1 2 3 4 + // 5 6 7 8 + // 101 102 103 104 + // 105 106 107 108 + // 201 202 203 204 + // 205 206 207 208]; + // transpose_blocks(A,1,3,B); + // B -> [ + // 1 5 + // 2 6 + // 3 7 + // 4 8 + // 101 105 + // 102 106 + // 103 107 + // 104 108 + // 201 205 + // 202 206 + // 203 207 + // 204 208]; + // + template + IGL_INLINE void transpose_blocks( + const Eigen::Matrix & A, + const size_t k, + const size_t dim, + Eigen::Matrix & B); +} + +#ifndef IGL_STATIC_LIBRARY +# include "transpose_blocks.cpp" +#endif + +#endif diff --git a/src/igl/triangle/cdt.cpp b/src/igl/triangle/cdt.cpp new file mode 100644 index 0000000000..cbe1b3e1a1 --- /dev/null +++ b/src/igl/triangle/cdt.cpp @@ -0,0 +1,58 @@ +#include "cdt.h" +#include "../bounding_box.h" +#include "../triangle/triangulate.h" +#include "../remove_duplicate_vertices.h" +#include "../remove_unreferenced.h" +#include "../slice_mask.h" + +template < + typename DerivedV, + typename DerivedE, + typename DerivedWV, + typename DerivedWF, + typename DerivedWE, + typename DerivedJ> +IGL_INLINE void igl::triangle::cdt( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & E, + const std::string & flags, + Eigen::PlainObjectBase & WV, + Eigen::PlainObjectBase & WF, + Eigen::PlainObjectBase & WE, + Eigen::PlainObjectBase & J) +{ + assert(V.cols() == 2); + assert(E.cols() == 2); + typedef typename DerivedV::Scalar Scalar; + typedef Eigen::Matrix MatrixX2S; + //MatrixX2S BV; + //Eigen::MatrixXi BE; + //igl::bounding_box(V,BV,BE); + //WV.resize(V.rows()+BV.rows(),2); + //WV<, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::triangle::cdt, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/triangle/cdt.h b/src/igl/triangle/cdt.h new file mode 100644 index 0000000000..9f513e8533 --- /dev/null +++ b/src/igl/triangle/cdt.h @@ -0,0 +1,54 @@ +#ifndef IGL_TRIANGLE_CDT_H +#define IGL_TRIANGLE_CDT_H + +#include "../igl_inline.h" +#include + +namespace igl +{ + namespace triangle + { + // CDT Construct the constrained delaunay triangulation of the convex hull + // of a given set of points and segments in 2D. This differs from a direct + // call to triangulate because it will preprocess the input to remove + // duplicates and return an adjusted segment list on the output. + // + // + // BACKGROUND_MESH Construct a background mesh for a (messy) texture mesh with + // cosntraint edges that are about to deform. + // + // Inputs: + // V #V by 2 list of texture mesh vertices + // E #E by 2 list of constraint edge indices into V + // flags string of triangle flags should contain "-c" unless the + // some subset of segments are known to enclose all other + // points/segments. + // Outputs: + // WV #WV by 2 list of background mesh vertices + // WF #WF by 2 list of background mesh triangle indices into WV + // WE #WE by 2 list of constraint edge indices into WV (might be smaller + // than E because degenerate constraints have been removed) + // J #V list of indices into WF/WE for each vertex in V + // + template < + typename DerivedV, + typename DerivedE, + typename DerivedWV, + typename DerivedWF, + typename DerivedWE, + typename DerivedJ> + IGL_INLINE void cdt( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & E, + const std::string & flags, + Eigen::PlainObjectBase & WV, + Eigen::PlainObjectBase & WF, + Eigen::PlainObjectBase & WE, + Eigen::PlainObjectBase & J); + } +} + +#ifndef IGL_STATIC_LIBRARY +#include "cdt.cpp" +#endif +#endif diff --git a/src/igl/triangle/triangulate.cpp b/src/igl/triangle/triangulate.cpp new file mode 100644 index 0000000000..5ee1d4be8d --- /dev/null +++ b/src/igl/triangle/triangulate.cpp @@ -0,0 +1,181 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "triangulate.h" +#ifdef ANSI_DECLARATORS +# define IGL_PREVIOUSLY_DEFINED_ANSI_DECLARATORS ANSI_DECLARATORS +# undef ANSI_DECLARATORS +#endif +#ifdef REAL +# define IGL_PREVIOUSLY_DEFINED_REAL REAL +# undef REAL +#endif +#ifdef VOID +# define IGL_PREVIOUSLY_DEFINED_VOID VOID +# undef VOID +#endif +#define ANSI_DECLARATORS +#define REAL double +#define VOID int + +extern "C" +{ +#include +} + +#undef ANSI_DECLARATORS +#ifdef IGL_PREVIOUSLY_DEFINED_ANSI_DECLARATORS +# define ANSI_DECLARATORS IGL_PREVIOUSLY_DEFINED_ANSI_DECLARATORS +#endif + +#undef REAL +#ifdef IGL_PREVIOUSLY_DEFINED_REAL +# define REAL IGL_PREVIOUSLY_DEFINED_REAL +#endif + +#undef VOID +#ifdef IGL_PREVIOUSLY_DEFINED_VOID +# define VOID IGL_PREVIOUSLY_DEFINED_VOID +#endif + +template < + typename DerivedV, + typename DerivedE, + typename DerivedH, + typename DerivedV2, + typename DerivedF2> +IGL_INLINE void igl::triangle::triangulate( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & H, + const std::string flags, + Eigen::PlainObjectBase & V2, + Eigen::PlainObjectBase & F2) +{ + Eigen::VectorXi VM,EM,VM2,EM2; + return triangulate(V,E,H,VM,EM,flags,V2,F2,VM2,EM2); +} + +template < + typename DerivedV, + typename DerivedE, + typename DerivedH, + typename DerivedVM, + typename DerivedEM, + typename DerivedV2, + typename DerivedF2, + typename DerivedVM2, + typename DerivedEM2> +IGL_INLINE void igl::triangle::triangulate( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & H, + const Eigen::MatrixBase & VM, + const Eigen::MatrixBase & EM, + const std::string flags, + Eigen::PlainObjectBase & V2, + Eigen::PlainObjectBase & F2, + Eigen::PlainObjectBase & VM2, + Eigen::PlainObjectBase & EM2) +{ + using namespace std; + using namespace Eigen; + + assert( (VM.size() == 0 || V.rows() == VM.size()) && + "Vertex markers must be empty or same size as V"); + assert( (EM.size() == 0 || E.rows() == EM.size()) && + "Segment markers must be empty or same size as E"); + assert(V.cols() == 2); + assert(E.size() == 0 || E.cols() == 2); + assert(H.size() == 0 || H.cols() == 2); + + // Prepare the flags + string full_flags = flags + "pz" + (EM.size() || VM.size() ? "" : "B"); + + typedef Map< Matrix > MapXdr; + typedef Map< Matrix > MapXir; + + // Prepare the input struct + triangulateio in; + in.numberofpoints = V.rows(); + in.pointlist = (double*)calloc(V.size(),sizeof(double)); + { + MapXdr inpl(in.pointlist,V.rows(),V.cols()); + inpl = V.template cast(); + } + + in.numberofpointattributes = 0; + in.pointmarkerlist = (int*)calloc(V.size(),sizeof(int)) ; + for(unsigned i=0;i(); + } + in.segmentmarkerlist = (int*)calloc(E.rows(),sizeof(int)); + for (unsigned i=0;i(); + } + in.numberofregions = 0; + + // Prepare the output struct + triangulateio out; + out.pointlist = NULL; + out.trianglelist = NULL; + out.segmentlist = NULL; + out.segmentmarkerlist = NULL; + out.pointmarkerlist = NULL; + + // Call triangle + ::triangulate(const_cast(full_flags.c_str()), &in, &out, 0); + + // Return the mesh + V2 = MapXdr(out.pointlist,out.numberofpoints,2).cast(); + F2 = MapXir(out.trianglelist,out.numberoftriangles,3).cast(); + if(VM.size()) + { + VM2 = MapXir(out.pointmarkerlist,out.numberofpoints,1).cast(); + } + if(EM.size()) + { + EM2 = MapXir(out.segmentmarkerlist,out.numberofsegments,1).cast(); + } + + // Cleanup in + free(in.pointlist); + free(in.pointmarkerlist); + free(in.segmentlist); + free(in.segmentmarkerlist); + free(in.holelist); + // Cleanup out + free(out.pointlist); + free(out.trianglelist); + free(out.segmentlist); + free(out.segmentmarkerlist); + free(out.pointmarkerlist); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::triangle::triangulate, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::triangle::triangulate, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::basic_string, std::allocator >, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/triangle/triangulate.h b/src/igl/triangle/triangulate.h new file mode 100644 index 0000000000..90abdac9d2 --- /dev/null +++ b/src/igl/triangle/triangulate.h @@ -0,0 +1,87 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_TRIANGLE_TRIANGULATE_H +#define IGL_TRIANGLE_TRIANGULATE_H +#include "../igl_inline.h" +#include +#include + +namespace igl +{ + namespace triangle + { + // Triangulate the interior of a polygon using the triangle library. + // + // Inputs: + // V #V by 2 list of 2D vertex positions + // E #E by 2 list of vertex ids forming unoriented edges of the boundary of the polygon + // H #H by 2 coordinates of points contained inside holes of the polygon + // flags string of options pass to triangle (see triangle documentation) + // Outputs: + // V2 #V2 by 2 coordinates of the vertives of the generated triangulation + // F2 #F2 by 3 list of indices forming the faces of the generated triangulation + // + template < + typename DerivedV, + typename DerivedE, + typename DerivedH, + typename DerivedV2, + typename DerivedF2> + IGL_INLINE void triangulate( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & H, + const std::string flags, + Eigen::PlainObjectBase & V2, + Eigen::PlainObjectBase & F2); + + // Triangulate the interior of a polygon using the triangle library. + // + // Inputs: + // V #V by 2 list of 2D vertex positions + // E #E by 2 list of vertex ids forming unoriented edges of the boundary of the polygon + // H #H by 2 coordinates of points contained inside holes of the polygon + // M #V list of markers for input vertices + // flags string of options pass to triangle (see triangle documentation) + // Outputs: + // V2 #V2 by 2 coordinates of the vertives of the generated triangulation + // F2 #F2 by 3 list of indices forming the faces of the generated triangulation + // M2 #V2 list of markers for output vertices + // + // TODO: expose the option to prevent Steiner points on the boundary + // + template < + typename DerivedV, + typename DerivedE, + typename DerivedH, + typename DerivedVM, + typename DerivedEM, + typename DerivedV2, + typename DerivedF2, + typename DerivedVM2, + typename DerivedEM2> + IGL_INLINE void triangulate( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & H, + const Eigen::MatrixBase & VM, + const Eigen::MatrixBase & EM, + const std::string flags, + Eigen::PlainObjectBase & V2, + Eigen::PlainObjectBase & F2, + Eigen::PlainObjectBase & VM2, + Eigen::PlainObjectBase & EM2); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "triangulate.cpp" +#endif + +#endif diff --git a/src/igl/triangle_fan.cpp b/src/igl/triangle_fan.cpp new file mode 100644 index 0000000000..35944d0266 --- /dev/null +++ b/src/igl/triangle_fan.cpp @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "triangle_fan.h" +#include "exterior_edges.h" +#include "list_to_matrix.h" + +IGL_INLINE void igl::triangle_fan( + const Eigen::MatrixXi & E, + Eigen::MatrixXi & cap) +{ + using namespace std; + using namespace Eigen; + + // Handle lame base case + if(E.size() == 0) + { + cap.resize(0,E.cols()+1); + return; + } + // "Triangulate" aka "close" the E trivially with facets + // Note: in 2D we need to know if E endpoints are incoming or + // outgoing (left or right). Thus this will not work. + assert(E.cols() == 2); + // Arbitrary starting vertex + //int s = E(int(((double)rand() / RAND_MAX)*E.rows()),0); + int s = E(rand()%E.rows(),0); + vector > lcap; + for(int i = 0;i e(3); + e[0] = s; + e[1] = E(i,0); + e[2] = E(i,1); + lcap.push_back(e); + } + list_to_matrix(lcap,cap); +} + +IGL_INLINE Eigen::MatrixXi igl::triangle_fan( const Eigen::MatrixXi & E) +{ + Eigen::MatrixXi cap; + triangle_fan(E,cap); + return cap; +} diff --git a/src/igl/triangle_fan.h b/src/igl/triangle_fan.h new file mode 100644 index 0000000000..b56a05bc7e --- /dev/null +++ b/src/igl/triangle_fan.h @@ -0,0 +1,30 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_TRIANGLE_FAN_H +#define IGL_TRIANGLE_FAN_H +#include "igl_inline.h" +#include +namespace igl +{ + // Given a list of faces tessellate all of the "exterior" edges forming another + // list of + // + // Inputs: + // E #E by simplex_size-1 list of exterior edges (see exterior_edges.h) + // Outputs: + // cap #cap by simplex_size list of "faces" tessellating the boundary edges + IGL_INLINE void triangle_fan( + const Eigen::MatrixXi & E, + Eigen::MatrixXi & cap); + // In-line version + IGL_INLINE Eigen::MatrixXi triangle_fan( const Eigen::MatrixXi & E); +} +#ifndef IGL_STATIC_LIBRARY +# include "triangle_fan.cpp" +#endif +#endif diff --git a/src/igl/triangle_triangle_adjacency.cpp b/src/igl/triangle_triangle_adjacency.cpp new file mode 100644 index 0000000000..62e8fbe1ca --- /dev/null +++ b/src/igl/triangle_triangle_adjacency.cpp @@ -0,0 +1,274 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2018 Alec Jacobson, Marc Alexa +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "triangle_triangle_adjacency.h" +#include "vertex_triangle_adjacency.h" +#include "parallel_for.h" +#include "unique_edge_map.h" +#include +#include + +// Extract the face adjacencies +template +IGL_INLINE void igl::triangle_triangle_adjacency_extractTT( + const Eigen::MatrixBase& F, + std::vector >& TTT, + Eigen::PlainObjectBase& TT) +{ + TT.setConstant((int)(F.rows()),F.cols(),-1); + + for(int i=1;i<(int)TTT.size();++i) + { + std::vector& r1 = TTT[i-1]; + std::vector& r2 = TTT[i]; + if ((r1[0] == r2[0]) && (r1[1] == r2[1])) + { + TT(r1[2],r1[3]) = r2[2]; + TT(r2[2],r2[3]) = r1[2]; + } + } +} + +template +IGL_INLINE void igl::triangle_triangle_adjacency( + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& TT) +{ + const int n = F.maxCoeff()+1; + typedef Eigen::Matrix VectorXI; + VectorXI VF,NI; + vertex_triangle_adjacency(F,n,VF,NI); + TT = DerivedTT::Constant(F.rows(),3,-1); + // Loop over faces + igl::parallel_for(F.rows(),[&](int f) + { + // Loop over corners + for (int k = 0; k < 3; k++) + { + int vi = F(f,k), vin = F(f,(k+1)%3); + // Loop over face neighbors incident on this corner + for (int j = NI[vi]; j < NI[vi+1]; j++) + { + int fn = VF[j]; + // Not this face + if (fn != f) + { + // Face neighbor also has [vi,vin] edge + if (F(fn,0) == vin || F(fn,1) == vin || F(fn,2) == vin) + { + TT(f,k) = fn; + break; + } + } + } + } + }); +} + +template +IGL_INLINE void igl::triangle_triangle_adjacency_preprocess( + const Eigen::MatrixBase& F, + std::vector >& TTT) +{ + for(int f=0;f v2) std::swap(v1,v2); + std::vector r(4); + r[0] = v1; r[1] = v2; + r[2] = f; r[3] = i; + TTT.push_back(r); + } + std::sort(TTT.begin(),TTT.end()); +} + +// Extract the face adjacencies indices (needed for fast traversal) +template +IGL_INLINE void igl::triangle_triangle_adjacency_extractTTi( + const Eigen::MatrixBase& F, + std::vector >& TTT, + Eigen::PlainObjectBase& TTi) +{ + TTi.setConstant((int)(F.rows()),F.cols(),-1); + + for(int i=1;i<(int)TTT.size();++i) + { + std::vector& r1 = TTT[i-1]; + std::vector& r2 = TTT[i]; + if ((r1[0] == r2[0]) && (r1[1] == r2[1])) + { + TTi(r1[2],r1[3]) = r2[3]; + TTi(r2[2],r2[3]) = r1[3]; + } + } +} + +// Compute triangle-triangle adjacency with indices +template +IGL_INLINE void igl::triangle_triangle_adjacency( + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& TT, + Eigen::PlainObjectBase& TTi) +{ + triangle_triangle_adjacency(F,TT); + TTi = DerivedTTi::Constant(TT.rows(),TT.cols(),-1); + //for(int f = 0; f= 0) + { + for(int kn = 0;kn<3;kn++) + { + int vin = F(fn,kn), vjn = F(fn,(kn+1)%3); + if(vi == vjn && vin == vj) + { + TTi(f,k) = kn; + break; + } + } + } + } + }); +} + +template < + typename DerivedF, + typename TTIndex, + typename TTiIndex> + IGL_INLINE void igl::triangle_triangle_adjacency( + const Eigen::MatrixBase & F, + std::vector > > & TT, + std::vector > > & TTi) +{ + return triangle_triangle_adjacency(F,true,TT,TTi); +} + +template < + typename DerivedF, + typename TTIndex> + IGL_INLINE void igl::triangle_triangle_adjacency( + const Eigen::MatrixBase & F, + std::vector > > & TT) +{ + std::vector > > not_used; + return triangle_triangle_adjacency(F,false,TT,not_used); +} + +template < + typename DerivedF, + typename TTIndex, + typename TTiIndex> + IGL_INLINE void igl::triangle_triangle_adjacency( + const Eigen::MatrixBase & F, + const bool construct_TTi, + std::vector > > & TT, + std::vector > > & TTi) +{ + using namespace Eigen; + using namespace std; + assert(F.cols() == 3 && "Faces must be triangles"); + // number of faces + typedef typename DerivedF::Index Index; + typedef Matrix MatrixX2I; + typedef Matrix VectorXI; + MatrixX2I E,uE; + VectorXI EMAP; + vector > uE2E; + unique_edge_map(F,E,uE,EMAP,uE2E); + return triangle_triangle_adjacency(E,EMAP,uE2E,construct_TTi,TT,TTi); +} + +template < + typename DerivedE, + typename DerivedEMAP, + typename uE2EType, + typename TTIndex, + typename TTiIndex> + IGL_INLINE void igl::triangle_triangle_adjacency( + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & EMAP, + const std::vector > & uE2E, + const bool construct_TTi, + std::vector > > & TT, + std::vector > > & TTi) +{ + using namespace std; + using namespace Eigen; + typedef typename DerivedE::Index Index; + const size_t m = E.rows()/3; + assert((size_t)E.rows() == m*3 && "E should come from list of triangles."); + // E2E[i] --> {j,k,...} means face edge i corresponds to other faces edges j + // and k + TT.resize (m,vector >(3)); + if(construct_TTi) + { + TTi.resize(m,vector >(3)); + } + + // No race conditions because TT*[f][c]'s are in bijection with e's + // Minimum number of items per thread + //const size_t num_e = E.rows(); + // Slightly better memory access than loop over E + igl::parallel_for( + m, + [&](const Index & f) + { + for(Index c = 0;c<3;c++) + { + const Index e = f + m*c; + //const Index c = e/m; + const vector & N = uE2E[EMAP(e)]; + for(const auto & ne : N) + { + const Index nf = ne%m; + // don't add self + if(nf != f) + { + TT[f][c].push_back(nf); + if(construct_TTi) + { + const Index nc = ne/m; + TTi[f][c].push_back(nc); + } + } + } + } + }, + 1000ul); + + +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::triangle_triangle_adjacency, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::triangle_triangle_adjacency, int>(Eigen::MatrixBase > const&, std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > >&); +// generated by autoexplicit.sh +template void igl::triangle_triangle_adjacency, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::triangle_triangle_adjacency, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::triangle_triangle_adjacency, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::triangle_triangle_adjacency, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::triangle_triangle_adjacency, long, long>(Eigen::MatrixBase > const&, std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > >&, std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > >&); +template void igl::triangle_triangle_adjacency, int>(Eigen::MatrixBase > const&, std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > >&); +#ifdef WIN32 +template void igl::triangle_triangle_adjacency, __int64, __int64>(class Eigen::MatrixBase> const &, class std::vector>, class std::allocator>>>, class std::allocator>, class std::allocator>>>>> &, class std::vector>, class std::allocator>>>, class std::allocator>, class std::allocator>>>>> &); +template void igl::triangle_triangle_adjacency, class Eigen::Matrix, unsigned __int64, int, int>(class Eigen::MatrixBase> const &, class Eigen::MatrixBase> const &, class std::vector>, class std::allocator>>> const &, bool, class std::vector>, class std::allocator>>>, class std::allocator>, class std::allocator>>>>> &, class std::vector>, class std::allocator>>>, class std::allocator>, class std::allocator>>>>> &); +#endif +template void igl::triangle_triangle_adjacency, Eigen::Matrix, long, long, long>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >, std::allocator > > > const&, bool, std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > >&, std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > >&); +template void igl::triangle_triangle_adjacency, Eigen::Matrix, unsigned long, int, int>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >, std::allocator > > > const&, bool, std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > >&, std::vector >, std::allocator > > >, std::allocator >, std::allocator > > > > >&); +#endif diff --git a/src/igl/triangle_triangle_adjacency.h b/src/igl/triangle_triangle_adjacency.h new file mode 100644 index 0000000000..632141bd09 --- /dev/null +++ b/src/igl/triangle_triangle_adjacency.h @@ -0,0 +1,115 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_TRIANGLE_TRIANGLE_ADJACENCY_H +#define IGL_TRIANGLE_TRIANGLE_ADJACENCY_H +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Constructs the triangle-triangle adjacency matrix for a given + // mesh (V,F). + // + // Inputs: + // F #F by simplex_size list of mesh faces (must be triangles) + // Outputs: + // TT #F by #3 adjacent matrix, the element i,j is the id of the triangle + // adjacent to the j edge of triangle i + // TTi #F by #3 adjacent matrix, the element i,j is the id of edge of the + // triangle TT(i,j) that is adjacent with triangle i + // + // NOTE: the first edge of a triangle is [0,1] the second [1,2] and the third + // [2,3]. this convention is DIFFERENT from cotmatrix_entries.h + template + IGL_INLINE void triangle_triangle_adjacency( + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& TT, + Eigen::PlainObjectBase& TTi); + template + IGL_INLINE void triangle_triangle_adjacency( + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& TT); + // Preprocessing + template + IGL_INLINE void triangle_triangle_adjacency_preprocess( + const Eigen::MatrixBase& F, + std::vector >& TTT); + // Extract the face adjacencies + template + IGL_INLINE void triangle_triangle_adjacency_extractTT( + const Eigen::MatrixBase& F, + std::vector >& TTT, + Eigen::PlainObjectBase& TT); + // Extract the face adjacencies indices (needed for fast traversal) + template + IGL_INLINE void triangle_triangle_adjacency_extractTTi( + const Eigen::MatrixBase& F, + std::vector >& TTT, + Eigen::PlainObjectBase& TTi); + // Adjacency list version, which works with non-manifold meshes + // + // Inputs: + // F #F by 3 list of triangle indices + // Outputs: + // TT #F by 3 list of lists so that TT[i][c] --> {j,k,...} means that + // faces j and k etc. are edge-neighbors of face i on face i's edge + // opposite corner c + // TTj #F list of lists so that TTj[i][c] --> {j,k,...} means that face + // TT[i][c][0] is an edge-neighbor of face i incident on the edge of face + // TT[i][c][0] opposite corner j, and TT[i][c][1] " corner k, etc. + template < + typename DerivedF, + typename TTIndex, + typename TTiIndex> + IGL_INLINE void triangle_triangle_adjacency( + const Eigen::MatrixBase & F, + std::vector > > & TT, + std::vector > > & TTi); + template < typename DerivedF, typename TTIndex> + IGL_INLINE void triangle_triangle_adjacency( + const Eigen::MatrixBase & F, + std::vector > > & TT); + // Wrapper with bool to choose whether to compute TTi (this prototype should + // be "hidden"). + template < + typename DerivedF, + typename TTIndex, + typename TTiIndex> + IGL_INLINE void triangle_triangle_adjacency( + const Eigen::MatrixBase & F, + const bool construct_TTi, + std::vector > > & TT, + std::vector > > & TTi); + // Inputs: + // E #F*3 by 2 list of all of directed edges in order (see + // `oriented_facets`) + // EMAP #F*3 list of indices into uE, mapping each directed edge to unique + // undirected edge + // uE2E #uE list of lists of indices into E of coexisting edges + // See also: unique_edge_map, oriented_facets + template < + typename DerivedE, + typename DerivedEMAP, + typename uE2EType, + typename TTIndex, + typename TTiIndex> + IGL_INLINE void triangle_triangle_adjacency( + const Eigen::MatrixBase & E, + const Eigen::MatrixBase & EMAP, + const std::vector > & uE2E, + const bool construct_TTi, + std::vector > > & TT, + std::vector > > & TTi); +} + +#ifndef IGL_STATIC_LIBRARY +# include "triangle_triangle_adjacency.cpp" +#endif + +#endif diff --git a/src/igl/triangles_from_strip.cpp b/src/igl/triangles_from_strip.cpp new file mode 100644 index 0000000000..1db854834d --- /dev/null +++ b/src/igl/triangles_from_strip.cpp @@ -0,0 +1,36 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "triangles_from_strip.h" +#include + +template +IGL_INLINE void igl::triangles_from_strip( + const Eigen::MatrixBase& S, + Eigen::PlainObjectBase& F) +{ + using namespace std; + F.resize(S.size()-2,3); + for(int s = 0;s < S.size()-2;s++) + { + if(s%2 == 0) + { + F(s,0) = S(s+2); + F(s,1) = S(s+1); + F(s,2) = S(s+0); + }else + { + F(s,0) = S(s+0); + F(s,1) = S(s+1); + F(s,2) = S(s+2); + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +#endif diff --git a/src/igl/triangles_from_strip.h b/src/igl/triangles_from_strip.h new file mode 100644 index 0000000000..01ffd64028 --- /dev/null +++ b/src/igl/triangles_from_strip.h @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_TRIANGLES_FROM_STRIP_H +#define IGL_TRIANGLES_FROM_STRIP_H +#include "igl_inline.h" +#include +namespace igl +{ + // TRIANGLES_FROM_STRIP Create a list of triangles from a stream of indices + // along a strip. + // + // Inputs: + // S #S list of indices + // Outputs: + // F #S-2 by 3 list of triangle indices + // + template + IGL_INLINE void triangles_from_strip( + const Eigen::MatrixBase& S, + Eigen::PlainObjectBase& F); +} + +#ifndef IGL_STATIC_LIBRARY +# include "triangles_from_strip.cpp" +#endif + +#endif + diff --git a/src/igl/two_axis_valuator_fixed_up.cpp b/src/igl/two_axis_valuator_fixed_up.cpp new file mode 100644 index 0000000000..4ffb607f41 --- /dev/null +++ b/src/igl/two_axis_valuator_fixed_up.cpp @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "two_axis_valuator_fixed_up.h" +#include "PI.h" + +template +IGL_INLINE void igl::two_axis_valuator_fixed_up( + const int w, + const int h, + const double speed, + const Eigen::Quaternion & down_quat, + const int down_x, + const int down_y, + const int mouse_x, + const int mouse_y, + Eigen::Quaternion & quat) +{ + using namespace Eigen; + Matrix axis(0,1,0); + quat = down_quat * + Quaternion( + AngleAxis( + PI*((Scalarquat)(mouse_x-down_x))/(Scalarquat)w*speed/2.0, + axis.normalized())); + quat.normalize(); + { + Matrix axis(1,0,0); + if(axis.norm() != 0) + { + quat = + Quaternion( + AngleAxis( + PI*(mouse_y-down_y)/(Scalarquat)h*speed/2.0, + axis.normalized())) * quat; + quat.normalize(); + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::two_axis_valuator_fixed_up(int, int, double, Eigen::Quaternion const&, int, int, int, int, Eigen::Quaternion&); +template void igl::two_axis_valuator_fixed_up(int, int, double, Eigen::Quaternion const&, int, int, int, int, Eigen::Quaternion&); +#endif diff --git a/src/igl/two_axis_valuator_fixed_up.h b/src/igl/two_axis_valuator_fixed_up.h new file mode 100644 index 0000000000..7b2e9aabf0 --- /dev/null +++ b/src/igl/two_axis_valuator_fixed_up.h @@ -0,0 +1,51 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_TWO_AXIS_VALUATOR_FIXED_AXIS_UP_H +#define IGL_TWO_AXIS_VALUATOR_FIXED_AXIS_UP_H + +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Applies a two-axis valuator drag rotation (as seen in Maya/Studio max) to a given rotation. + // Inputs: + // w width of the trackball context + // h height of the trackball context + // speed controls how fast the trackball feels, 1 is normal + // down_quat rotation at mouse down, i.e. the rotation we're applying the + // trackball motion to (as quaternion). **Note:** Up-vector that is fixed + // is with respect to this rotation. + // down_x position of mouse down + // down_y position of mouse down + // mouse_x current x position of mouse + // mouse_y current y position of mouse + // Outputs: + // quat the resulting rotation (as quaternion) + // + // See also: snap_to_fixed_up + template + IGL_INLINE void two_axis_valuator_fixed_up( + const int w, + const int h, + const double speed, + const Eigen::Quaternion & down_quat, + const int down_x, + const int down_y, + const int mouse_x, + const int mouse_y, + Eigen::Quaternion & quat); +} + +#ifndef IGL_STATIC_LIBRARY +# include "two_axis_valuator_fixed_up.cpp" +#endif + +#endif + diff --git a/src/igl/uniformly_sample_two_manifold.cpp b/src/igl/uniformly_sample_two_manifold.cpp new file mode 100644 index 0000000000..d35f741131 --- /dev/null +++ b/src/igl/uniformly_sample_two_manifold.cpp @@ -0,0 +1,427 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "uniformly_sample_two_manifold.h" +#include "verbose.h" +#include "slice.h" +#include "colon.h" +#include "all_pairs_distances.h" +#include "mat_max.h" +#include "vertex_triangle_adjacency.h" +#include "get_seconds.h" +#include "cat.h" +//#include "MT19937.h" +#include "partition.h" + +////////////////////////////////////////////////////////////////////////////// +// Helper functions +////////////////////////////////////////////////////////////////////////////// + +IGL_INLINE void igl::uniformly_sample_two_manifold( + const Eigen::MatrixXd & W, + const Eigen::MatrixXi & F, + const int k, + const double push, + Eigen::MatrixXd & WS) +{ + using namespace Eigen; + using namespace std; + + // Euclidean distance between two points on a mesh given as barycentric + // coordinates + // Inputs: + // W #W by dim positions of mesh in weight space + // F #F by 3 indices of triangles + // face_A face index where 1st point lives + // bary_A barycentric coordinates of 1st point on face_A + // face_B face index where 2nd point lives + // bary_B barycentric coordinates of 2nd point on face_B + // Returns distance in euclidean space + const auto & bary_dist = [] ( + const Eigen::MatrixXd & W, + const Eigen::MatrixXi & F, + const int face_A, + const Eigen::Vector3d & bary_A, + const int face_B, + const Eigen::Vector3d & bary_B) -> double + { + return + ((bary_A(0)*W.row(F(face_A,0)) + + bary_A(1)*W.row(F(face_A,1)) + + bary_A(2)*W.row(F(face_A,2))) + - + (bary_B(0)*W.row(F(face_B,0)) + + bary_B(1)*W.row(F(face_B,1)) + + bary_B(2)*W.row(F(face_B,2)))).norm(); + }; + + // Base case if F is a tet list, find all faces and pass as non-manifold + // triangle mesh + if(F.cols() == 4) + { + verbose("uniform_sample.h: sampling tet mesh\n"); + MatrixXi T0 = F.col(0); + MatrixXi T1 = F.col(1); + MatrixXi T2 = F.col(2); + MatrixXi T3 = F.col(3); + // Faces from tets + MatrixXi TF = + cat(1, + cat(1, + cat(2,T0, cat(2,T1,T2)), + cat(2,T0, cat(2,T2,T3))), + cat(1, + cat(2,T0, cat(2,T3,T1)), + cat(2,T1, cat(2,T3,T2))) + ); + assert(TF.rows() == 4*F.rows()); + assert(TF.cols() == 3); + uniformly_sample_two_manifold(W,TF,k,push,WS); + return; + } + + double start = get_seconds(); + + VectorXi S; + // First get sampling as best as possible on mesh + uniformly_sample_two_manifold_at_vertices(W,k,push,S); + verbose("Lap: %g\n",get_seconds()-start); + slice(W,S,colon(0,W.cols()-1),WS); + //cout<<"WSmesh=["< > VF,VFi; + vertex_triangle_adjacency(W,F,VF,VFi); + + // List of list of face indices, for each sample gives index to face it is on + vector > sample_faces; sample_faces.resize(k); + // List of list of barycentric coordinates, for each sample gives b-coords in + // face its on + vector > sample_barys; sample_barys.resize(k); + // List of current maxmins amongst samples + vector cur_maxmin; cur_maxmin.resize(k); + // List of distance matrices, D(i)(s,j) reveals distance from i's sth sample + // to jth seed if j D; D.resize(k); + + // Precompute an W.cols() by W.cols() identity matrix + MatrixXd I(MatrixXd::Identity(W.cols(),W.cols())); + + // Describe each seed as a face index and barycentric coordinates + for(int i = 0;i < k;i++) + { + // Unreferenced vertex? + assert(VF[S(i)].size() > 0); + sample_faces[i].push_back(VF[S(i)][0]); + // We're right on a face vertex so barycentric coordinates are 0, but 1 at + // that vertex + Eigen::Vector3d bary(0,0,0); + bary( VFi[S(i)][0] ) = 1; + sample_barys[i].push_back(bary); + // initialize this to current maxmin + cur_maxmin[i] = 0; + } + + // initialize radius + double radius = 1.0; + // minimum radius (bound on precision) + //double min_radius = 1e-5; + double min_radius = 1e-5; + int max_num_rand_samples_per_triangle = 100; + int max_sample_attempts_per_triangle = 1000; + // Max number of outer iterations for a given radius + int max_iters = 1000; + + // continue iterating until radius is smaller than some threshold + while(radius > min_radius) + { + // initialize each seed + for(int i = 0;i < k;i++) + { + // Keep track of cur_maxmin data + int face_i = sample_faces[i][cur_maxmin[i]]; + Eigen::Vector3d bary(sample_barys[i][cur_maxmin[i]]); + // Find index in face of closest mesh vertex (on this face) + int index_in_face = + (bary(0) > bary(1) ? (bary(0) > bary(2) ? 0 : 2) + : (bary(1) > bary(2) ? 1 : 2)); + // find closest mesh vertex + int vertex_i = F(face_i,index_in_face); + // incident triangles + vector incident_F = VF[vertex_i]; + // We're going to try to place num_rand_samples_per_triangle samples on + // each sample *after* this location + sample_barys[i].clear(); + sample_faces[i].clear(); + cur_maxmin[i] = 0; + sample_barys[i].push_back(bary); + sample_faces[i].push_back(face_i); + // Current seed location in weight space + VectorXd seed = + bary(0)*W.row(F(face_i,0)) + + bary(1)*W.row(F(face_i,1)) + + bary(2)*W.row(F(face_i,2)); +#ifdef EXTREME_VERBOSE + verbose("i: %d\n",i); + verbose("face_i: %d\n",face_i); + //cout<<"bary: "<1) + { + u = 1-rv; + v = 1-ru; + }else + { + u = ru; + v = rv; + } + Eigen::Vector3d sample_bary(u,v,1-u-v); + double d = bary_dist(W,F,face_i,bary,face_f,sample_bary); + // check that sample is close enough + if(d= max_num_rand_samples_per_triangle) + { +#ifdef EXTREME_VERBOSE + verbose("Reached maximum number of samples per face\n"); +#endif + break; + } + if(s==(max_sample_attempts_per_triangle-1)) + { +#ifdef EXTREME_VERBOSE + verbose("Reached maximum sample attempts per triangle\n"); +#endif + } + } +#ifdef EXTREME_VERBOSE + verbose("sample_faces[%d].size(): %d\n",i,sample_faces[i].size()); + verbose("sample_barys[%d].size(): %d\n",i,sample_barys[i].size()); +#endif + } + } + + // Precompute distances from each seed's random samples to each "pushed" + // corner + // Put -1 in entries corresponding distance of a seed's random samples to + // self + // Loop over seeds + for(int i = 0;i < k;i++) + { + // resize distance matrix for new samples + D[i].resize(sample_faces[i].size(),k+W.cols()); + // Loop over i's samples + for(int s = 0;s<(int)sample_faces[i].size();s++) + { + int sample_face = sample_faces[i][s]; + Eigen::Vector3d sample_bary = sample_barys[i][s]; + // Loop over other seeds + for(int j = 0;j < k;j++) + { + // distance from sample(i,s) to seed j + double d; + if(i==j) + { + // phony self distance: Ilya's idea of infinite + d = 10; + }else + { + int seed_j_face = sample_faces[j][cur_maxmin[j]]; + Eigen::Vector3d seed_j_bary(sample_barys[j][cur_maxmin[j]]); + d = bary_dist(W,F,sample_face,sample_bary,seed_j_face,seed_j_bary); + } + D[i](s,j) = d; + } + // Loop over corners + for(int j = 0;j < W.cols();j++) + { + // distance from sample(i,s) to corner j + double d = + ((sample_bary(0)*W.row(F(sample_face,0)) + + sample_bary(1)*W.row(F(sample_face,1)) + + sample_bary(2)*W.row(F(sample_face,2))) + - I.row(j)).norm()/push; + // append after distances to seeds + D[i](s,k+j) = d; + } + } + } + + int iters = 0; + while(true) + { + bool has_changed = false; + // try to move each seed + for(int i = 0;i < k;i++) + { + // for each sample look at distance to closest seed/corner + VectorXd minD = D[i].rowwise().minCoeff(); + assert(minD.size() == (int)sample_faces[i].size()); + // find random sample with maximum minimum distance to other seeds + int old_cur_maxmin = cur_maxmin[i]; + double max_min = -2; + for(int s = 0;s<(int)sample_faces[i].size();s++) + { + if(max_min < minD(s)) + { + max_min = minD(s); + // Set this as the new seed location + cur_maxmin[i] = s; + } + } +#ifdef EXTREME_VERBOSE + verbose("max_min: %g\n",max_min); + verbose("cur_maxmin[%d]: %d->%d\n",i,old_cur_maxmin,cur_maxmin[i]); +#endif + // did location change? + has_changed |= (old_cur_maxmin!=cur_maxmin[i]); + // update distances of random samples of other seeds + } + // if no seed moved, exit + if(!has_changed) + { + break; + } + iters++; + if(iters>=max_iters) + { + verbose("Hit max iters (%d) before converging\n",iters); + } + } + // shrink radius + //radius *= 0.9; + //radius *= 0.99; + radius *= 0.9; + } + // Collect weight space locations + WS.resize(k,W.cols()); + for(int i = 0;i ignore; + partition(W,k+W.cols(),G,S,ignore); + // Remove corners, which better be at top + S = S.segment(W.cols(),k).eval(); + + MatrixXd WS; + slice(W,S,colon(0,W.cols()-1),WS); + //cout<<"WSpartition=["< +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UNIFORMLY_SAMPLE_TWO_MANIFOLD_H +#define IGL_UNIFORMLY_SAMPLE_TWO_MANIFOLD_H +#include "igl_inline.h" +#include +namespace igl +{ + // UNIFORMLY_SAMPLE_TWO_MANIFOLD Attempt to sample a mesh uniformly by + // furthest point relaxation as described in "Fast Automatic Skinning + // Transformations" + // + // [Jacobson et al. 12] Section 3.3. + // + // Inputs: + // W #W by dim positions of mesh in weight space + // F #F by 3 indices of triangles + // k number of samplse + // push factor by which corners should be pushed away + // Outputs + // WS k by dim locations in weights space + // + IGL_INLINE void uniformly_sample_two_manifold( + const Eigen::MatrixXd & W, + const Eigen::MatrixXi & F, + const int k, + const double push, + Eigen::MatrixXd & WS); + // Find uniform sampling up to placing samples on mesh vertices + IGL_INLINE void uniformly_sample_two_manifold_at_vertices( + const Eigen::MatrixXd & OW, + const int k, + const double push, + Eigen::VectorXi & S); +} +#ifndef IGL_STATIC_LIBRARY +# include "uniformly_sample_two_manifold.cpp" +#endif +#endif diff --git a/src/igl/unique.cpp b/src/igl/unique.cpp new file mode 100644 index 0000000000..9c5c7cb8d5 --- /dev/null +++ b/src/igl/unique.cpp @@ -0,0 +1,222 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unique.h" +#include "sort.h" +#include "IndexComparison.h" +#include "SortableRow.h" +#include "sortrows.h" +#include "list_to_matrix.h" +#include "matrix_to_list.h" + +#include +#include +#include + +template +IGL_INLINE void igl::unique( + const std::vector & A, + std::vector & C, + std::vector & IA, + std::vector & IC) +{ + using namespace std; + std::vector IM; + std::vector sortA; + igl::sort(A,true,sortA,IM); + // Original unsorted index map + IA.resize(sortA.size()); + for(int i=0;i<(int)sortA.size();i++) + { + IA[i] = i; + } + IA.erase( + std::unique( + IA.begin(), + IA.end(), + igl::IndexEquals& >(sortA)),IA.end()); + + IC.resize(A.size()); + { + int j = 0; + for(int i = 0;i<(int)sortA.size();i++) + { + if(sortA[IA[j]] != sortA[i]) + { + j++; + } + IC[IM[i]] = j; + } + } + C.resize(IA.size()); + // Reindex IA according to IM + for(int i = 0;i<(int)IA.size();i++) + { + IA[i] = IM[IA[i]]; + C[i] = A[IA[i]]; + } + +} + +template +IGL_INLINE void igl::unique( + const std::vector & A, + std::vector & C) +{ + std::vector IA,IC; + return igl::unique(A,C,IA,IC); +} + +template < + typename DerivedA, + typename DerivedC, + typename DerivedIA, + typename DerivedIC> +IGL_INLINE void igl::unique( + const Eigen::DenseBase & A, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & IA, + Eigen::PlainObjectBase & IC) +{ + using namespace std; + using namespace Eigen; + vector vA; + vector vC; + vector vIA,vIC; + matrix_to_list(A,vA); + unique(vA,vC,vIA,vIC); + list_to_matrix(vC,C); + list_to_matrix(vIA,IA); + list_to_matrix(vIC,IC); +} + +template < + typename DerivedA, + typename DerivedC + > +IGL_INLINE void igl::unique( + const Eigen::DenseBase & A, + Eigen::PlainObjectBase & C) +{ + using namespace std; + using namespace Eigen; + vector vA; + vector vC; + vector vIA,vIC; + matrix_to_list(A,vA); + unique(vA,vC,vIA,vIC); + list_to_matrix(vC,C); +} + +// Obsolete slow version converting to vectors +// template +// IGL_INLINE void igl::unique_rows( +// const Eigen::PlainObjectBase& A, +// Eigen::PlainObjectBase& C, +// Eigen::PlainObjectBase& IA, +// Eigen::PlainObjectBase& IC) +// { +// using namespace std; +// +// typedef Eigen::Matrix RowVector; +// vector > rows; +// rows.resize(A.rows()); +// // Loop over rows +// for(int i = 0;i(ri); +// } +// vector > vC; +// +// // unique on rows +// vector vIA; +// vector vIC; +// unique(rows,vC,vIA,vIC); +// +// // Convert to eigen +// C.resize(vC.size(),A.cols()); +// IA.resize(vIA.size(),1); +// IC.resize(vIC.size(),1); +// for(int i = 0;i +// IGL_INLINE void igl::unique_rows_many( +// const Eigen::PlainObjectBase& A, +// Eigen::PlainObjectBase& C, +// Eigen::PlainObjectBase& IA, +// Eigen::PlainObjectBase& IC) +// { +// using namespace std; +// // frequency map +// typedef Eigen::Matrix RowVector; +// IC.resize(A.rows(),1); +// map, int> fm; +// const int m = A.rows(); +// for(int i = 0;i(ri)) == 0) +// { +// fm[SortableRow(ri)] = i; +// } +// IC(i) = fm[SortableRow(ri)]; +// } +// IA.resize(fm.size(),1); +// Eigen::VectorXi RIA(m); +// C.resize(fm.size(),A.cols()); +// { +// int i = 0; +// for(typename map , int >::const_iterator fit = fm.begin(); +// fit != fm.end(); +// fit++) +// { +// IA(i) = fit->second; +// RIA(fit->second) = i; +// C.row(i) = fit->first.data; +// i++; +// } +// } +// // IC should index C +// for(int i = 0;i, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::unique, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::unique, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::unique, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&); +template void igl::unique, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique(std::vector > const&, std::vector >&); +template void igl::unique(std::vector > const&, std::vector >&); +template void igl::unique(std::vector > const&, std::vector >&, std::vector >&, std::vector >&); +template void igl::unique(std::vector > const&, std::vector >&, std::vector >&, std::vector >&); +#ifdef WIN32 +template void igl::unique,class Eigen::Matrix,class Eigen::Matrix<__int64,-1,1,0,-1,1>,class Eigen::Matrix<__int64,-1,1,0,-1,1> >(class Eigen::DenseBase > const &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &); +#endif +#endif diff --git a/src/igl/unique.h b/src/igl/unique.h new file mode 100644 index 0000000000..8618da15ed --- /dev/null +++ b/src/igl/unique.h @@ -0,0 +1,58 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UNIQUE_H +#define IGL_UNIQUE_H +#include "igl_inline.h" + +#include +#include +namespace igl +{ + // Act like matlab's [C,IA,IC] = unique(X) + // + // Templates: + // T comparable type T + // Inputs: + // A #A vector of type T + // Outputs: + // C #C vector of unique entries in A + // IA #C index vector so that C = A(IA); + // IC #A index vector so that A = C(IC); + template + IGL_INLINE void unique( + const std::vector & A, + std::vector & C, + std::vector & IA, + std::vector & IC); + template + IGL_INLINE void unique( + const std::vector & A, + std::vector & C); + template < + typename DerivedA, + typename DerivedC, + typename DerivedIA, + typename DerivedIC> + IGL_INLINE void unique( + const Eigen::DenseBase & A, + Eigen::PlainObjectBase & C, + Eigen::PlainObjectBase & IA, + Eigen::PlainObjectBase & IC); + template < + typename DerivedA, + typename DerivedC> + IGL_INLINE void unique( + const Eigen::DenseBase & A, + Eigen::PlainObjectBase & C); +} + +#ifndef IGL_STATIC_LIBRARY +# include "unique.cpp" +#endif + +#endif diff --git a/src/igl/unique_edge_map.cpp b/src/igl/unique_edge_map.cpp new file mode 100644 index 0000000000..a69be58a0e --- /dev/null +++ b/src/igl/unique_edge_map.cpp @@ -0,0 +1,69 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unique_edge_map.h" +#include "oriented_facets.h" +#include "unique_simplices.h" +#include +#include +template < + typename DerivedF, + typename DerivedE, + typename DeriveduE, + typename DerivedEMAP, + typename uE2EType> +IGL_INLINE void igl::unique_edge_map( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & uE, + Eigen::PlainObjectBase & EMAP, + std::vector > & uE2E) +{ + using namespace Eigen; + using namespace std; + // All occurrences of directed edges + oriented_facets(F,E); + const size_t ne = E.rows(); + // This is 2x faster to create than a map from pairs to lists of edges and 5x + // faster to access (actually access is probably assympotically faster O(1) + // vs. O(log m) + Matrix IA; + unique_simplices(E,uE,IA,EMAP); + uE2E.resize(uE.rows()); + // This does help a little + for_each(uE2E.begin(),uE2E.end(),[](vector & v){v.reserve(2);}); + assert((size_t)EMAP.size() == ne); + for(uE2EType e = 0;e<(uE2EType)ne;e++) + { + uE2E[EMAP(e)].push_back(e); + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::unique_edge_map, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, unsigned long>(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&); +// generated by autoexplicit.sh +template void igl::unique_edge_map, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&); +// generated by autoexplicit.sh +template void igl::unique_edge_map, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, unsigned long>(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&); +template void igl::unique_edge_map, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, long>(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&); +template void igl::unique_edge_map, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, long>(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&); +template void igl::unique_edge_map, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&); +template void igl::unique_edge_map, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&); +template void igl::unique_edge_map, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, long>(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&); +template void igl::unique_edge_map, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, int>(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&); +template void igl::unique_edge_map, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, unsigned long>(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, std::vector >, std::allocator > > >&); + +#ifdef WIN32 +template void igl::unique_edge_map, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, __int64>(class Eigen::MatrixBase > const &, class Eigen::PlainObjectBase > &, class Eigen::PlainObjectBase > &, class Eigen::PlainObjectBase > &, class std::vector >, class std::allocator > > > &); +template void igl::unique_edge_map,class Eigen::Matrix,class Eigen::Matrix,class Eigen::Matrix<__int64,-1,1,0,-1,1>,__int64>(class Eigen::MatrixBase > const &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &,class std::vector >,class std::allocator > > > &); +template void igl::unique_edge_map, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, unsigned __int64>(class Eigen::MatrixBase> const &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class std::vector>, class std::allocator>>> &); +template void igl::unique_edge_map, class Eigen::Matrix, class Eigen::Matrix, class Eigen::Matrix, unsigned __int64>(class Eigen::MatrixBase> const &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class Eigen::PlainObjectBase> &, class std::vector>, class std::allocator>>> &); +#endif + +#endif diff --git a/src/igl/unique_edge_map.h b/src/igl/unique_edge_map.h new file mode 100644 index 0000000000..4915db66f0 --- /dev/null +++ b/src/igl/unique_edge_map.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UNIQUE_EDGE_MAP_H +#define IGL_UNIQUE_EDGE_MAP_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Construct relationships between facet "half"-(or rather "viewed")-edges E + // to unique edges of the mesh seen as a graph. + // + // Inputs: + // F #F by 3 list of simplices + // Outputs: + // E #F*3 by 2 list of all of directed edges + // uE #uE by 2 list of unique undirected edges + // EMAP #F*3 list of indices into uE, mapping each directed edge to unique + // undirected edge + // uE2E #uE list of lists of indices into E of coexisting edges + template < + typename DerivedF, + typename DerivedE, + typename DeriveduE, + typename DerivedEMAP, + typename uE2EType> + IGL_INLINE void unique_edge_map( + const Eigen::MatrixBase & F, + Eigen::PlainObjectBase & E, + Eigen::PlainObjectBase & uE, + Eigen::PlainObjectBase & EMAP, + std::vector > & uE2E); + +} +#ifndef IGL_STATIC_LIBRARY +# include "unique_edge_map.cpp" +#endif + +#endif diff --git a/src/igl/unique_rows.cpp b/src/igl/unique_rows.cpp new file mode 100644 index 0000000000..1973a80c22 --- /dev/null +++ b/src/igl/unique_rows.cpp @@ -0,0 +1,111 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unique_rows.h" +#include "sortrows.h" + +#include +#include +#include + + +template +IGL_INLINE void igl::unique_rows( + const Eigen::DenseBase& A, + Eigen::PlainObjectBase& C, + Eigen::PlainObjectBase& IA, + Eigen::PlainObjectBase& IC) +{ + using namespace std; + using namespace Eigen; + VectorXi IM; + DerivedA sortA; + sortrows(A,true,sortA,IM); + + + const int num_rows = sortA.rows(); + const int num_cols = sortA.cols(); + vector vIA(num_rows); + for(int i=0;i, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_rows, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::unique_rows, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::unique_rows, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_rows, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_rows, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_rows, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_rows, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_rows, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::DenseBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +template void igl::unique_rows,Eigen::Matrix,Eigen::Matrix,Eigen::Matrix >(Eigen::DenseBase > const&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&,Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::unique_rows, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1> >(class Eigen::DenseBase > const &, class Eigen::PlainObjectBase > &, class Eigen::PlainObjectBase > &, class Eigen::PlainObjectBase > &); +template void igl::unique_rows,class Eigen::Matrix,class Eigen::Matrix<__int64,-1,1,0,-1,1>,class Eigen::Matrix<__int64,-1,1,0,-1,1> >(class Eigen::DenseBase > const &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &); +template void igl::unique_rows,class Eigen::Matrix,class Eigen::Matrix<__int64,-1,1,0,-1,1>,class Eigen::Matrix<__int64,-1,1,0,-1,1> >(class Eigen::DenseBase > const &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &); +template void igl::unique_rows,class Eigen::Matrix,class Eigen::Matrix<__int64,-1,1,0,-1,1>,class Eigen::Matrix<__int64,-1,1,0,-1,1> >(class Eigen::DenseBase > const &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &); +template void igl::unique_rows,class Eigen::Matrix,class Eigen::Matrix<__int64,-1,1,0,-1,1>,class Eigen::Matrix<__int64,-1,1,0,-1,1> >(class Eigen::DenseBase > const &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &,class Eigen::PlainObjectBase > &); +#endif +#endif diff --git a/src/igl/unique_rows.h b/src/igl/unique_rows.h new file mode 100644 index 0000000000..c612c8b1db --- /dev/null +++ b/src/igl/unique_rows.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2017 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UNIQUE_ROWS_H +#define IGL_UNIQUE_ROWS_H +#include "igl_inline.h" + +#include +#include +namespace igl +{ + // Act like matlab's [C,IA,IC] = unique(X,'rows') + // + // Templates: + // DerivedA derived scalar type, e.g. MatrixXi or MatrixXd + // DerivedIA derived integer type, e.g. MatrixXi + // DerivedIC derived integer type, e.g. MatrixXi + // Inputs: + // A m by n matrix whose entries are to unique'd according to rows + // Outputs: + // C #C vector of unique rows in A + // IA #C index vector so that C = A(IA,:); + // IC #A index vector so that A = C(IC,:); + template + IGL_INLINE void unique_rows( + const Eigen::DenseBase& A, + Eigen::PlainObjectBase& C, + Eigen::PlainObjectBase& IA, + Eigen::PlainObjectBase& IC); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "unique_rows.cpp" +#endif + +#endif diff --git a/src/igl/unique_simplices.cpp b/src/igl/unique_simplices.cpp new file mode 100644 index 0000000000..463459866d --- /dev/null +++ b/src/igl/unique_simplices.cpp @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unique_simplices.h" +#include "sort.h" +#include "unique_rows.h" +#include "parallel_for.h" + +template < + typename DerivedF, + typename DerivedFF, + typename DerivedIA, + typename DerivedIC> +IGL_INLINE void igl::unique_simplices( + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& FF, + Eigen::PlainObjectBase& IA, + Eigen::PlainObjectBase& IC) +{ + using namespace Eigen; + using namespace std; + // Sort each face + MatrixXi sortF, unusedI; + igl::sort(F,2,true,sortF,unusedI); + // Find unique faces + MatrixXi C; + igl::unique_rows(sortF,C,IA,IC); + FF.resize(IA.size(),F.cols()); + const size_t mff = FF.rows(); + parallel_for(mff,[&F,&IA,&FF](size_t & i){FF.row(i) = F.row(IA(i));},1000ul); +} + +template < + typename DerivedF, + typename DerivedFF> +IGL_INLINE void igl::unique_simplices( + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& FF) +{ + Eigen::VectorXi IA,IC; + return unique_simplices(F,FF,IA,IC); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::unique_simplices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_simplices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_simplices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_simplices, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::unique_simplices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_simplices, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::unique_simplices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_simplices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_simplices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::unique_simplices, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::unique_simplices, class Eigen::Matrix, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1>, class Eigen::Matrix<__int64, -1, 1, 0, -1, 1> >(class Eigen::MatrixBase > const &, class Eigen::PlainObjectBase > &, class Eigen::PlainObjectBase > &, class Eigen::PlainObjectBase > &); +#endif +#endif diff --git a/src/igl/unique_simplices.h b/src/igl/unique_simplices.h new file mode 100644 index 0000000000..a7c13849ef --- /dev/null +++ b/src/igl/unique_simplices.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UNIQUE_SIMPLICES_H +#define IGL_UNIQUE_SIMPLICES_H +#include "igl_inline.h" +#include +namespace igl +{ + // Find *combinatorially* unique simplices in F. **Order independent** + // + // Inputs: + // F #F by simplex-size list of simplices + // Outputs: + // FF #FF by simplex-size list of unique simplices in F + // IA #FF index vector so that FF == sort(F(IA,:),2); + // IC #F index vector so that sort(F,2) == FF(IC,:); + template < + typename DerivedF, + typename DerivedFF, + typename DerivedIA, + typename DerivedIC> + IGL_INLINE void unique_simplices( + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& FF, + Eigen::PlainObjectBase& IA, + Eigen::PlainObjectBase& IC); + template < + typename DerivedF, + typename DerivedFF> + IGL_INLINE void unique_simplices( + const Eigen::MatrixBase& F, + Eigen::PlainObjectBase& FF); +} + +#ifndef IGL_STATIC_LIBRARY +# include "unique_simplices.cpp" +#endif + +#endif diff --git a/src/igl/unproject.cpp b/src/igl/unproject.cpp new file mode 100644 index 0000000000..702d77feef --- /dev/null +++ b/src/igl/unproject.cpp @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unproject.h" + +#include +#include + +template < + typename Derivedwin, + typename Derivedmodel, + typename Derivedproj, + typename Derivedviewport, + typename Derivedscene> +IGL_INLINE void igl::unproject( + const Eigen::MatrixBase& win, + const Eigen::MatrixBase& model, + const Eigen::MatrixBase& proj, + const Eigen::MatrixBase& viewport, + Eigen::PlainObjectBase & scene) +{ + if(win.cols() != 3) + { + assert(win.rows() == 3); + // needless transposes + Eigen::Matrix sceneT; + unproject(win.transpose().eval(),model,proj,viewport,sceneT); + scene = sceneT.head(3); + return; + } + assert(win.cols() == 3); + const int n = win.rows(); + scene.resize(n,3); + for(int i = 0;i Inverse = + (proj.template cast() * model.template cast()).inverse(); + + Eigen::Matrix tmp; + tmp << win.row(i).head(3).transpose(), 1; + tmp(0) = (tmp(0) - viewport(0)) / viewport(2); + tmp(1) = (tmp(1) - viewport(1)) / viewport(3); + tmp = tmp.array() * 2.0f - 1.0f; + + Eigen::Matrix obj = Inverse * tmp; + obj /= obj(3); + + scene.row(i).head(3) = obj.head(3); + } +} + +template +IGL_INLINE Eigen::Matrix igl::unproject( + const Eigen::Matrix& win, + const Eigen::Matrix& model, + const Eigen::Matrix& proj, + const Eigen::Matrix& viewport) +{ + Eigen::Matrix scene; + unproject(win,model,proj,viewport,scene); + return scene; +} + +#ifdef IGL_STATIC_LIBRARY +template Eigen::Matrix igl::unproject(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); +template Eigen::Matrix igl::unproject(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); +template void igl::unproject, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template void igl::unproject, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/unproject.h b/src/igl/unproject.h new file mode 100644 index 0000000000..f34e87513e --- /dev/null +++ b/src/igl/unproject.h @@ -0,0 +1,47 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UNPROJECT_H +#define IGL_UNPROJECT_H +#include "igl_inline.h" +#include +namespace igl +{ + // Eigen reimplementation of gluUnproject + // + // Inputs: + // win #P by 3 or 3-vector (#P=1) of screen space x, y, and z coordinates + // model 4x4 model-view matrix + // proj 4x4 projection matrix + // viewport 4-long viewport vector + // Outputs: + // scene #P by 3 or 3-vector (#P=1) the unprojected x, y, and z coordinates + template < + typename Derivedwin, + typename Derivedmodel, + typename Derivedproj, + typename Derivedviewport, + typename Derivedscene> + IGL_INLINE void unproject( + const Eigen::MatrixBase& win, + const Eigen::MatrixBase& model, + const Eigen::MatrixBase& proj, + const Eigen::MatrixBase& viewport, + Eigen::PlainObjectBase & scene); + template + IGL_INLINE Eigen::Matrix unproject( + const Eigen::Matrix& win, + const Eigen::Matrix& model, + const Eigen::Matrix& proj, + const Eigen::Matrix& viewport); +} + +#ifndef IGL_STATIC_LIBRARY +# include "unproject.cpp" +#endif + +#endif diff --git a/src/igl/unproject_in_mesh.cpp b/src/igl/unproject_in_mesh.cpp new file mode 100644 index 0000000000..6238aaf2a3 --- /dev/null +++ b/src/igl/unproject_in_mesh.cpp @@ -0,0 +1,97 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unproject_in_mesh.h" +#include "unproject_ray.h" +#include "ray_mesh_intersect.h" + +template < typename Derivedobj> + IGL_INLINE int igl::unproject_in_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const std::function< + void( + const Eigen::Vector3f&, + const Eigen::Vector3f&, + std::vector &) + > & shoot_ray, + Eigen::PlainObjectBase & obj, + std::vector & hits) +{ + using namespace std; + using namespace Eigen; + Vector3f s,dir; + unproject_ray(pos,model,proj,viewport,s,dir); + shoot_ray(s,dir,hits); + switch(hits.size()) + { + case 0: + break; + case 1: + { + obj = (s + dir*hits[0].t).cast(); + break; + } + case 2: + default: + { + obj = 0.5*((s + dir*hits[0].t) + (s + dir*hits[1].t)).cast(); + break; + } + } + return hits.size(); +} + +extern "C" +{ +#include "raytri.c" +} + +template < typename DerivedV, typename DerivedF, typename Derivedobj> + IGL_INLINE int igl::unproject_in_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & obj, + std::vector & hits) +{ + using namespace std; + using namespace Eigen; + const auto & shoot_ray = [&V,&F]( + const Eigen::Vector3f& s, + const Eigen::Vector3f& dir, + std::vector & hits) + { + ray_mesh_intersect(s,dir,V,F,hits); + }; + return unproject_in_mesh(pos,model,proj,viewport,shoot_ray,obj,hits); +} + +template < typename DerivedV, typename DerivedF, typename Derivedobj> + IGL_INLINE int igl::unproject_in_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & obj) +{ + std::vector hits; + return unproject_in_mesh(pos,model,proj,viewport,V,F,obj,hits); +} +#ifdef IGL_STATIC_LIBRARY +template int igl::unproject_in_mesh >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, std::function const&, Eigen::Matrix const&, std::vector >&)> const&, Eigen::PlainObjectBase >&, std::vector >&); +template int igl::unproject_in_mesh >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, std::function const&, Eigen::Matrix const&, std::vector >&)> const&, Eigen::PlainObjectBase >&, std::vector >&); +template int igl::unproject_in_mesh >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, std::function const&, Eigen::Matrix const&, std::vector >&)> const&, Eigen::PlainObjectBase >&, std::vector >&); +template int igl::unproject_in_mesh, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/unproject_in_mesh.h b/src/igl/unproject_in_mesh.h new file mode 100644 index 0000000000..edb1a0e65d --- /dev/null +++ b/src/igl/unproject_in_mesh.h @@ -0,0 +1,88 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UNPROJECT_IN_MESH +#define IGL_UNPROJECT_IN_MESH +#include "igl_inline.h" +#include + +#include +#include "Hit.h" + +namespace igl +{ + // Unproject a screen location (using current opengl viewport, projection, and + // model view) to a 3D position _inside_ a given mesh. If the ray through the + // given screen location (x,y) _hits_ the mesh more than twice then the 3D + // midpoint between the first two hits is return. If it hits once, then that + // point is return. If it does not hit the mesh then obj is not set. + // + // Inputs: + // pos screen space coordinates + // model model matrix + // proj projection matrix + // viewport vieweport vector + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of mesh triangle indices into V + // Outputs: + // obj 3d unprojected mouse point in mesh + // hits vector of hits + // Returns number of hits + // + template < typename DerivedV, typename DerivedF, typename Derivedobj> + IGL_INLINE int unproject_in_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & obj, + std::vector & hits); + // + // Inputs: + // pos screen space coordinates + // model model matrix + // proj projection matrix + // viewport vieweport vector + // shoot_ray function handle that outputs first hit of a given ray + // against a mesh (embedded in function handles as captured + // variable/data) + // Outputs: + // obj 3d unprojected mouse point in mesh + // hits vector of hits + // Returns number of hits + // + template < typename Derivedobj> + IGL_INLINE int unproject_in_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const std::function< + void( + const Eigen::Vector3f&, + const Eigen::Vector3f&, + std::vector &) + > & shoot_ray, + Eigen::PlainObjectBase & obj, + std::vector & hits); + template < typename DerivedV, typename DerivedF, typename Derivedobj> + IGL_INLINE int unproject_in_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + Eigen::PlainObjectBase & obj); +} +#ifndef IGL_STATIC_LIBRARY +# include "unproject_in_mesh.cpp" +#endif +#endif + diff --git a/src/igl/unproject_onto_mesh.cpp b/src/igl/unproject_onto_mesh.cpp new file mode 100644 index 0000000000..897d206c11 --- /dev/null +++ b/src/igl/unproject_onto_mesh.cpp @@ -0,0 +1,78 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unproject_onto_mesh.h" +#include "unproject.h" +#include "unproject_ray.h" +#include "ray_mesh_intersect.h" +#include + +template < typename DerivedV, typename DerivedF, typename Derivedbc> +IGL_INLINE bool igl::unproject_onto_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + int & fid, + Eigen::PlainObjectBase & bc) +{ + using namespace std; + using namespace Eigen; + const auto & shoot_ray = [&V,&F]( + const Eigen::Vector3f& s, + const Eigen::Vector3f& dir, + igl::Hit & hit)->bool + { + std::vector hits; + if(!ray_mesh_intersect(s,dir,V,F,hits)) + { + return false; + } + hit = hits[0]; + return true; + }; + return unproject_onto_mesh(pos,model,proj,viewport,shoot_ray,fid,bc); +} + +template +IGL_INLINE bool igl::unproject_onto_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const std::function< + bool( + const Eigen::Vector3f&, + const Eigen::Vector3f&, + igl::Hit &) + > & shoot_ray, + int & fid, + Eigen::PlainObjectBase & bc) +{ + using namespace std; + using namespace Eigen; + Vector3f s,dir; + unproject_ray(pos,model,proj,viewport,s,dir); + Hit hit; + if(!shoot_ray(s,dir,hit)) + { + return false; + } + bc.resize(3); + bc << 1.0-hit.u-hit.v, hit.u, hit.v; + fid = hit.id; + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::unproject_onto_mesh, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, Eigen::PlainObjectBase >&); +template bool igl::unproject_onto_mesh, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int&, Eigen::PlainObjectBase >&); +#endif + diff --git a/src/igl/unproject_onto_mesh.h b/src/igl/unproject_onto_mesh.h new file mode 100644 index 0000000000..57252f99b6 --- /dev/null +++ b/src/igl/unproject_onto_mesh.h @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UNPROJECT_ONTO_MESH +#define IGL_UNPROJECT_ONTO_MESH +#include "igl_inline.h" +#include "Hit.h" +#include +#include + +namespace igl +{ + // Unproject a screen location (using current opengl viewport, projection, and + // model view) to a 3D position _onto_ a given mesh, if the ray through the + // given screen location (x,y) _hits_ the mesh. + // + // Inputs: + // pos screen space coordinates + // model model matrix + // proj projection matrix + // viewport vieweport vector + // V #V by 3 list of mesh vertex positions + // F #F by 3 list of mesh triangle indices into V + // Outputs: + // fid id of the first face hit + // bc barycentric coordinates of hit + // Returns true if there's a hit + template < typename DerivedV, typename DerivedF, typename Derivedbc> + IGL_INLINE bool unproject_onto_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + int & fid, + Eigen::PlainObjectBase & bc); + // + // Inputs: + // pos screen space coordinates + // model model matrix + // proj projection matrix + // viewport vieweport vector + // shoot_ray function handle that outputs hits of a given ray against a + // mesh (embedded in function handles as captured variable/data) + // Outputs: + // fid id of the first face hit + // bc barycentric coordinates of hit + // Returns true if there's a hit + template + IGL_INLINE bool unproject_onto_mesh( + const Eigen::Vector2f& pos, + const Eigen::Matrix4f& model, + const Eigen::Matrix4f& proj, + const Eigen::Vector4f& viewport, + const std::function< + bool( + const Eigen::Vector3f&, + const Eigen::Vector3f&, + igl::Hit &) + > & shoot_ray, + int & fid, + Eigen::PlainObjectBase & bc); +} +#ifndef IGL_STATIC_LIBRARY +# include "unproject_onto_mesh.cpp" +#endif +#endif + + diff --git a/src/igl/unproject_ray.cpp b/src/igl/unproject_ray.cpp new file mode 100644 index 0000000000..f6eb7ebdde --- /dev/null +++ b/src/igl/unproject_ray.cpp @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unproject_ray.h" +#include "unproject.h" + +template < + typename Derivedpos, + typename Derivedmodel, + typename Derivedproj, + typename Derivedviewport, + typename Deriveds, + typename Deriveddir> +IGL_INLINE void igl::unproject_ray( + const Eigen::PlainObjectBase & pos, + const Eigen::PlainObjectBase & model, + const Eigen::PlainObjectBase & proj, + const Eigen::PlainObjectBase & viewport, + Eigen::PlainObjectBase & s, + Eigen::PlainObjectBase & dir) +{ + using namespace std; + using namespace Eigen; + // Source and direction on screen + typedef Eigen::Matrix Vec3; + Vec3 win_s(pos(0),pos(1),0); + Vec3 win_d(pos(0),pos(1),1); + // Source, destination and direction in world + Vec3 d; + igl::unproject(win_s,model,proj,viewport,s); + igl::unproject(win_d,model,proj,viewport,d); + dir = d-s; +} + +#ifdef IGL_STATIC_LIBRARY +template void igl::unproject_ray, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/unproject_ray.h b/src/igl/unproject_ray.h new file mode 100644 index 0000000000..4ef66f266b --- /dev/null +++ b/src/igl/unproject_ray.h @@ -0,0 +1,44 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UNPROJECT_RAY_H +#define IGL_UNPROJECT_RAY_H +#include "igl_inline.h" +#include +namespace igl +{ + // Construct a ray (source point + direction vector) given a screen space + // positions (e.g. mouse) and a model-view projection constellation. + // + // Inputs: + // pos 2d screen-space position (x,y) + // model 4x4 model-view matrix + // proj 4x4 projection matrix + // viewport 4-long viewport vector + // Outputs: + // s source of ray (pos unprojected with z=0) + /// dir direction of ray (d - s) where d is pos unprojected with z=1 + // + template < + typename Derivedpos, + typename Derivedmodel, + typename Derivedproj, + typename Derivedviewport, + typename Deriveds, + typename Deriveddir> + IGL_INLINE void unproject_ray( + const Eigen::PlainObjectBase & pos, + const Eigen::PlainObjectBase & model, + const Eigen::PlainObjectBase & proj, + const Eigen::PlainObjectBase & viewport, + Eigen::PlainObjectBase & s, + Eigen::PlainObjectBase & dir); +} +#ifndef IGL_STATIC_LIBRARY +# include "unproject_ray.cpp" +#endif +#endif diff --git a/src/igl/unzip_corners.cpp b/src/igl/unzip_corners.cpp new file mode 100644 index 0000000000..0a2ed004c8 --- /dev/null +++ b/src/igl/unzip_corners.cpp @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "unzip_corners.h" + +#include "unique_rows.h" +#include "slice.h" + +template < typename DerivedA, typename DerivedU, typename DerivedG, typename DerivedJ > +IGL_INLINE void igl::unzip_corners( + const std::vector > & A, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & J) +{ + if(A.size() == 0) + { + U.resize(0,0); + G.resize(0,3); + J.resize(0,0); + return; + } + const size_t num_a = A.size(); + const typename DerivedA::Index m = A[0].get().rows(); + DerivedU C(m*3,num_a); + for(int a = 0;a +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UNZIP_CORNERS_H +#define IGL_UNZIP_CORNERS_H +#include "igl_inline.h" +#include +#include +#include + +namespace igl +{ + // UNZIP_CORNERS Given a triangle mesh where corners of each triangle index + // different matrices of attributes (e.g. read from an OBJ file), unzip the + // corners into unique efficiently: attributes become properly vertex valued + // (usually creating greater than #V but less than #F*3 vertices). + // + // To pass a list of attributes this function takes an std::vector of + // std::reference_wrapper of an Eigen::... type. This allows you to use list + // initializers **without** incurring a copy, but means you'll need to + // provide the derived type of A as an explicit template parameter: + // + // unzip_corners({F,FTC,FN},U,G,J); + // + // Inputs: + // A #A list of #F by 3 attribute indices, typically {F,FTC,FN} + // Outputs: + // U #U by #A list of indices into each attribute for each unique mesh + // vertex: U(v,a) is the attribute index of vertex v in attribute a. + // G #F by 3 list of triangle indices into U + // Example: + // [V,F,TC,FTC] = readOBJ('~/Downloads/kiwis/kiwi.obj'); + // [U,G] = unzip_corners(cat(3,F,FTC)); + // % display mesh + // tsurf(G,V(U(:,1),:)); + // % display texture coordinates + // tsurf(G,TC(U(:,2),:)); + // + template < typename DerivedA, typename DerivedU, typename DerivedG, typename DerivedJ> + IGL_INLINE void unzip_corners( + const std::vector > & A, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & G, + Eigen::PlainObjectBase & J); +} + +#ifndef IGL_STATIC_LIBRARY +# include "unzip_corners.cpp" +#endif +#endif diff --git a/src/igl/upsample.cpp b/src/igl/upsample.cpp new file mode 100644 index 0000000000..8b5e3da245 --- /dev/null +++ b/src/igl/upsample.cpp @@ -0,0 +1,145 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "upsample.h" + +#include "triangle_triangle_adjacency.h" + + +template < + typename DerivedF, + typename SType, + typename DerivedNF> +IGL_INLINE void igl::upsample( + const int n_verts, + const Eigen::PlainObjectBase& F, + Eigen::SparseMatrix& S, + Eigen::PlainObjectBase& NF) +{ + using namespace std; + using namespace Eigen; + + typedef Eigen::Triplet Triplet_t; + + Eigen::Matrix< typename DerivedF::Scalar,Eigen::Dynamic,Eigen::Dynamic> + FF,FFi; + triangle_triangle_adjacency(F,FF,FFi); + + // TODO: Cache optimization missing from here, it is a mess + + // Compute the number and positions of the vertices to insert (on edges) + Eigen::MatrixXi NI = Eigen::MatrixXi::Constant(FF.rows(),FF.cols(),-1); + Eigen::MatrixXi NIdoubles = Eigen::MatrixXi::Zero(FF.rows(), FF.cols()); + int counter = 0; + + for(int i=0;i tripletList; + + // Fill the odd vertices position + for (int i=0; i +IGL_INLINE void igl::upsample( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& NV, + Eigen::PlainObjectBase& NF, + const int number_of_subdivs) +{ + NV = V; + NF = F; + for(int i=0; iS; + upsample(NV.rows(), tempF, S, NF); + // This .eval is super important + NV = (S*NV).eval(); + } +} + +template < + typename MatV, + typename MatF> +IGL_INLINE void igl::upsample( + MatV& V, + MatF& F, + const int number_of_subdivs) +{ + const MatV V_copy = V; + const MatF F_copy = F; + return upsample(V_copy,F_copy,V,F,number_of_subdivs); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::upsample, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, int); +template void igl::upsample, double, Eigen::Matrix >(int, Eigen::PlainObjectBase > const&, Eigen::SparseMatrix&, Eigen::PlainObjectBase >&); +template void igl::upsample, Eigen::Matrix >(Eigen::Matrix&, Eigen::Matrix&, int); +#endif diff --git a/src/igl/upsample.h b/src/igl/upsample.h new file mode 100644 index 0000000000..51608aef60 --- /dev/null +++ b/src/igl/upsample.h @@ -0,0 +1,82 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_UPSAMPLE_H +#define IGL_UPSAMPLE_H +#include "igl_inline.h" + +#include +#include + +// History: +// changed templates from generic matrices to PlainObjectBase Alec May 7, 2011 +namespace igl +{ + // Subdivide without moving vertices: Given the triangle mesh [V, F], + // where n_verts = V.rows(), computes newV and a sparse matrix S s.t. + // [newV, newF] is the subdivided mesh where newV = S*V. + // + // Inputs: + // n_verts an integer (number of mesh vertices) + // F an m by 3 matrix of integers of triangle faces + // Outputs: + // S a sparse matrix (will become the subdivision matrix) + // newF a matrix containing the new faces + template < + typename DerivedF, + typename SType, + typename DerivedNF> + IGL_INLINE void upsample( + const int n_verts, + const Eigen::PlainObjectBase& F, + Eigen::SparseMatrix& S, + Eigen::PlainObjectBase& NF); + // Subdivide a mesh without moving vertices: loop subdivision but odd + // vertices stay put and even vertices are just edge midpoints + // + // Templates: + // MatV matrix for vertex positions, e.g. MatrixXd + // MatF matrix for vertex positions, e.g. MatrixXi + // Inputs: + // V #V by dim mesh vertices + // F #F by 3 mesh triangles + // Outputs: + // NV new vertex positions, V is guaranteed to be at top + // NF new list of face indices + // + // NOTE: V should not be the same as NV, + // NOTE: F should not be the same as NF, use other proto + // + // Known issues: + // - assumes (V,F) is edge-manifold. + template < + typename DerivedV, + typename DerivedF, + typename DerivedNV, + typename DerivedNF> + IGL_INLINE void upsample( + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + Eigen::PlainObjectBase& NV, + Eigen::PlainObjectBase& NF, + const int number_of_subdivs = 1); + + // Virtually in place wrapper + template < + typename MatV, + typename MatF> + IGL_INLINE void upsample( + MatV& V, + MatF& F, + const int number_of_subdivs = 1); +} + +#ifndef IGL_STATIC_LIBRARY +# include "upsample.cpp" +#endif + +#endif diff --git a/src/igl/vector_area_matrix.cpp b/src/igl/vector_area_matrix.cpp new file mode 100644 index 0000000000..6464c14a7a --- /dev/null +++ b/src/igl/vector_area_matrix.cpp @@ -0,0 +1,54 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "vector_area_matrix.h" +#include + +// Bug in unsupported/Eigen/SparseExtra needs iostream first +#include +#include + +//#include +#include + +template +IGL_INLINE void igl::vector_area_matrix( + const Eigen::PlainObjectBase & F, + Eigen::SparseMatrix& A) +{ + using namespace Eigen; + using namespace std; + + // number of vertices + const int n = F.maxCoeff()+1; + + MatrixXi E; + boundary_facets(F,E); + + //Prepare a vector of triplets to set the matrix + vector > tripletList; + tripletList.reserve(4*E.rows()); + + for(int k = 0; k < E.rows(); k++) + { + int i = E(k,0); + int j = E(k,1); + tripletList.push_back(Triplet(i+n, j, -0.25)); + tripletList.push_back(Triplet(j, i+n, -0.25)); + tripletList.push_back(Triplet(i, j+n, 0.25)); + tripletList.push_back(Triplet(j+n, i, 0.25)); + } + + //Set A from triplets (Eigen will sum triplets with same coordinates) + A.resize(n * 2, n * 2); + A.setFromTriplets(tripletList.begin(), tripletList.end()); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::vector_area_matrix, double>(Eigen::PlainObjectBase > const&, Eigen::SparseMatrix&); +#endif diff --git a/src/igl/vector_area_matrix.h b/src/igl/vector_area_matrix.h new file mode 100644 index 0000000000..6d385329cc --- /dev/null +++ b/src/igl/vector_area_matrix.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_VECTOR_AREA_MATRIX_H +#define IGL_VECTOR_AREA_MATRIX_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // Constructs the symmetric area matrix A, s.t. [V.col(0)' V.col(1)'] * A * + // [V.col(0); V.col(1)] is the **vector area** of the mesh (V,F). + // + // Templates: + // DerivedV derived type of eigen matrix for V (e.g. derived from + // MatrixXd) + // DerivedF derived type of eigen matrix for F (e.g. derived from + // MatrixXi) + // Scalar scalar type for eigen sparse matrix (e.g. double) + // Inputs: + // F #F by 3 list of mesh faces (must be triangles) + // Outputs: + // A #Vx2 by #Vx2 area matrix + // + template + IGL_INLINE void vector_area_matrix( + const Eigen::PlainObjectBase & F, + Eigen::SparseMatrix& A); +} + +#ifndef IGL_STATIC_LIBRARY +# include "vector_area_matrix.cpp" +#endif + +#endif diff --git a/src/igl/verbose.h b/src/igl/verbose.h new file mode 100644 index 0000000000..e4aa869580 --- /dev/null +++ b/src/igl/verbose.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_VERBOSE_H +#define IGL_VERBOSE_H + +// This function is only useful as a header-only inlined function + +namespace igl +{ + // Provide a wrapper for printf, called verbose that functions exactly like + // printf if VERBOSE is defined and does exactly nothing if VERBOSE is + // undefined + inline int verbose(const char * msg,...); +} + + + +#include +#ifdef VERBOSE +# include +#endif + +#include +// http://channel9.msdn.com/forums/techoff/254707-wrapping-printf-in-c/ +#ifdef VERBOSE +inline int igl::verbose(const char * msg,...) +{ + va_list argList; + va_start(argList, msg); + int count = vprintf(msg, argList); + va_end(argList); + return count; +} +#else +inline int igl::verbose(const char * /*msg*/,...) +{ + return 0; +} +#endif + +#endif diff --git a/src/igl/vertex_triangle_adjacency.cpp b/src/igl/vertex_triangle_adjacency.cpp new file mode 100644 index 0000000000..352d06244f --- /dev/null +++ b/src/igl/vertex_triangle_adjacency.cpp @@ -0,0 +1,102 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "vertex_triangle_adjacency.h" +#include "cumsum.h" + +template +IGL_INLINE void igl::vertex_triangle_adjacency( + const typename DerivedF::Scalar n, + const Eigen::MatrixBase& F, + std::vector >& VF, + std::vector >& VFi) +{ + VF.clear(); + VFi.clear(); + + VF.resize(n); + VFi.resize(n); + + typedef typename DerivedF::Index Index; + for(Index fi=0; fi +IGL_INLINE void igl::vertex_triangle_adjacency( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + std::vector >& VF, + std::vector >& VFi) +{ + return vertex_triangle_adjacency(V.rows(),F,VF,VFi); +} + +template < + typename DerivedF, + typename DerivedVF, + typename DerivedNI> +IGL_INLINE void igl::vertex_triangle_adjacency( + const Eigen::MatrixBase & F, + const int n, + Eigen::PlainObjectBase & VF, + Eigen::PlainObjectBase & NI) +{ + typedef Eigen::Matrix VectorXI; + // vfd #V list so that vfd(i) contains the vertex-face degree (number of + // faces incident on vertex i) + VectorXI vfd = VectorXI::Zero(n); + for (int i = 0; i < F.rows(); i++) + { + for (int j = 0; j < 3; j++) + { + vfd[F(i,j)]++; + } + } + igl::cumsum(vfd,1,NI); + // Prepend a zero + NI = (DerivedNI(n+1)<<0,NI).finished(); + // vfd now acts as a counter + vfd = NI; + + VF.derived()= Eigen::VectorXi(3*F.rows()); + for (int i = 0; i < F.rows(); i++) + { + for (int j = 0; j < 3; j++) + { + VF[vfd[F(i,j)]] = i; + vfd[F(i,j)]++; + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::vertex_triangle_adjacency, unsigned long, unsigned long>(Eigen::Matrix::Scalar, Eigen::MatrixBase > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +// generated by autoexplicit.sh +template void igl::vertex_triangle_adjacency, Eigen::Matrix, int>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::vertex_triangle_adjacency, Eigen::Matrix, unsigned int>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::vertex_triangle_adjacency, Eigen::Matrix, int>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::vertex_triangle_adjacency, long, long>(Eigen::Matrix::Scalar, Eigen::MatrixBase > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::vertex_triangle_adjacency, long, long>(Eigen::Matrix::Scalar, Eigen::MatrixBase > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::vertex_triangle_adjacency, unsigned long, unsigned long>(Eigen::Matrix::Scalar, Eigen::MatrixBase > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::vertex_triangle_adjacency, Eigen::Matrix, int>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >, std::allocator > > >&, std::vector >, std::allocator > > >&); +template void igl::vertex_triangle_adjacency, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::vertex_triangle_adjacency, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#ifdef WIN32 +template void igl::vertex_triangle_adjacency, unsigned __int64, unsigned __int64>(int, class Eigen::MatrixBase> const &, class std::vector>, class std::allocator>>> &, class std::vector>, class std::allocator>>> &); +template void igl::vertex_triangle_adjacency, unsigned __int64, unsigned __int64>(int, class Eigen::MatrixBase> const &, class std::vector>, class std::allocator>>> &, class std::vector>, class std::allocator>>> &); +#endif +#endif diff --git a/src/igl/vertex_triangle_adjacency.h b/src/igl/vertex_triangle_adjacency.h new file mode 100644 index 0000000000..ce67b1f3dd --- /dev/null +++ b/src/igl/vertex_triangle_adjacency.h @@ -0,0 +1,71 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Daniele Panozzo +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_VERTEX_TRIANGLE_ADJACENCY_H +#define IGL_VERTEX_TRIANGLE_ADJACENCY_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // vertex_face_adjacency constructs the vertex-face topology of a given mesh (V,F) + // + // Inputs: + // //V #V by 3 list of vertex coordinates + // n number of vertices #V (e.g. `F.maxCoeff()+1` or `V.rows()`) + // F #F by dim list of mesh faces (must be triangles) + // Outputs: + // VF #V list of lists of incident faces (adjacency list) + // VI #V list of lists of index of incidence within incident faces listed + // in VF + // + // See also: edges, cotmatrix, diag, vv + // + // Known bugs: this should not take V as an input parameter. + // Known bugs/features: if a facet is combinatorially degenerate then faces + // will appear multiple times in VF and correspondingly in VFI (j appears + // twice in F.row(i) then i will appear twice in VF[j]) + template + IGL_INLINE void vertex_triangle_adjacency( + const typename DerivedF::Scalar n, + const Eigen::MatrixBase& F, + std::vector >& VF, + std::vector >& VFi); + template + IGL_INLINE void vertex_triangle_adjacency( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + std::vector >& VF, + std::vector >& VFi); + // Inputs: + // F #F by 3 list of triangle indices into some vertex list V + // n number of vertices, #V (e.g., F.maxCoeff()+1) + // Outputs: + // VF 3*#F list List of faces indice on each vertex, so that VF(NI(i)+j) = + // f, means that face f is the jth face (in no particular order) incident + // on vertex i. + // NI #V+1 list cumulative sum of vertex-triangle degrees with a + // preceeding zero. "How many faces" have been seen before visiting this + // vertex and its incident faces. + template < + typename DerivedF, + typename DerivedVF, + typename DerivedNI> + IGL_INLINE void vertex_triangle_adjacency( + const Eigen::MatrixBase & F, + const int n, + Eigen::PlainObjectBase & VF, + Eigen::PlainObjectBase & NI); +} + +#ifndef IGL_STATIC_LIBRARY +# include "vertex_triangle_adjacency.cpp" +#endif + +#endif diff --git a/src/igl/volume.cpp b/src/igl/volume.cpp new file mode 100644 index 0000000000..68568cf453 --- /dev/null +++ b/src/igl/volume.cpp @@ -0,0 +1,120 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "volume.h" +#include "cross.h" +#include +template < + typename DerivedV, + typename DerivedT, + typename Derivedvol> +IGL_INLINE void igl::volume( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& T, + Eigen::PlainObjectBase& vol) +{ + using namespace Eigen; + const int m = T.rows(); + vol.resize(m,1); + for(int t = 0;t RowVector3S; + const RowVector3S & a = V.row(T(t,0)); + const RowVector3S & b = V.row(T(t,1)); + const RowVector3S & c = V.row(T(t,2)); + const RowVector3S & d = V.row(T(t,3)); + vol(t) = -(a-d).dot((b-d).cross(c-d))/6.; + } +} + +template < + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedD, + typename Derivedvol> +IGL_INLINE void igl::volume( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C, + const Eigen::MatrixBase & D, + Eigen::PlainObjectBase & vol) +{ + const auto & AmD = A-D; + const auto & BmD = B-D; + const auto & CmD = C-D; + DerivedA BmDxCmD; + cross(BmD.eval(),CmD.eval(),BmDxCmD); + const auto & AmDdx = (AmD.array() * BmDxCmD.array()).rowwise().sum(); + vol = -AmDdx/6.; +} + +template < + typename VecA, + typename VecB, + typename VecC, + typename VecD> +IGL_INLINE typename VecA::Scalar igl::volume_single( + const VecA & a, + const VecB & b, + const VecC & c, + const VecD & d) +{ + return -(a-d).dot((b-d).cross(c-d))/6.; +} + + +template < + typename DerivedL, + typename Derivedvol> +IGL_INLINE void igl::volume( + const Eigen::MatrixBase& L, + Eigen::PlainObjectBase& vol) +{ + using namespace Eigen; + const int m = L.rows(); + typedef typename Derivedvol::Scalar ScalarS; + vol.resize(m,1); + for(int t = 0;t, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::volume, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::volume, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template void igl::volume, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +template Eigen::Matrix::Scalar igl::volume_single, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); +template void igl::volume,Eigen::Matrix,Eigen::Matrix >(Eigen::MatrixBase > const &,Eigen::MatrixBase > const &,Eigen::PlainObjectBase > &); +#endif diff --git a/src/igl/volume.h b/src/igl/volume.h new file mode 100644 index 0000000000..2dee7cc950 --- /dev/null +++ b/src/igl/volume.h @@ -0,0 +1,74 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_VOLUME_H +#define IGL_VOLUME_H +#include "igl_inline.h" +#include +namespace igl +{ + // VOLUME Compute volume for all tets of a given tet mesh + // (V,T) + // + // vol = volume(V,T) + // + // Inputs: + // V #V by dim list of vertex positions + // T #V by 4 list of tet indices + // Outputs: + // vol #T list of dihedral angles (in radians) + // + template < + typename DerivedV, + typename DerivedT, + typename Derivedvol> + IGL_INLINE void volume( + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& T, + Eigen::PlainObjectBase& vol); + template < + typename DerivedA, + typename DerivedB, + typename DerivedC, + typename DerivedD, + typename Derivedvol> + IGL_INLINE void volume( + const Eigen::MatrixBase & A, + const Eigen::MatrixBase & B, + const Eigen::MatrixBase & C, + const Eigen::MatrixBase & D, + Eigen::PlainObjectBase & vol); + // Single tet + template < + typename VecA, + typename VecB, + typename VecC, + typename VecD> + IGL_INLINE typename VecA::Scalar volume_single( + const VecA & a, + const VecB & b, + const VecC & c, + const VecD & d); + // Intrinsic version: + // + // Inputs: + // L #V by 6 list of edge lengths (see edge_lengths) + template < + typename DerivedL, + typename Derivedvol> + IGL_INLINE void volume( + const Eigen::MatrixBase& L, + Eigen::PlainObjectBase& vol); +} + +#ifndef IGL_STATIC_LIBRARY +# include "volume.cpp" +#endif + +#endif + + diff --git a/src/igl/voxel_grid.cpp b/src/igl/voxel_grid.cpp new file mode 100644 index 0000000000..388e58af48 --- /dev/null +++ b/src/igl/voxel_grid.cpp @@ -0,0 +1,73 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "voxel_grid.h" +#include "grid.h" + +template < + typename Scalar, + typename DerivedGV, + typename Derivedside> +IGL_INLINE void igl::voxel_grid( + const Eigen::AlignedBox & box, + const int in_s, + const int pad_count, + Eigen::PlainObjectBase & GV, + Eigen::PlainObjectBase & side) +{ + using namespace Eigen; + using namespace std; + typename DerivedGV::Index si = -1; + box.diagonal().maxCoeff(&si); + //DerivedGV::Index si = 0; + //assert(si>=0); + const Scalar s_len = box.diagonal()(si); + assert(in_s>(pad_count*2+1) && "s should be > 2*pad_count+1"); + const Scalar s = in_s - 2*pad_count; + side(si) = s; + for(int i = 0;i<3;i++) + { + if(i!=si) + { + side(i) = std::ceil(s * (box.max()(i)-box.min()(i))/s_len); + } + } + side.array() += 2*pad_count; + grid(side,GV); + // A * p/s + B = min + // A * (1-p/s) + B = max + // B = min - A * p/s + // A * (1-p/s) + min - A * p/s = max + // A * (1-p/s) - A * p/s = max-min + // A * (1-2p/s) = max-min + // A = (max-min)/(1-2p/s) + const Array ps= + (Scalar)(pad_count)/(side.transpose().template cast().array()-1.); + const Array A = box.diagonal().array()/(1.0-2.*ps); + //// This would result in an "anamorphic", but perfectly fit grid: + //const Array B = box.min().array() - A.array()*ps; + //GV.array().rowwise() *= A.transpose(); + //GV.array().rowwise() += B.transpose(); + // Instead scale by largest factor and move to match center + typename Array::Index ai = -1; + Scalar a = A.maxCoeff(&ai); + const Array ratio = + a*(side.template cast().array()-1.0)/(Scalar)(side(ai)-1.0); + GV.array().rowwise() *= ratio; + const Eigen::Matrix offset = (box.center().transpose()-GV.colwise().mean()).eval(); + GV.rowwise() += offset; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template void igl::voxel_grid, Eigen::Matrix >(Eigen::AlignedBox const&, int, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::voxel_grid, Eigen::Matrix >(Eigen::AlignedBox const&, int, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::voxel_grid, Eigen::Matrix >(Eigen::AlignedBox const&, int, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::voxel_grid, Eigen::Matrix >(Eigen::AlignedBox const&, int, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +template void igl::voxel_grid, Eigen::Matrix >(Eigen::AlignedBox const&, int, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); +#endif diff --git a/src/igl/voxel_grid.h b/src/igl/voxel_grid.h new file mode 100644 index 0000000000..4f8ac1bcb1 --- /dev/null +++ b/src/igl/voxel_grid.h @@ -0,0 +1,39 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_VOXEL_GRID_H +#define IGL_VOXEL_GRID_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Construct the cell center positions of a regular voxel grid (lattice) made + // of perfectly square voxels. + // + // Inputs: + // box bounding box to enclose by grid + // s number of cell centers on largest side (including 2*pad_count) + // pad_count number of cells beyond box + // Outputs: + // GV side(0)*side(1)*side(2) by 3 list of cell center positions + // side 3-long list of dimension of voxel grid + template < + typename Scalar, + typename DerivedGV, + typename Derivedside> + IGL_INLINE void voxel_grid( + const Eigen::AlignedBox & box, + const int s, + const int pad_count, + Eigen::PlainObjectBase & GV, + Eigen::PlainObjectBase & side); +} +#ifndef IGL_STATIC_LIBRARY +# include "voxel_grid.cpp" +#endif +#endif diff --git a/src/igl/winding_number.cpp b/src/igl/winding_number.cpp new file mode 100644 index 0000000000..d6af18d98e --- /dev/null +++ b/src/igl/winding_number.cpp @@ -0,0 +1,117 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "winding_number.h" +#include "WindingNumberAABB.h" +#include "signed_angle.h" +#include "parallel_for.h" +#include "solid_angle.h" +#include "PI.h" + +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedO, + typename DerivedW> +IGL_INLINE void igl::winding_number( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & O, + Eigen::PlainObjectBase & W) +{ + using namespace Eigen; + // make room for output + W.resize(O.rows(),1); + switch(F.cols()) + { + case 2: + { + igl::parallel_for(O.rows(),[&](const int o) + { + W(o) = winding_number(V,F,O.row(o)); + },10000); + return; + } + case 3: + { + WindingNumberAABB< + Eigen::Matrix, + DerivedV, + DerivedF> + hier(V,F); + hier.grow(); + // loop over origins + igl::parallel_for(O.rows(),[&](const int o) + { + W(o) = hier.winding_number(O.row(o)); + },10000); + break; + } + default: assert(false && "Bad simplex size"); break; + } +} + +template < + typename DerivedV, + typename DerivedF, + typename Derivedp> +IGL_INLINE typename DerivedV::Scalar igl::winding_number( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & p) +{ + typedef typename DerivedV::Scalar wType; + const int ss = F.cols(); + const int m = F.rows(); + wType w = 0; + for(int f = 0;f::Scalar igl::winding_number, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Matrix::Scalar igl::winding_number, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template void igl::winding_number, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); +// generated by autoexplicit.sh +template Eigen::Matrix::Scalar igl::winding_number, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Matrix::Scalar igl::winding_number, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Matrix::Scalar igl::winding_number, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Matrix::Scalar igl::winding_number, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Matrix::Scalar igl::winding_number, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Matrix::Scalar igl::winding_number, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Matrix::Scalar igl::winding_number, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template Eigen::Matrix::Scalar igl::winding_number, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +#endif diff --git a/src/igl/winding_number.h b/src/igl/winding_number.h new file mode 100644 index 0000000000..4a1c51d81a --- /dev/null +++ b/src/igl/winding_number.h @@ -0,0 +1,64 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WINDING_NUMBER_H +#define IGL_WINDING_NUMBER_H +#include "igl_inline.h" +#include + +// Minimum number of iterms per openmp thread +#ifndef IGL_WINDING_NUMBER_OMP_MIN_VALUE +# define IGL_WINDING_NUMBER_OMP_MIN_VALUE 1000 +#endif +namespace igl +{ + // WINDING_NUMBER Compute the sum of solid angles of a triangle/tetrahedron + // described by points (vectors) V + // + // Templates: + // dim dimension of input + // Inputs: + // V n by 3 list of vertex positions + // F #F by 3 list of triangle indices, minimum index is 0 + // O no by 3 list of origin positions + // Outputs: + // S no by 1 list of winding numbers + // + template < + typename DerivedV, + typename DerivedF, + typename DerivedO, + typename DerivedW> + IGL_INLINE void winding_number( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & O, + Eigen::PlainObjectBase & W); + // Compute winding number of a single point + // + // Inputs: + // V n by dim list of vertex positions + // F #F by dim list of triangle indices, minimum index is 0 + // p single origin position + // Outputs: + // w winding number of this point + // + template < + typename DerivedV, + typename DerivedF, + typename Derivedp> + IGL_INLINE typename DerivedV::Scalar winding_number( + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & p); +} + +#ifndef IGL_STATIC_LIBRARY +# include "winding_number.cpp" +#endif + +#endif diff --git a/src/igl/writeBF.cpp b/src/igl/writeBF.cpp new file mode 100644 index 0000000000..18dc6a9343 --- /dev/null +++ b/src/igl/writeBF.cpp @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writeBF.h" +#include +#include +template < + typename DerivedWI, + typename DerivedP, + typename DerivedO> +IGL_INLINE bool igl::writeBF( + const std::string & filename, + const Eigen::PlainObjectBase & WI, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & O) +{ + using namespace Eigen; + using namespace std; + const int n = WI.rows(); + assert(n == WI.rows() && "WI must have n rows"); + assert(n == P.rows() && "P must have n rows"); + assert(n == O.rows() && "O must have n rows"); + MatrixXd WIPO(n,1+1+3); + for(int i = 0;i, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +#endif diff --git a/src/igl/writeBF.h b/src/igl/writeBF.h new file mode 100644 index 0000000000..5ea51f1bc1 --- /dev/null +++ b/src/igl/writeBF.h @@ -0,0 +1,37 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WRITEBF_H +#define IGL_WRITEBF_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Write a bones forest to a file + // + // Input: + // file_name path to .bf bones tree file + // WI #B list of unique weight indices + // P #B list of parent indices into B, -1 for roots + // O #B list of tip offsets + // Returns true on success, false on errors + template < + typename DerivedWI, + typename DerivedP, + typename DerivedO> + IGL_INLINE bool writeBF( + const std::string & filename, + const Eigen::PlainObjectBase & WI, + const Eigen::PlainObjectBase & P, + const Eigen::PlainObjectBase & O); +} + +#ifndef IGL_STATIC_LIBRARY +# include "writeBF.cpp" +#endif +#endif diff --git a/src/igl/writeDMAT.cpp b/src/igl/writeDMAT.cpp new file mode 100644 index 0000000000..c8f6ae0f6c --- /dev/null +++ b/src/igl/writeDMAT.cpp @@ -0,0 +1,92 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writeDMAT.h" +#include "list_to_matrix.h" +#include + +#include + +template +IGL_INLINE bool igl::writeDMAT( + const std::string file_name, + const Eigen::MatrixBase & W, + const bool ascii) +{ + FILE * fp = fopen(file_name.c_str(),"wb"); + if(fp == NULL) + { + fprintf(stderr,"IOError: writeDMAT() could not open %s...",file_name.c_str()); + return false; + } + if(ascii) + { + // first line contains number of rows and number of columns + fprintf(fp,"%d %d\n",(int)W.cols(),(int)W.rows()); + // Loop over columns slowly + for(int j = 0;j < W.cols();j++) + { + // loop over rows (down columns) quickly + for(int i = 0;i < W.rows();i++) + { + fprintf(fp,"%0.17lg\n",(double)W(i,j)); + } + } + }else + { + // write header for ascii + fprintf(fp,"0 0\n"); + // first line contains number of rows and number of columns + fprintf(fp,"%d %d\n",(int)W.cols(),(int)W.rows()); + // reader assumes the binary part is double precision + Eigen::MatrixXd Wd = W.template cast(); + fwrite(Wd.data(),sizeof(double),Wd.size(),fp); + //// Loop over columns slowly + //for(int j = 0;j < W.cols();j++) + //{ + // // loop over rows (down columns) quickly + // for(int i = 0;i < W.rows();i++) + // { + // double d = (double)W(i,j); + // fwrite(&d,sizeof(double),1,fp); + // } + //} + } + fclose(fp); + return true; +} + +template +IGL_INLINE bool igl::writeDMAT( + const std::string file_name, + const std::vector > & W, + const bool ascii) +{ + Eigen::Matrix mW; + list_to_matrix(W,mW); + return igl::writeDMAT(file_name,mW,ascii); +} + +template +IGL_INLINE bool igl::writeDMAT( + const std::string file_name, + const std::vector & W, + const bool ascii) +{ + Eigen::Matrix mW; + list_to_matrix(W,mW); + return igl::writeDMAT(file_name,mW,ascii); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template bool igl::writeDMAT >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, bool); +template bool igl::writeDMAT >(std::string, Eigen::MatrixBase > const&, bool); +template bool igl::writeDMAT >(std::string, Eigen::MatrixBase > const&, bool); +template bool igl::writeDMAT >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, bool); +template bool igl::writeDMAT >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, bool); +#endif diff --git a/src/igl/writeDMAT.h b/src/igl/writeDMAT.h new file mode 100644 index 0000000000..08409ef67c --- /dev/null +++ b/src/igl/writeDMAT.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WRITEDMAT_H +#define IGL_WRITEDMAT_H +#include "igl_inline.h" +// See writeDMAT.h for a description of the .dmat file type +#include +#include +#include +namespace igl +{ + // Write a matrix using ascii dmat file type + // + // Template: + // Mat matrix type that supports .rows(), .cols(), operator(i,j) + // Inputs: + // file_name path to .dmat file + // W eigen matrix containing to-be-written coefficients + // ascii write ascii file {true} + // Returns true on success, false on error + // + template + IGL_INLINE bool writeDMAT( + const std::string file_name, + const Eigen::MatrixBase & W, + const bool ascii=true); + template + IGL_INLINE bool writeDMAT( + const std::string file_name, + const std::vector > & W, + const bool ascii=true); + template + IGL_INLINE bool writeDMAT( + const std::string file_name, + const std::vector &W, + const bool ascii=true); +} + +#ifndef IGL_STATIC_LIBRARY +# include "writeDMAT.cpp" +#endif + +#endif diff --git a/src/igl/writeMESH.cpp b/src/igl/writeMESH.cpp new file mode 100644 index 0000000000..27b0123cb9 --- /dev/null +++ b/src/igl/writeMESH.cpp @@ -0,0 +1,152 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writeMESH.h" + +#include "verbose.h" +#include "list_to_matrix.h" +#include + +#include +#include +#include + +template +IGL_INLINE bool igl::writeMESH( + const std::string mesh_file_name, + const std::vector > & V, + const std::vector > & T, + const std::vector > & F) +{ + Eigen::MatrixXd mV; + Eigen::MatrixXi mT,mF; + bool is_rect; + is_rect = list_to_matrix(V,mV); + if(!is_rect) + { + return false; + } + is_rect = list_to_matrix(T,mT); + if(!is_rect) + { + return false; + } + is_rect = list_to_matrix(F,mF); + if(!is_rect) + { + return false; + } + return igl::writeMESH(mesh_file_name,mV,mT,mF); +} + + +template +IGL_INLINE bool igl::writeMESH( + const std::string str, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & T, + const Eigen::PlainObjectBase & F) +{ + using namespace std; + using namespace Eigen; + + //// This is (surprisingly) slower than the C-ish code below + //ofstream mesh_file; + //mesh_file.open(str.c_str()); + //if(!mesh_file.is_open()) + //{ + // cerr<<"IOError: "<, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +// generated by autoexplicit.sh +template bool igl::writeMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +// generated by autoexplicit.sh +template bool igl::writeMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +// generated by autoexplicit.sh +template bool igl::writeMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +//template bool igl::writeMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +template bool igl::writeMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); + +template bool igl::writeMESH, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template bool igl::writeMESH(std::basic_string, std::allocator >, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > > const&); +#endif diff --git a/src/igl/writeMESH.h b/src/igl/writeMESH.h new file mode 100644 index 0000000000..4bb51418ed --- /dev/null +++ b/src/igl/writeMESH.h @@ -0,0 +1,58 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WRITEMESH_H +#define IGL_WRITEMESH_H +#include "igl_inline.h" + +#include +#include +#include + +namespace igl +{ + // save a tetrahedral volume mesh to a .mesh file + // + // Templates: + // Scalar type for positions and vectors (will be cast as double) + // Index type for indices (will be cast to int) + // Input: + // mesh_file_name path of .mesh file + // V double matrix of vertex positions #V by 3 + // T #T list of tet indices into vertex positions + // F #F list of face indices into vertex positions + // + // Known bugs: Holes and regions are not supported + template + IGL_INLINE bool writeMESH( + const std::string mesh_file_name, + const std::vector > & V, + const std::vector > & T, + const std::vector > & F); + + // Templates: + // DerivedV real-value: i.e. from MatrixXd + // DerivedT integer-value: i.e. from MatrixXi + // DerivedF integer-value: i.e. from MatrixXi + // Input: + // mesh_file_name path of .mesh file + // V eigen double matrix #V by 3 + // T eigen int matrix #T by 4 + // F eigen int matrix #F by 3 + template + IGL_INLINE bool writeMESH( + const std::string str, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & T, + const Eigen::PlainObjectBase & F); +} + +#ifndef IGL_STATIC_LIBRARY +# include "writeMESH.cpp" +#endif + +#endif diff --git a/src/igl/writeOBJ.cpp b/src/igl/writeOBJ.cpp new file mode 100644 index 0000000000..2862541743 --- /dev/null +++ b/src/igl/writeOBJ.cpp @@ -0,0 +1,136 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writeOBJ.h" + +#include +#include +#include +#include +#include +#include + +template < + typename DerivedV, + typename DerivedF, + typename DerivedCN, + typename DerivedFN, + typename DerivedTC, + typename DerivedFTC> +IGL_INLINE bool igl::writeOBJ( + const std::string str, + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const Eigen::MatrixBase& CN, + const Eigen::MatrixBase& FN, + const Eigen::MatrixBase& TC, + const Eigen::MatrixBase& FTC) +{ + FILE * obj_file = fopen(str.c_str(),"w"); + if(NULL==obj_file) + { + printf("IOError: %s could not be opened for writing...",str.c_str()); + return false; + } + // Loop over V + for(int i = 0;i<(int)V.rows();i++) + { + fprintf(obj_file,"v"); + for(int j = 0;j<(int)V.cols();++j) + { + fprintf(obj_file," %0.17g", V(i,j)); + } + fprintf(obj_file,"\n"); + } + bool write_N = CN.rows() >0; + + if(write_N) + { + for(int i = 0;i<(int)CN.rows();i++) + { + fprintf(obj_file,"vn %0.17g %0.17g %0.17g\n", + CN(i,0), + CN(i,1), + CN(i,2) + ); + } + fprintf(obj_file,"\n"); + } + + bool write_texture_coords = TC.rows() >0; + + if(write_texture_coords) + { + for(int i = 0;i<(int)TC.rows();i++) + { + fprintf(obj_file, "vt %0.17g %0.17g\n",TC(i,0),TC(i,1)); + } + fprintf(obj_file,"\n"); + } + + // loop over F + for(int i = 0;i<(int)F.rows();++i) + { + fprintf(obj_file,"f"); + for(int j = 0; j<(int)F.cols();++j) + { + // OBJ is 1-indexed + fprintf(obj_file," %u",F(i,j)+1); + + if(write_texture_coords) + fprintf(obj_file,"/%u",FTC(i,j)+1); + if(write_N) + { + if (write_texture_coords) + fprintf(obj_file,"/%u",FN(i,j)+1); + else + fprintf(obj_file,"//%u",FN(i,j)+1); + } + } + fprintf(obj_file,"\n"); + } + fclose(obj_file); + return true; +} + +template +IGL_INLINE bool igl::writeOBJ( + const std::string str, + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F) +{ + using namespace std; + using namespace Eigen; + assert(V.cols() == 3 && "V should have 3 columns"); + ofstream s(str); + if(!s.is_open()) + { + fprintf(stderr,"IOError: writeOBJ() could not open %s\n",str.c_str()); + return false; + } + s<< + V.format(IOFormat(FullPrecision,DontAlignCols," ","\n","v ","","","\n"))<< + (F.array()+1).format(IOFormat(FullPrecision,DontAlignCols," ","\n","f ","","","\n")); + return true; +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::writeOBJ, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template bool igl::writeOBJ, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template bool igl::writeOBJ, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +// generated by autoexplicit.sh +template bool igl::writeOBJ, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +template bool igl::writeOBJ, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +template bool igl::writeOBJ, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +template bool igl::writeOBJ, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +template bool igl::writeOBJ, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +template bool igl::writeOBJ, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); +#endif diff --git a/src/igl/writeOBJ.h b/src/igl/writeOBJ.h new file mode 100644 index 0000000000..d515796dbf --- /dev/null +++ b/src/igl/writeOBJ.h @@ -0,0 +1,59 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WRITEOBJ_H +#define IGL_WRITEOBJ_H +#include "igl_inline.h" +// History: +// return type changed from void to bool Alec 20 Sept 2011 + +#include +#include + +namespace igl +{ + // Write a mesh in an ascii obj file + // Inputs: + // str path to outputfile + // V #V by 3 mesh vertex positions + // F #F by 3|4 mesh indices into V + // CN #CN by 3 normal vectors + // FN #F by 3|4 corner normal indices into CN + // TC #TC by 2|3 texture coordinates + // FTC #F by 3|4 corner texture coord indices into TC + // Returns true on success, false on error + // + // Known issues: Horrifyingly, this does not have the same order of + // parameters as readOBJ. + template < + typename DerivedV, + typename DerivedF, + typename DerivedCN, + typename DerivedFN, + typename DerivedTC, + typename DerivedFTC> + IGL_INLINE bool writeOBJ( + const std::string str, + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F, + const Eigen::MatrixBase& CN, + const Eigen::MatrixBase& FN, + const Eigen::MatrixBase& TC, + const Eigen::MatrixBase& FTC); + template + IGL_INLINE bool writeOBJ( + const std::string str, + const Eigen::MatrixBase& V, + const Eigen::MatrixBase& F); + +} + +#ifndef IGL_STATIC_LIBRARY +# include "writeOBJ.cpp" +#endif + +#endif diff --git a/src/igl/writeOFF.cpp b/src/igl/writeOFF.cpp new file mode 100644 index 0000000000..9222d76a03 --- /dev/null +++ b/src/igl/writeOFF.cpp @@ -0,0 +1,94 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writeOFF.h" +#include +#include + +// write mesh to an ascii off file +template +IGL_INLINE bool igl::writeOFF( + const std::string fname, + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F) +{ + using namespace std; + using namespace Eigen; + assert(V.cols() == 3 && "V should have 3 columns"); + ofstream s(fname); + if(!s.is_open()) + { + fprintf(stderr,"IOError: writeOFF() could not open %s\n",fname.c_str()); + return false; + } + + s<< + "OFF\n"< +IGL_INLINE bool igl::writeOFF( + const std::string fname, + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& C) +{ + using namespace std; + using namespace Eigen; + assert(V.cols() == 3 && "V should have 3 columns"); + assert(C.cols() == 3 && "C should have 3 columns"); + + if(V.rows() != C.rows()) + { + fprintf(stderr,"IOError: writeOFF() Only color per vertex supported. V and C should have same size.\n"); + return false; + } + + ofstream s(fname); + if(!s.is_open()) + { + fprintf(stderr,"IOError: writeOFF() could not open %s\n",fname.c_str()); + return false; + } + + //Check if RGB values are in the range [0..1] or [0..255] + int rgbScale = (C.maxCoeff() <= 1.0)?255:1; + // Use RGB_Array instead of RGB because of clash with mingw macro + // (https://github.com/libigl/libigl/pull/679) + Eigen::MatrixXd RGB_Array = rgbScale * C; + + s<< "COFF\n"<, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +// generated by autoexplicit.sh +template bool igl::writeOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +// generated by autoexplicit.sh +template bool igl::writeOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template bool igl::writeOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template bool igl::writeOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template bool igl::writeOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template bool igl::writeOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template bool igl::writeOFF, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template bool igl::writeOFF, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template bool igl::writeOFF, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +#endif diff --git a/src/igl/writeOFF.h b/src/igl/writeOFF.h new file mode 100644 index 0000000000..754c18a531 --- /dev/null +++ b/src/igl/writeOFF.h @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WRITEOFF_H +#define IGL_WRITEOFF_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + //Export geometry and colors-by-vertex + // Export a mesh from an ascii OFF file, filling in vertex positions. + // Only triangle meshes are supported + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Index type for indices (will be read as int and cast to Index) + // Inputs: + // str path to .off output file + // V #V by 3 mesh vertex positions + // F #F by 3 mesh indices into V + // C double matrix of rgb values per vertex #V by 3 + // Outputs: + // Returns true on success, false on errors + template + IGL_INLINE bool writeOFF( + const std::string str, + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const Eigen::PlainObjectBase& C); + + template + IGL_INLINE bool writeOFF( + const std::string str, + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F); +} + +#ifndef IGL_STATIC_LIBRARY +# include "writeOFF.cpp" +#endif + +#endif diff --git a/src/igl/writePLY.cpp b/src/igl/writePLY.cpp new file mode 100644 index 0000000000..55c9b83fd3 --- /dev/null +++ b/src/igl/writePLY.cpp @@ -0,0 +1,182 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writePLY.h" +#include + +#include +#include + +namespace +{ + template int ply_type(); + template <> int ply_type(){ return PLY_CHAR; } + template <> int ply_type(){ return PLY_SHORT; } + template <> int ply_type(){ return PLY_INT; } + template <> int ply_type(){ return PLY_UCHAR; } + template <> int ply_type(){ return PLY_SHORT; } + template <> int ply_type(){ return PLY_UINT; } + template <> int ply_type(){ return PLY_FLOAT; } + template <> int ply_type(){ return PLY_DOUBLE; } +} + +template < + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DerivedUV> +IGL_INLINE bool igl::writePLY( + const std::string & filename, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & N, + const Eigen::MatrixBase & UV, + const bool ascii) +{ + // Largely based on obj2ply.c + typedef typename DerivedV::Scalar VScalar; + typedef typename DerivedN::Scalar NScalar; + typedef typename DerivedUV::Scalar UVScalar; + typedef typename DerivedF::Scalar FScalar; + + typedef struct Vertex + { + VScalar x,y,z,w; /* position */ + NScalar nx,ny,nz; /* surface normal */ + UVScalar s,t; /* texture coordinates */ + } Vertex; + + typedef struct Face + { + unsigned char nverts; /* number of vertex indices in list */ + FScalar *verts; /* vertex index list */ + } Face; + + igl::ply::PlyProperty vert_props[] = + { /* list of property information for a vertex */ + {"x", ply_type(), ply_type(),offsetof(Vertex,x),0,0,0,0}, + {"y", ply_type(), ply_type(),offsetof(Vertex,y),0,0,0,0}, + {"z", ply_type(), ply_type(),offsetof(Vertex,z),0,0,0,0}, + {"nx",ply_type(), ply_type(),offsetof(Vertex,nx),0,0,0,0}, + {"ny",ply_type(), ply_type(),offsetof(Vertex,ny),0,0,0,0}, + {"nz",ply_type(), ply_type(),offsetof(Vertex,nz),0,0,0,0}, + {"s", ply_type(),ply_type(),offsetof(Vertex,s),0,0,0,0}, + {"t", ply_type(),ply_type(),offsetof(Vertex,t),0,0,0,0}, + }; + + igl::ply::PlyProperty face_props[] = + { /* list of property information for a face */ + {"vertex_indices", ply_type(), ply_type(), + offsetof(Face,verts), 1, PLY_UCHAR, PLY_UCHAR, offsetof(Face,nverts)}, + }; + const bool has_normals = N.rows() > 0; + const bool has_texture_coords = UV.rows() > 0; + std::vector vlist(V.rows()); + std::vector flist(F.rows()); + for(size_t i = 0;i<(size_t)V.rows();i++) + { + vlist[i].x = V(i,0); + vlist[i].y = V(i,1); + vlist[i].z = V(i,2); + if(has_normals) + { + vlist[i].nx = N(i,0); + vlist[i].ny = N(i,1); + vlist[i].nz = N(i,2); + } + if(has_texture_coords) + { + vlist[i].s = UV(i,0); + vlist[i].t = UV(i,1); + } + } + for(size_t i = 0;i<(size_t)F.rows();i++) + { + flist[i].nverts = F.cols(); + flist[i].verts = new FScalar[F.cols()]; + for(size_t c = 0;c<(size_t)F.cols();c++) + { + flist[i].verts[c] = F(i,c); + } + } + + const char * elem_names[] = {"vertex","face"}; + FILE * fp = fopen(filename.c_str(),"w"); + if(fp==NULL) + { + return false; + } + igl::ply::PlyFile * ply = igl::ply::ply_write(fp, 2,elem_names, + (ascii ? PLY_ASCII : PLY_BINARY_LE)); + if(ply==NULL) + { + return false; + } + + std::vector plist; + plist.push_back(vert_props[0]); + plist.push_back(vert_props[1]); + plist.push_back(vert_props[2]); + if (has_normals) + { + plist.push_back(vert_props[3]); + plist.push_back(vert_props[4]); + plist.push_back(vert_props[5]); + } + if (has_texture_coords) + { + plist.push_back(vert_props[6]); + plist.push_back(vert_props[7]); + } + ply_describe_element(ply, "vertex", V.rows(),plist.size(), + &plist[0]); + + ply_describe_element(ply, "face", F.rows(),1,&face_props[0]); + ply_header_complete(ply); + int native_binary_type = igl::ply::get_native_binary_type2(); + ply_put_element_setup(ply, "vertex"); + for(const auto v : vlist) + { + ply_put_element(ply, (void *) &v, &native_binary_type); + } + ply_put_element_setup(ply, "face"); + for(const auto f : flist) + { + ply_put_element(ply, (void *) &f, &native_binary_type); + } + + ply_close(ply); + for(size_t i = 0;i<(size_t)F.rows();i++) + { + delete[] flist[i].verts; + } + return true; +} + +template < + typename DerivedV, + typename DerivedF> +IGL_INLINE bool igl::writePLY( + const std::string & filename, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const bool ascii) +{ + Eigen::Matrix N,UV; + return writePLY(filename,V,F,N,UV,ascii); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::writePLY, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, bool); +// generated by autoexplicit.sh +template bool igl::writePLY, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, bool); +template bool igl::writePLY, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, bool); +template bool igl::writePLY, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, bool); +template bool igl::writePLY, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, bool); +#endif diff --git a/src/igl/writePLY.h b/src/igl/writePLY.h new file mode 100644 index 0000000000..8b99ea1d04 --- /dev/null +++ b/src/igl/writePLY.h @@ -0,0 +1,49 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WRITEPLY_H +#define IGL_WRITEPLY_H +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Write a mesh to a .ply file. + // + // Inputs: + // filename path to .ply file + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // N #V by 3 list of vertex normals + // UV #V by 2 list of vertex texture coordinates + // Returns true iff success + template < + typename DerivedV, + typename DerivedF, + typename DerivedN, + typename DerivedUV> + IGL_INLINE bool writePLY( + const std::string & filename, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const Eigen::MatrixBase & N, + const Eigen::MatrixBase & UV, + const bool ascii = true); + template < + typename DerivedV, + typename DerivedF> + IGL_INLINE bool writePLY( + const std::string & filename, + const Eigen::MatrixBase & V, + const Eigen::MatrixBase & F, + const bool ascii = true); +} +#ifndef IGL_STATIC_LIBRARY +# include "writePLY.cpp" +#endif +#endif diff --git a/src/igl/writeSTL.cpp b/src/igl/writeSTL.cpp new file mode 100644 index 0000000000..d95f2a8d77 --- /dev/null +++ b/src/igl/writeSTL.cpp @@ -0,0 +1,121 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writeSTL.h" +#include + +template +IGL_INLINE bool igl::writeSTL( + const std::string & filename, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & N, + const bool ascii) +{ + using namespace std; + assert(N.rows() == 0 || F.rows() == N.rows()); + if(ascii) + { + FILE * stl_file = fopen(filename.c_str(),"w"); + if(stl_file == NULL) + { + cerr<<"IOError: "<0) + { + fprintf(stl_file,"%e %e %e\n", + (float)N(f,0), + (float)N(f,1), + (float)N(f,2)); + }else + { + fprintf(stl_file,"0 0 0\n"); + } + fprintf(stl_file,"outer loop\n"); + for(int c = 0;c n(3,0); + if(N.rows() > 0) + { + n[0] = N(f,0); + n[1] = N(f,1); + n[2] = N(f,2); + } + fwrite(&n[0],sizeof(float),3,stl_file); + for(int c = 0;c<3;c++) + { + vector v(3); + v[0] = V(F(f,c),0); + v[1] = V(F(f,c),1); + v[2] = V(F(f,c),2); + fwrite(&v[0],sizeof(float),3,stl_file); + } + unsigned short att_count = 0; + fwrite(&att_count,sizeof(unsigned short),1,stl_file); + } + fclose(stl_file); + return true; + } +} + +template +IGL_INLINE bool igl::writeSTL( + const std::string & filename, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const bool ascii) +{ + return writeSTL(filename,V,F, DerivedV(), ascii); +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +// generated by autoexplicit.sh +template bool igl::writeSTL, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool); +// generated by autoexplicit.sh +template bool igl::writeSTL, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool); +// generated by autoexplicit.sh +template bool igl::writeSTL, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool); +template bool igl::writeSTL, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool); +template bool igl::writeSTL, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool); +#endif diff --git a/src/igl/writeSTL.h b/src/igl/writeSTL.h new file mode 100644 index 0000000000..1ffd202818 --- /dev/null +++ b/src/igl/writeSTL.h @@ -0,0 +1,52 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WRITESTL_H +#define IGL_WRITESTL_H +#include "igl_inline.h" + +#ifndef IGL_NO_EIGEN +# include +#endif +#include +#include + +namespace igl +{ + // Write a mesh to an stl file. + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Inputs: + // filename path to .obj file + // V double matrix of vertex positions #F*3 by 3 + // F index matrix of triangle indices #F by 3 + // N double matrix of vertex positions #F by 3 + // asci write ascii file {true} + // Returns true on success, false on errors + // + template + IGL_INLINE bool writeSTL( + const std::string & filename, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & N, + const bool ascii=true); + template + IGL_INLINE bool writeSTL( + const std::string & filename, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const bool ascii=true); +} + +#ifndef IGL_STATIC_LIBRARY +# include "writeSTL.cpp" +#endif + +#endif diff --git a/src/igl/writeTGF.cpp b/src/igl/writeTGF.cpp new file mode 100644 index 0000000000..65c8fc6a42 --- /dev/null +++ b/src/igl/writeTGF.cpp @@ -0,0 +1,73 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writeTGF.h" +#include + +IGL_INLINE bool igl::writeTGF( + const std::string tgf_filename, + const std::vector > & C, + const std::vector > & E) +{ + FILE * tgf_file = fopen(tgf_filename.c_str(),"w"); + if(NULL==tgf_file) + { + printf("IOError: %s could not be opened\n",tgf_filename.c_str()); + return false; + } + // Loop over vertices + for(int i = 0; i<(int)C.size();i++) + { + assert(C[i].size() == 3); + // print a line with vertex number then "description" + // Where "description" in our case is the 3d position in space + // + fprintf(tgf_file, + "%4d " + "%10.17g %10.17g %10.17g " // current location + // All others are not needed for this legacy support + "\n", + i+1, + C[i][0], C[i][1], C[i][2]); + } + + // print a comment to separate vertices and edges + fprintf(tgf_file,"#\n"); + + // loop over edges + for(int i = 0;i<(int)E.size();i++) + { + assert(E[i].size()==2); + fprintf(tgf_file,"%4d %4d\n", + E[i][0]+1, + E[i][1]+1); + } + + // print a comment to separate edges and faces + fprintf(tgf_file,"#\n"); + + fclose(tgf_file); + + return true; +} + +#ifndef IGL_NO_EIGEN +#include "matrix_to_list.h" + +IGL_INLINE bool igl::writeTGF( + const std::string tgf_filename, + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & E) +{ + using namespace std; + vector > vC; + vector > vE; + matrix_to_list(C,vC); + matrix_to_list(E,vE); + return writeTGF(tgf_filename,vC,vE); +} +#endif diff --git a/src/igl/writeTGF.h b/src/igl/writeTGF.h new file mode 100644 index 0000000000..999584463a --- /dev/null +++ b/src/igl/writeTGF.h @@ -0,0 +1,48 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WRITETGF_H +#define IGL_WRITETGF_H +#include "igl_inline.h" + +#include +#include +#ifndef IGL_NO_EIGEN +#include +#endif + +namespace igl +{ + // WRITETGF + // + // Write a graph to a .tgf file + // + // Input: + // filename .tgf file name + // V # vertices by 3 list of vertex positions + // E # edges by 2 list of edge indices + // + // Assumes that graph vertices are 3 dimensional + IGL_INLINE bool writeTGF( + const std::string tgf_filename, + const std::vector > & C, + const std::vector > & E); + + #ifndef IGL_NO_EIGEN + IGL_INLINE bool writeTGF( + const std::string tgf_filename, + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & E); + #endif +} + +#ifndef IGL_STATIC_LIBRARY +# include "writeTGF.cpp" +#endif + +#endif + diff --git a/src/igl/writeWRL.cpp b/src/igl/writeWRL.cpp new file mode 100644 index 0000000000..2740d1c2fb --- /dev/null +++ b/src/igl/writeWRL.cpp @@ -0,0 +1,126 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writeWRL.h" +#include +#include +template +IGL_INLINE bool igl::writeWRL( + const std::string & str, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F) +{ + using namespace std; + using namespace Eigen; + assert(V.cols() == 3 && "V should have 3 columns"); + assert(F.cols() == 3 && "F should have 3 columns"); + ofstream s(str); + if(!s.is_open()) + { + cerr<<"IOError: writeWRL() could not open "< FF(F.rows(),4); + FF.leftCols(3) = F; + FF.col(3).setConstant(-1); + + s< +IGL_INLINE bool igl::writeWRL( + const std::string & str, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & C) +{ + using namespace std; + using namespace Eigen; + assert(V.cols() == 3 && "V should have 3 columns"); + assert(F.cols() == 3 && "F should have 3 columns"); + ofstream s(str); + if(!s.is_open()) + { + cerr<<"IOError: writeWRL() could not open "< FF(F.rows(),4); + FF.leftCols(3) = F; + FF.col(3).setConstant(-1); + + + //Check if RGB values are in the range [0..1] or [0..255] + double rgbScale = (C.maxCoeff() <= 1.0)?1.0:1.0/255.0; + Eigen::MatrixXd RGB = rgbScale * C; + + s<, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +// generated by autoexplicit.sh +template bool igl::writeWRL, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +// generated by autoexplicit.sh +template bool igl::writeWRL, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +// generated by autoexplicit.sh +template bool igl::writeWRL, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template bool igl::writeWRL, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +template bool igl::writeWRL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&); +#endif diff --git a/src/igl/writeWRL.h b/src/igl/writeWRL.h new file mode 100644 index 0000000000..5ff6a25ed6 --- /dev/null +++ b/src/igl/writeWRL.h @@ -0,0 +1,46 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2015 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WRITE_WRL_H +#define IGL_WRITE_WRL_H +#include "igl_inline.h" +#include +#include +namespace igl +{ + // Write mesh to a .wrl file + // + // Inputs: + // str path to .wrl file + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // Returns true iff succes + template + IGL_INLINE bool writeWRL( + const std::string & str, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F); + + // Write mesh to a .wrl file + // + // Inputs: + // str path to .wrl file + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // C double matrix of rgb values per vertex #V by 3 + // Returns true iff succes + template + IGL_INLINE bool writeWRL( + const std::string & str, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F, + const Eigen::PlainObjectBase & C); +} +#ifndef IGL_STATIC_LIBRARY +#include "writeWRL.cpp" +#endif +#endif diff --git a/src/igl/write_triangle_mesh.cpp b/src/igl/write_triangle_mesh.cpp new file mode 100644 index 0000000000..3699009f5c --- /dev/null +++ b/src/igl/write_triangle_mesh.cpp @@ -0,0 +1,70 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "write_triangle_mesh.h" +#include "pathinfo.h" +#include "writeMESH.h" +#include "writeOBJ.h" +#include "writeOFF.h" +#include "writePLY.h" +#include "writeSTL.h" +#include "writeWRL.h" + +#include + +template +IGL_INLINE bool igl::write_triangle_mesh( + const std::string str, + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const bool force_ascii) +{ + using namespace std; + // dirname, basename, extension and filename + string d,b,e,f; + pathinfo(str,d,b,e,f); + // Convert extension to lower case + std::transform(e.begin(), e.end(), e.begin(), ::tolower); + if(e == "mesh") + { + Eigen::MatrixXi _1; + return writeMESH(str,V,_1,F); + }else if(e == "obj") + { + return writeOBJ(str,V,F); + }else if(e == "off") + { + return writeOFF(str,V,F); + }else if(e == "ply") + { + return writePLY(str,V,F,force_ascii); + }else if(e == "stl") + { + return writeSTL(str,V,F,force_ascii); + }else if(e == "wrl") + { + return writeWRL(str,V,F); + }else + { + assert("Unsupported file format"); + cerr<<"Unsupported file format: ."<, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool); +// generated by autoexplicit.sh +template bool igl::write_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool); +// generated by autoexplicit.sh +template bool igl::write_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool); +// generated by autoexplicit.sh +template bool igl::write_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, const bool); +template bool igl::write_triangle_mesh, Eigen::Matrix >(std::basic_string, std::allocator >, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, bool); +#endif diff --git a/src/igl/write_triangle_mesh.h b/src/igl/write_triangle_mesh.h new file mode 100644 index 0000000000..6a72416281 --- /dev/null +++ b/src/igl/write_triangle_mesh.h @@ -0,0 +1,42 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_WRITE_TRIANGLE_MESH_H +#define IGL_WRITE_TRIANGLE_MESH_H +#include "igl_inline.h" + +#include +#include + +namespace igl +{ + // write mesh to a file with automatic detection of file format. supported: + // obj, off, stl, wrl, ply, mesh). + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Index type for indices (will be read as int and cast to Index) + // Inputs: + // str path to file + // V eigen double matrix #V by 3 + // F eigen int matrix #F by 3 + // force_ascii force ascii format even if binary is available + // Returns true iff success + template + IGL_INLINE bool write_triangle_mesh( + const std::string str, + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const bool force_ascii = true); +} + +#ifndef IGL_STATIC_LIBRARY +# include "write_triangle_mesh.cpp" +#endif + +#endif diff --git a/src/igl/xml/ReAntTweakBarXMLSerialization.h b/src/igl/xml/ReAntTweakBarXMLSerialization.h new file mode 100644 index 0000000000..c43af0c809 --- /dev/null +++ b/src/igl/xml/ReAntTweakBarXMLSerialization.h @@ -0,0 +1,269 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_XML_REANTTWEAKBAR_XML_SERIALIZATION_H +#define IGL_XML_REANTTWEAKBAR_XML_SERIALIZATION_H +#include "../igl_inline.h" +#include "serialize_xml.h" + +#undef IGL_HEADER_ONLY +#include "../anttweakbar/ReAntTweakBar.h" + +// Forward declarations +namespace igl +{ + namespace anttweakbar + { + class ReTwBar; + } +}; +namespace tinyxml2 +{ + class XMLDocument; +}; + +namespace igl +{ + namespace xml + { + +// namespace +// { + +// IGL_INLINE bool save_ReAntTweakBar(::igl::anttweakbar::ReTwBar* bar, const char* file_name); +// IGL_INLINE bool save_ReAntTweakBar(::igl::anttweakbar::ReTwBar* bar, tinyxml2::XMLDocument* doc); +// IGL_INLINE bool load_ReAntTweakBar(::igl::anttweakbar::ReTwBar* bar, const char *file_name); +// IGL_INLINE bool load_ReAntTweakBar(::igl::anttweakbar::ReTwBar* bar, tinyxml2::XMLDocument* doc); + + + IGL_INLINE bool save_ReAntTweakBar(::igl::anttweakbar::ReTwBar* bar, const char* file_name) + { + const char * name_chars = TwGetBarName(bar->bar); + std::string name = std::string(name_chars) + "_AntTweakBar"; + + const std::vector< ::igl::anttweakbar::ReTwRWItem>& rw_items = bar->get_rw_items(); + for(std::vector< ::igl::anttweakbar::ReTwRWItem>::const_iterator it = rw_items.begin(); it != rw_items.end(); it++) + { + std::string val = bar->get_value_as_string(it->var,it->type); + //::igl::XMLSerializer::SaveObject(val,it->name,name,file_name,false); + ::igl::serialize_xml(val,it->name,file_name,false,false); + } + + char var[REANTTWEAKBAR_MAX_CB_VAR_SIZE]; + // Print all CB variables + const std::vector< ::igl::anttweakbar::ReTwCBItem>& cb_items = bar->get_cb_items(); + for(std::vector< ::igl::anttweakbar::ReTwCBItem>::const_iterator it = cb_items.begin(); it != cb_items.end(); it++) + { + TwType type = it->type; + //TwSetVarCallback setCallback = it->setCallback; + TwGetVarCallback getCallback = it->getCallback; + void * clientData = it->clientData; + // I'm not sure how to do what I want to do. getCallback needs to be sure + // that it can write to var. So var needs to point to a valid and big + // enough chunk of memory + getCallback(var,clientData); + + std::string val = bar->get_value_as_string(var,type); + //::igl::XMLSerializer::SaveObject(val,it->name,name,file_name,false); + ::igl::serialize_xml(val,it->name,file_name,false,false); + } + + return true; + } + + /*IGL_INLINE bool save_ReAntTweakBar(::igl::anttweakbar::ReTwBar* bar, tinyxml2::XMLDocument* doc) + { + std::vector buffer; + + const char * name_chars = TwGetBarName(bar->bar); + std::string name = std::string(name_chars) + "_AntTweakBar"; + ::igl::XMLSerializer* s = new ::igl::XMLSerializer(name); + + const std::vector< ::igl::anttweakbar::ReTwRWItem>& rw_items = bar->get_rw_items(); + for(std::vector< ::igl::anttweakbar::ReTwRWItem>::const_iterator it = rw_items.begin(); it != rw_items.end(); it++) + { + std::string val = bar->get_value_as_string(it->var,it->type); + char** cval = new char*; // create char* on heap + *cval = new char[val.size()+1]; + buffer.push_back(cval); + strcpy(*cval,val.c_str()); + s->Add(*cval,it->name); + } + + char var[REANTTWEAKBAR_MAX_CB_VAR_SIZE]; + // Print all CB variables + const std::vector< ::igl::anttweakbar::ReTwCBItem>& cb_items = bar->get_cb_items(); + for(std::vector< ::igl::anttweakbar::ReTwCBItem>::const_iterator it = cb_items.begin(); it != cb_items.end(); it++) + { + TwType type = it->type; + //TwSetVarCallback setCallback = it->setCallback; + TwGetVarCallback getCallback = it->getCallback; + void * clientData = it->clientData; + // I'm not sure how to do what I want to do. getCallback needs to be sure + // that it can write to var. So var needs to point to a valid and big + // enough chunk of memory + getCallback(var,clientData); + + std::string val = bar->get_value_as_string(var,type); + char** cval = new char*; // create char* on heap + *cval = new char[val.size()+1]; + buffer.push_back(cval); + strcpy(*cval,val.c_str()); + s->Add(*cval,it->name); + } + + s->SaveToXMLDoc(name,doc); + + // delete pointer buffers + for(unsigned int i=0;ibar); + std::string name = std::string(name_chars) + "_AntTweakBar"; + + const std::vector< ::igl::anttweakbar::ReTwRWItem>& rw_items = bar->get_rw_items(); + for(std::vector< ::igl::anttweakbar::ReTwRWItem>::const_iterator it = rw_items.begin(); it != rw_items.end(); it++) + { + char* val; + //::igl::XMLSerializer::LoadObject(val,it->name,name,file_name); + ::igl::deserialize_xml(val,it->name,file_name); + sscanf(val,"%s %[^\n]",type_str,value_str); + + if(!bar->type_from_string(type_str,type)) + { + printf("ERROR: %s type not found... Skipping...\n",type_str); + continue; + } + + bar->set_value_from_string(it->name.c_str(),type,value_str); + delete[] val; + } + + const std::vector< ::igl::anttweakbar::ReTwCBItem>& cb_items = bar->get_cb_items(); + for(std::vector< ::igl::anttweakbar::ReTwCBItem>::const_iterator it = cb_items.begin(); it != cb_items.end(); it++) + { + char* val; + //::igl::XMLSerializer::LoadObject(val,it->name,name,file_name); + ::igl::deserialize_xml(val,it->name,file_name); + sscanf(val,"%s %[^\n]",type_str,value_str); + + if(!bar->type_from_string(type_str,type)) + { + printf("ERROR: %s type not found... Skipping...\n",type_str); + continue; + } + + bar->set_value_from_string(it->name.c_str(),type,value_str); + delete[] val; + } + + return true; + } + + /*IGL_INLINE bool load_ReAntTweakBar(::igl::anttweakbar::ReTwBar* bar, tinyxml2::XMLDocument* doc) + { + std::map variables; + std::map cbVariables; + + const char * name_chars = TwGetBarName(bar->bar); + std::string name = std::string(name_chars) + "_AntTweakBar"; + ::igl::XMLSerializer* s = new ::igl::XMLSerializer(name); + + std::map::iterator iter; + const std::vector< ::igl::anttweakbar::ReTwRWItem>& rw_items = bar->get_rw_items(); + for(std::vector< ::igl::anttweakbar::ReTwRWItem>::const_iterator it = rw_items.begin(); it != rw_items.end(); it++) + { + variables[it->name] = NULL; + iter = variables.find(it->name); + s->Add(iter->second,iter->first); + } + + // Add all CB variables + const std::vector< ::igl::anttweakbar::ReTwCBItem>& cb_items = bar->get_cb_items(); + for(std::vector< ::igl::anttweakbar::ReTwCBItem>::const_iterator it = cb_items.begin(); it != cb_items.end(); it++) + { + cbVariables[it->name] = NULL; + iter = cbVariables.find(it->name); + s->Add(iter->second,iter->first); + } + + s->LoadFromXMLDoc(doc); + + // Set loaded values + char type_str[REANTTWEAKBAR_MAX_WORD]; + char value_str[REANTTWEAKBAR_MAX_WORD]; + TwType type; + + for(iter = variables.begin(); iter != variables.end(); iter++) + { + if(iter->second == NULL) + { + printf("ERROR: '%s' entry not found... Skipping...\n",iter->first.c_str()); + continue; + } + + sscanf(iter->second,"%s %[^\n]",type_str,value_str); + + if(!bar->type_from_string(type_str,type)) + { + printf("ERROR: Type '%s' of '%s' not found... Skipping...\n",type_str,iter->first.c_str()); + continue; + } + + bar->set_value_from_string(iter->first.c_str(),type,value_str); + } + + for(iter = cbVariables.begin(); iter != cbVariables.end(); iter++) + { + if(iter->second == NULL) + { + printf("ERROR: '%s' entry not found... Skipping...\n",iter->first.c_str()); + continue; + } + + sscanf(iter->second,"%s %[^\n]",type_str,value_str); + + if(!bar->type_from_string(type_str,type)) + { + printf("ERROR: Type '%s' of '%s' not found... Skipping...\n",type_str,iter->first.c_str()); + continue; + } + + bar->set_value_from_string(iter->first.c_str(),type,value_str); + } + + // delete buffers + for(iter = variables.begin(); iter != variables.end(); iter++) + delete[] iter->second; + + for(iter = cbVariables.begin(); iter != cbVariables.end(); iter++) + delete[] iter->second; + + delete s; + + return true; + }*/ + +// } + } +} + +#endif diff --git a/src/igl/xml/XMLSerializable.h b/src/igl/xml/XMLSerializable.h new file mode 100644 index 0000000000..6105596439 --- /dev/null +++ b/src/igl/xml/XMLSerializable.h @@ -0,0 +1,225 @@ +#ifndef IGL_XML_XMLSERIALIZABLE_H +#define IGL_XML_XMLSERIALIZABLE_H + +#include "serialize_xml.h" +#include "../igl_inline.h" +#include "../serialize.h" + +#include + + +// Interface for xml-serializable class see serialize_xml.h + +// Pretty sure all of these IGL_INLINE should be inline + +namespace igl +{ + namespace xml + { + // interface for user defined types + struct XMLSerializableBase : public SerializableBase + { + virtual void Serialize(std::vector& buffer) const = 0; + virtual void Deserialize(const std::vector& buffer) = 0; + virtual void Serialize(tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element) const = 0; + virtual void Deserialize(const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element) = 0; + }; + + // Convenient interface for user defined types + class XMLSerializable: public XMLSerializableBase + { + private: + + template + struct XMLSerializationObject: public XMLSerializableBase + { + bool Binary; + std::string Name; + T* Object; + + void Serialize(std::vector& buffer) const { + serialize(*Object,Name,buffer); + } + + void Deserialize(const std::vector& buffer) { + deserialize(*Object,Name,buffer); + } + + void Serialize(tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element) const { + serialize_xml(*Object,Name,doc,element,Binary); + } + + void Deserialize(const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element) { + deserialize_xml(*Object,Name,doc,element); + } + }; + + mutable bool initialized; + mutable std::vector objects; + + public: + + // Override this function to add your member variables which should be serialized + IGL_INLINE virtual void InitSerialization() = 0; + + // Following functions can be overridden to handle the specific events. + // Return false to prevent the de-/serialization of an object. + IGL_INLINE virtual bool PreSerialization() const; + IGL_INLINE virtual void PostSerialization() const; + IGL_INLINE virtual bool PreDeserialization(); + IGL_INLINE virtual void PostDeserialization(); + + // Default implementation of XMLSerializableBase interface + IGL_INLINE void Serialize(std::vector& buffer) const; + IGL_INLINE void Deserialize(const std::vector& buffer); + IGL_INLINE void Serialize(tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element) const; + IGL_INLINE void Deserialize(const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element); + + // Default constructor, destructor, assignment and copy constructor + IGL_INLINE XMLSerializable(); + IGL_INLINE XMLSerializable(const XMLSerializable& obj); + IGL_INLINE ~XMLSerializable(); + IGL_INLINE XMLSerializable& operator=(const XMLSerializable& obj); + + // Use this function to add your variables which should be serialized + template + IGL_INLINE void Add(T& obj,std::string name,bool binary = false); + }; + + // IMPLEMENTATION + + IGL_INLINE bool XMLSerializable::PreSerialization() const + { + return true; + } + + IGL_INLINE void XMLSerializable::PostSerialization() const + { + } + + IGL_INLINE bool XMLSerializable::PreDeserialization() + { + return true; + } + + IGL_INLINE void XMLSerializable::PostDeserialization() + { + } + + IGL_INLINE void XMLSerializable::Serialize(std::vector& buffer) const + { + if(this->PreSerialization()) + { + if(initialized == false) + { + objects.clear(); + (const_cast(this))->InitSerialization(); + initialized = true; + } + + for(unsigned int i=0;iSerialize(buffer); + + this->PostSerialization(); + } + } + + IGL_INLINE void XMLSerializable::Deserialize(const std::vector& buffer) + { + if(this->PreDeserialization()) + { + if(initialized == false) + { + objects.clear(); + (const_cast(this))->InitSerialization(); + initialized = true; + } + + for(unsigned int i=0;iDeserialize(buffer); + + this->PostDeserialization(); + } + } + + IGL_INLINE void XMLSerializable::Serialize(tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element) const + { + if(this->PreSerialization()) + { + if(initialized == false) + { + objects.clear(); + (const_cast(this))->InitSerialization(); + initialized = true; + } + + for(unsigned int i=0;iSerialize(doc,element); + + this->PostSerialization(); + } + } + + IGL_INLINE void XMLSerializable::Deserialize(const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element) + { + if(this->PreDeserialization()) + { + if(initialized == false) + { + objects.clear(); + (const_cast(this))->InitSerialization(); + initialized = true; + } + + for(unsigned int i=0;iDeserialize(doc,element); + + this->PostDeserialization(); + } + } + + IGL_INLINE XMLSerializable::XMLSerializable() + { + initialized = false; + } + + IGL_INLINE XMLSerializable::XMLSerializable(const XMLSerializable& obj) + { + initialized = false; + objects.clear(); + } + + IGL_INLINE XMLSerializable::~XMLSerializable() + { + initialized = false; + objects.clear(); + } + + + IGL_INLINE XMLSerializable& XMLSerializable::operator=(const XMLSerializable& obj) + { + if(this != &obj) + { + if(initialized) + { + initialized = false; + objects.clear(); + } + } + return *this; + } + + template + IGL_INLINE void XMLSerializable::Add(T& obj,std::string name,bool binary) + { + XMLSerializationObject* object = new XMLSerializationObject(); + object->Binary = binary; + object->Name = name; + object->Object = &obj; + + objects.push_back(object); + } + + } +} +#endif diff --git a/src/igl/xml/serialization_test.skip b/src/igl/xml/serialization_test.skip new file mode 100644 index 0000000000..5888075c40 --- /dev/null +++ b/src/igl/xml/serialization_test.skip @@ -0,0 +1,489 @@ +// +// Copyright (C) 2014 Christian Schüller +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +//#ifndef IGL_SERIALIZATION_TEST_H +//#define IGL_SERIALIZATION_TEST_H + +//#include +#include "serialize_xml.h" +#include "XMLSerializable.h" + +namespace igl +{ + namespace xml + { + + struct Test1111 + { + }; + + struct Test1 : public XMLSerializable + { + std::string ts; + std::vector tvt; + Test1* tt; + + Test1() + { + tt = NULL; + } + + void InitSerialization() + { + Add(ts,"ts",false); + Add(tvt,"tvt"); + Add(tt,"tt"); + } + }; + + struct Test2: public XMLSerializableBase + { + char tc; + int* ti; + std::vector tvb; + float tf; + + Test2() + { + tc = '1'; + ti = NULL; + tf = 1.0004; + tvb.push_back(2); + tvb.push_back(3); + } + + void Serialize(tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element) const + { + serialize_xml(tc,"tc",doc,element); + serialize_xml(ti,"ti",doc,element); + serialize_xml(tvb,"tvb",doc,element); + } + void Deserialize(const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element) + { + deserialize_xml(tc,"tc",doc,element); + deserialize_xml(ti,"ti",doc,element); + deserialize_xml(tvb,"tvb",doc,element); + } + void Serialize(std::vector& buffer) const + { + serialize(tc,"tc",buffer); + serialize(ti,"ti",buffer); + serialize(tvb,"tvb",buffer); + serialize(tf,"tf",buffer); + } + void Deserialize(const std::vector& buffer) + { + deserialize(tc,"tc",buffer); + deserialize(ti,"ti",buffer); + deserialize(tvb,"tvb",buffer); + deserialize(tf,"tf",buffer); + } + }; + + void serialization_test() + { + std::string file("test"); + + bool tbIn = true,tbOut; + char tcIn = 't',tcOut; + unsigned char tucIn = 'u',tucOut; + short tsIn = 6,tsOut; + int tiIn = -10,tiOut; + unsigned int tuiIn = 10,tuiOut; + float tfIn = 1.0005,tfOut; + double tdIn = 1.000000005,tdOut; + + int* tinpIn = NULL,*tinpOut = NULL; + float* tfpIn = new float,*tfpOut = NULL; + *tfpIn = 1.11101; + + std::string tstrIn("test12345"),tstrOut; + + Test2 tObjIn,tObjOut; + int ti = 2; + tObjIn.ti = &ti; + + + Test1 test1,test2,test3; + test1.ts = "100"; + test2.ts = "200"; + test3.ts = "300"; + + Test1 testA, testC; + testA.tt = &test1; + testA.ts = "test123"; + testA.tvt.push_back(&test2); + testA.tvt.push_back(&test3); + + Test1 testB = testA; + testB.ts = "400"; + testB.tvt.pop_back(); + + std::pair tPairIn(10,true); + std::pair tPairOut; + + std::vector tVector1In ={1,2,3,4,5}; + std::vector tVector1Out; + + std::pair p1(10,1); + std::pair p2(1,0); + std::pair p3(10000,1); + std::vector > tVector2In ={p1,p2,p3}; + std::vector > tVector2Out; + + std::set > tSetIn ={p1,p2,p3}; + std::set > tSetOut; + + std::map tMapIn ={p1,p2,p3}; + std::map tMapOut; + + Eigen::Matrix tDenseMatrixIn; + tDenseMatrixIn << Eigen::Matrix::Random(); + tDenseMatrixIn.coeffRef(0,0) = 1.00001; + Eigen::Matrix tDenseMatrixOut; + + Eigen::Matrix tDenseRowMatrixIn; + tDenseRowMatrixIn << Eigen::Matrix::Random(); + Eigen::Matrix tDenseRowMatrixOut; + + Eigen::SparseMatrix tSparseMatrixIn; + tSparseMatrixIn.resize(3,3); + tSparseMatrixIn.insert(0,0) = 1.3; + tSparseMatrixIn.insert(1,1) = 10.2; + tSparseMatrixIn.insert(2,2) = 100.1; + tSparseMatrixIn.finalize(); + Eigen::SparseMatrix tSparseMatrixOut; + + // binary serialization + + serialize(tbIn,file); + deserialize(tbOut,file); + assert(tbIn == tbOut); + + serialize(tcIn,file); + deserialize(tcOut,file); + assert(tcIn == tcOut); + + serialize(tucIn,file); + deserialize(tucOut,file); + assert(tucIn == tucOut); + + serialize(tsIn,file); + deserialize(tsOut,file); + assert(tsIn == tsOut); + + serialize(tiIn,file); + deserialize(tiOut,file); + assert(tiIn == tiOut); + + serialize(tuiIn,file); + deserialize(tuiOut,file); + assert(tuiIn == tuiOut); + + serialize(tfIn,file); + deserialize(tfOut,file); + assert(tfIn == tfOut); + + serialize(tdIn,file); + deserialize(tdOut,file); + assert(tdIn == tdOut); + + serialize(tinpIn,file); + deserialize(tinpOut,file); + assert(tinpIn == tinpOut); + + serialize(tfpIn,file); + deserialize(tfpOut,file); + assert(*tfpIn == *tfpOut); + tfpOut = NULL; + + serialize(tstrIn,file); + deserialize(tstrOut,file); + assert(tstrIn == tstrOut); + + // updating + serialize(tbIn,"tb",file,true); + serialize(tcIn,"tc",file); + serialize(tiIn,"ti",file); + tiIn++; + serialize(tiIn,"ti",file); + tiIn++; + serialize(tiIn,"ti",file); + deserialize(tbOut,"tb",file); + deserialize(tcOut,"tc",file); + deserialize(tiOut,"ti",file); + assert(tbIn == tbOut); + assert(tcIn == tcOut); + assert(tiIn == tiOut); + + serialize(tsIn,"tsIn",file,true); + serialize(tVector1In,"tVector1In",file); + serialize(tVector2In,"tsIn",file); + deserialize(tVector2Out,"tsIn",file); + for(unsigned int i=0;its == testC.tvt[i]->ts); + assert(testB.tvt[i]->tvt.size() == testC.tvt[i]->tvt.size()); + assert(testB.tvt[i]->tt == testC.tvt[i]->tt); + } + assert(testB.tt->ts == testC.tt->ts); + assert(testB.tt->tvt.size() == testC.tt->tvt.size()); + assert(testB.tt->tt == testC.tt->tt); + testC = Test1(); + + // big data test + /*std::vector > bigDataIn,bigDataOut; + for(unsigned int i=0;i<10000;i++) + { + std::vector v; + for(unsigned int j=0;j<10000;j++) + { + v.push_back(j); + } + bigDataIn.push_back(v); + } + + Timer timer; + timer.start(); + serialize(bigDataIn,file); + timer.stop(); + std::cout << "ser: " << timer.getElapsedTimeInMilliSec() << std::endl; + + timer.start(); + deserialize(bigDataOut,file); + timer.stop(); + std::cout << "des: " << timer.getElapsedTimeInMilliSec() << std::endl; + char c; + std::cin >> c; */ + + // xml serialization + + serialize_xml(tbIn,file); + deserialize_xml(tbOut,file); + assert(tbIn == tbOut); + + serialize_xml(tcIn,file); + deserialize_xml(tcOut,file); + assert(tcIn == tcOut); + + serialize_xml(tucIn,file); + deserialize_xml(tucOut,file); + assert(tucIn == tucOut); + + serialize_xml(tsIn,file); + deserialize_xml(tsOut,file); + assert(tsIn == tsOut); + + serialize_xml(tiIn,file); + deserialize_xml(tiOut,file); + assert(tiIn == tiOut); + + serialize_xml(tuiIn,file); + deserialize_xml(tuiOut,file); + assert(tuiIn == tuiOut); + + serialize_xml(tfIn,file); + deserialize_xml(tfOut,file); + assert(tfIn == tfOut); + + serialize_xml(tdIn,file); + deserialize_xml(tdOut,file); + assert(tdIn == tdOut); + + serialize_xml(tinpIn,file); + deserialize_xml(tinpOut,file); + assert(tinpIn == tinpOut); + + serialize_xml(tfpIn,file); + deserialize_xml(tfpOut,file); + assert(*tfpIn == *tfpOut); + + serialize_xml(tstrIn,file); + deserialize_xml(tstrOut,file); + assert(tstrIn == tstrOut); + + // updating + serialize_xml(tbIn,"tb",file,false,true); + serialize_xml(tcIn,"tc",file); + serialize_xml(tiIn,"ti",file); + tiIn++; + serialize_xml(tiIn,"ti",file); + tiIn++; + serialize_xml(tiIn,"ti",file); + deserialize_xml(tbOut,"tb",file); + deserialize_xml(tcOut,"tc",file); + deserialize_xml(tiOut,"ti",file); + assert(tbIn == tbOut); + assert(tcIn == tcOut); + assert(tiIn == tiOut); + + serialize_xml(tsIn,"tsIn",file,false,true); + serialize_xml(tVector1In,"tVector1In",file); + serialize_xml(tVector2In,"tsIn",file); + deserialize_xml(tVector2Out,"tsIn",file); + for(unsigned int i=0;its == testC.tvt[i]->ts); + assert(testB.tvt[i]->tvt.size() == testC.tvt[i]->tvt.size()); + assert(testB.tvt[i]->tt == testC.tvt[i]->tt); + } + assert(testB.tt->ts == testC.tt->ts); + assert(testB.tt->tvt.size() == testC.tt->tvt.size()); + assert(testB.tt->tt == testC.tt->tt); + + // big data test + /*std::vector > bigDataIn,bigDataOut; + for(unsigned int i=0;i<10000;i++) + { + std::vector v; + for(unsigned int j=0;j<10000;j++) + { + v.push_back(j); + } + bigDataIn.push_back(v); + } + + Timer timer; + timer.start(); + serialize_xml(bigDataIn,"bigDataIn",file,seRIALIZE_BINARY); + timer.stop(); + std::cout << "ser: " << timer.getElapsedTimeInMilliSec() << std::endl; + + timer.start(); + deserialize_xml(bigDataOut,"bigDataIn",file); + timer.stop(); + std::cout << "des: " << timer.getElapsedTimeInMilliSec() << std::endl; + char c; + std::cin >> c;*/ + + std::cout << "All tests run successfully!\n"; + } + } +} + +//#endif diff --git a/src/igl/xml/serialize_xml.cpp b/src/igl/xml/serialize_xml.cpp new file mode 100644 index 0000000000..ee6ea11bce --- /dev/null +++ b/src/igl/xml/serialize_xml.cpp @@ -0,0 +1,912 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Christian Schüller +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. + +#include "serialize_xml.h" +#include "../STR.h" +#include "../serialize.h" +#include "XMLSerializable.h" + +#include +#include +#include + +namespace igl +{ + namespace xml + { + template + IGL_INLINE void serialize_xml( + const T& obj, + const std::string& filename) + { + serialize_xml(obj,"object",filename,false,true); + } + + template + IGL_INLINE void serialize_xml( + const T& obj, + const std::string& objectName, + const std::string& filename, + bool binary, + bool overwrite) + { + tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument(); + + if(overwrite == false) + { + // Check if file exists + tinyxml2::XMLError error = doc->LoadFile(filename.c_str()); + if(error != tinyxml2::XML_SUCCESS) + { + doc->Clear(); + } + } + + tinyxml2::XMLElement* element = doc->FirstChildElement("serialization"); + if(element == NULL) + { + element = doc->NewElement("serialization"); + doc->InsertEndChild(element); + } + + serialize_xml(obj,objectName,doc,element,binary); + + // Save + tinyxml2::XMLError error = doc->SaveFile(filename.c_str()); + if(error != tinyxml2::XML_SUCCESS) + { + doc->PrintError(); + } + + delete doc; + } + + template + IGL_INLINE void serialize_xml( + const T& obj, + const std::string& objectName, + tinyxml2::XMLDocument* doc, + tinyxml2::XMLElement* element, + bool binary) + { + static_assert( + serialization_xml::is_serializable::value, + "'igl::xml::serialize_xml': type is not serializable"); + + std::string name(objectName); + serialization_xml::encodeXMLElementName(name); + + tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + + if(child != NULL) + element->DeleteChild(child); + + child = doc->NewElement(name.c_str()); + element->InsertEndChild(child); + + if(binary) + { + std::vector buffer; + serialize(obj,name,buffer); + std::string data = + serialization_xml::base64_encode( + reinterpret_cast( + buffer.data()),buffer.size()); + + child->SetAttribute("binary",true); + + serialization_xml::serialize(data,doc,element,name); + } + else + { + serialization_xml::serialize(obj,doc,element,name); + } + } + + template + IGL_INLINE void deserialize_xml(T& obj,const std::string& filename) + { + deserialize_xml(obj,"object",filename); + } + + template + IGL_INLINE void deserialize_xml(T& obj,const std::string& objectName,const std::string& filename) + { + tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument(); + + tinyxml2::XMLError error = doc->LoadFile(filename.c_str()); + if(error != tinyxml2::XML_SUCCESS) + { + std::cerr << "File not found!" << std::endl; + doc->PrintError(); + doc = NULL; + } + else + { + tinyxml2::XMLElement* element = doc->FirstChildElement("serialization"); + if(element == NULL) + { + std::cerr << "Name of object not found! Initialized with default value." << std::endl; + obj = T(); + } + else + { + deserialize_xml(obj,objectName,doc,element); + } + + delete doc; + } + } + + template + IGL_INLINE void deserialize_xml(T& obj,const std::string& objectName,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element) + { + static_assert(serialization::is_serializable::value,"'igl::xml::deserialize_xml': type is not deserializable"); + + std::string name(objectName); + serialization_xml::encodeXMLElementName(name); + + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + if(child != NULL) + { + bool isBinary = false; + const tinyxml2::XMLAttribute* attr = child->FindAttribute("binary"); + if(attr != NULL) + { + std::string code; + serialization_xml::deserialize(code,doc,element,name); + std::string decoded = serialization_xml::base64_decode(code); + + std::vector buffer; + std::copy(decoded.c_str(),decoded.c_str()+decoded.length(),std::back_inserter(buffer)); + + deserialize(obj,name,buffer); + } + else + { + serialization_xml::deserialize(obj,doc,element,name); + } + } + } + + namespace serialization_xml + { + // fundamental types + + template + IGL_INLINE typename std::enable_if::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) + { + tinyxml2::XMLElement* child = getElement(doc,element,name.c_str()); + child->SetAttribute("val",obj); + } + + template + IGL_INLINE typename std::enable_if::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) + { + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + if(child != NULL) + { + getAttribute(child->Attribute("val"),obj); + } + else + { + obj = T(); + } + } + + // std::string + + IGL_INLINE void serialize(const std::string& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) + { + tinyxml2::XMLElement* child = getElement(doc,element,name.c_str()); + child->SetAttribute("val",obj.c_str()); + } + + IGL_INLINE void deserialize(std::string& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) + { + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + if(child != NULL) + { + getAttribute(child->Attribute("val"),obj); + } + else + { + obj = std::string(""); + } + } + + // Serializable + + template + IGL_INLINE typename std::enable_if::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) + { + // Serialize object implementing Serializable interface + const XMLSerializableBase& object = dynamic_cast(obj); + + tinyxml2::XMLElement* child = getElement(doc,element,name.c_str()); + object.Serialize(doc,child); + } + + template + IGL_INLINE typename std::enable_if::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) + { + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + + if(child != NULL) + { + obj.Deserialize(doc,child); + } + else + { + obj = T(); + } + } + + // STL containers + + template + IGL_INLINE void serialize(const std::pair& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) + { + tinyxml2::XMLElement* pair = getElement(doc,element,name.c_str()); + serialize(obj.first,doc,pair,"first"); + serialize(obj.second,doc,pair,"second"); + } + + template + IGL_INLINE void deserialize(std::pair& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) + { + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + if(child != NULL) + { + deserialize(obj.first,doc,child,"first"); + deserialize(obj.second,doc,child,"second"); + } + else + { + obj.first = T1(); + obj.second = T2(); + } + } + + template + IGL_INLINE void serialize(const std::vector& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) + { + tinyxml2::XMLElement* vector = getElement(doc,element,name.c_str()); + vector->SetAttribute("size",(unsigned int)obj.size()); + + std::stringstream num; + for(unsigned int i=0;i + IGL_INLINE void deserialize(std::vector& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) + { + obj.clear(); + + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + if(child != NULL) + { + unsigned int size = child->UnsignedAttribute("size"); + obj.resize(size); + + std::stringstream num; + for(unsigned int i=0;i + IGL_INLINE void serialize(const std::set& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) + { + tinyxml2::XMLElement* set = getElement(doc,element,name.c_str()); + set->SetAttribute("size",(unsigned int)obj.size()); + + std::stringstream num; + typename std::set::iterator iter = obj.begin(); + for(int i=0;iter!=obj.end();iter++,i++) + { + num.str(""); + num << "value" << i; + serialize((T)*iter,doc,set,num.str()); + } + } + + template + IGL_INLINE void deserialize(std::set& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) + { + obj.clear(); + + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + if(child != NULL) + { + unsigned int size = child->UnsignedAttribute("size"); + + std::stringstream num; + typename std::set::iterator iter = obj.begin(); + for(int i=0;i + IGL_INLINE void serialize(const std::map& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) + { + tinyxml2::XMLElement* map = getElement(doc,element,name.c_str()); + map->SetAttribute("size",(unsigned int)obj.size()); + + std::stringstream num; + typename std::map::const_iterator iter = obj.cbegin(); + for(int i=0;iter!=obj.end();iter++,i++) + { + num.str(""); + num << "value" << i; + serialize((std::pair)*iter,doc,map,num.str()); + } + } + + template + IGL_INLINE void deserialize(std::map& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) + { + obj.clear(); + + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + if(child != NULL) + { + unsigned int size = child->UnsignedAttribute("size"); + + std::stringstream num; + typename std::map::iterator iter = obj.begin(); + for(int i=0;i pair; + deserialize(pair,doc,child,num.str()); + obj.insert(pair); + } + } + else + { + obj.clear(); + } + } + + template + IGL_INLINE void serialize( + const Eigen::Matrix& obj, + const std::string& name, + const std::function& to_string, + tinyxml2::XMLDocument* doc, + tinyxml2::XMLElement* element) + { + tinyxml2::XMLElement* matrix = getElement(doc,element,name.c_str()); + + const unsigned int rows = obj.rows(); + const unsigned int cols = obj.cols(); + + matrix->SetAttribute("rows",rows); + matrix->SetAttribute("cols",cols); + + std::stringstream ms; + ms << "\n"; + for(unsigned int r=0;r 1) + mString[mString.size()-2] = '\0'; + + matrix->SetAttribute("matrix",mString.c_str()); + } + + // Eigen types + template + IGL_INLINE void serialize( + const Eigen::Matrix& obj, + tinyxml2::XMLDocument* doc, + tinyxml2::XMLElement* element, + const std::string& name) + { + const std::function to_string = + [](const T & v)->std::string + { + return + STR(std::setprecision(std::numeric_limits::digits10+2)< + IGL_INLINE void deserialize( + const tinyxml2::XMLDocument* doc, + const tinyxml2::XMLElement* element, + const std::string& name, + const std::function & from_string, + Eigen::Matrix& obj) + { + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + bool initialized = false; + if(child != NULL) + { + const unsigned int rows = child->UnsignedAttribute("rows"); + const unsigned int cols = child->UnsignedAttribute("cols"); + + if(rows > 0 && cols > 0) + { + obj.resize(rows,cols); + + const tinyxml2::XMLAttribute* attribute = child->FindAttribute("matrix"); + if(attribute != NULL) + { + std::string matTemp; + getAttribute(attribute->Value(),matTemp); + + std::string line,srows,scols; + std::stringstream mats; + mats << matTemp; + + int r=0; + std::string val; + // for each line + getline(mats,line); + while(getline(mats,line)) + { + // get current line + std::stringstream liness(line); + + for(unsigned int c=0;c(); + } + } + + template + IGL_INLINE void deserialize( + Eigen::Matrix& obj, + const tinyxml2::XMLDocument* doc, + const tinyxml2::XMLElement* element, + const std::string& name) + { + const std::function & from_string = + [](const std::string & s,T & v) + { + getAttribute(s.c_str(),v); + }; + deserialize(doc,element,name,from_string,obj); + } + + template + IGL_INLINE void serialize(const Eigen::SparseMatrix& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) + { + tinyxml2::XMLElement* matrix = getElement(doc,element,name.c_str()); + + const unsigned int rows = obj.rows(); + const unsigned int cols = obj.cols(); + + matrix->SetAttribute("rows",rows); + matrix->SetAttribute("cols",cols); + + char buffer[200]; + std::stringstream ms; + ms << "\n"; + for(int k=0;k::InnerIterator it(obj,k);it;++it) + { + tinyxml2::XMLUtil::ToStr(it.value(),buffer,200); + ms << it.row() << "," << it.col() << "," << buffer << "\n"; + } + } + + std::string mString = ms.str(); + if(mString.size() > 0) + mString[mString.size()-1] = '\0'; + + matrix->SetAttribute("matrix",mString.c_str()); + } + + template + IGL_INLINE void deserialize(Eigen::SparseMatrix& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) + { + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + bool initialized = false; + if(child != NULL) + { + const unsigned int rows = child->UnsignedAttribute("rows"); + const unsigned int cols = child->UnsignedAttribute("cols"); + + if(rows > 0 && cols > 0) + { + obj.resize(rows,cols); + obj.setZero(); + + const tinyxml2::XMLAttribute* attribute = child->FindAttribute("matrix"); + if(attribute != NULL) + { + std::string matTemp; + getAttribute(attribute->Value(),matTemp); + + std::string line,srows,scols; + std::stringstream mats; + mats << matTemp; + + std::vector > triplets; + int r=0; + std::string val; + + // for each line + getline(mats,line); + while(getline(mats,line)) + { + // get current line + std::stringstream liness(line); + + // row + getline(liness,val,','); + int row = atoi(val.c_str()); + // col + getline(liness,val,','); + int col = atoi(val.c_str()); + // val + getline(liness,val); + T value; + getAttribute(val.c_str(),value); + + triplets.push_back(Eigen::Triplet(row,col,value)); + + r++; + } + + obj.setFromTriplets(triplets.begin(),triplets.end()); + initialized = true; + } + } + } + + if(!initialized) + { + obj = Eigen::SparseMatrix(); + } + } + + // pointers + + template + IGL_INLINE typename std::enable_if::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) + { + tinyxml2::XMLElement* pointer = getElement(doc,element,name.c_str()); + + bool isNullPtr = (obj == NULL); + + pointer->SetAttribute("isNullPtr",isNullPtr); + + if(isNullPtr == false) + serialization_xml::serialize(*obj,doc,element,name); + } + + template + IGL_INLINE typename std::enable_if::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) + { + const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + if(child != NULL) + { + bool isNullPtr = child->BoolAttribute("isNullPtr"); + + if(isNullPtr) + { + if(obj != NULL) + { + std::cout << "deserialization: possible memory leak for '" << typeid(obj).name() << "'" << std::endl; + obj = NULL; + } + } + else + { + if(obj != NULL) + std::cout << "deserialization: possible memory leak for '" << typeid(obj).name() << "'" << std::endl; + + obj = new typename std::remove_pointer::type(); + + serialization_xml::deserialize(*obj,doc,element,name); + } + } + } + + // helper functions + + IGL_INLINE tinyxml2::XMLElement* getElement(tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) + { + tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); + if(child == NULL) + { + child = doc->NewElement(name.c_str()); + element->InsertEndChild(child); + } + return child; + } + + IGL_INLINE void getAttribute(const char* src,bool& dest) + { + tinyxml2::XMLUtil::ToBool(src,&dest); + } + + IGL_INLINE void getAttribute(const char* src,char& dest) + { + dest = (char)atoi(src); + } + + IGL_INLINE void getAttribute(const char* src,std::string& dest) + { + dest = src; + } + + IGL_INLINE void getAttribute(const char* src,float& dest) + { + tinyxml2::XMLUtil::ToFloat(src,&dest); + } + + IGL_INLINE void getAttribute(const char* src,double& dest) + { + tinyxml2::XMLUtil::ToDouble(src,&dest); + } + + template + IGL_INLINE typename std::enable_if::value && std::is_unsigned::value>::type getAttribute(const char* src,T& dest) + { + unsigned int val; + tinyxml2::XMLUtil::ToUnsigned(src,&val); + dest = (T)val; + } + + template + IGL_INLINE typename std::enable_if::value && !std::is_unsigned::value>::type getAttribute(const char* src,T& dest) + { + int val; + tinyxml2::XMLUtil::ToInt(src,&val); + dest = (T)val; + } + + // tinyXML2 related stuff + static const int numForbiddenChars = 8; + static const char forbiddenChars[] ={' ','/','~','#','&','>','<','='}; + + IGL_INLINE void replaceSubString(std::string& str,const std::string& search,const std::string& replace) + { + size_t pos = 0; + while((pos = str.find(search,pos)) != std::string::npos) + { + str.replace(pos,search.length(),replace); + pos += replace.length(); + } + } + + IGL_INLINE void encodeXMLElementName(std::string& name) + { + // must not start with a digit + if(isdigit(*name.begin())) + { + name = ":::" + name; + } + + std::stringstream stream; + for(int i=0;i> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + + i = 0; + } + } + + if(i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + } + + return ret; + } + + std::string base64_decode(std::string const& encoded_string) + { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4],char_array_3[3]; + std::string ret; + + // construct fast lookup table + // added by Christian Sch�ller (schuellc@inf.ethz.ch) + int charLookup[200]; + for(int i=0;i<(int)(base64_chars.length());i++) + charLookup[(int)base64_chars[i]] = i; + + while(in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if(i ==4) { + for(i = 0; i <4; i++) + char_array_4[i] = charLookup[char_array_4[i]]; // new fast lookup + //char_array_4[i] = base64_chars.find(char_array_4[i]); // original version + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for(i = 0; (i < 3); i++) + ret += char_array_3[i]; + + i = 0; + } + } + + if(i) { + for(j = i; j <4; j++) + char_array_4[j] = 0; + + for(j = 0; j <4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for(j = 0; (j < i - 1); + j++) ret += char_array_3[j]; + } + + return ret; + } + } + } +} + +#ifdef IGL_STATIC_LIBRARY +// Explicit template instantiation +template void igl::xml::serialize_xml > >(std::vector > const&, std::basic_string, std::allocator > const&, std::basic_string, std::allocator > const&, bool, bool); +template void igl::xml::deserialize_xml > >(std::vector >&, std::basic_string, std::allocator > const&, std::basic_string, std::allocator > const&); +#endif diff --git a/src/igl/xml/serialize_xml.h b/src/igl/xml/serialize_xml.h new file mode 100644 index 0000000000..2190f676a9 --- /dev/null +++ b/src/igl/xml/serialize_xml.h @@ -0,0 +1,247 @@ +// +// Copyright (C) 2014 Christian Sch�ller +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_XML_SERIALIZABLE_XML_H +#define IGL_XML_SERIALIZABLE_XML_H +// ----------------------------------------------------------------------------- +// Functions to save and load a serialization of fundamental c++ data types to +// and from a xml file. STL containers, Eigen matrix types and nested data +// structures are also supported. To serialize a user defined class implement +// the interface XMLSerializable or XMLSerializableBase. +// +// See also: serialize.h +// ----------------------------------------------------------------------------- + +#include "../igl_inline.h" + + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +//#define SERIALIZE_XML(x) igl::xml::serialize_xml(x,#x,doc,element); +//#define DESERIALIZE_XML(x) igl::xml::deserialize_xml(x,#x,,doc,element); + +namespace igl +{ + namespace xml + { + struct XMLSerializableBase; + // serializes the given object either to a xml file or to the provided doc data + // + // Templates: + // T type of the object to serialize + // Inputs: + // obj object to serialize + // objectName unique object name,used for the identification + // filename name of the file containing the serialization + // binary set to true to serialize the object in binary format (faster for big data) + // overwrite set to true to overwrite an existing xml file + // element tinyxml2 virtual representation of the current xml node + // Outputs: + // doc contains current tinyxml2 virtual representation of the xml data + // + template + IGL_INLINE void serialize_xml(const T& obj,const std::string& filename); + template + IGL_INLINE void serialize_xml( + const T& obj, + const std::string& objectName, + const std::string& filename, + bool binary = false, + bool overwrite = false); + template + IGL_INLINE void serialize_xml( + const T& obj, + const std::string& objectName, + tinyxml2::XMLDocument* doc, + tinyxml2::XMLElement* element, + bool binary = false); + + // deserializes the given data from a xml file or doc data back to the provided object + // + // Templates: + // T type of the object to serialize + // Inputs: + // + // objectName unique object name,used for the identification + // filename name of the file containing the serialization + // binary set to true to serialize the object in binary format (faster for big data) + // overwrite set to true to overwrite an existing xml file + // doc contains current tinyxml2 virtual representation of the xml data + // element tinyxml2 virtual representation of the current xml node + // Outputs: + // obj object to load back serialization to + // + template + IGL_INLINE void deserialize_xml(T& obj,const std::string& filename); + template + IGL_INLINE void deserialize_xml(T& obj,const std::string& objectName,const std::string& filename); + template + IGL_INLINE void deserialize_xml(T& obj,const std::string& objectName,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element); + + // internal functions + namespace serialization_xml + { + // fundamental types + template + IGL_INLINE typename std::enable_if::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name); + template + IGL_INLINE typename std::enable_if::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name); + + // std::string + IGL_INLINE void serialize(const std::string& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name); + IGL_INLINE void deserialize(std::string& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name); + + // XMLSerializableBase + template + IGL_INLINE typename std::enable_if::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name); + template + IGL_INLINE typename std::enable_if::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name); + + // STL containers + template + IGL_INLINE void serialize(const std::pair& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name); + template + IGL_INLINE void deserialize(std::pair& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name); + + template + IGL_INLINE void serialize(const std::vector& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name); + template + IGL_INLINE void deserialize(std::vector& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name); + + template + IGL_INLINE void serialize(const std::set& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name); + template + IGL_INLINE void deserialize(std::set& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name); + + template + IGL_INLINE void serialize(const std::map& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name); + template + IGL_INLINE void deserialize(std::map& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name); + + // Eigen types + + // Serialize a Dense Eigen Matrix to xml (in the matrix= attribute, + // awkward...) + // + // Inputs: + // obj MR by MC matrix of T types + // name name of matrix + // to_string function converting T to string + // Outputs: + // doc pointer to xml document + // element pointer to xml element + // + template + IGL_INLINE void serialize( + const Eigen::Matrix& obj, + const std::string& name, + const std::function& to_string, + tinyxml2::XMLDocument* doc, + tinyxml2::XMLElement* element); + // De-Serialize a Dense Eigen Matrix from xml (in the matrix= attribute, + // awkward...) + // + // Inputs: + // doc pointer to xml document + // element pointer to xml element + // name name of matrix + // from_string function string to T + // Outputs: + // obj MR by MC matrix of T types + template + IGL_INLINE void deserialize( + const tinyxml2::XMLDocument* doc, + const tinyxml2::XMLElement* element, + const std::string& name, + const std::function & from_string, + Eigen::Matrix& obj); + + // Legacy APIs + template + IGL_INLINE void serialize( + const Eigen::Matrix& obj, + tinyxml2::XMLDocument* doc, + tinyxml2::XMLElement* element, + const std::string& name); + template + IGL_INLINE void deserialize( + Eigen::Matrix& obj, + const tinyxml2::XMLDocument* doc, + const tinyxml2::XMLElement* element, + const std::string& name); + + template + IGL_INLINE void serialize(const Eigen::SparseMatrix& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name); + template + IGL_INLINE void deserialize(Eigen::SparseMatrix& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name); + + // raw pointers + template + IGL_INLINE typename std::enable_if::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name); + template + IGL_INLINE typename std::enable_if::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name); + + // helper functions + tinyxml2::XMLElement* getElement(tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name); + IGL_INLINE void getAttribute(const char* src,bool& dest); + IGL_INLINE void getAttribute(const char* scr,char& dest); + IGL_INLINE void getAttribute(const char* src,std::string& dest); + IGL_INLINE void getAttribute(const char* src,float& dest); + IGL_INLINE void getAttribute(const char* src,double& dest); + template + IGL_INLINE typename std::enable_if::value && std::is_unsigned::value>::type getAttribute(const char* src,T& dest); + template + IGL_INLINE typename std::enable_if::value && !std::is_unsigned::value>::type getAttribute(const char* src,T& dest); + IGL_INLINE void replaceSubString(std::string& str,const std::string& search,const std::string& replace); + IGL_INLINE void encodeXMLElementName(std::string& name); + IGL_INLINE void decodeXMLElementName(std::string& name); + IGL_INLINE std::string base64_encode(unsigned char const* bytes_to_encode,unsigned int in_len); + IGL_INLINE std::string base64_decode(std::string const& encoded_string); + + // compile time type serializable check + template + struct is_stl_container { static const bool value = false; }; + template + struct is_stl_container > { static const bool value = true; }; + template + struct is_stl_container > { static const bool value = true; }; + template + struct is_stl_container > { static const bool value = true; }; + template + struct is_stl_container > { static const bool value = true; }; + + template + struct is_eigen_type { static const bool value = false; }; + template + struct is_eigen_type > { static const bool value = true; }; + template + struct is_eigen_type > { static const bool value = true; }; + + template + struct is_serializable { + using T0 = typename std::remove_pointer::type; + static const bool value = std::is_fundamental::value || std::is_same::value || std::is_base_of::value + || is_stl_container::value || is_eigen_type::value; + }; + } + } +} + +#ifndef IGL_STATIC_LIBRARY + #include "serialize_xml.cpp" +#endif + +#endif diff --git a/src/igl/xml/writeDAE.cpp b/src/igl/xml/writeDAE.cpp new file mode 100644 index 0000000000..2c2bb43010 --- /dev/null +++ b/src/igl/xml/writeDAE.cpp @@ -0,0 +1,138 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "writeDAE.h" +#include "../STR.h" +#include +#include +#include + +template +IGL_INLINE bool igl::xml::writeDAE( + const std::string & filename, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F) +{ + using namespace std; + using namespace Eigen; + using namespace tinyxml2; + + XMLDocument* doc = new XMLDocument(); + + const auto & ele = [&doc]( + const std::string tag, + // Can't just use `{}` as the default argument because of a clang-bug + // http://stackoverflow.com/questions/17264067/ + const std::map attribs = + std::map(), + const std::string text="", + const std::list children = + std::list() + )->XMLElement * + { + XMLElement * element = doc->NewElement(tag.c_str()); + for(const auto & key_value : attribs) + { + element->SetAttribute(key_value.first.c_str(),key_value.second.c_str()); + } + if(!text.empty()) + { + element->InsertEndChild(doc->NewText(text.c_str())); + } + for(auto & child : children) + { + element->InsertEndChild(child); + } + return element; + }; + + Eigen::IOFormat row_format(Eigen::FullPrecision,0," "," ","","",""); + doc->InsertEndChild( + ele("COLLADA", + { + {"xmlns","http://www.collada.org/2005/11/COLLADASchema"}, + {"version","1.4.1"} + }, + "", + { + ele("asset",{},"", + { + ele("unit",{{"meter","0.0254000"},{"name","inch"}}), + ele("up_axis",{},"Y_UP") + }), + ele("library_visual_scenes",{},"", + { + ele("visual_scene",{{"id","ID2"}},"", + { + ele("node",{{"name","SketchUp"}},"", + { + ele("node",{{"id","ID3"},{"name","group_0"}},"", + { + ele("matrix",{},"1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"), + ele("instance_geometry",{{"url","#ID4"}},"", + { + ele("bind_material",{},"",{ele("technique_common")}), + }), + }), + }), + }), + }), + ele("library_geometries",{},"", + { + ele("geometry",{{"id","ID4"}},"", + { + ele("mesh",{},"", + { + ele("source",{{"id","ID7"}},"", + { + ele( + "float_array", + {{"count",STR(V.size())},{"id","ID10"}}, + STR(V.format(row_format))), + ele("technique_common",{},"", + { + ele( + "accessor", + {{"count",STR(V.rows())},{"source","#ID8"},{"stride","3"}}, + "", + { + ele("param",{{"name","X"},{"type","float"}}), + ele("param",{{"name","Y"},{"type","float"}}), + ele("param",{{"name","Z"},{"type","float"}}), + }) + }) + }), + ele( + "vertices", + {{"id","ID9"}}, + "", + {ele("input",{{"semantic","POSITION"},{"source","#ID7"}})}), + ele( + "triangles", + {{"count",STR(F.rows())}}, + "", + { + ele("input",{{"semantic","VERTEX"},{"source","#ID9"}}), + ele("p",{},STR(F.format(row_format))), + }) + }) + }) + }), + ele("scene",{},"",{ele("instance_visual_scene",{{"url","#ID2"}})}), + })); + // tinyxml2 seems **not** to print the header by default, but it + // also seems that that's OK + XMLError error = doc->SaveFile(filename.c_str()); + bool ret = true; + if(error != XML_NO_ERROR) + { + doc->PrintError(); + ret = false; + } + delete doc; + return ret; +} diff --git a/src/igl/xml/writeDAE.h b/src/igl/xml/writeDAE.h new file mode 100644 index 0000000000..731fe7255b --- /dev/null +++ b/src/igl/xml/writeDAE.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_XML_WRITEDAE_H +#define IGL_XML_WRITEDAE_H +#include "../igl_inline.h" +#include +#include +namespace igl +{ + namespace xml + { + // Write a mesh to a Collada .dae scene file. The resulting scene contains + // a single "geometry" suitable for solid operaions (boolean union, + // intersection, etc.) in SketchUp. + // + // Inputs: + // filename path to .dae file + // V #V by 3 list of vertex positions + // F #F by 3 list of face indices + // Returns true iff success + // + template + IGL_INLINE bool writeDAE( + const std::string & filename, + const Eigen::PlainObjectBase & V, + const Eigen::PlainObjectBase & F); + } +} + +#ifndef IGL_STATIC_LIBRARY +#include "writeDAE.cpp" +#endif +#endif diff --git a/src/igl/xml/write_triangle_mesh.cpp b/src/igl/xml/write_triangle_mesh.cpp new file mode 100644 index 0000000000..1f3d54b8d0 --- /dev/null +++ b/src/igl/xml/write_triangle_mesh.cpp @@ -0,0 +1,33 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "write_triangle_mesh.h" +#include "../write_triangle_mesh.h" +#include "../pathinfo.h" +#include "writeDAE.h" + +template +IGL_INLINE bool igl::xml::write_triangle_mesh( + const std::string str, + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const bool ascii) +{ + using namespace std; + // dirname, basename, extension and filename + string d,b,e,f; + pathinfo(str,d,b,e,f); + // Convert extension to lower case + std::transform(e.begin(), e.end(), e.begin(), ::tolower); + if(e == "dae") + { + return writeDAE(str,V,F); + }else + { + return igl::write_triangle_mesh(str,V,F,ascii); + } +} diff --git a/src/igl/xml/write_triangle_mesh.h b/src/igl/xml/write_triangle_mesh.h new file mode 100644 index 0000000000..a5f8dad69b --- /dev/null +++ b/src/igl/xml/write_triangle_mesh.h @@ -0,0 +1,45 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2016 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_XML_WRITE_TRIANGLE_MESH_H +#define IGL_XML_WRITE_TRIANGLE_MESH_H +#include "../igl_inline.h" + +#include +#include + +namespace igl +{ + namespace xml + { + // write mesh to a file with automatic detection of file format. supported: + // dae, or any of the formats supported by igl::write_triangle_mesh + // + // Templates: + // Scalar type for positions and vectors (will be read as double and cast + // to Scalar) + // Index type for indices (will be read as int and cast to Index) + // Inputs: + // str path to file + // V eigen double matrix #V by 3 + // F eigen int matrix #F by 3 + // Returns true iff success + template + IGL_INLINE bool write_triangle_mesh( + const std::string str, + const Eigen::PlainObjectBase& V, + const Eigen::PlainObjectBase& F, + const bool ascii = true); + } +} + +#ifndef IGL_STATIC_LIBRARY +# include "write_triangle_mesh.cpp" +#endif + +#endif + diff --git a/src/libnest2d/CMakeLists.txt b/src/libnest2d/CMakeLists.txt new file mode 100644 index 0000000000..163143bcbf --- /dev/null +++ b/src/libnest2d/CMakeLists.txt @@ -0,0 +1,122 @@ +cmake_minimum_required(VERSION 3.0) + +project(Libnest2D) + +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + # Update if necessary + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long ") +endif() + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED) + +# Add our own cmake module path. +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/) + +option(LIBNEST2D_UNITTESTS "If enabled, googletest framework will be downloaded + and the provided unit tests will be included in the build." OFF) + +option(LIBNEST2D_BUILD_EXAMPLES "If enabled, examples will be built." OFF) + +option(LIBNEST2D_HEADER_ONLY "If enabled static library will not be built." ON) + +set(GEOMETRY_BACKENDS clipper boost eigen) +set(LIBNEST2D_GEOMETRIES clipper CACHE STRING "Geometry backend") +set_property(CACHE LIBNEST2D_GEOMETRIES PROPERTY STRINGS ${GEOMETRY_BACKENDS}) +list(FIND GEOMETRY_BACKENDS ${LIBNEST2D_GEOMETRIES} GEOMETRY_TYPE) +if(${GEOMETRY_TYPE} EQUAL -1) + message(FATAL_ERROR "Option ${LIBNEST2D_GEOMETRIES} not supported, valid entries are ${GEOMETRY_BACKENDS}") +endif() + +set(OPTIMIZERS nlopt optimlib) +set(LIBNEST2D_OPTIMIZER nlopt CACHE STRING "Optimization backend") +set_property(CACHE LIBNEST2D_OPTIMIZER PROPERTY STRINGS ${OPTIMIZERS}) +list(FIND OPTIMIZERS ${LIBNEST2D_OPTIMIZER} OPTIMIZER_TYPE) +if(${OPTIMIZER_TYPE} EQUAL -1) + message(FATAL_ERROR "Option ${LIBNEST2D_OPTIMIZER} not supported, valid entries are ${OPTIMIZERS}") +endif() + +add_library(libnest2d INTERFACE) + +set(SRC_DIR ${PROJECT_SOURCE_DIR}/include) + +set(LIBNEST2D_SRCFILES + ${SRC_DIR}/libnest2d/libnest2d.hpp # Templates only + ${SRC_DIR}/libnest2d/geometry_traits.hpp + ${SRC_DIR}/libnest2d/geometry_traits_nfp.hpp + ${SRC_DIR}/libnest2d/common.hpp + ${SRC_DIR}/libnest2d/optimizer.hpp + ${SRC_DIR}/libnest2d/utils/metaloop.hpp + ${SRC_DIR}/libnest2d/utils/rotfinder.hpp + ${SRC_DIR}/libnest2d/placers/placer_boilerplate.hpp + ${SRC_DIR}/libnest2d/placers/bottomleftplacer.hpp + ${SRC_DIR}/libnest2d/placers/nfpplacer.hpp + ${SRC_DIR}/libnest2d/selections/selection_boilerplate.hpp + ${SRC_DIR}/libnest2d/selections/filler.hpp + ${SRC_DIR}/libnest2d/selections/firstfit.hpp + ${SRC_DIR}/libnest2d/selections/djd_heuristic.hpp + ) + +set(TBB_STATIC ON) +find_package(TBB QUIET) +if(TBB_FOUND) + message(STATUS "Parallelization with Intel TBB") + target_include_directories(libnest2d INTERFACE ${TBB_INCLUDE_DIRS}) + target_compile_definitions(libnest2d INTERFACE ${TBB_DEFINITIONS} -DUSE_TBB) + if(MSVC) + # Suppress implicit linking of the TBB libraries by the Visual Studio compiler. + target_compile_definitions(libnest2d INTERFACE -D__TBB_NO_IMPLICIT_LINKAGE) + endif() + # The Intel TBB library will use the std::exception_ptr feature of C++11. + target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=1) + + target_link_libraries(libnest2d INTERFACE tbb) +else() + find_package(OpenMP QUIET) + + if(OpenMP_CXX_FOUND) + message(STATUS "Parallelization with OpenMP") + target_include_directories(libnest2d INTERFACE OpenMP::OpenMP_CXX) + target_link_libraries(libnest2d INTERFACE OpenMP::OpenMP_CXX) + else() + message("Parallelization with C++11 threads") + find_package(Threads REQUIRED) + target_link_libraries(libnest2d INTERFACE Threads::Threads) + endif() +endif() + +add_subdirectory(${SRC_DIR}/libnest2d/backends/${LIBNEST2D_GEOMETRIES}) +add_subdirectory(${SRC_DIR}/libnest2d/optimizers/${LIBNEST2D_OPTIMIZER}) + +#target_sources(libnest2d INTERFACE ${LIBNEST2D_SRCFILES}) +target_include_directories(libnest2d INTERFACE ${SRC_DIR}) + +if(NOT LIBNEST2D_HEADER_ONLY) + set(LIBNAME libnest2d_${LIBNEST2D_GEOMETRIES}_${LIBNEST2D_OPTIMIZER}) + add_library(${LIBNAME} ${PROJECT_SOURCE_DIR}/src/libnest2d.cpp) + target_link_libraries(${LIBNAME} PUBLIC libnest2d) + target_compile_definitions(${LIBNAME} PUBLIC LIBNEST2D_STATIC) +endif() + +if(LIBNEST2D_BUILD_EXAMPLES) + + add_executable(example examples/main.cpp + # tools/libnfpglue.hpp + # tools/libnfpglue.cpp + tools/nfp_svgnest.hpp + tools/nfp_svgnest_glue.hpp + tools/svgtools.hpp + tests/printer_parts.cpp + tests/printer_parts.h + ) + + if(NOT LIBNEST2D_HEADER_ONLY) + target_link_libraries(example ${LIBNAME}) + else() + target_link_libraries(example libnest2d) + endif() +endif() + +if(LIBNEST2D_UNITTESTS) + add_subdirectory(${PROJECT_SOURCE_DIR}/tests) +endif() diff --git a/xs/src/libnest2d/LICENSE.txt b/src/libnest2d/LICENSE.txt similarity index 100% rename from xs/src/libnest2d/LICENSE.txt rename to src/libnest2d/LICENSE.txt diff --git a/xs/src/libnest2d/cmake_modules/DownloadNLopt.cmake b/src/libnest2d/cmake_modules/DownloadNLopt.cmake similarity index 80% rename from xs/src/libnest2d/cmake_modules/DownloadNLopt.cmake rename to src/libnest2d/cmake_modules/DownloadNLopt.cmake index 0f5392596e..62b2b4c1a9 100644 --- a/xs/src/libnest2d/cmake_modules/DownloadNLopt.cmake +++ b/src/libnest2d/cmake_modules/DownloadNLopt.cmake @@ -6,11 +6,14 @@ else() set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") endif() +set(URL_NLOPT "https://github.com/stevengj/nlopt.git" + CACHE STRING "Location of the nlopt git repository") + # set(NLopt_DIR ${CMAKE_BINARY_DIR}/nlopt) include(DownloadProject) download_project( PROJ nlopt - GIT_REPOSITORY https://github.com/stevengj/nlopt.git - GIT_TAG 1fcbcbf2fe8e34234e016cc43a6c41d3e8453e1f #master #nlopt-2.4.2 + GIT_REPOSITORY ${URL_NLOPT} + GIT_TAG v2.5.0 # CMAKE_CACHE_ARGS -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${NLopt_DIR} ${UPDATE_DISCONNECTED_IF_AVAILABLE} ) diff --git a/xs/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in b/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in similarity index 100% rename from xs/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in rename to src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in diff --git a/xs/src/libnest2d/cmake_modules/DownloadProject.cmake b/src/libnest2d/cmake_modules/DownloadProject.cmake similarity index 100% rename from xs/src/libnest2d/cmake_modules/DownloadProject.cmake rename to src/libnest2d/cmake_modules/DownloadProject.cmake diff --git a/xs/src/libnest2d/cmake_modules/FindClipper.cmake b/src/libnest2d/cmake_modules/FindClipper.cmake similarity index 76% rename from xs/src/libnest2d/cmake_modules/FindClipper.cmake rename to src/libnest2d/cmake_modules/FindClipper.cmake index f6b9734409..01b6b99d59 100644 --- a/xs/src/libnest2d/cmake_modules/FindClipper.cmake +++ b/src/libnest2d/cmake_modules/FindClipper.cmake @@ -47,4 +47,12 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(Clipper MARK_AS_ADVANCED( CLIPPER_INCLUDE_DIRS - CLIPPER_LIBRARIES) \ No newline at end of file + CLIPPER_LIBRARIES) + +if(CLIPPER_FOUND) + add_library(Clipper::Clipper INTERFACE IMPORTED) + set_target_properties(Clipper::Clipper PROPERTIES INTERFACE_LINK_LIBRARIES ${CLIPPER_LIBRARIES}) + set_target_properties(Clipper::Clipper PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CLIPPER_INCLUDE_DIRS}) + #target_link_libraries(Clipper::Clipper INTERFACE ${CLIPPER_LIBRARIES}) + #target_include_directories(Clipper::Clipper INTERFACE ${CLIPPER_INCLUDE_DIRS}) +endif() diff --git a/xs/src/libnest2d/cmake_modules/FindNLopt.cmake b/src/libnest2d/cmake_modules/FindNLopt.cmake similarity index 86% rename from xs/src/libnest2d/cmake_modules/FindNLopt.cmake rename to src/libnest2d/cmake_modules/FindNLopt.cmake index 4b93be7b66..2f813b6aab 100644 --- a/xs/src/libnest2d/cmake_modules/FindNLopt.cmake +++ b/src/libnest2d/cmake_modules/FindNLopt.cmake @@ -114,6 +114,13 @@ if(NLopt_FOUND) message(STATUS "Found NLopt in '${NLopt_DIR}'.") message(STATUS "Using NLopt include directory '${NLopt_INCLUDE_DIR}'.") message(STATUS "Using NLopt library '${NLopt_LIBS}'.") + add_library(Nlopt::Nlopt INTERFACE IMPORTED) + set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_LINK_LIBRARIES ${NLopt_LIBS}) + set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${NLopt_INCLUDE_DIR}) + set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${NLopt_DEFINITIONS}") + # target_link_libraries(Nlopt::Nlopt INTERFACE ${NLopt_LIBS}) + # target_include_directories(Nlopt::Nlopt INTERFACE ${NLopt_INCLUDE_DIR}) + # target_compile_definitions(Nlopt::Nlopt INTERFACE ${NLopt_DEFINITIONS}) else() if(NLopt_FIND_REQUIRED) message(FATAL_ERROR "Unable to find requested NLopt installation:${NLopt_ERROR_REASON}") @@ -122,4 +129,4 @@ else() message(STATUS "NLopt was not found:${NLopt_ERROR_REASON}") endif() endif() -endif() \ No newline at end of file +endif() diff --git a/src/libnest2d/include/libnest2d.h b/src/libnest2d/include/libnest2d.h new file mode 100644 index 0000000000..4ad7524219 --- /dev/null +++ b/src/libnest2d/include/libnest2d.h @@ -0,0 +1,175 @@ +#ifndef LIBNEST2D_H +#define LIBNEST2D_H + +// The type of backend should be set conditionally by the cmake configuriation +// for now we set it statically to clipper backend +#ifdef LIBNEST2D_BACKEND_CLIPPER +#include +#endif + +#ifdef LIBNEST2D_OPTIMIZER_NLOPT +// We include the stock optimizers for local and global optimization +#include // Local subplex for NfpPlacer +#include // Genetic for min. bounding box +#endif + +#include +#include +#include +#include +#include +#include + +namespace libnest2d { + +using Point = PointImpl; +using Coord = TCoord; +using Box = _Box; +using Segment = _Segment; +using Circle = _Circle; + +using Item = _Item; +using Rectangle = _Rectangle; + +using PackGroup = _PackGroup; +using IndexedPackGroup = _IndexedPackGroup; + +using FillerSelection = selections::_FillerSelection; +using FirstFitSelection = selections::_FirstFitSelection; +using DJDHeuristic = selections::_DJDHeuristic; + +template // Generic placer for arbitrary bin types +using _NfpPlacer = placers::_NofitPolyPlacer; + +// NfpPlacer is with Box bin +using NfpPlacer = _NfpPlacer; + +// This supports only box shaped bins +using BottomLeftPlacer = placers::_BottomLeftPlacer; + +template::iterator> +PackGroup nest(Iterator from, Iterator to, + const typename Placer::BinType& bin, + Coord dist = 0, + const typename Placer::Config& pconf = {}, + const typename Selector::Config& sconf = {}) +{ + Nester nester(bin, dist, pconf, sconf); + return nester.execute(from, to); +} + +template> +PackGroup nest(Container&& cont, + const typename Placer::BinType& bin, + Coord dist = 0, + const typename Placer::Config& pconf = {}, + const typename Selector::Config& sconf = {}) +{ + return nest(cont.begin(), cont.end(), + bin, dist, pconf, sconf); +} + +template::iterator> +PackGroup nest(Iterator from, Iterator to, + const typename Placer::BinType& bin, + ProgressFunction prg, + StopCondition scond = []() { return false; }, + Coord dist = 0, + const typename Placer::Config& pconf = {}, + const typename Selector::Config& sconf = {}) +{ + Nester nester(bin, dist, pconf, sconf); + if(prg) nester.progressIndicator(prg); + if(scond) nester.stopCondition(scond); + return nester.execute(from, to); +} + +template> +PackGroup nest(Container&& cont, + const typename Placer::BinType& bin, + ProgressFunction prg, + StopCondition scond = []() { return false; }, + Coord dist = 0, + const typename Placer::Config& pconf = {}, + const typename Selector::Config& sconf = {}) +{ + return nest(cont.begin(), cont.end(), + bin, prg, scond, dist, pconf, sconf); +} + +#ifdef LIBNEST2D_STATIC +extern template +PackGroup nest&>( + std::vector& cont, + const Box& bin, + Coord dist, + const NfpPlacer::Config& pcfg, + const FirstFitSelection::Config& scfg +); + +extern template +PackGroup nest&>( + std::vector& cont, + const Box& bin, + ProgressFunction prg, + StopCondition scond, + Coord dist, + const NfpPlacer::Config& pcfg, + const FirstFitSelection::Config& scfg +); + +extern template +PackGroup nest>( + std::vector&& cont, + const Box& bin, + Coord dist, + const NfpPlacer::Config& pcfg, + const FirstFitSelection::Config& scfg +); + +extern template +PackGroup nest>( + std::vector&& cont, + const Box& bin, + ProgressFunction prg, + StopCondition scond, + Coord dist, + const NfpPlacer::Config& pcfg, + const FirstFitSelection::Config& scfg +); + +extern template +PackGroup nest::iterator>( + std::vector::iterator from, + std::vector::iterator to, + const Box& bin, + Coord dist, + const NfpPlacer::Config& pcfg, + const FirstFitSelection::Config& scfg +); + +extern template +PackGroup nest::iterator>( + std::vector::iterator from, + std::vector::iterator to, + const Box& bin, + ProgressFunction prg, + StopCondition scond, + Coord dist, + const NfpPlacer::Config& pcfg, + const FirstFitSelection::Config& scfg +); + +#endif + +} + +#endif // LIBNEST2D_H diff --git a/xs/src/libnest2d/libnest2d/clipper_backend/CMakeLists.txt b/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt similarity index 55% rename from xs/src/libnest2d/libnest2d/clipper_backend/CMakeLists.txt rename to src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt index b6f2de4397..aa53f957e5 100644 --- a/xs/src/libnest2d/libnest2d/clipper_backend/CMakeLists.txt +++ b/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt @@ -5,6 +5,10 @@ if(NOT TARGET clipper) # If there is a clipper target in the parent project we a if(NOT CLIPPER_FOUND) find_package(Subversion QUIET) if(Subversion_FOUND) + + set(URL_CLIPPER "https://svn.code.sf.net/p/polyclipping/code/trunk/cpp" + CACHE STRING "Clipper source code repository location.") + message(STATUS "Clipper not found so it will be downloaded.") # Silently download and build the library in the build dir @@ -16,7 +20,7 @@ if(NOT TARGET clipper) # If there is a clipper target in the parent project we a include(DownloadProject) download_project( PROJ clipper_library - SVN_REPOSITORY https://svn.code.sf.net/p/polyclipping/code/trunk/cpp + SVN_REPOSITORY ${URL_CLIPPER} SVN_REVISION -r540 #SOURCE_SUBDIR cpp INSTALL_COMMAND "" @@ -29,20 +33,41 @@ if(NOT TARGET clipper) # If there is a clipper target in the parent project we a # ${clipper_library_BINARY_DIR} # ) - add_library(clipper_lib STATIC + add_library(ClipperBackend STATIC ${clipper_library_SOURCE_DIR}/clipper.cpp ${clipper_library_SOURCE_DIR}/clipper.hpp) - set(CLIPPER_INCLUDE_DIRS ${clipper_library_SOURCE_DIR} - PARENT_SCOPE) - - set(CLIPPER_LIBRARIES clipper_lib PARENT_SCOPE) - + target_include_directories(ClipperBackend INTERFACE + ${clipper_library_SOURCE_DIR}) else() message(FATAL_ERROR "Can't find clipper library and no SVN client found to download. You can download the clipper sources and define a clipper target in your project, that will work for libnest2d.") endif() + else() + add_library(ClipperBackend INTERFACE) + target_link_libraries(ClipperBackend INTERFACE Clipper::Clipper) endif() else() - set(CLIPPER_LIBRARIES clipper PARENT_SCOPE) + # set(CLIPPER_INCLUDE_DIRS "" PARENT_SCOPE) + # set(CLIPPER_LIBRARIES clipper PARENT_SCOPE) + add_library(ClipperBackend INTERFACE) + target_link_libraries(ClipperBackend INTERFACE clipper) endif() + +# Clipper backend is not enough on its own, it still needs some functions +# from Boost geometry +if(NOT Boost_INCLUDE_DIRS_FOUND) + find_package(Boost 1.58 REQUIRED) + # TODO automatic download of boost geometry headers +endif() + +target_include_directories(ClipperBackend INTERFACE ${Boost_INCLUDE_DIRS} ) +#target_sources(ClipperBackend INTERFACE +# ${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp +# ${SRC_DIR}/libnest2d/utils/boost_alg.hpp ) + +target_compile_definitions(ClipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER) + +# And finally plug the ClipperBackend into libnest2d +target_link_libraries(libnest2d INTERFACE ClipperBackend) + diff --git a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp similarity index 95% rename from xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp rename to src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index 745fd2108d..c05d08d0dd 100644 --- a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -7,8 +7,8 @@ #include #include -#include "../geometry_traits.hpp" -#include "../geometry_traits_nfp.hpp" +#include +#include #include @@ -99,6 +99,10 @@ template<> struct PointType { using Type = PointImpl; }; +template<> struct PointType { + using Type = PointImpl; +}; + template<> struct PointType { using Type = PointImpl; }; @@ -108,6 +112,7 @@ template<> struct CountourType { }; template<> struct ShapeTag { using Type = PolygonTag; }; +template<> struct ShapeTag { using Type = PathTag; }; template<> struct ShapeTag> { using Type = MultiPolygonTag; @@ -185,11 +190,6 @@ inline double area(const PolygonImpl& sh) { namespace shapelike { -template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity) -{ - return sh.Contour.reserve(vertex_capacity); -} - // Tell libnest2d how to make string out of a ClipperPolygon object template<> inline double area(const PolygonImpl& sh, const PolygonTag&) { @@ -327,13 +327,13 @@ template<> inline THolesContainer& holes(PolygonImpl& sh) } template<> -inline TContour& getHole(PolygonImpl& sh, unsigned long idx) +inline TContour& hole(PolygonImpl& sh, unsigned long idx) { return sh.Holes[idx]; } template<> -inline const TContour& getHole(const PolygonImpl& sh, +inline const TContour& hole(const PolygonImpl& sh, unsigned long idx) { return sh.Holes[idx]; @@ -344,13 +344,13 @@ template<> inline size_t holeCount(const PolygonImpl& sh) return sh.Holes.size(); } -template<> inline PathImpl& getContour(PolygonImpl& sh) +template<> inline PathImpl& contour(PolygonImpl& sh) { return sh.Contour; } template<> -inline const PathImpl& getContour(const PolygonImpl& sh) +inline const PathImpl& contour(const PolygonImpl& sh) { return sh.Contour; } @@ -455,6 +455,6 @@ merge(const std::vector& shapes) //#define DISABLE_BOOST_UNSERIALIZE // All other operators and algorithms are implemented with boost -#include "../boost_alg.hpp" +#include #endif // CLIPPER_BACKEND_HPP diff --git a/xs/src/libnest2d/libnest2d/common.hpp b/src/libnest2d/include/libnest2d/common.hpp similarity index 100% rename from xs/src/libnest2d/libnest2d/common.hpp rename to src/libnest2d/include/libnest2d/common.hpp diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp new file mode 100644 index 0000000000..828044afe0 --- /dev/null +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -0,0 +1,965 @@ +#ifndef GEOMETRY_TRAITS_HPP +#define GEOMETRY_TRAITS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.hpp" + +namespace libnest2d { + +/// Getting the coordinate data type for a geometry class. +template struct CoordType { using Type = long; }; + +/// TCoord as shorthand for typename `CoordType::Type`. +template +using TCoord = typename CoordType>::Type; + + +/// Getting the type of point structure used by a shape. +template struct PointType { using Type = typename Sh::PointType; }; + +/// TPoint as shorthand for `typename PointType::Type`. +template +using TPoint = typename PointType>::Type; + + +template struct CountourType { using Type = RawShape; }; + +template +using TContour = typename CountourType>::Type; + + +template +struct HolesContainer { using Type = std::vector>; }; + +template +using THolesContainer = typename HolesContainer>::Type; + + +template +struct LastPointIsFirst { static const bool Value = true; }; + +enum class Orientation { + CLOCKWISE, + COUNTER_CLOCKWISE +}; + +template +struct OrientationType { + + // Default Polygon orientation that the library expects + static const Orientation Value = Orientation::CLOCKWISE; +}; + +/** + * \brief A point pair base class for other point pairs (segment, box, ...). + * \tparam RawPoint The actual point type to use. + */ +template +struct PointPair { + RawPoint p1; + RawPoint p2; +}; + +struct PolygonTag {}; +struct PathTag {}; +struct MultiPolygonTag {}; +struct BoxTag {}; +struct CircleTag {}; + +template struct ShapeTag { using Type = typename Shape::Tag; }; +template using Tag = typename ShapeTag>::Type; + +template struct MultiShape { using Type = std::vector; }; +template +using TMultiShape =typename MultiShape>::Type; + +/** + * \brief An abstraction of a box; + */ +template +class _Box: PointPair { + using PointPair::p1; + using PointPair::p2; +public: + + using Tag = BoxTag; + using PointType = RawPoint; + + inline _Box() = default; + inline _Box(const RawPoint& p, const RawPoint& pp): + PointPair({p, pp}) {} + + inline _Box(TCoord width, TCoord height): + _Box(RawPoint{0, 0}, RawPoint{width, height}) {} + + inline const RawPoint& minCorner() const BP2D_NOEXCEPT { return p1; } + inline const RawPoint& maxCorner() const BP2D_NOEXCEPT { return p2; } + + inline RawPoint& minCorner() BP2D_NOEXCEPT { return p1; } + inline RawPoint& maxCorner() BP2D_NOEXCEPT { return p2; } + + inline TCoord width() const BP2D_NOEXCEPT; + inline TCoord height() const BP2D_NOEXCEPT; + + inline RawPoint center() const BP2D_NOEXCEPT; + + inline double area() const BP2D_NOEXCEPT { + return double(width()*height()); + } +}; + +template +class _Circle { + RawPoint center_; + double radius_ = 0; +public: + + using Tag = CircleTag; + using PointType = RawPoint; + + _Circle() = default; + + _Circle(const RawPoint& center, double r): center_(center), radius_(r) {} + + inline const RawPoint& center() const BP2D_NOEXCEPT { return center_; } + inline const void center(const RawPoint& c) { center_ = c; } + + inline double radius() const BP2D_NOEXCEPT { return radius_; } + inline void radius(double r) { radius_ = r; } + + inline double area() const BP2D_NOEXCEPT { + return 2.0*Pi*radius_*radius_; + } +}; + +/** + * \brief An abstraction of a directed line segment with two points. + */ +template +class _Segment: PointPair { + using PointPair::p1; + using PointPair::p2; + mutable Radians angletox_ = std::nan(""); +public: + + using PointType = RawPoint; + + inline _Segment() = default; + + inline _Segment(const RawPoint& p, const RawPoint& pp): + PointPair({p, pp}) {} + + /** + * @brief Get the first point. + * @return Returns the starting point. + */ + inline const RawPoint& first() const BP2D_NOEXCEPT { return p1; } + + /** + * @brief The end point. + * @return Returns the end point of the segment. + */ + inline const RawPoint& second() const BP2D_NOEXCEPT { return p2; } + + inline void first(const RawPoint& p) BP2D_NOEXCEPT + { + angletox_ = std::nan(""); p1 = p; + } + + inline void second(const RawPoint& p) BP2D_NOEXCEPT { + angletox_ = std::nan(""); p2 = p; + } + + /// Returns the angle measured to the X (horizontal) axis. + inline Radians angleToXaxis() const; + + /// The length of the segment in the measure of the coordinate system. + inline double length(); +}; + +// This struct serves almost as a namespace. The only difference is that is can +// used in friend declarations. +namespace pointlike { + +template +inline TCoord x(const RawPoint& p) +{ + return p.x(); +} + +template +inline TCoord y(const RawPoint& p) +{ + return p.y(); +} + +template +inline TCoord& x(RawPoint& p) +{ + return p.x(); +} + +template +inline TCoord& y(RawPoint& p) +{ + return p.y(); +} + +template +inline double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/) +{ + static_assert(always_false::value, + "PointLike::distance(point, point) unimplemented!"); + return 0; +} + +template +inline double distance(const RawPoint& /*p1*/, + const _Segment& /*s*/) +{ + static_assert(always_false::value, + "PointLike::distance(point, segment) unimplemented!"); + return 0; +} + +template +inline std::pair, bool> horizontalDistance( + const RawPoint& p, const _Segment& s) +{ + using Unit = TCoord; + auto x = pointlike::x(p), y = pointlike::y(p); + auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first()); + auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second()); + + TCoord ret; + + if( (y < y1 && y < y2) || (y > y1 && y > y2) ) + return {0, false}; + if ((y == y1 && y == y2) && (x > x1 && x > x2)) + ret = std::min( x-x1, x -x2); + else if( (y == y1 && y == y2) && (x < x1 && x < x2)) + ret = -std::min(x1 - x, x2 - x); + else if(std::abs(y - y1) <= std::numeric_limits::epsilon() && + std::abs(y - y2) <= std::numeric_limits::epsilon()) + ret = 0; + else + ret = x - x1 + (x1 - x2)*(y1 - y)/(y1 - y2); + + return {ret, true}; +} + +template +inline std::pair, bool> verticalDistance( + const RawPoint& p, const _Segment& s) +{ + using Unit = TCoord; + auto x = pointlike::x(p), y = pointlike::y(p); + auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first()); + auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second()); + + TCoord ret; + + if( (x < x1 && x < x2) || (x > x1 && x > x2) ) + return {0, false}; + if ((x == x1 && x == x2) && (y > y1 && y > y2)) + ret = std::min( y-y1, y -y2); + else if( (x == x1 && x == x2) && (y < y1 && y < y2)) + ret = -std::min(y1 - y, y2 - y); + else if(std::abs(x - x1) <= std::numeric_limits::epsilon() && + std::abs(x - x2) <= std::numeric_limits::epsilon()) + ret = 0; + else + ret = y - y1 + (y1 - y2)*(x1 - x)/(x1 - x2); + + return {ret, true}; +} +} + +template +TCoord _Box::width() const BP2D_NOEXCEPT +{ + return pointlike::x(maxCorner()) - pointlike::x(minCorner()); +} + +template +TCoord _Box::height() const BP2D_NOEXCEPT +{ + return pointlike::y(maxCorner()) - pointlike::y(minCorner()); +} + +template +TCoord getX(const RawPoint& p) { return pointlike::x(p); } + +template +TCoord getY(const RawPoint& p) { return pointlike::y(p); } + +template +void setX(RawPoint& p, const TCoord& val) +{ + pointlike::x(p) = val; +} + +template +void setY(RawPoint& p, const TCoord& val) +{ + pointlike::y(p) = val; +} + +template +inline Radians _Segment::angleToXaxis() const +{ + if(std::isnan(static_cast(angletox_))) { + TCoord dx = getX(second()) - getX(first()); + TCoord dy = getY(second()) - getY(first()); + + double a = std::atan2(dy, dx); + auto s = std::signbit(a); + + if(s) a += Pi_2; + angletox_ = a; + } + return angletox_; +} + +template +inline double _Segment::length() +{ + return pointlike::distance(first(), second()); +} + +template +inline RawPoint _Box::center() const BP2D_NOEXCEPT { + auto& minc = minCorner(); + auto& maxc = maxCorner(); + + using Coord = TCoord; + + RawPoint ret = { // No rounding here, we dont know if these are int coords + static_cast( (getX(minc) + getX(maxc))/2.0 ), + static_cast( (getY(minc) + getY(maxc))/2.0 ) + }; + + return ret; +} + +enum class Formats { + WKT, + SVG +}; + +// This struct serves as a namespace. The only difference is that it can be +// used in friend declarations and can be aliased at class scope. +namespace shapelike { + +template +using Shapes = TMultiShape; + +template +inline RawShape create(const TContour& contour, + const THolesContainer& holes) +{ + return RawShape(contour, holes); +} + +template +inline RawShape create(TContour&& contour, + THolesContainer&& holes) +{ + return RawShape(contour, holes); +} + +template +inline RawShape create(const TContour& contour) +{ + return create(contour, {}); +} + +template +inline RawShape create(TContour&& contour) +{ + return create(contour, {}); +} + +template +inline THolesContainer& holes(RawShape& /*sh*/) +{ + static THolesContainer empty; + return empty; +} + +template +inline const THolesContainer& holes(const RawShape& /*sh*/) +{ + static THolesContainer empty; + return empty; +} + +template +inline TContour& hole(RawShape& sh, unsigned long idx) +{ + return holes(sh)[idx]; +} + +template +inline const TContour& hole(const RawShape& sh, unsigned long idx) +{ + return holes(sh)[idx]; +} + +template +inline size_t holeCount(const RawShape& sh) +{ + return holes(sh).size(); +} + +template +inline TContour& contour(RawShape& sh) +{ + static_assert(always_false::value, + "shapelike::contour() unimplemented!"); + return sh; +} + +template +inline const TContour& contour(const RawShape& sh) +{ + static_assert(always_false::value, + "shapelike::contour() unimplemented!"); + return sh; +} + +// Optional, does nothing by default +template +inline void reserve(RawPath& p, size_t vertex_capacity, const PathTag&) +{ + p.reserve(vertex_capacity); +} + +template +inline void addVertex(RawShape& sh, const PathTag&, Args...args) +{ + return sh.emplace_back(std::forward(args)...); +} + +template +inline void foreachVertex(RawShape& sh, Fn fn, const PathTag&) { + std::for_each(sh.begin(), sh.end(), fn); +} + +template +inline typename RawShape::iterator begin(RawShape& sh, const PathTag&) +{ + return sh.begin(); +} + +template +inline typename RawShape::iterator end(RawShape& sh, const PathTag&) +{ + return sh.end(); +} + +template +inline typename RawShape::const_iterator +cbegin(const RawShape& sh, const PathTag&) +{ + return sh.cbegin(); +} + +template +inline typename RawShape::const_iterator +cend(const RawShape& sh, const PathTag&) +{ + return sh.cend(); +} + +template +inline std::string toString(const RawShape& /*sh*/) +{ + return ""; +} + +template +inline std::string serialize(const RawShape& /*sh*/, double /*scale*/=1) +{ + static_assert(always_false::value, + "shapelike::serialize() unimplemented!"); + return ""; +} + +template +inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/) +{ + static_assert(always_false::value, + "shapelike::unserialize() unimplemented!"); +} + +template +inline double area(const RawShape& /*sh*/, const PolygonTag&) +{ + static_assert(always_false::value, + "shapelike::area() unimplemented!"); + return 0; +} + +template +inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/) +{ + static_assert(always_false::value, + "shapelike::intersects() unimplemented!"); + return false; +} + +template +inline bool isInside(const TPoint& /*point*/, + const RawShape& /*shape*/) +{ + static_assert(always_false::value, + "shapelike::isInside(point, shape) unimplemented!"); + return false; +} + +template +inline bool isInside(const RawShape& /*shape*/, + const RawShape& /*shape*/) +{ + static_assert(always_false::value, + "shapelike::isInside(shape, shape) unimplemented!"); + return false; +} + +template +inline bool touches( const RawShape& /*shape*/, + const RawShape& /*shape*/) +{ + static_assert(always_false::value, + "shapelike::touches(shape, shape) unimplemented!"); + return false; +} + +template +inline bool touches( const TPoint& /*point*/, + const RawShape& /*shape*/) +{ + static_assert(always_false::value, + "shapelike::touches(point, shape) unimplemented!"); + return false; +} + +template +inline _Box> boundingBox(const RawShape& /*sh*/, + const PolygonTag&) +{ + static_assert(always_false::value, + "shapelike::boundingBox(shape) unimplemented!"); +} + +template +inline _Box> +boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&) +{ + static_assert(always_false::value, + "shapelike::boundingBox(shapes) unimplemented!"); +} + +template +inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&) +{ + static_assert(always_false::value, + "shapelike::convexHull(shape) unimplemented!"); + return RawShape(); +} + +template +inline typename RawShapes::value_type +convexHull(const RawShapes& /*sh*/, const MultiPolygonTag&) +{ + static_assert(always_false::value, + "shapelike::convexHull(shapes) unimplemented!"); + return typename RawShapes::value_type(); +} + +template +inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/) +{ + static_assert(always_false::value, + "shapelike::rotate() unimplemented!"); +} + +template +inline void translate(RawShape& /*sh*/, const RawPoint& /*offs*/) +{ + static_assert(always_false::value, + "shapelike::translate() unimplemented!"); +} + +template +inline void offset(RawShape& /*sh*/, TCoord> /*distance*/) +{ + dout() << "The current geometry backend does not support offsetting!\n"; +} + +template +inline std::pair isValid(const RawShape& /*sh*/) +{ + return {false, "shapelike::isValid() unimplemented!"}; +} + +template inline bool isConvex(const RawPath& sh, const PathTag&) +{ + using Vertex = TPoint; + auto first = begin(sh); + auto middle = std::next(first); + auto last = std::next(middle); + using CVrRef = const Vertex&; + + auto zcrossproduct = [](CVrRef k, CVrRef k1, CVrRef k2) { + auto dx1 = getX(k1) - getX(k); + auto dy1 = getY(k1) - getY(k); + auto dx2 = getX(k2) - getX(k1); + auto dy2 = getY(k2) - getY(k1); + return dx1*dy2 - dy1*dx2; + }; + + auto firstprod = zcrossproduct( *(std::prev(std::prev(end(sh)))), + *first, + *middle ); + + bool ret = true; + bool frsign = firstprod > 0; + while(last != end(sh)) { + auto &k = *first, &k1 = *middle, &k2 = *last; + auto zc = zcrossproduct(k, k1, k2); + ret &= frsign == (zc > 0); + ++first; ++middle; ++last; + } + + return ret; +} + +// ***************************************************************************** +// No need to implement these +// ***************************************************************************** + +template +inline typename TContour::iterator +begin(RawShape& sh, const PolygonTag& t) +{ + return begin(contour(sh), PathTag()); +} + +template // Tag dispatcher +inline auto begin(RawShape& sh) -> decltype(begin(sh, Tag())) +{ + return begin(sh, Tag()); +} + +template +inline typename TContour::const_iterator +cbegin(const RawShape& sh, const PolygonTag&) +{ + return cbegin(contour(sh), PathTag()); +} + +template // Tag dispatcher +inline auto cbegin(const RawShape& sh) -> decltype(cbegin(sh, Tag())) +{ + return cbegin(sh, Tag()); +} + +template +inline typename TContour::iterator +end(RawShape& sh, const PolygonTag&) +{ + return end(contour(sh), PathTag()); +} + +template // Tag dispatcher +inline auto end(RawShape& sh) -> decltype(begin(sh, Tag())) +{ + return end(sh, Tag()); +} + +template +inline typename TContour::const_iterator +cend(const RawShape& sh, const PolygonTag&) +{ + return cend(contour(sh), PathTag()); +} + +template // Tag dispatcher +inline auto cend(const RawShape& sh) -> decltype(cend(sh, Tag())) +{ + return cend(sh, Tag()); +} + +template std::reverse_iterator _backward(It iter) { + return std::reverse_iterator(iter); +} + +template auto rbegin(P& p) -> decltype(_backward(end(p))) +{ + return _backward(end(p)); +} + +template auto rcbegin(const P& p) -> decltype(_backward(end(p))) +{ + return _backward(end(p)); +} + +template auto rend(P& p) -> decltype(_backward(begin(p))) +{ + return _backward(begin(p)); +} + +template auto rcend(const P& p) -> decltype(_backward(cbegin(p))) +{ + return _backward(cbegin(p)); +} + +template TPoint

front(const P& p) { return *shapelike::cbegin(p); } +template TPoint

back (const P& p) { + return *backward(shapelike::cend(p)); +} + +// Optional, does nothing by default +template +inline void reserve(RawShape& sh, size_t vertex_capacity, const PolygonTag&) +{ + reserve(contour(sh), vertex_capacity, PathTag()); +} + +template // Tag dispatcher +inline void reserve(T& sh, size_t vertex_capacity) { + reserve(sh, vertex_capacity, Tag()); +} + +template +inline void addVertex(RawShape& sh, const PolygonTag&, Args...args) +{ + return addVertex(contour(sh), PathTag(), std::forward(args)...); +} + +template // Tag dispatcher +inline void addVertex(RawShape& sh, Args...args) +{ + return addVertex(sh, Tag(), std::forward(args)...); +} + +template +inline Box boundingBox(const Box& box, const BoxTag& ) +{ + return box; +} + +template +inline _Box boundingBox( + const Circle& circ, const CircleTag&) +{ + using Point = typename Circle::PointType; + using Coord = TCoord; + Point pmin = { + static_cast(getX(circ.center()) - circ.radius()), + static_cast(getY(circ.center()) - circ.radius()) }; + + Point pmax = { + static_cast(getX(circ.center()) + circ.radius()), + static_cast(getY(circ.center()) + circ.radius()) }; + + return {pmin, pmax}; +} + +template // Dispatch function +inline _Box> boundingBox(const S& sh) +{ + return boundingBox(sh, Tag() ); +} + +template +inline double area(const Box& box, const BoxTag& ) +{ + return box.area(); +} + +template +inline double area(const Circle& circ, const CircleTag& ) +{ + return circ.area(); +} + +template // Dispatching function +inline double area(const RawShape& sh) +{ + return area(sh, Tag()); +} + +template +inline double area(const RawShapes& shapes, const MultiPolygonTag&) +{ + using RawShape = typename RawShapes::value_type; + return std::accumulate(shapes.begin(), shapes.end(), 0.0, + [](double a, const RawShape& b) { + return a += area(b); + }); +} + +template +inline auto convexHull(const RawShape& sh) + -> decltype(convexHull(sh, Tag())) // TODO: C++14 could deduce +{ + return convexHull(sh, Tag()); +} + +template +inline bool isInside(const TPoint& point, + const _Circle>& circ) +{ + return pointlike::distance(point, circ.center()) < circ.radius(); +} + +template +inline bool isInside(const TPoint& point, + const _Box>& box) +{ + auto px = getX(point); + auto py = getY(point); + auto minx = getX(box.minCorner()); + auto miny = getY(box.minCorner()); + auto maxx = getX(box.maxCorner()); + auto maxy = getY(box.maxCorner()); + + return px > minx && px < maxx && py > miny && py < maxy; +} + +template +inline bool isInside(const RawShape& sh, + const _Circle>& circ) +{ + return std::all_of(cbegin(sh), cend(sh), + [&circ](const TPoint& p){ + return isInside(p, circ); + }); +} + +template +inline bool isInside(const _Box>& box, + const _Circle>& circ) +{ + return isInside(box.minCorner(), circ) && + isInside(box.maxCorner(), circ); +} + +template +inline bool isInside(const _Box>& ibb, + const _Box>& box) +{ + auto iminX = getX(ibb.minCorner()); + auto imaxX = getX(ibb.maxCorner()); + auto iminY = getY(ibb.minCorner()); + auto imaxY = getY(ibb.maxCorner()); + + auto minX = getX(box.minCorner()); + auto maxX = getX(box.maxCorner()); + auto minY = getY(box.minCorner()); + auto maxY = getY(box.maxCorner()); + + return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY; +} + +template // Potential O(1) implementation may exist +inline TPoint& vertex(RawShape& sh, unsigned long idx, + const PolygonTag&) +{ + return *(shapelike::begin(contour(sh)) + idx); +} + +template // Potential O(1) implementation may exist +inline TPoint& vertex(RawShape& sh, unsigned long idx, + const PathTag&) +{ + return *(shapelike::begin(sh) + idx); +} + +template // Potential O(1) implementation may exist +inline TPoint& vertex(RawShape& sh, unsigned long idx) +{ + return vertex(sh, idx, Tag()); +} + +template // Potential O(1) implementation may exist +inline const TPoint& vertex(const RawShape& sh, + unsigned long idx, + const PolygonTag&) +{ + return *(shapelike::cbegin(contour(sh)) + idx); +} + +template // Potential O(1) implementation may exist +inline const TPoint& vertex(const RawShape& sh, + unsigned long idx, + const PathTag&) +{ + return *(shapelike::cbegin(sh) + idx); +} + + +template // Potential O(1) implementation may exist +inline const TPoint& vertex(const RawShape& sh, + unsigned long idx) +{ + return vertex(sh, idx, Tag()); +} + +template +inline size_t contourVertexCount(const RawShape& sh) +{ + return shapelike::cend(sh) - shapelike::cbegin(sh); +} + +template +inline void foreachVertex(RawShape& sh, Fn fn, const PolygonTag&) { + foreachVertex(contour(sh), fn, PathTag()); + for(auto& h : holes(sh)) foreachVertex(h, fn, PathTag()); +} + +template +inline void foreachVertex(RawShape& sh, Fn fn) { + foreachVertex(sh, fn, Tag()); +} + +template inline bool isConvex(const Poly& sh, const PolygonTag&) +{ + bool convex = true; + convex &= isConvex(contour(sh), PathTag()); + convex &= holeCount(sh) == 0; + return convex; +} + +template inline bool isConvex(const RawShape& sh) // dispatch +{ + return isConvex(sh, Tag()); +} + +} + +#define DECLARE_MAIN_TYPES(T) \ + using Polygon = T; \ + using Point = TPoint; \ + using Coord = TCoord; \ + using Contour = TContour; \ + using Box = _Box; \ + using Circle = _Circle; \ + using Segment = _Segment; \ + using Polygons = TMultiShape + +} + +#endif // GEOMETRY_TRAITS_HPP diff --git a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp new file mode 100644 index 0000000000..b57b8dc536 --- /dev/null +++ b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp @@ -0,0 +1,282 @@ +#ifndef GEOMETRIES_NOFITPOLYGON_HPP +#define GEOMETRIES_NOFITPOLYGON_HPP + +#include "geometry_traits.hpp" +#include +#include +#include +#include + +namespace libnest2d { + +namespace __nfp { +// Do not specialize this... +template +inline bool _vsort(const TPoint& v1, const TPoint& v2) +{ + using Coord = TCoord>; + Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2); + auto diff = y1 - y2; + if(std::abs(diff) <= std::numeric_limits::epsilon()) + return x1 < x2; + + return diff < 0; +} + +template> +inline void buildPolygon(const EdgeList& edgelist, + RawShape& rpoly, + Vertex& top_nfp) +{ + namespace sl = shapelike; + + auto& rsh = sl::contour(rpoly); + + sl::reserve(rsh, 2*edgelist.size()); + + // Add the two vertices from the first edge into the final polygon. + sl::addVertex(rsh, edgelist.front().first()); + sl::addVertex(rsh, edgelist.front().second()); + + // Sorting function for the nfp reference vertex search + auto& cmp = _vsort; + + // the reference (rightmost top) vertex so far + top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp ); + + auto tmp = std::next(sl::begin(rsh)); + + // Construct final nfp by placing each edge to the end of the previous + for(auto eit = std::next(edgelist.begin()); + eit != edgelist.end(); + ++eit) + { + auto d = *tmp - eit->first(); + Vertex p = eit->second() + d; + + sl::addVertex(rsh, p); + + // Set the new reference vertex + if(cmp(top_nfp, p)) top_nfp = p; + + tmp = std::next(tmp); + } + +} + +template +void advance(Iterator& it, Container& cont, bool direction) +{ + int dir = direction ? 1 : -1; + if(dir < 0 && it == cont.begin()) it = std::prev(cont.end()); + else it += dir; + if(dir > 0 && it == cont.end()) it = cont.begin(); +} + +} + +/// A collection of static methods for handling the no fit polygon creation. +namespace nfp { + +const double BP2D_CONSTEXPR TwoPi = 2*Pi; + +/// The complexity level of a polygon that an NFP implementation can handle. +enum class NfpLevel: unsigned { + CONVEX_ONLY, + ONE_CONVEX, + BOTH_CONCAVE, + ONE_CONVEX_WITH_HOLES, + BOTH_CONCAVE_WITH_HOLES +}; + +template +using NfpResult = std::pair>; + +template struct MaxNfpLevel { + static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY; +}; + + +// Shorthand for a pile of polygons +template +using Shapes = TMultiShape; + +/** + * Merge a bunch of polygons with the specified additional polygon. + * + * \tparam RawShape the Polygon data type. + * \param shc The pile of polygons that will be unified with sh. + * \param sh A single polygon to unify with shc. + * + * \return A set of polygons that is the union of the input polygons. Note that + * mostly it will be a set containing only one big polygon but if the input + * polygons are disjunct than the resulting set will contain more polygons. + */ +template +inline RawShapes merge(const RawShapes& /*shc*/) +{ + static_assert(always_false::value, + "Nfp::merge(shapes, shape) unimplemented!"); +} + +/** + * Merge a bunch of polygons with the specified additional polygon. + * + * \tparam RawShape the Polygon data type. + * \param shc The pile of polygons that will be unified with sh. + * \param sh A single polygon to unify with shc. + * + * \return A set of polygons that is the union of the input polygons. Note that + * mostly it will be a set containing only one big polygon but if the input + * polygons are disjunct than the resulting set will contain more polygons. + */ +template +inline TMultiShape merge(const TMultiShape& shc, + const RawShape& sh) +{ + auto m = nfp::merge(shc); + m.emplace_back(sh); + return nfp::merge(m); +} + +/** + * Get the vertex of the polygon that is at the lowest values (bottom) in the Y + * axis and if there are more than one vertices on the same Y coordinate than + * the result will be the leftmost (with the highest X coordinate). + */ +template +inline TPoint leftmostDownVertex(const RawShape& sh) +{ + + // find min x and min y vertex + auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh), + __nfp::_vsort); + + return it == shapelike::cend(sh) ? TPoint() : *it;; +} + +/** + * Get the vertex of the polygon that is at the highest values (top) in the Y + * axis and if there are more than one vertices on the same Y coordinate than + * the result will be the rightmost (with the lowest X coordinate). + */ +template +TPoint rightmostUpVertex(const RawShape& sh) +{ + + // find max x and max y vertex + auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh), + __nfp::_vsort); + + return it == shapelike::cend(sh) ? TPoint() : *it; +} + +/** + * A method to get a vertex from a polygon that always maintains a relative + * position to the coordinate system: It is always the rightmost top vertex. + * + * This way it does not matter in what order the vertices are stored, the + * reference will be always the same for the same polygon. + */ +template +inline TPoint referenceVertex(const RawShape& sh) +{ + return rightmostUpVertex(sh); +} + +/** + * The "trivial" Cuninghame-Green implementation of NFP for convex polygons. + * + * You can use this even if you provide implementations for the more complex + * cases (Through specializing the the NfpImpl struct). Currently, no other + * cases are covered in the library. + * + * Complexity should be no more than nlogn (std::sort) in the number of edges + * of the input polygons. + * + * \tparam RawShape the Polygon data type. + * \param sh The stationary polygon + * \param cother The orbiting polygon + * \return Returns a pair of the NFP and its reference vertex of the two input + * polygons which have to be strictly convex. The resulting NFP is proven to be + * convex as well in this case. + * + */ +template +inline NfpResult nfpConvexOnly(const RawShape& sh, + const RawShape& other) +{ + using Vertex = TPoint; using Edge = _Segment; + namespace sl = shapelike; + + RawShape rsh; // Final nfp placeholder + Vertex top_nfp; + std::vector edgelist; + + auto cap = sl::contourVertexCount(sh) + sl::contourVertexCount(other); + + // Reserve the needed memory + edgelist.reserve(cap); + sl::reserve(rsh, static_cast(cap)); + + { // place all edges from sh into edgelist + auto first = sl::cbegin(sh); + auto next = std::next(first); + + while(next != sl::cend(sh)) { + edgelist.emplace_back(*(first), *(next)); + ++first; ++next; + } + } + + { // place all edges from other into edgelist + auto first = sl::cbegin(other); + auto next = std::next(first); + + while(next != sl::cend(other)) { + edgelist.emplace_back(*(next), *(first)); + ++first; ++next; + } + } + + // Sort the edges by angle to X axis. + std::sort(edgelist.begin(), edgelist.end(), + [](const Edge& e1, const Edge& e2) + { + return e1.angleToXaxis() > e2.angleToXaxis(); + }); + + __nfp::buildPolygon(edgelist, rsh, top_nfp); + + return {rsh, top_nfp}; +} + +// Specializable NFP implementation class. Specialize it if you have a faster +// or better NFP implementation +template +struct NfpImpl { + NfpResult operator()(const RawShape& sh, const RawShape& other) + { + static_assert(nfptype == NfpLevel::CONVEX_ONLY, + "Nfp::noFitPolygon() unimplemented!"); + + // Libnest2D has a default implementation for convex polygons and will + // use it if feasible. + return nfpConvexOnly(sh, other); + } +}; + +/// Helper function to get the NFP +template +inline NfpResult noFitPolygon(const RawShape& sh, + const RawShape& other) +{ + NfpImpl nfps; + return nfps(sh, other); +} + +} + +} + +#endif // GEOMETRIES_NOFITPOLYGON_HPP diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp similarity index 98% rename from xs/src/libnest2d/libnest2d/libnest2d.hpp rename to src/libnest2d/include/libnest2d/libnest2d.hpp index 4d1e62f991..aac62e094e 100644 --- a/xs/src/libnest2d/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -215,7 +215,7 @@ public: switch(convexity_) { case Convexity::UNCHECKED: - ret = sl::isConvex(sl::getContour(transformedShape())); + ret = sl::isConvex(sl::contour(transformedShape())); convexity_ = ret? Convexity::C_TRUE : Convexity::C_FALSE; break; case Convexity::C_TRUE: ret = true; break; @@ -640,6 +640,7 @@ public: // The progress function will be called with the number of placed items using ProgressFunction = std::function; +using StopCondition = std::function; /** * A wrapper interface (trait) class for any selections strategy provider. @@ -674,6 +675,8 @@ public: */ void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); } + void stopCondition(StopCondition cond) { impl_.stopCondition(cond); } + /** * \brief A method to start the calculation on the input sequence. * @@ -802,16 +805,16 @@ public: class SConf = SelectionConfig> Nester( TBinType&& bin, Unit min_obj_distance = 0, - PConf&& pconfig = PConf(), - SConf&& sconfig = SConf()): + const PConf& pconfig = PConf(), + const SConf& sconfig = SConf()): bin_(std::forward(bin)), - pconfig_(std::forward(pconfig)), + pconfig_(pconfig), min_obj_distance_(min_obj_distance) { static_assert( std::is_same::value, "Incompatible placement and selection strategy!"); - selector_.configure(std::forward(sconfig)); + selector_.configure(sconfig); } void configure(const PlacementConfig& pconf) { pconfig_ = pconf; } @@ -864,6 +867,11 @@ public: selector_.progressIndicator(func); return *this; } + /// Set a predicate to tell when to abort nesting. + inline Nester& stopCondition(StopCondition fn) { + selector_.stopCondition(fn); return *this; + } + inline PackGroup lastResult() { PackGroup ret; for(size_t i = 0; i < selector_.binCount(); i++) { diff --git a/xs/src/libnest2d/libnest2d/optimizer.hpp b/src/libnest2d/include/libnest2d/optimizer.hpp similarity index 96% rename from xs/src/libnest2d/libnest2d/optimizer.hpp rename to src/libnest2d/include/libnest2d/optimizer.hpp index 90d2f2ff99..78e1055987 100644 --- a/xs/src/libnest2d/libnest2d/optimizer.hpp +++ b/src/libnest2d/include/libnest2d/optimizer.hpp @@ -105,6 +105,11 @@ struct StopCriteria { /// Stop if this value or better is found. double stop_score = std::nan(""); + /// A predicate that if evaluates to true, the optimization should terminate + /// and the best result found prior to termination should be returned. + std::function stop_condition = [] { return false; }; + + /// The max allowed number of iterations. unsigned max_iterations = 0; }; diff --git a/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt b/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt new file mode 100644 index 0000000000..2a32019f42 --- /dev/null +++ b/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt @@ -0,0 +1,61 @@ +find_package(NLopt 1.4) + +if(NOT NLopt_FOUND) + message(STATUS "NLopt not found so downloading " + "and automatic build is performed...") + + include(DownloadProject) + + if (CMAKE_VERSION VERSION_LESS 3.2) + set(UPDATE_DISCONNECTED_IF_AVAILABLE "") + else() + set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") + endif() + + set(URL_NLOPT "https://github.com/stevengj/nlopt.git" + CACHE STRING "Location of the nlopt git repository") + + # set(NLopt_DIR ${CMAKE_BINARY_DIR}/nlopt) + include(DownloadProject) + download_project( PROJ nlopt + GIT_REPOSITORY ${URL_NLOPT} + GIT_TAG v2.5.0 + # CMAKE_CACHE_ARGS -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${NLopt_DIR} + ${UPDATE_DISCONNECTED_IF_AVAILABLE} + ) + + set(SHARED_LIBS_STATE BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) + set(NLOPT_PYTHON OFF CACHE BOOL "" FORCE) + set(NLOPT_OCTAVE OFF CACHE BOOL "" FORCE) + set(NLOPT_MATLAB OFF CACHE BOOL "" FORCE) + set(NLOPT_GUILE OFF CACHE BOOL "" FORCE) + set(NLOPT_SWIG OFF CACHE BOOL "" FORCE) + set(NLOPT_LINK_PYTHON OFF CACHE BOOL "" FORCE) + + add_subdirectory(${nlopt_SOURCE_DIR} ${nlopt_BINARY_DIR}) + + set(NLopt_LIBS nlopt) + set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR} ${nlopt_BINARY_DIR}/src/api) + set(SHARED_LIBS_STATE ${SHARED_STATE}) + + add_library(NloptOptimizer INTERFACE) + target_link_libraries(NloptOptimizer INTERFACE nlopt) + target_include_directories(NloptOptimizer INTERFACE ${NLopt_INCLUDE_DIR}) + +else() + add_library(NloptOptimizer INTERFACE) + target_link_libraries(NloptOptimizer INTERFACE Nlopt::Nlopt) +endif() + +#target_sources( NloptOptimizer INTERFACE +#${CMAKE_CURRENT_SOURCE_DIR}/simplex.hpp +#${CMAKE_CURRENT_SOURCE_DIR}/subplex.hpp +#${CMAKE_CURRENT_SOURCE_DIR}/genetic.hpp +#${CMAKE_CURRENT_SOURCE_DIR}/nlopt_boilerplate.hpp +#) + +target_compile_definitions(NloptOptimizer INTERFACE LIBNEST2D_OPTIMIZER_NLOPT) + +# And finally plug the NloptOptimizer into libnest2d +target_link_libraries(libnest2d INTERFACE NloptOptimizer) diff --git a/xs/src/libnest2d/libnest2d/optimizers/genetic.hpp b/src/libnest2d/include/libnest2d/optimizers/nlopt/genetic.hpp similarity index 88% rename from xs/src/libnest2d/libnest2d/optimizers/genetic.hpp rename to src/libnest2d/include/libnest2d/optimizers/nlopt/genetic.hpp index 276854a12d..731ead554e 100644 --- a/xs/src/libnest2d/libnest2d/optimizers/genetic.hpp +++ b/src/libnest2d/include/libnest2d/optimizers/nlopt/genetic.hpp @@ -19,7 +19,8 @@ public: template<> struct OptimizerSubclass { using Type = GeneticOptimizer; }; -template<> TOptimizer GlobalOptimizer( +template<> +inline TOptimizer GlobalOptimizer( Method localm, const StopCriteria& scr ) { return GeneticOptimizer (scr).localMethod(localm); diff --git a/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp b/src/libnest2d/include/libnest2d/optimizers/nlopt/nlopt_boilerplate.hpp similarity index 85% rename from xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp rename to src/libnest2d/include/libnest2d/optimizers/nlopt/nlopt_boilerplate.hpp index 1a0f06e028..286d176c52 100644 --- a/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/optimizers/nlopt/nlopt_boilerplate.hpp @@ -13,7 +13,7 @@ #include #include -#include "libnest2d/metaloop.hpp" +#include #include @@ -100,7 +100,13 @@ protected: std::vector& /*grad*/, void *data) { - auto fnptr = static_cast*>(data); + using TData = std::pair*, NloptOptimizer*>; + auto typeddata = static_cast(data); + + if(typeddata->second->stopcr_.stop_condition()) + typeddata->second->opt_.force_stop(); + + auto fnptr = typeddata->first; auto funval = std::tuple(); // copy the obtained objectfunction arguments to the funval tuple. @@ -155,17 +161,25 @@ protected: // Take care of the initial values, copy them to initvals_ metaloop::apply(InitValFunc(*this), initvals); + std::pair*, NloptOptimizer*> data = + std::make_pair(&func, this); + switch(dir_) { case OptDir::MIN: - opt_.set_min_objective(optfunc, &func); break; + opt_.set_min_objective(optfunc, &data); break; case OptDir::MAX: - opt_.set_max_objective(optfunc, &func); break; + opt_.set_max_objective(optfunc, &data); break; } Result result; + nlopt::result rescode; - auto rescode = opt_.optimize(initvals_, result.score); - result.resultcode = static_cast(rescode); + try { + rescode = opt_.optimize(initvals_, result.score); + result.resultcode = static_cast(rescode); + } catch( nlopt::forced_stop& ) { + result.resultcode = ResultCodes::FORCED_STOP; + } metaloop::apply(ResultCopyFunc(*this), result.optimum); diff --git a/xs/src/libnest2d/libnest2d/optimizers/simplex.hpp b/src/libnest2d/include/libnest2d/optimizers/nlopt/simplex.hpp similarity index 100% rename from xs/src/libnest2d/libnest2d/optimizers/simplex.hpp rename to src/libnest2d/include/libnest2d/optimizers/nlopt/simplex.hpp diff --git a/xs/src/libnest2d/libnest2d/optimizers/subplex.hpp b/src/libnest2d/include/libnest2d/optimizers/nlopt/subplex.hpp similarity index 100% rename from xs/src/libnest2d/libnest2d/optimizers/subplex.hpp rename to src/libnest2d/include/libnest2d/optimizers/nlopt/subplex.hpp diff --git a/src/libnest2d/include/libnest2d/optimizers/optimlib/CMakeLists.txt b/src/libnest2d/include/libnest2d/optimizers/optimlib/CMakeLists.txt new file mode 100644 index 0000000000..efbbd9cfb2 --- /dev/null +++ b/src/libnest2d/include/libnest2d/optimizers/optimlib/CMakeLists.txt @@ -0,0 +1,5 @@ +find_package(Armadillo REQUIRED) + +add_library(OptimlibOptimizer INTERFACE) +target_include_directories(OptimlibOptimizer INTERFACE ${ARMADILLO_INCLUDE_DIRS}) +target_link_libraries(OptimlibOptimizer INTERFACE ${ARMADILLO_LIBRARIES}) \ No newline at end of file diff --git a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp b/src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp similarity index 98% rename from xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp rename to src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp index 18c27c40cd..7f10be7d73 100644 --- a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp @@ -233,7 +233,8 @@ protected: assert(pleft.vertexCount() > 0); - auto trpleft = pleft.transformedShape(); + auto trpleft_poly = pleft.transformedShape(); + auto& trpleft = sl::contour(trpleft_poly); auto first = sl::begin(trpleft); auto next = first + 1; auto endit = sl::end(trpleft); @@ -355,8 +356,10 @@ protected: auto start = std::min(topleft_it->first, bottomleft_it->first); auto finish = std::max(topleft_it->first, bottomleft_it->first); + RawShape ret; + // the return shape - RawShape rsh; + auto& rsh = sl::contour(ret); // reserve for all vertices plus 2 for the left horizontal wall, 2 for // the additional vertices for maintaning min object distance @@ -401,7 +404,7 @@ protected: // Close the polygon sl::addVertex(rsh, topleft_vertex); - return rsh; + return ret; } }; diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp similarity index 97% rename from xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp rename to src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index c86fb507e3..14ed3d22c1 100644 --- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -15,11 +15,13 @@ #ifndef NDEBUG #include #endif -#include "placer_boilerplate.hpp" -#include "../geometry_traits_nfp.hpp" -#include "libnest2d/optimizer.hpp" +#include +#include -#include "tools/svgtools.hpp" +#include "placer_boilerplate.hpp" + +// temporary +//#include "../tools/svgtools.hpp" #ifdef USE_TBB #include @@ -55,7 +57,7 @@ inline void enumerate( #elif defined(_OPENMP) if((policy & std::launch::async) == std::launch::async) { #pragma omp parallel for - for(TN n = 0; n < N; n++) fn(*(from + n), n); + for(int n = 0; n < int(N); n++) fn(*(from + n), TN(n)); } else { for(TN n = 0; n < N; n++) fn(*(from + n), n); @@ -72,19 +74,6 @@ inline void enumerate( #endif } -class SpinLock { - static std::atomic_flag locked; -public: - void lock() { - while (locked.test_and_set(std::memory_order_acquire)) { ; } - } - void unlock() { - locked.clear(std::memory_order_release); - } -}; - -std::atomic_flag SpinLock::locked = ATOMIC_FLAG_INIT ; - } namespace __itemhash { @@ -101,7 +90,7 @@ Key hash(const _Item& item) { std::string ret; auto& rhs = item.rawShape(); - auto& ctr = sl::getContour(rhs); + auto& ctr = sl::contour(rhs); auto it = ctr.begin(); auto nx = std::next(it); @@ -467,7 +456,7 @@ Circle minimizeCircle(const RawShape& sh) { using Point = TPoint; using Coord = TCoord; - auto& ctr = sl::getContour(sh); + auto& ctr = sl::contour(sh); if(ctr.empty()) return {{0, 0}, 0}; auto bb = sl::boundingBox(sh); @@ -641,6 +630,23 @@ private: Shapes nfps(items_.size()); const Item& trsh = itsh.first; + // ///////////////////////////////////////////////////////////////////// + // TODO: this is a workaround and should be solved in Item with mutexes + // guarding the mutable members when writing them. + // ///////////////////////////////////////////////////////////////////// + trsh.transformedShape(); + trsh.referenceVertex(); + trsh.rightmostTopVertex(); + trsh.leftmostBottomVertex(); + + for(Item& itm : items_) { + itm.transformedShape(); + itm.referenceVertex(); + itm.rightmostTopVertex(); + itm.leftmostBottomVertex(); + } + // ///////////////////////////////////////////////////////////////////// + __parallel::enumerate(items_.begin(), items_.end(), [&nfps, &trsh](const Item& sh, size_t n) { @@ -651,14 +657,10 @@ private: nfps[n] = subnfp_r.first; }); -// for(auto& n : nfps) { -// auto valid = sl::isValid(n); -// if(!valid.first) std::cout << "Warning: " << valid.second << std::endl; -// } - return nfp::merge(nfps); } + template Shapes calcnfp( const ItemWithHash itsh, Level) { // Function for arbitrary level of nfp implementation @@ -842,7 +844,11 @@ private: bool can_pack = false; double best_overfit = std::numeric_limits::max(); - auto remlist = ItemGroup(remaining.from, remaining.to); + ItemGroup remlist; + if(remaining.valid) { + remlist.insert(remlist.end(), remaining.from, remaining.to); + } + size_t itemhash = __itemhash::hash(item); if(items_.empty()) { diff --git a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp similarity index 98% rename from xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp rename to src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp index 0df1b8c913..9f940af4dd 100644 --- a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp @@ -1,7 +1,7 @@ #ifndef PLACER_BOILERPLATE_HPP #define PLACER_BOILERPLATE_HPP -#include "../libnest2d.hpp" +#include namespace libnest2d { namespace placers { diff --git a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp similarity index 98% rename from xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp rename to src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp index ee93d05928..39761f5572 100644 --- a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp +++ b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp @@ -551,7 +551,7 @@ public: // Safety test: try to pack each item into an empty bin. If it fails // then it should be removed from the not_packed list { auto it = store_.begin(); - while (it != store_.end()) { + while (it != store_.end() && !this->stopcond_()) { Placer p(bin); p.configure(pconfig); if(!p.pack(*it, rem(it, store_))) { it = store_.erase(it); @@ -592,9 +592,11 @@ public: bool do_pairs = config_.try_pairs; bool do_triplets = config_.try_triplets; + StopCondition stopcond = this->stopcond_; // The DJD heuristic algorithm itself: auto packjob = [INITIAL_FILL_AREA, bin_area, w, do_triplets, do_pairs, + stopcond, &tryOneByOne, &tryGroupsOfTwo, &tryGroupsOfThree, @@ -606,12 +608,12 @@ public: double waste = .0; bool lasttry = false; - while(!not_packed.empty()) { + while(!not_packed.empty() && !stopcond()) { {// Fill the bin up to INITIAL_FILL_PROPORTION of its capacity auto it = not_packed.begin(); - while(it != not_packed.end() && + while(it != not_packed.end() && !stopcond() && filled_area < INITIAL_FILL_AREA) { if(placer.pack(*it, rem(it, not_packed))) { @@ -623,14 +625,14 @@ public: } } - // try pieses one by one + // try pieces one by one while(tryOneByOne(placer, not_packed, waste, free_area, filled_area)) { waste = 0; lasttry = false; makeProgress(placer, idx, 1); } - // try groups of 2 pieses + // try groups of 2 pieces while(do_pairs && tryGroupsOfTwo(placer, not_packed, waste, free_area, filled_area)) { @@ -638,7 +640,7 @@ public: makeProgress(placer, idx, 2); } - // try groups of 3 pieses + // try groups of 3 pieces while(do_triplets && tryGroupsOfThree(placer, not_packed, waste, free_area, filled_area)) { diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/src/libnest2d/include/libnest2d/selections/filler.hpp similarity index 97% rename from xs/src/libnest2d/libnest2d/selections/filler.hpp rename to src/libnest2d/include/libnest2d/selections/filler.hpp index 0da7220a19..5f95a6eff8 100644 --- a/xs/src/libnest2d/libnest2d/selections/filler.hpp +++ b/src/libnest2d/include/libnest2d/selections/filler.hpp @@ -60,7 +60,7 @@ public: placer.configure(pconfig); auto it = store_.begin(); - while(it != store_.end()) { + while(it != store_.end() && !this->stopcond_()) { if(!placer.pack(*it, {std::next(it), store_.end()})) { if(packed_bins_.back().empty()) ++it; placer.clearItems(); diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/src/libnest2d/include/libnest2d/selections/firstfit.hpp similarity index 89% rename from xs/src/libnest2d/libnest2d/selections/firstfit.hpp rename to src/libnest2d/include/libnest2d/selections/firstfit.hpp index bca7497db8..d25487d6bc 100644 --- a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp +++ b/src/libnest2d/include/libnest2d/selections/firstfit.hpp @@ -1,7 +1,6 @@ #ifndef FIRSTFIT_HPP #define FIRSTFIT_HPP -#include "../libnest2d.hpp" #include "selection_boilerplate.hpp" namespace libnest2d { namespace selections { @@ -56,10 +55,12 @@ public: this->progress_(static_cast(--total)); }; + auto& cancelled = this->stopcond_; + // Safety test: try to pack each item into an empty bin. If it fails // then it should be removed from the list { auto it = store_.begin(); - while (it != store_.end()) { + while (it != store_.end() && !cancelled()) { Placer p(bin); p.configure(pconfig); if(!p.pack(*it)) { it = store_.erase(it); @@ -67,13 +68,14 @@ public: } } + auto it = store_.begin(); - while(it != store_.end()) { + while(it != store_.end() && !cancelled()) { bool was_packed = false; size_t j = 0; - while(!was_packed) { - for(; j < placers.size() && !was_packed; j++) { + while(!was_packed && !cancelled()) { + for(; j < placers.size() && !was_packed && !cancelled(); j++) { if((was_packed = placers[j].pack(*it, rem(it, store_) ))) makeProgress(placers[j], j); } diff --git a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp similarity index 76% rename from xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp rename to src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp index 05bbae658e..8351a99e7c 100644 --- a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp @@ -1,7 +1,8 @@ #ifndef SELECTION_BOILERPLATE_HPP #define SELECTION_BOILERPLATE_HPP -#include "../libnest2d.hpp" +#include +#include namespace libnest2d { namespace selections { @@ -25,14 +26,15 @@ public: return packed_bins_[binIndex]; } - inline void progressIndicator(ProgressFunction fn) { - progress_ = fn; - } + inline void progressIndicator(ProgressFunction fn) { progress_ = fn; } + + inline void stopCondition(StopCondition cond) { stopcond_ = cond; } protected: PackGroup packed_bins_; ProgressFunction progress_ = [](unsigned){}; + StopCondition stopcond_ = [](){ return false; }; }; } diff --git a/xs/src/libnest2d/libnest2d/boost_alg.hpp b/src/libnest2d/include/libnest2d/utils/boost_alg.hpp similarity index 98% rename from xs/src/libnest2d/libnest2d/boost_alg.hpp rename to src/libnest2d/include/libnest2d/utils/boost_alg.hpp index bb0403b066..c573edb47b 100644 --- a/xs/src/libnest2d/libnest2d/boost_alg.hpp +++ b/src/libnest2d/include/libnest2d/utils/boost_alg.hpp @@ -241,11 +241,11 @@ template<> struct tag { template<> struct exterior_ring { static inline bp2d::PathImpl& get(bp2d::PolygonImpl& p) { - return libnest2d::shapelike::getContour(p); + return libnest2d::shapelike::contour(p); } static inline bp2d::PathImpl const& get(bp2d::PolygonImpl const& p) { - return libnest2d::shapelike::getContour(p); + return libnest2d::shapelike::contour(p); } }; @@ -388,6 +388,14 @@ inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&) return b; } +template<> +inline bp2d::Box boundingBox(const PathImpl& sh, const PolygonTag&) +{ + bp2d::Box b; + boost::geometry::envelope(sh, b); + return b; +} + template<> inline bp2d::Box boundingBox(const bp2d::Shapes& shapes, const MultiPolygonTag&) diff --git a/xs/src/libnest2d/libnest2d/metaloop.hpp b/src/libnest2d/include/libnest2d/utils/metaloop.hpp similarity index 98% rename from xs/src/libnest2d/libnest2d/metaloop.hpp rename to src/libnest2d/include/libnest2d/utils/metaloop.hpp index d88988ba15..ac099ac464 100644 --- a/xs/src/libnest2d/libnest2d/metaloop.hpp +++ b/src/libnest2d/include/libnest2d/utils/metaloop.hpp @@ -1,7 +1,7 @@ #ifndef METALOOP_HPP #define METALOOP_HPP -#include "common.hpp" +#include #include #include @@ -12,7 +12,7 @@ namespace libnest2d { /* ************************************************************************** */ /** - * \brief C++11 conformant implementation of the index_sequence type from C++14 + * \brief C++11 compatible implementation of the index_sequence type from C++14 */ template struct index_sequence { using value_type = size_t; diff --git a/xs/src/libnest2d/libnest2d/rotfinder.hpp b/src/libnest2d/include/libnest2d/utils/rotfinder.hpp similarity index 100% rename from xs/src/libnest2d/libnest2d/rotfinder.hpp rename to src/libnest2d/include/libnest2d/utils/rotfinder.hpp diff --git a/xs/src/libnest2d/tests/CMakeLists.txt b/src/libnest2d/tests/CMakeLists.txt similarity index 57% rename from xs/src/libnest2d/tests/CMakeLists.txt rename to src/libnest2d/tests/CMakeLists.txt index 3777f3c568..fc3cd309dd 100644 --- a/xs/src/libnest2d/tests/CMakeLists.txt +++ b/src/libnest2d/tests/CMakeLists.txt @@ -3,7 +3,10 @@ find_package(GTest 1.7) if(NOT GTEST_FOUND) - message(STATUS "GTest not found so downloading...") + set(URL_GTEST "https://github.com/google/googletest.git" + CACHE STRING "Google test source code repository location.") + + message(STATUS "GTest not found so downloading from ${URL_GTEST}") # Go and download google test framework, integrate it with the build set(GTEST_LIBS_TO_LINK gtest gtest_main) @@ -15,7 +18,7 @@ if(NOT GTEST_FOUND) include(DownloadProject) download_project(PROJ googletest - GIT_REPOSITORY https://github.com/google/googletest.git + GIT_REPOSITORY ${URL_GTEST} GIT_TAG release-1.7.0 ${UPDATE_DISCONNECTED_IF_AVAILABLE} ) @@ -35,17 +38,18 @@ else() set(GTEST_LIBS_TO_LINK ${GTEST_BOTH_LIBRARIES} Threads::Threads) endif() -add_executable(bp2d_tests test.cpp - ../tools/svgtools.hpp -# ../tools/libnfpglue.hpp -# ../tools/libnfpglue.cpp - printer_parts.h - printer_parts.cpp - ${LIBNEST2D_SRCFILES} - ) -target_link_libraries(bp2d_tests ${LIBNEST2D_LIBRARIES} ${GTEST_LIBS_TO_LINK} ) +add_executable(tests_clipper_nlopt + test.cpp + ../tools/svgtools.hpp +# ../tools/libnfpglue.hpp +# ../tools/libnfpglue.cpp + printer_parts.h + printer_parts.cpp +) -target_include_directories(bp2d_tests PRIVATE BEFORE ${LIBNEST2D_HEADERS} - ${GTEST_INCLUDE_DIRS}) +target_link_libraries(tests_clipper_nlopt libnest2d ${GTEST_LIBS_TO_LINK} ) -add_test(libnest2d_tests bp2d_tests) +target_include_directories(tests_clipper_nlopt PRIVATE BEFORE + ${GTEST_INCLUDE_DIRS}) + +add_test(libnest2d_tests tests_clipper_nlopt) diff --git a/xs/src/libnest2d/tests/printer_parts.cpp b/src/libnest2d/tests/printer_parts.cpp similarity index 100% rename from xs/src/libnest2d/tests/printer_parts.cpp rename to src/libnest2d/tests/printer_parts.cpp diff --git a/xs/src/libnest2d/tests/printer_parts.h b/src/libnest2d/tests/printer_parts.h similarity index 100% rename from xs/src/libnest2d/tests/printer_parts.h rename to src/libnest2d/tests/printer_parts.h diff --git a/xs/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp similarity index 98% rename from xs/src/libnest2d/tests/test.cpp rename to src/libnest2d/tests/test.cpp index 323fb8d31e..3b0eae1618 100644 --- a/xs/src/libnest2d/tests/test.cpp +++ b/src/libnest2d/tests/test.cpp @@ -4,6 +4,7 @@ #include #include "printer_parts.h" #include +#include "../tools/svgtools.hpp" //#include "../tools/libnfpglue.hpp" //#include "../tools/nfp_svgnest_glue.hpp" @@ -125,7 +126,7 @@ TEST(GeometryAlgorithms, boundingCircle) { c = boundingCircle(part.transformedShape()); if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl; - else for(auto v : shapelike::getContour(part.transformedShape()) ) { + else for(auto v : shapelike::contour(part.transformedShape()) ) { auto d = pointlike::distance(v, c.center()); if(d > c.radius() ) { auto e = std::abs( 1.0 - d/c.radius()); @@ -788,15 +789,6 @@ TEST(GeometryAlgorithms, nfpConvexConvex) { // testNfp(nfp_concave_testdata); //} -TEST(GeometryAlgorithms, nfpConcaveConcave) { - using namespace libnest2d; - -// Rectangle r1(10, 10); -// Rectangle r2(20, 20); -// auto result = Nfp::nfpSimpleSimple(r1.transformedShape(), -// r2.transformedShape()); -} - TEST(GeometryAlgorithms, pointOnPolygonContour) { using namespace libnest2d; diff --git a/xs/src/benchmark.h b/src/libnest2d/tools/benchmark.h similarity index 100% rename from xs/src/benchmark.h rename to src/libnest2d/tools/benchmark.h diff --git a/xs/src/libnest2d/tools/svgtools.hpp b/src/libnest2d/tools/svgtools.hpp similarity index 98% rename from xs/src/libnest2d/tools/svgtools.hpp rename to src/libnest2d/tools/svgtools.hpp index 776dd5a1a8..e1ed1ad05a 100644 --- a/xs/src/libnest2d/tools/svgtools.hpp +++ b/src/libnest2d/tools/svgtools.hpp @@ -56,7 +56,7 @@ public: auto d = static_cast( std::round(conf_.height*conf_.mm_in_coord_units) ); - auto& contour = shapelike::getContour(tsh); + auto& contour = shapelike::contour(tsh); for(auto& v : contour) setY(v, -getY(v) + d); auto& holes = shapelike::holes(tsh); diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp new file mode 100644 index 0000000000..d3cca7ff24 --- /dev/null +++ b/src/libslic3r/BoundingBox.cpp @@ -0,0 +1,283 @@ +#include "BoundingBox.hpp" +#include +#include + +#include + +namespace Slic3r { + +template BoundingBoxBase::BoundingBoxBase(const std::vector &points); +template BoundingBoxBase::BoundingBoxBase(const std::vector &points); + +template BoundingBox3Base::BoundingBox3Base(const std::vector &points); + +BoundingBox::BoundingBox(const Lines &lines) +{ + Points points; + points.reserve(lines.size()); + for (const Line &line : lines) { + points.emplace_back(line.a); + points.emplace_back(line.b); + } + *this = BoundingBox(points); +} + +void BoundingBox::polygon(Polygon* polygon) const +{ + polygon->points.clear(); + polygon->points.resize(4); + polygon->points[0](0) = this->min(0); + polygon->points[0](1) = this->min(1); + polygon->points[1](0) = this->max(0); + polygon->points[1](1) = this->min(1); + polygon->points[2](0) = this->max(0); + polygon->points[2](1) = this->max(1); + polygon->points[3](0) = this->min(0); + polygon->points[3](1) = this->max(1); +} + +Polygon BoundingBox::polygon() const +{ + Polygon p; + this->polygon(&p); + return p; +} + +BoundingBox BoundingBox::rotated(double angle) const +{ + BoundingBox out; + out.merge(this->min.rotated(angle)); + out.merge(this->max.rotated(angle)); + out.merge(Point(this->min(0), this->max(1)).rotated(angle)); + out.merge(Point(this->max(0), this->min(1)).rotated(angle)); + return out; +} + +BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const +{ + BoundingBox out; + out.merge(this->min.rotated(angle, center)); + out.merge(this->max.rotated(angle, center)); + out.merge(Point(this->min(0), this->max(1)).rotated(angle, center)); + out.merge(Point(this->max(0), this->min(1)).rotated(angle, center)); + return out; +} + +template void +BoundingBoxBase::scale(double factor) +{ + this->min *= factor; + this->max *= factor; +} +template void BoundingBoxBase::scale(double factor); +template void BoundingBoxBase::scale(double factor); +template void BoundingBoxBase::scale(double factor); + +template void +BoundingBoxBase::merge(const PointClass &point) +{ + if (this->defined) { + this->min = this->min.cwiseMin(point); + this->max = this->max.cwiseMax(point); + } else { + this->min = point; + this->max = point; + this->defined = true; + } +} +template void BoundingBoxBase::merge(const Point &point); +template void BoundingBoxBase::merge(const Vec2d &point); + +template void +BoundingBoxBase::merge(const std::vector &points) +{ + this->merge(BoundingBoxBase(points)); +} +template void BoundingBoxBase::merge(const Points &points); +template void BoundingBoxBase::merge(const Pointfs &points); + +template void +BoundingBoxBase::merge(const BoundingBoxBase &bb) +{ + assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1)); + if (bb.defined) { + if (this->defined) { + this->min = this->min.cwiseMin(bb.min); + this->max = this->max.cwiseMax(bb.max); + } else { + this->min = bb.min; + this->max = bb.max; + this->defined = true; + } + } +} +template void BoundingBoxBase::merge(const BoundingBoxBase &bb); +template void BoundingBoxBase::merge(const BoundingBoxBase &bb); + +template void +BoundingBox3Base::merge(const PointClass &point) +{ + if (this->defined) { + this->min = this->min.cwiseMin(point); + this->max = this->max.cwiseMax(point); + } else { + this->min = point; + this->max = point; + this->defined = true; + } +} +template void BoundingBox3Base::merge(const Vec3d &point); + +template void +BoundingBox3Base::merge(const std::vector &points) +{ + this->merge(BoundingBox3Base(points)); +} +template void BoundingBox3Base::merge(const Pointf3s &points); + +template void +BoundingBox3Base::merge(const BoundingBox3Base &bb) +{ + assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2)); + if (bb.defined) { + if (this->defined) { + this->min = this->min.cwiseMin(bb.min); + this->max = this->max.cwiseMax(bb.max); + } else { + this->min = bb.min; + this->max = bb.max; + this->defined = true; + } + } +} +template void BoundingBox3Base::merge(const BoundingBox3Base &bb); + +template PointClass +BoundingBoxBase::size() const +{ + return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1)); +} +template Point BoundingBoxBase::size() const; +template Vec2d BoundingBoxBase::size() const; + +template PointClass +BoundingBox3Base::size() const +{ + return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1), this->max(2) - this->min(2)); +} +template Vec3d BoundingBox3Base::size() const; + +template double BoundingBoxBase::radius() const +{ + assert(this->defined); + double x = this->max(0) - this->min(0); + double y = this->max(1) - this->min(1); + return 0.5 * sqrt(x*x+y*y); +} +template double BoundingBoxBase::radius() const; +template double BoundingBoxBase::radius() const; + +template double BoundingBox3Base::radius() const +{ + double x = this->max(0) - this->min(0); + double y = this->max(1) - this->min(1); + double z = this->max(2) - this->min(2); + return 0.5 * sqrt(x*x+y*y+z*z); +} +template double BoundingBox3Base::radius() const; + +template void +BoundingBoxBase::offset(coordf_t delta) +{ + PointClass v(delta, delta); + this->min -= v; + this->max += v; +} +template void BoundingBoxBase::offset(coordf_t delta); +template void BoundingBoxBase::offset(coordf_t delta); + +template void +BoundingBox3Base::offset(coordf_t delta) +{ + PointClass v(delta, delta, delta); + this->min -= v; + this->max += v; +} +template void BoundingBox3Base::offset(coordf_t delta); + +template PointClass +BoundingBoxBase::center() const +{ + return (this->min + this->max) / 2; +} +template Point BoundingBoxBase::center() const; +template Vec2d BoundingBoxBase::center() const; + +template PointClass +BoundingBox3Base::center() const +{ + return (this->min + this->max) / 2; +} +template Vec3d BoundingBox3Base::center() const; + +template coordf_t +BoundingBox3Base::max_size() const +{ + PointClass s = size(); + return std::max(s(0), std::max(s(1), s(2))); +} +template coordf_t BoundingBox3Base::max_size() const; + +// Align a coordinate to a grid. The coordinate may be negative, +// the aligned value will never be bigger than the original one. +static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) { + // Current C++ standard defines the result of integer division to be rounded to zero, + // for both positive and negative numbers. Here we want to round down for negative + // numbers as well. + coord_t aligned = (coord < 0) ? + ((coord - spacing + 1) / spacing) * spacing : + (coord / spacing) * spacing; + assert(aligned <= coord); + return aligned; +} + +void BoundingBox::align_to_grid(const coord_t cell_size) +{ + if (this->defined) { + min(0) = _align_to_grid(min(0), cell_size); + min(1) = _align_to_grid(min(1), cell_size); + } +} + +BoundingBoxf3 BoundingBoxf3::transformed(const Transform3d& matrix) const +{ + typedef Eigen::Matrix Vertices; + + Vertices src_vertices; + src_vertices(0, 0) = min(0); src_vertices(1, 0) = min(1); src_vertices(2, 0) = min(2); + src_vertices(0, 1) = max(0); src_vertices(1, 1) = min(1); src_vertices(2, 1) = min(2); + src_vertices(0, 2) = max(0); src_vertices(1, 2) = max(1); src_vertices(2, 2) = min(2); + src_vertices(0, 3) = min(0); src_vertices(1, 3) = max(1); src_vertices(2, 3) = min(2); + src_vertices(0, 4) = min(0); src_vertices(1, 4) = min(1); src_vertices(2, 4) = max(2); + src_vertices(0, 5) = max(0); src_vertices(1, 5) = min(1); src_vertices(2, 5) = max(2); + src_vertices(0, 6) = max(0); src_vertices(1, 6) = max(1); src_vertices(2, 6) = max(2); + src_vertices(0, 7) = min(0); src_vertices(1, 7) = max(1); src_vertices(2, 7) = max(2); + + Vertices dst_vertices = matrix * src_vertices.colwise().homogeneous(); + + Vec3d v_min(dst_vertices(0, 0), dst_vertices(1, 0), dst_vertices(2, 0)); + Vec3d v_max = v_min; + + for (int i = 1; i < 8; ++i) + { + for (int j = 0; j < 3; ++j) + { + v_min(j) = std::min(v_min(j), dst_vertices(j, i)); + v_max(j) = std::max(v_max(j), dst_vertices(j, i)); + } + } + + return BoundingBoxf3(v_min, v_max); +} + +} diff --git a/xs/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp similarity index 51% rename from xs/src/libslic3r/BoundingBox.hpp rename to src/libslic3r/BoundingBox.hpp index 5324dbe3b0..b3a1c2f5c8 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -7,11 +7,6 @@ namespace Slic3r { -typedef Point Size; -typedef Point3 Size3; -typedef Pointf Sizef; -typedef Pointf3 Sizef3; - template class BoundingBoxBase { @@ -20,25 +15,22 @@ public: PointClass max; bool defined; - BoundingBoxBase() : defined(false) {}; + BoundingBoxBase() : defined(false), min(PointClass::Zero()), max(PointClass::Zero()) {} BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : - min(pmin), max(pmax), defined(pmin.x < pmax.x && pmin.y < pmax.y) {} - BoundingBoxBase(const std::vector& points) + min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {} + BoundingBoxBase(const std::vector& points) : min(PointClass::Zero()), max(PointClass::Zero()) { if (points.empty()) - CONFESS("Empty point set supplied to BoundingBoxBase constructor"); + throw std::invalid_argument("Empty point set supplied to BoundingBoxBase constructor"); typename std::vector::const_iterator it = points.begin(); - this->min.x = this->max.x = it->x; - this->min.y = this->max.y = it->y; - for (++it; it != points.end(); ++it) - { - this->min.x = std::min(it->x, this->min.x); - this->min.y = std::min(it->y, this->min.y); - this->max.x = std::max(it->x, this->max.x); - this->max.y = std::max(it->y, this->max.y); + this->min = *it; + this->max = *it; + for (++ it; it != points.end(); ++ it) { + this->min = this->min.cwiseMin(*it); + this->max = this->max.cwiseMax(*it); } - this->defined = (this->min.x < this->max.x) && (this->min.y < this->max.y); + this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)); } void merge(const PointClass &point); void merge(const std::vector &points); @@ -46,17 +38,17 @@ public: void scale(double factor); PointClass size() const; double radius() const; - void translate(coordf_t x, coordf_t y) { assert(this->defined); this->min.translate(x, y); this->max.translate(x, y); } - void translate(const Pointf &pos) { this->translate(pos.x, pos.y); } + void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; } + void translate(const Vec2d &v) { this->min += v; this->max += v; } void offset(coordf_t delta); PointClass center() const; bool contains(const PointClass &point) const { - return point.x >= this->min.x && point.x <= this->max.x - && point.y >= this->min.y && point.y <= this->max.y; + return point(0) >= this->min(0) && point(0) <= this->max(0) + && point(1) >= this->min(1) && point(1) <= this->max(1); } bool overlap(const BoundingBoxBase &other) const { - return ! (this->max.x < other.min.x || this->min.x > other.max.x || - this->max.y < other.min.y || this->min.y > other.max.y); + return ! (this->max(0) < other.min(0) || this->min(0) > other.max(0) || + this->max(1) < other.min(1) || this->min(1) > other.max(1)); } bool operator==(const BoundingBoxBase &rhs) { return this->min == rhs.min && this->max == rhs.max; } bool operator!=(const BoundingBoxBase &rhs) { return ! (*this == rhs); } @@ -69,35 +61,33 @@ public: BoundingBox3Base() : BoundingBoxBase() {}; BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase(pmin, pmax) - { if (pmin.z >= pmax.z) BoundingBoxBase::defined = false; } + { if (pmin(2) >= pmax(2)) BoundingBoxBase::defined = false; } BoundingBox3Base(const std::vector& points) - : BoundingBoxBase(points) { if (points.empty()) - CONFESS("Empty point set supplied to BoundingBox3Base constructor"); - + throw std::invalid_argument("Empty point set supplied to BoundingBox3Base constructor"); typename std::vector::const_iterator it = points.begin(); - this->min.z = this->max.z = it->z; - for (++it; it != points.end(); ++it) - { - this->min.z = std::min(it->z, this->min.z); - this->max.z = std::max(it->z, this->max.z); + this->min = *it; + this->max = *it; + for (++ it; it != points.end(); ++ it) { + this->min = this->min.cwiseMin(*it); + this->max = this->max.cwiseMax(*it); } - this->defined &= (this->min.z < this->max.z); + this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)) && (this->min(2) < this->max(2)); } void merge(const PointClass &point); void merge(const std::vector &points); void merge(const BoundingBox3Base &bb); PointClass size() const; double radius() const; - void translate(coordf_t x, coordf_t y, coordf_t z) { this->min.translate(x, y, z); this->max.translate(x, y, z); } - void translate(const Pointf3 &pos) { this->translate(pos.x, pos.y, pos.z); } + void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; } + void translate(const Vec3d &v) { this->min += v; this->max += v; } void offset(coordf_t delta); PointClass center() const; coordf_t max_size() const; bool contains(const PointClass &point) const { - return BoundingBoxBase::contains(point) && point.z >= this->min.z && point.z <= this->max.z; + return BoundingBoxBase::contains(point) && point(2) >= this->min(2) && point(2) <= this->max(2); } bool contains(const BoundingBox3Base& other) const { @@ -105,7 +95,7 @@ public: } bool intersects(const BoundingBox3Base& other) const { - return (this->min.x < other.max.x) && (this->max.x > other.min.x) && (this->min.y < other.max.y) && (this->max.y > other.min.y) && (this->min.z < other.max.z) && (this->max.z > other.min.z); + return (this->min(0) < other.max(0)) && (this->max(0) > other.min(0)) && (this->min(1) < other.max(1)) && (this->max(1) > other.min(1)) && (this->min(2) < other.max(2)) && (this->max(2) > other.min(2)); } }; @@ -130,42 +120,42 @@ public: friend BoundingBox get_extents_rotated(const Points &points, double angle); }; -class BoundingBox3 : public BoundingBox3Base +class BoundingBox3 : public BoundingBox3Base { public: - BoundingBox3() : BoundingBox3Base() {}; - BoundingBox3(const Point3 &pmin, const Point3 &pmax) : BoundingBox3Base(pmin, pmax) {}; - BoundingBox3(const Points3& points) : BoundingBox3Base(points) {}; + BoundingBox3() : BoundingBox3Base() {}; + BoundingBox3(const Vec3crd &pmin, const Vec3crd &pmax) : BoundingBox3Base(pmin, pmax) {}; + BoundingBox3(const Points3& points) : BoundingBox3Base(points) {}; }; -class BoundingBoxf : public BoundingBoxBase +class BoundingBoxf : public BoundingBoxBase { public: - BoundingBoxf() : BoundingBoxBase() {}; - BoundingBoxf(const Pointf &pmin, const Pointf &pmax) : BoundingBoxBase(pmin, pmax) {}; - BoundingBoxf(const std::vector &points) : BoundingBoxBase(points) {}; + BoundingBoxf() : BoundingBoxBase() {}; + BoundingBoxf(const Vec2d &pmin, const Vec2d &pmax) : BoundingBoxBase(pmin, pmax) {}; + BoundingBoxf(const std::vector &points) : BoundingBoxBase(points) {}; }; -class BoundingBoxf3 : public BoundingBox3Base +class BoundingBoxf3 : public BoundingBox3Base { public: - BoundingBoxf3() : BoundingBox3Base() {}; - BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base(pmin, pmax) {}; - BoundingBoxf3(const std::vector &points) : BoundingBox3Base(points) {}; + BoundingBoxf3() : BoundingBox3Base() {}; + BoundingBoxf3(const Vec3d &pmin, const Vec3d &pmax) : BoundingBox3Base(pmin, pmax) {}; + BoundingBoxf3(const std::vector &points) : BoundingBox3Base(points) {}; - BoundingBoxf3 transformed(const std::vector& matrix) const; + BoundingBoxf3 transformed(const Transform3d& matrix) const; }; template inline bool empty(const BoundingBoxBase &bb) { - return ! bb.defined || bb.min.x >= bb.max.x || bb.min.y >= bb.max.y; + return ! bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1); } template inline bool empty(const BoundingBox3Base &bb) { - return ! bb.defined || bb.min.x >= bb.max.x || bb.min.y >= bb.max.y || bb.min.z >= bb.max.z; + return ! bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2); } } // namespace Slic3r diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/src/libslic3r/BridgeDetector.cpp similarity index 92% rename from xs/src/libslic3r/BridgeDetector.cpp rename to src/libslic3r/BridgeDetector.cpp index a5272683f0..ccc3505cea 100644 --- a/xs/src/libslic3r/BridgeDetector.cpp +++ b/src/libslic3r/BridgeDetector.cpp @@ -102,16 +102,16 @@ bool BridgeDetector::detect_angle(double bridge_direction_override) // Get an oriented bounding box around _anchor_regions. BoundingBox bbox = get_extents_rotated(this->_anchor_regions, - angle); // Cover the region with line segments. - lines.reserve((bbox.max.y - bbox.min.y + this->spacing) / this->spacing); + lines.reserve((bbox.max(1) - bbox.min(1) + this->spacing) / this->spacing); double s = sin(angle); double c = cos(angle); //FIXME Vojtech: The lines shall be spaced half the line width from the edge, but then // some of the test cases fail. Need to adjust the test cases then? -// for (coord_t y = bbox.min.y + this->spacing / 2; y <= bbox.max.y; y += this->spacing) - for (coord_t y = bbox.min.y; y <= bbox.max.y; y += this->spacing) +// for (coord_t y = bbox.min(1) + this->spacing / 2; y <= bbox.max(1); y += this->spacing) + for (coord_t y = bbox.min(1); y <= bbox.max(1); y += this->spacing) lines.push_back(Line( - Point((coord_t)round(c * bbox.min.x - s * y), (coord_t)round(c * y + s * bbox.min.x)), - Point((coord_t)round(c * bbox.max.x - s * y), (coord_t)round(c * y + s * bbox.max.x)))); + Point((coord_t)round(c * bbox.min(0) - s * y), (coord_t)round(c * y + s * bbox.min(0))), + Point((coord_t)round(c * bbox.max(0) - s * y), (coord_t)round(c * y + s * bbox.max(0))))); } double total_length = 0; @@ -182,9 +182,9 @@ std::vector BridgeDetector::bridge_direction_candidates() const /* we also test angles of each open supporting edge (this finds the optimal angle for C-shaped supports) */ - for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) - if (! edge->first_point().coincides_with(edge->last_point())) - angles.push_back(Line(edge->first_point(), edge->last_point()).direction()); + for (const Polyline &edge : this->_edges) + if (edge.first_point() != edge.last_point()) + angles.push_back(Line(edge.first_point(), edge.last_point()).direction()); // remove duplicates double min_resolution = PI/180.0; // 1 degree @@ -282,10 +282,12 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const extrusions would be anchored within such length (i.e. a slightly non-parallel bridging direction might still benefit from anchors if long enough) double angle_tolerance = PI / 180.0 * 5.0; */ - for (Lines::const_iterator line = unsupported_lines.begin(); line != unsupported_lines.end(); ++line) { - if (!Slic3r::Geometry::directions_parallel(line->direction(), angle)) - unsupported->push_back(*line); - } + for (const Line &line : unsupported_lines) + if (! Slic3r::Geometry::directions_parallel(line.direction(), angle)) { + unsupported->emplace_back(Polyline()); + unsupported->back().points.emplace_back(line.a); + unsupported->back().points.emplace_back(line.b); + } } /* diff --git a/xs/src/libslic3r/BridgeDetector.hpp b/src/libslic3r/BridgeDetector.hpp similarity index 100% rename from xs/src/libslic3r/BridgeDetector.hpp rename to src/libslic3r/BridgeDetector.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt new file mode 100644 index 0000000000..952b11ed03 --- /dev/null +++ b/src/libslic3r/CMakeLists.txt @@ -0,0 +1,194 @@ +project(libslic3r) +cmake_minimum_required(VERSION 2.6) + +include(PrecompiledHeader) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libslic3r_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h @ONLY) + +add_library(libslic3r STATIC + pchheader.cpp + pchheader.hpp + BoundingBox.cpp + BoundingBox.hpp + BridgeDetector.cpp + BridgeDetector.hpp + ClipperUtils.cpp + ClipperUtils.hpp + Config.cpp + Config.hpp + EdgeGrid.cpp + EdgeGrid.hpp + ExPolygon.cpp + ExPolygon.hpp + ExPolygonCollection.cpp + ExPolygonCollection.hpp + Extruder.cpp + Extruder.hpp + ExtrusionEntity.cpp + ExtrusionEntity.hpp + ExtrusionEntityCollection.cpp + ExtrusionEntityCollection.hpp + ExtrusionSimulator.cpp + ExtrusionSimulator.hpp + FileParserError.hpp + Fill/Fill.cpp + Fill/Fill.hpp + Fill/Fill3DHoneycomb.cpp + Fill/Fill3DHoneycomb.hpp + Fill/FillBase.cpp + Fill/FillBase.hpp + Fill/FillConcentric.cpp + Fill/FillConcentric.hpp + Fill/FillHoneycomb.cpp + Fill/FillHoneycomb.hpp + Fill/FillGyroid.cpp + Fill/FillGyroid.hpp + Fill/FillPlanePath.cpp + Fill/FillPlanePath.hpp + Fill/FillRectilinear.cpp + Fill/FillRectilinear.hpp + Fill/FillRectilinear2.cpp + Fill/FillRectilinear2.hpp + Fill/FillRectilinear3.cpp + Fill/FillRectilinear3.hpp + Flow.cpp + Flow.hpp + Format/3mf.cpp + Format/3mf.hpp + Format/AMF.cpp + Format/AMF.hpp + Format/OBJ.cpp + Format/OBJ.hpp + Format/objparser.cpp + Format/objparser.hpp + Format/PRUS.cpp + Format/PRUS.hpp + Format/STL.cpp + Format/STL.hpp + GCode/Analyzer.cpp + GCode/Analyzer.hpp + GCode/CoolingBuffer.cpp + GCode/CoolingBuffer.hpp + GCode/PostProcessor.cpp + GCode/PostProcessor.hpp + GCode/PressureEqualizer.cpp + GCode/PressureEqualizer.hpp + GCode/PreviewData.cpp + GCode/PreviewData.hpp + GCode/PrintExtents.cpp + GCode/PrintExtents.hpp + GCode/SpiralVase.cpp + GCode/SpiralVase.hpp + GCode/ToolOrdering.cpp + GCode/ToolOrdering.hpp + GCode/WipeTower.hpp + GCode/WipeTowerPrusaMM.cpp + GCode/WipeTowerPrusaMM.hpp + GCode.cpp + GCode.hpp + GCodeReader.cpp + GCodeReader.hpp + GCodeSender.cpp + GCodeSender.hpp + GCodeTimeEstimator.cpp + GCodeTimeEstimator.hpp + GCodeWriter.cpp + GCodeWriter.hpp + Geometry.cpp + Geometry.hpp + Int128.hpp +# KdTree.hpp + Layer.cpp + Layer.hpp + LayerRegion.cpp + libslic3r.h + "${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h" + Line.cpp + Line.hpp + Model.cpp + Model.hpp + ModelArrange.hpp + ModelArrange.cpp + MotionPlanner.cpp + MotionPlanner.hpp + MultiPoint.cpp + MultiPoint.hpp + MutablePriorityQueue.hpp + PerimeterGenerator.cpp + PerimeterGenerator.hpp + PlaceholderParser.cpp + PlaceholderParser.hpp + Point.cpp + Point.hpp + Polygon.cpp + Polygon.hpp + Polyline.cpp + Polyline.hpp + PolylineCollection.cpp + PolylineCollection.hpp + Print.cpp + Print.hpp + PrintBase.cpp + PrintBase.hpp + PrintExport.hpp + PrintConfig.cpp + PrintConfig.hpp + PrintObject.cpp + PrintRegion.cpp + Rasterizer/Rasterizer.hpp + Rasterizer/Rasterizer.cpp + SLAPrint.cpp + SLAPrint.hpp + Slicing.cpp + Slicing.hpp + SlicingAdaptive.cpp + SlicingAdaptive.hpp + SupportMaterial.cpp + SupportMaterial.hpp + Surface.cpp + Surface.hpp + SurfaceCollection.cpp + SurfaceCollection.hpp + SVG.cpp + SVG.hpp + Technologies.hpp + TriangleMesh.cpp + TriangleMesh.hpp + utils.cpp + Utils.hpp + SLA/SLABoilerPlate.hpp + SLA/SLABasePool.hpp + SLA/SLABasePool.cpp + SLA/SLASupportTree.hpp + SLA/SLASupportTree.cpp + SLA/SLASupportTreeIGL.cpp + SLA/SLARotfinder.hpp + SLA/SLARotfinder.cpp + SLA/SLABoostAdapter.hpp + SLA/SLASpatIndex.hpp +) + +add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE) + +target_compile_definitions(libslic3r PUBLIC -DUSE_TBB ${PNG_DEFINITIONS}) +target_include_directories(libslic3r PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${LIBNEST2D_INCLUDES} ${PNG_INCLUDE_DIRS}) +target_link_libraries(libslic3r + libnest2d + admesh + miniz + ${Boost_LIBRARIES} + clipper + nowide + ${EXPAT_LIBRARIES} + ${GLEW_LIBRARIES} + ${PNG_LIBRARIES} + polypartition + poly2tri + qhull + semver + tbb + ) + +if(SLIC3R_PROFILE) + target_link_libraries(slic3r Shiny) +endif() diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp similarity index 98% rename from xs/src/libslic3r/ClipperUtils.cpp rename to src/libslic3r/ClipperUtils.cpp index 31a12aa77a..f00e908ce5 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -171,7 +171,7 @@ Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input) { ClipperLib::Path retval; for (Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) - retval.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y )); + retval.push_back(ClipperLib::IntPoint( (*pit)(0), (*pit)(1) )); return retval; } @@ -181,7 +181,7 @@ Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input) ClipperLib::Path output; output.reserve(input.points.size()); for (Slic3r::Points::const_reverse_iterator pit = input.points.rbegin(); pit != input.points.rend(); ++pit) - output.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y )); + output.push_back(ClipperLib::IntPoint( (*pit)(0), (*pit)(1) )); return output; } @@ -595,26 +595,26 @@ Polylines _clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, co to recombine continuous polylines. */ for (size_t i = 0; i < retval.size(); ++i) { for (size_t j = i+1; j < retval.size(); ++j) { - if (retval[i].points.back().coincides_with(retval[j].points.front())) { + if (retval[i].points.back() == retval[j].points.front()) { /* If last point of i coincides with first point of j, append points of j to i and delete j */ retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end()); retval.erase(retval.begin() + j); --j; - } else if (retval[i].points.front().coincides_with(retval[j].points.back())) { + } else if (retval[i].points.front() == retval[j].points.back()) { /* If first point of i coincides with last point of j, prepend points of j to i and delete j */ retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1); retval.erase(retval.begin() + j); --j; - } else if (retval[i].points.front().coincides_with(retval[j].points.front())) { + } else if (retval[i].points.front() == retval[j].points.front()) { /* Since Clipper does not preserve orientation of polylines, also check the case when first point of i coincides with first point of j. */ retval[j].reverse(); retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1); retval.erase(retval.begin() + j); --j; - } else if (retval[i].points.back().coincides_with(retval[j].points.back())) { + } else if (retval[i].points.back() == retval[j].points.back()) { /* Since Clipper does not preserve orientation of polylines, also check the case when last point of i coincides with last point of j. */ retval[j].reverse(); @@ -634,8 +634,8 @@ _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons // convert Lines to Polylines Polylines polylines; polylines.reserve(subject.size()); - for (Lines::const_iterator line = subject.begin(); line != subject.end(); ++line) - polylines.push_back(*line); + for (const Line &line : subject) + polylines.emplace_back(Polyline(line.a, line.b)); // perform operation polylines = _clipper_pl(clipType, polylines, clip, safety_offset_); diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp similarity index 100% rename from xs/src/libslic3r/ClipperUtils.hpp rename to src/libslic3r/ClipperUtils.hpp diff --git a/xs/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp similarity index 83% rename from xs/src/libslic3r/Config.cpp rename to src/libslic3r/Config.cpp index 5db093c5c0..e065360aa0 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include namespace Slic3r { @@ -439,14 +440,16 @@ void ConfigBase::load_from_gcode_file(const std::string &file) ifs.read(data.data(), data_length); ifs.close(); - load_from_gcode_string(data.data()); + size_t key_value_pairs = load_from_gcode_string(data.data()); + if (key_value_pairs < 80) + throw std::runtime_error((boost::format("Suspiciously low number of configuration values extracted from %1: %2") % file % key_value_pairs).str()); } // Load the config keys from the given string. -void ConfigBase::load_from_gcode_string(const char* str) +size_t ConfigBase::load_from_gcode_string(const char* str) { if (str == nullptr) - return; + return 0; // Walk line by line in reverse until a non-configuration key appears. char *data_start = const_cast(str); @@ -497,11 +500,8 @@ void ConfigBase::load_from_gcode_string(const char* str) } end = start; } - if (num_key_value_pairs < 90) { - char msg[80]; - sprintf(msg, "Suspiciously low number of configuration values extracted: %d", num_key_value_pairs); - throw std::runtime_error(msg); - } + + return num_key_value_pairs; } void ConfigBase::save(const std::string &file) const @@ -566,6 +566,103 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre return opt; } +void DynamicConfig::read_cli(const std::vector &tokens, t_config_option_keys* extra) +{ + std::vector args; + // push a bogus executable name (argv[0]) + args.emplace_back(const_cast("")); + for (size_t i = 0; i < tokens.size(); ++ i) + args.emplace_back(const_cast(tokens[i].c_str())); + this->read_cli(args.size(), &args[0], extra); +} + +bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra) +{ + // cache the CLI option => opt_key mapping + std::map opts; + for (const auto &oit : this->def()->options) { + std::string cli = oit.second.cli; + cli = cli.substr(0, cli.find("=")); + boost::trim_right_if(cli, boost::is_any_of("!")); + std::vector tokens; + boost::split(tokens, cli, boost::is_any_of("|")); + for (const std::string &t : tokens) + opts[t] = oit.first; + } + + bool parse_options = true; + for (int i = 1; i < argc; ++ i) { + std::string token = argv[i]; + // Store non-option arguments in the provided vector. + if (! parse_options || ! boost::starts_with(token, "-")) { + extra->push_back(token); + continue; + } + // Stop parsing tokens as options when -- is supplied. + if (token == "--") { + parse_options = false; + continue; + } + // Remove leading dashes + boost::trim_left_if(token, boost::is_any_of("-")); + // Remove the "no-" prefix used to negate boolean options. + bool no = false; + if (boost::starts_with(token, "no-")) { + no = true; + boost::replace_first(token, "no-", ""); + } + // Read value when supplied in the --key=value form. + std::string value; + { + size_t equals_pos = token.find("="); + if (equals_pos != std::string::npos) { + value = token.substr(equals_pos+1); + token.erase(equals_pos); + } + } + // Look for the cli -> option mapping. + const auto it = opts.find(token); + if (it == opts.end()) { + printf("Warning: unknown option --%s\n", token.c_str()); + // instead of continuing, return false to caller + // to stop execution and print usage + return false; + //continue; + } + const t_config_option_key opt_key = it->second; + const ConfigOptionDef &optdef = this->def()->options.at(opt_key); + // If the option type expects a value and it was not already provided, + // look for it in the next token. + if (optdef.type != coBool && optdef.type != coBools && value.empty()) { + if (i == (argc-1)) { + printf("No value supplied for --%s\n", token.c_str()); + continue; + } + value = argv[++ i]; + } + // Store the option value. + const bool existing = this->has(opt_key); + if (ConfigOptionBool* opt = this->opt(opt_key, true)) { + opt->value = !no; + } else if (ConfigOptionBools* opt = this->opt(opt_key, true)) { + if (!existing) opt->values.clear(); // remove the default values + opt->values.push_back(!no); + } else if (ConfigOptionStrings* opt = this->opt(opt_key, true)) { + if (!existing) opt->values.clear(); // remove the default values + opt->deserialize(value, true); + } else if (ConfigOptionFloats* opt = this->opt(opt_key, true)) { + if (!existing) opt->values.clear(); // remove the default values + opt->deserialize(value, true); + } else if (ConfigOptionPoints* opt = this->opt(opt_key, true)) { + if (!existing) opt->values.clear(); // remove the default values + opt->deserialize(value, true); + } else { + this->set_deserialize(opt_key, value, true); + } + } + return true; +} + t_config_option_keys DynamicConfig::keys() const { t_config_option_keys keys; @@ -592,7 +689,7 @@ void StaticConfig::set_defaults() t_config_option_keys StaticConfig::keys() const { t_config_option_keys keys; - assert(this->def != nullptr); + assert(this->def() != nullptr); for (const auto &opt_def : this->def()->options) if (this->option(opt_def.first) != nullptr) keys.push_back(opt_def.first); diff --git a/xs/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp similarity index 91% rename from xs/src/libslic3r/Config.hpp rename to src/libslic3r/Config.hpp index 2d995551ca..04cc45768a 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -13,6 +13,7 @@ #include "libslic3r.h" #include "Point.hpp" +#include #include namespace Slic3r { @@ -49,9 +50,9 @@ enum ConfigOptionType { coPercents = coPercent + coVectorType, // a fraction or an absolute value coFloatOrPercent = 5, - // single 2d point. Currently not used. + // single 2d point (Point2f). Currently not used. coPoint = 6, - // vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets. + // vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets. coPoints = coPoint + coVectorType, // single boolean value coBool = 7, @@ -61,6 +62,12 @@ enum ConfigOptionType { coEnum = 8, }; +enum ConfigOptionMode { + comSimple, + comAdvanced, + comExpert +}; + // A generic value of a configuration option. class ConfigOption { public: @@ -622,11 +629,11 @@ public: } }; -class ConfigOptionPoint : public ConfigOptionSingle +class ConfigOptionPoint : public ConfigOptionSingle { public: - ConfigOptionPoint() : ConfigOptionSingle(Pointf(0,0)) {} - explicit ConfigOptionPoint(const Pointf &value) : ConfigOptionSingle(value) {} + ConfigOptionPoint() : ConfigOptionSingle(Vec2d(0,0)) {} + explicit ConfigOptionPoint(const Vec2d &value) : ConfigOptionSingle(value) {} static ConfigOptionType static_type() { return coPoint; } ConfigOptionType type() const override { return static_type(); } @@ -637,9 +644,9 @@ public: std::string serialize() const override { std::ostringstream ss; - ss << this->value.x; + ss << this->value(0); ss << ","; - ss << this->value.y; + ss << this->value(1); return ss.str(); } @@ -647,18 +654,18 @@ public: { UNUSED(append); char dummy; - return sscanf(str.data(), " %lf , %lf %c", &this->value.x, &this->value.y, &dummy) == 2 || - sscanf(str.data(), " %lf x %lf %c", &this->value.x, &this->value.y, &dummy) == 2; + return sscanf(str.data(), " %lf , %lf %c", &this->value(0), &this->value(1), &dummy) == 2 || + sscanf(str.data(), " %lf x %lf %c", &this->value(0), &this->value(1), &dummy) == 2; } }; -class ConfigOptionPoints : public ConfigOptionVector +class ConfigOptionPoints : public ConfigOptionVector { public: - ConfigOptionPoints() : ConfigOptionVector() {} - explicit ConfigOptionPoints(size_t n, const Pointf &value) : ConfigOptionVector(n, value) {} - explicit ConfigOptionPoints(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} - explicit ConfigOptionPoints(const std::vector &values) : ConfigOptionVector(values) {} + ConfigOptionPoints() : ConfigOptionVector() {} + explicit ConfigOptionPoints(size_t n, const Vec2d &value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionPoints(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + explicit ConfigOptionPoints(const std::vector &values) : ConfigOptionVector(values) {} static ConfigOptionType static_type() { return coPoints; } ConfigOptionType type() const override { return static_type(); } @@ -671,9 +678,9 @@ public: std::ostringstream ss; for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { if (it - this->values.begin() != 0) ss << ","; - ss << it->x; + ss << (*it)(0); ss << "x"; - ss << it->y; + ss << (*it)(1); } return ss.str(); } @@ -696,13 +703,13 @@ public: std::istringstream is(str); std::string point_str; while (std::getline(is, point_str, ',')) { - Pointf point; + Vec2d point(Vec2d::Zero()); std::istringstream iss(point_str); std::string coord_str; if (std::getline(iss, coord_str, 'x')) { - std::istringstream(coord_str) >> point.x; + std::istringstream(coord_str) >> point(0); if (std::getline(iss, coord_str, 'x')) { - std::istringstream(coord_str) >> point.y; + std::istringstream(coord_str) >> point(1); } } this->values.push_back(point); @@ -756,7 +763,7 @@ public: } //FIXME this smells, the parent class has the method declared returning (unsigned char&). - bool get_at(size_t i) const { return bool((i < this->values.size()) ? this->values[i] : this->values.front()); } + bool get_at(size_t i) const { return ((i < this->values.size()) ? this->values[i] : this->values.front()) != 0; } std::string serialize() const override { @@ -810,6 +817,22 @@ public: ConfigOption* clone() const override { return new ConfigOptionEnum(*this); } ConfigOptionEnum& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionEnum &rhs) const { return this->value == rhs.value; } + int getInt() const override { return (int)this->value; } + + bool operator==(const ConfigOption &rhs) const override + { + if (rhs.type() != this->type()) + throw std::runtime_error("ConfigOptionEnum: Comparing incompatible types"); + // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum + return this->value == (T)rhs.getInt(); + } + + void set(const ConfigOption *rhs) override { + if (rhs->type() != this->type()) + throw std::runtime_error("ConfigOptionEnum: Assigning an incompatible type"); + // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum + this->value = (T)rhs->getInt(); + } std::string serialize() const override { @@ -821,12 +844,7 @@ public: bool deserialize(const std::string &str, bool append = false) override { UNUSED(append); - const t_config_enum_values &enum_keys_map = ConfigOptionEnum::get_enum_values(); - auto it = enum_keys_map.find(str); - if (it == enum_keys_map.end()) - return false; - this->value = static_cast(it->second); - return true; + return from_string(str, this->value); } static bool has(T value) @@ -838,7 +856,7 @@ public: } // Map from an enum name to an enum integer value. - static t_config_enum_names& get_enum_names() + static const t_config_enum_names& get_enum_names() { static t_config_enum_names names; if (names.empty()) { @@ -855,7 +873,17 @@ public: return names; } // Map from an enum name to an enum integer value. - static t_config_enum_values& get_enum_values(); + static const t_config_enum_values& get_enum_values(); + + static bool from_string(const std::string &str, T &value) + { + const t_config_enum_values &enum_keys_map = ConfigOptionEnum::get_enum_values(); + auto it = enum_keys_map.find(str); + if (it == enum_keys_map.end()) + return false; + value = static_cast(it->second); + return true; + } }; // Generic enum configuration value. @@ -874,6 +902,21 @@ public: ConfigOptionEnumGeneric& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionEnumGeneric &rhs) const { return this->value == rhs.value; } + bool operator==(const ConfigOption &rhs) const override + { + if (rhs.type() != this->type()) + throw std::runtime_error("ConfigOptionEnumGeneric: Comparing incompatible types"); + // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum + return this->value == rhs.getInt(); + } + + void set(const ConfigOption *rhs) override { + if (rhs->type() != this->type()) + throw std::runtime_error("ConfigOptionEnumGeneric: Assigning an incompatible type"); + // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum + this->value = rhs->getInt(); + } + std::string serialize() const override { for (const auto &kvp : *this->keys_map) @@ -900,7 +943,7 @@ public: // What type? bool, int, string etc. ConfigOptionType type = coNone; // Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor. - ConfigOption *default_value = nullptr; + const ConfigOption *default_value = nullptr; // Usually empty. // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection, @@ -946,6 +989,7 @@ public: // By setting min=0, only nonnegative input is allowed. int min = INT_MIN; int max = INT_MAX; + ConfigOptionMode mode = comSimple; // Legacy names for this configuration option. // Used when parsing legacy configuration file. std::vector aliases; @@ -958,7 +1002,7 @@ public: std::vector enum_labels; // For enums (when type == coEnum). Maps enum_values to enums. // Initialized by ConfigOptionEnum::get_enum_values() - t_config_enum_values *enum_keys_map = nullptr; + const t_config_enum_values *enum_keys_map = nullptr; bool has_enum_value(const std::string &value) const { for (const std::string &v : enum_values) @@ -971,7 +1015,7 @@ public: // Map from a config option name to its definition. // The definition does not carry an actual value of the config option, only its constant default value. // t_config_option_key is std::string -typedef std::map t_optiondef_map; +typedef std::map t_optiondef_map; // Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling. // The configuration definition is static: It does not carry the actual configuration values, @@ -979,18 +1023,33 @@ typedef std::map t_optiondef_map; class ConfigDef { public: - t_optiondef_map options; - ~ConfigDef() { for (auto &opt : this->options) delete opt.second.default_value; } - ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) { - ConfigOptionDef* opt = &this->options[opt_key]; - opt->type = type; - return opt; - } + ~ConfigDef() { + for (std::pair &def : this->options) + delete def.second.default_value; + this->options.clear(); + } + + t_optiondef_map options; + bool has(const t_config_option_key &opt_key) const { return this->options.count(opt_key) > 0; } const ConfigOptionDef* get(const t_config_option_key &opt_key) const { t_optiondef_map::iterator it = const_cast(this)->options.find(opt_key); return (it == this->options.end()) ? nullptr : &it->second; } + std::vector keys() const { + std::vector out; + out.reserve(options.size()); + for(auto const& kvp : options) + out.push_back(kvp.first); + return out; + } + +protected: + ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) { + ConfigOptionDef* opt = &this->options[opt_key]; + opt->type = type; + return opt; + } }; // An abstract configuration store. @@ -1030,7 +1089,7 @@ public: TYPE* option(const t_config_option_key &opt_key, bool create = false) { ConfigOption *opt = this->optptr(opt_key, create); - assert(opt == nullptr || opt->type() == TYPE::static_type()); +// assert(opt == nullptr || opt->type() == TYPE::static_type()); return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast(opt); } template @@ -1061,7 +1120,8 @@ public: void load(const std::string &file); void load_from_ini(const std::string &file); void load_from_gcode_file(const std::string &file); - void load_from_gcode_string(const char* str); + // Returns number of key/value pairs extracted. + size_t load_from_gcode_string(const char* str); void load(const boost::property_tree::ptree &tree); void save(const std::string &file) const; @@ -1171,7 +1231,6 @@ public: // Allow DynamicConfig to be instantiated on ints own without a definition. // If the definition is not defined, the method requiring the definition will throw NoDefinitionException. const ConfigDef* def() const override { return nullptr; }; - bool has(const t_config_option_key &opt_key) const { return this->options.find(opt_key) != this->options.end(); } template T* opt(const t_config_option_key &opt_key, bool create = false) { return dynamic_cast(this->option(opt_key, create)); } template const T* opt(const t_config_option_key &opt_key) const @@ -1180,6 +1239,7 @@ public: ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override; // Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. t_config_option_keys keys() const override; + bool empty() const { return options.empty(); } // Set a value for an opt_key. Returns true if the value did not exist yet. // This DynamicConfig will take ownership of opt. @@ -1212,11 +1272,21 @@ public: int& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } const int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } + template + ENUM opt_enum(const t_config_option_key &opt_key) const { return (ENUM)dynamic_cast(this->option(opt_key))->value; } + bool opt_bool(const t_config_option_key &opt_key) const { return this->option(opt_key)->value != 0; } bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option(opt_key)->get_at(idx) != 0; } -protected: + // Command line processing + void read_cli(const std::vector &tokens, t_config_option_keys* extra); + bool read_cli(int argc, char** argv, t_config_option_keys* extra); + typedef std::map t_options_map; + t_options_map::const_iterator cbegin() const { return options.cbegin(); } + t_options_map::const_iterator cend() const { return options.cend(); } + +private: t_options_map options; }; diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp similarity index 74% rename from xs/src/libslic3r/EdgeGrid.cpp rename to src/libslic3r/EdgeGrid.cpp index 9249d0ff1b..2b2893c805 100644 --- a/xs/src/libslic3r/EdgeGrid.cpp +++ b/src/libslic3r/EdgeGrid.cpp @@ -9,9 +9,7 @@ #endif /* SLIC3R_GUI */ #include "libslic3r.h" -#include "ClipperUtils.hpp" #include "EdgeGrid.hpp" -#include "SVG.hpp" #if 0 // Enable debugging and assert in this file. @@ -119,15 +117,15 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) m_bbox.merge(pts[j]); } coord_t eps = 16; - m_bbox.min.x -= eps; - m_bbox.min.y -= eps; - m_bbox.max.x += eps; - m_bbox.max.y += eps; + m_bbox.min(0) -= eps; + m_bbox.min(1) -= eps; + m_bbox.max(0) += eps; + m_bbox.max(1) += eps; // 2) Initialize the edge grid. m_resolution = resolution; - m_cols = (m_bbox.max.x - m_bbox.min.x + m_resolution - 1) / m_resolution; - m_rows = (m_bbox.max.y - m_bbox.min.y + m_resolution - 1) / m_resolution; + m_cols = (m_bbox.max(0) - m_bbox.min(0) + m_resolution - 1) / m_resolution; + m_rows = (m_bbox.max(1) - m_bbox.min(1) + m_resolution - 1) / m_resolution; m_cells.assign(m_rows * m_cols, Cell()); // 3) First round of contour rasterization, count the edges per grid cell. @@ -137,15 +135,15 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) // End points of the line segment. Slic3r::Point p1(pts[j]); Slic3r::Point p2 = pts[(j + 1 == pts.size()) ? 0 : j + 1]; - p1.x -= m_bbox.min.x; - p1.y -= m_bbox.min.y; - p2.x -= m_bbox.min.x; - p2.y -= m_bbox.min.y; + p1(0) -= m_bbox.min(0); + p1(1) -= m_bbox.min(1); + p2(0) -= m_bbox.min(0); + p2(1) -= m_bbox.min(1); // Get the cells of the end points. - coord_t ix = p1.x / m_resolution; - coord_t iy = p1.y / m_resolution; - coord_t ixb = p2.x / m_resolution; - coord_t iyb = p2.y / m_resolution; + coord_t ix = p1(0) / m_resolution; + coord_t iy = p1(1) / m_resolution; + coord_t ixb = p2(0) / m_resolution; + coord_t iyb = p2(1) / m_resolution; assert(ix >= 0 && ix < m_cols); assert(iy >= 0 && iy < m_rows); assert(ixb >= 0 && ixb < m_cols); @@ -156,13 +154,13 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) // Both ends fall into the same cell. continue; // Raster the centeral part of the line. - coord_t dx = std::abs(p2.x - p1.x); - coord_t dy = std::abs(p2.y - p1.y); - if (p1.x < p2.x) { - int64_t ex = int64_t((ix + 1)*m_resolution - p1.x) * int64_t(dy); - if (p1.y < p2.y) { + coord_t dx = std::abs(p2(0) - p1(0)); + coord_t dy = std::abs(p2(1) - p1(1)); + if (p1(0) < p2(0)) { + int64_t ex = int64_t((ix + 1)*m_resolution - p1(0)) * int64_t(dy); + if (p1(1) < p2(1)) { // x positive, y positive - int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx); + int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx); do { assert(ix <= ixb && iy <= iyb); if (ex < ey) { @@ -187,7 +185,7 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) } else { // x positive, y non positive - int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx); + int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx); do { assert(ix <= ixb && iy >= iyb); if (ex <= ey) { @@ -205,10 +203,10 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) } } else { - int64_t ex = int64_t(p1.x - ix*m_resolution) * int64_t(dy); - if (p1.y < p2.y) { + int64_t ex = int64_t(p1(0) - ix*m_resolution) * int64_t(dy); + if (p1(1) < p2(1)) { // x non positive, y positive - int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx); + int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx); do { assert(ix >= ixb && iy <= iyb); if (ex < ey) { @@ -227,7 +225,7 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) } else { // x non positive, y non positive - int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx); + int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx); do { assert(ix >= ixb && iy >= iyb); if (ex < ey) { @@ -281,15 +279,15 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) // End points of the line segment. Slic3r::Point p1(pts[j]); Slic3r::Point p2 = pts[(j + 1 == pts.size()) ? 0 : j + 1]; - p1.x -= m_bbox.min.x; - p1.y -= m_bbox.min.y; - p2.x -= m_bbox.min.x; - p2.y -= m_bbox.min.y; + p1(0) -= m_bbox.min(0); + p1(1) -= m_bbox.min(1); + p2(0) -= m_bbox.min(0); + p2(1) -= m_bbox.min(1); // Get the cells of the end points. - coord_t ix = p1.x / m_resolution; - coord_t iy = p1.y / m_resolution; - coord_t ixb = p2.x / m_resolution; - coord_t iyb = p2.y / m_resolution; + coord_t ix = p1(0) / m_resolution; + coord_t iy = p1(1) / m_resolution; + coord_t ixb = p2(0) / m_resolution; + coord_t iyb = p2(1) / m_resolution; assert(ix >= 0 && ix < m_cols); assert(iy >= 0 && iy < m_rows); assert(ixb >= 0 && ixb < m_cols); @@ -300,13 +298,13 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) // Both ends fall into the same cell. continue; // Raster the centeral part of the line. - coord_t dx = std::abs(p2.x - p1.x); - coord_t dy = std::abs(p2.y - p1.y); - if (p1.x < p2.x) { - int64_t ex = int64_t((ix + 1)*m_resolution - p1.x) * int64_t(dy); - if (p1.y < p2.y) { + coord_t dx = std::abs(p2(0) - p1(0)); + coord_t dy = std::abs(p2(1) - p1(1)); + if (p1(0) < p2(0)) { + int64_t ex = int64_t((ix + 1)*m_resolution - p1(0)) * int64_t(dy); + if (p1(1) < p2(1)) { // x positive, y positive - int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx); + int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx); do { assert(ix <= ixb && iy <= iyb); if (ex < ey) { @@ -331,7 +329,7 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) } else { // x positive, y non positive - int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx); + int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx); do { assert(ix <= ixb && iy >= iyb); if (ex <= ey) { @@ -349,10 +347,10 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) } } else { - int64_t ex = int64_t(p1.x - ix*m_resolution) * int64_t(dy); - if (p1.y < p2.y) { + int64_t ex = int64_t(p1(0) - ix*m_resolution) * int64_t(dy); + if (p1(1) < p2(1)) { // x non positive, y positive - int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx); + int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx); do { assert(ix >= ixb && iy <= iyb); if (ex < ey) { @@ -371,7 +369,7 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) } else { // x non positive, y non positive - int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx); + int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx); do { assert(ix >= ixb && iy >= iyb); if (ex < ey) { @@ -431,15 +429,15 @@ bool EdgeGrid::Grid::intersect(const MultiPoint &polyline, bool closed) Point p1 = p1src; Point p2 = p2src; // Discretize the line segment p1, p2. - p1.x -= m_bbox.min.x; - p1.y -= m_bbox.min.y; - p2.x -= m_bbox.min.x; - p2.y -= m_bbox.min.y; + p1(0) -= m_bbox.min(0); + p1(1) -= m_bbox.min(1); + p2(0) -= m_bbox.min(0); + p2(1) -= m_bbox.min(1); // Get the cells of the end points. - coord_t ix = div_floor(p1.x, m_resolution); - coord_t iy = div_floor(p1.y, m_resolution); - coord_t ixb = div_floor(p2.x, m_resolution); - coord_t iyb = div_floor(p2.y, m_resolution); + coord_t ix = div_floor(p1(0), m_resolution); + coord_t iy = div_floor(p1(1), m_resolution); + coord_t ixb = div_floor(p2(0), m_resolution); + coord_t iyb = div_floor(p2(1), m_resolution); // assert(ix >= 0 && ix < m_cols); // assert(iy >= 0 && iy < m_rows); // assert(ixb >= 0 && ixb < m_cols); @@ -451,12 +449,12 @@ bool EdgeGrid::Grid::intersect(const MultiPoint &polyline, bool closed) // Both ends fall into the same cell. continue; // Raster the centeral part of the line. - coord_t dx = std::abs(p2.x - p1.x); - coord_t dy = std::abs(p2.y - p1.y); - if (p1.x < p2.x) { - int64_t ex = int64_t((ix + 1)*m_resolution - p1.x) * int64_t(dy); - if (p1.y < p2.y) { - int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx); + coord_t dx = std::abs(p2(0) - p1(0)); + coord_t dy = std::abs(p2(1) - p1(1)); + if (p1(0) < p2(0)) { + int64_t ex = int64_t((ix + 1)*m_resolution - p1(0)) * int64_t(dy); + if (p1(1) < p2(1)) { + int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx); do { assert(ix <= ixb && iy <= iyb); if (ex < ey) { @@ -481,7 +479,7 @@ bool EdgeGrid::Grid::intersect(const MultiPoint &polyline, bool closed) } while (ix != ixb || iy != iyb); } else { - int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx); + int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx); do { assert(ix <= ixb && iy >= iyb); if (ex <= ey) { @@ -500,9 +498,9 @@ bool EdgeGrid::Grid::intersect(const MultiPoint &polyline, bool closed) } } else { - int64_t ex = int64_t(p1.x - ix*m_resolution) * int64_t(dy); - if (p1.y < p2.y) { - int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx); + int64_t ex = int64_t(p1(0) - ix*m_resolution) * int64_t(dy); + if (p1(1) < p2(1)) { + int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx); do { assert(ix >= ixb && iy <= iyb); if (ex < ey) { @@ -521,7 +519,7 @@ bool EdgeGrid::Grid::intersect(const MultiPoint &polyline, bool closed) } while (ix != ixb || iy != iyb); } else { - int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx); + int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx); do { assert(ix >= ixb && iy >= iyb); if (ex < ey) { @@ -558,8 +556,8 @@ bool EdgeGrid::Grid::line_cell_intersect(const Point &p1a, const Point &p2a, con { BoundingBox bbox(p1a, p1a); bbox.merge(p2a); - int64_t va_x = p2a.x - p1a.x; - int64_t va_y = p2a.y - p1a.y; + int64_t va_x = p2a(0) - p1a(0); + int64_t va_y = p2a(1) - p1a(1); for (size_t i = cell.begin; i != cell.end; ++ i) { const std::pair &cell_data = m_cell_data[i]; // Contour indexed by the ith line of this cell. @@ -578,21 +576,21 @@ bool EdgeGrid::Grid::line_cell_intersect(const Point &p1a, const Point &p2a, con if (! bbox.overlap(bbox2)) continue; // Now intersect the two line segments using exact arithmetics. - int64_t w1_x = p1b.x - p1a.x; - int64_t w1_y = p1b.y - p1a.y; - int64_t w2_x = p2b.x - p1a.x; - int64_t w2_y = p2b.y - p1a.y; + int64_t w1_x = p1b(0) - p1a(0); + int64_t w1_y = p1b(1) - p1a(1); + int64_t w2_x = p2b(0) - p1a(0); + int64_t w2_y = p2b(1) - p1a(1); int64_t side1 = va_x * w1_y - va_y * w1_x; int64_t side2 = va_x * w2_y - va_y * w2_x; if (side1 == side2 && side1 != 0) // The line segments don't intersect. continue; - w1_x = p1a.x - p1b.x; - w1_y = p1a.y - p1b.y; - w2_x = p2a.x - p1b.x; - w2_y = p2a.y - p1b.y; - int64_t vb_x = p2b.x - p1b.x; - int64_t vb_y = p2b.y - p1b.y; + w1_x = p1a(0) - p1b(0); + w1_y = p1a(1) - p1b(1); + w2_x = p2a(0) - p1b(0); + w2_y = p2a(1) - p1b(1); + int64_t vb_x = p2b(0) - p1b(0); + int64_t vb_y = p2b(1) - p1b(1); side1 = vb_x * w1_y - vb_y * w1_x; side2 = vb_x * w2_y - vb_y * w2_x; if (side1 == side2 && side1 != 0) @@ -609,13 +607,13 @@ bool EdgeGrid::Grid::line_cell_intersect(const Point &p1a, const Point &p2a, con bool EdgeGrid::Grid::inside(const Point &pt_src) { Point p = pt_src; - p.x -= m_bbox.min.x; - p.y -= m_bbox.min.y; + p(0) -= m_bbox.min(0); + p(1) -= m_bbox.min(1); // Get the cell of the point. - if (p.x < 0 || p.y < 0) + if (p(0) < 0 || p(1) < 0) return false; - coord_t ix = p.x / m_resolution; - coord_t iy = p.y / m_resolution; + coord_t ix = p(0) / m_resolution; + coord_t iy = p(1) / m_resolution; if (ix >= this->m_cols || iy >= this->m_rows) return false; @@ -636,21 +634,21 @@ bool EdgeGrid::Grid::inside(const Point &pt_src) idx2 = 0; const Point &p1 = contour[idx1]; const Point &p2 = contour[idx2]; - if (p1.y < p2.y) { - if (p.y < p1.y || p.y > p2.y) + if (p1(1) < p2(1)) { + if (p(1) < p1(1) || p(1) > p2(1)) continue; //FIXME finish this! int64_t vx = 0;// pt_src //FIXME finish this! int64_t det = 0; - } else if (p1.y != p2.y) { - assert(p1.y > p2.y); - if (p.y < p2.y || p.y > p1.y) + } else if (p1(1) != p2(1)) { + assert(p1(1) > p2(1)); + if (p(1) < p2(1) || p(1) > p1(1)) continue; } else { - assert(p1.y == p2.y); - if (p1.y == p.y) { - if (p.x >= p1.x && p.x <= p2.x) + assert(p1(1) == p2(1)); + if (p1(1) == p(1)) { + if (p(0) >= p1(0) && p(0) <= p2(0)) // On the segment. return true; // Before or after the segment. @@ -758,8 +756,8 @@ void EdgeGrid::Grid::calculate_sdf() float search_radius = float(m_resolution<<1); m_signed_distance_field.assign(nrows * ncols, search_radius); // For each cell: - for (int r = 0; r < (int)m_rows; ++ r) { - for (int c = 0; c < (int)m_cols; ++ c) { + for (size_t r = 0; r < m_rows; ++ r) { + for (size_t c = 0; c < m_cols; ++ c) { const Cell &cell = m_cells[r * m_cols + c]; // For each segment in the cell: for (size_t i = cell.begin; i != cell.end; ++ i) { @@ -769,9 +767,9 @@ void EdgeGrid::Grid::calculate_sdf() const Slic3r::Point &p1 = pts[ipt]; const Slic3r::Point &p2 = pts[(ipt + 1 == pts.size()) ? 0 : ipt + 1]; // Segment vector - const Slic3r::Point v_seg = p1.vector_to(p2); + const Slic3r::Point v_seg = p2 - p1; // l2 of v_seg - const int64_t l2_seg = int64_t(v_seg.x) * int64_t(v_seg.x) + int64_t(v_seg.y) * int64_t(v_seg.y); + const int64_t l2_seg = int64_t(v_seg(0)) * int64_t(v_seg(0)) + int64_t(v_seg(1)) * int64_t(v_seg(1)); // For each corner of this cell and its 1 ring neighbours: for (int corner_y = -1; corner_y < 3; ++ corner_y) { coord_t corner_r = r + corner_y; @@ -782,28 +780,28 @@ void EdgeGrid::Grid::calculate_sdf() if (corner_c < 0 || corner_c >= ncols) continue; float &d_min = m_signed_distance_field[corner_r * ncols + corner_c]; - Slic3r::Point pt(m_bbox.min.x + corner_c * m_resolution, m_bbox.min.y + corner_r * m_resolution); - Slic3r::Point v_pt = p1.vector_to(pt); + Slic3r::Point pt(m_bbox.min(0) + corner_c * m_resolution, m_bbox.min(1) + corner_r * m_resolution); + Slic3r::Point v_pt = pt - p1; // dot(p2-p1, pt-p1) - int64_t t_pt = int64_t(v_seg.x) * int64_t(v_pt.x) + int64_t(v_seg.y) * int64_t(v_pt.y); + int64_t t_pt = int64_t(v_seg(0)) * int64_t(v_pt(0)) + int64_t(v_seg(1)) * int64_t(v_pt(1)); if (t_pt < 0) { // Closest to p1. - double dabs = sqrt(int64_t(v_pt.x) * int64_t(v_pt.x) + int64_t(v_pt.y) * int64_t(v_pt.y)); + double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1))); if (dabs < d_min) { // Previous point. const Slic3r::Point &p0 = pts[(ipt == 0) ? (pts.size() - 1) : ipt - 1]; - Slic3r::Point v_seg_prev = p0.vector_to(p1); - int64_t t2_pt = int64_t(v_seg_prev.x) * int64_t(v_pt.x) + int64_t(v_seg_prev.y) * int64_t(v_pt.y); + Slic3r::Point v_seg_prev = p1 - p0; + int64_t t2_pt = int64_t(v_seg_prev(0)) * int64_t(v_pt(0)) + int64_t(v_seg_prev(1)) * int64_t(v_pt(1)); if (t2_pt > 0) { // Inside the wedge between the previous and the next segment. // Set the signum depending on whether the vertex is convex or reflex. - int64_t det = int64_t(v_seg_prev.x) * int64_t(v_seg.y) - int64_t(v_seg_prev.y) * int64_t(v_seg.x); + int64_t det = int64_t(v_seg_prev(0)) * int64_t(v_seg(1)) - int64_t(v_seg_prev(1)) * int64_t(v_seg(0)); assert(det != 0); d_min = dabs; // Fill in an unsigned vector towards the zero iso surface. float *l = &L[(corner_r * ncols + corner_c) << 1]; - l[0] = std::abs(v_pt.x); - l[1] = std::abs(v_pt.y); + l[0] = std::abs(v_pt(0)); + l[1] = std::abs(v_pt(1)); #ifdef _DEBUG double dabs2 = sqrt(l[0]*l[0]+l[1]*l[1]); assert(std::abs(dabs-dabs2) < 1e-4 * std::max(dabs, dabs2)); @@ -818,7 +816,7 @@ void EdgeGrid::Grid::calculate_sdf() } else { // Closest to the segment. assert(t_pt >= 0 && t_pt <= l2_seg); - int64_t d_seg = int64_t(v_seg.y) * int64_t(v_pt.x) - int64_t(v_seg.x) * int64_t(v_pt.y); + int64_t d_seg = int64_t(v_seg(1)) * int64_t(v_pt(0)) - int64_t(v_seg(0)) * int64_t(v_pt(1)); double d = double(d_seg) / sqrt(double(l2_seg)); double dabs = std::abs(d); if (dabs < d_min) { @@ -826,8 +824,8 @@ void EdgeGrid::Grid::calculate_sdf() // Fill in an unsigned vector towards the zero iso surface. float *l = &L[(corner_r * ncols + corner_c) << 1]; float linv = float(d_seg) / float(l2_seg); - l[0] = std::abs(float(v_seg.y) * linv); - l[1] = std::abs(float(v_seg.x) * linv); + l[0] = std::abs(float(v_seg(1)) * linv); + l[1] = std::abs(float(v_seg(0)) * linv); #ifdef _DEBUG double dabs2 = sqrt(l[0]*l[0]+l[1]*l[1]); assert(std::abs(dabs-dabs2) <= 1e-4 * std::max(dabs, dabs2)); @@ -844,8 +842,6 @@ void EdgeGrid::Grid::calculate_sdf() #if 0 static int iRun = 0; ++ iRun; - if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) - wxImage::AddHandler(new wxPNGHandler); //#ifdef SLIC3R_GUI { wxImage img(ncols, nrows); @@ -1063,8 +1059,8 @@ void EdgeGrid::Grid::calculate_sdf() float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const { - coord_t x = pt.x - m_bbox.min.x; - coord_t y = pt.y - m_bbox.min.y; + coord_t x = pt(0) - m_bbox.min(0); + coord_t y = pt(1) - m_bbox.min(1); coord_t w = m_resolution * m_cols; coord_t h = m_resolution * m_rows; bool clamped = false; @@ -1128,39 +1124,39 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const { BoundingBox bbox; - bbox.min = bbox.max = Point(pt.x - m_bbox.min.x, pt.y - m_bbox.min.y); + bbox.min = bbox.max = Point(pt(0) - m_bbox.min(0), pt(1) - m_bbox.min(1)); bbox.defined = true; // Upper boundary, round to grid and test validity. - bbox.max.x += search_radius; - bbox.max.y += search_radius; - if (bbox.max.x < 0 || bbox.max.y < 0) + bbox.max(0) += search_radius; + bbox.max(1) += search_radius; + if (bbox.max(0) < 0 || bbox.max(1) < 0) return false; - bbox.max.x /= m_resolution; - bbox.max.y /= m_resolution; - if (bbox.max.x >= m_cols) - bbox.max.x = m_cols - 1; - if (bbox.max.y >= m_rows) - bbox.max.y = m_rows - 1; + bbox.max(0) /= m_resolution; + bbox.max(1) /= m_resolution; + if (bbox.max(0) >= m_cols) + bbox.max(0) = m_cols - 1; + if (bbox.max(1) >= m_rows) + bbox.max(1) = m_rows - 1; // Lower boundary, round to grid and test validity. - bbox.min.x -= search_radius; - bbox.min.y -= search_radius; - if (bbox.min.x < 0) - bbox.min.x = 0; - if (bbox.min.y < 0) - bbox.min.y = 0; - bbox.min.x /= m_resolution; - bbox.min.y /= m_resolution; + bbox.min(0) -= search_radius; + bbox.min(1) -= search_radius; + if (bbox.min(0) < 0) + bbox.min(0) = 0; + if (bbox.min(1) < 0) + bbox.min(1) = 0; + bbox.min(0) /= m_resolution; + bbox.min(1) /= m_resolution; // Is the interval empty? - if (bbox.min.x > bbox.max.x || - bbox.min.y > bbox.max.y) + if (bbox.min(0) > bbox.max(0) || + bbox.min(1) > bbox.max(1)) return false; // Traverse all cells in the bounding box. float d_min = search_radius; // Signum of the distance field at pt. int sign_min = 0; bool on_segment = false; - for (int r = bbox.min.y; r <= bbox.max.y; ++ r) { - for (int c = bbox.min.x; c <= bbox.max.x; ++ c) { + for (int r = bbox.min(1); r <= bbox.max(1); ++ r) { + for (int c = bbox.min(0); c <= bbox.max(0); ++ c) { const Cell &cell = m_cells[r * m_cols + c]; for (size_t i = cell.begin; i < cell.end; ++ i) { const Slic3r::Points &pts = *m_contours[m_cell_data[i].first]; @@ -1168,25 +1164,25 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu // End points of the line segment. const Slic3r::Point &p1 = pts[ipt]; const Slic3r::Point &p2 = pts[(ipt + 1 == pts.size()) ? 0 : ipt + 1]; - Slic3r::Point v_seg = p1.vector_to(p2); - Slic3r::Point v_pt = p1.vector_to(pt); + Slic3r::Point v_seg = p2 - p1; + Slic3r::Point v_pt = pt - p1; // dot(p2-p1, pt-p1) - int64_t t_pt = int64_t(v_seg.x) * int64_t(v_pt.x) + int64_t(v_seg.y) * int64_t(v_pt.y); + int64_t t_pt = int64_t(v_seg(0)) * int64_t(v_pt(0)) + int64_t(v_seg(1)) * int64_t(v_pt(1)); // l2 of seg - int64_t l2_seg = int64_t(v_seg.x) * int64_t(v_seg.x) + int64_t(v_seg.y) * int64_t(v_seg.y); + int64_t l2_seg = int64_t(v_seg(0)) * int64_t(v_seg(0)) + int64_t(v_seg(1)) * int64_t(v_seg(1)); if (t_pt < 0) { // Closest to p1. - double dabs = sqrt(int64_t(v_pt.x) * int64_t(v_pt.x) + int64_t(v_pt.y) * int64_t(v_pt.y)); + double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1))); if (dabs < d_min) { // Previous point. const Slic3r::Point &p0 = pts[(ipt == 0) ? (pts.size() - 1) : ipt - 1]; - Slic3r::Point v_seg_prev = p0.vector_to(p1); - int64_t t2_pt = int64_t(v_seg_prev.x) * int64_t(v_pt.x) + int64_t(v_seg_prev.y) * int64_t(v_pt.y); + Slic3r::Point v_seg_prev = p1 - p0; + int64_t t2_pt = int64_t(v_seg_prev(0)) * int64_t(v_pt(0)) + int64_t(v_seg_prev(1)) * int64_t(v_pt(1)); if (t2_pt > 0) { // Inside the wedge between the previous and the next segment. d_min = dabs; // Set the signum depending on whether the vertex is convex or reflex. - int64_t det = int64_t(v_seg_prev.x) * int64_t(v_seg.y) - int64_t(v_seg_prev.y) * int64_t(v_seg.x); + int64_t det = int64_t(v_seg_prev(0)) * int64_t(v_seg(1)) - int64_t(v_seg_prev(1)) * int64_t(v_seg(0)); assert(det != 0); sign_min = (det > 0) ? 1 : -1; on_segment = false; @@ -1199,7 +1195,7 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu } else { // Closest to the segment. assert(t_pt >= 0 && t_pt <= l2_seg); - int64_t d_seg = int64_t(v_seg.y) * int64_t(v_pt.x) - int64_t(v_seg.x) * int64_t(v_pt.y); + int64_t d_seg = int64_t(v_seg(1)) * int64_t(v_pt(0)) - int64_t(v_seg(0)) * int64_t(v_pt(1)); double d = double(d_seg) / sqrt(double(l2_seg)); double dabs = std::abs(d); if (dabs < d_min) { @@ -1313,7 +1309,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co const Line &line_next = lines[it->second]; const Vector v1 = line_current.vector(); const Vector v2 = line_next.vector(); - int64_t cross = int64_t(v1.x) * int64_t(v2.y) - int64_t(v2.x) * int64_t(v1.y); + int64_t cross = int64_t(v1(0)) * int64_t(v2(1)) - int64_t(v2(0)) * int64_t(v1(1)); if (cross > 0) { // This has to be a convex right angle. There is no better next line. i_next = it->second; @@ -1334,10 +1330,10 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co Polygon &poly = out[i]; for (size_t j = 0; j < poly.points.size(); ++ j) { Point &p = poly.points[j]; - p.x *= m_resolution; - p.y *= m_resolution; - p.x += m_bbox.min.x; - p.y += m_bbox.min.y; + p(0) *= m_resolution; + p(1) *= m_resolution; + p(0) += m_bbox.min(0); + p(1) += m_bbox.min(1); } // Shrink the contour slightly, so if the same contour gets discretized and simplified again, one will get the same result. // Remove collineaer points. @@ -1347,11 +1343,11 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co size_t j0 = (j == 0) ? poly.points.size() - 1 : j - 1; size_t j2 = (j + 1 == poly.points.size()) ? 0 : j + 1; Point v = poly.points[j2] - poly.points[j0]; - if (v.x != 0 && v.y != 0) { + if (v(0) != 0 && v(1) != 0) { // This is a corner point. Copy it to the output contour. Point p = poly.points[j]; - p.y += (v.x < 0) ? - offset : offset; - p.x += (v.y > 0) ? - offset : offset; + p(1) += (v(0) < 0) ? - offset : offset; + p(0) += (v(1) > 0) ? - offset : offset; pts.push_back(p); } } @@ -1360,103 +1356,11 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co return out; } -inline int segments_could_intersect( - const Slic3r::Point &ip1, const Slic3r::Point &ip2, - const Slic3r::Point &jp1, const Slic3r::Point &jp2) -{ - Slic3r::Point iv = ip1.vector_to(ip2); - Slic3r::Point vij1 = ip1.vector_to(jp1); - Slic3r::Point vij2 = ip1.vector_to(jp2); - int64_t tij1 = int64_t(iv.x) * int64_t(vij1.y) - int64_t(iv.y) * int64_t(vij1.x); // cross(iv, vij1) - int64_t tij2 = int64_t(iv.x) * int64_t(vij2.y) - int64_t(iv.y) * int64_t(vij2.x); // cross(iv, vij2) - int sij1 = (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0); // signum - int sij2 = (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0); - return sij1 * sij2; -} - -inline bool segments_intersect( - const Slic3r::Point &ip1, const Slic3r::Point &ip2, - const Slic3r::Point &jp1, const Slic3r::Point &jp2) -{ - return segments_could_intersect(ip1, ip2, jp1, jp2) <= 0 && - segments_could_intersect(jp1, jp2, ip1, ip2) <= 0; -} - -std::vector> EdgeGrid::Grid::intersecting_edges() const -{ - std::vector> out; - // For each cell: - for (int r = 0; r < (int)m_rows; ++ r) { - for (int c = 0; c < (int)m_cols; ++ c) { - const Cell &cell = m_cells[r * m_cols + c]; - // For each pair of segments in the cell: - for (size_t i = cell.begin; i != cell.end; ++ i) { - const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first]; - size_t ipt = m_cell_data[i].second; - // End points of the line segment and their vector. - const Slic3r::Point &ip1 = ipts[ipt]; - const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1]; - for (size_t j = i + 1; j != cell.end; ++ j) { - const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first]; - size_t jpt = m_cell_data[j].second; - // End points of the line segment and their vector. - const Slic3r::Point &jp1 = jpts[jpt]; - const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1]; - if (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) - // Segments of the same contour share a common vertex. - continue; - if (segments_intersect(ip1, ip2, jp1, jp2)) { - // The two segments intersect. Add them to the output. - int jfirst = (&jpts < &ipts) || (&jpts == &ipts && jpt < ipt); - out.emplace_back(jfirst ? - std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt)) : - std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt))); - } - } - } - } - } - Slic3r::sort_remove_duplicates(out); - return out; -} - -bool EdgeGrid::Grid::has_intersecting_edges() const -{ - // For each cell: - for (int r = 0; r < (int)m_rows; ++ r) { - for (int c = 0; c < (int)m_cols; ++ c) { - const Cell &cell = m_cells[r * m_cols + c]; - // For each pair of segments in the cell: - for (size_t i = cell.begin; i != cell.end; ++ i) { - const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first]; - size_t ipt = m_cell_data[i].second; - // End points of the line segment and their vector. - const Slic3r::Point &ip1 = ipts[ipt]; - const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1]; - for (size_t j = i + 1; j != cell.end; ++ j) { - const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first]; - size_t jpt = m_cell_data[j].second; - // End points of the line segment and their vector. - const Slic3r::Point &jp1 = jpts[jpt]; - const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1]; - if (! (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) && - segments_intersect(ip1, ip2, jp1, jp2)) - return true; - } - } - } - } - return false; -} - #if 0 void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path) { - if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) - wxImage::AddHandler(new wxPNGHandler); - - unsigned int w = (bbox.max.x - bbox.min.x + resolution - 1) / resolution; - unsigned int h = (bbox.max.y - bbox.min.y + resolution - 1) / resolution; + unsigned int w = (bbox.max(0) - bbox.min(0) + resolution - 1) / resolution; + unsigned int h = (bbox.max(1) - bbox.min(1) + resolution - 1) / resolution; wxImage img(w, h); unsigned char *data = img.GetData(); memset(data, 0, w * h * 3); @@ -1469,7 +1373,7 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo for (coord_t r = 0; r < h; ++r) { for (coord_t c = 0; c < w; ++ c) { unsigned char *pxl = data + (((h - r - 1) * w) + c) * 3; - Point pt(c * resolution + bbox.min.x, r * resolution + bbox.min.y); + Point pt(c * resolution + bbox.min(0), r * resolution + bbox.min(1)); coordf_t min_dist; bool on_segment = true; #if 0 @@ -1507,8 +1411,8 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo pxl[2] = 0; } - float gridx = float(pt.x - grid.bbox().min.x) / float(grid.resolution()); - float gridy = float(pt.y - grid.bbox().min.y) / float(grid.resolution()); + float gridx = float(pt(0) - grid.bbox().min(0)) / float(grid.resolution()); + float gridy = float(pt(1) - grid.bbox().min(1)) / float(grid.resolution()); if (gridx >= -0.4f && gridy >= -0.4f && gridx <= grid.cols() + 0.4f && gridy <= grid.rows() + 0.4f) { int ix = int(floor(gridx + 0.5f)); int iy = int(floor(gridy + 0.5f)); @@ -1546,59 +1450,4 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo } #endif /* SLIC3R_GUI */ -// Find all pairs of intersectiong edges from the set of polygons. -std::vector> intersecting_edges(const Polygons &polygons) -{ - double len = 0; - size_t cnt = 0; - BoundingBox bbox; - for (const Polygon &poly : polygons) { - if (poly.points.size() < 2) - continue; - for (size_t i = 0; i < poly.points.size(); ++ i) { - bbox.merge(poly.points[i]); - size_t j = (i == 0) ? (poly.points.size() - 1) : i - 1; - len += poly.points[i].distance_to(poly.points[j]); - ++ cnt; - } - } - len /= double(cnt); - bbox.offset(20); - EdgeGrid::Grid grid; - grid.set_bbox(bbox); - grid.create(polygons, len); - return grid.intersecting_edges(); -} - -// Find all pairs of intersectiong edges from the set of polygons, highlight them in an SVG. -void export_intersections_to_svg(const std::string &filename, const Polygons &polygons) -{ - std::vector> intersections = intersecting_edges(polygons); - BoundingBox bbox = get_extents(polygons); - SVG svg(filename.c_str(), bbox); - svg.draw(union_ex(polygons), "gray", 0.25f); - svg.draw_outline(polygons, "black"); - std::set intersecting_contours; - for (const std::pair &ie : intersections) { - intersecting_contours.insert(ie.first.first); - intersecting_contours.insert(ie.second.first); - } - // Highlight the contours with intersections. - coord_t line_width = coord_t(scale_(0.01)); - for (const Points *ic : intersecting_contours) { - svg.draw_outline(Polygon(*ic), "green"); - svg.draw_outline(Polygon(*ic), "black", line_width); - } - // Paint the intersections. - for (const std::pair &intersecting_edges : intersections) { - auto edge = [](const EdgeGrid::Grid::ContourEdge &e) { - return Line(e.first->at(e.second), - e.first->at((e.second + 1 == e.first->size()) ? 0 : e.second + 1)); - }; - svg.draw(edge(intersecting_edges.first), "red", line_width); - svg.draw(edge(intersecting_edges.second), "red", line_width); - } - svg.Close(); -} - } // namespace Slic3r diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp similarity index 100% rename from xs/src/libslic3r/EdgeGrid.hpp rename to src/libslic3r/EdgeGrid.hpp diff --git a/xs/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp similarity index 79% rename from xs/src/libslic3r/ExPolygon.cpp rename to src/libslic3r/ExPolygon.cpp index cd57fd7b0d..00bb4ffe4e 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -34,54 +34,43 @@ ExPolygon::operator Polylines() const return to_polylines(*this); } -void -ExPolygon::scale(double factor) +void ExPolygon::scale(double factor) { contour.scale(factor); - for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) { - (*it).scale(factor); - } + for (Polygon &hole : holes) + hole.scale(factor); } -void -ExPolygon::translate(double x, double y) +void ExPolygon::translate(double x, double y) { contour.translate(x, y); - for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) { - (*it).translate(x, y); - } + for (Polygon &hole : holes) + hole.translate(x, y); } -void -ExPolygon::rotate(double angle) +void ExPolygon::rotate(double angle) { contour.rotate(angle); - for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) { - (*it).rotate(angle); - } + for (Polygon &hole : holes) + hole.rotate(angle); } -void -ExPolygon::rotate(double angle, const Point ¢er) +void ExPolygon::rotate(double angle, const Point ¢er) { contour.rotate(angle, center); - for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) { - (*it).rotate(angle, center); - } + for (Polygon &hole : holes) + hole.rotate(angle, center); } -double -ExPolygon::area() const +double ExPolygon::area() const { double a = this->contour.area(); - for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { - a -= -(*it).area(); // holes have negative area - } + for (const Polygon &hole : holes) + a -= - hole.area(); // holes have negative area return a; } -bool -ExPolygon::is_valid() const +bool ExPolygon::is_valid() const { if (!this->contour.is_valid() || !this->contour.is_counter_clockwise()) return false; for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { @@ -90,20 +79,17 @@ ExPolygon::is_valid() const return true; } -bool -ExPolygon::contains(const Line &line) const +bool ExPolygon::contains(const Line &line) const { - return this->contains((Polyline)line); + return this->contains(Polyline(line.a, line.b)); } -bool -ExPolygon::contains(const Polyline &polyline) const +bool ExPolygon::contains(const Polyline &polyline) const { return diff_pl((Polylines)polyline, *this).empty(); } -bool -ExPolygon::contains(const Polylines &polylines) const +bool ExPolygon::contains(const Polylines &polylines) const { #if 0 BoundingBox bbox = get_extents(polylines); @@ -120,8 +106,7 @@ ExPolygon::contains(const Polylines &polylines) const return pl_out.empty(); } -bool -ExPolygon::contains(const Point &point) const +bool ExPolygon::contains(const Point &point) const { if (!this->contour.contains(point)) return false; for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { @@ -131,8 +116,7 @@ ExPolygon::contains(const Point &point) const } // inclusive version of contains() that also checks whether point is on boundaries -bool -ExPolygon::contains_b(const Point &point) const +bool ExPolygon::contains_b(const Point &point) const { return this->contains(point) || this->has_boundary_point(point); } @@ -243,25 +227,24 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl Point new_front = polyline.points.front(); Point new_back = polyline.points.back(); if (polyline.endpoints.first && !this->has_boundary_point(new_front)) { - Line line(polyline.points.front(), polyline.points[1]); - + Vec2d p1 = polyline.points.front().cast(); + Vec2d p2 = polyline.points[1].cast(); // prevent the line from touching on the other side, otherwise intersection() might return that solution - if (polyline.points.size() == 2) line.b = line.midpoint(); - - line.extend_start(max_width); - (void)this->contour.intersection(line, &new_front); + if (polyline.points.size() == 2) + p2 = (p1 + p2) * 0.5; + // Extend the start of the segment. + p1 -= (p2 - p1).normalized() * max_width; + this->contour.intersection(Line(p1.cast(), p2.cast()), &new_front); } if (polyline.endpoints.second && !this->has_boundary_point(new_back)) { - Line line( - *(polyline.points.end() - 2), - polyline.points.back() - ); - + Vec2d p1 = (polyline.points.end() - 2)->cast(); + Vec2d p2 = polyline.points.back().cast(); // prevent the line from touching on the other side, otherwise intersection() might return that solution - if (polyline.points.size() == 2) line.a = line.midpoint(); - line.extend_end(max_width); - - (void)this->contour.intersection(line, &new_back); + if (polyline.points.size() == 2) + p1 = (p1 + p2) * 0.5; + // Extend the start of the segment. + p2 += (p2 - p1).normalized() * max_width; + this->contour.intersection(Line(p1.cast(), p2.cast()), &new_back); } polyline.points.front() = new_front; polyline.points.back() = new_back; @@ -294,14 +277,14 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl // find another polyline starting here for (size_t j = i+1; j < pp.size(); ++j) { ThickPolyline& other = pp[j]; - if (polyline.last_point().coincides_with(other.last_point())) { + if (polyline.last_point() == other.last_point()) { other.reverse(); - } else if (polyline.first_point().coincides_with(other.last_point())) { + } else if (polyline.first_point() == other.last_point()) { polyline.reverse(); other.reverse(); - } else if (polyline.first_point().coincides_with(other.first_point())) { + } else if (polyline.first_point() == other.first_point()) { polyline.reverse(); - } else if (!polyline.last_point().coincides_with(other.first_point())) { + } else if (polyline.last_point() != other.first_point()) { continue; } @@ -361,7 +344,7 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const std::vector xx; xx.reserve(pp.size()); for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) - xx.push_back(p->x); + xx.push_back(p->x()); std::sort(xx.begin(), xx.end()); // find trapezoids by looping from first to next-to-last coordinate @@ -372,14 +355,14 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const // build rectangle Polygon poly; poly.points.resize(4); - poly[0].x = *x; - poly[0].y = bb.min.y; - poly[1].x = next_x; - poly[1].y = bb.min.y; - poly[2].x = next_x; - poly[2].y = bb.max.y; - poly[3].x = *x; - poly[3].y = bb.max.y; + poly[0](0) = *x; + poly[0](1) = bb.min(1); + poly[1](0) = next_x; + poly[1](1) = bb.min(1); + poly[2](0) = next_x; + poly[2](1) = bb.max(1); + poly[3](0) = *x; + poly[3](1) = bb.max(1); // intersect with this expolygon // append results to return value @@ -425,10 +408,11 @@ ExPolygon::triangulate_pp(Polygons* polygons) const TPPLPoly p; p.Init(int(ex->contour.points.size())); //printf(PRINTF_ZU "\n0\n", ex->contour.points.size()); - for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) { - p[ point-ex->contour.points.begin() ].x = point->x; - p[ point-ex->contour.points.begin() ].y = point->y; - //printf("%ld %ld\n", point->x, point->y); + for (const Point &point : ex->contour.points) { + size_t i = &point - &ex->contour.points.front(); + p[i].x = point(0); + p[i].y = point(1); + //printf("%ld %ld\n", point->x(), point->y()); } p.SetHole(false); input.push_back(p); @@ -439,10 +423,11 @@ ExPolygon::triangulate_pp(Polygons* polygons) const TPPLPoly p; p.Init(hole->points.size()); //printf(PRINTF_ZU "\n1\n", hole->points.size()); - for (Points::const_iterator point = hole->points.begin(); point != hole->points.end(); ++point) { - p[ point-hole->points.begin() ].x = point->x; - p[ point-hole->points.begin() ].y = point->y; - //printf("%ld %ld\n", point->x, point->y); + for (const Point &point : hole->points) { + size_t i = &point - &hole->points.front(); + p[i].x = point(0); + p[i].y = point(1); + //printf("%ld %ld\n", point->x(), point->y()); } p.SetHole(true); input.push_back(p); @@ -452,7 +437,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const // perform triangulation std::list output; int res = TPPLPartition().Triangulate_MONO(&input, &output); - if (res != 1) CONFESS("Triangulation failed"); + if (res != 1) + throw std::runtime_error("Triangulation failed"); // convert output polygons for (std::list::iterator poly = output.begin(); poly != output.end(); ++poly) { @@ -460,8 +446,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const Polygon p; p.points.resize(num_points); for (long i = 0; i < num_points; ++i) { - p.points[i].x = coord_t((*poly)[i].x); - p.points[i].y = coord_t((*poly)[i].y); + p.points[i](0) = coord_t((*poly)[i].x); + p.points[i](1) = coord_t((*poly)[i].y); } polygons->push_back(p); } @@ -477,19 +463,17 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const // contour std::vector ContourPoints; - for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) { + for (const Point &pt : ex->contour.points) // We should delete each p2t::Point object - ContourPoints.push_back(new p2t::Point(point->x, point->y)); - } + ContourPoints.push_back(new p2t::Point(pt(0), pt(1))); p2t::CDT cdt(ContourPoints); // holes for (Polygons::const_iterator hole = ex->holes.begin(); hole != ex->holes.end(); ++hole) { std::vector points; - for (Points::const_iterator point = hole->points.begin(); point != hole->points.end(); ++point) { + for (const Point &pt : hole->points) // will be destructed in SweepContext::~SweepContext - points.push_back(new p2t::Point(point->x, point->y)); - } + points.push_back(new p2t::Point(pt(0), pt(1))); cdt.AddHole(points); } @@ -506,9 +490,8 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const polygons->push_back(p); } - for(std::vector::iterator it = ContourPoints.begin(); it != ContourPoints.end(); ++it) { - delete *it; - } + for (p2t::Point *ptr : ContourPoints) + delete ptr; } } @@ -523,17 +506,6 @@ ExPolygon::lines() const return lines; } -std::string -ExPolygon::dump_perl() const -{ - std::ostringstream ret; - ret << "[" << this->contour.dump_perl(); - for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) - ret << "," << h->dump_perl(); - ret << "]"; - return ret.str(); -} - BoundingBox get_extents(const ExPolygon &expolygon) { return get_extents(expolygon.contour); diff --git a/xs/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp similarity index 99% rename from xs/src/libslic3r/ExPolygon.hpp rename to src/libslic3r/ExPolygon.hpp index f4782ba55f..4833ee49ec 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -63,7 +63,6 @@ public: void triangulate_pp(Polygons* polygons) const; void triangulate_p2t(Polygons* polygons) const; Lines lines() const; - std::string dump_perl() const; }; // Count a nuber of polygons stored inside the vector of expolygons. diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/src/libslic3r/ExPolygonCollection.cpp similarity index 100% rename from xs/src/libslic3r/ExPolygonCollection.cpp rename to src/libslic3r/ExPolygonCollection.cpp diff --git a/xs/src/libslic3r/ExPolygonCollection.hpp b/src/libslic3r/ExPolygonCollection.hpp similarity index 100% rename from xs/src/libslic3r/ExPolygonCollection.hpp rename to src/libslic3r/ExPolygonCollection.hpp diff --git a/xs/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp similarity index 100% rename from xs/src/libslic3r/Extruder.cpp rename to src/libslic3r/Extruder.cpp diff --git a/xs/src/libslic3r/Extruder.hpp b/src/libslic3r/Extruder.hpp similarity index 100% rename from xs/src/libslic3r/Extruder.hpp rename to src/libslic3r/Extruder.hpp diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp similarity index 99% rename from xs/src/libslic3r/ExtrusionEntity.cpp rename to src/libslic3r/ExtrusionEntity.cpp index c6f67b1691..92f0d3669c 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -220,7 +220,7 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) double min_non_overhang = std::numeric_limits::max(); for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { Point p_tmp = point.projection_onto(path->polyline); - double dist = point.distance_to(p_tmp); + double dist = (p_tmp - point).cast().norm(); if (dist < min) { p = p_tmp; min = dist; diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp similarity index 96% rename from xs/src/libslic3r/ExtrusionEntity.hpp rename to src/libslic3r/ExtrusionEntity.hpp index 504d264fea..cce3020f88 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -120,8 +120,8 @@ public: ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), m_role(rhs.m_role) {} // ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {}; - ExtrusionPath& operator=(const ExtrusionPath &rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = rhs.polyline; return *this; } - ExtrusionPath& operator=(ExtrusionPath &&rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = std::move(rhs.polyline); return *this; } + ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = rhs.polyline; return *this; } + ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = std::move(rhs.polyline); return *this; } ExtrusionPath* clone() const { return new ExtrusionPath (*this); } void reverse() { this->polyline.reverse(); } @@ -155,7 +155,7 @@ public: double min_mm3_per_mm() const { return this->mm3_per_mm; } Polyline as_polyline() const { return this->polyline; } void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); } - double total_volume() const override { return mm3_per_mm * unscale(length()); } + double total_volume() const override { return mm3_per_mm * unscale(length()); } private: void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp similarity index 100% rename from xs/src/libslic3r/ExtrusionEntityCollection.cpp rename to src/libslic3r/ExtrusionEntityCollection.cpp diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp similarity index 90% rename from xs/src/libslic3r/ExtrusionEntityCollection.hpp rename to src/libslic3r/ExtrusionEntityCollection.hpp index 81582a94d0..230c041601 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -50,10 +50,15 @@ public: src.clear(); } } - void append(const ExtrusionPaths &paths) { + void append(const ExtrusionPaths &paths) { this->entities.reserve(this->entities.size() + paths.size()); - for (ExtrusionPaths::const_iterator path = paths.begin(); path != paths.end(); ++path) - this->entities.push_back(path->clone()); + for (const ExtrusionPath &path : paths) + this->entities.emplace_back(path.clone()); + } + void append(ExtrusionPaths &&paths) { + this->entities.reserve(this->entities.size() + paths.size()); + for (ExtrusionPath &path : paths) + this->entities.emplace_back(new ExtrusionPath(std::move(path))); } void replace(size_t i, const ExtrusionEntity &entity); void remove(size_t i); @@ -83,7 +88,7 @@ public: // Following methods shall never be called on an ExtrusionEntityCollection. Polyline as_polyline() const { - CONFESS("Calling as_polyline() on a ExtrusionEntityCollection"); + throw std::runtime_error("Calling as_polyline() on a ExtrusionEntityCollection"); return Polyline(); }; @@ -93,7 +98,7 @@ public: } double length() const override { - CONFESS("Calling length() on a ExtrusionEntityCollection"); + throw std::runtime_error("Calling length() on a ExtrusionEntityCollection"); return 0.; } }; diff --git a/xs/src/libslic3r/ExtrusionSimulator.cpp b/src/libslic3r/ExtrusionSimulator.cpp similarity index 95% rename from xs/src/libslic3r/ExtrusionSimulator.cpp rename to src/libslic3r/ExtrusionSimulator.cpp index daecbc0d10..fcb2fe825a 100644 --- a/xs/src/libslic3r/ExtrusionSimulator.cpp +++ b/src/libslic3r/ExtrusionSimulator.cpp @@ -893,24 +893,24 @@ ExtrusionSimulator::~ExtrusionSimulator() void ExtrusionSimulator::set_image_size(const Point &image_size) { // printf("ExtrusionSimulator::set_image_size()\n"); - if (this->image_size.x == image_size.x && - this->image_size.y == image_size.y) + if (this->image_size.x() == image_size.x() && + this->image_size.y() == image_size.y()) return; // printf("Setting image size: %d, %d\n", image_size.x, image_size.y); this->image_size = image_size; // Allocate the image data in an RGBA format. // printf("Allocating image data, size %d\n", image_size.x * image_size.y * 4); - pimpl->image_data.assign(image_size.x * image_size.y * 4, 0); + pimpl->image_data.assign(image_size.x() * image_size.y() * 4, 0); // printf("Allocating image data, allocated\n"); //FIXME fill the image with red vertical lines. - for (size_t r = 0; r < image_size.y; ++ r) { - for (size_t c = 0; c < image_size.x; c += 2) { + for (size_t r = 0; r < image_size.y(); ++ r) { + for (size_t c = 0; c < image_size.x(); c += 2) { // Color red - pimpl->image_data[r * image_size.x * 4 + c * 4] = 255; + pimpl->image_data[r * image_size.x() * 4 + c * 4] = 255; // Opacity full - pimpl->image_data[r * image_size.x * 4 + c * 4 + 3] = 255; + pimpl->image_data[r * image_size.x() * 4 + c * 4 + 3] = 255; } } // printf("Allocating image data, set\n"); @@ -922,8 +922,8 @@ void ExtrusionSimulator::set_viewport(const BoundingBox &viewport) if (this->viewport != viewport) { this->viewport = viewport; Point sz = viewport.size(); - pimpl->accumulator.resize(boost::extents[sz.y][sz.x]); - pimpl->bitmap.resize(boost::extents[sz.y*pimpl->bitmap_oversampled][sz.x*pimpl->bitmap_oversampled]); + pimpl->accumulator.resize(boost::extents[sz.y()][sz.x()]); + pimpl->bitmap.resize(boost::extents[sz.y()*pimpl->bitmap_oversampled][sz.x()*pimpl->bitmap_oversampled]); // printf("Accumulator size: %d, %d\n", sz.y, sz.x); } } @@ -943,8 +943,8 @@ void ExtrusionSimulator::reset_accumulator() // printf("ExtrusionSimulator::reset_accumulator()\n"); Point sz = viewport.size(); // printf("Reset accumulator, Accumulator size: %d, %d\n", sz.y, sz.x); - memset(&pimpl->accumulator[0][0], 0, sizeof(float) * sz.x * sz.y); - memset(&pimpl->bitmap[0][0], 0, sz.x * sz.y * pimpl->bitmap_oversampled * pimpl->bitmap_oversampled); + memset(&pimpl->accumulator[0][0], 0, sizeof(float) * sz.x() * sz.y()); + memset(&pimpl->bitmap[0][0], 0, sz.x() * sz.y() * pimpl->bitmap_oversampled * pimpl->bitmap_oversampled); pimpl->extrusion_points.clear(); // printf("Reset accumulator, done.\n"); } @@ -955,17 +955,17 @@ void ExtrusionSimulator::extrude_to_accumulator(const ExtrusionPath &path, const // Convert the path to V2f points, shift and scale them to the viewport. std::vector polyline; polyline.reserve(path.polyline.points.size()); - float scalex = float(viewport.size().x) / float(bbox.size().x); - float scaley = float(viewport.size().y) / float(bbox.size().y); + float scalex = float(viewport.size().x()) / float(bbox.size().x()); + float scaley = float(viewport.size().y()) / float(bbox.size().y()); float w = scale_(path.width) * scalex; float h = scale_(path.height) * scalex; w = scale_(path.mm3_per_mm / path.height) * scalex; // printf("scalex: %f, scaley: %f\n", scalex, scaley); - // printf("bbox: %d,%d %d,%d\n", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y); + // printf("bbox: %d,%d %d,%d\n", bbox.min.x(), bbox.min.y, bbox.max.x(), bbox.max.y); for (Points::const_iterator it = path.polyline.points.begin(); it != path.polyline.points.end(); ++ it) { - // printf("point %d,%d\n", it->x+shift.x, it->y+shift.y); + // printf("point %d,%d\n", it->x+shift.x(), it->y+shift.y); ExtrusionPoint ept; - ept.center = V2f(float(it->x+shift.x-bbox.min.x) * scalex, float(it->y+shift.y-bbox.min.y) * scaley); + ept.center = V2f(float((*it)(0)+shift.x()-bbox.min.x()) * scalex, float((*it)(1)+shift.y()-bbox.min.y()) * scaley); ept.radius = w/2.f; ept.height = 0.5f; polyline.push_back(ept.center); @@ -989,9 +989,9 @@ void ExtrusionSimulator::evaluate_accumulator(ExtrusionSimulationType simulation if (simulationType > ExtrusionSimulationDontSpread) { // Average the cells of a bitmap into a lower resolution floating point mask. - A2f mask(boost::extents[sz.y][sz.x]); - for (int r = 0; r < sz.y; ++r) { - for (int c = 0; c < sz.x; ++c) { + A2f mask(boost::extents[sz.y()][sz.x()]); + for (int r = 0; r < sz.y(); ++r) { + for (int c = 0; c < sz.x(); ++c) { float p = 0; for (int j = 0; j < pimpl->bitmap_oversampled; ++ j) { for (int i = 0; i < pimpl->bitmap_oversampled; ++ i) { @@ -1009,9 +1009,9 @@ void ExtrusionSimulator::evaluate_accumulator(ExtrusionSimulationType simulation } // Color map the accumulator. - for (int r = 0; r < sz.y; ++r) { - unsigned char *ptr = &pimpl->image_data[(image_size.x * (viewport.min.y + r) + viewport.min.x) * 4]; - for (int c = 0; c < sz.x; ++c) { + for (int r = 0; r < sz.y(); ++r) { + unsigned char *ptr = &pimpl->image_data[(image_size.x() * (viewport.min.y() + r) + viewport.min.x()) * 4]; + for (int c = 0; c < sz.x(); ++c) { #if 1 float p = pimpl->accumulator[r][c]; #else diff --git a/xs/src/libslic3r/ExtrusionSimulator.hpp b/src/libslic3r/ExtrusionSimulator.hpp similarity index 100% rename from xs/src/libslic3r/ExtrusionSimulator.hpp rename to src/libslic3r/ExtrusionSimulator.hpp diff --git a/xs/src/libslic3r/FileParserError.hpp b/src/libslic3r/FileParserError.hpp similarity index 100% rename from xs/src/libslic3r/FileParserError.hpp rename to src/libslic3r/FileParserError.hpp diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp similarity index 94% rename from xs/src/libslic3r/Fill/Fill.cpp rename to src/libslic3r/Fill/Fill.cpp index 5333fcfecd..016d162d84 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -34,7 +34,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) { // Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id; - double fill_density = layerm.region()->config.fill_density; + double fill_density = layerm.region()->config().fill_density; Flow infill_flow = layerm.flow(frInfill); Flow solid_infill_flow = layerm.flow(frSolidInfill); Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill); @@ -69,7 +69,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) { group_attrib[i].is_solid = true; group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; - group_attrib[i].pattern = surface.is_external() ? layerm.region()->config.external_fill_pattern.value : ipRectilinear; + group_attrib[i].pattern = surface.is_external() ? layerm.region()->config().external_fill_pattern.value : ipRectilinear; } } // Loop through solid groups, find compatible groups and append them to this one. @@ -152,7 +152,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) for (const Surface &surface : surfaces) { if (surface.surface_type == stInternalVoid) continue; - InfillPattern fill_pattern = layerm.region()->config.fill_pattern.value; + InfillPattern fill_pattern = layerm.region()->config().fill_pattern.value; double density = fill_density; FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill); @@ -161,7 +161,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) if (surface.is_solid()) { density = 100.; fill_pattern = (surface.is_external() && ! is_bridge) ? - layerm.region()->config.external_fill_pattern.value : + layerm.region()->config().external_fill_pattern.value : ipRectilinear; } else if (density <= 0) continue; @@ -190,7 +190,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) // layer height Flow internal_flow = layerm.region()->flow( frInfill, - layerm.layer()->object()->config.layer_height.value, // TODO: handle infill_every_layers? + layerm.layer()->object()->config().layer_height.value, // TODO: handle infill_every_layers? false, // no bridge false, // no first layer -1, // auto width @@ -205,7 +205,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) double link_max_length = 0.; if (! is_bridge) { #if 0 - link_max_length = layerm.region()->config.get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing()); + link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing()); // printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length); #else if (density > 80.) // 80% @@ -215,7 +215,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) f->layer_id = layerm.layer()->id(); f->z = layerm.layer()->print_z; - f->angle = float(Geometry::deg2rad(layerm.region()->config.fill_angle.value)); + f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); // Maximum length of the perimeter segment linking two infill lines. f->link_max_length = scale_(link_max_length); // Used by the concentric infill pattern to clip the loops to create extrusion paths. @@ -247,7 +247,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) // Only concentric fills are not sorted. eec->no_sort = f->no_sort(); extrusion_entities_append_paths( - eec->entities, STDMOVE(polylines), + eec->entities, std::move(polylines), is_bridge ? erBridgeInfill : (surface.is_solid() ? diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/src/libslic3r/Fill/Fill.hpp similarity index 100% rename from xs/src/libslic3r/Fill/Fill.hpp rename to src/libslic3r/Fill/Fill.hpp diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp similarity index 94% rename from xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp rename to src/libslic3r/Fill/Fill3DHoneycomb.cpp index aa9774784d..6a37e4369f 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -54,9 +54,9 @@ static std::vector perpendPoints(const coordf_t offset, const size_t b // components that are outside these limits are set to the limits. static inline void trim(Pointfs &pts, coordf_t minX, coordf_t minY, coordf_t maxX, coordf_t maxY) { - for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it) { - it->x = clamp(minX, maxX, it->x); - it->y = clamp(minY, maxY, it->y); + for (Vec2d &pt : pts) { + pt(0) = clamp(minX, maxX, pt(0)); + pt(1) = clamp(minY, maxY, pt(1)); } } @@ -66,7 +66,7 @@ static inline Pointfs zip(const std::vector &x, const std::vectorbegin(); it != it_polylines->end(); ++ it) - polyline.points.push_back(Point(coord_t(it->x * scaleFactor), coord_t(it->y * scaleFactor))); + polyline.points.push_back(Point(coord_t((*it)(0) * scaleFactor), coord_t((*it)(1) * scaleFactor))); } return result; } @@ -153,13 +153,13 @@ void Fill3DHoneycomb::_fill_surface_single( Polylines polylines = makeGrid( scale_(this->z), distance, - ceil(bb.size().x / distance) + 1, - ceil(bb.size().y / distance) + 1, + ceil(bb.size()(0) / distance) + 1, + ceil(bb.size()(1) / distance) + 1, ((this->layer_id/thickness_layers) % 2) + 1); // move pattern in place for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) - it->translate(bb.min.x, bb.min.y); + it->translate(bb.min(0), bb.min(1)); // clip pattern to boundaries polylines = intersection_pl(polylines, (Polygons)expolygon); @@ -187,7 +187,7 @@ void Fill3DHoneycomb::_fill_surface_single( const Point &last_point = pts_end.back(); // TODO: we should also check that both points are on a fill_boundary to avoid // connecting paths on the boundaries of internal regions - if (first_point.distance_to(last_point) <= 1.5 * distance && + if ((last_point - first_point).cast().norm() <= 1.5 * distance && expolygon_off.contains(Line(last_point, first_point))) { // Append the polyline. pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp b/src/libslic3r/Fill/Fill3DHoneycomb.hpp similarity index 100% rename from xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp rename to src/libslic3r/Fill/Fill3DHoneycomb.hpp diff --git a/xs/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp similarity index 98% rename from xs/src/libslic3r/Fill/FillBase.cpp rename to src/libslic3r/Fill/FillBase.cpp index 88645b3d95..7a99e84f71 100644 --- a/xs/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -34,7 +34,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipArchimedeanChords: return new FillArchimedeanChords(); case ipHilbertCurve: return new FillHilbertCurve(); case ipOctagramSpiral: return new FillOctagramSpiral(); - default: CONFESS("unknown type"); return nullptr; + default: throw std::invalid_argument("unknown type");; } } diff --git a/xs/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp similarity index 95% rename from xs/src/libslic3r/Fill/FillBase.hpp rename to src/libslic3r/Fill/FillBase.hpp index 62d18e5183..b67d14339b 100644 --- a/xs/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -121,11 +121,11 @@ public: return aligned; } static Point _align_to_grid(Point coord, Point spacing) - { return Point(_align_to_grid(coord.x, spacing.x), _align_to_grid(coord.y, spacing.y)); } + { return Point(_align_to_grid(coord(0), spacing(0)), _align_to_grid(coord(1), spacing(1))); } static coord_t _align_to_grid(coord_t coord, coord_t spacing, coord_t base) { return base + _align_to_grid(coord - base, spacing); } static Point _align_to_grid(Point coord, Point spacing, Point base) - { return Point(_align_to_grid(coord.x, spacing.x, base.x), _align_to_grid(coord.y, spacing.y, base.y)); } + { return Point(_align_to_grid(coord(0), spacing(0), base(0)), _align_to_grid(coord(1), spacing(1), base(1))); } }; } // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp similarity index 97% rename from xs/src/libslic3r/Fill/FillConcentric.cpp rename to src/libslic3r/Fill/FillConcentric.cpp index b21ad2799e..8a3a7ea89d 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -20,8 +20,8 @@ void FillConcentric::_fill_surface_single( coord_t distance = coord_t(min_spacing / params.density); if (params.density > 0.9999f && !params.dont_adjust) { - distance = this->_adjust_solid_spacing(bounding_box.size().x, distance); - this->spacing = unscale(distance); + distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance); + this->spacing = unscale(distance); } Polygons loops = (Polygons)expolygon; diff --git a/xs/src/libslic3r/Fill/FillConcentric.hpp b/src/libslic3r/Fill/FillConcentric.hpp similarity index 100% rename from xs/src/libslic3r/Fill/FillConcentric.hpp rename to src/libslic3r/Fill/FillConcentric.hpp diff --git a/xs/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp similarity index 76% rename from xs/src/libslic3r/Fill/FillGyroid.cpp rename to src/libslic3r/Fill/FillGyroid.cpp index 46d6382f7d..d6bf03ce65 100644 --- a/xs/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -30,39 +30,39 @@ static inline double f(double x, double z_sin, double z_cos, bool vertical, bool } static inline Polyline make_wave( - const std::vector& one_period, double width, double height, double offset, double scaleFactor, + const std::vector& one_period, double width, double height, double offset, double scaleFactor, double z_cos, double z_sin, bool vertical) { - std::vector points = one_period; - double period = points.back().x; + std::vector points = one_period; + double period = points.back()(0); points.pop_back(); int n = points.size(); do { - points.emplace_back(Pointf(points[points.size()-n].x + period, points[points.size()-n].y)); - } while (points.back().x < width); - points.back().x = width; + points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1))); + } while (points.back()(0) < width); + points.back()(0) = width; // and construct the final polyline to return: Polyline polyline; for (auto& point : points) { - point.y += offset; - point.y = clamp(0., height, double(point.y)); + point(1) += offset; + point(1) = clamp(0., height, double(point(1))); if (vertical) - std::swap(point.x, point.y); - polyline.points.emplace_back(convert_to(point * scaleFactor)); + std::swap(point(0), point(1)); + polyline.points.emplace_back((point * scaleFactor).cast()); } return polyline; } -static std::vector make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip) +static std::vector make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip) { - std::vector points; + std::vector points; double dx = M_PI_4; // very coarse spacing to begin with double limit = std::min(2*M_PI, width); for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too x = std::min(x, limit); - points.emplace_back(Pointf(x,f(x, z_sin,z_cos, vertical, flip))); + points.emplace_back(Vec2d(x,f(x, z_sin,z_cos, vertical, flip))); } // now we will check all internal points and in case some are too far from the line connecting its neighbours, @@ -71,17 +71,19 @@ static std::vector make_one_period(double width, double scaleFactor, dou for (unsigned int i=1;i(scaleFactor) * std::abs(cross2(rp, lp) - cross2(rp - lp, tp)) / lrv.norm(); if (dist_mm > tolerance) { // if the difference from straight line is more than this - double x = 0.5f * (points[i-1].x + points[i].x); - points.emplace_back(Pointf(x, f(x, z_sin, z_cos, vertical, flip))); - x = 0.5f * (points[i+1].x + points[i].x); - points.emplace_back(Pointf(x, f(x, z_sin, z_cos, vertical, flip))); - std::sort(points.begin(), points.end()); // we added the points to the end, but need them all in order - --i; // decrement i so we also check the first newly added point + double x = 0.5f * (points[i-1](0) + points[i](0)); + points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip))); + x = 0.5f * (points[i+1](0) + points[i](0)); + points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip))); + // we added the points to the end, but need them all in order + std::sort(points.begin(), points.end(), [](const Vec2d &lhs, const Vec2d &rhs){ return lhs < rhs; }); + // decrement i so we also check the first newly added point + --i; } } return points; @@ -107,7 +109,7 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double std::swap(width,height); } - std::vector one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time + std::vector one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time Polylines result; for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates odd polylines @@ -131,7 +133,7 @@ void FillGyroid::_fill_surface_single( // no rotation is supported for this infill pattern (yet) BoundingBox bb = expolygon.contour.bounding_box(); // Density adjusted to have a good %of weight. - double density_adjusted = std::max(0., params.density * 2.44); + double density_adjusted = std::max(0., params.density * 2.); // Distance between the gyroid waves in scaled coordinates. coord_t distance = coord_t(scale_(this->spacing) / density_adjusted); @@ -143,12 +145,12 @@ void FillGyroid::_fill_surface_single( scale_(this->z), density_adjusted, this->spacing, - ceil(bb.size().x / distance) + 1., - ceil(bb.size().y / distance) + 1.); + ceil(bb.size()(0) / distance) + 1., + ceil(bb.size()(1) / distance) + 1.); // move pattern in place for (Polyline &polyline : polylines) - polyline.translate(bb.min.x, bb.min.y); + polyline.translate(bb.min(0), bb.min(1)); // clip pattern to boundaries polylines = intersection_pl(polylines, (Polygons)expolygon); @@ -177,7 +179,7 @@ void FillGyroid::_fill_surface_single( // TODO: we should also check that both points are on a fill_boundary to avoid // connecting paths on the boundaries of internal regions // TODO: avoid crossing current infill path - if (first_point.distance_to(last_point) <= 5 * distance && + if ((last_point - first_point).cast().norm() <= 5 * distance && expolygon_off.contains(Line(last_point, first_point))) { // Append the polyline. pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); diff --git a/xs/src/libslic3r/Fill/FillGyroid.hpp b/src/libslic3r/Fill/FillGyroid.hpp similarity index 100% rename from xs/src/libslic3r/Fill/FillGyroid.hpp rename to src/libslic3r/Fill/FillGyroid.hpp diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/src/libslic3r/Fill/FillHoneycomb.cpp similarity index 94% rename from xs/src/libslic3r/Fill/FillHoneycomb.cpp rename to src/libslic3r/Fill/FillHoneycomb.cpp index aa52856ae1..cbfe926f2f 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/src/libslic3r/Fill/FillHoneycomb.cpp @@ -50,13 +50,13 @@ void FillHoneycomb::_fill_surface_single( bounding_box.merge(_align_to_grid(bounding_box.min, Point(m.hex_width, m.pattern_height))); } - coord_t x = bounding_box.min.x; - while (x <= bounding_box.max.x) { + coord_t x = bounding_box.min(0); + while (x <= bounding_box.max(0)) { Polygon p; coord_t ax[2] = { x + m.x_offset, x + m.distance - m.x_offset }; for (size_t i = 0; i < 2; ++ i) { std::reverse(p.points.begin(), p.points.end()); // turn first half upside down - for (coord_t y = bounding_box.min.y; y <= bounding_box.max.y; y += m.y_short + m.hex_side + m.y_short + m.hex_side) { + for (coord_t y = bounding_box.min(1); y <= bounding_box.max(1); y += m.y_short + m.hex_side + m.y_short + m.hex_side) { p.points.push_back(Point(ax[1], y + m.y_offset)); p.points.push_back(Point(ax[0], y + m.y_short - m.y_offset)); p.points.push_back(Point(ax[0], y + m.y_short + m.hex_side + m.y_offset)); @@ -101,7 +101,7 @@ void FillHoneycomb::_fill_surface_single( for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) { if (! paths.empty()) { // distance between first point of this path and last point of last path - double distance = paths.back().last_point().distance_to(it_path->first_point()); + double distance = (it_path->first_point() - paths.back().last_point()).cast().norm(); if (distance <= m.hex_width) { paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end()); continue; diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.hpp b/src/libslic3r/Fill/FillHoneycomb.hpp similarity index 100% rename from xs/src/libslic3r/Fill/FillHoneycomb.hpp rename to src/libslic3r/Fill/FillHoneycomb.hpp diff --git a/xs/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp similarity index 78% rename from xs/src/libslic3r/Fill/FillPlanePath.cpp rename to src/libslic3r/Fill/FillPlanePath.cpp index f71ef95a1a..615cc6efed 100644 --- a/xs/src/libslic3r/Fill/FillPlanePath.cpp +++ b/src/libslic3r/Fill/FillPlanePath.cpp @@ -24,14 +24,14 @@ void FillPlanePath::_fill_surface_single( Point shift = this->_centered() ? bounding_box.center() : bounding_box.min; - expolygon.translate(-shift.x, -shift.y); - bounding_box.translate(-shift.x, -shift.y); + expolygon.translate(-shift(0), -shift(1)); + bounding_box.translate(-shift(0), -shift(1)); Pointfs pts = _generate( - coord_t(ceil(coordf_t(bounding_box.min.x) / distance_between_lines)), - coord_t(ceil(coordf_t(bounding_box.min.y) / distance_between_lines)), - coord_t(ceil(coordf_t(bounding_box.max.x) / distance_between_lines)), - coord_t(ceil(coordf_t(bounding_box.max.y) / distance_between_lines))); + coord_t(ceil(coordf_t(bounding_box.min(0)) / distance_between_lines)), + coord_t(ceil(coordf_t(bounding_box.min(1)) / distance_between_lines)), + coord_t(ceil(coordf_t(bounding_box.max(0)) / distance_between_lines)), + coord_t(ceil(coordf_t(bounding_box.max(1)) / distance_between_lines))); Polylines polylines; if (pts.size() >= 2) { @@ -41,8 +41,8 @@ void FillPlanePath::_fill_surface_single( polyline.points.reserve(pts.size()); for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it) polyline.points.push_back(Point( - coord_t(floor(it->x * distance_between_lines + 0.5)), - coord_t(floor(it->y * distance_between_lines + 0.5)))); + coord_t(floor((*it)(0) * distance_between_lines + 0.5)), + coord_t(floor((*it)(1) * distance_between_lines + 0.5)))); // intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines); polylines = intersection_pl(polylines, to_polygons(expolygon)); @@ -62,7 +62,7 @@ void FillPlanePath::_fill_surface_single( // paths must be repositioned and rotated back for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) { - it->translate(shift.x, shift.y); + it->translate(shift(0), shift(1)); it->rotate(direction.first); } } @@ -86,12 +86,12 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m coordf_t r = 1; Pointfs out; //FIXME Vojtech: If used as a solid infill, there is a gap left at the center. - out.push_back(Pointf(0, 0)); - out.push_back(Pointf(1, 0)); + out.push_back(Vec2d(0, 0)); + out.push_back(Vec2d(1, 0)); while (r < rmax) { theta += 1. / r; r = a + b * theta; - out.push_back(Pointf(r * cos(theta), r * sin(theta))); + out.push_back(Vec2d(r * cos(theta), r * sin(theta))); } return out; } @@ -162,7 +162,7 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, line.reserve(sz2); for (size_t i = 0; i < sz2; ++ i) { Point p = hilbert_n_to_xy(i); - line.push_back(Pointf(p.x + min_x, p.y + min_y)); + line.push_back(Vec2d(p(0) + min_x, p(1) + min_y)); } return line; } @@ -175,27 +175,27 @@ Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_ coordf_t r = 0; coordf_t r_inc = sqrt(2.); Pointfs out; - out.push_back(Pointf(0, 0)); + out.push_back(Vec2d(0, 0)); while (r < rmax) { r += r_inc; coordf_t rx = r / sqrt(2.); coordf_t r2 = r + rx; - out.push_back(Pointf( r, 0.)); - out.push_back(Pointf( r2, rx)); - out.push_back(Pointf( rx, rx)); - out.push_back(Pointf( rx, r2)); - out.push_back(Pointf(0., r)); - out.push_back(Pointf(-rx, r2)); - out.push_back(Pointf(-rx, rx)); - out.push_back(Pointf(-r2, rx)); - out.push_back(Pointf(-r, 0.)); - out.push_back(Pointf(-r2, -rx)); - out.push_back(Pointf(-rx, -rx)); - out.push_back(Pointf(-rx, -r2)); - out.push_back(Pointf(0., -r)); - out.push_back(Pointf( rx, -r2)); - out.push_back(Pointf( rx, -rx)); - out.push_back(Pointf( r2+r_inc, -rx)); + out.push_back(Vec2d( r, 0.)); + out.push_back(Vec2d( r2, rx)); + out.push_back(Vec2d( rx, rx)); + out.push_back(Vec2d( rx, r2)); + out.push_back(Vec2d(0., r)); + out.push_back(Vec2d(-rx, r2)); + out.push_back(Vec2d(-rx, rx)); + out.push_back(Vec2d(-r2, rx)); + out.push_back(Vec2d(-r, 0.)); + out.push_back(Vec2d(-r2, -rx)); + out.push_back(Vec2d(-rx, -rx)); + out.push_back(Vec2d(-rx, -r2)); + out.push_back(Vec2d(0., -r)); + out.push_back(Vec2d( rx, -r2)); + out.push_back(Vec2d( rx, -rx)); + out.push_back(Vec2d( r2+r_inc, -rx)); } return out; } diff --git a/xs/src/libslic3r/Fill/FillPlanePath.hpp b/src/libslic3r/Fill/FillPlanePath.hpp similarity index 100% rename from xs/src/libslic3r/Fill/FillPlanePath.hpp rename to src/libslic3r/Fill/FillPlanePath.hpp diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp similarity index 86% rename from xs/src/libslic3r/Fill/FillRectilinear.cpp rename to src/libslic3r/Fill/FillRectilinear.cpp index 5ba30ba51d..205eb1b669 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -26,8 +26,8 @@ void FillRectilinear::_fill_surface_single( // define flow spacing according to requested density if (params.density > 0.9999f && !params.dont_adjust) { - this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size().x, this->_line_spacing); - this->spacing = unscale(this->_line_spacing); + this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing); + this->spacing = unscale(this->_line_spacing); } else { // extend bounding box so that our pattern will be aligned with other layers // Transform the reference point to the rotated coordinate system. @@ -38,14 +38,14 @@ void FillRectilinear::_fill_surface_single( } // generate the basic pattern - coord_t x_max = bounding_box.max.x + SCALED_EPSILON; + coord_t x_max = bounding_box.max(0) + SCALED_EPSILON; Lines lines; - for (coord_t x = bounding_box.min.x; x <= x_max; x += this->_line_spacing) - lines.push_back(this->_line(lines.size(), x, bounding_box.min.y, bounding_box.max.y)); + for (coord_t x = bounding_box.min(0); x <= x_max; x += this->_line_spacing) + lines.push_back(this->_line(lines.size(), x, bounding_box.min(1), bounding_box.max(1))); if (this->_horizontal_lines()) { - coord_t y_max = bounding_box.max.y + SCALED_EPSILON; - for (coord_t y = bounding_box.min.y; y <= y_max; y += this->_line_spacing) - lines.push_back(Line(Point(bounding_box.min.x, y), Point(bounding_box.max.x, y))); + coord_t y_max = bounding_box.max(1) + SCALED_EPSILON; + for (coord_t y = bounding_box.min(1); y <= y_max; y += this->_line_spacing) + lines.push_back(Line(Point(bounding_box.min(0), y), Point(bounding_box.max(0), y))); } // clip paths against a slightly larger expolygon, so that the first and last paths @@ -72,10 +72,10 @@ void FillRectilinear::_fill_surface_single( for (Polylines::iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { Point *first_point = &it_polyline->points.front(); Point *last_point = &it_polyline->points.back(); - if (first_point->y > last_point->y) + if (first_point->y() > last_point->y()) std::swap(first_point, last_point); - first_point->y -= extra; - last_point->y += extra; + first_point->y() -= extra; + last_point->y() += extra; } size_t n_polylines_out_old = polylines_out.size(); @@ -103,10 +103,10 @@ void FillRectilinear::_fill_surface_single( const Point &first_point = it_polyline->points.front(); const Point &last_point = pts_end.back(); // Distance in X, Y. - const Vector distance = first_point.vector_to(last_point); + const Vector distance = last_point - first_point; // TODO: we should also check that both points are on a fill_boundary to avoid // connecting paths on the boundaries of internal regions - if (this->_can_connect(std::abs(distance.x), std::abs(distance.y)) && + if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) && expolygon_off.contains(Line(last_point, first_point))) { // Append the polyline. pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); @@ -122,7 +122,7 @@ void FillRectilinear::_fill_surface_single( // paths must be rotated back for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_old; it != polylines_out.end(); ++ it) { // No need to translate, the absolute position is irrelevant. - // it->translate(- direction.second.x, - direction.second.y); + // it->translate(- direction.second(0), - direction.second(1)); it->rotate(direction.first); } } diff --git a/xs/src/libslic3r/Fill/FillRectilinear.hpp b/src/libslic3r/Fill/FillRectilinear.hpp similarity index 100% rename from xs/src/libslic3r/Fill/FillRectilinear.hpp rename to src/libslic3r/Fill/FillRectilinear.hpp diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp similarity index 97% rename from xs/src/libslic3r/Fill/FillRectilinear2.cpp rename to src/libslic3r/Fill/FillRectilinear2.cpp index ddd7851017..65440d0efe 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -42,12 +42,12 @@ static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Po Point px = (i == 0) ? p1 : p2; Point pa = poly.points[((seg == 0) ? poly.points.size() : seg) - 1]; Point pb = poly.points[seg]; - if (pa.x > pb.x) - std::swap(pa.x, pb.x); - if (pa.y > pb.y) - std::swap(pa.y, pb.y); - assert(px.x >= pa.x && px.x <= pb.x); - assert(px.y >= pa.y && px.y <= pb.y); + if (pa(0) > pb(0)) + std::swap(pa(0), pb(0)); + if (pa(1) > pb(1)) + std::swap(pa(1), pb(1)); + assert(px(0) >= pa(0) && px(0) <= pb(0)); + assert(px(1) >= pa(1) && px(1) <= pb(1)); } #endif /* SLIC3R_DEBUG */ const Point *pPrev = &p1; @@ -55,14 +55,14 @@ static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Po coordf_t len = 0; if (seg1 <= seg2) { for (size_t i = seg1; i < seg2; ++ i, pPrev = pThis) - len += pPrev->distance_to(*(pThis = &poly.points[i])); + len += (*pPrev - *(pThis = &poly.points[i])).cast().norm(); } else { for (size_t i = seg1; i < poly.points.size(); ++ i, pPrev = pThis) - len += pPrev->distance_to(*(pThis = &poly.points[i])); + len += (*pPrev - *(pThis = &poly.points[i])).cast().norm(); for (size_t i = 0; i < seg2; ++ i, pPrev = pThis) - len += pPrev->distance_to(*(pThis = &poly.points[i])); + len += (*pPrev - *(pThis = &poly.points[i])).cast().norm(); } - len += pPrev->distance_to(p2); + len += (*pPrev - p2).cast().norm(); return len; } @@ -791,15 +791,15 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP // define flow spacing according to requested density if (params.full_infill() && !params.dont_adjust) { - line_spacing = this->_adjust_solid_spacing(bounding_box.size().x, line_spacing); - this->spacing = unscale(line_spacing); + line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing); + this->spacing = unscale(line_spacing); } else { // extend bounding box so that our pattern will be aligned with other layers // Transform the reference point to the rotated coordinate system. Point refpt = rotate_vector.second.rotated(- rotate_vector.first); // _align_to_grid will not work correctly with positive pattern_shift. coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing; - refpt.x -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); + refpt(0) -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); bounding_box.merge(_align_to_grid( bounding_box.min, Point(line_spacing, line_spacing), @@ -808,8 +808,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP // Intersect a set of euqally spaced vertical lines wiht expolygon. // n_vlines = ceil(bbox_width / line_spacing) - size_t n_vlines = (bounding_box.max.x - bounding_box.min.x + line_spacing - 1) / line_spacing; - coord_t x0 = bounding_box.min.x; + size_t n_vlines = (bounding_box.max(0) - bounding_box.min(0) + line_spacing - 1) / line_spacing; + coord_t x0 = bounding_box.min(0); if (params.full_infill()) x0 += (line_spacing + SCALED_EPSILON) / 2; @@ -842,8 +842,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP const Point &p1 = contour[iPrev]; const Point &p2 = contour[iSegment]; // Which of the equally spaced vertical lines is intersected by this segment? - coord_t l = p1.x; - coord_t r = p2.x; + coord_t l = p1(0); + coord_t r = p2(0); if (l > r) std::swap(l, r); // il, ir are the left / right indices of vertical lines intersecting a segment @@ -869,33 +869,33 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP assert(l <= this_x); assert(r >= this_x); // Calculate the intersection position in y axis. x is known. - if (p1.x == this_x) { - if (p2.x == this_x) { + if (p1(0) == this_x) { + if (p2(0) == this_x) { // Ignore strictly vertical segments. continue; } - is.pos_p = p1.y; + is.pos_p = p1(1); is.pos_q = 1; - } else if (p2.x == this_x) { - is.pos_p = p2.y; + } else if (p2(0) == this_x) { + is.pos_p = p2(1); is.pos_q = 1; } else { // First calculate the intersection parameter 't' as a rational number with non negative denominator. - if (p2.x > p1.x) { - is.pos_p = this_x - p1.x; - is.pos_q = p2.x - p1.x; + if (p2(0) > p1(0)) { + is.pos_p = this_x - p1(0); + is.pos_q = p2(0) - p1(0); } else { - is.pos_p = p1.x - this_x; - is.pos_q = p1.x - p2.x; + is.pos_p = p1(0) - this_x; + is.pos_q = p1(0) - p2(0); } assert(is.pos_p >= 0 && is.pos_p <= is.pos_q); // Make an intersection point from the 't'. - is.pos_p *= int64_t(p2.y - p1.y); - is.pos_p += p1.y * int64_t(is.pos_q); + is.pos_p *= int64_t(p2(1) - p1(1)); + is.pos_p += p1(1) * int64_t(is.pos_q); } // +-1 to take rounding into account. - assert(is.pos() + 1 >= std::min(p1.y, p2.y)); - assert(is.pos() <= std::max(p1.y, p2.y) + 1); + assert(is.pos() + 1 >= std::min(p1(1), p2(1))); + assert(is.pos() <= std::max(p1(1), p2(1)) + 1); segs[i].intersections.push_back(is); } } @@ -919,7 +919,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP const Points &contour = poly_with_offset.contour(iContour).points; size_t iSegment = sil.intersections[i].iSegment; size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1; - coord_t dir = contour[iSegment].x - contour[iPrev].x; + coord_t dir = contour[iSegment](0) - contour[iPrev](0); bool low = dir > 0; sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ? (low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) : @@ -1066,7 +1066,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP intrsctn.consumed_vertical_up : seg.intersections[i-1].consumed_vertical_up; if (! consumed) { - coordf_t dist2 = sqr(coordf_t(pointLast.x - seg.pos)) + sqr(coordf_t(pointLast.y - intrsctn.pos())); + coordf_t dist2 = sqr(coordf_t(pointLast(0) - seg.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos())); if (dist2 < dist2min) { dist2min = dist2; i_vline = i_vline2; @@ -1356,8 +1356,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP // Handle nearly zero length edges. if (polyline_current->points.size() <= 1 || (polyline_current->points.size() == 2 && - std::abs(polyline_current->points.front().x - polyline_current->points.back().x) < SCALED_EPSILON && - std::abs(polyline_current->points.front().y - polyline_current->points.back().y) < SCALED_EPSILON)) + std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON && + std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON)) polylines_out.pop_back(); intrsctn = NULL; i_intersection = -1; @@ -1383,7 +1383,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP // paths must be rotated back for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_initial; it != polylines_out.end(); ++ it) { // No need to translate, the absolute position is irrelevant. - // it->translate(- rotate_vector.second.x, - rotate_vector.second.y); + // it->translate(- rotate_vector.second(0), - rotate_vector.second(1)); assert(! it->has_duplicate_points()); it->rotate(rotate_vector.first); //FIXME rather simplify the paths to avoid very short edges? diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.hpp b/src/libslic3r/Fill/FillRectilinear2.hpp similarity index 100% rename from xs/src/libslic3r/Fill/FillRectilinear2.hpp rename to src/libslic3r/Fill/FillRectilinear2.hpp diff --git a/xs/src/libslic3r/Fill/FillRectilinear3.cpp b/src/libslic3r/Fill/FillRectilinear3.cpp similarity index 95% rename from xs/src/libslic3r/Fill/FillRectilinear3.cpp rename to src/libslic3r/Fill/FillRectilinear3.cpp index cccea30301..8fc129eacb 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear3.cpp +++ b/src/libslic3r/Fill/FillRectilinear3.cpp @@ -217,30 +217,30 @@ Point SegmentIntersection::pos() const const Point &seg_start = poly.points[(this->iSegment == 0) ? poly.points.size() - 1 : this->iSegment - 1]; const Point &seg_end = poly.points[this->iSegment]; // Point, vector of the segment. - const Pointf p1 = convert_to(seg_start); - const Pointf v1 = convert_to(seg_end - seg_start); + const Vec2d p1(seg_start.cast()); + const Vec2d v1((seg_end - seg_start).cast()); // Point, vector of this hatching line. - const Pointf p2 = convert_to(line->pos); - const Pointf v2 = convert_to(line->dir); + const Vec2d p2(line->pos.cast()); + const Vec2d v2(line->dir.cast()); // Intersect the two rays. - double denom = v1.x * v2.y - v2.x * v1.y; + double denom = v1(0) * v2(1) - v2(0) * v1(1); Point out; if (denom == 0.) { // Lines are collinear. As the pos() method is not supposed to be called on collinear vectors, // the source vectors are not quite collinear. Return the center of the contour segment. out = seg_start + seg_end; - out.x >>= 1; - out.y >>= 1; + out(0) >>= 1; + out(1) >>= 1; } else { // Find the intersection point. - double t = (v2.x * (p1.y - p2.y) - v2.y * (p1.x - p2.x)) / denom; + double t = (v2(0) * (p1(1) - p2(1)) - v2(1) * (p1(0) - p2(0))) / denom; if (t < 0.) out = seg_start; else if (t > 1.) out = seg_end; else { - out.x = coord_t(floor(p1.x + t * v1.x + 0.5)); - out.y = coord_t(floor(p1.y + t * v1.y + 0.5)); + out(0) = coord_t(floor(p1(0) + t * v1(0) + 0.5)); + out(1) = coord_t(floor(p1(1) + t * v1(1) + 0.5)); } } return out; @@ -276,13 +276,13 @@ int SegmentIntersection::ordering_along_line(const SegmentIntersection &other) c // other.iSegment succeeds this->iSegment assert(seg_end_a == seg_start_b); // Avoid calling the 128bit x 128bit multiplication below if this->line intersects the common point. - if (cross(this->line->dir, seg_end_b - this->line->pos) == 0) + if (cross2(Vec2i64(this->line->dir.cast()), (seg_end_b - this->line->pos).cast()) == 0) return 0; } else if ((other.iSegment + 1) % poly_a.points.size() == this->iSegment) { // this->iSegment succeeds other.iSegment assert(seg_start_a == seg_end_b); // Avoid calling the 128bit x 128bit multiplication below if this->line intersects the common point. - if (cross(this->line->dir, seg_start_a - this->line->pos) == 0) + if (cross2(Vec2i64(this->line->dir.cast()), (seg_start_a - this->line->pos).cast()) == 0) return 0; } else { // General case. @@ -290,35 +290,35 @@ int SegmentIntersection::ordering_along_line(const SegmentIntersection &other) c } // First test, whether both points of one segment are completely in one half-plane of the other line. - const Point vec_b = seg_end_b - seg_start_b; - int side_start = signum(cross(vec_b, seg_start_a - seg_start_b)); - int side_end = signum(cross(vec_b, seg_end_a - seg_start_b)); + const Vec2i64 vec_b = (seg_end_b - seg_start_b).cast(); + int side_start = signum(cross2(vec_b, (seg_start_a - seg_start_b).cast())); + int side_end = signum(cross2(vec_b, (seg_end_a - seg_start_b).cast())); int side = side_start * side_end; if (side > 0) // This segment is completely inside one half-plane of the other line, therefore the ordering is trivial. - return signum(cross(vec_b, this->line->dir)) * side_start; + return signum(cross2(vec_b, this->line->dir.cast())) * side_start; - const Point vec_a = seg_end_a - seg_start_a; - int side_start2 = signum(cross(vec_a, seg_start_b - seg_start_a)); - int side_end2 = signum(cross(vec_a, seg_end_b - seg_start_a)); + const Vec2i64 vec_a = (seg_end_a - seg_start_a).cast(); + int side_start2 = signum(cross2(vec_a, (seg_start_b - seg_start_a).cast())); + int side_end2 = signum(cross2(vec_a, (seg_end_b - seg_start_a).cast())); int side2 = side_start2 * side_end2; //if (side == 0 && side2 == 0) // The segments share one of their end points. if (side2 > 0) // This segment is completely inside one half-plane of the other line, therefore the ordering is trivial. - return signum(cross(this->line->dir, vec_a)) * side_start2; + return signum(cross2(this->line->dir.cast(), vec_a)) * side_start2; // The two segments intersect and they are not sucessive segments of the same contour. // Ordering of the points depends on the position of the segment intersection (left / right from this->line), // therefore a simple test over the input segment end points is not sufficient. // Find the parameters of intersection of the two segmetns with this->line. - int64_t denom1 = cross(this->line->dir, vec_a); - int64_t denom2 = cross(this->line->dir, vec_b); - Point vx_a = seg_start_a - this->line->pos; - Point vx_b = seg_start_b - this->line->pos; - int64_t t1_times_denom1 = int64_t(vx_a.x) * int64_t(vec_a.y) - int64_t(vx_a.y) * int64_t(vec_a.x); - int64_t t2_times_denom2 = int64_t(vx_b.x) * int64_t(vec_b.y) - int64_t(vx_b.y) * int64_t(vec_b.x); + int64_t denom1 = cross2(this->line->dir.cast(), vec_a); + int64_t denom2 = cross2(this->line->dir.cast(), vec_b); + Vec2i64 vx_a = (seg_start_a - this->line->pos).cast(); + Vec2i64 vx_b = (seg_start_b - this->line->pos).cast(); + int64_t t1_times_denom1 = vx_a(0) * vec_a(1) - vx_a(1) * vec_a(0); + int64_t t2_times_denom2 = vx_b(0) * vec_b(1) - vx_b(1) * vec_b(0); assert(denom1 != 0); assert(denom2 != 0); return Int128::compare_rationals_filtered(t1_times_denom1, denom1, t2_times_denom2, denom2); @@ -330,7 +330,7 @@ bool SegmentIntersection::operator<(const SegmentIntersection &other) const #ifdef _DEBUG Point p1 = this->pos(); Point p2 = other.pos(); - int64_t d = dot(this->line->dir, p2 - p1); + int64_t d = this->line->dir.cast().dot((p2 - p1).cast()); #endif /* _DEBUG */ int ordering = this->ordering_along_line(other); #ifdef _DEBUG @@ -389,16 +389,16 @@ static bool prepare_infill_hatching_segments( // Define the flow spacing according to requested density. if (params.full_infill() && ! params.dont_adjust) { // Full infill, adjust the line spacing to fit an integer number of lines. - out.line_spacing = Fill::_adjust_solid_spacing(bounding_box.size().x, line_spacing); + out.line_spacing = Fill::_adjust_solid_spacing(bounding_box.size()(0), line_spacing); // Report back the adjusted line spacing. - fill_dir_params.spacing = float(unscale(line_spacing)); + fill_dir_params.spacing = unscale(line_spacing); } else { // Extend bounding box so that our pattern will be aligned with the other layers. // Transform the reference point to the rotated coordinate system. Point refpt = rotate_vector.second.rotated(- out.angle); // _align_to_grid will not work correctly with positive pattern_shift. coord_t pattern_shift_scaled = coord_t(scale_(fill_dir_params.pattern_shift)) % line_spacing; - refpt.x -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); + refpt(0) -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); bounding_box.merge(Fill::_align_to_grid( bounding_box.min, Point(line_spacing, line_spacing), @@ -407,13 +407,13 @@ static bool prepare_infill_hatching_segments( // Intersect a set of euqally spaced vertical lines wiht expolygon. // n_vlines = ceil(bbox_width / line_spacing) - size_t n_vlines = (bounding_box.max.x - bounding_box.min.x + line_spacing - 1) / line_spacing; - coord_t x0 = bounding_box.min.x; + size_t n_vlines = (bounding_box.max(0) - bounding_box.min(0) + line_spacing - 1) / line_spacing; + coord_t x0 = bounding_box.min(0); if (params.full_infill()) x0 += coord_t((line_spacing + SCALED_EPSILON) / 2); out.line_spacing = line_spacing; - out.start_point = Point(x0, bounding_box.min.y); + out.start_point = Point(x0, bounding_box.min(1)); out.start_point.rotate(out.angle); #ifdef SLIC3R_DEBUG @@ -436,10 +436,10 @@ static bool prepare_infill_hatching_segments( for (size_t i = 0; i < n_vlines; ++ i) { auto &seg = out.segs[i]; seg.idx = i; - // seg.x = x0 + coord_t(i) * line_spacing; + // seg(0) = x0 + coord_t(i) * line_spacing; coord_t x = x0 + coord_t(i) * line_spacing; - seg.pos.x = coord_t(floor(cos_a * x - sin_a * bounding_box.min.y + 0.5)); - seg.pos.y = coord_t(floor(cos_a * bounding_box.min.y + sin_a * x + 0.5)); + seg.pos(0) = coord_t(floor(cos_a * x - sin_a * bounding_box.min(1) + 0.5)); + seg.pos(1) = coord_t(floor(cos_a * bounding_box.min(1) + sin_a * x + 0.5)); seg.dir = out.direction; } @@ -454,7 +454,7 @@ static bool prepare_infill_hatching_segments( const Point *pr = &contour[iSegment]; // Orient the segment to the direction vector. const Point v = *pr - *pl; - int orientation = Int128::sign_determinant_2x2_filtered(v.x, v.y, out.direction.x, out.direction.y); + int orientation = Int128::sign_determinant_2x2_filtered(v(0), v(1), out.direction(0), out.direction(1)); if (orientation == 0) // Ignore strictly vertical segments. continue; @@ -462,8 +462,8 @@ static bool prepare_infill_hatching_segments( // Always orient the input segment consistently towards the hatching direction. std::swap(pl, pr); // Which of the equally spaced vertical lines is intersected by this segment? - coord_t l = (coord_t)floor(cos_a * pl->x + sin_a * pl->y - SCALED_EPSILON); - coord_t r = (coord_t)ceil (cos_a * pr->x + sin_a * pr->y + SCALED_EPSILON); + coord_t l = (coord_t)floor(cos_a * (*pl)(0) + sin_a * (*pl)(1) - SCALED_EPSILON); + coord_t r = (coord_t)ceil (cos_a * (*pr)(0) + sin_a * (*pr)(1) + SCALED_EPSILON); assert(l < r - SCALED_EPSILON); // il, ir are the left / right indices of vertical lines intersecting a segment int il = std::max(0, (l - x0 + line_spacing) / line_spacing); @@ -479,9 +479,9 @@ static bool prepare_infill_hatching_segments( // 2) all lines from il to ir intersect . assert(il >= 0 && ir < int(out.segs.size())); for (int i = il; i <= ir; ++ i) { - // assert(out.segs[i].x == i * line_spacing + x0); - // assert(l <= out.segs[i].x); - // assert(r >= out.segs[i].x); + // assert(out.segs[i](0) == i * line_spacing + x0); + // assert(l <= out.segs[i](0)); + // assert(r >= out.segs[i](0)); SegmentIntersection is; is.line = &out.segs[i]; is.expoly_with_offset = &poly_with_offset; @@ -491,10 +491,10 @@ static bool prepare_infill_hatching_segments( // +-1 to take rounding into account. assert(int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pl) >= 0); assert(int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pr) <= 0); - assert(is.pos().x + 1 >= std::min(pl->x, pr->x)); - assert(is.pos().y + 1 >= std::min(pl->y, pr->y)); - assert(is.pos().x <= std::max(pl->x, pr->x) + 1); - assert(is.pos().y <= std::max(pl->y, pr->y) + 1); + assert(is.pos()(0) + 1 >= std::min((*pl)(0), (*pr)(0))); + assert(is.pos()(1) + 1 >= std::min((*pl)(1), (*pr)(1))); + assert(is.pos()(0) <= std::max((*pl)(0), (*pr)(0)) + 1); + assert(is.pos()(1) <= std::max((*pl)(1), (*pr)(1)) + 1); out.segs[i].intersections.push_back(is); } } @@ -510,7 +510,7 @@ static bool prepare_infill_hatching_segments( for (size_t i = 1; i < sil.intersections.size(); ++ i) { Point p1 = sil.intersections[i - 1].pos(); Point p2 = sil.intersections[i].pos(); - int64_t d = dot(sil.dir, p2 - p1); + int64_t d = sil.dir.cast().dot((p2 - p1).cast()); assert(d >= - int64_t(SCALED_EPSILON)); } #endif /* _DEBUG */ @@ -659,12 +659,12 @@ static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Po Point px = (i == 0) ? p1 : p2; Point pa = poly.points[((seg == 0) ? poly.points.size() : seg) - 1]; Point pb = poly.points[seg]; - if (pa.x > pb.x) - std::swap(pa.x, pb.x); - if (pa.y > pb.y) - std::swap(pa.y, pb.y); - assert(px.x >= pa.x && px.x <= pb.x); - assert(px.y >= pa.y && px.y <= pb.y); + if (pa(0) > pb(0)) + std::swap(pa(0), pb(0)); + if (pa(1) > pb(1)) + std::swap(pa(1), pb(1)); + assert(px(0) >= pa(0) && px(0) <= pb(0)); + assert(px(1) >= pa(1) && px(1) <= pb(1)); } #endif /* SLIC3R_DEBUG */ const Point *pPrev = &p1; @@ -672,14 +672,14 @@ static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Po coordf_t len = 0; if (seg1 <= seg2) { for (size_t i = seg1; i < seg2; ++ i, pPrev = pThis) - len += pPrev->distance_to(*(pThis = &poly.points[i])); + len += (*pPrev - *(pThis = &poly.points[i])).cast().norm(); } else { for (size_t i = seg1; i < poly.points.size(); ++ i, pPrev = pThis) - len += pPrev->distance_to(*(pThis = &poly.points[i])); + len += (*pPrev - *(pThis = &poly.points[i])).cast().norm(); for (size_t i = 0; i < seg2; ++ i, pPrev = pThis) - len += pPrev->distance_to(*(pThis = &poly.points[i])); + len += (*pPrev - *(pThis = &poly.points[i])).cast().norm(); } - len += pPrev->distance_to(p2); + len += (*pPrev - p2).cast().norm(); return len; } @@ -1191,7 +1191,7 @@ static bool fill_hatching_segments_legacy( intrsctn.consumed_vertical_up : seg.intersections[i-1].consumed_vertical_up; if (! consumed) { - coordf_t dist2 = pointLast.distance_to(intrsctn.pos()); + coordf_t dist2 = (intrsctn.pos() - pointLast).cast().norm(); if (dist2 < dist2min) { dist2min = dist2; i_vline = i_vline2; @@ -1481,8 +1481,8 @@ static bool fill_hatching_segments_legacy( // Handle nearly zero length edges. if (polyline_current->points.size() <= 1 || (polyline_current->points.size() == 2 && - std::abs(polyline_current->points.front().x - polyline_current->points.back().x) < SCALED_EPSILON && - std::abs(polyline_current->points.front().y - polyline_current->points.back().y) < SCALED_EPSILON)) + std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON && + std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON)) polylines_out.pop_back(); intrsctn = NULL; i_intersection = -1; @@ -1510,7 +1510,7 @@ static bool fill_hatching_segments_legacy( // paths must be rotated back for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_initial; it != polylines_out.end(); ++ it) { // No need to translate, the absolute position is irrelevant. - // it->translate(- rotate_vector.second.x, - rotate_vector.second.y); + // it->translate(- rotate_vector.second(0), - rotate_vector.second(1)); assert(! it->has_duplicate_points()); //it->rotate(rotate_vector.first); //FIXME rather simplify the paths to avoid very short edges? diff --git a/xs/src/libslic3r/Fill/FillRectilinear3.hpp b/src/libslic3r/Fill/FillRectilinear3.hpp similarity index 100% rename from xs/src/libslic3r/Fill/FillRectilinear3.hpp rename to src/libslic3r/Fill/FillRectilinear3.hpp diff --git a/xs/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp similarity index 76% rename from xs/src/libslic3r/Flow.cpp rename to src/libslic3r/Flow.cpp index e92674a171..e71b935dbb 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -28,7 +28,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent { // we need layer height unless it's a bridge if (height <= 0 && bridge_flow_ratio == 0) - CONFESS("Invalid flow height supplied to new_from_config_width()"); + throw std::invalid_argument("Invalid flow height supplied to new_from_config_width()"); float w; if (bridge_flow_ratio > 0) { @@ -53,7 +53,7 @@ Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, { // we need layer height unless it's a bridge if (height <= 0 && !bridge) - CONFESS("Invalid flow height supplied to new_from_spacing()"); + throw std::invalid_argument("Invalid flow height supplied to new_from_spacing()"); // Calculate width from spacing. // For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions. // For bridge extrusions, the extrusions are placed with a tiny BRIDGE_EXTRA_SPACING gaps between the threads. @@ -111,23 +111,23 @@ Flow support_material_flow(const PrintObject *object, float layer_height) return Flow::new_from_config_width( frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, - // if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config.layer_height.value), + (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, + // if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. + float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), + (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value), // bridge_flow_ratio 0.f); } Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) { - const auto &width = (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width; + const auto &width = (object->print()->config().first_layer_extrusion_width.value > 0) ? object->print()->config().first_layer_extrusion_width : object->config().support_material_extrusion_width; return Flow::new_from_config_width( frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (width.value > 0) ? width : object->config.extrusion_width, - float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config.first_layer_height.get_abs_value(object->config.layer_height.value)), + (width.value > 0) ? width : object->config().extrusion_width, + float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), + (layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)), // bridge_flow_ratio 0.f); } @@ -137,10 +137,10 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig return Flow::new_from_config_width( frSupportMaterialInterface, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (object->config.support_material_extrusion_width > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, - // if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config.layer_height.value), + (object->config().support_material_extrusion_width > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, + // if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. + float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)), + (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value), // bridge_flow_ratio 0.f); } diff --git a/xs/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp similarity index 100% rename from xs/src/libslic3r/Flow.hpp rename to src/libslic3r/Flow.hpp diff --git a/xs/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp similarity index 85% rename from xs/src/libslic3r/Format/3mf.cpp rename to src/libslic3r/Format/3mf.cpp index 5f6a48cf82..6356fa17a6 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2,7 +2,7 @@ #include "../Model.hpp" #include "../Utils.hpp" #include "../GCode.hpp" -#include "../slic3r/GUI/PresetBundle.hpp" +#include "../Geometry.hpp" #include "3mf.hpp" @@ -30,6 +30,7 @@ const std::string RELATIONSHIPS_FILE = "_rels/.rels"; const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config"; const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config"; const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt"; +const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt"; const char* MODEL_TAG = "model"; const char* RESOURCES_TAG = "resources"; @@ -88,8 +89,6 @@ const char* INVALID_OBJECT_TYPES[] = "other" }; -typedef Eigen::Matrix Matrix4x4; - const char* get_attribute_value_charptr(const char** attributes, unsigned int attributes_size, const char* attribute_key) { if ((attributes == nullptr) || (attributes_size == 0) || (attributes_size % 2 != 0) || (attribute_key == nullptr)) @@ -122,11 +121,11 @@ int get_attribute_value_int(const char** attributes, unsigned int attributes_siz return (text != nullptr) ? ::atoi(text) : 0; } -Matrix4x4 get_matrix_from_string(const std::string& mat_str) +Slic3r::Transform3d get_transform_from_string(const std::string& mat_str) { if (mat_str.empty()) // empty string means default identity matrix - return Matrix4x4::Identity(); + return Slic3r::Transform3d::Identity(); std::vector mat_elements_str; boost::split(mat_elements_str, mat_str, boost::is_any_of(" "), boost::token_compress_on); @@ -134,9 +133,9 @@ Matrix4x4 get_matrix_from_string(const std::string& mat_str) unsigned int size = (unsigned int)mat_elements_str.size(); if (size != 12) // invalid data, return identity matrix - return Matrix4x4::Identity(); + return Slic3r::Transform3d::Identity(); - Matrix4x4 ret = Matrix4x4::Identity(); + Slic3r::Transform3d ret = Slic3r::Transform3d::Identity(); unsigned int i = 0; // matrices are stored into 3mf files as 4x3 // we need to transpose them @@ -144,7 +143,7 @@ Matrix4x4 get_matrix_from_string(const std::string& mat_str) { for (unsigned int r = 0; r < 3; ++r) { - ret(r, c) = (float)::atof(mat_elements_str[i++].c_str()); + ret(r, c) = ::atof(mat_elements_str[i++].c_str()); } } return ret; @@ -210,17 +209,17 @@ namespace Slic3r { struct Component { int object_id; - Matrix4x4 matrix; + Transform3d transform; explicit Component(int object_id) : object_id(object_id) - , matrix(Matrix4x4::Identity()) + , transform(Transform3d::Identity()) { } - Component(int object_id, const Matrix4x4& matrix) + Component(int object_id, const Transform3d& transform) : object_id(object_id) - , matrix(matrix) + , transform(transform) { } }; @@ -274,11 +273,11 @@ namespace Slic3r { struct Instance { ModelInstance* instance; - Matrix4x4 matrix; + Transform3d transform; - Instance(ModelInstance* instance, const Matrix4x4& matrix) + Instance(ModelInstance* instance, const Transform3d& transform) : instance(instance) - , matrix(matrix) + , transform(transform) { } }; @@ -324,6 +323,7 @@ namespace Slic3r { typedef std::map IdToMetadataMap; typedef std::map IdToGeometryMap; typedef std::map> IdToLayerHeightsProfileMap; + typedef std::map> IdToSlaSupportPointsMap; // Version of the 3mf file unsigned int m_version; @@ -339,23 +339,27 @@ namespace Slic3r { CurrentConfig m_curr_config; IdToMetadataMap m_objects_metadata; IdToLayerHeightsProfileMap m_layer_heights_profiles; + IdToSlaSupportPointsMap m_sla_support_points; std::string m_curr_metadata_name; std::string m_curr_characters; + std::string m_name; public: _3MF_Importer(); ~_3MF_Importer(); - bool load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle); + bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config); private: void _destroy_xml_parser(); void _stop_xml_parser(); - bool _load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle); + bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config); bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); - void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename); + void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); + + void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename); bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model); // handlers to parse the .model file @@ -406,9 +410,9 @@ namespace Slic3r { bool _handle_start_metadata(const char** attributes, unsigned int num_attributes); bool _handle_end_metadata(); - bool _create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter); + bool _create_object_instance(int object_id, const Transform3d& transform, unsigned int recur_counter); - void _apply_transform(ModelInstance& instance, const Matrix4x4& matrix); + void _apply_transform(ModelInstance& instance, const Transform3d& transform); bool _handle_start_config(const char** attributes, unsigned int num_attributes); bool _handle_end_config(); @@ -441,6 +445,7 @@ namespace Slic3r { , m_unit_factor(1.0f) , m_curr_metadata_name("") , m_curr_characters("") + , m_name("") { } @@ -449,7 +454,7 @@ namespace Slic3r { _destroy_xml_parser(); } - bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle) + bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config) { m_version = 0; m_model = &model; @@ -463,11 +468,12 @@ namespace Slic3r { m_curr_config.volume_id = -1; m_objects_metadata.clear(); m_layer_heights_profiles.clear(); + m_sla_support_points.clear(); m_curr_metadata_name.clear(); m_curr_characters.clear(); clear_errors(); - return _load_model_from_file(filename, model, bundle); + return _load_model_from_file(filename, model, config); } void _3MF_Importer::_destroy_xml_parser() @@ -485,7 +491,7 @@ namespace Slic3r { XML_StopParser(m_xml_parser, false); } - bool _3MF_Importer::_load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle) + bool _3MF_Importer::_load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config) { mz_zip_archive archive; mz_zip_zero_struct(&archive); @@ -501,6 +507,8 @@ namespace Slic3r { mz_zip_archive_file_stat stat; + m_name = boost::filesystem::path(filename).filename().stem().string(); + // we first loop the entries to read from the archive the .model file only, in order to extract the version from it for (mz_uint i = 0; i < num_entries; ++i) { @@ -535,10 +543,15 @@ namespace Slic3r { // extract slic3r lazer heights profile file _extract_layer_heights_profile_config_from_archive(archive, stat); } + else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE)) + { + // extract sla support points file + _extract_sla_support_points_from_archive(archive, stat); + } else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE)) { // extract slic3r print config file - _extract_print_config_from_archive(archive, stat, bundle, filename); + _extract_print_config_from_archive(archive, stat, config, filename); } else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE)) { @@ -574,6 +587,10 @@ namespace Slic3r { object.second->layer_height_profile_valid = true; } + IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.first); + if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) + object.second->sla_support_points = obj_sla_support_points->second; + IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first); if (obj_metadata != m_objects_metadata.end()) { @@ -658,7 +675,7 @@ namespace Slic3r { return true; } - void _3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename) + void _3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename) { if (stat.m_uncomp_size > 0) { @@ -669,7 +686,7 @@ namespace Slic3r { add_error("Error while reading config data to buffer"); return; } - bundle.load_config_string(buffer.data(), archive_filename.c_str()); + config.load_from_gcode_string(buffer.data()); } } @@ -744,6 +761,71 @@ namespace Slic3r { } } + void _3MF_Importer::_extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) + { + if (stat.m_uncomp_size > 0) + { + std::string buffer((size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + if (res == 0) + { + add_error("Error while reading sla support points data to buffer"); + return; + } + + if (buffer.back() == '\n') + buffer.pop_back(); + + std::vector objects; + boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off); + + for (const std::string& object : objects) + { + std::vector object_data; + boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off); + if (object_data.size() != 2) + { + add_error("Error while reading object data"); + continue; + } + + std::vector object_data_id; + boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off); + if (object_data_id.size() != 2) + { + add_error("Error while reading object id"); + continue; + } + + int object_id = std::atoi(object_data_id[1].c_str()); + if (object_id == 0) + { + add_error("Found invalid object id"); + continue; + } + + IdToSlaSupportPointsMap::iterator object_item = m_sla_support_points.find(object_id); + if (object_item != m_sla_support_points.end()) + { + add_error("Found duplicated SLA support points"); + continue; + } + + std::vector object_data_points; + boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off); + + std::vector sla_support_points; + + for (unsigned int i=0; i()); + + if (!sla_support_points.empty()) + m_sla_support_points.insert(IdToSlaSupportPointsMap::value_type(object_id, sla_support_points)); + } + } + } + + bool _3MF_Importer::_extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model) { if (stat.m_uncomp_size == 0) @@ -935,8 +1017,8 @@ namespace Slic3r { ModelObject* object = instance.instance->get_object(); if (object != nullptr) { - // apply the matrix to the instance - _apply_transform(*instance.instance, instance.matrix); + // apply the transform to the instance + _apply_transform(*instance.instance, instance.transform); } } } @@ -973,6 +1055,9 @@ namespace Slic3r { // set object data m_curr_object.object->name = get_attribute_value_string(attributes, num_attributes, NAME_ATTR); + if (m_curr_object.object->name.empty()) + m_curr_object.object->name = m_name + "_" + std::to_string(m_model->objects.size()); + m_curr_object.id = get_attribute_value_int(attributes, num_attributes, ID_ATTR); } @@ -1120,7 +1205,7 @@ namespace Slic3r { bool _3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes) { int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); - Matrix4x4 matrix = get_matrix_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); + Transform3d transform = get_transform_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); IdToModelObjectMap::iterator object_item = m_objects.find(object_id); if (object_item == m_objects.end()) @@ -1133,7 +1218,7 @@ namespace Slic3r { } } - m_curr_object.components.emplace_back(object_id, matrix); + m_curr_object.components.emplace_back(object_id, transform); return true; } @@ -1166,9 +1251,9 @@ namespace Slic3r { // see specifications int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); - Matrix4x4 matrix = get_matrix_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); + Transform3d transform = get_transform_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); - return _create_object_instance(object_id, matrix, 1); + return _create_object_instance(object_id, transform, 1); } bool _3MF_Importer::_handle_end_item() @@ -1196,7 +1281,7 @@ namespace Slic3r { return true; } - bool _3MF_Importer::_create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter) + bool _3MF_Importer::_create_object_instance(int object_id, const Transform3d& transform, unsigned int recur_counter) { static const unsigned int MAX_RECURSIONS = 10; @@ -1233,7 +1318,7 @@ namespace Slic3r { return false; } - m_instances.emplace_back(instance, matrix); + m_instances.emplace_back(instance, transform); } } else @@ -1241,7 +1326,7 @@ namespace Slic3r { // recursively process nested components for (const Component& component : it->second) { - if (!_create_object_instance(component.object_id, matrix * component.matrix, recur_counter + 1)) + if (!_create_object_instance(component.object_id, transform * component.transform, recur_counter + 1)) return false; } } @@ -1249,114 +1334,51 @@ namespace Slic3r { return true; } - void _3MF_Importer::_apply_transform(ModelInstance& instance, const Matrix4x4& matrix) + void _3MF_Importer::_apply_transform(ModelInstance& instance, const Transform3d& transform) { - // slic3r ModelInstance cannot be transformed using a matrix - // we extract from the given matrix only the values currently used +#if ENABLE_MODELVOLUME_TRANSFORM + Slic3r::Geometry::Transformation t(transform); + // invalid scale value, return + if (!t.get_scaling_factor().all()) + return; + instance.set_transformation(t); +#else // translation - double offset_x = (double)matrix(0, 3); - double offset_y = (double)matrix(1, 3); - double offset_z = (double)matrix(2, 3); + Vec3d offset = transform.matrix().block(0, 3, 3, 1); + + Eigen::Matrix m3x3 = transform.matrix().block(0, 0, 3, 3); + // mirror + // it is impossible to reconstruct the original mirroring factors from a matrix, + // we can only detect if the matrix contains a left handed reference system + // in which case we reorient it back to right handed by mirroring the x axis + Vec3d mirror = Vec3d::Ones(); + if (m3x3.col(0).dot(m3x3.col(1).cross(m3x3.col(2))) < 0.0) + { + mirror(0) = -1.0; + // remove mirror + m3x3.col(0) *= -1.0; + } // scale - double sx = ::sqrt(sqr((double)matrix(0, 0)) + sqr((double)matrix(1, 0)) + sqr((double)matrix(2, 0))); - double sy = ::sqrt(sqr((double)matrix(0, 1)) + sqr((double)matrix(1, 1)) + sqr((double)matrix(2, 1))); - double sz = ::sqrt(sqr((double)matrix(0, 2)) + sqr((double)matrix(1, 2)) + sqr((double)matrix(2, 2))); + Vec3d scale(m3x3.col(0).norm(), m3x3.col(1).norm(), m3x3.col(2).norm()); // invalid scale value, return - if ((sx == 0.0) || (sy == 0.0) || (sz == 0.0)) + if ((scale(0) == 0.0) || (scale(1) == 0.0) || (scale(2) == 0.0)) return; - // non-uniform scale value, return - if ((std::abs(sx - sy) > 0.00001) || (std::abs(sx - sz) > 0.00001)) - return; + // remove scale + m3x3.col(0).normalize(); + m3x3.col(1).normalize(); + m3x3.col(2).normalize(); -#if 0 // use quaternions - // rotations (extracted using quaternion) - double inv_sx = 1.0 / sx; - double inv_sy = 1.0 / sy; - double inv_sz = 1.0 / sz; - - Eigen::Matrix m3x3; - m3x3 << (double)matrix(0, 0) * inv_sx, (double)matrix(0, 1) * inv_sy, (double)matrix(0, 2) * inv_sz, - (double)matrix(1, 0) * inv_sx, (double)matrix(1, 1) * inv_sy, (double)matrix(1, 2) * inv_sz, - (double)matrix(2, 0) * inv_sx, (double)matrix(2, 1) * inv_sy, (double)matrix(2, 2) * inv_sz; + Vec3d rotation = Slic3r::Geometry::extract_euler_angles(m3x3); - double qw = 0.5 * ::sqrt(std::max(0.0, 1.0 + m3x3(0, 0) + m3x3(1, 1) + m3x3(2, 2))); - double qx = 0.5 * ::sqrt(std::max(0.0, 1.0 + m3x3(0, 0) - m3x3(1, 1) - m3x3(2, 2))); - double qy = 0.5 * ::sqrt(std::max(0.0, 1.0 - m3x3(0, 0) + m3x3(1, 1) - m3x3(2, 2))); - double qz = 0.5 * ::sqrt(std::max(0.0, 1.0 - m3x3(0, 0) - m3x3(1, 1) + m3x3(2, 2))); - - double q_magnitude = ::sqrt(sqr(qw) + sqr(qx) + sqr(qy) + sqr(qz)); - - // invalid length, return - if (q_magnitude == 0.0) - return; - - double inv_q_magnitude = 1.0 / q_magnitude; - - qw *= inv_q_magnitude; - qx *= inv_q_magnitude; - qy *= inv_q_magnitude; - qz *= inv_q_magnitude; - - double test = qx * qy + qz * qw; - double angle_x, angle_y, angle_z; - - if (test > 0.499) - { - // singularity at north pole - angle_x = 0.0; - angle_y = 2.0 * ::atan2(qx, qw); - angle_z = 0.5 * PI; - } - else if (test < -0.499) - { - // singularity at south pole - angle_x = 0.0; - angle_y = -2.0 * ::atan2(qx, qw); - angle_z = -0.5 * PI; - } - else - { - angle_x = ::atan2(2.0 * qx * qw - 2.0 * qy * qz, 1.0 - 2.0 * sqr(qx) - 2.0 * sqr(qz)); - angle_y = ::atan2(2.0 * qy * qw - 2.0 * qx * qz, 1.0 - 2.0 * sqr(qy) - 2.0 * sqr(qz)); - angle_z = ::asin(2.0 * qx * qy + 2.0 * qz * qw); - - if (angle_x < 0.0) - angle_x += 2.0 * PI; - - if (angle_y < 0.0) - angle_y += 2.0 * PI; - - if (angle_z < 0.0) - angle_z += 2.0 * PI; - } -#else // use eigen library - double inv_sx = 1.0 / sx; - double inv_sy = 1.0 / sy; - double inv_sz = 1.0 / sz; - - Eigen::Matrix3d m3x3; - m3x3 << (double)matrix(0, 0) * inv_sx, (double)matrix(0, 1) * inv_sy, (double)matrix(0, 2) * inv_sz, - (double)matrix(1, 0) * inv_sx, (double)matrix(1, 1) * inv_sy, (double)matrix(1, 2) * inv_sz, - (double)matrix(2, 0) * inv_sx, (double)matrix(2, 1) * inv_sy, (double)matrix(2, 2) * inv_sz; - - Eigen::AngleAxisd rotation; - rotation.fromRotationMatrix(m3x3); - - // invalid rotation axis, we currently handle only rotations around Z axis - if ((rotation.angle() != 0.0) && (rotation.axis() != Eigen::Vector3d::UnitZ()) && (rotation.axis() != -Eigen::Vector3d::UnitZ())) - return; - - double angle_z = (rotation.axis() == Eigen::Vector3d::UnitZ()) ? rotation.angle() : -rotation.angle(); -#endif - - instance.offset.x = offset_x; - instance.offset.y = offset_y; - instance.scaling_factor = sx; - instance.rotation = angle_z; + instance.set_offset(offset); + instance.set_scaling_factor(scale); + instance.set_rotation(rotation); + instance.set_mirror(mirror); +#endif // ENABLE_MODELVOLUME_TRANSFORM } bool _3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes) @@ -1401,7 +1423,7 @@ namespace Slic3r { return false; } - m_curr_config.volume_id = object->second.volumes.size(); + m_curr_config.volume_id = (int)object->second.volumes.size(); unsigned int first_triangle_id = (unsigned int)get_attribute_value_int(attributes, num_attributes, FIRST_TRIANGLE_ID_ATTR); unsigned int last_triangle_id = (unsigned int)get_attribute_value_int(attributes, num_attributes, LAST_TRIANGLE_ID_ATTR); @@ -1459,7 +1481,7 @@ namespace Slic3r { return false; } - unsigned int geo_tri_count = geometry.triangles.size() / 3; + unsigned int geo_tri_count = (unsigned int)geometry.triangles.size() / 3; for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) { @@ -1480,13 +1502,13 @@ namespace Slic3r { unsigned int src_start_id = volume_data.first_triangle_id * 3; - for (size_t i = 0; i < triangles_count; ++i) + for (unsigned int i = 0; i < triangles_count; ++i) { unsigned int ii = i * 3; stl_facet& facet = stl.facet_start[i]; for (unsigned int v = 0; v < 3; ++v) { - ::memcpy((void*)&facet.vertex[v].x, (const void*)&geometry.vertices[geometry.triangles[src_start_id + ii + v] * 3], 3 * sizeof(float)); + ::memcpy(facet.vertex[v].data(), (const void*)&geometry.vertices[geometry.triangles[src_start_id + ii + v] * 3], 3 * sizeof(float)); } } @@ -1551,11 +1573,11 @@ namespace Slic3r { struct BuildItem { unsigned int id; - Matrix4x4 matrix; + Transform3d transform; - BuildItem(unsigned int id, const Matrix4x4& matrix) + BuildItem(unsigned int id, const Transform3d& transform) : id(id) - , matrix(matrix) + , transform(transform) { } }; @@ -1593,10 +1615,10 @@ namespace Slic3r { IdToObjectDataMap m_objects_data; public: - bool save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config); + bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config); private: - bool _save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config); + bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config); bool _add_content_types_file_to_archive(mz_zip_archive& archive); bool _add_relationships_file_to_archive(mz_zip_archive& archive); bool _add_model_file_to_archive(mz_zip_archive& archive, Model& model); @@ -1604,17 +1626,18 @@ namespace Slic3r { bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets); bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items); bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model); - bool _add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print); + bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model); + bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config); bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model); }; - bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config) + bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config) { clear_errors(); - return _save_model_to_file(filename, model, print, export_print_config); + return _save_model_to_file(filename, model, config); } - bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config) + bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config) { mz_zip_archive archive; mz_zip_zero_struct(&archive); @@ -1660,10 +1683,18 @@ namespace Slic3r { return false; } - // adds slic3r print config file - if (export_print_config) + // adds sla support points file + if (!_add_sla_support_points_file_to_archive(archive, model)) { - if (!_add_print_config_file_to_archive(archive, print)) + mz_zip_writer_end(&archive); + boost::filesystem::remove(filename); + return false; + } + + // adds slic3r print config file + if (config != nullptr) + { + if (!_add_print_config_file_to_archive(archive, *config)) { mz_zip_writer_end(&archive); boost::filesystem::remove(filename); @@ -1804,9 +1835,8 @@ namespace Slic3r { stream << " \n"; } - Eigen::Affine3f transform; - transform = Eigen::Translation3f((float)instance->offset.x, (float)instance->offset.y, 0.0f) * Eigen::AngleAxisf((float)instance->rotation, Eigen::Vector3f::UnitZ()) * Eigen::Scaling((float)instance->scaling_factor); - build_items.emplace_back(instance_id, transform.matrix()); + Transform3d t = instance->get_matrix(); + build_items.emplace_back(instance_id, t); stream << " \n"; @@ -1828,7 +1858,7 @@ namespace Slic3r { if (volume == nullptr) continue; - VolumeToOffsetsMap::iterator volume_it = volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first; + volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first; if (!volume->mesh.repaired) volume->mesh.repair(); @@ -1845,12 +1875,23 @@ namespace Slic3r { vertices_count += stl.stats.shared_vertices; +#if ENABLE_MODELVOLUME_TRANSFORM + Transform3d matrix = volume->get_matrix(); +#endif // ENABLE_MODELVOLUME_TRANSFORM + for (int i = 0; i < stl.stats.shared_vertices; ++i) { stream << " <" << VERTEX_TAG << " "; - stream << "x=\"" << stl.v_shared[i].x << "\" "; - stream << "y=\"" << stl.v_shared[i].y << "\" "; - stream << "z=\"" << stl.v_shared[i].z << "\" />\n"; +#if ENABLE_MODELVOLUME_TRANSFORM + Vec3d v = matrix * stl.v_shared[i].cast(); + stream << "x=\"" << v(0) << "\" "; + stream << "y=\"" << v(1) << "\" "; + stream << "z=\"" << v(2) << "\" />\n"; +#else + stream << "x=\"" << stl.v_shared[i](0) << "\" "; + stream << "y=\"" << stl.v_shared[i](1) << "\" "; + stream << "z=\"" << stl.v_shared[i](2) << "\" />\n"; +#endif // ENABLE_MODELVOLUME_TRANSFORM } } @@ -1907,7 +1948,7 @@ namespace Slic3r { { for (unsigned r = 0; r < 3; ++r) { - stream << item.matrix(r, c); + stream << item.transform(r, c); if ((r != 2) || (c != 3)) stream << " "; } @@ -1958,13 +1999,51 @@ namespace Slic3r { return true; } - bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print) + bool _3MF_Exporter::_add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model) + { + std::string out = ""; + char buffer[1024]; + + unsigned int count = 0; + for (const ModelObject* object : model.objects) + { + ++count; + const std::vector& sla_support_points = object->sla_support_points; + if (!sla_support_points.empty()) + { + sprintf(buffer, "object_id=%d|", count); + out += buffer; + + // Store the layer height profile as a single space separated list. + for (size_t i = 0; i < sla_support_points.size(); ++i) + { + sprintf(buffer, (i==0 ? "%f %f %f" : " %f %f %f"), sla_support_points[i](0), sla_support_points[i](1), sla_support_points[i](2)); + out += buffer; + } + out += "\n"; + } + } + + if (!out.empty()) + { + if (!mz_zip_writer_add_mem(&archive, SLA_SUPPORT_POINTS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) + { + add_error("Unable to add sla support points file to archive"); + return false; + } + } + return true; + } + + bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config) { char buffer[1024]; sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str()); std::string out = buffer; - GCode::append_full_config(print, out); + for (const std::string &key : config.keys()) + if (key != "compatible_printers") + out += "; " + key + " = " + config.serialize(key) + "\n"; if (!out.empty()) { @@ -2053,24 +2132,24 @@ namespace Slic3r { return true; } - bool load_3mf(const char* path, PresetBundle* bundle, Model* model) + bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model) { - if ((path == nullptr) || (bundle == nullptr) || (model == nullptr)) + if ((path == nullptr) || (config == nullptr) || (model == nullptr)) return false; _3MF_Importer importer; - bool res = importer.load_model_from_file(path, *model, *bundle); + bool res = importer.load_model_from_file(path, *model, *config); importer.log_errors(); return res; } - bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config) + bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config) { - if ((path == nullptr) || (model == nullptr) || (print == nullptr)) + if ((path == nullptr) || (model == nullptr)) return false; _3MF_Exporter exporter; - bool res = exporter.save_model_to_file(path, *model, *print, export_print_config); + bool res = exporter.save_model_to_file(path, *model, config); if (!res) exporter.log_errors(); diff --git a/xs/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp similarity index 67% rename from xs/src/libslic3r/Format/3mf.hpp rename to src/libslic3r/Format/3mf.hpp index 85bc812e38..44b37c1aee 100644 --- a/xs/src/libslic3r/Format/3mf.hpp +++ b/src/libslic3r/Format/3mf.hpp @@ -4,15 +4,14 @@ namespace Slic3r { class Model; - class Print; - class PresetBundle; + class DynamicPrintConfig; // Load the content of a 3mf file into the given model and preset bundle. - extern bool load_3mf(const char* path, PresetBundle* bundle, Model* model); + extern bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model); // Save the given model and the config data contained in the given Print into a 3mf file. // The model could be modified during the export process if meshes are not repaired or have no shared vertices - extern bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config); + extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config); }; // namespace Slic3r diff --git a/xs/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp similarity index 72% rename from xs/src/libslic3r/Format/AMF.cpp rename to src/libslic3r/Format/AMF.cpp index 5aa922f62d..0893688658 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -8,8 +8,8 @@ #include "../libslic3r.h" #include "../Model.hpp" #include "../GCode.hpp" +#include "../PrintConfig.hpp" #include "../Utils.hpp" -#include "../slic3r/GUI/PresetBundle.hpp" #include "AMF.hpp" #include @@ -29,7 +29,11 @@ // VERSION NUMBERS // 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them. // 1 : Introduction of amf versioning. No other change in data saved into amf files. -const unsigned int VERSION_AMF = 1; +// 2 : Added z component of offset +// Added x and y components of rotation +// Added x, y and z components of scale +// Added x, y and z components of mirror +const unsigned int VERSION_AMF = 2; const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version"; const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config"; @@ -39,7 +43,7 @@ namespace Slic3r struct AMFParserContext { - AMFParserContext(XML_Parser parser, const std::string& archive_filename, PresetBundle* preset_bundle, Model *model) : + AMFParserContext(XML_Parser parser, DynamicPrintConfig *config, Model *model) : m_version(0), m_parser(parser), m_model(*model), @@ -47,8 +51,7 @@ struct AMFParserContext m_volume(nullptr), m_material(nullptr), m_instance(nullptr), - m_preset_bundle(preset_bundle), - m_archive_filename(archive_filename) + m_config(config) { m_path.reserve(12); } @@ -119,25 +122,58 @@ struct AMFParserContext NODE_TYPE_INSTANCE, // amf/constellation/instance NODE_TYPE_DELTAX, // amf/constellation/instance/deltax NODE_TYPE_DELTAY, // amf/constellation/instance/deltay + NODE_TYPE_DELTAZ, // amf/constellation/instance/deltaz + NODE_TYPE_RX, // amf/constellation/instance/rx + NODE_TYPE_RY, // amf/constellation/instance/ry NODE_TYPE_RZ, // amf/constellation/instance/rz NODE_TYPE_SCALE, // amf/constellation/instance/scale + NODE_TYPE_SCALEX, // amf/constellation/instance/scalex + NODE_TYPE_SCALEY, // amf/constellation/instance/scaley + NODE_TYPE_SCALEZ, // amf/constellation/instance/scalez + NODE_TYPE_MIRRORX, // amf/constellation/instance/mirrorx + NODE_TYPE_MIRRORY, // amf/constellation/instance/mirrory + NODE_TYPE_MIRRORZ, // amf/constellation/instance/mirrorz NODE_TYPE_METADATA, // anywhere under amf/*/metadata }; struct Instance { - Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {} + Instance() + : deltax_set(false), deltay_set(false), deltaz_set(false) + , rx_set(false), ry_set(false), rz_set(false) + , scalex_set(false), scaley_set(false), scalez_set(false) + , mirrorx_set(false), mirrory_set(false), mirrorz_set(false) {} // Shift in the X axis. float deltax; bool deltax_set; // Shift in the Y axis. float deltay; bool deltay_set; + // Shift in the Z axis. + float deltaz; + bool deltaz_set; + // Rotation around the X axis. + float rx; + bool rx_set; + // Rotation around the Y axis. + float ry; + bool ry_set; // Rotation around the Z axis. float rz; bool rz_set; - // Scaling factor - float scale; - bool scale_set; + // Scaling factors + float scalex; + bool scalex_set; + float scaley; + bool scaley_set; + float scalez; + bool scalez_set; + // Mirroring factors + float mirrorx; + bool mirrorx_set; + float mirrory; + bool mirrory_set; + float mirrorz; + bool mirrorz_set; }; struct Object { @@ -170,10 +206,8 @@ struct AMFParserContext Instance *m_instance; // Generic string buffer for vertices, face indices, metadata etc. std::string m_value[3]; - // Pointer to preset bundle to update if config data are stored inside the amf file - PresetBundle* m_preset_bundle; - // Fullpath name of the amf file - std::string m_archive_filename; + // Pointer to config to update if config data are stored inside the amf file + DynamicPrintConfig *m_config; private: AMFParserContext& operator=(AMFParserContext&); @@ -254,10 +288,28 @@ void AMFParserContext::startElement(const char *name, const char **atts) node_type_new = NODE_TYPE_DELTAX; else if (strcmp(name, "deltay") == 0) node_type_new = NODE_TYPE_DELTAY; + else if (strcmp(name, "deltaz") == 0) + node_type_new = NODE_TYPE_DELTAZ; + else if (strcmp(name, "rx") == 0) + node_type_new = NODE_TYPE_RX; + else if (strcmp(name, "ry") == 0) + node_type_new = NODE_TYPE_RY; else if (strcmp(name, "rz") == 0) node_type_new = NODE_TYPE_RZ; + else if (strcmp(name, "scalex") == 0) + node_type_new = NODE_TYPE_SCALEX; + else if (strcmp(name, "scaley") == 0) + node_type_new = NODE_TYPE_SCALEY; + else if (strcmp(name, "scalez") == 0) + node_type_new = NODE_TYPE_SCALEZ; else if (strcmp(name, "scale") == 0) node_type_new = NODE_TYPE_SCALE; + else if (strcmp(name, "mirrorx") == 0) + node_type_new = NODE_TYPE_MIRRORX; + else if (strcmp(name, "mirrory") == 0) + node_type_new = NODE_TYPE_MIRRORY; + else if (strcmp(name, "mirrorz") == 0) + node_type_new = NODE_TYPE_MIRRORZ; } break; case 4: @@ -314,7 +366,19 @@ void AMFParserContext::characters(const XML_Char *s, int len) { switch (m_path.size()) { case 4: - if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE) + if (m_path.back() == NODE_TYPE_DELTAX || + m_path.back() == NODE_TYPE_DELTAY || + m_path.back() == NODE_TYPE_DELTAZ || + m_path.back() == NODE_TYPE_RX || + m_path.back() == NODE_TYPE_RY || + m_path.back() == NODE_TYPE_RZ || + m_path.back() == NODE_TYPE_SCALEX || + m_path.back() == NODE_TYPE_SCALEY || + m_path.back() == NODE_TYPE_SCALEZ || + m_path.back() == NODE_TYPE_SCALE || + m_path.back() == NODE_TYPE_MIRRORX || + m_path.back() == NODE_TYPE_MIRRORY || + m_path.back() == NODE_TYPE_MIRRORZ) m_value[0].append(s, len); break; case 6: @@ -354,6 +418,24 @@ void AMFParserContext::endElement(const char * /* name */) m_instance->deltay_set = true; m_value[0].clear(); break; + case NODE_TYPE_DELTAZ: + assert(m_instance); + m_instance->deltaz = float(atof(m_value[0].c_str())); + m_instance->deltaz_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_RX: + assert(m_instance); + m_instance->rx = float(atof(m_value[0].c_str())); + m_instance->rx_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_RY: + assert(m_instance); + m_instance->ry = float(atof(m_value[0].c_str())); + m_instance->ry_set = true; + m_value[0].clear(); + break; case NODE_TYPE_RZ: assert(m_instance); m_instance->rz = float(atof(m_value[0].c_str())); @@ -362,8 +444,48 @@ void AMFParserContext::endElement(const char * /* name */) break; case NODE_TYPE_SCALE: assert(m_instance); - m_instance->scale = float(atof(m_value[0].c_str())); - m_instance->scale_set = true; + m_instance->scalex = float(atof(m_value[0].c_str())); + m_instance->scalex_set = true; + m_instance->scaley = float(atof(m_value[0].c_str())); + m_instance->scaley_set = true; + m_instance->scalez = float(atof(m_value[0].c_str())); + m_instance->scalez_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_SCALEX: + assert(m_instance); + m_instance->scalex = float(atof(m_value[0].c_str())); + m_instance->scalex_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_SCALEY: + assert(m_instance); + m_instance->scaley = float(atof(m_value[0].c_str())); + m_instance->scaley_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_SCALEZ: + assert(m_instance); + m_instance->scalez = float(atof(m_value[0].c_str())); + m_instance->scalez_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_MIRRORX: + assert(m_instance); + m_instance->mirrorx = float(atof(m_value[0].c_str())); + m_instance->mirrorx_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_MIRRORY: + assert(m_instance); + m_instance->mirrory = float(atof(m_value[0].c_str())); + m_instance->mirrory_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_MIRRORZ: + assert(m_instance); + m_instance->mirrorz = float(atof(m_value[0].c_str())); + m_instance->mirrorz_set = true; m_value[0].clear(); break; @@ -402,7 +524,7 @@ void AMFParserContext::endElement(const char * /* name */) for (size_t i = 0; i < m_volume_facets.size();) { stl_facet &facet = stl.facet_start[i/3]; for (unsigned int v = 0; v < 3; ++ v) - memcpy(&facet.vertex[v].x, &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float)); + memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float)); } stl_get_size(&stl); m_volume->mesh.repair(); @@ -429,9 +551,8 @@ void AMFParserContext::endElement(const char * /* name */) break; case NODE_TYPE_METADATA: - if ((m_preset_bundle != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) { - m_preset_bundle->load_config_string(m_value[1].c_str(), m_archive_filename.c_str()); - } + if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) + m_config->load_from_gcode_string(m_value[1].c_str()); else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) { const char *opt_key = m_value[0].c_str() + 7; if (print_config_def.options.find(opt_key) != print_config_def.options.end()) { @@ -458,7 +579,28 @@ void AMFParserContext::endElement(const char * /* name */) p = end + 1; } m_object->layer_height_profile_valid = true; - } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) { + } + else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "sla_support_points") == 0) { + // Parse object's layer height profile, a semicolon separated list of floats. + unsigned char coord_idx = 0; + Vec3f point(Vec3f::Zero()); + char *p = const_cast(m_value[1].c_str()); + for (;;) { + char *end = strchr(p, ';'); + if (end != nullptr) + *end = 0; + + point(coord_idx) = atof(p); + if (++coord_idx == 3) { + m_object->sla_support_points.push_back(point); + coord_idx = 0; + } + if (end == nullptr) + break; + p = end + 1; + } + } + else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) { if (strcmp(opt_key, "modifier") == 0) { // Is this volume a modifier volume? // "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag. @@ -489,7 +631,6 @@ void AMFParserContext::endElement(const char * /* name */) default: break; } - m_path.pop_back(); } @@ -503,16 +644,16 @@ void AMFParserContext::endDocument() for (const Instance &instance : object.second.instances) if (instance.deltax_set && instance.deltay_set) { ModelInstance *mi = m_model.objects[object.second.idx]->add_instance(); - mi->offset.x = instance.deltax; - mi->offset.y = instance.deltay; - mi->rotation = instance.rz_set ? instance.rz : 0.f; - mi->scaling_factor = instance.scale_set ? instance.scale : 1.f; + mi->set_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0)); + mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0)); + mi->set_scaling_factor(Vec3d(instance.scalex_set ? (double)instance.scalex : 1.0, instance.scaley_set ? (double)instance.scaley : 1.0, instance.scalez_set ? (double)instance.scalez : 1.0)); + mi->set_mirror(Vec3d(instance.mirrorx_set ? (double)instance.mirrorx : 1.0, instance.mirrory_set ? (double)instance.mirrory : 1.0, instance.mirrorz_set ? (double)instance.mirrorz : 1.0)); } } } // Load an AMF file into a provided model. -bool load_amf_file(const char *path, PresetBundle* bundle, Model *model) +bool load_amf_file(const char *path, DynamicPrintConfig *config, Model *model) { if ((path == nullptr) || (model == nullptr)) return false; @@ -529,7 +670,7 @@ bool load_amf_file(const char *path, PresetBundle* bundle, Model *model) return false; } - AMFParserContext ctx(parser, path, bundle, model); + AMFParserContext ctx(parser, config, model); XML_SetUserData(parser, (void*)&ctx); XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement); XML_SetCharacterDataHandler(parser, AMFParserContext::characters); @@ -564,7 +705,7 @@ bool load_amf_file(const char *path, PresetBundle* bundle, Model *model) return result; } -bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, const char* path, PresetBundle* bundle, Model* model, unsigned int& version) +bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig* config, Model* model, unsigned int& version) { if (stat.m_uncomp_size == 0) { @@ -580,7 +721,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi return false; } - AMFParserContext ctx(parser, path, bundle, model); + AMFParserContext ctx(parser, config, model); XML_SetUserData(parser, (void*)&ctx); XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement); XML_SetCharacterDataHandler(parser, AMFParserContext::characters); @@ -616,7 +757,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi } // Load an AMF archive into a provided model. -bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model) +bool load_amf_archive(const char *path, DynamicPrintConfig *config, Model *model) { if ((path == nullptr) || (model == nullptr)) return false; @@ -643,7 +784,7 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model) { if (boost::iends_with(stat.m_filename, ".amf")) { - if (!extract_model_from_archive(archive, stat, path, bundle, model, version)) + if (!extract_model_from_archive(archive, stat, config, model, version)) { mz_zip_reader_end(&archive); printf("Archive does not contain a valid model"); @@ -671,12 +812,12 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model) } // Load an AMF file into a provided model. -// If bundle is not a null pointer, updates it if the amf file/archive contains config data -bool load_amf(const char *path, PresetBundle* bundle, Model *model) +// If config is not a null pointer, updates it if the amf file/archive contains config data +bool load_amf(const char *path, DynamicPrintConfig *config, Model *model) { if (boost::iends_with(path, ".amf.xml")) // backward compatibility with older slic3r output - return load_amf_file(path, bundle, model); + return load_amf_file(path, config, model); else if (boost::iends_with(path, ".amf")) { boost::nowide::ifstream file(path, boost::nowide::ifstream::binary); @@ -687,15 +828,15 @@ bool load_amf(const char *path, PresetBundle* bundle, Model *model) file.read(const_cast(zip_mask.data()), 2); file.close(); - return (zip_mask == "PK") ? load_amf_archive(path, bundle, model) : load_amf_file(path, bundle, model); + return (zip_mask == "PK") ? load_amf_archive(path, config, model) : load_amf_file(path, config, model); } else return false; } -bool store_amf(const char *path, Model *model, Print* print, bool export_print_config) +bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) { - if ((path == nullptr) || (model == nullptr) || (print == nullptr)) + if ((path == nullptr) || (model == nullptr)) return false; // forces ".zip.amf" extension @@ -716,11 +857,13 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c stream << "Slic3r " << SLIC3R_VERSION << "\n"; stream << "" << VERSION_AMF << "\n"; - if (export_print_config) + if (config != nullptr) { - std::string config = "\n"; - GCode::append_full_config(*print, config); - stream << "" << xml_escape(config) << "\n"; + std::string str_config = "\n"; + for (const std::string &key : config->keys()) + if (key != "compatible_printers") + str_config += "; " + key + " = " + config->serialize(key) + "\n"; + stream << "" << xml_escape(str_config) << "\n"; } for (const auto &material : model->materials) { @@ -752,6 +895,19 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c stream << "\n \n"; } //FIXME Store the layer height ranges (ModelObject::layer_height_ranges) + + const std::vector& sla_support_points = object->sla_support_points; + if (!sla_support_points.empty()) { + // Store the SLA supports as a single semicolon separated list. + stream << " "; + for (size_t i = 0; i < sla_support_points.size(); ++i) { + if (i != 0) + stream << ";"; + stream << sla_support_points[i](0) << ";" << sla_support_points[i](1) << ";" << sla_support_points[i](2); + } + stream << "\n \n"; + } + stream << " \n"; stream << " \n"; std::vector vertices_offsets; @@ -759,16 +915,16 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c for (ModelVolume *volume : object->volumes) { vertices_offsets.push_back(num_vertices); if (! volume->mesh.repaired) - CONFESS("store_amf() requires repair()"); + throw std::runtime_error("store_amf() requires repair()"); auto &stl = volume->mesh.stl; if (stl.v_shared == nullptr) stl_generate_shared_vertices(&stl); for (size_t i = 0; i < stl.stats.shared_vertices; ++ i) { stream << " \n"; stream << " \n"; - stream << " " << stl.v_shared[i].x << "\n"; - stream << " " << stl.v_shared[i].y << "\n"; - stream << " " << stl.v_shared[i].z << "\n"; + stream << " " << stl.v_shared[i](0) << "\n"; + stream << " " << stl.v_shared[i](1) << "\n"; + stream << " " << stl.v_shared[i](2) << "\n"; stream << " \n"; stream << " \n"; } @@ -789,7 +945,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c if (volume->is_modifier()) stream << " 1\n"; stream << " " << ModelVolume::type_to_string(volume->type()) << "\n"; - for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) { + for (int i = 0; i < (int)volume->mesh.stl.stats.number_of_facets; ++i) { stream << " \n"; for (int j = 0; j < 3; ++j) stream << " " << volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset << "\n"; @@ -806,14 +962,31 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c " \n" " %lf\n" " %lf\n" + " %lf\n" + " %lf\n" + " %lf\n" " %lf\n" - " %lf\n" + " %lf\n" + " %lf\n" + " %lf\n" + " %lf\n" + " %lf\n" + " %lf\n" " \n", object_id, - instance->offset.x, - instance->offset.y, - instance->rotation, - instance->scaling_factor); + instance->get_offset(X), + instance->get_offset(Y), + instance->get_offset(Z), + instance->get_rotation(X), + instance->get_rotation(Y), + instance->get_rotation(Z), + instance->get_scaling_factor(X), + instance->get_scaling_factor(Y), + instance->get_scaling_factor(Z), + instance->get_mirror(X), + instance->get_mirror(Y), + instance->get_mirror(Z)); + //FIXME missing instance->scaling_factor instances.append(buf); } diff --git a/src/libslic3r/Format/AMF.hpp b/src/libslic3r/Format/AMF.hpp new file mode 100644 index 0000000000..e085ad22ea --- /dev/null +++ b/src/libslic3r/Format/AMF.hpp @@ -0,0 +1,18 @@ +#ifndef slic3r_Format_AMF_hpp_ +#define slic3r_Format_AMF_hpp_ + +namespace Slic3r { + +class Model; +class DynamicPrintConfig; + +// Load the content of an amf file into the given model and configuration. +extern bool load_amf(const char *path, DynamicPrintConfig *config, Model *model); + +// Save the given model and the config data into an amf file. +// The model could be modified during the export process if meshes are not repaired or have no shared vertices +extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config); + +}; // namespace Slic3r + +#endif /* slic3r_Format_AMF_hpp_ */ diff --git a/xs/src/libslic3r/Format/OBJ.cpp b/src/libslic3r/Format/OBJ.cpp similarity index 73% rename from xs/src/libslic3r/Format/OBJ.cpp rename to src/libslic3r/Format/OBJ.cpp index ea6b5604c4..ee5756083d 100644 --- a/xs/src/libslic3r/Format/OBJ.cpp +++ b/src/libslic3r/Format/OBJ.cpp @@ -57,14 +57,14 @@ bool load_obj(const char *path, Model *model, const char *object_name_in) continue; stl_facet &facet = stl.facet_start[i_face ++]; size_t num_normals = 0; - stl_normal normal = { 0.f }; + stl_normal normal(stl_normal::Zero()); for (unsigned int v = 0; v < 3; ++ v) { const ObjParser::ObjVertex &vertex = data.vertices[i++]; - memcpy(&facet.vertex[v].x, &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float)); + memcpy(facet.vertex[v].data(), &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float)); if (vertex.normalIdx != -1) { - normal.x += data.normals[vertex.normalIdx*3]; - normal.y += data.normals[vertex.normalIdx*3+1]; - normal.z += data.normals[vertex.normalIdx*3+2]; + normal(0) += data.normals[vertex.normalIdx*3]; + normal(1) += data.normals[vertex.normalIdx*3+1]; + normal(2) += data.normals[vertex.normalIdx*3+2]; ++ num_normals; } } @@ -74,33 +74,27 @@ bool load_obj(const char *path, Model *model, const char *object_name_in) facet2.vertex[0] = facet.vertex[0]; facet2.vertex[1] = facet.vertex[2]; const ObjParser::ObjVertex &vertex = data.vertices[i++]; - memcpy(&facet2.vertex[2].x, &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float)); + memcpy(facet2.vertex[2].data(), &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float)); if (vertex.normalIdx != -1) { - normal.x += data.normals[vertex.normalIdx*3]; - normal.y += data.normals[vertex.normalIdx*3+1]; - normal.z += data.normals[vertex.normalIdx*3+2]; + normal(0) += data.normals[vertex.normalIdx*3]; + normal(1) += data.normals[vertex.normalIdx*3+1]; + normal(2) += data.normals[vertex.normalIdx*3+2]; ++ num_normals; } if (num_normals == 4) { // Normalize an average normal of a quad. - float len = sqrt(facet.normal.x*facet.normal.x + facet.normal.y*facet.normal.y + facet.normal.z*facet.normal.z); + float len = facet.normal.norm(); if (len > EPSILON) { - normal.x /= len; - normal.y /= len; - normal.z /= len; + normal /= len; facet.normal = normal; facet2.normal = normal; } } } else if (num_normals == 3) { // Normalize an average normal of a triangle. - float len = sqrt(facet.normal.x*facet.normal.x + facet.normal.y*facet.normal.y + facet.normal.z*facet.normal.z); - if (len > EPSILON) { - normal.x /= len; - normal.y /= len; - normal.z /= len; - facet.normal = normal; - } + float len = facet.normal.norm(); + if (len > EPSILON) + facet.normal = normal / len; } } stl_get_size(&stl); diff --git a/xs/src/libslic3r/Format/OBJ.hpp b/src/libslic3r/Format/OBJ.hpp similarity index 100% rename from xs/src/libslic3r/Format/OBJ.hpp rename to src/libslic3r/Format/OBJ.hpp diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp new file mode 100644 index 0000000000..f79721be71 --- /dev/null +++ b/src/libslic3r/Format/PRUS.cpp @@ -0,0 +1,354 @@ +#include +#include + +#include +#include + +#include + +#include + +#include "../libslic3r.h" +#include "../Model.hpp" + +#include "PRUS.hpp" + +#if 0 +// Enable debugging and assert in this file. +#define DEBUG +#define _DEBUG +#undef NDEBUG +#endif + +#include + +namespace Slic3r +{ + +struct StlHeader +{ + char comment[80]; + uint32_t nTriangles; +}; + +static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct"); + +// Buffered line reader to a string buffer. +class LineReader +{ +public: + LineReader(std::vector &data) : m_buffer(data), m_pos(0), m_len((int)data.size()) {} + + const char* next_line() { + // Skip empty lines. + while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n')) + ++ m_pos; + if (m_pos == m_len) { + // End of file. + return nullptr; + } + // The buffer is nonempty and it does not start with end of lines. Find the first end of line. + int end = m_pos + 1; + while (end < m_len && m_buffer[end] != '\r' && m_buffer[end] != '\n') + ++ end; + char *ptr_out = m_buffer.data() + m_pos; + m_pos = end + 1; + m_buffer[end] = 0; + return ptr_out; + } + + int next_line_scanf(const char *format, ...) + { + const char *line = next_line(); + if (line == nullptr) + return -1; + int result; + va_list arglist; + va_start(arglist, format); + result = vsscanf(line, format, arglist); + va_end(arglist); + return result; + } + +private: + std::vector &m_buffer; + int m_pos; + int m_len; +}; + +static void extract_model_from_archive( + // name of the model file + const char *name, + // path to the archive + const char *path, + // content of scene.xml + const std::vector &scene_xml_data, + // loaded data of this STL + std::vector &data, + // Model, to which the newly loaded objects will be added + Model *model, + // To map multiple STLs into a single model object for multi-material prints. + std::map &group_to_model_object) +{ + // Find the model entry in the XML data. + char model_name_tag[1024]; + sprintf(model_name_tag, "", name); + const char *model_xml = strstr(scene_xml_data.data(), model_name_tag); + const char *zero_tag = ""; + const char *zero_xml = strstr(scene_xml_data.data(), zero_tag); + float trafo[3][4] = { 0 }; + Vec3d instance_rotation = Vec3d::Zero(); + Vec3d instance_scaling_factor = Vec3d::Ones(); + Vec3d instance_offset = Vec3d::Zero(); + bool trafo_set = false; + unsigned int group_id = (unsigned int)-1; + unsigned int extruder_id = (unsigned int)-1; + ModelObject *model_object = nullptr; + if (model_xml != nullptr) { + model_xml += strlen(model_name_tag); + const char *position_tag = ""; + const char *position_xml = strstr(model_xml, position_tag); + const char *rotation_tag = ""; + const char *rotation_xml = strstr(model_xml, rotation_tag); + const char *scale_tag = ""; + const char *scale_xml = strstr(model_xml, scale_tag); + float position[3], rotation[3], scale[3], zero[3]; + if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr && zero_xml != nullptr && + sscanf(position_xml+strlen(position_tag), + "[%f, %f, %f]", position, position+1, position+2) == 3 && + sscanf(rotation_xml+strlen(rotation_tag), + "[%f, %f, %f]", rotation, rotation+1, rotation+2) == 3 && + sscanf(scale_xml+strlen(scale_tag), + "[%f, %f, %f]", scale, scale+1, scale+2) == 3 && + sscanf(zero_xml+strlen(zero_tag), + "[%f, %f, %f]", zero, zero+1, zero+2) == 3) { + instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]); + instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]); + Eigen::Matrix3f mat_rot, mat_scale, mat_trafo; + mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) * + Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) * + Eigen::AngleAxisf(-rotation[0], Eigen::Vector3f::UnitX()); + mat_scale = Eigen::Scaling(scale[0], scale[1], scale[2]); + mat_trafo = mat_rot * mat_scale; + for (size_t r = 0; r < 3; ++ r) { + for (size_t c = 0; c < 3; ++ c) + trafo[r][c] += mat_trafo(r, c); + } + instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2])); + // CHECK_ME -> Is the following correct ? + trafo[2][3] = position[2] / (float)instance_scaling_factor(2); + trafo_set = true; + } + const char *group_tag = ""; + const char *group_xml = strstr(model_xml, group_tag); + const char *extruder_tag = ""; + const char *extruder_xml = strstr(model_xml, extruder_tag); + if (group_xml != nullptr) { + int group = atoi(group_xml + strlen(group_tag)); + if (group > 0) { + group_id = group; + auto it = group_to_model_object.find(group_id); + if (it != group_to_model_object.end()) + model_object = it->second; + } + } + if (extruder_xml != nullptr) { + int e = atoi(extruder_xml + strlen(extruder_tag)); + if (e > 0) + extruder_id = e; + } + } + if (! trafo_set) + throw std::runtime_error(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name); + + // Extract the STL. + StlHeader header; + TriangleMesh mesh; + bool mesh_valid = false; + bool stl_ascii = false; + if (data.size() > sizeof(StlHeader)) { + memcpy((char*)&header, data.data(), sizeof(StlHeader)); + if (strncmp(header.comment, "solid ", 6) == 0) + stl_ascii = true; + else { + // Header has been extracted. Now read the faces. + stl_file &stl = mesh.stl; + stl.error = 0; + stl.stats.type = inmemory; + stl.stats.number_of_facets = header.nTriangles; + stl.stats.original_num_facets = header.nTriangles; + stl_allocate(&stl); + if (header.nTriangles > 0 && data.size() == 50 * header.nTriangles + sizeof(StlHeader)) { + memcpy((char*)stl.facet_start, data.data() + sizeof(StlHeader), 50 * header.nTriangles); + if (sizeof(stl_facet) > SIZEOF_STL_FACET) { + // The stl.facet_start is not packed tightly. Unpack the array of stl_facets. + unsigned char *data = (unsigned char*)stl.facet_start; + for (size_t i = header.nTriangles - 1; i > 0; -- i) + memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET); + } + // All the faces have been read. + stl_get_size(&stl); + mesh.repair(); + // Transform the model. + stl_transform(&stl, &trafo[0][0]); + if (std::abs(stl.stats.min(2)) < EPSILON) + stl.stats.min(2) = 0.; + // Add a mesh to a model. + if (mesh.facets_count() > 0) + mesh_valid = true; + } + } + } else + stl_ascii = true; + + if (stl_ascii) { + // Try to parse ASCII STL. + char normal_buf[3][32]; + stl_facet facet; + std::vector facets; + LineReader line_reader(data); + std::string solid_name; + facet.extra[0] = facet.extra[1] = 0; + for (;;) { + const char *line = line_reader.next_line(); + if (line == nullptr) + // End of file. + break; + if (strncmp(line, "solid", 5) == 0) { + // Opening the "solid" block. + if (! solid_name.empty()) { + // Error, solid block is already open. + facets.clear(); + break; + } + solid_name = line + 5; + if (solid_name.empty()) + solid_name = "unknown"; + continue; + } + if (strncmp(line, "endsolid", 8) == 0) { + // Closing the "solid" block. + if (solid_name.empty()) { + // Error, no solid block is open. + facets.clear(); + break; + } + solid_name.clear(); + continue; + } + // Line has to start with the word solid. + int res_normal = sscanf(line, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]); + assert(res_normal == 3); + int res_outer_loop = line_reader.next_line_scanf(" outer loop"); + assert(res_outer_loop == 0); + int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2)); + assert(res_vertex1 == 3); + int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2)); + assert(res_vertex2 == 3); + int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2)); + assert(res_vertex3 == 3); + int res_endloop = line_reader.next_line_scanf(" endloop"); + assert(res_endloop == 0); + int res_endfacet = line_reader.next_line_scanf(" endfacet"); + if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) { + // perror("Something is syntactically very wrong with this ASCII STL!"); + facets.clear(); + break; + } + // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. + if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 || + sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 || + sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) { + // Normal was mangled. Maybe denormals or "not a number" were stored? + // Just reset the normal and silently ignore it. + memset(&facet.normal, 0, sizeof(facet.normal)); + } + facets.emplace_back(facet); + } + if (! facets.empty() && solid_name.empty()) { + stl_file &stl = mesh.stl; + stl.stats.type = inmemory; + stl.stats.number_of_facets = (uint32_t)facets.size(); + stl.stats.original_num_facets = (int)facets.size(); + stl_allocate(&stl); + memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50); + stl_get_size(&stl); + mesh.repair(); + // Transform the model. + stl_transform(&stl, &trafo[0][0]); + // Add a mesh to a model. + if (mesh.facets_count() > 0) + mesh_valid = true; + } + } + + if (! mesh_valid) + throw std::runtime_error(std::string("Archive ") + path + " does not contain a valid mesh for " + name); + + // Add this mesh to the model. + ModelVolume *volume = nullptr; + if (model_object == nullptr) { + // This is a first mesh of a group. Create a new object & volume. + model_object = model->add_object(name, path, std::move(mesh)); + volume = model_object->volumes.front(); + ModelInstance *instance = model_object->add_instance(); + instance->set_rotation(instance_rotation); + instance->set_scaling_factor(instance_scaling_factor); + instance->set_offset(instance_offset); + if (group_id != (size_t)-1) + group_to_model_object[group_id] = model_object; + } else { + // This is not the 1st mesh of a group. Add it to the ModelObject. + volume = model_object->add_volume(std::move(mesh)); + volume->name = name; + } + // Set the extruder to the volume. + if (extruder_id != (unsigned int)-1) { + char str_extruder[64]; + sprintf(str_extruder, "%ud", extruder_id); + volume->config.set_deserialize("extruder", str_extruder); + } +} + +// Load a PrusaControl project file into a provided model. +bool load_prus(const char *path, Model *model) +{ + mz_zip_archive archive; + mz_zip_zero_struct(&archive); + mz_bool res = mz_zip_reader_init_file(&archive, path, 0); + size_t n_models_initial = model->objects.size(); + try { + if (res == MZ_FALSE) + throw std::runtime_error(std::string("Unable to init zip reader for ") + path); + std::vector scene_xml_data; + // For grouping multiple STLs into a single ModelObject for multi-material prints. + std::map group_to_model_object; + mz_uint num_entries = mz_zip_reader_get_num_files(&archive); + for (mz_uint i = 0; i < num_entries; ++ i) { + mz_zip_archive_file_stat stat; + if (! mz_zip_reader_file_stat(&archive, i, &stat)) + continue; + std::vector buffer; + buffer.assign((size_t)stat.m_uncomp_size + 1, 0); + res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + if (res == MZ_FALSE) + std::runtime_error(std::string("Error while extracting a file from ") + path); + if (strcmp(stat.m_filename, "scene.xml") == 0) { + if (! scene_xml_data.empty()) + throw std::runtime_error(std::string("Multiple scene.xml were found in the archive.") + path); + scene_xml_data = std::move(buffer); + } else if (boost::iends_with(stat.m_filename, ".stl")) { + // May throw std::exception + extract_model_from_archive(stat.m_filename, path, scene_xml_data, buffer, model, group_to_model_object); + } + } + } catch (std::exception &ex) { + mz_zip_reader_end(&archive); + throw ex; + } + + mz_zip_reader_end(&archive); + return model->objects.size() > n_models_initial; +} + +}; // namespace Slic3r diff --git a/xs/src/libslic3r/Format/PRUS.hpp b/src/libslic3r/Format/PRUS.hpp similarity index 63% rename from xs/src/libslic3r/Format/PRUS.hpp rename to src/libslic3r/Format/PRUS.hpp index 8559a70d65..be5c5c61ac 100644 --- a/xs/src/libslic3r/Format/PRUS.hpp +++ b/src/libslic3r/Format/PRUS.hpp @@ -1,4 +1,3 @@ -#if defined(SLIC3R_PRUS) && ! defined(slic3r_Format_PRUS_hpp_) #define slic3r_Format_PRUS_hpp_ namespace Slic3r { @@ -10,5 +9,3 @@ class Model; extern bool load_prus(const char *path, Model *model); }; // namespace Slic3r - -#endif /* SLIC3R_PRUS && ! defined(slic3r_Format_PRUS_hpp_) */ diff --git a/xs/src/libslic3r/Format/STL.cpp b/src/libslic3r/Format/STL.cpp similarity index 100% rename from xs/src/libslic3r/Format/STL.cpp rename to src/libslic3r/Format/STL.cpp diff --git a/xs/src/libslic3r/Format/STL.hpp b/src/libslic3r/Format/STL.hpp similarity index 100% rename from xs/src/libslic3r/Format/STL.hpp rename to src/libslic3r/Format/STL.hpp diff --git a/xs/src/libslic3r/Format/objparser.cpp b/src/libslic3r/Format/objparser.cpp similarity index 100% rename from xs/src/libslic3r/Format/objparser.cpp rename to src/libslic3r/Format/objparser.cpp diff --git a/xs/src/libslic3r/Format/objparser.hpp b/src/libslic3r/Format/objparser.hpp similarity index 100% rename from xs/src/libslic3r/Format/objparser.hpp rename to src/libslic3r/Format/objparser.hpp diff --git a/xs/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp similarity index 83% rename from xs/src/libslic3r/GCode.cpp rename to src/libslic3r/GCode.cpp index 2254c21548..b9625523d3 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -49,11 +50,11 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point & // If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset). // Otherwise perform the path planning in the coordinate system of the active object. bool use_external = this->use_external_mp || this->use_external_mp_once; - Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin().x, gcodegen.origin().y) : Point(0, 0); + Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0); Polyline result = (use_external ? m_external_mp.get() : m_layer_mp.get())-> shortest_path(gcodegen.last_pos() + scaled_origin, point + scaled_origin); if (use_external) - result.translate(scaled_origin.negative()); + result.translate(- scaled_origin); return result; } @@ -64,8 +65,8 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen) // move to the nearest standby point if (!this->standby_points.empty()) { // get current position in print coordinates - Pointf3 writer_pos = gcodegen.writer().get_position(); - Point pos = Point::new_scale(writer_pos.x, writer_pos.y); + Vec3d writer_pos = gcodegen.writer().get_position(); + Point pos = Point::new_scale(writer_pos(0), writer_pos(1)); // find standby point Point standby_point; @@ -74,7 +75,7 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen) /* We don't call gcodegen.travel_to() because we don't need retraction (it was already triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates of the destination point must not be transformed by origin nor current extruder offset. */ - gcode += gcodegen.writer().travel_to_xy(Pointf::new_unscale(standby_point), + gcode += gcodegen.writer().travel_to_xy(unscale(standby_point), "move to standby position"); } @@ -160,7 +161,7 @@ Wipe::wipe(GCode &gcodegen, bool toolchange) static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const WipeTower::xy &wipe_tower_pt) { - return Point(scale_(wipe_tower_pt.x - gcodegen.origin().x), scale_(wipe_tower_pt.y - gcodegen.origin().y)); + return Point(scale_(wipe_tower_pt.x - gcodegen.origin()(0)), scale_(wipe_tower_pt.y - gcodegen.origin()(1))); } std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const @@ -207,7 +208,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T check_add_eol(gcode); } // A phony move to the end position at the wipe tower. - gcodegen.writer().travel_to_xy(Pointf(end_pos.x, end_pos.y)); + gcodegen.writer().travel_to_xy(Vec2d(end_pos.x, end_pos.y)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); // Prepare a future wipe. @@ -292,7 +293,7 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) gcodegen.writer().toolchange(current_extruder_id); gcodegen.placeholder_parser().set("current_extruder", current_extruder_id); // A phony move to the end position at the wipe tower. - gcodegen.writer().travel_to_xy(Pointf(m_priming.end_pos.x, m_priming.end_pos.y)); + gcodegen.writer().travel_to_xy(Vec2d(m_priming.end_pos.x, m_priming.end_pos.y)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos)); // Prepare a future wipe. gcodegen.m_wipe.path.points.clear(); @@ -324,7 +325,7 @@ std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, std::string WipeTowerIntegration::finalize(GCode &gcodegen) { std::string gcode; - if (std::abs(gcodegen.writer().get_position().z - m_final_purge.print_z) > EPSILON) + if (std::abs(gcodegen.writer().get_position()(2) - m_final_purge.print_z) > EPSILON) gcode += gcodegen.change_layer(m_final_purge.print_z); gcode += append_tcr(gcodegen, m_final_purge, -1); return gcode; @@ -337,15 +338,15 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen) std::vector GCode::collect_layers_to_print(const PrintObject &object) { std::vector layers_to_print; - layers_to_print.reserve(object.layers.size() + object.support_layers.size()); + layers_to_print.reserve(object.layers().size() + object.support_layers().size()); // Pair the object layers with the support layers by z. size_t idx_object_layer = 0; size_t idx_support_layer = 0; - while (idx_object_layer < object.layers.size() || idx_support_layer < object.support_layers.size()) { + while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()) { LayerToPrint layer_to_print; - layer_to_print.object_layer = (idx_object_layer < object.layers.size()) ? object.layers[idx_object_layer ++] : nullptr; - layer_to_print.support_layer = (idx_support_layer < object.support_layers.size()) ? object.support_layers[idx_support_layer ++] : nullptr; + layer_to_print.object_layer = (idx_object_layer < object.layers().size()) ? object.layers()[idx_object_layer ++] : nullptr; + layer_to_print.support_layer = (idx_support_layer < object.support_layers().size()) ? object.support_layers()[idx_support_layer ++] : nullptr; if (layer_to_print.object_layer && layer_to_print.support_layer) { if (layer_to_print.object_layer->print_z < layer_to_print.support_layer->print_z - EPSILON) { layer_to_print.support_layer = nullptr; @@ -372,11 +373,10 @@ std::vector>> GCode::collec size_t layer_idx; }; - PrintObjectPtrs printable_objects = print.get_printable_objects(); - std::vector> per_object(printable_objects.size(), std::vector()); + std::vector> per_object(print.objects().size(), std::vector()); std::vector ordering; - for (size_t i = 0; i < printable_objects.size(); ++i) { - per_object[i] = collect_layers_to_print(*printable_objects[i]); + for (size_t i = 0; i < print.objects().size(); ++i) { + per_object[i] = collect_layers_to_print(*print.objects()[i]); OrderingItem ordering_item; ordering_item.object_idx = i; ordering.reserve(ordering.size() + per_object[i].size()); @@ -401,7 +401,7 @@ std::vector>> GCode::collec std::pair> merged; // Assign an average print_z to the set of layers with nearly equal print_z. merged.first = 0.5 * (ordering[i].print_z + ordering[j-1].print_z); - merged.second.assign(printable_objects.size(), LayerToPrint()); + merged.second.assign(print.objects().size(), LayerToPrint()); for (; i < j; ++i) { const OrderingItem &oi = ordering[i]; assert(merged.second[oi.object_idx].layer() == nullptr); @@ -417,6 +417,12 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ { PROFILE_CLEAR(); + // Does the file exist? If so, we hope that it is still valid. + if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path))) + return; + + print->set_started(psGCodeExport); + BOOST_LOG_TRIVIAL(info) << "Exporting G-code..."; // Remove the old g-code if it exists. @@ -429,27 +435,37 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ if (file == nullptr) throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); - this->m_placeholder_parser_failed_templates.clear(); - this->_do_export(*print, file, preview_data); - fflush(file); - if (ferror(file)) { + try { + m_placeholder_parser_failed_templates.clear(); + this->_do_export(*print, file, preview_data); + fflush(file); + if (ferror(file)) { + fclose(file); + boost::nowide::remove(path_tmp.c_str()); + throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); + } + } catch (std::exception & /* ex */) { + // Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown. + // Close and remove the file. fclose(file); boost::nowide::remove(path_tmp.c_str()); - throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); + throw; } fclose(file); - if (print->config.remaining_times.value) - { + if (print->config().remaining_times.value) { + BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode"; m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); - if (m_silent_time_estimator_enabled) + if (m_silent_time_estimator_enabled) { + BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode"; m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + } } - if (! this->m_placeholder_parser_failed_templates.empty()) { + if (! m_placeholder_parser_failed_templates.empty()) { // G-code export proceeded, but some of the PlaceholderParser substitutions failed. std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n"; - for (const std::string &name : this->m_placeholder_parser_failed_templates) + for (const std::string &name : m_placeholder_parser_failed_templates) msg += std::string("\t") + name + "\n"; msg += "\nPlease inspect the file "; msg += path_tmp + " for error messages enclosed between\n"; @@ -459,12 +475,13 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } - if (boost::nowide::rename(path_tmp.c_str(), path) != 0) + if (rename_file(path_tmp, path) != 0) throw std::runtime_error( std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "Is " + path_tmp + " locked?" + '\n'); BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished"; + print->set_done(psGCodeExport); // Write the profiler measurements to file PROFILE_UPDATE(); @@ -477,66 +494,66 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // resets time estimators m_normal_time_estimator.reset(); - m_normal_time_estimator.set_dialect(print.config.gcode_flavor); - m_silent_time_estimator_enabled = (print.config.gcode_flavor == gcfMarlin) && print.config.silent_mode; + m_normal_time_estimator.set_dialect(print.config().gcode_flavor); + m_silent_time_estimator_enabled = (print.config().gcode_flavor == gcfMarlin) && print.config().silent_mode; // Until we have a UI support for the other firmwares than the Marlin, use the hardcoded default values // and let the user to enter the G-code limits into the start G-code. // If the following block is enabled for other firmwares than the Marlin, then the function // this->print_machine_envelope(file, print); // shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor. - if (print.config.gcode_flavor.value == gcfMarlin) { - m_normal_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[0]); - m_normal_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[0]); - m_normal_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[0]); - m_normal_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[0]); + if (print.config().gcode_flavor.value == gcfMarlin) { + m_normal_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[0]); + m_normal_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[0]); + m_normal_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[0]); + m_normal_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[0]); if (m_silent_time_estimator_enabled) { m_silent_time_estimator.reset(); - m_silent_time_estimator.set_dialect(print.config.gcode_flavor); - m_silent_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[1]); - m_silent_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[1]); - m_silent_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[1]); - m_silent_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[1]); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[1]); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[1]); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[1]); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[1]); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[1]); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[1]); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[1]); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[1]); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[1]); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[1]); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[1]); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[1]); - if (print.config.single_extruder_multi_material) { + m_silent_time_estimator.set_dialect(print.config().gcode_flavor); + m_silent_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[1]); + m_silent_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[1]); + m_silent_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[1]); + m_silent_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[1]); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[1]); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[1]); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[1]); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[1]); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[1]); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[1]); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[1]); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[1]); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[1]); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[1]); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[1]); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[1]); + if (print.config().single_extruder_multi_material) { // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they // are considered to be active for the single extruder multi-material printers only. - m_silent_time_estimator.set_filament_load_times(print.config.filament_load_time.values); - m_silent_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values); + m_silent_time_estimator.set_filament_load_times(print.config().filament_load_time.values); + m_silent_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values); } } } // Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful. - if (print.config.single_extruder_multi_material) { + if (print.config().single_extruder_multi_material) { // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they // are considered to be active for the single extruder multi-material printers only. - m_normal_time_estimator.set_filament_load_times(print.config.filament_load_time.values); - m_normal_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values); + m_normal_time_estimator.set_filament_load_times(print.config().filament_load_time.values); + m_normal_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values); } // resets analyzer @@ -551,15 +568,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. m_layer_count = 0; - PrintObjectPtrs printable_objects = print.get_printable_objects(); - if (print.config.complete_objects.value) { + if (print.config().complete_objects.value) { // Add each of the object's layers separately. - for (auto object : printable_objects) { + for (auto object : print.objects()) { std::vector zs; - zs.reserve(object->layers.size() + object->support_layers.size()); - for (auto layer : object->layers) + zs.reserve(object->layers().size() + object->support_layers().size()); + for (auto layer : object->layers()) zs.push_back(layer->print_z); - for (auto layer : object->support_layers) + for (auto layer : object->support_layers()) zs.push_back(layer->print_z); std::sort(zs.begin(), zs.end()); m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); @@ -567,47 +583,52 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) } else { // Print all objects with the same print_z together. std::vector zs; - for (auto object : printable_objects) { - zs.reserve(zs.size() + object->layers.size() + object->support_layers.size()); - for (auto layer : object->layers) + for (auto object : print.objects()) { + zs.reserve(zs.size() + object->layers().size() + object->support_layers().size()); + for (auto layer : object->layers()) zs.push_back(layer->print_z); - for (auto layer : object->support_layers) + for (auto layer : object->support_layers()) zs.push_back(layer->print_z); } std::sort(zs.begin(), zs.end()); m_layer_count = (unsigned int)(std::unique(zs.begin(), zs.end()) - zs.begin()); } + print.throw_if_canceled(); m_enable_cooling_markers = true; - this->apply_print_config(print.config); + this->apply_print_config(print.config()); this->set_extruders(print.extruders()); + // Initialize colorprint. + m_colorprint_heights = cast(print.config().colorprint_heights.values); + // Initialize autospeed. { // get the minimum cross-section used in the print std::vector mm3_per_mm; - for (auto object : printable_objects) { - for (size_t region_id = 0; region_id < print.regions.size(); ++region_id) { - auto region = print.regions[region_id]; - for (auto layer : object->layers) { - auto layerm = layer->regions[region_id]; - if (region->config.get_abs_value("perimeter_speed" ) == 0 || - region->config.get_abs_value("small_perimeter_speed" ) == 0 || - region->config.get_abs_value("external_perimeter_speed" ) == 0 || - region->config.get_abs_value("bridge_speed" ) == 0) + for (auto object : print.objects()) { + for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { + const PrintRegion* region = print.regions()[region_id]; + for (auto layer : object->layers()) { + const LayerRegion* layerm = layer->regions()[region_id]; + if (region->config().get_abs_value("perimeter_speed" ) == 0 || + region->config().get_abs_value("small_perimeter_speed" ) == 0 || + region->config().get_abs_value("external_perimeter_speed" ) == 0 || + region->config().get_abs_value("bridge_speed" ) == 0) mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm()); - if (region->config.get_abs_value("infill_speed" ) == 0 || - region->config.get_abs_value("solid_infill_speed" ) == 0 || - region->config.get_abs_value("top_solid_infill_speed" ) == 0 || - region->config.get_abs_value("bridge_speed" ) == 0) + if (region->config().get_abs_value("infill_speed" ) == 0 || + region->config().get_abs_value("solid_infill_speed" ) == 0 || + region->config().get_abs_value("top_solid_infill_speed" ) == 0 || + region->config().get_abs_value("bridge_speed" ) == 0) mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm()); } } - if (object->config.get_abs_value("support_material_speed" ) == 0 || - object->config.get_abs_value("support_material_interface_speed" ) == 0) - for (auto layer : object->support_layers) + if (object->config().get_abs_value("support_material_speed" ) == 0 || + object->config().get_abs_value("support_material_interface_speed" ) == 0) + for (auto layer : object->support_layers()) mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm()); } + print.throw_if_canceled(); // filter out 0-width segments mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end()); if (! mm3_per_mm.empty()) { @@ -616,19 +637,20 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // volumetric speed as the volumetric speed produced by printing the // smallest cross-section at the maximum speed: any larger cross-section // will need slower feedrates. - m_volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config.max_print_speed.value; + m_volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config().max_print_speed.value; // limit such volumetric speed with max_volumetric_speed if set - if (print.config.max_volumetric_speed.value > 0) - m_volumetric_speed = std::min(m_volumetric_speed, print.config.max_volumetric_speed.value); + if (print.config().max_volumetric_speed.value > 0) + m_volumetric_speed = std::min(m_volumetric_speed, print.config().max_volumetric_speed.value); } } + print.throw_if_canceled(); m_cooling_buffer = make_unique(*this); - if (print.config.spiral_vase.value) - m_spiral_vase = make_unique(print.config); - if (print.config.max_volumetric_extrusion_rate_slope_positive.value > 0 || - print.config.max_volumetric_extrusion_rate_slope_negative.value > 0) - m_pressure_equalizer = make_unique(&print.config); + if (print.config().spiral_vase.value) + m_spiral_vase = make_unique(print.config()); + if (print.config().max_volumetric_extrusion_rate_slope_positive.value > 0 || + print.config().max_volumetric_extrusion_rate_slope_negative.value > 0) + m_pressure_equalizer = make_unique(&print.config()); m_enable_extrusion_role_markers = (bool)m_pressure_equalizer; // Write information on the generator. @@ -636,7 +658,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // Write notes (content of the Print Settings tab -> Notes) { std::list lines; - boost::split(lines, print.config.notes.value, boost::is_any_of("\n"), boost::token_compress_off); + boost::split(lines, print.config().notes.value, boost::is_any_of("\n"), boost::token_compress_off); for (auto line : lines) { // Remove the trailing '\r' from the '\r\n' sequence. if (! line.empty() && line.back() == '\r') @@ -646,12 +668,13 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) if (! lines.empty()) _write(file, "\n"); } + print.throw_if_canceled(); + // Write some terse information on the slicing parameters. - const PrintObject *first_object = printable_objects.front(); - const double layer_height = first_object->config.layer_height.value; - const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height); - for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { - auto region = print.regions[region_id]; + const PrintObject *first_object = print.objects().front(); + const double layer_height = first_object->config().layer_height.value; + const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height); + for (const PrintRegion* region : print.regions()) { _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); _write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width); _write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width); @@ -659,13 +682,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width); if (print.has_support_material()) _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width); - if (print.config.first_layer_extrusion_width.value > 0) + if (print.config().first_layer_extrusion_width.value > 0) _write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width); _write_format(file, "\n"); } + print.throw_if_canceled(); // adds tags for time estimators - if (print.config.remaining_times.value) + if (print.config().remaining_times.value) { _writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag); if (m_silent_time_estimator_enabled) @@ -673,7 +697,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) } // Prepare the helper object for replacing placeholders in custom G-code and output filename. - m_placeholder_parser = print.placeholder_parser; + m_placeholder_parser = print.placeholder_parser(); m_placeholder_parser.update_timestamp(); // Get optimal tool ordering to minimize tool switches of a multi-exruder print. @@ -683,21 +707,21 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) unsigned int final_extruder_id = (unsigned int)-1; size_t initial_print_object_id = 0; bool has_wipe_tower = false; - if (print.config.complete_objects.value) { + if (print.config().complete_objects.value) { // Find the 1st printing object, find its tool ordering and the initial extruder ID. - for (; initial_print_object_id < printable_objects.size(); ++initial_print_object_id) { - tool_ordering = ToolOrdering(*printable_objects[initial_print_object_id], initial_extruder_id); + for (; initial_print_object_id < print.objects().size(); ++initial_print_object_id) { + tool_ordering = ToolOrdering(*print.objects()[initial_print_object_id], initial_extruder_id); if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1) break; } } else { // Find tool ordering for all the objects at once, and the initial extruder ID. // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it. - tool_ordering = print.m_tool_ordering.empty() ? + tool_ordering = print.wipe_tower_data().tool_ordering.empty() ? ToolOrdering(print, initial_extruder_id) : - print.m_tool_ordering; + print.wipe_tower_data().tool_ordering; has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); - initial_extruder_id = (has_wipe_tower && ! print.config.single_extruder_multi_material_priming) ? + initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ? // The priming towers will be skipped. tool_ordering.all_extruders().back() : // Don't skip the priming towers. @@ -711,6 +735,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) final_extruder_id = tool_ordering.last_extruder(); assert(final_extruder_id != (unsigned int)-1); } + print.throw_if_canceled(); m_cooling_buffer->set_current_extruder(initial_extruder_id); @@ -718,7 +743,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) this->print_machine_envelope(file, print); // Disable fan. - if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id)) + if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id)) _write(file, m_writer.set_fan(0, true)); // Let the start-up script prime the 1st printing tool. @@ -729,8 +754,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) m_placeholder_parser.set("current_object_idx", 0); // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); - m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config.single_extruder_multi_material_priming); - std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id); + m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); + std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); // Set extruder(s) temperature before and after start G-code. @@ -747,51 +772,53 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // Write the custom start G-code _writeln(file, start_gcode); // Process filament-specific gcode in extruder order. - if (print.config.single_extruder_multi_material) { + if (print.config().single_extruder_multi_material) { if (has_wipe_tower) { // Wipe tower will control the extruder switching, it will call the start_filament_gcode. } else { // Only initialize the initial extruder. - _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id)); + _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id)); } } else { - for (const std::string &start_gcode : print.config.start_filament_gcode.values) - _writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front()))); + for (const std::string &start_gcode : print.config().start_filament_gcode.values) + _writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front()))); } this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); - + print.throw_if_canceled(); + // Set other general things. _write(file, this->preamble()); // Initialize a motion planner for object-to-object travel moves. - if (print.config.avoid_crossing_perimeters.value) { + if (print.config().avoid_crossing_perimeters.value) { // Collect outer contours of all objects over all layers. // Discard objects only containing thin walls (offset would fail on an empty polygon). Polygons islands; - for (const PrintObject *object : printable_objects) - for (const Layer *layer : object->layers) + for (const PrintObject *object : print.objects()) + for (const Layer *layer : object->layers()) for (const ExPolygon &expoly : layer->slices.expolygons) - for (const Point © : object->_shifted_copies) { + for (const Point © : object->copies()) { islands.emplace_back(expoly.contour); - islands.back().translate(copy); + islands.back().translate(- copy); } //FIXME Mege the islands in parallel. m_avoid_crossing_perimeters.init_external_mp(union_ex(islands)); + print.throw_if_canceled(); } // Calculate wiping points if needed - if (print.config.ooze_prevention.value && ! print.config.single_extruder_multi_material) { + if (print.config().ooze_prevention.value && ! print.config().single_extruder_multi_material) { Points skirt_points; - for (const ExtrusionEntity *ee : print.skirt.entities) + for (const ExtrusionEntity *ee : print.skirt().entities) for (const ExtrusionPath &path : dynamic_cast(ee)->paths) append(skirt_points, path.polyline.points); if (! skirt_points.empty()) { Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points); Polygons skirts; for (unsigned int extruder_id : print.extruders()) { - const Pointf &extruder_offset = print.config.extruder_offset.get_at(extruder_id); + const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id); Polygon s(outer_skirt); - s.translate(-scale_(extruder_offset.x), -scale_(extruder_offset.y)); + s.translate(Point::new_scale(- extruder_offset(0), - extruder_offset(1))); skirts.emplace_back(std::move(s)); } m_ooze_prevention.enable = true; @@ -807,26 +834,27 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) ); #endif } + print.throw_if_canceled(); } - if (! (has_wipe_tower && print.config.single_extruder_multi_material_priming)) { + if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) { // Set initial extruder only after custom start G-code. // Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed. _write(file, this->set_extruder(initial_extruder_id)); } // Do all objects for each layer. - if (print.config.complete_objects.value) { + if (print.config().complete_objects.value) { // Print objects from the smallest to the tallest to avoid collisions // when moving onto next object starting point. - std::vector objects(printable_objects); - std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size.z < po2->size.z; }); + std::vector objects(print.objects()); + std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size(2) < po2->size(2); }); size_t finished_objects = 0; for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) { const PrintObject &object = *objects[object_id]; - for (const Point © : object._shifted_copies) { + for (const Point © : object.copies()) { // Get optimal tool ordering to minimize tool switches of a multi-exruder print. - if (object_id != initial_print_object_id || © != object._shifted_copies.data()) { + if (object_id != initial_print_object_id || © != object.copies().data()) { // Don't initialize for the first object and first copy. tool_ordering = ToolOrdering(object, final_extruder_id); unsigned int new_extruder_id = tool_ordering.first_extruder(); @@ -837,7 +865,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) final_extruder_id = tool_ordering.last_extruder(); assert(final_extruder_id != (unsigned int)-1); } - this->set_origin(unscale(copy.x), unscale(copy.y)); + print.throw_if_canceled(); + this->set_origin(unscale(copy)); if (finished_objects > 0) { // Move to the origin position for the copy we're going to print. // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. @@ -852,7 +881,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // another one, set first layer temperatures. This happens before the Z move // is triggered, so machine has more time to reach such temperatures. m_placeholder_parser.set("current_object_idx", int(finished_objects)); - std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config.between_objects_gcode.value, initial_extruder_id); + std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id); // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); @@ -866,7 +895,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) for (const LayerToPrint <p : layers_to_print) { std::vector lrs; lrs.emplace_back(std::move(ltp)); - this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - object._shifted_copies.data()); + this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - object.copies().data()); + print.throw_if_canceled(); } if (m_pressure_equalizer) _write(file, m_pressure_equalizer->process("", true)); @@ -880,23 +910,22 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // Order objects using a nearest neighbor search. std::vector object_indices; Points object_reference_points; - PrintObjectPtrs printable_objects = print.get_printable_objects(); - for (PrintObject *object : printable_objects) - object_reference_points.push_back(object->_shifted_copies.front()); + for (PrintObject *object : print.objects()) + object_reference_points.push_back(object->copies().front()); Slic3r::Geometry::chained_path(object_reference_points, object_indices); // Sort layers by Z. // All extrusion moves with the same top layer height are extruded uninterrupted. std::vector>> layers_to_print = collect_layers_to_print(print); // Prusa Multi-Material wipe tower. if (has_wipe_tower && ! layers_to_print.empty()) { - m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get())); + m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get())); _write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height")); - if (print.config.single_extruder_multi_material_priming) { + if (print.config().single_extruder_multi_material_priming) { _write(file, m_wipe_tower->prime(*this)); // Verify, whether the print overaps the priming extrusions. BoundingBoxf bbox_print(get_print_extrusions_extents(print)); coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON; - for (const PrintObject *print_object : printable_objects) + for (const PrintObject *print_object : print.objects()) bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz)); bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz)); BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print)); @@ -915,6 +944,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write(file, "M1 S10\n"); } } + print.throw_if_canceled(); } // Extrude the layers. for (auto &layer : layers_to_print) { @@ -922,6 +952,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) if (m_wipe_tower && layer_tools.has_wipe_tower) m_wipe_tower->next_layer(); this->process_layer(file, print, layer.second, layer_tools, size_t(-1)); + print.throw_if_canceled(); } if (m_pressure_equalizer) _write(file, m_pressure_equalizer->process("", true)); @@ -946,18 +977,19 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) { DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z - m_config.z_offset.value)); - if (print.config.single_extruder_multi_material) { + config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value)); + if (print.config().single_extruder_multi_material) { // Process the end_filament_gcode for the active filament only. - _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id(), &config)); + _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id(), &config)); } else { - for (const std::string &end_gcode : print.config.end_filament_gcode.values) - _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()), &config)); + for (const std::string &end_gcode : print.config().end_filament_gcode.values) + _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front()), &config)); } - _writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id(), &config)); + _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config)); } _write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% _write(file, m_writer.postamble()); + print.throw_if_canceled(); // calculates estimated printing time m_normal_time_estimator.calculate_time(false); @@ -965,37 +997,30 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) m_silent_time_estimator.calculate_time(false); // Get filament stats. - print.filament_stats.clear(); - print.total_used_filament = 0.; - print.total_extruded_volume = 0.; - print.total_weight = 0.; - print.total_cost = 0.; - print.total_wipe_tower_cost = 0.; - print.total_wipe_tower_filament = 0.; - print.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms(); - print.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A"; + print.m_print_statistics.clear(); + print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms(); + print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A"; for (const Extruder &extruder : m_writer.extruders()) { - double used_filament = extruder.used_filament() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] : 0.f); - double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter + double used_filament = extruder.used_filament() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] : 0.f); + double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter double filament_weight = extruded_volume * extruder.filament_density() * 0.001; double filament_cost = filament_weight * extruder.filament_cost() * 0.001; - - print.filament_stats.insert(std::pair(extruder.id(), (float)used_filament)); + print.m_print_statistics.filament_stats.insert(std::pair(extruder.id(), (float)used_filament)); _write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001); if (filament_weight > 0.) { - print.total_weight = print.total_weight + filament_weight; + print.m_print_statistics.total_weight = print.m_print_statistics.total_weight + filament_weight; _write_format(file, "; filament used = %.1lf\n", filament_weight); if (filament_cost > 0.) { - print.total_cost = print.total_cost + filament_cost; + print.m_print_statistics.total_cost = print.m_print_statistics.total_cost + filament_cost; _write_format(file, "; filament cost = %.1lf\n", filament_cost); } } - print.total_used_filament += used_filament; - print.total_extruded_volume += extruded_volume; - print.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; - print.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; + print.m_print_statistics.total_used_filament += used_filament; + print.m_print_statistics.total_extruded_volume += extruded_volume; + print.m_print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; + print.m_print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; } - _write_format(file, "; total filament cost = %.1lf\n", print.total_cost); + _write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost); _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str()); if (m_silent_time_estimator_enabled) _write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str()); @@ -1008,10 +1033,13 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) if (!full_config.empty()) _write(file, full_config); } + print.throw_if_canceled(); - // starts analizer calculations - if (preview_data != nullptr) + // starts analyzer calculations + if (preview_data != nullptr) { + BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data"; m_analyzer.calc_gcode_preview_data(*preview_data); + } } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) @@ -1020,7 +1048,7 @@ std::string GCode::placeholder_parser_process(const std::string &name, const std return m_placeholder_parser.process(templ, current_extruder_id, config_override); } catch (std::runtime_error &err) { // Collect the names of failed template substitutions for error reporting. - this->m_placeholder_parser_failed_templates.insert(name); + m_placeholder_parser_failed_templates.insert(name); // Insert the macro error message into the G-code. return std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" + @@ -1087,29 +1115,29 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc // Do not process this piece of G-code by the time estimator, it already knows the values through another sources. void GCode::print_machine_envelope(FILE *file, Print &print) { - if (print.config.gcode_flavor.value == gcfMarlin) { + if (print.config().gcode_flavor.value == gcfMarlin) { fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n", - int(print.config.machine_max_acceleration_x.values.front() + 0.5), - int(print.config.machine_max_acceleration_y.values.front() + 0.5), - int(print.config.machine_max_acceleration_z.values.front() + 0.5), - int(print.config.machine_max_acceleration_e.values.front() + 0.5)); + int(print.config().machine_max_acceleration_x.values.front() + 0.5), + int(print.config().machine_max_acceleration_y.values.front() + 0.5), + int(print.config().machine_max_acceleration_z.values.front() + 0.5), + int(print.config().machine_max_acceleration_e.values.front() + 0.5)); fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n", - int(print.config.machine_max_feedrate_x.values.front() + 0.5), - int(print.config.machine_max_feedrate_y.values.front() + 0.5), - int(print.config.machine_max_feedrate_z.values.front() + 0.5), - int(print.config.machine_max_feedrate_e.values.front() + 0.5)); + int(print.config().machine_max_feedrate_x.values.front() + 0.5), + int(print.config().machine_max_feedrate_y.values.front() + 0.5), + int(print.config().machine_max_feedrate_z.values.front() + 0.5), + int(print.config().machine_max_feedrate_e.values.front() + 0.5)); fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n", - int(print.config.machine_max_acceleration_extruding.values.front() + 0.5), - int(print.config.machine_max_acceleration_retracting.values.front() + 0.5), - int(print.config.machine_max_acceleration_extruding.values.front() + 0.5)); + int(print.config().machine_max_acceleration_extruding.values.front() + 0.5), + int(print.config().machine_max_acceleration_retracting.values.front() + 0.5), + int(print.config().machine_max_acceleration_extruding.values.front() + 0.5)); fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n", - print.config.machine_max_jerk_x.values.front(), - print.config.machine_max_jerk_y.values.front(), - print.config.machine_max_jerk_z.values.front(), - print.config.machine_max_jerk_e.values.front()); + print.config().machine_max_jerk_x.values.front(), + print.config().machine_max_jerk_y.values.front(), + print.config().machine_max_jerk_z.values.front(), + print.config().machine_max_jerk_e.values.front()); fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n", - int(print.config.machine_min_extruding_rate.values.front() + 0.5), - int(print.config.machine_min_travel_rate.values.front() + 0.5)); + int(print.config().machine_min_extruding_rate.values.front() + 0.5), + int(print.config().machine_min_travel_rate.values.front() + 0.5)); } } @@ -1120,7 +1148,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print) void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { // Initial bed temperature based on the first extruder. - int temp = print.config.first_layer_bed_temperature.get_at(first_printing_extruder_id); + int temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id); // Is the bed temperature set by the provided custom G-code? int temp_by_gcode = -1; bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, temp_by_gcode); @@ -1143,23 +1171,23 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c int temp_by_gcode = -1; if (custom_gcode_sets_temperature(gcode, 104, 109, temp_by_gcode)) { // Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code. - int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id); + int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id); if (temp_by_gcode >= 0 && temp_by_gcode < 1000) temp = temp_by_gcode; m_writer.set_temperature(temp_by_gcode, wait, first_printing_extruder_id); } else { // Custom G-code does not set the extruder temperature. Do it now. - if (print.config.single_extruder_multi_material.value) { + if (print.config().single_extruder_multi_material.value) { // Set temperature of the first printing extruder only. - int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id); + int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id); if (temp > 0) _write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id)); } else { // Set temperatures of all the printing extruders. for (unsigned int tool_id : print.extruders()) { - int temp = print.config.first_layer_temperature.get_at(tool_id); - if (print.config.ooze_prevention.value) - temp += print.config.standby_temperature_delta.value; + int temp = print.config().first_layer_temperature.get_at(tool_id); + if (print.config().ooze_prevention.value) + temp += print.config().standby_temperature_delta.value; if (temp > 0) _write(file, m_writer.set_temperature(temp, wait, tool_id)); } @@ -1209,7 +1237,7 @@ void GCode::process_layer( const size_t single_object_idx) { assert(! layers.empty()); - assert(! layer_tools.extruders.empty()); +// assert(! layer_tools.extruders.empty()); // Either printing all copies of all objects, or just a single copy of a single object. assert(single_object_idx == size_t(-1) || layers.size() == 1); @@ -1232,15 +1260,15 @@ void GCode::process_layer( unsigned int first_extruder_id = layer_tools.extruders.front(); // Initialize config with the 1st object to be printed at this layer. - m_config.apply(layer.object()->config, true); + m_config.apply(layer.object()->config(), true); // Check whether it is possible to apply the spiral vase logic for this layer. // Just a reminder: A spiral vase mode is allowed for a single object, single material print only. if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) { - bool enable = (layer.id() > 0 || print.config.brim_width.value == 0.) && (layer.id() >= print.config.skirt_height.value && ! print.has_infinite_skirt()); + bool enable = (layer.id() > 0 || print.config().brim_width.value == 0.) && (layer.id() >= print.config().skirt_height.value && ! print.has_infinite_skirt()); if (enable) { - for (const LayerRegion *layer_region : layer.regions) - if (layer_region->region()->config.bottom_solid_layers.value > layer.id() || + for (const LayerRegion *layer_region : layer.regions()) + if (layer_region->region()->config().bottom_solid_layers.value > layer.id() || layer_region->perimeters.items_count() > 1 || layer_region->fills.items_count() > 0) { enable = false; @@ -1255,22 +1283,22 @@ void GCode::process_layer( std::string gcode; // Set new layer - this will change Z and force a retraction if retract_layer_change is enabled. - if (! print.config.before_layer_gcode.value.empty()) { + if (! print.config().before_layer_gcode.value.empty()) { DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); gcode += this->placeholder_parser_process("before_layer_gcode", - print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config) + print.config().before_layer_gcode.value, m_writer.extruder()->id(), &config) + "\n"; } gcode += this->change_layer(print_z); // this will increase m_layer_index m_layer = &layer; - if (! print.config.layer_gcode.value.empty()) { + if (! print.config().layer_gcode.value.empty()) { DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); gcode += this->placeholder_parser_process("layer_gcode", - print.config.layer_gcode.value, m_writer.extruder()->id(), &config) + print.config().layer_gcode.value, m_writer.extruder()->id(), &config) + "\n"; } @@ -1278,24 +1306,36 @@ void GCode::process_layer( // Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent // first_layer_temperature vs. temperature settings. for (const Extruder &extruder : m_writer.extruders()) { - if (print.config.single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id()) + if (print.config().single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id()) // In single extruder multi material mode, set the temperature for the current extruder only. continue; - int temperature = print.config.temperature.get_at(extruder.id()); - if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(extruder.id())) + int temperature = print.config().temperature.get_at(extruder.id()); + if (temperature > 0 && temperature != print.config().first_layer_temperature.get_at(extruder.id())) gcode += m_writer.set_temperature(temperature, false, extruder.id()); } - gcode += m_writer.set_bed_temperature(print.config.bed_temperature.get_at(first_extruder_id)); + gcode += m_writer.set_bed_temperature(print.config().bed_temperature.get_at(first_extruder_id)); // Mark the temperature transition from 1st to 2nd layer to be finished. m_second_layer_things_done = true; } + // Let's issue a filament change command if requested at this layer. + // In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all. + // (Layers can be close to each other, model could have been resliced with bigger layer height, ...). + bool colorprint_change = false; + while (!m_colorprint_heights.empty() && m_colorprint_heights.front()-EPSILON < layer.print_z) { + m_colorprint_heights.erase(m_colorprint_heights.begin()); + colorprint_change = true; + } + if (colorprint_change) + gcode += "M600\n"; + + // Extrude skirt at the print_z of the raft layers and normal object layers // not at the print_z of the interlaced support material layers. bool extrude_skirt = - ! print.skirt.entities.empty() && + ! print.skirt().entities.empty() && // Not enough skirt layers printed yet. - (m_skirt_done.size() < print.config.skirt_height.value || print.has_infinite_skirt()) && + (m_skirt_done.size() < print.config().skirt_height.value || print.has_infinite_skirt()) && // This print_z has not been extruded yet (m_skirt_done.empty() ? 0. : m_skirt_done.back()) < print_z - EPSILON && // and this layer is the 1st layer, or it is an object layer, or it is a raft layer. @@ -1317,7 +1357,7 @@ void GCode::process_layer( extruder_ids.front() = first_extruder_id; break; } - size_t n_loops = print.skirt.entities.size(); + size_t n_loops = print.skirt().entities.size(); if (n_loops <= extruder_ids.size()) { for (size_t i = 0; i < n_loops; ++i) skirt_loops_per_extruder[extruder_ids[i]] = std::pair(i, i + 1); @@ -1336,7 +1376,7 @@ void GCode::process_layer( } } else // Extrude all skirts with the current extruder. - skirt_loops_per_extruder[first_extruder_id] = std::pair(0, print.config.skirts.value); + skirt_loops_per_extruder[first_extruder_id] = std::pair(0, print.config().skirts.value); } // Group extrusions by an extruder, then by an object, an island and a region. @@ -1350,22 +1390,22 @@ void GCode::process_layer( bool has_support = role == erMixed || role == erSupportMaterial; bool has_interface = role == erMixed || role == erSupportMaterialInterface; // Extruder ID of the support base. -1 if "don't care". - unsigned int support_extruder = object.config.support_material_extruder.value - 1; + unsigned int support_extruder = object.config().support_material_extruder.value - 1; // Shall the support be printed with the active extruder, preferably with non-soluble, to avoid tool changes? - bool support_dontcare = object.config.support_material_extruder.value == 0; + bool support_dontcare = object.config().support_material_extruder.value == 0; // Extruder ID of the support interface. -1 if "don't care". - unsigned int interface_extruder = object.config.support_material_interface_extruder.value - 1; + unsigned int interface_extruder = object.config().support_material_interface_extruder.value - 1; // Shall the support interface be printed with the active extruder, preferably with non-soluble, to avoid tool changes? - bool interface_dontcare = object.config.support_material_interface_extruder.value == 0; + bool interface_dontcare = object.config().support_material_interface_extruder.value == 0; if (support_dontcare || interface_dontcare) { // Some support will be printed with "don't care" material, preferably non-soluble. // Is the current extruder assigned a soluble filament? unsigned int dontcare_extruder = first_extruder_id; - if (print.config.filament_soluble.get_at(dontcare_extruder)) { + if (print.config().filament_soluble.get_at(dontcare_extruder)) { // The last extruder printed on the previous layer extrudes soluble filament. // Try to find a non-soluble extruder on the same layer. for (unsigned int extruder_id : layer_tools.extruders) - if (! print.config.filament_soluble.get_at(extruder_id)) { + if (! print.config().filament_soluble.get_at(extruder_id)) { dontcare_extruder = extruder_id; break; } @@ -1407,16 +1447,16 @@ void GCode::process_layer( layer_surface_bboxes.push_back(get_extents(expoly.contour)); auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) { const BoundingBox &bbox = layer_surface_bboxes[i]; - return point.x >= bbox.min.x && point.x < bbox.max.x && - point.y >= bbox.min.y && point.y < bbox.max.y && + return point(0) >= bbox.min(0) && point(0) < bbox.max(0) && + point(1) >= bbox.min(1) && point(1) < bbox.max(1) && layer.slices.expolygons[i].contour.contains(point); }; - for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { - const LayerRegion *layerm = layer.regions[region_id]; + for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) { + const LayerRegion *layerm = (region_id < layer.regions().size()) ? layer.regions()[region_id] : nullptr; if (layerm == nullptr) continue; - const PrintRegion ®ion = *print.regions[region_id]; + const PrintRegion ®ion = *print.regions()[region_id]; // Now we must process perimeters and infills and create islands of extrusions in by_region std::map. @@ -1433,11 +1473,14 @@ void GCode::process_layer( continue; // This extrusion is part of certain Region, which tells us which extruder should be used for it: - int correct_extruder_id = Print::get_extruder(*fill, region); entity_type=="infills" ? std::max(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) : - std::max(region.config.perimeter_extruder.value - 1, 0); + int correct_extruder_id = Print::get_extruder(*fill, region); + //FIXME what is this? + entity_type=="infills" ? + std::max(0, (is_solid_infill(fill->entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) : + std::max(region.config().perimeter_extruder.value - 1, 0); // Let's recover vector of extruder overrides: - const ExtruderPerCopy* entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size()); + const ExtruderPerCopy* entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size()); // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it: for (unsigned int extruder : layer_tools.extruders) @@ -1459,8 +1502,8 @@ void GCode::process_layer( // fill->first_point fits inside ith slice point_inside_surface(i, fill->first_point())) { if (islands[i].by_region.empty()) - islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region()); - islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->_shifted_copies.size()); + islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); + islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size()); break; } } @@ -1494,7 +1537,7 @@ void GCode::process_layer( Flow skirt_flow = print.skirt_flow(); for (size_t i = loops.first; i < loops.second; ++ i) { // Adjust flow according to this layer's layer height. - ExtrusionLoop loop = *dynamic_cast(print.skirt.entities[i]); + ExtrusionLoop loop = *dynamic_cast(print.skirt().entities[i]); Flow layer_skirt_flow(skirt_flow); layer_skirt_flow.height = (float)skirt_height; double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); @@ -1515,7 +1558,7 @@ void GCode::process_layer( if (! m_brim_done) { this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp = true; - for (const ExtrusionEntity *ee : print.brim.entities) + for (const ExtrusionEntity *ee : print.brim().entities) gcode += this->extrude_loop(*dynamic_cast(ee), "brim", m_config.support_material_speed.value); m_brim_done = true; m_avoid_crossing_perimeters.use_external_mp = false; @@ -1527,7 +1570,6 @@ void GCode::process_layer( auto objects_by_extruder_it = by_extruder.find(extruder_id); if (objects_by_extruder_it == by_extruder.end()) continue; - // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature): for (int print_wipe_extrusions=const_cast(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) { if (print_wipe_extrusions == 0) @@ -1540,15 +1582,15 @@ void GCode::process_layer( // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z. continue; - m_config.apply(print_object->config, true); + m_config.apply(print_object->config(), true); m_layer = layers[layer_id].layer(); if (m_config.avoid_crossing_perimeters) m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true)); Points copies; if (single_object_idx == size_t(-1)) - copies = print_object->_shifted_copies; + copies = print_object->copies(); else - copies.push_back(print_object->_shifted_copies[single_object_idx]); + copies.push_back(print_object->copies()[single_object_idx]); // Sort the copies by the closest point starting with the current print position. unsigned int copy_id = 0; @@ -1558,7 +1600,7 @@ void GCode::process_layer( if (m_last_obj_copy != this_object_copy) m_avoid_crossing_perimeters.use_external_mp_once = true; m_last_obj_copy = this_object_copy; - this->set_origin(unscale(copy.x), unscale(copy.y)); + this->set_origin(unscale(copy)); if (object_by_extruder.support != nullptr && !print_wipe_extrusions) { m_layer = layers[layer_id].support_layer; gcode += this->extrude_support( @@ -1569,7 +1611,7 @@ void GCode::process_layer( for (ObjectByExtruder::Island &island : object_by_extruder.islands) { const auto& by_region_specific = const_cast(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region; - if (print.config.infill_first) { + if (print.config().infill_first) { gcode += this->extrude_infill(print, by_region_specific); gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]); } else { @@ -1612,17 +1654,18 @@ void GCode::apply_print_config(const PrintConfig &print_config) void GCode::append_full_config(const Print& print, std::string& str) { - const StaticPrintConfig *configs[] = { static_cast(&print.config), &print.default_object_config, &print.default_region_config }; + const StaticPrintConfig *configs[] = { static_cast(&print.config()), &print.default_object_config(), &print.default_region_config() }; for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) { const StaticPrintConfig *cfg = configs[i]; for (const std::string &key : cfg->keys()) if (key != "compatible_printers") str += "; " + key + " = " + cfg->serialize(key) + "\n"; } - const DynamicConfig &full_config = print.placeholder_parser.config(); + const DynamicConfig &full_config = print.placeholder_parser().config(); for (const char *key : { - "print_settings_id", "filament_settings_id", "printer_settings_id", - "printer_model", "printer_variant", "default_print_profile", "default_filament_profile", + "print_settings_id", "filament_settings_id", "sla_print_settings_id", "sla_material_settings_id", "printer_settings_id", + "printer_model", "printer_variant", + "default_print_profile", "default_filament_profile", "default_sla_print_profile", "default_sla_material_profile", "compatible_printers_condition_cummulative", "inherits_cummulative" }) { const ConfigOption *opt = full_config.option(key); if (opt != nullptr) @@ -1643,14 +1686,14 @@ void GCode::set_extruders(const std::vector &extruder_ids) } } -void GCode::set_origin(const Pointf &pointf) +void GCode::set_origin(const Vec2d &pointf) { // if origin increases (goes towards right), last_pos decreases because it goes towards left const Point translate( - scale_(m_origin.x - pointf.x), - scale_(m_origin.y - pointf.y) + scale_(m_origin(0) - pointf(0)), + scale_(m_origin(1) - pointf(1)) ); - m_last_pos.translate(translate); + m_last_pos += translate; m_wipe.path.translate(translate); m_origin = pointf; } @@ -1781,13 +1824,13 @@ static Points::iterator project_point_to_polygon_and_insert(Polygon &polygon, co j = 0; const Point &p1 = polygon.points[i]; const Point &p2 = polygon.points[j]; - const Slic3r::Point v_seg = p1.vector_to(p2); - const Slic3r::Point v_pt = p1.vector_to(pt); - const int64_t l2_seg = int64_t(v_seg.x) * int64_t(v_seg.x) + int64_t(v_seg.y) * int64_t(v_seg.y); - int64_t t_pt = int64_t(v_seg.x) * int64_t(v_pt.x) + int64_t(v_seg.y) * int64_t(v_pt.y); + const Slic3r::Point v_seg = p2 - p1; + const Slic3r::Point v_pt = pt - p1; + const int64_t l2_seg = int64_t(v_seg(0)) * int64_t(v_seg(0)) + int64_t(v_seg(1)) * int64_t(v_seg(1)); + int64_t t_pt = int64_t(v_seg(0)) * int64_t(v_pt(0)) + int64_t(v_seg(1)) * int64_t(v_pt(1)); if (t_pt < 0) { // Closest to p1. - double dabs = sqrt(int64_t(v_pt.x) * int64_t(v_pt.x) + int64_t(v_pt.y) * int64_t(v_pt.y)); + double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1))); if (dabs < d_min) { d_min = dabs; i_min = i; @@ -1800,7 +1843,7 @@ static Points::iterator project_point_to_polygon_and_insert(Polygon &polygon, co } else { // Closest to the segment. assert(t_pt >= 0 && t_pt <= l2_seg); - int64_t d_seg = int64_t(v_seg.y) * int64_t(v_pt.x) - int64_t(v_seg.x) * int64_t(v_pt.y); + int64_t d_seg = int64_t(v_seg(1)) * int64_t(v_pt(0)) - int64_t(v_seg(0)) * int64_t(v_pt(1)); double d = double(d_seg) / sqrt(double(l2_seg)); double dabs = std::abs(d); if (dabs < d_min) { @@ -1809,15 +1852,15 @@ static Points::iterator project_point_to_polygon_and_insert(Polygon &polygon, co // Evaluate the foot point. pt_min = p1; double linv = double(d_seg) / double(l2_seg); - pt_min.x = pt.x - coord_t(floor(double(v_seg.y) * linv + 0.5)); - pt_min.y = pt.y + coord_t(floor(double(v_seg.x) * linv + 0.5)); + pt_min(0) = pt(0) - coord_t(floor(double(v_seg(1)) * linv + 0.5)); + pt_min(1) = pt(1) + coord_t(floor(double(v_seg(0)) * linv + 0.5)); assert(Line(p1, p2).distance_to(pt_min) < scale_(1e-5)); } } } assert(i_min != size_t(-1)); - if (pt_min.distance_to(polygon.points[i_min]) > eps) { + if ((pt_min - polygon.points[i_min]).cast().norm() > eps) { // Insert a new point on the segment i_min, i_min+1. return polygon.points.insert(polygon.points.begin() + (i_min + 1), pt_min); } @@ -1829,8 +1872,8 @@ std::vector polygon_parameter_by_length(const Polygon &polygon) // Parametrize the polygon by its length. std::vector lengths(polygon.points.size()+1, 0.); for (size_t i = 1; i < polygon.points.size(); ++ i) - lengths[i] = lengths[i-1] + float(polygon.points[i].distance_to(polygon.points[i-1])); - lengths.back() = lengths[lengths.size()-2] + float(polygon.points.front().distance_to(polygon.points.back())); + lengths[i] = lengths[i-1] + (polygon.points[i] - polygon.points[i-1]).cast().norm(); + lengths.back() = lengths[lengths.size()-2] + (polygon.points.front() - polygon.points.back()).cast().norm(); return lengths; } @@ -1878,10 +1921,10 @@ std::vector polygon_angles_at_vertices(const Polygon &polygon, const std: const Point &p0 = polygon.points[idx_prev]; const Point &p1 = polygon.points[idx_curr]; const Point &p2 = polygon.points[idx_next]; - const Point v1 = p0.vector_to(p1); - const Point v2 = p1.vector_to(p2); - int64_t dot = int64_t(v1.x)*int64_t(v2.x) + int64_t(v1.y)*int64_t(v2.y); - int64_t cross = int64_t(v1.x)*int64_t(v2.y) - int64_t(v1.y)*int64_t(v2.x); + const Point v1 = p1 - p0; + const Point v2 = p2 - p1; + int64_t dot = int64_t(v1(0))*int64_t(v2(0)) + int64_t(v1(1))*int64_t(v2(1)); + int64_t cross = int64_t(v1(0))*int64_t(v2(1)) - int64_t(v1(1))*int64_t(v2(0)); float angle = float(atan2(double(cross), double(dot))); angles[idx_curr] = angle; } @@ -1905,10 +1948,10 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou { static int iRun = 0; BoundingBox bbox = (*lower_layer_edge_grid)->bbox(); - bbox.min.x -= scale_(5.f); - bbox.min.y -= scale_(5.f); - bbox.max.x += scale_(5.f); - bbox.max.y += scale_(5.f); + bbox.min(0) -= scale_(5.f); + bbox.min(1) -= scale_(5.f); + bbox.max(0) += scale_(5.f); + bbox.max(1) += scale_(5.f); EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++)); } #endif @@ -1944,7 +1987,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou break; case spRear: last_pos = m_layer->object()->bounding_box().center(); - last_pos.y += coord_t(3. * m_layer->object()->bounding_box().radius()); + last_pos(1) += coord_t(3. * m_layer->object()->bounding_box().radius()); last_pos_weight = 5.f; break; } @@ -2077,7 +2120,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou //FIXME Better parametrize the loop by its length. Polygon polygon = loop.polygon(); Point centroid = polygon.centroid(); - last_pos = Point(polygon.bounding_box().max.x, centroid.y); + last_pos = Point(polygon.bounding_box().max(0), centroid(1)); last_pos.rotate(fmod((float)rand()/16.0, 2.0*PI), centroid); } // Find the closest point, avoid overhangs. @@ -2134,19 +2177,17 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // create the destination point along the first segment and rotate it // we make sure we don't exceed the segment length because we don't know // the rotation of the second segment so we might cross the object boundary - Line first_segment( - paths.front().polyline.points[0], - paths.front().polyline.points[1] - ); - double distance = std::min( - scale_(EXTRUDER_CONFIG(nozzle_diameter)), - first_segment.length() - ); - Point point = first_segment.point_at(distance); - point.rotate(angle, first_segment.a); - + Vec2d p1 = paths.front().polyline.points.front().cast(); + Vec2d p2 = paths.front().polyline.points[1].cast(); + Vec2d v = p2 - p1; + double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter)); + double l2 = v.squaredNorm(); + // Shift by no more than a nozzle diameter. + //FIXME Hiding the seams will not work nicely for very densely discretized contours! + Point pt = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast(); + pt.rotate(angle, paths.front().polyline.points.front()); // generate the travel move - gcode += m_writer.travel_to_xy(this->point_to_gcode(point), "move inwards before travel"); + gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), "move inwards before travel"); } return gcode; @@ -2180,7 +2221,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des else if (const ExtrusionLoop* loop = dynamic_cast(&entity)) return this->extrude_loop(*loop, description, speed, lower_layer_edge_grid); else { - CONFESS("Invalid argument supplied to extrude()"); + throw std::invalid_argument("Invalid argument supplied to extrude()"); return ""; } } @@ -2204,7 +2245,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vectorconfig); + m_config.apply(print.regions()[®ion - &by_region.front()]->config()); for (ExtrusionEntity *ee : region.perimeters.entities) gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid); } @@ -2216,7 +2257,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorconfig); + m_config.apply(print.regions()[®ion - &by_region.front()]->config()); ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false); for (ExtrusionEntity *fill : chained.entities) { auto *eec = dynamic_cast(fill); @@ -2316,7 +2357,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, std::string gcode; // go to first point of extrusion path - if (!m_last_pos_defined || !m_last_pos.coincides_with(path.first_point())) { + if (!m_last_pos_defined || m_last_pos != path.first_point()) { gcode += this->travel_to( path.first_point(), path.role(), @@ -2365,7 +2406,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } else if (path.role() == erGapFill) { speed = m_config.get_abs_value("gap_fill_speed"); } else { - CONFESS("Invalid speed"); + throw std::invalid_argument("Invalid speed"); } } if (this->on_first_layer()) @@ -2631,24 +2672,21 @@ std::string GCode::set_extruder(unsigned int extruder_id) } // convert a model-space scaled point into G-code coordinates -Pointf GCode::point_to_gcode(const Point &point) const +Vec2d GCode::point_to_gcode(const Point &point) const { - Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset); - return Pointf( - unscale(point.x) + m_origin.x - extruder_offset.x, - unscale(point.y) + m_origin.y - extruder_offset.y); + Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); + return unscale(point) + m_origin - extruder_offset; } // convert a model-space scaled point into G-code coordinates -Point GCode::gcode_to_point(const Pointf &point) const +Point GCode::gcode_to_point(const Vec2d &point) const { - Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset); + Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); return Point( - scale_(point.x - m_origin.x + extruder_offset.x), - scale_(point.y - m_origin.y + extruder_offset.y)); + scale_(point(0) - m_origin(0) + extruder_offset(0)), + scale_(point(1) - m_origin(1) + extruder_offset(1))); } - // Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed // during infill/perimeter wiping, or normally (depends on wiping_entities parameter) // Returns a reference to member to avoid copying. @@ -2694,7 +2732,7 @@ void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, co } else if (type != "infills") { - CONFESS("Unknown parameter!"); + throw std::invalid_argument("Unknown parameter!"); return; } diff --git a/xs/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp similarity index 96% rename from xs/src/libslic3r/GCode.hpp rename to src/libslic3r/GCode.hpp index d319bee014..bf65311db4 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -126,6 +126,7 @@ private: class GCode { public: GCode() : + m_origin(Vec2d::Zero()), m_enable_loop_clipping(true), m_enable_cooling_markers(false), m_enable_extrusion_role_markers(false), @@ -149,20 +150,22 @@ public: {} ~GCode() {} - // throws std::runtime_exception + // throws std::runtime_exception on error, + // throws CanceledException through print->throw_if_canceled(). void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr); // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. - const Pointf& origin() const { return m_origin; } - void set_origin(const Pointf &pointf); - void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Pointf(x, y)); } + const Vec2d& origin() const { return m_origin; } + void set_origin(const Vec2d &pointf); + void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); } const Point& last_pos() const { return m_last_pos; } - Pointf point_to_gcode(const Point &point) const; - Point gcode_to_point(const Pointf &point) const; + Vec2d point_to_gcode(const Point &point) const; + Point gcode_to_point(const Vec2d &point) const; const FullPrintConfig &config() const { return m_config; } const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } + const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } // Process a template through the placeholder parser, collect error messages to be reported // inside the generated string and after the G-code export finishes. std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr); @@ -259,7 +262,7 @@ protected: /* Origin of print coordinates expressed in unscaled G-code coordinates. This affects the input arguments supplied to the extrude*() and travel_to() methods. */ - Pointf m_origin; + Vec2d m_origin; FullPrintConfig m_config; GCodeWriter m_writer; PlaceholderParser m_placeholder_parser; @@ -314,6 +317,9 @@ protected: bool m_second_layer_things_done; // Index of a last object copy extruded. std::pair m_last_obj_copy; + // Layer heights for colorprint - updated before the export and erased during the process + // so no toolchange occurs twice. + std::vector m_colorprint_heights; // Time estimators GCodeTimeEstimator m_normal_time_estimator; diff --git a/xs/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp similarity index 92% rename from xs/src/libslic3r/GCode/Analyzer.cpp rename to src/libslic3r/GCode/Analyzer.cpp index b7ecee5a4c..e3cd67e7cc 100644 --- a/xs/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -14,7 +14,7 @@ static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; static const float INCHES_TO_MM = 25.4f; static const float DEFAULT_FEEDRATE = 0.0f; static const unsigned int DEFAULT_EXTRUDER_ID = 0; -static const Slic3r::Pointf3 DEFAULT_START_POSITION = Slic3r::Pointf3(0.0f, 0.0f, 0.0f); +static const Slic3r::Vec3d DEFAULT_START_POSITION = Slic3r::Vec3d(0.0f, 0.0f, 0.0f); static const float DEFAULT_START_EXTRUSION = 0.0f; namespace Slic3r { @@ -71,7 +71,7 @@ bool GCodeAnalyzer::Metadata::operator != (const GCodeAnalyzer::Metadata& other) return false; } -GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder) +GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder) : type(type) , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate) , start_position(start_position) @@ -80,7 +80,7 @@ GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusi { } -GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, const GCodeAnalyzer::Metadata& data, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder) +GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, const GCodeAnalyzer::Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder) : type(type) , data(data) , start_position(start_position) @@ -587,12 +587,12 @@ void GCodeAnalyzer::_reset_axes_position() ::memset((void*)m_state.position, 0, Num_Axis * sizeof(float)); } -void GCodeAnalyzer::_set_start_position(const Pointf3& position) +void GCodeAnalyzer::_set_start_position(const Vec3d& position) { m_state.start_position = position; } -const Pointf3& GCodeAnalyzer::_get_start_position() const +const Vec3d& GCodeAnalyzer::_get_start_position() const { return m_state.start_position; } @@ -612,9 +612,9 @@ float GCodeAnalyzer::_get_delta_extrusion() const return _get_axis_position(E) - m_state.start_extrusion; } -Pointf3 GCodeAnalyzer::_get_end_position() const +Vec3d GCodeAnalyzer::_get_end_position() const { - return Pointf3(m_state.position[X], m_state.position[Y], m_state.position[Z]); + return Vec3d(m_state.position[X], m_state.position[Y], m_state.position[Z]); } void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) @@ -673,7 +673,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ Metadata data; float z = FLT_MAX; Polyline polyline; - Pointf3 position(FLT_MAX, FLT_MAX, FLT_MAX); + Vec3d position(FLT_MAX, FLT_MAX, FLT_MAX); float volumetric_rate = FLT_MAX; GCodePreviewData::Range height_range; GCodePreviewData::Range width_range; @@ -683,7 +683,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ // constructs the polylines while traversing the moves for (const GCodeMove& move : extrude_moves->second) { - if ((data != move.data) || (z != move.start_position.z) || (position != move.start_position) || (volumetric_rate != move.data.feedrate * (float)move.data.mm3_per_mm)) + if ((data != move.data) || (z != move.start_position.z()) || (position != move.start_position) || (volumetric_rate != move.data.feedrate * (float)move.data.mm3_per_mm)) { // store current polyline polyline.remove_duplicate_points(); @@ -693,12 +693,12 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ polyline = Polyline(); // add both vertices of the move - polyline.append(Point(scale_(move.start_position.x), scale_(move.start_position.y))); - polyline.append(Point(scale_(move.end_position.x), scale_(move.end_position.y))); + polyline.append(Point(scale_(move.start_position.x()), scale_(move.start_position.y()))); + polyline.append(Point(scale_(move.end_position.x()), scale_(move.end_position.y()))); // update current values data = move.data; - z = move.start_position.z; + z = (float)move.start_position.z(); volumetric_rate = move.data.feedrate * (float)move.data.mm3_per_mm; height_range.update_from(move.data.height); width_range.update_from(move.data.width); @@ -707,7 +707,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ } else // append end vertex of the move to current polyline - polyline.append(Point(scale_(move.end_position.x), scale_(move.end_position.y))); + polyline.append(Point(scale_(move.end_position.x()), scale_(move.end_position.y()))); // update current values position = move.end_position; @@ -742,7 +742,7 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data) return; Polyline3 polyline; - Pointf3 position(FLT_MAX, FLT_MAX, FLT_MAX); + Vec3d position(FLT_MAX, FLT_MAX, FLT_MAX); GCodePreviewData::Travel::EType type = GCodePreviewData::Travel::Num_Types; GCodePreviewData::Travel::Polyline::EDirection direction = GCodePreviewData::Travel::Polyline::Num_Directions; float feedrate = FLT_MAX; @@ -756,7 +756,7 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data) for (const GCodeMove& move : travel_moves->second) { GCodePreviewData::Travel::EType move_type = (move.delta_extruder < 0.0f) ? GCodePreviewData::Travel::Retract : ((move.delta_extruder > 0.0f) ? GCodePreviewData::Travel::Extrude : GCodePreviewData::Travel::Move); - GCodePreviewData::Travel::Polyline::EDirection move_direction = ((move.start_position.x != move.end_position.x) || (move.start_position.y != move.end_position.y)) ? GCodePreviewData::Travel::Polyline::Generic : GCodePreviewData::Travel::Polyline::Vertical; + GCodePreviewData::Travel::Polyline::EDirection move_direction = ((move.start_position.x() != move.end_position.x()) || (move.start_position.y() != move.end_position.y())) ? GCodePreviewData::Travel::Polyline::Generic : GCodePreviewData::Travel::Polyline::Vertical; if ((type != move_type) || (direction != move_direction) || (feedrate != move.data.feedrate) || (position != move.start_position) || (extruder_id != move.data.extruder_id)) { @@ -768,12 +768,12 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data) polyline = Polyline3(); // add both vertices of the move - polyline.append(Point3(scale_(move.start_position.x), scale_(move.start_position.y), scale_(move.start_position.z))); - polyline.append(Point3(scale_(move.end_position.x), scale_(move.end_position.y), scale_(move.end_position.z))); + polyline.append(Vec3crd(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()))); + polyline.append(Vec3crd(scale_(move.end_position.x()), scale_(move.end_position.y()), scale_(move.end_position.z()))); } else // append end vertex of the move to current polyline - polyline.append(Point3(scale_(move.end_position.x), scale_(move.end_position.y), scale_(move.end_position.z))); + polyline.append(Vec3crd(scale_(move.end_position.x()), scale_(move.end_position.y()), scale_(move.end_position.z()))); // update current values position = move.end_position; @@ -804,7 +804,7 @@ void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_da for (const GCodeMove& move : retraction_moves->second) { // store position - Point3 position(scale_(move.start_position.x), scale_(move.start_position.y), scale_(move.start_position.z)); + Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z())); preview_data.retraction.positions.emplace_back(position, move.data.width, move.data.height); } } @@ -818,7 +818,7 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_ for (const GCodeMove& move : unretraction_moves->second) { // store position - Point3 position(scale_(move.start_position.x), scale_(move.start_position.y), scale_(move.start_position.z)); + Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z())); preview_data.unretraction.positions.emplace_back(position, move.data.width, move.data.height); } } diff --git a/xs/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp similarity index 92% rename from xs/src/libslic3r/GCode/Analyzer.hpp rename to src/libslic3r/GCode/Analyzer.hpp index 03dbab3383..27a49b8690 100644 --- a/xs/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -75,12 +75,12 @@ public: EType type; Metadata data; - Pointf3 start_position; - Pointf3 end_position; + Vec3d start_position; + Vec3d end_position; float delta_extruder; - GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder); - GCodeMove(EType type, const Metadata& data, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder); + GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder); + GCodeMove(EType type, const Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder); }; typedef std::vector GCodeMovesList; @@ -93,7 +93,7 @@ private: EPositioningType global_positioning_type; EPositioningType e_local_positioning_type; Metadata data; - Pointf3 start_position; + Vec3d start_position = Vec3d::Zero(); float start_extrusion; float position[Num_Axis]; }; @@ -206,15 +206,15 @@ private: // Sets axes position to zero void _reset_axes_position(); - void _set_start_position(const Pointf3& position); - const Pointf3& _get_start_position() const; + void _set_start_position(const Vec3d& position); + const Vec3d& _get_start_position() const; void _set_start_extrusion(float extrusion); float _get_start_extrusion() const; float _get_delta_extrusion() const; // Returns current xyz position (from m_state.position[]) - Pointf3 _get_end_position() const; + Vec3d _get_end_position() const; // Adds a new move with the given data void _store_move(GCodeMove::EType type); diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp similarity index 99% rename from xs/src/libslic3r/GCode/CoolingBuffer.cpp rename to src/libslic3r/GCode/CoolingBuffer.cpp index a15247693c..40ccc7b09f 100644 --- a/xs/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -23,10 +23,10 @@ CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen), m_current_ void CoolingBuffer::reset() { m_current_pos.assign(5, 0.f); - Pointf3 pos = m_gcodegen.writer().get_position(); - m_current_pos[0] = float(pos.x); - m_current_pos[1] = float(pos.y); - m_current_pos[2] = float(pos.z); + Vec3d pos = m_gcodegen.writer().get_position(); + m_current_pos[0] = float(pos(0)); + m_current_pos[1] = float(pos(1)); + m_current_pos[2] = float(pos(2)); m_current_pos[4] = float(m_gcodegen.config().travel_speed.value); } diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp similarity index 100% rename from xs/src/libslic3r/GCode/CoolingBuffer.hpp rename to src/libslic3r/GCode/CoolingBuffer.hpp diff --git a/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp new file mode 100644 index 0000000000..c04aeae3cb --- /dev/null +++ b/src/libslic3r/GCode/PostProcessor.cpp @@ -0,0 +1,60 @@ +#include "PostProcessor.hpp" + +#if 1 +//#ifdef WIN32 + +namespace Slic3r { + +//FIXME Ignore until we include boost::process +void run_post_process_scripts(const std::string &path, const PrintConfig &config) +{ +} + +} // namespace Slic3r + +#else + +#include + +namespace Slic3r { + +void run_post_process_scripts(const std::string &path, const PrintConfig &config) +{ + if (config.post_process.values.empty()) + return; + config.setenv_(); + for (std::string script: config.post_process.values) { + // Ignore empty post processing script lines. + boost::trim(script); + if (script.empty()) + continue; + BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path; + if (! boost::filesystem::exists(boost::filesystem::path(path))) + throw std::runtime_exception(std::string("The configured post-processing script does not exist: ") + path); +#ifndef WIN32 + file_status fs = boost::filesystem::status(path); + //FIXME test if executible by the effective UID / GID. + // throw std::runtime_exception(std::string("The configured post-processing script is not executable: check permissions. ") + path)); +#endif + int result = 0; +#ifdef WIN32 + if (boost::iends_with(file, ".gcode")) { + // The current process may be slic3r.exe or slic3r-console.exe. + // Find the path of the process: + wchar_t wpath_exe[_MAX_PATH + 1]; + ::GetModuleFileNameW(nullptr, wpath_exe, _MAX_PATH); + boost::filesystem::path path_exe(wpath_exe); + // Replace it with the current perl interpreter. + result = boost::process::system((path_exe.parent_path() / "perl5.24.0.exe").string(), script, output_file); + } else +#else + result = boost::process::system(script, output_file); +#endif + if (result < 0) + BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned."; + } +} + +} // namespace Slic3r + +#endif diff --git a/src/libslic3r/GCode/PostProcessor.hpp b/src/libslic3r/GCode/PostProcessor.hpp new file mode 100644 index 0000000000..ce47374cb3 --- /dev/null +++ b/src/libslic3r/GCode/PostProcessor.hpp @@ -0,0 +1,15 @@ +#ifndef slic3r_GCode_PostProcessor_hpp_ +#define slic3r_GCode_PostProcessor_hpp_ + +#include + +#include "../libslic3r.h" +#include "../PrintConfig.hpp" + +namespace Slic3r { + +extern void run_post_process_scripts(const std::string &path, const PrintConfig &config); + +} // namespace Slic3r + +#endif /* slic3r_GCode_PostProcessor_hpp_ */ diff --git a/xs/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp similarity index 98% rename from xs/src/libslic3r/GCode/PressureEqualizer.cpp rename to src/libslic3r/GCode/PressureEqualizer.cpp index 68e642f3c0..3b2a58a884 100644 --- a/xs/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -49,10 +49,10 @@ void PressureEqualizer::reset() // Volumetric rate of a 0.45mm x 0.2mm extrusion at 60mm/s XY movement: 0.45*0.2*60*60=5.4*60 = 324 mm^3/min // Volumetric rate of a 0.45mm x 0.2mm extrusion at 20mm/s XY movement: 0.45*0.2*20*60=1.8*60 = 108 mm^3/min // Slope of the volumetric rate, changing from 20mm/s to 60mm/s over 2 seconds: (5.4-1.8)*60*60/2=60*60*1.8 = 6480 mm^3/min^2 = 1.8 mm^3/s^2 - m_max_volumetric_extrusion_rate_slope_positive = (this->m_config == NULL) ? 6480.f : - this->m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f; - m_max_volumetric_extrusion_rate_slope_negative = (this->m_config == NULL) ? 6480.f : - this->m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f; + m_max_volumetric_extrusion_rate_slope_positive = (m_config == NULL) ? 6480.f : + m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f; + m_max_volumetric_extrusion_rate_slope_negative = (m_config == NULL) ? 6480.f : + m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f; for (size_t i = 0; i < numExtrusionRoles; ++ i) { m_max_volumetric_extrusion_rate_slopes[i].negative = m_max_volumetric_extrusion_rate_slope_negative; @@ -171,7 +171,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) { line += strlen(EXTRUSION_ROLE_TAG); int role = atoi(line); - this->m_current_extrusion_role = ExtrusionRole(role); + m_current_extrusion_role = ExtrusionRole(role); ++ line_idx; return false; } diff --git a/xs/src/libslic3r/GCode/PressureEqualizer.hpp b/src/libslic3r/GCode/PressureEqualizer.hpp similarity index 100% rename from xs/src/libslic3r/GCode/PressureEqualizer.hpp rename to src/libslic3r/GCode/PressureEqualizer.hpp diff --git a/xs/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp similarity index 99% rename from xs/src/libslic3r/GCode/PreviewData.cpp rename to src/libslic3r/GCode/PreviewData.cpp index 3833bca06c..564fc34dce 100644 --- a/xs/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -1,7 +1,6 @@ #include "Analyzer.hpp" #include "PreviewData.hpp" #include -#include #include #include @@ -226,7 +225,7 @@ void GCodePreviewData::Travel::set_default() const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f); -GCodePreviewData::Retraction::Position::Position(const Point3& position, float width, float height) +GCodePreviewData::Retraction::Position::Position(const Vec3crd& position, float width, float height) : position(position) , width(width) , height(height) diff --git a/xs/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp similarity index 97% rename from xs/src/libslic3r/GCode/PreviewData.hpp rename to src/libslic3r/GCode/PreviewData.hpp index ea8ca6d58a..ab74993f55 100644 --- a/xs/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -151,11 +151,11 @@ public: struct Position { - Point3 position; + Vec3crd position; float width; float height; - Position(const Point3& position, float width, float height); + Position(const Vec3crd& position, float width, float height); }; typedef std::vector PositionsList; diff --git a/xs/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp similarity index 69% rename from xs/src/libslic3r/GCode/PrintExtents.cpp rename to src/libslic3r/GCode/PrintExtents.cpp index 37b79f3434..92a58fdf06 100644 --- a/xs/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -19,10 +19,10 @@ static inline BoundingBox extrusion_polyline_extents(const Polyline &polyline, c if (! polyline.points.empty()) bbox.merge(polyline.points.front()); for (const Point &pt : polyline.points) { - bbox.min.x = std::min(bbox.min.x, pt.x - radius); - bbox.min.y = std::min(bbox.min.y, pt.y - radius); - bbox.max.x = std::max(bbox.max.x, pt.x + radius); - bbox.max.y = std::max(bbox.max.y, pt.y + radius); + bbox.min(0) = std::min(bbox.min(0), pt(0) - radius); + bbox.min(1) = std::min(bbox.min(1), pt(1) - radius); + bbox.max(0) = std::max(bbox.max(0), pt(0) + radius); + bbox.max(1) = std::max(bbox.max(1), pt(1) + radius); } return bbox; } @@ -32,8 +32,8 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusio BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)); BoundingBoxf bboxf; if (! empty(bbox)) { - bboxf.min = Pointf::new_unscale(bbox.min); - bboxf.max = Pointf::new_unscale(bbox.max); + bboxf.min = unscale(bbox.min); + bboxf.max = unscale(bbox.max); bboxf.defined = true; } return bboxf; @@ -46,8 +46,8 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusio bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width))); BoundingBoxf bboxf; if (! empty(bbox)) { - bboxf.min = Pointf::new_unscale(bbox.min); - bboxf.max = Pointf::new_unscale(bbox.max); + bboxf.min = unscale(bbox.min); + bboxf.max = unscale(bbox.max); bboxf.defined = true; } return bboxf; @@ -60,8 +60,8 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &ext bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width))); BoundingBoxf bboxf; if (! empty(bbox)) { - bboxf.min = Pointf::new_unscale(bbox.min); - bboxf.max = Pointf::new_unscale(bbox.max); + bboxf.min = unscale(bbox.min); + bboxf.max = unscale(bbox.max); bboxf.defined = true; } return bboxf; @@ -93,25 +93,25 @@ static BoundingBoxf extrusionentity_extents(const ExtrusionEntity *extrusion_ent auto *extrusion_entity_collection = dynamic_cast(extrusion_entity); if (extrusion_entity_collection != nullptr) return extrusionentity_extents(*extrusion_entity_collection); - CONFESS("Unexpected extrusion_entity type in extrusionentity_extents()"); + throw std::runtime_error("Unexpected extrusion_entity type in extrusionentity_extents()"); return BoundingBoxf(); } BoundingBoxf get_print_extrusions_extents(const Print &print) { - BoundingBoxf bbox(extrusionentity_extents(print.brim)); - bbox.merge(extrusionentity_extents(print.skirt)); + BoundingBoxf bbox(extrusionentity_extents(print.brim())); + bbox.merge(extrusionentity_extents(print.skirt())); return bbox; } BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object, const coordf_t max_print_z) { BoundingBoxf bbox; - for (const Layer *layer : print_object.layers) { + for (const Layer *layer : print_object.layers()) { if (layer->print_z > max_print_z) break; BoundingBoxf bbox_this; - for (const LayerRegion *layerm : layer->regions) { + for (const LayerRegion *layerm : layer->regions()) { bbox_this.merge(extrusionentity_extents(layerm->perimeters)); for (const ExtrusionEntity *ee : layerm->fills.entities) // fill represents infill extrusions of a single island. @@ -121,9 +121,9 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object if (support_layer) for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) bbox_this.merge(extrusionentity_extents(extrusion_entity)); - for (const Point &offset : print_object._shifted_copies) { + for (const Point &offset : print_object.copies()) { BoundingBoxf bbox_translated(bbox_this); - bbox_translated.translate(Pointf::new_unscale(offset)); + bbox_translated.translate(unscale(offset)); bbox.merge(bbox_translated); } } @@ -136,30 +136,23 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_ { // Wipe tower extrusions are saved as if the tower was at the origin with no rotation // We need to get position and angle of the wipe tower to transform them to actual position. - Pointf wipe_tower_pos(print.config.wipe_tower_x.value, print.config.wipe_tower_y.value); - float wipe_tower_angle = print.config.wipe_tower_rotation_angle.value; + Transform2d trafo = + Eigen::Translation2d(print.config().wipe_tower_x.value, print.config().wipe_tower_y.value) * + Eigen::Rotation2Dd(print.config().wipe_tower_rotation_angle.value); BoundingBoxf bbox; - for (const std::vector &tool_changes : print.m_wipe_tower_tool_changes) { + for (const std::vector &tool_changes : print.wipe_tower_data().tool_changes) { if (! tool_changes.empty() && tool_changes.front().print_z > max_print_z) break; for (const WipeTower::ToolChangeResult &tcr : tool_changes) { for (size_t i = 1; i < tcr.extrusions.size(); ++ i) { const WipeTower::Extrusion &e = tcr.extrusions[i]; if (e.width > 0) { - Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y); - Pointf p2(e.pos.x, e.pos.y); - p1.rotate(wipe_tower_angle); - p1.translate(wipe_tower_pos); - p2.rotate(wipe_tower_angle); - p2.translate(wipe_tower_pos); - - bbox.merge(p1); - coordf_t radius = 0.5 * e.width; - bbox.min.x = std::min(bbox.min.x, std::min(p1.x, p2.x) - radius); - bbox.min.y = std::min(bbox.min.y, std::min(p1.y, p2.y) - radius); - bbox.max.x = std::max(bbox.max.x, std::max(p1.x, p2.x) + radius); - bbox.max.y = std::max(bbox.max.y, std::max(p1.y, p2.y) + radius); + Vec2d delta = 0.5 * Vec2d(e.width, e.width); + Vec2d p1 = trafo * Vec2d((&e - 1)->pos.x, (&e - 1)->pos.y); + Vec2d p2 = trafo * Vec2d(e.pos.x, e.pos.y); + bbox.merge(p1.cwiseMin(p2) - delta); + bbox.merge(p1.cwiseMax(p2) + delta); } } } @@ -171,19 +164,19 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_ BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print) { BoundingBoxf bbox; - if (print.m_wipe_tower_priming) { - const WipeTower::ToolChangeResult &tcr = *print.m_wipe_tower_priming.get(); + if (print.wipe_tower_data().priming != nullptr) { + const WipeTower::ToolChangeResult &tcr = *print.wipe_tower_data().priming; for (size_t i = 1; i < tcr.extrusions.size(); ++ i) { const WipeTower::Extrusion &e = tcr.extrusions[i]; if (e.width > 0) { - Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y); - Pointf p2(e.pos.x, e.pos.y); + Vec2d p1((&e - 1)->pos.x, (&e - 1)->pos.y); + Vec2d p2(e.pos.x, e.pos.y); bbox.merge(p1); coordf_t radius = 0.5 * e.width; - bbox.min.x = std::min(bbox.min.x, std::min(p1.x, p2.x) - radius); - bbox.min.y = std::min(bbox.min.y, std::min(p1.y, p2.y) - radius); - bbox.max.x = std::max(bbox.max.x, std::max(p1.x, p2.x) + radius); - bbox.max.y = std::max(bbox.max.y, std::max(p1.y, p2.y) + radius); + bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius); + bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius); + bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius); + bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius); } } } diff --git a/xs/src/libslic3r/GCode/PrintExtents.hpp b/src/libslic3r/GCode/PrintExtents.hpp similarity index 100% rename from xs/src/libslic3r/GCode/PrintExtents.hpp rename to src/libslic3r/GCode/PrintExtents.hpp diff --git a/xs/src/libslic3r/GCode/SpiralVase.cpp b/src/libslic3r/GCode/SpiralVase.cpp similarity index 100% rename from xs/src/libslic3r/GCode/SpiralVase.cpp rename to src/libslic3r/GCode/SpiralVase.cpp diff --git a/xs/src/libslic3r/GCode/SpiralVase.hpp b/src/libslic3r/GCode/SpiralVase.hpp similarity index 100% rename from xs/src/libslic3r/GCode/SpiralVase.hpp rename to src/libslic3r/GCode/SpiralVase.hpp diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp similarity index 82% rename from xs/src/libslic3r/GCode/ToolOrdering.cpp rename to src/libslic3r/GCode/ToolOrdering.cpp index 189a94d496..e800cd53f7 100644 --- a/xs/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -34,19 +34,19 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const // For the use case when each object is printed separately -// (print.config.complete_objects is true). +// (print.config().complete_objects is true). ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material) { - if (object.layers.empty()) + if (object.layers().empty()) return; // Initialize the print layers for just a single object. { std::vector zs; - zs.reserve(zs.size() + object.layers.size() + object.support_layers.size()); - for (auto layer : object.layers) + zs.reserve(zs.size() + object.layers().size() + object.support_layers().size()); + for (auto layer : object.layers()) zs.emplace_back(layer->print_z); - for (auto layer : object.support_layers) + for (auto layer : object.support_layers()) zs.emplace_back(layer->print_z); this->initialize_layers(zs); } @@ -57,42 +57,41 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); - this->fill_wipe_tower_partitions(object.print()->config, object.layers.front()->print_z - object.layers.front()->height); + this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height); this->collect_extruder_statistics(prime_multi_material); } // For the use case when all objects are printed at once. -// (print.config.complete_objects is false). +// (print.config().complete_objects is false). ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material) { - m_print_config_ptr = &print.config; + m_print_config_ptr = &print.config(); - PrintObjectPtrs objects = print.get_printable_objects(); // Initialize the print layers for all objects and all layers. coordf_t object_bottom_z = 0.; { std::vector zs; - for (auto object : objects) { - zs.reserve(zs.size() + object->layers.size() + object->support_layers.size()); - for (auto layer : object->layers) + for (auto object : print.objects()) { + zs.reserve(zs.size() + object->layers().size() + object->support_layers().size()); + for (auto layer : object->layers()) zs.emplace_back(layer->print_z); - for (auto layer : object->support_layers) + for (auto layer : object->support_layers()) zs.emplace_back(layer->print_z); - if (! object->layers.empty()) - object_bottom_z = object->layers.front()->print_z - object->layers.front()->height; + if (! object->layers().empty()) + object_bottom_z = object->layers().front()->print_z - object->layers().front()->height; } this->initialize_layers(zs); } // Collect extruders reuqired to print the layers. - for (auto object : objects) + for (auto object : print.objects()) this->collect_extruders(*object); // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); - this->fill_wipe_tower_partitions(print.config, object_bottom_z); + this->fill_wipe_tower_partitions(print.config(), object_bottom_z); this->collect_extruder_statistics(prime_multi_material); } @@ -133,13 +132,13 @@ void ToolOrdering::initialize_layers(std::vector &zs) void ToolOrdering::collect_extruders(const PrintObject &object) { // Collect the support extruders. - for (auto support_layer : object.support_layers) { + for (auto support_layer : object.support_layers()) { LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z); ExtrusionRole role = support_layer->support_fills.role(); bool has_support = role == erMixed || role == erSupportMaterial; bool has_interface = role == erMixed || role == erSupportMaterialInterface; - unsigned int extruder_support = object.config.support_material_extruder.value; - unsigned int extruder_interface = object.config.support_material_interface_extruder.value; + unsigned int extruder_support = object.config().support_material_extruder.value; + unsigned int extruder_interface = object.config().support_material_interface_extruder.value; if (has_support) layer_tools.extruders.push_back(extruder_support); if (has_interface) @@ -148,14 +147,14 @@ void ToolOrdering::collect_extruders(const PrintObject &object) layer_tools.has_support = true; } // Collect the object extruders. - for (auto layer : object.layers) { + for (auto layer : object.layers()) { LayerTools &layer_tools = this->tools_for_layer(layer->print_z); // What extruders are required to print this object layer? - for (size_t region_id = 0; region_id < object.print()->regions.size(); ++ region_id) { - const LayerRegion *layerm = (region_id < layer->regions.size()) ? layer->regions[region_id] : nullptr; + for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) { + const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; if (layerm == nullptr) continue; - const PrintRegion ®ion = *object.print()->regions[region_id]; + const PrintRegion ®ion = *object.print()->regions()[region_id]; if (! layerm->perimeters.entities.empty()) { bool something_nonoverriddable = true; @@ -170,7 +169,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object) } if (something_nonoverriddable) - layer_tools.extruders.push_back(region.config.perimeter_extruder.value); + layer_tools.extruders.push_back(region.config().perimeter_extruder.value); layer_tools.has_object = true; } @@ -197,9 +196,9 @@ void ToolOrdering::collect_extruders(const PrintObject &object) if (something_nonoverriddable || !m_print_config_ptr) { if (has_solid_infill) - layer_tools.extruders.push_back(region.config.solid_infill_extruder); + layer_tools.extruders.push_back(region.config().solid_infill_extruder); if (has_infill) - layer_tools.extruders.push_back(region.config.infill_extruder); + layer_tools.extruders.push_back(region.config().infill_extruder); } if (has_solid_infill || has_infill) layer_tools.has_object = true; @@ -430,10 +429,10 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region))) return false; - if (object.config.wipe_into_objects) + if (object.config().wipe_into_objects) return true; - if (!region.config.wipe_into_infill || eec.role() != erInternalInfill) + if (!region.config().wipe_into_infill || eec.role() != erInternalInfill) return false; return true; @@ -447,12 +446,12 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int const LayerTools& lt = *m_layer_tools; const float min_infill_volume = 0.f; // ignore infill with smaller volume than this - if (print.config.filament_soluble.get_at(old_extruder) || print.config.filament_soluble.get_at(new_extruder)) + if (print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder)) return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it // we will sort objects so that dedicated for wiping are at the beginning: - PrintObjectPtrs object_list = print.get_printable_objects(); - std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; }); + PrintObjectPtrs object_list = print.objects(); + std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config().wipe_into_objects; }); // We will now iterate through // - first the dedicated objects to mark perimeters or infills (depending on infill_first) @@ -462,7 +461,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int bool perimeters_done = false; for (int i=0 ; i<(int)object_list.size() + (perimeters_done ? 0 : 1); ++i) { - if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list + if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config().wipe_into_objects)) { // we passed the last dedicated object in list perimeters_done = true; i=-1; // let's go from the start again continue; @@ -471,26 +470,26 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int const auto& object = object_list[i]; // Finds this layer: - auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)layers.end()) + auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)layers().end()) continue; const Layer* this_layer = *this_layer_it; - unsigned int num_of_copies = object->_shifted_copies.size(); + unsigned int num_of_copies = object->copies().size(); for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves - for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) { - const auto& region = *object->print()->regions[region_id]; + for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { + const auto& region = *object->print()->regions()[region_id]; - if (!region.config.wipe_into_infill && !object->config.wipe_into_objects) + if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) continue; - if ((!print.config.infill_first ? perimeters_done : !perimeters_done) || (!object->config.wipe_into_objects && region.config.wipe_into_infill)) { - for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections + if ((!print.config().infill_first ? perimeters_done : !perimeters_done) || (!object->config().wipe_into_objects && region.config().wipe_into_infill)) { + for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections auto* fill = dynamic_cast(ee); - if (!is_overriddable(*fill, print.config, *object, region)) + if (!is_overriddable(*fill, print.config(), *object, region)) continue; // What extruder would this normally be printed with? @@ -499,10 +498,10 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int if (volume_to_wipe<=0) continue; - if (!object->config.wipe_into_objects && !print.config.infill_first && region.config.wipe_into_infill) + if (!object->config().wipe_into_objects && !print.config().infill_first && region.config().wipe_into_infill) // In this case we must check that the original extruder is used on this layer before the one we are overridding // (and the perimeters will be finished before the infill is printed): - if (!lt.is_extruder_order(region.config.perimeter_extruder - 1, new_extruder)) + if (!lt.is_extruder_order(region.config().perimeter_extruder - 1, new_extruder)) continue; if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder @@ -513,11 +512,11 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int } // Now the same for perimeters - see comments above for explanation: - if (object->config.wipe_into_objects && (print.config.infill_first ? perimeters_done : !perimeters_done)) + if (object->config().wipe_into_objects && (print.config().infill_first ? perimeters_done : !perimeters_done)) { - for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) { + for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { auto* fill = dynamic_cast(ee); - if (!is_overriddable(*fill, print.config, *object, region)) + if (!is_overriddable(*fill, print.config(), *object, region)) continue; if (volume_to_wipe<=0) @@ -544,29 +543,28 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) { const LayerTools& lt = *m_layer_tools; - unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config); - unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config); + unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config()); + unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config()); - PrintObjectPtrs printable_objects = print.get_printable_objects(); - for (const PrintObject* object : printable_objects) { + for (const PrintObject* object : print.objects()) { // Finds this layer: - auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)layers.end()) + auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)layers().end()) continue; const Layer* this_layer = *this_layer_it; - unsigned int num_of_copies = object->_shifted_copies.size(); + unsigned int num_of_copies = object->copies().size(); for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves - for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) { - const auto& region = *object->print()->regions[region_id]; + for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { + const auto& region = *object->print()->regions()[region_id]; - if (!region.config.wipe_into_infill && !object->config.wipe_into_objects) + if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) continue; - for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections + for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections auto* fill = dynamic_cast(ee); - if (!is_overriddable(*fill, print.config, *object, region) + if (!is_overriddable(*fill, print.config(), *object, region) || is_entity_overridden(fill, copy) ) continue; @@ -574,12 +572,12 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) // printed before its perimeter, or not be printed at all (in case its original extruder has // not been added to LayerTools // Either way, we will now force-override it with something suitable: - if (print.config.infill_first - || object->config.wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely - || lt.is_extruder_order(region.config.perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints - || std::find(lt.extruders.begin(), lt.extruders.end(), region.config.infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME) + if (print.config().infill_first + || object->config().wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely + || lt.is_extruder_order(region.config().perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints + || std::find(lt.extruders.begin(), lt.extruders.end(), region.config().infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME) ) - set_extruder_override(fill, copy, (print.config.infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies); + set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies); else { // In this case we can (and should) leave it to be printed normally. // Force overriding would mean it gets printed before its perimeter. @@ -587,13 +585,13 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) } // Now the same for perimeters - see comments above for explanation: - for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) { // iterate through all perimeter Collections + for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections auto* fill = dynamic_cast(ee); - if (!is_overriddable(*fill, print.config, *object, region) + if (!is_overriddable(*fill, print.config(), *object, region) || is_entity_overridden(fill, copy) ) continue; - set_extruder_override(fill, copy, (print.config.infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies); + set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies); } } } diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp similarity index 100% rename from xs/src/libslic3r/GCode/ToolOrdering.hpp rename to src/libslic3r/GCode/ToolOrdering.hpp diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp similarity index 99% rename from xs/src/libslic3r/GCode/WipeTower.hpp rename to src/libslic3r/GCode/WipeTower.hpp index 21c10969a0..8ea3abd93f 100644 --- a/xs/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_WipeTower_hpp_ #define slic3r_WipeTower_hpp_ +#include #include #include #include @@ -30,10 +31,9 @@ public: xy out(0,0); float temp_x = x - width / 2.f; float temp_y = y - depth / 2.f; - angle *= M_PI/180.; + angle *= float(M_PI/180.); out.x += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f; out.y += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f; - return out; } diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp similarity index 99% rename from xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp rename to src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 3ef1418291..54bdfdfd66 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -643,8 +643,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo "\n\n"); // Ask our writer about how much material was consumed: - if (m_current_tool < m_used_filament_length.size()) - m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); ToolChangeResult result; result.priming = false; @@ -1069,9 +1068,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() m_depth_traversed = m_wipe_tower_depth-m_perimeter_width; - // Ask our writer about how much material was consumed. - if (m_current_tool < m_used_filament_length.size()) - m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + // Ask our writer about how much material was consumed: + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); ToolChangeResult result; result.priming = false; diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp similarity index 100% rename from xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp rename to src/libslic3r/GCode/WipeTowerPrusaMM.hpp diff --git a/xs/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp similarity index 98% rename from xs/src/libslic3r/GCodeReader.cpp rename to src/libslic3r/GCodeReader.cpp index 79b6ed9707..51853e9fa2 100644 --- a/xs/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -101,7 +101,7 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pairm_position[i] = gline.value(Axis(i)); + m_position[i] = gline.value(Axis(i)); } } } diff --git a/xs/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp similarity index 100% rename from xs/src/libslic3r/GCodeReader.hpp rename to src/libslic3r/GCodeReader.hpp diff --git a/xs/src/libslic3r/GCodeSender.cpp b/src/libslic3r/GCodeSender.cpp similarity index 100% rename from xs/src/libslic3r/GCodeSender.cpp rename to src/libslic3r/GCodeSender.cpp diff --git a/xs/src/libslic3r/GCodeSender.hpp b/src/libslic3r/GCodeSender.hpp similarity index 100% rename from xs/src/libslic3r/GCodeSender.hpp rename to src/libslic3r/GCodeSender.hpp diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp similarity index 99% rename from xs/src/libslic3r/GCodeTimeEstimator.cpp rename to src/libslic3r/GCodeTimeEstimator.cpp index 7471367fe5..4bfd5d63fe 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -1,4 +1,5 @@ #include "GCodeTimeEstimator.hpp" +#include "Utils.hpp" #include #include @@ -367,8 +368,7 @@ namespace Slic3r { fclose(out); in.close(); - boost::nowide::remove(filename.c_str()); - if (boost::nowide::rename(path_tmp.c_str(), filename.c_str()) != 0) + if (rename_file(path_tmp, filename) != 0) throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + "Is " + path_tmp + " locked?" + '\n'); @@ -484,14 +484,14 @@ namespace Slic3r { { _state.filament_load_times.clear(); for (double t : filament_load_times) - _state.filament_load_times.push_back(t); + _state.filament_load_times.push_back((float)t); } void GCodeTimeEstimator::set_filament_unload_times(const std::vector &filament_unload_times) { _state.filament_unload_times.clear(); for (double t : filament_unload_times) - _state.filament_unload_times.push_back(t); + _state.filament_unload_times.push_back((float)t); } float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder) @@ -731,7 +731,7 @@ namespace Slic3r { #endif // ENABLE_MOVE_STATS } - _last_st_synchronized_block_id = _blocks.size() - 1; + _last_st_synchronized_block_id = (int)_blocks.size() - 1; // The additional time has been consumed (added to the total time), reset it to zero. set_additional_time(0.); } diff --git a/xs/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp similarity index 100% rename from xs/src/libslic3r/GCodeTimeEstimator.hpp rename to src/libslic3r/GCodeTimeEstimator.hpp diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp similarity index 91% rename from xs/src/libslic3r/GCodeWriter.cpp rename to src/libslic3r/GCodeWriter.cpp index 34e6b7ec3d..6ef17f4f41 100644 --- a/xs/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -276,30 +276,30 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s return gcode.str(); } -std::string GCodeWriter::travel_to_xy(const Pointf &point, const std::string &comment) +std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment) { - m_pos.x = point.x; - m_pos.y = point.y; + m_pos(0) = point(0); + m_pos(1) = point(1); std::ostringstream gcode; - gcode << "G1 X" << XYZF_NUM(point.x) - << " Y" << XYZF_NUM(point.y) + gcode << "G1 X" << XYZF_NUM(point(0)) + << " Y" << XYZF_NUM(point(1)) << " F" << XYZF_NUM(this->config.travel_speed.value * 60.0); COMMENT(comment); gcode << "\n"; return gcode.str(); } -std::string GCodeWriter::travel_to_xyz(const Pointf3 &point, const std::string &comment) +std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment) { /* If target Z is lower than current Z but higher than nominal Z we don't perform the Z move but we only move in the XY plane and adjust the nominal Z by reducing the lift amount that will be used for unlift. */ - if (!this->will_move_z(point.z)) { - double nominal_z = m_pos.z - m_lifted; - m_lifted = m_lifted - (point.z - nominal_z); - return this->travel_to_xy(point); + if (!this->will_move_z(point(2))) { + double nominal_z = m_pos(2) - m_lifted; + m_lifted = m_lifted - (point(2) - nominal_z); + return this->travel_to_xy(to_2d(point)); } /* In all the other cases, we perform an actual XYZ move and cancel @@ -308,9 +308,9 @@ std::string GCodeWriter::travel_to_xyz(const Pointf3 &point, const std::string & m_pos = point; std::ostringstream gcode; - gcode << "G1 X" << XYZF_NUM(point.x) - << " Y" << XYZF_NUM(point.y) - << " Z" << XYZF_NUM(point.z) + gcode << "G1 X" << XYZF_NUM(point(0)) + << " Y" << XYZF_NUM(point(1)) + << " Z" << XYZF_NUM(point(2)) << " F" << XYZF_NUM(this->config.travel_speed.value * 60.0); COMMENT(comment); gcode << "\n"; @@ -323,7 +323,7 @@ std::string GCodeWriter::travel_to_z(double z, const std::string &comment) we don't perform the move but we only adjust the nominal Z by reducing the lift amount that will be used for unlift. */ if (!this->will_move_z(z)) { - double nominal_z = m_pos.z - m_lifted; + double nominal_z = m_pos(2) - m_lifted; m_lifted = m_lifted - (z - nominal_z); return ""; } @@ -336,7 +336,7 @@ std::string GCodeWriter::travel_to_z(double z, const std::string &comment) std::string GCodeWriter::_travel_to_z(double z, const std::string &comment) { - m_pos.z = z; + m_pos(2) = z; std::ostringstream gcode; gcode << "G1 Z" << XYZF_NUM(z) @@ -351,38 +351,38 @@ bool GCodeWriter::will_move_z(double z) const /* If target Z is lower than current Z but higher than nominal Z we don't perform an actual Z move. */ if (m_lifted > 0) { - double nominal_z = m_pos.z - m_lifted; - if (z >= nominal_z && z <= m_pos.z) + double nominal_z = m_pos(2) - m_lifted; + if (z >= nominal_z && z <= m_pos(2)) return false; } return true; } -std::string GCodeWriter::extrude_to_xy(const Pointf &point, double dE, const std::string &comment) +std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment) { - m_pos.x = point.x; - m_pos.y = point.y; + m_pos(0) = point(0); + m_pos(1) = point(1); m_extruder->extrude(dE); std::ostringstream gcode; - gcode << "G1 X" << XYZF_NUM(point.x) - << " Y" << XYZF_NUM(point.y) + gcode << "G1 X" << XYZF_NUM(point(0)) + << " Y" << XYZF_NUM(point(1)) << " " << m_extrusion_axis << E_NUM(m_extruder->E()); COMMENT(comment); gcode << "\n"; return gcode.str(); } -std::string GCodeWriter::extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment) +std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment) { m_pos = point; m_lifted = 0; m_extruder->extrude(dE); std::ostringstream gcode; - gcode << "G1 X" << XYZF_NUM(point.x) - << " Y" << XYZF_NUM(point.y) - << " Z" << XYZF_NUM(point.z) + gcode << "G1 X" << XYZF_NUM(point(0)) + << " Y" << XYZF_NUM(point(1)) + << " Z" << XYZF_NUM(point(2)) << " " << m_extrusion_axis << E_NUM(m_extruder->E()); COMMENT(comment); gcode << "\n"; @@ -486,12 +486,12 @@ std::string GCodeWriter::lift() { double above = this->config.retract_lift_above.get_at(m_extruder->id()); double below = this->config.retract_lift_below.get_at(m_extruder->id()); - if (m_pos.z >= above && (below == 0 || m_pos.z <= below)) + if (m_pos(2) >= above && (below == 0 || m_pos(2) <= below)) target_lift = this->config.retract_lift.get_at(m_extruder->id()); } if (m_lifted == 0 && target_lift > 0) { m_lifted = target_lift; - return this->_travel_to_z(m_pos.z + target_lift, "lift Z"); + return this->_travel_to_z(m_pos(2) + target_lift, "lift Z"); } return ""; } @@ -500,7 +500,7 @@ std::string GCodeWriter::unlift() { std::string gcode; if (m_lifted > 0) { - gcode += this->_travel_to_z(m_pos.z - m_lifted, "restore layer Z"); + gcode += this->_travel_to_z(m_pos(2) - m_lifted, "restore layer Z"); m_lifted = 0; } return gcode; diff --git a/xs/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp similarity index 90% rename from xs/src/libslic3r/GCodeWriter.hpp rename to src/libslic3r/GCodeWriter.hpp index f706b87689..664b0e3a1d 100644 --- a/xs/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -55,18 +55,18 @@ public: std::string toolchange_prefix() const; std::string toolchange(unsigned int extruder_id); std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const; - std::string travel_to_xy(const Pointf &point, const std::string &comment = std::string()); - std::string travel_to_xyz(const Pointf3 &point, const std::string &comment = std::string()); + std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string()); + std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string()); std::string travel_to_z(double z, const std::string &comment = std::string()); bool will_move_z(double z) const; - std::string extrude_to_xy(const Pointf &point, double dE, const std::string &comment = std::string()); - std::string extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment = std::string()); + std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string()); + std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string()); std::string retract(bool before_wipe = false); std::string retract_for_toolchange(bool before_wipe = false); std::string unretract(); std::string lift(); std::string unlift(); - Pointf3 get_position() const { return m_pos; } + Vec3d get_position() const { return m_pos; } private: std::vector m_extruders; @@ -81,7 +81,7 @@ private: unsigned int m_last_bed_temperature; bool m_last_bed_temperature_reached; double m_lifted; - Pointf3 m_pos; + Vec3d m_pos = Vec3d::Zero(); std::string _travel_to_z(double z, const std::string &comment); std::string _retract(double length, double restart_extra, const std::string &comment); diff --git a/xs/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp similarity index 75% rename from xs/src/libslic3r/Geometry.cpp rename to src/libslic3r/Geometry.cpp index aaf0352c99..256f48e85c 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1,3 +1,4 @@ +#include "libslic3r.h" #include "Geometry.hpp" #include "ClipperUtils.hpp" #include "ExPolygon.hpp" @@ -195,26 +196,29 @@ using namespace boost::polygon; // provides also high() and low() namespace Slic3r { namespace Geometry { -struct SortPoints { - template - bool operator()(const T& a, const T& b) const { - return (b.x > a.x) || (a.x == b.x && b.y > a.y); - } -}; +static bool sort_points(const Point& a, const Point& b) +{ + return (a(0) < b(0)) || (a(0) == b(0) && a(1) < b(1)); +} + +static bool sort_pointfs(const Vec3d& a, const Vec3d& b) +{ + return (a(0) < b(0)) || (a(0) == b(0) && a(1) < b(1)); +} // This implementation is based on Andrew's monotone chain 2D convex hull algorithm -template -static T raw_convex_hull(T& points) +Polygon +convex_hull(Points points) { assert(points.size() >= 3); // sort input points - std::sort(points.begin(), points.end(), SortPoints()); - + std::sort(points.begin(), points.end(), sort_points); + int n = points.size(), k = 0; - T hull; + Polygon hull; if (n >= 3) { - hull.resize(2*n); + hull.points.resize(2 * n); // Build lower hull for (int i = 0; i < n; i++) { @@ -228,10 +232,10 @@ static T raw_convex_hull(T& points) hull[k++] = points[i]; } - hull.resize(k); - - assert( hull.front().coincides_with(hull.back()) ); - hull.pop_back(); + hull.points.resize(k); + + assert(hull.points.front() == hull.points.back()); + hull.points.pop_back(); } return hull; @@ -240,14 +244,59 @@ static T raw_convex_hull(T& points) Pointf3s convex_hull(Pointf3s points) { - return raw_convex_hull(points); -} + assert(points.size() >= 3); + // sort input points + std::sort(points.begin(), points.end(), sort_pointfs); + + int n = points.size(), k = 0; + Pointf3s hull; + + if (n >= 3) + { + hull.resize(2 * n); + + // Build lower hull + for (int i = 0; i < n; ++i) + { + Point p = Point::new_scale(points[i](0), points[i](1)); + while (k >= 2) + { + Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1)); + Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1)); + + if (p.ccw(k2, k1) <= 0) + --k; + else + break; + } + + hull[k++] = points[i]; + } + + // Build upper hull + for (int i = n - 2, t = k + 1; i >= 0; --i) + { + Point p = Point::new_scale(points[i](0), points[i](1)); + while (k >= t) + { + Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1)); + Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1)); + + if (p.ccw(k2, k1) <= 0) + --k; + else + break; + } + + hull[k++] = points[i]; + } + + hull.resize(k); + + assert(hull.front() == hull.back()); + hull.pop_back(); + } -Polygon -convex_hull(Points points) -{ - Polygon hull; - hull.points = raw_convex_hull(points); return hull; } @@ -321,12 +370,6 @@ contains(const std::vector &vector, const Point &point) } template bool contains(const ExPolygons &vector, const Point &point); -double -rad2deg(double angle) -{ - return angle / PI * 180.0; -} - double rad2deg_dir(double angle) { @@ -360,54 +403,54 @@ linint(double value, double oldmin, double oldmax, double newmin, double newmax) // If the points have the same weight, sort them lexicographically by their positions. struct ArrangeItem { ArrangeItem() {} - Pointf pos; + Vec2d pos; coordf_t weight; bool operator<(const ArrangeItem &other) const { return weight < other.weight || - ((weight == other.weight) && (pos.y < other.pos.y || (pos.y == other.pos.y && pos.x < other.pos.x))); + ((weight == other.weight) && (pos(1) < other.pos(1) || (pos(1) == other.pos(1) && pos(0) < other.pos(0)))); } }; -Pointfs arrange(size_t num_parts, const Pointf &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box) +Pointfs arrange(size_t num_parts, const Vec2d &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box) { // Use actual part size (the largest) plus separation distance (half on each side) in spacing algorithm. - const Pointf cell_size(part_size.x + gap, part_size.y + gap); + const Vec2d cell_size(part_size(0) + gap, part_size(1) + gap); const BoundingBoxf bed_bbox = (bed_bounding_box != NULL && bed_bounding_box->defined) ? *bed_bounding_box : // Bogus bed size, large enough not to trigger the unsufficient bed size error. BoundingBoxf( - Pointf(0, 0), - Pointf(cell_size.x * num_parts, cell_size.y * num_parts)); + Vec2d(0, 0), + Vec2d(cell_size(0) * num_parts, cell_size(1) * num_parts)); // This is how many cells we have available into which to put parts. - size_t cellw = size_t(floor((bed_bbox.size().x + gap) / cell_size.x)); - size_t cellh = size_t(floor((bed_bbox.size().y + gap) / cell_size.y)); + size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0))); + size_t cellh = size_t(floor((bed_bbox.size()(1) + gap) / cell_size(1))); if (num_parts > cellw * cellh) - CONFESS(PRINTF_ZU " parts won't fit in your print area!\n", num_parts); + throw std::invalid_argument(PRINTF_ZU " parts won't fit in your print area!\n", num_parts); // Get a bounding box of cellw x cellh cells, centered at the center of the bed. - Pointf cells_size(cellw * cell_size.x - gap, cellh * cell_size.y - gap); - Pointf cells_offset(bed_bbox.center() - 0.5 * cells_size); + Vec2d cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap); + Vec2d cells_offset(bed_bbox.center() - 0.5 * cells_size); BoundingBoxf cells_bb(cells_offset, cells_size + cells_offset); // List of cells, sorted by distance from center. std::vector cellsorder(cellw * cellh, ArrangeItem()); for (size_t j = 0; j < cellh; ++ j) { // Center of the jth row on the bed. - coordf_t cy = linint(j + 0.5, 0., double(cellh), cells_bb.min.y, cells_bb.max.y); + coordf_t cy = linint(j + 0.5, 0., double(cellh), cells_bb.min(1), cells_bb.max(1)); // Offset from the bed center. - coordf_t yd = cells_bb.center().y - cy; + coordf_t yd = cells_bb.center()(1) - cy; for (size_t i = 0; i < cellw; ++ i) { // Center of the ith column on the bed. - coordf_t cx = linint(i + 0.5, 0., double(cellw), cells_bb.min.x, cells_bb.max.x); + coordf_t cx = linint(i + 0.5, 0., double(cellw), cells_bb.min(0), cells_bb.max(0)); // Offset from the bed center. - coordf_t xd = cells_bb.center().x - cx; + coordf_t xd = cells_bb.center()(0) - cx; // Cell with a distance from the bed center. ArrangeItem &ci = cellsorder[j * cellw + i]; // Cell center - ci.pos.x = cx; - ci.pos.y = cy; + ci.pos(0) = cx; + ci.pos(1) = cy; // Square distance of the cell center to the bed center. ci.weight = xd * xd + yd * yd; } @@ -420,61 +463,61 @@ Pointfs arrange(size_t num_parts, const Pointf &part_size, coordf_t gap, const B Pointfs positions; positions.reserve(num_parts); for (std::vector::const_iterator it = cellsorder.begin(); it != cellsorder.end(); ++ it) - positions.push_back(Pointf(it->pos.x - 0.5 * part_size.x, it->pos.y - 0.5 * part_size.y)); + positions.push_back(Vec2d(it->pos(0) - 0.5 * part_size(0), it->pos(1) - 0.5 * part_size(1))); return positions; } #else class ArrangeItem { - public: - Pointf pos; +public: + Vec2d pos = Vec2d::Zero(); size_t index_x, index_y; coordf_t dist; }; class ArrangeItemIndex { - public: +public: coordf_t index; ArrangeItem item; ArrangeItemIndex(coordf_t _index, ArrangeItem _item) : index(_index), item(_item) {}; }; bool -arrange(size_t total_parts, const Pointf &part_size, coordf_t dist, const BoundingBoxf* bb, Pointfs &positions) +arrange(size_t total_parts, const Vec2d &part_size, coordf_t dist, const BoundingBoxf* bb, Pointfs &positions) { positions.clear(); - Pointf part = part_size; + Vec2d part = part_size; // use actual part size (the largest) plus separation distance (half on each side) in spacing algorithm - part.x += dist; - part.y += dist; + part(0) += dist; + part(1) += dist; - Pointf area; + Vec2d area(Vec2d::Zero()); if (bb != NULL && bb->defined) { area = bb->size(); } else { // bogus area size, large enough not to trigger the error below - area.x = part.x * total_parts; - area.y = part.y * total_parts; + area(0) = part(0) * total_parts; + area(1) = part(1) * total_parts; } // this is how many cells we have available into which to put parts - size_t cellw = floor((area.x + dist) / part.x); - size_t cellh = floor((area.y + dist) / part.y); + size_t cellw = floor((area(0) + dist) / part(0)); + size_t cellh = floor((area(1) + dist) / part(1)); if (total_parts > (cellw * cellh)) return false; // total space used by cells - Pointf cells(cellw * part.x, cellh * part.y); + Vec2d cells(cellw * part(0), cellh * part(1)); // bounding box of total space used by cells BoundingBoxf cells_bb; - cells_bb.merge(Pointf(0,0)); // min + cells_bb.merge(Vec2d(0,0)); // min cells_bb.merge(cells); // max // center bounding box to area cells_bb.translate( - (area.x - cells.x) / 2, - (area.y - cells.y) / 2 + (area(0) - cells(0)) / 2, + (area(1) - cells(1)) / 2 ); // list of cells, sorted by distance from center @@ -483,15 +526,15 @@ arrange(size_t total_parts, const Pointf &part_size, coordf_t dist, const Boundi // work out distance for all cells, sort into list for (size_t i = 0; i <= cellw-1; ++i) { for (size_t j = 0; j <= cellh-1; ++j) { - coordf_t cx = linint(i + 0.5, 0, cellw, cells_bb.min.x, cells_bb.max.x); - coordf_t cy = linint(j + 0.5, 0, cellh, cells_bb.min.y, cells_bb.max.y); + coordf_t cx = linint(i + 0.5, 0, cellw, cells_bb.min(0), cells_bb.max(0)); + coordf_t cy = linint(j + 0.5, 0, cellh, cells_bb.min(1), cells_bb.max(1)); - coordf_t xd = fabs((area.x / 2) - cx); - coordf_t yd = fabs((area.y / 2) - cy); + coordf_t xd = fabs((area(0) / 2) - cx); + coordf_t yd = fabs((area(1) / 2) - cy); ArrangeItem c; - c.pos.x = cx; - c.pos.y = cy; + c.pos(0) = cx; + c.pos(1) = cy; c.index_x = i; c.index_y = j; c.dist = xd * xd + yd * yd - fabs((cellw / 2) - (i + 0.5)); @@ -548,13 +591,13 @@ arrange(size_t total_parts, const Pointf &part_size, coordf_t dist, const Boundi coordf_t cx = c.item.index_x - lx; coordf_t cy = c.item.index_y - ty; - positions.push_back(Pointf(cx * part.x, cy * part.y)); + positions.push_back(Vec2d(cx * part(0), cy * part(1))); } if (bb != NULL && bb->defined) { for (Pointfs::iterator p = positions.begin(); p != positions.end(); ++p) { - p->x += bb->min.x; - p->y += bb->min.y; + p->x() += bb->min(0); + p->y() += bb->min(1); } } @@ -623,15 +666,15 @@ namespace Voronoi { namespace Internal { if (cell1.contains_point() && cell2.contains_point()) { point_type p1 = retrieve_point(segments, cell1); point_type p2 = retrieve_point(segments, cell2); - origin.x((p1.x() + p2.x()) * 0.5); - origin.y((p1.y() + p2.y()) * 0.5); - direction.x(p1.y() - p2.y()); - direction.y(p2.x() - p1.x()); + origin.x((p1(0) + p2(0)) * 0.5); + origin.y((p1(1) + p2(1)) * 0.5); + direction.x(p1(1) - p2(1)); + direction.y(p2(0) - p1(0)); } else { origin = cell1.contains_segment() ? retrieve_point(segments, cell2) : retrieve_point(segments, cell1); segment_type segment = cell1.contains_segment() ? segments[cell1.source_index()] : segments[cell2.source_index()]; - coordinate_type dx = high(segment).x() - low(segment).x(); - coordinate_type dy = high(segment).y() - low(segment).y(); + coordinate_type dx = high(segment)(0) - low(segment)(0); + coordinate_type dy = high(segment)(1) - low(segment)(1); if ((low(segment) == origin) ^ cell1.contains_point()) { direction.x(dy); direction.y(-dx); @@ -640,19 +683,19 @@ namespace Voronoi { namespace Internal { direction.y(dx); } } - coordinate_type koef = bbox_max_size / (std::max)(fabs(direction.x()), fabs(direction.y())); + coordinate_type koef = bbox_max_size / (std::max)(fabs(direction(0)), fabs(direction(1))); if (edge.vertex0() == NULL) { clipped_edge->push_back(point_type( - origin.x() - direction.x() * koef, - origin.y() - direction.y() * koef)); + origin(0) - direction(0) * koef, + origin(1) - direction(1) * koef)); } else { clipped_edge->push_back( point_type(edge.vertex0()->x(), edge.vertex0()->y())); } if (edge.vertex1() == NULL) { clipped_edge->push_back(point_type( - origin.x() + direction.x() * koef, - origin.y() + direction.y() * koef)); + origin(0) + direction(0) * koef, + origin(1) + direction(1) * koef)); } else { clipped_edge->push_back( point_type(edge.vertex1()->x(), edge.vertex1()->y())); @@ -691,10 +734,10 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d const bool primaryEdgesOnly = false; BoundingBox bbox = BoundingBox(lines); - bbox.min.x -= coord_t(1. / SCALING_FACTOR); - bbox.min.y -= coord_t(1. / SCALING_FACTOR); - bbox.max.x += coord_t(1. / SCALING_FACTOR); - bbox.max.y += coord_t(1. / SCALING_FACTOR); + bbox.min(0) -= coord_t(1. / SCALING_FACTOR); + bbox.min(1) -= coord_t(1. / SCALING_FACTOR); + bbox.max(0) += coord_t(1. / SCALING_FACTOR); + bbox.max(1) += coord_t(1. / SCALING_FACTOR); ::Slic3r::SVG svg(path, bbox); @@ -704,7 +747,7 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d // bbox.scale(1.2); // For clipping of half-lines to some reasonable value. // The line will then be clipped by the SVG viewer anyway. - const double bbox_dim_max = double(bbox.max.x - bbox.min.x) + double(bbox.max.y - bbox.min.y); + const double bbox_dim_max = double(bbox.max(0) - bbox.min(0)) + double(bbox.max(1) - bbox.min(1)); // For the discretization of the Voronoi parabolic segments. const double discretization_step = 0.0005 * bbox_dim_max; @@ -712,8 +755,8 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d std::vector segments; for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it) segments.push_back(Voronoi::Internal::segment_type( - Voronoi::Internal::point_type(double(it->a.x), double(it->a.y)), - Voronoi::Internal::point_type(double(it->b.x), double(it->b.y)))); + Voronoi::Internal::point_type(double(it->a(0)), double(it->a(1))), + Voronoi::Internal::point_type(double(it->b(0)), double(it->b(1))))); // Color exterior edges. for (voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) @@ -727,13 +770,13 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d } // Draw the input polygon. for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) - svg.draw(Line(Point(coord_t(it->a.x), coord_t(it->a.y)), Point(coord_t(it->b.x), coord_t(it->b.y))), inputSegmentColor, inputSegmentLineWidth); + svg.draw(Line(Point(coord_t(it->a(0)), coord_t(it->a(1))), Point(coord_t(it->b(0)), coord_t(it->b(1)))), inputSegmentColor, inputSegmentLineWidth); #if 1 // Draw voronoi vertices. for (voronoi_diagram::const_vertex_iterator it = vd.vertices().begin(); it != vd.vertices().end(); ++it) if (! internalEdgesOnly || it->color() != Voronoi::Internal::EXTERNAL_COLOR) - svg.draw(Point(coord_t(it->x()), coord_t(it->y())), voronoiPointColor, voronoiPointRadius); + svg.draw(Point(coord_t((*it)(0)), coord_t((*it)(1))), voronoiPointColor, voronoiPointRadius); for (voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) { if (primaryEdgesOnly && !it->is_primary()) @@ -758,7 +801,7 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d color = voronoiLineColorSecondary; } for (std::size_t i = 0; i + 1 < samples.size(); ++i) - svg.draw(Line(Point(coord_t(samples[i].x()), coord_t(samples[i].y())), Point(coord_t(samples[i+1].x()), coord_t(samples[i+1].y()))), color, voronoiLineWidth); + svg.draw(Line(Point(coord_t(samples[i](0)), coord_t(samples[i](1))), Point(coord_t(samples[i+1](0)), coord_t(samples[i+1](1)))), color, voronoiLineWidth); } #endif @@ -773,8 +816,8 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d template T dist(const boost::polygon::point_data &p1,const boost::polygon::point_data &p2) { - T dx = p2.x() - p1.x(); - T dy = p2.y() - p1.y(); + T dx = p2(0) - p1(0); + T dy = p2(1) - p1(1); return sqrt(dx*dx+dy*dy); } @@ -785,11 +828,11 @@ inline point_type project_point_to_segment(segment_type &seg, point_type &px) typedef typename point_type::coordinate_type T; const point_type &p0 = low(seg); const point_type &p1 = high(seg); - const point_type dir(p1.x()-p0.x(), p1.y()-p0.y()); - const point_type dproj(px.x()-p0.x(), px.y()-p0.y()); - const T t = (dir.x()*dproj.x() + dir.y()*dproj.y()) / (dir.x()*dir.x() + dir.y()*dir.y()); + const point_type dir(p1(0)-p0(0), p1(1)-p0(1)); + const point_type dproj(px(0)-p0(0), px(1)-p0(1)); + const T t = (dir(0)*dproj(0) + dir(1)*dproj(1)) / (dir(0)*dir(0) + dir(1)*dir(1)); assert(t >= T(-1e-6) && t <= T(1. + 1e-6)); - return point_type(p0.x() + t*dir.x(), p0.y() + t*dir.y()); + return point_type(p0(0) + t*dir(0), p0(1) + t*dir(1)); } template @@ -843,8 +886,8 @@ public: Lines2VDSegments(const Lines &alines) : lines(alines) {} typename VD::segment_type operator[](size_t idx) const { return typename VD::segment_type( - typename VD::point_type(typename VD::coord_type(lines[idx].a.x), typename VD::coord_type(lines[idx].a.y)), - typename VD::point_type(typename VD::coord_type(lines[idx].b.x), typename VD::coord_type(lines[idx].b.y))); + typename VD::point_type(typename VD::coord_type(lines[idx].a(0)), typename VD::coord_type(lines[idx].a(1))), + typename VD::point_type(typename VD::coord_type(lines[idx].b(0)), typename VD::coord_type(lines[idx].b(1)))); } private: const Lines &lines; @@ -925,7 +968,7 @@ MedialAxis::build(ThickPolylines* polylines) assert(polyline.width.size() == polyline.points.size()*2 - 2); // prevent loop endpoints from being extended - if (polyline.first_point().coincides_with(polyline.last_point())) { + if (polyline.first_point() == polyline.last_point()) { polyline.endpoints.first = false; polyline.endpoints.second = false; } @@ -1018,7 +1061,7 @@ MedialAxis::validate_edge(const VD::edge_type* edge) // this could maybe be optimized (checking inclusion of the endpoints // might give false positives as they might belong to the contour itself) if (this->expolygon != NULL) { - if (line.a.coincides_with(line.b)) { + if (line.a == line.b) { // in this case, contains(line) returns a false positive if (!this->expolygon->contains(line.a)) return false; } else { @@ -1057,12 +1100,12 @@ MedialAxis::validate_edge(const VD::edge_type* edge) calculate the distance to that endpoint instead. */ coordf_t w0 = cell_r->contains_segment() - ? line.a.distance_to(segment_r)*2 - : line.a.distance_to(this->retrieve_endpoint(cell_r))*2; + ? segment_r.distance_to(line.a)*2 + : (this->retrieve_endpoint(cell_r) - line.a).cast().norm()*2; coordf_t w1 = cell_l->contains_segment() - ? line.b.distance_to(segment_l)*2 - : line.b.distance_to(this->retrieve_endpoint(cell_l))*2; + ? segment_l.distance_to(line.b)*2 + : (this->retrieve_endpoint(cell_l) - line.b).cast().norm()*2; if (cell_l->contains_segment() && cell_r->contains_segment()) { // calculate the relative angle between the two boundary segments @@ -1118,4 +1161,228 @@ MedialAxis::retrieve_endpoint(const VD::cell_type* cell) const } } +void assemble_transform(Transform3d& transform, const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale, const Vec3d& mirror) +{ + transform = Transform3d::Identity(); + transform.translate(translation); + transform.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ())); + transform.rotate(Eigen::AngleAxisd(rotation(1), Vec3d::UnitY())); + transform.rotate(Eigen::AngleAxisd(rotation(0), Vec3d::UnitX())); + transform.scale(scale); + transform.scale(mirror); +} + +Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale, const Vec3d& mirror) +{ + Transform3d transform; + assemble_transform(transform, translation, rotation, scale, mirror); + return transform; +} + +Vec3d extract_euler_angles(const Eigen::Matrix& rotation_matrix) +{ + // see: https://www.learnopencv.com/rotation-matrix-to-euler-angles/ + double sy = ::sqrt(sqr(rotation_matrix(0, 0)) + sqr(rotation_matrix(1, 0))); + + Vec3d angles = Vec3d::Zero(); + + if (sy >= 1e-6) + { + angles(0) = ::atan2(rotation_matrix(2, 1), rotation_matrix(2, 2)); + angles(1) = ::atan2(-rotation_matrix(2, 0), sy); + angles(2) = ::atan2(rotation_matrix(1, 0), rotation_matrix(0, 0)); + } + else + { + angles(0) = ::atan2(-rotation_matrix(1, 2), rotation_matrix(1, 1)); + angles(1) = ::atan2(-rotation_matrix(2, 0), sy); + angles(2) = 0.0; + } + + return angles; +} + +Vec3d extract_euler_angles(const Transform3d& transform) +{ + // use only the non-translational part of the transform + Eigen::Matrix m = transform.matrix().block(0, 0, 3, 3); + // remove scale + m.col(0).normalize(); + m.col(1).normalize(); + m.col(2).normalize(); + return extract_euler_angles(m); +} + +#if ENABLE_MODELVOLUME_TRANSFORM +Transformation::Flags::Flags() + : dont_translate(true) + , dont_rotate(true) + , dont_scale(true) + , dont_mirror(true) +{ +} + +bool Transformation::Flags::needs_update(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const +{ + return (this->dont_translate != dont_translate) || (this->dont_rotate != dont_rotate) || (this->dont_scale != dont_scale) || (this->dont_mirror != dont_mirror); +} + +void Transformation::Flags::set(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) +{ + this->dont_translate = dont_translate; + this->dont_rotate = dont_rotate; + this->dont_scale = dont_scale; + this->dont_mirror = dont_mirror; +} + +Transformation::Transformation() + : m_offset(Vec3d::Zero()) + , m_rotation(Vec3d::Zero()) + , m_scaling_factor(Vec3d::Ones()) + , m_mirror(Vec3d::Ones()) + , m_matrix(Transform3d::Identity()) + , m_dirty(false) +{ +} + +Transformation::Transformation(const Transform3d& transform) +{ + set_from_transform(transform); +} + +void Transformation::set_offset(const Vec3d& offset) +{ + set_offset(X, offset(0)); + set_offset(Y, offset(1)); + set_offset(Z, offset(2)); +} + +void Transformation::set_offset(Axis axis, double offset) +{ + if (m_offset(axis) != offset) + { + m_offset(axis) = offset; + m_dirty = true; + } +} + +void Transformation::set_rotation(const Vec3d& rotation) +{ + set_rotation(X, rotation(0)); + set_rotation(Y, rotation(1)); + set_rotation(Z, rotation(2)); +} + +void Transformation::set_rotation(Axis axis, double rotation) +{ + rotation = angle_to_0_2PI(rotation); + + if (m_rotation(axis) != rotation) + { + m_rotation(axis) = rotation; + m_dirty = true; + } +} + +void Transformation::set_scaling_factor(const Vec3d& scaling_factor) +{ + set_scaling_factor(X, scaling_factor(0)); + set_scaling_factor(Y, scaling_factor(1)); + set_scaling_factor(Z, scaling_factor(2)); +} + +void Transformation::set_scaling_factor(Axis axis, double scaling_factor) +{ + if (m_scaling_factor(axis) != std::abs(scaling_factor)) + { + m_scaling_factor(axis) = std::abs(scaling_factor); + m_dirty = true; + } +} + +void Transformation::set_mirror(const Vec3d& mirror) +{ + set_mirror(X, mirror(0)); + set_mirror(Y, mirror(1)); + set_mirror(Z, mirror(2)); +} + +void Transformation::set_mirror(Axis axis, double mirror) +{ + double abs_mirror = std::abs(mirror); + if (abs_mirror == 0.0) + mirror = 1.0; + else if (abs_mirror != 1.0) + mirror /= abs_mirror; + + if (m_mirror(axis) != mirror) + { + m_mirror(axis) = mirror; + m_dirty = true; + } +} + +void Transformation::set_from_transform(const Transform3d& transform) +{ + // offset + set_offset(transform.matrix().block(0, 3, 3, 1)); + + Eigen::Matrix m3x3 = transform.matrix().block(0, 0, 3, 3); + + // mirror + // it is impossible to reconstruct the original mirroring factors from a matrix, + // we can only detect if the matrix contains a left handed reference system + // in which case we reorient it back to right handed by mirroring the x axis + Vec3d mirror = Vec3d::Ones(); + if (m3x3.col(0).dot(m3x3.col(1).cross(m3x3.col(2))) < 0.0) + { + mirror(0) = -1.0; + // remove mirror + m3x3.col(0) *= -1.0; + } + set_mirror(mirror); + + // scale + set_scaling_factor(Vec3d(m3x3.col(0).norm(), m3x3.col(1).norm(), m3x3.col(2).norm())); + + // remove scale + m3x3.col(0).normalize(); + m3x3.col(1).normalize(); + m3x3.col(2).normalize(); + + // rotation + set_rotation(extract_euler_angles(m3x3)); + + // forces matrix recalculation matrix + m_matrix = get_matrix(); + +// // debug check +// if (!m_matrix.isApprox(transform)) +// std::cout << "something went wrong in extracting data from matrix" << std::endl; +} + +const Transform3d& Transformation::get_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const +{ + if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror)) + { + m_matrix = Geometry::assemble_transform( + dont_translate ? Vec3d::Zero() : m_offset, + dont_rotate ? Vec3d::Zero() : m_rotation, + dont_scale ? Vec3d::Ones() : m_scaling_factor, + dont_mirror ? Vec3d::Ones() : m_mirror + ); + + m_flags.set(dont_translate, dont_rotate, dont_scale, dont_mirror); + m_dirty = false; + } + + return m_matrix; +} + +Transformation Transformation::operator * (const Transformation& other) const +{ + return Transformation(get_matrix() * other.get_matrix()); +} +#endif // ENABLE_MODELVOLUME_TRANSFORM + } } diff --git a/xs/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp similarity index 52% rename from xs/src/libslic3r/Geometry.hpp rename to src/libslic3r/Geometry.hpp index 956ef82aab..c21c3946d3 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -30,9 +30,9 @@ enum Orientation static inline Orientation orient(const Point &a, const Point &b, const Point &c) { // BOOST_STATIC_ASSERT(sizeof(coord_t) * 2 == sizeof(int64_t)); - int64_t u = int64_t(b.x) * int64_t(c.y) - int64_t(b.y) * int64_t(c.x); - int64_t v = int64_t(a.x) * int64_t(c.y) - int64_t(a.y) * int64_t(c.x); - int64_t w = int64_t(a.x) * int64_t(b.y) - int64_t(a.y) * int64_t(b.x); + int64_t u = int64_t(b(0)) * int64_t(c(1)) - int64_t(b(1)) * int64_t(c(0)); + int64_t v = int64_t(a(0)) * int64_t(c(1)) - int64_t(a(1)) * int64_t(c(0)); + int64_t w = int64_t(a(0)) * int64_t(b(1)) - int64_t(a(1)) * int64_t(b(0)); int64_t d = u - v + w; return (d > 0) ? ORIENTATION_CCW : ((d == 0) ? ORIENTATION_COLINEAR : ORIENTATION_CW); } @@ -52,7 +52,7 @@ static inline bool is_ccw(const Polygon &poly) for (unsigned int i = 1; i < poly.points.size(); ++ i) { const Point &pmin = poly.points[imin]; const Point &p = poly.points[i]; - if (p.x < pmin.x || (p.x == pmin.x && p.y < pmin.y)) + if (p(0) < pmin(0) || (p(0) == pmin(0) && p(1) < pmin(1))) imin = i; } @@ -66,26 +66,26 @@ static inline bool is_ccw(const Polygon &poly) return o == ORIENTATION_CCW; } -inline bool ray_ray_intersection(const Pointf &p1, const Vectorf &v1, const Pointf &p2, const Vectorf &v2, Pointf &res) +inline bool ray_ray_intersection(const Vec2d &p1, const Vec2d &v1, const Vec2d &p2, const Vec2d &v2, Vec2d &res) { - double denom = v1.x * v2.y - v2.x * v1.y; + double denom = v1(0) * v2(1) - v2(0) * v1(1); if (std::abs(denom) < EPSILON) return false; - double t = (v2.x * (p1.y - p2.y) - v2.y * (p1.x - p2.x)) / denom; - res.x = p1.x + t * v1.x; - res.y = p1.y + t * v1.y; + double t = (v2(0) * (p1(1) - p2(1)) - v2(1) * (p1(0) - p2(0))) / denom; + res(0) = p1(0) + t * v1(0); + res(1) = p1(1) + t * v1(1); return true; } -inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, const Pointf &p2, const Vectorf &v2, Pointf &res) +inline bool segment_segment_intersection(const Vec2d &p1, const Vec2d &v1, const Vec2d &p2, const Vec2d &v2, Vec2d &res) { - double denom = v1.x * v2.y - v2.x * v1.y; + double denom = v1(0) * v2(1) - v2(0) * v1(1); if (std::abs(denom) < EPSILON) // Lines are collinear. return false; - double s12_x = p1.x - p2.x; - double s12_y = p1.y - p2.y; - double s_numer = v1.x * s12_y - v1.y * s12_x; + double s12_x = p1(0) - p2(0); + double s12_y = p1(1) - p2(1); + double s_numer = v1(0) * s12_y - v1(1) * s12_x; bool denom_is_positive = false; if (denom < 0.) { denom_is_positive = true; @@ -95,7 +95,7 @@ inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, co if (s_numer < 0.) // Intersection outside of the 1st segment. return false; - double t_numer = v2.x * s12_y - v2.y * s12_x; + double t_numer = v2(0) * s12_y - v2(1) * s12_x; if (! denom_is_positive) t_numer = - t_numer; if (t_numer < 0. || s_numer > denom || t_numer > denom) @@ -103,8 +103,8 @@ inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, co return false; // Intersection inside both of the segments. double t = t_numer / denom; - res.x = p1.x + t * v1.x; - res.y = p1.y + t * v1.y; + res(0) = p1(0) + t * v1(0); + res(1) = p1(1) + t * v1(1); return true; } @@ -117,15 +117,29 @@ void chained_path(const Points &points, std::vector &retval); template void chained_path_items(Points &points, T &items, T &retval); bool directions_parallel(double angle1, double angle2, double max_diff = 0); template bool contains(const std::vector &vector, const Point &point); -double rad2deg(double angle); +template T rad2deg(T angle) { return T(180.0) * angle / T(PI); } double rad2deg_dir(double angle); template T deg2rad(T angle) { return T(PI) * angle / T(180.0); } +template T angle_to_0_2PI(T angle) +{ + static const T TWO_PI = T(2) * T(PI); + while (angle < T(0)) + { + angle += TWO_PI; + } + while (TWO_PI < angle) + { + angle -= TWO_PI; + } + + return angle; +} void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval); double linint(double value, double oldmin, double oldmax, double newmin, double newmax); bool arrange( // input - size_t num_parts, const Pointf &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box, + size_t num_parts, const Vec2d &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box, // output Pointfs &positions); @@ -157,6 +171,93 @@ class MedialAxis { const Point& retrieve_endpoint(const VD::cell_type* cell) const; }; +// Sets the given transform by assembling the given transformations in the following order: +// 1) mirror +// 2) scale +// 3) rotate X +// 4) rotate Y +// 5) rotate Z +// 6) translate +void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); + +// Returns the transform obtained by assembling the given transformations in the following order: +// 1) mirror +// 2) scale +// 3) rotate X +// 4) rotate Y +// 5) rotate Z +// 6) translate +Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); + +// Returns the euler angles extracted from the given rotation matrix +// Warning -> The matrix should not contain any scale or shear !!! +Vec3d extract_euler_angles(const Eigen::Matrix& rotation_matrix); + +// Returns the euler angles extracted from the given affine transform +// Warning -> The transform should not contain any shear !!! +Vec3d extract_euler_angles(const Transform3d& transform); + +#if ENABLE_MODELVOLUME_TRANSFORM +class Transformation +{ + struct Flags + { + bool dont_translate; + bool dont_rotate; + bool dont_scale; + bool dont_mirror; + + Flags(); + + bool needs_update(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const; + void set(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror); + }; + + Vec3d m_offset; // In unscaled coordinates + Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point + Vec3d m_scaling_factor; // Scaling factors along the three axes + Vec3d m_mirror; // Mirroring along the three axes + + mutable Transform3d m_matrix; + mutable Flags m_flags; + mutable bool m_dirty; + +public: + Transformation(); + explicit Transformation(const Transform3d& transform); + + const Vec3d& get_offset() const { return m_offset; } + double get_offset(Axis axis) const { return m_offset(axis); } + + void set_offset(const Vec3d& offset); + void set_offset(Axis axis, double offset); + + const Vec3d& get_rotation() const { return m_rotation; } + double get_rotation(Axis axis) const { return m_rotation(axis); } + + void set_rotation(const Vec3d& rotation); + void set_rotation(Axis axis, double rotation); + + Vec3d get_scaling_factor() const { return m_scaling_factor; } + double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); } + + void set_scaling_factor(const Vec3d& scaling_factor); + void set_scaling_factor(Axis axis, double scaling_factor); + + const Vec3d& get_mirror() const { return m_mirror; } + double get_mirror(Axis axis) const { return m_mirror(axis); } + + void set_mirror(const Vec3d& mirror); + void set_mirror(Axis axis, double mirror); + + void set_from_transform(const Transform3d& transform); + + const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const; + + Transformation operator * (const Transformation& other) const; +}; +#endif // ENABLE_MODELVOLUME_TRANSFORM + } } #endif diff --git a/xs/src/libslic3r/I18N.hpp b/src/libslic3r/I18N.hpp similarity index 100% rename from xs/src/libslic3r/I18N.hpp rename to src/libslic3r/I18N.hpp diff --git a/xs/src/libslic3r/Int128.hpp b/src/libslic3r/Int128.hpp similarity index 100% rename from xs/src/libslic3r/Int128.hpp rename to src/libslic3r/Int128.hpp diff --git a/xs/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp similarity index 68% rename from xs/src/libslic3r/Layer.cpp rename to src/libslic3r/Layer.cpp index 652bcdaa04..7878bffabb 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -12,29 +12,28 @@ namespace Slic3r { Layer::~Layer() { this->lower_layer = this->upper_layer = nullptr; - for (LayerRegion *region : this->regions) + for (LayerRegion *region : m_regions) delete region; - this->regions.clear(); + m_regions.clear(); } LayerRegion* Layer::add_region(PrintRegion* print_region) { - this->regions.emplace_back(new LayerRegion(this, print_region)); - return this->regions.back(); + m_regions.emplace_back(new LayerRegion(this, print_region)); + return m_regions.back(); } // merge all regions' slices to get islands void Layer::make_slices() { ExPolygons slices; - if (this->regions.size() == 1) { + if (m_regions.size() == 1) { // optimization: if we only have one region, take its slices - slices = this->regions.front()->slices; + slices = m_regions.front()->slices; } else { Polygons slices_p; - FOREACH_LAYERREGION(this, layerm) { - polygons_append(slices_p, to_polygons((*layerm)->slices)); - } + for (LayerRegion *layerm : m_regions) + polygons_append(slices_p, to_polygons(layerm->slices)); slices = union_ex(slices_p); } @@ -53,20 +52,19 @@ void Layer::make_slices() // populate slices vector for (size_t i : order) - this->slices.expolygons.push_back(STDMOVE(slices[i])); + this->slices.expolygons.push_back(std::move(slices[i])); } void Layer::merge_slices() { - if (this->regions.size() == 1) { + if (m_regions.size() == 1) { // Optimization, also more robust. Don't merge classified pieces of layerm->slices, // but use the non-split islands of a layer. For a single region print, these shall be equal. - this->regions.front()->slices.set(this->slices.expolygons, stInternal); + m_regions.front()->slices.set(this->slices.expolygons, stInternal); } else { - FOREACH_LAYERREGION(this, layerm) { + for (LayerRegion *layerm : m_regions) // without safety offset, artifacts are generated (GH #2494) - (*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal); - } + layerm->slices.set(union_ex(to_polygons(std::move(layerm->slices.surfaces)), true), stInternal); } } @@ -80,19 +78,19 @@ void Layer::make_perimeters() // keep track of regions whose perimeters we have already generated std::set done; - FOREACH_LAYERREGION(this, layerm) { - size_t region_id = layerm - this->regions.begin(); + for (LayerRegionPtrs::iterator layerm = m_regions.begin(); layerm != m_regions.end(); ++ layerm) { + size_t region_id = layerm - m_regions.begin(); if (done.find(region_id) != done.end()) continue; BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id; done.insert(region_id); - const PrintRegionConfig &config = (*layerm)->region()->config; + const PrintRegionConfig &config = (*layerm)->region()->config(); // find compatible regions LayerRegionPtrs layerms; layerms.push_back(*layerm); - for (LayerRegionPtrs::const_iterator it = layerm + 1; it != this->regions.end(); ++it) { + for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it) { LayerRegion* other_layerm = *it; - const PrintRegionConfig &other_config = other_layerm->region()->config; + const PrintRegionConfig &other_config = other_layerm->region()->config(); if (config.perimeter_extruder == other_config.perimeter_extruder && config.perimeters == other_config.perimeters @@ -104,7 +102,7 @@ void Layer::make_perimeters() && config.thin_walls == other_config.thin_walls && config.external_perimeters_first == other_config.external_perimeters_first) { layerms.push_back(other_layerm); - done.insert(it - this->regions.begin()); + done.insert(it - m_regions.begin()); } } @@ -137,7 +135,7 @@ void Layer::make_perimeters() // Separate the fill surfaces. ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices); (*l)->fill_expolygons = expp; - (*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front()); + (*l)->fill_surfaces.set(std::move(expp), fill_surfaces.surfaces.front()); } } } @@ -150,13 +148,12 @@ void Layer::make_fills() #ifdef SLIC3R_DEBUG printf("Making fills for layer " PRINTF_ZU "\n", this->id()); #endif - for (LayerRegionPtrs::iterator it_layerm = regions.begin(); it_layerm != regions.end(); ++ it_layerm) { - LayerRegion &layerm = *(*it_layerm); - layerm.fills.clear(); - make_fill(layerm, layerm.fills); + for (LayerRegion *layerm : m_regions) { + layerm->fills.clear(); + make_fill(*layerm, layerm->fills); #ifndef NDEBUG - for (size_t i = 0; i < layerm.fills.entities.size(); ++ i) - assert(dynamic_cast(layerm.fills.entities[i]) != NULL); + for (size_t i = 0; i < layerm->fills.entities.size(); ++ i) + assert(dynamic_cast(layerm->fills.entities[i]) != NULL); #endif } } @@ -164,18 +161,18 @@ void Layer::make_fills() void Layer::export_region_slices_to_svg(const char *path) const { BoundingBox bbox; - for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region) - for (Surfaces::const_iterator surface = (*region)->slices.surfaces.begin(); surface != (*region)->slices.surfaces.end(); ++surface) - bbox.merge(get_extents(surface->expolygon)); + for (const auto *region : m_regions) + for (const auto &surface : region->slices.surfaces) + bbox.merge(get_extents(surface.expolygon)); Point legend_size = export_surface_type_legend_to_svg_box_size(); - Point legend_pos(bbox.min.x, bbox.max.y); - bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y)); + Point legend_pos(bbox.min(0), bbox.max(1)); + bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); SVG svg(path, bbox); const float transparency = 0.5f; - for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region) - for (Surfaces::const_iterator surface = (*region)->slices.surfaces.begin(); surface != (*region)->slices.surfaces.end(); ++surface) - svg.draw(surface->expolygon, surface_type_to_color_name(surface->surface_type), transparency); + for (const auto *region : m_regions) + for (const auto &surface : region->slices.surfaces) + svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency); export_surface_type_legend_to_svg(svg, legend_pos); svg.Close(); } @@ -190,18 +187,18 @@ void Layer::export_region_slices_to_svg_debug(const char *name) const void Layer::export_region_fill_surfaces_to_svg(const char *path) const { BoundingBox bbox; - for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region) - for (Surfaces::const_iterator surface = (*region)->fill_surfaces.surfaces.begin(); surface != (*region)->fill_surfaces.surfaces.end(); ++surface) - bbox.merge(get_extents(surface->expolygon)); + for (const auto *region : m_regions) + for (const auto &surface : region->slices.surfaces) + bbox.merge(get_extents(surface.expolygon)); Point legend_size = export_surface_type_legend_to_svg_box_size(); - Point legend_pos(bbox.min.x, bbox.max.y); - bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y)); + Point legend_pos(bbox.min(0), bbox.max(1)); + bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); SVG svg(path, bbox); const float transparency = 0.5f; - for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region) - for (Surfaces::const_iterator surface = (*region)->fill_surfaces.surfaces.begin(); surface != (*region)->fill_surfaces.surfaces.end(); ++surface) - svg.draw(surface->expolygon, surface_type_to_color_name(surface->surface_type), transparency); + for (const auto *region : m_regions) + for (const auto &surface : region->slices.surfaces) + svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency); export_surface_type_legend_to_svg(svg, legend_pos); svg.Close(); } diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp new file mode 100644 index 0000000000..8dbe850ccc --- /dev/null +++ b/src/libslic3r/Layer.hpp @@ -0,0 +1,179 @@ +#ifndef slic3r_Layer_hpp_ +#define slic3r_Layer_hpp_ + +#include "libslic3r.h" +#include "Flow.hpp" +#include "SurfaceCollection.hpp" +#include "ExtrusionEntityCollection.hpp" +#include "ExPolygonCollection.hpp" +#include "PolylineCollection.hpp" + + +namespace Slic3r { + +class Layer; +class PrintRegion; +class PrintObject; + +class LayerRegion +{ +public: + Layer* layer() { return m_layer; } + const Layer* layer() const { return m_layer; } + PrintRegion* region() { return m_region; } + const PrintRegion* region() const { return m_region; } + + // collection of surfaces generated by slicing the original geometry + // divided by type top/bottom/internal + SurfaceCollection slices; + + // collection of extrusion paths/loops filling gaps + // These fills are generated by the perimeter generator. + // They are not printed on their own, but they are copied to this->fills during infill generation. + ExtrusionEntityCollection thin_fills; + + // Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature") + // and for re-starting of infills. + ExPolygons fill_expolygons; + // collection of surfaces for infill generation + SurfaceCollection fill_surfaces; + + // Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces). + // While not necessary, the memory consumption is meager and it speeds up calculation. + // The perimeter_surfaces keep the IDs of the slices (top/bottom/) + SurfaceCollection perimeter_surfaces; + + // collection of expolygons representing the bridged areas (thus not + // needing support material) + Polygons bridged; + + // collection of polylines representing the unsupported bridge edges + PolylineCollection unsupported_bridge_edges; + + // ordered collection of extrusion paths/loops to build all perimeters + // (this collection contains only ExtrusionEntityCollection objects) + ExtrusionEntityCollection perimeters; + + // ordered collection of extrusion paths to fill surfaces + // (this collection contains only ExtrusionEntityCollection objects) + ExtrusionEntityCollection fills; + + Flow flow(FlowRole role, bool bridge = false, double width = -1) const; + void slices_to_fill_surfaces_clipped(); + void prepare_fill_surfaces(); + void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); + void process_external_surfaces(const Layer* lower_layer); + double infill_area_threshold() const; + + void export_region_slices_to_svg(const char *path) const; + void export_region_fill_surfaces_to_svg(const char *path) const; + // Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export. + void export_region_slices_to_svg_debug(const char *name) const; + void export_region_fill_surfaces_to_svg_debug(const char *name) const; + + // Is there any valid extrusion assigned to this LayerRegion? + bool has_extrusions() const { return ! this->perimeters.entities.empty() || ! this->fills.entities.empty(); } + +protected: + friend class Layer; + + LayerRegion(Layer *layer, PrintRegion *region) : m_layer(layer), m_region(region) {} + ~LayerRegion() {} + +private: + Layer *m_layer; + PrintRegion *m_region; +}; + + +typedef std::vector LayerRegionPtrs; + +class Layer +{ +public: + size_t id() const { return m_id; } + void set_id(size_t id) { m_id = id; } + PrintObject* object() { return m_object; } + const PrintObject* object() const { return m_object; } + + Layer *upper_layer; + Layer *lower_layer; + bool slicing_errors; + coordf_t slice_z; // Z used for slicing in unscaled coordinates + coordf_t print_z; // Z used for printing in unscaled coordinates + coordf_t height; // layer height in unscaled coordinates + + // collection of expolygons generated by slicing the original geometry; + // also known as 'islands' (all regions and surface types are merged here) + // The slices are chained by the shortest traverse distance and this traversal + // order will be recovered by the G-code generator. + ExPolygonCollection slices; + + size_t region_count() const { return m_regions.size(); } + const LayerRegion* get_region(int idx) const { return m_regions.at(idx); } + LayerRegion* get_region(int idx) { return m_regions[idx]; } + LayerRegion* add_region(PrintRegion* print_region); + const LayerRegionPtrs& regions() const { return m_regions; } + + void make_slices(); + void merge_slices(); + template bool any_internal_region_slice_contains(const T &item) const { + for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_internal_contains(item)) return true; + return false; + } + template bool any_bottom_region_slice_contains(const T &item) const { + for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_bottom_contains(item)) return true; + return false; + } + void make_perimeters(); + void make_fills(); + + void export_region_slices_to_svg(const char *path) const; + void export_region_fill_surfaces_to_svg(const char *path) const; + // Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export. + void export_region_slices_to_svg_debug(const char *name) const; + void export_region_fill_surfaces_to_svg_debug(const char *name) const; + + // Is there any valid extrusion assigned to this LayerRegion? + virtual bool has_extrusions() const { for (auto layerm : m_regions) if (layerm->has_extrusions()) return true; return false; } + +protected: + friend class PrintObject; + + Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : + upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false), + slice_z(slice_z), print_z(print_z), height(height), + m_id(id), m_object(object) {} + virtual ~Layer(); + +private: + // sequential number of layer, 0-based + size_t m_id; + PrintObject *m_object; + LayerRegionPtrs m_regions; +}; + +class SupportLayer : public Layer +{ +public: + // Polygons covered by the supports: base, interface and contact areas. + ExPolygonCollection support_islands; + // Extrusion paths for the support base and for the support interface and contacts. + ExtrusionEntityCollection support_fills; + + // Is there any valid extrusion assigned to this LayerRegion? + virtual bool has_extrusions() const { return ! support_fills.empty(); } + +protected: + friend class PrintObject; + + // The constructor has been made public to be able to insert additional support layers for the skirt or a wipe tower + // between the raft and the object first layer. + SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : + Layer(id, object, height, print_z, slice_z) {} + virtual ~SupportLayer() {} +}; + +} + +#endif diff --git a/xs/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp similarity index 90% rename from xs/src/libslic3r/LayerRegion.cpp rename to src/libslic3r/LayerRegion.cpp index 12526f2eca..6f70fba650 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -17,13 +17,13 @@ namespace Slic3r { Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const { - return this->_region->flow( + return m_region->flow( role, - this->_layer->height, + m_layer->height, bridge, - this->_layer->id() == 0, + m_layer->id() == 0, width, - *this->_layer->object() + *m_layer->object() ); } @@ -34,7 +34,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped() // in place. However we're now only using its boundaries (which are invariant) // so we're safe. This guarantees idempotence of prepare_infill() also in case // that combine_infill() turns some fill_surface into VOID surfaces. -// Polygons fill_boundaries = to_polygons(STDMOVE(this->fill_surfaces)); +// Polygons fill_boundaries = to_polygons(std::move(this->fill_surfaces)); Polygons fill_boundaries = to_polygons(this->fill_expolygons); // Collect polygons per surface type. std::vector polygons_by_surface; @@ -60,9 +60,9 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec &slices, this->layer()->height, this->flow(frPerimeter), - &this->region()->config, - &this->layer()->object()->config, - &this->layer()->object()->print()->config, + &this->region()->config(), + &this->layer()->object()->config(), + &this->layer()->object()->print()->config(), // output: &this->perimeters, @@ -115,7 +115,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) { // bottom_polygons are used to trim inflated top surfaces. fill_boundaries.reserve(number_polygons(surfaces)); - bool has_infill = this->region()->config.fill_density.value > 0.; + bool has_infill = this->region()->config().fill_density.value > 0.; for (const Surface &surface : this->fill_surfaces.surfaces) { if (surface.surface_type == stTop) { // Collect the top surfaces, inflate them and trim them by the bottom surfaces. @@ -133,9 +133,9 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) if (internal_surface) // Make a copy as the following line uses the move semantics. internal.push_back(surface); - polygons_append(fill_boundaries, STDMOVE(surface.expolygon)); + polygons_append(fill_boundaries, std::move(surface.expolygon)); } else if (internal_surface) - internal.push_back(STDMOVE(surface)); + internal.push_back(std::move(surface)); } } @@ -192,7 +192,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) polys = intersection(polys, to_polygons(fill_boundaries_ex[idx_island])); } bridge_bboxes.push_back(get_extents(polys)); - bridges_grown.push_back(STDMOVE(polys)); + bridges_grown.push_back(std::move(polys)); } } @@ -243,7 +243,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) for (size_t i = 0; i < bridges.size(); ++ i) { if (bridge_group[i] != group_id) continue; - initial.push_back(STDMOVE(bridges[i].expolygon)); + initial.push_back(std::move(bridges[i].expolygon)); polygons_append(grown, bridges_grown[i]); } // detect bridge direction before merging grown surfaces otherwise adjacent bridges @@ -258,9 +258,9 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) #ifdef SLIC3R_DEBUG printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id()); #endif - if (bd.detect_angle(Geometry::deg2rad(this->region()->config.bridge_angle.value))) { + if (bd.detect_angle(Geometry::deg2rad(this->region()->config().bridge_angle.value))) { bridges[idx_last].bridge_angle = bd.angle; - if (this->layer()->object()->config.support_material) { + if (this->layer()->object()->config().support_material) { polygons_append(this->bridged, bd.coverage()); this->unsupported_bridge_edges.append(bd.unsupported_edges()); } @@ -269,7 +269,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) surfaces_append(bottom, union_ex(grown, true), bridges[idx_last]); } - fill_boundaries = STDMOVE(to_polygons(fill_boundaries_ex)); + fill_boundaries = std::move(to_polygons(fill_boundaries_ex)); BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done"; } @@ -284,7 +284,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) Surfaces new_surfaces; { // Merge top and bottom in a single collection. - surfaces_append(top, STDMOVE(bottom)); + surfaces_append(top, std::move(bottom)); // Intersect the grown surfaces with the actual fill boundaries. Polygons bottom_polygons = to_polygons(bottom); for (size_t i = 0; i < top.size(); ++ i) { @@ -292,11 +292,11 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) if (s1.empty()) continue; Polygons polys; - polygons_append(polys, STDMOVE(s1)); + polygons_append(polys, std::move(s1)); for (size_t j = i + 1; j < top.size(); ++ j) { Surface &s2 = top[j]; if (! s2.empty() && surfaces_could_merge(s1, s2)) { - polygons_append(polys, STDMOVE(s2)); + polygons_append(polys, std::move(s2)); s2.clear(); } } @@ -306,7 +306,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) surfaces_append( new_surfaces, // Don't use a safety offset as fill_boundaries were already united using the safety offset. - STDMOVE(intersection_ex(polys, fill_boundaries, false)), + std::move(intersection_ex(polys, fill_boundaries, false)), s1); } } @@ -318,20 +318,20 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) if (s1.empty()) continue; Polygons polys; - polygons_append(polys, STDMOVE(s1)); + polygons_append(polys, std::move(s1)); for (size_t j = i + 1; j < internal.size(); ++ j) { Surface &s2 = internal[j]; if (! s2.empty() && surfaces_could_merge(s1, s2)) { - polygons_append(polys, STDMOVE(s2)); + polygons_append(polys, std::move(s2)); s2.clear(); } } ExPolygons new_expolys = diff_ex(polys, new_polygons); polygons_append(new_polygons, to_polygons(new_expolys)); - surfaces_append(new_surfaces, STDMOVE(new_expolys), s1); + surfaces_append(new_surfaces, std::move(new_expolys), s1); } - this->fill_surfaces.surfaces = STDMOVE(new_surfaces); + this->fill_surfaces.surfaces = std::move(new_surfaces); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final"); @@ -350,13 +350,13 @@ void LayerRegion::prepare_fill_surfaces() the only meaningful information returned by psPerimeters. */ // if no solid layers are requested, turn top/bottom surfaces to internal - if (this->region()->config.top_solid_layers == 0) { + if (this->region()->config().top_solid_layers == 0) { for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) if (surface->surface_type == stTop) - surface->surface_type = (this->layer()->object()->config.infill_only_where_needed) ? + surface->surface_type = (this->layer()->object()->config().infill_only_where_needed) ? stInternalVoid : stInternal; } - if (this->region()->config.bottom_solid_layers == 0) { + if (this->region()->config().bottom_solid_layers == 0) { for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { if (surface->surface_type == stBottom || surface->surface_type == stBottomBridge) surface->surface_type = stInternal; @@ -364,9 +364,9 @@ void LayerRegion::prepare_fill_surfaces() } // turn too small internal regions into solid regions according to the user setting - if (this->region()->config.fill_density.value > 0) { + if (this->region()->config().fill_density.value > 0) { // scaling an area requires two calls! - double min_area = scale_(scale_(this->region()->config.solid_infill_below_area.value)); + double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value)); for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { if (surface->surface_type == stInternal && surface->area() <= min_area) surface->surface_type = stInternalSolid; @@ -391,8 +391,8 @@ void LayerRegion::export_region_slices_to_svg(const char *path) const for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++surface) bbox.merge(get_extents(surface->expolygon)); Point legend_size = export_surface_type_legend_to_svg_box_size(); - Point legend_pos(bbox.min.x, bbox.max.y); - bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y)); + Point legend_pos(bbox.min(0), bbox.max(1)); + bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); SVG svg(path, bbox); const float transparency = 0.5f; @@ -418,8 +418,8 @@ void LayerRegion::export_region_fill_surfaces_to_svg(const char *path) const for (Surfaces::const_iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) bbox.merge(get_extents(surface->expolygon)); Point legend_size = export_surface_type_legend_to_svg_box_size(); - Point legend_pos(bbox.min.x, bbox.max.y); - bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y)); + Point legend_pos(bbox.min(0), bbox.max(1)); + bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); SVG svg(path, bbox); const float transparency = 0.5f; diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp new file mode 100644 index 0000000000..35cfa2b760 --- /dev/null +++ b/src/libslic3r/Line.cpp @@ -0,0 +1,119 @@ +#include "Geometry.hpp" +#include "Line.hpp" +#include "Polyline.hpp" +#include +#include +#include + +namespace Slic3r { + +Linef3 transform(const Linef3& line, const Transform3d& t) +{ + typedef Eigen::Matrix LineInMatrixForm; + + LineInMatrixForm world_line; + ::memcpy((void*)world_line.col(0).data(), (const void*)line.a.data(), 3 * sizeof(double)); + ::memcpy((void*)world_line.col(1).data(), (const void*)line.b.data(), 3 * sizeof(double)); + + LineInMatrixForm local_line = t * world_line.colwise().homogeneous(); + return Linef3(Vec3d(local_line(0, 0), local_line(1, 0), local_line(2, 0)), Vec3d(local_line(0, 1), local_line(1, 1), local_line(2, 1))); +} + +bool Line::intersection_infinite(const Line &other, Point* point) const +{ + Vec2d a1 = this->a.cast(); + Vec2d a2 = other.a.cast(); + Vec2d v12 = (other.a - this->a).cast(); + Vec2d v1 = (this->b - this->a).cast(); + Vec2d v2 = (other.b - other.a).cast(); + double denom = cross2(v1, v2); + if (std::fabs(denom) < EPSILON) + return false; + double t1 = cross2(v12, v2) / denom; + *point = (a1 + t1 * v1).cast(); + return true; +} + +/* distance to the closest point of line */ +double Line::distance_to(const Point &point) const +{ + const Line &line = *this; + const Vec2d v = (line.b - line.a).cast(); + const Vec2d va = (point - line.a).cast(); + const double l2 = v.squaredNorm(); // avoid a sqrt + if (l2 == 0.0) + // line.a == line.b case + return va.norm(); + // Consider the line extending the segment, parameterized as line.a + t (line.b - line.a). + // We find projection of this point onto the line. + // It falls where t = [(this-line.a) . (line.b-line.a)] / |line.b-line.a|^2 + const double t = va.dot(v) / l2; + if (t < 0.0) return va.norm(); // beyond the 'a' end of the segment + else if (t > 1.0) return (point - line.b).cast().norm(); // beyond the 'b' end of the segment + return (t * v - va).norm(); +} + +double Line::perp_distance_to(const Point &point) const +{ + const Line &line = *this; + const Vec2d v = (line.b - line.a).cast(); + const Vec2d va = (point - line.a).cast(); + if (line.a == line.b) + return va.norm(); + return std::abs(cross2(v, va)) / v.norm(); +} + +double Line::orientation() const +{ + double angle = this->atan2_(); + if (angle < 0) angle = 2*PI + angle; + return angle; +} + +double Line::direction() const +{ + double atan2 = this->atan2_(); + return (fabs(atan2 - PI) < EPSILON) ? 0 + : (atan2 < 0) ? (atan2 + PI) + : atan2; +} + +bool Line::parallel_to(double angle) const +{ + return Slic3r::Geometry::directions_parallel(this->direction(), angle); +} + +bool Line::intersection(const Line &l2, Point *intersection) const +{ + const Line &l1 = *this; + const Vec2d v1 = (l1.b - l1.a).cast(); + const Vec2d v2 = (l2.b - l2.a).cast(); + const Vec2d v12 = (l1.a - l2.a).cast(); + double denom = cross2(v1, v2); + double nume_a = cross2(v2, v12); + double nume_b = cross2(v1, v12); + if (fabs(denom) < EPSILON) +#if 0 + // Lines are collinear. Return true if they are coincident (overlappign). + return ! (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON); +#else + return false; +#endif + double t1 = nume_a / denom; + double t2 = nume_b / denom; + if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) { + // Get the intersection point. + (*intersection) = (l1.a.cast() + t1 * v1).cast(); + return true; + } + return false; // not intersecting +} + +Vec3d Linef3::intersect_plane(double z) const +{ + auto v = (this->b - this->a).cast(); + double t = (z - this->a(2)) / v(2); + return Vec3d(this->a(0) + v(0) * t, this->a(1) + v(1) * t, z); +} + +} diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp new file mode 100644 index 0000000000..36e02247ce --- /dev/null +++ b/src/libslic3r/Line.hpp @@ -0,0 +1,119 @@ +#ifndef slic3r_Line_hpp_ +#define slic3r_Line_hpp_ + +#include "libslic3r.h" +#include "Point.hpp" + +namespace Slic3r { + +class Line; +class Line3; +class Linef3; +class Polyline; +class ThickLine; +typedef std::vector Lines; +typedef std::vector Lines3; +typedef std::vector ThickLines; + +Linef3 transform(const Linef3& line, const Transform3d& t); + +class Line +{ +public: + Line() {} + Line(const Point& _a, const Point& _b) : a(_a), b(_b) {} + explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; } + void scale(double factor) { this->a *= factor; this->b *= factor; } + void translate(double x, double y) { Vector v(x, y); this->a += v; this->b += v; } + void rotate(double angle, const Point ¢er) { this->a.rotate(angle, center); this->b.rotate(angle, center); } + void reverse() { std::swap(this->a, this->b); } + double length() const { return (b - a).cast().norm(); } + Point midpoint() const { return (this->a + this->b) / 2; } + bool intersection_infinite(const Line &other, Point* point) const; + bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; } + double distance_to(const Point &point) const; + double perp_distance_to(const Point &point) const; + bool parallel_to(double angle) const; + bool parallel_to(const Line &line) const { return this->parallel_to(line.direction()); } + double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); } + double orientation() const; + double direction() const; + Vector vector() const { return this->b - this->a; } + Vector normal() const { return Vector((this->b(1) - this->a(1)), -(this->b(0) - this->a(0))); } + bool intersection(const Line& line, Point* intersection) const; + double ccw(const Point& point) const { return point.ccw(*this); } + + Point a; + Point b; +}; + +class ThickLine : public Line +{ +public: + ThickLine() : a_width(0), b_width(0) {} + ThickLine(const Point& a, const Point& b) : Line(a, b), a_width(0), b_width(0) {} + ThickLine(const Point& a, const Point& b, double wa, double wb) : Line(a, b), a_width(wa), b_width(wb) {} + + double a_width, b_width; +}; + +class Line3 +{ +public: + Line3() : a(Vec3crd::Zero()), b(Vec3crd::Zero()) {} + Line3(const Vec3crd& _a, const Vec3crd& _b) : a(_a), b(_b) {} + + double length() const { return (this->a - this->b).cast().norm(); } + Vec3crd vector() const { return this->b - this->a; } + + Vec3crd a; + Vec3crd b; +}; + +class Linef +{ +public: + Linef() : a(Vec2d::Zero()), b(Vec2d::Zero()) {} + Linef(const Vec2d& _a, const Vec2d& _b) : a(_a), b(_b) {} + + Vec2d a; + Vec2d b; +}; + +class Linef3 +{ +public: + Linef3() : a(Vec3d::Zero()), b(Vec3d::Zero()) {} + Linef3(const Vec3d& _a, const Vec3d& _b) : a(_a), b(_b) {} + + Vec3d intersect_plane(double z) const; + void scale(double factor) { this->a *= factor; this->b *= factor; } + Vec3d vector() const { return this->b - this->a; } + Vec3d unit_vector() const { return (length() == 0.0) ? Vec3d::Zero() : vector().normalized(); } + double length() const { return vector().norm(); } + + Vec3d a; + Vec3d b; +}; + +} // namespace Slic3r + +// start Boost +#include +namespace boost { namespace polygon { + template <> + struct geometry_concept { typedef segment_concept type; }; + + template <> + struct segment_traits { + typedef coord_t coordinate_type; + typedef Slic3r::Point point_type; + + static inline point_type get(const Slic3r::Line& line, direction_1d dir) { + return dir.to_int() ? line.b : line.a; + } + }; +} } +// end Boost + +#endif diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp new file mode 100644 index 0000000000..e9e3b395fc --- /dev/null +++ b/src/libslic3r/Model.cpp @@ -0,0 +1,1516 @@ +#include "Model.hpp" +#include "Geometry.hpp" + +#include "Format/AMF.hpp" +#include "Format/OBJ.hpp" +#include "Format/PRUS.hpp" +#include "Format/STL.hpp" +#include "Format/3mf.hpp" + +#include + +#include +#include +#include +#include + +#include "SVG.hpp" +#include + +namespace Slic3r { + +unsigned int Model::s_auto_extruder_id = 1; + +size_t ModelBase::s_last_id = 0; + +Model& Model::assign_copy(const Model &rhs) +{ + this->copy_id(rhs); + // copy materials + this->clear_materials(); + this->materials = rhs.materials; + for (std::pair &m : this->materials) { + // Copy including the ID and m_model. + m.second = new ModelMaterial(*m.second); + m.second->set_model(this); + } + // copy objects + this->clear_objects(); + this->objects.reserve(rhs.objects.size()); + for (const ModelObject *model_object : rhs.objects) { + // Copy including the ID, leave ID set to invalid (zero). + auto mo = ModelObject::new_copy(*model_object); + mo->set_model(this); + this->objects.emplace_back(mo); + } + return *this; +} + +Model& Model::assign_copy(Model &&rhs) +{ + this->copy_id(rhs); + // Move materials, adjust the parent pointer. + this->clear_materials(); + this->materials = std::move(rhs.materials); + for (std::pair &m : this->materials) + m.second->set_model(this); + rhs.materials.clear(); + // Move objects, adjust the parent pointer. + this->clear_objects(); + this->objects = std::move(rhs.objects); + for (ModelObject *model_object : this->objects) + model_object->set_model(this); + rhs.objects.clear(); + return *this; +} + +void Model::assign_new_unique_ids_recursive() +{ + this->set_new_unique_id(); + for (std::pair &m : this->materials) + m.second->assign_new_unique_ids_recursive(); + for (ModelObject *model_object : this->objects) + model_object->assign_new_unique_ids_recursive(); +} + +Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances) +{ + Model model; + + DynamicPrintConfig temp_config; + if (config == nullptr) + config = &temp_config; + + bool result = false; + if (boost::algorithm::iends_with(input_file, ".stl")) + result = load_stl(input_file.c_str(), &model); + else if (boost::algorithm::iends_with(input_file, ".obj")) + result = load_obj(input_file.c_str(), &model); + else if (!boost::algorithm::iends_with(input_file, ".zip.amf") && (boost::algorithm::iends_with(input_file, ".amf") || + boost::algorithm::iends_with(input_file, ".amf.xml"))) + result = load_amf(input_file.c_str(), config, &model); + else if (boost::algorithm::iends_with(input_file, ".3mf")) + result = load_3mf(input_file.c_str(), config, &model); + else if (boost::algorithm::iends_with(input_file, ".prusa")) + result = load_prus(input_file.c_str(), &model); + else + throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension."); + + if (! result) + throw std::runtime_error("Loading of a model file failed."); + + if (model.objects.empty()) + throw std::runtime_error("The supplied file couldn't be read because it's empty"); + + for (ModelObject *o : model.objects) + o->input_file = input_file; + + if (add_default_instances) + model.add_default_instances(); + + return model; +} + +Model Model::read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances) +{ + Model model; + + bool result = false; + if (boost::algorithm::iends_with(input_file, ".3mf")) + result = load_3mf(input_file.c_str(), config, &model); + else if (boost::algorithm::iends_with(input_file, ".zip.amf")) + result = load_amf(input_file.c_str(), config, &model); + else + throw std::runtime_error("Unknown file format. Input file must have .3mf or .zip.amf extension."); + + if (!result) + throw std::runtime_error("Loading of a model file failed."); + + if (model.objects.empty()) + throw std::runtime_error("The supplied file couldn't be read because it's empty"); + + for (ModelObject *o : model.objects) + { + if (boost::algorithm::iends_with(input_file, ".zip.amf")) + { + // we remove the .zip part of the extension to avoid it be added to filenames when exporting + o->input_file = boost::ireplace_last_copy(input_file, ".zip.", "."); + } + else + o->input_file = input_file; + } + + if (add_default_instances) + model.add_default_instances(); + + return model; +} + +void Model::repair() +{ + for (ModelObject *o : this->objects) + o->repair(); +} + +ModelObject* Model::add_object() +{ + this->objects.emplace_back(new ModelObject(this)); + return this->objects.back(); +} + +ModelObject* Model::add_object(const char *name, const char *path, const TriangleMesh &mesh) +{ + ModelObject* new_object = new ModelObject(this); + this->objects.push_back(new_object); + new_object->name = name; + new_object->input_file = path; + ModelVolume *new_volume = new_object->add_volume(mesh); + new_volume->name = name; + new_object->invalidate_bounding_box(); + return new_object; +} + +ModelObject* Model::add_object(const char *name, const char *path, TriangleMesh &&mesh) +{ + ModelObject* new_object = new ModelObject(this); + this->objects.push_back(new_object); + new_object->name = name; + new_object->input_file = path; + ModelVolume *new_volume = new_object->add_volume(std::move(mesh)); + new_volume->name = name; + new_object->invalidate_bounding_box(); + return new_object; +} + +ModelObject* Model::add_object(const ModelObject &other) +{ + ModelObject* new_object = ModelObject::new_clone(other); + new_object->set_model(this); + this->objects.push_back(new_object); + return new_object; +} + +void Model::delete_object(size_t idx) +{ + ModelObjectPtrs::iterator i = this->objects.begin() + idx; + delete *i; + this->objects.erase(i); +} + +bool Model::delete_object(ModelObject* object) +{ + if (object != nullptr) { + size_t idx = 0; + for (ModelObject *model_object : objects) { + if (model_object == object) { + delete model_object; + objects.erase(objects.begin() + idx); + return true; + } + ++ idx; + } + } + return false; +} + +bool Model::delete_object(ModelID id) +{ + if (id.id != 0) { + size_t idx = 0; + for (ModelObject *model_object : objects) { + if (model_object->id() == id) { + delete model_object; + objects.erase(objects.begin() + idx); + return true; + } + ++ idx; + } + } + return false; +} + +void Model::clear_objects() +{ + for (ModelObject *o : this->objects) + delete o; + this->objects.clear(); +} + +void Model::delete_material(t_model_material_id material_id) +{ + ModelMaterialMap::iterator i = this->materials.find(material_id); + if (i != this->materials.end()) { + delete i->second; + this->materials.erase(i); + } +} + +void Model::clear_materials() +{ + for (auto &m : this->materials) + delete m.second; + this->materials.clear(); +} + +ModelMaterial* Model::add_material(t_model_material_id material_id) +{ + assert(! material_id.empty()); + ModelMaterial* material = this->get_material(material_id); + if (material == nullptr) + material = this->materials[material_id] = new ModelMaterial(this); + return material; +} + +ModelMaterial* Model::add_material(t_model_material_id material_id, const ModelMaterial &other) +{ + assert(! material_id.empty()); + // delete existing material if any + ModelMaterial* material = this->get_material(material_id); + delete material; + // set new material + material = new ModelMaterial(other); + material->set_model(this); + this->materials[material_id] = material; + return material; +} + +// makes sure all objects have at least one instance +bool Model::add_default_instances() +{ + // apply a default position to all objects not having one + for (ModelObject *o : this->objects) + if (o->instances.empty()) + o->add_instance(); + return true; +} + +// this returns the bounding box of the *transformed* instances +BoundingBoxf3 Model::bounding_box() const +{ + BoundingBoxf3 bb; + for (ModelObject *o : this->objects) + bb.merge(o->bounding_box()); + return bb; +} + +unsigned int Model::update_print_volume_state(const BoundingBoxf3 &print_volume) +{ + unsigned int num_printable = 0; + for (ModelObject *model_object : this->objects) + num_printable += model_object->check_instances_print_volume_state(print_volume); + return num_printable; +} + +bool Model::center_instances_around_point(const Vec2d &point) +{ + BoundingBoxf3 bb; + for (ModelObject *o : this->objects) + for (size_t i = 0; i < o->instances.size(); ++ i) + bb.merge(o->instance_bounding_box(i, false)); + + Vec2d shift2 = point - to_2d(bb.center()); + if (std::abs(shift2(0)) < EPSILON && std::abs(shift2(1)) < EPSILON) + // No significant shift, don't do anything. + return false; + + Vec3d shift3 = Vec3d(shift2(0), shift2(1), 0.0); + for (ModelObject *o : this->objects) { + for (ModelInstance *i : o->instances) + i->set_offset(i->get_offset() + shift3); + o->invalidate_bounding_box(); + } + return true; +} + +// flattens everything to a single mesh +TriangleMesh Model::mesh() const +{ + TriangleMesh mesh; + for (const ModelObject *o : this->objects) + mesh.merge(o->mesh()); + return mesh; +} + +static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out) +{ + if (sizes.empty()) + // return if the list is empty or the following call to BoundingBoxf constructor will lead to a crash + return true; + + // we supply unscaled data to arrange() + bool result = Slic3r::Geometry::arrange( + sizes.size(), // number of parts + BoundingBoxf(sizes).max, // width and height of a single cell + dist, // distance between cells + bb, // bounding box of the area to fill + out // output positions + ); + + if (!result && bb != nullptr) { + // Try to arrange again ignoring bb + result = Slic3r::Geometry::arrange( + sizes.size(), // number of parts + BoundingBoxf(sizes).max, // width and height of a single cell + dist, // distance between cells + nullptr, // bounding box of the area to fill + out // output positions + ); + } + + return result; +} + +/* arrange objects preserving their instance count + but altering their instance positions */ +bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) +{ + // get the (transformed) size of each instance so that we take + // into account their different transformations when packing + Pointfs instance_sizes; + Pointfs instance_centers; + for (const ModelObject *o : this->objects) + for (size_t i = 0; i < o->instances.size(); ++ i) { + // an accurate snug bounding box around the transformed mesh. + BoundingBoxf3 bbox(o->instance_bounding_box(i, true)); + instance_sizes.emplace_back(to_2d(bbox.size())); + instance_centers.emplace_back(to_2d(bbox.center())); + } + + Pointfs positions; + if (! _arrange(instance_sizes, dist, bb, positions)) + return false; + + size_t idx = 0; + for (ModelObject *o : this->objects) { + for (ModelInstance *i : o->instances) { + Vec2d offset_xy = positions[idx] - instance_centers[idx]; + i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z))); + ++idx; + } + o->invalidate_bounding_box(); + } + + return true; +} + +// Duplicate the entire model preserving instance relative positions. +void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) +{ + Pointfs model_sizes(copies_num-1, to_2d(this->bounding_box().size())); + Pointfs positions; + if (! _arrange(model_sizes, dist, bb, positions)) + throw std::invalid_argument("Cannot duplicate part as the resulting objects would not fit on the print bed.\n"); + + // note that this will leave the object count unaltered + + for (ModelObject *o : this->objects) { + // make a copy of the pointers in order to avoid recursion when appending their copies + ModelInstancePtrs instances = o->instances; + for (const ModelInstance *i : instances) { + for (const Vec2d &pos : positions) { + ModelInstance *instance = o->add_instance(*i); + instance->set_offset(instance->get_offset() + Vec3d(pos(0), pos(1), 0.0)); + } + } + o->invalidate_bounding_box(); + } +} + +/* this will append more instances to each object + and then automatically rearrange everything */ +void Model::duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) +{ + for (ModelObject *o : this->objects) { + // make a copy of the pointers in order to avoid recursion when appending their copies + ModelInstancePtrs instances = o->instances; + for (const ModelInstance *i : instances) + for (size_t k = 2; k <= copies_num; ++ k) + o->add_instance(*i); + } + + this->arrange_objects(dist, bb); +} + +void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist) +{ + if (this->objects.size() > 1) throw "Grid duplication is not supported with multiple objects"; + if (this->objects.empty()) throw "No objects!"; + + ModelObject* object = this->objects.front(); + object->clear_instances(); + + Vec3d ext_size = object->bounding_box().size() + dist * Vec3d::Ones(); + + for (size_t x_copy = 1; x_copy <= x; ++x_copy) { + for (size_t y_copy = 1; y_copy <= y; ++y_copy) { + ModelInstance* instance = object->add_instance(); + instance->set_offset(Vec3d(ext_size(0) * (double)(x_copy - 1), ext_size(1) * (double)(y_copy - 1), 0.0)); + } + } +} + +bool Model::looks_like_multipart_object() const +{ + if (this->objects.size() <= 1) + return false; + double zmin = std::numeric_limits::max(); + for (const ModelObject *obj : this->objects) { + if (obj->volumes.size() > 1 || obj->config.keys().size() > 1) + return false; + for (const ModelVolume *vol : obj->volumes) { + double zmin_this = vol->mesh.bounding_box().min(2); + if (zmin == std::numeric_limits::max()) + zmin = zmin_this; + else if (std::abs(zmin - zmin_this) > EPSILON) + // The volumes don't share zmin. + return true; + } + } + return false; +} + +void Model::convert_multipart_object(unsigned int max_extruders) +{ + if (this->objects.empty()) + return; + + ModelObject* object = new ModelObject(this); + object->input_file = this->objects.front()->input_file; + object->name = this->objects.front()->name; + //FIXME copy the config etc? + + reset_auto_extruder_id(); + + for (const ModelObject* o : this->objects) + for (const ModelVolume* v : o->volumes) + { + ModelVolume* new_v = object->add_volume(*v); + if (new_v != nullptr) + { + new_v->name = o->name; + new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string(max_extruders)); + } + } + + for (const ModelInstance* i : this->objects.front()->instances) + object->add_instance(*i); + + this->clear_objects(); + this->objects.push_back(object); +} + +void Model::adjust_min_z() +{ + if (objects.empty()) + return; + + if (bounding_box().min(2) < 0.0) + { + for (ModelObject* obj : objects) + { + if (obj != nullptr) + { + coordf_t obj_min_z = obj->bounding_box().min(2); + if (obj_min_z < 0.0) + obj->translate(0.0, 0.0, -obj_min_z); + } + } + } +} + +unsigned int Model::get_auto_extruder_id(unsigned int max_extruders) +{ + unsigned int id = s_auto_extruder_id; + + if (++s_auto_extruder_id > max_extruders) + reset_auto_extruder_id(); + + return id; +} + +std::string Model::get_auto_extruder_id_as_string(unsigned int max_extruders) +{ + char str_extruder[64]; + sprintf(str_extruder, "%ud", get_auto_extruder_id(max_extruders)); + return str_extruder; +} + +void Model::reset_auto_extruder_id() +{ + s_auto_extruder_id = 1; +} + +ModelObject::~ModelObject() +{ + this->clear_volumes(); + this->clear_instances(); +} + +// maintains the m_model pointer +ModelObject& ModelObject::assign_copy(const ModelObject &rhs) +{ + this->copy_id(rhs); + + this->name = rhs.name; + this->input_file = rhs.input_file; + this->config = rhs.config; + this->sla_support_points = rhs.sla_support_points; + this->layer_height_ranges = rhs.layer_height_ranges; + this->layer_height_profile = rhs.layer_height_profile; + this->layer_height_profile_valid = rhs.layer_height_profile_valid; + this->origin_translation = rhs.origin_translation; + m_bounding_box = rhs.m_bounding_box; + m_bounding_box_valid = rhs.m_bounding_box_valid; + + this->clear_volumes(); + this->volumes.reserve(rhs.volumes.size()); + for (ModelVolume *model_volume : rhs.volumes) { + this->volumes.emplace_back(new ModelVolume(*model_volume)); + this->volumes.back()->set_model_object(this); + } + this->clear_instances(); + this->instances.reserve(rhs.instances.size()); + for (const ModelInstance *model_instance : rhs.instances) { + this->instances.emplace_back(new ModelInstance(*model_instance)); + this->instances.back()->set_model_object(this); + } + + return *this; +} + +// maintains the m_model pointer +ModelObject& ModelObject::assign_copy(ModelObject &&rhs) +{ + this->copy_id(rhs); + + this->name = std::move(rhs.name); + this->input_file = std::move(rhs.input_file); + this->config = std::move(rhs.config); + this->sla_support_points = std::move(rhs.sla_support_points); + this->layer_height_ranges = std::move(rhs.layer_height_ranges); + this->layer_height_profile = std::move(rhs.layer_height_profile); + this->layer_height_profile_valid = std::move(rhs.layer_height_profile_valid); + this->origin_translation = std::move(rhs.origin_translation); + m_bounding_box = std::move(rhs.m_bounding_box); + m_bounding_box_valid = std::move(rhs.m_bounding_box_valid); + + this->clear_volumes(); + this->volumes = std::move(rhs.volumes); + rhs.volumes.clear(); + for (ModelVolume *model_volume : this->volumes) + model_volume->set_model_object(this); + this->clear_instances(); + this->instances = std::move(rhs.instances); + rhs.instances.clear(); + for (ModelInstance *model_instance : this->instances) + model_instance->set_model_object(this); + + return *this; +} + +void ModelObject::assign_new_unique_ids_recursive() +{ + this->set_new_unique_id(); + for (ModelVolume *model_volume : this->volumes) + model_volume->assign_new_unique_ids_recursive(); + for (ModelInstance *model_instance : this->instances) + model_instance->assign_new_unique_ids_recursive(); +} + +// Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original. +// Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing. +//ModelObject* ModelObject::clone(Model *parent) +//{ +// return new ModelObject(parent, *this, true); +//} + +ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh) +{ + ModelVolume* v = new ModelVolume(this, mesh); + this->volumes.push_back(v); + this->invalidate_bounding_box(); + return v; +} + +ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh) +{ + ModelVolume* v = new ModelVolume(this, std::move(mesh)); + this->volumes.push_back(v); + this->invalidate_bounding_box(); + return v; +} + +ModelVolume* ModelObject::add_volume(const ModelVolume &other) +{ + ModelVolume* v = new ModelVolume(this, other); + this->volumes.push_back(v); + this->invalidate_bounding_box(); + return v; +} + +ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&mesh) +{ + ModelVolume* v = new ModelVolume(this, other, std::move(mesh)); + this->volumes.push_back(v); + this->invalidate_bounding_box(); + return v; +} + +void ModelObject::delete_volume(size_t idx) +{ + ModelVolumePtrs::iterator i = this->volumes.begin() + idx; + delete *i; + this->volumes.erase(i); + this->invalidate_bounding_box(); +} + +void ModelObject::clear_volumes() +{ + for (ModelVolume *v : this->volumes) + delete v; + this->volumes.clear(); + this->invalidate_bounding_box(); +} + +ModelInstance* ModelObject::add_instance() +{ + ModelInstance* i = new ModelInstance(this); + this->instances.push_back(i); + this->invalidate_bounding_box(); + return i; +} + +ModelInstance* ModelObject::add_instance(const ModelInstance &other) +{ + ModelInstance* i = new ModelInstance(this, other); + this->instances.push_back(i); + this->invalidate_bounding_box(); + return i; +} + +ModelInstance* ModelObject::add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation) +{ + auto *instance = add_instance(); + instance->set_offset(offset); + instance->set_scaling_factor(scaling_factor); + instance->set_rotation(rotation); + return instance; +} + +void ModelObject::delete_instance(size_t idx) +{ + ModelInstancePtrs::iterator i = this->instances.begin() + idx; + delete *i; + this->instances.erase(i); + this->invalidate_bounding_box(); +} + +void ModelObject::delete_last_instance() +{ + this->delete_instance(this->instances.size() - 1); +} + +void ModelObject::clear_instances() +{ + for (ModelInstance *i : this->instances) + delete i; + this->instances.clear(); + this->invalidate_bounding_box(); +} + +// Returns the bounding box of the transformed instances. +// This bounding box is approximate and not snug. +const BoundingBoxf3& ModelObject::bounding_box() const +{ + if (! m_bounding_box_valid) { + BoundingBoxf3 raw_bbox; + for (const ModelVolume *v : this->volumes) + if (v->is_model_part()) +#if ENABLE_MODELVOLUME_TRANSFORM + { + TriangleMesh m = v->mesh; + m.transform(v->get_matrix()); + raw_bbox.merge(m.bounding_box()); + } +#else + // mesh.bounding_box() returns a cached value. + raw_bbox.merge(v->mesh.bounding_box()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + BoundingBoxf3 bb; + for (const ModelInstance *i : this->instances) + bb.merge(i->transform_bounding_box(raw_bbox)); + m_bounding_box = bb; + m_bounding_box_valid = true; + } + return m_bounding_box; +} + +// A mesh containing all transformed instances of this object. +TriangleMesh ModelObject::mesh() const +{ + TriangleMesh mesh; + TriangleMesh raw_mesh = this->raw_mesh(); + for (const ModelInstance *i : this->instances) { + TriangleMesh m = raw_mesh; + i->transform_mesh(&m); + mesh.merge(m); + } + return mesh; +} + +// Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes. +// Currently used by ModelObject::mesh(), to calculate the 2D envelope for 2D platter +// and to display the object statistics at ModelObject::print_info(). +TriangleMesh ModelObject::raw_mesh() const +{ + TriangleMesh mesh; + for (const ModelVolume *v : this->volumes) + if (v->is_model_part()) +#if ENABLE_MODELVOLUME_TRANSFORM + { + TriangleMesh vol_mesh(v->mesh); + vol_mesh.transform(v->get_matrix()); + mesh.merge(vol_mesh); + } +#else + mesh.merge(v->mesh); +#endif // ENABLE_MODELVOLUME_TRANSFORM + return mesh; +} + +// A transformed snug bounding box around the non-modifier object volumes, without the translation applied. +// This bounding box is only used for the actual slicing. +BoundingBoxf3 ModelObject::raw_bounding_box() const +{ + BoundingBoxf3 bb; + for (const ModelVolume *v : this->volumes) + if (v->is_model_part()) { + if (this->instances.empty()) + throw std::invalid_argument("Can't call raw_bounding_box() with no instances"); + +#if ENABLE_MODELVOLUME_TRANSFORM + TriangleMesh vol_mesh(v->mesh); + vol_mesh.transform(v->get_matrix()); + bb.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true)); +#else + bb.merge(this->instances.front()->transform_mesh_bounding_box(v->mesh, true)); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + return bb; +} + +// This returns an accurate snug bounding box of the transformed object instance, without the translation applied. +BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_translate) const +{ + BoundingBoxf3 bb; +#if ENABLE_MODELVOLUME_TRANSFORM + for (ModelVolume *v : this->volumes) + { + if (v->is_model_part()) + { + TriangleMesh mesh(v->mesh); + mesh.transform(v->get_matrix()); + bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(mesh, dont_translate)); + } + } +#else + for (ModelVolume *v : this->volumes) + if (v->is_model_part()) + bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate)); +#endif // ENABLE_MODELVOLUME_TRANSFORM + return bb; +} + +void ModelObject::center_around_origin() +{ + // calculate the displacements needed to + // center this object around the origin + BoundingBoxf3 bb; + for (ModelVolume *v : this->volumes) + if (v->is_model_part()) + bb.merge(v->mesh.bounding_box()); + + // Shift is the vector from the center of the bounding box to the origin + Vec3d shift = -bb.center(); + + this->translate(shift); + this->origin_translation += shift; + +#if !ENABLE_MODELVOLUME_TRANSFORM + if (!this->instances.empty()) { + for (ModelInstance *i : this->instances) { + i->set_offset(i->get_offset() - shift); + } + this->invalidate_bounding_box(); + } +#endif // !ENABLE_MODELVOLUME_TRANSFORM +} + +void ModelObject::ensure_on_bed() +{ + translate_instances(Vec3d(0.0, 0.0, -get_min_z())); +} + +void ModelObject::translate_instances(const Vec3d& vector) +{ + for (size_t i = 0; i < instances.size(); ++i) + { + translate_instance(i, vector); + } +} + +void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector) +{ + ModelInstance* i = instances[instance_idx]; + i->set_offset(i->get_offset() + vector); + invalidate_bounding_box(); +} + +void ModelObject::translate(double x, double y, double z) +{ + for (ModelVolume *v : this->volumes) + { + v->translate(x, y, z); + } + + if (m_bounding_box_valid) + m_bounding_box.translate(x, y, z); +} + +void ModelObject::scale(const Vec3d &versor) +{ + for (ModelVolume *v : this->volumes) + { + v->scale(versor); + } +#if !ENABLE_MODELVOLUME_TRANSFORM + // reset origin translation since it doesn't make sense anymore + this->origin_translation = Vec3d::Zero(); +#endif // !ENABLE_MODELVOLUME_TRANSFORM + this->invalidate_bounding_box(); +} + +void ModelObject::rotate(double angle, Axis axis) +{ + for (ModelVolume *v : this->volumes) + { + v->rotate(angle, axis); + } + + center_around_origin(); + +#if !ENABLE_MODELVOLUME_TRANSFORM + this->origin_translation = Vec3d::Zero(); +#endif // !ENABLE_MODELVOLUME_TRANSFORM + this->invalidate_bounding_box(); +} + +void ModelObject::rotate(double angle, const Vec3d& axis) +{ + for (ModelVolume *v : this->volumes) + { + v->rotate(angle, axis); + } + + center_around_origin(); + +#if !ENABLE_MODELVOLUME_TRANSFORM + this->origin_translation = Vec3d::Zero(); +#endif // !ENABLE_MODELVOLUME_TRANSFORM + this->invalidate_bounding_box(); +} + +void ModelObject::mirror(Axis axis) +{ + for (ModelVolume *v : this->volumes) + { + v->mirror(axis); + } + +#if !ENABLE_MODELVOLUME_TRANSFORM + this->origin_translation = Vec3d::Zero(); +#endif // !ENABLE_MODELVOLUME_TRANSFORM + this->invalidate_bounding_box(); +} + +size_t ModelObject::materials_count() const +{ + std::set material_ids; + for (const ModelVolume *v : this->volumes) + material_ids.insert(v->material_id()); + return material_ids.size(); +} + +size_t ModelObject::facets_count() const +{ + size_t num = 0; + for (const ModelVolume *v : this->volumes) + if (v->is_model_part()) + num += v->mesh.stl.stats.number_of_facets; + return num; +} + +bool ModelObject::needed_repair() const +{ + for (const ModelVolume *v : this->volumes) + if (v->is_model_part() && v->mesh.needed_repair()) + return true; + return false; +} + +template static void cut_reset_transform(T *thing) { + const Vec3d offset = thing->get_offset(); + thing->set_transformation(Geometry::Transformation()); + thing->set_offset(offset); +} + +ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z) +{ + // Clone the object to duplicate instances, materials etc. + ModelObject* upper = ModelObject::new_clone(*this); + ModelObject* lower = ModelObject::new_clone(*this); + upper->set_model(nullptr); + lower->set_model(nullptr); + upper->sla_support_points.clear(); + lower->sla_support_points.clear(); + upper->clear_volumes(); + lower->clear_volumes(); + upper->input_file = ""; + lower->input_file = ""; + + const auto instance_matrix = instances[instance]->get_matrix(true); + + // Because transformations are going to be applied to meshes directly, + // we reset transformation of all instances and volumes, + // _except_ for translation, which is preserved in the transformation matrix + // and not applied to the mesh transform. + // TODO: Do the same for Z-rotation as well? + + // Convert z from relative to bb's base to object coordinates + // FIXME: doesn't work well for rotated objects + const auto bb = instance_bounding_box(instance, true); + z -= bb.min(2); + + for (auto *instance : upper->instances) { cut_reset_transform(instance); } + for (auto *instance : lower->instances) { cut_reset_transform(instance); } + + for (ModelVolume *volume : volumes) { + if (! volume->is_model_part()) { + // don't cut modifiers + upper->add_volume(*volume); + lower->add_volume(*volume); + } else { + TriangleMesh upper_mesh, lower_mesh; + + // Transform the mesh by the object transformation matrix + volume->mesh.transform(instance_matrix * volume->get_matrix(true)); + cut_reset_transform(volume); + + TriangleMeshSlicer tms(&volume->mesh); + tms.cut(z, &upper_mesh, &lower_mesh); + + upper_mesh.repair(); + lower_mesh.repair(); + upper_mesh.reset_repair_stats(); + lower_mesh.reset_repair_stats(); + + if (upper_mesh.facets_count() > 0) { + ModelVolume* vol = upper->add_volume(upper_mesh); + vol->name = volume->name; + vol->config = volume->config; + vol->set_material(volume->material_id(), *volume->material()); + } + if (lower_mesh.facets_count() > 0) { + ModelVolume* vol = lower->add_volume(lower_mesh); + vol->name = volume->name; + vol->config = volume->config; + vol->set_material(volume->material_id(), *volume->material()); + } + } + } + + upper->invalidate_bounding_box(); + lower->invalidate_bounding_box(); + + ModelObjectPtrs res; + if (upper->volumes.size() > 0) { res.push_back(upper); } + if (lower->volumes.size() > 0) { res.push_back(lower); } + + return res; +} + +void ModelObject::split(ModelObjectPtrs* new_objects) +{ + if (this->volumes.size() > 1) { + // We can't split meshes if there's more than one volume, because + // we can't group the resulting meshes by object afterwards + new_objects->emplace_back(this); + return; + } + + ModelVolume* volume = this->volumes.front(); + TriangleMeshPtrs meshptrs = volume->mesh.split(); + for (TriangleMesh *mesh : meshptrs) { + // Snap the mesh to Z=0. + float z0 = FLT_MAX; + + mesh->repair(); + + // XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed? + ModelObject* new_object = m_model->add_object(); + new_object->name = this->name; + new_object->config = this->config; + new_object->instances.reserve(this->instances.size()); + for (const ModelInstance *model_instance : this->instances) + new_object->add_instance(*model_instance); + new_object->add_volume(*volume, std::move(*mesh)); + new_objects->emplace_back(new_object); + delete mesh; + } + + return; +} + +void ModelObject::repair() +{ + for (ModelVolume *v : this->volumes) + v->mesh.repair(); +} + +double ModelObject::get_min_z() const +{ + if (instances.empty()) + return 0.0; + else + { + double min_z = DBL_MAX; + for (size_t i = 0; i < instances.size(); ++i) + { + min_z = std::min(min_z, get_instance_min_z(i)); + } + return min_z; + } +} + +double ModelObject::get_instance_min_z(size_t instance_idx) const +{ + double min_z = DBL_MAX; + + ModelInstance* inst = instances[instance_idx]; + const Transform3d& mi = inst->get_matrix(true); + + for (const ModelVolume* v : volumes) + { + if (!v->is_model_part()) + continue; + +#if ENABLE_MODELVOLUME_TRANSFORM + Transform3d mv = mi * v->get_matrix(); + const TriangleMesh& hull = v->get_convex_hull(); + for (uint32_t f = 0; f < hull.stl.stats.number_of_facets; ++f) + { + const stl_facet* facet = hull.stl.facet_start + f; + min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[0].cast())); + min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[1].cast())); + min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast())); + } +#else + for (uint32_t f = 0; f < v->mesh.stl.stats.number_of_facets; ++f) + { + const stl_facet* facet = v->mesh.stl.facet_start + f; + min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[0].cast())); + min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[1].cast())); + min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[2].cast())); + } +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + + return min_z + inst->get_offset(Z); +} + +unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) +{ + unsigned int num_printable = 0; + enum { + INSIDE = 1, + OUTSIDE = 2 + }; + for (ModelInstance *model_instance : this->instances) { + unsigned int inside_outside = 0; + for (const ModelVolume *vol : this->volumes) + if (vol->is_model_part()) { +#if ENABLE_MODELVOLUME_TRANSFORM + BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix()); +#else + BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + if (print_volume.contains(bb)) + inside_outside |= INSIDE; + else if (print_volume.intersects(bb)) + inside_outside |= INSIDE | OUTSIDE; + else + inside_outside |= OUTSIDE; + } + model_instance->print_volume_state = + (inside_outside == (INSIDE | OUTSIDE)) ? ModelInstance::PVS_Partly_Outside : + (inside_outside == INSIDE) ? ModelInstance::PVS_Inside : ModelInstance::PVS_Fully_Outside; + if (inside_outside == INSIDE) + ++ num_printable; + } + return num_printable; +} + +void ModelObject::print_info() const +{ + using namespace std; + cout << fixed; + boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl; + + TriangleMesh mesh = this->raw_mesh(); + mesh.check_topology(); + BoundingBoxf3 bb = mesh.bounding_box(); + Vec3d size = bb.size(); + cout << "size_x = " << size(0) << endl; + cout << "size_y = " << size(1) << endl; + cout << "size_z = " << size(2) << endl; + cout << "min_x = " << bb.min(0) << endl; + cout << "min_y = " << bb.min(1) << endl; + cout << "min_z = " << bb.min(2) << endl; + cout << "max_x = " << bb.max(0) << endl; + cout << "max_y = " << bb.max(1) << endl; + cout << "max_z = " << bb.max(2) << endl; + cout << "number_of_facets = " << mesh.stl.stats.number_of_facets << endl; + cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl; + + mesh.repair(); // this calculates number_of_parts + if (mesh.needed_repair()) { + mesh.repair(); + if (mesh.stl.stats.degenerate_facets > 0) + cout << "degenerate_facets = " << mesh.stl.stats.degenerate_facets << endl; + if (mesh.stl.stats.edges_fixed > 0) + cout << "edges_fixed = " << mesh.stl.stats.edges_fixed << endl; + if (mesh.stl.stats.facets_removed > 0) + cout << "facets_removed = " << mesh.stl.stats.facets_removed << endl; + if (mesh.stl.stats.facets_added > 0) + cout << "facets_added = " << mesh.stl.stats.facets_added << endl; + if (mesh.stl.stats.facets_reversed > 0) + cout << "facets_reversed = " << mesh.stl.stats.facets_reversed << endl; + if (mesh.stl.stats.backwards_edges > 0) + cout << "backwards_edges = " << mesh.stl.stats.backwards_edges << endl; + } + cout << "number_of_parts = " << mesh.stl.stats.number_of_parts << endl; + cout << "volume = " << mesh.volume() << endl; +} + +void ModelVolume::set_material_id(t_model_material_id material_id) +{ + m_material_id = material_id; + // ensure m_material_id references an existing material + if (! material_id.empty()) + this->object->get_model()->add_material(material_id); +} + +ModelMaterial* ModelVolume::material() const +{ + return this->object->get_model()->get_material(m_material_id); +} + +void ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material) +{ + m_material_id = material_id; + if (! material_id.empty()) + this->object->get_model()->add_material(material_id, material); +} + +#if ENABLE_MODELVOLUME_TRANSFORM +void ModelVolume::center_geometry() +{ + Vec3d shift = -mesh.bounding_box().center(); + mesh.translate((float)shift(0), (float)shift(1), (float)shift(2)); + m_convex_hull.translate((float)shift(0), (float)shift(1), (float)shift(2)); + translate(-shift); +} +#endif // ENABLE_MODELVOLUME_TRANSFORM + +void ModelVolume::calculate_convex_hull() +{ + m_convex_hull = mesh.convex_hull_3d(); +} + +const TriangleMesh& ModelVolume::get_convex_hull() const +{ + return m_convex_hull; +} + +ModelVolume::Type ModelVolume::type_from_string(const std::string &s) +{ + // Legacy support + if (s == "1") + return PARAMETER_MODIFIER; + // New type (supporting the support enforcers & blockers) + if (s == "ModelPart") + return MODEL_PART; + if (s == "ParameterModifier") + return PARAMETER_MODIFIER; + if (s == "SupportEnforcer") + return SUPPORT_ENFORCER; + if (s == "SupportBlocker") + return SUPPORT_BLOCKER; + assert(s == "0"); + // Default value if invalud type string received. + return MODEL_PART; +} + +std::string ModelVolume::type_to_string(const Type t) +{ + switch (t) { + case MODEL_PART: return "ModelPart"; + case PARAMETER_MODIFIER: return "ParameterModifier"; + case SUPPORT_ENFORCER: return "SupportEnforcer"; + case SUPPORT_BLOCKER: return "SupportBlocker"; + default: + assert(false); + return "ModelPart"; + } +} + +// Split this volume, append the result to the object owning this volume. +// Return the number of volumes created from this one. +// This is useful to assign different materials to different volumes of an object. +size_t ModelVolume::split(unsigned int max_extruders) +{ + TriangleMeshPtrs meshptrs = this->mesh.split(); + if (meshptrs.size() <= 1) { + delete meshptrs.front(); + return 1; + } + + size_t idx = 0; + size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin(); + std::string name = this->name; + + Model::reset_auto_extruder_id(); +#if ENABLE_MODELVOLUME_TRANSFORM + Vec3d offset = this->get_offset(); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + for (TriangleMesh *mesh : meshptrs) { + mesh->repair(); + if (idx == 0) + { + this->mesh = std::move(*mesh); + this->calculate_convex_hull(); + } + else + this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh))); + +#if ENABLE_MODELVOLUME_TRANSFORM + this->object->volumes[ivolume]->set_offset(Vec3d::Zero()); + this->object->volumes[ivolume]->center_geometry(); + this->object->volumes[ivolume]->translate(offset); +#endif // ENABLE_MODELVOLUME_TRANSFORM + this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1); + this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders)); + delete mesh; + ++ idx; + } + + return idx; +} + +void ModelVolume::translate(const Vec3d& displacement) +{ +#if ENABLE_MODELVOLUME_TRANSFORM + set_offset(get_offset() + displacement); +#else + mesh.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); + m_convex_hull.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); +#endif // ENABLE_MODELVOLUME_TRANSFORM +} + +void ModelVolume::scale(const Vec3d& scaling_factors) +{ +#if ENABLE_MODELVOLUME_TRANSFORM + set_scaling_factor(get_scaling_factor().cwiseProduct(scaling_factors)); +#else + mesh.scale(scaling_factors); + m_convex_hull.scale(scaling_factors); +#endif // ENABLE_MODELVOLUME_TRANSFORM +} + +void ModelVolume::rotate(double angle, Axis axis) +{ +#if ENABLE_MODELVOLUME_TRANSFORM + switch (axis) + { + case X: { rotate(angle, Vec3d::UnitX()); break; } + case Y: { rotate(angle, Vec3d::UnitY()); break; } + case Z: { rotate(angle, Vec3d::UnitZ()); break; } + } +#else + mesh.rotate(angle, axis); + m_convex_hull.rotate(angle, axis); +#endif // ENABLE_MODELVOLUME_TRANSFORM +} + +void ModelVolume::rotate(double angle, const Vec3d& axis) +{ +#if ENABLE_MODELVOLUME_TRANSFORM + set_rotation(get_rotation() + Geometry::extract_euler_angles(Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis)).toRotationMatrix())); +#else + mesh.rotate(angle, axis); + m_convex_hull.rotate(angle, axis); +#endif // ENABLE_MODELVOLUME_TRANSFORM +} + +void ModelVolume::mirror(Axis axis) +{ +#if ENABLE_MODELVOLUME_TRANSFORM + Vec3d mirror = get_mirror(); + switch (axis) + { + case X: { mirror(0) *= -1.0; break; } + case Y: { mirror(1) *= -1.0; break; } + case Z: { mirror(2) *= -1.0; break; } + } + set_mirror(mirror); +#else + mesh.mirror(axis); + m_convex_hull.mirror(axis); +#endif // ENABLE_MODELVOLUME_TRANSFORM +} + +#if !ENABLE_MODELVOLUME_TRANSFORM +void ModelInstance::set_rotation(const Vec3d& rotation) +{ + set_rotation(X, rotation(0)); + set_rotation(Y, rotation(1)); + set_rotation(Z, rotation(2)); +} + +void ModelInstance::set_rotation(Axis axis, double rotation) +{ + static const double TWO_PI = 2.0 * (double)PI; + while (rotation < 0.0) + { + rotation += TWO_PI; + } + while (TWO_PI < rotation) + { + rotation -= TWO_PI; + } + m_rotation(axis) = rotation; +} + +void ModelInstance::set_scaling_factor(const Vec3d& scaling_factor) +{ + set_scaling_factor(X, scaling_factor(0)); + set_scaling_factor(Y, scaling_factor(1)); + set_scaling_factor(Z, scaling_factor(2)); +} + +void ModelInstance::set_scaling_factor(Axis axis, double scaling_factor) +{ + m_scaling_factor(axis) = std::abs(scaling_factor); +} + +void ModelInstance::set_mirror(const Vec3d& mirror) +{ + set_mirror(X, mirror(0)); + set_mirror(Y, mirror(1)); + set_mirror(Z, mirror(2)); +} + +void ModelInstance::set_mirror(Axis axis, double mirror) +{ + double abs_mirror = std::abs(mirror); + if (abs_mirror == 0.0) + mirror = 1.0; + else if (abs_mirror != 1.0) + mirror /= abs_mirror; + + m_mirror(axis) = mirror; +} +#endif // !ENABLE_MODELVOLUME_TRANSFORM + +void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const +{ + mesh->transform(get_matrix(dont_translate)); +} + +BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh& mesh, bool dont_translate) const +{ + // Rotate around mesh origin. + TriangleMesh copy(mesh); + copy.transform(get_matrix(true, false, true, true)); + BoundingBoxf3 bbox = copy.bounding_box(); + + if (!empty(bbox)) { + // Scale the bounding box along the three axes. + for (unsigned int i = 0; i < 3; ++i) + { +#if ENABLE_MODELVOLUME_TRANSFORM + if (std::abs(get_scaling_factor((Axis)i)-1.0) > EPSILON) + { + bbox.min(i) *= get_scaling_factor((Axis)i); + bbox.max(i) *= get_scaling_factor((Axis)i); +#else + if (std::abs(this->m_scaling_factor(i) - 1.0) > EPSILON) + { + bbox.min(i) *= this->m_scaling_factor(i); + bbox.max(i) *= this->m_scaling_factor(i); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + } + + // Translate the bounding box. + if (! dont_translate) { +#if ENABLE_MODELVOLUME_TRANSFORM + bbox.min += get_offset(); + bbox.max += get_offset(); +#else + bbox.min += this->m_offset; + bbox.max += this->m_offset; +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + } + return bbox; +} + +BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const +{ + return bbox.transformed(get_matrix(dont_translate)); +} + +Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const +{ + return get_matrix(dont_translate) * v; +} + +void ModelInstance::transform_polygon(Polygon* polygon) const +{ +#if ENABLE_MODELVOLUME_TRANSFORM + // CHECK_ME -> Is the following correct or it should take in account all three rotations ? + polygon->rotate(get_rotation(Z)); // rotate around polygon origin + // CHECK_ME -> Is the following correct ? + polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin +#else + // CHECK_ME -> Is the following correct or it should take in account all three rotations ? + polygon->rotate(this->m_rotation(2)); // rotate around polygon origin + // CHECK_ME -> Is the following correct ? + polygon->scale(this->m_scaling_factor(0), this->m_scaling_factor(1)); // scale around polygon origin +#endif // ENABLE_MODELVOLUME_TRANSFORM +} + +#if !ENABLE_MODELVOLUME_TRANSFORM +Transform3d ModelInstance::get_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const +{ + Vec3d translation = dont_translate ? Vec3d::Zero() : m_offset; + Vec3d rotation = dont_rotate ? Vec3d::Zero() : m_rotation; + Vec3d scale = dont_scale ? Vec3d::Ones() : m_scaling_factor; + Vec3d mirror = dont_mirror ? Vec3d::Ones() : m_mirror; + return Geometry::assemble_transform(translation, rotation, scale, mirror); +} +#endif // !ENABLE_MODELVOLUME_TRANSFORM + +} diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp new file mode 100644 index 0000000000..8e3ee3f306 --- /dev/null +++ b/src/libslic3r/Model.hpp @@ -0,0 +1,657 @@ +#ifndef slic3r_Model_hpp_ +#define slic3r_Model_hpp_ + +#include "libslic3r.h" +#include "PrintConfig.hpp" +#include "Layer.hpp" +#include "Point.hpp" +#include "TriangleMesh.hpp" +#include "Slicing.hpp" +#include +#include +#include +#include +#if ENABLE_MODELVOLUME_TRANSFORM +#include "Geometry.hpp" +#endif // ENABLE_MODELVOLUME_TRANSFORM + +namespace Slic3r { + +class Model; +class ModelInstance; +class ModelMaterial; +class ModelObject; +class ModelVolume; +class Print; + +typedef std::string t_model_material_id; +typedef std::string t_model_material_attribute; +typedef std::map t_model_material_attributes; + +typedef std::map ModelMaterialMap; +typedef std::vector ModelObjectPtrs; +typedef std::vector ModelVolumePtrs; +typedef std::vector ModelInstancePtrs; + +// Unique identifier of a Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial. +// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject) +// Valid IDs are strictly positive (non zero). +// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t +// for parameter overload. +struct ModelID +{ + ModelID(size_t id) : id(id) {} + + bool operator==(const ModelID &rhs) const { return this->id == rhs.id; } + bool operator!=(const ModelID &rhs) const { return this->id != rhs.id; } + bool operator< (const ModelID &rhs) const { return this->id < rhs.id; } + bool operator> (const ModelID &rhs) const { return this->id > rhs.id; } + bool operator<=(const ModelID &rhs) const { return this->id <= rhs.id; } + bool operator>=(const ModelID &rhs) const { return this->id >= rhs.id; } + + size_t id; +}; + +// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID +// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject). +// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ModelBase derived instances +// are only instantiated from the main thread. +class ModelBase +{ +public: + ModelID id() const { return m_id; } + +protected: + // Constructors to be only called by derived classes. + // Default constructor to assign a unique ID. + ModelBase() : m_id(generate_new_id()) {} + // Constructor with ignored int parameter to assign an invalid ID, to be replaced + // by an existing ID copied from elsewhere. + ModelBase(int) : m_id(ModelID(0)) {} + + // Use with caution! + void set_new_unique_id() { m_id = generate_new_id(); } + void set_invalid_id() { m_id = 0; } + // Use with caution! + void copy_id(const ModelBase &rhs) { m_id = rhs.id(); } + + // Override this method if a ModelBase derived class owns other ModelBase derived instances. + void assign_new_unique_ids_recursive() { this->set_new_unique_id(); } + +private: + ModelID m_id; + + static inline ModelID generate_new_id() { return ModelID(++ s_last_id); } + static size_t s_last_id; +}; + +#define MODELBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \ + /* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \ + /* to make a private copy for background processing. */ \ + static TYPE* new_copy(const TYPE &rhs) { return new TYPE(rhs); } \ + static TYPE* new_copy(TYPE &&rhs) { return new TYPE(std::move(rhs)); } \ + static TYPE make_copy(const TYPE &rhs) { return TYPE(rhs); } \ + static TYPE make_copy(TYPE &&rhs) { return TYPE(std::move(rhs)); } \ + TYPE& assign_copy(const TYPE &rhs); \ + TYPE& assign_copy(TYPE &&rhs); \ + /* Copy a TYPE, generate new IDs. The front end will use this call. */ \ + static TYPE* new_clone(const TYPE &rhs) { \ + /* Default constructor assigning an invalid ID. */ \ + auto obj = new TYPE(-1); \ + obj->assign_clone(rhs); \ + return obj; \ + } \ + TYPE make_clone(const TYPE &rhs) { \ + /* Default constructor assigning an invalid ID. */ \ + TYPE obj(-1); \ + obj.assign_clone(rhs); \ + return obj; \ + } \ + TYPE& assign_clone(const TYPE &rhs) { \ + this->assign_copy(rhs); \ + this->assign_new_unique_ids_recursive(); \ + return *this; \ + } + +#define MODELBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \ +private: \ + /* Private constructor with an unused int parameter will create a TYPE instance with an invalid ID. */ \ + explicit TYPE(int) : ModelBase(-1) {}; \ + void assign_new_unique_ids_recursive(); + +// Material, which may be shared across multiple ModelObjects of a single Model. +class ModelMaterial : public ModelBase +{ +public: + // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose. + t_model_material_attributes attributes; + // Dynamic configuration storage for the object specific configuration values, overriding the global configuration. + DynamicPrintConfig config; + + Model* get_model() const { return m_model; } + void apply(const t_model_material_attributes &attributes) + { this->attributes.insert(attributes.begin(), attributes.end()); } + +protected: + friend class Model; + // Constructor, which assigns a new unique ID. + ModelMaterial(Model *model) : m_model(model) {} + // Copy constructor copies the ID and m_model! + ModelMaterial(const ModelMaterial &rhs) = default; + void set_model(Model *model) { m_model = model; } + +private: + // Parent, owning this material. + Model *m_model; + + ModelMaterial() = delete; + ModelMaterial(ModelMaterial &&rhs) = delete; + ModelMaterial& operator=(const ModelMaterial &rhs) = delete; + ModelMaterial& operator=(ModelMaterial &&rhs) = delete; +}; + +// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), +// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. +// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, +// different rotation and different uniform scaling. +class ModelObject : public ModelBase +{ + friend class Model; +public: + std::string name; + std::string input_file; // XXX: consider fs::path + // Instances of this ModelObject. Each instance defines a shift on the print bed, rotation around the Z axis and a uniform scaling. + // Instances are owned by this ModelObject. + ModelInstancePtrs instances; + // Printable and modifier volumes, each with its material ID and a set of override parameters. + // ModelVolumes are owned by this ModelObject. + ModelVolumePtrs volumes; + // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings. + DynamicPrintConfig config; + // Variation of a layer thickness for spans of Z coordinates. + t_layer_height_ranges layer_height_ranges; + // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. + // The pairs of are packed into a 1D array to simplify handling by the Perl XS. + std::vector layer_height_profile; + // layer_height_profile is initialized when the layer editing mode is entered. + // Only if the user really modified the layer height, layer_height_profile_valid is set + // and used subsequently by the PrintObject. + bool layer_height_profile_valid; + + // This vector holds position of selected support points for SLA. The data are + // saved in mesh coordinates to allow using them for several instances. + std::vector sla_support_points; + + /* This vector accumulates the total translation applied to the object by the + center_around_origin() method. Callers might want to apply the same translation + to new volumes before adding them to this object in order to preserve alignment + when user expects that. */ + Vec3d origin_translation; + + Model* get_model() { return m_model; }; + const Model* get_model() const { return m_model; }; + + ModelVolume* add_volume(const TriangleMesh &mesh); + ModelVolume* add_volume(TriangleMesh &&mesh); + ModelVolume* add_volume(const ModelVolume &volume); + ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh); + void delete_volume(size_t idx); + void clear_volumes(); + bool is_multiparts() const { return volumes.size() > 1; } + + ModelInstance* add_instance(); + ModelInstance* add_instance(const ModelInstance &instance); + ModelInstance* add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation); + void delete_instance(size_t idx); + void delete_last_instance(); + void clear_instances(); + + // Returns the bounding box of the transformed instances. + // This bounding box is approximate and not snug. + // This bounding box is being cached. + const BoundingBoxf3& bounding_box() const; + void invalidate_bounding_box() { m_bounding_box_valid = false; } + + // A mesh containing all transformed instances of this object. + TriangleMesh mesh() const; + // Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes. + // Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D platter. + TriangleMesh raw_mesh() const; + // A transformed snug bounding box around the non-modifier object volumes, without the translation applied. + // This bounding box is only used for the actual slicing. + BoundingBoxf3 raw_bounding_box() const; + // A snug bounding box around the transformed non-modifier object volumes. + BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const; + void center_around_origin(); + void ensure_on_bed(); + void translate_instances(const Vec3d& vector); + void translate_instance(size_t instance_idx, const Vec3d& vector); + void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); } + void translate(double x, double y, double z); + void scale(const Vec3d &versor); + void scale(const double s) { this->scale(Vec3d(s, s, s)); } + void scale(double x, double y, double z) { this->scale(Vec3d(x, y, z)); } + void rotate(double angle, Axis axis); + void rotate(double angle, const Vec3d& axis); + void mirror(Axis axis); + size_t materials_count() const; + size_t facets_count() const; + bool needed_repair() const; + ModelObjectPtrs cut(size_t instance, coordf_t z); + void split(ModelObjectPtrs* new_objects); + void repair(); + + double get_min_z() const; + double get_instance_min_z(size_t instance_idx) const; + + // Called by Print::validate() from the UI thread. + unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume); + + // Print object statistics to console. + void print_info() const; + +protected: + friend class Print; + // Called by Print::apply() to set the model pointer after making a copy. + void set_model(Model *model) { m_model = model; } + +private: + ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {} + ~ModelObject(); + + /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ + /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ + ModelObject(const ModelObject &rhs) : ModelBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); } + explicit ModelObject(ModelObject &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); } + ModelObject& operator=(const ModelObject &rhs) { this->assign_copy(rhs); m_model = rhs.m_model; return *this; } + ModelObject& operator=(ModelObject &&rhs) { this->assign_copy(std::move(rhs)); m_model = rhs.m_model; return *this; } + + MODELBASE_DERIVED_COPY_MOVE_CLONE(ModelObject) + MODELBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject) + + // Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized. + Model *m_model = nullptr; + + // Bounding box, cached. + mutable BoundingBoxf3 m_bounding_box; + mutable bool m_bounding_box_valid; +}; + +// An object STL, or a modifier volume, over which a different set of parameters shall be applied. +// ModelVolume instances are owned by a ModelObject. +class ModelVolume : public ModelBase +{ +public: + std::string name; + // The triangular model. + TriangleMesh mesh; + // Configuration parameters specific to an object model geometry or a modifier volume, + // overriding the global Slic3r settings and the ModelObject settings. + DynamicPrintConfig config; + + enum Type { + MODEL_TYPE_INVALID = -1, + MODEL_PART = 0, + PARAMETER_MODIFIER, + SUPPORT_ENFORCER, + SUPPORT_BLOCKER, + }; + + // A parent object owning this modifier volume. + ModelObject* get_object() const { return this->object; }; + Type type() const { return m_type; } + void set_type(const Type t) { m_type = t; } + bool is_model_part() const { return m_type == MODEL_PART; } + bool is_modifier() const { return m_type == PARAMETER_MODIFIER; } + bool is_support_enforcer() const { return m_type == SUPPORT_ENFORCER; } + bool is_support_blocker() const { return m_type == SUPPORT_BLOCKER; } + bool is_support_modifier() const { return m_type == SUPPORT_BLOCKER || m_type == SUPPORT_ENFORCER; } + t_model_material_id material_id() const { return m_material_id; } + void set_material_id(t_model_material_id material_id); + ModelMaterial* material() const; + void set_material(t_model_material_id material_id, const ModelMaterial &material); + // Split this volume, append the result to the object owning this volume. + // Return the number of volumes created from this one. + // This is useful to assign different materials to different volumes of an object. + size_t split(unsigned int max_extruders); + void translate(double x, double y, double z) { translate(Vec3d(x, y, z)); } + void translate(const Vec3d& displacement); + void scale(const Vec3d& scaling_factors); + void scale(double x, double y, double z) { scale(Vec3d(x, y, z)); } + void scale(double s) { scale(Vec3d(s, s, s)); } + void rotate(double angle, Axis axis); + void rotate(double angle, const Vec3d& axis); + void mirror(Axis axis); + +#if ENABLE_MODELVOLUME_TRANSFORM + // translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box + void center_geometry(); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + void calculate_convex_hull(); + const TriangleMesh& get_convex_hull() const; + + // Helpers for loading / storing into AMF / 3MF files. + static Type type_from_string(const std::string &s); + static std::string type_to_string(const Type t); + +#if ENABLE_MODELVOLUME_TRANSFORM + const Geometry::Transformation& get_transformation() const { return m_transformation; } + void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } + + const Vec3d& get_offset() const { return m_transformation.get_offset(); } + double get_offset(Axis axis) const { return m_transformation.get_offset(axis); } + + void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); } + void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); } + + const Vec3d& get_rotation() const { return m_transformation.get_rotation(); } + double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); } + + void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); } + void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); } + + Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); } + double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); } + + void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); } + void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); } + + const Vec3d& get_mirror() const { return m_transformation.get_mirror(); } + double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); } + + void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); } + void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); } + + const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } +#endif // ENABLE_MODELVOLUME_TRANSFORM + +protected: + friend class Print; + friend class ModelObject; + + explicit ModelVolume(const ModelVolume &rhs) = default; + void set_model_object(ModelObject *model_object) { object = model_object; } + +private: + // Parent object owning this ModelVolume. + ModelObject* object; + // Is it an object to be printed, or a modifier volume? + Type m_type; + t_model_material_id m_material_id; + // The convex hull of this model's mesh. + TriangleMesh m_convex_hull; +#if ENABLE_MODELVOLUME_TRANSFORM + Geometry::Transformation m_transformation; +#endif // ENABLE_MODELVOLUME_TRANSFORM + + ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object) + { + if (mesh.stl.stats.number_of_facets > 1) + calculate_convex_hull(); + } + ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : + mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {} + +#if ENABLE_MODELVOLUME_TRANSFORM + // Copying an existing volume, therefore this volume will get a copy of the ID assigned. + ModelVolume(ModelObject *object, const ModelVolume &other) : + ModelBase(other), // copy the ID + name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) + { + this->set_material_id(other.material_id()); + } + // Providing a new mesh, therefore this volume will get a new unique ID assigned. + ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : + name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) + { + this->set_material_id(other.material_id()); + if (mesh.stl.stats.number_of_facets > 1) + calculate_convex_hull(); + } +#else + // Copying an existing volume, therefore this volume will get a copy of the ID assigned. + ModelVolume(ModelObject *object, const ModelVolume &other) : + ModelBase(other), // copy the ID + name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object) + { + if (! other.material_id().empty()) + this->set_material_id(other.material_id()); + } + // Providing a new mesh, therefore this volume will get a new unique ID assigned. + ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) : + name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object) + { + if (! other.material_id().empty()) + this->set_material_id(other.material_id()); + if (mesh.stl.stats.number_of_facets > 1) + calculate_convex_hull(); + } +#endif // ENABLE_MODELVOLUME_TRANSFORM + + ModelVolume& operator=(ModelVolume &rhs) = delete; +}; + +// A single instance of a ModelObject. +// Knows the affine transformation of an object. +class ModelInstance : public ModelBase +{ +public: + enum EPrintVolumeState : unsigned char + { + PVS_Inside, + PVS_Partly_Outside, + PVS_Fully_Outside, + Num_BedStates + }; + +private: +#if ENABLE_MODELVOLUME_TRANSFORM + Geometry::Transformation m_transformation; +#else + Vec3d m_offset; // in unscaled coordinates + Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point + Vec3d m_scaling_factor; // Scaling factors along the three axes + Vec3d m_mirror; // Mirroring along the three axes +#endif // ENABLE_MODELVOLUME_TRANSFORM + +public: + // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state()) + EPrintVolumeState print_volume_state; + + ModelObject* get_object() const { return this->object; } + +#if ENABLE_MODELVOLUME_TRANSFORM + const Geometry::Transformation& get_transformation() const { return m_transformation; } + void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } + + const Vec3d& get_offset() const { return m_transformation.get_offset(); } + double get_offset(Axis axis) const { return m_transformation.get_offset(axis); } + + void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); } + void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); } + + const Vec3d& get_rotation() const { return m_transformation.get_rotation(); } + double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); } + + void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); } + void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); } + + Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); } + double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); } + + void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); } + void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); } + + const Vec3d& get_mirror() const { return m_transformation.get_mirror(); } + double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); } + + void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); } + void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); } +#else + const Vec3d& get_offset() const { return m_offset; } + double get_offset(Axis axis) const { return m_offset(axis); } + + void set_offset(const Vec3d& offset) { m_offset = offset; } + void set_offset(Axis axis, double offset) { m_offset(axis) = offset; } + + const Vec3d& get_rotation() const { return m_rotation; } + double get_rotation(Axis axis) const { return m_rotation(axis); } + + void set_rotation(const Vec3d& rotation); + void set_rotation(Axis axis, double rotation); + + Vec3d get_scaling_factor() const { return m_scaling_factor; } + double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); } + + void set_scaling_factor(const Vec3d& scaling_factor); + void set_scaling_factor(Axis axis, double scaling_factor); + + const Vec3d& get_mirror() const { return m_mirror; } + double get_mirror(Axis axis) const { return m_mirror(axis); } + + void set_mirror(const Vec3d& mirror); + void set_mirror(Axis axis, double mirror); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + // To be called on an external mesh + void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; + // Calculate a bounding box of a transformed mesh. To be called on an external mesh. + BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh& mesh, bool dont_translate = false) const; + // Transform an external bounding box. + BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const; + // Transform an external vector. + Vec3d transform_vector(const Vec3d& v, bool dont_translate = false) const; + // To be called on an external polygon. It does not translate the polygon, only rotates and scales. + void transform_polygon(Polygon* polygon) const; + +#if ENABLE_MODELVOLUME_TRANSFORM + const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } +#else + Transform3d get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const; +#endif // ENABLE_MODELVOLUME_TRANSFORM + + bool is_printable() const { return print_volume_state == PVS_Inside; } + +protected: + friend class Print; + friend class ModelObject; + + explicit ModelInstance(const ModelInstance &rhs) = default; + void set_model_object(ModelObject *model_object) { object = model_object; } + +private: + // Parent object, owning this instance. + ModelObject* object; + +#if ENABLE_MODELVOLUME_TRANSFORM + // Constructor, which assigns a new unique ID. + explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {} + // Constructor, which assigns a new unique ID. + explicit ModelInstance(ModelObject *object, const ModelInstance &other) : + m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {} +#else + explicit ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {} + explicit ModelInstance(ModelObject *object, const ModelInstance &other) : + m_offset(other.m_offset), m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_mirror(other.m_mirror), object(object), print_volume_state(PVS_Inside) {} +#endif // ENABLE_MODELVOLUME_TRANSFORM + + ModelInstance() = delete; + explicit ModelInstance(ModelInstance &&rhs) = delete; + ModelInstance& operator=(const ModelInstance &rhs) = delete; + ModelInstance& operator=(ModelInstance &&rhs) = delete; +}; + +// The print bed content. +// Description of a triangular model with multiple materials, multiple instances with various affine transformations +// and with multiple modifier meshes. +// A model groups multiple objects, each object having possibly multiple instances, +// all objects may share mutliple materials. +class Model : public ModelBase +{ + static unsigned int s_auto_extruder_id; + +public: + // Materials are owned by a model and referenced by objects through t_model_material_id. + // Single material may be shared by multiple models. + ModelMaterialMap materials; + // Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation). + ModelObjectPtrs objects; + + // Default constructor assigns a new ID to the model. + Model() {} + ~Model() { this->clear_objects(); this->clear_materials(); } + + /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ + /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ + Model(const Model &rhs) : ModelBase(-1) { this->assign_copy(rhs); } + explicit Model(Model &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); } + Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; } + Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); return *this; } + + MODELBASE_DERIVED_COPY_MOVE_CLONE(Model) + + static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true); + static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true); + + /// Repair the ModelObjects of the current Model. + /// This function calls repair function on each TriangleMesh of each model object volume + void repair(); + + // Add a new ModelObject to this Model, generate a new ID for this ModelObject. + ModelObject* add_object(); + ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh); + ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh); + ModelObject* add_object(const ModelObject &other); + void delete_object(size_t idx); + bool delete_object(ModelID id); + bool delete_object(ModelObject* object); + void clear_objects(); + + ModelMaterial* add_material(t_model_material_id material_id); + ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other); + ModelMaterial* get_material(t_model_material_id material_id) { + ModelMaterialMap::iterator i = this->materials.find(material_id); + return (i == this->materials.end()) ? nullptr : i->second; + } + + void delete_material(t_model_material_id material_id); + void clear_materials(); + bool add_default_instances(); + // Returns approximate axis aligned bounding box of this model + BoundingBoxf3 bounding_box() const; + // Set the print_volume_state of PrintObject::instances, + // return total number of printable objects. + unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume); + // Returns true if any ModelObject was modified. + bool center_instances_around_point(const Vec2d &point); + void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); } + TriangleMesh mesh() const; + bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL); + // Croaks if the duplicated objects do not fit the print bed. + void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); + void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); + void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); + + bool looks_like_multipart_object() const; + void convert_multipart_object(unsigned int max_extruders); + + // Ensures that the min z of the model is not negative + void adjust_min_z(); + + void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); } + + static unsigned int get_auto_extruder_id(unsigned int max_extruders); + static std::string get_auto_extruder_id_as_string(unsigned int max_extruders); + static void reset_auto_extruder_id(); + +private: + MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model) +}; + +#undef MODELBASE_DERIVED_COPY_MOVE_CLONE +#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE + +} + +#endif diff --git a/xs/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.cpp similarity index 71% rename from xs/src/libslic3r/ModelArrange.hpp rename to src/libslic3r/ModelArrange.cpp index b1ccf4d13f..9d59238faa 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/ModelArrange.cpp @@ -1,8 +1,7 @@ -#ifndef MODELARRANGE_HPP -#define MODELARRANGE_HPP - +#include "ModelArrange.hpp" #include "Model.hpp" #include "SVG.hpp" + #include #include @@ -11,6 +10,7 @@ #include namespace Slic3r { + namespace arr { using namespace libnest2d; @@ -29,7 +29,8 @@ std::string toString(const Model& model, bool holes = true) { if(!objinst) continue; Slic3r::TriangleMesh tmpmesh = rmesh; - tmpmesh.scale(objinst->scaling_factor); + // CHECK_ME -> Is the following correct ? + tmpmesh.scale(objinst->get_scaling_factor()); objinst->transform_mesh(&tmpmesh); ExPolygons expolys = tmpmesh.horizontal_projection(); for(auto& expoly_complex : expolys) { @@ -44,11 +45,11 @@ std::string toString(const Model& model, bool holes = true) { ss << "\t\t{\n"; for(auto v : expoly.contour.points) ss << "\t\t\t{" - << v.x << ", " - << v.y << "},\n"; + << v(0) << ", " + << v(1) << "},\n"; { auto v = expoly.contour.points.front(); - ss << "\t\t\t{" << v.x << ", " << v.y << "},\n"; + ss << "\t\t\t{" << v(0) << ", " << v(1) << "},\n"; } ss << "\t\t},\n"; @@ -57,11 +58,11 @@ std::string toString(const Model& model, bool holes = true) { if(holes) for(auto h : expoly.holes) { ss << "\t\t\t{\n"; for(auto v : h.points) ss << "\t\t\t\t{" - << v.x << ", " - << v.y << "},\n"; + << v(0) << ", " + << v(1) << "},\n"; { auto v = h.points.front(); - ss << "\t\t\t\t{" << v.x << ", " << v.y << "},\n"; + ss << "\t\t\t\t{" << v(0) << ", " << v(1) << "},\n"; } ss << "\t\t\t},\n"; } @@ -87,7 +88,7 @@ void toSVG(SVG& svg, const Model& model) { if(!objinst) continue; Slic3r::TriangleMesh tmpmesh = rmesh; - tmpmesh.scale(objinst->scaling_factor); + tmpmesh.scale(objinst->get_scaling_factor()); objinst->transform_mesh(&tmpmesh); ExPolygons expolys = tmpmesh.horizontal_projection(); svg.draw(expolys); @@ -292,57 +293,59 @@ protected: using Distance = TCoord; using Pile = sl::Shapes; - Packer pck_; - PConfig pconf_; // Placement configuration - double bin_area_; - SpatIndex rtree_; - SpatIndex smallsrtree_; - double norm_; - Pile merged_pile_; - Box pilebb_; - ItemGroup remaining_; - ItemGroup items_; + Packer m_pck; + PConfig m_pconf; // Placement configuration + double m_bin_area; + SpatIndex m_rtree; + SpatIndex m_smallsrtree; + double m_norm; + Pile m_merged_pile; + Box m_pilebb; + ItemGroup m_remaining; + ItemGroup m_items; public: _ArrBase(const TBin& bin, Distance dist, - std::function progressind): - pck_(bin, dist), bin_area_(sl::area(bin)), - norm_(std::sqrt(sl::area(bin))) + std::function progressind, + std::function stopcond): + m_pck(bin, dist), m_bin_area(sl::area(bin)), + m_norm(std::sqrt(sl::area(bin))) { - fillConfig(pconf_); + fillConfig(m_pconf); - pconf_.before_packing = + m_pconf.before_packing = [this](const Pile& merged_pile, // merged pile const ItemGroup& items, // packed items const ItemGroup& remaining) // future items to be packed { - items_ = items; - merged_pile_ = merged_pile; - remaining_ = remaining; + m_items = items; + m_merged_pile = merged_pile; + m_remaining = remaining; - pilebb_ = sl::boundingBox(merged_pile); + m_pilebb = sl::boundingBox(merged_pile); - rtree_.clear(); - smallsrtree_.clear(); + m_rtree.clear(); + m_smallsrtree.clear(); // We will treat big items (compared to the print bed) differently auto isBig = [this](double a) { - return a/bin_area_ > BIG_ITEM_TRESHOLD ; + return a/m_bin_area > BIG_ITEM_TRESHOLD ; }; for(unsigned idx = 0; idx < items.size(); ++idx) { Item& itm = items[idx]; - if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx}); - smallsrtree_.insert({itm.boundingBox(), idx}); + if(isBig(itm.area())) m_rtree.insert({itm.boundingBox(), idx}); + m_smallsrtree.insert({itm.boundingBox(), idx}); } }; - pck_.progressIndicator(progressind); + m_pck.progressIndicator(progressind); + m_pck.stopCondition(stopcond); } template inline IndexedPackGroup operator()(Args&&...args) { - rtree_.clear(); - return pck_.executeIndexed(std::forward(args)...); + m_rtree.clear(); + return m_pck.executeIndexed(std::forward(args)...); } }; @@ -351,22 +354,23 @@ class AutoArranger: public _ArrBase { public: AutoArranger(const Box& bin, Distance dist, - std::function progressind): - _ArrBase(bin, dist, progressind) + std::function progressind, + std::function stopcond): + _ArrBase(bin, dist, progressind, stopcond) { - pconf_.object_function = [this, bin] (const Item &item) { + m_pconf.object_function = [this, bin] (const Item &item) { auto result = objfunc(bin.center(), - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, - bin_area_, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_bin_area, + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); double score = std::get<0>(result); auto& fullbb = std::get<1>(result); @@ -378,41 +382,46 @@ public: return score; }; - pck_.configure(pconf_); + m_pck.configure(m_pconf); } }; using lnCircle = libnest2d::_Circle; +inline lnCircle to_lnCircle(const Circle& circ) { + return lnCircle({circ.center()(0), circ.center()(1)}, circ.radius()); +} + template<> class AutoArranger: public _ArrBase { public: AutoArranger(const lnCircle& bin, Distance dist, - std::function progressind): - _ArrBase(bin, dist, progressind) { + std::function progressind, + std::function stopcond): + _ArrBase(bin, dist, progressind, stopcond) { - pconf_.object_function = [this, &bin] (const Item &item) { + m_pconf.object_function = [this, &bin] (const Item &item) { auto result = objfunc(bin.center(), - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, - bin_area_, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_bin_area, + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); double score = std::get<0>(result); auto isBig = [this](const Item& itm) { - return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ; + return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; }; if(isBig(item)) { - auto mp = merged_pile_; + auto mp = m_merged_pile; mp.push_back(item.transformedShape()); auto chull = sl::convexHull(mp); double miss = Placer::overfit(chull, bin); @@ -423,7 +432,7 @@ public: return score; }; - pck_.configure(pconf_); + m_pck.configure(m_pconf); } }; @@ -431,28 +440,29 @@ template<> class AutoArranger: public _ArrBase { public: AutoArranger(const PolygonImpl& bin, Distance dist, - std::function progressind): - _ArrBase(bin, dist, progressind) + std::function progressind, + std::function stopcond): + _ArrBase(bin, dist, progressind, stopcond) { - pconf_.object_function = [this, &bin] (const Item &item) { + m_pconf.object_function = [this, &bin] (const Item &item) { auto binbb = sl::boundingBox(bin); auto result = objfunc(binbb.center(), - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, - bin_area_, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_bin_area, + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); double score = std::get<0>(result); return score; }; - pck_.configure(pconf_); + m_pck.configure(m_pconf); } }; @@ -460,25 +470,26 @@ template<> // Specialization with no bin class AutoArranger: public _ArrBase { public: - AutoArranger(Distance dist, std::function progressind): - _ArrBase(Box(0, 0), dist, progressind) + AutoArranger(Distance dist, std::function progressind, + std::function stopcond): + _ArrBase(Box(0, 0), dist, progressind, stopcond) { - this->pconf_.object_function = [this] (const Item &item) { + this->m_pconf.object_function = [this] (const Item &item) { auto result = objfunc({0, 0}, - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, 0, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); return std::get<0>(result); }; - this->pck_.configure(pconf_); + this->m_pck.configure(m_pconf); } }; @@ -490,7 +501,7 @@ using ShapeData2D = ShapeData2D projectModelFromTop(const Slic3r::Model &model) { ShapeData2D ret; - auto s = std::accumulate(model.objects.begin(), model.objects.end(), 0, + auto s = std::accumulate(model.objects.begin(), model.objects.end(), size_t(0), [](size_t s, ModelObject* o){ return s + o->instances.size(); }); @@ -507,7 +518,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { Slic3r::TriangleMesh tmpmesh = rmesh; ClipperLib::PolygonImpl pn; - tmpmesh.scale(objinst->scaling_factor); + // CHECK_ME -> is the following correct ? + tmpmesh.scale(objinst->get_scaling_factor()); // TODO export the exact 2D projection auto p = tmpmesh.convex_hull(); @@ -521,10 +533,11 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { // Invalid geometries would throw exceptions when arranging if(item.vertexCount() > 3) { - item.rotation(objinst->rotation); - item.translation( { - ClipperLib::cInt(objinst->offset.x/SCALING_FACTOR), - ClipperLib::cInt(objinst->offset.y/SCALING_FACTOR) + // CHECK_ME -> is the following correct or it should take in account all three rotations ? + item.rotation(objinst->get_rotation(Z)); + item.translation({ + ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR), + ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR) }); ret.emplace_back(objinst, item); } @@ -536,46 +549,44 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { return ret; } -class Circle { - Point center_; - double radius_; -public: +void applyResult( + IndexedPackGroup::value_type& group, + Coord batch_offset, + ShapeData2D& shapemap) +{ + for(auto& r : group) { + auto idx = r.first; // get the original item index + Item& item = r.second; // get the item itself - inline Circle(): center_(0, 0), radius_(std::nan("")) {} - inline Circle(const Point& c, double r): center_(c), radius_(r) {} + // Get the model instance from the shapemap using the index + ModelInstance *inst_ptr = shapemap[idx].first; - inline double radius() const { return radius_; } - inline const Point& center() const { return center_; } - inline operator bool() { return !std::isnan(radius_); } -}; + // Get the transformation data from the item object and scale it + // appropriately + auto off = item.translation(); + Radians rot = item.rotation(); + Vec3d foff(off.X*SCALING_FACTOR + batch_offset, + off.Y*SCALING_FACTOR, + inst_ptr->get_offset()(2)); -enum class BedShapeType { - BOX, - CIRCLE, - IRREGULAR, - WHO_KNOWS -}; - -struct BedShapeHint { - BedShapeType type; - /*union*/ struct { // I know but who cares... - Circle circ; - BoundingBox box; - Polyline polygon; - } shape; -}; - -BedShapeHint bedShape(const Polyline& bed) { - static const double E = 10/SCALING_FACTOR; + // write the transformation data into the model instance + inst_ptr->set_rotation(Z, rot); + inst_ptr->set_offset(foff); + } +} +BedShapeHint bedShape(const Polyline &bed) { BedShapeHint ret; - auto width = [](const BoundingBox& box) { - return box.max.x - box.min.x; + auto x = [](const Point& p) { return p(0); }; + auto y = [](const Point& p) { return p(1); }; + + auto width = [x](const BoundingBox& box) { + return x(box.max) - x(box.min); }; - auto height = [](const BoundingBox& box) { - return box.max.y - box.min.y; + auto height = [y](const BoundingBox& box) { + return y(box.max) - y(box.min); }; auto area = [&width, &height](const BoundingBox& box) { @@ -591,16 +602,21 @@ BedShapeHint bedShape(const Polyline& bed) { return std::abs(pp.area()); }; + auto distance_to = [x, y](const Point& p1, const Point& p2) { + double dx = x(p2) - x(p1); + double dy = y(p2) - y(p1); + return std::sqrt(dx*dx + dy*dy); + }; auto bb = bed.bounding_box(); - auto isCircle = [bb](const Polyline& polygon) { + auto isCircle = [bb, distance_to](const Polyline& polygon) { auto center = bb.center(); std::vector vertex_distances; double avg_dist = 0; for (auto pt: polygon.points) { - double distance = center.distance_to(pt); + double distance = distance_to(center, pt); vertex_distances.push_back(distance); avg_dist += distance; } @@ -636,60 +652,13 @@ BedShapeHint bedShape(const Polyline& bed) { return ret; } -void applyResult( - IndexedPackGroup::value_type& group, - Coord batch_offset, - ShapeData2D& shapemap) -{ - for(auto& r : group) { - auto idx = r.first; // get the original item index - Item& item = r.second; // get the item itself - - // Get the model instance from the shapemap using the index - ModelInstance *inst_ptr = shapemap[idx].first; - - // Get the tranformation data from the item object and scale it - // appropriately - auto off = item.translation(); - Radians rot = item.rotation(); - Pointf foff(off.X*SCALING_FACTOR + batch_offset, - off.Y*SCALING_FACTOR); - - // write the tranformation data into the model instance - inst_ptr->rotation = rot; - inst_ptr->offset = foff; - } -} - - -/** - * \brief Arranges the model objects on the screen. - * - * The arrangement considers multiple bins (aka. print beds) for placing all - * the items provided in the model argument. If the items don't fit on one - * print bed, the remaining will be placed onto newly created print beds. - * The first_bin_only parameter, if set to true, disables this behaviour and - * makes sure that only one print bed is filled and the remaining items will be - * untouched. When set to false, the items which could not fit onto the - * print bed will be placed next to the print bed so the user should see a - * pile of items on the print bed and some other piles outside the print - * area that can be dragged later onto the print bed as a group. - * - * \param model The model object with the 3D content. - * \param dist The minimum distance which is allowed for any pair of items - * on the print bed in any direction. - * \param bb The bounding box of the print bed. It corresponds to the 'bin' - * for bin packing. - * \param first_bin_only This parameter controls whether to place the - * remaining items which do not fit onto the print area next to the print - * bed or leave them untouched (let the user arrange them by hand or remove - * them). - */ -bool arrange(Model &model, coordf_t min_obj_distance, - const Slic3r::Polyline& bed, +bool arrange(Model &model, + coord_t min_obj_distance, + const Polyline &bed, BedShapeHint bedhint, bool first_bin_only, - std::function progressind) + std::function progressind, + std::function stopcondition) { using ArrangeResult = _IndexedPackGroup; @@ -710,24 +679,27 @@ bool arrange(Model &model, coordf_t min_obj_distance, IndexedPackGroup result; + // If there is no hint about the shape, we will try to guess if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed); BoundingBox bbb(bed); + auto& cfn = stopcondition; + auto binbb = Box({ - static_cast(bbb.min.x), - static_cast(bbb.min.y) + static_cast(bbb.min(0)), + static_cast(bbb.min(1)) }, - { - static_cast(bbb.max.x), - static_cast(bbb.max.y) + { + static_cast(bbb.max(0)), + static_cast(bbb.max(1)) }); switch(bedhint.type) { case BedShapeType::BOX: { // Create the arranger for the box shaped bed - AutoArranger arrange(binbb, min_obj_distance, progressind); + AutoArranger arrange(binbb, min_obj_distance, progressind, cfn); // Arrange and return the items with their respective indices within the // input sequence. @@ -737,9 +709,9 @@ bool arrange(Model &model, coordf_t min_obj_distance, case BedShapeType::CIRCLE: { auto c = bedhint.shape.circ; - auto cc = lnCircle({c.center().x, c.center().y} , c.radius()); + auto cc = to_lnCircle(c); - AutoArranger arrange(cc, min_obj_distance, progressind); + AutoArranger arrange(cc, min_obj_distance, progressind, cfn); result = arrange(shapes.begin(), shapes.end()); break; } @@ -751,7 +723,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); P irrbed = sl::create(std::move(ctour)); - AutoArranger

arrange(irrbed, min_obj_distance, progressind); + AutoArranger

arrange(irrbed, min_obj_distance, progressind, cfn); // Arrange and return the items with their respective indices within the // input sequence. @@ -760,7 +732,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, } }; - if(result.empty()) return false; + if(result.empty() || stopcondition()) return false; if(first_bin_only) { applyResult(result.front(), 0, shapemap); @@ -789,4 +761,3 @@ bool arrange(Model &model, coordf_t min_obj_distance, } } -#endif // MODELARRANGE_HPP diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp new file mode 100644 index 0000000000..78a4006670 --- /dev/null +++ b/src/libslic3r/ModelArrange.hpp @@ -0,0 +1,82 @@ +#ifndef MODELARRANGE_HPP +#define MODELARRANGE_HPP + +#include "Model.hpp" + +namespace Slic3r { + +class Model; + +namespace arr { + +class Circle { + Point center_; + double radius_; +public: + + inline Circle(): center_(0, 0), radius_(std::nan("")) {} + inline Circle(const Point& c, double r): center_(c), radius_(r) {} + + inline double radius() const { return radius_; } + inline const Point& center() const { return center_; } + inline operator bool() { return !std::isnan(radius_); } +// inline operator lnCircle() { +// return lnCircle({center_(0), center_(1)}, radius_); +// } +}; + +enum class BedShapeType { + BOX, + CIRCLE, + IRREGULAR, + WHO_KNOWS +}; + +struct BedShapeHint { + BedShapeType type; + /*union*/ struct { // I know but who cares... + Circle circ; + BoundingBox box; + Polyline polygon; + } shape; +}; + +BedShapeHint bedShape(const Polyline& bed); + +/** + * \brief Arranges the model objects on the screen. + * + * The arrangement considers multiple bins (aka. print beds) for placing all + * the items provided in the model argument. If the items don't fit on one + * print bed, the remaining will be placed onto newly created print beds. + * The first_bin_only parameter, if set to true, disables this behavior and + * makes sure that only one print bed is filled and the remaining items will be + * untouched. When set to false, the items which could not fit onto the + * print bed will be placed next to the print bed so the user should see a + * pile of items on the print bed and some other piles outside the print + * area that can be dragged later onto the print bed as a group. + * + * \param model The model object with the 3D content. + * \param dist The minimum distance which is allowed for any pair of items + * on the print bed in any direction. + * \param bb The bounding box of the print bed. It corresponds to the 'bin' + * for bin packing. + * \param first_bin_only This parameter controls whether to place the + * remaining items which do not fit onto the print area next to the print + * bed or leave them untouched (let the user arrange them by hand or remove + * them). + * \param progressind Progress indicator callback called when an object gets + * packed. The unsigned argument is the number of items remaining to pack. + * \param stopcondition A predicate returning true if abort is needed. + */ +bool arrange(Model &model, coord_t min_obj_distance, + const Slic3r::Polyline& bed, + BedShapeHint bedhint, + bool first_bin_only, + std::function progressind, + std::function stopcondition); + +} + +} +#endif // MODELARRANGE_HPP diff --git a/xs/src/libslic3r/MotionPlanner.cpp b/src/libslic3r/MotionPlanner.cpp similarity index 97% rename from xs/src/libslic3r/MotionPlanner.cpp rename to src/libslic3r/MotionPlanner.cpp index e8605d68ca..198c39f311 100644 --- a/xs/src/libslic3r/MotionPlanner.cpp +++ b/src/libslic3r/MotionPlanner.cpp @@ -58,7 +58,7 @@ Polyline MotionPlanner::shortest_path(const Point &from, const Point &to) { // If we have an empty configuration space, return a straight move. if (m_islands.empty()) - return Line(from, to); + return Polyline(from, to); // Are both points in the same island? int island_idx_from = -1; @@ -74,7 +74,7 @@ Polyline MotionPlanner::shortest_path(const Point &from, const Point &to) // Since both points are in the same island, is a direct move possible? // If so, we avoid generating the visibility environment. if (island.m_island.contains(Line(from, to))) - return Line(from, to); + return Polyline(from, to); // Both points are inside a single island, but the straight line crosses the island boundary. island_idx = idx; break; @@ -90,7 +90,7 @@ Polyline MotionPlanner::shortest_path(const Point &from, const Point &to) if (env.m_env.expolygons.empty()) { // if this environment is empty (probably because it's too small), perform straight move // and avoid running the algorithms on empty dataset - return Line(from, to); + return Polyline(from, to); } // Now check whether points are inside the environment. @@ -224,7 +224,7 @@ const MotionPlannerGraph& MotionPlanner::init_graph(int island_idx) else v1_idx = i_v1->second; // Euclidean distance is used as weight for the graph edge - graph->add_edge(v0_idx, v1_idx, p0.distance_to(p1)); + graph->add_edge(v0_idx, v1_idx, (p1 - p0).cast().norm()); } } } @@ -238,7 +238,7 @@ static inline size_t nearest_waypoint_index(const Point &start_point, const Poin size_t idx = size_t(-1); double dmin = std::numeric_limits::infinity(); for (const Point &p : middle_points) { - double d = start_point.distance_to(p) + p.distance_to(end_point); + double d = (p - start_point).cast().norm() + (end_point - p).cast().norm(); if (d < dmin) { idx = &p - middle_points.data(); dmin = d; @@ -297,7 +297,7 @@ void MotionPlannerGraph::add_edge(size_t from, size_t to, double weight) // Extend adjacency list until this start node. if (m_adjacency_list.size() < from + 1) { // Reserve in powers of two to avoid repeated reallocation. - m_adjacency_list.reserve(std::max(8, next_highest_power_of_2(from + 1))); + m_adjacency_list.reserve(std::max(8, next_highest_power_of_2((uint32_t)(from + 1)))); // Allocate new empty adjacency vectors. m_adjacency_list.resize(from + 1); } diff --git a/xs/src/libslic3r/MotionPlanner.hpp b/src/libslic3r/MotionPlanner.hpp similarity index 100% rename from xs/src/libslic3r/MotionPlanner.hpp rename to src/libslic3r/MotionPlanner.hpp diff --git a/xs/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp similarity index 65% rename from xs/src/libslic3r/MultiPoint.cpp rename to src/libslic3r/MultiPoint.cpp index 2e65492cd8..6e49be958f 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -8,59 +8,61 @@ MultiPoint::operator Points() const return this->points; } -void -MultiPoint::scale(double factor) +void MultiPoint::scale(double factor) { - for (Points::iterator it = points.begin(); it != points.end(); ++it) { - (*it).scale(factor); + for (Point &pt : points) + pt *= factor; +} + +void MultiPoint::scale(double factor_x, double factor_y) +{ + for (Point &pt : points) + { + pt(0) *= factor_x; + pt(1) *= factor_y; } } -void -MultiPoint::translate(double x, double y) +void MultiPoint::translate(double x, double y) { - for (Points::iterator it = points.begin(); it != points.end(); ++it) { - (*it).translate(x, y); - } + Vector v(x, y); + for (Point &pt : points) + pt += v; } -void -MultiPoint::translate(const Point &vector) +void MultiPoint::translate(const Point &v) { - this->translate(vector.x, vector.y); + for (Point &pt : points) + pt += v; } void MultiPoint::rotate(double cos_angle, double sin_angle) { for (Point &pt : this->points) { - double cur_x = double(pt.x); - double cur_y = double(pt.y); - pt.x = coord_t(round(cos_angle * cur_x - sin_angle * cur_y)); - pt.y = coord_t(round(cos_angle * cur_y + sin_angle * cur_x)); + double cur_x = double(pt(0)); + double cur_y = double(pt(1)); + pt(0) = coord_t(round(cos_angle * cur_x - sin_angle * cur_y)); + pt(1) = coord_t(round(cos_angle * cur_y + sin_angle * cur_x)); } } -void -MultiPoint::rotate(double angle, const Point ¢er) +void MultiPoint::rotate(double angle, const Point ¢er) { double s = sin(angle); double c = cos(angle); - for (Points::iterator it = points.begin(); it != points.end(); ++it) { - double dx = double(it->x - center.x); - double dy = double(it->y - center.y); - it->x = (coord_t)round(double(center.x) + c * dx - s * dy); - it->y = (coord_t)round(double(center.y) + c * dy + s * dx); + for (Point &pt : points) { + Vec2crd v(pt - center); + pt(0) = (coord_t)round(double(center(0)) + c * v[0] - s * v[1]); + pt(1) = (coord_t)round(double(center(1)) + c * v[1] + s * v[0]); } } -void -MultiPoint::reverse() +void MultiPoint::reverse() { std::reverse(this->points.begin(), this->points.end()); } -Point -MultiPoint::first_point() const +Point MultiPoint::first_point() const { return this->points.front(); } @@ -79,16 +81,16 @@ MultiPoint::length() const int MultiPoint::find_point(const Point &point) const { - for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) { - if (it->coincides_with(point)) return it - this->points.begin(); - } + for (const Point &pt : this->points) + if (pt == point) + return &pt - &this->points.front(); return -1; // not found } bool MultiPoint::has_boundary_point(const Point &point) const { - double dist = point.distance_to(point.projection_onto(*this)); + double dist = (point.projection_onto(*this) - point).cast().norm(); return dist < SCALED_EPSILON; } @@ -102,7 +104,7 @@ bool MultiPoint::has_duplicate_points() const { for (size_t i = 1; i < points.size(); ++i) - if (points[i-1].coincides_with(points[i])) + if (points[i-1] == points[i]) return true; return false; } @@ -112,7 +114,7 @@ MultiPoint::remove_duplicate_points() { size_t j = 0; for (size_t i = 1; i < points.size(); ++i) { - if (points[j].coincides_with(points[i])) { + if (points[j] == points[i]) { // Just increase index i. } else { ++ j; @@ -146,10 +148,10 @@ bool MultiPoint::first_intersection(const Line& line, Point* intersection) const if (l.intersection(line, &ip)) { if (! found) { found = true; - dmin = ip.distance_to(line.a); + dmin = (line.a - ip).cast().norm(); *intersection = ip; } else { - double d = ip.distance_to(line.a); + double d = (line.a - ip).cast().norm(); if (d < dmin) { dmin = d; *intersection = ip; @@ -160,19 +162,6 @@ bool MultiPoint::first_intersection(const Line& line, Point* intersection) const return found; } -std::string -MultiPoint::dump_perl() const -{ - std::ostringstream ret; - ret << "["; - for (Points::const_iterator p = this->points.begin(); p != this->points.end(); ++p) { - ret << p->dump_perl(); - if (p != this->points.end()-1) ret << ","; - } - ret << "]"; - return ret.str(); -} - //FIXME This is very inefficient in term of memory use. // The recursive algorithm shall run in place, not allocating temporary data in each recursion. Points @@ -185,7 +174,7 @@ MultiPoint::_douglas_peucker(const Points &points, const double tolerance) Line full(points.front(), points.back()); for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) { // we use shortest distance, not perpendicular distance - double d = it->distance_to(full); + double d = full.distance_to(*it); if (d > dmax) { index = it - points.begin(); dmax = d; @@ -216,25 +205,22 @@ MultiPoint::_douglas_peucker(const Points &points, const double tolerance) void MultiPoint3::translate(double x, double y) { - for (Point3& p : points) - { - p.translate(x, y); + for (Vec3crd &p : points) { + p(0) += x; + p(1) += y; } } void MultiPoint3::translate(const Point& vector) { - translate(vector.x, vector.y); + this->translate(vector(0), vector(1)); } double MultiPoint3::length() const { - Lines3 lines = this->lines(); double len = 0.0; - for (const Line3& line : lines) - { + for (const Line3& line : this->lines()) len += line.length(); - } return len; } @@ -246,15 +232,11 @@ BoundingBox3 MultiPoint3::bounding_box() const bool MultiPoint3::remove_duplicate_points() { size_t j = 0; - for (size_t i = 1; i < points.size(); ++i) - { - if (points[j].coincides_with(points[i])) - { + for (size_t i = 1; i < points.size(); ++i) { + if (points[j] == points[i]) { // Just increase index i. - } - else - { - ++j; + } else { + ++ j; if (j < i) points[j] = points[i]; } @@ -281,19 +263,19 @@ BoundingBox get_extents_rotated(const Points &points, double angle) double s = sin(angle); double c = cos(angle); Points::const_iterator it = points.begin(); - double cur_x = (double)it->x; - double cur_y = (double)it->y; - bbox.min.x = bbox.max.x = (coord_t)round(c * cur_x - s * cur_y); - bbox.min.y = bbox.max.y = (coord_t)round(c * cur_y + s * cur_x); + double cur_x = (double)(*it)(0); + double cur_y = (double)(*it)(1); + bbox.min(0) = bbox.max(0) = (coord_t)round(c * cur_x - s * cur_y); + bbox.min(1) = bbox.max(1) = (coord_t)round(c * cur_y + s * cur_x); for (++it; it != points.end(); ++it) { - double cur_x = (double)it->x; - double cur_y = (double)it->y; + double cur_x = (double)(*it)(0); + double cur_y = (double)(*it)(1); coord_t x = (coord_t)round(c * cur_x - s * cur_y); coord_t y = (coord_t)round(c * cur_y + s * cur_x); - bbox.min.x = std::min(x, bbox.min.x); - bbox.min.y = std::min(y, bbox.min.y); - bbox.max.x = std::max(x, bbox.max.x); - bbox.max.y = std::max(y, bbox.max.y); + bbox.min(0) = std::min(x, bbox.min(0)); + bbox.min(1) = std::min(y, bbox.min(1)); + bbox.max(0) = std::max(x, bbox.max(0)); + bbox.max(1) = std::max(y, bbox.max(1)); } bbox.defined = true; } diff --git a/xs/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp similarity index 80% rename from xs/src/libslic3r/MultiPoint.hpp rename to src/libslic3r/MultiPoint.hpp index 02086ded8a..9a7bd83e56 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -18,13 +18,15 @@ public: Points points; operator Points() const; - MultiPoint() {}; + MultiPoint() {} MultiPoint(const MultiPoint &other) : points(other.points) {} MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {} - explicit MultiPoint(const Points &_points): points(_points) {} + MultiPoint(std::initializer_list list) : points(list) {} + explicit MultiPoint(const Points &_points) : points(_points) {} MultiPoint& operator=(const MultiPoint &other) { points = other.points; return *this; } MultiPoint& operator=(MultiPoint &&other) { points = std::move(other.points); return *this; } void scale(double factor); + void scale(double factor_x, double factor_y); void translate(double x, double y); void translate(const Point &vector); void rotate(double angle) { this->rotate(cos(angle), sin(angle)); } @@ -45,9 +47,9 @@ public: int idx = -1; if (! this->points.empty()) { idx = 0; - double dist_min = this->points.front().distance_to(point); + double dist_min = (point - this->points.front()).cast().norm(); for (int i = 1; i < int(this->points.size()); ++ i) { - double d = this->points[i].distance_to(point); + double d = (this->points[i] - point).cast().norm(); if (d < dist_min) { dist_min = d; idx = i; @@ -77,7 +79,6 @@ public: bool intersection(const Line& line, Point* intersection) const; bool first_intersection(const Line& line, Point* intersection) const; - std::string dump_perl() const; static Points _douglas_peucker(const Points &points, const double tolerance); }; @@ -87,7 +88,7 @@ class MultiPoint3 public: Points3 points; - void append(const Point3& point) { this->points.push_back(point); } + void append(const Vec3crd& point) { this->points.push_back(point); } void translate(double x, double y); void translate(const Point& vector); @@ -105,23 +106,6 @@ extern BoundingBox get_extents(const MultiPoint &mp); extern BoundingBox get_extents_rotated(const std::vector &points, double angle); extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle); -inline double length(const Points &pts) { - double total = 0; - if (! pts.empty()) { - auto it = pts.begin(); - for (auto it_prev = it ++; it != pts.end(); ++ it, ++ it_prev) - total += it->distance_to(*it_prev); - } - return total; -} - -inline double area(const Points &polygon) { - double area = 0.; - for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j = i ++) - area += double(polygon[j].x + polygon[i].x) * double(polygon[i].y - polygon[j].y); - return area; -} - } // namespace Slic3r #endif diff --git a/xs/src/libslic3r/MutablePriorityQueue.hpp b/src/libslic3r/MutablePriorityQueue.hpp similarity index 100% rename from xs/src/libslic3r/MutablePriorityQueue.hpp rename to src/libslic3r/MutablePriorityQueue.hpp diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp similarity index 85% rename from xs/src/libslic3r/PerimeterGenerator.cpp rename to src/libslic3r/PerimeterGenerator.cpp index 1d2e6248f6..de8aeeb2ab 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -243,7 +243,7 @@ void PerimeterGenerator::process() perimeter_spacing / 2; // only apply infill overlap if we actually have one perimeter if (inset > 0) - inset -= scale_(this->config->get_abs_value("infill_overlap", unscale(inset + solid_infill_spacing / 2))); + inset -= scale_(this->config->get_abs_value("infill_overlap", unscale(inset + solid_infill_spacing / 2))); // simplify infill contours according to resolution Polygons pp; for (ExPolygon &ex : last) @@ -366,99 +366,103 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( return entities; } -ExtrusionEntityCollection PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const +static inline ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, Flow &flow, const float tolerance) { - // this value determines granularity of adaptive width, as G-code does not allow - // variable extrusion within a single move; this value shall only affect the amount - // of segments, and any pruning shall be performed before we apply this tolerance - const double tolerance = scale_(0.05); + ExtrusionPaths paths; + ExtrusionPath path(role); + ThickLines lines = thick_polyline.thicklines(); - ExtrusionEntityCollection coll; - for (const ThickPolyline &p : polylines) { - ExtrusionPaths paths; - ExtrusionPath path(role); - ThickLines lines = p.thicklines(); + for (int i = 0; i < (int)lines.size(); ++i) { + const ThickLine& line = lines[i]; - for (int i = 0; i < (int)lines.size(); ++i) { - const ThickLine& line = lines[i]; - - const coordf_t line_len = line.length(); - if (line_len < SCALED_EPSILON) continue; - - double thickness_delta = fabs(line.a_width - line.b_width); - if (thickness_delta > tolerance) { - const unsigned short segments = ceil(thickness_delta / tolerance); - const coordf_t seg_len = line_len / segments; - Points pp; - std::vector width; - { - pp.push_back(line.a); - width.push_back(line.a_width); - for (size_t j = 1; j < segments; ++j) { - pp.push_back(line.point_at(j*seg_len)); - - coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len; - width.push_back(w); - width.push_back(w); - } - pp.push_back(line.b); - width.push_back(line.b_width); + const coordf_t line_len = line.length(); + if (line_len < SCALED_EPSILON) continue; + + double thickness_delta = fabs(line.a_width - line.b_width); + if (thickness_delta > tolerance) { + const unsigned short segments = ceil(thickness_delta / tolerance); + const coordf_t seg_len = line_len / segments; + Points pp; + std::vector width; + { + pp.push_back(line.a); + width.push_back(line.a_width); + for (size_t j = 1; j < segments; ++j) { + pp.push_back((line.a.cast() + (line.b - line.a).cast().normalized() * (j * seg_len)).cast()); - assert(pp.size() == segments + 1); - assert(width.size() == segments*2); + coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len; + width.push_back(w); + width.push_back(w); } + pp.push_back(line.b); + width.push_back(line.b_width); - // delete this line and insert new ones - lines.erase(lines.begin() + i); - for (size_t j = 0; j < segments; ++j) { - ThickLine new_line(pp[j], pp[j+1]); - new_line.a_width = width[2*j]; - new_line.b_width = width[2*j+1]; - lines.insert(lines.begin() + i + j, new_line); - } - - -- i; - continue; + assert(pp.size() == segments + 1); + assert(width.size() == segments*2); } - const double w = fmax(line.a_width, line.b_width); - if (path.polyline.points.empty()) { - path.polyline.append(line.a); - path.polyline.append(line.b); - // Convert from spacing to extrusion width based on the extrusion model - // of a square extrusion ended with semi circles. - flow.width = unscale(w) + flow.height * (1. - 0.25 * PI); - #ifdef SLIC3R_DEBUG - printf(" filling %f gap\n", flow.width); - #endif - path.mm3_per_mm = flow.mm3_per_mm(); - path.width = flow.width; - path.height = flow.height; - } else { - thickness_delta = fabs(scale_(flow.width) - w); - if (thickness_delta <= tolerance) { - // the width difference between this line and the current flow width is - // within the accepted tolerance - path.polyline.append(line.b); - } else { - // we need to initialize a new line - paths.emplace_back(std::move(path)); - path = ExtrusionPath(role); - -- i; - } + // delete this line and insert new ones + lines.erase(lines.begin() + i); + for (size_t j = 0; j < segments; ++j) { + ThickLine new_line(pp[j], pp[j+1]); + new_line.a_width = width[2*j]; + new_line.b_width = width[2*j+1]; + lines.insert(lines.begin() + i + j, new_line); } + + -- i; + continue; } - if (path.polyline.is_valid()) - paths.emplace_back(std::move(path)); - // Append paths to collection. - if (! paths.empty()) { - if (paths.front().first_point().coincides_with(paths.back().last_point())) - coll.append(ExtrusionLoop(paths)); - else - coll.append(paths); + + const double w = fmax(line.a_width, line.b_width); + if (path.polyline.points.empty()) { + path.polyline.append(line.a); + path.polyline.append(line.b); + // Convert from spacing to extrusion width based on the extrusion model + // of a square extrusion ended with semi circles. + flow.width = unscale(w) + flow.height * (1. - 0.25 * PI); + #ifdef SLIC3R_DEBUG + printf(" filling %f gap\n", flow.width); + #endif + path.mm3_per_mm = flow.mm3_per_mm(); + path.width = flow.width; + path.height = flow.height; + } else { + thickness_delta = fabs(scale_(flow.width) - w); + if (thickness_delta <= tolerance) { + // the width difference between this line and the current flow width is + // within the accepted tolerance + path.polyline.append(line.b); + } else { + // we need to initialize a new line + paths.emplace_back(std::move(path)); + path = ExtrusionPath(role); + -- i; + } + } + } + if (path.polyline.is_valid()) + paths.emplace_back(std::move(path)); + return paths; +} + +ExtrusionEntityCollection PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const +{ + // This value determines granularity of adaptive width, as G-code does not allow + // variable extrusion within a single move; this value shall only affect the amount + // of segments, and any pruning shall be performed before we apply this tolerance. + ExtrusionEntityCollection coll; + const double tolerance = scale_(0.05); + for (const ThickPolyline &p : polylines) { + ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance); + // Append paths to collection. + if (! paths.empty()) { + if (paths.front().first_point() == paths.back().last_point()) + coll.append(ExtrusionLoop(std::move(paths))); + else + coll.append(std::move(paths)); } } - return coll; } diff --git a/xs/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp similarity index 68% rename from xs/src/libslic3r/PerimeterGenerator.hpp rename to src/libslic3r/PerimeterGenerator.hpp index c0f449908d..44af8c8bef 100644 --- a/xs/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -37,30 +37,30 @@ typedef std::vector PerimeterGeneratorLoops; class PerimeterGenerator { public: // Inputs: - const SurfaceCollection* slices; - const ExPolygonCollection* lower_slices; - double layer_height; - int layer_id; - Flow perimeter_flow; - Flow ext_perimeter_flow; - Flow overhang_flow; - Flow solid_infill_flow; - PrintRegionConfig* config; - PrintObjectConfig* object_config; - PrintConfig* print_config; + const SurfaceCollection *slices; + const ExPolygonCollection *lower_slices; + double layer_height; + int layer_id; + Flow perimeter_flow; + Flow ext_perimeter_flow; + Flow overhang_flow; + Flow solid_infill_flow; + const PrintRegionConfig *config; + const PrintObjectConfig *object_config; + const PrintConfig *print_config; // Outputs: - ExtrusionEntityCollection* loops; - ExtrusionEntityCollection* gap_fill; - SurfaceCollection* fill_surfaces; + ExtrusionEntityCollection *loops; + ExtrusionEntityCollection *gap_fill; + SurfaceCollection *fill_surfaces; PerimeterGenerator( // Input: const SurfaceCollection* slices, double layer_height, Flow flow, - PrintRegionConfig* config, - PrintObjectConfig* object_config, - PrintConfig* print_config, + const PrintRegionConfig* config, + const PrintObjectConfig* object_config, + const PrintConfig* print_config, // Output: // Loops with the external thin walls ExtrusionEntityCollection* loops, @@ -78,15 +78,13 @@ public: void process(); private: - double _ext_mm3_per_mm; - double _mm3_per_mm; - double _mm3_per_mm_overhang; - Polygons _lower_slices_p; + double _ext_mm3_per_mm; + double _mm3_per_mm; + double _mm3_per_mm_overhang; + Polygons _lower_slices_p; - ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, - ThickPolylines &thin_walls) const; - ExtrusionEntityCollection _variable_width - (const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const; + ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const; + ExtrusionEntityCollection _variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const; }; } diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp similarity index 97% rename from xs/src/libslic3r/PlaceholderParser.cpp rename to src/libslic3r/PlaceholderParser.cpp index 80740b20d9..198872b1d1 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -69,7 +69,7 @@ PlaceholderParser::PlaceholderParser() this->update_timestamp(); } -void PlaceholderParser::update_timestamp() +void PlaceholderParser::update_timestamp(DynamicConfig &config) { time_t rawtime; time(&rawtime); @@ -84,14 +84,14 @@ void PlaceholderParser::update_timestamp() ss << std::setw(2) << std::setfill('0') << timeinfo->tm_hour; ss << std::setw(2) << std::setfill('0') << timeinfo->tm_min; ss << std::setw(2) << std::setfill('0') << timeinfo->tm_sec; - this->set("timestamp", ss.str()); + config.set_key_value("timestamp", new ConfigOptionString(ss.str())); } - this->set("year", 1900 + timeinfo->tm_year); - this->set("month", 1 + timeinfo->tm_mon); - this->set("day", timeinfo->tm_mday); - this->set("hour", timeinfo->tm_hour); - this->set("minute", timeinfo->tm_min); - this->set("second", timeinfo->tm_sec); + config.set_key_value("year", new ConfigOptionInt(1900 + timeinfo->tm_year)); + config.set_key_value("month", new ConfigOptionInt(1 + timeinfo->tm_mon)); + config.set_key_value("day", new ConfigOptionInt(timeinfo->tm_mday)); + config.set_key_value("hour", new ConfigOptionInt(timeinfo->tm_hour)); + config.set_key_value("minute", new ConfigOptionInt(timeinfo->tm_min)); + config.set_key_value("second", new ConfigOptionInt(timeinfo->tm_sec)); } // Scalar configuration values are stored into m_single, @@ -100,22 +100,33 @@ void PlaceholderParser::update_timestamp() // are expected to be addressed by the extruder ID, therefore // if a vector configuration value is addressed without an index, // a current extruder ID is used. -void PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) +bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) { const ConfigDef *def = rhs.def(); + bool modified = false; for (const t_config_option_key &opt_key : rhs.keys()) { const ConfigOptionDef *opt_def = def->get(opt_key); if ((opt_def->multiline && boost::ends_with(opt_key, "_gcode")) || opt_key == "post_process") continue; - const ConfigOption *opt = rhs.option(opt_key); + const ConfigOption *opt_rhs = rhs.option(opt_key); + const ConfigOption *opt_old = m_config.option(opt_key, false); + if (opt_old != nullptr) { + if (opt_rhs->type() == coFloatOrPercent ? + dynamic_cast(opt_old)->value == rhs.get_abs_value(opt_key) + : *opt_rhs == *opt_old) + // no need to update + continue; + } // Store a copy of the config option. // Convert FloatOrPercent values to floats first. //FIXME there are some ratio_over chains, which end with empty ratio_with. // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. - this->set(opt_key, (opt->type() == coFloatOrPercent) ? + this->set(opt_key, (opt_rhs->type() == coFloatOrPercent) ? new ConfigOptionFloat(rhs.get_abs_value(opt_key)) : - opt->clone()); + opt_rhs->clone()); + modified = true; } + return modified; } void PlaceholderParser::apply_env_variables() @@ -442,7 +453,7 @@ namespace client param1.data.d = d; param1.type = TYPE_DOUBLE; } else { - int i = 0.; + int i = 0; switch (fun) { case FUNCTION_MIN: i = std::min(param1.as_i(), param2.as_i()); break; case FUNCTION_MAX: i = std::max(param1.as_i(), param2.as_i()); break; @@ -693,7 +704,7 @@ namespace client case coInts: output.set_i(static_cast(opt.opt)->values[idx]); break; case coStrings: output.set_s(static_cast(opt.opt)->values[idx]); break; case coPercents: output.set_d(static_cast(opt.opt)->values[idx]); break; - case coPoints: output.set_s(static_cast(opt.opt)->values[idx].dump_perl()); break; + case coPoints: output.set_s(to_string(static_cast(opt.opt)->values[idx])); break; case coBools: output.set_b(static_cast(opt.opt)->values[idx] != 0); break; default: ctx->throw_exception("Unknown vector variable type", opt.it_range); diff --git a/xs/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp similarity index 83% rename from xs/src/libslic3r/PlaceholderParser.hpp rename to src/libslic3r/PlaceholderParser.hpp index 4e0aa9ee28..d833969fca 100644 --- a/xs/src/libslic3r/PlaceholderParser.hpp +++ b/src/libslic3r/PlaceholderParser.hpp @@ -14,8 +14,8 @@ class PlaceholderParser public: PlaceholderParser(); - void update_timestamp(); - void apply_config(const DynamicPrintConfig &config); + // Return true if modified. + bool apply_config(const DynamicPrintConfig &config); void apply_env_variables(); // Add new ConfigOption values to m_config. @@ -37,6 +37,11 @@ public: // Throws std::runtime_error on syntax or runtime error. static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr); + // Update timestamp, year, month, day, hour, minute, second variables at the provided config. + static void update_timestamp(DynamicConfig &config); + // Update timestamp, year, month, day, hour, minute, second variables at m_config. + void update_timestamp() { update_timestamp(m_config); } + private: DynamicConfig m_config; }; diff --git a/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp new file mode 100644 index 0000000000..c2417d0dc9 --- /dev/null +++ b/src/libslic3r/Point.cpp @@ -0,0 +1,210 @@ +#include "Point.hpp" +#include "Line.hpp" +#include "MultiPoint.hpp" +#include "Int128.hpp" +#include + +namespace Slic3r { + +std::vector transform(const std::vector& points, const Transform3f& t) +{ + unsigned int vertices_count = (unsigned int)points.size(); + if (vertices_count == 0) + return std::vector(); + + unsigned int data_size = 3 * vertices_count * sizeof(float); + + Eigen::MatrixXf src(3, vertices_count); + ::memcpy((void*)src.data(), (const void*)points.data(), data_size); + + Eigen::MatrixXf dst(3, vertices_count); + dst = t * src.colwise().homogeneous(); + + std::vector ret_points(vertices_count, Vec3f::Zero()); + ::memcpy((void*)ret_points.data(), (const void*)dst.data(), data_size); + return ret_points; +} + +Pointf3s transform(const Pointf3s& points, const Transform3d& t) +{ + unsigned int vertices_count = (unsigned int)points.size(); + if (vertices_count == 0) + return Pointf3s(); + + unsigned int data_size = 3 * vertices_count * sizeof(double); + + Eigen::MatrixXd src(3, vertices_count); + ::memcpy((void*)src.data(), (const void*)points.data(), data_size); + + Eigen::MatrixXd dst(3, vertices_count); + dst = t * src.colwise().homogeneous(); + + Pointf3s ret_points(vertices_count, Vec3d::Zero()); + ::memcpy((void*)ret_points.data(), (const void*)dst.data(), data_size); + return ret_points; +} + +void Point::rotate(double angle) +{ + double cur_x = (double)(*this)(0); + double cur_y = (double)(*this)(1); + double s = ::sin(angle); + double c = ::cos(angle); + (*this)(0) = (coord_t)round(c * cur_x - s * cur_y); + (*this)(1) = (coord_t)round(c * cur_y + s * cur_x); +} + +void Point::rotate(double angle, const Point ¢er) +{ + double cur_x = (double)(*this)(0); + double cur_y = (double)(*this)(1); + double s = ::sin(angle); + double c = ::cos(angle); + double dx = cur_x - (double)center(0); + double dy = cur_y - (double)center(1); + (*this)(0) = (coord_t)round( (double)center(0) + c * dx - s * dy ); + (*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx ); +} + +int Point::nearest_point_index(const Points &points) const +{ + PointConstPtrs p; + p.reserve(points.size()); + for (Points::const_iterator it = points.begin(); it != points.end(); ++it) + p.push_back(&*it); + return this->nearest_point_index(p); +} + +int Point::nearest_point_index(const PointConstPtrs &points) const +{ + int idx = -1; + double distance = -1; // double because long is limited to 2147483647 on some platforms and it's not enough + + for (PointConstPtrs::const_iterator it = points.begin(); it != points.end(); ++it) { + /* If the X distance of the candidate is > than the total distance of the + best previous candidate, we know we don't want it */ + double d = sqr((*this)(0) - (*it)->x()); + if (distance != -1 && d > distance) continue; + + /* If the Y distance of the candidate is > than the total distance of the + best previous candidate, we know we don't want it */ + d += sqr((*this)(1) - (*it)->y()); + if (distance != -1 && d > distance) continue; + + idx = it - points.begin(); + distance = d; + + if (distance < EPSILON) break; + } + + return idx; +} + +int Point::nearest_point_index(const PointPtrs &points) const +{ + PointConstPtrs p; + p.reserve(points.size()); + for (PointPtrs::const_iterator it = points.begin(); it != points.end(); ++it) + p.push_back(*it); + return this->nearest_point_index(p); +} + +bool Point::nearest_point(const Points &points, Point* point) const +{ + int idx = this->nearest_point_index(points); + if (idx == -1) return false; + *point = points.at(idx); + return true; +} + +/* Three points are a counter-clockwise turn if ccw > 0, clockwise if + * ccw < 0, and collinear if ccw = 0 because ccw is a determinant that + * gives the signed area of the triangle formed by p1, p2 and this point. + * In other words it is the 2D cross product of p1-p2 and p1-this, i.e. + * z-component of their 3D cross product. + * We return double because it must be big enough to hold 2*max(|coordinate|)^2 + */ +double Point::ccw(const Point &p1, const Point &p2) const +{ + return (double)(p2(0) - p1(0))*(double)((*this)(1) - p1(1)) - (double)(p2(1) - p1(1))*(double)((*this)(0) - p1(0)); +} + +double Point::ccw(const Line &line) const +{ + return this->ccw(line.a, line.b); +} + +// returns the CCW angle between this-p1 and this-p2 +// i.e. this assumes a CCW rotation from p1 to p2 around this +double Point::ccw_angle(const Point &p1, const Point &p2) const +{ + double angle = atan2(p1(0) - (*this)(0), p1(1) - (*this)(1)) + - atan2(p2(0) - (*this)(0), p2(1) - (*this)(1)); + + // we only want to return only positive angles + return angle <= 0 ? angle + 2*PI : angle; +} + +Point Point::projection_onto(const MultiPoint &poly) const +{ + Point running_projection = poly.first_point(); + double running_min = (running_projection - *this).cast().norm(); + + Lines lines = poly.lines(); + for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { + Point point_temp = this->projection_onto(*line); + if ((point_temp - *this).cast().norm() < running_min) { + running_projection = point_temp; + running_min = (running_projection - *this).cast().norm(); + } + } + return running_projection; +} + +Point Point::projection_onto(const Line &line) const +{ + if (line.a == line.b) return line.a; + + /* + (Ported from VisiLibity by Karl J. Obermeyer) + The projection of point_temp onto the line determined by + line_segment_temp can be represented as an affine combination + expressed in the form projection of + Point = theta*line_segment_temp.first + (1.0-theta)*line_segment_temp.second. + If theta is outside the interval [0,1], then one of the Line_Segment's endpoints + must be closest to calling Point. + */ + double lx = (double)(line.b(0) - line.a(0)); + double ly = (double)(line.b(1) - line.a(1)); + double theta = ( (double)(line.b(0) - (*this)(0))*lx + (double)(line.b(1)- (*this)(1))*ly ) + / ( sqr(lx) + sqr(ly) ); + + if (0.0 <= theta && theta <= 1.0) + return (theta * line.a.cast() + (1.0-theta) * line.b.cast()).cast(); + + // Else pick closest endpoint. + return ((line.a - *this).cast().squaredNorm() < (line.b - *this).cast().squaredNorm()) ? line.a : line.b; +} + +std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf) +{ + return stm << pointf(0) << "," << pointf(1); +} + +namespace int128 { + +int orient(const Vec2crd &p1, const Vec2crd &p2, const Vec2crd &p3) +{ + Slic3r::Vector v1(p2 - p1); + Slic3r::Vector v2(p3 - p1); + return Int128::sign_determinant_2x2_filtered(v1(0), v1(1), v2(0), v2(1)); +} + +int cross(const Vec2crd &v1, const Vec2crd &v2) +{ + return Int128::sign_determinant_2x2_filtered(v1(0), v1(1), v2(0), v2(1)); +} + +} + +} diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp new file mode 100644 index 0000000000..277fc58773 --- /dev/null +++ b/src/libslic3r/Point.hpp @@ -0,0 +1,270 @@ +#ifndef slic3r_Point_hpp_ +#define slic3r_Point_hpp_ + +#include "libslic3r.h" +#include +#include +#include +#include +#include +#include + +#include + +namespace Slic3r { + +class Line; +class MultiPoint; +class Point; +typedef Point Vector; + +// Eigen types, to replace the Slic3r's own types in the future. +// Vector types with a fixed point coordinate base type. +typedef Eigen::Matrix Vec2crd; +typedef Eigen::Matrix Vec3crd; +typedef Eigen::Matrix Vec3i; +typedef Eigen::Matrix Vec2i64; +typedef Eigen::Matrix Vec3i64; + +// Vector types with a double coordinate base type. +typedef Eigen::Matrix Vec2f; +typedef Eigen::Matrix Vec3f; +typedef Eigen::Matrix Vec2d; +typedef Eigen::Matrix Vec3d; + +typedef std::vector Points; +typedef std::vector PointPtrs; +typedef std::vector PointConstPtrs; +typedef std::vector Points3; +typedef std::vector Pointfs; +typedef std::vector Pointf3s; + +typedef Eigen::Transform Transform2f; +typedef Eigen::Transform Transform2d; +typedef Eigen::Transform Transform3f; +typedef Eigen::Transform Transform3d; + +inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); } + +inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } +inline coord_t cross2(const Vec2crd &v1, const Vec2crd &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } +inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } +inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } + +inline Vec2crd to_2d(const Vec3crd &pt3) { return Vec2crd(pt3(0), pt3(1)); } +inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); } +inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); } +inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); } + +inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); } +inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); } +inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); } +inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); } + +inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale(x), unscale(y)); } +inline Vec2d unscale(const Vec2crd &pt) { return Vec2d(unscale(pt(0)), unscale(pt(1))); } +inline Vec2d unscale(const Vec2d &pt) { return Vec2d(unscale(pt(0)), unscale(pt(1))); } +inline Vec3d unscale(coord_t x, coord_t y, coord_t z) { return Vec3d(unscale(x), unscale(y), unscale(z)); } +inline Vec3d unscale(const Vec3crd &pt) { return Vec3d(unscale(pt(0)), unscale(pt(1)), unscale(pt(2))); } +inline Vec3d unscale(const Vec3d &pt) { return Vec3d(unscale(pt(0)), unscale(pt(1)), unscale(pt(2))); } + +inline std::string to_string(const Vec2crd &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + "]"; } +inline std::string to_string(const Vec2d &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + "]"; } +inline std::string to_string(const Vec3crd &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + ", " + std::to_string(pt(2)) + "]"; } +inline std::string to_string(const Vec3d &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + ", " + std::to_string(pt(2)) + "]"; } + +std::vector transform(const std::vector& points, const Transform3f& t); +Pointf3s transform(const Pointf3s& points, const Transform3d& t); + +class Point : public Vec2crd +{ +public: + typedef coord_t coord_type; + + Point() : Vec2crd() { (*this)(0) = 0; (*this)(1) = 0; } + Point(coord_t x, coord_t y) { (*this)(0) = x; (*this)(1) = y; } + Point(int64_t x, int64_t y) { (*this)(0) = coord_t(x); (*this)(1) = coord_t(y); } // for Clipper + Point(double x, double y) { (*this)(0) = coord_t(lrint(x)); (*this)(1) = coord_t(lrint(y)); } + Point(const Point &rhs) { *this = rhs; } + // This constructor allows you to construct Point from Eigen expressions + template + Point(const Eigen::MatrixBase &other) : Vec2crd(other) {} + static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); } + + // This method allows you to assign Eigen expressions to MyVectorType + template + Point& operator=(const Eigen::MatrixBase &other) + { + this->Vec2crd::operator=(other); + return *this; + } + + bool operator< (const Point& rhs) const { return (*this)(0) < rhs(0) || ((*this)(0) == rhs(0) && (*this)(1) < rhs(1)); } + + Point& operator+=(const Point& rhs) { (*this)(0) += rhs(0); (*this)(1) += rhs(1); return *this; } + Point& operator-=(const Point& rhs) { (*this)(0) -= rhs(0); (*this)(1) -= rhs(1); return *this; } + Point& operator*=(const double &rhs) { (*this)(0) *= rhs; (*this)(1) *= rhs; return *this; } + + void rotate(double angle); + void rotate(double angle, const Point ¢er); + Point rotated(double angle) const { Point res(*this); res.rotate(angle); return res; } + Point rotated(double angle, const Point ¢er) const { Point res(*this); res.rotate(angle, center); return res; } + int nearest_point_index(const Points &points) const; + int nearest_point_index(const PointConstPtrs &points) const; + int nearest_point_index(const PointPtrs &points) const; + bool nearest_point(const Points &points, Point* point) const; + double ccw(const Point &p1, const Point &p2) const; + double ccw(const Line &line) const; + double ccw_angle(const Point &p1, const Point &p2) const; + Point projection_onto(const MultiPoint &poly) const; + Point projection_onto(const Line &line) const; +}; + +namespace int128 { + // Exact orientation predicate, + // returns +1: CCW, 0: collinear, -1: CW. + int orient(const Vec2crd &p1, const Vec2crd &p2, const Vec2crd &p3); + // Exact orientation predicate, + // returns +1: CCW, 0: collinear, -1: CW. + int cross(const Vec2crd &v1, const Vec2crd &v2); +} + +// To be used by std::unordered_map, std::unordered_multimap and friends. +struct PointHash { + size_t operator()(const Vec2crd &pt) const { + return std::hash()(pt(0)) ^ std::hash()(pt(1)); + } +}; + +// A generic class to search for a closest Point in a given radius. +// It uses std::unordered_multimap to implement an efficient 2D spatial hashing. +// The PointAccessor has to return const Point*. +// If a nullptr is returned, it is ignored by the query. +template class ClosestPointInRadiusLookup +{ +public: + ClosestPointInRadiusLookup(coord_t search_radius, PointAccessor point_accessor = PointAccessor()) : + m_search_radius(search_radius), m_point_accessor(point_accessor), m_grid_log2(0) + { + // Resolution of a grid, twice the search radius + some epsilon. + coord_t gridres = 2 * m_search_radius + 4; + m_grid_resolution = gridres; + assert(m_grid_resolution > 0); + assert(m_grid_resolution < (coord_t(1) << 30)); + // Compute m_grid_log2 = log2(m_grid_resolution) + if (m_grid_resolution > 32767) { + m_grid_resolution >>= 16; + m_grid_log2 += 16; + } + if (m_grid_resolution > 127) { + m_grid_resolution >>= 8; + m_grid_log2 += 8; + } + if (m_grid_resolution > 7) { + m_grid_resolution >>= 4; + m_grid_log2 += 4; + } + if (m_grid_resolution > 1) { + m_grid_resolution >>= 2; + m_grid_log2 += 2; + } + if (m_grid_resolution > 0) + ++ m_grid_log2; + m_grid_resolution = 1 << m_grid_log2; + assert(m_grid_resolution >= gridres); + assert(gridres > m_grid_resolution / 2); + } + + void insert(const ValueType &value) { + const Vec2crd *pt = m_point_accessor(value); + if (pt != nullptr) + m_map.emplace(std::make_pair(Vec2crd(pt->x()>>m_grid_log2, pt->y()>>m_grid_log2), value)); + } + + void insert(ValueType &&value) { + const Vec2crd *pt = m_point_accessor(value); + if (pt != nullptr) + m_map.emplace(std::make_pair(Vec2crd(pt->x()>>m_grid_log2, pt->y()>>m_grid_log2), std::move(value))); + } + + // Return a pair of + std::pair find(const Vec2crd &pt) { + // Iterate over 4 closest grid cells around pt, + // find the closest start point inside these cells to pt. + const ValueType *value_min = nullptr; + double dist_min = std::numeric_limits::max(); + // Round pt to a closest grid_cell corner. + Vec2crd grid_corner((pt(0)+(m_grid_resolution>>1))>>m_grid_log2, (pt(1)+(m_grid_resolution>>1))>>m_grid_log2); + // For four neighbors of grid_corner: + for (coord_t neighbor_y = -1; neighbor_y < 1; ++ neighbor_y) { + for (coord_t neighbor_x = -1; neighbor_x < 1; ++ neighbor_x) { + // Range of fragment starts around grid_corner, close to pt. + auto range = m_map.equal_range(Vec2crd(grid_corner(0) + neighbor_x, grid_corner(1) + neighbor_y)); + // Find the map entry closest to pt. + for (auto it = range.first; it != range.second; ++it) { + const ValueType &value = it->second; + const Vec2crd *pt2 = m_point_accessor(value); + if (pt2 != nullptr) { + const double d2 = (pt - *pt2).squaredNorm(); + if (d2 < dist_min) { + dist_min = d2; + value_min = &value; + } + } + } + } + } + return (value_min != nullptr && dist_min < coordf_t(m_search_radius * m_search_radius)) ? + std::make_pair(value_min, dist_min) : + std::make_pair(nullptr, std::numeric_limits::max()); + } + +private: + typedef typename std::unordered_multimap map_type; + PointAccessor m_point_accessor; + map_type m_map; + coord_t m_search_radius; + coord_t m_grid_resolution; + coord_t m_grid_log2; +}; + +std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf); + +} // namespace Slic3r + +// start Boost +#include +#include +namespace boost { namespace polygon { + template <> + struct geometry_concept { typedef point_concept type; }; + + template <> + struct point_traits { + typedef coord_t coordinate_type; + + static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) { + return (orient == HORIZONTAL) ? (coordinate_type)point(0) : (coordinate_type)point(1); + } + }; + + template <> + struct point_mutable_traits { + typedef coord_t coordinate_type; + static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) { + if (orient == HORIZONTAL) + point(0) = value; + else + point(1) = value; + } + static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) { + Slic3r::Point retval; + retval(0) = x_value; + retval(1) = y_value; + return retval; + } + }; +} } +// end Boost + +#endif diff --git a/xs/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp similarity index 83% rename from xs/src/libslic3r/Polygon.cpp rename to src/libslic3r/Polygon.cpp index b5fd7e64f7..cf0783bae5 100644 --- a/xs/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -44,12 +44,10 @@ Polyline Polygon::split_at_vertex(const Point &point) const { // find index of point - for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) { - if (it->coincides_with(point)) { - return this->split_at_index(it - this->points.begin()); - } - } - CONFESS("Point not found"); + for (const Point &pt : this->points) + if (pt == point) + return this->split_at_index(&pt - &this->points.front()); + throw std::invalid_argument("Point not found"); return Polyline(); } @@ -88,7 +86,7 @@ int64_t Polygon::area2x() const int64_t a = 0; for (size_t i = 0, j = n - 1; i < n; ++i) - a += int64_t(poly[j].x + poly[i].x) * int64_t(poly[j].y - poly[i].y); + a += int64_t(poly[j](0) + poly[i](0)) * int64_t(poly[j](1) - poly[i](1)); j = i; } return -a * 0.5; @@ -103,7 +101,7 @@ double Polygon::area() const double a = 0.; for (size_t i = 0, j = n - 1; i < n; ++i) { - a += ((double)points[j].x + (double)points[i].x) * ((double)points[i].y - (double)points[j].y); + a += ((double)points[j](0) + (double)points[i](0)) * ((double)points[i](1) - (double)points[j](1)); j = i; } return 0.5 * a; @@ -157,17 +155,17 @@ Polygon::contains(const Point &point) const Points::const_iterator i = this->points.begin(); Points::const_iterator j = this->points.end() - 1; for (; i != this->points.end(); j = i++) { - //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point.y well. - // Does the ray with y == point.y intersect this line segment? + //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well. + // Does the ray with y == point(1) intersect this line segment? #if 1 - if ( ((i->y > point.y) != (j->y > point.y)) - && ((double)point.x < (double)(j->x - i->x) * (double)(point.y - i->y) / (double)(j->y - i->y) + (double)i->x) ) + if ( (((*i)(1) > point(1)) != ((*j)(1) > point(1))) + && ((double)point(0) < (double)((*j)(0) - (*i)(0)) * (double)(point(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1)) + (double)(*i)(0)) ) result = !result; #else - if ((i->y > point.y) != (j->y > point.y)) { + if (((*i)(1) > point(1)) != ((*j)(1) > point(1))) { // Orientation predicated relative to i-th point. - double orient = (double)(point.x - i->x) * (double)(j->y - i->y) - (double)(point.y - i->y) * (double)(j->x - i->x); - if ((i->y > j->y) ? (orient > 0.) : (orient < 0.)) + double orient = (double)(point(0) - (*i)(0)) * (double)((*j)(1) - (*i)(1)) - (double)(point(1) - (*i)(1)) * (double)((*j)(0) - (*i)(0)); + if (((*i)(1) > (*j)(1)) ? (orient > 0.) : (orient < 0.)) result = !result; } #endif @@ -225,26 +223,13 @@ Polygon::centroid() const Polyline polyline = this->split_at_first_point(); for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) { - x_temp += (double)( point->x + (point+1)->x ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y ); - y_temp += (double)( point->y + (point+1)->y ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y ); + x_temp += (double)( point->x() + (point+1)->x() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() ); + y_temp += (double)( point->y() + (point+1)->y() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() ); } return Point(x_temp/(6*area_temp), y_temp/(6*area_temp)); } -std::string -Polygon::wkt() const -{ - std::ostringstream wkt; - wkt << "POLYGON(("; - for (Points::const_iterator p = this->points.begin(); p != this->points.end(); ++p) { - wkt << p->x << " " << p->y; - if (p != this->points.end()-1) wkt << ","; - } - wkt << "))"; - return wkt.str(); -} - // find all concave vertices (i.e. having an internal angle greater than the supplied angle) // (external = right side, thus we consider ccw orientation) Points @@ -302,24 +287,24 @@ Point Polygon::point_projection(const Point &point) const for (size_t i = 0; i < this->points.size(); ++ i) { const Point &pt0 = this->points[i]; const Point &pt1 = this->points[(i + 1 == this->points.size()) ? 0 : i + 1]; - double d = pt0.distance_to(point); + double d = (point - pt0).cast().norm(); if (d < dmin) { dmin = d; proj = pt0; } - d = pt1.distance_to(point); + d = (point - pt1).cast().norm(); if (d < dmin) { dmin = d; proj = pt1; } - Pointf v1(coordf_t(pt1.x - pt0.x), coordf_t(pt1.y - pt0.y)); - coordf_t div = dot(v1); + Vec2d v1(coordf_t(pt1(0) - pt0(0)), coordf_t(pt1(1) - pt0(1))); + coordf_t div = v1.squaredNorm(); if (div > 0.) { - Pointf v2(coordf_t(point.x - pt0.x), coordf_t(point.y - pt0.y)); - coordf_t t = dot(v1, v2) / div; + Vec2d v2(coordf_t(point(0) - pt0(0)), coordf_t(point(1) - pt0(1))); + coordf_t t = v1.dot(v2) / div; if (t > 0. && t < 1.) { - Point foot(coord_t(floor(coordf_t(pt0.x) + t * v1.x + 0.5)), coord_t(floor(coordf_t(pt0.y) + t * v1.y + 0.5))); - d = foot.distance_to(point); + Point foot(coord_t(floor(coordf_t(pt0(0)) + t * v1(0) + 0.5)), coord_t(floor(coordf_t(pt0(1)) + t * v1(1) + 0.5))); + d = (point - foot).cast().norm(); if (d < dmin) { dmin = d; proj = foot; @@ -376,12 +361,12 @@ static inline bool is_stick(const Point &p1, const Point &p2, const Point &p3) { Point v1 = p2 - p1; Point v2 = p3 - p2; - int64_t dir = int64_t(v1.x) * int64_t(v2.x) + int64_t(v1.y) * int64_t(v2.y); + int64_t dir = int64_t(v1(0)) * int64_t(v2(0)) + int64_t(v1(1)) * int64_t(v2(1)); if (dir > 0) // p3 does not turn back to p1. Do not remove p2. return false; - double l2_1 = double(v1.x) * double(v1.x) + double(v1.y) * double(v1.y); - double l2_2 = double(v2.x) * double(v2.x) + double(v2.y) * double(v2.y); + double l2_1 = double(v1(0)) * double(v1(0)) + double(v1(1)) * double(v1(1)); + double l2_2 = double(v2(0)) * double(v2(0)) + double(v2(1)) * double(v2(1)); if (dir == 0) // p1, p2, p3 may make a perpendicular corner, or there is a zero edge length. // Remove p2 if it is coincident with p1 or p2. @@ -389,7 +374,7 @@ static inline bool is_stick(const Point &p1, const Point &p2, const Point &p3) // p3 turns back to p1 after p2. Are p1, p2, p3 collinear? // Calculate distance from p3 to a segment (p1, p2) or from p1 to a segment(p2, p3), // whichever segment is longer - double cross = double(v1.x) * double(v2.y) - double(v2.x) * double(v1.y); + double cross = double(v1(0)) * double(v2(1)) - double(v2(0)) * double(v1(1)); double dist2 = cross * cross / std::max(l2_1, l2_2); return dist2 < EPSILON * EPSILON; } diff --git a/xs/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp similarity index 97% rename from xs/src/libslic3r/Polygon.hpp rename to src/libslic3r/Polygon.hpp index 2d624e71a9..63162d9539 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -24,11 +24,12 @@ public: explicit Polygon(const Points &points): MultiPoint(points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {} Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {} - static Polygon new_scale(std::vector points) { - Points int_points; - for (auto pt : points) - int_points.push_back(Point::new_scale(pt.x, pt.y)); - return Polygon(int_points); + static Polygon new_scale(const std::vector &points) { + Polygon pgn; + pgn.points.reserve(points.size()); + for (const Vec2d &pt : points) + pgn.points.emplace_back(Point::new_scale(pt(0), pt(1))); + return pgn; } Polygon& operator=(const Polygon &other) { points = other.points; return *this; } Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; } @@ -54,7 +55,6 @@ public: void simplify(double tolerance, Polygons &polygons) const; void triangulate_convex(Polygons* polygons) const; Point centroid() const; - std::string wkt() const; Points concave_points(double angle = PI) const; Points convex_points(double angle = PI) const; // Projection of a point onto the polygon. diff --git a/xs/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp similarity index 67% rename from xs/src/libslic3r/Polyline.cpp rename to src/libslic3r/Polyline.cpp index 05bd8c7fb2..af155468ab 100644 --- a/xs/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -18,7 +18,8 @@ Polyline::operator Polylines() const Polyline::operator Line() const { - if (this->points.size() > 2) CONFESS("Can't convert polyline with more than two points to a line"); + if (this->points.size() > 2) + throw std::invalid_argument("Can't convert polyline with more than two points to a line"); return Line(this->points.front(), this->points.back()); } @@ -33,7 +34,7 @@ Polyline::leftmost_point() const { Point p = this->points.front(); for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) { - if (it->x < p.x) p = *it; + if ((*it)(0) < p(0)) p = *it; } return p; } @@ -52,92 +53,82 @@ Polyline::lines() const } // removes the given distance from the end of the polyline -void -Polyline::clip_end(double distance) +void Polyline::clip_end(double distance) { while (distance > 0) { - Point last_point = this->last_point(); + Vec2d last_point = this->last_point().cast(); this->points.pop_back(); - if (this->points.empty()) break; - - double last_segment_length = last_point.distance_to(this->last_point()); - if (last_segment_length <= distance) { - distance -= last_segment_length; - continue; + if (this->points.empty()) + break; + Vec2d v = this->last_point().cast() - last_point; + double lsqr = v.squaredNorm(); + if (lsqr > distance * distance) { + this->points.emplace_back((last_point + v * (distance / sqrt(lsqr))).cast()); + return; } - - Line segment(last_point, this->last_point()); - this->points.push_back(segment.point_at(distance)); - distance = 0; + distance -= sqrt(lsqr); } } // removes the given distance from the start of the polyline -void -Polyline::clip_start(double distance) +void Polyline::clip_start(double distance) { this->reverse(); this->clip_end(distance); - if (this->points.size() >= 2) this->reverse(); + if (this->points.size() >= 2) + this->reverse(); } -void -Polyline::extend_end(double distance) +void Polyline::extend_end(double distance) { // relocate last point by extending the last segment by the specified length - Line line( - this->points.back(), - *(this->points.end() - 2) - ); - this->points.back() = line.point_at(-distance); + Vec2d v = (this->points.back() - *(this->points.end() - 2)).cast().normalized(); + this->points.back() += (v * distance).cast(); } -void -Polyline::extend_start(double distance) +void Polyline::extend_start(double distance) { // relocate first point by extending the first segment by the specified length - this->points.front() = Line(this->points.front(), this->points[1]).point_at(-distance); + Vec2d v = (this->points.front() - this->points[1]).cast().normalized(); + this->points.front() += (v * distance).cast(); } /* this method returns a collection of points picked on the polygon contour so that they are evenly spaced according to the input distance */ -Points -Polyline::equally_spaced_points(double distance) const +Points Polyline::equally_spaced_points(double distance) const { Points points; - points.push_back(this->first_point()); + points.emplace_back(this->first_point()); double len = 0; for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) { - double segment_length = it->distance_to(*(it-1)); + Vec2d p1 = (it-1)->cast(); + Vec2d v = it->cast() - p1; + double segment_length = v.norm(); len += segment_length; - if (len < distance) continue; - + if (len < distance) + continue; if (len == distance) { - points.push_back(*it); + points.emplace_back(*it); len = 0; continue; } - double take = segment_length - (len - distance); // how much we take of this segment - Line segment(*(it-1), *it); - points.push_back(segment.point_at(take)); - --it; - len = -take; + points.emplace_back((p1 + v * (take / v.norm())).cast()); + -- it; + len = - take; } return points; } -void -Polyline::simplify(double tolerance) +void Polyline::simplify(double tolerance) { this->points = MultiPoint::_douglas_peucker(this->points, tolerance); } /* This method simplifies all *lines* contained in the supplied area */ template -void -Polyline::simplify_by_visibility(const T &area) +void Polyline::simplify_by_visibility(const T &area) { Points &pp = this->points; @@ -157,30 +148,29 @@ Polyline::simplify_by_visibility(const T &area) template void Polyline::simplify_by_visibility(const ExPolygon &area); template void Polyline::simplify_by_visibility(const ExPolygonCollection &area); -void -Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const +void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const { if (this->points.empty()) return; // find the line to split at size_t line_idx = 0; Point p = this->first_point(); - double min = point.distance_to(p); + double min = (p - point).cast().norm(); Lines lines = this->lines(); for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { Point p_tmp = point.projection_onto(*line); - if (point.distance_to(p_tmp) < min) { + if ((p_tmp - point).cast().norm() < min) { p = p_tmp; - min = point.distance_to(p); + min = (p - point).cast().norm(); line_idx = line - lines.begin(); } } // create first half p1->points.clear(); - for (Lines::const_iterator line = lines.begin(); line != lines.begin() + line_idx + 1; ++line) { - if (!line->a.coincides_with(p)) p1->points.push_back(line->a); - } + for (Lines::const_iterator line = lines.begin(); line != lines.begin() + line_idx + 1; ++line) + if (line->a != p) + p1->points.push_back(line->a); // we add point instead of p because they might differ because of numerical issues // and caller might want to rely on point belonging to result polylines p1->points.push_back(point); @@ -205,18 +195,6 @@ bool Polyline::is_straight() const return true; } -std::string Polyline::wkt() const -{ - std::ostringstream wkt; - wkt << "LINESTRING(("; - for (Points::const_iterator p = this->points.begin(); p != this->points.end(); ++p) { - wkt << p->x << " " << p->y; - if (p != this->points.end()-1) wkt << ","; - } - wkt << "))"; - return wkt.str(); -} - BoundingBox get_extents(const Polyline &polyline) { return polyline.bounding_box(); @@ -250,30 +228,17 @@ bool remove_degenerate(Polylines &polylines) return modified; } -ThickLines -ThickPolyline::thicklines() const +ThickLines ThickPolyline::thicklines() const { ThickLines lines; if (this->points.size() >= 2) { lines.reserve(this->points.size() - 1); - for (size_t i = 0; i < this->points.size()-1; ++i) { - ThickLine line(this->points[i], this->points[i+1]); - line.a_width = this->width[2*i]; - line.b_width = this->width[2*i+1]; - lines.push_back(line); - } + for (size_t i = 0; i + 1 < this->points.size(); ++ i) + lines.emplace_back(this->points[i], this->points[i + 1], this->width[2 * i], this->width[2 * i + 1]); } return lines; } -void -ThickPolyline::reverse() -{ - Polyline::reverse(); - std::reverse(this->width.begin(), this->width.end()); - std::swap(this->endpoints.first, this->endpoints.second); -} - Lines3 Polyline3::lines() const { Lines3 lines; diff --git a/xs/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp similarity index 85% rename from xs/src/libslic3r/Polyline.hpp rename to src/libslic3r/Polyline.hpp index 774af3fabb..925b88acae 100644 --- a/xs/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -19,16 +19,17 @@ public: Polyline() {}; Polyline(const Polyline &other) : MultiPoint(other.points) {} Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {} + Polyline(std::initializer_list list) : MultiPoint(list) {} + explicit Polyline(const Point &p1, const Point &p2) { points.reserve(2); points.emplace_back(p1); points.emplace_back(p2); } explicit Polyline(const Points &points) : MultiPoint(points) {} explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {} Polyline& operator=(const Polyline &other) { points = other.points; return *this; } Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; } - static Polyline new_scale(std::vector points) { + static Polyline new_scale(const std::vector &points) { Polyline pl; - Points int_points; - for (auto pt : points) - int_points.push_back(Point::new_scale(pt.x, pt.y)); - pl.append(int_points); + pl.points.reserve(points.size()); + for (const Vec2d &pt : points) + pl.points.emplace_back(Point::new_scale(pt(0), pt(1))); return pl; } @@ -73,7 +74,6 @@ public: template void simplify_by_visibility(const T &area); void split_at(const Point &point, Polyline* p1, Polyline* p2) const; bool is_straight() const; - std::string wkt() const; }; extern BoundingBox get_extents(const Polyline &polyline); @@ -81,8 +81,8 @@ extern BoundingBox get_extents(const Polylines &polylines); inline double total_length(const Polylines &polylines) { double total = 0; - for (const Polyline &pl : polylines) - total += pl.length(); + for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) + total += it->length(); return total; } @@ -131,12 +131,17 @@ inline void polylines_append(Polylines &dst, Polylines &&src) bool remove_degenerate(Polylines &polylines); class ThickPolyline : public Polyline { - public: - std::vector width; - std::pair endpoints; - ThickPolyline() : endpoints(std::make_pair(false, false)) {}; +public: + ThickPolyline() : endpoints(std::make_pair(false, false)) {} ThickLines thicklines() const; - void reverse(); + void reverse() { + Polyline::reverse(); + std::reverse(this->width.begin(), this->width.end()); + std::swap(this->endpoints.first, this->endpoints.second); + } + + std::vector width; + std::pair endpoints; }; class Polyline3 : public MultiPoint3 diff --git a/xs/src/libslic3r/PolylineCollection.cpp b/src/libslic3r/PolylineCollection.cpp similarity index 86% rename from xs/src/libslic3r/PolylineCollection.cpp rename to src/libslic3r/PolylineCollection.cpp index ca9c64d23e..1304161c3f 100644 --- a/xs/src/libslic3r/PolylineCollection.cpp +++ b/src/libslic3r/PolylineCollection.cpp @@ -15,9 +15,9 @@ inline int nearest_point_index(const std::vector &pairs, const Point & T dmin = std::numeric_limits::max(); int idx = 0; for (std::vector::const_iterator it = pairs.begin(); it != pairs.end(); ++it) { - T d = sqr(T(start_near.x - it->first.x)); + T d = sqr(T(start_near(0) - it->first(0))); if (d <= dmin) { - d += sqr(T(start_near.y - it->first.y)); + d += sqr(T(start_near(1) - it->first(1))); if (d < dmin) { idx = (it - pairs.begin()) * 2; dmin = d; @@ -26,9 +26,9 @@ inline int nearest_point_index(const std::vector &pairs, const Point & } } if (! no_reverse) { - d = sqr(T(start_near.x - it->last.x)); + d = sqr(T(start_near(0) - it->last(0))); if (d <= dmin) { - d += sqr(T(start_near.y - it->last.y)); + d += sqr(T(start_near(1) - it->last(1))); if (d < dmin) { idx = (it - pairs.begin()) * 2 + 1; dmin = d; @@ -77,12 +77,13 @@ Polylines PolylineCollection::_chained_path_from( Point PolylineCollection::leftmost_point(const Polylines &polylines) { - if (polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection"); + if (polylines.empty()) + throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); Polylines::const_iterator it = polylines.begin(); Point p = it->leftmost_point(); for (++ it; it != polylines.end(); ++it) { Point p2 = it->leftmost_point(); - if (p2.x < p.x) + if (p2(0) < p(0)) p = p2; } return p; diff --git a/xs/src/libslic3r/PolylineCollection.hpp b/src/libslic3r/PolylineCollection.hpp similarity index 100% rename from xs/src/libslic3r/PolylineCollection.hpp rename to src/libslic3r/PolylineCollection.hpp diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp new file mode 100644 index 0000000000..c661da5ca9 --- /dev/null +++ b/src/libslic3r/Print.cpp @@ -0,0 +1,2038 @@ +#include "Print.hpp" +#include "BoundingBox.hpp" +#include "ClipperUtils.hpp" +#include "Extruder.hpp" +#include "Flow.hpp" +#include "Geometry.hpp" +#include "I18N.hpp" +#include "SupportMaterial.hpp" +#include "GCode.hpp" +#include "GCode/WipeTowerPrusaMM.hpp" +#include +#include +#include +#include +#include + +#include "PrintExport.hpp" + +//! macro used to mark string used at localization, +//! return same string +#define L(s) Slic3r::I18N::translate(s) + +namespace Slic3r { + +template class PrintState; +template class PrintState; + +void Print::clear() +{ + tbb::mutex::scoped_lock lock(this->cancel_mutex()); + // The following call should stop background processing if it is running. + this->invalidate_all_steps(); + for (PrintObject *object : m_objects) + delete object; + m_objects.clear(); + for (PrintRegion *region : m_regions) + delete region; + m_regions.clear(); +} + +// Only used by the Perl test cases. +void Print::reload_object(size_t /* idx */) +{ + ModelObjectPtrs model_objects; + { + tbb::mutex::scoped_lock lock(this->cancel_mutex()); + // The following call should stop background processing if it is running. + this->invalidate_all_steps(); + /* TODO: this method should check whether the per-object config and per-material configs + have changed in such a way that regions need to be rearranged or we can just apply + the diff and invalidate something. Same logic as apply_config() + For now we just re-add all objects since we haven't implemented this incremental logic yet. + This should also check whether object volumes (parts) have changed. */ + // collect all current model objects + model_objects.reserve(m_objects.size()); + for (PrintObject *object : m_objects) + model_objects.push_back(object->model_object()); + // remove our print objects + for (PrintObject *object : m_objects) + delete object; + m_objects.clear(); + for (PrintRegion *region : m_regions) + delete region; + m_regions.clear(); + } + // re-add model objects + for (ModelObject *mo : model_objects) + this->add_model_object(mo); +} + +PrintRegion* Print::add_region() +{ + m_regions.emplace_back(new PrintRegion(this)); + return m_regions.back(); +} + +PrintRegion* Print::add_region(const PrintRegionConfig &config) +{ + m_regions.emplace_back(new PrintRegion(this, config)); + return m_regions.back(); +} + +// Called by Print::apply_config(). +// This method only accepts PrintConfig option keys. +bool Print::invalidate_state_by_config_options(const std::vector &opt_keys) +{ + if (opt_keys.empty()) + return false; + + // Cache the plenty of parameters, which influence the G-code generator only, + // or they are only notes not influencing the generated G-code. + static std::unordered_set steps_gcode = { + "avoid_crossing_perimeters", + "bed_shape", + "bed_temperature", + "before_layer_gcode", + "between_objects_gcode", + "bridge_acceleration", + "bridge_fan_speed", + "colorprint_heights", + "cooling", + "default_acceleration", + "deretract_speed", + "disable_fan_first_layers", + "duplicate_distance", + "end_gcode", + "end_filament_gcode", + "extrusion_axis", + "extruder_clearance_height", + "extruder_clearance_radius", + "extruder_colour", + "extruder_offset", + "extrusion_multiplier", + "fan_always_on", + "fan_below_layer_time", + "filament_colour", + "filament_diameter", + "filament_density", + "filament_notes", + "filament_cost", + "filament_max_volumetric_speed", + "first_layer_acceleration", + "first_layer_bed_temperature", + "first_layer_speed", + "gcode_comments", + "gcode_flavor", + "infill_acceleration", + "layer_gcode", + "min_fan_speed", + "max_fan_speed", + "max_print_height", + "min_print_speed", + "max_print_speed", + "max_volumetric_speed", + "max_volumetric_extrusion_rate_slope_positive", + "max_volumetric_extrusion_rate_slope_negative", + "notes", + "only_retract_when_crossing_perimeters", + "output_filename_format", + "perimeter_acceleration", + "post_process", + "printer_notes", + "retract_before_travel", + "retract_before_wipe", + "retract_layer_change", + "retract_length", + "retract_length_toolchange", + "retract_lift", + "retract_lift_above", + "retract_lift_below", + "retract_restart_extra", + "retract_restart_extra_toolchange", + "retract_speed", + "single_extruder_multi_material_priming", + "slowdown_below_layer_time", + "standby_temperature_delta", + "start_gcode", + "start_filament_gcode", + "toolchange_gcode", + "threads", + "travel_speed", + "use_firmware_retraction", + "use_relative_e_distances", + "use_volumetric_e", + "variable_layer_height", + "wipe", + "wipe_tower_x", + "wipe_tower_y", + "wipe_tower_rotation_angle" + }; + + static std::unordered_set steps_ignore; + + std::vector steps; + std::vector osteps; + bool invalidated = false; + + for (const t_config_option_key &opt_key : opt_keys) { + if (steps_gcode.find(opt_key) != steps_gcode.end()) { + // These options only affect G-code export or they are just notes without influence on the generated G-code, + // so there is nothing to invalidate. + steps.emplace_back(psGCodeExport); + } else if (steps_ignore.find(opt_key) != steps_ignore.end()) { + // These steps have no influence on the G-code whatsoever. Just ignore them. + } else if ( + opt_key == "skirts" + || opt_key == "skirt_height" + || opt_key == "skirt_distance" + || opt_key == "min_skirt_length" + || opt_key == "ooze_prevention") { + steps.emplace_back(psSkirt); + } else if (opt_key == "brim_width") { + steps.emplace_back(psBrim); + steps.emplace_back(psSkirt); + } else if ( + opt_key == "nozzle_diameter" + || opt_key == "resolution") { + osteps.emplace_back(posSlice); + } else if ( + opt_key == "complete_objects" + || opt_key == "filament_type" + || opt_key == "filament_soluble" + || opt_key == "first_layer_temperature" + || opt_key == "filament_loading_speed" + || opt_key == "filament_loading_speed_start" + || opt_key == "filament_unloading_speed" + || opt_key == "filament_unloading_speed_start" + || opt_key == "filament_toolchange_delay" + || opt_key == "filament_cooling_moves" + || opt_key == "filament_minimal_purge_on_wipe_tower" + || opt_key == "filament_cooling_initial_speed" + || opt_key == "filament_cooling_final_speed" + || opt_key == "filament_ramming_parameters" + || opt_key == "gcode_flavor" + || opt_key == "infill_first" + || opt_key == "single_extruder_multi_material" + || opt_key == "spiral_vase" + || opt_key == "temperature" + || opt_key == "wipe_tower" + || opt_key == "wipe_tower_width" + || opt_key == "wipe_tower_bridging" + || opt_key == "wiping_volumes_matrix" + || opt_key == "parking_pos_retraction" + || opt_key == "cooling_tube_retraction" + || opt_key == "cooling_tube_length" + || opt_key == "extra_loading_move" + || opt_key == "z_offset") { + steps.emplace_back(psWipeTower); + } else if ( + opt_key == "first_layer_extrusion_width" + || opt_key == "min_layer_height" + || opt_key == "max_layer_height") { + osteps.emplace_back(posPerimeters); + osteps.emplace_back(posInfill); + osteps.emplace_back(posSupportMaterial); + steps.emplace_back(psSkirt); + steps.emplace_back(psBrim); + } else { + // for legacy, if we can't handle this option let's invalidate all steps + //FIXME invalidate all steps of all objects as well? + invalidated |= this->invalidate_all_steps(); + // Continue with the other opt_keys to possibly invalidate any object specific steps. + } + } + + sort_remove_duplicates(steps); + for (PrintStep step : steps) + invalidated |= this->invalidate_step(step); + sort_remove_duplicates(osteps); + for (PrintObjectStep ostep : osteps) + for (PrintObject *object : m_objects) + invalidated |= object->invalidate_step(ostep); + return invalidated; +} + +bool Print::invalidate_step(PrintStep step) +{ + bool invalidated = Inherited::invalidate_step(step); + // Propagate to dependent steps. + //FIXME Why should skirt invalidate brim? Shouldn't it be vice versa? + if (step == psSkirt) + invalidated |= Inherited::invalidate_step(psBrim); + if (step != psGCodeExport) + invalidated |= Inherited::invalidate_step(psGCodeExport); + return invalidated; +} + +// returns true if an object step is done on all objects +// and there's at least one object +bool Print::is_step_done(PrintObjectStep step) const +{ + if (m_objects.empty()) + return false; + for (const PrintObject *object : m_objects) + if (!object->m_state.is_done(step)) + return false; + return true; +} + +// returns 0-based indices of used extruders +std::vector Print::object_extruders() const +{ + std::vector extruders; + + for (PrintRegion* region : m_regions) { + // these checks reflect the same logic used in the GUI for enabling/disabling + // extruder selection fields + if (region->config().perimeters.value > 0 || m_config.brim_width.value > 0) + extruders.push_back(region->config().perimeter_extruder - 1); + if (region->config().fill_density.value > 0) + extruders.push_back(region->config().infill_extruder - 1); + if (region->config().top_solid_layers.value > 0 || region->config().bottom_solid_layers.value > 0) + extruders.push_back(region->config().solid_infill_extruder - 1); + } + + sort_remove_duplicates(extruders); + return extruders; +} + +// returns 0-based indices of used extruders +std::vector Print::support_material_extruders() const +{ + std::vector extruders; + bool support_uses_current_extruder = false; + + for (PrintObject *object : m_objects) { + if (object->has_support_material()) { + if (object->config().support_material_extruder == 0) + support_uses_current_extruder = true; + else + extruders.push_back(object->config().support_material_extruder - 1); + if (object->config().support_material_interface_extruder == 0) + support_uses_current_extruder = true; + else + extruders.push_back(object->config().support_material_interface_extruder - 1); + } + } + + if (support_uses_current_extruder) + // Add all object extruders to the support extruders as it is not know which one will be used to print supports. + append(extruders, this->object_extruders()); + + sort_remove_duplicates(extruders); + return extruders; +} + +// returns 0-based indices of used extruders +std::vector Print::extruders() const +{ + std::vector extruders = this->object_extruders(); + append(extruders, this->support_material_extruders()); + sort_remove_duplicates(extruders); + return extruders; +} + +unsigned int Print::num_object_instances() const +{ + unsigned int instances = 0; + for (const PrintObject *print_object : m_objects) + instances += print_object->copies().size(); + return instances; +} + +void Print::_simplify_slices(double distance) +{ + for (PrintObject *object : m_objects) { + for (Layer *layer : object->m_layers) { + layer->slices.simplify(distance); + for (LayerRegion *layerm : layer->regions()) + layerm->slices.simplify(distance); + } + } +} + +double Print::max_allowed_layer_height() const +{ + double nozzle_diameter_max = 0.; + for (unsigned int extruder_id : this->extruders()) + nozzle_diameter_max = std::max(nozzle_diameter_max, m_config.nozzle_diameter.get_at(extruder_id)); + return nozzle_diameter_max; +} + +static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume) +{ + PrintRegionConfig config = default_region_config; + normalize_and_apply_config(config, volume.get_object()->config); + normalize_and_apply_config(config, volume.config); + if (! volume.material_id().empty()) + normalize_and_apply_config(config, volume.material()->config); + return config; +} + +// Caller is responsible for supplying models whose objects don't collide +// and have explicit instance positions. +void Print::add_model_object(ModelObject* model_object, int idx) +{ + tbb::mutex::scoped_lock lock(this->cancel_mutex()); + // Initialize a new print object and store it at the given position. + PrintObject *object = new PrintObject(this, model_object); + if (idx != -1) { + delete m_objects[idx]; + m_objects[idx] = object; + } else + m_objects.emplace_back(object); + // Invalidate all print steps. + this->invalidate_all_steps(); + + // Set the transformation matrix without translation from the first instance. + if (! model_object->instances.empty()) { + // Trafo and bounding box, both in world coordinate system. + Transform3d trafo = model_object->instances.front()->get_matrix(); + BoundingBoxf3 bbox = model_object->instance_bounding_box(0); + // Now shift the object up to align it with the print bed. + trafo.data()[14] -= bbox.min(2); + // and reset the XY translation. + trafo.data()[12] = 0; + trafo.data()[13] = 0; + object->set_trafo(trafo); + } + + size_t volume_id = 0; + for (const ModelVolume *volume : model_object->volumes) { + if (! volume->is_model_part() && ! volume->is_modifier()) + continue; + // Get the config applied to this volume. + PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume); + // Find an existing print region with the same config. + size_t region_id = size_t(-1); + for (size_t i = 0; i < m_regions.size(); ++ i) + if (config.equals(m_regions[i]->config())) { + region_id = i; + break; + } + // If no region exists with the same config, create a new one. + if (region_id == size_t(-1)) { + region_id = m_regions.size(); + this->add_region(config); + } + // Assign volume to a region. + object->add_region_volume(region_id, volume_id); + ++ volume_id; + } + + // Apply config to print object. + object->config_apply(this->default_object_config()); + { + //normalize_and_apply_config(object->config(), model_object->config); + DynamicPrintConfig src_normalized(model_object->config); + src_normalized.normalize(); + object->config_apply(src_normalized, true); + } + + this->update_object_placeholders(); +} + +bool Print::apply_config(DynamicPrintConfig config) +{ + tbb::mutex::scoped_lock lock(this->cancel_mutex()); + + // we get a copy of the config object so we can modify it safely + config.normalize(); + + // apply variables to placeholder parser + m_placeholder_parser.apply_config(config); + + // handle changes to print config + t_config_option_keys print_diff = m_config.diff(config); + m_config.apply_only(config, print_diff, true); + bool invalidated = this->invalidate_state_by_config_options(print_diff); + + // handle changes to object config defaults + m_default_object_config.apply(config, true); + for (PrintObject *object : m_objects) { + // we don't assume that config contains a full ObjectConfig, + // so we base it on the current print-wise default + PrintObjectConfig new_config = this->default_object_config(); + // we override the new config with object-specific options + normalize_and_apply_config(new_config, object->model_object()->config); + // Force a refresh of a variable layer height profile at the PrintObject if it is not valid. + if (! object->layer_height_profile_valid) { + // The layer_height_profile is not valid for some reason (updated by the user or invalidated due to some option change). + // Invalidate the slicing step, which in turn invalidates everything. + object->invalidate_step(posSlice); + // Trigger recalculation. + invalidated = true; + } + // check whether the new config is different from the current one + t_config_option_keys diff = object->config().diff(new_config); + object->config_apply_only(new_config, diff, true); + invalidated |= object->invalidate_state_by_config_options(diff); + } + + // handle changes to regions config defaults + m_default_region_config.apply(config, true); + + // All regions now have distinct settings. + // Check whether applying the new region config defaults we'd get different regions. + bool rearrange_regions = false; + { + // Collect the already visited region configs into other_region_configs, + // so one may check for duplicates. + std::vector other_region_configs; + for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) { + PrintRegion ®ion = *m_regions[region_id]; + PrintRegionConfig this_region_config; + bool this_region_config_set = false; + for (PrintObject *object : m_objects) { + if (region_id < object->region_volumes.size()) { + for (int volume_id : object->region_volumes[region_id]) { + const ModelVolume &volume = *object->model_object()->volumes[volume_id]; + if (this_region_config_set) { + // If the new config for this volume differs from the other + // volume configs currently associated to this region, it means + // the region subdivision does not make sense anymore. + if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume))) { + rearrange_regions = true; + goto exit_for_rearrange_regions; + } + } else { + this_region_config = region_config_from_model_volume(m_default_region_config, volume); + this_region_config_set = true; + } + for (const PrintRegionConfig &cfg : other_region_configs) { + // If the new config for this volume equals any of the other + // volume configs that are not currently associated to this + // region, it means the region subdivision does not make + // sense anymore. + if (cfg.equals(this_region_config)) { + rearrange_regions = true; + goto exit_for_rearrange_regions; + } + } + } + } + } + if (this_region_config_set) { + t_config_option_keys diff = region.config().diff(this_region_config); + if (! diff.empty()) { + region.config_apply_only(this_region_config, diff, false); + for (PrintObject *object : m_objects) + if (region_id < object->region_volumes.size() && ! object->region_volumes[region_id].empty()) + invalidated |= object->invalidate_state_by_config_options(diff); + } + other_region_configs.emplace_back(std::move(this_region_config)); + } + } + } + +exit_for_rearrange_regions: + + if (rearrange_regions) { + // The current subdivision of regions does not make sense anymore. + // We need to remove all objects and re-add them. + ModelObjectPtrs model_objects; + model_objects.reserve(m_objects.size()); + for (PrintObject *object : m_objects) + model_objects.push_back(object->model_object()); + this->clear(); + for (ModelObject *mo : model_objects) + this->add_model_object(mo); + invalidated = true; + } + + // Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads. + for (PrintObject *object : m_objects) + if (! object->layer_height_profile_valid) + object->update_layer_height_profile(); + + return invalidated; +} + +// Test whether the two models contain the same number of ModelObjects with the same set of IDs +// ordered in the same order. In that case it is not necessary to kill the background processing. +static inline bool model_object_list_equal(const Model &model_old, const Model &model_new) +{ + if (model_old.objects.size() != model_new.objects.size()) + return false; + for (size_t i = 0; i < model_old.objects.size(); ++ i) + if (model_old.objects[i]->id() != model_new.objects[i]->id()) + return false; + return true; +} + +// Test whether the new model is just an extension of the old model (new objects were added +// to the end of the original list. In that case it is not necessary to kill the background processing. +static inline bool model_object_list_extended(const Model &model_old, const Model &model_new) +{ + if (model_old.objects.size() >= model_new.objects.size()) + return false; + for (size_t i = 0; i < model_old.objects.size(); ++ i) + if (model_old.objects[i]->id() != model_new.objects[i]->id()) + return false; + return true; +} + +static inline bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type) +{ + bool modifiers_differ = false; + size_t i_old, i_new; + for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) { + const ModelVolume &mv_old = *model_object_old.volumes[i_old]; + const ModelVolume &mv_new = *model_object_new.volumes[i_new]; + if (mv_old.type() != type) { + ++ i_old; + continue; + } + if (mv_new.type() != type) { + ++ i_new; + continue; + } + if (mv_old.id() != mv_new.id()) + return true; + //FIXME test for the content of the mesh! + +#if ENABLE_MODELVOLUME_TRANSFORM + if (!mv_old.get_matrix().isApprox(mv_new.get_matrix())) + return true; +#endif // ENABLE_MODELVOLUME_TRANSFORM + ++i_old; + ++ i_new; + } + for (; i_old < model_object_old.volumes.size(); ++ i_old) { + const ModelVolume &mv_old = *model_object_old.volumes[i_old]; + if (mv_old.type() == type) + // ModelVolume was deleted. + return true; + } + for (; i_new < model_object_new.volumes.size(); ++ i_new) { + const ModelVolume &mv_new = *model_object_new.volumes[i_new]; + if (mv_new.type() == type) + // ModelVolume was added. + return true; + } + return false; +} + +// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new +// in the exact order and with the same IDs. +// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order. +void Print::model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new) +{ + typedef std::pair ModelVolumeWithStatus; + std::vector old_volumes; + old_volumes.reserve(model_object_dst.volumes.size()); + for (const ModelVolume *model_volume : model_object_dst.volumes) + old_volumes.emplace_back(ModelVolumeWithStatus(model_volume, false)); + auto model_volume_lower = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() < mv2.first->id(); }; + auto model_volume_equal = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() == mv2.first->id(); }; + std::sort(old_volumes.begin(), old_volumes.end(), model_volume_lower); + model_object_dst.volumes.clear(); + model_object_dst.volumes.reserve(model_object_new.volumes.size()); + for (const ModelVolume *model_volume_src : model_object_new.volumes) { + ModelVolumeWithStatus key(model_volume_src, false); + auto it = std::lower_bound(old_volumes.begin(), old_volumes.end(), key, model_volume_lower); + if (it != old_volumes.end() && model_volume_equal(*it, key)) { + // The volume was found in the old list. Just copy it. + assert(! it->second); // not consumed yet + it->second = true; + ModelVolume *model_volume_dst = const_cast(it->first); + assert(model_volume_dst->type() == model_volume_src->type()); + model_object_dst.volumes.emplace_back(model_volume_dst); + if (model_volume_dst->is_support_modifier()) + model_volume_dst->set_transformation(model_volume_src->get_transformation()); + assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix())); + } else { + // The volume was not found in the old list. Create a new copy. + assert(model_volume_src->is_support_modifier()); + model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src)); + model_object_dst.volumes.back()->set_model_object(&model_object_dst); + } + } + // Release the non-consumed old volumes (those were deleted from the new list). + for (ModelVolumeWithStatus &mv_with_status : old_volumes) + if (! mv_with_status.second) + delete mv_with_status.first; +} + +static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolume::Type type) +{ + size_t i_src, i_dst; + for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) { + const ModelVolume &mv_src = *model_object_src.volumes[i_src]; + ModelVolume &mv_dst = *model_object_dst.volumes[i_dst]; + if (mv_src.type() != type) { + ++ i_src; + continue; + } + if (mv_dst.type() != type) { + ++ i_dst; + continue; + } + assert(mv_src.id() == mv_dst.id()); + // Copy the ModelVolume data. + mv_dst.name = mv_src.name; + mv_dst.config = mv_src.config; + //FIXME what to do with the materials? + // mv_dst.m_material_id = mv_src.m_material_id; + ++ i_src; + ++ i_dst; + } +} + +static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs) +{ + typedef Transform3d::Scalar T; + const T *lv = lhs.data(); + const T *rv = rhs.data(); + for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) { + if (*lv < *rv) + return true; + else if (*lv > *rv) + return false; + } + return false; +} + +static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs) +{ + typedef Transform3d::Scalar T; + const T *lv = lhs.data(); + const T *rv = rhs.data(); + for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) + if (*lv != *rv) + return false; + return true; +} + +struct PrintInstances +{ + Transform3d trafo; + Points copies; + bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); } +}; + +// Generate a list of trafos and XY offsets for instances of a ModelObject +static std::vector print_objects_from_model_object(const ModelObject &model_object) +{ + std::set trafos; + PrintInstances trafo; + trafo.copies.assign(1, Point()); + for (ModelInstance *model_instance : model_object.instances) + if (model_instance->is_printable()) { + trafo.trafo = model_instance->get_matrix(); + // Set the Z axis of the transformation. + trafo.copies.front() = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]); + trafo.trafo.data()[12] = 0; + trafo.trafo.data()[13] = 0; + auto it = trafos.find(trafo); + if (it == trafos.end()) + trafos.emplace(trafo); + else + const_cast(*it).copies.emplace_back(trafo.copies.front()); + } + return std::vector(trafos.begin(), trafos.end()); +} + +#ifdef _DEBUG +// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. +static inline void check_model_ids_validity(const Model &model) +{ + std::set ids; + auto check = [&ids](ModelID id) { + assert(id.id > 0); + assert(ids.find(id) == ids.end()); + ids.insert(id); + }; + for (const ModelObject *model_object : model.objects) { + check(model_object->id()); + for (const ModelVolume *model_volume : model_object->volumes) + check(model_volume->id()); + for (const ModelInstance *model_instance : model_object->instances) + check(model_instance->id()); + } + for (const auto mm : model.materials) + check(mm.second->id()); +} + +static inline void check_model_ids_equal(const Model &model1, const Model &model2) +{ + // Verify whether the IDs of model1 and model match. + assert(model1.objects.size() == model2.objects.size()); + for (size_t idx_model = 0; idx_model < model2.objects.size(); ++ idx_model) { + const ModelObject &model_object1 = *model1.objects[idx_model]; + const ModelObject &model_object2 = * model2.objects[idx_model]; + assert(model_object1.id() == model_object2.id()); + assert(model_object1.volumes.size() == model_object2.volumes.size()); + assert(model_object1.instances.size() == model_object2.instances.size()); + for (size_t i = 0; i < model_object1.volumes.size(); ++ i) + assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id()); + for (size_t i = 0; i < model_object1.instances.size(); ++ i) + assert(model_object1.instances[i]->id() == model_object2.instances[i]->id()); + } + assert(model1.materials.size() == model2.materials.size()); + { + auto it1 = model1.materials.begin(); + auto it2 = model2.materials.begin(); + for (; it1 != model1.materials.end(); ++ it1, ++ it2) { + assert(it1->first == it2->first); // compare keys + assert(it1->second->id() == it2->second->id()); + } + } +} +#endif /* _DEBUG */ + +Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in) +{ +#ifdef _DEBUG + check_model_ids_validity(model); +#endif /* _DEBUG */ + + // Make a copy of the config, normalize it. + DynamicPrintConfig config(config_in); + config.normalize(); + // Collect changes to print config. + t_config_option_keys print_diff = m_config.diff(config); + t_config_option_keys object_diff = m_default_object_config.diff(config); + t_config_option_keys region_diff = m_default_region_config.diff(config); + + // Do not use the ApplyStatus as we will use the max function when updating apply_status. + unsigned int apply_status = APPLY_STATUS_UNCHANGED; + auto update_apply_status = [&apply_status](bool invalidated) + { apply_status = std::max(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); }; + if (! (print_diff.empty() && object_diff.empty() && region_diff.empty())) + update_apply_status(false); + + // Grab the lock for the Print / PrintObject milestones. + tbb::mutex::scoped_lock lock(this->cancel_mutex()); + + // The following call may stop the background processing. + update_apply_status(this->invalidate_state_by_config_options(print_diff)); + // Apply variables to placeholder parser. The placeholder parser is used by G-code export, + // which should be stopped if print_diff is not empty. + if (m_placeholder_parser.apply_config(config)) + update_apply_status(this->invalidate_step(psGCodeExport)); + + // It is also safe to change m_config now after this->invalidate_state_by_config_options() call. + m_config.apply_only(config, print_diff, true); + // Handle changes to object config defaults + m_default_object_config.apply_only(config, object_diff, true); + // Handle changes to regions config defaults + m_default_region_config.apply_only(config, region_diff, true); + + struct ModelObjectStatus { + enum Status { + Unknown, + Old, + New, + Moved, + Deleted, + }; + ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {} + ModelID id; + Status status; + // Search by id. + bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; } + }; + std::set model_object_status; + + // 1) Synchronize model objects. + if (model.id() != m_model.id()) { + // Kill everything, initialize from scratch. + // Stop background processing. + this->call_cancell_callback(); + update_apply_status(this->invalidate_all_steps()); + for (PrintObject *object : m_objects) { + model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); + delete object; + } + m_objects.clear(); + for (PrintRegion *region : m_regions) + delete region; + m_regions.clear(); + m_model.assign_copy(model); + for (const ModelObject *model_object : m_model.objects) + model_object_status.emplace(model_object->id(), ModelObjectStatus::New); + } else { + if (model_object_list_equal(m_model, model)) { + // The object list did not change. + for (const ModelObject *model_object : m_model.objects) + model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); + } else if (model_object_list_extended(m_model, model)) { + // Add new objects. Their volumes and configs will be synchronized later. + update_apply_status(this->invalidate_step(psGCodeExport)); + for (const ModelObject *model_object : m_model.objects) + model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); + for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) { + model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New); + m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i])); + m_model.objects.back()->set_model(&m_model); + } + } else { + // Reorder the objects, add new objects. + // First stop background processing before shuffling or deleting the PrintObjects in the object list. + this->call_cancell_callback(); + update_apply_status(this->invalidate_step(psGCodeExport)); + // Second create a new list of objects. + std::vector model_objects_old(std::move(m_model.objects)); + m_model.objects.clear(); + m_model.objects.reserve(model.objects.size()); + auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); }; + std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower); + for (const ModelObject *mobj : model.objects) { + auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower); + if (it == model_objects_old.end() || (*it)->id() != mobj->id()) { + // New ModelObject added. + m_model.objects.emplace_back(ModelObject::new_copy(*mobj)); + m_model.objects.back()->set_model(&m_model); + model_object_status.emplace(mobj->id(), ModelObjectStatus::New); + } else { + // Existing ModelObject re-added (possibly moved in the list). + m_model.objects.emplace_back(*it); + model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved); + } + } + bool deleted_any = false; + for (ModelObject *&model_object : model_objects_old) { + if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) { + model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted); + deleted_any = true; + } else + // Do not delete this ModelObject instance. + model_object = nullptr; + } + if (deleted_any) { + // Delete PrintObjects of the deleted ModelObjects. + std::vector print_objects_old = std::move(m_objects); + m_objects.clear(); + m_objects.reserve(print_objects_old.size()); + for (PrintObject *print_object : print_objects_old) { + auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); + assert(it_status != model_object_status.end()); + if (it_status->status == ModelObjectStatus::Deleted) { + update_apply_status(print_object->invalidate_all_steps()); + delete print_object; + } else + m_objects.emplace_back(print_object); + } + for (ModelObject *model_object : model_objects_old) + delete model_object; + } + } + } + + // 2) Map print objects including their transformation matrices. + struct PrintObjectStatus { + enum Status { + Unknown, + Deleted, + Reused, + New + }; + PrintObjectStatus(PrintObject *print_object, Status status = Unknown) : + id(print_object->model_object()->id()), + print_object(print_object), + trafo(print_object->trafo()), + status(status) {} + PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {} + // ID of the ModelObject & PrintObject + ModelID id; + // Pointer to the old PrintObject + PrintObject *print_object; + // Trafo generated with model_object->world_matrix(true) + Transform3d trafo; + Status status; + // Search by id. + bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; } + }; + std::multiset print_object_status; + for (PrintObject *print_object : m_objects) + print_object_status.emplace(PrintObjectStatus(print_object)); + + // 3) Synchronize ModelObjects & PrintObjects. + for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { + ModelObject &model_object = *m_model.objects[idx_model_object]; + auto it_status = model_object_status.find(ModelObjectStatus(model_object.id())); + assert(it_status != model_object_status.end()); + assert(it_status->status != ModelObjectStatus::Deleted); + if (it_status->status == ModelObjectStatus::New) + // PrintObject instances will be added in the next loop. + continue; + // Update the ModelObject instance, possibly invalidate the linked PrintObjects. + assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved); + const ModelObject &model_object_new = *model.objects[idx_model_object]; + // Check whether a model part volume was added or removed, their transformations or order changed. + bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::MODEL_PART); + bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::PARAMETER_MODIFIER); + bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::SUPPORT_BLOCKER); + bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::SUPPORT_ENFORCER); + if (model_parts_differ || modifiers_differ || + model_object.origin_translation != model_object_new.origin_translation || + model_object.layer_height_ranges != model_object_new.layer_height_ranges || + model_object.layer_height_profile != model_object_new.layer_height_profile || + model_object.layer_height_profile_valid != model_object_new.layer_height_profile_valid) { + // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects. + auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); + for (auto it = range.first; it != range.second; ++ it) { + update_apply_status(it->print_object->invalidate_all_steps()); + const_cast(*it).status = PrintObjectStatus::Deleted; + } + // Copy content of the ModelObject including its ID, do not change the parent. + model_object.assign_copy(model_object_new); + } else if (support_blockers_differ || support_enforcers_differ) { + // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list. + this->call_cancell_callback(); + update_apply_status(false); + // Invalidate just the supports step. + auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); + for (auto it = range.first; it != range.second; ++ it) + update_apply_status(it->print_object->invalidate_step(posSupportMaterial)); + // Copy just the support volumes. + model_volume_list_update_supports(model_object, model_object_new); + } + if (! model_parts_differ && ! modifiers_differ) { + // Synchronize Object's config. + bool object_config_changed = model_object.config != model_object_new.config; + if (object_config_changed) + model_object.config = model_object_new.config; + if (! object_diff.empty() || object_config_changed) { + PrintObjectConfig new_config = m_default_object_config; + normalize_and_apply_config(new_config, model_object.config); + auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); + for (auto it = range.first; it != range.second; ++ it) { + t_config_option_keys diff = it->print_object->config().diff(new_config); + if (! diff.empty()) { + update_apply_status(it->print_object->invalidate_state_by_config_options(diff)); + it->print_object->config_apply_only(new_config, diff, true); + } + } + } + // Synchronize (just copy) the remaining data of ModelVolumes (name, config). + //FIXME What to do with m_material_id? + model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolume::MODEL_PART); + model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolume::PARAMETER_MODIFIER); + // Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step. + model_object.name = model_object_new.name; + model_object.input_file = model_object_new.input_file; + model_object.clear_instances(); + model_object.instances.reserve(model_object_new.instances.size()); + for (const ModelInstance *model_instance : model_object_new.instances) { + model_object.instances.emplace_back(new ModelInstance(*model_instance)); + model_object.instances.back()->set_model_object(&model_object); + } + } + } + + // 4) Generate PrintObjects from ModelObjects and their instances. + { + std::vector print_objects_new; + print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size())); + bool new_objects = false; + // Walk over all new model objects and check, whether there are matching PrintObjects. + for (ModelObject *model_object : m_model.objects) { + auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id())); + std::vector old; + if (range.first != range.second) { + old.reserve(print_object_status.count(PrintObjectStatus(model_object->id()))); + for (auto it = range.first; it != range.second; ++ it) + if (it->status != PrintObjectStatus::Deleted) + old.emplace_back(&(*it)); + } + // Generate a list of trafos and XY offsets for instances of a ModelObject + PrintObjectConfig config = m_default_object_config; + normalize_and_apply_config(config, model_object->config); + std::vector new_print_instances = print_objects_from_model_object(*model_object); + if (old.empty()) { + // Simple case, just generate new instances. + for (const PrintInstances &print_instances : new_print_instances) { + PrintObject *print_object = new PrintObject(this, model_object); + print_object->set_trafo(print_instances.trafo); + print_object->set_copies(print_instances.copies); + print_object->config_apply(config); + print_objects_new.emplace_back(print_object); + // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); + new_objects = true; + } + continue; + } + // Complex case, try to merge the two lists. + // Sort the old lexicographically by their trafos. + std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); }); + // Merge the old / new lists. + auto it_old = old.begin(); + for (const PrintInstances &new_instances : new_print_instances) { + for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old); + if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) { + // This is a new instance (or a set of instances with the same trafo). Just add it. + PrintObject *print_object = new PrintObject(this, model_object); + print_object->set_trafo(new_instances.trafo); + print_object->set_copies(new_instances.copies); + print_object->config_apply(config); + print_objects_new.emplace_back(print_object); + // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); + new_objects = true; + if (it_old != old.end()) + const_cast(*it_old)->status = PrintObjectStatus::Deleted; + } else { + // The PrintObject already exists and the copies differ. + if ((*it_old)->print_object->copies().size() != new_instances.copies.size()) + update_apply_status(this->invalidate_step(psWipeTower)); + if ((*it_old)->print_object->set_copies(new_instances.copies)) { + // Invalidated + update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psGCodeExport })); + } + print_objects_new.emplace_back((*it_old)->print_object); + const_cast(*it_old)->status = PrintObjectStatus::Reused; + } + } + } + if (m_objects != print_objects_new) { + this->call_cancell_callback(); + update_apply_status(this->invalidate_all_steps()); + m_objects = print_objects_new; + // Delete the PrintObjects marked as Unknown or Deleted. + bool deleted_objects = false; + for (auto &pos : print_object_status) + if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { + // update_apply_status(pos.print_object->invalidate_all_steps()); + delete pos.print_object; + deleted_objects = true; + } + if (new_objects || deleted_objects) + update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport })); + update_apply_status(new_objects); + } + print_object_status.clear(); + } + + // 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions. + // Update reference counts of regions from the remaining PrintObjects and their volumes. + // Regions with zero references could and should be reused. + for (PrintRegion *region : m_regions) + region->m_refcnt = 0; + for (PrintObject *print_object : m_objects) { + int idx_region = 0; + for (const auto &volumes : print_object->region_volumes) { + if (! volumes.empty()) + ++ m_regions[idx_region]->m_refcnt; + ++ idx_region; + } + } + + // All regions now have distinct settings. + // Check whether applying the new region config defaults we'd get different regions. + for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) { + PrintRegion ®ion = *m_regions[region_id]; + PrintRegionConfig this_region_config; + bool this_region_config_set = false; + for (PrintObject *print_object : m_objects) { + if (region_id < print_object->region_volumes.size()) { + for (int volume_id : print_object->region_volumes[region_id]) { + const ModelVolume &volume = *print_object->model_object()->volumes[volume_id]; + if (this_region_config_set) { + // If the new config for this volume differs from the other + // volume configs currently associated to this region, it means + // the region subdivision does not make sense anymore. + if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume))) + // Regions were split. Reset this print_object. + goto print_object_end; + } else { + this_region_config = region_config_from_model_volume(m_default_region_config, volume); + for (size_t i = 0; i < region_id; ++ i) + if (m_regions[i]->config().equals(this_region_config)) + // Regions were merged. Reset this print_object. + goto print_object_end; + this_region_config_set = true; + } + } + } + continue; + print_object_end: + update_apply_status(print_object->invalidate_all_steps()); + // Decrease the references to regions from this volume. + int ireg = 0; + for (const std::vector &volumes : print_object->region_volumes) { + if (! volumes.empty()) + -- m_regions[ireg]->m_refcnt; + ++ ireg; + } + print_object->region_volumes.clear(); + } + if (this_region_config_set) { + t_config_option_keys diff = region.config().diff(this_region_config); + if (! diff.empty()) { + region.config_apply_only(this_region_config, diff, false); + for (PrintObject *print_object : m_objects) + if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty()) + update_apply_status(print_object->invalidate_state_by_config_options(diff)); + } + } + } + + // Possibly add new regions for the newly added or resetted PrintObjects. + for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) { + PrintObject &print_object0 = *m_objects[idx_print_object]; + const ModelObject &model_object = *print_object0.model_object(); + std::vector map_volume_to_region(model_object.volumes.size(), -1); + for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) { + PrintObject &print_object = *m_objects[i]; + bool fresh = print_object.region_volumes.empty(); + unsigned int volume_id = 0; + for (const ModelVolume *volume : model_object.volumes) { + if (! volume->is_model_part() && ! volume->is_modifier()) + continue; + int region_id = -1; + if (&print_object == &print_object0) { + // Get the config applied to this volume. + PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume); + // Find an existing print region with the same config. + int idx_empty_slot = -1; + for (int i = 0; i < (int)m_regions.size(); ++ i) { + if (m_regions[i]->m_refcnt == 0) + idx_empty_slot = i; + else if (config.equals(m_regions[i]->config())) { + region_id = i; + break; + } + } + // If no region exists with the same config, create a new one. + if (region_id == -1) { + if (idx_empty_slot == -1) { + region_id = (int)m_regions.size(); + this->add_region(config); + } else { + region_id = idx_empty_slot; + m_regions[region_id]->set_config(std::move(config)); + } + } + map_volume_to_region[volume_id] = region_id; + } else + region_id = map_volume_to_region[volume_id]; + // Assign volume to a region. + if (fresh) { + if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty()) + ++ m_regions[region_id]->m_refcnt; + print_object.add_region_volume(region_id, volume_id); + } + ++ volume_id; + } + } + } + + // Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads. + for (PrintObject *object : m_objects) + if (! object->layer_height_profile_valid) + object->update_layer_height_profile(); + + this->update_object_placeholders(); + +#ifdef _DEBUG + check_model_ids_equal(m_model, model); +#endif /* _DEBUG */ + + return static_cast(apply_status); +} + +// Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. +void Print::update_object_placeholders() +{ + // get the first input file name + std::string input_file; + std::vector v_scale; + for (const PrintObject *object : m_objects) { + const ModelObject &mobj = *object->model_object(); + // CHECK_ME -> Is the following correct ? + v_scale.push_back("x:" + boost::lexical_cast(mobj.instances[0]->get_scaling_factor(X) * 100) + + "% y:" + boost::lexical_cast(mobj.instances[0]->get_scaling_factor(Y) * 100) + + "% z:" + boost::lexical_cast(mobj.instances[0]->get_scaling_factor(Z) * 100) + "%"); + if (input_file.empty()) + input_file = mobj.input_file; + } + + PlaceholderParser &pp = m_placeholder_parser; + pp.set("scale", v_scale); + if (! input_file.empty()) { + // get basename with and without suffix + const std::string input_basename = boost::filesystem::path(input_file).filename().string(); + pp.set("input_filename", input_basename); + const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of(".")); + pp.set("input_filename_base", input_basename_base); + } +} + +bool Print::has_infinite_skirt() const +{ + return (m_config.skirt_height == -1 && m_config.skirts > 0) + || (m_config.ooze_prevention && this->extruders().size() > 1); +} + +bool Print::has_skirt() const +{ + return (m_config.skirt_height > 0 && m_config.skirts > 0) + || this->has_infinite_skirt(); +} + +std::string Print::validate() const +{ + if (m_objects.empty()) + return L("All objects are outside of the print volume."); + + if (m_config.complete_objects) { + // Check horizontal clearance. + { + Polygons convex_hulls_other; + for (PrintObject *object : m_objects) { + // Get convex hull of all meshes assigned to this print object. + Polygon convex_hull; + { + Polygons mesh_convex_hulls; + for (const std::vector &volumes : object->region_volumes) + for (int volume_id : volumes) + mesh_convex_hulls.emplace_back(object->model_object()->volumes[volume_id]->mesh.convex_hull()); + // make a single convex hull for all of them + convex_hull = Slic3r::Geometry::convex_hull(mesh_convex_hulls); + } + // Apply the same transformations we apply to the actual meshes when slicing them. + object->model_object()->instances.front()->transform_polygon(&convex_hull); + // Grow convex hull with the clearance margin. + convex_hull = offset(convex_hull, scale_(m_config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front(); + // Now we check that no instance of convex_hull intersects any of the previously checked object instances. + for (const Point © : object->m_copies) { + Polygon p = convex_hull; + p.translate(copy); + if (! intersection(convex_hulls_other, p).empty()) + return L("Some objects are too close; your extruder will collide with them."); + polygons_append(convex_hulls_other, p); + } + } + } + // Check vertical clearance. + { + std::vector object_height; + for (const PrintObject *object : m_objects) + object_height.insert(object_height.end(), object->copies().size(), object->size(2)); + std::sort(object_height.begin(), object_height.end()); + // Ignore the tallest *copy* (this is why we repeat height for all of them): + // it will be printed as last one so its height doesn't matter. + object_height.pop_back(); + if (! object_height.empty() && object_height.back() > scale_(m_config.extruder_clearance_height.value)) + return L("Some objects are too tall and cannot be printed without extruder collisions."); + } + } // end if (m_config.complete_objects) + + if (m_config.spiral_vase) { + size_t total_copies_count = 0; + for (const PrintObject *object : m_objects) + total_copies_count += object->copies().size(); + // #4043 + if (total_copies_count > 1 && ! m_config.complete_objects.value) + return L("The Spiral Vase option can only be used when printing a single object."); + if (m_regions.size() > 1) + return L("The Spiral Vase option can only be used when printing single material objects."); + } + + if (m_config.single_extruder_multi_material) { + for (size_t i=1; ihas_wipe_tower() && ! m_objects.empty()) { + if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfMarlin) + return L("The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors."); + if (! m_config.use_relative_e_distances) + return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."); + SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters(); + + const PrintObject* tallest_object = m_objects.front(); // let's find the tallest object + for (const auto* object : m_objects) + if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) ) + tallest_object = object; + + for (PrintObject *object : m_objects) { + SlicingParameters slicing_params = object->slicing_parameters(); + if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON || + std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON) + return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths"); + if (slicing_params.raft_layers() != slicing_params0.raft_layers()) + return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers"); + if (object->config().support_material_contact_distance != m_objects.front()->config().support_material_contact_distance) + return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance"); + if (! equal_layering(slicing_params, slicing_params0)) + return L("The Wipe Tower is only supported for multiple objects if they are sliced equally."); + bool was_layer_height_profile_valid = object->layer_height_profile_valid; + object->update_layer_height_profile(); + object->layer_height_profile_valid = was_layer_height_profile_valid; + + if ( m_config.variable_layer_height ) { // comparing layer height profiles + bool failed = false; + if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) { + int i = 0; + while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) { + if (std::abs(tallest_object->layer_height_profile[i] - object->layer_height_profile[i])) { + failed = true; + break; + } + ++i; + if (i == object->layer_height_profile.size()-2) // this element contains this objects max z + if (tallest_object->layer_height_profile[i] > object->layer_height_profile[i]) // the difference does not matter in this case + ++i; + } + } + else + failed = true; + + if (failed) + return L("The Wipe tower is only supported if all objects have the same layer height profile"); + } + } + } + + { + // find the smallest nozzle diameter + std::vector extruders = this->extruders(); + if (extruders.empty()) + return L("The supplied settings will cause an empty print."); + + std::vector nozzle_diameters; + for (unsigned int extruder_id : extruders) + nozzle_diameters.push_back(m_config.nozzle_diameter.get_at(extruder_id)); + double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end()); + unsigned int total_extruders_count = m_config.nozzle_diameter.size(); + for (const auto& extruder_idx : extruders) + if ( extruder_idx >= total_extruders_count ) + return L("One or more object were assigned an extruder that the printer does not have."); + + for (PrintObject *object : m_objects) { + if ((object->config().support_material_extruder == -1 || object->config().support_material_interface_extruder == -1) && + (object->config().raft_layers > 0 || object->config().support_material.value)) { + // The object has some form of support and either support_material_extruder or support_material_interface_extruder + // will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles + // are of the same diameter. + if (nozzle_diameters.size() > 1) + return L("Printing with multiple extruders of differing nozzle diameters. " + "If support is to be printed with the current extruder (support_material_extruder == 0 or support_material_interface_extruder == 0), " + "all nozzles have to be of the same diameter."); + } + + // validate first_layer_height + double first_layer_height = object->config().get_abs_value(L("first_layer_height")); + double first_layer_min_nozzle_diameter; + if (object->config().raft_layers > 0) { + // if we have raft layers, only support material extruder is used on first layer + size_t first_layer_extruder = object->config().raft_layers == 1 + ? object->config().support_material_interface_extruder-1 + : object->config().support_material_extruder-1; + first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ? + min_nozzle_diameter : + m_config.nozzle_diameter.get_at(first_layer_extruder); + } else { + // if we don't have raft layers, any nozzle diameter is potentially used in first layer + first_layer_min_nozzle_diameter = min_nozzle_diameter; + } + if (first_layer_height > first_layer_min_nozzle_diameter) + return L("First layer height can't be greater than nozzle diameter"); + + // validate layer_height + if (object->config().layer_height.value > min_nozzle_diameter) + return L("Layer height can't be greater than nozzle diameter"); + } + } + + return std::string(); +} + +// the bounding box of objects placed in copies position +// (without taking skirt/brim/support material into account) +BoundingBox Print::bounding_box() const +{ + BoundingBox bb; + for (const PrintObject *object : m_objects) + for (Point copy : object->m_copies) { + bb.merge(copy); + copy += to_2d(object->size); + bb.merge(copy); + } + return bb; +} + +// the total bounding box of extrusions, including skirt/brim/support material +// this methods needs to be called even when no steps were processed, so it should +// only use configuration values +BoundingBox Print::total_bounding_box() const +{ + // get objects bounding box + BoundingBox bb = this->bounding_box(); + + // we need to offset the objects bounding box by at least half the perimeters extrusion width + Flow perimeter_flow = m_objects.front()->get_layer(0)->get_region(0)->flow(frPerimeter); + double extra = perimeter_flow.width/2; + + // consider support material + if (this->has_support_material()) { + extra = std::max(extra, SUPPORT_MATERIAL_MARGIN); + } + + // consider brim and skirt + if (m_config.brim_width.value > 0) { + Flow brim_flow = this->brim_flow(); + extra = std::max(extra, m_config.brim_width.value + brim_flow.width/2); + } + if (this->has_skirt()) { + int skirts = m_config.skirts.value; + if (skirts == 0 && this->has_infinite_skirt()) skirts = 1; + Flow skirt_flow = this->skirt_flow(); + extra = std::max( + extra, + m_config.brim_width.value + + m_config.skirt_distance.value + + skirts * skirt_flow.spacing() + + skirt_flow.width/2 + ); + } + + if (extra > 0) + bb.offset(scale_(extra)); + + return bb; +} + +double Print::skirt_first_layer_height() const +{ + if (m_objects.empty()) + throw std::invalid_argument("skirt_first_layer_height() can't be called without PrintObjects"); + return m_objects.front()->config().get_abs_value("first_layer_height"); +} + +Flow Print::brim_flow() const +{ + ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; + if (width.value == 0) + width = m_regions.front()->config().perimeter_extrusion_width; + if (width.value == 0) + width = m_objects.front()->config().extrusion_width; + + /* We currently use a random region's perimeter extruder. + While this works for most cases, we should probably consider all of the perimeter + extruders and take the one with, say, the smallest index. + The same logic should be applied to the code that selects the extruder during G-code + generation as well. */ + return Flow::new_from_config_width( + frPerimeter, + width, + m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1), + this->skirt_first_layer_height(), + 0 + ); +} + +Flow Print::skirt_flow() const +{ + ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; + if (width.value == 0) + width = m_regions.front()->config().perimeter_extrusion_width; + if (width.value == 0) + width = m_objects.front()->config().extrusion_width; + + /* We currently use a random object's support material extruder. + While this works for most cases, we should probably consider all of the support material + extruders and take the one with, say, the smallest index; + The same logic should be applied to the code that selects the extruder during G-code + generation as well. */ + return Flow::new_from_config_width( + frPerimeter, + width, + m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1), + this->skirt_first_layer_height(), + 0 + ); +} + +bool Print::has_support_material() const +{ + for (const PrintObject *object : m_objects) + if (object->has_support_material()) + return true; + return false; +} + +/* This method assigns extruders to the volumes having a material + but not having extruders set in the volume config. */ +void Print::auto_assign_extruders(ModelObject* model_object) const +{ + // only assign extruders if object has more than one volume + if (model_object->volumes.size() < 2) + return; + +// size_t extruders = m_config.nozzle_diameter.values.size(); + for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { + ModelVolume *volume = model_object->volumes[volume_id]; + //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned. + if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder")) + volume->config.opt("extruder", true)->value = int(volume_id + 1); + } +} + +// Slicing process, running at a background thread. +void Print::process() +{ + BOOST_LOG_TRIVIAL(info) << "Staring the slicing process."; + for (PrintObject *obj : m_objects) + obj->make_perimeters(); + this->throw_if_canceled(); + this->set_status(70, "Infilling layers"); + for (PrintObject *obj : m_objects) + obj->infill(); + this->throw_if_canceled(); + for (PrintObject *obj : m_objects) + obj->generate_support_material(); + this->throw_if_canceled(); + if (! this->is_step_done(psSkirt)) { + this->set_started(psSkirt); + m_skirt.clear(); + if (this->has_skirt()) { + this->set_status(88, "Generating skirt"); + this->_make_skirt(); + } + this->set_done(psSkirt); + } + this->throw_if_canceled(); + if (! this->is_step_done(psBrim)) { + this->set_started(psBrim); + m_brim.clear(); + if (m_config.brim_width > 0) { + this->set_status(88, "Generating brim"); + this->_make_brim(); + } + this->set_done(psBrim); + } + this->throw_if_canceled(); + if (! this->is_step_done(psWipeTower)) { + this->set_started(psWipeTower); + m_wipe_tower_data.clear(); + if (this->has_wipe_tower()) { + //this->set_status(95, "Generating wipe tower"); + this->_make_wipe_tower(); + } + this->set_done(psWipeTower); + } + BOOST_LOG_TRIVIAL(info) << "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. +void Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data) +{ + // prerequisites + this->process(); + + // output everything to a G-code file + // The following call may die if the output_filename_format template substitution fails. + std::string path = this->output_filepath(path_template); + std::string message = "Exporting G-code"; + if (! path.empty()) { + message += " to "; + message += path; + } + this->set_status(90, message); + + // The following line may die for multiple reasons. + GCode gcode; + gcode.do_export(this, path.c_str(), preview_data); +} + +void Print::_make_skirt() +{ + // First off we need to decide how tall the skirt must be. + // The skirt_height option from config is expressed in layers, but our + // object might have different layer heights, so we need to find the print_z + // of the highest layer involved. + // Note that unless has_infinite_skirt() == true + // the actual skirt might not reach this $skirt_height_z value since the print + // order of objects on each layer is not guaranteed and will not generally + // include the thickest object first. It is just guaranteed that a skirt is + // prepended to the first 'n' layers (with 'n' = skirt_height). + // $skirt_height_z in this case is the highest possible skirt height for safety. + coordf_t skirt_height_z = 0.; + for (const PrintObject *object : m_objects) { + size_t skirt_layers = this->has_infinite_skirt() ? + object->layer_count() : + std::min(size_t(m_config.skirt_height.value), object->layer_count()); + skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z); + } + + // Collect points from all layers contained in skirt height. + Points points; + for (const PrintObject *object : m_objects) { + Points object_points; + // Get object layers up to skirt_height_z. + for (const Layer *layer : object->m_layers) { + if (layer->print_z > skirt_height_z) + break; + for (const ExPolygon &expoly : layer->slices.expolygons) + // Collect the outer contour points only, ignore holes for the calculation of the convex hull. + append(object_points, expoly.contour.points); + } + // Get support layers up to skirt_height_z. + for (const SupportLayer *layer : object->support_layers()) { + if (layer->print_z > skirt_height_z) + break; + for (const ExtrusionEntity *extrusion_entity : layer->support_fills.entities) + append(object_points, extrusion_entity->as_polyline().points); + } + // Repeat points for each object copy. + for (const Point &shift : object->m_copies) { + Points copy_points = object_points; + for (Point &pt : copy_points) + pt += shift; + append(points, copy_points); + } + } + + if (points.size() < 3) + // At least three points required for a convex hull. + return; + + this->throw_if_canceled(); + Polygon convex_hull = Slic3r::Geometry::convex_hull(points); + + // Skirt may be printed on several layers, having distinct layer heights, + // but loops must be aligned so can't vary width/spacing + // TODO: use each extruder's own flow + double first_layer_height = this->skirt_first_layer_height(); + Flow flow = this->skirt_flow(); + float spacing = flow.spacing(); + double mm3_per_mm = flow.mm3_per_mm(); + + std::vector extruders; + std::vector extruders_e_per_mm; + { + auto set_extruders = this->extruders(); + extruders.reserve(set_extruders.size()); + extruders_e_per_mm.reserve(set_extruders.size()); + for (auto &extruder_id : set_extruders) { + extruders.push_back(extruder_id); + extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &m_config).e_per_mm(mm3_per_mm)); + } + } + + // Number of skirt loops per skirt layer. + int n_skirts = m_config.skirts.value; + if (this->has_infinite_skirt() && n_skirts == 0) + n_skirts = 1; + + // Initial offset of the brim inner edge from the object (possible with a support & raft). + // The skirt will touch the brim if the brim is extruded. + Flow brim_flow = this->brim_flow(); + double actual_brim_width = brim_flow.spacing() * floor(m_config.brim_width.value / brim_flow.spacing()); + coord_t distance = scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.); + // Draw outlines from outside to inside. + // Loop while we have less skirts than required or any extruder hasn't reached the min length if any. + std::vector extruded_length(extruders.size(), 0.); + for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) { + this->throw_if_canceled(); + // Offset the skirt outside. + distance += coord_t(scale_(spacing)); + // Generate the skirt centerline. + Polygon loop; + { + Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1)); + Geometry::simplify_polygons(loops, scale_(0.05), &loops); + loop = loops.front(); + } + // Extrude the skirt loop. + ExtrusionLoop eloop(elrSkirt); + eloop.paths.emplace_back(ExtrusionPath( + ExtrusionPath( + erSkirt, + mm3_per_mm, // this will be overridden at G-code export time + flow.width, + first_layer_height // this will be overridden at G-code export time + ))); + eloop.paths.back().polyline = loop.split_at_first_point(); + m_skirt.append(eloop); + if (m_config.min_skirt_length.value > 0) { + // The skirt length is limited. Sum the total amount of filament length extruded, in mm. + extruded_length[extruder_idx] += unscale(loop.length()) * extruders_e_per_mm[extruder_idx]; + if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) { + // Not extruded enough yet with the current extruder. Add another loop. + if (i == 1) + ++ i; + } else { + assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value); + // Enough extruded with the current extruder. Extrude with the next one, + // until the prescribed number of skirt loops is extruded. + if (extruder_idx + 1 < extruders.size()) + ++ extruder_idx; + } + } else { + // The skirt lenght is not limited, extrude the skirt with the 1st extruder only. + } + } + // Brims were generated inside out, reverse to print the outmost contour first. + m_skirt.reverse(); +} + +void Print::_make_brim() +{ + // Brim is only printed on first layer and uses perimeter extruder. + Flow flow = this->brim_flow(); + Polygons islands; + for (PrintObject *object : m_objects) { + Polygons object_islands; + for (ExPolygon &expoly : object->m_layers.front()->slices.expolygons) + object_islands.push_back(expoly.contour); + if (! object->support_layers().empty()) + object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); + islands.reserve(islands.size() + object_islands.size() * object->m_copies.size()); + for (const Point &pt : object->m_copies) + for (Polygon &poly : object_islands) { + islands.push_back(poly); + islands.back().translate(pt); + } + } + Polygons loops; + size_t num_loops = size_t(floor(m_config.brim_width.value / flow.spacing())); + for (size_t i = 0; i < num_loops; ++ i) { + this->throw_if_canceled(); + islands = offset(islands, float(flow.scaled_spacing()), jtSquare); + for (Polygon &poly : islands) { + // poly.simplify(SCALED_RESOLUTION); + poly.points.push_back(poly.points.front()); + Points p = MultiPoint::_douglas_peucker(poly.points, SCALED_RESOLUTION); + p.pop_back(); + poly.points = std::move(p); + } + polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing()))); + } + + loops = union_pt_chained(loops, false); + std::reverse(loops.begin(), loops.end()); + extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())); +} + +// Wipe tower support. +bool Print::has_wipe_tower() const +{ + return + m_config.single_extruder_multi_material.value && + ! m_config.spiral_vase.value && + m_config.wipe_tower.value && + m_config.nozzle_diameter.values.size() > 1; +} + +void Print::_make_wipe_tower() +{ + m_wipe_tower_data.clear(); + if (! this->has_wipe_tower()) + return; + + // Get wiping matrix to get number of extruders and convert vector to vector: + std::vector wiping_matrix(cast(m_config.wiping_volumes_matrix.values)); + // Extract purging volumes for each extruder pair: + std::vector> wipe_volumes; + const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON); + for (unsigned int i = 0; i(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders)); + + // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print. + m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true); + if (! m_wipe_tower_data.tool_ordering.has_wipe_tower()) + // Don't generate any wipe tower. + return; + + // Check whether there are any layers in m_tool_ordering, which are marked with has_wipe_tower, + // they print neither object, nor support. These layers are above the raft and below the object, and they + // shall be added to the support layers to be printed. + // see https://github.com/prusa3d/Slic3r/issues/607 + { + size_t idx_begin = size_t(-1); + size_t idx_end = m_wipe_tower_data.tool_ordering.layer_tools().size(); + // Find the first wipe tower layer, which does not have a counterpart in an object or a support layer. + for (size_t i = 0; i < idx_end; ++ i) { + const LayerTools < = m_wipe_tower_data.tool_ordering.layer_tools()[i]; + if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) { + idx_begin = i; + break; + } + } + if (idx_begin != size_t(-1)) { + // Find the position in m_objects.first()->support_layers to insert these new support layers. + double wipe_tower_new_layer_print_z_first = m_wipe_tower_data.tool_ordering.layer_tools()[idx_begin].print_z; + SupportLayerPtrs::const_iterator it_layer = m_objects.front()->support_layers().begin(); + SupportLayerPtrs::const_iterator it_end = m_objects.front()->support_layers().end(); + for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer); + // Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer. + for (size_t i = idx_begin; i < idx_end; ++ i) { + LayerTools < = const_cast(m_wipe_tower_data.tool_ordering.layer_tools()[i]); + if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support)) + break; + lt.has_support = true; + // Insert the new support layer. + double height = lt.print_z - m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z; + //FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway. + it_layer = m_objects.front()->insert_support_layer(it_layer, size_t(-1), height, lt.print_z, lt.print_z - 0.5 * height); + ++ it_layer; + } + } + } + this->throw_if_canceled(); + + // Initialize the wipe tower. + WipeTowerPrusaMM wipe_tower( + float(m_config.wipe_tower_x.value), float(m_config.wipe_tower_y.value), + float(m_config.wipe_tower_width.value), + float(m_config.wipe_tower_rotation_angle.value), float(m_config.cooling_tube_retraction.value), + float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value), + float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging), wipe_volumes, + m_wipe_tower_data.tool_ordering.first_extruder()); + + //wipe_tower.set_retract(); + //wipe_tower.set_zhop(); + + // Set the extruder & material properties at the wipe tower object. + for (size_t i = 0; i < number_of_extruders; ++ i) + wipe_tower.set_extruder( + i, + WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()), + m_config.temperature.get_at(i), + m_config.first_layer_temperature.get_at(i), + m_config.filament_loading_speed.get_at(i), + m_config.filament_loading_speed_start.get_at(i), + m_config.filament_unloading_speed.get_at(i), + m_config.filament_unloading_speed_start.get_at(i), + m_config.filament_toolchange_delay.get_at(i), + m_config.filament_cooling_moves.get_at(i), + m_config.filament_cooling_initial_speed.get_at(i), + m_config.filament_cooling_final_speed.get_at(i), + m_config.filament_ramming_parameters.get_at(i), + m_config.nozzle_diameter.get_at(i)); + + m_wipe_tower_data.priming = Slic3r::make_unique( + wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false)); + + // Lets go through the wipe tower layers and determine pairs of extruder changes for each + // to pass to wipe_tower (so that it can use it for planning the layout of the tower) + { + unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.all_extruders().back(); + for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers + if (!layer_tools.has_wipe_tower) continue; + bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front(); + wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false); + for (const auto extruder_id : layer_tools.extruders) { + if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) { + float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange + // Not all of that can be used for infill purging: + volume_to_wipe -= m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id); + + // try to assign some infills/objects for the wiping: + volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe); + + // add back the minimal amount toforce on the wipe tower: + volume_to_wipe += m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id); + + // request a toolchange at the wipe tower with at least volume_to_wipe purging amount + wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, + first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe); + current_extruder_id = extruder_id; + } + } + layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this); + if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0) + break; + } + } + + // Generate the wipe tower layers. + m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size()); + wipe_tower.generate(m_wipe_tower_data.tool_changes); + m_wipe_tower_data.depth = wipe_tower.get_depth(); + + // Unload the current filament over the purge tower. + coordf_t layer_height = m_objects.front()->config().layer_height.value; + if (m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions > 0) { + // The wipe tower goes up to the last layer of the print. + if (wipe_tower.layer_finished()) { + // The wipe tower is printed to the top of the print and it has no space left for the final extruder purge. + // Lift Z to the next layer. + wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z + layer_height), float(layer_height), 0, false, true); + } else { + // There is yet enough space at this layer of the wipe tower for the final purge. + } + } else { + // The wipe tower does not reach the last print layer, perform the pruge at the last print layer. + assert(m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions == 0); + wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z), float(layer_height), 0, false, true); + } + m_wipe_tower_data.final_purge = Slic3r::make_unique( + wipe_tower.tool_change((unsigned int)-1, false)); + + m_wipe_tower_data.used_filament = wipe_tower.get_used_filament(); + m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges(); +} + +std::string Print::output_filename() const +{ + DynamicConfig cfg_timestamp; + PlaceholderParser::update_timestamp(cfg_timestamp); + try { + return this->placeholder_parser().process(m_config.output_filename_format.value, 0, &cfg_timestamp); + } catch (std::runtime_error &err) { + throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what()); + } +} + +std::string Print::output_filepath(const std::string &path) const +{ + // if we were supplied no path, generate an automatic one based on our first object's input file + if (path.empty()) { + // get the first input file name + std::string input_file; + for (const PrintObject *object : m_objects) { + input_file = object->model_object()->input_file; + if (! input_file.empty()) + break; + } + return (boost::filesystem::path(input_file).parent_path() / this->output_filename()).make_preferred().string(); + } + + // if we were supplied a directory, use it and append our automatically generated filename + boost::filesystem::path p(path); + if (boost::filesystem::is_directory(p)) + return (p / this->output_filename()).make_preferred().string(); + + // if we were supplied a file which is not a directory, use it + return path; +} + +void Print::export_png(const std::string &dirpath) +{ +// size_t idx = 0; +// for (PrintObject *obj : m_objects) { +// obj->slice(); +// this->set_status(int(floor(idx * 100. / m_objects.size() + 0.5)), "Slicing..."); +// ++ idx; +// } +// this->set_status(90, "Exporting zipped archive..."); +// print_to(*this, +// dirpath, +// float(m_config.bed_size_x.value), +// float(m_config.bed_size_y.value), +// int(m_config.pixel_width.value), +// int(m_config.pixel_height.value), +// float(m_config.exp_time.value), +// float(m_config.exp_time_first.value)); +// this->set_status(100, "Done."); +} + +// Returns extruder this eec should be printed with, according to PrintRegion config +int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion) +{ + return is_infill(fill.role()) ? std::max(0, (is_solid_infill(fill.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) : + std::max(region.config().perimeter_extruder.value - 1, 0); +} + +} // namespace Slic3r + diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp new file mode 100644 index 0000000000..b2f9f95010 --- /dev/null +++ b/src/libslic3r/Print.hpp @@ -0,0 +1,410 @@ +#ifndef slic3r_Print_hpp_ +#define slic3r_Print_hpp_ + +#include "PrintBase.hpp" + +#include "BoundingBox.hpp" +#include "Flow.hpp" +#include "Point.hpp" +#include "Layer.hpp" +#include "Model.hpp" +#include "PlaceholderParser.hpp" +#include "Slicing.hpp" +#include "GCode/ToolOrdering.hpp" +#include "GCode/WipeTower.hpp" + +namespace Slic3r { + +class Print; +class PrintObject; +class ModelObject; +class GCode; +class GCodePreviewData; + +// Print step IDs for keeping track of the print state. +enum PrintStep { + psSkirt, psBrim, psWipeTower, psGCodeExport, psCount, +}; +enum PrintObjectStep { + posSlice, posPerimeters, posPrepareInfill, + posInfill, posSupportMaterial, posCount, +}; + +// A PrintRegion object represents a group of volumes to print +// sharing the same config (including the same assigned extruder(s)) +class PrintRegion +{ + friend class Print; + +// Methods NOT modifying the PrintRegion's state: +public: + const Print* print() const { return m_print; } + const PrintRegionConfig& config() const { return m_config; } + Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; + // Average diameter of nozzles participating on extruding this region. + coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; + // Average diameter of nozzles participating on extruding this region. + coordf_t bridging_height_avg(const PrintConfig &print_config) const; + +// Methods modifying the PrintRegion's state: +public: + Print* print() { return m_print; } + void set_config(const PrintRegionConfig &config) { m_config = config; } + void set_config(PrintRegionConfig &&config) { m_config = std::move(config); } + void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) + { this->m_config.apply_only(other, keys, ignore_nonexistent); } + +protected: + size_t m_refcnt; + +private: + Print *m_print; + PrintRegionConfig m_config; + + PrintRegion(Print* print) : m_refcnt(0), m_print(print) {} + PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {} + ~PrintRegion() {} +}; + + +typedef std::vector LayerPtrs; +typedef std::vector SupportLayerPtrs; +class BoundingBoxf3; // TODO: for temporary constructor parameter + +class PrintObject : public PrintObjectBaseWithState +{ +private: // Prevents erroneous use by other classes. + typedef PrintObjectBaseWithState Inherited; + +public: + // vector of (vectors of volume ids), indexed by region_id + std::vector> region_volumes; + t_layer_height_ranges layer_height_ranges; + + // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. + // The pairs of are packed into a 1D array to simplify handling by the Perl XS. + // layer_height_profile must not be set by the background thread. + std::vector layer_height_profile; + // There is a layer_height_profile at both PrintObject and ModelObject. The layer_height_profile at the ModelObject + // is used for interactive editing and for loading / storing into a project file (AMF file as of today). + // This flag indicates that the layer_height_profile at the UI has been updated, therefore the backend needs to get it. + // This flag is necessary as we cannot safely clear the layer_height_profile if the background calculation is running. + bool layer_height_profile_valid; + + // this is set to true when LayerRegion->slices is split in top/internal/bottom + // so that next call to make_perimeters() performs a union() before computing loops + bool typed_slices; + + Vec3crd size; // XYZ in scaled coordinates + + const ModelObject* model_object() const { return m_model_object; } + ModelObject* model_object() { return m_model_object; } + const PrintObjectConfig& config() const { return m_config; } + const LayerPtrs& layers() const { return m_layers; } + const SupportLayerPtrs& support_layers() const { return m_support_layers; } + const Transform3d& trafo() const { return m_trafo; } + const Points& copies() const { return m_copies; } + + // since the object is aligned to origin, bounding box coincides with size + BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); } + + // adds region_id, too, if necessary + void add_region_volume(unsigned int region_id, int volume_id) { + if (region_id >= region_volumes.size()) + region_volumes.resize(region_id + 1); + region_volumes[region_id].push_back(volume_id); + } + // This is the *total* layer count (including support layers) + // this value is not supposed to be compared with Layer::id + // since they have different semantics. + size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } + size_t layer_count() const { return m_layers.size(); } + void clear_layers(); + Layer* get_layer(int idx) { return m_layers[idx]; } + const Layer* get_layer(int idx) const { return m_layers[idx]; } + + // print_z: top of the layer; slice_z: center of the layer. + Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); + + size_t support_layer_count() const { return m_support_layers.size(); } + void clear_support_layers(); + SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; } + SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z); + SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z); + void delete_support_layer(int idx); + + // methods for handling state + bool invalidate_state_by_config_options(const std::vector &opt_keys); + + // To be used over the layer_height_profile of both the PrintObject and ModelObject + // to initialize the height profile with the height ranges. + bool update_layer_height_profile(std::vector &layer_height_profile) const; + + // Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile. + // The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces. + bool update_layer_height_profile(); + + void reset_layer_height_profile(); + + void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action); + + // Collect the slicing parameters, to be used by variable layer thickness algorithm, + // by the interactive layer height editor and by the printing process itself. + // The slicing parameters are dependent on various configuration values + // (layer height, first layer height, raft settings, print nozzle diameter etc). + SlicingParameters slicing_parameters() const; + + // Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t + void slice(); + + // Helpers to slice support enforcer / blocker meshes by the support generator. + std::vector slice_support_enforcers() const; + std::vector slice_support_blockers() const; + +protected: + // to be called from Print only. + friend class Print; + + PrintObject(Print* print, ModelObject* model_object); + ~PrintObject() {} + + void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } + void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } + void set_trafo(const Transform3d& trafo) { m_trafo = trafo; } + bool set_copies(const Points &points); + // Invalidates the step, and its depending steps in PrintObject and Print. + bool invalidate_step(PrintObjectStep step); + // Invalidates all PrintObject and Print steps. + bool invalidate_all_steps(); + +private: + void make_perimeters(); + void prepare_infill(); + void infill(); + void generate_support_material(); + + void _slice(); + std::string _fix_slicing_errors(); + void _simplify_slices(double distance); + void _make_perimeters(); + bool has_support_material() const; + void detect_surfaces_type(); + void process_external_surfaces(); + void discover_vertical_shells(); + void bridge_over_infill(); + void clip_fill_surfaces(); + void discover_horizontal_shells(); + void combine_infill(); + void _generate_support_material(); + + ModelObject *m_model_object; + PrintObjectConfig m_config; + // Translation in Z + Rotation + Scaling / Mirroring. + Transform3d m_trafo = Transform3d::Identity(); + // Slic3r::Point objects in scaled G-code coordinates + Points m_copies; + // scaled coordinates to add to copies (to compensate for the alignment + // operated when creating the object but still preserving a coherent API + // for external callers) + Point m_copies_shift; + + LayerPtrs m_layers; + SupportLayerPtrs m_support_layers; + + std::vector _slice_region(size_t region_id, const std::vector &z, bool modifier); + std::vector _slice_volumes(const std::vector &z, const std::vector &volumes) const; +}; + +struct WipeTowerData +{ + // Following section will be consumed by the GCodeGenerator. + // Tool ordering of a non-sequential print has to be known to calculate the wipe tower. + // Cache it here, so it does not need to be recalculated during the G-code generation. + ToolOrdering tool_ordering; + // Cache of tool changes per print layer. + std::unique_ptr priming; + std::vector> tool_changes; + std::unique_ptr final_purge; + std::vector used_filament; + int number_of_toolchanges; + + // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box: + float depth; + + void clear() { + tool_ordering.clear(); + priming.reset(nullptr); + tool_changes.clear(); + final_purge.reset(nullptr); + used_filament.clear(); + number_of_toolchanges = -1; + depth = 0.f; + } +}; + +struct PrintStatistics +{ + PrintStatistics() { clear(); } + std::string estimated_normal_print_time; + std::string estimated_silent_print_time; + double total_used_filament; + double total_extruded_volume; + double total_cost; + double total_weight; + double total_wipe_tower_cost; + double total_wipe_tower_filament; + std::map filament_stats; + + void clear() { + estimated_normal_print_time.clear(); + estimated_silent_print_time.clear(); + total_used_filament = 0.; + total_extruded_volume = 0.; + total_cost = 0.; + total_weight = 0.; + total_wipe_tower_cost = 0.; + total_wipe_tower_filament = 0.; + filament_stats.clear(); + } +}; + +typedef std::vector PrintObjectPtrs; +typedef std::vector PrintRegionPtrs; + +// The complete print tray with possibly multiple objects. +class Print : public PrintBaseWithState +{ +private: // Prevents erroneous use by other classes. + typedef PrintBaseWithState Inherited; + +public: + Print() {} + virtual ~Print() { this->clear(); } + + PrinterTechnology technology() const noexcept { return ptFFF; } + + // Methods, which change the state of Print / PrintObject / PrintRegion. + // The following methods are synchronized with process() and export_gcode(), + // so that process() and export_gcode() may be called from a background thread. + // In case the following methods need to modify data processed by process() or export_gcode(), + // a cancellation callback is executed to stop the background processing before the operation. + void clear() override; + bool empty() const override { return m_objects.empty(); } + + ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override; + + // The following three methods are used by the Perl tests only. Get rid of them! + void reload_object(size_t idx); + void add_model_object(ModelObject* model_object, int idx = -1); + bool apply_config(DynamicPrintConfig config); + + void process() override; + void export_gcode(const std::string &path_template, GCodePreviewData *preview_data); + // SLA export, temporary. + void export_png(const std::string &dirpath); + + // methods for handling state + bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); } + bool is_step_done(PrintObjectStep step) const; + + bool has_infinite_skirt() const; + bool has_skirt() const; + float get_wipe_tower_depth() const { return m_wipe_tower_data.depth; } + + // Returns an empty string if valid, otherwise returns an error message. + std::string validate() const override; + BoundingBox bounding_box() const; + BoundingBox total_bounding_box() const; + double skirt_first_layer_height() const; + Flow brim_flow() const; + Flow skirt_flow() const; + + std::vector object_extruders() const; + std::vector support_material_extruders() const; + std::vector extruders() const; + double max_allowed_layer_height() const; + bool has_support_material() const; + // Make sure the background processing has no access to this model_object during this call! + void auto_assign_extruders(ModelObject* model_object) const; + + const PrintConfig& config() const { return m_config; } + const PrintObjectConfig& default_object_config() const { return m_default_object_config; } + const PrintRegionConfig& default_region_config() const { return m_default_region_config; } + const PrintObjectPtrs& objects() const { return m_objects; } + PrintObject* get_object(size_t idx) { return m_objects[idx]; } + const PrintObject* get_object(size_t idx) const { return m_objects[idx]; } + const PrintRegionPtrs& regions() const { return m_regions; } + const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } + PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } + // How many of PrintObject::copies() over all print objects are there? + // If zero, then the print is empty and the print shall not be executed. + unsigned int num_object_instances() const; + + // Returns extruder this eec should be printed with, according to PrintRegion config: + static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion); + + const ExtrusionEntityCollection& skirt() const { return m_skirt; } + const ExtrusionEntityCollection& brim() const { return m_brim; } + + const PrintStatistics& print_statistics() const { return m_print_statistics; } + + // Wipe tower support. + bool has_wipe_tower() const; + const WipeTowerData& wipe_tower_data() const { return m_wipe_tower_data; } + + std::string output_filename() const; + std::string output_filepath(const std::string &path) const; + + // Accessed by SupportMaterial + const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } + +protected: + // methods for handling regions + PrintRegion* get_region(size_t idx) { return m_regions[idx]; } + PrintRegion* add_region(); + PrintRegion* add_region(const PrintRegionConfig &config); + + // Invalidates the step, and its depending steps in Print. + bool invalidate_step(PrintStep step); + +private: + // Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. + void update_object_placeholders(); + + bool invalidate_state_by_config_options(const std::vector &opt_keys); + + void _make_skirt(); + void _make_brim(); + void _make_wipe_tower(); + void _simplify_slices(double distance); + + // Declared here to have access to Model / ModelObject / ModelInstance + static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src); + + Model m_model; + PrintConfig m_config; + PrintObjectConfig m_default_object_config; + PrintRegionConfig m_default_region_config; + PrintObjectPtrs m_objects; + PrintRegionPtrs m_regions; + PlaceholderParser m_placeholder_parser; + + // Ordered collections of extrusion paths to build skirt loops and brim. + ExtrusionEntityCollection m_skirt; + ExtrusionEntityCollection m_brim; + + // Following section will be consumed by the GCodeGenerator. + WipeTowerData m_wipe_tower_data; + + // Estimated print time, filament consumed. + PrintStatistics m_print_statistics; + + // To allow GCode to set the Print's GCodeExport step status. + friend class GCode; + // Allow PrintObject to access m_mutex and m_cancel_callback. + friend class PrintObject; +}; + +} /* slic3r_Print_hpp_ */ + +#endif diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp new file mode 100644 index 0000000000..d4fffca948 --- /dev/null +++ b/src/libslic3r/PrintBase.cpp @@ -0,0 +1,16 @@ +#include "PrintBase.hpp" + +namespace Slic3r +{ + +tbb::mutex& PrintObjectBase::cancel_mutex(PrintBase *print) +{ + return print->cancel_mutex(); +} + +std::function PrintObjectBase::cancel_callback(PrintBase *print) +{ + return print->cancel_callback(); +} + +} // namespace Slic3r diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp new file mode 100644 index 0000000000..8e9a6693a9 --- /dev/null +++ b/src/libslic3r/PrintBase.hpp @@ -0,0 +1,303 @@ +#ifndef slic3r_PrintBase_hpp_ +#define slic3r_PrintBase_hpp_ + +#include "libslic3r.h" +#include +#include +#include +#include +#include + +#include "tbb/atomic.h" +// tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros. +#ifndef NOMINMAX + #define NOMINMAX +#endif +#include "tbb/mutex.h" + +#include "Model.hpp" +#include "PrintConfig.hpp" + +namespace Slic3r { + +class CanceledException : public std::exception { +public: + const char* what() const throw() { return "Background processing has been canceled"; } +}; + +// To be instantiated over PrintStep or PrintObjectStep enums. +template +class PrintState +{ +public: + PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i].store(INVALID, std::memory_order_relaxed); } + + enum State { + INVALID, + STARTED, + DONE, + }; + + // With full memory barrier. + bool is_done(StepType step) const { return m_state[step] == DONE; } + + // Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being + // modified by the UI thread. + // This is necessary to block until the Print::apply_config() updates its state, which may + // influence the processing step being entered. + void set_started(StepType step, tbb::mutex &mtx) { + mtx.lock(); + m_state[step].store(STARTED, std::memory_order_relaxed); + mtx.unlock(); + } + + // Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being + // modified by the UI thread. + void set_done(StepType step, tbb::mutex &mtx) { + mtx.lock(); + m_state[step].store(DONE, std::memory_order_relaxed); + mtx.unlock(); + } + + // Make the step invalid. + // The provided mutex should be locked at this point, guarding access to m_state. + // In case the step has already been entered or finished, cancel the background + // processing by calling the cancel callback. + template + bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback cancel) { + bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID; + if (invalidated) { +#if 0 + if (mtx.state != mtx.HELD) { + printf("Not held!\n"); + } +#endif + // Raise the mutex, so that the following cancel() callback could cancel + // the background processing. + mtx.unlock(); + cancel(); + m_state[step] = INVALID; + mtx.lock(); + } + return invalidated; + } + + template + bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, tbb::mutex &mtx, CancelationCallback cancel) { + bool invalidated = false; + for (StepTypeIterator it = step_begin; ! invalidated && it != step_end; ++ it) + invalidated = m_state[*it].load(std::memory_order_relaxed) != INVALID; + if (invalidated) { +#if 0 + if (mtx.state != mtx.HELD) { + printf("Not held!\n"); + } +#endif + // Raise the mutex, so that the following cancel() callback could cancel + // the background processing. + mtx.unlock(); + cancel(); + for (StepTypeIterator it = step_begin; it != step_end; ++ it) + m_state[*it] = INVALID; + mtx.lock(); + } + return invalidated; + } + + // Make all steps invalid. + // The provided mutex should be locked at this point, guarding access to m_state. + // In case any step has already been entered or finished, cancel the background + // processing by calling the cancel callback. + template + bool invalidate_all(tbb::mutex &mtx, CancelationCallback cancel) { + bool invalidated = false; + for (size_t i = 0; i < COUNT; ++ i) + if (m_state[i].load(std::memory_order_relaxed) != INVALID) { + invalidated = true; + break; + } + if (invalidated) { + mtx.unlock(); + cancel(); + for (size_t i = 0; i < COUNT; ++ i) + m_state[i].store(INVALID, std::memory_order_relaxed); + mtx.lock(); + } + return invalidated; + } + +private: + std::atomic m_state[COUNT]; +}; + +class PrintBase; + +class PrintObjectBase +{ +protected: + virtual ~PrintObjectBase() {} + // Declared here to allow access from PrintBase through friendship. + static tbb::mutex& cancel_mutex(PrintBase *print); + static std::function cancel_callback(PrintBase *print); +}; + +/** + * @brief Printing involves slicing and export of device dependent instructions. + * + * Every technology has a potentially different set of requirements for + * slicing, support structures and output print instructions. The pipeline + * however remains roughly the same: + * slice -> convert to instructions -> send to printer + * + * The PrintBase class will abstract this flow for different technologies. + * + */ +class PrintBase +{ +public: + PrintBase() { this->restart(); } + inline virtual ~PrintBase() {} + + virtual PrinterTechnology technology() const noexcept = 0; + + // Reset the print status including the copy of the Model / ModelObject hierarchy. + virtual void clear() = 0; + // The Print is empty either after clear() or after apply() over an empty model, + // or after apply() over a model, where no object is printable (all outside the print volume). + virtual bool empty() const = 0; + + // Validate the print, return empty string if valid, return error if process() cannot (or should not) be started. + virtual std::string validate() const { return std::string(); } + + enum ApplyStatus { + // No change after the Print::apply() call. + APPLY_STATUS_UNCHANGED, + // Some of the Print / PrintObject / PrintObjectInstance data was changed, + // but no result was invalidated (only data influencing not yet calculated results were changed). + APPLY_STATUS_CHANGED, + // Some data was changed, which in turn invalidated already calculated steps. + APPLY_STATUS_INVALIDATED, + }; + virtual ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) = 0; + + virtual void process() = 0; + + typedef std::function status_callback_type; + // Default status console print out in the form of percent => message. + void set_status_default() { m_status_callback = nullptr; } + // No status output or callback whatsoever, useful mostly for automatic tests. + void set_status_silent() { m_status_callback = [](int, const std::string&){}; } + // Register a custom status callback. + void set_status_callback(status_callback_type cb) { m_status_callback = cb; } + // Calls a registered callback to update the status, or print out the default message. + void set_status(int percent, const std::string &message) { + if (m_status_callback) m_status_callback(percent, message); + else printf("%d => %s\n", percent, message.c_str()); + } + + typedef std::function cancel_callback_type; + // Various methods will call this callback to stop the background processing (the Print::process() call) + // in case a successive change of the Print / PrintObject / PrintRegion instances changed + // the state of the finished or running calculations. + void set_cancel_callback(cancel_callback_type cancel_callback) { m_cancel_callback = cancel_callback; } + // Has the calculation been canceled? + enum CancelStatus { + // No cancelation, background processing should run. + NOT_CANCELED = 0, + // Canceled by user from the user interface (user pressed the "Cancel" button or user closed the application). + CANCELED_BY_USER = 1, + // Canceled internally from Print::apply() through the Print/PrintObject::invalidate_step() or ::invalidate_all_steps(). + CANCELED_INTERNAL = 2 + }; + CancelStatus cancel_status() const { return m_cancel_status; } + // Has the calculation been canceled? + bool canceled() const { return m_cancel_status != NOT_CANCELED; } + // Cancel the running computation. Stop execution of all the background threads. + void cancel() { m_cancel_status = CANCELED_BY_USER; } + void cancel_internal() { m_cancel_status = CANCELED_INTERNAL; } + // Cancel the running computation. Stop execution of all the background threads. + void restart() { m_cancel_status = NOT_CANCELED; } + +protected: + friend class PrintObjectBase; + + tbb::mutex& cancel_mutex() { return m_cancel_mutex; } + std::function cancel_callback() { return m_cancel_callback; } + void call_cancell_callback() { m_cancel_callback(); } + + // If the background processing stop was requested, throw CanceledException. + // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly. + void throw_if_canceled() const { if (m_cancel_status) throw CanceledException(); } + +private: + tbb::atomic m_cancel_status; + // Callback to be evoked regularly to update state of the UI thread. + status_callback_type m_status_callback; + + // Callback to be evoked to stop the background processing before a state is updated. + cancel_callback_type m_cancel_callback = [](){}; + + // Mutex used for synchronization of the worker thread with the UI thread: + // The mutex will be used to guard the worker thread against entering a stage + // while the data influencing the stage is modified. + mutable tbb::mutex m_cancel_mutex; +}; + +template +class PrintBaseWithState : public PrintBase +{ +public: + bool is_step_done(PrintStepEnum step) const { return m_state.is_done(step); } + +protected: + void set_started(PrintStepEnum step) { m_state.set_started(step, this->cancel_mutex()); throw_if_canceled(); } + void set_done(PrintStepEnum step) { m_state.set_done(step, this->cancel_mutex()); throw_if_canceled(); } + bool invalidate_step(PrintStepEnum step) + { return m_state.invalidate(step, this->cancel_mutex(), this->cancel_callback()); } + template + bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end) + { return m_state.invalidate_multiple(step_begin, step_end, this->cancel_mutex(), this->cancel_callback()); } + bool invalidate_steps(std::initializer_list il) + { return m_state.invalidate_multiple(il.begin(), il.end(), this->cancel_mutex(), this->cancel_callback()); } + bool invalidate_all_steps() + { return m_state.invalidate_all(this->cancel_mutex(), this->cancel_callback()); } + +private: + PrintState m_state; +}; + +template +class PrintObjectBaseWithState : public PrintObjectBase +{ +public: + PrintType* print() { return m_print; } + const PrintType* print() const { return m_print; } + + bool is_step_done(PrintObjectStepEnum step) const { return m_state.is_done(step); } + +protected: + PrintObjectBaseWithState(PrintType *print) : m_print(print) {} + + void set_started(PrintObjectStepEnum step) { m_state.set_started(step, PrintObjectBase::cancel_mutex(m_print)); } + void set_done(PrintObjectStepEnum step) { m_state.set_done(step, PrintObjectBase::cancel_mutex(m_print)); } + + bool invalidate_step(PrintObjectStepEnum step) + { return m_state.invalidate(step, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); } + template + bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end) + { return m_state.invalidate_multiple(step_begin, step_end, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); } + bool invalidate_steps(std::initializer_list il) + { return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); } + bool invalidate_all_steps() { return m_state.invalidate_all(PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); } + +protected: + friend PrintType; + PrintType *m_print; + +private: + PrintState m_state; +}; + +} // namespace Slic3r + +#endif /* slic3r_PrintBase_hpp_ */ diff --git a/xs/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp similarity index 84% rename from xs/src/libslic3r/PrintConfig.cpp rename to src/libslic3r/PrintConfig.cpp index decbc78338..1e8f26d381 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1,7 +1,6 @@ #include "PrintConfig.hpp" #include "I18N.hpp" -#include #include #include #include @@ -18,9 +17,53 @@ namespace Slic3r { #define L(s) Slic3r::I18N::translate(s) PrintConfigDef::PrintConfigDef() +{ + this->init_common_params(); + this->init_fff_params(); + this->init_sla_params(); +} + +void PrintConfigDef::init_common_params() { t_optiondef_map &Options = this->options; + ConfigOptionDef* def; + + def = this->add("printer_technology", coEnum); + def->label = L("Printer technology"); + def->tooltip = L("Printer technology"); + def->cli = "printer-technology=s"; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("FFF"); + def->enum_values.push_back("SLA"); + def->default_value = new ConfigOptionEnum(ptFFF); + + def = this->add("bed_shape", coPoints); + def->label = L("Bed shape"); + def->mode = comAdvanced; + def->default_value = new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) }; + def = this->add("layer_height", coFloat); + def->label = L("Layer height"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("This setting controls the height (and thus the total number) of the slices/layers. " + "Thinner layers give better accuracy but take more time to print."); + def->sidetext = L("mm"); + def->cli = "layer-height=f"; + def->min = 0; + def->default_value = new ConfigOptionFloat(0.3); + + def = this->add("max_print_height", coFloat); + def->label = L("Max print height"); + def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing."); + def->sidetext = L("mm"); + def->cli = "max-print-height=f"; + def->mode = comAdvanced; + def->default_value = new ConfigOptionFloat(200.0); +} + +void PrintConfigDef::init_fff_params() +{ + t_optiondef_map &Options = this->options; ConfigOptionDef* def; // Maximum extruder temperature, bumped to 1500 to support printing of glass. @@ -32,12 +75,9 @@ PrintConfigDef::PrintConfigDef() "This is mostly useful with Bowden extruders which suffer from oozing. " "This feature slows down both the print and the G-code generation."); def->cli = "avoid-crossing-perimeters!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(false); - def = this->add("bed_shape", coPoints); - def->label = L("Bed shape"); - def->default_value = new ConfigOptionPoints { Pointf(0,0), Pointf(200,0), Pointf(200,200), Pointf(0,200) }; - def = this->add("bed_temperature", coInts); def->label = L("Other layers"); def->tooltip = L("Bed temperature for layers after the first one. " @@ -57,6 +97,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 50; + def->mode = comExpert; def->default_value = new ConfigOptionString(""); def = this->add("between_objects_gcode", coString); @@ -66,6 +107,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 120; + def->mode = comExpert; def->default_value = new ConfigOptionString(""); def = this->add("bottom_solid_layers", coInt); @@ -84,6 +126,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s²"); def->cli = "bridge-acceleration=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("bridge_angle", coFloat); @@ -95,6 +138,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("°"); def->cli = "bridge-angle=f"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(0.); def = this->add("bridge_fan_speed", coInts); @@ -104,6 +148,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "bridge-fan-speed=i@"; def->min = 0; def->max = 100; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 100 }; def = this->add("bridge_flow_ratio", coFloat); @@ -116,6 +161,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "bridge-flow-ratio=f"; def->min = 0; def->max = 2; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(1); def = this->add("bridge_speed", coFloat); @@ -134,6 +180,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "brim-width=f"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(0); def = this->add("clip_multipart_objects", coBool); @@ -142,10 +189,18 @@ PrintConfigDef::PrintConfigDef() "to clip the overlapping object parts one by the other " "(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc)."); def->cli = "clip-multipart-objects!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); + def = this->add("colorprint_heights", coFloats); + def->label = L("Colorprint height"); + def->tooltip = L("Heights at which a filament change is to occur. "); + def->cli = "colorprint-heights=f@"; + def->default_value = new ConfigOptionFloats { }; + def = this->add("compatible_printers", coStrings); def->label = L("Compatible printers"); + def->mode = comAdvanced; def->default_value = new ConfigOptionStrings(); def = this->add("compatible_printers_condition", coString); @@ -153,6 +208,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("A boolean expression using the configuration values of an active printer profile. " "If this expression evaluates to true, this profile is considered compatible " "with the active printer profile."); + def->mode = comExpert; def->default_value = new ConfigOptionString(); // The following value is to be stored into the project file (AMF, 3MF, Config ...) @@ -167,6 +223,7 @@ PrintConfigDef::PrintConfigDef() "This feature is useful to avoid the risk of ruined prints. " "Slic3r should warn and prevent you from extruder collisions, but beware."); def->cli = "complete-objects!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(false); def = this->add("cooling", coBools); @@ -182,6 +239,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "cooling_tube_retraction=f"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(91.5f); def = this->add("cooling_tube_length", coFloat); @@ -190,6 +248,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "cooling_tube_length=f"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(5.f); def = this->add("default_acceleration", coFloat); @@ -200,6 +259,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s²"); def->cli = "default-acceleration=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("default_filament_profile", coStrings); @@ -222,6 +282,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "disable-fan-first-layers=i@"; def->min = 0; def->max = 1000; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 3 }; def = this->add("dont_support_bridges", coBool); @@ -230,6 +291,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Experimental option for preventing support material from being generated " "under bridged areas."); def->cli = "dont-support-bridges!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(true); def = this->add("duplicate_distance", coFloat); @@ -249,6 +311,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "elefant-foot-compensation=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("end_gcode", coString); @@ -259,6 +322,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 120; + def->mode = comExpert; def->default_value = new ConfigOptionString("M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n"); def = this->add("end_filament_gcode", coStrings); @@ -270,6 +334,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 120; + def->mode = comExpert; def->default_value = new ConfigOptionStrings { "; Filament-specific end gcode \n;END gcode for filament\n" }; def = this->add("ensure_vertical_shell_thickness", coBool); @@ -278,6 +343,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Add solid infill near sloping surfaces to guarantee the vertical shell thickness " "(top+bottom solid layers)."); def->cli = "ensure-vertical-shell-thickness!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(false); def = this->add("external_fill_pattern", coEnum); @@ -309,6 +375,7 @@ PrintConfigDef::PrintConfigDef() "If expressed as percentage (for example 200%), it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "external-perimeter-extrusion-width=s"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("external_perimeter_speed", coFloatOrPercent); @@ -329,6 +396,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Print contour perimeters from the outermost one to the innermost one " "instead of the default inverse order."); def->cli = "external-perimeters-first!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("extra_perimeters", coBool); @@ -338,6 +406,7 @@ PrintConfigDef::PrintConfigDef() "Slic3r keeps adding perimeters, until more than 70% of the loop immediately above " "is supported."); def->cli = "extra-perimeters!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(true); def = this->add("extruder", coInt); @@ -364,6 +433,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "extruder-clearance-height=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(20); def = this->add("extruder_clearance_radius", coFloat); @@ -375,6 +445,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "extruder-clearance-radius=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(20); def = this->add("extruder_colour", coStrings); @@ -393,7 +464,8 @@ PrintConfigDef::PrintConfigDef() "from the XY coordinate)."); def->sidetext = L("mm"); def->cli = "extruder-offset=s@"; - def->default_value = new ConfigOptionPoints { Pointf(0,0) }; + def->mode = comAdvanced; + def->default_value = new ConfigOptionPoints { Vec2d(0,0) }; def = this->add("extrusion_axis", coString); def->label = L("Extrusion axis"); @@ -409,6 +481,7 @@ PrintConfigDef::PrintConfigDef() "Usual values are between 0.9 and 1.1. If you think you need to change this more, " "check filament diameter and your firmware E steps."); def->cli = "extrusion-multiplier=f@"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats { 1. }; def = this->add("extrusion_width", coFloatOrPercent); @@ -420,6 +493,7 @@ PrintConfigDef::PrintConfigDef() "If expressed as percentage (for example: 230%), it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for auto)"); def->cli = "extrusion-width=s"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("fan_always_on", coBools); @@ -438,6 +512,7 @@ PrintConfigDef::PrintConfigDef() def->width = 60; def->min = 0; def->max = 1000; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 60 }; def = this->add("filament_colour", coStrings); @@ -454,6 +529,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 130; + def->mode = comAdvanced; def->default_value = new ConfigOptionStrings { "" }; def = this->add("filament_max_volumetric_speed", coFloats); @@ -464,6 +540,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm³/s"); def->cli = "filament-max-volumetric-speed=f@"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("filament_loading_speed", coFloats); @@ -472,6 +549,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s"); def->cli = "filament-loading-speed=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 28. }; def = this->add("filament_loading_speed_start", coFloats); @@ -480,6 +558,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s"); def->cli = "filament-loading-speed-start=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 3. }; def = this->add("filament_unloading_speed", coFloats); @@ -489,6 +568,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s"); def->cli = "filament-unloading-speed=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 90. }; def = this->add("filament_unloading_speed_start", coFloats); @@ -497,6 +577,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s"); def->cli = "filament-unloading-speed-start=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 100. }; def = this->add("filament_toolchange_delay", coFloats); @@ -507,6 +588,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("s"); def->cli = "filament-toolchange-delay=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("filament_cooling_moves", coInts); @@ -516,6 +598,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "filament-cooling-moves=i@"; def->max = 0; def->max = 20; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 4 }; def = this->add("filament_cooling_initial_speed", coFloats); @@ -524,6 +607,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "filament-cooling-initial-speed=f@"; def->sidetext = L("mm/s"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 2.2f }; def = this->add("filament_minimal_purge_on_wipe_tower", coFloats); @@ -535,6 +619,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "filament-minimal-purge-on-wipe-tower=f@"; def->sidetext = L("mm³"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 15.f }; def = this->add("filament_cooling_final_speed", coFloats); @@ -543,6 +628,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "filament-cooling-final-speed=f@"; def->sidetext = L("mm/s"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 3.4f }; def = this->add("filament_load_time", coFloats); @@ -551,12 +637,14 @@ PrintConfigDef::PrintConfigDef() def->cli = "filament-load-time=i@"; def->sidetext = L("s"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 0.0f }; def = this->add("filament_ramming_parameters", coStrings); def->label = L("Ramming parameters"); def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters "); def->cli = "filament-ramming-parameters=s@"; + def->mode = comExpert; def->default_value = new ConfigOptionStrings { "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0|" " 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" }; @@ -566,6 +654,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "filament-unload-time=i@"; def->sidetext = L("s"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 0.0f }; def = this->add("filament_diameter", coFloats); @@ -631,6 +720,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "fill-angle=f"; def->min = 0; def->max = 360; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(45); def = this->add("fill_density", coPercent); @@ -714,6 +804,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s²"); def->cli = "first-layer-acceleration=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("first_layer_bed_temperature", coInts); @@ -735,6 +826,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "first-layer-extrusion-width=s"; def->ratio_over = "first_layer_height"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloatOrPercent(200, true); def = this->add("first_layer_height", coFloatOrPercent); @@ -757,6 +849,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s or %"); def->cli = "first-layer-speed=s"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloatOrPercent(30, false); def = this->add("first_layer_temperature", coInts); @@ -784,6 +877,7 @@ PrintConfigDef::PrintConfigDef() "If you print from SD card, the additional weight of the file could make your firmware " "slow down."); def->cli = "gcode-comments!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(0); def = this->add("gcode_flavor", coEnum); @@ -813,6 +907,7 @@ PrintConfigDef::PrintConfigDef() def->enum_labels.push_back("Machinekit"); def->enum_labels.push_back("Smoothie"); def->enum_labels.push_back(L("No extrusion")); + def->mode = comExpert; def->default_value = new ConfigOptionEnum(gcfRepRap); def = this->add("infill_acceleration", coFloat); @@ -822,6 +917,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s²"); def->cli = "infill-acceleration=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("infill_every_layers", coInt); @@ -833,6 +929,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "infill-every-layers=i"; def->full_label = L("Combine infill every n layers"); def->min = 1; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(1); def = this->add("infill_extruder", coInt); @@ -841,6 +938,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("The extruder to use when printing infill."); def->cli = "infill-extruder=i"; def->min = 1; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(1); def = this->add("infill_extrusion_width", coFloatOrPercent); @@ -852,12 +950,14 @@ PrintConfigDef::PrintConfigDef() "If expressed as percentage (for example 90%) it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "infill-extrusion-width=s"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("infill_first", coBool); def->label = L("Infill before perimeters"); def->tooltip = L("This option will switch the print order of perimeters and infill, making the latter first."); def->cli = "infill-first!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("infill_only_where_needed", coBool); @@ -867,6 +967,7 @@ PrintConfigDef::PrintConfigDef() "(it will act as internal support material). If enabled, slows down the G-code generation " "due to the multiple checks involved."); def->cli = "infill-only-where-needed!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(false); def = this->add("infill_overlap", coFloatOrPercent); @@ -878,6 +979,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm or %"); def->cli = "infill-overlap=s"; def->ratio_over = "perimeter_extrusion_width"; + def->mode = comExpert; def->default_value = new ConfigOptionFloatOrPercent(25, true); def = this->add("infill_speed", coFloat); @@ -909,6 +1011,7 @@ PrintConfigDef::PrintConfigDef() "support material."); def->cli = "interface-shells!"; def->category = L("Layers and Perimeters"); + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("layer_gcode", coString); @@ -920,29 +1023,22 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 50; + def->mode = comExpert; def->default_value = new ConfigOptionString(""); - def = this->add("layer_height", coFloat); - def->label = L("Layer height"); - def->category = L("Layers and Perimeters"); - def->tooltip = L("This setting controls the height (and thus the total number) of the slices/layers. " - "Thinner layers give better accuracy but take more time to print."); - def->sidetext = L("mm"); - def->cli = "layer-height=f"; - def->min = 0; - def->default_value = new ConfigOptionFloat(0.3); - def = this->add("remaining_times", coBool); def->label = L("Supports remaining times"); def->tooltip = L("Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute" " intervals into the G-code to let the firmware show accurate remaining time." " As of now only the Prusa i3 MK3 firmware recognizes M73." " Also the i3 MK3 firmware supports M73 Qxx Sxx for the silent mode."); + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("silent_mode", coBool); def->label = L("Supports silent mode"); def->tooltip = L("Set silent mode for the G-code flavor"); + def->mode = comExpert; def->default_value = new ConfigOptionBool(true); const int machine_limits_opt_width = 70; @@ -970,6 +1066,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats(axis.max_feedrate); // Add the machine acceleration limits for XYZE axes (M201) def = this->add("machine_max_acceleration_" + axis.name, coFloats); @@ -979,6 +1076,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s²"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats(axis.max_acceleration); // Add the machine jerk limits for XYZE axes (M205) def = this->add("machine_max_jerk_" + axis.name, coFloats); @@ -988,6 +1086,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats(axis.max_jerk); } } @@ -1000,6 +1099,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats{ 0., 0. }; // M205 T... [mm/sec] @@ -1010,6 +1110,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats{ 0., 0. }; // M204 S... [mm/sec^2] @@ -1020,6 +1121,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s²"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats{ 1500., 1250. }; // M204 T... [mm/sec^2] @@ -1030,6 +1132,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s²"); def->min = 0; def->width = machine_limits_opt_width; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats{ 1500., 1250. }; def = this->add("max_fan_speed", coInts); @@ -1039,6 +1142,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "max-fan-speed=i@"; def->min = 0; def->max = 100; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 100 }; def = this->add("max_layer_height", coFloats); @@ -1050,15 +1154,9 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "max-layer-height=f@"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats { 0. }; - def = this->add("max_print_height", coFloat); - def->label = L("Max print height"); - def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing."); - def->sidetext = L("mm"); - def->cli = "max-print-height=f"; - def->default_value = new ConfigOptionFloat(200.0); - def = this->add("max_print_speed", coFloat); def->label = L("Max print speed"); def->tooltip = L("When setting other speed settings to 0 Slic3r will autocalculate the optimal speed " @@ -1067,6 +1165,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s"); def->cli = "max-print-speed=f"; def->min = 1; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(80); def = this->add("max_volumetric_speed", coFloat); @@ -1076,6 +1175,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm³/s"); def->cli = "max-volumetric-speed=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("max_volumetric_extrusion_rate_slope_positive", coFloat); @@ -1087,6 +1187,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm³/s²"); def->cli = "max-volumetric-extrusion-rate-slope-positive=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("max_volumetric_extrusion_rate_slope_negative", coFloat); @@ -1098,6 +1199,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm³/s²"); def->cli = "max-volumetric-extrusion-rate-slope-negative=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("min_fan_speed", coInts); @@ -1107,6 +1209,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "min-fan-speed=i@"; def->min = 0; def->max = 100; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 35 }; def = this->add("min_layer_height", coFloats); @@ -1116,6 +1219,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "min-layer-height=f@"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats { 0.07 }; def = this->add("min_print_speed", coFloats); @@ -1124,6 +1228,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm/s"); def->cli = "min-print-speed=f@"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 10. }; def = this->add("min_skirt_length", coFloat); @@ -1134,6 +1239,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "min-skirt-length=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("notes", coString); @@ -1144,6 +1250,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 130; + def->mode = comAdvanced; def->default_value = new ConfigOptionString(""); def = this->add("nozzle_diameter", coFloats); @@ -1163,6 +1270,7 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("duet"); def->enum_labels.push_back("OctoPrint"); def->enum_labels.push_back("Duet"); + def->mode = comAdvanced; def->default_value = new ConfigOptionEnum(htOctoPrint); def = this->add("printhost_apikey", coString); @@ -1170,6 +1278,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " "the API Key or the password required for authentication."); def->cli = "printhost-apikey=s"; + def->mode = comAdvanced; def->default_value = new ConfigOptionString(""); def = this->add("printhost_cafile", coString); @@ -1184,6 +1293,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " "the hostname, IP address or URL of the printer host instance."); def->cli = "print-host=s"; + def->mode = comAdvanced; def->default_value = new ConfigOptionString(""); def = this->add("only_retract_when_crossing_perimeters", coBool); @@ -1191,6 +1301,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Disables retraction when the travel path does not exceed the upper layer's perimeters " "(and thus any ooze will be probably invisible)."); def->cli = "only-retract-when-crossing-perimeters!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(true); def = this->add("ooze_prevention", coBool); @@ -1199,6 +1310,7 @@ PrintConfigDef::PrintConfigDef() "It will enable a tall skirt automatically and move extruders outside such " "skirt when changing temperatures."); def->cli = "ooze-prevention!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("output_filename_format", coString); @@ -1209,6 +1321,7 @@ PrintConfigDef::PrintConfigDef() "[input_filename_base]."); def->cli = "output-filename-format=s"; def->full_width = true; + def->mode = comExpert; def->default_value = new ConfigOptionString("[input_filename_base].gcode"); def = this->add("overhangs", coBool); @@ -1217,6 +1330,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Experimental option to adjust flow for overhangs (bridge flow will be used), " "to apply bridge speed to them and enable fan."); def->cli = "overhangs!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(true); def = this->add("parking_pos_retraction", coFloat); @@ -1226,6 +1340,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "parking_pos_retraction=f"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(92.f); def = this->add("extra_loading_move", coFloat); @@ -1235,6 +1350,7 @@ PrintConfigDef::PrintConfigDef() " if negative, the loading move is shorter than unloading. "); def->sidetext = L("mm"); def->cli = "extra_loading_move=f"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(-2.f); def = this->add("perimeter_acceleration", coFloat); @@ -1244,6 +1360,7 @@ PrintConfigDef::PrintConfigDef() "Set zero to disable acceleration control for perimeters."); def->sidetext = L("mm/s²"); def->cli = "perimeter-acceleration=f"; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("perimeter_extruder", coInt); @@ -1253,6 +1370,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "perimeter-extruder=i"; def->aliases = { "perimeters_extruder" }; def->min = 1; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(1); def = this->add("perimeter_extrusion_width", coFloatOrPercent); @@ -1265,6 +1383,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "perimeter-extrusion-width=s"; def->aliases = { "perimeters_extrusion_width" }; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("perimeter_speed", coFloat); @@ -1301,6 +1420,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 60; + def->mode = comExpert; def->default_value = new ConfigOptionStrings(); def = this->add("printer_model", coString); @@ -1315,6 +1435,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 130; + def->mode = comAdvanced; def->default_value = new ConfigOptionString(""); def = this->add("printer_vendor", coString); @@ -1341,6 +1462,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("layers"); def->cli = "raft-layers=i"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(0); def = this->add("resolution", coFloat); @@ -1352,6 +1474,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "resolution=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("retract_before_travel", coFloats); @@ -1359,6 +1482,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Retraction is not triggered when travel moves are shorter than this length."); def->sidetext = L("mm"); def->cli = "retract-before-travel=f@"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats { 2. }; def = this->add("retract_before_wipe", coPercents); @@ -1367,12 +1491,14 @@ PrintConfigDef::PrintConfigDef() "before doing the wipe movement."); def->sidetext = L("%"); def->cli = "retract-before-wipe=s@"; + def->mode = comAdvanced; def->default_value = new ConfigOptionPercents { 0. }; def = this->add("retract_layer_change", coBools); def->label = L("Retract on layer change"); def->tooltip = L("This flag enforces a retraction whenever a Z move is done."); def->cli = "retract-layer-change!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBools { false }; def = this->add("retract_length", coFloats); @@ -1392,6 +1518,7 @@ PrintConfigDef::PrintConfigDef() "the extruder)."); def->sidetext = L("mm (zero to disable)"); def->cli = "retract-length-toolchange=f@"; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 10. }; def = this->add("retract_lift", coFloats); @@ -1410,6 +1537,7 @@ PrintConfigDef::PrintConfigDef() "absolute Z. You can tune this setting for skipping lift on the first layers."); def->sidetext = L("mm"); def->cli = "retract-lift-above=f@"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_lift_below", coFloats); @@ -1420,6 +1548,7 @@ PrintConfigDef::PrintConfigDef() "to the first layers."); def->sidetext = L("mm"); def->cli = "retract-lift-below=f@"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_restart_extra", coFloats); @@ -1428,6 +1557,7 @@ PrintConfigDef::PrintConfigDef() "this additional amount of filament. This setting is rarely needed."); def->sidetext = L("mm"); def->cli = "retract-restart-extra=f@"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_restart_extra_toolchange", coFloats); @@ -1436,6 +1566,7 @@ PrintConfigDef::PrintConfigDef() "this additional amount of filament."); def->sidetext = L("mm"); def->cli = "retract-restart-extra-toolchange=f@"; + def->mode = comExpert; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_speed", coFloats); @@ -1444,6 +1575,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("The speed for retractions (it only applies to the extruder motor)."); def->sidetext = L("mm/s"); def->cli = "retract-speed=f@"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats { 40. }; def = this->add("deretract_speed", coFloats); @@ -1453,6 +1585,7 @@ PrintConfigDef::PrintConfigDef() "(it only applies to the extruder motor). If left to zero, the retraction speed is used."); def->sidetext = L("mm/s"); def->cli = "retract-speed=f@"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("seam_position", coEnum); @@ -1469,6 +1602,7 @@ PrintConfigDef::PrintConfigDef() def->enum_labels.push_back(L("Nearest")); def->enum_labels.push_back(L("Aligned")); def->enum_labels.push_back(L("Rear")); + def->mode = comAdvanced; def->default_value = new ConfigOptionEnum(spAligned); #if 0 @@ -1531,6 +1665,7 @@ PrintConfigDef::PrintConfigDef() "as a shield against drafts."); def->sidetext = L("layers"); def->cli = "skirt-height=i"; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(1); def = this->add("skirts", coInt); @@ -1541,6 +1676,7 @@ PrintConfigDef::PrintConfigDef() "to disable skirt completely."); def->cli = "skirts=i"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(1); def = this->add("slowdown_below_layer_time", coInts); @@ -1552,6 +1688,7 @@ PrintConfigDef::PrintConfigDef() def->width = 60; def->min = 0; def->max = 1000; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 5 }; def = this->add("small_perimeter_speed", coFloatOrPercent); @@ -1573,6 +1710,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm²"); def->cli = "solid-infill-below-area=f"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(70); def = this->add("solid_infill_extruder", coInt); @@ -1581,6 +1719,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("The extruder to use when printing solid infill."); def->cli = "solid-infill-extruder=i"; def->min = 1; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(1); def = this->add("solid_infill_every_layers", coInt); @@ -1593,6 +1732,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("layers"); def->cli = "solid-infill-every-layers=i"; def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionInt(0); def = this->add("solid_infill_extrusion_width", coFloatOrPercent); @@ -1603,6 +1743,7 @@ PrintConfigDef::PrintConfigDef() "If expressed as percentage (for example 90%) it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "solid-infill-extrusion-width=s"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("solid_infill_speed", coFloatOrPercent); @@ -1644,6 +1785,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "standby-temperature-delta=i"; def->min = -max_temp; def->max = max_temp; + def->mode = comExpert; def->default_value = new ConfigOptionInt(-5); def = this->add("start_gcode", coString); @@ -1659,6 +1801,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 120; + def->mode = comExpert; def->default_value = new ConfigOptionString("G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n"); def = this->add("start_filament_gcode", coStrings); @@ -1675,18 +1818,21 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 120; + def->mode = comExpert; def->default_value = new ConfigOptionStrings { "; Filament gcode\n" }; def = this->add("single_extruder_multi_material", coBool); def->label = L("Single Extruder Multi Material"); def->tooltip = L("The printer multiplexes filaments into a single hot end."); def->cli = "single-extruder-multi-material!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("single_extruder_multi_material_priming", coBool); def->label = L("Prime all printing extruders"); def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print."); def->cli = "single-extruder-multi-material-priming!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(true); def = this->add("support_material", coBool); @@ -1702,6 +1848,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\ " If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only."); def->cli = "support-material-auto!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(true); def = this->add("support_material_xy_spacing", coFloatOrPercent); @@ -1713,6 +1860,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "support-material-xy-spacing=s"; def->ratio_over = "external_perimeter_extrusion_width"; def->min = 0; + def->mode = comAdvanced; // Default is half the external perimeter width. def->default_value = new ConfigOptionFloatOrPercent(50, true); @@ -1724,6 +1872,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "support-material-angle=f"; def->min = 0; def->max = 359; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("support_material_buildplate_only", coBool); @@ -1731,6 +1880,7 @@ PrintConfigDef::PrintConfigDef() def->category = L("Support material"); def->tooltip = L("Only create support if it lies on a build plate. Don't create support on a print."); def->cli = "support-material-buildplate-only!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(false); def = this->add("support_material_contact_distance", coFloat); @@ -1747,6 +1897,7 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("0.2"); def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str()); def->enum_labels.push_back((boost::format("0.2 (%1%)") % L("detachable")).str()); + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(0.2); def = this->add("support_material_enforce_layers", coInt); @@ -1760,6 +1911,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "support-material-enforce-layers=f"; def->full_label = L("Enforce support for the first n layers"); def->min = 0; + def->mode = comExpert; def->default_value = new ConfigOptionInt(0); def = this->add("support_material_extruder", coInt); @@ -1769,6 +1921,7 @@ PrintConfigDef::PrintConfigDef() "(1+, 0 to use the current extruder to minimize tool changes)."); def->cli = "support-material-extruder=i"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(1); def = this->add("support_material_extrusion_width", coFloatOrPercent); @@ -1779,6 +1932,7 @@ PrintConfigDef::PrintConfigDef() "If expressed as percentage (for example 90%) it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "support-material-extrusion-width=s"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("support_material_interface_contact_loops", coBool); @@ -1786,6 +1940,7 @@ PrintConfigDef::PrintConfigDef() def->category = L("Support material"); def->tooltip = L("Cover the top contact layer of the supports with loops. Disabled by default."); def->cli = "support-material-interface-contact-loops!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("support_material_interface_extruder", coInt); @@ -1795,6 +1950,7 @@ PrintConfigDef::PrintConfigDef() "(1+, 0 to use the current extruder to minimize tool changes). This affects raft too."); def->cli = "support-material-interface-extruder=i"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(1); def = this->add("support_material_interface_layers", coInt); @@ -1804,6 +1960,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("layers"); def->cli = "support-material-interface-layers=i"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(3); def = this->add("support_material_interface_spacing", coFloat); @@ -1813,6 +1970,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "support-material-interface-spacing=f"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(0); def = this->add("support_material_interface_speed", coFloatOrPercent); @@ -1838,6 +1996,7 @@ PrintConfigDef::PrintConfigDef() def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Rectilinear grid")); def->enum_labels.push_back(L("Honeycomb")); + def->mode = comAdvanced; def->default_value = new ConfigOptionEnum(smpRectilinear); def = this->add("support_material_spacing", coFloat); @@ -1847,6 +2006,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = L("mm"); def->cli = "support-material-spacing=f"; def->min = 0; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(2.5); def = this->add("support_material_speed", coFloat); @@ -1864,6 +2024,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Synchronize support layers with the object print layers. This is useful " "with multi-material printers, where the extruder switch is expensive."); def->cli = "support-material-synchronize-layers!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("support_material_threshold", coInt); @@ -1878,6 +2039,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "support-material-threshold=i"; def->min = 0; def->max = 90; + def->mode = comAdvanced; def->default_value = new ConfigOptionInt(0); def = this->add("support_material_with_sheath", coBool); @@ -1886,6 +2048,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Add a sheath (a single perimeter line) around the base support. This makes " "the support more reliable, but also more difficult to remove."); def->cli = "support-material-with-sheath!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(true); def = this->add("temperature", coInts); @@ -1904,6 +2067,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Detect single-width walls (parts where two extrusions don't fit and we need " "to collapse them into a single trace)."); def->cli = "thin-walls!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(true); def = this->add("threads", coInt); @@ -1927,6 +2091,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 50; + def->mode = comExpert; def->default_value = new ConfigOptionString(""); def = this->add("top_infill_extrusion_width", coFloatOrPercent); @@ -1938,6 +2103,7 @@ PrintConfigDef::PrintConfigDef() "If expressed as percentage (for example 90%) it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "top-infill-extrusion-width=s"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("top_solid_infill_speed", coFloatOrPercent); @@ -1970,6 +2136,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "travel-speed=f"; def->aliases = { "travel_feed_rate" }; def->min = 1; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(130); def = this->add("use_firmware_retraction", coBool); @@ -1977,6 +2144,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("This experimental setting uses G10 and G11 commands to have the firmware " "handle the retraction. This is only supported in recent Marlin."); def->cli = "use-firmware-retraction!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("use_relative_e_distances", coBool); @@ -1984,6 +2152,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("If your firmware requires relative E values, check this, " "otherwise leave it unchecked. Most firmwares use absolute values."); def->cli = "use-relative-e-distances!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("use_volumetric_e", coBool); @@ -1995,6 +2164,7 @@ PrintConfigDef::PrintConfigDef() "diameter associated to the filament selected in Slic3r. This is only supported " "in recent Marlin."); def->cli = "use-volumetric-e!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("variable_layer_height", coBool); @@ -2002,6 +2172,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Some printers or printer setups may have difficulties printing " "with a variable layer height. Enabled by default."); def->cli = "variable-layer-height!"; + def->mode = comExpert; def->default_value = new ConfigOptionBool(true); def = this->add("wipe", coBools); @@ -2009,6 +2180,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("This flag will move the nozzle while retracting to minimize the possible blob " "on leaky extruders."); def->cli = "wipe!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBools { false }; def = this->add("wipe_tower", coBool); @@ -2016,6 +2188,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Multi material printers may need to prime or purge extruders on tool changes. " "Extrude the excess material into the wipe tower."); def->cli = "wipe-tower!"; + def->mode = comAdvanced; def->default_value = new ConfigOptionBool(false); def = this->add("wiping_volumes_extruders", coFloats); @@ -2042,6 +2215,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("X coordinate of the left front corner of a wipe tower"); def->sidetext = L("mm"); def->cli = "wipe-tower-x=f"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(180.); def = this->add("wipe_tower_y", coFloat); @@ -2049,6 +2223,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Y coordinate of the left front corner of a wipe tower"); def->sidetext = L("mm"); def->cli = "wipe-tower-y=f"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(140.); def = this->add("wipe_tower_width", coFloat); @@ -2056,6 +2231,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Width of a wipe tower"); def->sidetext = L("mm"); def->cli = "wipe-tower-width=f"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(60.); def = this->add("wipe_tower_rotation_angle", coFloat); @@ -2063,6 +2239,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Wipe tower rotation angle with respect to x-axis "); def->sidetext = L("degrees"); def->cli = "wipe-tower-rotation-angle=f"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(0.); def = this->add("wipe_into_infill", coBool); @@ -2088,6 +2265,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = L("Maximal distance between supports on sparse infill sections. "); def->sidetext = L("mm"); def->cli = "wipe-tower-bridging=f"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(10.); def = this->add("xy_size_compensation", coFloat); @@ -2098,6 +2276,7 @@ PrintConfigDef::PrintConfigDef() "for fine-tuning hole sizes."); def->sidetext = L("mm"); def->cli = "xy-size-compensation=f"; + def->mode = comExpert; def->default_value = new ConfigOptionFloat(0); def = this->add("z_offset", coFloat); @@ -2108,7 +2287,267 @@ PrintConfigDef::PrintConfigDef() "from the print bed, set this to -0.3 (or fix your endstop)."); def->sidetext = L("mm"); def->cli = "z-offset=f"; + def->mode = comAdvanced; def->default_value = new ConfigOptionFloat(0); + + def = this->add("bed_size_x", coFloat); + def->label = L("Bed size X"); + def->category = L("Dwarf"); + def->sidetext = L("mm"); + def->cli = "bed-size-x=f"; + def->default_value = new ConfigOptionFloat(68.); + + def = this->add("bed_size_y", coFloat); + def->label = L("Bed size Y"); + def->category = L("Dwarf"); + def->sidetext = L("mm"); + def->cli = "bed-size-y=f"; + def->default_value = new ConfigOptionFloat(120.); + + def = this->add("pixel_width", coInt); + def->label = L("Picture resolution X"); + def->category = L("Dwarf"); + def->sidetext = L("px"); + def->cli = "pixel-width=i"; + def->min = 1; + def->mode = comExpert; + def->default_value = new ConfigOptionInt(1440); + + def = this->add("pixel_height", coInt); + def->label = L("Picture resolution Y"); + def->category = L("Dwarf"); + def->sidetext = L("px"); + def->cli = "pixel-height=i"; + def->min = 1; + def->default_value = new ConfigOptionInt(2560); + + def = this->add("exp_time", coFloat); + def->label = L("Exposure time"); + def->category = L("Dwarf"); + def->sidetext = L("s"); + def->cli = "exp-time=f"; + def->min = 1; + def->default_value = new ConfigOptionFloat(8.); + + def = this->add("exp_time_first", coFloat); + def->label = L("Exposure time first layers"); + def->category = L("Dwarf"); + def->sidetext = L("s"); + def->cli = "exp-time-first=f"; + def->min = 1; + def->default_value = new ConfigOptionFloat(35.); +} + +void PrintConfigDef::init_sla_params() +{ + t_optiondef_map &Options = this->options; + ConfigOptionDef* def; + + // SLA Printer settings + def = this->add("display_width", coFloat); + def->label = L("Display width"); + def->tooltip = L("Width of the display"); + def->cli = "display-width=f"; + def->min = 1; + def->default_value = new ConfigOptionFloat(150.); + + def = this->add("display_height", coFloat); + def->label = L("Display height"); + def->tooltip = L("Height of the display"); + def->cli = "display-height=f"; + def->min = 1; + def->default_value = new ConfigOptionFloat(100.); + + def = this->add("display_pixels_x", coInt); + def->full_label = L("Number of pixels in"); + def->label = ("X"); + def->tooltip = L("Number of pixels in X"); + def->cli = "display-pixels-x=i"; + def->min = 100; + def->default_value = new ConfigOptionInt(2000); + + def = this->add("display_pixels_y", coInt); + def->label = ("Y"); + def->tooltip = L("Number of pixels in Y"); + def->cli = "display-pixels-y=i"; + def->min = 100; + def->default_value = new ConfigOptionInt(1000); + + def = this->add("printer_correction", coFloats); + def->full_label = L("Printer scaling correction"); + def->tooltip = L("Printer scaling correction"); + def->min = 0; + def->default_value = new ConfigOptionFloats( { 1., 1., 1. } ); + + // SLA Material settings. + def = this->add("initial_layer_height", coFloat); + def->label = L("Initial layer height"); + def->tooltip = L("Initial layer height"); + def->sidetext = L("mm"); + def->cli = "initial-layer-height=f"; + def->min = 0; + def->default_value = new ConfigOptionFloat(0.3); + + def = this->add("exposure_time", coFloat); + def->label = L("Exposure time"); + def->tooltip = L("Exposure time"); + def->sidetext = L("s"); + def->cli = "exposure-time=f"; + def->min = 0; + def->default_value = new ConfigOptionFloat(10); + + def = this->add("initial_exposure_time", coFloat); + def->label = L("Initial exposure time"); + def->tooltip = L("Initial exposure time"); + def->sidetext = L("s"); + def->cli = "initial-exposure-time=f"; + def->min = 0; + def->default_value = new ConfigOptionFloat(15); + + def = this->add("material_correction_printing", coFloats); + def->full_label = L("Correction for expansion when printing"); + def->tooltip = L("Correction for expansion when printing"); + def->min = 0; + def->default_value = new ConfigOptionFloats( { 1. , 1., 1. } ); + + def = this->add("material_correction_curing", coFloats); + def->full_label = L("Correction for expansion after curing"); + def->tooltip = L("Correction for expansion after curing"); + def->min = 0; + def->default_value = new ConfigOptionFloats( { 1. , 1., 1. } ); + + def = this->add("material_notes", coString); + def->label = L("SLA print material notes"); + def->tooltip = L("You can put your notes regarding the SLA print material here."); + def->cli = "material-notes=s"; + def->multiline = true; + def->full_width = true; + def->height = 130; + def->mode = comAdvanced; + def->default_value = new ConfigOptionString(""); + + def = this->add("default_sla_material_profile", coString); + def->label = L("Default SLA material profile"); + def->tooltip = L("Default print profile associated with the current printer profile. " + "On selection of the current printer profile, this print profile will be activated."); + def->default_value = new ConfigOptionString(); + + def = this->add("sla_material_settings_id", coString); + def->default_value = new ConfigOptionString(""); + + + def = this->add("default_sla_print_profile", coString); + def->label = L("Default SLA material profile"); + def->tooltip = L("Default print profile associated with the current printer profile. " + "On selection of the current printer profile, this print profile will be activated."); + def->default_value = new ConfigOptionString(); + + def = this->add("sla_print_settings_id", coString); + def->default_value = new ConfigOptionString(""); + + def = this->add("support_head_front_radius", coFloat); + def->label = L("Support head front radius"); + def->tooltip = L("Radius of the pointing side of the head"); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(0.2); + + def = this->add("support_head_penetration", coFloat); + def->label = L("Support head penetration"); + def->tooltip = L("How much the pinhead has to penetrate the model surface"); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(0.2); + + def = this->add("support_head_back_radius", coFloat); + def->label = L("Support head back radius"); + def->tooltip = L("Radius of the back side of the 3d arrow"); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(0.5); + + def = this->add("support_head_width", coFloat); + def->label = L("Support head width"); + def->tooltip = L("Width from the back sphere center to the front sphere center"); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(1.0); + + def = this->add("support_pillar_radius", coFloat); + def->label = L("Support pillar radius"); + def->tooltip = L("Radius in mm of the support pillars"); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(0.8); + + def = this->add("support_base_radius", coFloat); + def->label = L("Support base radius"); + def->tooltip = L("Radius in mm of the pillar base"); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(2.0); + + def = this->add("support_base_height", coFloat); + def->label = L("Support base height"); + def->tooltip = L("The height of the pillar base cone"); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(1.0); + + def = this->add("support_critical_angle", coFloat); + def->label = L("Critical angle"); + def->tooltip = L("The default angle for connecting support sticks and junctions."); + def->sidetext = L("°"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(45); + + def = this->add("support_max_bridge_length", coFloat); + def->label = L("Max bridge length"); + def->tooltip = L("The max length of a bridge"); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(); + + def = this->add("pad_wall_thickness", coFloat); + def->label = L("Pad wall thickness"); + def->tooltip = L(""); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(2.0); + + def = this->add("pad_wall_height", coFloat); + def->label = L("Pad wall height"); + def->tooltip = L(""); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(5.0); + + def = this->add("pad_max_merge_distance", coFloat); + def->label = L("Max merge distance"); + def->tooltip = L(""); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(50.0); + + def = this->add("pad_edge_radius", coFloat); + def->label = L("pad edge radius"); + def->tooltip = L(""); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(1.0); } void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) @@ -2141,7 +2580,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va ConfigOptionPoint p; p.deserialize(value); std::ostringstream oss; - oss << "0x0," << p.value.x << "x0," << p.value.x << "x" << p.value.y << ",0x" << p.value.y; + oss << "0x0," << p.value(0) << "x0," << p.value(0) << "x" << p.value(1) << ",0x" << p.value(1); value = oss.str(); } else if ((opt_key == "perimeter_acceleration" && value == "25") || (opt_key == "infill_acceleration" && value == "50")) { @@ -2182,7 +2621,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va } } -PrintConfigDef print_config_def; +const PrintConfigDef print_config_def; DynamicPrintConfig* DynamicPrintConfig::new_from_defaults() { @@ -2236,28 +2675,20 @@ void DynamicPrintConfig::normalize() std::string DynamicPrintConfig::validate() { // Full print config is initialized from the defaults. - FullPrintConfig fpc; - fpc.apply(*this, true); - // Verify this print options through the FullPrintConfig. - return fpc.validate(); -} - -size_t DynamicPrintConfig::remove_keys_not_in(const DynamicPrintConfig &default_config, std::string &removed_keys_message) -{ - size_t n_removed_keys = 0; - for (const std::string &key : this->keys()) { - if (! default_config.has(key)) { - if (removed_keys_message.empty()) - removed_keys_message = key; - else { - removed_keys_message += ", "; - removed_keys_message += key; - } - this->erase(key); - ++ n_removed_keys; - } + const ConfigOption *opt = this->option("printer_technology", false); + auto printer_technology = (opt == nullptr) ? ptFFF : static_cast(dynamic_cast(opt)->value); + switch (printer_technology) { + case ptFFF: + { + FullPrintConfig fpc; + fpc.apply(*this, true); + // Verify this print options through the FullPrintConfig. + return fpc.validate(); + } + default: + //FIXME no validation on SLA data? + return std::string(); } - return n_removed_keys; } double PrintConfig::min_object_distance() const @@ -2452,4 +2883,162 @@ StaticPrintConfig::StaticCache PrintConfig::s_c StaticPrintConfig::StaticCache HostConfig::s_cache_HostConfig; StaticPrintConfig::StaticCache FullPrintConfig::s_cache_FullPrintConfig; +StaticPrintConfig::StaticCache SLAMaterialConfig::s_cache_SLAMaterialConfig; +StaticPrintConfig::StaticCache SLAPrintObjectConfig::s_cache_SLAPrintObjectConfig; +StaticPrintConfig::StaticCache SLAPrinterConfig::s_cache_SLAPrinterConfig; +StaticPrintConfig::StaticCache SLAFullPrintConfig::s_cache_SLAFullPrintConfig; + +CLIConfigDef::CLIConfigDef() +{ + ConfigOptionDef *def; + + def = this->add("cut", coFloat); + def->label = L("Cut"); + def->tooltip = L("Cut model at the given Z."); + def->cli = "cut"; + def->default_value = new ConfigOptionFloat(0); + + def = this->add("dont_arrange", coBool); + def->label = L("Dont arrange"); + def->tooltip = L("Don't arrange the objects on the build plate. The model coordinates " + "define the absolute positions on the build plate. " + "The option --center will be ignored."); + def->cli = "dont-arrange"; + def->default_value = new ConfigOptionBool(false); + + def = this->add("datadir", coString); + def->label = L("User data directory"); + def->tooltip = L("Load and store settings at the given directory. " + "This is useful for maintaining different profiles or including " + "configurations from a network storage."); + def->cli = "datadir"; + def->default_value = new ConfigOptionString(); + + def = this->add("export_3mf", coBool); + def->label = L("Export 3MF"); + def->tooltip = L("Slice the model and export slices as 3MF."); + def->cli = "export-3mf"; + def->default_value = new ConfigOptionBool(false); + + def = this->add("slice", coBool); + def->label = L("Slice"); + def->tooltip = L("Slice the model and export gcode."); + def->cli = "slice"; + def->default_value = new ConfigOptionBool(false); + + def = this->add("help", coBool); + def->label = L("Help"); + def->tooltip = L("Show this help."); + def->cli = "help"; + def->default_value = new ConfigOptionBool(false); + + def = this->add("gui", coBool); + def->label = L("Use GUI"); + def->tooltip = L("Forces the GUI launch instead of command line slicing " + "(if you supply a model file, it will be loaded into the plater)"); + def->cli = "gui"; + def->default_value = new ConfigOptionBool(true); + + def = this->add("info", coBool); + def->label = L("Output Model Info"); + def->tooltip = L("Write information about the model to the console."); + def->cli = "info"; + def->default_value = new ConfigOptionBool(false); + + def = this->add("load", coStrings); + def->label = L("Load config file"); + def->tooltip = L("Load configuration from the specified file. It can be used more than once to load options from multiple files."); + def->cli = "load"; + def->default_value = new ConfigOptionStrings(); + + def = this->add("no_gui", coBool); + def->label = L("Do not use GUI"); + def->tooltip = L("Forces the command line slicing instead of gui. This takes precedence over --gui if both are present."); + def->cli = "no-gui"; + def->default_value = new ConfigOptionBool(false); + + def = this->add("output", coString); + def->label = L("Output File"); + def->tooltip = L("The file where the output will be written (if not specified, it will be based on the input file)."); + def->cli = "output"; + def->default_value = new ConfigOptionString(""); + + def = this->add("rotate", coFloat); + def->label = L("Rotate"); + def->tooltip = L("Rotation angle around the Z axis in degrees (0-360, default: 0)."); + def->cli = "rotate"; + def->default_value = new ConfigOptionFloat(0); + + def = this->add("rotate_x", coFloat); + def->label = L("Rotate around X"); + def->tooltip = L("Rotation angle around the X axis in degrees (0-360, default: 0)."); + def->cli = "rotate-x"; + def->default_value = new ConfigOptionFloat(0); + + def = this->add("rotate_y", coFloat); + def->label = L("Rotate around Y"); + def->tooltip = L("Rotation angle around the Y axis in degrees (0-360, default: 0)."); + def->cli = "rotate-y"; + def->default_value = new ConfigOptionFloat(0); + + def = this->add("save", coString); + def->label = L("Save config file"); + def->tooltip = L("Save configuration to the specified file."); + def->cli = "save"; + def->default_value = new ConfigOptionString(); + + def = this->add("scale", coFloat); + def->label = L("Scale"); + def->tooltip = L("Scaling factor (default: 1)."); + def->cli = "scale"; + def->default_value = new ConfigOptionFloat(1); + +/* + def = this->add("scale_to_fit", coPoint3); + def->label = L("Scale to Fit"); + def->tooltip = L("Scale to fit the given volume."); + def->cli = "scale-to-fit"; + def->default_value = new ConfigOptionPoint3(Pointf3(0,0,0)); +*/ + + def = this->add("print_center", coPoint); + def->label = L("Print center"); + def->tooltip = L("Center the print around the given center (default: 100, 100)."); + def->cli = "print-center"; + def->default_value = new ConfigOptionPoint(Vec2d(100,100)); +} + +const CLIConfigDef cli_config_def; +DynamicPrintAndCLIConfig::PrintAndCLIConfigDef DynamicPrintAndCLIConfig::s_def; + +std::ostream& print_cli_options(std::ostream& out) +{ + for (const auto& opt : cli_config_def.options) { + if (opt.second.cli.size() != 0) { + out << "\t" << std::left << std::setw(40) << std::string("--") + opt.second.cli; + out << "\t" << opt.second.tooltip << "\n"; + if (opt.second.default_value != nullptr) + out << "\t" << std::setw(40) << " " << "\t" << " (default: " << opt.second.default_value->serialize() << ")"; + out << "\n"; + } + } + std::cerr << std::endl; + return out; +} + +std::ostream& print_print_options(std::ostream& out) +{ + for (const auto& opt : print_config_def.options) { + if (opt.second.cli.size() != 0) { + out << "\t" << std::left << std::setw(40) << std::string("--") + opt.second.cli; + out << "\t" << opt.second.tooltip << "\n"; + if (opt.second.default_value != nullptr) + out << "\t" << std::setw(40) << " " << "\t" << " (default: " << opt.second.default_value->serialize() << ")"; + out << "\n"; + } + } + std::cerr << std::endl; + return out; +} + } diff --git a/xs/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp similarity index 78% rename from xs/src/libslic3r/PrintConfig.hpp rename to src/libslic3r/PrintConfig.hpp index 28d5aa8f42..545f6d8be3 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -22,6 +22,14 @@ namespace Slic3r { +enum PrinterTechnology +{ + // Fused Filament Fabrication + ptFFF, + // Stereolitography + ptSLA, +}; + enum GCodeFlavor { gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit, gcfSmoothie, gcfNoExtrusion, @@ -48,7 +56,16 @@ enum FilamentType { ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA }; -template<> inline t_config_enum_values& ConfigOptionEnum::get_enum_values() { +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { + static t_config_enum_values keys_map; + if (keys_map.empty()) { + keys_map["FFF"] = ptFFF; + keys_map["SLA"] = ptSLA; + } + return keys_map; +} + +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { keys_map["reprap"] = gcfRepRap; @@ -65,7 +82,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum::get_enum_ return keys_map; } -template<> inline t_config_enum_values& ConfigOptionEnum::get_enum_values() { +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { keys_map["octoprint"] = htOctoPrint; @@ -74,7 +91,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum::get_enu return keys_map; } -template<> inline t_config_enum_values& ConfigOptionEnum::get_enum_values() { +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { keys_map["rectilinear"] = ipRectilinear; @@ -94,7 +111,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum::get_enu return keys_map; } -template<> inline t_config_enum_values& ConfigOptionEnum::get_enum_values() { +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { keys_map["rectilinear"] = smpRectilinear; @@ -104,7 +121,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum return keys_map; } -template<> inline t_config_enum_values& ConfigOptionEnum::get_enum_values() { +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { keys_map["random"] = spRandom; @@ -115,7 +132,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum::get_enum return keys_map; } -template<> inline t_config_enum_values& ConfigOptionEnum::get_enum_values() { +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { keys_map["PLA"] = ftPLA; @@ -139,11 +156,16 @@ public: PrintConfigDef(); static void handle_legacy(t_config_option_key &opt_key, std::string &value); + +private: + void init_common_params(); + void init_fff_params(); + void init_sla_params(); }; // The one and only global definition of SLic3r configuration options. // This definition is constant. -extern PrintConfigDef print_config_def; +extern const PrintConfigDef print_config_def; // Slic3r dynamic configuration, used to override the configuration // per object, per modification volume or per printing material. @@ -168,10 +190,6 @@ public: // Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned. std::string validate(); - // Remove all keys not in "valid_keys", return number of removed keys and add the list of keys to "removed_keys_message. - // valid_keys has to be sorted lexicographically. - size_t remove_keys_not_in(const DynamicPrintConfig &default_config, std::string &removed_keys_message); - // Verify whether the opt_key has not been obsoleted or renamed. // Both opt_key and value may be modified by handle_legacy(). // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). @@ -686,6 +704,7 @@ public: ConfigOptionInts bridge_fan_speed; ConfigOptionFloat brim_width; ConfigOptionBool complete_objects; + ConfigOptionFloats colorprint_heights; ConfigOptionBools cooling; ConfigOptionFloat default_acceleration; ConfigOptionInts disable_fan_first_layers; @@ -743,6 +762,12 @@ public: ConfigOptionFloats wiping_volumes_matrix; ConfigOptionFloats wiping_volumes_extruders; ConfigOptionFloat z_offset; + ConfigOptionFloat bed_size_x; + ConfigOptionFloat bed_size_y; + ConfigOptionInt pixel_width; + ConfigOptionInt pixel_height; + ConfigOptionFloat exp_time; + ConfigOptionFloat exp_time_first; protected: PrintConfig(int) : GCodeConfig(1) {} @@ -757,6 +782,7 @@ protected: OPT_PTR(bridge_fan_speed); OPT_PTR(brim_width); OPT_PTR(complete_objects); + OPT_PTR(colorprint_heights); OPT_PTR(cooling); OPT_PTR(default_acceleration); OPT_PTR(disable_fan_first_layers); @@ -814,6 +840,12 @@ protected: OPT_PTR(wiping_volumes_matrix); OPT_PTR(wiping_volumes_extruders); OPT_PTR(z_offset); + OPT_PTR(bed_size_x); + OPT_PTR(bed_size_y); + OPT_PTR(pixel_width); + OPT_PTR(pixel_height); + OPT_PTR(exp_time); + OPT_PTR(exp_time_first); } }; @@ -866,11 +898,241 @@ protected: } }; +class SLAPrintObjectConfig : public StaticPrintConfig +{ + STATIC_PRINT_CONFIG_CACHE(SLAPrintObjectConfig) +public: + ConfigOptionFloat layer_height; + + // Radius in mm of the pointing side of the head. + ConfigOptionFloat support_head_front_radius /*= 0.2*/; + + // How much the pinhead has to penetrate the model surface + ConfigOptionFloat support_head_penetration /*= 0.2*/; + + // Radius of the back side of the 3d arrow. + ConfigOptionFloat support_head_back_radius /*= 0.5*/; + + // Width in mm from the back sphere center to the front sphere center. + ConfigOptionFloat support_head_width /*= 1.0*/; + + // Radius in mm of the support pillars. + // TODO: This parameter is invalid. The pillar radius will be dynamic in + // nature. Merged pillars will have an increased thickness. This parameter + // may serve as the maximum radius, or maybe an increase when two are merged + // The default radius will be derived from head_back_radius_mm + ConfigOptionFloat support_pillar_radius /*= 0.8*/; + + // Radius in mm of the pillar base. + ConfigOptionFloat support_base_radius /*= 2.0*/; + + // The height of the pillar base cone in mm. + ConfigOptionFloat support_base_height /*= 1.0*/; + + // The default angle for connecting support sticks and junctions. + ConfigOptionFloat support_critical_angle /*= M_PI/4*/; + + // The max length of a bridge in mm + ConfigOptionFloat support_max_bridge_length /*= 15.0*/; + + // The elevation in Z direction upwards. This is the space between the pad + // and the model object's bounding box bottom. + ConfigOptionFloat support_object_elevation; + + // Now for the base pool (plate) /////////////////////////////////////////// + + ConfigOptionFloat pad_wall_thickness /*= 2*/; + ConfigOptionFloat pad_wall_height /*= 5*/; + ConfigOptionFloat pad_max_merge_distance /*= 50*/; + ConfigOptionFloat pad_edge_radius /*= 1*/; + +protected: + void initialize(StaticCacheBase &cache, const char *base_ptr) + { + OPT_PTR(layer_height); + OPT_PTR(support_head_front_radius); + OPT_PTR(support_head_penetration); + OPT_PTR(support_head_back_radius); + OPT_PTR(support_head_width); + OPT_PTR(support_pillar_radius); + OPT_PTR(support_base_radius); + OPT_PTR(support_base_height); + OPT_PTR(support_critical_angle); + OPT_PTR(support_max_bridge_length); + OPT_PTR(pad_wall_thickness); + OPT_PTR(pad_wall_height); + OPT_PTR(pad_max_merge_distance); + OPT_PTR(pad_edge_radius); + } +}; + +class SLAMaterialConfig : public StaticPrintConfig +{ + STATIC_PRINT_CONFIG_CACHE(SLAMaterialConfig) +public: + ConfigOptionFloat initial_layer_height; + ConfigOptionFloat exposure_time; + ConfigOptionFloat initial_exposure_time; + ConfigOptionFloats material_correction_printing; + ConfigOptionFloats material_correction_curing; +protected: + void initialize(StaticCacheBase &cache, const char *base_ptr) + { + OPT_PTR(initial_layer_height); + OPT_PTR(exposure_time); + OPT_PTR(initial_exposure_time); + OPT_PTR(material_correction_printing); + OPT_PTR(material_correction_curing); + } +}; + +class SLAPrinterConfig : public StaticPrintConfig +{ + STATIC_PRINT_CONFIG_CACHE(SLAPrinterConfig) +public: + ConfigOptionEnum printer_technology; + ConfigOptionPoints bed_shape; + ConfigOptionFloat max_print_height; + ConfigOptionFloat display_width; + ConfigOptionFloat display_height; + ConfigOptionInt display_pixels_x; + ConfigOptionInt display_pixels_y; + ConfigOptionFloats printer_correction; +protected: + void initialize(StaticCacheBase &cache, const char *base_ptr) + { + OPT_PTR(printer_technology); + OPT_PTR(bed_shape); + OPT_PTR(max_print_height); + OPT_PTR(display_width); + OPT_PTR(display_height); + OPT_PTR(display_pixels_x); + OPT_PTR(display_pixels_y); + OPT_PTR(printer_correction); + } +}; + +class SLAFullPrintConfig : public SLAPrinterConfig, public SLAPrintObjectConfig, public SLAMaterialConfig +{ + STATIC_PRINT_CONFIG_CACHE_DERIVED(SLAFullPrintConfig) + SLAFullPrintConfig() : SLAPrinterConfig(0), SLAPrintObjectConfig(0), SLAMaterialConfig(0) { initialize_cache(); *this = s_cache_SLAFullPrintConfig.defaults(); } + +public: + // Validate the SLAFullPrintConfig. Returns an empty string on success, otherwise an error message is returned. +// std::string validate(); + +protected: + // Protected constructor to be called to initialize ConfigCache::m_default. + SLAFullPrintConfig(int) : SLAPrinterConfig(0), SLAPrintObjectConfig(0), SLAMaterialConfig(0) {} + void initialize(StaticCacheBase &cache, const char *base_ptr) + { + this->SLAPrinterConfig ::initialize(cache, base_ptr); + this->SLAPrintObjectConfig::initialize(cache, base_ptr); + this->SLAMaterialConfig ::initialize(cache, base_ptr); + } +}; + #undef STATIC_PRINT_CONFIG_CACHE #undef STATIC_PRINT_CONFIG_CACHE_BASE #undef STATIC_PRINT_CONFIG_CACHE_DERIVED #undef OPT_PTR -} +class CLIConfigDef : public ConfigDef +{ +public: + CLIConfigDef(); +}; + +extern const CLIConfigDef cli_config_def; + +#define OPT_PTR(KEY) if (opt_key == #KEY) return &this->KEY + +class CLIConfig : public virtual ConfigBase, public StaticConfig +{ +public: + ConfigOptionFloat cut; + ConfigOptionString datadir; + ConfigOptionBool dont_arrange; + ConfigOptionBool export_3mf; + ConfigOptionBool gui; + ConfigOptionBool info; + ConfigOptionBool help; + ConfigOptionStrings load; + ConfigOptionBool no_gui; + ConfigOptionString output; + ConfigOptionPoint print_center; + ConfigOptionFloat rotate; + ConfigOptionFloat rotate_x; + ConfigOptionFloat rotate_y; + ConfigOptionString save; + ConfigOptionFloat scale; +// ConfigOptionPoint3 scale_to_fit; + ConfigOptionBool slice; + + CLIConfig() : ConfigBase(), StaticConfig() + { + this->set_defaults(); + }; + + // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. + const ConfigDef* def() const override { return &cli_config_def; } + t_config_option_keys keys() const override { return cli_config_def.keys(); } + + ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override + { + OPT_PTR(cut); + OPT_PTR(datadir); + OPT_PTR(dont_arrange); + OPT_PTR(export_3mf); + OPT_PTR(gui); + OPT_PTR(help); + OPT_PTR(info); + OPT_PTR(load); + OPT_PTR(no_gui); + OPT_PTR(output); + OPT_PTR(print_center); + OPT_PTR(rotate); + OPT_PTR(rotate_x); + OPT_PTR(rotate_y); + OPT_PTR(save); + OPT_PTR(scale); +// OPT_PTR(scale_to_fit); + OPT_PTR(slice); + return NULL; + } +}; + +#undef OPT_PTR + +class DynamicPrintAndCLIConfig : public DynamicPrintConfig +{ +public: + DynamicPrintAndCLIConfig() { this->apply(FullPrintConfig::defaults()); this->apply(CLIConfig()); } + DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {} + + // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. + const ConfigDef* def() const override { return &s_def; } + t_config_option_keys keys() const override { return s_def.keys(); } + +private: + class PrintAndCLIConfigDef : public ConfigDef + { + public: + PrintAndCLIConfigDef() { + this->options.insert(print_config_def.options.begin(), print_config_def.options.end()); + this->options.insert(cli_config_def.options.begin(), cli_config_def.options.end()); + } + // Do not release the default values, they are handled by print_config_def & cli_config_def. + ~PrintAndCLIConfigDef() { this->options.clear(); } + }; + static PrintAndCLIConfigDef s_def; +}; + +/// Iterate through all of the print options and write them to a stream. +std::ostream& print_print_options(std::ostream& out); +/// Iterate through all of the CLI options and write them to a stream. +std::ostream& print_cli_options(std::ostream& out); + +} // namespace Slic3r #endif diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp new file mode 100644 index 0000000000..60bc7dbe66 --- /dev/null +++ b/src/libslic3r/PrintExport.hpp @@ -0,0 +1,395 @@ +#ifndef PRINTEXPORT_HPP +#define PRINTEXPORT_HPP + +// For png export of the sliced model +#include +#include +#include + +#include + +#include "Rasterizer/Rasterizer.hpp" +//#include +//#include //#include "tbb/mutex.h" + +namespace Slic3r { + +enum class FilePrinterFormat { + SLA_PNGZIP, + SVG +}; + +/* + * Interface for a file printer of the slices. Implementation can be an SVG + * or PNG printer or any other format. + * + * The format argument specifies the output format of the printer and it enables + * different implementations of this class template for each supported format. + * + */ +template +class FilePrinter { +public: + + void print_config(const Print&); + + // Draw an ExPolygon which is a polygon inside a slice on the specified layer. + void draw_polygon(const ExPolygon& p, unsigned lyr); + + // Tell the printer how many layers should it consider. + void layers(unsigned layernum); + + // Get the number of layers in the print. + unsigned layers() const; + + /* Switch to a particular layer. If there where less layers then the + * specified layer number than an appropriate number of layers will be + * allocated in the printer. + */ + void begin_layer(unsigned layer); + + // Allocate a new layer on top of the last and switch to it. + void begin_layer(); + + /* + * Finish the selected layer. It means that no drawing is allowed on that + * layer anymore. This fact can be used to prepare the file system output + * data like png comprimation and so on. + */ + void finish_layer(unsigned layer); + + // Finish the top layer. + void finish_layer(); + + // Save all the layers into the file (or dir) specified in the path argument + void save(const std::string& path); + + // Save only the selected layer to the file specified in path argument. + void save_layer(unsigned lyr, const std::string& path); +}; + +// Provokes static_assert in the right way. +template struct VeryFalse { static const bool value = false; }; + +// This has to be explicitly implemented in the gui layer or a default zlib +// based implementation is needed. I don't have time for that and I'm delegating +// the implementation to the gui layer where the gui toolkit can cover this. +template class LayerWriter { +public: + + LayerWriter(const std::string& /*zipfile_path*/) { + static_assert(VeryFalse::value, + "No layer writer implementation provided!"); + } + + void next_entry(const std::string& /*fname*/) {} + + std::string get_name() { return ""; } + + bool is_ok() { return false; } + + template LayerWriter& operator<<(const T& /*arg*/) { + return *this; + } + + void close() {} +}; + +// Implementation for PNG raster output +// Be aware that if a large number of layers are allocated, it can very well +// exhaust the available memory especially on 32 bit platform. +template<> class FilePrinter +{ + struct Layer { + Raster first; + std::stringstream second; + + Layer() {} + + Layer(const Layer&) = delete; + Layer(Layer&& m): + first(std::move(m.first))/*, second(std::move(m.second))*/ {} + }; + + // We will save the compressed PNG data into stringstreams which can be done + // in parallel. Later we can write every layer to the disk sequentially. + std::vector m_layers_rst; + Raster::Resolution m_res; + Raster::PixelDim m_pxdim; + double m_exp_time_s = .0, m_exp_time_first_s = .0; + double m_layer_height = .0; + + std::string createIniContent(const std::string& projectname) { + double layer_height = m_layer_height; + + using std::string; + using std::to_string; + + auto expt_str = to_string(m_exp_time_s); + auto expt_first_str = to_string(m_exp_time_first_s); + auto stepnum_str = to_string(static_cast(800*layer_height)); + auto layerh_str = to_string(layer_height); + + return string( + "action = print\n" + "jobDir = ") + projectname + "\n" + + "expTime = " + expt_str + "\n" + "expTimeFirst = " + expt_first_str + "\n" + "stepNum = " + stepnum_str + "\n" + "wifiOn = 1\n" + "tiltSlow = 60\n" + "tiltFast = 15\n" + "numFade = 10\n" + "startdelay = 0\n" + "layerHeight = " + layerh_str + "\n" + "noteInfo = " + "expTime="+expt_str+"+resinType=generic+layerHeight=" + +layerh_str+"+printer=DWARF3\n"; + } + + // Change this to TOP_LEFT if you want correct PNG orientation + static const Raster::Origin ORIGIN = Raster::Origin::BOTTOM_LEFT; + +public: + inline FilePrinter(double width_mm, double height_mm, + unsigned width_px, unsigned height_px, + double layer_height, + double exp_time, double exp_time_first): + m_res(width_px, height_px), + m_pxdim(width_mm/width_px, height_mm/height_px), + m_exp_time_s(exp_time), + m_exp_time_first_s(exp_time_first), + m_layer_height(layer_height) + { + } + + FilePrinter(const FilePrinter& ) = delete; + FilePrinter(FilePrinter&& m): + m_layers_rst(std::move(m.m_layers_rst)), + m_res(m.m_res), + m_pxdim(m.m_pxdim) {} + + inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } + inline unsigned layers() const { return unsigned(m_layers_rst.size()); } + + inline void draw_polygon(const ExPolygon& p, unsigned lyr) { + assert(lyr < m_layers_rst.size()); + m_layers_rst[lyr].first.draw(p); + } + + inline void begin_layer(unsigned lyr) { + if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1); + m_layers_rst[lyr].first.reset(m_res, m_pxdim, ORIGIN); + } + + inline void begin_layer() { + m_layers_rst.emplace_back(); + m_layers_rst.front().first.reset(m_res, m_pxdim, ORIGIN); + } + + inline void finish_layer(unsigned lyr_id) { + assert(lyr_id < m_layers_rst.size()); + m_layers_rst[lyr_id].first.save(m_layers_rst[lyr_id].second, + Raster::Compression::PNG); + m_layers_rst[lyr_id].first.reset(); + } + + inline void finish_layer() { + if(!m_layers_rst.empty()) { + m_layers_rst.back().first.save(m_layers_rst.back().second, + Raster::Compression::PNG); + m_layers_rst.back().first.reset(); + } + } + + template + inline void save(const std::string& path) { + try { + LayerWriter writer(path); + + std::string project = writer.get_name(); + + writer.next_entry("config.ini"); + writer << createIniContent(project); + + for(unsigned i = 0; i < m_layers_rst.size(); i++) { + if(m_layers_rst[i].second.rdbuf()->in_avail() > 0) { + char lyrnum[6]; + std::sprintf(lyrnum, "%.5d", i); + auto zfilename = project + lyrnum + ".png"; + writer.next_entry(zfilename); + writer << m_layers_rst[i].second.rdbuf(); + m_layers_rst[i].second.str(""); + } + } + + writer.close(); + } catch(std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + return; + } + } + + void save_layer(unsigned lyr, const std::string& path) { + unsigned i = lyr; + assert(i < m_layers_rst.size()); + + char lyrnum[6]; + std::sprintf(lyrnum, "%.5d", lyr); + std::string loc = path + "layer" + lyrnum + ".png"; + + std::fstream out(loc, std::fstream::out | std::fstream::binary); + if(out.good()) { + m_layers_rst[i].first.save(out, Raster::Compression::PNG); + } else { + BOOST_LOG_TRIVIAL(error) << "Can't create file for layer"; + } + + out.close(); + m_layers_rst[i].first.reset(); + } +}; + +//// Let's shadow this eigen interface +//inline coord_t px(const Point& p) { return p(0); } +//inline coord_t py(const Point& p) { return p(1); } +//inline coordf_t px(const Vec2d& p) { return p(0); } +//inline coordf_t py(const Vec2d& p) { return p(1); } + +//template +//void print_to(Print& print, +// std::string dirpath, +// double width_mm, +// double height_mm, +// Args&&...args) +//{ + +// std::string& dir = dirpath; + +// // This map will hold the layers sorted by z coordinate. Layers on the +// // same height (from different objects) will be mapped to the same key and +// // rasterized to the same image. +// std::map layers; + +// auto& objects = print.objects(); + +// // Merge the sliced layers with the support layers +// std::for_each(objects.cbegin(), objects.cend(), +// [&layers](const PrintObject *o) +// { +// for(const auto l : o->layers()) { +// auto& lyrs = layers[static_cast(scale_(l->print_z))]; +// lyrs.push_back(l); +// } + +// for(const auto l : o->support_layers()) { +// auto& lyrs = layers[static_cast(scale_(l->print_z))]; +// lyrs.push_back(l); +// } +// }); + +// auto print_bb = print.bounding_box(); +// Vec2d punsc = unscale(print_bb.size()); + +// // If the print does not fit into the print area we should cry about it. +// if(px(punsc) > width_mm || py(punsc) > height_mm) { +// BOOST_LOG_TRIVIAL(warning) << "Warning: Print will not fit!" << "\n" +// << "Width needed: " << px(punsc) << "\n" +// << "Height needed: " << py(punsc) << "\n"; +// } + +// // Offset for centering the print onto the print area +// auto cx = scale_(width_mm)/2 - (px(print_bb.center()) - px(print_bb.min)); +// auto cy = scale_(height_mm)/2 - (py(print_bb.center()) - py(print_bb.min)); + +// // Create the actual printer, forward any additional arguments to it. +// FilePrinter printer(width_mm, height_mm, +// std::forward(args)...); + +// printer.print_config(print); + +// printer.layers(layers.size()); // Allocate space for all the layers + +// int st_prev = 0; +// const std::string jobdesc = "Rasterizing and compressing sliced layers"; +// tbb::spin_mutex m; + +// std::vector keys; +// keys.reserve(layers.size()); +// for(auto& e : layers) keys.push_back(e.first); + +// print.set_status(0, jobdesc); + +// // Method that prints one layer +// auto process_layer = [&layers, &keys, &printer, &st_prev, &m, +// &jobdesc, print_bb, dir, cx, cy, &print] +// (unsigned layer_id) +// { +// LayerPtrs lrange = layers[keys[layer_id]]; + +// printer.begin_layer(layer_id); // Switch to the appropriate layer + +// for(Layer *lp : lrange) { +// Layer& l = *lp; + +// ExPolygonCollection slices = l.slices; // Copy the layer slices + +// // Sort the polygons in the layer +// std::stable_sort(slices.expolygons.begin(), slices.expolygons.end(), +// [](const ExPolygon& a, const ExPolygon& b) { +// return a.contour.contains(b.contour.first_point()) ? false : +// true; +// }); + +// // Draw all the polygons in the slice to the actual layer. +// for (const Point &d : l.object()->copies()) +// for (ExPolygon slice : slices.expolygons) { +// slice.translate(px(d), py(d)); +// slice.translate(-px(print_bb.min) + cx, +// -py(print_bb.min) + cy); + +// printer.draw_polygon(slice, layer_id); +// } + +// /*if(print.has_support_material() && layer_id > 0) { +// BOOST_LOG_TRIVIAL(warning) << "support material for layer " +// << layer_id +// << " defined but export is " +// "not yet implemented."; + +// }*/ + +// } + +// printer.finish_layer(layer_id); // Finish the layer for later saving it. + +// auto st = static_cast(layer_id*80.0/layers.size()); +// m.lock(); +// if( st - st_prev > 10) { +// print.set_status(st, jobdesc); +// st_prev = st; +// } +// m.unlock(); + +// // printer.saveLayer(layer_id, dir); We could save the layer immediately +// }; + +// // Print all the layers in parallel +// tbb::parallel_for(0, +// layers.size(), +// process_layer); + +// // Sequential version (for testing) +// // for(unsigned l = 0; l < layers.size(); ++l) process_layer(l); + +//// print.set_status(100, jobdesc); + +// // Save the print into the file system. +// print.set_status(90, "Writing layers to disk"); +// printer.save(dir); +// print.set_status(100, "Writing layers completed"); +//} + +} + +#endif // PRINTEXPORT_HPP diff --git a/xs/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp similarity index 77% rename from xs/src/libslic3r/PrintObject.cpp rename to src/libslic3r/PrintObject.cpp index 1ddcac13d8..a71fa194fe 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -34,10 +34,11 @@ namespace Slic3r { -PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) : +PrintObject::PrintObject(Print* print, ModelObject* model_object) : + PrintObjectBaseWithState(print), typed_slices(false), - _print(print), - _model_object(model_object), + m_model_object(model_object), + size(Vec3crd::Zero()), layer_height_profile_valid(false) { // Compute the translation to be applied to our meshes so that we work with smaller coordinates @@ -48,93 +49,382 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding // don't assume it's already aligned and we don't alter the original position in model. // We store the XY translation so that we can place copies correctly in the output G-code // (copies are expressed in G-code coordinates and this translation is not publicly exposed). - this->_copies_shift = Point::new_scale(modobj_bbox.min.x, modobj_bbox.min.y); + const BoundingBoxf3 modobj_bbox = model_object->raw_bounding_box(); + m_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1)); // Scale the object size and store it - Pointf3 size = modobj_bbox.size(); - this->size = Point3::new_scale(size.x, size.y, size.z); + this->size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast(); } - this->reload_model_instances(); + { + Points copies; + copies.reserve(m_model_object->instances.size()); + for (const ModelInstance *mi : m_model_object->instances) { + assert(mi->is_printable()); + const Vec3d& offset = mi->get_offset(); + copies.emplace_back(Point::new_scale(offset(0), offset(1))); + } + this->set_copies(copies); + } + this->layer_height_ranges = model_object->layer_height_ranges; this->layer_height_profile = model_object->layer_height_profile; } -bool PrintObject::add_copy(const Pointf &point) -{ - Points points = this->_copies; - points.push_back(Point::new_scale(point.x, point.y)); - return this->set_copies(points); -} - -bool PrintObject::delete_last_copy() -{ - Points points = this->_copies; - points.pop_back(); - return this->set_copies(points); -} - bool PrintObject::set_copies(const Points &points) { - bool copies_num_changed = this->_copies.size() != points.size(); - this->_copies = points; - - // order copies with a nearest neighbor search and translate them by _copies_shift - this->_shifted_copies.clear(); - this->_shifted_copies.reserve(points.size()); - - // order copies with a nearest-neighbor search - std::vector ordered_copies; - Slic3r::Geometry::chained_path(points, ordered_copies); - - for (size_t point_idx : ordered_copies) { - Point copy = points[point_idx]; - copy.translate(this->_copies_shift); - this->_shifted_copies.push_back(copy); + // Order copies with a nearest-neighbor search. + std::vector copies; + { + std::vector ordered_copies; + Slic3r::Geometry::chained_path(points, ordered_copies); + copies.reserve(ordered_copies.size()); + for (size_t point_idx : ordered_copies) + copies.emplace_back(points[point_idx] + m_copies_shift); + } + // Invalidate and set copies. + bool invalidated = false; + if (copies != m_copies) { + invalidated = m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }); + if (copies.size() != m_copies.size()) + invalidated |= m_print->invalidate_step(psWipeTower); + m_copies = copies; } - - bool invalidated = this->_print->invalidate_step(psSkirt); - invalidated |= this->_print->invalidate_step(psBrim); - if (copies_num_changed) - invalidated |= this->_print->invalidate_step(psWipeTower); return invalidated; } -bool PrintObject::reload_model_instances() +// 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 +void PrintObject::slice() { - Points copies; - copies.reserve(this->_model_object->instances.size()); - for (const ModelInstance *mi : this->_model_object->instances) - { - if (mi->is_printable()) - copies.emplace_back(Point::new_scale(mi->offset.x, mi->offset.y)); + if (this->is_step_done(posSlice)) + return; + this->set_started(posSlice); + m_print->set_status(10, "Processing triangulated mesh"); + this->_slice(); + m_print->throw_if_canceled(); + // Fix the model. + //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend. + std::string warning = this->_fix_slicing_errors(); + m_print->throw_if_canceled(); + if (! warning.empty()) + BOOST_LOG_TRIVIAL(info) << warning; + // Simplify slices if required. + if (m_print->config().resolution) + this->_simplify_slices(scale_(this->print()->config().resolution)); + if (m_layers.empty()) + throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); + this->set_done(posSlice); +} + +// 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). +void PrintObject::make_perimeters() +{ + // prerequisites + this->slice(); + + if (this->is_step_done(posPerimeters)) + return; + + this->set_started(posPerimeters); + m_print->set_status(20, "Generating perimeters"); + BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; + + // merge slices if they were split into types + if (this->typed_slices) { + for (Layer *layer : m_layers) { + layer->merge_slices(); + m_print->throw_if_canceled(); + } + this->typed_slices = false; + } + + // compare each layer to the one below, and mark those slices needing + // one additional inner perimeter, like the top of domed objects- + + // this algorithm makes sure that at least one perimeter is overlapping + // but we don't generate any extra perimeter if fill density is zero, as they would be floating + // inside the object - infill_only_where_needed should be the method of choice for printing + // hollow objects + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + const PrintRegion ®ion = *m_print->regions()[region_id]; + if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) + continue; + + BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start"; + tbb::parallel_for( + tbb::blocked_range(0, m_layers.size() - 1), + [this, ®ion, region_id](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + m_print->throw_if_canceled(); + LayerRegion &layerm = *m_layers[layer_idx]->m_regions[region_id]; + const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->m_regions[region_id]; + const Polygons upper_layerm_polygons = upper_layerm.slices; + // Filter upper layer polygons in intersection_ppl by their bounding boxes? + // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; + const double total_loop_length = total_length(upper_layerm_polygons); + const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing(); + const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter); + const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width(); + const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing(); + + for (Surface &slice : layerm.slices.surfaces) { + for (;;) { + // compute the total thickness of perimeters + const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 + + (region.config().perimeters-1 + slice.extra_perimeters) * perimeter_spacing; + // define a critical area where we don't want the upper slice to fall into + // (it should either lay over our perimeters or outside this area) + const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5); + const Polygons critical_area = diff( + offset(slice.expolygon, float(- perimeters_thickness)), + offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth)) + ); + // check whether a portion of the upper slices falls inside the critical area + const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area); + // only add an additional loop if at least 30% of the slice loop would benefit from it + if (total_length(intersection) <= total_loop_length*0.3) + break; + /* + if (0) { + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "extra.svg", + no_arrows => 1, + expolygons => union_ex($critical_area), + polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], + ); + } + */ + ++ slice.extra_perimeters; + } + #ifdef DEBUG + if (slice.extra_perimeters > 0) + printf(" adding %d more perimeter(s) at layer %zu\n", slice.extra_perimeters, layer_idx); + #endif + } + } + }); + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end"; + } + + BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; + tbb::parallel_for( + tbb::blocked_range(0, m_layers.size()), + [this](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + m_print->throw_if_canceled(); + m_layers[layer_idx]->make_perimeters(); + } + } + ); + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; + + /* + simplify slices (both layer and region slices), + we only need the max resolution for perimeters + ### This makes this method not-idempotent, so we keep it disabled for now. + ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); + */ + + this->set_done(posPerimeters); +} + +void PrintObject::prepare_infill() +{ + if (this->is_step_done(posPrepareInfill)) + return; + + this->set_started(posPrepareInfill); + m_print->set_status(30, "Preparing infill"); + + // This will assign a type (top/bottom/internal) to $layerm->slices. + // Then the classifcation of $layerm->slices is transfered onto + // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces + // by the cummulative area of the previous $layerm->fill_surfaces. + this->detect_surfaces_type(); + m_print->throw_if_canceled(); + + // Decide what surfaces are to be filled. + // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. + // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. + BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..."; + for (auto *layer : m_layers) + for (auto *region : layer->m_regions) { + region->prepare_fill_surfaces(); + m_print->throw_if_canceled(); + } + + // this will detect bridges and reverse bridges + // and rearrange top/bottom/internal surfaces + // It produces enlarged overlapping bridging areas. + // + // 1) S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap. + // 2) S_TYPE_TOP is grown by 3mm and clipped by the grown bottom areas. The areas may overlap. + // 3) Clip the internal surfaces by the grown top/bottom surfaces. + // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps. + //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties. + this->process_external_surfaces(); + m_print->throw_if_canceled(); + + // Add solid fills to ensure the shell vertical thickness. + this->discover_vertical_shells(); + m_print->throw_if_canceled(); + + // Debugging output. +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (const Layer *layer : m_layers) { + LayerRegion *layerm = layer->m_regions[region_id]; + layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final"); + layerm->export_region_fill_surfaces_to_svg_debug("6_discover_vertical_shells-final"); + } // for each layer + } // for each region +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + // Detect, which fill surfaces are near external layers. + // They will be split in internal and internal-solid surfaces. + // The purpose is to add a configurable number of solid layers to support the TOP surfaces + // and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces + // to close these surfaces reliably. + //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters? + this->discover_horizontal_shells(); + m_print->throw_if_canceled(); + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (const Layer *layer : m_layers) { + LayerRegion *layerm = layer->m_regions[region_id]; + layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final"); + layerm->export_region_fill_surfaces_to_svg_debug("7_discover_horizontal_shells-final"); + } // for each layer + } // for each region +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + // Only active if config->infill_only_where_needed. This step trims the sparse infill, + // so it acts as an internal support. It maintains all other infill types intact. + // Here the internal surfaces and perimeters have to be supported by the sparse infill. + //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support. + // Likely the sparse infill will not be anchored correctly, so it will not work as intended. + // Also one wishes the perimeters to be supported by a full infill. + this->clip_fill_surfaces(); + m_print->throw_if_canceled(); + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (const Layer *layer : m_layers) { + LayerRegion *layerm = layer->m_regions[region_id]; + layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final"); + layerm->export_region_fill_surfaces_to_svg_debug("8_clip_surfaces-final"); + } // for each layer + } // for each region +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + // the following step needs to be done before combination because it may need + // to remove only half of the combined infill + this->bridge_over_infill(); + m_print->throw_if_canceled(); + + // combine fill surfaces to honor the "infill every N layers" option + this->combine_infill(); + m_print->throw_if_canceled(); + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (const Layer *layer : m_layers) { + LayerRegion *layerm = layer->m_regions[region_id]; + layerm->export_region_slices_to_svg_debug("9_prepare_infill-final"); + layerm->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final"); + } // for each layer + } // for each region + for (const Layer *layer : m_layers) { + layer->export_region_slices_to_svg_debug("9_prepare_infill-final"); + layer->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final"); + } // for each layer +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + this->set_done(posPrepareInfill); +} + +void PrintObject::infill() +{ + // prerequisites + this->prepare_infill(); + + if (! this->is_step_done(posInfill)) { + this->set_started(posInfill); + BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; + tbb::parallel_for( + tbb::blocked_range(0, m_layers.size()), + [this](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + m_print->throw_if_canceled(); + m_layers[layer_idx]->make_fills(); + } + } + ); + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end"; + /* we could free memory now, but this would make this step not idempotent + ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; + */ + this->set_done(posInfill); + } +} + +void PrintObject::generate_support_material() +{ + if (! this->is_step_done(posSupportMaterial)) { + this->set_started(posSupportMaterial); + this->clear_support_layers(); + if ((m_config.support_material || m_config.raft_layers > 0) && m_layers.size() > 1) { + m_print->set_status(85, "Generating support material"); + this->_generate_support_material(); + m_print->throw_if_canceled(); + } + this->set_done(posSupportMaterial); } - return this->set_copies(copies); } void PrintObject::clear_layers() { - for (Layer *l : this->layers) + for (Layer *l : m_layers) delete l; - this->layers.clear(); + m_layers.clear(); } Layer* PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z) { - layers.push_back(new Layer(id, this, height, print_z, slice_z)); - return layers.back(); + m_layers.emplace_back(new Layer(id, this, height, print_z, slice_z)); + return m_layers.back(); } void PrintObject::clear_support_layers() { - for (Layer *l : this->support_layers) + for (Layer *l : m_support_layers) delete l; - this->support_layers.clear(); + m_support_layers.clear(); } SupportLayer* PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z) { - support_layers.emplace_back(new SupportLayer(id, this, height, print_z, -1)); - return support_layers.back(); + m_support_layers.emplace_back(new SupportLayer(id, this, height, print_z, -1)); + return m_support_layers.back(); +} + +SupportLayerPtrs::const_iterator PrintObject::insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z) +{ + return m_support_layers.insert(pos, new SupportLayer(id, this, height, print_z, slice_z)); } // Called by Print::apply_config(). @@ -246,8 +536,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorinvalidate_all_steps(); this->reset_layer_height_profile(); - this->invalidate_all_steps(); invalidated = true; } } @@ -260,141 +550,41 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorstate.invalidate(step); + bool invalidated = Inherited::invalidate_step(step); // propagate to dependent steps if (step == posPerimeters) { invalidated |= this->invalidate_step(posPrepareInfill); - invalidated |= this->_print->invalidate_step(psSkirt); - invalidated |= this->_print->invalidate_step(psBrim); + invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posPrepareInfill) { invalidated |= this->invalidate_step(posInfill); } else if (step == posInfill) { - invalidated |= this->_print->invalidate_step(psSkirt); - invalidated |= this->_print->invalidate_step(psBrim); + invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posSlice) { - invalidated |= this->invalidate_step(posPerimeters); - invalidated |= this->invalidate_step(posSupportMaterial); - invalidated |= this->_print->invalidate_step(psWipeTower); - } else if (step == posSupportMaterial) { - invalidated |= this->_print->invalidate_step(psSkirt); - invalidated |= this->_print->invalidate_step(psBrim); - } + invalidated |= this->invalidate_steps({ posPerimeters, posSupportMaterial }); + invalidated |= m_print->invalidate_step(psWipeTower); + } else if (step == posSupportMaterial) + invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); // Wipe tower depends on the ordering of extruders, which in turn depends on everything. // It also decides about what the wipe_into_infill / wipe_into_object features will do, // and that too depends on many of the settings. - invalidated |= this->_print->invalidate_step(psWipeTower); + invalidated |= m_print->invalidate_step(psWipeTower); + // Invalidate G-code export in any case. + invalidated |= m_print->invalidate_step(psGCodeExport); return invalidated; } +bool PrintObject::invalidate_all_steps() +{ + return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps(); +} + bool PrintObject::has_support_material() const { - return this->config.support_material - || this->config.raft_layers > 0 - || this->config.support_material_enforce_layers > 0; -} - -void PrintObject::_prepare_infill() -{ - if (!this->is_printable()) - return; - - // This will assign a type (top/bottom/internal) to $layerm->slices. - // Then the classifcation of $layerm->slices is transfered onto - // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces - // by the cummulative area of the previous $layerm->fill_surfaces. - this->detect_surfaces_type(); - - // Decide what surfaces are to be filled. - // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. - // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. - BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..."; - for (auto *layer : this->layers) - for (auto *region : layer->regions) - region->prepare_fill_surfaces(); - - // this will detect bridges and reverse bridges - // and rearrange top/bottom/internal surfaces - // It produces enlarged overlapping bridging areas. - // - // 1) S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap. - // 2) S_TYPE_TOP is grown by 3mm and clipped by the grown bottom areas. The areas may overlap. - // 3) Clip the internal surfaces by the grown top/bottom surfaces. - // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps. - //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties. - this->process_external_surfaces(); - - // Add solid fills to ensure the shell vertical thickness. - this->discover_vertical_shells(); - - // Debugging output. -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; - layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final"); - layerm->export_region_fill_surfaces_to_svg_debug("6_discover_vertical_shells-final"); - } // for each layer - } // for each region -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - - // Detect, which fill surfaces are near external layers. - // They will be split in internal and internal-solid surfaces. - // The purpose is to add a configurable number of solid layers to support the TOP surfaces - // and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces - // to close these surfaces reliably. - //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters? - this->discover_horizontal_shells(); - -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; - layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final"); - layerm->export_region_fill_surfaces_to_svg_debug("7_discover_horizontal_shells-final"); - } // for each layer - } // for each region -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - - // Only active if config->infill_only_where_needed. This step trims the sparse infill, - // so it acts as an internal support. It maintains all other infill types intact. - // Here the internal surfaces and perimeters have to be supported by the sparse infill. - //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support. - // Likely the sparse infill will not be anchored correctly, so it will not work as intended. - // Also one wishes the perimeters to be supported by a full infill. - this->clip_fill_surfaces(); - -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; - layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final"); - layerm->export_region_fill_surfaces_to_svg_debug("8_clip_surfaces-final"); - } // for each layer - } // for each region -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - - // the following step needs to be done before combination because it may need - // to remove only half of the combined infill - this->bridge_over_infill(); - - // combine fill surfaces to honor the "infill every N layers" option - this->combine_infill(); - -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; - layerm->export_region_slices_to_svg_debug("9_prepare_infill-final"); - layerm->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final"); - } // for each layer - } // for each region - for (const Layer *layer : this->layers) { - layer->export_region_slices_to_svg_debug("9_prepare_infill-final"); - layer->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final"); - } // for each layer -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + return m_config.support_material + || m_config.raft_layers > 0 + || m_config.support_material_enforce_layers > 0; } // This function analyzes slices of a region (SurfaceCollection slices). @@ -415,41 +605,42 @@ void PrintObject::detect_surfaces_type() // are completely hidden inside a collective body of intersecting parts. // This is useful if one of the parts is to be dissolved, or if it is transparent and the internal shells // should be visible. - bool interface_shells = this->config.interface_shells.value; + bool interface_shells = m_config.interface_shells.value; - for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { + for (int idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (Layer *layer : this->layers) - layer->regions[idx_region]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial"); + for (Layer *layer : m_layers) + layer->m_regions[idx_region]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // If interface shells are allowed, the region->surfaces cannot be overwritten as they may be used by other threads. // Cache the result of the following parallel_loop. std::vector surfaces_new; if (interface_shells) - surfaces_new.assign(this->layers.size(), Surfaces()); + surfaces_new.assign(m_layers.size(), Surfaces()); tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { // If we have raft layers, consider bottom layer as a bridge just like any other bottom surface lying on the void. SurfaceType surface_type_bottom_1st = - (this->config.raft_layers.value > 0 && this->config.support_material_contact_distance.value > 0) ? + (m_config.raft_layers.value > 0 && m_config.support_material_contact_distance.value > 0) ? stBottomBridge : stBottom; // If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating // the support from the print. SurfaceType surface_type_bottom_other = - (this->config.support_material.value && this->config.support_material_contact_distance.value == 0) ? + (m_config.support_material.value && m_config.support_material_contact_distance.value == 0) ? stBottom : stBottomBridge; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { + m_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z; - Layer *layer = this->layers[idx_layer]; + Layer *layer = m_layers[idx_layer]; LayerRegion *layerm = layer->get_region(idx_region); // comparison happens against the *full* slices (considering all regions) // unless internal shells are requested - Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? this->layers[idx_layer + 1] : nullptr; - Layer *lower_layer = (idx_layer > 0) ? this->layers[idx_layer - 1] : nullptr; + Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr; + Layer *lower_layer = (idx_layer > 0) ? m_layers[idx_layer - 1] : nullptr; // collapse very narrow parts (using the safety offset in the diff is not enough) float offset = layerm->flow(frExternalPerimeter).scaled_width() / 10.f; @@ -567,26 +758,29 @@ void PrintObject::detect_surfaces_type() } } ); // for each layer of a region + m_print->throw_if_canceled(); if (interface_shells) { // Move surfaces_new to layerm->slices.surfaces - for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++ idx_layer) - this->layers[idx_layer]->get_region(idx_region)->slices.surfaces = std::move(surfaces_new[idx_layer]); + for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) + m_layers[idx_layer]->get_region(idx_region)->slices.surfaces = std::move(surfaces_new[idx_layer]); } BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start"; // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region); + m_print->throw_if_canceled(); + LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region); layerm->slices_to_fill_surfaces_clipped(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } // for each layer of a region }); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - end"; } // for each this->print->region_count @@ -598,19 +792,21 @@ void PrintObject::process_external_surfaces() { BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..."; - FOREACH_REGION(this->_print, region) { - int region_id = int(region - this->_print->regions.begin()); + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) { + const PrintRegion ®ion = *m_print->regions()[region_id]; BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << this->layers[layer_idx]->print_z; - this->layers[layer_idx]->get_region(region_id)->process_external_surfaces((layer_idx == 0) ? NULL : this->layers[layer_idx - 1]); + m_print->throw_if_canceled(); + // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z; + m_layers[layer_idx]->get_region(region_id)->process_external_surfaces((layer_idx == 0) ? NULL : m_layers[layer_idx - 1]); } } ); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - end"; } } @@ -628,17 +824,17 @@ void PrintObject::discover_vertical_shells() Polygons bottom_surfaces; Polygons holes; }; - std::vector cache_top_botom_regions(this->layers.size(), DiscoverVerticalShellsCacheEntry()); - bool top_bottom_surfaces_all_regions = this->_print->regions.size() > 1 && ! this->config.interface_shells.value; + std::vector cache_top_botom_regions(m_layers.size(), DiscoverVerticalShellsCacheEntry()); + bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value; if (top_bottom_surfaces_all_regions) { // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness // is calculated over all materials. // Is the "ensure vertical wall thickness" applicable to any region? bool has_extra_layers = false; - for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { - const PrintRegion ®ion = *this->_print->get_region(idx_region); - if (region.config.ensure_vertical_shell_thickness.value && - (region.config.top_solid_layers.value > 1 || region.config.bottom_solid_layers.value > 1)) { + for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { + const PrintRegion ®ion = *m_print->get_region(idx_region); + if (region.config().ensure_vertical_shell_thickness.value && + (region.config().top_solid_layers.value > 1 || region.config().bottom_solid_layers.value > 1)) { has_extra_layers = true; } } @@ -647,14 +843,15 @@ void PrintObject::discover_vertical_shells() return; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom"; //FIXME Improve the heuristics for a grain size. - size_t grain_size = std::max(this->layers.size() / 16, size_t(1)); + size_t grain_size = std::max(m_layers.size() / 16, size_t(1)); tbb::parallel_for( - tbb::blocked_range(0, this->layers.size(), grain_size), + tbb::blocked_range(0, m_layers.size(), grain_size), [this, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; - const size_t num_regions = this->_print->regions.size(); + const size_t num_regions = this->region_volumes.size(); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - const Layer &layer = *this->layers[idx_layer]; + m_print->throw_if_canceled(); + const Layer &layer = *m_layers[idx_layer]; DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[idx_layer]; // Simulate single set of perimeters over all merged regions. float perimeter_offset = 0.f; @@ -664,7 +861,7 @@ void PrintObject::discover_vertical_shells() ++ debug_idx; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ for (size_t idx_region = 0; idx_region < num_regions; ++ idx_region) { - LayerRegion &layerm = *layer.regions[idx_region]; + LayerRegion &layerm = *layer.m_regions[idx_region]; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; // Top surfaces. append(cache.top_surfaces, offset(to_expolygons(layerm.slices.filter_by_type(stTop)), min_perimeter_infill_spacing)); @@ -677,7 +874,7 @@ void PrintObject::discover_vertical_shells() unsigned int perimeters = 0; for (Surface &s : layerm.slices.surfaces) perimeters = std::max(perimeters, s.extra_perimeters); - perimeters += layerm.region()->config.perimeters.value; + perimeters += layerm.region()->config().perimeters.value; // Then calculate the infill offset. if (perimeters > 0) { Flow extflow = layerm.flow(frExternalPerimeter); @@ -708,36 +905,38 @@ void PrintObject::discover_vertical_shells() cache.holes = union_(cache.holes, false); } }); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; } - for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { + for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { PROFILE_BLOCK(discover_vertical_shells_region); - const PrintRegion ®ion = *this->_print->get_region(idx_region); - if (! region.config.ensure_vertical_shell_thickness.value) + const PrintRegion ®ion = *m_print->get_region(idx_region); + if (! region.config().ensure_vertical_shell_thickness.value) // This region will be handled by discover_horizontal_shells(). continue; - int n_extra_top_layers = std::max(0, region.config.top_solid_layers.value - 1); - int n_extra_bottom_layers = std::max(0, region.config.bottom_solid_layers.value - 1); + int n_extra_top_layers = std::max(0, region.config().top_solid_layers.value - 1); + int n_extra_bottom_layers = std::max(0, region.config().bottom_solid_layers.value - 1); if (n_extra_top_layers + n_extra_bottom_layers == 0) // Zero or 1 layer, there is no additional vertical wall thickness enforced. continue; //FIXME Improve the heuristics for a grain size. - size_t grain_size = std::max(this->layers.size() / 16, size_t(1)); + size_t grain_size = std::max(m_layers.size() / 16, size_t(1)); if (! top_bottom_surfaces_all_regions) { // This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness // is calculated over a single material. BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : cache top / bottom"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size(), grain_size), + tbb::blocked_range(0, m_layers.size(), grain_size), [this, idx_region, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - Layer &layer = *this->layers[idx_layer]; - LayerRegion &layerm = *layer.regions[idx_region]; + m_print->throw_if_canceled(); + Layer &layer = *m_layers[idx_layer]; + LayerRegion &layerm = *layer.m_regions[idx_region]; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; // Top surfaces. auto &cache = cache_top_botom_regions[idx_layer]; @@ -748,30 +947,31 @@ void PrintObject::discover_vertical_shells() append(cache.bottom_surfaces, offset(to_expolygons(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing)); // Holes over all regions. Only collect them once, they are valid for all idx_region iterations. if (cache.holes.empty()) { - for (size_t idx_region = 0; idx_region < layer.regions.size(); ++ idx_region) - polygons_append(cache.holes, to_polygons(layer.regions[idx_region]->fill_expolygons)); + for (size_t idx_region = 0; idx_region < layer.regions().size(); ++ idx_region) + polygons_append(cache.holes, to_polygons(layer.regions()[idx_region]->fill_expolygons)); } } }); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end : cache top / bottom"; } BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size(), grain_size), + tbb::blocked_range(0, m_layers.size(), grain_size), [this, idx_region, n_extra_top_layers, n_extra_bottom_layers, &cache_top_botom_regions] (const tbb::blocked_range& range) { // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { PROFILE_BLOCK(discover_vertical_shells_region_layer); - + m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING static size_t debug_idx = 0; ++ debug_idx; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - Layer *layer = this->layers[idx_layer]; - LayerRegion *layerm = layer->regions[idx_region]; + Layer *layer = m_layers[idx_layer]; + LayerRegion *layerm = layer->m_regions[idx_region]; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial"); @@ -794,9 +994,9 @@ void PrintObject::discover_vertical_shells() { Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box()); for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) { - if (n < 0 || n >= (int)this->layers.size()) + if (n < 0 || n >= (int)m_layers.size()) continue; - ExPolygons &expolys = this->layers[n]->perimeter_expolygons; + ExPolygons &expolys = m_layers[n]->perimeter_expolygons; for (size_t i = 0; i < expolys.size(); ++ i) { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", debug_idx, n, i), get_extents(expolys[i])); svg.draw(expolys[i]); @@ -814,8 +1014,8 @@ void PrintObject::discover_vertical_shells() // Reset the top / bottom inflated regions caches of entries, which are out of the moving window. bool hole_first = true; for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) - if (n >= 0 && n < (int)this->layers.size()) { - Layer &neighbor_layer = *this->layers[n]; + if (n >= 0 && n < (int)m_layers.size()) { + Layer &neighbor_layer = *m_layers[n]; const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[n]; if (hole_first) { hole_first = false; @@ -915,7 +1115,7 @@ void PrintObject::discover_vertical_shells() #if 1 // Intentionally inflate a bit more than how much the region has been shrunk, // so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill). - shell = offset(offset_ex(union_ex(shell), - 0.5f * min_perimeter_infill_spacing), 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); + shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); if (shell.empty()) continue; #else @@ -977,12 +1177,13 @@ void PrintObject::discover_vertical_shells() layerm->fill_surfaces.append(new_internal_void, stInternalVoid); layerm->fill_surfaces.append(new_internal_solid, stInternalSolid); } // for each layer - }); + }); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++idx_layer) { - LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region); + for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++idx_layer) { + LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region); layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final"); layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final"); } @@ -1000,14 +1201,14 @@ void PrintObject::bridge_over_infill() { BOOST_LOG_TRIVIAL(info) << "Bridge over infill..."; - FOREACH_REGION(this->_print, region) { - size_t region_id = region - this->_print->regions.begin(); + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + const PrintRegion ®ion = *m_print->regions()[region_id]; // skip bridging in case there are no voids - if ((*region)->config.fill_density.value == 100) continue; + if (region.config().fill_density.value == 100) continue; // get bridge flow - Flow bridge_flow = (*region)->flow( + Flow bridge_flow = region.flow( frSolidInfill, -1, // layer height, not relevant for bridge flow true, // bridge @@ -1016,12 +1217,13 @@ void PrintObject::bridge_over_infill() *this ); - FOREACH_LAYER(this, layer_it) { + for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) { // skip first layer - if (layer_it == this->layers.begin()) continue; + if (layer_it == m_layers.begin()) + continue; Layer* layer = *layer_it; - LayerRegion* layerm = layer->regions[region_id]; + LayerRegion* layerm = layer->m_regions[region_id]; // extract the stInternalSolid surfaces that might be transformed into bridges Polygons internal_solid; @@ -1036,16 +1238,16 @@ void PrintObject::bridge_over_infill() // iterate through lower layers spanned by bridge_flow double bottom_z = layer->print_z - bridge_flow.height; - for (int i = int(layer_it - this->layers.begin()) - 1; i >= 0; --i) { - const Layer* lower_layer = this->layers[i]; + for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { + const Layer* lower_layer = m_layers[i]; // stop iterating if layer is lower than bottom_z if (lower_layer->print_z < bottom_z) break; // iterate through regions and collect internal surfaces Polygons lower_internal; - FOREACH_LAYERREGION(lower_layer, lower_layerm_it) - (*lower_layerm_it)->fill_surfaces.filter_by_type(stInternal, &lower_internal); + for (LayerRegion *lower_layerm : lower_layer->m_regions) + lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal); // intersect such lower internal surfaces with the candidate solid surfaces to_bridge_pp = intersection(to_bridge_pp, lower_internal); @@ -1117,6 +1319,7 @@ void PrintObject::bridge_over_infill() layerm->export_region_slices_to_svg_debug("7_bridge_over_infill"); layerm->export_region_fill_surfaces_to_svg_debug("7_bridge_over_infill"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + m_print->throw_if_canceled(); } } } @@ -1124,8 +1327,8 @@ void PrintObject::bridge_over_infill() SlicingParameters PrintObject::slicing_parameters() const { return SlicingParameters::create_from_config( - this->print()->config, this->config, - unscale(this->size.z), this->print()->object_extruders()); + this->print()->config(), m_config, + unscale(this->size(2)), this->print()->object_extruders()); } bool PrintObject::update_layer_height_profile(std::vector &layer_height_profile) const @@ -1215,36 +1418,39 @@ void PrintObject::_slice() layer->lower_layer = prev; } // Make sure all layers contain layer region objects for all regions. - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) - layer->add_region(this->print()->regions[region_id]); + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) + layer->add_region(this->print()->regions()[region_id]); prev = layer; } } // Slice all non-modifier volumes. - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, false); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start"; for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) - this->layers[layer_id]->regions[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); + m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " end"; } // Slice all modifier volumes. - if (this->print()->regions.size() > 1) { - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + if (this->region_volumes.size() > 1) { + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, true); + m_print->throw_if_canceled(); // loop through the other regions and 'steal' the slices belonging to this one BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start"; - for (size_t other_region_id = 0; other_region_id < this->print()->regions.size(); ++ other_region_id) { + for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) { if (region_id == other_region_id) continue; for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) { - Layer *layer = layers[layer_id]; - LayerRegion *layerm = layer->regions[region_id]; - LayerRegion *other_layerm = layer->regions[other_region_id]; + Layer *layer = m_layers[layer_id]; + LayerRegion *layerm = layer->m_regions[region_id]; + LayerRegion *other_layerm = layer->m_regions[other_region_id]; if (layerm == nullptr || other_layerm == nullptr) continue; Polygons other_slices = to_polygons(other_layerm->slices); @@ -1257,56 +1463,59 @@ void PrintObject::_slice() layerm->slices.append(std::move(my_parts), stInternal); } } + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " end"; } } BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers"; - while (! this->layers.empty()) { - const Layer *layer = this->layers.back(); - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) - if (layer->regions[region_id] != nullptr && ! layer->regions[region_id]->slices.empty()) + while (! m_layers.empty()) { + const Layer *layer = m_layers.back(); + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) + if (layer->m_regions[region_id] != nullptr && ! layer->m_regions[region_id]->slices.empty()) // Non empty layer. goto end; delete layer; - this->layers.pop_back(); - if (! this->layers.empty()) - this->layers.back()->upper_layer = nullptr; + m_layers.pop_back(); + if (! m_layers.empty()) + m_layers.back()->upper_layer = nullptr; } + m_print->throw_if_canceled(); end: ; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - Layer *layer = this->layers[layer_id]; + m_print->throw_if_canceled(); + Layer *layer = m_layers[layer_id]; // Apply size compensation and perform clipping of multi-part objects. - float delta = float(scale_(this->config.xy_size_compensation.value)); + float delta = float(scale_(m_config.xy_size_compensation.value)); if (layer_id == 0) - delta -= float(scale_(this->config.elefant_foot_compensation.value)); + delta -= float(scale_(m_config.elefant_foot_compensation.value)); bool scale = delta != 0.f; - bool clip = this->config.clip_multipart_objects.value || delta > 0.f; - if (layer->regions.size() == 1) { + bool clip = m_config.clip_multipart_objects.value || delta > 0.f; + if (layer->m_regions.size() == 1) { if (scale) { // Single region, growing or shrinking. - LayerRegion *layerm = layer->regions.front(); + LayerRegion *layerm = layer->m_regions.front(); layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stInternal); } } else if (scale || clip) { // Multiple regions, growing, shrinking or just clipping one region by the other. // When clipping the regions, priority is given to the first regions. Polygons processed; - for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) { - LayerRegion *layerm = layer->regions[region_id]; + for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) { + LayerRegion *layerm = layer->m_regions[region_id]; ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces)); if (scale) slices = offset_ex(slices, delta); if (region_id > 0 && clip) // Trim by the slices of already processed regions. slices = diff_ex(to_polygons(std::move(slices)), processed); - if (clip && region_id + 1 < layer->regions.size()) + if (clip && region_id + 1 < layer->m_regions.size()) // Collect the already processed regions to trim the to be processed regions. polygons_append(processed, slices); layerm->slices.set(std::move(slices), stInternal); @@ -1316,6 +1525,7 @@ end: layer->make_slices(); } }); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end"; } @@ -1339,9 +1549,9 @@ std::vector PrintObject::slice_support_enforcers() const if (volume->is_support_enforcer()) volumes.emplace_back(volume); std::vector zs; - zs.reserve(this->layers.size()); - for (const Layer *l : this->layers) - zs.emplace_back(l->slice_z); + zs.reserve(this->layers().size()); + for (const Layer *l : this->layers()) + zs.emplace_back((float)l->slice_z); return this->_slice_volumes(zs, volumes); } @@ -1352,9 +1562,9 @@ std::vector PrintObject::slice_support_blockers() const if (volume->is_support_blocker()) volumes.emplace_back(volume); std::vector zs; - zs.reserve(this->layers.size()); - for (const Layer *l : this->layers) - zs.emplace_back(l->slice_z); + zs.reserve(this->layers().size()); + for (const Layer *l : this->layers()) + zs.emplace_back((float)l->slice_z); return this->_slice_volumes(zs, volumes); } @@ -1366,17 +1576,26 @@ std::vector PrintObject::_slice_volumes(const std::vector &z, //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. TriangleMesh mesh; for (const ModelVolume *v : volumes) - mesh.merge(v->mesh); +#if ENABLE_MODELVOLUME_TRANSFORM + { + TriangleMesh vol_mesh(v->mesh); + vol_mesh.transform(v->get_matrix()); + mesh.merge(vol_mesh); + } +#else + mesh.merge(v->mesh); +#endif // ENABLE_MODELVOLUME_TRANSFORM if (mesh.stl.stats.number_of_facets > 0) { - // transform mesh - // we ignore the per-instance transformations currently and only - // consider the first one - this->model_object()->instances.front()->transform_mesh(&mesh, true); - // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift - mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z)); + mesh.transform(m_trafo); + // apply XY shift + mesh.translate(- unscale(m_copies_shift(0)), - unscale(m_copies_shift(1)), 0); // perform actual slicing - TriangleMeshSlicer mslicer(&mesh); - mslicer.slice(z, &layers); + TriangleMeshSlicer mslicer; + const Print *print = this->print(); + auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); + mslicer.init(&mesh, callback); + mslicer.slice(z, &layers, callback); + m_print->throw_if_canceled(); } } return layers; @@ -1387,9 +1606,9 @@ std::string PrintObject::_fix_slicing_errors() // Collect layers with slicing errors. // These layers will be fixed in parallel. std::vector buggy_layers; - buggy_layers.reserve(this->layers.size()); - for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++ idx_layer) - if (this->layers[idx_layer]->slicing_errors) + buggy_layers.reserve(m_layers.size()); + for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) + if (m_layers[idx_layer]->slicing_errors) buggy_layers.push_back(idx_layer); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - begin"; @@ -1397,24 +1616,25 @@ std::string PrintObject::_fix_slicing_errors() tbb::blocked_range(0, buggy_layers.size()), [this, &buggy_layers](const tbb::blocked_range& range) { for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) { + m_print->throw_if_canceled(); size_t idx_layer = buggy_layers[buggy_layer_idx]; - Layer *layer = this->layers[idx_layer]; + Layer *layer = m_layers[idx_layer]; assert(layer->slicing_errors); // Try to repair the layer surfaces by merging all contours and all holes from neighbor layers. // BOOST_LOG_TRIVIAL(trace) << "Attempting to repair layer" << idx_layer; - for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) { - LayerRegion *layerm = layer->regions[region_id]; + for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) { + LayerRegion *layerm = layer->m_regions[region_id]; // Find the first valid layer below / above the current layer. const Surfaces *upper_surfaces = nullptr; const Surfaces *lower_surfaces = nullptr; - for (size_t j = idx_layer + 1; j < this->layers.size(); ++ j) - if (! this->layers[j]->slicing_errors) { - upper_surfaces = &this->layers[j]->regions[region_id]->slices.surfaces; + for (size_t j = idx_layer + 1; j < m_layers.size(); ++ j) + if (! m_layers[j]->slicing_errors) { + upper_surfaces = &m_layers[j]->regions()[region_id]->slices.surfaces; break; } for (int j = int(idx_layer) - 1; j >= 0; -- j) - if (! this->layers[j]->slicing_errors) { - lower_surfaces = &this->layers[j]->regions[region_id]->slices.surfaces; + if (! m_layers[j]->slicing_errors) { + lower_surfaces = &m_layers[j]->regions()[region_id]->slices.surfaces; break; } // Collect outer contours and holes from the valid layers above & below. @@ -1447,15 +1667,16 @@ std::string PrintObject::_fix_slicing_errors() layer->make_slices(); } }); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end"; // remove empty layers from bottom - while (! this->layers.empty() && this->layers.front()->slices.expolygons.empty()) { - delete this->layers.front(); - this->layers.erase(this->layers.begin()); - this->layers.front()->lower_layer = nullptr; - for (size_t i = 0; i < this->layers.size(); ++ i) - this->layers[i]->set_id(this->layers[i]->id() - 1); + while (! m_layers.empty() && m_layers.front()->slices.expolygons.empty()) { + delete m_layers.front(); + m_layers.erase(m_layers.begin()); + m_layers.front()->lower_layer = nullptr; + for (size_t i = 0; i < m_layers.size(); ++ i) + m_layers[i]->set_id(m_layers[i]->id() - 1); } return buggy_layers.empty() ? "" : @@ -1470,12 +1691,13 @@ void PrintObject::_simplify_slices(double distance) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - begin"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this, distance](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - Layer *layer = this->layers[layer_idx]; - for (size_t region_idx = 0; region_idx < layer->regions.size(); ++ region_idx) - layer->regions[region_idx]->slices.simplify(distance); + m_print->throw_if_canceled(); + Layer *layer = m_layers[layer_idx]; + for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++ region_idx) + layer->m_regions[region_idx]->slices.simplify(distance); layer->slices.simplify(distance); } }); @@ -1484,20 +1706,18 @@ void PrintObject::_simplify_slices(double distance) void PrintObject::_make_perimeters() { - if (!this->is_printable()) + if (this->is_step_done(posPerimeters)) return; - - if (this->state.is_done(posPerimeters)) return; - this->state.set_started(posPerimeters); + this->set_started(posPerimeters); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; // merge slices if they were split into types if (this->typed_slices) { - FOREACH_LAYER(this, layer_it) - (*layer_it)->merge_slices(); + for (Layer *layer : m_layers) + layer->merge_slices(); this->typed_slices = false; - this->state.invalidate(posPrepareInfill); + this->invalidate_step(posPrepareInfill); } // compare each layer to the one below, and mark those slices needing @@ -1507,23 +1727,18 @@ void PrintObject::_make_perimeters() // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing // hollow objects - FOREACH_REGION(this->_print, region_it) { - size_t region_id = region_it - this->_print->regions.begin(); - const PrintRegion ®ion = **region_it; - - if (!region.config.extra_perimeters - || region.config.perimeters == 0 - || region.config.fill_density == 0 - || this->layer_count() < 2) + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + const PrintRegion ®ion = *m_print->regions()[region_id]; + if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) continue; BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size() - 1), + tbb::blocked_range(0, m_layers.size() - 1), [this, ®ion, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - LayerRegion &layerm = *this->layers[layer_idx]->regions[region_id]; - const LayerRegion &upper_layerm = *this->layers[layer_idx+1]->regions[region_id]; + LayerRegion &layerm = *m_layers[layer_idx]->regions()[region_id]; + const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->regions()[region_id]; const Polygons upper_layerm_polygons = upper_layerm.slices; // Filter upper layer polygons in intersection_ppl by their bounding boxes? // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; @@ -1537,7 +1752,7 @@ void PrintObject::_make_perimeters() for (;;) { // compute the total thickness of perimeters const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 - + (region.config.perimeters-1 + slice.extra_perimeters) * perimeter_spacing; + + (region.config().perimeters-1 + slice.extra_perimeters) * perimeter_spacing; // define a critical area where we don't want the upper slice to fall into // (it should either lay over our perimeters or outside this area) const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5); @@ -1575,10 +1790,10 @@ void PrintObject::_make_perimeters() BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) - this->layers[layer_idx]->make_perimeters(); + m_layers[layer_idx]->make_perimeters(); } ); BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; @@ -1590,32 +1805,7 @@ void PrintObject::_make_perimeters() ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); */ - this->state.set_done(posPerimeters); -} - -void PrintObject::_infill() -{ - if (!this->is_printable()) - return; - - if (this->state.is_done(posInfill)) return; - this->state.set_started(posInfill); - - BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; - tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), - [this](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) - this->layers[layer_idx]->make_fills(); - } - ); - BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end"; - - /* we could free memory now, but this would make this step not idempotent - ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; - */ - - this->state.set_done(posInfill); + this->set_done(posPerimeters); } // Only active if config->infill_only_where_needed. This step trims the sparse infill, @@ -1628,18 +1818,18 @@ void PrintObject::_infill() // fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. void PrintObject::clip_fill_surfaces() { - if (! this->config.infill_only_where_needed.value || - ! std::any_of(this->print()->regions.begin(), this->print()->regions.end(), - [](const PrintRegion *region) { return region->config.fill_density > 0; })) + if (! m_config.infill_only_where_needed.value || + ! std::any_of(this->print()->regions().begin(), this->print()->regions().end(), + [](const PrintRegion *region) { return region->config().fill_density > 0; })) return; // We only want infill under ceilings; this is almost like an // internal support material. // Proceed top-down, skipping the bottom layer. Polygons upper_internal; - for (int layer_id = int(this->layers.size()) - 1; layer_id > 0; -- layer_id) { - Layer *layer = this->layers[layer_id]; - Layer *lower_layer = this->layers[layer_id - 1]; + for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) { + Layer *layer = m_layers[layer_id]; + Layer *lower_layer = m_layers[layer_id - 1]; // Detect things that we need to support. // Cummulative slices. Polygons slices; @@ -1649,7 +1839,7 @@ void PrintObject::clip_fill_surfaces() Polygons fill_surfaces; // Solid surfaces to be supported. Polygons overhangs; - for (const LayerRegion *layerm : layer->regions) + for (const LayerRegion *layerm : layer->m_regions) for (const Surface &surface : layerm->fill_surfaces.surfaces) { Polygons polygons = to_polygons(surface.expolygon); if (surface.is_solid()) @@ -1658,7 +1848,7 @@ void PrintObject::clip_fill_surfaces() } Polygons lower_layer_fill_surfaces; Polygons lower_layer_internal_surfaces; - for (const LayerRegion *layerm : lower_layer->regions) + for (const LayerRegion *layerm : lower_layer->m_regions) for (const Surface &surface : layerm->fill_surfaces.surfaces) { Polygons polygons = to_polygons(surface.expolygon); if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) @@ -1674,7 +1864,7 @@ void PrintObject::clip_fill_surfaces() //FIXME Offset2 eats out from both sides, while the perimeters are create outside in. //Should the pw not be half of the current value? float pw = FLT_MAX; - for (const LayerRegion *layerm : layer->regions) + for (const LayerRegion *layerm : layer->m_regions) pw = std::min(pw, layerm->flow(frPerimeter).scaled_width()); // Append such thick perimeters to the areas that need support polygons_append(overhangs, offset2(perimeters, -pw, +pw)); @@ -1683,8 +1873,8 @@ void PrintObject::clip_fill_surfaces() polygons_append(overhangs, std::move(upper_internal)); upper_internal = intersection(overhangs, lower_layer_internal_surfaces); // Apply new internal infill to regions. - for (LayerRegion *layerm : lower_layer->regions) { - if (layerm->region()->config.fill_density.value == 0) + for (LayerRegion *layerm : lower_layer->m_regions) { + if (layerm->region()->config().fill_density.value == 0) continue; SurfaceType internal_surface_types[] = { stInternal, stInternalVoid }; Polygons internal; @@ -1701,6 +1891,7 @@ void PrintObject::clip_fill_surfaces() layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces"); #endif } + m_print->throw_if_canceled(); } } @@ -1708,10 +1899,11 @@ void PrintObject::discover_horizontal_shells() { BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (int i = 0; i < int(this->layers.size()); ++ i) { - LayerRegion *layerm = this->layers[i]->regions[region_id]; - PrintRegionConfig ®ion_config = layerm->region()->config; + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (int i = 0; i < int(m_layers.size()); ++ i) { + m_print->throw_if_canceled(); + LayerRegion *layerm = m_layers[i]->regions()[region_id]; + const PrintRegionConfig ®ion_config = layerm->region()->config(); if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 && (i % region_config.solid_infill_every_layers) == 0) { // Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge. @@ -1726,6 +1918,7 @@ void PrintObject::discover_horizontal_shells() continue; for (int idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { + m_print->throw_if_canceled(); SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge; // Find slices of current type for current layer. // Use slices instead of fill_surfaces, because they also include the perimeter area, @@ -1754,11 +1947,11 @@ void PrintObject::discover_horizontal_shells() size_t solid_layers = (type == stTop) ? region_config.top_solid_layers.value : region_config.bottom_solid_layers.value; for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - i) < solid_layers; (type == stTop) ? -- n : ++ n) { - if (n < 0 || n >= int(this->layers.size())) + if (n < 0 || n >= int(m_layers.size())) continue; // Slic3r::debugf " looking for neighbors on layer %d...\n", $n; // Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface. - LayerRegion *neighbor_layerm = this->layers[n]->regions[region_id]; + LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id]; // find intersection between neighbor and current layer's surfaces // intersections have contours and holes @@ -1880,9 +2073,9 @@ void PrintObject::discover_horizontal_shells() } // for each region #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - const LayerRegion *layerm = layer->regions[region_id]; + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (const Layer *layer : m_layers) { + const LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells"); layerm->export_region_fill_surfaces_to_svg_debug("5_discover_horizontal_shells"); } // for each layer @@ -1896,23 +2089,24 @@ void PrintObject::discover_horizontal_shells() void PrintObject::combine_infill() { // Work on each region separately. - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - const PrintRegion *region = this->print()->regions[region_id]; - const int every = region->config.infill_every_layers.value; - if (every < 2 || region->config.fill_density == 0.) + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + const PrintRegion *region = this->print()->regions()[region_id]; + const int every = region->config().infill_every_layers.value; + if (every < 2 || region->config().fill_density == 0.) continue; // Limit the number of combined layers to the maximum height allowed by this regions' nozzle. //FIXME limit the layer height to max_layer_height double nozzle_diameter = std::min( - this->print()->config.nozzle_diameter.get_at(region->config.infill_extruder.value - 1), - this->print()->config.nozzle_diameter.get_at(region->config.solid_infill_extruder.value - 1)); + this->print()->config().nozzle_diameter.get_at(region->config().infill_extruder.value - 1), + this->print()->config().nozzle_diameter.get_at(region->config().solid_infill_extruder.value - 1)); // define the combinations - std::vector combine(this->layers.size(), 0); + std::vector combine(m_layers.size(), 0); { double current_height = 0.; size_t num_layers = 0; - for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { - const Layer *layer = this->layers[layer_idx]; + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { + m_print->throw_if_canceled(); + const Layer *layer = m_layers[layer_idx]; if (layer->id() == 0) // Skip first print layer (which may not be first layer in array because of raft). continue; @@ -1929,11 +2123,12 @@ void PrintObject::combine_infill() } // Append lower layers (if any) to uppermost layer. - combine[this->layers.size() - 1] = num_layers; + combine[m_layers.size() - 1] = num_layers; } // loop through layers to which we have assigned layers to combine - for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { + m_print->throw_if_canceled(); size_t num_layers = combine[layer_idx]; if (num_layers <= 1) continue; @@ -1941,7 +2136,7 @@ void PrintObject::combine_infill() std::vector layerms; layerms.reserve(num_layers); for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++ i) - layerms.emplace_back(this->layers[i]->regions[region_id]); + layerms.emplace_back(m_layers[i]->regions()[region_id]); // We need to perform a multi-layer intersection, so let's split it in pairs. // Initialize the intersection with the candidates of the lowest layer. ExPolygons intersection = to_expolygons(layerms.front()->fill_surfaces.filter_by_type(stInternal)); @@ -1970,10 +2165,10 @@ void PrintObject::combine_infill() 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. - ((region->config.fill_pattern == ipRectilinear || - region->config.fill_pattern == ipGrid || - region->config.fill_pattern == ipLine || - region->config.fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * + ((region->config().fill_pattern == ipRectilinear || + region->config().fill_pattern == ipGrid || + region->config().fill_pattern == ipLine || + region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * layerms.back()->flow(frSolidInfill).scaled_width(); for (ExPolygon &expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); @@ -2002,9 +2197,6 @@ void PrintObject::combine_infill() void PrintObject::_generate_support_material() { - if (!this->is_printable()) - return; - PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters()); support_material.generate(*this); } @@ -2021,9 +2213,9 @@ void PrintObject::reset_layer_height_profile() void PrintObject::adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) { - update_layer_height_profile(_model_object->layer_height_profile); - Slic3r::adjust_layer_height_profile(slicing_parameters(), _model_object->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); - _model_object->layer_height_profile_valid = true; + update_layer_height_profile(m_model_object->layer_height_profile); + Slic3r::adjust_layer_height_profile(slicing_parameters(), m_model_object->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); + m_model_object->layer_height_profile_valid = true; layer_height_profile_valid = false; } diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp new file mode 100644 index 0000000000..4ea777b4a4 --- /dev/null +++ b/src/libslic3r/PrintRegion.cpp @@ -0,0 +1,64 @@ +#include "Print.hpp" + +namespace Slic3r { + +Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const +{ + ConfigOptionFloatOrPercent config_width; + if (width != -1) { + // use the supplied custom width, if any + config_width.value = width; + config_width.percent = false; + } else { + // otherwise, get extrusion width from configuration + // (might be an absolute value, or a percent value, or zero for auto) + if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) { + config_width = m_print->config().first_layer_extrusion_width; + } else if (role == frExternalPerimeter) { + config_width = m_config.external_perimeter_extrusion_width; + } else if (role == frPerimeter) { + config_width = m_config.perimeter_extrusion_width; + } else if (role == frInfill) { + config_width = m_config.infill_extrusion_width; + } else if (role == frSolidInfill) { + config_width = m_config.solid_infill_extrusion_width; + } else if (role == frTopSolidInfill) { + config_width = m_config.top_infill_extrusion_width; + } else { + throw std::invalid_argument("Unknown role"); + } + } + if (config_width.value == 0) { + config_width = object.config().extrusion_width; + } + + // get the configured nozzle_diameter for the extruder associated + // to the flow role requested + size_t extruder = 0; // 1-based + if (role == frPerimeter || role == frExternalPerimeter) { + extruder = m_config.perimeter_extruder; + } else if (role == frInfill) { + extruder = m_config.infill_extruder; + } else if (role == frSolidInfill || role == frTopSolidInfill) { + extruder = m_config.solid_infill_extruder; + } else { + throw std::invalid_argument("Unknown role"); + } + double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1); + + return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0); +} + +coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const +{ + return (print_config.nozzle_diameter.get_at(m_config.perimeter_extruder.value - 1) + + print_config.nozzle_diameter.get_at(m_config.infill_extruder.value - 1) + + print_config.nozzle_diameter.get_at(m_config.solid_infill_extruder.value - 1)) / 3.; +} + +coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const +{ + return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value); +} + +} diff --git a/src/libslic3r/Rasterizer/Rasterizer.cpp b/src/libslic3r/Rasterizer/Rasterizer.cpp new file mode 100644 index 0000000000..3ff3e09496 --- /dev/null +++ b/src/libslic3r/Rasterizer/Rasterizer.cpp @@ -0,0 +1,214 @@ +#include "Rasterizer.hpp" +#include + +#include + +// For rasterizing +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// For png compression +#include + +namespace Slic3r { + +class Raster::Impl { +public: + using TPixelRenderer = agg::pixfmt_gray8; // agg::pixfmt_rgb24; + using TRawRenderer = agg::renderer_base; + using TPixel = TPixelRenderer::color_type; + using TRawBuffer = agg::rendering_buffer; + + using TBuffer = std::vector; + + using TRendererAA = agg::renderer_scanline_aa_solid; + + static const TPixel ColorWhite; + static const TPixel ColorBlack; + + using Origin = Raster::Origin; + +private: + Raster::Resolution m_resolution; + Raster::PixelDim m_pxdim; + TBuffer m_buf; + TRawBuffer m_rbuf; + TPixelRenderer m_pixfmt; + TRawRenderer m_raw_renderer; + TRendererAA m_renderer; + Origin m_o; + std::function m_flipy = [](agg::path_storage&) {}; +public: + inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd, + Origin o): + m_resolution(res), m_pxdim(pd), + m_buf(res.pixels()), + m_rbuf(reinterpret_cast(m_buf.data()), + res.width_px, res.height_px, + res.width_px*TPixelRenderer::num_components), + m_pixfmt(m_rbuf), + m_raw_renderer(m_pixfmt), + m_renderer(m_raw_renderer), + m_o(o) + { + m_renderer.color(ColorWhite); + + // If we would like to play around with gamma + // ras.gamma(agg::gamma_power(1.0)); + + clear(); + + if(m_o == Origin::TOP_LEFT) m_flipy = [this](agg::path_storage& path) { + path.flip_y(0, m_resolution.height_px); + }; + } + + void draw(const ExPolygon &poly) { + agg::rasterizer_scanline_aa<> ras; + agg::scanline_p8 scanlines; + + auto&& path = to_path(poly.contour); + m_flipy(path); + ras.add_path(path); + + for(auto h : poly.holes) { + auto&& holepath = to_path(h); + m_flipy(holepath); + ras.add_path(holepath); + } + + agg::render_scanlines(ras, scanlines, m_renderer); + } + + inline void clear() { + m_raw_renderer.clear(ColorBlack); + } + + inline TBuffer& buffer() { return m_buf; } + + inline const Raster::Resolution resolution() { return m_resolution; } + + inline Origin origin() const /*noexcept*/ { return m_o; } + +private: + double getPx(const Point& p) { + return p(0) * SCALING_FACTOR/m_pxdim.w_mm; + } + + double getPy(const Point& p) { + return p(1) * SCALING_FACTOR/m_pxdim.h_mm; + } + + agg::path_storage to_path(const Polygon& poly) { + agg::path_storage path; + auto it = poly.points.begin(); + path.move_to(getPx(*it), getPy(*it)); + while(++it != poly.points.end()) + path.line_to(getPx(*it), getPy(*it)); + + path.line_to(getPx(poly.points.front()), getPy(poly.points.front())); + return path; + } + +}; + +const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255); +const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0); + +Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o): + m_impl(new Impl(r, pd, o)) {} + +Raster::Raster() {} + +Raster::~Raster() {} + +Raster::Raster(Raster &&m): + m_impl(std::move(m.m_impl)) {} + +void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd) +{ + // Free up the unnecessary memory and make sure it stays clear after + // an exception + auto o = m_impl? m_impl->origin() : Origin::TOP_LEFT; + reset(r, pd, o); +} + +void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, + Raster::Origin o) +{ + m_impl.reset(); + m_impl.reset(new Impl(r, pd, o)); +} + +void Raster::reset() +{ + m_impl.reset(); +} + +Raster::Resolution Raster::resolution() const +{ + if(m_impl) return m_impl->resolution(); + + return Resolution(0, 0); +} + +void Raster::clear() +{ + assert(m_impl); + m_impl->clear(); +} + +void Raster::draw(const ExPolygon &poly) +{ + assert(m_impl); + m_impl->draw(poly); +} + +void Raster::save(std::ostream& stream, Compression comp) +{ + assert(m_impl); + switch(comp) { + case Compression::PNG: { + + png::writer wr(stream); + + wr.set_bit_depth(8); + wr.set_color_type(png::color_type_gray); + wr.set_width(resolution().width_px); + wr.set_height(resolution().height_px); + wr.set_compression_type(png::compression_type_default); + + wr.write_info(); + + auto& b = m_impl->buffer(); + auto ptr = reinterpret_cast( b.data() ); + unsigned stride = + sizeof(Impl::TBuffer::value_type) * resolution().width_px; + + for(unsigned r = 0; r < resolution().height_px; r++, ptr+=stride) { + wr.write_row(ptr); + } + + break; + } + case Compression::RAW: { + stream << "P5 " + << m_impl->resolution().width_px << " " + << m_impl->resolution().height_px << " " + << "255 "; + + stream.write(reinterpret_cast(m_impl->buffer().data()), + m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type)); + } + } +} + +} diff --git a/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/Rasterizer/Rasterizer.hpp new file mode 100644 index 0000000000..b6406b7705 --- /dev/null +++ b/src/libslic3r/Rasterizer/Rasterizer.hpp @@ -0,0 +1,86 @@ +#ifndef RASTERIZER_HPP +#define RASTERIZER_HPP + +#include +#include + +namespace Slic3r { + +class ExPolygon; + +/** + * @brief Raster captures an anti-aliased monochrome canvas where vectorial + * polygons can be rasterized. Fill color is always white and the background is + * black. Contours are anti-aliased. + * + * It also supports saving the raster data into a standard output stream in raw + * or PNG format. + */ +class Raster { + class Impl; + std::unique_ptr m_impl; +public: + + /// Supported compression types + enum class Compression { + RAW, //!> Uncompressed pixel data + PNG //!> PNG compression + }; + + enum class Origin { + TOP_LEFT, + BOTTOM_LEFT + }; + + /// Type that represents a resolution in pixels. + struct Resolution { + unsigned width_px; + unsigned height_px; + inline Resolution(unsigned w, unsigned h): width_px(w), height_px(h) {} + inline unsigned pixels() const /*noexcept*/ { + return width_px * height_px; + } + }; + + /// Types that represents the dimension of a pixel in millimeters. + struct PixelDim { + double w_mm; + double h_mm; + inline PixelDim(double px_width_mm, double px_height_mm ): + w_mm(px_width_mm), h_mm(px_height_mm) {} + }; + + /// Constructor taking the resolution and the pixel dimension. + explicit Raster(const Resolution& r, const PixelDim& pd, + Origin o = Origin::BOTTOM_LEFT ); + Raster(); + Raster(const Raster& cpy) = delete; + Raster& operator=(const Raster& cpy) = delete; + Raster(Raster&& m); + ~Raster(); + + /// Reallocated everything for the given resolution and pixel dimension. + void reset(const Resolution& r, const PixelDim& pd); + void reset(const Resolution& r, const PixelDim& pd, Origin o); + + /** + * Release the allocated resources. Drawing in this state ends in + * unspecified behavior. + */ + void reset(); + + /// Get the resolution of the raster. + Resolution resolution() const; + + /// Clear the raster with black color. + void clear(); + + /// Draw a polygon with holes. + void draw(const ExPolygon& poly); + + /// Save the raster on the specified stream. + void save(std::ostream& stream, Compression comp = Compression::RAW); +}; + +} +#endif // RASTERIZER_HPP diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp new file mode 100644 index 0000000000..3a92f371ec --- /dev/null +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -0,0 +1,551 @@ +#include "SLABasePool.hpp" +#include "SLABoilerPlate.hpp" + +#include "boost/log/trivial.hpp" +#include "SLABoostAdapter.hpp" +#include "ClipperUtils.hpp" + +//#include "SVG.hpp" +//#include "benchmark.h" + +namespace Slic3r { namespace sla { + +/// Convert the triangulation output to an intermediate mesh. +Contour3D convert(const Polygons& triangles, coord_t z, bool dir) { + + Pointf3s points; + points.reserve(3*triangles.size()); + Indices indices; + indices.reserve(points.size()); + + for(auto& tr : triangles) { + auto c = coord_t(points.size()), b = c++, a = c++; + if(dir) indices.emplace_back(a, b, c); + else indices.emplace_back(c, b, a); + for(auto& p : tr.points) { + points.emplace_back(unscale(x(p), y(p), z)); + } + } + + return {points, indices}; +} + +Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling, + double floor_z_mm, double ceiling_z_mm) { + using std::transform; using std::back_inserter; + + ExPolygon poly; + poly.contour.points = floor_plate.contour.points; + poly.holes.emplace_back(ceiling.contour); + auto& h = poly.holes.front(); + std::reverse(h.points.begin(), h.points.end()); + Polygons tri = triangulate(poly); + + Contour3D ret; + ret.points.reserve(tri.size() * 3); + + double fz = floor_z_mm; + double cz = ceiling_z_mm; + auto& rp = ret.points; + auto& rpi = ret.indices; + ret.indices.reserve(tri.size() * 3); + + coord_t idx = 0; + + auto hlines = h.lines(); + auto is_upper = [&hlines](const Point& p) { + return std::any_of(hlines.begin(), hlines.end(), + [&p](const Line& l) { + return l.distance_to(p) < mm(1e-6); + }); + }; + + std::for_each(tri.begin(), tri.end(), + [&rp, &rpi, &poly, &idx, is_upper, fz, cz](const Polygon& pp) + { + for(auto& p : pp.points) + if(is_upper(p)) + rp.emplace_back(unscale(x(p), y(p), mm(cz))); + else rp.emplace_back(unscale(x(p), y(p), mm(fz))); + + coord_t a = idx++, b = idx++, c = idx++; + if(fz > cz) rpi.emplace_back(c, b, a); + else rpi.emplace_back(a, b, c); + }); + + return ret; +} + +/// Offsetting with clipper and smoothing the edges into a curvature. +void offset(ExPolygon& sh, coord_t distance) { + using ClipperLib::ClipperOffset; + using ClipperLib::jtRound; + using ClipperLib::etClosedPolygon; + using ClipperLib::Paths; + using ClipperLib::Path; + + auto&& ctour = Slic3rMultiPoint_to_ClipperPath(sh.contour); + auto&& holes = Slic3rMultiPoints_to_ClipperPaths(sh.holes); + + // If the input is not at least a triangle, we can not do this algorithm + if(ctour.size() < 3 || + std::any_of(holes.begin(), holes.end(), + [](const Path& p) { return p.size() < 3; }) + ) { + BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!"; + return; + } + + ClipperOffset offs; + offs.ArcTolerance = 0.01*mm(1); + Paths result; + offs.AddPath(ctour, jtRound, etClosedPolygon); + offs.AddPaths(holes, jtRound, etClosedPolygon); + offs.Execute(result, static_cast(distance)); + + // Offsetting reverts the orientation and also removes the last vertex + // so boost will not have a closed polygon. + + bool found_the_contour = false; + sh.holes.clear(); + for(auto& r : result) { + if(ClipperLib::Orientation(r)) { + // We don't like if the offsetting generates more than one contour + // but throwing would be an overkill. Instead, we should warn the + // caller about the inability to create correct geometries + if(!found_the_contour) { + auto rr = ClipperPath_to_Slic3rPolygon(r); + sh.contour.points.swap(rr.points); + found_the_contour = true; + } else { + BOOST_LOG_TRIVIAL(warning) + << "Warning: offsetting result is invalid!"; + } + } else { + // TODO If there are multiple contours we can't be sure which hole + // belongs to the first contour. (But in this case the situation is + // bad enough to let it go...) + sh.holes.emplace_back(ClipperPath_to_Slic3rPolygon(r)); + } + } +} + +/// Unification of polygons (with clipper) preserving holes as well. +ExPolygons unify(const ExPolygons& shapes) { + using ClipperLib::ptSubject; + + ExPolygons retv; + + bool closed = true; + bool valid = true; + + ClipperLib::Clipper clipper; + + for(auto& path : shapes) { + auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path.contour); + + if(!clipperpath.empty()) + valid &= clipper.AddPath(clipperpath, ptSubject, closed); + + auto clipperholes = Slic3rMultiPoints_to_ClipperPaths(path.holes); + + for(auto& hole : clipperholes) { + if(!hole.empty()) + valid &= clipper.AddPath(hole, ptSubject, closed); + } + } + + if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!"; + + ClipperLib::PolyTree result; + clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero); + + retv.reserve(static_cast(result.Total())); + + // Now we will recursively traverse the polygon tree and serialize it + // into an ExPolygon with holes. The polygon tree has the clipper-ish + // PolyTree structure which alternates its nodes as contours and holes + + // A "declaration" of function for traversing leafs which are holes + std::function processHole; + + // Process polygon which calls processHoles which than calls processPoly + // again until no leafs are left. + auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) { + ExPolygon poly; + poly.contour.points = ClipperPath_to_Slic3rPolygon(pptr->Contour); + for(auto h : pptr->Childs) { processHole(h, poly); } + retv.push_back(poly); + }; + + // Body of the processHole function + processHole = [&processPoly](ClipperLib::PolyNode *pptr, ExPolygon& poly) + { + poly.holes.emplace_back(); + poly.holes.back().points = ClipperPath_to_Slic3rPolygon(pptr->Contour); + for(auto c : pptr->Childs) processPoly(c); + }; + + // Wrapper for traversing. + auto traverse = [&processPoly] (ClipperLib::PolyNode *node) + { + for(auto ch : node->Childs) { + processPoly(ch); + } + }; + + // Here is the actual traverse + traverse(&result); + + return retv; +} + +/// Only a debug function to generate top and bottom plates from a 2D shape. +/// It is not used in the algorithm directly. +inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) { + Polygons triangles = triangulate(poly); + + auto lower = convert(triangles, 0, false); + auto upper = convert(triangles, z_distance, true); + lower.merge(upper); + return lower; +} + +template +Contour3D round_edges(const ExPolygon& base_plate, + double radius_mm, + double degrees, + double ceilheight_mm, + bool dir, + ExP&& last_offset = ExP(), D&& last_height = D()) +{ + auto ob = base_plate; + auto ob_prev = ob; + double wh = ceilheight_mm, wh_prev = wh; + Contour3D curvedwalls; + + int steps = 15; // int(std::ceil(10*std::pow(radius_mm, 1.0/3))); + double stepx = radius_mm / steps; + coord_t s = dir? 1 : -1; + degrees = std::fmod(degrees, 180); + + if(degrees >= 90) { + for(int i = 1; i <= steps; ++i) { + ob = base_plate; + + double r2 = radius_mm * radius_mm; + double xx = i*stepx; + double x2 = xx*xx; + double stepy = std::sqrt(r2 - x2); + + offset(ob, s*mm(xx)); + wh = ceilheight_mm - radius_mm + stepy; + + Contour3D pwalls; + pwalls = walls(ob, ob_prev, wh, wh_prev); + + curvedwalls.merge(pwalls); + ob_prev = ob; + wh_prev = wh; + } + } + + double tox = radius_mm - radius_mm*std::sin(degrees * PI / 180); + int tos = int(tox / stepx); + + for(int i = 1; i <= tos; ++i) { + ob = base_plate; + + double r2 = radius_mm * radius_mm; + double xx = radius_mm - i*stepx; + double x2 = xx*xx; + double stepy = std::sqrt(r2 - x2); + offset(ob, s*mm(xx)); + wh = ceilheight_mm - radius_mm - stepy; + + Contour3D pwalls; + pwalls = walls(ob_prev, ob, wh_prev, wh); + + curvedwalls.merge(pwalls); + ob_prev = ob; + wh_prev = wh; + } + + last_offset = std::move(ob); + last_height = wh; + + return curvedwalls; +} + +/// Generating the concave part of the 3D pool with the bottom plate and the +/// side walls. +Contour3D inner_bed(const ExPolygon& poly, double depth_mm, + double begin_h_mm = 0) { + + Polygons triangles = triangulate(poly); + + coord_t depth = mm(depth_mm); + coord_t begin_h = mm(begin_h_mm); + + auto bottom = convert(triangles, -depth + begin_h, false); + auto lines = poly.lines(); + + // Generate outer walls + auto fp = [](const Point& p, Point::coord_type z) { + return unscale(x(p), y(p), z); + }; + + for(auto& l : lines) { + auto s = coord_t(bottom.points.size()); + + bottom.points.emplace_back(fp(l.a, -depth + begin_h)); + bottom.points.emplace_back(fp(l.b, -depth + begin_h)); + bottom.points.emplace_back(fp(l.a, begin_h)); + bottom.points.emplace_back(fp(l.b, begin_h)); + + bottom.indices.emplace_back(s + 3, s + 1, s); + bottom.indices.emplace_back(s + 2, s + 3, s); + } + + return bottom; +} + +inline Point centroid(Points& pp) { + Point c; + switch(pp.size()) { + case 0: break; + case 1: c = pp.front(); break; + case 2: c = (pp[0] + pp[1]) / 2; break; + default: { + auto MAX = std::numeric_limits::max(); + auto MIN = std::numeric_limits::min(); + Point min = {MAX, MAX}, max = {MIN, MIN}; + + for(auto& p : pp) { + if(p(0) < min(0)) min(0) = p(0); + if(p(1) < min(1)) min(1) = p(1); + if(p(0) > max(0)) max(0) = p(0); + if(p(1) > max(1)) max(1) = p(1); + } + c(0) = min(0) + (max(0) - min(0)) / 2; + c(1) = min(1) + (max(1) - min(1)) / 2; + + // TODO: fails for non convex cluster +// c = std::accumulate(pp.begin(), pp.end(), Point{0, 0}); +// x(c) /= coord_t(pp.size()); y(c) /= coord_t(pp.size()); + break; + } + } + + return c; +} + +inline Point centroid(const ExPolygon& poly) { + return poly.contour.centroid(); +} + +/// A fake concave hull that is constructed by connecting separate shapes +/// with explicit bridges. Bridges are generated from each shape's centroid +/// to the center of the "scene" which is the centroid calculated from the shape +/// centroids (a star is created...) +ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50) +{ + namespace bgi = boost::geometry::index; + using SpatElement = std::pair; + using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; + + if(polys.empty()) return ExPolygons(); + + ExPolygons punion = unify(polys); // could be redundant + + if(punion.size() == 1) return punion; + + // We get the centroids of all the islands in the 2D slice + Points centroids; centroids.reserve(punion.size()); + std::transform(punion.begin(), punion.end(), std::back_inserter(centroids), + [](const ExPolygon& poly) { return centroid(poly); }); + + + SpatIndex boxindex; unsigned idx = 0; + std::for_each(punion.begin(), punion.end(), + [&boxindex, &idx](const ExPolygon& expo) { + BoundingBox bb(expo); + boxindex.insert(std::make_pair(bb, idx++)); + }); + + + // Centroid of the centroids of islands. This is where the additional + // connector sticks are routed. + Point cc = centroid(centroids); + + punion.reserve(punion.size() + centroids.size()); + + idx = 0; + std::transform(centroids.begin(), centroids.end(), + std::back_inserter(punion), + [&punion, &boxindex, cc, max_dist_mm, &idx](const Point& c) + { + + double dx = x(c) - x(cc), dy = y(c) - y(cc); + double l = std::sqrt(dx * dx + dy * dy); + double nx = dx / l, ny = dy / l; + double max_dist = mm(max_dist_mm); + + ExPolygon& expo = punion[idx++]; + BoundingBox querybb(expo); + + querybb.offset(max_dist); + std::vector result; + boxindex.query(bgi::intersects(querybb), std::back_inserter(result)); + if(result.size() <= 1) return ExPolygon(); + + ExPolygon r; + auto& ctour = r.contour.points; + + ctour.reserve(3); + ctour.emplace_back(cc); + + Point d(coord_t(mm(1)*nx), coord_t(mm(1)*ny)); + ctour.emplace_back(c + Point( -y(d), x(d) )); + ctour.emplace_back(c + Point( y(d), -x(d) )); + offset(r, mm(1)); + + return r; + }); + + punion = unify(punion); + + return punion; +} + +void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, + float layerh) +{ + TriangleMesh m = mesh; + TriangleMeshSlicer slicer(&m); + +// TriangleMesh upper, lower; +// slicer.cut(h, &upper, &lower); + + // TODO: this might be slow (in fact it was) +// output = lower.horizontal_projection(); + + auto bb = mesh.bounding_box(); + float gnd = float(bb.min(Z)); + std::vector heights = {float(bb.min(Z))}; + for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh) + heights.emplace_back(hi); + + std::vector out; out.reserve(size_t(std::ceil(h/layerh))); + slicer.slice(heights, &out, [](){}); + + size_t count = 0; for(auto& o : out) count += o.size(); + ExPolygons tmp; tmp.reserve(count); + for(auto& o : out) for(auto& e : o) tmp.emplace_back(std::move(e)); + + output = unify(tmp); + for(auto& o : output) o = o.simplify(0.1/SCALING_FACTOR).front(); +} + +void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, + const PoolConfig& cfg) +{ + double mdist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm) + + cfg.max_merge_distance_mm; + + auto concavehs = concave_hull(ground_layer, mdist); + for(ExPolygon& concaveh : concavehs) { + if(concaveh.contour.points.empty()) return; + concaveh.holes.clear(); + + const coord_t WALL_THICKNESS = mm(cfg.min_wall_thickness_mm); + + const coord_t WALL_DISTANCE = mm(2*cfg.edge_radius_mm) + + coord_t(0.8*WALL_THICKNESS); + + const coord_t HEIGHT = mm(cfg.min_wall_height_mm); + + auto outer_base = concaveh; + offset(outer_base, WALL_THICKNESS+WALL_DISTANCE); + auto inner_base = outer_base; + offset(inner_base, -WALL_THICKNESS); + inner_base.holes.clear(); outer_base.holes.clear(); + + ExPolygon top_poly; + top_poly.contour = outer_base.contour; + top_poly.holes.emplace_back(inner_base.contour); + auto& tph = top_poly.holes.back().points; + std::reverse(tph.begin(), tph.end()); + + Contour3D pool; + + ExPolygon ob = outer_base; double wh = 0; + + // now we will calculate the angle or portion of the circle from + // pi/2 that will connect perfectly with the bottom plate. + // this is a tangent point calculation problem and the equation can + // be found for example here: + // http://www.ambrsoft.com/TrigoCalc/Circles2/CirclePoint/CirclePointDistance.htm + // the y coordinate would be: + // y = cy + (r^2*py - r*px*sqrt(px^2 + py^2 - r^2) / (px^2 + py^2) + // where px and py are the coordinates of the point outside the circle + // cx and cy are the circle center, r is the radius + // to get the angle we use arcsin function and subtract 90 degrees then + // flip the sign to get the right input to the round_edge function. + double r = cfg.edge_radius_mm; + double cy = 0; + double cx = 0; + double px = cfg.min_wall_thickness_mm; + double py = r - cfg.min_wall_height_mm; + + double pxcx = px - cx; + double pycy = py - cy; + double b_2 = pxcx*pxcx + pycy*pycy; + double r_2 = r*r; + double D = std::sqrt(b_2 - r_2); + double vy = (r_2*pycy - r*pxcx*D) / b_2; + double phi = -(std::asin(vy/r) * 180 / PI - 90); + + auto curvedwalls = round_edges(ob, + r, + phi, // 170 degrees + 0, // z position of the input plane + true, + ob, wh); + + pool.merge(curvedwalls); + + ExPolygon ob_contr = ob; + ob_contr.holes.clear(); + + auto pwalls = walls(ob_contr, inner_base, wh, -cfg.min_wall_height_mm); + pool.merge(pwalls); + + Polygons top_triangles, bottom_triangles; + triangulate(top_poly, top_triangles); + triangulate(inner_base, bottom_triangles); + auto top_plate = convert(top_triangles, 0, false); + auto bottom_plate = convert(bottom_triangles, -HEIGHT, true); + + ob = inner_base; wh = 0; + // rounded edge generation for the inner bed + curvedwalls = round_edges(ob, + cfg.edge_radius_mm, + 90, // 90 degrees + 0, // z position of the input plane + false, + ob, wh); + pool.merge(curvedwalls); + + auto innerbed = inner_bed(ob, cfg.min_wall_height_mm/2 + wh, wh); + + pool.merge(top_plate); + pool.merge(bottom_plate); + pool.merge(innerbed); + + out.merge(mesh(pool)); + } +} + +} +} diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp new file mode 100644 index 0000000000..e773de29cd --- /dev/null +++ b/src/libslic3r/SLA/SLABasePool.hpp @@ -0,0 +1,38 @@ +#ifndef SLABASEPOOL_HPP +#define SLABASEPOOL_HPP + +#include + +namespace Slic3r { + +class ExPolygon; +class TriangleMesh; + +namespace sla { + +using ExPolygons = std::vector; + +/// Calculate the polygon representing the silhouette from the specified height +void base_plate(const TriangleMesh& mesh, + ExPolygons& output, + float zlevel = 0.1f, + float layerheight = 0.05f); + +struct PoolConfig { + double min_wall_thickness_mm = 2; + double min_wall_height_mm = 5; + double max_merge_distance_mm = 50; + double edge_radius_mm = 1; +}; + +/// Calculate the pool for the mesh for SLA printing +void create_base_pool(const ExPolygons& base_plate, + TriangleMesh& output_mesh, + const PoolConfig& = PoolConfig() + ); + +} + +} + +#endif // SLABASEPOOL_HPP diff --git a/src/libslic3r/SLA/SLABoilerPlate.hpp b/src/libslic3r/SLA/SLABoilerPlate.hpp new file mode 100644 index 0000000000..9a79c8e58f --- /dev/null +++ b/src/libslic3r/SLA/SLABoilerPlate.hpp @@ -0,0 +1,87 @@ +#ifndef SLABOILERPLATE_HPP +#define SLABOILERPLATE_HPP + +#include +#include +#include + +#include "ExPolygon.hpp" +#include "TriangleMesh.hpp" + +namespace Slic3r { +namespace sla { + +using coord_t = Point::coord_type; + +/// get the scaled clipper units for a millimeter value +inline coord_t mm(double v) { return coord_t(v/SCALING_FACTOR); } + +/// Get x and y coordinates (because we are eigenizing...) +inline coord_t x(const Point& p) { return p(0); } +inline coord_t y(const Point& p) { return p(1); } +inline coord_t& x(Point& p) { return p(0); } +inline coord_t& y(Point& p) { return p(1); } + +inline coordf_t x(const Vec3d& p) { return p(0); } +inline coordf_t y(const Vec3d& p) { return p(1); } +inline coordf_t z(const Vec3d& p) { return p(2); } +inline coordf_t& x(Vec3d& p) { return p(0); } +inline coordf_t& y(Vec3d& p) { return p(1); } +inline coordf_t& z(Vec3d& p) { return p(2); } + +inline coord_t& x(Vec3crd& p) { return p(0); } +inline coord_t& y(Vec3crd& p) { return p(1); } +inline coord_t& z(Vec3crd& p) { return p(2); } +inline coord_t x(const Vec3crd& p) { return p(0); } +inline coord_t y(const Vec3crd& p) { return p(1); } +inline coord_t z(const Vec3crd& p) { return p(2); } + +inline void triangulate(const ExPolygon& expoly, Polygons& triangles) { + expoly.triangulate_p2t(&triangles); +} + +inline Polygons triangulate(const ExPolygon& expoly) { + Polygons tri; triangulate(expoly, tri); return tri; +} + +using Indices = std::vector; + +/// Intermediate struct for a 3D mesh +struct Contour3D { + Pointf3s points; + Indices indices; + + void merge(const Contour3D& ctr) { + auto s3 = coord_t(points.size()); + auto s = coord_t(indices.size()); + + points.insert(points.end(), ctr.points.begin(), ctr.points.end()); + indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end()); + + for(auto n = s; n < indices.size(); n++) { + auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3; + } + } +}; + +//using PointSet = Eigen::Matrix; //Eigen::MatrixXd; +using ClusterEl = std::vector; +using ClusteredPoints = std::vector; + +/// Convert the triangulation output to an intermediate mesh. +Contour3D convert(const Polygons& triangles, coord_t z, bool dir); + +/// Mesh from an existing contour. +inline TriangleMesh mesh(const Contour3D& ctour) { + return {ctour.points, ctour.indices}; +} + +/// Mesh from an evaporating 3D contour +inline TriangleMesh mesh(Contour3D&& ctour) { + return {std::move(ctour.points), std::move(ctour.indices)}; +} + +} +} + +#endif // SLABOILERPLATE_HPP diff --git a/src/libslic3r/SLA/SLABoostAdapter.hpp b/src/libslic3r/SLA/SLABoostAdapter.hpp new file mode 100644 index 0000000000..1e9daf4613 --- /dev/null +++ b/src/libslic3r/SLA/SLABoostAdapter.hpp @@ -0,0 +1,132 @@ +#ifndef SLABOOSTADAPTER_HPP +#define SLABOOSTADAPTER_HPP + +#include "SLA/SLABoilerPlate.hpp" +#include + +namespace boost { +namespace geometry { +namespace traits { + +/* ************************************************************************** */ +/* Point concept adaptation ************************************************* */ +/* ************************************************************************** */ + +template<> struct tag { + using type = point_tag; +}; + +template<> struct coordinate_type { + using type = coord_t; +}; + +template<> struct coordinate_system { + using type = cs::cartesian; +}; + +template<> struct dimension: boost::mpl::int_<2> {}; + +template struct access { + static inline coord_t get(Slic3r::Point const& a) { + return a(d); + } + + static inline void set(Slic3r::Point& a, coord_t const& value) { + a(d) = value; + } +}; + +// For Vec2d /////////////////////////////////////////////////////////////////// + +template<> struct tag { + using type = point_tag; +}; + +template<> struct coordinate_type { + using type = double; +}; + +template<> struct coordinate_system { + using type = cs::cartesian; +}; + +template<> struct dimension: boost::mpl::int_<2> {}; + +template struct access { + static inline double get(Slic3r::Vec2d const& a) { + return a(d); + } + + static inline void set(Slic3r::Vec2d& a, double const& value) { + a(d) = value; + } +}; + +// For Vec3d /////////////////////////////////////////////////////////////////// + +template<> struct tag { + using type = point_tag; +}; + +template<> struct coordinate_type { + using type = double; +}; + +template<> struct coordinate_system { + using type = cs::cartesian; +}; + +template<> struct dimension: boost::mpl::int_<3> {}; + +template struct access { + static inline double get(Slic3r::Vec3d const& a) { + return a(d); + } + + static inline void set(Slic3r::Vec3d& a, double const& value) { + a(d) = value; + } +}; + +/* ************************************************************************** */ +/* Box concept adaptation *************************************************** */ +/* ************************************************************************** */ + +template<> struct tag { + using type = box_tag; +}; + +template<> struct point_type { + using type = Slic3r::Point; +}; + +template +struct indexed_access { + static inline coord_t get(Slic3r::BoundingBox const& box) { + return box.min(d); + } + static inline void set(Slic3r::BoundingBox &box, coord_t const& coord) { + box.min(d) = coord; + } +}; + +template +struct indexed_access { + static inline coord_t get(Slic3r::BoundingBox const& box) { + return box.max(d); + } + static inline void set(Slic3r::BoundingBox &box, coord_t const& coord) { + box.max(d) = coord; + } +}; + +} +} + +template<> struct range_value> { + using type = Slic3r::Vec2d; +}; + +} + +#endif // SLABOOSTADAPTER_HPP diff --git a/src/libslic3r/SLA/SLARotfinder.cpp b/src/libslic3r/SLA/SLARotfinder.cpp new file mode 100644 index 0000000000..05cea7748c --- /dev/null +++ b/src/libslic3r/SLA/SLARotfinder.cpp @@ -0,0 +1,126 @@ +#include +#include + +#include +#include "SLABoilerPlate.hpp" +#include "SLARotfinder.hpp" +#include "SLASupportTree.hpp" +#include "Model.hpp" + +namespace Slic3r { +namespace sla { + +std::array find_best_rotation(const ModelObject& modelobj, + float accuracy, + std::function statuscb, + std::function stopcond) +{ + using libnest2d::opt::Method; + using libnest2d::opt::bound; + using libnest2d::opt::Optimizer; + using libnest2d::opt::TOptimizer; + using libnest2d::opt::StopCriteria; + using Quaternion = Eigen::Quaternion; + + static const unsigned MAX_TRIES = 100000; + + // return value + std::array rot; + + // We will use only one instance of this converted mesh to examine different + // rotations + EigenMesh3D emesh = to_eigenmesh(modelobj); + + // For current iteration number + unsigned status = 0; + + // The maximum number of iterations + auto max_tries = unsigned(accuracy * MAX_TRIES); + + // call status callback with zero, because we are at the start + statuscb(status); + + // So this is the object function which is called by the solver many times + // It has to yield a single value representing the current score. We will + // call the status callback in each iteration but the actual value may be + // the same for subsequent iterations (status goes from 0 to 100 but + // iterations can be many more) + auto objfunc = [&emesh, &status, &statuscb, max_tries] + (double rx, double ry, double rz) + { + EigenMesh3D& m = emesh; + + // prepare the rotation transformation + Transform3d rt = Transform3d::Identity(); + + rt.rotate(Eigen::AngleAxisd(rz, Vec3d::UnitZ())); + rt.rotate(Eigen::AngleAxisd(ry, Vec3d::UnitY())); + rt.rotate(Eigen::AngleAxisd(rx, Vec3d::UnitX())); + + double score = 0; + + // For all triangles we calculate the normal and sum up the dot product + // (a scalar indicating how much are two vectors aligned) with each axis + // this will result in a value that is greater if a normal is aligned + // with all axes. If the normal is aligned than the triangle itself is + // orthogonal to the axes and that is good for print quality. + + // TODO: some applications optimize for minimum z-axis cross section + // area. The current function is only an example of how to optimize. + + // Later we can add more criteria like the number of overhangs, etc... + for(int i = 0; i < m.F.rows(); i++) { + auto idx = m.F.row(i); + + Vec3d p1 = m.V.row(idx(0)); + Vec3d p2 = m.V.row(idx(1)); + Vec3d p3 = m.V.row(idx(2)); + + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + + // So this is the normal + auto n = U.cross(V).normalized(); + + // rotate the normal with the current rotation given by the solver + n = rt * n; + + // We should score against the alignment with the reference planes + score += std::abs(n.dot(Vec3d::UnitX())); + score += std::abs(n.dot(Vec3d::UnitY())); + score += std::abs(n.dot(Vec3d::UnitZ())); + } + + // report status + statuscb( unsigned(++status * 100.0/max_tries) ); + + return score; + }; + + // Firing up the genetic optimizer. For now it uses the nlopt library. + StopCriteria stc; + stc.max_iterations = max_tries; + stc.relative_score_difference = 1e-3; + stc.stop_condition = stopcond; // stop when stopcond returns true + TOptimizer solver(stc); + + // We are searching rotations around the three axes x, y, z. Thus the + // problem becomes a 3 dimensional optimization task. + // We can specify the bounds for a dimension in the following way: + auto b = bound(-PI/2, PI/2); + + // Now we start the optimization process with initial angles (0, 0, 0) + auto result = solver.optimize_max(objfunc, + libnest2d::opt::initvals(0.0, 0.0, 0.0), + b, b, b); + + // Save the result and fck off + rot[0] = std::get<0>(result.optimum); + rot[1] = std::get<1>(result.optimum); + rot[2] = std::get<2>(result.optimum); + + return rot; +} + +} +} diff --git a/src/libslic3r/SLA/SLARotfinder.hpp b/src/libslic3r/SLA/SLARotfinder.hpp new file mode 100644 index 0000000000..bbfab670af --- /dev/null +++ b/src/libslic3r/SLA/SLARotfinder.hpp @@ -0,0 +1,38 @@ +#ifndef SLAROTFINDER_HPP +#define SLAROTFINDER_HPP + +#include +#include + +namespace Slic3r { + +class ModelObject; + +namespace sla { + +/** + * The function should find the best rotation for SLA upside down printing. + * + * @param modelobj The model object representing the 3d mesh. + * @param accuracy The optimization accuracy from 0.0f to 1.0f. Currently, + * the nlopt genetic optimizer is used and the number of iterations is + * accuracy * 100000. This can change in the future. + * @param statuscb A status indicator callback called with the unsigned + * argument spanning from 0 to 100. May not reach 100 if the optimization finds + * an optimum before max iterations are reached. + * @param stopcond A function that if returns true, the search process will be + * terminated and the best solution found will be returned. + * + * @return Returns the rotations around each axis (x, y, z) + */ +std::array find_best_rotation( + const ModelObject& modelobj, + float accuracy = .0f, + std::function statuscb = [] (unsigned) {}, + std::function stopcond = [] () { return false; } + ); + +} +} + +#endif // SLAROTFINDER_HPP diff --git a/src/libslic3r/SLA/SLASpatIndex.hpp b/src/libslic3r/SLA/SLASpatIndex.hpp new file mode 100644 index 0000000000..792e86c859 --- /dev/null +++ b/src/libslic3r/SLA/SLASpatIndex.hpp @@ -0,0 +1,51 @@ +#ifndef SPATINDEX_HPP +#define SPATINDEX_HPP + +#include +#include +#include + +#include + +namespace Slic3r { +namespace sla { + +typedef Eigen::Matrix Vec3d; +using SpatElement = std::pair; + +class SpatIndex { + class Impl; + + // We use Pimpl because it takes a long time to compile boost headers which + // is the engine of this class. We include it only in the cpp file. + std::unique_ptr m_impl; +public: + + SpatIndex(); + ~SpatIndex(); + + SpatIndex(const SpatIndex&); + SpatIndex(SpatIndex&&); + SpatIndex& operator=(const SpatIndex&); + SpatIndex& operator=(SpatIndex&&); + + void insert(const SpatElement&); + bool remove(const SpatElement&); + + inline void insert(const Vec3d& v, unsigned idx) + { + insert(std::make_pair(v, unsigned(idx))); + } + + std::vector query(std::function); + std::vector nearest(const Vec3d&, unsigned k); + + // For testing + size_t size() const; + bool empty() const { return size() == 0; } +}; + +} +} + +#endif // SPATINDEX_HPP diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp new file mode 100644 index 0000000000..a4c5abc9b7 --- /dev/null +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -0,0 +1,1832 @@ +/** + * In this file we will implement the automatic SLA support tree generation. + * + */ + +#include +#include "SLASupportTree.hpp" +#include "SLABoilerPlate.hpp" +#include "SLASpatIndex.hpp" +#include "SLABasePool.hpp" +#include +#include "ClipperUtils.hpp" + +#include "Model.hpp" + +/** + * Terminology: + * + * Support point: + * The point on the model surface that needs support. + * + * Pillar: + * A thick column that spans from a support point to the ground and has + * a thick cone shaped base where it touches the ground. + * + * Ground facing support point: + * A support point that can be directly connected with the ground with a pillar + * that does not collide or cut through the model. + * + * Non ground facing support point: + * A support point that cannot be directly connected with the ground (only with + * the model surface). + * + * Head: + * The pinhead that connects to the model surface with the sharp end end + * to a pillar or bridge stick with the dull end. + * + * Headless support point: + * A support point on the model surface for which there is not enough place for + * the head. It is either in a hole or there is some barrier that would collide + * with the head geometry. The headless support point can be ground facing and + * non ground facing as well. + * + * Bridge: + * A stick that connects two pillars or a head with a pillar. + * + * Junction: + * A small ball in the intersection of two or more sticks (pillar, bridge, ...) + * + * CompactBridge: + * A bridge that connects a headless support point with the model surface or a + * nearby pillar. + */ + +namespace Slic3r { +namespace sla { + +using Coordf = double; +using Portion = std::tuple; + +inline Portion make_portion(double a, double b) { + return std::make_tuple(a, b); +} + +template double distance(const Vec& p) { + return std::sqrt(p.transpose() * p); +} + +template double distance(const Vec& pp1, const Vec& pp2) { + auto p = pp2 - pp1; + return distance(p); +} + +Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI), + double fa=(2*PI/360)) { + + Contour3D ret; + + // prohibit close to zero radius + if(rho <= 1e-6 && rho >= -1e-6) return ret; + + auto& vertices = ret.points; + auto& facets = ret.indices; + + // Algorithm: + // Add points one-by-one to the sphere grid and form facets using relative + // coordinates. Sphere is composed effectively of a mesh of stacked circles. + + // adjust via rounding to get an even multiple for any provided angle. + double angle = (2*PI / floor(2*PI / fa)); + + // Ring to be scaled to generate the steps of the sphere + std::vector ring; + + for (double i = 0; i < 2*PI; i+=angle) ring.emplace_back(i); + + const auto sbegin = size_t(2*std::get<0>(portion)/angle); + const auto send = size_t(2*std::get<1>(portion)/angle); + + const size_t steps = ring.size(); + const double increment = (double)(1.0 / (double)steps); + + // special case: first ring connects to 0,0,0 + // insert and form facets. + if(sbegin == 0) + vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*sbegin*2.0*rho)); + + auto id = coord_t(vertices.size()); + for (size_t i = 0; i < ring.size(); i++) { + // Fixed scaling + const double z = -rho + increment*rho*2.0 * (sbegin + 1.0); + // radius of the circle for this step. + const double r = sqrt(abs(rho*rho - z*z)); + Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); + vertices.emplace_back(Vec3d(b(0), b(1), z)); + + if(sbegin == 0) + facets.emplace_back((i == 0) ? Vec3crd(coord_t(ring.size()), 0, 1) : + Vec3crd(id - 1, 0, id)); + ++ id; + } + + // General case: insert and form facets for each step, + // joining it to the ring below it. + for (size_t s = sbegin + 2; s < send - 1; s++) { + const double z = -rho + increment*(double)s*2.0*rho; + const double r = sqrt(abs(rho*rho - z*z)); + + for (size_t i = 0; i < ring.size(); i++) { + Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); + vertices.emplace_back(Vec3d(b(0), b(1), z)); + auto id_ringsize = coord_t(id - ring.size()); + if (i == 0) { + // wrap around + facets.emplace_back(Vec3crd(id - 1, id, + id + coord_t(ring.size() - 1))); + facets.emplace_back(Vec3crd(id - 1, id_ringsize, id)); + } else { + facets.emplace_back(Vec3crd(id_ringsize - 1, id_ringsize, id)); + facets.emplace_back(Vec3crd(id - 1, id_ringsize - 1, id)); + } + id++; + } + } + + // special case: last ring connects to 0,0,rho*2.0 + // only form facets. + if(send >= size_t(2*PI / angle)) { + vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*send*2.0*rho)); + for (size_t i = 0; i < ring.size(); i++) { + auto id_ringsize = coord_t(id - ring.size()); + if (i == 0) { + // third vertex is on the other side of the ring. + facets.emplace_back(Vec3crd(id - 1, id_ringsize, id)); + } else { + auto ci = coord_t(id_ringsize + i); + facets.emplace_back(Vec3crd(ci - 1, ci, id)); + } + } + } + id++; + + return ret; +} + +Contour3D cylinder(double r, double h, size_t ssteps) { + Contour3D ret; + + auto steps = int(ssteps); + auto& points = ret.points; + auto& indices = ret.indices; + points.reserve(2*steps); + double a = 2*PI/steps; + + Vec3d jp = {0, 0, 0}; + Vec3d endp = {0, 0, h}; + + for(int i = 0; i < steps; ++i) { + double phi = i*a; + double ex = endp(X) + r*std::cos(phi); + double ey = endp(Y) + r*std::sin(phi); + points.emplace_back(ex, ey, endp(Z)); + } + + for(int i = 0; i < steps; ++i) { + double phi = i*a; + double x = jp(X) + r*std::cos(phi); + double y = jp(Y) + r*std::sin(phi); + points.emplace_back(x, y, jp(Z)); + } + + indices.reserve(2*steps); + auto offs = steps; + for(int i = 0; i < steps - 1; ++i) { + indices.emplace_back(i, i + offs, offs + i + 1); + indices.emplace_back(i, offs + i + 1, i + 1); + } + + auto last = steps - 1; + indices.emplace_back(0, last, offs); + indices.emplace_back(last, offs + last, offs); + + return ret; +} + +struct Head { + Contour3D mesh; + + size_t steps = 45; + Vec3d dir = {0, 0, -1}; + Vec3d tr = {0, 0, 0}; + + double r_back_mm = 1; + double r_pin_mm = 0.5; + double width_mm = 2; + + // For identification purposes. This will be used as the index into the + // container holding the head structures. See SLASupportTree::Impl + long id = -1; + + // If there is a pillar connecting to this head, then the id will be set. + long pillar_id = -1; + + Head(double r_big_mm, + double r_small_mm, + double length_mm, + Vec3d direction = {0, 0, -1}, // direction (normal to the dull end ) + Vec3d offset = {0, 0, 0}, // displacement + const size_t circlesteps = 45): + steps(circlesteps), dir(direction), tr(offset), + r_back_mm(r_big_mm), r_pin_mm(r_small_mm), width_mm(length_mm) + { + + // We create two spheres which will be connected with a robe that fits + // both circles perfectly. + + // Set up the model detail level + const double detail = 2*PI/steps; + + // We don't generate whole circles. Instead, we generate only the + // portions which are visible (not covered by the robe) To know the + // exact portion of the bottom and top circles we need to use some + // rules of tangent circles from which we can derive (using simple + // triangles the following relations: + + // The height of the whole mesh + const double h = r_big_mm + r_small_mm + width_mm; + double phi = PI/2 - std::acos( (r_big_mm - r_small_mm) / h ); + + // To generate a whole circle we would pass a portion of (0, Pi) + // To generate only a half horizontal circle we can pass (0, Pi/2) + // The calculated phi is an offset to the half circles needed to smooth + // the transition from the circle to the robe geometry + + auto&& s1 = sphere(r_big_mm, make_portion(0, PI/2 + phi), detail); + auto&& s2 = sphere(r_small_mm, make_portion(PI/2 + phi, PI), detail); + + for(auto& p : s2.points) z(p) += h; + + mesh.merge(s1); + mesh.merge(s2); + + for(size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size(); + idx1 < s1.points.size() - 1; + idx1++, idx2++) + { + coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2); + coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; + + mesh.indices.emplace_back(i1s1, i2s1, i2s2); + mesh.indices.emplace_back(i1s1, i2s2, i1s2); + } + + auto i1s1 = coord_t(s1.points.size()) - coord_t(steps); + auto i2s1 = coord_t(s1.points.size()) - 1; + auto i1s2 = coord_t(s1.points.size()); + auto i2s2 = coord_t(s1.points.size()) + coord_t(steps) - 1; + + mesh.indices.emplace_back(i2s2, i2s1, i1s1); + mesh.indices.emplace_back(i1s2, i2s2, i1s1); + + // To simplify further processing, we translate the mesh so that the + // last vertex of the pointing sphere (the pinpoint) will be at (0,0,0) + for(auto& p : mesh.points) { z(p) -= (h + 0.5 * r_small_mm); } + } + + void transform() + { + using Quaternion = Eigen::Quaternion; + + // We rotate the head to the specified direction The head's pointing + // side is facing upwards so this means that it would hold a support + // point with a normal pointing straight down. This is the reason of + // the -1 z coordinate + auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, -1}, dir); + + for(auto& p : mesh.points) p = quatern * p + tr; + } + + double fullwidth() const { + return 1.5 * r_pin_mm + width_mm + 2*r_back_mm; + } + + Vec3d junction_point() const { + return tr + ( 1.5 * r_pin_mm + width_mm + r_back_mm)*dir; + } + + double request_pillar_radius(double radius) const { + const double rmax = r_back_mm /* * 0.65*/ ; + return radius > 0 && radius < rmax ? radius : rmax; + } +}; + +struct Junction { + Contour3D mesh; + double r = 1; + size_t steps = 45; + Vec3d pos; + + long id = -1; + + Junction(const Vec3d& tr, double r_mm, size_t stepnum = 45): + r(r_mm), steps(stepnum), pos(tr) + { + mesh = sphere(r_mm, make_portion(0, PI), 2*PI/steps); + for(auto& p : mesh.points) p += tr; + } +}; + +struct Pillar { + Contour3D mesh; + Contour3D base; + double r = 1; + size_t steps = 0; + Vec3d endpoint; + + long id = -1; + + // If the pillar connects to a head, this is the id of that head + bool starts_from_head = true; // Could start from a junction as well + long start_junction_id = -1; + + Pillar(const Vec3d& jp, const Vec3d& endp, + double radius = 1, size_t st = 45): + r(radius), steps(st), endpoint(endp), starts_from_head(false) + { + auto& points = mesh.points; + auto& indices = mesh.indices; + points.reserve(2*steps); + double a = 2*PI/steps; + + for(int i = 0; i < steps; ++i) { + double phi = i*a; + double x = jp(X) + r*std::cos(phi); + double y = jp(Y) + r*std::sin(phi); + points.emplace_back(x, y, jp(Z)); + } + + for(int i = 0; i < steps; ++i) { + double phi = i*a; + double ex = endp(X) + r*std::cos(phi); + double ey = endp(Y) + r*std::sin(phi); + points.emplace_back(ex, ey, endp(Z)); + } + + indices.reserve(2*steps); + int offs = int(steps); + for(int i = 0; i < steps - 1; ++i) { + indices.emplace_back(i, i + offs, offs + i + 1); + indices.emplace_back(i, offs + i + 1, i + 1); + } + + int last = int(steps) - 1; + indices.emplace_back(0, last, offs); + indices.emplace_back(last, offs + last, offs); + } + + Pillar(const Junction& junc, const Vec3d& endp): + Pillar(junc.pos, endp, junc.r, junc.steps){} + + Pillar(const Head& head, const Vec3d& endp, double radius = 1): + Pillar(head.junction_point(), endp, head.request_pillar_radius(radius), + head.steps) + { + } + + void add_base(double height = 3, double radius = 2) { + if(height <= 0) return; + + if(radius < r ) radius = r; + + double a = 2*PI/steps; + double z = endpoint(2) + height; + + for(int i = 0; i < steps; ++i) { + double phi = i*a; + double x = endpoint(0) + r*std::cos(phi); + double y = endpoint(1) + r*std::sin(phi); + base.points.emplace_back(x, y, z); + } + + for(int i = 0; i < steps; ++i) { + double phi = i*a; + double x = endpoint(0) + radius*std::cos(phi); + double y = endpoint(1) + radius*std::sin(phi); + base.points.emplace_back(x, y, z - height); + } + + auto ep = endpoint; ep(2) += height; + base.points.emplace_back(endpoint); + base.points.emplace_back(ep); + + auto& indices = base.indices; + auto hcenter = int(base.points.size() - 1); + auto lcenter = int(base.points.size() - 2); + auto offs = int(steps); + for(int i = 0; i < steps - 1; ++i) { + indices.emplace_back(i, i + offs, offs + i + 1); + indices.emplace_back(i, offs + i + 1, i + 1); + indices.emplace_back(i, i + 1, hcenter); + indices.emplace_back(lcenter, offs + i + 1, offs + i); + } + + auto last = int(steps - 1); + indices.emplace_back(0, last, offs); + indices.emplace_back(last, offs + last, offs); + indices.emplace_back(hcenter, last, 0); + indices.emplace_back(offs, offs + last, lcenter); + + } + + bool has_base() const { return !base.points.empty(); } +}; + +// A Bridge between two pillars (with junction endpoints) +struct Bridge { + Contour3D mesh; + double r = 0.8; + + long id = -1; + long start_jid = -1; + long end_jid = -1; + + // We should reduce the radius a tiny bit to help the convex hull algorithm + Bridge(const Vec3d& j1, const Vec3d& j2, + double r_mm = 0.8, size_t steps = 45): + r(r_mm) + { + using Quaternion = Eigen::Quaternion; + Vec3d dir = (j2 - j1).normalized(); + double d = distance(j2, j1); + + mesh = cylinder(r, d, steps); + + auto quater = Quaternion::FromTwoVectors(Vec3d{0,0,1}, dir); + for(auto& p : mesh.points) p = quater * p + j1; + } + + Bridge(const Junction& j1, const Junction& j2, double r_mm = 0.8): + Bridge(j1.pos, j2.pos, r_mm, j1.steps) {} + + Bridge(const Junction& j, const Pillar& cl) {} + +}; + +// A bridge that spans from model surface to model surface with small connecting +// edges on the endpoints. Used for headless support points. +struct CompactBridge { + Contour3D mesh; + long id = -1; + + CompactBridge(const Vec3d& sp, + const Vec3d& ep, + const Vec3d& n, + double r, + size_t steps = 45) + { + Vec3d startp = sp + r * n; + Vec3d dir = (ep - startp).normalized(); + Vec3d endp = ep - r * dir; + + Bridge br(startp, endp, r, steps); + mesh.merge(br.mesh); + + // now add the pins + double fa = 2*PI/steps; + auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa); + for(auto& p : upperball.points) p += startp; + + auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa); + for(auto& p : lowerball.points) p += endp; + + mesh.merge(upperball); + mesh.merge(lowerball); + } +}; + +// A wrapper struct around the base pool (pad) +struct Pad { +// Contour3D mesh; + TriangleMesh tmesh; + PoolConfig cfg; + double zlevel; + + Pad() {} + + Pad(const TriangleMesh& object_support_mesh, + const ExPolygons& baseplate, + double ground_level, + const PoolConfig& cfg) : zlevel(ground_level + cfg.min_wall_height_mm/2) + { + ExPolygons basep; + base_plate(object_support_mesh, basep, + float(cfg.min_wall_height_mm)/*,layer_height*/); + for(auto& bp : baseplate) basep.emplace_back(bp); + + create_base_pool(basep, tmesh, cfg); + tmesh.translate(0, 0, float(zlevel)); + } + + bool empty() const { return tmesh.facets_count() == 0; } +}; + +EigenMesh3D to_eigenmesh(const Contour3D& cntr) { + EigenMesh3D emesh; + + auto& V = emesh.V; + auto& F = emesh.F; + + V.resize(cntr.points.size(), 3); + F.resize(cntr.indices.size(), 3); + + for (int i = 0; i < V.rows(); ++i) { + V.row(i) = cntr.points[i]; + F.row(i) = cntr.indices[i]; + } + + return emesh; +} + +void create_head(TriangleMesh& out, double r1_mm, double r2_mm, double width_mm) +{ + Head head(r1_mm, r2_mm, width_mm, {0, std::sqrt(0.5), -std::sqrt(0.5)}, + {0, 0, 30}); + out.merge(mesh(head.mesh)); + + Pillar cst(head, {0, 0, 0}); + cst.add_base(); + + out.merge(mesh(cst.mesh)); + out.merge(mesh(cst.base)); +} + +// The minimum distance for two support points to remain valid. +static const double /*constexpr*/ D_SP = 0.1; + +enum { // For indexing Eigen vectors as v(X), v(Y), v(Z) instead of numbers + X, Y, Z +}; + +EigenMesh3D to_eigenmesh(const TriangleMesh& tmesh) { + + const stl_file& stl = tmesh.stl; + + EigenMesh3D outmesh; + + auto&& bb = tmesh.bounding_box(); + outmesh.ground_level += bb.min(Z); + + auto& V = outmesh.V; + auto& F = outmesh.F; + + V.resize(3*stl.stats.number_of_facets, 3); + F.resize(stl.stats.number_of_facets, 3); + for (unsigned int i=0; ivertex[0](0); V(3*i+0, 1) = + facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2); + V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = + facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2); + V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = + facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2); + + F(i, 0) = 3*i+0; + F(i, 1) = 3*i+1; + F(i, 2) = 3*i+2; + } + + return outmesh; +} + +EigenMesh3D to_eigenmesh(const ModelObject& modelobj) { + return to_eigenmesh(modelobj.raw_mesh()); +} + +EigenMesh3D to_eigenmesh(const Model& model) { + TriangleMesh combined_mesh; + + for(ModelObject *o : model.objects) { + TriangleMesh tmp = o->raw_mesh(); + for(ModelInstance * inst: o->instances) { + TriangleMesh ttmp(tmp); + inst->transform_mesh(&ttmp); + combined_mesh.merge(ttmp); + } + } + + return to_eigenmesh(combined_mesh); +} + +PointSet to_point_set(const std::vector &v) +{ + PointSet ret(v.size(), 3); + { long i = 0; for(const Vec3d& p : v) ret.row(i++) = p; } + return ret; +} + +Vec3d model_coord(const ModelInstance& object, const Vec3f& mesh_coord) { + return object.transform_vector(mesh_coord.cast()); +} + +PointSet support_points(const Model& model) { + size_t sum = 0; + for(auto *o : model.objects) + sum += o->instances.size() * o->sla_support_points.size(); + + PointSet ret(sum, 3); + + for(ModelObject *o : model.objects) + for(ModelInstance *inst : o->instances) { + int i = 0; + for(Vec3f& msource : o->sla_support_points) { + ret.row(i++) = model_coord(*inst, msource); + } + } + + return ret; +} + +PointSet support_points(const ModelObject& modelobject) +{ + PointSet ret(modelobject.sla_support_points.size(), 3); + auto rot = modelobject.instances.front()->get_rotation(); +// auto scaling = modelobject.instances.front()->get_scaling_factor(); + +// Transform3d tr; +// tr.rotate(Eigen::AngleAxisd(rot(X), Vec3d::UnitX()) * +// Eigen::AngleAxisd(rot(Y), Vec3d::UnitY())); + + long i = 0; + for(const Vec3f& msource : modelobject.sla_support_points) { + Vec3d&& p = msource.cast(); +// p = tr * p; + ret.row(i++) = p; + } + return ret; +} + +double ray_mesh_intersect(const Vec3d& s, + const Vec3d& dir, + const EigenMesh3D& m); + +PointSet normals(const PointSet& points, const EigenMesh3D& mesh); + +inline Vec2d to_vec2(const Vec3d& v3) { + return {v3(X), v3(Y)}; +} + +bool operator==(const SpatElement& e1, const SpatElement& e2) { + return e1.second == e2.second; +} + +// Clustering a set of points by the given criteria +ClusteredPoints cluster( + const PointSet& points, + std::function pred, + unsigned max_points = 0); + +class SLASupportTree::Impl { + std::vector m_heads; + std::vector m_pillars; + std::vector m_junctions; + std::vector m_bridges; + std::vector m_compact_bridges; + Pad m_pad; + mutable TriangleMesh meshcache; mutable bool meshcache_valid; + mutable double model_height = 0; // the full height of the model +public: + double ground_level = 0; + + template Head& add_head(Args&&... args) { + m_heads.emplace_back(std::forward(args)...); + m_heads.back().id = long(m_heads.size() - 1); + meshcache_valid = false; + return m_heads.back(); + } + + template Pillar& add_pillar(long headid, Args&&... args) { + assert(headid >= 0 && headid < m_heads.size()); + Head& head = m_heads[headid]; + m_pillars.emplace_back(head, std::forward(args)...); + Pillar& pillar = m_pillars.back(); + pillar.id = long(m_pillars.size() - 1); + head.pillar_id = pillar.id; + pillar.start_junction_id = head.id; + pillar.starts_from_head = true; + meshcache_valid = false; + return m_pillars.back(); + } + + const Head& pillar_head(long pillar_id) const { + assert(pillar_id >= 0 && pillar_id < m_pillars.size()); + const Pillar& p = m_pillars[pillar_id]; + assert(p.starts_from_head && p.start_junction_id >= 0 && + p.start_junction_id < m_heads.size() ); + return m_heads[p.start_junction_id]; + } + + const Pillar& head_pillar(long headid) const { + assert(headid >= 0 && headid < m_heads.size()); + const Head& h = m_heads[headid]; + assert(h.pillar_id >= 0 && h.pillar_id < m_pillars.size()); + return m_pillars[h.pillar_id]; + } + + template const Junction& add_junction(Args&&... args) { + m_junctions.emplace_back(std::forward(args)...); + m_junctions.back().id = long(m_junctions.size() - 1); + meshcache_valid = false; + return m_junctions.back(); + } + + template const Bridge& add_bridge(Args&&... args) { + m_bridges.emplace_back(std::forward(args)...); + m_bridges.back().id = long(m_bridges.size() - 1); + meshcache_valid = false; + return m_bridges.back(); + } + + template + const CompactBridge& add_compact_bridge(Args&&...args) { + m_compact_bridges.emplace_back(std::forward(args)...); + m_compact_bridges.back().id = long(m_compact_bridges.size() - 1); + meshcache_valid = false; + return m_compact_bridges.back(); + } + + const std::vector& heads() const { return m_heads; } + Head& head(size_t idx) { meshcache_valid = false; return m_heads[idx]; } + const std::vector& pillars() const { return m_pillars; } + const std::vector& bridges() const { return m_bridges; } + const std::vector& junctions() const { return m_junctions; } + const std::vector& compact_bridges() const { + return m_compact_bridges; + } + + const Pad& create_pad(const TriangleMesh& object_supports, + const ExPolygons& baseplate, + const PoolConfig& cfg) { + m_pad = Pad(object_supports, baseplate, ground_level, cfg); + return m_pad; + } + + const Pad& pad() const { return m_pad; } + + // WITHOUT THE PAD!!! + const TriangleMesh& merged_mesh() const { + if(meshcache_valid) return meshcache; + + meshcache = TriangleMesh(); + + for(auto& head : heads()) { + meshcache.merge(mesh(head.mesh)); + } + + for(auto& stick : pillars()) { + meshcache.merge(mesh(stick.mesh)); + meshcache.merge(mesh(stick.base)); + } + + for(auto& j : junctions()) { + meshcache.merge(mesh(j.mesh)); + } + + for(auto& cb : compact_bridges()) { + meshcache.merge(mesh(cb.mesh)); + } + + for(auto& bs : bridges()) { + meshcache.merge(mesh(bs.mesh)); + } + + BoundingBoxf3&& bb = meshcache.bounding_box(); + model_height = bb.max(Z) - bb.min(Z); + + meshcache_valid = true; + return meshcache; + } + + // WITH THE PAD + double full_height() const { + double h = mesh_height(); + if(!pad().empty()) h += pad().cfg.min_wall_height_mm / 2; + return h; + } + + // WITHOUT THE PAD!!! + double mesh_height() const { + if(!meshcache_valid) merged_mesh(); + return model_height; + } + +}; + +template +long cluster_centroid(const ClusterEl& clust, + std::function pointfn, + DistFn df) +{ + switch(clust.size()) { + case 0: /* empty cluster */ return -1; + case 1: /* only one element */ return 0; + case 2: /* if two elements, there is no center */ return 0; + default: ; + } + + // The function works by calculating for each point the average distance + // from all the other points in the cluster. We create a selector bitmask of + // the same size as the cluster. The bitmask will have two true bits and + // false bits for the rest of items and we will loop through all the + // permutations of the bitmask (combinations of two points). Get the + // distance for the two points and add the distance to the averages. + // The point with the smallest average than wins. + + std::vector sel(clust.size(), false); // create full zero bitmask + std::fill(sel.end() - 2, sel.end(), true); // insert the two ones + std::vector avgs(clust.size(), 0.0); // store the average distances + + do { + std::array idx; + for(size_t i = 0, j = 0; i < clust.size(); i++) if(sel[i]) idx[j++] = i; + + double d = df(pointfn(clust[idx[0]]), + pointfn(clust[idx[1]])); + + // add the distance to the sums for both associated points + for(auto i : idx) avgs[i] += d; + + // now continue with the next permutation of the bitmask with two 1s + } while(std::next_permutation(sel.begin(), sel.end())); + + // Divide by point size in the cluster to get the average (may be redundant) + for(auto& a : avgs) a /= clust.size(); + + // get the lowest average distance and return the index + auto minit = std::min_element(avgs.begin(), avgs.end()); + return long(minit - avgs.begin()); +} + +/** + * This function will calculate the convex hull of the input point set and + * return the indices of those points belonging to the chull in the right + * (counter clockwise) order. The input is also the set of indices and a + * functor to get the actual point form the index. + * + * I've adapted this algorithm from here: + * https://www.geeksforgeeks.org/convex-hull-set-1-jarviss-algorithm-or-wrapping/ + * and modified it so that it starts with the leftmost lower vertex. Also added + * support for floating point coordinates. + * + * This function is a modded version of the standard convex hull. If the points + * are all collinear with each other, it will return their indices in spatially + * subsequent order (the order they appear on the screen). + */ +ClusterEl pts_convex_hull(const ClusterEl& inpts, + std::function pfn) +{ + using Point = Vec2d; + using std::vector; + + static const double ERR = 1e-3; + + auto orientation = [](const Point& p, const Point& q, const Point& r) + { + double val = (q(Y) - p(Y)) * (r(X) - q(X)) - + (q(X) - p(X)) * (r(Y) - q(Y)); + + if (std::abs(val) < ERR) return 0; // collinear + return (val > ERR)? 1: 2; // clock or counterclockwise + }; + + size_t n = inpts.size(); + + if (n < 3) return inpts; + + // Initialize Result + ClusterEl hull; + vector points; points.reserve(n); + for(auto i : inpts) { + points.emplace_back(pfn(i)); + } + + // Check if the triplet of points is collinear. The standard convex hull + // algorithms are not capable of handling such input properly. + bool collinear = true; + for(auto one = points.begin(), two = std::next(one), three = std::next(two); + three != points.end() && collinear; + ++one, ++two, ++three) + { + // check if the points are collinear + if(orientation(*one, *two, *three) != 0) collinear = false; + } + + // Find the leftmost (bottom) point + int l = 0; + for (int i = 1; i < n; i++) { + if(std::abs(points[i](X) - points[l](X)) < ERR) { + if(points[i](Y) < points[l](Y)) l = i; + } + else if (points[i](X) < points[l](X)) l = i; + } + + if(collinear) { + // fill the output with the spatially ordered set of points. + + // find the direction + Vec2d dir = (points[l] - points[(l+1)%n]).normalized(); + hull = inpts; + auto& lp = points[l]; + std::sort(hull.begin(), hull.end(), + [&lp, points](unsigned i1, unsigned i2) { + // compare the distance from the leftmost point + return distance(lp, points[i1]) < distance(lp, points[i2]); + }); + + return hull; + } + + // TODO: this algorithm is O(m*n) and O(n^2) in the worst case so it needs + // to be replaced with a graham scan or something O(nlogn) + + // Start from leftmost point, keep moving counterclockwise + // until reach the start point again. This loop runs O(h) + // times where h is number of points in result or output. + int p = l; + do + { + // Add current point to result + hull.push_back(inpts[p]); + + // Search for a point 'q' such that orientation(p, x, + // q) is counterclockwise for all points 'x'. The idea + // is to keep track of last visited most counterclock- + // wise point in q. If any point 'i' is more counterclock- + // wise than q, then update q. + int q = (p+1)%n; + for (int i = 0; i < n; i++) + { + // If i is more counterclockwise than current q, then + // update q + if (orientation(points[p], points[i], points[q]) == 2) q = i; + } + + // Now q is the most counterclockwise with respect to p + // Set p as q for next iteration, so that q is added to + // result 'hull' + p = q; + + } while (p != l); // While we don't come to first point + + auto first = hull.front(); + hull.emplace_back(first); + + return hull; +} + +Vec3d dirv(const Vec3d& startp, const Vec3d& endp) { + return (endp - startp).normalized(); +} + +/// Generation of the supports, entry point function. This is called from the +/// SLASupportTree constructor and throws an SLASupportsStoppedException if it +/// gets canceled by the ctl object's stopcondition functor. +bool SLASupportTree::generate(const PointSet &points, + const EigenMesh3D& mesh, + const SupportConfig &cfg, + const Controller &ctl) +{ + PointSet filtered_points; // all valid support points + PointSet head_positions; // support points with pinhead + PointSet head_normals; // head normals + PointSet headless_positions; // headless support points + PointSet headless_normals; // headless support point normals + + using IndexSet = std::vector; + + // Distances from head positions to ground or mesh touch points + std::vector head_heights; + + // Indices of those who touch the ground + IndexSet ground_heads; + + // Indices of those who don't touch the ground + IndexSet noground_heads; + + ClusteredPoints ground_connectors; + + auto gnd_head_pt = [&ground_heads, &head_positions] (size_t idx) { + return Vec3d(head_positions.row(ground_heads[idx])); + }; + + using Result = SLASupportTree::Impl; + + Result& result = *m_impl; + + enum Steps { + BEGIN, + FILTER, + PINHEADS, + CLASSIFY, + ROUTING_GROUND, + ROUTING_NONGROUND, + HEADLESS, + DONE, + HALT, + ABORT, + NUM_STEPS + //... + }; + + // Debug: + // for(int pn = 0; pn < points.rows(); ++pn) { + // std::cout << "p " << pn << " " << points.row(pn) << std::endl; + // } + + auto filterfn = [] ( + const SupportConfig& cfg, + const PointSet& points, + const EigenMesh3D& mesh, + PointSet& filt_pts, + PointSet& head_norm, + PointSet& head_pos, + PointSet& headless_pos, + PointSet& headless_norm) + { + + /* ******************************************************** */ + /* Filtering step */ + /* ******************************************************** */ + + // Get the points that are too close to each other and keep only the + // first one + auto aliases = cluster(points, + [cfg](const SpatElement& p, + const SpatElement& se){ + return distance(p.first, se.first) < D_SP; + }, 2); + + filt_pts.resize(aliases.size(), 3); + int count = 0; + for(auto& a : aliases) { + // Here we keep only the front point of the cluster. TODO: centroid + filt_pts.row(count++) = points.row(a.front()); + } + + // calculate the normals to the triangles belonging to filtered points + auto nmls = sla::normals(filt_pts, mesh); + + head_norm.resize(count, 3); + head_pos.resize(count, 3); + headless_pos.resize(count, 3); + headless_norm.resize(count, 3); + + // Not all of the support points have to be a valid position for + // support creation. The angle may be inappropriate or there may + // not be enough space for the pinhead. Filtering is applied for + // these reasons. + + int pcount = 0, hlcount = 0; + for(int i = 0; i < count; i++) { + auto n = nmls.row(i); + + // for all normals we generate the spherical coordinates and + // saturate the polar angle to 45 degrees from the bottom then + // convert back to standard coordinates to get the new normal. + // Then we just create a quaternion from the two normals + // (Quaternion::FromTwoVectors) and apply the rotation to the + // arrow head. + + double z = n(2); + double r = 1.0; // for normalized vector + double polar = std::acos(z / r); + double azimuth = std::atan2(n(1), n(0)); + + if(polar >= PI / 2) { // skip if the tilt is not sane + + // We saturate the polar angle to 3pi/4 + polar = std::max(polar, 3*PI / 4); + + // Reassemble the now corrected normal + Vec3d nn(std::cos(azimuth) * std::sin(polar), + std::sin(azimuth) * std::sin(polar), + std::cos(polar)); + + // save the head (pinpoint) position + Vec3d hp = filt_pts.row(i); + + // the full width of the head + double w = cfg.head_width_mm + + cfg.head_back_radius_mm + + 2*cfg.head_front_radius_mm; + + // We should shoot a ray in the direction of the pinhead and + // see if there is enough space for it + double t = ray_mesh_intersect(hp + 0.1*nn, nn, mesh); + + if(t > 2*w || std::isinf(t)) { + // 2*w because of lower and upper pinhead + + head_pos.row(pcount) = hp; + + // save the verified and corrected normal + head_norm.row(pcount) = nn; + + ++pcount; + } else { + headless_norm.row(hlcount) = nn; + headless_pos.row(hlcount++) = hp; + } + } + } + + head_pos.conservativeResize(pcount, Eigen::NoChange); + head_norm.conservativeResize(pcount, Eigen::NoChange); + headless_pos.conservativeResize(hlcount, Eigen::NoChange); + headless_norm.conservativeResize(hlcount, Eigen::NoChange); + }; + + // Function to write the pinheads into the result + auto pinheadfn = [] ( + const SupportConfig& cfg, + PointSet& head_pos, + PointSet& nmls, + Result& result + ) + { + + /* ******************************************************** */ + /* Generating Pinheads */ + /* ******************************************************** */ + + for (int i = 0; i < head_pos.rows(); ++i) { + result.add_head( + cfg.head_back_radius_mm, + cfg.head_front_radius_mm, + cfg.head_width_mm, + nmls.row(i), // dir + head_pos.row(i) // displacement + ); + } + }; + + // &filtered_points, &head_positions, &result, &mesh, + // &gndidx, &gndheight, &nogndidx, cfg + auto classifyfn = [] ( + const SupportConfig& cfg, + const EigenMesh3D& mesh, + PointSet& head_pos, + IndexSet& gndidx, + IndexSet& nogndidx, + std::vector& gndheight, + ClusteredPoints& ground_clusters, + Result& result + ) { + + /* ******************************************************** */ + /* Classification */ + /* ******************************************************** */ + + // We should first get the heads that reach the ground directly + gndheight.reserve(head_pos.rows()); + gndidx.reserve(head_pos.rows()); + nogndidx.reserve(head_pos.rows()); + + for(unsigned i = 0; i < head_pos.rows(); i++) { + auto& head = result.heads()[i]; + + Vec3d dir(0, 0, -1); + Vec3d startpoint = head.junction_point(); + + double t = ray_mesh_intersect(startpoint, dir, mesh); + + gndheight.emplace_back(t); + + if(std::isinf(t)) gndidx.emplace_back(i); + else nogndidx.emplace_back(i); + } + + PointSet gnd(gndidx.size(), 3); + + for(size_t i = 0; i < gndidx.size(); i++) + gnd.row(i) = head_pos.row(gndidx[i]); + + // We want to search for clusters of points that are far enough from + // each other in the XY plane to not cross their pillar bases + // These clusters of support points will join in one pillar, possibly in + // their centroid support point. + auto d_base = 2*cfg.base_radius_mm; + ground_clusters = cluster(gnd, + [d_base, &cfg](const SpatElement& p, const SpatElement& s){ + return distance(Vec2d(p.first(X), p.first(Y)), + Vec2d(s.first(X), s.first(Y))) < d_base; + }, 3); // max 3 heads to connect to one centroid + }; + + // Helper function for interconnecting two pillars with zig-zag bridges + auto interconnect = [&cfg]( + const Pillar& pillar, + const Pillar& nextpillar, + const EigenMesh3D& emesh, + Result& result) + { + const Head& phead = result.pillar_head(pillar.id); + const Head& nextphead = result.pillar_head(nextpillar.id); + +// double d = 2*pillar.r; +// const Vec3d& pp = pillar.endpoint.cwiseProduct(Vec3d{1, 1, 0}); + + Vec3d sj = phead.junction_point(); + sj(Z) = std::min(sj(Z), nextphead.junction_point()(Z)); + Vec3d ej = nextpillar.endpoint; + double pillar_dist = distance(Vec2d{sj(X), sj(Y)}, + Vec2d{ej(X), ej(Y)}); + double zstep = pillar_dist * std::tan(-cfg.tilt); + ej(Z) = sj(Z) + zstep; + + double chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); + double bridge_distance = pillar_dist / std::cos(-cfg.tilt); + + // If the pillars are so close that they touch each other, + // there is no need to bridge them together. + if(pillar_dist > 2*cfg.pillar_radius_mm && + bridge_distance < cfg.max_bridge_length_mm) + while(sj(Z) > pillar.endpoint(Z) && + ej(Z) > nextpillar.endpoint(Z)) + { + if(chkd >= bridge_distance) { + result.add_bridge(sj, ej, pillar.r); + + // double bridging: (crosses) + if(bridge_distance > 2*cfg.base_radius_mm) { + // If the columns are close together, no need to + // double bridge them + Vec3d bsj(ej(X), ej(Y), sj(Z)); + Vec3d bej(sj(X), sj(Y), ej(Z)); + + // need to check collision for the cross stick + double backchkd = ray_mesh_intersect(bsj, + dirv(bsj, bej), + emesh); + + if(backchkd >= bridge_distance) { + result.add_bridge(bsj, bej, pillar.r); + } + } + } + sj.swap(ej); + ej(Z) = sj(Z) + zstep; + chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); + } + }; + + auto routing_ground_fn = [gnd_head_pt, interconnect]( + const SupportConfig& cfg, + const ClusteredPoints& gnd_clusters, + const IndexSet& gndidx, + const EigenMesh3D& emesh, + Result& result) + { + const double hbr = cfg.head_back_radius_mm; + const double pradius = cfg.pillar_radius_mm; + const double maxbridgelen = cfg.max_bridge_length_mm; + const double gndlvl = result.ground_level; + + ClusterEl cl_centroids; + cl_centroids.reserve(gnd_clusters.size()); + + SpatIndex pheadindex; // spatial index for the junctions + for(auto cl : gnd_clusters) { + // place all the centroid head positions into the index. We will + // query for alternative pillar positions. If a sidehead cannot + // connect to the cluster centroid, we have to search for another + // head with a full pillar. Also when there are two elements in the + // cluster, the centroid is arbitrary and the sidehead is allowed to + // connect to a nearby pillar to increase structural stability. + + // get the current cluster centroid + unsigned cid = cluster_centroid(cl, gnd_head_pt, + [](const Vec3d& p1, const Vec3d& p2) + { + return distance(Vec2d(p1(X), p1(Y)), Vec2d(p2(X), p2(Y))); + }); + + cl_centroids.push_back(cid); + + unsigned hid = gndidx[cl[cid]]; // Head index + Head& h = result.head(hid); + h.transform(); + Vec3d p = h.junction_point(); p(Z) = gndlvl; + + pheadindex.insert(p, hid); + } + + // now we will go through the clusters ones again and connect the + // sidepoints with the cluster centroid (which is a ground pillar) + // or a nearby pillar if the centroid is unreachable. + long ci = 0; + for(auto cl : gnd_clusters) { + auto cidx = cl_centroids[ci]; + cl_centroids[ci++] = cl[cidx]; + + long index_to_heads = gndidx[cl[cidx]]; + auto& head = result.head(index_to_heads); + + Vec3d startpoint = head.junction_point(); + auto endpoint = startpoint; endpoint(Z) = gndlvl; + + // Create the central pillar of the cluster with its base on the + // ground + result.add_pillar(index_to_heads, endpoint, pradius) + .add_base(cfg.base_height_mm, cfg.base_radius_mm); + + // Process side point in current cluster + cl.erase(cl.begin() + cidx); // delete the centroid before looping + + // TODO: dont consider the cluster centroid but calculate a central + // position where the pillar can be placed. this way the weight + // is distributed more effectively on the pillar. + + auto search_nearest = + [&cfg, &result, &emesh, maxbridgelen, gndlvl] + (SpatIndex& spindex, const Vec3d& jsh) + { + long nearest_id = -1; + const double max_len = maxbridgelen / 2; + while(nearest_id < 0 && !spindex.empty()) { + // loop until a suitable head is not found + // if there is a pillar closer than the cluster center + // (this may happen as the clustering is not perfect) + // than we will bridge to this closer pillar + + Vec3d qp(jsh(X), jsh(Y), gndlvl); + auto ne = spindex.nearest(qp, 1).front(); + const Head& nearhead = result.heads()[ne.second]; + + Vec3d jh = nearhead.junction_point(); + Vec3d jp = jsh; + double dist2d = distance(qp, ne.first); + + // Bridge endpoint on the main pillar + Vec3d jn(jh(X), jh(Y), jp(Z) + dist2d*std::tan(-cfg.tilt)); + + if(jn(Z) > jh(Z)) { + // If the sidepoint cannot connect to the pillar from + // its head junction, then just skip this pillar. + spindex.remove(ne); + continue; + } + + double d = distance(jp, jn); + if(jn(Z) <= gndlvl || d > max_len) break; + + double chkd = ray_mesh_intersect(jp, dirv(jp, jn), emesh); + if(chkd >= d) nearest_id = ne.second; + + spindex.remove(ne); + } + return nearest_id; + }; + + for(auto c : cl) { + auto& sidehead = result.head(gndidx[c]); + sidehead.transform(); + + Vec3d jsh = sidehead.junction_point(); +// Vec3d jp2d = {jsh(X), jsh(Y), gndlvl}; + SpatIndex spindex = pheadindex; + long nearest_id = search_nearest(spindex, jsh); + + // at this point we either have our pillar index or we have + // to connect the sidehead to the ground + if(nearest_id < 0) { + // Could not find a pillar, create one + Vec3d jp = jsh; jp(Z) = gndlvl; + result.add_pillar(sidehead.id, jp, pradius). + add_base(cfg.base_height_mm, cfg.base_radius_mm); + + // connects to ground, eligible for bridging + cl_centroids.emplace_back(sidehead.id); + } else { + // Creating the bridge to the nearest pillar + + const Head& nearhead = result.heads()[nearest_id]; + Vec3d jp = jsh; + Vec3d jh = nearhead.junction_point(); + + double d = distance(Vec2d{jp(X), jp(Y)}, + Vec2d{jh(X), jh(Y)}); + Vec3d jn(jh(X), jh(Y), jp(Z) + d*std::tan(-cfg.tilt)); + + if(jn(Z) > jh(Z)) { + double hdiff = jn(Z) - jh(Z); + jp(Z) -= hdiff; + jn(Z) -= hdiff; + + // pillar without base, this does not connect to ground. + result.add_pillar(sidehead.id, jp, pradius); + } + + if(jp(Z) < jsh(Z)) result.add_junction(jp, hbr); + if(jn(Z) >= jh(Z)) result.add_junction(jn, hbr); + double r_pillar = sidehead.request_pillar_radius(pradius); + result.add_bridge(jp, jn, r_pillar); + } + } + } + + // We will break down the pillar positions in 2D into concentric rings. + // Connecting the pillars belonging to the same ring will prevent + // bridges from crossing each other. After bridging the rings we can + // create bridges between the rings without the possibility of crossing + // bridges. Two pillars will be bridged with X shaped stick pairs. + // If they are really close to each other, than only one stick will be + // used in zig-zag mode. + + // Breaking down the points into rings will be done with a modified + // convex hull algorithm (see pts_convex_hull()), that works for + // collinear points as well. If the points are on the same surface, + // they can be part of an imaginary line segment for which the convex + // hull is not defined. I this case it is enough to sort the points + // spatially and create the bridge stick from the one endpoint to + // another. + + ClusterEl rem = cl_centroids; + ClusterEl ring; + + while(!rem.empty()) { // loop until all the points belong to some ring + std::sort(rem.begin(), rem.end()); + + auto newring = pts_convex_hull(rem, + [gnd_head_pt](unsigned i) { + auto&& p = gnd_head_pt(i); + return Vec2d(p(X), p(Y)); // project to 2D in along Z axis + }); + + if(!ring.empty()) { + // inner ring is now in 'newring' and outer ring is in 'ring' + SpatIndex innerring; + for(unsigned i : newring) { + const Pillar& pill = result.head_pillar(gndidx[i]); + innerring.insert(pill.endpoint, pill.id); + } + + // For all pillars in the outer ring find the closest in the + // inner ring and connect them. This will create the spider web + // fashioned connections between pillars + for(unsigned i : ring) { + const Pillar& outerpill = result.head_pillar(gndidx[i]); + auto res = innerring.nearest(outerpill.endpoint, 1); + if(res.empty()) continue; + + auto ne = res.front(); + const Pillar& innerpill = result.pillars()[ne.second]; + interconnect(outerpill, innerpill, emesh, result); + } + } + + // no need for newring anymore in the current iteration + ring.swap(newring); + + /*std::cout << "ring: \n"; + for(auto ri : ring) { + std::cout << ri << " " << " X = " << gnd_head_pt(ri)(X) + << " Y = " << gnd_head_pt(ri)(Y) << std::endl; + } + std::cout << std::endl;*/ + + // now the ring has to be connected with bridge sticks + for(auto it = ring.begin(), next = std::next(it); + next != ring.end(); + ++it, ++next) + { + const Pillar& pillar = result.head_pillar(gndidx[*it]); + const Pillar& nextpillar = result.head_pillar(gndidx[*next]); + interconnect(pillar, nextpillar, emesh, result); + } + + auto sring = ring; ClusterEl tmp; + std::sort(sring.begin(), sring.end()); + std::set_difference(rem.begin(), rem.end(), + sring.begin(), sring.end(), + std::back_inserter(tmp)); + rem.swap(tmp); + } + }; + + auto routing_nongnd_fn = []( + const SupportConfig& cfg, + const std::vector& gndheight, + const IndexSet& nogndidx, + Result& result) + { + // TODO: connect these to the ground pillars if possible + for(auto idx : nogndidx) { + auto& head = result.head(idx); + head.transform(); + + double gh = gndheight[idx]; + Vec3d headend = head.junction_point(); + + Head base_head(cfg.head_back_radius_mm, + cfg.head_front_radius_mm, + cfg.head_width_mm, + {0.0, 0.0, 1.0}, + {headend(X), headend(Y), headend(Z) - gh}); + + base_head.transform(); + + double hl = head.fullwidth() - head.r_back_mm; + + result.add_pillar(idx, + Vec3d{headend(X), headend(Y), headend(Z) - gh + hl}, + cfg.pillar_radius_mm + ).base = base_head.mesh; + } + }; + + auto process_headless = []( + const SupportConfig& cfg, + const PointSet& headless_pts, + const PointSet& headless_norm, + const EigenMesh3D& emesh, + Result& result) + { + // For now we will just generate smaller headless sticks with a sharp + // ending point that connects to the mesh surface. + + const double R = 0.5*cfg.pillar_radius_mm; + const double HWIDTH_MM = R/3; + + // We will sink the pins into the model surface for a distance of 1/3 of + // HWIDTH_MM + for(int i = 0; i < headless_pts.rows(); i++) { + Vec3d sp = headless_pts.row(i); + + Vec3d n = headless_norm.row(i); + sp = sp - n * HWIDTH_MM; + + Vec3d dir = {0, 0, -1}; + Vec3d sj = sp + R * n; + double dist = ray_mesh_intersect(sj, dir, emesh); + + if(std::isinf(dist) || std::isnan(dist)) continue; + + Vec3d ej = sj + (dist + HWIDTH_MM)* dir; + result.add_compact_bridge(sp, ej, n, R); + } + }; + + using std::ref; + using std::cref; + using std::bind; + + // Here we can easily track what goes in and what comes out of each step: + // (see the cref-s as inputs and ref-s as outputs) + std::array, NUM_STEPS> program = { + [] () { + // Begin + // clear up the shared data + }, + + // Filtering unnecessary support points + bind(filterfn, cref(cfg), cref(points), cref(mesh), + ref(filtered_points), ref(head_normals), + ref(head_positions), ref(headless_positions), ref(headless_normals)), + + // Pinhead generation + bind(pinheadfn, cref(cfg), + ref(head_positions), ref(head_normals), ref(result)), + + // Classification of support points + bind(classifyfn, cref(cfg), cref(mesh), + ref(head_positions), ref(ground_heads), ref(noground_heads), + ref(head_heights), ref(ground_connectors), ref(result)), + + // Routing ground connecting clusters + bind(routing_ground_fn, + cref(cfg), cref(ground_connectors), cref(ground_heads), cref(mesh), + ref(result)), + + // routing non ground connecting support points + bind(routing_nongnd_fn, cref(cfg), cref(head_heights), cref(noground_heads), + ref(result)), + + bind(process_headless, + cref(cfg), cref(headless_positions), + cref(headless_normals), cref(mesh), + ref(result)), + [] () { + // Done + }, + [] () { + // Halt + }, + [] () { + // Abort + } + }; + + Steps pc = BEGIN, pc_prev = BEGIN; + + auto progress = [&ctl, &pc, &pc_prev] () { + static const std::array stepstr { + "Starting", + "Filtering", + "Generate pinheads", + "Classification", + "Routing to ground", + "Routing supports to model surface", + "Processing small holes", + "Done", + "Halt", + "Abort" + }; + + static const std::array stepstate { + 0, + 10, + 30, + 50, + 60, + 70, + 80, + 100, + 0, + 0 + }; + + if(ctl.stopcondition()) pc = ABORT; + + switch(pc) { + case BEGIN: pc = FILTER; break; + case FILTER: pc = PINHEADS; break; + case PINHEADS: pc = CLASSIFY; break; + case CLASSIFY: pc = ROUTING_GROUND; break; + case ROUTING_GROUND: pc = ROUTING_NONGROUND; break; + case ROUTING_NONGROUND: pc = HEADLESS; break; + case HEADLESS: pc = DONE; break; + case HALT: pc = pc_prev; break; + case DONE: + case ABORT: break; + } + ctl.statuscb(stepstate[pc], stepstr[pc]); + }; + + // Just here we run the computation... + while(pc < DONE || pc == HALT) { + progress(); + program[pc](); + } + + if(pc == ABORT) throw SLASupportsStoppedException(); + + return pc == ABORT; +} + +void SLASupportTree::merged_mesh(TriangleMesh &outmesh) const +{ + outmesh.merge(get().merged_mesh()); +} + +void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const { + merged_mesh(outmesh); + outmesh.merge(get_pad()); +} + +SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const +{ + if(init_layerh < 0) init_layerh = layerh; + auto& stree = get(); + + const auto modelh = float(stree.full_height()); + auto gndlvl = float(this->m_impl->ground_level); + const Pad& pad = m_impl->pad(); + if(!pad.empty()) gndlvl -= float(pad.cfg.min_wall_height_mm/2); + + std::vector heights = {gndlvl}; + heights.reserve(size_t(modelh/layerh) + 1); + + for(float h = gndlvl + init_layerh; h < gndlvl + modelh; h += layerh) { + heights.emplace_back(h); + } + + TriangleMesh fullmesh = m_impl->merged_mesh(); + fullmesh.merge(get_pad()); + TriangleMeshSlicer slicer(&fullmesh); + SlicedSupports ret; + slicer.slice(heights, &ret, m_ctl.cancelfn); + + return ret; +} + +const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate, + double min_wall_thickness_mm, + double min_wall_height_mm, + double max_merge_distance_mm, + double edge_radius_mm) const +{ + TriangleMesh mm; + merged_mesh(mm); + PoolConfig pcfg; +// pcfg.min_wall_thickness_mm = min_wall_thickness_mm; +// pcfg.min_wall_height_mm = min_wall_height_mm; +// pcfg.max_merge_distance_mm = max_merge_distance_mm; +// pcfg.edge_radius_mm = edge_radius_mm; + return m_impl->create_pad(mm, baseplate, pcfg).tmesh; +} + +const TriangleMesh &SLASupportTree::get_pad() const +{ + return m_impl->pad().tmesh; +} + +double SLASupportTree::get_elevation() const +{ + double ph = m_impl->pad().empty()? 0 : + m_impl->pad().cfg.min_wall_height_mm/2.0; + return -m_impl->ground_level + ph; +} + +SLASupportTree::SLASupportTree(const Model& model, + const SupportConfig& cfg, + const Controller& ctl): + m_impl(new Impl()), m_ctl(ctl) +{ + generate(support_points(model), to_eigenmesh(model), cfg, ctl); +} + +SLASupportTree::SLASupportTree(const PointSet &points, + const EigenMesh3D& emesh, + const SupportConfig &cfg, + const Controller &ctl): + m_impl(new Impl()), m_ctl(ctl) +{ + m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm; + generate(points, emesh, cfg, ctl); +} + +SLASupportTree::SLASupportTree(const SLASupportTree &c): + m_impl(new Impl(*c.m_impl)), m_ctl(c.m_ctl) {} + +SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c) +{ + m_impl = make_unique(*c.m_impl); + return *this; +} + +SLASupportTree::~SLASupportTree() {} + +void add_sla_supports(Model &model, + const SupportConfig &cfg, + const Controller &ctl) +{ + Benchmark bench; + + bench.start(); + SLASupportTree _stree(model, cfg, ctl); + bench.stop(); + + std::cout << "Support tree creation time: " << bench.getElapsedSec() + << " seconds" << std::endl; + + bench.start(); + ModelObject* o = model.add_object(); + o->add_instance(); + + TriangleMesh streemsh; + _stree.merged_mesh(streemsh); + o->add_volume(streemsh); + + bench.stop(); + std::cout << "support tree added to model in: " << bench.getElapsedSec() + << " seconds" << std::endl; + + // TODO this would roughly be the code for the base pool + ExPolygons plate; + auto modelmesh = model.mesh(); + TriangleMesh poolmesh; + sla::PoolConfig poolcfg; + poolcfg.min_wall_height_mm = 1; + poolcfg.edge_radius_mm = 0.1; + poolcfg.min_wall_thickness_mm = 0.8; + + bench.start(); + sla::base_plate(modelmesh, plate); + bench.stop(); + + std::cout << "Base plate calculation time: " << bench.getElapsedSec() + << " seconds." << std::endl; + + bench.start(); + sla::create_base_pool(plate, poolmesh, poolcfg); + bench.stop(); + + std::cout << "Pool generation completed in " << bench.getElapsedSec() + << " second." << std::endl; + + bench.start(); + poolmesh.translate(.0f, .0f, float(poolcfg.min_wall_height_mm / 2)); + o->add_volume(poolmesh); + bench.stop(); + + // TODO: will cause incorrect placement of the model; +// o->translate({0, 0, poolcfg.min_wall_height_mm / 2}); + + std::cout << "Added pool to model in " << bench.getElapsedSec() + << " seconds." << std::endl; + +} + +} +} diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp new file mode 100644 index 0000000000..f0605a356f --- /dev/null +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -0,0 +1,197 @@ +#ifndef SLASUPPORTTREE_HPP +#define SLASUPPORTTREE_HPP + +#include +#include +#include +#include +#include + +namespace Slic3r { + +// Needed types from Point.hpp +typedef int32_t coord_t; +typedef Eigen::Matrix Vec3d; +typedef Eigen::Matrix Vec3f; +typedef Eigen::Matrix Vec3crd; +typedef std::vector Pointf3s; +typedef std::vector Points3; + +class TriangleMesh; +class Model; +class ModelInstance; +class ModelObject; +class ExPolygon; + +using SliceLayer = std::vector; +using SlicedSupports = std::vector; + +namespace sla { + +struct SupportConfig { + // Radius in mm of the pointing side of the head. + double head_front_radius_mm = 0.2; + + // How much the pinhead has to penetrate the model surface + double head_penetraiton = 0.2; + + // Radius of the back side of the 3d arrow. + double head_back_radius_mm = 0.5; + + // Width in mm from the back sphere center to the front sphere center. + double head_width_mm = 1.0; + + // Radius in mm of the support pillars. + // Warning: this value will be at most 65% of head_back_radius_mm + // TODO: This parameter is invalid. The pillar radius will be dynamic in + // nature. Merged pillars will have an increased thickness. This parameter + // may serve as the maximum radius, or maybe an increase when two are merged + // The default radius will be derived from head_back_radius_mm + double pillar_radius_mm = 0.8; + + // Radius in mm of the pillar base. + double base_radius_mm = 2.0; + + // The height of the pillar base cone in mm. + double base_height_mm = 1.0; + + // The default angle for connecting support sticks and junctions. + double tilt = M_PI/4; + + // The max length of a bridge in mm + double max_bridge_length_mm = 15.0; + + // The elevation in Z direction upwards. This is the space between the pad + // and the model object's bounding box bottom. + double object_elevation_mm = 10; +}; + +/// A Control structure for the support calculation. Consists of the status +/// indicator callback and the stop condition predicate. +struct Controller { + + // This will signal the status of the calculation to the front-end + std::function statuscb = + [](unsigned, const std::string&){}; + + // Returns true if the calculation should be aborted. + std::function stopcondition = [](){ return false; }; + + // Similar to cancel callback. This should check the stop condition and + // if true, throw an appropriate exception. (TriangleMeshSlicer needs this) + // consider it a hard abort. stopcondition is permits the algorithm to + // terminate itself + std::function cancelfn = [](){}; +}; + +/// An index-triangle structure for libIGL functions. Also serves as an +/// alternative (raw) input format for the SLASupportTree +struct EigenMesh3D { + Eigen::MatrixXd V; + Eigen::MatrixXi F; + double ground_level = 0; + + // igl crashes with the following data types: +// Eigen::Matrix V; +// Eigen::Matrix F; +}; + +//using PointSet = Eigen::Matrix; //Eigen::MatrixXd; +using PointSet = Eigen::MatrixXd; + +/* ************************************************************************** */ +/* TODO: May not be needed: */ +/* ************************************************************************** */ + +void create_head(TriangleMesh&, double r1_mm, double r2_mm, double width_mm); + +/// Add support volumes to the model directly +void add_sla_supports(Model& model, const SupportConfig& cfg = {}, + const Controller& ctl = {}); + +EigenMesh3D to_eigenmesh(const TriangleMesh& m); +PointSet to_point_set(const std::vector&); + + +// obsolete, not used anymore +EigenMesh3D to_eigenmesh(const Model& model); +EigenMesh3D to_eigenmesh(const ModelObject& model); +PointSet support_points(const ModelObject& modelobject); +PointSet support_points(const Model& model); + + +/* ************************************************************************** */ + +/// Just a wrapper to the runtime error to be recognizable in try blocks +class SLASupportsStoppedException: public std::runtime_error { +public: + using std::runtime_error::runtime_error; + SLASupportsStoppedException(): std::runtime_error("") {} +}; + +/// The class containing mesh data for the generated supports. +class SLASupportTree { + class Impl; + std::unique_ptr m_impl; + Controller m_ctl; + + Impl& get() { return *m_impl; } + const Impl& get() const { return *m_impl; } + + friend void add_sla_supports(Model&, + const SupportConfig&, + const Controller&); + + /// Generate the 3D supports for a model intended for SLA print. + bool generate(const PointSet& pts, + const EigenMesh3D& mesh, + const SupportConfig& cfg = {}, + const Controller& ctl = {}); +public: + + // Constructors will throw if the stop condition becomes true. + SLASupportTree(const Model& model, + const SupportConfig& cfg = {}, + const Controller& ctl = {}); + + SLASupportTree(const PointSet& pts, + const EigenMesh3D& em, + const SupportConfig& cfg = {}, + const Controller& ctl = {}); + + SLASupportTree(const SLASupportTree&); + SLASupportTree& operator=(const SLASupportTree&); + + ~SLASupportTree(); + + /// Get the whole mesh united into the output TriangleMesh + /// WITHOUT THE PAD + void merged_mesh(TriangleMesh& outmesh) const; + + void merged_mesh_with_pad(TriangleMesh&) const; + + /// Get the sliced 2d layers of the support geometry. + SlicedSupports slice(float layerh, float init_layerh = -1.0) const; + + /// Adding the "pad" (base pool) under the supports + const TriangleMesh& add_pad(const SliceLayer& baseplate, + double min_wall_thickness_mm, + double min_wall_height_mm, + double max_merge_distance_mm, + double edge_radius_mm) const; + + /// Get the pad geometry + const TriangleMesh& get_pad() const; + + /// The Z offset to raise the model and the supports to the ground level. + /// This is the elevation given in the support config and the height of the + /// pad (if requested). + double get_elevation() const; + +}; + +} + +} + +#endif // SLASUPPORTTREE_HPP diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp new file mode 100644 index 0000000000..42799ffd0e --- /dev/null +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -0,0 +1,252 @@ +#include "SLA/SLASupportTree.hpp" +#include "SLA/SLABoilerPlate.hpp" +#include "SLA/SLASpatIndex.hpp" + +// HEAVY headers... takes eternity to compile + +// for concave hull merging decisions +#include "SLABoostAdapter.hpp" +#include "boost/geometry/index/rtree.hpp" + +#include + +#if !defined(_MSC_VER) || defined(_WIN64) +#define IGL_COMPATIBLE +#endif + +#ifdef IGL_COMPATIBLE +#include +#endif + +#include "SLASpatIndex.hpp" +#include "ClipperUtils.hpp" + +namespace Slic3r { +namespace sla { + +class SpatIndex::Impl { +public: + using BoostIndex = boost::geometry::index::rtree< SpatElement, + boost::geometry::index::rstar<16, 4> /* ? */ >; + + BoostIndex m_store; +}; + +SpatIndex::SpatIndex(): m_impl(new Impl()) {} +SpatIndex::~SpatIndex() {} + +SpatIndex::SpatIndex(const SpatIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {} +SpatIndex::SpatIndex(SpatIndex&& cpy): m_impl(std::move(cpy.m_impl)) {} + +SpatIndex& SpatIndex::operator=(const SpatIndex &cpy) +{ + m_impl.reset(new Impl(*cpy.m_impl)); + return *this; +} + +SpatIndex& SpatIndex::operator=(SpatIndex &&cpy) +{ + m_impl.swap(cpy.m_impl); + return *this; +} + +void SpatIndex::insert(const SpatElement &el) +{ + m_impl->m_store.insert(el); +} + +bool SpatIndex::remove(const SpatElement& el) +{ + return m_impl->m_store.remove(el) == 1; +} + +std::vector +SpatIndex::query(std::function fn) +{ + namespace bgi = boost::geometry::index; + + std::vector ret; + m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret)); + return ret; +} + +std::vector SpatIndex::nearest(const Vec3d &el, unsigned k = 1) +{ + namespace bgi = boost::geometry::index; + std::vector ret; ret.reserve(k); + m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret)); + return ret; +} + +size_t SpatIndex::size() const +{ + return m_impl->m_store.size(); +} + +PointSet normals(const PointSet& points, const EigenMesh3D& mesh) { +#ifdef IGL_COMPATIBLE + Eigen::VectorXd dists; + Eigen::VectorXi I; +// Eigen::Matrix dists; +// Eigen::Matrix I; + PointSet C; + + igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C); + + PointSet ret(I.rows(), 3); + for(int i = 0; i < I.rows(); i++) { + auto idx = I(i); + auto trindex = mesh.F.row(idx); + + auto& p1 = mesh.V.row(trindex(0)); + auto& p2 = mesh.V.row(trindex(1)); + auto& p3 = mesh.V.row(trindex(2)); + + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + ret.row(i) = U.cross(V).normalized(); + } + + return ret; +#else + return {}; +#endif +} + +double ray_mesh_intersect(const Vec3d& s, + const Vec3d& dir, + const EigenMesh3D& m) +{ + igl::Hit hit; + hit.t = std::numeric_limits::infinity(); + igl::ray_mesh_intersect(s, dir, m.V, m.F, hit); + return hit.t; +} + +// Clustering a set of points by the given criteria +ClusteredPoints cluster( + const sla::PointSet& points, + std::function pred, + unsigned max_points = 0) +{ + + namespace bgi = boost::geometry::index; + using Index3D = bgi::rtree< SpatElement, bgi::rstar<16, 4> /* ? */ >; + + // A spatial index for querying the nearest points + Index3D sindex; + + // Build the index + for(unsigned idx = 0; idx < points.rows(); idx++) + sindex.insert( std::make_pair(points.row(idx), idx)); + + using Elems = std::vector; + + // Recursive function for visiting all the points in a given distance to + // each other + std::function group = + [&sindex, &group, pred, max_points](Elems& pts, Elems& cluster) + { + for(auto& p : pts) { + std::vector tmp; + + sindex.query( + bgi::satisfies([p, pred](const SpatElement& se) { + return pred(p, se); + }), + std::back_inserter(tmp) + ); + + auto cmp = [](const SpatElement& e1, const SpatElement& e2){ + return e1.second < e2.second; + }; + + std::sort(tmp.begin(), tmp.end(), cmp); + + Elems newpts; + std::set_difference(tmp.begin(), tmp.end(), + cluster.begin(), cluster.end(), + std::back_inserter(newpts), cmp); + + int c = max_points && newpts.size() + cluster.size() > max_points? + int(max_points - cluster.size()) : int(newpts.size()); + + cluster.insert(cluster.end(), newpts.begin(), newpts.begin() + c); + std::sort(cluster.begin(), cluster.end(), cmp); + + if(!newpts.empty() && (!max_points || cluster.size() < max_points)) + group(newpts, cluster); + } + }; + + std::vector clusters; + for(auto it = sindex.begin(); it != sindex.end();) { + Elems cluster = {}; + Elems pts = {*it}; + group(pts, cluster); + + for(auto& c : cluster) sindex.remove(c); + it = sindex.begin(); + + clusters.emplace_back(cluster); + } + + ClusteredPoints result; + for(auto& cluster : clusters) { + result.emplace_back(); + for(auto c : cluster) result.back().emplace_back(c.second); + } + + return result; +} + +using Segments = std::vector>; + +Segments model_boundary(const EigenMesh3D& emesh, double offs) +{ + Segments ret; + Polygons pp; + pp.reserve(emesh.F.rows()); + + for (int i = 0; i < emesh.F.rows(); i++) { + auto trindex = emesh.F.row(i); + auto& p1 = emesh.V.row(trindex(0)); + auto& p2 = emesh.V.row(trindex(1)); + auto& p3 = emesh.V.row(trindex(2)); + + Polygon p; + p.points.resize(3); + p.points[0] = Point::new_scale(p1(X), p1(Y)); + p.points[1] = Point::new_scale(p2(X), p2(Y)); + p.points[2] = Point::new_scale(p3(X), p3(Y)); + p.make_counter_clockwise(); + pp.emplace_back(p); + } + + ExPolygons merged = union_ex(offset(pp, float(scale_(offs))), true); + + for(auto& expoly : merged) { + auto lines = expoly.lines(); + for(Line& l : lines) { + Vec2d a(l.a(X) * SCALING_FACTOR, l.a(Y) * SCALING_FACTOR); + Vec2d b(l.b(X) * SCALING_FACTOR, l.b(Y) * SCALING_FACTOR); + ret.emplace_back(std::make_pair(a, b)); + } + } + + return ret; +} + +//struct SegmentIndex { + +//}; + +//using SegmentIndexEl = std::pair; + +//SegmentIndexEl + + + + +} +} diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp new file mode 100644 index 0000000000..da24e71c69 --- /dev/null +++ b/src/libslic3r/SLAPrint.cpp @@ -0,0 +1,491 @@ +#include "SLAPrint.hpp" +#include "SLA/SLASupportTree.hpp" +#include "SLA/SLABasePool.hpp" + +#include +//#include //#include "tbb/mutex.h" + +#include "I18N.hpp" + +//! macro used to mark string used at localization, +//! return same string +#define L(s) Slic3r::I18N::translate(s) + +namespace Slic3r { + +using SlicedModel = SlicedSupports; +using SupportTreePtr = std::unique_ptr; + +class SLAPrintObject::SupportData { +public: + sla::EigenMesh3D emesh; // index-triangle representation + sla::PointSet support_points; // all the support points (manual/auto) + SupportTreePtr support_tree_ptr; // the supports + SlicedSupports support_slices; // sliced supports +}; + +namespace { + +const std::array OBJ_STEP_LEVELS = +{ + 0, + 20, + 30, + 50, + 70, + 90 +}; + +const std::array OBJ_STEP_LABELS = +{ + L("Slicing model"), // slaposObjectSlice, + L("Generating islands"), // slaposSupportIslands, + L("Scanning model structure"), // slaposSupportPoints, + L("Generating support tree"), // slaposSupportTree, + L("Generating base pool"), // slaposBasePool, + L("Slicing supports") // slaposSliceSupports, +}; + +const std::array PRINT_STEP_LEVELS = +{ + // This is after processing all the Print objects, so we start from 50% + 50, // slapsRasterize + 90, // slapsValidate +}; + +const std::array PRINT_STEP_LABELS = +{ + L("Rasterizing layers"), // slapsRasterize + L("Validating"), // slapsValidate +}; + +} + +void SLAPrint::clear() +{ + tbb::mutex::scoped_lock lock(this->cancel_mutex()); + // The following call should stop background processing if it is running. + this->invalidate_all_steps(); + + for (SLAPrintObject *object : m_objects) delete object; + m_objects.clear(); +} + +SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, + const DynamicPrintConfig &config_in) +{ +// if (m_objects.empty()) +// return APPLY_STATUS_UNCHANGED; + + // Grab the lock for the Print / PrintObject milestones. + tbb::mutex::scoped_lock lock(this->cancel_mutex()); + if(m_objects.empty() && model.objects.empty()) + return APPLY_STATUS_UNCHANGED; + + // Temporary: just to have to correct layer height for the rasterization + DynamicPrintConfig config(config_in); + config.normalize(); + auto lh = config.opt("layer_height"); + + // Temporary quick fix, just invalidate everything. + { + for (SLAPrintObject *print_object : m_objects) { + print_object->invalidate_all_steps(); + delete print_object; + } + m_objects.clear(); + this->invalidate_all_steps(); + + // Copy the model by value (deep copy), + // keep the Model / ModelObject / ModelInstance / ModelVolume IDs. + m_model.assign_copy(model); + // Generate new SLAPrintObjects. + for (ModelObject *model_object : m_model.objects) { + auto po = new SLAPrintObject(this, model_object); + po->m_config.layer_height.set(lh); + m_objects.emplace_back(po); + for (ModelInstance *oinst : model_object->instances) { + Point tr = Point::new_scale(oinst->get_offset()(X), + oinst->get_offset()(Y)); + auto rotZ = float(oinst->get_rotation()(Z)); + po->m_instances.emplace_back(oinst->id(), tr, rotZ); + } + } + } + + return APPLY_STATUS_INVALIDATED; +} + +void SLAPrint::process() +{ + using namespace sla; + + // Assumption: at this point the print objects should be populated only with + // the model objects we have to process and the instances are also filtered + + // shortcut to initial layer height + auto ilh = float(m_material_config.initial_layer_height.getFloat()); + + // Slicing the model object. This method is oversimplified and needs to + // be compared with the fff slicing algorithm for verification + auto slice_model = [this, ilh](SLAPrintObject& po) { + auto lh = float(po.m_config.layer_height.getFloat()); + + TriangleMesh mesh = po.transformed_mesh(); + TriangleMeshSlicer slicer(&mesh); + auto bb3d = mesh.bounding_box(); + + auto H = bb3d.max(Z) - bb3d.min(Z); + auto gnd = float(bb3d.min(Z)); + std::vector heights = {gnd}; + for(float h = gnd + ilh; h < gnd + H; h += lh) heights.emplace_back(h); + + auto& layers = po.m_model_slices; + slicer.slice(heights, &layers, [this](){ + throw_if_canceled(); + }); + }; + + auto support_points = [](SLAPrintObject& po) { + ModelObject& mo = *po.m_model_object; + if(!mo.sla_support_points.empty()) { + po.m_supportdata.reset(new SLAPrintObject::SupportData()); + po.m_supportdata->emesh = sla::to_eigenmesh(po.transformed_mesh()); + + po.m_supportdata->support_points = + sla::to_point_set(po.transformed_support_points()); + } + + // for(SLAPrintObject *po : pobjects) { + // TODO: calculate automatic support points + // po->m_supportdata->slice_cache contains the slices at this point + //} + }; + + // In this step we create the supports + auto support_tree = [this](SLAPrintObject& po) { + if(!po.m_supportdata) return; + + auto& emesh = po.m_supportdata->emesh; + auto& pts = po.m_supportdata->support_points; // nowhere filled yet + try { + SupportConfig scfg; // TODO fill or replace with po.m_config + + sla::Controller ctl; + ctl.statuscb = [this](unsigned st, const std::string& msg) { + unsigned stinit = OBJ_STEP_LEVELS[slaposSupportTree]; + double d = (OBJ_STEP_LEVELS[slaposBasePool] - stinit) / 100.0; + set_status(unsigned(stinit + st*d), msg); + }; + ctl.stopcondition = [this](){ return canceled(); }; + ctl.cancelfn = [this]() { throw_if_canceled(); }; + + po.m_supportdata->support_tree_ptr.reset( + new SLASupportTree(pts, emesh, scfg, ctl)); + + } catch(sla::SLASupportsStoppedException&) { + // no need to rethrow + // throw_if_canceled(); + } + }; + + // This step generates the sla base pad + auto base_pool = [](SLAPrintObject& po) { + // this step can only go after the support tree has been created + // and before the supports had been sliced. (or the slicing has to be + // repeated) + + if(po.is_step_done(slaposSupportTree) && + po.m_supportdata && + po.m_supportdata->support_tree_ptr) + { + double wt = po.m_config.pad_wall_thickness.getFloat(); + double h = po.m_config.pad_wall_height.getFloat(); + double md = po.m_config.pad_max_merge_distance.getFloat(); + double er = po.m_config.pad_edge_radius.getFloat(); + double lh = po.m_config.layer_height.getFloat(); + double elevation = po.m_config.support_object_elevation.getFloat(); + + sla::ExPolygons bp; + if(elevation < h/2) + sla::base_plate(po.transformed_mesh(), bp, + float(h/2), float(lh)); + + po.m_supportdata->support_tree_ptr->add_pad(bp, wt, h, md, er); + } + }; + + // Slicing the support geometries similarly to the model slicing procedure. + // If the pad had been added previously (see step "base_pool" than it will + // be part of the slices) + auto slice_supports = [ilh](SLAPrintObject& po) { + auto& sd = po.m_supportdata; + if(sd && sd->support_tree_ptr) { + auto lh = float(po.m_config.layer_height.getFloat()); + sd->support_slices = sd->support_tree_ptr->slice(lh, ilh); + } + }; + + // Rasterizing the model objects, and their supports + auto rasterize = [this, ilh]() { + using Layer = sla::ExPolygons; + using LayerCopies = std::vector; + struct LayerRef { + std::reference_wrapper lref; + std::reference_wrapper copies; + LayerRef(const Layer& lyr, const LayerCopies& cp) : + lref(std::cref(lyr)), copies(std::cref(cp)) {} + }; + + using LayerRefs = std::vector; + + // layers according to quantized height levels + std::map levels; + + // For all print objects, go through its initial layers and place them + // into the layers hash + for(SLAPrintObject *o : m_objects) { + + double gndlvl = o->transformed_mesh().bounding_box().min(Z); + + double lh = o->m_config.layer_height.getFloat(); + SlicedModel & oslices = o->m_model_slices; + for(int i = 0; i < oslices.size(); ++i) { + int a = i == 0 ? 0 : 1; + int b = i == 0 ? 0 : i - 1; + + double h = gndlvl + ilh * a + b * lh; + long long lyridx = static_cast(scale_(h)); + auto& lyrs = levels[lyridx]; // this initializes a new record + lyrs.emplace_back(oslices[i], o->m_instances); + } + + if(o->m_supportdata) { // deal with the support slices if present + auto& sslices = o->m_supportdata->support_slices; + double el = o->m_config.support_object_elevation.getFloat(); + //TODO: remove next line: + el = SupportConfig().object_elevation_mm; + + for(int i = 0; i < sslices.size(); ++i) { + int a = i == 0 ? 0 : 1; + int b = i == 0 ? 0 : i - 1; + + double h = gndlvl - el + ilh * a + b * lh; + + long long lyridx = static_cast(scale_(h)); + auto& lyrs = levels[lyridx]; + lyrs.emplace_back(sslices[i], o->m_instances); + } + } + } + + if(canceled()) return; + + // collect all the keys + std::vector keys; keys.reserve(levels.size()); + for(auto& e : levels) keys.emplace_back(e.first); + + { // create a raster printer for the current print parameters + // I don't know any better + auto& ocfg = m_objects.front()->m_config; + auto& matcfg = m_material_config; + auto& printcfg = m_printer_config; + + double w = printcfg.display_width.getFloat(); + double h = printcfg.display_height.getFloat(); + unsigned pw = printcfg.display_pixels_x.getInt(); + unsigned ph = printcfg.display_pixels_y.getInt(); + double lh = ocfg.layer_height.getFloat(); + double exp_t = matcfg.exposure_time.getFloat(); + double iexp_t = matcfg.initial_exposure_time.getFloat(); + + m_printer.reset(new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t)); + } + + // Allocate space for all the layers + SLAPrinter& printer = *m_printer; + auto lvlcnt = unsigned(levels.size()); + printer.layers(lvlcnt); + + // TODO exclusive progress indication for this step would be good + // as it is the longest of all. It would require synchronization + // in the parallel processing. + + // procedure to process one height level. This will run in parallel + auto lvlfn = [this, &keys, &levels, &printer](unsigned level_id) { + if(canceled()) return; + + LayerRefs& lrange = levels[keys[level_id]]; + + // Switch to the appropriate layer in the printer + printer.begin_layer(level_id); + + for(auto& lyrref : lrange) { // for all layers in the current level + if(canceled()) break; + const Layer& sl = lyrref.lref; // get the layer reference + const LayerCopies& copies = lyrref.copies; + + // Draw all the polygons in the slice to the actual layer. + for(auto& cp : copies) { + for(ExPolygon slice : sl) { + slice.translate(cp.shift(X), cp.shift(Y)); + slice.rotate(cp.rotation); + printer.draw_polygon(slice, level_id); + } + } + } + + // Finish the layer for later saving it. + printer.finish_layer(level_id); + }; + + // last minute escape + if(canceled()) return; + + // Sequential version (for testing) + // for(unsigned l = 0; l < lvlcnt; ++l) process_level(l); + + // Print all the layers in parallel + tbb::parallel_for(0, lvlcnt, lvlfn); + }; + + using slaposFn = std::function; + using slapsFn = std::function; + + std::array objectsteps = { + slaposObjectSlice, + slaposSupportIslands, + slaposSupportPoints, + slaposSupportTree, + slaposBasePool, + slaposSliceSupports + }; + + std::array pobj_program = + { + slice_model, + [](SLAPrintObject&){}, // slaposSupportIslands now empty + support_points, + support_tree, + base_pool, + slice_supports + }; + + std::array print_program = + { + rasterize, + [](){} // validate + }; + + const unsigned min_objstatus = 0; + const unsigned max_objstatus = PRINT_STEP_LEVELS[slapsRasterize]; + const size_t objcount = m_objects.size(); + const double ostepd = (max_objstatus - min_objstatus) / (objcount * 100.0); + + for(SLAPrintObject * po : m_objects) { + for(size_t s = 0; s < pobj_program.size(); ++s) { + auto currentstep = objectsteps[s]; + + // Cancellation checking. Each step will check for cancellation + // on its own and return earlier gracefully. Just after it returns + // execution gets to this point and throws the canceled signal. + throw_if_canceled(); + + if(po->m_stepmask[s] && !po->is_step_done(currentstep)) { + unsigned st = OBJ_STEP_LEVELS[currentstep]; + st = unsigned(min_objstatus + st * ostepd); + set_status(st, OBJ_STEP_LABELS[currentstep]); + + po->set_started(currentstep); + pobj_program[s](*po); + po->set_done(currentstep); + } + } + } + + std::array printsteps = { + slapsRasterize, slapsValidate + }; + + // this would disable the rasterization step +// m_stepmask[slapsRasterize] = false; + + for(size_t s = 0; s < print_program.size(); ++s) { + auto currentstep = printsteps[s]; + + throw_if_canceled(); + + if(m_stepmask[s] && !is_step_done(currentstep)) { + set_status(PRINT_STEP_LEVELS[currentstep], + PRINT_STEP_LABELS[currentstep]); + + set_started(currentstep); + print_program[s](); + set_done(currentstep); + } + } + + // If everything vent well + set_status(100, L("Slicing done")); +} + +SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object): + Inherited(print), + m_model_object(model_object), + m_stepmask(slaposCount, true) +{ +} + +SLAPrintObject::~SLAPrintObject() {} + +TriangleMesh SLAPrintObject::support_mesh() const +{ + TriangleMesh trm; + + if(m_supportdata && m_supportdata->support_tree_ptr) + m_supportdata->support_tree_ptr->merged_mesh(trm); + + // TODO: is this necessary? + trm.repair(); + + return trm; +} + +TriangleMesh SLAPrintObject::pad_mesh() const +{ + if(!m_supportdata || !m_supportdata->support_tree_ptr) return {}; + + return m_supportdata->support_tree_ptr->get_pad(); +} + +const TriangleMesh &SLAPrintObject::transformed_mesh() const { + // we need to transform the raw mesh... + // currently all the instances share the same x and y rotation and scaling + // so we have to extract those from e.g. the first instance and apply to the + // raw mesh. This is also true for the support points. + // BUT: when the support structure is spawned for each instance than it has + // to omit the X, Y rotation and scaling as those have been already applied + // or apply an inverse transformation on the support structure after it + // has been created. + + if(m_trmesh_valid) return m_transformed_rmesh; + m_transformed_rmesh = m_model_object->raw_mesh(); + m_transformed_rmesh.transform(m_trafo); + m_trmesh_valid = true; + return m_transformed_rmesh; +} + +std::vector SLAPrintObject::transformed_support_points() const +{ + assert(m_model_object != nullptr); + auto& spts = m_model_object->sla_support_points; + + // this could be cached as well + std::vector ret; ret.reserve(spts.size()); + + for(auto& sp : spts) ret.emplace_back( trafo() * Vec3d(sp.cast())); + + return ret; +} + +} // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp new file mode 100644 index 0000000000..de8c6cdae2 --- /dev/null +++ b/src/libslic3r/SLAPrint.hpp @@ -0,0 +1,162 @@ +#ifndef slic3r_SLAPrint_hpp_ +#define slic3r_SLAPrint_hpp_ + +#include "PrintBase.hpp" +#include "PrintExport.hpp" +#include "Point.hpp" + +namespace Slic3r { + +enum SLAPrintStep : unsigned int { + slapsRasterize, + slapsValidate, + slapsCount +}; + +enum SLAPrintObjectStep : unsigned int { + slaposObjectSlice, + slaposSupportIslands, + slaposSupportPoints, + slaposSupportTree, + slaposBasePool, + slaposSliceSupports, + slaposCount +}; + +class SLAPrint; +class GLCanvas; + +using _SLAPrintObjectBase = + PrintObjectBaseWithState; + +class SLAPrintObject : public _SLAPrintObjectBase +{ +private: // Prevents erroneous use by other classes. + using Inherited = _SLAPrintObjectBase; + +public: + const ModelObject* model_object() const { return m_model_object; } + ModelObject* model_object() { return m_model_object; } + const Transform3d& trafo() const { return m_trafo; } + + struct Instance { + Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {} + // ID of the corresponding ModelInstance. + ModelID instance_id; + // Slic3r::Point objects in scaled G-code coordinates + Point shift; + // Rotation along the Z axis, in radians. + float rotation; + }; + const std::vector& instances() const { return m_instances; } + + // Get a support mesh centered around origin in XY, and with zero rotation around Z applied. + // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true. + TriangleMesh support_mesh() const; + // Get a pad mesh centered around origin in XY, and with zero rotation around Z applied. + // Support mesh is only valid if this->is_step_done(slaposPad) is true. + TriangleMesh pad_mesh() const; + + // This will return the transformed mesh which is cached + const TriangleMesh& transformed_mesh() const; + + std::vector transformed_support_points() const; + + // I refuse to grantee copying (Tamas) + SLAPrintObject(const SLAPrintObject&) = delete; + SLAPrintObject& operator=(const SLAPrintObject&) = delete; + +protected: + // to be called from SLAPrint only. + friend class SLAPrint; + + SLAPrintObject(SLAPrint* print, ModelObject* model_object); + ~SLAPrintObject(); + + void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } + void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) + { this->m_config.apply_only(other, keys, ignore_nonexistent); } + void set_trafo(const Transform3d& trafo) { m_trafo = trafo; m_trmesh_valid = false; } + + bool set_instances(const std::vector &instances); + // Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint. + bool invalidate_step(SLAPrintObjectStep step); + +private: + // Points to the instance owned by a Model stored at the parent SLAPrint instance. + ModelObject *m_model_object; + // Object specific configuration, pulled from the configuration layer. + SLAPrintObjectConfig m_config; + // Translation in Z + Rotation by Y and Z + Scaling / Mirroring. + Transform3d m_trafo = Transform3d::Identity(); + std::vector m_instances; + + // Which steps have to be performed. Implicitly: all + std::vector m_stepmask; + std::vector m_model_slices; + + // Caching the transformed (m_trafo) raw mesh of the object + mutable TriangleMesh m_transformed_rmesh; + mutable bool m_trmesh_valid = false; + + class SupportData; + std::unique_ptr m_supportdata; +}; + +using PrintObjects = std::vector; + +class TriangleMesh; + +/** + * @brief This class is the high level FSM for the SLA printing process. + * + * It should support the background processing framework and contain the + * metadata for the support geometries and their slicing. It should also + * dispatch the SLA printing configuration values to the appropriate calculation + * steps. + * + * TODO (decide): The last important feature is the support for visualization + * which (at least for now) will be implemented as a method(s) returning the + * triangle meshes or receiving the rendering canvas and drawing on that + * directly. + * + */ +class SLAPrint : public PrintBaseWithState +{ +private: // Prevents erroneous use by other classes. + typedef PrintBaseWithState Inherited; + +public: + SLAPrint(): m_stepmask(slapsCount, true) {} + + virtual ~SLAPrint() { this->clear(); } + + PrinterTechnology technology() const noexcept { return ptSLA; } + + void clear() override; + bool empty() const override { return false; } + ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override; + void process() override; + + template void export_raster(const std::string& fname) { + if(m_printer) m_printer->save(fname); + } + const PrintObjects& objects() const { return m_objects; } + +private: + using SLAPrinter = FilePrinter; + using SLAPrinterPtr = std::unique_ptr; + + Model m_model; + SLAPrinterConfig m_printer_config; + SLAMaterialConfig m_material_config; + PrintObjects m_objects; + std::vector m_stepmask; + SLAPrinterPtr m_printer; + + friend SLAPrintObject; +}; + +} // namespace Slic3r + +#endif /* slic3r_SLAPrint_hpp_ */ diff --git a/xs/src/libslic3r/SVG.cpp b/src/libslic3r/SVG.cpp similarity index 89% rename from xs/src/libslic3r/SVG.cpp rename to src/libslic3r/SVG.cpp index c94db8e743..03f55802ef 100644 --- a/xs/src/libslic3r/SVG.cpp +++ b/src/libslic3r/SVG.cpp @@ -3,7 +3,7 @@ #include -#define COORD(x) ((float)unscale((x))*10) +#define COORD(x) (unscale((x))*10) namespace Slic3r { @@ -32,8 +32,8 @@ bool SVG::open(const char* afilename, const BoundingBox &bbox, const coord_t bbo this->f = boost::nowide::fopen(afilename, "w"); if (f == NULL) return false; - float w = COORD(bbox.max.x - bbox.min.x + 2 * bbox_offset); - float h = COORD(bbox.max.y - bbox.min.y + 2 * bbox_offset); + float w = COORD(bbox.max(0) - bbox.min(0) + 2 * bbox_offset); + float h = COORD(bbox.max(1) - bbox.min(1) + 2 * bbox_offset); fprintf(this->f, "\n" "\n" @@ -50,7 +50,7 @@ SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width) { fprintf(this->f, " arrows) fprintf(this->f, " marker-end=\"url(#endArrow)\""); fprintf(this->f, "/>\n"); @@ -58,21 +58,21 @@ SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width) void SVG::draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coordf_t stroke_width) { - Pointf dir(line.b.x-line.a.x, line.b.y-line.a.y); - Pointf perp(-dir.y, dir.x); - coordf_t len = sqrt(perp.x*perp.x + perp.y*perp.y); + Vec2d dir(line.b(0)-line.a(0), line.b(1)-line.a(1)); + Vec2d perp(-dir(1), dir(0)); + coordf_t len = sqrt(perp(0)*perp(0) + perp(1)*perp(1)); coordf_t da = coordf_t(0.5)*line.a_width/len; coordf_t db = coordf_t(0.5)*line.b_width/len; fprintf(this->f, " \n", - COORD(line.a.x-da*perp.x-origin.x), - COORD(line.a.y-da*perp.y-origin.y), - COORD(line.b.x-db*perp.x-origin.x), - COORD(line.b.y-db*perp.y-origin.y), - COORD(line.b.x+db*perp.x-origin.x), - COORD(line.b.y+db*perp.y-origin.y), - COORD(line.a.x+da*perp.x-origin.x), - COORD(line.a.y+da*perp.y-origin.y), + COORD(line.a(0)-da*perp(0)-origin(0)), + COORD(line.a(1)-da*perp(1)-origin(1)), + COORD(line.b(0)-db*perp(0)-origin(0)), + COORD(line.b(1)-db*perp(1)-origin(1)), + COORD(line.b(0)+db*perp(0)-origin(0)), + COORD(line.b(1)+db*perp(1)-origin(1)), + COORD(line.a(0)+da*perp(0)-origin(0)), + COORD(line.a(1)+da*perp(1)-origin(1)), fill.c_str(), stroke.c_str(), (stroke_width == 0) ? 1.f : COORD(stroke_width)); } @@ -220,7 +220,7 @@ SVG::draw(const Point &point, std::string fill, coord_t iradius) { float radius = (iradius == 0) ? 3.f : COORD(iradius); std::ostringstream svg; - svg << " "; @@ -287,8 +287,8 @@ SVG::get_path_d(const MultiPoint &mp, bool closed) const std::ostringstream d; d << "M "; for (Points::const_iterator p = mp.points.begin(); p != mp.points.end(); ++p) { - d << COORD(p->x - origin.x) << " "; - d << COORD(p->y - origin.y) << " "; + d << COORD((*p)(0) - origin(0)) << " "; + d << COORD((*p)(1) - origin(1)) << " "; } if (closed) d << "z"; return d.str(); @@ -300,8 +300,8 @@ SVG::get_path_d(const ClipperLib::Path &path, double scale, bool closed) const std::ostringstream d; d << "M "; for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) { - d << COORD(scale * p->X - origin.x) << " "; - d << COORD(scale * p->Y - origin.y) << " "; + d << COORD(scale * p->X - origin(0)) << " "; + d << COORD(scale * p->Y - origin(1)) << " "; } if (closed) d << "z"; return d.str(); @@ -311,8 +311,8 @@ void SVG::draw_text(const Point &pt, const char *text, const char *color) { fprintf(this->f, "%s", - COORD(pt.x-origin.x), - COORD(pt.y-origin.y), + COORD(pt(0)-origin(0)), + COORD(pt(1)-origin(1)), color, text); } @@ -320,13 +320,13 @@ void SVG::draw_legend(const Point &pt, const char *text, const char *color) { fprintf(this->f, "", - COORD(pt.x-origin.x), - COORD(pt.y-origin.y), + COORD(pt(0)-origin(0)), + COORD(pt(1)-origin(1)), color); fprintf(this->f, "%s", - COORD(pt.x-origin.x) + 20.f, - COORD(pt.y-origin.y), + COORD(pt(0)-origin(0)) + 20.f, + COORD(pt(1)-origin(1)), "black", text); } diff --git a/xs/src/libslic3r/SVG.hpp b/src/libslic3r/SVG.hpp similarity index 100% rename from xs/src/libslic3r/SVG.hpp rename to src/libslic3r/SVG.hpp diff --git a/xs/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp similarity index 95% rename from xs/src/libslic3r/Slicing.cpp rename to src/libslic3r/Slicing.cpp index d3fbcc7cb5..b3e3145493 100644 --- a/xs/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -561,15 +561,15 @@ int generate_layer_height_texture( void *data, int rows, int cols, bool level_of_detail_2nd_level) { // https://github.com/aschn/gnuplot-colorbrewer - std::vector palette_raw; - palette_raw.push_back(Point3(0x01A, 0x098, 0x050)); - palette_raw.push_back(Point3(0x066, 0x0BD, 0x063)); - palette_raw.push_back(Point3(0x0A6, 0x0D9, 0x06A)); - palette_raw.push_back(Point3(0x0D9, 0x0F1, 0x0EB)); - palette_raw.push_back(Point3(0x0FE, 0x0E6, 0x0EB)); - palette_raw.push_back(Point3(0x0FD, 0x0AE, 0x061)); - palette_raw.push_back(Point3(0x0F4, 0x06D, 0x043)); - palette_raw.push_back(Point3(0x0D7, 0x030, 0x027)); + std::vector palette_raw; + palette_raw.push_back(Vec3crd(0x01A, 0x098, 0x050)); + palette_raw.push_back(Vec3crd(0x066, 0x0BD, 0x063)); + palette_raw.push_back(Vec3crd(0x0A6, 0x0D9, 0x06A)); + palette_raw.push_back(Vec3crd(0x0D9, 0x0F1, 0x0EB)); + palette_raw.push_back(Vec3crd(0x0FE, 0x0E6, 0x0EB)); + palette_raw.push_back(Vec3crd(0x0FD, 0x0AE, 0x061)); + palette_raw.push_back(Vec3crd(0x0F4, 0x06D, 0x043)); + palette_raw.push_back(Vec3crd(0x0D7, 0x030, 0x027)); // Clear the main texture and the 2nd LOD level. // memset(data, 0, rows * cols * (level_of_detail_2nd_level ? 5 : 4)); @@ -600,25 +600,25 @@ int generate_layer_height_texture( int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); coordf_t t = idxf - coordf_t(idx1); - const Point3 &color1 = palette_raw[idx1]; - const Point3 &color2 = palette_raw[idx2]; + const Vec3crd &color1 = palette_raw[idx1]; + const Vec3crd &color2 = palette_raw[idx2]; coordf_t z = cell_to_z * coordf_t(cell); assert(z >= lo && z <= hi); // Intensity profile to visualize the layers. coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h); // Color mapping from layer height to RGB. - Pointf3 color( - intensity * lerp(coordf_t(color1.x), coordf_t(color2.x), t), - intensity * lerp(coordf_t(color1.y), coordf_t(color2.y), t), - intensity * lerp(coordf_t(color1.z), coordf_t(color2.z), t)); + Vec3d color( + intensity * lerp(coordf_t(color1(0)), coordf_t(color2(0)), t), + intensity * lerp(coordf_t(color1(1)), coordf_t(color2(1)), t), + intensity * lerp(coordf_t(color1(2)), coordf_t(color2(2)), t)); int row = cell / (cols - 1); int col = cell - row * (cols - 1); assert(row >= 0 && row < rows); assert(col >= 0 && col < cols); unsigned char *ptr = (unsigned char*)data + (row * cols + col) * 4; - ptr[0] = (unsigned char)clamp(0, 255, int(floor(color.x + 0.5))); - ptr[1] = (unsigned char)clamp(0, 255, int(floor(color.y + 0.5))); - ptr[2] = (unsigned char)clamp(0, 255, int(floor(color.z + 0.5))); + ptr[0] = (unsigned char)clamp(0, 255, int(floor(color(0) + 0.5))); + ptr[1] = (unsigned char)clamp(0, 255, int(floor(color(1) + 0.5))); + ptr[2] = (unsigned char)clamp(0, 255, int(floor(color(2) + 0.5))); ptr[3] = 255; if (col == 0 && row > 0) { // Duplicate the first value in a row as a last value of the preceding row. @@ -636,21 +636,21 @@ int generate_layer_height_texture( int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); coordf_t t = idxf - coordf_t(idx1); - const Point3 &color1 = palette_raw[idx1]; - const Point3 &color2 = palette_raw[idx2]; + const Vec3crd &color1 = palette_raw[idx1]; + const Vec3crd &color2 = palette_raw[idx2]; // Color mapping from layer height to RGB. - Pointf3 color( - lerp(coordf_t(color1.x), coordf_t(color2.x), t), - lerp(coordf_t(color1.y), coordf_t(color2.y), t), - lerp(coordf_t(color1.z), coordf_t(color2.z), t)); + Vec3d color( + lerp(coordf_t(color1(0)), coordf_t(color2(0)), t), + lerp(coordf_t(color1(1)), coordf_t(color2(1)), t), + lerp(coordf_t(color1(2)), coordf_t(color2(2)), t)); int row = cell / (cols1 - 1); int col = cell - row * (cols1 - 1); assert(row >= 0 && row < rows/2); assert(col >= 0 && col < cols/2); unsigned char *ptr = data1 + (row * cols1 + col) * 4; - ptr[0] = (unsigned char)clamp(0, 255, int(floor(color.x + 0.5))); - ptr[1] = (unsigned char)clamp(0, 255, int(floor(color.y + 0.5))); - ptr[2] = (unsigned char)clamp(0, 255, int(floor(color.z + 0.5))); + ptr[0] = (unsigned char)clamp(0, 255, int(floor(color(0) + 0.5))); + ptr[1] = (unsigned char)clamp(0, 255, int(floor(color(1) + 0.5))); + ptr[2] = (unsigned char)clamp(0, 255, int(floor(color(2) + 0.5))); ptr[3] = 255; if (col == 0 && row > 0) { // Duplicate the first value in a row as a last value of the preceding row. diff --git a/xs/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp similarity index 100% rename from xs/src/libslic3r/Slicing.hpp rename to src/libslic3r/Slicing.hpp diff --git a/xs/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp similarity index 96% rename from xs/src/libslic3r/SlicingAdaptive.cpp rename to src/libslic3r/SlicingAdaptive.cpp index ff0da76365..2ef4aec8c6 100644 --- a/xs/src/libslic3r/SlicingAdaptive.cpp +++ b/src/libslic3r/SlicingAdaptive.cpp @@ -15,8 +15,8 @@ void SlicingAdaptive::clear() std::pair face_z_span(const stl_facet *f) { return std::pair( - std::min(std::min(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z), - std::max(std::max(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z)); + std::min(std::min(f->vertex[0](2), f->vertex[1](2)), f->vertex[2](2)), + std::max(std::max(f->vertex[0](2), f->vertex[1](2)), f->vertex[2](2))); } void SlicingAdaptive::prepare() @@ -40,7 +40,7 @@ void SlicingAdaptive::prepare() // 3) Generate Z components of the facet normals. m_face_normal_z.assign(m_faces.size(), 0.f); for (size_t iface = 0; iface < m_faces.size(); ++ iface) - m_face_normal_z[iface] = m_faces[iface]->normal.z; + m_face_normal_z[iface] = m_faces[iface]->normal(2); } float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet) diff --git a/xs/src/libslic3r/SlicingAdaptive.hpp b/src/libslic3r/SlicingAdaptive.hpp similarity index 100% rename from xs/src/libslic3r/SlicingAdaptive.hpp rename to src/libslic3r/SlicingAdaptive.hpp diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp similarity index 91% rename from xs/src/libslic3r/SupportMaterial.cpp rename to src/libslic3r/SupportMaterial.cpp index e31b7a201a..31305f3323 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -67,9 +67,9 @@ Point export_support_surface_type_legend_to_svg_box_size() void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos) { // 1st row - coord_t pos_x0 = pos.x + scale_(1.); + coord_t pos_x0 = pos(0) + scale_(1.); coord_t pos_x = pos_x0; - coord_t pos_y = pos.y + scale_(1.5); + coord_t pos_y = pos(1) + scale_(1.5); coord_t step_x = scale_(10.); svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltTopContact)); pos_x += step_x; @@ -82,7 +82,7 @@ void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos) svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltBottomContact)); // 2nd row pos_x = pos_x0; - pos_y = pos.y+scale_(2.8); + pos_y = pos(1)+scale_(2.8); svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltRaftInterface)); pos_x += step_x; svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltRaftBase)); @@ -98,8 +98,8 @@ void export_print_z_polygons_to_svg(const char *path, PrintObjectSupportMaterial for (int i = 0; i < n_layers; ++ i) bbox.merge(get_extents(layers[i]->polygons)); Point legend_size = export_support_surface_type_legend_to_svg_box_size(); - Point legend_pos(bbox.min.x, bbox.max.y); - bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y)); + Point legend_pos(bbox.min(0), bbox.max(1)); + bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); SVG svg(path, bbox); const float transparency = 0.5f; for (int i = 0; i < n_layers; ++ i) @@ -120,8 +120,8 @@ void export_print_z_polygons_and_extrusions_to_svg( for (int i = 0; i < n_layers; ++ i) bbox.merge(get_extents(layers[i]->polygons)); Point legend_size = export_support_surface_type_legend_to_svg_box_size(); - Point legend_pos(bbox.min.x, bbox.max.y); - bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y)); + Point legend_pos(bbox.min(0), bbox.max(1)); + bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); SVG svg(path, bbox); const float transparency = 0.5f; for (int i = 0; i < n_layers; ++ i) @@ -142,8 +142,8 @@ void export_print_z_polygons_and_extrusions_to_svg( PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : m_object (object), - m_print_config (&object->print()->config), - m_object_config (&object->config), + m_print_config (&object->print()->config()), + m_object_config (&object->config()), m_slicing_params (slicing_params), m_first_layer_flow (support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height))), m_support_material_flow (support_material_flow(object, float(slicing_params.layer_height))), @@ -164,7 +164,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object coordf_t external_perimeter_width = 0.; for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { if (! object->region_volumes[region_id].empty()) { - const PrintRegionConfig &config = object->print()->get_region(region_id)->config; + const PrintRegionConfig &config = object->print()->get_region(region_id)->config(); coordf_t width = config.external_perimeter_extrusion_width.get_abs_value(slicing_params.layer_height); if (width <= 0.) width = m_print_config->nozzle_diameter.get_at(config.perimeter_extruder-1); @@ -226,7 +226,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) coordf_t max_object_layer_height = 0.; for (size_t i = 0; i < object.layer_count(); ++ i) - max_object_layer_height = std::max(max_object_layer_height, object.layers[i]->height); + max_object_layer_height = std::max(max_object_layer_height, object.layers()[i]->height); // Layer instances will be allocated by std::deque and they will be kept until the end of this function call. // The layers will be referenced by various LayersPtr (of type std::vector) @@ -266,9 +266,9 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) layer_support_areas); #ifdef SLIC3R_DEBUG - for (size_t layer_id = 0; layer_id < object.layers.size(); ++ layer_id) + for (size_t layer_id = 0; layer_id < object.layers().size(); ++ layer_id) Slic3r::SVG::export_expolygons( - debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers[layer_id]->print_z), + debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers()[layer_id]->print_z), union_ex(layer_support_areas[layer_id], false)); #endif /* SLIC3R_DEBUG */ @@ -364,7 +364,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Sort the layers lexicographically by a raising print_z and a decreasing height. std::sort(layers_sorted.begin(), layers_sorted.end(), MyLayersPtrCompare()); int layer_id = 0; - assert(object.support_layers.empty()); + assert(object.support_layers().empty()); for (int i = 0; i < int(layers_sorted.size());) { // Find the last layer with roughly the same print_z, find the minimum layer height of all. // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. @@ -430,14 +430,14 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t { // 1) Count the new polygons first. size_t n_polygons_new = 0; - for (const LayerRegion *region : layer.regions) + for (const LayerRegion *region : layer.regions()) for (const Surface &surface : region->slices.surfaces) if (surface.surface_type == surface_type) n_polygons_new += surface.expolygon.holes.size() + 1; // 2) Collect the new polygons. Polygons out; out.reserve(n_polygons_new); - for (const LayerRegion *region : layer.regions) + for (const LayerRegion *region : layer.regions()) for (const Surface &surface : region->slices.surfaces) if (surface.surface_type == surface_type) polygons_append(out, surface.expolygon); @@ -458,8 +458,6 @@ Polygons collect_slices_outer(const Layer &layer) class SupportGridPattern { public: - // Achtung! The support_polygons need to be trimmed by trimming_polygons, otherwise - // the selection by island_samples (see the island_samples() method) will not work! SupportGridPattern( // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy) const Polygons &support_polygons, @@ -487,18 +485,6 @@ public: bbox.align_to_grid(grid_resolution); m_grid.set_bbox(bbox); m_grid.create(*m_support_polygons, grid_resolution); -#if 0 - if (m_grid.has_intersecting_edges()) { - // EdgeGrid fails to produce valid signed distance function for self-intersecting polygons. - m_support_polygons_rotated = simplify_polygons(*m_support_polygons); - m_support_polygons = &m_support_polygons_rotated; - m_grid.set_bbox(bbox); - m_grid.create(*m_support_polygons, grid_resolution); -// assert(! m_grid.has_intersecting_edges()); - printf("SupportGridPattern: fixing polygons with intersection %s\n", - m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED"); - } -#endif m_grid.calculate_sdf(); // Sample a single point per input support polygon, keep it as a reference to maintain corresponding // polygons if ever these polygons get split into parts by the trimming polygons. @@ -513,20 +499,17 @@ public: { // Generate islands, so each island may be tested for overlap with m_island_samples. assert(std::abs(2 * offset_in_grid) < m_grid.resolution()); -#ifdef SLIC3R_DEBUG - Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes); - ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false); -#else - ExPolygons islands = diff_ex(m_grid.contours_simplified(offset_in_grid, fill_holes), *m_trimming_polygons, false); -#endif + ExPolygons islands = diff_ex( + m_grid.contours_simplified(offset_in_grid, fill_holes), + *m_trimming_polygons, false); // Extract polygons, which contain some of the m_island_samples. Polygons out; for (ExPolygon &island : islands) { BoundingBox bbox = get_extents(island.contour); // Samples are sorted lexicographically. - auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), bbox.min - Point(1, 1)); - auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), bbox.max + Point(1, 1)); + auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.min - Point(1, 1))); + auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.max + Point(1, 1))); std::vector> samples_inside; for (auto it = it_lower; it != it_upper; ++ it) if (bbox.contains(*it)) @@ -538,12 +521,12 @@ public: Points::const_iterator i = contour.points.begin(); Points::const_iterator j = contour.points.end() - 1; for (; i != contour.points.end(); j = i ++) { - //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point.y well. - // Does the ray with y == point.y intersect this line segment? + //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well. + // Does the ray with y == point(1) intersect this line segment? for (auto &sample_inside : samples_inside) { - if ((i->y > sample_inside.first.y) != (j->y > sample_inside.first.y)) { - double x1 = (double)sample_inside.first.x; - double x2 = (double)i->x + (double)(j->x - i->x) * (double)(sample_inside.first.y - i->y) / (double)(j->y - i->y); + if (((*i)(1) > sample_inside.first(1)) != ((*j)(1) > sample_inside.first(1))) { + double x1 = (double)sample_inside.first(0); + double x2 = (double)(*i)(0) + (double)((*j)(0) - (*i)(0)) * (double)(sample_inside.first(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1)); if (x1 < x2) sample_inside.second = !sample_inside.second; } @@ -568,10 +551,7 @@ public: bbox.merge(get_extents(islands)); if (!out.empty()) bbox.merge(get_extents(out)); - if (!support_polygons_simplified.empty()) - bbox.merge(get_extents(support_polygons_simplified)); SVG svg(debug_out_path("extract_support_from_grid_trimmed-%d.svg", iRun).c_str(), bbox); - svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f); svg.draw(islands, "red", 0.5f); svg.draw(union_ex(out), "green", 0.5f); svg.draw(union_ex(*m_support_polygons), "blue", 0.5f); @@ -588,121 +568,7 @@ public: return out; } -#ifdef SLIC3R_DEBUG - void serialize(const std::string &path) - { - FILE *file = ::fopen(path.c_str(), "wb"); - ::fwrite(&m_support_spacing, 8, 1, file); - ::fwrite(&m_support_angle, 8, 1, file); - uint32_t n_polygons = m_support_polygons->size(); - ::fwrite(&n_polygons, 4, 1, file); - for (uint32_t i = 0; i < n_polygons; ++ i) { - const Polygon &poly = (*m_support_polygons)[i]; - uint32_t n_points = poly.size(); - ::fwrite(&n_points, 4, 1, file); - for (uint32_t j = 0; j < n_points; ++ j) { - const Point &pt = poly.points[j]; - ::fwrite(&pt.x, sizeof(coord_t), 1, file); - ::fwrite(&pt.y, sizeof(coord_t), 1, file); - } - } - n_polygons = m_trimming_polygons->size(); - ::fwrite(&n_polygons, 4, 1, file); - for (uint32_t i = 0; i < n_polygons; ++ i) { - const Polygon &poly = (*m_trimming_polygons)[i]; - uint32_t n_points = poly.size(); - ::fwrite(&n_points, 4, 1, file); - for (uint32_t j = 0; j < n_points; ++ j) { - const Point &pt = poly.points[j]; - ::fwrite(&pt.x, sizeof(coord_t), 1, file); - ::fwrite(&pt.y, sizeof(coord_t), 1, file); - } - } - ::fclose(file); - } - - static SupportGridPattern deserialize(const std::string &path, int which = -1) - { - SupportGridPattern out; - out.deserialize_(path, which); - return out; - } - - // Deserialization constructor - bool deserialize_(const std::string &path, int which = -1) - { - FILE *file = ::fopen(path.c_str(), "rb"); - if (file == nullptr) - return false; - - m_support_polygons = &m_support_polygons_deserialized; - m_trimming_polygons = &m_trimming_polygons_deserialized; - - ::fread(&m_support_spacing, 8, 1, file); - ::fread(&m_support_angle, 8, 1, file); - //FIXME - //m_support_spacing *= 0.01 / 2; - uint32_t n_polygons; - ::fread(&n_polygons, 4, 1, file); - m_support_polygons_deserialized.reserve(n_polygons); - int32_t scale = 1; - for (uint32_t i = 0; i < n_polygons; ++ i) { - Polygon poly; - uint32_t n_points; - ::fread(&n_points, 4, 1, file); - poly.points.reserve(n_points); - for (uint32_t j = 0; j < n_points; ++ j) { - coord_t x, y; - ::fread(&x, sizeof(coord_t), 1, file); - ::fread(&y, sizeof(coord_t), 1, file); - poly.points.emplace_back(Point(x * scale, y * scale)); - } - if (which == -1 || which == i) - m_support_polygons_deserialized.emplace_back(std::move(poly)); - printf("Polygon %d, area: %lf\n", i, area(poly.points)); - } - ::fread(&n_polygons, 4, 1, file); - m_trimming_polygons_deserialized.reserve(n_polygons); - for (uint32_t i = 0; i < n_polygons; ++ i) { - Polygon poly; - uint32_t n_points; - ::fread(&n_points, 4, 1, file); - poly.points.reserve(n_points); - for (uint32_t j = 0; j < n_points; ++ j) { - coord_t x, y; - ::fread(&x, sizeof(coord_t), 1, file); - ::fread(&y, sizeof(coord_t), 1, file); - poly.points.emplace_back(Point(x * scale, y * scale)); - } - m_trimming_polygons_deserialized.emplace_back(std::move(poly)); - } - ::fclose(file); - - m_support_polygons_deserialized = simplify_polygons(m_support_polygons_deserialized, false); - //m_support_polygons_deserialized = to_polygons(union_ex(m_support_polygons_deserialized, false)); - - // Create an EdgeGrid, initialize it with projection, initialize signed distance field. - coord_t grid_resolution = coord_t(scale_(m_support_spacing)); - BoundingBox bbox = get_extents(*m_support_polygons); - bbox.offset(20); - bbox.align_to_grid(grid_resolution); - m_grid.set_bbox(bbox); - m_grid.create(*m_support_polygons, grid_resolution); - m_grid.calculate_sdf(); - // Sample a single point per input support polygon, keep it as a reference to maintain corresponding - // polygons if ever these polygons get split into parts by the trimming polygons. - m_island_samples = island_samples(*m_support_polygons); - return true; - } - - const Polygons& support_polygons() const { return *m_support_polygons; } - const Polygons& trimming_polygons() const { return *m_trimming_polygons; } - const EdgeGrid::Grid& grid() const { return m_grid; } - -#endif /* SLIC3R_DEBUG */ - private: - SupportGridPattern() {} SupportGridPattern& operator=(const SupportGridPattern &rhs); #if 0 @@ -723,11 +589,11 @@ private: const Point &p3 = (pt_min == &expoly.contour.points.back()) ? expoly.contour.points.front() : *(pt_min + 1); Vector v = (p3 - p2) + (p1 - p2); - double l2 = double(v.x)*double(v.x)+double(v.y)*double(v.y); + double l2 = double(v(0))*double(v(0))+double(v(1))*double(v(1)); if (l2 == 0.) return p2; double coef = 20. / sqrt(l2); - return Point(p2.x + coef * v.x, p2.y + coef * v.y); + return Point(p2(0) + coef * v(0), p2(1) + coef * v(1)); } #endif @@ -773,12 +639,6 @@ private: // Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding // to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons. Points m_island_samples; - -#ifdef SLIC3R_DEBUG - // support for deserialization of m_support_polygons, m_trimming_polygons - Polygons m_support_polygons_deserialized; - Polygons m_trimming_polygons_deserialized; -#endif /* SLIC3R_DEBUG */ }; namespace SupportMaterialInternal { @@ -819,7 +679,7 @@ namespace SupportMaterialInternal { } static bool has_bridging_extrusions(const Layer &layer) { - for (const LayerRegion *region : layer.regions) { + for (const LayerRegion *region : layer.regions()) { if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters)) return true; if (region->fill_surfaces.has(stBottomBridge) && has_bridging_fills(region->fills)) @@ -882,7 +742,7 @@ namespace SupportMaterialInternal { // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported. Polygons lower_grown_slices = offset(lower_layer_polygons, //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width. - 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1))), + 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1))), SUPPORT_SURFACES_OFFSET_PARAMETERS); // Collect perimeters of this layer. //FIXME split_at_first_point() could split a bridge mid-way @@ -923,40 +783,17 @@ namespace SupportMaterialInternal { if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1) polygons_append(bridges, surface.expolygon); //FIXME add the gap filled areas. Extrude the gaps with a bridge flow? - // Remove the unsupported ends of the bridges from the bridged areas. - //FIXME add supports at regular intervals to support long bridges! - bridges = diff(bridges, - // Offset unsupported edges into polygons. - offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); - // Remove bridged areas from the supported areas. contact_polygons = diff(contact_polygons, bridges, true); + // Add the bridge anchors into the region. + //FIXME add supports at regular intervals to support long bridges! + polygons_append(contact_polygons, + intersection( + // Offset unsupported edges into polygons. + offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS), + bridges)); } } -#ifdef SLIC3R_DEBUG -static int Test() -{ -// for (int i = 0; i < 30; ++ i) - { - int i = -1; -// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000-prev.bin", i); -// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000.bin", i); - auto grid = SupportGridPattern::deserialize("d:\\temp\\support-top-contacts-final-run1-layer27-z5.650000.bin", i); - std::vector> intersections = grid.grid().intersecting_edges(); - if (! intersections.empty()) - printf("Intersections between contours!\n"); - Slic3r::export_intersections_to_svg("d:\\temp\\support_polygon_intersections.svg", grid.support_polygons()); - Slic3r::SVG::export_expolygons("d:\\temp\\support_polygons.svg", union_ex(grid.support_polygons(), false)); - Slic3r::SVG::export_expolygons("d:\\temp\\trimming_polygons.svg", union_ex(grid.trimming_polygons(), false)); - Polygons extracted = grid.extract_support(scale_(0.21 / 2), true); - Slic3r::SVG::export_expolygons("d:\\temp\\extracted.svg", union_ex(extracted, false)); - printf("hu!"); - } - return 0; -} -static int run_support_test = Test(); -#endif /* SLIC3R_DEBUG */ - // Generate top contact layers supporting overhangs. // For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined. // If supports over bed surface only are requested, don't generate contact layers over an object. @@ -988,9 +825,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ std::vector buildplate_covered; if (buildplate_only) { BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() - collecting regions covering the print bed."; - buildplate_covered.assign(object.layers.size(), Polygons()); - for (size_t layer_id = 1; layer_id < object.layers.size(); ++ layer_id) { - const Layer &lower_layer = *object.layers[layer_id-1]; + buildplate_covered.assign(object.layers().size(), Polygons()); + for (size_t layer_id = 1; layer_id < object.layers().size(); ++ layer_id) { + const Layer &lower_layer = *object.layers()[layer_id-1]; // Merge the new slices with the preceding slices. // Apply the safety offset to the newly added polygons, so they will connect // with the polygons collected before, @@ -1019,7 +856,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ (const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - const Layer &layer = *object.layers[layer_id]; + const Layer &layer = *object.layers()[layer_id]; // Detect overhangs and contact areas needed to support them. // Collect overhangs and contacts of all regions of this layer supported by the layer immediately below. @@ -1027,7 +864,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ Polygons contact_polygons; Polygons slices_margin_cached; float slices_margin_cached_offset = -1.; - Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers[layer_id-1]->slices.expolygons); + Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices.expolygons); // Offset of the lower layer, to trim the support polygons with to calculate dense supports. float no_interface_offset = 0.f; if (layer_id == 0) { @@ -1039,14 +876,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ contact_polygons = offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN)); } else { // Generate overhang / contact_polygons for non-raft layers. - const Layer &lower_layer = *object.layers[layer_id-1]; - for (LayerRegion *layerm : layer.regions) { + const Layer &lower_layer = *object.layers()[layer_id-1]; + for (LayerRegion *layerm : layer.regions()) { // Extrusion width accounts for the roundings of the extrudates. // It is the maximum widh of the extrudate. float fw = float(layerm->flow(frExternalPerimeter).scaled_width()); no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw); float lower_layer_offset = - (layer_id < this->m_object_config->support_material_enforce_layers.value) ? + (layer_id < m_object_config->support_material_enforce_layers.value) ? // Enforce a full possible support, ignore the overhang angle. 0.f : (threshold_rad > 0. ? @@ -1204,8 +1041,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Align the contact surface height with a layer immediately below the supported layer. // Interface layer will be synchronized with the object. new_layer.print_z = layer.print_z - layer.height; - new_layer.height = object.layers[layer_id - 1]->height; - new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z; + new_layer.height = object.layers()[layer_id - 1]->height; + new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers()[layer_id - 2]->print_z; } else { new_layer.print_z = layer.print_z - layer.height - m_object_config->support_material_contact_distance; new_layer.bottom_z = new_layer.print_z; @@ -1231,9 +1068,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // it will support layers printed with a bridging flow. if (SupportMaterialInternal::has_bridging_extrusions(layer)) { coordf_t bridging_height = 0.; - for (const LayerRegion *region : layer.regions) + for (const LayerRegion *region : layer.regions()) bridging_height += region->region()->bridging_height_avg(*m_print_config); - bridging_height /= coordf_t(layer.regions.size()); + bridging_height /= coordf_t(layer.regions().size()); coordf_t bridging_print_z = layer.print_z - bridging_height - m_object_config->support_material_contact_distance; if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) { // Not below the first layer height means this layer is printable. @@ -1259,8 +1096,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } } - // Achtung! The contact_polygons need to be trimmed by slices_margin_cached, otherwise - // the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work! SupportGridPattern support_grid_pattern( // Support islands, to be stretched into a grid. contact_polygons, @@ -1279,14 +1114,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions. Polygons dense_interface_polygons = diff(overhang_polygons, offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS)); +// offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (! dense_interface_polygons.empty()) { - dense_interface_polygons = - // Achtung! The dense_interface_polygons need to be trimmed by slices_margin_cached, otherwise - // the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work! - diff( - // Regularize the contour. - offset(dense_interface_polygons, no_interface_offset * 0.1f), - slices_margin_cached); + //FIXME do it for the bridges only? SupportGridPattern support_grid_pattern( // Support islands, to be stretched into a grid. dense_interface_polygons, @@ -1296,34 +1126,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), Geometry::deg2rad(m_object_config->support_material_angle.value)); new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false); - #ifdef SLIC3R_DEBUG - { - support_grid_pattern.serialize(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.bin", iRun, layer_id, layer.print_z)); - - BoundingBox bbox = get_extents(contact_polygons); - bbox.merge(get_extents(new_layer.polygons)); - ::Slic3r::SVG svg(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z)); - svg.draw(union_ex(*new_layer.contact_polygons, false), "gray", 0.5f); - svg.draw(union_ex(contact_polygons, false), "blue", 0.5f); - svg.draw(union_ex(dense_interface_polygons, false), "green", 0.5f); - svg.draw(union_ex(new_layer.polygons, true), "red", 0.5f); - svg.draw_outline(union_ex(new_layer.polygons, true), "black", "black", scale_(0.1f)); - } - #endif /* SLIC3R_DEBUG */ } } - #ifdef SLIC3R_DEBUG - { - BoundingBox bbox = get_extents(contact_polygons); - bbox.merge(get_extents(new_layer.polygons)); - ::Slic3r::SVG svg(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z)); - svg.draw(union_ex(*new_layer.contact_polygons, false), "gray", 0.5f); - svg.draw(union_ex(contact_polygons, false), "blue", 0.5f); - svg.draw(union_ex(overhang_polygons, false), "green", 0.5f); - svg.draw(union_ex(new_layer.polygons, true), "red", 0.5f); - svg.draw_outline(union_ex(new_layer.polygons, true), "black", "black", scale_(0.1f)); - } - #endif /* SLIC3R_DEBUG */ // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded. // Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons. @@ -1493,13 +1297,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // If the layer is extruded with no bridging flow, support just the normal extrusions. layer_new.height = m_slicing_params.soluble_interface ? // Align the interface layer with the object's layer height. - object.layers[layer_id + 1]->height : + object.layers()[layer_id + 1]->height : // Place a bridge flow interface layer over the top surface. //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?) // According to Jindrich the bottom surfaces work well. //FIXME test the bridging flow instead? m_support_material_interface_flow.nozzle_diameter; - layer_new.print_z = m_slicing_params.soluble_interface ? object.layers[layer_id + 1]->print_z : + layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z : layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value; layer_new.bottom_z = layer.print_z; layer_new.idx_object_layer_below = layer_id; @@ -1520,7 +1324,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta if (diff > 0.) { // The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer. assert(diff < layer_new.height + EPSILON); - assert(layer_new.height - diff >= this->m_support_layer_height_min - EPSILON); + assert(layer_new.height - diff >= m_support_layer_height_min - EPSILON); layer_new.print_z = top_contacts[top_idx]->print_z; layer_new.height -= diff; } else { @@ -1543,7 +1347,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta //FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage? touching = offset(touching, float(SCALED_EPSILON)); for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) { - const Layer &layer_above = *object.layers[layer_id_above]; + const Layer &layer_above = *object.layers()[layer_id_above]; if (layer_above.print_z > layer_new.print_z - EPSILON) break; if (! layer_support_areas[layer_id_above].empty()) { @@ -1774,7 +1578,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // Verify that the extremes are separated by m_support_layer_height_min. for (size_t i = 1; i < extremes.size(); ++ i) { assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() == 0. || - extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > this->m_support_layer_height_min - EPSILON); + extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > m_support_layer_height_min - EPSILON); assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > 0. || extremes[i]->layer_type == extremes[i-1]->layer_type || (extremes[i]->layer_type == sltBottomContact && extremes[i - 1]->layer_type == sltTopContact)); @@ -1798,7 +1602,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // This is a bottom of a synchronized (or soluble) top contact layer, its height has been decided in this->top_contact_layers(). assert(extr2->layer_type == sltTopContact); assert(extr2->bottom_z == m_slicing_params.first_print_layer_height); - assert(extr2->print_z >= m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON); + assert(extr2->print_z >= m_slicing_params.first_print_layer_height + m_support_layer_height_min - EPSILON); if (intermediate_layers.empty() || intermediate_layers.back()->print_z < m_slicing_params.first_print_layer_height) { MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); layer_new.bottom_z = 0.; @@ -1838,7 +1642,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int if (synchronize) { // Emit support layers synchronized with the object layers. // Find the first object layer, which has its print_z in this support Z range. - while (idx_layer_object < object.layers.size() && object.layers[idx_layer_object]->print_z < extr1z + EPSILON) + while (idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr1z + EPSILON) ++ idx_layer_object; if (idx_layer_object == 0 && extr1z == m_slicing_params.raft_interface_top_z) { // Insert one base support layer below the object. @@ -1849,11 +1653,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int intermediate_layers.push_back(&layer_new); } // Emit all intermediate support layers synchronized with object layers up to extr2z. - for (; idx_layer_object < object.layers.size() && object.layers[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) { + for (; idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) { MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); - layer_new.print_z = object.layers[idx_layer_object]->print_z; - layer_new.height = object.layers[idx_layer_object]->height; - layer_new.bottom_z = (idx_layer_object > 0) ? object.layers[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height); + layer_new.print_z = object.layers()[idx_layer_object]->print_z; + layer_new.height = object.layers()[idx_layer_object]->height; + layer_new.bottom_z = (idx_layer_object > 0) ? object.layers()[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height); assert(intermediate_layers.empty() || intermediate_layers.back()->print_z < layer_new.print_z + EPSILON); intermediate_layers.push_back(&layer_new); } @@ -1863,10 +1667,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int assert(n_layers_extra > 0); coordf_t step = dist / coordf_t(n_layers_extra); if (extr1 != nullptr && extr1->layer_type == sltTopContact && - extr1->print_z + this->m_support_layer_height_min > extr1->bottom_z + step) { + extr1->print_z + m_support_layer_height_min > extr1->bottom_z + step) { // The bottom extreme is a bottom of a top surface. Ensure that the gap // between the 1st intermediate layer print_z and extr1->print_z is not too small. - assert(extr1->bottom_z + this->m_support_layer_height_min < extr1->print_z + EPSILON); + assert(extr1->bottom_z + m_support_layer_height_min < extr1->print_z + EPSILON); // Generate the first intermediate layer. MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); layer_new.bottom_z = extr1->bottom_z; @@ -1960,7 +1764,7 @@ void PrintObjectSupportMaterial::generate_base_layers( Polygons polygons_new; // Use the precomputed layer_support_areas. - idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers, idx_object_layer_above, + idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers(), idx_object_layer_above, [&layer_intermediate](const Layer *layer){ return layer->print_z <= layer_intermediate.print_z + EPSILON; })); polygons_new = layer_support_areas[idx_object_layer_above]; @@ -2096,23 +1900,23 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( // Find the overlapping object layers including the extra above / below gap. coordf_t z_threshold = support_layer.print_z - support_layer.height - gap_extra_below + EPSILON; idx_object_layer_overlapping = idx_higher_or_equal( - object.layers, idx_object_layer_overlapping, + object.layers(), idx_object_layer_overlapping, [z_threshold](const Layer *layer){ return layer->print_z >= z_threshold; }); // Collect all the object layers intersecting with this layer. Polygons polygons_trimming; size_t i = idx_object_layer_overlapping; - for (; i < object.layers.size(); ++ i) { - const Layer &object_layer = *object.layers[i]; + for (; i < object.layers().size(); ++ i) { + const Layer &object_layer = *object.layers()[i]; if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON) break; polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } - if (! this->m_slicing_params.soluble_interface) { + if (! m_slicing_params.soluble_interface) { // Collect all bottom surfaces, which will be extruded with a bridging flow. - for (; i < object.layers.size(); ++ i) { - const Layer &object_layer = *object.layers[i]; + for (; i < object.layers().size(); ++ i) { + const Layer &object_layer = *object.layers()[i]; bool some_region_overlaps = false; - for (LayerRegion *region : object_layer.regions) { + for (LayerRegion *region : object_layer.regions()) { coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config); if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON) break; @@ -2120,7 +1924,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( polygons_append(polygons_trimming, offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)), gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - if (region->region()->config.overhangs.value) + if (region->region()->config().overhangs.value) SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming); } if (! some_region_overlaps) @@ -2218,7 +2022,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf // Expand the bases of the support columns in the 1st layer. columns_base->polygons = diff( offset(columns_base->polygons, inflate_factor_1st_layer), - offset(m_object->layers.front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); + offset(m_object->layers().front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (contacts != nullptr) columns_base->polygons = diff(columns_base->polygons, interface_polygons); } @@ -2364,8 +2168,8 @@ struct MyLayerExtruded else *m_polygons_to_extrude = std::move(polygons); } - Polygons& polygons_to_extrude() { return (this->m_polygons_to_extrude == nullptr) ? layer->polygons : *this->m_polygons_to_extrude; } - const Polygons& polygons_to_extrude() const { return (this->m_polygons_to_extrude == nullptr) ? layer->polygons : *this->m_polygons_to_extrude; } + Polygons& polygons_to_extrude() { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } + const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } bool could_merge(const MyLayerExtruded &other) const { return ! this->empty() && ! other.empty() && @@ -2378,21 +2182,21 @@ struct MyLayerExtruded assert(this->could_merge(other)); // 1) Merge the rest polygons to extrude, if there are any. if (other.m_polygons_to_extrude != nullptr) { - if (this->m_polygons_to_extrude == nullptr) { + if (m_polygons_to_extrude == nullptr) { // This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). assert(this->extrusions.empty()); - this->m_polygons_to_extrude = new Polygons(this->layer->polygons); + m_polygons_to_extrude = new Polygons(this->layer->polygons); } - Slic3r::polygons_append(*this->m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude)); - *this->m_polygons_to_extrude = union_(*this->m_polygons_to_extrude, true); + Slic3r::polygons_append(*m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude)); + *m_polygons_to_extrude = union_(*m_polygons_to_extrude, true); delete other.m_polygons_to_extrude; other.m_polygons_to_extrude = nullptr; - } else if (this->m_polygons_to_extrude != nullptr) { + } else if (m_polygons_to_extrude != nullptr) { assert(other.m_polygons_to_extrude == nullptr); // The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). assert(other.extrusions.empty()); - Slic3r::polygons_append(*this->m_polygons_to_extrude, other.layer->polygons); - *this->m_polygons_to_extrude = union_(*this->m_polygons_to_extrude, true); + Slic3r::polygons_append(*m_polygons_to_extrude, other.layer->polygons); + *m_polygons_to_extrude = union_(*m_polygons_to_extrude, true); } // 2) Merge the extrusions. this->extrusions.insert(this->extrusions.end(), other.extrusions.begin(), other.extrusions.end()); @@ -2512,11 +2316,11 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const const Point &p1 = *(it-1); const Point &p2 = *it; // Intersection of a ray (p1, p2) with a circle placed at center_last, with radius of circle_distance. - const Pointf v_seg(coordf_t(p2.x) - coordf_t(p1.x), coordf_t(p2.y) - coordf_t(p1.y)); - const Pointf v_cntr(coordf_t(p1.x - center_last.x), coordf_t(p1.y - center_last.y)); - coordf_t a = dot(v_seg); - coordf_t b = 2. * dot(v_seg, v_cntr); - coordf_t c = dot(v_cntr) - circle_distance * circle_distance; + const Vec2d v_seg(coordf_t(p2(0)) - coordf_t(p1(0)), coordf_t(p2(1)) - coordf_t(p1(1))); + const Vec2d v_cntr(coordf_t(p1(0) - center_last(0)), coordf_t(p1(1) - center_last(1))); + coordf_t a = v_seg.squaredNorm(); + coordf_t b = 2. * v_seg.dot(v_cntr); + coordf_t c = v_cntr.squaredNorm() - circle_distance * circle_distance; coordf_t disc = b * b - 4. * a * c; if (disc > 0.) { // The circle intersects a ray. Avoid the parts of the segment inside the circle. @@ -2536,7 +2340,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const } seg_current_pt = &p1; seg_current_t = t; - center_last = Point(p1.x + coord_t(v_seg.x * t), p1.y + coord_t(v_seg.y * t)); + center_last = Point(p1(0) + coord_t(v_seg(0) * t), p1(1) + coord_t(v_seg(1) * t)); // It has been verified that the new point is far enough from center_last. // Ensure, that it is far enough from all the centers. std::pair circle_closest = circle_centers_lookup.find(center_last); @@ -2555,9 +2359,9 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const circle_centers.push_back(center_last); } external_loops.push_back(std::move(contour)); - for (Points::const_iterator it_center = circle_centers.begin(); it_center != circle_centers.end(); ++ it_center) { + for (const Point ¢er : circle_centers) { circles.push_back(circle); - circles.back().translate(*it_center); + circles.back().translate(center); } } } @@ -2646,7 +2450,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const // Transform loops into ExtrusionPath objects. extrusion_entities_append_paths( top_contact_layer.extrusions, - STDMOVE(loop_lines), + std::move(loop_lines), erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height); } @@ -2814,7 +2618,10 @@ void modulate_extrusion_by_overlapping_layers( (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back()); } private: - ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {} + ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) { + return *this; + } + const std::vector &m_path_fragments; }; const coord_t search_radius = 7; @@ -2847,7 +2654,7 @@ void modulate_extrusion_by_overlapping_layers( if (end_and_dist2.first == nullptr) { // New fragment connecting to pt_current was not found. // Verify that the last point found is close to the original end point of the unfragmented path. - //const double d2 = pt_end.distance_to_sq(pt_current); + //const double d2 = (pt_end - pt_current).squaredNorm(); //assert(d2 < coordf_t(search_radius * search_radius)); // End of the path. break; @@ -2990,7 +2797,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { assert(support_layer_id < raft_layers.size()); - SupportLayer &support_layer = *object.support_layers[support_layer_id]; + SupportLayer &support_layer = *object.support_layers()[support_layer_id]; assert(support_layer.support_fills.entities.empty()); MyLayer &raft_layer = *raft_layers[support_layer_id]; @@ -3020,7 +2827,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( to_infill = offset_ex(to_infill, float(- 0.4 * flow.scaled_spacing())); extrusion_entities_append_paths( support_layer.support_fills.entities, - to_polylines(STDMOVE(to_infill_polygons)), + to_polylines(std::move(to_infill_polygons)), erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); } if (! to_infill.empty()) { @@ -3034,7 +2841,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Destination support_layer.support_fills.entities, // Regions to fill - STDMOVE(to_infill), + std::move(to_infill), // Filler and its parameters filler, float(support_density), // Extrusion parameters @@ -3086,9 +2893,9 @@ void PrintObjectSupportMaterial::generate_toolpaths( MyLayerExtruded interface_layer; std::vector overlaps; }; - std::vector layer_caches(object.support_layers.size(), LayerCache()); + std::vector layer_caches(object.support_layers().size(), LayerCache()); - tbb::parallel_for(tbb::blocked_range(n_raft_layers, object.support_layers.size()), + tbb::parallel_for(tbb::blocked_range(n_raft_layers, object.support_layers().size()), [this, &object, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &layer_caches, &loop_interface_processor, infill_pattern, &bbox_object, support_density, interface_density, interface_angle, &angles, link_max_length_factor, with_sheath] (const tbb::blocked_range& range) { @@ -3103,7 +2910,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler_support->set_bounding_box(bbox_object); for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { - SupportLayer &support_layer = *object.support_layers[support_layer_id]; + SupportLayer &support_layer = *object.support_layers()[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; // Find polygons with the same print_z. @@ -3230,14 +3037,14 @@ void PrintObjectSupportMaterial::generate_toolpaths( to_infill = offset_ex(to_infill, - 0.4 * float(flow.scaled_spacing())); extrusion_entities_append_paths( base_layer.extrusions, - to_polylines(STDMOVE(to_infill_polygons)), + to_polylines(std::move(to_infill_polygons)), erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); } fill_expolygons_generate_paths( // Destination base_layer.extrusions, // Regions to fill - STDMOVE(to_infill), + std::move(to_infill), // Filler and its parameters filler, density, // Extrusion parameters @@ -3301,11 +3108,11 @@ void PrintObjectSupportMaterial::generate_toolpaths( }); // Now modulate the support layer height in parallel. - tbb::parallel_for(tbb::blocked_range(n_raft_layers, object.support_layers.size()), + tbb::parallel_for(tbb::blocked_range(n_raft_layers, object.support_layers().size()), [this, &object, &layer_caches] (const tbb::blocked_range& range) { for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { - SupportLayer &support_layer = *object.support_layers[support_layer_id]; + SupportLayer &support_layer = *object.support_layers()[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) { modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping); @@ -3344,9 +3151,9 @@ void PrintObjectSupportMaterial::clip_by_pillars( BoundingBox bbox; for (LayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it) bbox.merge(get_extents((*it)->polygons)); - grid.reserve(size_t(ceil(bb.size().x / pillar_spacing)) * size_t(ceil(bb.size().y / pillar_spacing))); - for (coord_t x = bb.min.x; x <= bb.max.x - pillar_size; x += pillar_spacing) { - for (coord_t y = bb.min.y; y <= bb.max.y - pillar_size; y += pillar_spacing) { + grid.reserve(size_t(ceil(bb.size()(0) / pillar_spacing)) * size_t(ceil(bb.size()(1) / pillar_spacing))); + for (coord_t x = bb.min(0); x <= bb.max(0) - pillar_size; x += pillar_spacing) { + for (coord_t y = bb.min(1); y <= bb.max(1) - pillar_size; y += pillar_spacing) { grid.push_back(pillar); for (size_t i = 0; i < pillar.points.size(); ++ i) grid.back().points[i].translate(Point(x, y)); diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp similarity index 99% rename from xs/src/libslic3r/SupportMaterial.hpp rename to src/libslic3r/SupportMaterial.hpp index dcb3bd5b3a..2e1a05946f 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -224,7 +224,7 @@ private: // Produce the actual G-code. void generate_toolpaths( - const PrintObject &object, + const PrintObject &object, const MyLayersPtr &raft_layers, const MyLayersPtr &bottom_contacts, const MyLayersPtr &top_contacts, diff --git a/xs/src/libslic3r/Surface.cpp b/src/libslic3r/Surface.cpp similarity index 97% rename from xs/src/libslic3r/Surface.cpp rename to src/libslic3r/Surface.cpp index 384540d87b..0e9eca7fd6 100644 --- a/xs/src/libslic3r/Surface.cpp +++ b/src/libslic3r/Surface.cpp @@ -106,9 +106,9 @@ Point export_surface_type_legend_to_svg_box_size() void export_surface_type_legend_to_svg(SVG &svg, const Point &pos) { // 1st row - coord_t pos_x0 = pos.x + scale_(1.); + coord_t pos_x0 = pos(0) + scale_(1.); coord_t pos_x = pos_x0; - coord_t pos_y = pos.y + scale_(1.5); + coord_t pos_y = pos(1) + scale_(1.5); coord_t step_x = scale_(10.); svg.draw_legend(Point(pos_x, pos_y), "perimeter" , surface_type_to_color_name(stPerimeter)); pos_x += step_x; @@ -121,7 +121,7 @@ void export_surface_type_legend_to_svg(SVG &svg, const Point &pos) svg.draw_legend(Point(pos_x, pos_y), "invalid" , surface_type_to_color_name(SurfaceType(-1))); // 2nd row pos_x = pos_x0; - pos_y = pos.y+scale_(2.8); + pos_y = pos(1)+scale_(2.8); svg.draw_legend(Point(pos_x, pos_y), "internal" , surface_type_to_color_name(stInternal)); pos_x += step_x; svg.draw_legend(Point(pos_x, pos_y), "internal solid" , surface_type_to_color_name(stInternalSolid)); diff --git a/xs/src/libslic3r/Surface.hpp b/src/libslic3r/Surface.hpp similarity index 100% rename from xs/src/libslic3r/Surface.hpp rename to src/libslic3r/Surface.hpp diff --git a/xs/src/libslic3r/SurfaceCollection.cpp b/src/libslic3r/SurfaceCollection.cpp similarity index 97% rename from xs/src/libslic3r/SurfaceCollection.cpp rename to src/libslic3r/SurfaceCollection.cpp index 42ddf9574f..6db5993067 100644 --- a/xs/src/libslic3r/SurfaceCollection.cpp +++ b/src/libslic3r/SurfaceCollection.cpp @@ -170,8 +170,8 @@ void SurfaceCollection::export_to_svg(const char *path, bool show_labels) for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) bbox.merge(get_extents(surface->expolygon)); Point legend_size = export_surface_type_legend_to_svg_box_size(); - Point legend_pos(bbox.min.x, bbox.max.y); - bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y)); + Point legend_pos(bbox.min(0), bbox.max(1)); + bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); SVG svg(path, bbox); const float transparency = 0.5f; diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/src/libslic3r/SurfaceCollection.hpp similarity index 100% rename from xs/src/libslic3r/SurfaceCollection.hpp rename to src/libslic3r/SurfaceCollection.hpp diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp new file mode 100644 index 0000000000..090013df0d --- /dev/null +++ b/src/libslic3r/Technologies.hpp @@ -0,0 +1,35 @@ +#ifndef _technologies_h_ +#define _technologies_h_ + +//============ +// debug techs +//============ + +// Shows camera target in the 3D scene +#define ENABLE_SHOW_CAMERA_TARGET 0 + +//============= +// 1.42.0 techs +//============= +#define ENABLE_1_42_0 1 + +// Add double click on gizmo grabbers to reset transformation components to their default value +#define ENABLE_GIZMOS_RESET (1 && ENABLE_1_42_0) +// Uses a unique opengl context +#define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0) +// Disable synchronization of unselected instances +#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0) +// Modified camera target behavior +#define ENABLE_MODIFIED_CAMERA_TARGET (1 && ENABLE_1_42_0) +// Add Geometry::Transformation class and use it into ModelInstance, ModelVolume and GLVolume +#define ENABLE_MODELVOLUME_TRANSFORM (1 && ENABLE_1_42_0) +// Keeps objects on bed while scaling them using the scale gizmo +#define ENABLE_ENSURE_ON_BED_WHILE_SCALING (1 && ENABLE_MODELVOLUME_TRANSFORM) +// Gizmos always rendered on top of objects +#define ENABLE_GIZMOS_ON_TOP (1 && ENABLE_1_42_0) +// New menu layout (open/save/save as project + import/export) +#define ENABLE_NEW_MENU_LAYOUT (1 && ENABLE_1_42_0) + +#endif // _technologies_h_ + + diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp similarity index 57% rename from xs/src/libslic3r/TriangleMesh.cpp rename to src/libslic3r/TriangleMesh.cpp index 544a5d00bc..425967f9f7 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1,7 +1,6 @@ #include "TriangleMesh.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" -#include "MultiPoint.hpp" #include "qhull/src/libqhullcpp/Qhull.h" #include "qhull/src/libqhullcpp/QhullFacetList.h" #include "qhull/src/libqhullcpp/QhullVertexSet.h" @@ -41,13 +40,7 @@ namespace Slic3r { -TriangleMesh::TriangleMesh() - : repaired(false) -{ - stl_initialize(&this->stl); -} - -TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& facets ) +TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& facets ) : repaired(false) { stl_initialize(&this->stl); @@ -62,51 +55,22 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& fa for (int i = 0; i < stl.stats.number_of_facets; i++) { stl_facet facet; - - const Pointf3& ref_f1 = points[facets[i].x]; - facet.vertex[0].x = ref_f1.x; - facet.vertex[0].y = ref_f1.y; - facet.vertex[0].z = ref_f1.z; - - const Pointf3& ref_f2 = points[facets[i].y]; - facet.vertex[1].x = ref_f2.x; - facet.vertex[1].y = ref_f2.y; - facet.vertex[1].z = ref_f2.z; - - const Pointf3& ref_f3 = points[facets[i].z]; - facet.vertex[2].x = ref_f3.x; - facet.vertex[2].y = ref_f3.y; - facet.vertex[2].z = ref_f3.z; - + facet.vertex[0] = points[facets[i](0)].cast(); + facet.vertex[1] = points[facets[i](1)].cast(); + facet.vertex[2] = points[facets[i](2)].cast(); facet.extra[0] = 0; facet.extra[1] = 0; - float normal[3]; + stl_normal normal; stl_calculate_normal(normal, &facet); stl_normalize_vector(normal); - facet.normal.x = normal[0]; - facet.normal.y = normal[1]; - facet.normal.z = normal[2]; + facet.normal = normal; stl.facet_start[i] = facet; } stl_get_size(&stl); } -TriangleMesh::TriangleMesh(const TriangleMesh &other) : - repaired(false) -{ - stl_initialize(&this->stl); - *this = other; -} - -TriangleMesh::TriangleMesh(TriangleMesh &&other) : - repaired(false) -{ - stl_initialize(&this->stl); - this->swap(other); -} - TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other) { stl_close(&this->stl); @@ -134,42 +98,8 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other) return *this; } -TriangleMesh& TriangleMesh::operator=(TriangleMesh &&other) +void TriangleMesh::repair() { - this->swap(other); - return *this; -} - -void -TriangleMesh::swap(TriangleMesh &other) -{ - std::swap(this->stl, other.stl); - std::swap(this->repaired, other.repaired); -} - -TriangleMesh::~TriangleMesh() { - stl_close(&this->stl); -} - -void -TriangleMesh::ReadSTLFile(const char* input_file) { - stl_open(&stl, input_file); -} - -void -TriangleMesh::write_ascii(const char* output_file) -{ - stl_write_ascii(&this->stl, output_file, ""); -} - -void -TriangleMesh::write_binary(const char* output_file) -{ - stl_write_binary(&this->stl, output_file, ""); -} - -void -TriangleMesh::repair() { if (this->repaired) return; // admesh fails when repairing empty meshes @@ -178,21 +108,23 @@ TriangleMesh::repair() { BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started"; // checking exact - stl_check_facets_exact(&stl); + BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact"; + stl_check_facets_exact(&stl); stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge); stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge); stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge); // checking nearby //int last_edges_fixed = 0; - float tolerance = stl.stats.shortest_edge; + float tolerance = stl.stats.shortest_edge; float increment = stl.stats.bounding_diameter / 10000.0; int iterations = 2; if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { for (int i = 0; i < iterations; i++) { if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { //printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations); - stl_check_facets_nearby(&stl, tolerance); + BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_nearby"; + stl_check_facets_nearby(&stl, tolerance); //printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed); //last_edges_fixed = stl.stats.edges_fixed; tolerance += increment; @@ -204,29 +136,31 @@ TriangleMesh::repair() { // remove_unconnected if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets"; stl_remove_unconnected_facets(&stl); } // fill_holes -#if 0 - // Don't fill holes, the current algorithm does more harm than good on complex holes. - // Rather let the slicing algorithm close gaps in 2D slices. if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + BOOST_LOG_TRIVIAL(trace) << "\tstl_fill_holes"; stl_fill_holes(&stl); stl_clear_error(&stl); } -#endif // normal_directions + BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions"; stl_fix_normal_directions(&stl); // normal_values + BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values"; stl_fix_normal_values(&stl); // always calculate the volume and reverse all normals if volume is negative + BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume"; stl_calculate_volume(&stl); // neighbors + BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors"; stl_verify_neighbors(&stl); this->repaired = true; @@ -269,13 +203,7 @@ void TriangleMesh::check_topology() } } -bool TriangleMesh::is_manifold() const -{ - return this->stl.stats.connected_facets_3_edge == this->stl.stats.number_of_facets; -} - -void -TriangleMesh::reset_repair_stats() { +void TriangleMesh::reset_repair_stats() { this->stl.stats.degenerate_facets = 0; this->stl.stats.edges_fixed = 0; this->stl.stats.facets_removed = 0; @@ -285,8 +213,7 @@ TriangleMesh::reset_repair_stats() { this->stl.stats.normals_fixed = 0; } -bool -TriangleMesh::needed_repair() const +bool TriangleMesh::needed_repair() const { return this->stl.stats.degenerate_facets > 0 || this->stl.stats.edges_fixed > 0 @@ -296,14 +223,8 @@ TriangleMesh::needed_repair() const || this->stl.stats.backwards_edges > 0; } -size_t -TriangleMesh::facets_count() const +void TriangleMesh::WriteOBJFile(char* output_file) { - return this->stl.stats.number_of_facets; -} - -void -TriangleMesh::WriteOBJFile(char* output_file) { stl_generate_shared_vertices(&stl); stl_write_obj(&stl, output_file); } @@ -314,13 +235,9 @@ void TriangleMesh::scale(float factor) stl_invalidate_shared_vertices(&this->stl); } -void TriangleMesh::scale(const Pointf3 &versor) +void TriangleMesh::scale(const Vec3d &versor) { - float fversor[3]; - fversor[0] = versor.x; - fversor[1] = versor.y; - fversor[2] = versor.z; - stl_scale_versor(&this->stl, fversor); + stl_scale_versor(&this->stl, versor.cast()); stl_invalidate_shared_vertices(&this->stl); } @@ -332,17 +249,6 @@ void TriangleMesh::translate(float x, float y, float z) stl_invalidate_shared_vertices(&this->stl); } -void TriangleMesh::rotate(float angle, Pointf3 axis) -{ - if (angle == 0.f) - return; - - axis = normalize(axis); - Eigen::Transform m = Eigen::Transform::Identity(); - m.rotate(Eigen::AngleAxisf(angle, Eigen::Vector3f(axis.x, axis.y, axis.z))); - stl_transform(&stl, (float*)m.data()); -} - void TriangleMesh::rotate(float angle, const Axis &axis) { if (angle == 0.f) @@ -361,19 +267,15 @@ void TriangleMesh::rotate(float angle, const Axis &axis) stl_invalidate_shared_vertices(&this->stl); } -void TriangleMesh::rotate_x(float angle) +void TriangleMesh::rotate(float angle, const Vec3d& axis) { - this->rotate(angle, X); -} + if (angle == 0.f) + return; -void TriangleMesh::rotate_y(float angle) -{ - this->rotate(angle, Y); -} - -void TriangleMesh::rotate_z(float angle) -{ - this->rotate(angle, Z); + Vec3d axis_norm = axis.normalized(); + Transform3d m = Transform3d::Identity(); + m.rotate(Eigen::AngleAxisd(angle, axis_norm)); + stl_transform(&stl, m); } void TriangleMesh::mirror(const Axis &axis) @@ -388,52 +290,34 @@ void TriangleMesh::mirror(const Axis &axis) stl_invalidate_shared_vertices(&this->stl); } -void TriangleMesh::mirror_x() +void TriangleMesh::transform(const Transform3d& t) { - this->mirror(X); -} - -void TriangleMesh::mirror_y() -{ - this->mirror(Y); -} - -void TriangleMesh::mirror_z() -{ - this->mirror(Z); -} - -void TriangleMesh::transform(const float* matrix3x4) -{ - if (matrix3x4 == nullptr) - return; - - stl_transform(&stl, const_cast(matrix3x4)); - stl_invalidate_shared_vertices(&stl); + stl_transform(&stl, t); } void TriangleMesh::align_to_origin() { this->translate( - -(this->stl.stats.min.x), - -(this->stl.stats.min.y), - -(this->stl.stats.min.z) - ); + - this->stl.stats.min(0), + - this->stl.stats.min(1), + - this->stl.stats.min(2)); } void TriangleMesh::rotate(double angle, Point* center) { if (angle == 0.) return; - this->translate(float(-center->x), float(-center->y), 0); + Vec2f c = center->cast(); + this->translate(-c(0), -c(1), 0); stl_rotate_z(&(this->stl), (float)angle); - this->translate(float(+center->x), float(+center->y), 0); + this->translate(c(0), c(1), 0); } bool TriangleMesh::has_multiple_patches() const { // we need neighbors - if (!this->repaired) CONFESS("split() requires repair()"); + if (!this->repaired) + throw std::runtime_error("split() requires repair()"); if (this->stl.stats.number_of_facets == 0) return false; @@ -463,7 +347,8 @@ bool TriangleMesh::has_multiple_patches() const size_t TriangleMesh::number_of_patches() const { // we need neighbors - if (!this->repaired) CONFESS("split() requires repair()"); + if (!this->repaired) + throw std::runtime_error("split() requires repair()"); if (this->stl.stats.number_of_facets == 0) return false; @@ -502,11 +387,12 @@ size_t TriangleMesh::number_of_patches() const TriangleMeshPtrs TriangleMesh::split() const { - TriangleMeshPtrs meshes; - std::set seen_facets; + TriangleMeshPtrs meshes; + std::vector facet_visited(this->stl.stats.number_of_facets, false); // we need neighbors - if (!this->repaired) CONFESS("split() requires repair()"); + if (!this->repaired) + throw std::runtime_error("split() requires repair()"); // loop while we have remaining facets for (;;) { @@ -514,38 +400,38 @@ TriangleMeshPtrs TriangleMesh::split() const std::queue facet_queue; std::deque facets; for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; facet_idx++) { - if (seen_facets.find(facet_idx) == seen_facets.end()) { + if (! facet_visited[facet_idx]) { // if facet was not seen put it into queue and start searching facet_queue.push(facet_idx); break; } } - if (facet_queue.empty()) break; - - while (!facet_queue.empty()) { + if (facet_queue.empty()) + break; + + while (! facet_queue.empty()) { int facet_idx = facet_queue.front(); facet_queue.pop(); - if (seen_facets.find(facet_idx) != seen_facets.end()) continue; - facets.push_back(facet_idx); - for (int j = 0; j <= 2; j++) { - facet_queue.push(this->stl.neighbors_start[facet_idx].neighbor[j]); + if (! facet_visited[facet_idx]) { + facets.emplace_back(facet_idx); + for (int j = 0; j < 3; ++ j) + facet_queue.push(this->stl.neighbors_start[facet_idx].neighbor[j]); + facet_visited[facet_idx] = true; } - seen_facets.insert(facet_idx); } - + TriangleMesh* mesh = new TriangleMesh; - meshes.push_back(mesh); + meshes.emplace_back(mesh); mesh->stl.stats.type = inmemory; mesh->stl.stats.number_of_facets = facets.size(); mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets; stl_clear_error(&mesh->stl); stl_allocate(&mesh->stl); - int first = 1; - for (std::deque::const_iterator facet = facets.begin(); facet != facets.end(); ++facet) { + bool first = true; + for (std::deque::const_iterator facet = facets.begin(); facet != facets.end(); ++ facet) { mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet]; stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first); - first = 0; } } @@ -583,25 +469,30 @@ ExPolygons TriangleMesh::horizontal_projection() const stl_facet* facet = &this->stl.facet_start[i]; Polygon p; p.points.resize(3); - p.points[0] = Point::new_scale(facet->vertex[0].x, facet->vertex[0].y); - p.points[1] = Point::new_scale(facet->vertex[1].x, facet->vertex[1].y); - p.points[2] = Point::new_scale(facet->vertex[2].x, facet->vertex[2].y); + p.points[0] = Point::new_scale(facet->vertex[0](0), facet->vertex[0](1)); + p.points[1] = Point::new_scale(facet->vertex[1](0), facet->vertex[1](1)); + p.points[2] = Point::new_scale(facet->vertex[2](0), facet->vertex[2](1)); p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that - pp.push_back(p); + pp.emplace_back(p); } // the offset factor was tuned using groovemount.stl return union_ex(offset(pp, scale_(0.01)), true); } +const float* TriangleMesh::first_vertex() const +{ + return this->stl.facet_start ? &this->stl.facet_start->vertex[0](0) : nullptr; +} + Polygon TriangleMesh::convex_hull() { this->require_shared_vertices(); Points pp; pp.reserve(this->stl.stats.shared_vertices); for (int i = 0; i < this->stl.stats.shared_vertices; ++ i) { - stl_vertex* v = &this->stl.v_shared[i]; - pp.emplace_back(Point::new_scale(v->x, v->y)); + const stl_vertex &v = this->stl.v_shared[i]; + pp.emplace_back(Point::new_scale(v(0), v(1))); } return Slic3r::Geometry::convex_hull(pp); } @@ -610,16 +501,12 @@ BoundingBoxf3 TriangleMesh::bounding_box() const { BoundingBoxf3 bb; bb.defined = true; - bb.min.x = this->stl.stats.min.x; - bb.min.y = this->stl.stats.min.y; - bb.min.z = this->stl.stats.min.z; - bb.max.x = this->stl.stats.max.x; - bb.max.y = this->stl.stats.max.y; - bb.max.z = this->stl.stats.max.z; + bb.min = this->stl.stats.min.cast(); + bb.max = this->stl.stats.max.cast(); return bb; } -BoundingBoxf3 TriangleMesh::transformed_bounding_box(const std::vector& matrix) const +BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& t) const { bool has_shared = (stl.v_shared != nullptr); if (!has_shared) @@ -630,16 +517,16 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const std::vector& m if (vertices_count == 0) return BoundingBoxf3(); - Eigen::MatrixXf src_vertices(3, vertices_count); + Eigen::MatrixXd src_vertices(3, vertices_count); if (stl.stats.shared_vertices > 0) { stl_vertex* vertex_ptr = stl.v_shared; for (int i = 0; i < stl.stats.shared_vertices; ++i) { - src_vertices(0, i) = vertex_ptr->x; - src_vertices(1, i) = vertex_ptr->y; - src_vertices(2, i) = vertex_ptr->z; + src_vertices(0, i) = (double)(*vertex_ptr)(0); + src_vertices(1, i) = (double)(*vertex_ptr)(1); + src_vertices(2, i) = (double)(*vertex_ptr)(2); vertex_ptr += 1; } } @@ -651,42 +538,34 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const std::vector& m { for (int i = 0; i < 3; ++i) { - src_vertices(0, v_id) = facet_ptr->vertex[i].x; - src_vertices(1, v_id) = facet_ptr->vertex[i].y; - src_vertices(2, v_id) = facet_ptr->vertex[i].z; + src_vertices(0, v_id) = (double)facet_ptr->vertex[i](0); + src_vertices(1, v_id) = (double)facet_ptr->vertex[i](1); + src_vertices(2, v_id) = (double)facet_ptr->vertex[i](2); + ++v_id; } facet_ptr += 1; - ++v_id; } } if (!has_shared && (stl.stats.shared_vertices > 0)) stl_invalidate_shared_vertices(&stl); - Eigen::Transform m; - ::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float)); + Eigen::MatrixXd dst_vertices(3, vertices_count); + dst_vertices = t * src_vertices.colwise().homogeneous(); - Eigen::MatrixXf dst_vertices(3, vertices_count); - dst_vertices = m * src_vertices.colwise().homogeneous(); - - float min_x = dst_vertices(0, 0); - float max_x = dst_vertices(0, 0); - float min_y = dst_vertices(1, 0); - float max_y = dst_vertices(1, 0); - float min_z = dst_vertices(2, 0); - float max_z = dst_vertices(2, 0); + Vec3d v_min(dst_vertices(0, 0), dst_vertices(1, 0), dst_vertices(2, 0)); + Vec3d v_max = v_min; for (int i = 1; i < vertices_count; ++i) { - min_x = std::min(min_x, dst_vertices(0, i)); - max_x = std::max(max_x, dst_vertices(0, i)); - min_y = std::min(min_y, dst_vertices(1, i)); - max_y = std::max(max_y, dst_vertices(1, i)); - min_z = std::min(min_z, dst_vertices(2, i)); - max_z = std::max(max_z, dst_vertices(2, i)); + for (int j = 0; j < 3; ++j) + { + v_min(j) = std::min(v_min(j), dst_vertices(j, i)); + v_max(j) = std::max(v_max(j), dst_vertices(j, i)); + } } - return BoundingBoxf3(Pointf3((coordf_t)min_x, (coordf_t)min_y, (coordf_t)min_z), Pointf3((coordf_t)max_x, (coordf_t)max_y, (coordf_t)max_z)); + return BoundingBoxf3(v_min, v_max); } TriangleMesh TriangleMesh::convex_hull_3d() const @@ -705,7 +584,7 @@ TriangleMesh TriangleMesh::convex_hull_3d() const for (int i = 0; i < 3; ++i) { const stl_vertex& v = facet_ptr->vertex[i]; - src_vertices.emplace_back(v.x, v.y, v.z); + src_vertices.emplace_back(v(0), v(1), v(2)); } facet_ptr += 1; @@ -725,8 +604,8 @@ TriangleMesh TriangleMesh::convex_hull_3d() const } // Let's collect results: - Pointf3s det_vertices; - std::vector facets; + Pointf3s dst_vertices; + std::vector facets; auto facet_list = qhull.facetList().toStdVector(); for (const orgQhull::QhullFacet& facet : facet_list) { // iterate through facets @@ -736,23 +615,18 @@ TriangleMesh TriangleMesh::convex_hull_3d() const orgQhull::QhullPoint p = vertices[i].point(); const float* coords = p.coordinates(); - det_vertices.emplace_back(coords[0], coords[1], coords[2]); + dst_vertices.emplace_back(coords[0], coords[1], coords[2]); } - unsigned int size = (unsigned int)det_vertices.size(); + unsigned int size = (unsigned int)dst_vertices.size(); facets.emplace_back(size - 3, size - 2, size - 1); } - TriangleMesh output_mesh(det_vertices, facets); + TriangleMesh output_mesh(dst_vertices, facets); output_mesh.repair(); output_mesh.require_shared_vertices(); return output_mesh; } -const float* TriangleMesh::first_vertex() const -{ - return stl.facet_start ? &stl.facet_start->vertex[0].x : nullptr; -} - void TriangleMesh::require_shared_vertices() { BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start"; @@ -763,34 +637,32 @@ void TriangleMesh::require_shared_vertices() stl_generate_shared_vertices(&(this->stl)); } #ifdef _DEBUG - // Verify validity of neighborship data. - for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) { - const stl_neighbors &nbr = stl.neighbors_start[facet_idx]; - const int *vertices = stl.v_indices[facet_idx].vertex; - for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) { - int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx]; - if (nbr_face != -1) { - assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]); - assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]); - } - } - } + // Verify validity of neighborship data. + for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) { + const stl_neighbors &nbr = stl.neighbors_start[facet_idx]; + const int *vertices = stl.v_indices[facet_idx].vertex; + for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) { + int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx]; + if (nbr_face != -1) { + assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]); + assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]); + } + } + } #endif /* _DEBUG */ BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end"; } -TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : - mesh(_mesh) +void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type throw_on_cancel) { + mesh = _mesh; _mesh->require_shared_vertices(); + throw_on_cancel(); facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1); v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices); // Scale the copied vertices. - for (int i = 0; i < this->mesh->stl.stats.shared_vertices; ++ i) { - this->v_scaled_shared[i].x /= float(SCALING_FACTOR); - this->v_scaled_shared[i].y /= float(SCALING_FACTOR); - this->v_scaled_shared[i].z /= float(SCALING_FACTOR); - } + for (int i = 0; i < this->mesh->stl.stats.shared_vertices; ++ i) + this->v_scaled_shared[i] *= float(1. / SCALING_FACTOR); // Create a mapping from triangle edge into face. struct EdgeToFace { @@ -822,6 +694,7 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : e2f.face_edge = - e2f.face_edge; } } + throw_on_cancel(); std::sort(edges_map.begin(), edges_map.end()); // Assign a unique common edge id to touching triangle edges. @@ -853,18 +726,20 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : } } // Assign an edge index to the 1st face. - this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges; + this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges; if (found) { EdgeToFace &edge_j = edges_map[j]; - this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges; - // Mark the edge as connected. - edge_j.face = -1; - } + this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges; + // Mark the edge as connected. + edge_j.face = -1; + } ++ num_edges; + if ((i & 0x0ffff) == 0) + throw_on_cancel(); } } -void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const +void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice"; @@ -901,13 +776,17 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vector(0,this->mesh->stl.stats.number_of_facets), - [&lines, &lines_mutex, &z, this](const tbb::blocked_range& range) { - for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx) + [&lines, &lines_mutex, &z, throw_on_cancel, this](const tbb::blocked_range& range) { + for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx) { + if ((facet_idx & 0x0ffff) == 0) + throw_on_cancel(); this->_slice_do(facet_idx, &lines, &lines_mutex, z); + } } ); } - + throw_on_cancel(); + // v_scaled_shared could be freed here // build loops @@ -915,9 +794,12 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vectorresize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&lines, &layers, this](const tbb::blocked_range& range) { - for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) + [&lines, &layers, throw_on_cancel, this](const tbb::blocked_range& range) { + for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) { + if ((line_idx & 0x0ffff) == 0) + throw_on_cancel(); this->make_loops(lines[line_idx], &(*layers)[line_idx]); + } } ); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice finished"; @@ -962,52 +844,75 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vectormesh->stl.facet_start[facet_idx]; // find facet extents - const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); - const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); + const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2))); + const float max_z = fmaxf(facet.vertex[0](2), fmaxf(facet.vertex[1](2), facet.vertex[2](2))); #ifdef SLIC3R_TRIANGLEMESH_DEBUG printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, - facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, - facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, - facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); + facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0](2), + facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1](2), + facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2](2)); printf("z: min = %.2f, max = %.2f\n", min_z, max_z); #endif /* SLIC3R_TRIANGLEMESH_DEBUG */ // find layer extents std::vector::const_iterator min_layer, max_layer; min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z - max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z + max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z); // first layer, whose slice_z is > max_z #ifdef SLIC3R_TRIANGLEMESH_DEBUG - printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); + printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()) - 1); #endif /* SLIC3R_TRIANGLEMESH_DEBUG */ - for (std::vector::const_iterator it = min_layer; it != max_layer + 1; ++it) { + for (std::vector::const_iterator it = min_layer; it != max_layer; ++it) { std::vector::size_type layer_idx = it - z.begin(); IntersectionLine il; if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il) == TriangleMeshSlicer::Slicing) { boost::lock_guard l(*lines_mutex); if (il.edge_type == feHorizontal) { - // Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume. + // Insert all marked edges of the face. The marked edges do not share an edge with another horizontal face + // (they may not have a nighbor, or their neighbor is vertical) + const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; + const bool reverse = this->mesh->stl.facet_start[facet_idx].normal(2) < 0; + for (int j = 0; j < 3; ++ j) + if (il.flags & ((IntersectionLine::EDGE0_NO_NEIGHBOR | IntersectionLine::EDGE0_FOLD) << j)) { + int a_id = vertices[j % 3]; + int b_id = vertices[(j+1) % 3]; + if (reverse) + std::swap(a_id, b_id); + const stl_vertex &a = this->v_scaled_shared[a_id]; + const stl_vertex &b = this->v_scaled_shared[b_id]; + il.a(0) = a(0); + il.a(1) = a(1); + il.b(0) = b(0); + il.b(1) = b(1); + il.a_id = a_id; + il.b_id = b_id; + assert(il.a != il.b); + // This edge will not be used as a seed for loop extraction if it was added due to a fold of two overlapping horizontal faces. + il.set_no_seed((IntersectionLine::EDGE0_FOLD << j) != 0); + (*lines)[layer_idx].emplace_back(il); + } } else (*lines)[layer_idx].emplace_back(il); } } } -void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const +void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { std::vector layers_p; - this->slice(z, &layers_p); + this->slice(z, &layers_p, throw_on_cancel); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start"; layers->resize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&layers_p, layers, this](const tbb::blocked_range& range) { + [&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { #ifdef SLIC3R_TRIANGLEMESH_DEBUG - printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]); + printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]); #endif + throw_on_cancel(); this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]); } }); @@ -1027,21 +932,22 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( // Reorder vertices so that the first one is the one with lowest Z. // This is needed to get all intersection lines in a consistent order // (external on the right of the line) - const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; - int i = (facet.vertex[1].z == min_z) ? 1 : ((facet.vertex[2].z == min_z) ? 2 : 0); - for (int j = i; j - i < 3; ++j) { // loop through facet edges + const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; + int i = (facet.vertex[1](2) == min_z) ? 1 : ((facet.vertex[2](2) == min_z) ? 2 : 0); + for (int j = i; j - i < 3; ++j ) { // loop through facet edges int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)]; int a_id = vertices[j % 3]; int b_id = vertices[(j+1) % 3]; - const stl_vertex *a = &this->v_scaled_shared[a_id]; - const stl_vertex *b = &this->v_scaled_shared[b_id]; + const stl_vertex &a = this->v_scaled_shared[a_id]; + const stl_vertex &b = this->v_scaled_shared[b_id]; // Is edge or face aligned with the cutting plane? - if (a->z == slice_z && b->z == slice_z) { + if (a(2) == slice_z && b(2) == slice_z) { // Edge is horizontal and belongs to the current layer. const stl_vertex &v0 = this->v_scaled_shared[vertices[0]]; const stl_vertex &v1 = this->v_scaled_shared[vertices[1]]; const stl_vertex &v2 = this->v_scaled_shared[vertices[2]]; + bool swap = false; const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal; // We may ignore this edge for slicing purposes, but we may still use it for object cutting. FacetSliceType result = Slicing; @@ -1049,85 +955,157 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( if (min_z == max_z) { // All three vertices are aligned with slice_z. line_out->edge_type = feHorizontal; - result = Cutting; - if (normal.z < 0) { + // Mark neighbor edges, which do not have a neighbor. + uint32_t edges = 0; + for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx) { + // If the neighbor with an edge starting with a vertex idx (nbr_idx - 2) shares no + // opposite face, add it to the edges to process when slicing. + if (nbr.neighbor[nbr_idx] == -1) { + // Mark this edge to be added to the slice. + edges |= (IntersectionLine::EDGE0_NO_NEIGHBOR << nbr_idx); + } +#if 1 + else if (normal(2) > 0) { + // Produce edges for opposite faced overlapping horizontal faces aka folds. + // This method often produces connecting lines (noise) at the cutting plane. + // Produce the edges for the top facing face of the pair of top / bottom facing faces. + + // Index of a neighbor face. + const int nbr_face = nbr.neighbor[nbr_idx]; + const int *nbr_vertices = this->mesh->stl.v_indices[nbr_face].vertex; + int idx_vertex_opposite = nbr_vertices[nbr.which_vertex_not[nbr_idx]]; + const stl_vertex &c2 = this->v_scaled_shared[idx_vertex_opposite]; + if (c2(2) == slice_z) { + // Edge shared by facet_idx and nbr_face. + int a_id = vertices[nbr_idx]; + int b_id = vertices[(nbr_idx + 1) % 3]; + int c1_id = vertices[(nbr_idx + 2) % 3]; + const stl_vertex &a = this->v_scaled_shared[a_id]; + const stl_vertex &b = this->v_scaled_shared[b_id]; + const stl_vertex &c1 = this->v_scaled_shared[c1_id]; + // Verify that the two neighbor faces share a common edge. + assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id); + assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id); + double n1 = (double(c1(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c1(1)) - double(a(1))) * (double(b(0)) - double(a(0))); + double n2 = (double(c2(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c2(1)) - double(a(1))) * (double(b(0)) - double(a(0))); + if (n1 * n2 > 0) + // The two faces overlap. This indicates an invalid mesh geometry (non-manifold), + // but these are the real world objects, and leaving out these edges leads to missing contours. + edges |= (IntersectionLine::EDGE0_FOLD << nbr_idx); + } + } +#endif + } + // Use some edges of this triangle for slicing only if at least one of its edge does not have an opposite face. + result = (edges == 0) ? Cutting : Slicing; + line_out->flags |= edges; + if (normal(2) < 0) { // If normal points downwards this is a bottom horizontal facet so we reverse its point order. - std::swap(a, b); - std::swap(a_id, b_id); + swap = true; } } else { // Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane. int nbr_idx = j % 3; int nbr_face = nbr.neighbor[nbr_idx]; // Is the third vertex below the cutting plane? - bool third_below = v0.z < slice_z || v1.z < slice_z || v2.z < slice_z; - // Two vertices on the cutting plane, the third vertex is below the plane. Consider the edge to be part of the slice - // only if it is the upper edge. - // (the bottom most edge resp. vertex of a triangle is not owned by the triangle, but the top most edge resp. vertex is part of the triangle - // in respect to the cutting plane). - result = third_below ? Slicing : Cutting; + bool third_below = v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z; + // Is this a concave corner? + if (nbr_face == -1) { +#ifdef _DEBUG + printf("Face has no neighbor!\n"); +#endif + } else { + assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id); + assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id); + int idx_vertex_opposite = this->mesh->stl.v_indices[nbr_face].vertex[nbr.which_vertex_not[nbr_idx]]; + const stl_vertex &c = this->v_scaled_shared[idx_vertex_opposite]; + if (c(2) == slice_z) { + double normal_nbr = (double(c(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c(1)) - double(a(1))) * (double(b(0)) - double(a(0))); +#if 0 + if ((normal_nbr < 0) == third_below) { + printf("Flipped normal?\n"); + } +#endif + result = + // A vertical face shares edge with a horizontal face. Verify, whether the shared edge makes a convex or concave corner. + // Unfortunately too often there are flipped normals, which brake our assumption. Let's rather return every edge, + // and leth the code downstream hopefully handle it. + #if 1 + // Ignore concave corners for slicing. + // This method has the unfortunate property, that folds in a horizontal plane create concave corners, + // leading to broken contours, if these concave corners are not replaced by edges of the folds, see above. + ((normal_nbr < 0) == third_below) ? Cutting : Slicing; + #else + // Use concave corners for slicing. This leads to the test 01_trianglemesh.t "slicing a top tangent plane includes its area" failing, + // and rightly so. + Slicing; + #endif + } else { + // For a pair of faces touching exactly at the cutting plane, ignore one of them. An arbitrary rule is to ignore the face with a higher index. + result = (facet_idx < nbr_face) ? Slicing : Cutting; + } + } if (third_below) { line_out->edge_type = feTop; - std::swap(a, b); - std::swap(a_id, b_id); + swap = true; } else line_out->edge_type = feBottom; } - line_out->a.x = a->x; - line_out->a.y = a->y; - line_out->b.x = b->x; - line_out->b.y = b->y; - line_out->a_id = a_id; - line_out->b_id = b_id; + line_out->a = to_2d(swap ? b : a).cast(); + line_out->b = to_2d(swap ? a : b).cast(); + line_out->a_id = swap ? b_id : a_id; + line_out->b_id = swap ? a_id : b_id; assert(line_out->a != line_out->b); return result; } - if (a->z == slice_z) { + if (a(2) == slice_z) { // Only point a alings with the cutting plane. if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) { point_on_layer = num_points; IntersectionPoint &point = points[num_points ++]; - point.x = a->x; - point.y = a->y; + point(0) = a(0); + point(1) = a(1); point.point_id = a_id; } - } else if (b->z == slice_z) { + } else if (b(2) == slice_z) { // Only point b alings with the cutting plane. if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) { point_on_layer = num_points; IntersectionPoint &point = points[num_points ++]; - point.x = b->x; - point.y = b->y; + point(0) = b(0); + point(1) = b(1); point.point_id = b_id; } - } else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) { + } else if ((a(2) < slice_z && b(2) > slice_z) || (b(2) < slice_z && a(2) > slice_z)) { // A general case. The face edge intersects the cutting plane. Calculate the intersection point. - assert(a_id != b_id); + assert(a_id != b_id); // Sort the edge to give a consistent answer. - if (a_id > b_id) { - std::swap(a_id, b_id); - std::swap(a, b); - } + const stl_vertex *pa = &a; + const stl_vertex *pb = &b; + if (a_id > b_id) { + std::swap(a_id, b_id); + std::swap(pa, pb); + } IntersectionPoint &point = points[num_points]; - double t = (double(slice_z) - double(b->z)) / (double(a->z) - double(b->z)); - if (t <= 0.) { - if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) { - point.x = a->x; - point.y = a->y; - point_on_layer = num_points ++; - point.point_id = a_id; - } - } else if (t >= 1.) { - if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) { - point.x = b->x; - point.y = b->y; - point_on_layer = num_points ++; - point.point_id = b_id; - } - } else { - point.x = coord_t(floor(double(b->x) + (double(a->x) - double(b->x)) * t + 0.5)); - point.y = coord_t(floor(double(b->y) + (double(a->y) - double(b->y)) * t + 0.5)); + double t = (double(slice_z) - double((*pb)(2))) / (double((*pa)(2)) - double((*pb)(2))); + if (t <= 0.) { + if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) { + point(0) = (*pa)(0); + point(1) = (*pa)(1); + point_on_layer = num_points ++; + point.point_id = a_id; + } + } else if (t >= 1.) { + if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) { + point(0) = (*pb)(0); + point(1) = (*pb)(1); + point_on_layer = num_points ++; + point.point_id = b_id; + } + } else { + point(0) = coord_t(floor(double((*pb)(0)) + (double((*pa)(0)) - double((*pb)(0))) * t + 0.5)); + point(1) = coord_t(floor(double((*pb)(1)) + (double((*pa)(1)) - double((*pb)(1))) * t + 0.5)); point.edge_id = edge_id; ++ num_points; } @@ -1233,344 +1211,6 @@ static inline void remove_tangent_edges(std::vector &lines) } } -struct OpenPolyline { - OpenPolyline() {}; - OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) : - start(start), end(end), points(std::move(points)), consumed(false) { this->length = Slic3r::length(this->points); } - void reverse() { - std::swap(start, end); - std::reverse(points.begin(), points.end()); - } - IntersectionReference start; - IntersectionReference end; - Points points; - double length; - bool consumed; -}; - -// called by TriangleMeshSlicer::make_loops() to connect sliced triangles into closed loops and open polylines by the triangle connectivity. -// Only connects segments crossing triangles of the same orientation. -static void chain_lines_by_triangle_connectivity(std::vector &lines, Polygons &loops, std::vector &open_polylines) -{ - // Build a map of lines by edge_a_id and a_id. - std::vector by_edge_a_id; - std::vector by_a_id; - by_edge_a_id.reserve(lines.size()); - by_a_id.reserve(lines.size()); - for (IntersectionLine &line : lines) { - if (! line.skip()) { - if (line.edge_a_id != -1) - by_edge_a_id.emplace_back(&line); - if (line.a_id != -1) - by_a_id.emplace_back(&line); - } - } - auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; }; - auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; }; - std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower); - std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower); - // Chain the segments with a greedy algorithm, collect the loops and unclosed polylines. - IntersectionLines::iterator it_line_seed = lines.begin(); - for (;;) { - // take first spare line and start a new loop - IntersectionLine *first_line = nullptr; - for (; it_line_seed != lines.end(); ++ it_line_seed) - if (it_line_seed->is_seed_candidate()) { - //if (! it_line_seed->skip()) { - first_line = &(*it_line_seed ++); - break; - } - if (first_line == nullptr) - break; - first_line->set_skip(); - Points loop_pts; - loop_pts.emplace_back(first_line->a); - IntersectionLine *last_line = first_line; - - /* - printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n", - first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id, - first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y); - */ - - IntersectionLine key; - for (;;) { - // find a line starting where last one finishes - IntersectionLine* next_line = nullptr; - if (last_line->edge_b_id != -1) { - key.edge_a_id = last_line->edge_b_id; - auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower); - if (it_begin != by_edge_a_id.end()) { - auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower); - for (auto it_line = it_begin; it_line != it_end; ++ it_line) - if (! (*it_line)->skip()) { - next_line = *it_line; - break; - } - } - } - if (next_line == nullptr && last_line->b_id != -1) { - key.a_id = last_line->b_id; - auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower); - if (it_begin != by_a_id.end()) { - auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower); - for (auto it_line = it_begin; it_line != it_end; ++ it_line) - if (! (*it_line)->skip()) { - next_line = *it_line; - break; - } - } - } - if (next_line == nullptr) { - // Check whether we closed this loop. - if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) || - (first_line->a_id != -1 && first_line->a_id == last_line->b_id)) { - // The current loop is complete. Add it to the output. - loops.emplace_back(std::move(loop_pts)); - #ifdef SLIC3R_TRIANGLEMESH_DEBUG - printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size()); - #endif - } else { - // This is an open polyline. Add it to the list of open polylines. These open polylines will processed later. - loop_pts.emplace_back(last_line->b); - open_polylines.emplace_back(OpenPolyline( - IntersectionReference(first_line->a_id, first_line->edge_a_id), - IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts))); - } - break; - } - /* - printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n", - next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id, - next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y); - */ - loop_pts.emplace_back(next_line->a); - last_line = next_line; - next_line->set_skip(); - } - } -} - -std::vector open_polylines_sorted(std::vector &open_polylines, bool update_lengths) -{ - std::vector out; - out.reserve(open_polylines.size()); - for (OpenPolyline &opl : open_polylines) - if (! opl.consumed) { - if (update_lengths) - opl.length = Slic3r::length(opl.points); - out.emplace_back(&opl); - } - std::sort(out.begin(), out.end(), [](const OpenPolyline *lhs, const OpenPolyline *rhs){ return lhs->length > rhs->length; }); - return out; -} - -// called by TriangleMeshSlicer::make_loops() to connect remaining open polylines across shared triangle edges and vertices. -// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation. -static void chain_open_polylines_exact(std::vector &open_polylines, Polygons &loops, bool try_connect_reversed) -{ - // Store the end points of open_polylines into vectors sorted - struct OpenPolylineEnd { - OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {} - OpenPolyline *polyline; - // Is it the start or end point? - bool start; - const IntersectionReference& ipref() const { return start ? polyline->start : polyline->end; } - // Return a unique ID for the intersection point. - // Return a positive id for a point, or a negative id for an edge. - int id() const { const IntersectionReference &r = ipref(); return (r.point_id >= 0) ? r.point_id : - r.edge_id; } - bool operator==(const OpenPolylineEnd &rhs) const { return this->polyline == rhs.polyline && this->start == rhs.start; } - }; - auto by_id_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.id() < ope2.id(); }; - std::vector by_id; - by_id.reserve(2 * open_polylines.size()); - for (OpenPolyline &opl : open_polylines) { - if (opl.start.point_id != -1 || opl.start.edge_id != -1) - by_id.emplace_back(OpenPolylineEnd(&opl, true)); - if (try_connect_reversed && (opl.end.point_id != -1 || opl.end.edge_id != -1)) - by_id.emplace_back(OpenPolylineEnd(&opl, false)); - } - std::sort(by_id.begin(), by_id.end(), by_id_lower); - // Find an iterator to by_id_lower for the particular end of OpenPolyline (by comparing the OpenPolyline pointer and the start attribute). - auto find_polyline_end = [&by_id, by_id_lower](const OpenPolylineEnd &end) -> std::vector::iterator { - for (auto it = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower); - it != by_id.end() && it->id() == end.id(); ++ it) - if (*it == end) - return it; - return by_id.end(); - }; - // Try to connect the loops. - std::vector sorted_by_length = open_polylines_sorted(open_polylines, false); - for (OpenPolyline *opl : sorted_by_length) { - if (opl->consumed) - continue; - opl->consumed = true; - OpenPolylineEnd end(opl, false); - for (;;) { - // find a line starting where last one finishes - auto it_next_start = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower); - for (; it_next_start != by_id.end() && it_next_start->id() == end.id(); ++ it_next_start) - if (! it_next_start->polyline->consumed) - goto found; - // The current loop could not be closed. Unmark the segment. - opl->consumed = false; - break; - found: - // Attach this polyline to the end of the initial polyline. - if (it_next_start->start) { - auto it = it_next_start->polyline->points.begin(); - std::copy(++ it, it_next_start->polyline->points.end(), back_inserter(opl->points)); - } else { - auto it = it_next_start->polyline->points.rbegin(); - std::copy(++ it, it_next_start->polyline->points.rend(), back_inserter(opl->points)); - } - opl->length += it_next_start->polyline->length; - // Mark the next polyline as consumed. - it_next_start->polyline->points.clear(); - it_next_start->polyline->length = 0.; - it_next_start->polyline->consumed = true; - if (try_connect_reversed) { - // Running in a mode, where the polylines may be connected by mixing their orientations. - // Update the end point lookup structure after the end point of the current polyline was extended. - auto it_end = find_polyline_end(end); - auto it_next_end = find_polyline_end(OpenPolylineEnd(it_next_start->polyline, !it_next_start->start)); - // Swap the end points of the current and next polyline, but keep the polyline ptr and the start flag. - std::swap(opl->end, it_next_end->start ? it_next_end->polyline->start : it_next_end->polyline->end); - // Swap the positions of OpenPolylineEnd structures in the sorted array to match their respective end point positions. - std::swap(*it_end, *it_next_end); - } - // Check whether we closed this loop. - if ((opl->start.edge_id != -1 && opl->start.edge_id == opl->end.edge_id) || - (opl->start.point_id != -1 && opl->start.point_id == opl->end.point_id)) { - // The current loop is complete. Add it to the output. - //assert(opl->points.front().point_id == opl->points.back().point_id); - //assert(opl->points.front().edge_id == opl->points.back().edge_id); - // Remove the duplicate last point. - opl->points.pop_back(); - if (opl->points.size() >= 3) { - if (try_connect_reversed && area(opl->points) < 0) - // The closed polygon is patched from pieces with messed up orientation, therefore - // the orientation of the patched up polygon is not known. - // Orient the patched up polygons CCW. This heuristic may close some holes and cavities. - std::reverse(opl->points.begin(), opl->points.end()); - loops.emplace_back(std::move(opl->points)); - } - opl->points.clear(); - break; - } - // Continue with the current loop. - } - } -} - -// called by TriangleMeshSlicer::make_loops() to connect remaining open polylines across shared triangle edges and vertices, -// possibly closing small gaps. -// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation. -static void chain_open_polylines_close_gaps(std::vector &open_polylines, Polygons &loops, double max_gap, bool try_connect_reversed) -{ - const coord_t max_gap_scaled = (coord_t)scale_(max_gap); - - // Sort the open polylines by their length, so the new loops will be seeded from longer chains. - // Update the polyline lengths, return only not yet consumed polylines. - std::vector sorted_by_length = open_polylines_sorted(open_polylines, true); - - // Store the end points of open_polylines into ClosestPointInRadiusLookup. - struct OpenPolylineEnd { - OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {} - OpenPolyline *polyline; - // Is it the start or end point? - bool start; - const Point& point() const { return start ? polyline->points.front() : polyline->points.back(); } - bool operator==(const OpenPolylineEnd &rhs) const { return this->polyline == rhs.polyline && this->start == rhs.start; } - }; - struct OpenPolylineEndAccessor { - const Point* operator()(const OpenPolylineEnd &pt) const { return pt.polyline->consumed ? nullptr : &pt.point(); } - }; - typedef ClosestPointInRadiusLookup ClosestPointLookupType; - ClosestPointLookupType closest_end_point_lookup(max_gap_scaled); - for (OpenPolyline *opl : sorted_by_length) { - closest_end_point_lookup.insert(OpenPolylineEnd(opl, true)); - if (try_connect_reversed) - closest_end_point_lookup.insert(OpenPolylineEnd(opl, false)); - } - // Try to connect the loops. - for (OpenPolyline *opl : sorted_by_length) { - if (opl->consumed) - continue; - OpenPolylineEnd end(opl, false); - if (try_connect_reversed) - // The end point of this polyline will be modified, thus the following entry will become invalid. Remove it. - closest_end_point_lookup.erase(end); - opl->consumed = true; - size_t n_segments_joined = 1; - for (;;) { - // Find a line starting where last one finishes, only return non-consumed open polylines (OpenPolylineEndAccessor returns null for consumed). - std::pair next_start_and_dist = closest_end_point_lookup.find(end.point()); - const OpenPolylineEnd *next_start = next_start_and_dist.first; - // Check whether we closed this loop. - double current_loop_closing_distance2 = opl->points.front().distance_to_sq(opl->points.back()); - bool loop_closed = current_loop_closing_distance2 < coordf_t(max_gap_scaled) * coordf_t(max_gap_scaled); - if (next_start != nullptr && loop_closed && current_loop_closing_distance2 < next_start_and_dist.second) { - // Heuristics to decide, whether to close the loop, or connect another polyline. - // One should avoid closing loops shorter than max_gap_scaled. - loop_closed = sqrt(current_loop_closing_distance2) < 0.3 * length(opl->points); - } - if (loop_closed) { - // Remove the start point of the current polyline from the lookup. - // Mark the current segment as not consumed, otherwise the closest_end_point_lookup.erase() would fail. - opl->consumed = false; - closest_end_point_lookup.erase(OpenPolylineEnd(opl, true)); - if (current_loop_closing_distance2 == 0.) { - // Remove the duplicate last point. - opl->points.pop_back(); - } else { - // The end points are different, keep both of them. - } - if (opl->points.size() >= 3) { - if (try_connect_reversed && n_segments_joined > 1 && area(opl->points) < 0) - // The closed polygon is patched from pieces with messed up orientation, therefore - // the orientation of the patched up polygon is not known. - // Orient the patched up polygons CCW. This heuristic may close some holes and cavities. - std::reverse(opl->points.begin(), opl->points.end()); - loops.emplace_back(std::move(opl->points)); - } - opl->points.clear(); - opl->consumed = true; - break; - } - if (next_start == nullptr) { - // The current loop could not be closed. Unmark the segment. - opl->consumed = false; - if (try_connect_reversed) - // Re-insert the end point. - closest_end_point_lookup.insert(OpenPolylineEnd(opl, false)); - break; - } - // Attach this polyline to the end of the initial polyline. - if (next_start->start) { - auto it = next_start->polyline->points.begin(); - if (*it == opl->points.back()) - ++ it; - std::copy(it, next_start->polyline->points.end(), back_inserter(opl->points)); - } else { - auto it = next_start->polyline->points.rbegin(); - if (*it == opl->points.back()) - ++ it; - std::copy(it, next_start->polyline->points.rend(), back_inserter(opl->points)); - } - ++ n_segments_joined; - // Remove the end points of the consumed polyline segment from the lookup. - OpenPolyline *opl2 = next_start->polyline; - closest_end_point_lookup.erase(OpenPolylineEnd(opl2, true)); - if (try_connect_reversed) - closest_end_point_lookup.erase(OpenPolylineEnd(opl2, false)); - opl2->points.clear(); - opl2->consumed = true; - // Continue with the current loop. - } - } -} - void TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) const { #if 0 @@ -1580,83 +1220,231 @@ void TriangleMeshSlicer::make_loops(std::vector &lines, Polygo assert(l.a != l.b); #endif /* _DEBUG */ - // There should be no tangent edges, as the horizontal triangles are ignored and if two triangles touch at a cutting plane, - // only the bottom triangle is considered to be cutting the plane. -// remove_tangent_edges(lines); + remove_tangent_edges(lines); -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - BoundingBox bbox_svg; - { - static int iRun = 0; - for (const Line &line : lines) { - bbox_svg.merge(line.a); - bbox_svg.merge(line.b); - } - SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-raw_lines-%d.svg", iRun ++).c_str(), bbox_svg); - for (const Line &line : lines) - svg.draw(line); - svg.Close(); + struct OpenPolyline { + OpenPolyline() {}; + OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) : + start(start), end(end), points(std::move(points)), consumed(false) {} + void reverse() { + std::swap(start, end); + std::reverse(points.begin(), points.end()); } -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + IntersectionReference start; + IntersectionReference end; + Points points; + bool consumed; + }; std::vector open_polylines; - chain_lines_by_triangle_connectivity(lines, *loops, open_polylines); - -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - { - static int iRun = 0; - SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-%d.svg", iRun ++).c_str(), bbox_svg); - svg.draw(union_ex(*loops)); - for (const OpenPolyline &pl : open_polylines) - svg.draw(Polyline(pl.points), "red"); - svg.Close(); + { + // Build a map of lines by edge_a_id and a_id. + std::vector by_edge_a_id; + std::vector by_a_id; + by_edge_a_id.reserve(lines.size()); + by_a_id.reserve(lines.size()); + for (IntersectionLine &line : lines) { + if (! line.skip()) { + if (line.edge_a_id != -1) + by_edge_a_id.emplace_back(&line); + if (line.a_id != -1) + by_a_id.emplace_back(&line); + } } -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; }; + auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; }; + std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower); + std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower); + // Chain the segments with a greedy algorithm, collect the loops and unclosed polylines. + IntersectionLines::iterator it_line_seed = lines.begin(); + for (;;) { + // take first spare line and start a new loop + IntersectionLine *first_line = nullptr; + for (; it_line_seed != lines.end(); ++ it_line_seed) + if (it_line_seed->is_seed_candidate()) { + //if (! it_line_seed->skip()) { + first_line = &(*it_line_seed ++); + break; + } + if (first_line == nullptr) + break; + first_line->set_skip(); + Points loop_pts; + loop_pts.emplace_back(first_line->a); + IntersectionLine *last_line = first_line; + + /* + printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n", + first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id, + first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y); + */ + + IntersectionLine key; + for (;;) { + // find a line starting where last one finishes + IntersectionLine* next_line = nullptr; + if (last_line->edge_b_id != -1) { + key.edge_a_id = last_line->edge_b_id; + auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower); + if (it_begin != by_edge_a_id.end()) { + auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower); + for (auto it_line = it_begin; it_line != it_end; ++ it_line) + if (! (*it_line)->skip()) { + next_line = *it_line; + break; + } + } + } + if (next_line == nullptr && last_line->b_id != -1) { + key.a_id = last_line->b_id; + auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower); + if (it_begin != by_a_id.end()) { + auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower); + for (auto it_line = it_begin; it_line != it_end; ++ it_line) + if (! (*it_line)->skip()) { + next_line = *it_line; + break; + } + } + } + if (next_line == nullptr) { + // Check whether we closed this loop. + if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) || + (first_line->a_id != -1 && first_line->a_id == last_line->b_id)) { + // The current loop is complete. Add it to the output. + loops->emplace_back(std::move(loop_pts)); + #ifdef SLIC3R_TRIANGLEMESH_DEBUG + printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size()); + #endif + } else { + // This is an open polyline. Add it to the list of open polylines. These open polylines will processed later. + loop_pts.emplace_back(last_line->b); + open_polylines.emplace_back(OpenPolyline( + IntersectionReference(first_line->a_id, first_line->edge_a_id), + IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts))); + } + break; + } + /* + printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n", + next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id, + next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y); + */ + loop_pts.emplace_back(next_line->a); + last_line = next_line; + next_line->set_skip(); + } + } + } // Now process the open polylines. - // Do it in two rounds, first try to connect in the same direction only, - // then try to connect the open polylines in reversed order as well. - chain_open_polylines_exact(open_polylines, *loops, false); - chain_open_polylines_exact(open_polylines, *loops, true); - -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - { - static int iRun = 0; - SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines2-%d.svg", iRun++).c_str(), bbox_svg); - svg.draw(union_ex(*loops)); - for (const OpenPolyline &pl : open_polylines) { - if (pl.points.empty()) - continue; - svg.draw(Polyline(pl.points), "red"); - svg.draw(pl.points.front(), "blue"); - svg.draw(pl.points.back(), "blue"); + if (! open_polylines.empty()) { + // Store the end points of open_polylines into vectors sorted + struct OpenPolylineEnd { + OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {} + OpenPolyline *polyline; + // Is it the start or end point? + bool start; + const IntersectionReference& ipref() const { return start ? polyline->start : polyline->end; } + int point_id() const { return ipref().point_id; } + int edge_id () const { return ipref().edge_id; } + }; + auto by_edge_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.edge_id() < ope2.edge_id(); }; + auto by_point_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.point_id() < ope2.point_id(); }; + std::vector by_edge_id; + std::vector by_point_id; + by_edge_id.reserve(2 * open_polylines.size()); + by_point_id.reserve(2 * open_polylines.size()); + for (OpenPolyline &opl : open_polylines) { + if (opl.start.edge_id != -1) + by_edge_id .emplace_back(OpenPolylineEnd(&opl, true)); + if (opl.end.edge_id != -1) + by_edge_id .emplace_back(OpenPolylineEnd(&opl, false)); + if (opl.start.point_id != -1) + by_point_id.emplace_back(OpenPolylineEnd(&opl, true)); + if (opl.end.point_id != -1) + by_point_id.emplace_back(OpenPolylineEnd(&opl, false)); } - svg.Close(); - } -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + std::sort(by_edge_id .begin(), by_edge_id .end(), by_edge_lower); + std::sort(by_point_id.begin(), by_point_id.end(), by_point_lower); - // Try to close gaps. - // Do it in two rounds, first try to connect in the same direction only, - // then try to connect the open polylines in reversed order as well. - const double max_gap = 2.; //mm - chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, false); - chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, true); - -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - { - static int iRun = 0; - SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-final-%d.svg", iRun++).c_str(), bbox_svg); - svg.draw(union_ex(*loops)); - for (const OpenPolyline &pl : open_polylines) { - if (pl.points.empty()) + // Try to connect the loops. + for (OpenPolyline &opl : open_polylines) { + if (opl.consumed) continue; - svg.draw(Polyline(pl.points), "red"); - svg.draw(pl.points.front(), "blue"); - svg.draw(pl.points.back(), "blue"); + opl.consumed = true; + OpenPolylineEnd end(&opl, false); + for (;;) { + // find a line starting where last one finishes + OpenPolylineEnd* next_start = nullptr; + if (end.edge_id() != -1) { + auto it_begin = std::lower_bound(by_edge_id.begin(), by_edge_id.end(), end, by_edge_lower); + if (it_begin != by_edge_id.end()) { + auto it_end = std::upper_bound(it_begin, by_edge_id.end(), end, by_edge_lower); + for (auto it_edge = it_begin; it_edge != it_end; ++ it_edge) + if (! it_edge->polyline->consumed) { + next_start = &(*it_edge); + break; + } + } + } + if (next_start == nullptr && end.point_id() != -1) { + auto it_begin = std::lower_bound(by_point_id.begin(), by_point_id.end(), end, by_point_lower); + if (it_begin != by_point_id.end()) { + auto it_end = std::upper_bound(it_begin, by_point_id.end(), end, by_point_lower); + for (auto it_point = it_begin; it_point != it_end; ++ it_point) + if (! it_point->polyline->consumed) { + next_start = &(*it_point); + break; + } + } + } + if (next_start == nullptr) { + // The current loop could not be closed. Unmark the segment. + opl.consumed = false; + break; + } + // Attach this polyline to the end of the initial polyline. + if (next_start->start) { + auto it = next_start->polyline->points.begin(); + std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points)); + //opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.end()); + } else { + auto it = next_start->polyline->points.rbegin(); + std::copy(++ it, next_start->polyline->points.rend(), back_inserter(opl.points)); + //opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.rend()); + } + end = *next_start; + end.start = !end.start; + next_start->polyline->points.clear(); + next_start->polyline->consumed = true; + // Check whether we closed this loop. + const IntersectionReference &ip1 = opl.start; + const IntersectionReference &ip2 = end.ipref(); + if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) || + (ip1.point_id != -1 && ip1.point_id == ip2.point_id)) { + // The current loop is complete. Add it to the output. + //assert(opl.points.front().point_id == opl.points.back().point_id); + //assert(opl.points.front().edge_id == opl.points.back().edge_id); + // Remove the duplicate last point. + opl.points.pop_back(); + if (opl.points.size() >= 3) { + // The closed polygon is patched from pieces with messed up orientation, therefore + // the orientation of the patched up polygon is not known. + // Orient the patched up polygons CCW. This heuristic may close some holes and cavities. + double area = 0.; + for (size_t i = 0, j = opl.points.size() - 1; i < opl.points.size(); j = i ++) + area += double(opl.points[j](0) + opl.points[i](0)) * double(opl.points[i](1) - opl.points[j](1)); + if (area < 0) + std::reverse(opl.points.begin(), opl.points.end()); + loops->emplace_back(std::move(opl.points)); + } + opl.points.clear(); + break; + } + // Continue with the current loop. + } } - svg.Close(); - } -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + } } // Only used to cut the mesh into two halves. @@ -1672,9 +1460,9 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector &l if (loop->area() >= 0.) { ExPolygon ex; ex.contour = *loop; - slices->push_back(ex); + slices->emplace_back(ex); } else { - holes.push_back(*loop); + holes.emplace_back(*loop); } } @@ -1701,15 +1489,15 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector &l if (slice_idx == -1) // Ignore this hole. continue; - assert(current_contour_area < std::numeric_limits::max() && current_contour_area >= -hole->area()); - (*slices)[slice_idx].holes.emplace_back(std::move(*hole)); + assert(current_contour_area < std::numeric_limits::max() && current_contour_area >= -hole->area()); + (*slices)[slice_idx].holes.emplace_back(std::move(*hole)); } #if 0 // If the input mesh is not valid, the holes may intersect with the external contour. // Rather subtract them from the outer contour. Polygons poly; - for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) { + for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) { if (it_slice->holes.empty()) { poly.emplace_back(std::move(it_slice->contour)); } else { @@ -1719,7 +1507,7 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector &l it->reverse(); polygons_append(poly, diff(contours, it_slice->holes)); } - } + } // If the input mesh is not valid, the input contours may intersect. *slices = union_ex(poly); #endif @@ -1765,8 +1553,8 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic //std::vector area; //std::vector sorted_area; // vector of indices //for (Polygons::const_iterator loop = loops.begin(); loop != loops.end(); ++ loop) { - // area.push_back(loop->area()); - // sorted_area.push_back(loop - loops.begin()); + // area.emplace_back(loop->area()); + // sorted_area.emplace_back(loop - loops.begin()); //} // //// outer first @@ -1781,7 +1569,7 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic // would do the same, thus repeating the calculation */ // Polygons::const_iterator loop = loops.begin() + *loop_idx; // if (area[*loop_idx] > +EPSILON) - // p_slices.push_back(*loop); + // p_slices.emplace_back(*loop); // else if (area[*loop_idx] < -EPSILON) // //FIXME This is arbitrary and possibly very slow. // // If the hole is inside a polygon, then there is no need to diff. @@ -1831,20 +1619,20 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; // find facet extents - float min_z = std::min(facet->vertex[0].z, std::min(facet->vertex[1].z, facet->vertex[2].z)); - float max_z = std::max(facet->vertex[0].z, std::max(facet->vertex[1].z, facet->vertex[2].z)); + float min_z = std::min(facet->vertex[0](2), std::min(facet->vertex[1](2), facet->vertex[2](2))); + float max_z = std::max(facet->vertex[0](2), std::max(facet->vertex[1](2), facet->vertex[2](2))); // intersect facet with cutting plane IntersectionLine line; if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line) != TriangleMeshSlicer::NoSlice) { // Save intersection lines for generating correct triangulations. if (line.edge_type == feTop) { - lower_lines.push_back(line); + lower_lines.emplace_back(line); } else if (line.edge_type == feBottom) { - upper_lines.push_back(line); + upper_lines.emplace_back(line); } else if (line.edge_type != feHorizontal) { - lower_lines.push_back(line); - upper_lines.push_back(line); + lower_lines.emplace_back(line); + upper_lines.emplace_back(line); } } @@ -1859,47 +1647,47 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) // look for the vertex on whose side of the slicing plane there are no other vertices int isolated_vertex; - if ( (facet->vertex[0].z > z) == (facet->vertex[1].z > z) ) { + if ( (facet->vertex[0](2) > z) == (facet->vertex[1](2) > z) ) { isolated_vertex = 2; - } else if ( (facet->vertex[1].z > z) == (facet->vertex[2].z > z) ) { + } else if ( (facet->vertex[1](2) > z) == (facet->vertex[2](2) > z) ) { isolated_vertex = 0; } else { isolated_vertex = 1; } // get vertices starting from the isolated one - stl_vertex* v0 = &facet->vertex[isolated_vertex]; - stl_vertex* v1 = &facet->vertex[(isolated_vertex+1) % 3]; - stl_vertex* v2 = &facet->vertex[(isolated_vertex+2) % 3]; + const stl_vertex &v0 = facet->vertex[isolated_vertex]; + const stl_vertex &v1 = facet->vertex[(isolated_vertex+1) % 3]; + const stl_vertex &v2 = facet->vertex[(isolated_vertex+2) % 3]; // intersect v0-v1 and v2-v0 with cutting plane and make new vertices stl_vertex v0v1, v2v0; - v0v1.x = v1->x + (v0->x - v1->x) * (z - v1->z) / (v0->z - v1->z); - v0v1.y = v1->y + (v0->y - v1->y) * (z - v1->z) / (v0->z - v1->z); - v0v1.z = z; - v2v0.x = v2->x + (v0->x - v2->x) * (z - v2->z) / (v0->z - v2->z); - v2v0.y = v2->y + (v0->y - v2->y) * (z - v2->z) / (v0->z - v2->z); - v2v0.z = z; + v0v1(0) = v1(0) + (v0(0) - v1(0)) * (z - v1(2)) / (v0(2) - v1(2)); + v0v1(1) = v1(1) + (v0(1) - v1(1)) * (z - v1(2)) / (v0(2) - v1(2)); + v0v1(2) = z; + v2v0(0) = v2(0) + (v0(0) - v2(0)) * (z - v2(2)) / (v0(2) - v2(2)); + v2v0(1) = v2(1) + (v0(1) - v2(1)) * (z - v2(2)) / (v0(2) - v2(2)); + v2v0(2) = z; // build the triangular facet stl_facet triangle; triangle.normal = facet->normal; - triangle.vertex[0] = *v0; + triangle.vertex[0] = v0; triangle.vertex[1] = v0v1; triangle.vertex[2] = v2v0; // build the facets forming a quadrilateral on the other side stl_facet quadrilateral[2]; quadrilateral[0].normal = facet->normal; - quadrilateral[0].vertex[0] = *v1; - quadrilateral[0].vertex[1] = *v2; + quadrilateral[0].vertex[0] = v1; + quadrilateral[0].vertex[1] = v2; quadrilateral[0].vertex[2] = v0v1; quadrilateral[1].normal = facet->normal; - quadrilateral[1].vertex[0] = *v2; + quadrilateral[1].vertex[0] = v2; quadrilateral[1].vertex[1] = v2v0; quadrilateral[1].vertex[2] = v0v1; - if (v0->z > z) { + if (v0(2) > z) { if (upper != NULL) stl_add_facet(&upper->stl, &triangle); if (lower != NULL) { stl_add_facet(&lower->stl, &quadrilateral[0]); @@ -1931,13 +1719,11 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) Polygon p = *polygon; p.reverse(); stl_facet facet; - facet.normal.x = 0; - facet.normal.y = 0; - facet.normal.z = -1; + facet.normal = stl_normal(0, 0, -1.f); for (size_t i = 0; i <= 2; ++i) { - facet.vertex[i].x = unscale(p.points[i].x); - facet.vertex[i].y = unscale(p.points[i].y); - facet.vertex[i].z = z; + facet.vertex[i](0) = unscale(p.points[i](0)); + facet.vertex[i](1) = unscale(p.points[i](1)); + facet.vertex[i](2) = z; } stl_add_facet(&upper->stl, &facet); } @@ -1957,13 +1743,11 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) // convert triangles to facets and append them to mesh for (Polygons::const_iterator polygon = triangles.begin(); polygon != triangles.end(); ++polygon) { stl_facet facet; - facet.normal.x = 0; - facet.normal.y = 0; - facet.normal.z = 1; + facet.normal = stl_normal(0, 0, 1.f); for (size_t i = 0; i <= 2; ++i) { - facet.vertex[i].x = unscale(polygon->points[i].x); - facet.vertex[i].y = unscale(polygon->points[i].y); - facet.vertex[i].z = z; + facet.vertex[i](0) = unscale(polygon->points[i](0)); + facet.vertex[i](1) = unscale(polygon->points[i](1)); + facet.vertex[i](2) = z; } stl_add_facet(&lower->stl, &facet); } @@ -1976,19 +1760,19 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) // Generate the vertex list for a cube solid of arbitrary size in X/Y/Z. TriangleMesh make_cube(double x, double y, double z) { - Pointf3 pv[8] = { - Pointf3(x, y, 0), Pointf3(x, 0, 0), Pointf3(0, 0, 0), - Pointf3(0, y, 0), Pointf3(x, y, z), Pointf3(0, y, z), - Pointf3(0, 0, z), Pointf3(x, 0, z) + Vec3d pv[8] = { + Vec3d(x, y, 0), Vec3d(x, 0, 0), Vec3d(0, 0, 0), + Vec3d(0, y, 0), Vec3d(x, y, z), Vec3d(0, y, z), + Vec3d(0, 0, z), Vec3d(x, 0, z) }; - Point3 fv[12] = { - Point3(0, 1, 2), Point3(0, 2, 3), Point3(4, 5, 6), - Point3(4, 6, 7), Point3(0, 4, 7), Point3(0, 7, 1), - Point3(1, 7, 6), Point3(1, 6, 2), Point3(2, 6, 5), - Point3(2, 5, 3), Point3(4, 0, 3), Point3(4, 3, 5) + Vec3crd fv[12] = { + Vec3crd(0, 1, 2), Vec3crd(0, 2, 3), Vec3crd(4, 5, 6), + Vec3crd(4, 6, 7), Vec3crd(0, 4, 7), Vec3crd(0, 7, 1), + Vec3crd(1, 7, 6), Vec3crd(1, 6, 2), Vec3crd(2, 6, 5), + Vec3crd(2, 5, 3), Vec3crd(4, 0, 3), Vec3crd(4, 3, 5) }; - std::vector facets(&fv[0], &fv[0]+12); + std::vector facets(&fv[0], &fv[0]+12); Pointf3s vertices(&pv[0], &pv[0]+8); TriangleMesh mesh(vertices ,facets); @@ -2000,11 +1784,11 @@ TriangleMesh make_cube(double x, double y, double z) { // Default is 360 sides, angle fa is in radians. TriangleMesh make_cylinder(double r, double h, double fa) { Pointf3s vertices; - std::vector facets; + std::vector facets; // 2 special vertices, top and bottom center, rest are relative to this - vertices.push_back(Pointf3(0.0, 0.0, 0.0)); - vertices.push_back(Pointf3(0.0, 0.0, h)); + vertices.emplace_back(Vec3d(0.0, 0.0, 0.0)); + vertices.emplace_back(Vec3d(0.0, 0.0, h)); // adjust via rounding to get an even multiple for any provided angle. double angle = (2*PI / floor(2*PI / fa)); @@ -2014,26 +1798,23 @@ TriangleMesh make_cylinder(double r, double h, double fa) { // top and bottom. // Special case: Last line shares 2 vertices with the first line. unsigned id = vertices.size() - 1; - vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, 0)); - vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, h)); + vertices.emplace_back(Vec3d(sin(0) * r , cos(0) * r, 0)); + vertices.emplace_back(Vec3d(sin(0) * r , cos(0) * r, h)); for (double i = 0; i < 2*PI; i+=angle) { - Pointf3 b(0, r, 0); - Pointf3 t(0, r, h); - b.rotate(i, Pointf3(0,0,0)); - t.rotate(i, Pointf3(0,0,h)); - vertices.push_back(b); - vertices.push_back(t); + Vec2d p = Eigen::Rotation2Dd(i) * Eigen::Vector2d(0, r); + vertices.emplace_back(Vec3d(p(0), p(1), 0.)); + vertices.emplace_back(Vec3d(p(0), p(1), h)); id = vertices.size() - 1; - facets.push_back(Point3( 0, id - 1, id - 3)); // top - facets.push_back(Point3(id, 1, id - 2)); // bottom - facets.push_back(Point3(id, id - 2, id - 3)); // upper-right of side - facets.push_back(Point3(id, id - 3, id - 1)); // bottom-left of side + facets.emplace_back(Vec3crd( 0, id - 1, id - 3)); // top + facets.emplace_back(Vec3crd(id, 1, id - 2)); // bottom + facets.emplace_back(Vec3crd(id, id - 2, id - 3)); // upper-right of side + facets.emplace_back(Vec3crd(id, id - 3, id - 1)); // bottom-left of side } // Connect the last set of vertices with the first. - facets.push_back(Point3( 2, 0, id - 1)); - facets.push_back(Point3( 1, 3, id)); - facets.push_back(Point3(id, 3, 2)); - facets.push_back(Point3(id, 2, id - 1)); + facets.emplace_back(Vec3crd( 2, 0, id - 1)); + facets.emplace_back(Vec3crd( 1, 3, id)); + facets.emplace_back(Vec3crd(id, 3, 2)); + facets.emplace_back(Vec3crd(id, 2, id - 1)); TriangleMesh mesh(vertices, facets); return mesh; @@ -2044,7 +1825,7 @@ TriangleMesh make_cylinder(double r, double h, double fa) { // Default angle is 1 degree. TriangleMesh make_sphere(double rho, double fa) { Pointf3s vertices; - std::vector facets; + std::vector facets; // Algorithm: // Add points one-by-one to the sphere grid and form facets using relative coordinates. @@ -2056,29 +1837,24 @@ TriangleMesh make_sphere(double rho, double fa) { // Ring to be scaled to generate the steps of the sphere std::vector ring; for (double i = 0; i < 2*PI; i+=angle) { - ring.push_back(i); + ring.emplace_back(i); } const size_t steps = ring.size(); const double increment = (double)(1.0 / (double)steps); // special case: first ring connects to 0,0,0 // insert and form facets. - vertices.push_back(Pointf3(0.0, 0.0, -rho)); + vertices.emplace_back(Vec3d(0.0, 0.0, -rho)); size_t id = vertices.size(); for (size_t i = 0; i < ring.size(); i++) { // Fixed scaling const double z = -rho + increment*rho*2.0; // radius of the circle for this step. const double r = sqrt(abs(rho*rho - z*z)); - Pointf3 b(0, r, z); - b.rotate(ring[i], Pointf3(0,0,z)); - vertices.push_back(b); - if (i == 0) { - facets.push_back(Point3(1, 0, ring.size())); - } else { - facets.push_back(Point3(id, 0, id - 1)); - } - id++; + Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); + vertices.emplace_back(Vec3d(b(0), b(1), z)); + facets.emplace_back((i == 0) ? Vec3crd(1, 0, ring.size()) : Vec3crd(id, 0, id - 1)); + ++ id; } // General case: insert and form facets for each step, joining it to the ring below it. @@ -2087,16 +1863,15 @@ TriangleMesh make_sphere(double rho, double fa) { const double r = sqrt(abs(rho*rho - z*z)); for (size_t i = 0; i < ring.size(); i++) { - Pointf3 b(0, r, z); - b.rotate(ring[i], Pointf3(0,0,z)); - vertices.push_back(b); + Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); + vertices.emplace_back(Vec3d(b(0), b(1), z)); if (i == 0) { // wrap around - facets.push_back(Point3(id + ring.size() - 1 , id, id - 1)); - facets.push_back(Point3(id, id - ring.size(), id - 1)); + facets.emplace_back(Vec3crd(id + ring.size() - 1 , id, id - 1)); + facets.emplace_back(Vec3crd(id, id - ring.size(), id - 1)); } else { - facets.push_back(Point3(id , id - ring.size(), (id - 1) - ring.size())); - facets.push_back(Point3(id, id - 1 - ring.size() , id - 1)); + facets.emplace_back(Vec3crd(id , id - ring.size(), (id - 1) - ring.size())); + facets.emplace_back(Vec3crd(id, id - 1 - ring.size() , id - 1)); } id++; } @@ -2105,13 +1880,13 @@ TriangleMesh make_sphere(double rho, double fa) { // special case: last ring connects to 0,0,rho*2.0 // only form facets. - vertices.push_back(Pointf3(0.0, 0.0, rho)); + vertices.emplace_back(Vec3d(0.0, 0.0, rho)); for (size_t i = 0; i < ring.size(); i++) { if (i == 0) { // third vertex is on the other side of the ring. - facets.push_back(Point3(id, id - ring.size(), id - 1)); + facets.emplace_back(Vec3crd(id, id - ring.size(), id - 1)); } else { - facets.push_back(Point3(id, id - ring.size() + i, id - ring.size() + (i - 1))); + facets.emplace_back(Vec3crd(id, id - ring.size() + i, id - ring.size() + (i - 1))); } } id++; diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp similarity index 73% rename from xs/src/libslic3r/TriangleMesh.hpp rename to src/libslic3r/TriangleMesh.hpp index 24e903c0ad..fd312d0e06 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -3,6 +3,7 @@ #include "libslic3r.h" #include +#include #include #include #include "BoundingBox.hpp" @@ -20,35 +21,35 @@ typedef std::vector TriangleMeshPtrs; class TriangleMesh { public: - TriangleMesh(); - TriangleMesh(const Pointf3s &points, const std::vector &facets); - TriangleMesh(const TriangleMesh &other); - TriangleMesh(TriangleMesh &&other); + TriangleMesh() : repaired(false) { stl_initialize(&this->stl); } + TriangleMesh(const Pointf3s &points, const std::vector &facets); + TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_initialize(&this->stl); *this = other; } + TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_initialize(&this->stl); this->swap(other); } + ~TriangleMesh() { stl_close(&this->stl); } TriangleMesh& operator=(const TriangleMesh &other); - TriangleMesh& operator=(TriangleMesh &&other); - void swap(TriangleMesh &other); - ~TriangleMesh(); - void ReadSTLFile(const char* input_file); - void write_ascii(const char* output_file); - void write_binary(const char* output_file); + TriangleMesh& operator=(TriangleMesh &&other) { this->swap(other); return *this; } + void swap(TriangleMesh &other) { std::swap(this->stl, other.stl); std::swap(this->repaired, other.repaired); } + void ReadSTLFile(const char* input_file) { stl_open(&stl, input_file); } + void write_ascii(const char* output_file) { stl_write_ascii(&this->stl, output_file, ""); } + void write_binary(const char* output_file) { stl_write_binary(&this->stl, output_file, ""); } void repair(); float volume(); void check_topology(); - bool is_manifold() const; + bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == this->stl.stats.number_of_facets; } void WriteOBJFile(char* output_file); void scale(float factor); - void scale(const Pointf3 &versor); + void scale(const Vec3d &versor); void translate(float x, float y, float z); void rotate(float angle, const Axis &axis); - void rotate(float angle, Pointf3 axis); - void rotate_x(float angle); - void rotate_y(float angle); - void rotate_z(float angle); + void rotate(float angle, const Vec3d& axis); + void rotate_x(float angle) { this->rotate(angle, X); } + void rotate_y(float angle) { this->rotate(angle, Y); } + void rotate_z(float angle) { this->rotate(angle, Z); } void mirror(const Axis &axis); - void mirror_x(); - void mirror_y(); - void mirror_z(); - void transform(const float* matrix3x4); + void mirror_x() { this->mirror(X); } + void mirror_y() { this->mirror(Y); } + void mirror_z() { this->mirror(Z); } + void transform(const Transform3d& t); void align_to_origin(); void rotate(double angle, Point* center); TriangleMeshPtrs split() const; @@ -57,13 +58,13 @@ public: const float* first_vertex() const; Polygon convex_hull(); BoundingBoxf3 bounding_box() const; - // Returns the bbox of this TriangleMesh transformed by the given matrix - BoundingBoxf3 transformed_bounding_box(const std::vector& matrix) const; + // Returns the bbox of this TriangleMesh transformed by the given transformation + BoundingBoxf3 transformed_bounding_box(const Transform3d& t) const; // Returns the convex hull of this TriangleMesh TriangleMesh convex_hull_3d() const; void reset_repair_stats(); bool needed_repair() const; - size_t facets_count() const; + size_t facets_count() const { return this->stl.stats.number_of_facets; } // Returns true, if there are two and more connected patches in the mesh. // Returns false, if one or zero connected patch is in the mesh. @@ -156,9 +157,13 @@ typedef std::vector IntersectionLinePtrs; class TriangleMeshSlicer { public: - TriangleMeshSlicer(TriangleMesh* _mesh); - void slice(const std::vector &z, std::vector* layers) const; - void slice(const std::vector &z, std::vector* layers) const; + typedef std::function throw_on_cancel_callback_type; + TriangleMeshSlicer() : mesh(nullptr) {} + // Not quite nice, but the constructor and init() methods require non-const mesh pointer to be able to call mesh->require_shared_vertices() + TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, [](){}); } + void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel); + void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; + void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; enum FacetSliceType { NoSlice = 0, Slicing = 1, diff --git a/xs/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp similarity index 53% rename from xs/src/libslic3r/Utils.hpp rename to src/libslic3r/Utils.hpp index 9334573e86..7f2c94f034 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -2,6 +2,9 @@ #define slic3r_Utils_hpp_ #include +#include +#include +#include #include "libslic3r.h" @@ -49,6 +52,9 @@ extern std::string normalize_utf8_nfc(const char *src); // for a short while, so the file may not be movable. Retry while we see recoverable errors. extern int rename_file(const std::string &from, const std::string &to); +// Copy a file, adjust the access attributes, so that the target is writable. +extern int copy_file(const std::string &from, const std::string &to); + // File path / name / extension splitting utilities, working with UTF-8, // to be published to Perl. namespace PerlUtils { @@ -62,6 +68,8 @@ namespace PerlUtils { extern std::string path_to_parent_path(const char *src); }; +std::string string_printf(const char *format, ...); + // Timestamp formatted for header_slic3r_generated(). extern std::string timestamp_str(); // Standard "generated by Slic3r version xxx timestamp xxx" header string, @@ -71,46 +79,110 @@ inline std::string header_slic3r_generated() { return std::string("generated by // getpid platform wrapper extern unsigned get_current_pid(); +template +Real round_nearest(Real value, unsigned int decimals) +{ + Real res = (Real)0; + if (decimals == 0) + res = ::round(value); + else + { + Real power = ::pow((Real)10, (int)decimals); + res = ::round(value * power + (Real)0.5) / power; + } + return res; +} + // Compute the next highest power of 2 of 32-bit v // http://graphics.stanford.edu/~seander/bithacks.html -template -inline T next_highest_power_of_2(T v) +inline uint16_t next_highest_power_of_2(uint16_t v) { - if (v != 0) - -- v; + if (v != 0) + -- v; v |= v >> 1; v |= v >> 2; v |= v >> 4; - if (sizeof(T) >= sizeof(uint16_t)) - v |= v >> 8; - if (sizeof(T) >= sizeof(uint32_t)) - v |= v >> 16; - if (sizeof(T) >= sizeof(uint64_t)) - v |= v >> 32; + v |= v >> 8; + return ++ v; +} +inline uint32_t next_highest_power_of_2(uint32_t v) +{ + if (v != 0) + -- v; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return ++ v; +} +inline uint64_t next_highest_power_of_2(uint64_t v) +{ + if (v != 0) + -- v; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; return ++ v; } +// On some implementations (such as some versions of clang), the size_t is a type of its own, so we need to overload for size_t. +// Typically, though, the size_t type aliases to uint64_t / uint32_t. +// We distinguish that here and provide implementation for size_t if and only if it is a distinct type +template size_t next_highest_power_of_2(T v, + typename std::enable_if::value, T>::type = 0, // T is size_t + typename std::enable_if::value, T>::type = 0, // T is not uint64_t + typename std::enable_if::value, T>::type = 0, // T is not uint32_t + typename std::enable_if::type = 0) // T is 64 bits +{ + return next_highest_power_of_2(uint64_t(v)); +} +template size_t next_highest_power_of_2(T v, + typename std::enable_if::value, T>::type = 0, // T is size_t + typename std::enable_if::value, T>::type = 0, // T is not uint64_t + typename std::enable_if::value, T>::type = 0, // T is not uint32_t + typename std::enable_if::type = 0) // T is 32 bits +{ + return next_highest_power_of_2(uint32_t(v)); +} + + extern std::string xml_escape(std::string text); -class PerlCallback { + +class ScopeGuard +{ public: - PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); } - PerlCallback() : m_callback(nullptr) {} - ~PerlCallback() { this->deregister_callback(); } - void register_callback(void *sv); - void deregister_callback(); - void call() const; - void call(int i) const; - void call(int i, int j) const; - void call(const std::vector& ints) const; - void call(double d) const; - void call(double a, double b) const; - void call(double a, double b, double c, double d) const; - void call(bool b) const; + typedef std::function Closure; private: - void *m_callback; + bool committed; + Closure closure; + +public: + ScopeGuard() {} + ScopeGuard(Closure closure) : closure(std::move(closure)) {} + ScopeGuard(const ScopeGuard&) = delete; + ScopeGuard(ScopeGuard &&other) : closure(std::move(other.closure)) {} + + ~ScopeGuard() + { + if (closure) { closure(); } + } + + ScopeGuard& operator=(const ScopeGuard&) = delete; + ScopeGuard& operator=(ScopeGuard &&other) + { + closure = std::move(other.closure); + return *this; + } + + void reset() { closure = Closure(); } }; + } // namespace Slic3r #endif // slic3r_Utils_hpp_ diff --git a/xs/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h similarity index 83% rename from xs/src/libslic3r/libslic3r.h rename to src/libslic3r/libslic3r.h index b8e7e0a4e3..f8088faeaf 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -1,6 +1,8 @@ #ifndef _libslic3r_h_ #define _libslic3r_h_ +#include "libslic3r_version.h" + // this needs to be included early for MSVC (listing it in Build.PL is not enough) #include #include @@ -13,9 +15,7 @@ #include #include -#define SLIC3R_FORK_NAME "Slic3r Prusa Edition" -#define SLIC3R_VERSION "1.41.2-beta" -#define SLIC3R_BUILD "UNKNOWN" +#include "Technologies.hpp" typedef int32_t coord_t; typedef double coordf_t; @@ -45,29 +45,7 @@ typedef double coordf_t; //FIXME Better to use an inline function with an explicit return type. //inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); } #define scale_(val) ((val) / SCALING_FACTOR) -#define unscale(val) ((val) * SCALING_FACTOR) #define SCALED_EPSILON scale_(EPSILON) -/* Implementation of CONFESS("foo"): */ -#ifdef _MSC_VER - #define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) -#else - #define CONFESS(...) confess_at(__FILE__, __LINE__, __func__, __VA_ARGS__) -#endif -void confess_at(const char *file, int line, const char *func, const char *pat, ...); -/* End implementation of CONFESS("foo"): */ - -// Which C++ version is supported? -// For example, could optimized functions with move semantics be used? -#if __cplusplus==201402L - #define SLIC3R_CPPVER 14 - #define STDMOVE(WHAT) std::move(WHAT) -#elif __cplusplus==201103L - #define SLIC3R_CPPVER 11 - #define STDMOVE(WHAT) std::move(WHAT) -#else - #define SLIC3R_CPPVER 0 - #define STDMOVE(WHAT) (WHAT) -#endif #define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/" @@ -102,6 +80,9 @@ inline std::string debug_out_path(const char *name, ...) namespace Slic3r { +template +inline T unscale(Q v) { return T(v) * T(SCALING_FACTOR); } + enum Axis { X=0, Y, Z, E, F, NUM_AXES }; template @@ -130,6 +111,17 @@ inline void append(std::vector& dest, std::vector&& src) src.shrink_to_fit(); } +// Casting an std::vector<> from one type to another type without warnings about a loss of accuracy. +template +std::vector cast(const std::vector &src) +{ + std::vector dst; + dst.reserve(src.size()); + for (const T_FROM &a : src) + dst.emplace_back((T_TO)a); + return dst; +} + template inline void remove_nulls(std::vector &vec) { diff --git a/src/libslic3r/libslic3r_version.h.in b/src/libslic3r/libslic3r_version.h.in new file mode 100644 index 0000000000..2259b2302b --- /dev/null +++ b/src/libslic3r/libslic3r_version.h.in @@ -0,0 +1,8 @@ +#ifndef __SLIC3R_VERSION_H +#define __SLIC3R_VERSION_H + +#define SLIC3R_FORK_NAME "@SLIC3R_FORK_NAME@" +#define SLIC3R_VERSION "@SLIC3R_VERSION@" +#define SLIC3R_BUILD "@SLIC3R_BUILD@" + +#endif /* __SLIC3R_VERSION_H */ diff --git a/src/libslic3r/pchheader.cpp b/src/libslic3r/pchheader.cpp new file mode 100644 index 0000000000..9ab59c53d5 --- /dev/null +++ b/src/libslic3r/pchheader.cpp @@ -0,0 +1 @@ +#include "pchheader.hpp" diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp new file mode 100644 index 0000000000..b27dfe6a2d --- /dev/null +++ b/src/libslic3r/pchheader.hpp @@ -0,0 +1,122 @@ +#ifdef WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "BoundingBox.hpp" +#include "ClipperUtils.hpp" +#include "Config.hpp" +#include "I18N.hpp" +#include "MultiPoint.hpp" +#include "Point.hpp" +#include "Polygon.hpp" +#include "Polyline.hpp" +#include "SVG.hpp" + +#include "libslic3r.h" +#include "libslic3r_version.h" + +#include "clipper.hpp" + +#include + +#include diff --git a/xs/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp similarity index 75% rename from xs/src/libslic3r/utils.cpp rename to src/libslic3r/utils.cpp index 45a39bbad7..7a51a61046 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -3,6 +3,8 @@ #include #include +#include +#include #ifdef WIN32 #include @@ -27,6 +29,8 @@ #include +#include + namespace Slic3r { static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error; @@ -150,14 +154,15 @@ const std::string& data_dir() return g_data_dir; } -// borrowed from LLVM lib/Support/Windows/Path.inc + +// borrowed from LVVM lib/Support/Windows/Path.inc int rename_file(const std::string &from, const std::string &to) { int ec = 0; #ifdef _WIN32 - // Convert to utf-16. + // Convert to utf-16. std::wstring wide_from = boost::nowide::widen(from); std::wstring wide_to = boost::nowide::widen(to); @@ -204,179 +209,33 @@ int rename_file(const std::string &from, const std::string &to) #else - boost::nowide::remove(to.c_str()); - ec = boost::nowide::rename(from.c_str(), to.c_str()); + boost::nowide::remove(to.c_str()); + ec = boost::nowide::rename(from.c_str(), to.c_str()); #endif return ec; } -} // namespace Slic3r - -#include - -void -confess_at(const char *file, int line, const char *func, - const char *pat, ...) +int copy_file(const std::string &from, const std::string &to) { - #ifdef SLIC3RXS - va_list args; - SV *error_sv = newSVpvf("Error in function %s at %s:%d: ", func, - file, line); + const boost::filesystem::path source(from); + const boost::filesystem::path target(to); + static const auto perms = boost::filesystem::owner_read | boost::filesystem::owner_write | boost::filesystem::group_read | boost::filesystem::others_read; // aka 644 - va_start(args, pat); - sv_vcatpvf(error_sv, pat, &args); - va_end(args); - - sv_catpvn(error_sv, "\n\t", 2); - - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs( sv_2mortal(error_sv) ); - PUTBACK; - call_pv("Carp::confess", G_DISCARD); - FREETMPS; - LEAVE; - #endif -} - -void PerlCallback::register_callback(void *sv) -{ - if (! SvROK((SV*)sv) || SvTYPE(SvRV((SV*)sv)) != SVt_PVCV) - croak("Not a Callback %_ for PerlFunction", (SV*)sv); - if (m_callback) - SvSetSV((SV*)m_callback, (SV*)sv); - else - m_callback = newSVsv((SV*)sv); -} - -void PerlCallback::deregister_callback() -{ - if (m_callback) { - sv_2mortal((SV*)m_callback); - m_callback = nullptr; - } -} - -void PerlCallback::call() const -{ - if (! m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(int i) const -{ - if (! m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSViv(i))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(int i, int j) const -{ - if (! m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSViv(i))); - XPUSHs(sv_2mortal(newSViv(j))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(const std::vector& ints) const -{ - if (! m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - for (int i : ints) - { - XPUSHs(sv_2mortal(newSViv(i))); + // Make sure the file has correct permission both before and after we copy over it. + try { + if (boost::filesystem::exists(target)) + boost::filesystem::permissions(target, perms); + boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists); + boost::filesystem::permissions(target, perms); + } catch (std::exception & /* ex */) { + return -1; } - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; + return 0; } -void PerlCallback::call(double d) const -{ - if (!m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVnv(d))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(double a, double b) const -{ - if (!m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVnv(a))); - XPUSHs(sv_2mortal(newSVnv(b))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(double a, double b, double c, double d) const -{ - if (!m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVnv(a))); - XPUSHs(sv_2mortal(newSVnv(b))); - XPUSHs(sv_2mortal(newSVnv(c))); - XPUSHs(sv_2mortal(newSVnv(d))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(bool b) const -{ - call(b ? 1 : 0); -} +} // namespace Slic3r #ifdef WIN32 #ifndef NOMINMAX @@ -440,6 +299,25 @@ namespace PerlUtils { std::string path_to_parent_path(const char *src) { return boost::filesystem::path(src).parent_path().string(); } }; + +std::string string_printf(const char *format, ...) +{ + va_list args1; + va_start(args1, format); + va_list args2; + va_copy(args2, args1); + + size_t needed_size = ::vsnprintf(nullptr, 0, format, args1) + 1; + va_end(args1); + + std::string res(needed_size, '\0'); + ::vsnprintf(&res.front(), res.size(), format, args2); + va_end(args2); + + return res; +} + + std::string timestamp_str() { const auto now = boost::posix_time::second_clock::local_time(); diff --git a/src/miniz/CMakeLists.txt b/src/miniz/CMakeLists.txt new file mode 100644 index 0000000000..da92b1252e --- /dev/null +++ b/src/miniz/CMakeLists.txt @@ -0,0 +1,14 @@ +project(miniz) +cmake_minimum_required(VERSION 2.6) + +add_library(miniz STATIC + miniz.h + miniz_common.h + miniz_tdef.h + miniz_tinfl.h + miniz_zip.h + miniz.cpp + miniz_tdef.cpp + miniz_tinfl.cpp + miniz_zip.cpp +) diff --git a/xs/src/miniz/miniz.cpp b/src/miniz/miniz.cpp similarity index 100% rename from xs/src/miniz/miniz.cpp rename to src/miniz/miniz.cpp diff --git a/xs/src/miniz/miniz.h b/src/miniz/miniz.h similarity index 100% rename from xs/src/miniz/miniz.h rename to src/miniz/miniz.h diff --git a/xs/src/miniz/miniz_common.h b/src/miniz/miniz_common.h similarity index 100% rename from xs/src/miniz/miniz_common.h rename to src/miniz/miniz_common.h diff --git a/xs/src/miniz/miniz_tdef.cpp b/src/miniz/miniz_tdef.cpp similarity index 100% rename from xs/src/miniz/miniz_tdef.cpp rename to src/miniz/miniz_tdef.cpp diff --git a/xs/src/miniz/miniz_tdef.h b/src/miniz/miniz_tdef.h similarity index 100% rename from xs/src/miniz/miniz_tdef.h rename to src/miniz/miniz_tdef.h diff --git a/xs/src/miniz/miniz_tinfl.cpp b/src/miniz/miniz_tinfl.cpp similarity index 100% rename from xs/src/miniz/miniz_tinfl.cpp rename to src/miniz/miniz_tinfl.cpp diff --git a/xs/src/miniz/miniz_tinfl.h b/src/miniz/miniz_tinfl.h similarity index 100% rename from xs/src/miniz/miniz_tinfl.h rename to src/miniz/miniz_tinfl.h diff --git a/xs/src/miniz/miniz_zip.cpp b/src/miniz/miniz_zip.cpp similarity index 100% rename from xs/src/miniz/miniz_zip.cpp rename to src/miniz/miniz_zip.cpp diff --git a/xs/src/miniz/miniz_zip.h b/src/miniz/miniz_zip.h similarity index 100% rename from xs/src/miniz/miniz_zip.h rename to src/miniz/miniz_zip.h diff --git a/src/platform/msw/slic3r.manifest.in b/src/platform/msw/slic3r.manifest.in new file mode 100644 index 0000000000..ab1cc5ae21 --- /dev/null +++ b/src/platform/msw/slic3r.manifest.in @@ -0,0 +1,37 @@ + + + + Perl + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/platform/msw/slic3r.rc.in b/src/platform/msw/slic3r.rc.in new file mode 100644 index 0000000000..493f9f7520 --- /dev/null +++ b/src/platform/msw/slic3r.rc.in @@ -0,0 +1,25 @@ +1 VERSIONINFO +FILEVERSION @SLIC3R_RC_VERSION@ +PRODUCTVERSION @SLIC3R_RC_VERSION@ +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" + { + VALUE "CompanyName", "Prusa Research" + VALUE "FileDescription", "Slic3r Prusa Edition" + VALUE "FileVersion", "@SLIC3R_BUILD_ID@" + VALUE "ProductName", "Slic3r Prusa Edition" + VALUE "ProductVersion", "@SLIC3R_BUILD_ID@" + VALUE "InternalName", "Slic3r Prusa Edition" + VALUE "LegalCopyright", "Copyright \251 2011-2017 Alessandro Ranelucci, \251 2016 Prusa Research" + VALUE "OriginalFilename", "slic3r.exe" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1252 + } +} +2 ICON "@SLIC3R_RESOURCES_DIR@/icons/Slic3r.ico" +1 24 "slic3r.manifest" diff --git a/src/platform/osx/Info.plist.in b/src/platform/osx/Info.plist.in new file mode 100644 index 0000000000..099cb5c375 --- /dev/null +++ b/src/platform/osx/Info.plist.in @@ -0,0 +1,32 @@ + + + + + CFBundleExecutable + Slic3r + CFBundleGetInfoString + Slic3r Copyright (C) 2011-2017 Alessandro Ranellucci, (C) 2016-2018 Prusa Reseach + CFBundleIconFile + Slic3r.icns + CFBundleName + Slic3r + CFBundleShortVersionString + Slic3r @SLIC3R_BUILD_ID@ + CFBundleIdentifier + com.prusa3d.slic3r/ + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + @SLIC3R_BUILD_ID@ + CGDisableCoalescedUpdates + + NSPrincipalClass + NSApplication + NSHighResolutionCapable + + + diff --git a/src/png/AUTHORS b/src/png/AUTHORS new file mode 100644 index 0000000000..25aad54dce --- /dev/null +++ b/src/png/AUTHORS @@ -0,0 +1,4 @@ +png++ is written by Alexander Shulgin (alex dot shulgin at gmail dot com) +Copyright (C) 2007,2008 + +When writing to me be sure to put png++: in the subject :-) diff --git a/src/png/COPYING b/src/png/COPYING new file mode 100644 index 0000000000..04889ba11d --- /dev/null +++ b/src/png/COPYING @@ -0,0 +1,25 @@ +Copying png++ is subject to the following license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/src/png/NEWS b/src/png/NEWS new file mode 100644 index 0000000000..cc006fbf5e --- /dev/null +++ b/src/png/NEWS @@ -0,0 +1,60 @@ +Version 0.2.7: + + - Added solid_pixel_buffer (patch by Andrey Potapov). + + - Fixed some compilation problems on Win32. + +Version 0.2.5: + + - Fixed compatibility with newer libpng versions (>= 1.4) + + - Fixed compilation on FreeBSD. + + - Fixed tRNS handling with transformations. + + - Added IO transformation debugging facility. + + - Better organized test suite. + +Version 0.2.3: + + - Fixed numerous `already defined' errors due to require_color_space + implementation. + + - Added `config.hpp'. + + - Fixed `strerror' usage. + + - Minor docs fixes. + + +Version 0.2.1: + + - Added support for tRNS chunk. + + - Added non-std IO streams support. + + - Fixed 16-bit endianness problems. + + - Improved test script. + + +Version 0.2.0: + + - Added support for 16-bit data (RGB, RGBA, Grayscale and Gray+Alpha + color types) + + - Added support for packed 1-, 2- or 4-bit pixels (Grayscale and + Indexed colors) + + - Fixed interlace handling code which was severely broken + + - Added possibility to process images without reading the entire + image into memory + + - Internals are refactored while the client interface is mostly + unchanged + + - Added intensive test suite + + - Added documentation diff --git a/src/png/color.hpp b/src/png/color.hpp new file mode 100644 index 0000000000..b5ffde2c4e --- /dev/null +++ b/src/png/color.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2007,2008 Alex Shulgin + * + * This file is part of png++ the C++ wrapper for libpng. PNG++ is free + * software; the exact copying conditions are as follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PNGPP_COLOR_HPP_INCLUDED +#define PNGPP_COLOR_HPP_INCLUDED + +#include "types.hpp" + +namespace png +{ + + /** + * \brief PNG color struct extension. Adds constructors. + */ + struct color + : png_color + { + explicit color(byte r = 0, byte g = 0, byte b = 0) + { + this->red = r; + this->green = g; + this->blue = b; + } + + /** + * \brief Initializes color with a copy of png_color object. + */ + color(png_color const& other) + { + this->red = other.red; + this->green = other.green; + this->blue = other.blue; + } + }; + +} // namespace png + +#endif // PNGPP_COLOR_HPP_INCLUDED diff --git a/src/png/end_info.hpp b/src/png/end_info.hpp new file mode 100644 index 0000000000..09590596df --- /dev/null +++ b/src/png/end_info.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2007,2008 Alex Shulgin + * + * This file is part of png++ the C++ wrapper for libpng. PNG++ is free + * software; the exact copying conditions are as follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PNGPP_END_INFO_HPP_INCLUDED +#define PNGPP_END_INFO_HPP_INCLUDED + +#include "info_base.hpp" + +namespace png +{ + + /** + * \brief Internal class to hold PNG ending %info. + * + * \see info, info_base + */ + class end_info + : public info_base + { + public: + end_info(io_base& io, png_struct* png) + : info_base(io, png) + { + } + + void destroy() + { + assert(m_info); + png_destroy_info_struct(m_png, & m_info); + } + + void read() + { + png_read_end(m_png, m_info); + } + + void write() const + { + png_write_end(m_png, m_info); + } + + // TODO: add methods to read/write text comments etc. + }; + +} // namespace png + +#endif // PNGPP_END_INFO_HPP_INCLUDED diff --git a/src/png/error.hpp b/src/png/error.hpp new file mode 100644 index 0000000000..c67f97630b --- /dev/null +++ b/src/png/error.hpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2007,2008 Alex Shulgin + * + * This file is part of png++ the C++ wrapper for libpng. PNG++ is free + * software; the exact copying conditions are as follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PNGPP_ERROR_HPP_INCLUDED +#define PNGPP_ERROR_HPP_INCLUDED + +/* check if we have strerror_s or strerror_r, prefer the former which is C11 std */ +#ifdef __STDC_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 +#include + +#define HAVE_STRERROR_S 1 +#else +#undef HAVE_STRERROR_S +#endif + +#include +#include +#include +#include +#include + +namespace png +{ + + /** + * \brief Exception class to represent runtime errors related to + * png++ operation. + */ + class error + : public std::runtime_error + { + public: + /** + * \param message error description + */ + explicit error(std::string const& message) + : std::runtime_error(message) + { + } + }; + + /** + * \brief Exception class to represent standard library errors + * (generally IO). + * + * \see reader, writer + */ + class std_error + : public std::runtime_error + { + public: + /** + * Constructs an std_error object. The \a message string is + * appended with ": " and the error description as + * returned by \c strerror(\a error). + * + * \param message error description + * \param error error number + */ + explicit std_error(std::string const& message, int errnum = errno) + : std::runtime_error((message + ": ") + thread_safe_strerror(errnum)) + { + } + + protected: + static std::string thread_safe_strerror(int errnum) + { +#define ERRBUF_SIZE 512 + +#ifdef HAVE_STRERROR_S + char buf[ERRBUF_SIZE] = { 0 }; + strerror_s(buf, ERRBUF_SIZE, errnum); + return std::string(buf); +#else +#if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE + char buf[ERRBUF_SIZE] = { 0 }; + strerror_r(errnum, buf, ERRBUF_SIZE); + return std::string(buf); +#elif _GNU_SOURCE + /* GNU variant can return a pointer to static buffer instead of buf */ + char buf[ERRBUF_SIZE] = { 0 }; + return std::string(strerror_r(errnum, buf, ERRBUF_SIZE)); +#else + return std::string("An error occured with errnum ") + + std::to_string(errnum) + + ". Converting to the appropriate error message is disabled" + "in this instance of the png++ library."; +#endif +#endif + +#undef ERRBUF_SIZE + } + }; + +} // namespace png + +#endif // PNGPP_ERROR_HPP_INCLUDED diff --git a/src/png/image_info.hpp b/src/png/image_info.hpp new file mode 100644 index 0000000000..8407bc18b8 --- /dev/null +++ b/src/png/image_info.hpp @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2007,2008 Alex Shulgin + * + * This file is part of png++ the C++ wrapper for libpng. PNG++ is free + * software; the exact copying conditions are as follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PNGPP_IMAGE_INFO_HPP_INCLUDED +#define PNGPP_IMAGE_INFO_HPP_INCLUDED + +#include "types.hpp" +#include "palette.hpp" +#include "tRNS.hpp" +#include "pixel_traits.hpp" + +namespace png +{ + + /** + * \brief Holds information about PNG image. + * + * \see image, generator, consumer + */ + class image_info + { + public: + /** + * \brief Constructs the image_info object with default values + * for color_type, interlace_type, compression_method and + * filter_type. + */ + image_info() + : m_width(0), + m_height(0), + m_bit_depth(0), + m_color_type(color_type_none), + m_interlace_type(interlace_none), + m_compression_type(compression_type_default), + m_filter_type(filter_type_default), + m_gamma(0.0) + { + } + + uint_32 get_width() const + { + return m_width; + } + + void set_width(uint_32 width) + { + m_width = width; + } + + uint_32 get_height() const + { + return m_height; + } + + void set_height(uint_32 height) + { + m_height = height; + } + + color_type get_color_type() const + { + return m_color_type; + } + + void set_color_type(color_type color_space) + { + m_color_type = color_space; + } + + int get_bit_depth() const + { + return m_bit_depth; + } + + void set_bit_depth(int bit_depth) + { + m_bit_depth = bit_depth; + } + + interlace_type get_interlace_type() const + { + return m_interlace_type; + } + + void set_interlace_type(interlace_type interlace) + { + m_interlace_type = interlace; + } + + compression_type get_compression_type() const + { + return m_compression_type; + } + + void set_compression_type(compression_type compression) + { + m_compression_type = compression; + } + + filter_type get_filter_type() const + { + return m_filter_type; + } + + void set_filter_type(filter_type filter) + { + m_filter_type = filter; + } + + palette const& get_palette() const + { + return m_palette; + } + + palette& get_palette() + { + return m_palette; + } + + void set_palette(palette const& plte) + { + m_palette = plte; + } + + /** + * \brief Removes all entries from the palette. + */ + void drop_palette() + { + m_palette.clear(); + } + + tRNS const& get_tRNS() const + { + return m_tRNS; + } + + tRNS& get_tRNS() + { + return m_tRNS; + } + + void set_tRNS(tRNS const& trns) + { + m_tRNS = trns; + } + + double get_gamma() const + { + return m_gamma; + } + + void set_gamma(double gamma) + { + m_gamma = gamma; + } + + protected: + uint_32 m_width; + uint_32 m_height; + int m_bit_depth; + color_type m_color_type; + interlace_type m_interlace_type; + compression_type m_compression_type; + filter_type m_filter_type; + palette m_palette; + tRNS m_tRNS; + double m_gamma; + }; + + /** + * \brief Returns an image_info object with color_type and + * bit_depth fields setup appropriate for the \c pixel type. + */ + template< typename pixel > + image_info + make_image_info() + { + typedef pixel_traits< pixel > traits; + image_info info; + info.set_color_type(traits::get_color_type()); + info.set_bit_depth(traits::get_bit_depth()); + return info; + } + +} // namespace png + +#endif // PNGPP_IMAGE_INFO_HPP_INCLUDED diff --git a/src/png/info.hpp b/src/png/info.hpp new file mode 100644 index 0000000000..3f3cf09c31 --- /dev/null +++ b/src/png/info.hpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2007,2008 Alex Shulgin + * + * This file is part of png++ the C++ wrapper for libpng. PNG++ is free + * software; the exact copying conditions are as follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PNGPP_INFO_HPP_INCLUDED +#define PNGPP_INFO_HPP_INCLUDED + +#include +#include "info_base.hpp" +#include "image_info.hpp" + +namespace png +{ + + /** + * \brief Holds information about PNG image. Adapter class for IO + * image operations. + */ + class info + : public info_base, + public image_info + { + public: + info(io_base& io, png_struct* png) + : info_base(io, png) + { + } + + void read() + { + assert(m_png); + assert(m_info); + + png_read_info(m_png, m_info); + png_get_IHDR(m_png, + m_info, + & m_width, + & m_height, + reinterpret_cast< int* >(& m_bit_depth), + reinterpret_cast< int* >(& m_color_type), + reinterpret_cast< int* >(& m_interlace_type), + reinterpret_cast< int* >(& m_compression_type), + reinterpret_cast< int* >(& m_filter_type)); + + if (png_get_valid(m_png, m_info, chunk_PLTE) == chunk_PLTE) + { + png_color* colors = 0; + int count = 0; + png_get_PLTE(m_png, m_info, & colors, & count); + m_palette.assign(colors, colors + count); + } + +#ifdef PNG_tRNS_SUPPORTED + if (png_get_valid(m_png, m_info, chunk_tRNS) == chunk_tRNS) + { + if (m_color_type == color_type_palette) + { + int count; + byte* values; + if (png_get_tRNS(m_png, m_info, & values, & count, NULL) + != PNG_INFO_tRNS) + { + throw error("png_get_tRNS() failed"); + } + m_tRNS.assign(values, values + count); + } + } +#endif + +#ifdef PNG_gAMA_SUPPORTED + if (png_get_valid(m_png, m_info, chunk_gAMA) == chunk_gAMA) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + if (png_get_gAMA(m_png, m_info, &m_gamma) != PNG_INFO_gAMA) + { + throw error("png_get_gAMA() failed"); + } +#else + png_fixed_point gamma = 0; + if (png_get_gAMA_fixed(m_png, m_info, &gamma) != PNG_INFO_gAMA) + { + throw error("png_get_gAMA_fixed() failed"); + } + m_gamma = gamma / 100000.0; +#endif + } +#endif + } + + void write() const + { + assert(m_png); + assert(m_info); + + sync_ihdr(); + if (m_color_type == color_type_palette) + { + if (! m_palette.empty()) + { + png_set_PLTE(m_png, m_info, + const_cast< color* >(& m_palette[0]), + (int) m_palette.size()); + } + if (! m_tRNS.empty()) + { +#ifdef PNG_tRNS_SUPPORTED + png_set_tRNS(m_png, m_info, + const_cast< byte* >(& m_tRNS[0]), + m_tRNS.size(), + NULL); +#else + throw error("attempted to write tRNS chunk; recompile with PNG_tRNS_SUPPORTED"); +#endif + } + } + + if (m_gamma > 0) + { +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_gAMA(m_png, m_info, m_gamma); +#else + png_set_gAMA_fixed(m_png, m_info, + (png_fixed_point)(m_gamma * 100000)); +#endif +#else + throw error("attempted to write gAMA chunk; recompile with PNG_gAMA_SUPPORTED"); +#endif + } + + png_write_info(m_png, m_info); + } + + void update() + { + assert(m_png); + assert(m_info); + + sync_ihdr(); + png_read_update_info(m_png, m_info); + } + + protected: + void sync_ihdr(void) const + { + png_set_IHDR(m_png, + m_info, + m_width, + m_height, + m_bit_depth, + m_color_type, + m_interlace_type, + m_compression_type, + m_filter_type); + } + }; + +} // namespace png + +#endif // PNGPP_INFO_HPP_INCLUDED diff --git a/src/png/info_base.hpp b/src/png/info_base.hpp new file mode 100644 index 0000000000..45e1542963 --- /dev/null +++ b/src/png/info_base.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2007,2008 Alex Shulgin + * + * This file is part of png++ the C++ wrapper for libpng. PNG++ is free + * software; the exact copying conditions are as follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PNGPP_INFO_BASE_HPP_INCLUDED +#define PNGPP_INFO_BASE_HPP_INCLUDED + +#include +#include "error.hpp" +#include "types.hpp" + +namespace png +{ + + class io_base; + + /** + * \brief Internal class to hold PNG info or end_info. + */ + class info_base + { + info_base(info_base const&); + info_base& operator=(info_base const&); + + public: + info_base(io_base& io, png_struct* png) + : m_io(io), + m_png(png), + m_info(png_create_info_struct(m_png)) + { + } + + png_info* get_png_info() const + { + return m_info; + } + + png_info** get_png_info_ptr() + { + return & m_info; + } + + protected: + io_base& m_io; + png_struct* m_png; + png_info* m_info; + }; + +} // namespace png + +#endif // PNGPP_INFO_BASE_HPP_INCLUDED diff --git a/src/png/io_base.hpp b/src/png/io_base.hpp new file mode 100644 index 0000000000..3c2d830435 --- /dev/null +++ b/src/png/io_base.hpp @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2007,2008 Alex Shulgin + * + * This file is part of png++ the C++ wrapper for libpng. PNG++ is free + * software; the exact copying conditions are as follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PNGPP_IO_BASE_HPP_INCLUDED +#define PNGPP_IO_BASE_HPP_INCLUDED + +#include +#include +#include +#include "error.hpp" +#include "info.hpp" +#include "end_info.hpp" + +static void +trace_io_transform(char const* fmt, ...) +{ +#ifdef DEBUG_IO_TRANSFORM + va_list va; + va_start(va, fmt); + fprintf(stderr, "TRANSFORM_IO: "); + vfprintf(stderr, fmt, va); + va_end(va); +#endif +} +#define TRACE_IO_TRANSFORM trace_io_transform + +namespace png +{ + + /** + * \brief Base class for PNG reader/writer classes. + * + * \see reader, writer + */ + class io_base + { + io_base(io_base const&); + io_base& operator=(io_base const&); + + public: + explicit io_base(png_struct* png) + : m_png(png), + m_info(*this, m_png), + m_end_info(*this, m_png) + { + } + + ~io_base() + { + assert(! m_png); + assert(! m_info.get_png_info()); + assert(! m_end_info.get_png_info()); + } + + png_struct* get_png_struct() const + { + return m_png; + } + + info& get_info() + { + return m_info; + } + + info const& get_info() const + { + return m_info; + } + + image_info const& get_image_info() const + { + return m_info; + } + + void set_image_info(image_info const& info) + { + static_cast< image_info& >(m_info) = info; // slice it + } + + end_info& get_end_info() + { + return m_end_info; + } + + end_info const& get_end_info() const + { + return m_end_info; + } + + ////////////////////////////////////////////////////////////////////// + // info accessors + // + uint_32 get_width() const + { + return m_info.get_width(); + } + + void set_width(uint_32 width) + { + m_info.set_width(width); + } + + uint_32 get_height() const + { + return m_info.get_height(); + } + + void set_height(uint_32 height) + { + m_info.set_height(height); + } + + color_type get_color_type() const + { + return m_info.get_color_type(); + } + + void set_color_type(color_type color_space) + { + m_info.set_color_type(color_space); + } + + int get_bit_depth() const + { + return m_info.get_bit_depth(); + } + + void set_bit_depth(int bit_depth) + { + m_info.set_bit_depth(bit_depth); + } + + interlace_type get_interlace_type() const + { + return m_info.get_interlace_type(); + } + + void set_interlace_type(interlace_type interlace) + { + m_info.set_interlace_type(interlace); + } + + compression_type get_compression_type() const + { + return m_info.get_compression_type(); + } + + void set_compression_type(compression_type compression) + { + m_info.set_compression_type(compression); + } + + filter_type get_filter_type() const + { + return m_info.get_filter_type(); + } + + void set_filter_type(filter_type filter) + { + m_info.set_filter_type(filter); + } + + ////////////////////////////////////////////////////////////////////// + + bool has_chunk(chunk id) + { + return png_get_valid(m_png, + m_info.get_png_info(), + uint_32(id)) == uint_32(id); + } + +#if defined(PNG_READ_EXPAND_SUPPORTED) + void set_gray_1_2_4_to_8() const + { + TRACE_IO_TRANSFORM("png_set_expand_gray_1_2_4_to_8\n"); + png_set_expand_gray_1_2_4_to_8(m_png); + } + + void set_palette_to_rgb() const + { + TRACE_IO_TRANSFORM("png_set_palette_to_rgb\n"); + png_set_palette_to_rgb(m_png); + } + + void set_tRNS_to_alpha() const + { + TRACE_IO_TRANSFORM("png_set_tRNS_to_alpha\n"); + png_set_tRNS_to_alpha(m_png); + } +#endif // defined(PNG_READ_EXPAND_SUPPORTED) + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) + void set_bgr() const + { + TRACE_IO_TRANSFORM("png_set_bgr\n"); + png_set_bgr(m_png); + } +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + void set_gray_to_rgb() const + { + TRACE_IO_TRANSFORM("png_set_gray_to_rgb\n"); + png_set_gray_to_rgb(m_png); + } +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED + void set_rgb_to_gray(rgb_to_gray_error_action error_action + = rgb_to_gray_silent, + double red_weight = -1.0, + double green_weight = -1.0) const + { + TRACE_IO_TRANSFORM("png_set_rgb_to_gray: error_action=%d," + " red_weight=%lf, green_weight=%lf\n", + error_action, red_weight, green_weight); + + png_set_rgb_to_gray(m_png, error_action, red_weight, green_weight); + } +#else + void set_rgb_to_gray(rgb_to_gray_error_action error_action + = rgb_to_gray_silent, + fixed_point red_weight = -1, + fixed_point green_weight = -1) const + { + TRACE_IO_TRANSFORM("png_set_rgb_to_gray_fixed: error_action=%d," + " red_weight=%d, green_weight=%d\n", + error_action, red_weight, green_weight); + + png_set_rgb_to_gray_fixed(m_png, error_action, + red_weight, green_weight); + } +#endif // PNG_FLOATING_POINT_SUPPORTED + + ////////////////////////////////////////////////////////////////////// + // alpha channel transformations + // +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + void set_strip_alpha() const + { + TRACE_IO_TRANSFORM("png_set_strip_alpha\n"); + png_set_strip_alpha(m_png); + } +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) \ + || defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + void set_swap_alpha() const + { + TRACE_IO_TRANSFORM("png_set_swap_alpha\n"); + png_set_swap_alpha(m_png); + } +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) \ + || defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + void set_invert_alpha() const + { + TRACE_IO_TRANSFORM("png_set_invert_alpha\n"); + png_set_invert_alpha(m_png); + } +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) + void set_filler(uint_32 filler, filler_type type) const + { + TRACE_IO_TRANSFORM("png_set_filler: filler=%08x, type=%d\n", + filler, type); + + png_set_filler(m_png, filler, type); + } + +#if !defined(PNG_1_0_X) + void set_add_alpha(uint_32 filler, filler_type type) const + { + TRACE_IO_TRANSFORM("png_set_add_alpha: filler=%08x, type=%d\n", + filler, type); + + png_set_add_alpha(m_png, filler, type); + } +#endif +#endif // PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) + void set_swap() const + { + TRACE_IO_TRANSFORM("png_set_swap\n"); + png_set_swap(m_png); + } +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) + void set_packing() const + { + TRACE_IO_TRANSFORM("png_set_packing\n"); + png_set_packing(m_png); + } +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) \ + || defined(PNG_WRITE_PACKSWAP_SUPPORTED) + void set_packswap() const + { + TRACE_IO_TRANSFORM("png_set_packswap\n"); + png_set_packswap(m_png); + } +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + void set_shift(byte red_bits, byte green_bits, byte blue_bits, + byte alpha_bits = 0) const + { + TRACE_IO_TRANSFORM("png_set_shift: red_bits=%d, green_bits=%d," + " blue_bits=%d, alpha_bits=%d\n", + red_bits, green_bits, blue_bits, alpha_bits); + + if (get_color_type() != color_type_rgb + || get_color_type() != color_type_rgb_alpha) + { + throw error("set_shift: expected RGB or RGBA color type"); + } + color_info bits; + bits.red = red_bits; + bits.green = green_bits; + bits.blue = blue_bits; + bits.alpha = alpha_bits; + png_set_shift(m_png, & bits); + } + + void set_shift(byte gray_bits, byte alpha_bits = 0) const + { + TRACE_IO_TRANSFORM("png_set_shift: gray_bits=%d, alpha_bits=%d\n", + gray_bits, alpha_bits); + + if (get_color_type() != color_type_gray + || get_color_type() != color_type_gray_alpha) + { + throw error("set_shift: expected Gray or Gray+Alpha color type"); + } + color_info bits; + bits.gray = gray_bits; + bits.alpha = alpha_bits; + png_set_shift(m_png, & bits); + } +#endif // PNG_READ_SHIFT_SUPPORTED || PNG_WRITE_SHIFT_SUPPORTED + +#if defined(PNG_READ_INTERLACING_SUPPORTED) \ + || defined(PNG_WRITE_INTERLACING_SUPPORTED) + int set_interlace_handling() const + { + TRACE_IO_TRANSFORM("png_set_interlace_handling\n"); + return png_set_interlace_handling(m_png); + } +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) + void set_invert_mono() const + { + TRACE_IO_TRANSFORM("png_set_invert_mono\n"); + png_set_invert_mono(m_png); + } +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + void set_strip_16() const + { + TRACE_IO_TRANSFORM("png_set_strip_16\n"); + png_set_strip_16(m_png); + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + void set_read_user_transform(png_user_transform_ptr transform_fn) + { + TRACE_IO_TRANSFORM("png_set_read_user_transform_fn\n"); + png_set_read_user_transform_fn(m_png, transform_fn); + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) \ + || defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + void set_user_transform_info(void* info, int bit_depth, int channels) + { + TRACE_IO_TRANSFORM("png_set_user_transform_info: bit_depth=%d," + " channels=%d\n", bit_depth, channels); + + png_set_user_transform_info(m_png, info, bit_depth, channels); + } +#endif + + protected: + void* get_io_ptr() const + { + return png_get_io_ptr(m_png); + } + + void set_error(char const* message) + { + assert(message); + m_error = message; + } + + void reset_error() + { + m_error.clear(); + } + +/* + std::string const& get_error() const + { + return m_error; + } +*/ + + bool is_error() const + { + return !m_error.empty(); + } + + void raise_error() + { + longjmp(png_jmpbuf(m_png), -1); + } + + static void raise_error(png_struct* png, char const* message) + { + io_base* io = static_cast< io_base* >(png_get_error_ptr(png)); + io->set_error(message); + io->raise_error(); + } + + png_struct* m_png; + info m_info; + end_info m_end_info; + std::string m_error; + }; + +} // namespace png + +#endif // PNGPP_IO_BASE_HPP_INCLUDED diff --git a/src/png/libpng/ANNOUNCE b/src/png/libpng/ANNOUNCE new file mode 100644 index 0000000000..0f66c0d1da --- /dev/null +++ b/src/png/libpng/ANNOUNCE @@ -0,0 +1,35 @@ +Libpng 1.6.34 - September 29, 2017 + +This is a public release of libpng, intended for use in production codes. + +Files available for download: + +Source files with LF line endings (for Unix/Linux) and with a +"configure" script + + libpng-1.6.34.tar.xz (LZMA-compressed, recommended) + libpng-1.6.34.tar.gz + +Source files with CRLF line endings (for Windows), without the +"configure" script + + lpng1634.7z (LZMA-compressed, recommended) + lpng1634.zip + +Other information: + + libpng-1.6.34-README.txt + libpng-1.6.34-LICENSE.txt + libpng-1.6.34-*.asc (armored detached GPG signatures) + +Changes since the last public release (1.6.33): + Removed contrib/pngsuite/i*.png; some of these were incorrect and caused + test failures. + +Send comments/corrections/commendations to png-mng-implement at lists.sf.net +(subscription required; visit +https://lists.sourceforge.net/lists/listinfo/png-mng-implement +to subscribe) +or to glennrp at users.sourceforge.net + +Glenn R-P diff --git a/src/png/libpng/CMakeLists.txt b/src/png/libpng/CMakeLists.txt new file mode 100644 index 0000000000..7697110c72 --- /dev/null +++ b/src/png/libpng/CMakeLists.txt @@ -0,0 +1,937 @@ +# CMakeLists.txt + +# Copyright (C) 2007,2009-2017 Glenn Randers-Pehrson +# Written by Christian Ehrlicher, 2007 +# Revised by Roger Lowman, 2009-2010 +# Revised by Clifford Yapp, 2011-2012 +# Revised by Roger Leigh, 2016 +# Revised by Andreas Franek, 2016 + +# This code is released under the libpng license. +# For conditions of distribution and use, see the disclaimer +# and license in png.h + +cmake_minimum_required(VERSION 3.0.2) +cmake_policy(VERSION 3.0.2) + +# Set MacOSX @rpath usage globally. +if (POLICY CMP0020) + cmake_policy(SET CMP0020 NEW) +endif(POLICY CMP0020) +if (POLICY CMP0042) + cmake_policy(SET CMP0042 NEW) +endif(POLICY CMP0042) +# Use new variable expansion policy. +if (POLICY CMP0053) + cmake_policy(SET CMP0053 NEW) +endif(POLICY CMP0053) +if (POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) +endif(POLICY CMP0054) + +set(CMAKE_CONFIGURATION_TYPES "Release;Debug;MinSizeRel;RelWithDebInfo") + +project(libpng ASM C) + +set(PNGLIB_MAJOR 1) +set(PNGLIB_MINOR 6) +set(PNGLIB_RELEASE 34) +set(PNGLIB_NAME libpng${PNGLIB_MAJOR}${PNGLIB_MINOR}) +set(PNGLIB_VERSION ${PNGLIB_MAJOR}.${PNGLIB_MINOR}.${PNGLIB_RELEASE}) + +# needed packages + +#Allow users to specify location of Zlib, +# Useful if zlib is being built alongside this as a sub-project + +set(PNG_BUILD_ZLIB ${CMAKE_CURRENT_SOURCE_DIR}/zlib) + +if(NOT WIN32) + find_library(M_LIBRARY + NAMES m + PATHS /usr/lib /usr/local/lib + ) + if(NOT M_LIBRARY) + message(STATUS "math lib 'libm' not found; floating point support disabled") + endif() +else() + # not needed on windows + set(M_LIBRARY "") +endif() + +# COMMAND LINE OPTIONS +option(PNG_SHARED "Build shared lib" OFF) +option(PNG_STATIC "Build static lib" ON) +option(PNG_TESTS "Build libpng tests" OFF) + +# Many more configuration options could be added here +option(PNG_FRAMEWORK "Build OS X framework" OFF) +option(PNG_DEBUG "Build with debug output" OFF) +option(PNGARG "Disable ANSI-C prototypes" OFF) + +option(PNG_HARDWARE_OPTIMIZATIONS "Enable Hardware Optimizations" ON) + +set(PNG_PREFIX "" CACHE STRING "Prefix to add to the API function names") +set(DFA_XTRA "" CACHE FILEPATH "File containing extra configuration settings") + +if(PNG_HARDWARE_OPTIMIZATIONS) +# set definitions and sources for arm +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" OR + CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64") + set(PNG_ARM_NEON_POSSIBLE_VALUES check on off) + set(PNG_ARM_NEON "check" CACHE STRING "Enable ARM NEON optimizations: + check: (default) use internal checking code; + off: disable the optimizations; + on: turn on unconditionally.") + set_property(CACHE PNG_ARM_NEON PROPERTY STRINGS + ${PNG_ARM_NEON_POSSIBLE_VALUES}) + list(FIND PNG_ARM_NEON_POSSIBLE_VALUES ${PNG_ARM_NEON} index) + if(index EQUAL -1) + message(FATAL_ERROR + " PNG_ARM_NEON must be one of [${PNG_ARM_NEON_POSSIBLE_VALUES}]") + elseif(NOT ${PNG_ARM_NEON} STREQUAL "no") + set(libpng_arm_sources + arm/arm_init.c + arm/filter_neon.S + arm/filter_neon_intrinsics.c) + + if(${PNG_ARM_NEON} STREQUAL "on") + add_definitions(-DPNG_ARM_NEON_OPT=2) + elseif(${PNG_ARM_NEON} STREQUAL "check") + add_definitions(-DPNG_ARM_NEON_CHECK_SUPPORTED) + endif() + else() + add_definitions(-DPNG_ARM_NEON_OPT=0) + endif() +endif() + +# set definitions and sources for powerpc +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^powerpc*" OR + CMAKE_SYSTEM_PROCESSOR MATCHES "^ppc64*" ) + set(PNG_POWERPC_VSX_POSSIBLE_VALUES on off) + set(PNG_POWERPC_VSX "on" CACHE STRING "Enable POWERPC VSX optimizations: + off: disable the optimizations.") + set_property(CACHE PNG_POWERPC_VSX PROPERTY STRINGS + ${PNG_POWERPC_VSX_POSSIBLE_VALUES}) + list(FIND PNG_POWERPC_VSX_POSSIBLE_VALUES ${PNG_POWERPC_VSX} index) + if(index EQUAL -1) + message(FATAL_ERROR + " PNG_POWERPC_VSX must be one of [${PNG_POWERPC_VSX_POSSIBLE_VALUES}]") + elseif(NOT ${PNG_POWERPC_VSX} STREQUAL "no") + set(libpng_powerpc_sources + powerpc/powerpc_init.c + powerpc/filter_vsx_intrinsics.c) + if(${PNG_POWERPC_VSX} STREQUAL "on") + add_definitions(-DPNG_POWERPC_VSX_OPT=2) + endif() + else() + add_definitions(-DPNG_POWERPC_VSX_OPT=0) + endif() +endif() + +# set definitions and sources for intel +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^i?86" OR + CMAKE_SYSTEM_PROCESSOR MATCHES "^x86_64*" ) + set(PNG_INTEL_SSE_POSSIBLE_VALUES on off) + set(PNG_INTEL_SSE "on" CACHE STRING "Enable INTEL_SSE optimizations: + off: disable the optimizations") + set_property(CACHE PNG_INTEL_SSE PROPERTY STRINGS + ${PNG_INTEL_SSE_POSSIBLE_VALUES}) + list(FIND PNG_INTEL_SSE_POSSIBLE_VALUES ${PNG_INTEL_SSE} index) + if(index EQUAL -1) + message(FATAL_ERROR + " PNG_INTEL_SSE must be one of [${PNG_INTEL_SSE_POSSIBLE_VALUES}]") + elseif(NOT ${PNG_INTEL_SSE} STREQUAL "no") + set(libpng_intel_sources + intel/intel_init.c + intel/filter_sse2_intrinsics.c) + if(${PNG_INTEL_SSE} STREQUAL "on") + add_definitions(-DPNG_INTEL_SSE_OPT=1) + endif() + else() + add_definitions(-DPNG_INTEL_SSE_OPT=0) + endif() +endif() + +# set definitions and sources for MIPS +if(CMAKE_SYSTEM_PROCESSOR MATCHES "mipsel*" OR + CMAKE_SYSTEM_PROCESSOR MATCHES "mips64el*" ) + set(PNG_MIPS_MSA_POSSIBLE_VALUES on off) + set(PNG_MIPS_MSA "on" CACHE STRING "Enable MIPS_MSA optimizations: + off: disable the optimizations") + set_property(CACHE PNG_MIPS_MSA PROPERTY STRINGS + ${PNG_MIPS_MSA_POSSIBLE_VALUES}) + list(FIND PNG_MIPS_MSA_POSSIBLE_VALUES ${PNG_MIPS_MSA} index) + if(index EQUAL -1) + message(FATAL_ERROR + " PNG_MIPS_MSA must be one of [${PNG_MIPS_MSA_POSSIBLE_VALUES}]") + elseif(NOT ${PNG_MIPS_MSA} STREQUAL "no") + set(libpng_mips_sources + mips/mips_init.c + mips/filter_msa_intrinsics.c) + if(${PNG_MIPS_MSA} STREQUAL "on") + add_definitions(-DPNG_MIPS_MSA_OPT=2) + endif() + else() + add_definitions(-DPNG_MIPS_MSA_OPT=0) + endif() +endif() +endif(PNG_HARDWARE_OPTIMIZATIONS) + +# SET LIBNAME +set(PNG_LIB_NAME png${PNGLIB_MAJOR}${PNGLIB_MINOR}) + +# to distinguish between debug and release lib +set(CMAKE_DEBUG_POSTFIX "d") + +include(CheckCSourceCompiles) +option(ld-version-script "Enable linker version script" ON) +if(ld-version-script AND NOT APPLE) + # Check if LD supports linker scripts. + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/conftest.map" "VERS_1 { + global: sym; + local: *; +}; + +VERS_2 { + global: sym2; + main; +} VERS_1; +") + set(CMAKE_REQUIRED_FLAGS_SAVE ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "-Wl,--version-script='${CMAKE_CURRENT_BINARY_DIR}/conftest.map'") + check_c_source_compiles("void sym(void) {} +void sym2(void) {} +int main(void) {return 0;} +" HAVE_LD_VERSION_SCRIPT) + if(NOT HAVE_LD_VERSION_SCRIPT) + set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS_SAVE} "-Wl,-M -Wl,${CMAKE_CURRENT_BINARY_DIR}/conftest.map") + check_c_source_compiles("void sym(void) {} +void sym2(void) {} +int main(void) {return 0;} +" HAVE_SOLARIS_LD_VERSION_SCRIPT) + endif() + set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS_SAVE}) + file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/conftest.map") +endif() + +# Find symbol prefix. Likely obsolete and unnecessary with recent +# toolchains (it's not done in many other projects). +function(symbol_prefix) + set(SYMBOL_PREFIX) + + execute_process(COMMAND "${CMAKE_C_COMPILER}" "-E" "-" + INPUT_FILE /dev/null + OUTPUT_VARIABLE OUT + RESULT_VARIABLE STATUS) + + if(CPP_FAIL) + message(WARNING "Failed to run the C preprocessor") + endif() + + string(REPLACE "\n" ";" OUT "${OUT}") + foreach(line ${OUT}) + string(REGEX MATCH "^PREFIX=" found_match "${line}") + if(found_match) + STRING(REGEX REPLACE "^PREFIX=(.*\)" "\\1" prefix "${line}") + string(REGEX MATCH "__USER_LABEL_PREFIX__" found_match "${prefix}") + if(found_match) + STRING(REGEX REPLACE "(.*)__USER_LABEL_PREFIX__(.*)" "\\1\\2" prefix "${prefix}") + endif() + set(SYMBOL_PREFIX "${prefix}") + endif() + endforeach() + + message(STATUS "Symbol prefix: ${SYMBOL_PREFIX}") + set(SYMBOL_PREFIX "${SYMBOL_PREFIX}" PARENT_SCOPE) +endfunction() + +if(UNIX) + symbol_prefix() +endif() + +find_program(AWK NAMES gawk awk) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +if(NOT AWK OR ANDROID) + # No awk available to generate sources; use pre-built pnglibconf.h + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt + ${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.h) + add_custom_target(genfiles) # Dummy +else() + include(CMakeParseArguments) + # Generate .chk from .out with awk + # generate_chk(INPUT inputfile OUTPUT outputfile [DEPENDS dep1 [dep2...]]) + function(generate_chk) + set(options) + set(oneValueArgs INPUT OUTPUT) + set(multiValueArgs DEPENDS) + cmake_parse_arguments(_GC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if (NOT _GC_INPUT) + message(FATAL_ERROR "Invalid arguments. generate_out requires input.") + endif() + if (NOT _GC_OUTPUT) + message(FATAL_ERROR "Invalid arguments. generate_out requires output.") + endif() + + add_custom_command(OUTPUT "${_GC_OUTPUT}" + COMMAND "${CMAKE_COMMAND}" + "-DINPUT=${_GC_INPUT}" + "-DOUTPUT=${_GC_OUTPUT}" + -P "${CMAKE_CURRENT_BINARY_DIR}/scripts/genchk.cmake" + DEPENDS "${_GC_INPUT}" ${_GC_DEPENDS} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + endfunction() + + # Generate .out from .c with awk + # generate_out(INPUT inputfile OUTPUT outputfile [DEPENDS dep1 [dep2...]]) + function(generate_out) + set(options) + set(oneValueArgs INPUT OUTPUT) + set(multiValueArgs DEPENDS) + cmake_parse_arguments(_GO "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if (NOT _GO_INPUT) + message(FATAL_ERROR "Invalid arguments. generate_out requires input.") + endif() + if (NOT _GO_OUTPUT) + message(FATAL_ERROR "Invalid arguments. generate_out requires output.") + endif() + + add_custom_command(OUTPUT "${_GO_OUTPUT}" + COMMAND "${CMAKE_COMMAND}" + "-DINPUT=${_GO_INPUT}" + "-DOUTPUT=${_GO_OUTPUT}" + -P "${CMAKE_CURRENT_BINARY_DIR}/scripts/genout.cmake" + DEPENDS "${_GO_INPUT}" ${_GO_DEPENDS} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + endfunction() + + # Generate specific source file with awk + # generate_source(OUTPUT outputfile [DEPENDS dep1 [dep2...]]) + function(generate_source) + set(options) + set(oneValueArgs OUTPUT) + set(multiValueArgs DEPENDS) + cmake_parse_arguments(_GSO "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if (NOT _GSO_OUTPUT) + message(FATAL_ERROR "Invalid arguments. generate_source requires output.") + endif() + + add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${_GSO_OUTPUT}" + COMMAND "${CMAKE_COMMAND}" + "-DOUTPUT=${_GSO_OUTPUT}" + -P "${CMAKE_CURRENT_BINARY_DIR}/scripts/gensrc.cmake" + DEPENDS ${_GSO_DEPENDS} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + endfunction() + + # Copy file + function(generate_copy source destination) + add_custom_command(OUTPUT "${destination}" + COMMAND "${CMAKE_COMMAND}" -E remove "${destination}" + COMMAND "${CMAKE_COMMAND}" -E copy "${source}" + "${destination}" + DEPENDS "${source}") + endfunction() + + # Generate scripts/pnglibconf.h + generate_source(OUTPUT "scripts/pnglibconf.c" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/scripts/pnglibconf.dfa" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/options.awk" + "${CMAKE_CURRENT_SOURCE_DIR}/pngconf.h") + + # Generate pnglibconf.c + generate_source(OUTPUT "pnglibconf.c" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/scripts/pnglibconf.dfa" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/options.awk" + "${CMAKE_CURRENT_SOURCE_DIR}/pngconf.h") + + if(PNG_PREFIX) + set(PNGLIBCONF_H_EXTRA_DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/scripts/prefix.out" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/macro.lst") + set(PNGPREFIX_H_EXTRA_DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/scripts/intprefix.out") + endif() + + generate_out(INPUT "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.c" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.out") + + # Generate pnglibconf.h + generate_source(OUTPUT "pnglibconf.h" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.out" + ${PNGLIBCONF_H_EXTRA_DEPENDS}) + + generate_out(INPUT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/intprefix.c" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/scripts/intprefix.out" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.h") + + generate_out(INPUT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/prefix.c" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/scripts/prefix.out" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/png.h" + "${CMAKE_CURRENT_SOURCE_DIR}/pngconf.h" + "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.out") + + # Generate pngprefix.h + generate_source(OUTPUT "pngprefix.h" + DEPENDS ${PNGPREFIX_H_EXTRA_DEPENDS}) + + generate_out(INPUT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/sym.c" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/scripts/sym.out" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.h") + + generate_out(INPUT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/symbols.c" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/scripts/symbols.out" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/png.h" + "${CMAKE_CURRENT_SOURCE_DIR}/pngconf.h" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt") + + generate_out(INPUT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/vers.c" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/scripts/vers.out" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/png.h" + "${CMAKE_CURRENT_SOURCE_DIR}/pngconf.h" + "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.h") + + generate_chk(INPUT "${CMAKE_CURRENT_BINARY_DIR}/scripts/symbols.out" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/scripts/symbols.chk" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/scripts/checksym.awk" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/symbols.def") + + add_custom_target(symbol-check DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/scripts/symbols.chk") + + generate_copy("${CMAKE_CURRENT_BINARY_DIR}/scripts/sym.out" + "${CMAKE_CURRENT_BINARY_DIR}/libpng.sym") + generate_copy("${CMAKE_CURRENT_BINARY_DIR}/scripts/vers.out" + "${CMAKE_CURRENT_BINARY_DIR}/libpng.vers") + + add_custom_target(genvers DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libpng.vers") + add_custom_target(gensym DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libpng.sym") + + add_custom_target("genprebuilt" + COMMAND "${CMAKE_COMMAND}" + "-DOUTPUT=scripts/pnglibconf.h.prebuilt" + -P "${CMAKE_CURRENT_BINARY_DIR}/scripts/gensrc.cmake" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + + # A single target handles generation of all generated files. If + # they are dependend upon separately by multiple targets, this + # confuses parallel make (it would require a separate top-level + # target for each file to track the dependencies properly). + add_custom_target(genfiles DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/libpng.sym" + "${CMAKE_CURRENT_BINARY_DIR}/libpng.vers" + "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.c" + "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.h" + "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.out" + "${CMAKE_CURRENT_BINARY_DIR}/pngprefix.h" + "${CMAKE_CURRENT_BINARY_DIR}/scripts/intprefix.out" + "${CMAKE_CURRENT_BINARY_DIR}/scripts/pnglibconf.c" + "${CMAKE_CURRENT_BINARY_DIR}/scripts/prefix.out" + "${CMAKE_CURRENT_BINARY_DIR}/scripts/sym.out" + "${CMAKE_CURRENT_BINARY_DIR}/scripts/symbols.chk" + "${CMAKE_CURRENT_BINARY_DIR}/scripts/symbols.out" + "${CMAKE_CURRENT_BINARY_DIR}/scripts/vers.out") +endif(NOT AWK OR ANDROID) + +# OUR SOURCES +set(libpng_public_hdrs + png.h + pngconf.h + "${CMAKE_CURRENT_BINARY_DIR}/pnglibconf.h" +) +set(libpng_private_hdrs + pngpriv.h + pngdebug.h + pnginfo.h + pngstruct.h +) +if(AWK AND NOT ANDROID) + list(APPEND libpng_private_hdrs "${CMAKE_CURRENT_BINARY_DIR}/pngprefix.h") +endif() +set(libpng_sources + ${libpng_public_hdrs} + ${libpng_private_hdrs} + png.c + pngerror.c + pngget.c + pngmem.c + pngpread.c + pngread.c + pngrio.c + pngrtran.c + pngrutil.c + pngset.c + pngtrans.c + pngwio.c + pngwrite.c + pngwtran.c + pngwutil.c + ${libpng_arm_sources} + ${libpng_intel_sources} + ${libpng_mips_sources} + ${libpng_powerpc_sources} +) +set(pngtest_sources + pngtest.c +) +set(pngvalid_sources + contrib/libtests/pngvalid.c +) +set(pngstest_sources + contrib/libtests/pngstest.c +) +set(pngunknown_sources + contrib/libtests/pngunknown.c +) +set(pngimage_sources + contrib/libtests/pngimage.c +) +set(pngfix_sources + contrib/tools/pngfix.c +) +set(png_fix_itxt_sources + contrib/tools/png-fix-itxt.c +) + +if(MSVC) + add_definitions(-D_CRT_SECURE_NO_DEPRECATE) +endif(MSVC) + +if(PNG_DEBUG) + add_definitions(-DPNG_DEBUG) +endif() + +# NOW BUILD OUR TARGET +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${ZLIB_INCLUDE_DIR}) + +unset(PNG_LIB_TARGETS) + +if(PNG_SHARED) + add_library(png SHARED ${libpng_sources}) + set(PNG_LIB_TARGETS png) + set_target_properties(png PROPERTIES OUTPUT_NAME ${PNG_LIB_NAME}) + add_dependencies(png genfiles) + if(MSVC) + # msvc does not append 'lib' - do it here to have consistent name + set_target_properties(png PROPERTIES PREFIX "lib") + set_target_properties(png PROPERTIES IMPORT_PREFIX "lib") + endif() + target_link_libraries(png ${ZLIB_LIBRARY} ${M_LIBRARY}) + + if(UNIX AND AWK) + if(HAVE_LD_VERSION_SCRIPT) + set_target_properties(png PROPERTIES LINK_FLAGS + "-Wl,--version-script='${CMAKE_CURRENT_BINARY_DIR}/libpng.vers'") + elseif(HAVE_SOLARIS_LD_VERSION_SCRIPT) + set_target_properties(png PROPERTIES LINK_FLAGS + "-Wl,-M -Wl,'${CMAKE_CURRENT_BINARY_DIR}/libpng.vers'") + endif() + endif() +endif() + +if(PNG_STATIC) + # does not work without changing name + set(PNG_LIB_NAME_STATIC png_static) + add_library(png_static STATIC ${libpng_sources}) + add_dependencies(png_static genfiles) + # MSVC doesn't use a different file extension for shared vs. static + # libs. We are able to change OUTPUT_NAME to remove the _static + # for all other platforms. + if(NOT MSVC) + set_target_properties(png_static PROPERTIES + OUTPUT_NAME "${PNG_LIB_NAME}" + CLEAN_DIRECT_OUTPUT 1) + else() + set_target_properties(png_static PROPERTIES + OUTPUT_NAME "${PNG_LIB_NAME}_static" + CLEAN_DIRECT_OUTPUT 1) + endif() + list(APPEND PNG_LIB_TARGETS png_static) + if(MSVC) + # msvc does not append 'lib' - do it here to have consistent name + set_target_properties(png_static PROPERTIES PREFIX "lib") + endif() + target_link_libraries(png_static ${ZLIB_LIBRARY} ${M_LIBRARY}) +endif() + +if(PNG_FRAMEWORK) + set(PNG_LIB_NAME_FRAMEWORK png_framework) + add_library(png_framework SHARED ${libpng_sources}) + add_dependencies(png_framework genfiles) + list(APPEND PNG_LIB_TARGETS png_framework) + set_target_properties(png_framework PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION ${PNGLIB_VERSION} + MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${PNGLIB_MAJOR}.${PNGLIB_MINOR} + MACOSX_FRAMEWORK_BUNDLE_VERSION ${PNGLIB_VERSION} + MACOSX_FRAMEWORK_IDENTIFIER org.libpng.libpng + XCODE_ATTRIBUTE_INSTALL_PATH "@rpath" + PUBLIC_HEADER "${libpng_public_hdrs}" + OUTPUT_NAME png) + target_link_libraries(png_framework ${ZLIB_LIBRARY} ${M_LIBRARY}) +endif() + +if(NOT PNG_LIB_TARGETS) + message(SEND_ERROR + "No library variant selected to build. " + "Please enable at least one of the following options: " + " PNG_STATIC, PNG_SHARED, PNG_FRAMEWORK") +endif() + +if(PNG_SHARED AND WIN32) + set_target_properties(png PROPERTIES DEFINE_SYMBOL PNG_BUILD_DLL) +endif() + +function(png_add_test) + set(options) + set(oneValueArgs NAME COMMAND) + set(multiValueArgs OPTIONS FILES) + cmake_parse_arguments(_PAT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT _PAT_NAME) + message(FATAL_ERROR "Invalid arguments. png_add_test requires name.") + endif() + if (NOT _PAT_COMMAND) + message(FATAL_ERROR "Invalid arguments. png_add_test requires command.") + endif() + + set(TEST_OPTIONS "${_PAT_OPTIONS}") + set(TEST_FILES "${_PAT_FILES}") + + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scripts/test.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/tests/${_PAT_NAME}.cmake" @ONLY) + if(CMAKE_MAJOR_VERSION GREATER 2) # have generator expressions + add_test(NAME "${_PAT_NAME}" + COMMAND "${CMAKE_COMMAND}" + "-DLIBPNG=$" + "-DTEST_COMMAND=$" + -P "${CMAKE_CURRENT_BINARY_DIR}/tests/${_PAT_NAME}.cmake") + else() # old 2.x add_test; limited and won't work well on Windows + # Note LIBPNG is a dummy value as there are no generator expressions + add_test("${_PAT_NAME}" "${CMAKE_COMMAND}" + "-DLIBPNG=${CMAKE_CURRENT_BINARY_DIR}/libpng.so" + "-DTEST_COMMAND=./${_PAT_COMMAND}" + -P "${CMAKE_CURRENT_BINARY_DIR}/tests/${_PAT_NAME}.cmake") + endif() +endfunction() + +if(PNG_TESTS AND PNG_SHARED) + # Find test PNG files by globbing, but sort lists to ensure + # consistency between different filesystems. + file(GLOB PNGSUITE_PNGS "${CMAKE_CURRENT_SOURCE_DIR}/contrib/pngsuite/*.png") + list(SORT PNGSUITE_PNGS) + file(GLOB TEST_PNGS "${CMAKE_CURRENT_SOURCE_DIR}/contrib/testpngs/*.png") + list(SORT TEST_PNGS) + + set(PNGTEST_PNG "${CMAKE_CURRENT_SOURCE_DIR}/pngtest.png") + + add_executable(pngtest ${pngtest_sources}) + target_link_libraries(pngtest png) + + png_add_test(NAME pngtest COMMAND pngtest FILES "${PNGTEST_PNG}") + + add_executable(pngvalid ${pngvalid_sources}) + target_link_libraries(pngvalid png) + + png_add_test(NAME pngvalid-gamma-16-to-8 + COMMAND pngvalid OPTIONS --gamma-16-to-8) + png_add_test(NAME pngvalid-gamma-alpha-mode + COMMAND pngvalid OPTIONS --gamma-alpha-mode) + png_add_test(NAME pngvalid-gamma-background + COMMAND pngvalid OPTIONS --gamma-background) + png_add_test(NAME pngvalid-gamma-expand16-alpha-mode + COMMAND pngvalid OPTIONS --gamma-alpha-mode --expand16) + png_add_test(NAME pngvalid-gamma-expand16-background + COMMAND pngvalid OPTIONS --gamma-background --expand16) + png_add_test(NAME pngvalid-gamma-expand16-transform + COMMAND pngvalid OPTIONS --gamma-transform --expand16) + png_add_test(NAME pngvalid-gamma-sbit + COMMAND pngvalid OPTIONS --gamma-sbit) + png_add_test(NAME pngvalid-gamma-threshold + COMMAND pngvalid OPTIONS --gamma-threshold) + png_add_test(NAME pngvalid-gamma-transform + COMMAND pngvalid OPTIONS --gamma-transform) + png_add_test(NAME pngvalid-progressive-interlace-standard + COMMAND pngvalid OPTIONS --standard --progressive-read --interlace) + png_add_test(NAME pngvalid-progressive-size + COMMAND pngvalid OPTIONS --size --progressive-read) + png_add_test(NAME pngvalid-progressive-standard + COMMAND pngvalid OPTIONS --standard --progressive-read) + png_add_test(NAME pngvalid-standard + COMMAND pngvalid OPTIONS --standard) + png_add_test(NAME pngvalid-transform + COMMAND pngvalid OPTIONS --transform) + + add_executable(pngstest ${pngstest_sources}) + target_link_libraries(pngstest png) + + foreach(gamma_type 1.8 linear none sRGB) + foreach(alpha_type none alpha) + set(PNGSTEST_FILES) + foreach(test_png ${TEST_PNGS}) + string(REGEX MATCH ".*-linear[-.].*" TEST_PNG_LINEAR "${test_png}") + string(REGEX MATCH ".*-sRGB[-.].*" TEST_PNG_SRGB "${test_png}") + string(REGEX MATCH ".*-1.8[-.].*" TEST_PNG_G18 "${test_png}") + string(REGEX MATCH ".*-alpha-.*" TEST_PNG_ALPHA "${test_png}") + + set(TEST_PNG_VALID TRUE) + + if(TEST_PNG_ALPHA) + if (NOT "${alpha_type}" STREQUAL "alpha") + set(TEST_PNG_VALID FALSE) + endif() + else() + if ("${alpha_type}" STREQUAL "alpha") + set(TEST_PNG_VALID FALSE) + endif() + endif() + + if(TEST_PNG_LINEAR) + if(NOT "${gamma_type}" STREQUAL "linear") + set(TEST_PNG_VALID FALSE) + endif() + elseif(TEST_PNG_SRGB) + if(NOT "${gamma_type}" STREQUAL "sRGB") + set(TEST_PNG_VALID FALSE) + endif() + elseif(TEST_PNG_G18) + if(NOT "${gamma_type}" STREQUAL "1.8") + set(TEST_PNG_VALID FALSE) + endif() + else() + if(NOT "${gamma_type}" STREQUAL "none") + set(TEST_PNG_VALID FALSE) + endif() + endif() + + if(TEST_PNG_VALID) + list(APPEND PNGSTEST_FILES "${test_png}") + endif() + endforeach() + # Should already be sorted, but sort anyway to be certain. + list(SORT PNGSTEST_FILES) + png_add_test(NAME pngstest-${gamma_type}-${alpha_type} + COMMAND pngstest + OPTIONS --tmpfile "${gamma_type}-${alpha_type}-" --log + FILES ${PNGSTEST_FILES}) + endforeach() + endforeach() + + add_executable(pngunknown ${pngunknown_sources}) + target_link_libraries(pngunknown png) + + png_add_test(NAME pngunknown-discard COMMAND pngunknown OPTIONS --strict default=discard FILES "${PNGTEST_PNG}") + png_add_test(NAME pngunknown-IDAT COMMAND pngunknown OPTIONS --strict default=discard IDAT=save FILES "${PNGTEST_PNG}") + png_add_test(NAME pngunknown-if-safe COMMAND pngunknown OPTIONS --strict default=if-safe FILES "${PNGTEST_PNG}") + png_add_test(NAME pngunknown-sAPI COMMAND pngunknown OPTIONS --strict bKGD=save cHRM=save gAMA=save all=discard iCCP=save sBIT=save sRGB=save FILES "${PNGTEST_PNG}") + png_add_test(NAME pngunknown-save COMMAND pngunknown OPTIONS --strict default=save FILES "${PNGTEST_PNG}") + png_add_test(NAME pngunknown-sTER COMMAND pngunknown OPTIONS --strict sTER=if-safe FILES "${PNGTEST_PNG}") + png_add_test(NAME pngunknown-vpAg COMMAND pngunknown OPTIONS --strict vpAg=if-safe FILES "${PNGTEST_PNG}") + + add_executable(pngimage ${pngimage_sources}) + target_link_libraries(pngimage png) + + png_add_test(NAME pngimage-quick COMMAND pngimage OPTIONS --list-combos --log FILES ${PNGSUITE_PNGS}) + png_add_test(NAME pngimage-full COMMAND pngimage OPTIONS --exhaustive --list-combos --log FILES ${PNGSUITE_PNGS}) +endif() + +if(PNG_SHARED) + add_executable(pngfix ${pngfix_sources}) + target_link_libraries(pngfix png) + set(PNG_BIN_TARGETS pngfix) + + add_executable(png-fix-itxt ${png_fix_itxt_sources}) + target_link_libraries(png-fix-itxt ${ZLIB_LIBRARY} ${M_LIBRARY}) + list(APPEND PNG_BIN_TARGETS png-fix-itxt) +endif() + +# Set a variable with CMake code which: +# Creates a symlink from src to dest (if possible) or alternatively +# copies if different. +include(CMakeParseArguments) + +function(CREATE_SYMLINK DEST_FILE) + + cmake_parse_arguments(S "" "FILE;TARGET" "" ${ARGN}) + + if(NOT S_TARGET AND NOT S_FILE) + message(FATAL_ERROR "Specify either a TARGET or a FILE for CREATE_SYMLINK to link to.") + endif(NOT S_TARGET AND NOT S_FILE) + + if(S_TARGET AND S_FILE) + message(FATAL_ERROR "CREATE_SYMLINK called with both source file ${S_FILE} and build target ${S_TARGET} arguments - can only handle 1 type per call.") + endif(S_TARGET AND S_FILE) + + if(S_FILE) + # If we don't need to symlink something that's coming from a build target, + # we can go ahead and symlink/copy at configure time. + + if(CMAKE_HOST_WIN32 AND NOT CYGWIN AND NOT MSYS) + execute_process( + COMMAND "${CMAKE_COMMAND}" -E copy_if_different ${S_FILE} ${DEST_FILE} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + ) + else(CMAKE_HOST_WIN32 AND NOT CYGWIN AND NOT MSYS) + execute_process( + COMMAND ${CMAKE_COMMAND} -E create_symlink ${S_FILE} ${DEST_FILE} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + ) + endif(CMAKE_HOST_WIN32 AND NOT CYGWIN AND NOT MSYS) + endif(S_FILE) + + if(S_TARGET) + # We need to use generator expressions, which can be a bit tricky, so for + # simplicity make the symlink a POST_BUILD step and use the TARGET + # signature of add_custom_command. + + if(CMAKE_HOST_WIN32 AND NOT CYGWIN AND NOT MSYS) + add_custom_command(TARGET ${S_TARGET} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different $ $/${DEST_FILE} + ) + else(CMAKE_HOST_WIN32 AND NOT CYGWIN AND NOT MSYS) + add_custom_command(TARGET ${S_TARGET} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E create_symlink $ $/${DEST_FILE} + ) + endif(CMAKE_HOST_WIN32 AND NOT CYGWIN AND NOT MSYS) + + endif(S_TARGET) + +endfunction() + +# Create source generation scripts. +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/genchk.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/scripts/genchk.cmake @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/genout.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/scripts/genout.cmake @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/gensrc.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/scripts/gensrc.cmake @ONLY) + + +# libpng is a library so default to 'lib' +if(NOT DEFINED CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR lib) +endif(NOT DEFINED CMAKE_INSTALL_LIBDIR) + +# CREATE PKGCONFIG FILES +# we use the same files like ./configure, so we have to set its vars +# Only do this on Windows for Cygwin - the files don't make much sense outside +# a UNIX look alike +if(NOT WIN32 OR CYGWIN OR MINGW) + set(prefix ${CMAKE_INSTALL_PREFIX}) + set(exec_prefix ${CMAKE_INSTALL_PREFIX}) + set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}) + set(includedir ${CMAKE_INSTALL_PREFIX}/include) + set(LIBS "-lz -lm") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libpng.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/${PNGLIB_NAME}.pc @ONLY) + CREATE_SYMLINK(libpng.pc FILE ${PNGLIB_NAME}.pc) + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libpng-config.in + ${CMAKE_CURRENT_BINARY_DIR}/${PNGLIB_NAME}-config @ONLY) + CREATE_SYMLINK(libpng-config FILE ${PNGLIB_NAME}-config) +endif(NOT WIN32 OR CYGWIN OR MINGW) + +# SET UP LINKS +if(PNG_SHARED) + set_target_properties(png PROPERTIES +# VERSION 16.${PNGLIB_RELEASE}.1.6.34 + VERSION 16.${PNGLIB_RELEASE}.0 + SOVERSION 16 + CLEAN_DIRECT_OUTPUT 1) +endif() + +# If CMake > 2.4.x, we set a variable used below to export +# targets to an export file. +# TODO: Use VERSION_GREATER after our cmake_minimum_required >= 2.6.2 +if(CMAKE_MAJOR_VERSION GREATER 1 AND CMAKE_MINOR_VERSION GREATER 4) + set(PNG_EXPORT_RULE EXPORT libpng) +elseif(CMAKE_MAJOR_VERSION GREATER 2) # future proof + set(PNG_EXPORT_RULE EXPORT libpng) +endif() + +# INSTALL +if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL ) + install(TARGETS ${PNG_LIB_TARGETS} + ${PNG_EXPORT_RULE} + RUNTIME DESTINATION bin + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR}) + + if(PNG_SHARED) + # Create a symlink for libpng.dll.a => libpng16.dll.a on Cygwin + if(CYGWIN OR MINGW) + CREATE_SYMLINK(libpng${CMAKE_IMPORT_LIBRARY_SUFFIX} TARGET png) + install(FILES $/libpng${CMAKE_IMPORT_LIBRARY_SUFFIX} DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif(CYGWIN OR MINGW) + + if(NOT WIN32) + CREATE_SYMLINK(libpng${CMAKE_SHARED_LIBRARY_SUFFIX} TARGET png) + install(FILES $/libpng${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif(NOT WIN32) + endif(PNG_SHARED) + + if(PNG_STATIC) + if(NOT WIN32 OR CYGWIN OR MINGW) + CREATE_SYMLINK( libpng${CMAKE_STATIC_LIBRARY_SUFFIX} TARGET png_static) + install(FILES $/libpng${CMAKE_STATIC_LIBRARY_SUFFIX} DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif(NOT WIN32 OR CYGWIN OR MINGW) + endif() +endif() + +if(NOT SKIP_INSTALL_HEADERS AND NOT SKIP_INSTALL_ALL ) + install(FILES ${libpng_public_hdrs} DESTINATION include) + install(FILES ${libpng_public_hdrs} DESTINATION include/${PNGLIB_NAME}) +endif() +if(NOT SKIP_INSTALL_EXECUTABLES AND NOT SKIP_INSTALL_ALL ) + if(NOT WIN32 OR CYGWIN OR MINGW) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/libpng-config DESTINATION bin) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${PNGLIB_NAME}-config + DESTINATION bin) + endif(NOT WIN32 OR CYGWIN OR MINGW) +endif() + +if(NOT SKIP_INSTALL_PROGRAMS AND NOT SKIP_INSTALL_ALL ) + install(TARGETS ${PNG_BIN_TARGETS} + RUNTIME DESTINATION bin) +endif() + +if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL ) + # Install man pages + if(NOT PNG_MAN_DIR) + set(PNG_MAN_DIR "share/man") + endif() + install(FILES libpng.3 libpngpf.3 DESTINATION ${PNG_MAN_DIR}/man3) + install(FILES png.5 DESTINATION ${PNG_MAN_DIR}/man5) + # Install pkg-config files + if(NOT CMAKE_HOST_WIN32 OR CYGWIN OR MINGW) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libpng.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/libpng-config + DESTINATION bin) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PNGLIB_NAME}.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${PNGLIB_NAME}-config + DESTINATION bin) + endif(NOT CMAKE_HOST_WIN32 OR CYGWIN OR MINGW) +endif() + +# On versions of CMake that support it, create an export file CMake +# users can include() to import our targets +if(PNG_EXPORT_RULE AND NOT SKIP_INSTALL_EXPORT AND NOT SKIP_INSTALL_ALL ) + install(EXPORT libpng DESTINATION lib/libpng FILE lib${PNG_LIB_NAME}.cmake) +endif() + +# what's with libpng-manual.txt and all the extra files? + +# UNINSTALL +# do we need this? + +# DIST +# do we need this? + +# to create msvc import lib for mingw compiled shared lib +# pexports libpng.dll > libpng.def +# lib /def:libpng.def /machine:x86 diff --git a/src/png/libpng/LICENSE b/src/png/libpng/LICENSE new file mode 100644 index 0000000000..4cda4fa0ad --- /dev/null +++ b/src/png/libpng/LICENSE @@ -0,0 +1,133 @@ + +This copy of the libpng notices is provided for your convenience. In case of +any discrepancy between this copy and the notices in the file png.h that is +included in the libpng distribution, the latter shall prevail. + +COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: + +If you modify libpng you may insert additional notices immediately following +this sentence. + +This code is released under the libpng license. + +libpng versions 1.0.7, July 1, 2000 through 1.6.34, September 29, 2017 are +Copyright (c) 2000-2002, 2004, 2006-2017 Glenn Randers-Pehrson, are +derived from libpng-1.0.6, and are distributed according to the same +disclaimer and license as libpng-1.0.6 with the following individuals +added to the list of Contributing Authors: + + Simon-Pierre Cadieux + Eric S. Raymond + Mans Rullgard + Cosmin Truta + Gilles Vollant + James Yu + Mandar Sahastrabuddhe + Google Inc. + Vadim Barkov + +and with the following additions to the disclaimer: + + There is no warranty against interference with your enjoyment of the + library or against infringement. There is no warranty that our + efforts or the library will fulfill any of your particular purposes + or needs. This library is provided with all faults, and the entire + risk of satisfactory quality, performance, accuracy, and effort is with + the user. + +Some files in the "contrib" directory and some configure-generated +files that are distributed with libpng have other copyright owners and +are released under other open source licenses. + +libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are +Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from +libpng-0.96, and are distributed according to the same disclaimer and +license as libpng-0.96, with the following individuals added to the list +of Contributing Authors: + + Tom Lane + Glenn Randers-Pehrson + Willem van Schaik + +libpng versions 0.89, June 1996, through 0.96, May 1997, are +Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, +and are distributed according to the same disclaimer and license as +libpng-0.88, with the following individuals added to the list of +Contributing Authors: + + John Bowler + Kevin Bracey + Sam Bushell + Magnus Holmgren + Greg Roelofs + Tom Tanner + +Some files in the "scripts" directory have other copyright owners +but are released under this license. + +libpng versions 0.5, May 1995, through 0.88, January 1996, are +Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + +For the purposes of this copyright and license, "Contributing Authors" +is defined as the following set of individuals: + + Andreas Dilger + Dave Martindale + Guy Eric Schalnat + Paul Schmidt + Tim Wegner + +The PNG Reference Library is supplied "AS IS". The Contributing Authors +and Group 42, Inc. disclaim all warranties, expressed or implied, +including, without limitation, the warranties of merchantability and of +fitness for any purpose. The Contributing Authors and Group 42, Inc. +assume no liability for direct, indirect, incidental, special, exemplary, +or consequential damages, which may result from the use of the PNG +Reference Library, even if advised of the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this +source code, or portions hereof, for any purpose, without fee, subject +to the following restrictions: + + 1. The origin of this source code must not be misrepresented. + + 2. Altered versions must be plainly marked as such and must not + be misrepresented as being the original source. + + 3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + +The Contributing Authors and Group 42, Inc. specifically permit, without +fee, and encourage the use of this source code as a component to +supporting the PNG file format in commercial products. If you use this +source code in a product, acknowledgment is not required but would be +appreciated. + +END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + +TRADEMARK: + +The name "libpng" has not been registered by the Copyright owner +as a trademark in any jurisdiction. However, because libpng has +been distributed and maintained world-wide, continually since 1995, +the Copyright owner claims "common-law trademark protection" in any +jurisdiction where common-law trademark is recognized. + +OSI CERTIFICATION: + +Libpng is OSI Certified Open Source Software. OSI Certified Open Source is +a certification mark of the Open Source Initiative. OSI has not addressed +the additional disclaimers inserted at version 1.0.7. + +EXPORT CONTROL: + +The Copyright owner believes that the Export Control Classification +Number (ECCN) for libpng is EAR99, which means not subject to export +controls or International Traffic in Arms Regulations (ITAR) because +it is open source, publicly available software, that does not contain +any encryption software. See the EAR, paragraphs 734.3(b)(3) and +734.7(b). + +Glenn Randers-Pehrson +glennrp at users.sourceforge.net +September 29, 2017 diff --git a/src/png/libpng/arm/arm_init.c b/src/png/libpng/arm/arm_init.c new file mode 100644 index 0000000000..02df812e77 --- /dev/null +++ b/src/png/libpng/arm/arm_init.c @@ -0,0 +1,135 @@ + +/* arm_init.c - NEON optimised filter functions + * + * Copyright (c) 2014,2016 Glenn Randers-Pehrson + * Written by Mans Rullgard, 2011. + * Last changed in libpng 1.6.22 [May 26, 2016] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ +/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are + * called. + */ +#define _POSIX_SOURCE 1 + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#if PNG_ARM_NEON_OPT > 0 +#ifdef PNG_ARM_NEON_CHECK_SUPPORTED /* Do run-time checks */ +/* WARNING: it is strongly recommended that you do not build libpng with + * run-time checks for CPU features if at all possible. In the case of the ARM + * NEON instructions there is no processor-specific way of detecting the + * presence of the required support, therefore run-time detection is extremely + * OS specific. + * + * You may set the macro PNG_ARM_NEON_FILE to the file name of file containing + * a fragment of C source code which defines the png_have_neon function. There + * are a number of implementations in contrib/arm-neon, but the only one that + * has partial support is contrib/arm-neon/linux.c - a generic Linux + * implementation which reads /proc/cpufino. + */ +#ifndef PNG_ARM_NEON_FILE +# ifdef __linux__ +# define PNG_ARM_NEON_FILE "contrib/arm-neon/linux.c" +# endif +#endif + +#ifdef PNG_ARM_NEON_FILE + +#include /* for sig_atomic_t */ +static int png_have_neon(png_structp png_ptr); +#include PNG_ARM_NEON_FILE + +#else /* PNG_ARM_NEON_FILE */ +# error "PNG_ARM_NEON_FILE undefined: no support for run-time ARM NEON checks" +#endif /* PNG_ARM_NEON_FILE */ +#endif /* PNG_ARM_NEON_CHECK_SUPPORTED */ + +#ifndef PNG_ALIGNED_MEMORY_SUPPORTED +# error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED" +#endif + +void +png_init_filter_functions_neon(png_structp pp, unsigned int bpp) +{ + /* The switch statement is compiled in for ARM_NEON_API, the call to + * png_have_neon is compiled in for ARM_NEON_CHECK. If both are defined + * the check is only performed if the API has not set the NEON option on + * or off explicitly. In this case the check controls what happens. + * + * If the CHECK is not compiled in and the option is UNSET the behavior prior + * to 1.6.7 was to use the NEON code - this was a bug caused by having the + * wrong order of the 'ON' and 'default' cases. UNSET now defaults to OFF, + * as documented in png.h + */ + png_debug(1, "in png_init_filter_functions_neon"); +#ifdef PNG_ARM_NEON_API_SUPPORTED + switch ((pp->options >> PNG_ARM_NEON) & 3) + { + case PNG_OPTION_UNSET: + /* Allow the run-time check to execute if it has been enabled - + * thus both API and CHECK can be turned on. If it isn't supported + * this case will fall through to the 'default' below, which just + * returns. + */ +#endif /* PNG_ARM_NEON_API_SUPPORTED */ +#ifdef PNG_ARM_NEON_CHECK_SUPPORTED + { + static volatile sig_atomic_t no_neon = -1; /* not checked */ + + if (no_neon < 0) + no_neon = !png_have_neon(pp); + + if (no_neon) + return; + } +#ifdef PNG_ARM_NEON_API_SUPPORTED + break; +#endif +#endif /* PNG_ARM_NEON_CHECK_SUPPORTED */ + +#ifdef PNG_ARM_NEON_API_SUPPORTED + default: /* OFF or INVALID */ + return; + + case PNG_OPTION_ON: + /* Option turned on */ + break; + } +#endif + + /* IMPORTANT: any new external functions used here must be declared using + * PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the + * 'prefix' option to configure works: + * + * ./configure --with-libpng-prefix=foobar_ + * + * Verify you have got this right by running the above command, doing a build + * and examining pngprefix.h; it must contain a #define for every external + * function you add. (Notice that this happens automatically for the + * initialization function.) + */ + pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_neon; + + if (bpp == 3) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_neon; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_neon; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth3_neon; + } + + else if (bpp == 4) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_neon; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_neon; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth4_neon; + } +} +#endif /* PNG_ARM_NEON_OPT > 0 */ +#endif /* READ */ diff --git a/src/png/libpng/arm/filter_neon.S b/src/png/libpng/arm/filter_neon.S new file mode 100644 index 0000000000..000764cd21 --- /dev/null +++ b/src/png/libpng/arm/filter_neon.S @@ -0,0 +1,253 @@ + +/* filter_neon.S - NEON optimised filter functions + * + * Copyright (c) 2014,2017 Glenn Randers-Pehrson + * Written by Mans Rullgard, 2011. + * Last changed in libpng 1.6.31 [July 27, 2017] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* This is required to get the symbol renames, which are #defines, and the + * definitions (or not) of PNG_ARM_NEON_OPT and PNG_ARM_NEON_IMPLEMENTATION. + */ +#define PNG_VERSION_INFO_ONLY +#include "../pngpriv.h" + +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits /* mark stack as non-executable */ +#endif + +#ifdef PNG_READ_SUPPORTED + +/* Assembler NEON support - only works for 32-bit ARM (i.e. it does not work for + * ARM64). The code in arm/filter_neon_intrinsics.c supports ARM64, however it + * only works if -mfpu=neon is specified on the GCC command line. See pngpriv.h + * for the logic which sets PNG_USE_ARM_NEON_ASM: + */ +#if PNG_ARM_NEON_IMPLEMENTATION == 2 /* hand-coded assembler */ + +#if PNG_ARM_NEON_OPT > 0 + +#ifdef __ELF__ +# define ELF +#else +# define ELF @ +#endif + + .arch armv7-a + .fpu neon + +.macro func name, export=0 + .macro endfunc +ELF .size \name, . - \name + .endfunc + .purgem endfunc + .endm + .text + + /* Explicitly specifying alignment here because some versions of + * GAS don't align code correctly. This is harmless in correctly + * written versions of GAS. + */ + .align 2 + + .if \export + .global \name + .endif +ELF .type \name, STT_FUNC + .func \name +\name: +.endm + +func png_read_filter_row_sub4_neon, export=1 + ldr r3, [r0, #4] @ rowbytes + vmov.i8 d3, #0 +1: + vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128] + vadd.u8 d0, d3, d4 + vadd.u8 d1, d0, d5 + vadd.u8 d2, d1, d6 + vadd.u8 d3, d2, d7 + vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]! + subs r3, r3, #16 + bgt 1b + + bx lr +endfunc + +func png_read_filter_row_sub3_neon, export=1 + ldr r3, [r0, #4] @ rowbytes + vmov.i8 d3, #0 + mov r0, r1 + mov r2, #3 + mov r12, #12 + vld1.8 {q11}, [r0], r12 +1: + vext.8 d5, d22, d23, #3 + vadd.u8 d0, d3, d22 + vext.8 d6, d22, d23, #6 + vadd.u8 d1, d0, d5 + vext.8 d7, d23, d23, #1 + vld1.8 {q11}, [r0], r12 + vst1.32 {d0[0]}, [r1,:32], r2 + vadd.u8 d2, d1, d6 + vst1.32 {d1[0]}, [r1], r2 + vadd.u8 d3, d2, d7 + vst1.32 {d2[0]}, [r1], r2 + vst1.32 {d3[0]}, [r1], r2 + subs r3, r3, #12 + bgt 1b + + bx lr +endfunc + +func png_read_filter_row_up_neon, export=1 + ldr r3, [r0, #4] @ rowbytes +1: + vld1.8 {q0}, [r1,:128] + vld1.8 {q1}, [r2,:128]! + vadd.u8 q0, q0, q1 + vst1.8 {q0}, [r1,:128]! + subs r3, r3, #16 + bgt 1b + + bx lr +endfunc + +func png_read_filter_row_avg4_neon, export=1 + ldr r12, [r0, #4] @ rowbytes + vmov.i8 d3, #0 +1: + vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128] + vld4.32 {d16[],d17[],d18[],d19[]},[r2,:128]! + vhadd.u8 d0, d3, d16 + vadd.u8 d0, d0, d4 + vhadd.u8 d1, d0, d17 + vadd.u8 d1, d1, d5 + vhadd.u8 d2, d1, d18 + vadd.u8 d2, d2, d6 + vhadd.u8 d3, d2, d19 + vadd.u8 d3, d3, d7 + vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]! + subs r12, r12, #16 + bgt 1b + + bx lr +endfunc + +func png_read_filter_row_avg3_neon, export=1 + push {r4,lr} + ldr r12, [r0, #4] @ rowbytes + vmov.i8 d3, #0 + mov r0, r1 + mov r4, #3 + mov lr, #12 + vld1.8 {q11}, [r0], lr +1: + vld1.8 {q10}, [r2], lr + vext.8 d5, d22, d23, #3 + vhadd.u8 d0, d3, d20 + vext.8 d17, d20, d21, #3 + vadd.u8 d0, d0, d22 + vext.8 d6, d22, d23, #6 + vhadd.u8 d1, d0, d17 + vext.8 d18, d20, d21, #6 + vadd.u8 d1, d1, d5 + vext.8 d7, d23, d23, #1 + vld1.8 {q11}, [r0], lr + vst1.32 {d0[0]}, [r1,:32], r4 + vhadd.u8 d2, d1, d18 + vst1.32 {d1[0]}, [r1], r4 + vext.8 d19, d21, d21, #1 + vadd.u8 d2, d2, d6 + vhadd.u8 d3, d2, d19 + vst1.32 {d2[0]}, [r1], r4 + vadd.u8 d3, d3, d7 + vst1.32 {d3[0]}, [r1], r4 + subs r12, r12, #12 + bgt 1b + + pop {r4,pc} +endfunc + +.macro paeth rx, ra, rb, rc + vaddl.u8 q12, \ra, \rb @ a + b + vaddl.u8 q15, \rc, \rc @ 2*c + vabdl.u8 q13, \rb, \rc @ pa + vabdl.u8 q14, \ra, \rc @ pb + vabd.u16 q15, q12, q15 @ pc + vcle.u16 q12, q13, q14 @ pa <= pb + vcle.u16 q13, q13, q15 @ pa <= pc + vcle.u16 q14, q14, q15 @ pb <= pc + vand q12, q12, q13 @ pa <= pb && pa <= pc + vmovn.u16 d28, q14 + vmovn.u16 \rx, q12 + vbsl d28, \rb, \rc + vbsl \rx, \ra, d28 +.endm + +func png_read_filter_row_paeth4_neon, export=1 + ldr r12, [r0, #4] @ rowbytes + vmov.i8 d3, #0 + vmov.i8 d20, #0 +1: + vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128] + vld4.32 {d16[],d17[],d18[],d19[]},[r2,:128]! + paeth d0, d3, d16, d20 + vadd.u8 d0, d0, d4 + paeth d1, d0, d17, d16 + vadd.u8 d1, d1, d5 + paeth d2, d1, d18, d17 + vadd.u8 d2, d2, d6 + paeth d3, d2, d19, d18 + vmov d20, d19 + vadd.u8 d3, d3, d7 + vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]! + subs r12, r12, #16 + bgt 1b + + bx lr +endfunc + +func png_read_filter_row_paeth3_neon, export=1 + push {r4,lr} + ldr r12, [r0, #4] @ rowbytes + vmov.i8 d3, #0 + vmov.i8 d4, #0 + mov r0, r1 + mov r4, #3 + mov lr, #12 + vld1.8 {q11}, [r0], lr +1: + vld1.8 {q10}, [r2], lr + paeth d0, d3, d20, d4 + vext.8 d5, d22, d23, #3 + vadd.u8 d0, d0, d22 + vext.8 d17, d20, d21, #3 + paeth d1, d0, d17, d20 + vst1.32 {d0[0]}, [r1,:32], r4 + vext.8 d6, d22, d23, #6 + vadd.u8 d1, d1, d5 + vext.8 d18, d20, d21, #6 + paeth d2, d1, d18, d17 + vext.8 d7, d23, d23, #1 + vld1.8 {q11}, [r0], lr + vst1.32 {d1[0]}, [r1], r4 + vadd.u8 d2, d2, d6 + vext.8 d19, d21, d21, #1 + paeth d3, d2, d19, d18 + vst1.32 {d2[0]}, [r1], r4 + vmov d4, d19 + vadd.u8 d3, d3, d7 + vst1.32 {d3[0]}, [r1], r4 + subs r12, r12, #12 + bgt 1b + + pop {r4,pc} +endfunc +#endif /* PNG_ARM_NEON_OPT > 0 */ +#endif /* PNG_ARM_NEON_IMPLEMENTATION == 2 (assembler) */ +#endif /* READ */ diff --git a/src/png/libpng/arm/filter_neon_intrinsics.c b/src/png/libpng/arm/filter_neon_intrinsics.c new file mode 100644 index 0000000000..ea7e356bcc --- /dev/null +++ b/src/png/libpng/arm/filter_neon_intrinsics.c @@ -0,0 +1,387 @@ + +/* filter_neon_intrinsics.c - NEON optimised filter functions + * + * Copyright (c) 2014,2016 Glenn Randers-Pehrson + * Written by James Yu , October 2013. + * Based on filter_neon.S, written by Mans Rullgard, 2011. + * + * Last changed in libpng 1.6.22 [May 26, 2016] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +/* This code requires -mfpu=neon on the command line: */ +#if PNG_ARM_NEON_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */ + +#include + +/* libpng row pointers are not necessarily aligned to any particular boundary, + * however this code will only work with appropriate alignment. arm/arm_init.c + * checks for this (and will not compile unless it is done). This code uses + * variants of png_aligncast to avoid compiler warnings. + */ +#define png_ptr(type,pointer) png_aligncast(type *,pointer) +#define png_ptrc(type,pointer) png_aligncastconst(const type *,pointer) + +/* The following relies on a variable 'temp_pointer' being declared with type + * 'type'. This is written this way just to hide the GCC strict aliasing + * warning; note that the code is safe because there never is an alias between + * the input and output pointers. + */ +#define png_ldr(type,pointer)\ + (temp_pointer = png_ptr(type,pointer), *temp_pointer) + +#if PNG_ARM_NEON_OPT > 0 + +void +png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_bytep rp = row; + png_bytep rp_stop = row + row_info->rowbytes; + png_const_bytep pp = prev_row; + + png_debug(1, "in png_read_filter_row_up_neon"); + + for (; rp < rp_stop; rp += 16, pp += 16) + { + uint8x16_t qrp, qpp; + + qrp = vld1q_u8(rp); + qpp = vld1q_u8(pp); + qrp = vaddq_u8(qrp, qpp); + vst1q_u8(rp, qrp); + } +} + +void +png_read_filter_row_sub3_neon(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_bytep rp = row; + png_bytep rp_stop = row + row_info->rowbytes; + + uint8x16_t vtmp = vld1q_u8(rp); + uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp); + uint8x8x2_t vrp = *vrpt; + + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + png_debug(1, "in png_read_filter_row_sub3_neon"); + + for (; rp < rp_stop;) + { + uint8x8_t vtmp1, vtmp2; + uint32x2_t *temp_pointer; + + vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); + vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]); + vtmp2 = vext_u8(vrp.val[0], vrp.val[1], 6); + vdest.val[1] = vadd_u8(vdest.val[0], vtmp1); + + vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1); + vdest.val[2] = vadd_u8(vdest.val[1], vtmp2); + vdest.val[3] = vadd_u8(vdest.val[2], vtmp1); + + vtmp = vld1q_u8(rp + 12); + vrpt = png_ptr(uint8x8x2_t, &vtmp); + vrp = *vrpt; + + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); + rp += 3; + } + + PNG_UNUSED(prev_row) +} + +void +png_read_filter_row_sub4_neon(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_bytep rp = row; + png_bytep rp_stop = row + row_info->rowbytes; + + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + png_debug(1, "in png_read_filter_row_sub4_neon"); + + for (; rp < rp_stop; rp += 16) + { + uint32x2x4_t vtmp = vld4_u32(png_ptr(uint32_t,rp)); + uint8x8x4_t *vrpt = png_ptr(uint8x8x4_t,&vtmp); + uint8x8x4_t vrp = *vrpt; + uint32x2x4_t *temp_pointer; + + vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]); + vdest.val[1] = vadd_u8(vdest.val[0], vrp.val[1]); + vdest.val[2] = vadd_u8(vdest.val[1], vrp.val[2]); + vdest.val[3] = vadd_u8(vdest.val[2], vrp.val[3]); + vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0); + } + + PNG_UNUSED(prev_row) +} + +void +png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_bytep rp = row; + png_const_bytep pp = prev_row; + png_bytep rp_stop = row + row_info->rowbytes; + + uint8x16_t vtmp; + uint8x8x2_t *vrpt; + uint8x8x2_t vrp; + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + vtmp = vld1q_u8(rp); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; + + png_debug(1, "in png_read_filter_row_avg3_neon"); + + for (; rp < rp_stop; pp += 12) + { + uint8x8_t vtmp1, vtmp2, vtmp3; + + uint8x8x2_t *vppt; + uint8x8x2_t vpp; + + uint32x2_t *temp_pointer; + + vtmp = vld1q_u8(pp); + vppt = png_ptr(uint8x8x2_t,&vtmp); + vpp = *vppt; + + vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); + vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]); + vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); + + vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3); + vtmp3 = vext_u8(vrp.val[0], vrp.val[1], 6); + vdest.val[1] = vhadd_u8(vdest.val[0], vtmp2); + vdest.val[1] = vadd_u8(vdest.val[1], vtmp1); + + vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 6); + vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1); + + vtmp = vld1q_u8(rp + 12); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; + + vdest.val[2] = vhadd_u8(vdest.val[1], vtmp2); + vdest.val[2] = vadd_u8(vdest.val[2], vtmp3); + + vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1); + + vdest.val[3] = vhadd_u8(vdest.val[2], vtmp2); + vdest.val[3] = vadd_u8(vdest.val[3], vtmp1); + + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); + rp += 3; + } +} + +void +png_read_filter_row_avg4_neon(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_bytep rp = row; + png_bytep rp_stop = row + row_info->rowbytes; + png_const_bytep pp = prev_row; + + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + png_debug(1, "in png_read_filter_row_avg4_neon"); + + for (; rp < rp_stop; rp += 16, pp += 16) + { + uint32x2x4_t vtmp; + uint8x8x4_t *vrpt, *vppt; + uint8x8x4_t vrp, vpp; + uint32x2x4_t *temp_pointer; + + vtmp = vld4_u32(png_ptr(uint32_t,rp)); + vrpt = png_ptr(uint8x8x4_t,&vtmp); + vrp = *vrpt; + vtmp = vld4_u32(png_ptrc(uint32_t,pp)); + vppt = png_ptr(uint8x8x4_t,&vtmp); + vpp = *vppt; + + vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]); + vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); + vdest.val[1] = vhadd_u8(vdest.val[0], vpp.val[1]); + vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]); + vdest.val[2] = vhadd_u8(vdest.val[1], vpp.val[2]); + vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]); + vdest.val[3] = vhadd_u8(vdest.val[2], vpp.val[3]); + vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]); + + vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0); + } +} + +static uint8x8_t +paeth(uint8x8_t a, uint8x8_t b, uint8x8_t c) +{ + uint8x8_t d, e; + uint16x8_t p1, pa, pb, pc; + + p1 = vaddl_u8(a, b); /* a + b */ + pc = vaddl_u8(c, c); /* c * 2 */ + pa = vabdl_u8(b, c); /* pa */ + pb = vabdl_u8(a, c); /* pb */ + pc = vabdq_u16(p1, pc); /* pc */ + + p1 = vcleq_u16(pa, pb); /* pa <= pb */ + pa = vcleq_u16(pa, pc); /* pa <= pc */ + pb = vcleq_u16(pb, pc); /* pb <= pc */ + + p1 = vandq_u16(p1, pa); /* pa <= pb && pa <= pc */ + + d = vmovn_u16(pb); + e = vmovn_u16(p1); + + d = vbsl_u8(d, b, c); + e = vbsl_u8(e, a, d); + + return e; +} + +void +png_read_filter_row_paeth3_neon(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_bytep rp = row; + png_const_bytep pp = prev_row; + png_bytep rp_stop = row + row_info->rowbytes; + + uint8x16_t vtmp; + uint8x8x2_t *vrpt; + uint8x8x2_t vrp; + uint8x8_t vlast = vdup_n_u8(0); + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + vtmp = vld1q_u8(rp); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; + + png_debug(1, "in png_read_filter_row_paeth3_neon"); + + for (; rp < rp_stop; pp += 12) + { + uint8x8x2_t *vppt; + uint8x8x2_t vpp; + uint8x8_t vtmp1, vtmp2, vtmp3; + uint32x2_t *temp_pointer; + + vtmp = vld1q_u8(pp); + vppt = png_ptr(uint8x8x2_t,&vtmp); + vpp = *vppt; + + vdest.val[0] = paeth(vdest.val[3], vpp.val[0], vlast); + vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); + + vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); + vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3); + vdest.val[1] = paeth(vdest.val[0], vtmp2, vpp.val[0]); + vdest.val[1] = vadd_u8(vdest.val[1], vtmp1); + + vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 6); + vtmp3 = vext_u8(vpp.val[0], vpp.val[1], 6); + vdest.val[2] = paeth(vdest.val[1], vtmp3, vtmp2); + vdest.val[2] = vadd_u8(vdest.val[2], vtmp1); + + vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1); + vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1); + + vtmp = vld1q_u8(rp + 12); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; + + vdest.val[3] = paeth(vdest.val[2], vtmp2, vtmp3); + vdest.val[3] = vadd_u8(vdest.val[3], vtmp1); + + vlast = vtmp2; + + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); + rp += 3; + } +} + +void +png_read_filter_row_paeth4_neon(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_bytep rp = row; + png_bytep rp_stop = row + row_info->rowbytes; + png_const_bytep pp = prev_row; + + uint8x8_t vlast = vdup_n_u8(0); + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + png_debug(1, "in png_read_filter_row_paeth4_neon"); + + for (; rp < rp_stop; rp += 16, pp += 16) + { + uint32x2x4_t vtmp; + uint8x8x4_t *vrpt, *vppt; + uint8x8x4_t vrp, vpp; + uint32x2x4_t *temp_pointer; + + vtmp = vld4_u32(png_ptr(uint32_t,rp)); + vrpt = png_ptr(uint8x8x4_t,&vtmp); + vrp = *vrpt; + vtmp = vld4_u32(png_ptrc(uint32_t,pp)); + vppt = png_ptr(uint8x8x4_t,&vtmp); + vpp = *vppt; + + vdest.val[0] = paeth(vdest.val[3], vpp.val[0], vlast); + vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); + vdest.val[1] = paeth(vdest.val[0], vpp.val[1], vpp.val[0]); + vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]); + vdest.val[2] = paeth(vdest.val[1], vpp.val[2], vpp.val[1]); + vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]); + vdest.val[3] = paeth(vdest.val[2], vpp.val[3], vpp.val[2]); + vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]); + + vlast = vpp.val[3]; + + vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0); + } +} + +#endif /* PNG_ARM_NEON_OPT > 0 */ +#endif /* PNG_ARM_NEON_IMPLEMENTATION == 1 (intrinsics) */ +#endif /* READ */ diff --git a/src/png/libpng/intel/filter_sse2_intrinsics.c b/src/png/libpng/intel/filter_sse2_intrinsics.c new file mode 100644 index 0000000000..5e8553fbb9 --- /dev/null +++ b/src/png/libpng/intel/filter_sse2_intrinsics.c @@ -0,0 +1,406 @@ + +/* filter_sse2_intrinsics.c - SSE2 optimized filter functions + * + * Copyright (c) 2016-2017 Glenn Randers-Pehrson + * Written by Mike Klein and Matt Sarett + * Derived from arm/filter_neon_intrinsics.c + * + * Last changed in libpng 1.6.31 [July 27, 2017] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#if PNG_INTEL_SSE_IMPLEMENTATION > 0 + +#include + +/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d). + * They're positioned like this: + * prev: c b + * row: a d + * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be + * whichever of a, b, or c is closest to p=a+b-c. + */ + +static __m128i load4(const void* p) { + return _mm_cvtsi32_si128(*(const int*)p); +} + +static void store4(void* p, __m128i v) { + *(int*)p = _mm_cvtsi128_si32(v); +} + +static __m128i load3(const void* p) { + /* We'll load 2 bytes, then 1 byte, + * then mask them together, and finally load into SSE. + */ + const png_uint_16* p01 = (png_const_uint_16p)p; + const png_byte* p2 = (const png_byte*)(p01+1); + + png_uint_32 v012 = (png_uint_32)(*p01) + | (png_uint_32)(*p2) << 16; + return load4(&v012); +} + +static void store3(void* p, __m128i v) { + /* We'll pull from SSE as a 32-bit int, then write + * its bottom two bytes, then its third byte. + */ + png_uint_32 v012; + png_uint_16* p01; + png_byte* p2; + + store4(&v012, v); + + p01 = (png_uint_16p)p; + p2 = (png_byte*)(p01+1); + *p01 = (png_uint_16)v012; + *p2 = (png_byte)(v012 >> 16); +} + +void png_read_filter_row_sub3_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* The Sub filter predicts each pixel as the previous pixel, a. + * There is no pixel to the left of the first pixel. It's encoded directly. + * That works with our main loop if we just say that left pixel was zero. + */ + png_size_t rb; + + __m128i a, d = _mm_setzero_si128(); + + png_debug(1, "in png_read_filter_row_sub3_sse2"); + + rb = row_info->rowbytes; + while (rb >= 4) { + a = d; d = load4(row); + d = _mm_add_epi8(d, a); + store3(row, d); + + row += 3; + rb -= 3; + } + if (rb > 0) { + a = d; d = load3(row); + d = _mm_add_epi8(d, a); + store3(row, d); + + row += 3; + rb -= 3; + } + PNG_UNUSED(prev) +} + +void png_read_filter_row_sub4_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* The Sub filter predicts each pixel as the previous pixel, a. + * There is no pixel to the left of the first pixel. It's encoded directly. + * That works with our main loop if we just say that left pixel was zero. + */ + png_size_t rb; + + __m128i a, d = _mm_setzero_si128(); + + png_debug(1, "in png_read_filter_row_sub4_sse2"); + + rb = row_info->rowbytes+4; + while (rb > 4) { + a = d; d = load4(row); + d = _mm_add_epi8(d, a); + store4(row, d); + + row += 4; + rb -= 4; + } + PNG_UNUSED(prev) +} + +void png_read_filter_row_avg3_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* The Avg filter predicts each pixel as the (truncated) average of a and b. + * There's no pixel to the left of the first pixel. Luckily, it's + * predicted to be half of the pixel above it. So again, this works + * perfectly with our loop if we make sure a starts at zero. + */ + + png_size_t rb; + + const __m128i zero = _mm_setzero_si128(); + + __m128i b; + __m128i a, d = zero; + + png_debug(1, "in png_read_filter_row_avg3_sse2"); + rb = row_info->rowbytes; + while (rb >= 4) { + __m128i avg; + b = load4(prev); + a = d; d = load4(row ); + + /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ + avg = _mm_avg_epu8(a,b); + /* ...but we can fix it up by subtracting off 1 if it rounded up. */ + avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b), + _mm_set1_epi8(1))); + d = _mm_add_epi8(d, avg); + store3(row, d); + + prev += 3; + row += 3; + rb -= 3; + } + if (rb > 0) { + __m128i avg; + b = load3(prev); + a = d; d = load3(row ); + + /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ + avg = _mm_avg_epu8(a,b); + /* ...but we can fix it up by subtracting off 1 if it rounded up. */ + avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b), + _mm_set1_epi8(1))); + + d = _mm_add_epi8(d, avg); + store3(row, d); + + prev += 3; + row += 3; + rb -= 3; + } +} + +void png_read_filter_row_avg4_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* The Avg filter predicts each pixel as the (truncated) average of a and b. + * There's no pixel to the left of the first pixel. Luckily, it's + * predicted to be half of the pixel above it. So again, this works + * perfectly with our loop if we make sure a starts at zero. + */ + png_size_t rb; + const __m128i zero = _mm_setzero_si128(); + __m128i b; + __m128i a, d = zero; + + png_debug(1, "in png_read_filter_row_avg4_sse2"); + + rb = row_info->rowbytes+4; + while (rb > 4) { + __m128i avg; + b = load4(prev); + a = d; d = load4(row ); + + /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ + avg = _mm_avg_epu8(a,b); + /* ...but we can fix it up by subtracting off 1 if it rounded up. */ + avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b), + _mm_set1_epi8(1))); + + d = _mm_add_epi8(d, avg); + store4(row, d); + + prev += 4; + row += 4; + rb -= 4; + } +} + +/* Returns |x| for 16-bit lanes. */ +static __m128i abs_i16(__m128i x) { +#if PNG_INTEL_SSE_IMPLEMENTATION >= 2 + return _mm_abs_epi16(x); +#else + /* Read this all as, return x<0 ? -x : x. + * To negate two's complement, you flip all the bits then add 1. + */ + __m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128()); + + /* Flip negative lanes. */ + x = _mm_xor_si128(x, is_negative); + + /* +1 to negative lanes, else +0. */ + x = _mm_sub_epi16(x, is_negative); + return x; +#endif +} + +/* Bytewise c ? t : e. */ +static __m128i if_then_else(__m128i c, __m128i t, __m128i e) { +#if PNG_INTEL_SSE_IMPLEMENTATION >= 3 + return _mm_blendv_epi8(e,t,c); +#else + return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e)); +#endif +} + +void png_read_filter_row_paeth3_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* Paeth tries to predict pixel d using the pixel to the left of it, a, + * and two pixels from the previous row, b and c: + * prev: c b + * row: a d + * The Paeth function predicts d to be whichever of a, b, or c is nearest to + * p=a+b-c. + * + * The first pixel has no left context, and so uses an Up filter, p = b. + * This works naturally with our main loop's p = a+b-c if we force a and c + * to zero. + * Here we zero b and d, which become c and a respectively at the start of + * the loop. + */ + png_size_t rb; + const __m128i zero = _mm_setzero_si128(); + __m128i c, b = zero, + a, d = zero; + + png_debug(1, "in png_read_filter_row_paeth3_sse2"); + + rb = row_info->rowbytes; + while (rb >= 4) { + /* It's easiest to do this math (particularly, deal with pc) with 16-bit + * intermediates. + */ + __m128i pa,pb,pc,smallest,nearest; + c = b; b = _mm_unpacklo_epi8(load4(prev), zero); + a = d; d = _mm_unpacklo_epi8(load4(row ), zero); + + /* (p-a) == (a+b-c - a) == (b-c) */ + + pa = _mm_sub_epi16(b,c); + + /* (p-b) == (a+b-c - b) == (a-c) */ + pb = _mm_sub_epi16(a,c); + + /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ + pc = _mm_add_epi16(pa,pb); + + pa = abs_i16(pa); /* |p-a| */ + pb = abs_i16(pb); /* |p-b| */ + pc = abs_i16(pc); /* |p-c| */ + + smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); + + /* Paeth breaks ties favoring a over b over c. */ + nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, + if_then_else(_mm_cmpeq_epi16(smallest, pb), b, + c)); + + /* Note `_epi8`: we need addition to wrap modulo 255. */ + d = _mm_add_epi8(d, nearest); + store3(row, _mm_packus_epi16(d,d)); + + prev += 3; + row += 3; + rb -= 3; + } + if (rb > 0) { + /* It's easiest to do this math (particularly, deal with pc) with 16-bit + * intermediates. + */ + __m128i pa,pb,pc,smallest,nearest; + c = b; b = _mm_unpacklo_epi8(load3(prev), zero); + a = d; d = _mm_unpacklo_epi8(load3(row ), zero); + + /* (p-a) == (a+b-c - a) == (b-c) */ + pa = _mm_sub_epi16(b,c); + + /* (p-b) == (a+b-c - b) == (a-c) */ + pb = _mm_sub_epi16(a,c); + + /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ + pc = _mm_add_epi16(pa,pb); + + pa = abs_i16(pa); /* |p-a| */ + pb = abs_i16(pb); /* |p-b| */ + pc = abs_i16(pc); /* |p-c| */ + + smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); + + /* Paeth breaks ties favoring a over b over c. */ + nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, + if_then_else(_mm_cmpeq_epi16(smallest, pb), b, + c)); + + /* Note `_epi8`: we need addition to wrap modulo 255. */ + d = _mm_add_epi8(d, nearest); + store3(row, _mm_packus_epi16(d,d)); + + prev += 3; + row += 3; + rb -= 3; + } +} + +void png_read_filter_row_paeth4_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* Paeth tries to predict pixel d using the pixel to the left of it, a, + * and two pixels from the previous row, b and c: + * prev: c b + * row: a d + * The Paeth function predicts d to be whichever of a, b, or c is nearest to + * p=a+b-c. + * + * The first pixel has no left context, and so uses an Up filter, p = b. + * This works naturally with our main loop's p = a+b-c if we force a and c + * to zero. + * Here we zero b and d, which become c and a respectively at the start of + * the loop. + */ + png_size_t rb; + const __m128i zero = _mm_setzero_si128(); + __m128i pa,pb,pc,smallest,nearest; + __m128i c, b = zero, + a, d = zero; + + png_debug(1, "in png_read_filter_row_paeth4_sse2"); + + rb = row_info->rowbytes+4; + while (rb > 4) { + /* It's easiest to do this math (particularly, deal with pc) with 16-bit + * intermediates. + */ + c = b; b = _mm_unpacklo_epi8(load4(prev), zero); + a = d; d = _mm_unpacklo_epi8(load4(row ), zero); + + /* (p-a) == (a+b-c - a) == (b-c) */ + pa = _mm_sub_epi16(b,c); + + /* (p-b) == (a+b-c - b) == (a-c) */ + pb = _mm_sub_epi16(a,c); + + /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ + pc = _mm_add_epi16(pa,pb); + + pa = abs_i16(pa); /* |p-a| */ + pb = abs_i16(pb); /* |p-b| */ + pc = abs_i16(pc); /* |p-c| */ + + smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); + + /* Paeth breaks ties favoring a over b over c. */ + nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, + if_then_else(_mm_cmpeq_epi16(smallest, pb), b, + c)); + + /* Note `_epi8`: we need addition to wrap modulo 255. */ + d = _mm_add_epi8(d, nearest); + store4(row, _mm_packus_epi16(d,d)); + + prev += 4; + row += 4; + rb -= 4; + } +} + +#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */ +#endif /* READ */ diff --git a/src/png/libpng/intel/intel_init.c b/src/png/libpng/intel/intel_init.c new file mode 100644 index 0000000000..8f08baf8c5 --- /dev/null +++ b/src/png/libpng/intel/intel_init.c @@ -0,0 +1,53 @@ + +/* intel_init.c - SSE2 optimized filter functions + * + * Copyright (c) 2016-2017 Glenn Randers-Pehrson + * Written by Mike Klein and Matt Sarett, Google, Inc. + * Derived from arm/arm_init.c + * + * Last changed in libpng 1.6.29 [March 16, 2017] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED +#if PNG_INTEL_SSE_IMPLEMENTATION > 0 + +void +png_init_filter_functions_sse2(png_structp pp, unsigned int bpp) +{ + /* The techniques used to implement each of these filters in SSE operate on + * one pixel at a time. + * So they generally speed up 3bpp images about 3x, 4bpp images about 4x. + * They can scale up to 6 and 8 bpp images and down to 2 bpp images, + * but they'd not likely have any benefit for 1bpp images. + * Most of these can be implemented using only MMX and 64-bit registers, + * but they end up a bit slower than using the equally-ubiquitous SSE2. + */ + png_debug(1, "in png_init_filter_functions_sse2"); + if (bpp == 3) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_sse2; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_sse2; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth3_sse2; + } + else if (bpp == 4) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_sse2; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_sse2; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth4_sse2; + } + + /* No need optimize PNG_FILTER_VALUE_UP. The compiler should + * autovectorize. + */ +} + +#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/src/png/libpng/libpng-config.in b/src/png/libpng/libpng-config.in new file mode 100644 index 0000000000..69bf8e33f0 --- /dev/null +++ b/src/png/libpng/libpng-config.in @@ -0,0 +1,127 @@ +#! /bin/sh + +# libpng-config +# provides configuration info for libpng. + +# Copyright (C) 2002, 2004, 2006, 2007 Glenn Randers-Pehrson + +# This code is released under the libpng license. +# For conditions of distribution and use, see the disclaimer +# and license in png.h + +# Modeled after libxml-config. + +version="@PNGLIB_VERSION@" +prefix="@prefix@" +exec_prefix="@exec_prefix@" +libdir="@libdir@" +includedir="@includedir@/libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@" +libs="-lpng@PNGLIB_MAJOR@@PNGLIB_MINOR@" +all_libs="-lpng@PNGLIB_MAJOR@@PNGLIB_MINOR@ @LIBS@" +I_opts="-I${includedir}" +L_opts="-L${libdir}" +R_opts="" +cppflags="" +ccopts="" +ldopts="" + +usage() +{ + cat < +#include +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +/* This code requires -mfpu=msa on the command line: */ +#if PNG_MIPS_MSA_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */ + +#include + +/* libpng row pointers are not necessarily aligned to any particular boundary, + * however this code will only work with appropriate alignment. mips/mips_init.c + * checks for this (and will not compile unless it is done). This code uses + * variants of png_aligncast to avoid compiler warnings. + */ +#define png_ptr(type,pointer) png_aligncast(type *,pointer) +#define png_ptrc(type,pointer) png_aligncastconst(const type *,pointer) + +/* The following relies on a variable 'temp_pointer' being declared with type + * 'type'. This is written this way just to hide the GCC strict aliasing + * warning; note that the code is safe because there never is an alias between + * the input and output pointers. + */ +#define png_ldr(type,pointer)\ + (temp_pointer = png_ptr(type,pointer), *temp_pointer) + +#if PNG_MIPS_MSA_OPT > 0 + +#ifdef CLANG_BUILD + #define MSA_SRLI_B(a, b) __msa_srli_b((v16i8) a, b) + + #define LW(psrc) \ + ( { \ + uint8_t *psrc_lw_m = (uint8_t *) (psrc); \ + uint32_t val_m; \ + \ + asm volatile ( \ + "lw %[val_m], %[psrc_lw_m] \n\t" \ + \ + : [val_m] "=r" (val_m) \ + : [psrc_lw_m] "m" (*psrc_lw_m) \ + ); \ + \ + val_m; \ + } ) + + #define SH(val, pdst) \ + { \ + uint8_t *pdst_sh_m = (uint8_t *) (pdst); \ + uint16_t val_m = (val); \ + \ + asm volatile ( \ + "sh %[val_m], %[pdst_sh_m] \n\t" \ + \ + : [pdst_sh_m] "=m" (*pdst_sh_m) \ + : [val_m] "r" (val_m) \ + ); \ + } + + #define SW(val, pdst) \ + { \ + uint8_t *pdst_sw_m = (uint8_t *) (pdst); \ + uint32_t val_m = (val); \ + \ + asm volatile ( \ + "sw %[val_m], %[pdst_sw_m] \n\t" \ + \ + : [pdst_sw_m] "=m" (*pdst_sw_m) \ + : [val_m] "r" (val_m) \ + ); \ + } + + #if (__mips == 64) + #define SD(val, pdst) \ + { \ + uint8_t *pdst_sd_m = (uint8_t *) (pdst); \ + uint64_t val_m = (val); \ + \ + asm volatile ( \ + "sd %[val_m], %[pdst_sd_m] \n\t" \ + \ + : [pdst_sd_m] "=m" (*pdst_sd_m) \ + : [val_m] "r" (val_m) \ + ); \ + } + #else + #define SD(val, pdst) \ + { \ + uint8_t *pdst_sd_m = (uint8_t *) (pdst); \ + uint32_t val0_m, val1_m; \ + \ + val0_m = (uint32_t) ((val) & 0x00000000FFFFFFFF); \ + val1_m = (uint32_t) (((val) >> 32) & 0x00000000FFFFFFFF); \ + \ + SW(val0_m, pdst_sd_m); \ + SW(val1_m, pdst_sd_m + 4); \ + } + #endif +#else + #define MSA_SRLI_B(a, b) (a >> b) + +#if (__mips_isa_rev >= 6) + #define LW(psrc) \ + ( { \ + uint8_t *psrc_lw_m = (uint8_t *) (psrc); \ + uint32_t val_m; \ + \ + asm volatile ( \ + "lw %[val_m], %[psrc_lw_m] \n\t" \ + \ + : [val_m] "=r" (val_m) \ + : [psrc_lw_m] "m" (*psrc_lw_m) \ + ); \ + \ + val_m; \ + } ) + + #define SH(val, pdst) \ + { \ + uint8_t *pdst_sh_m = (uint8_t *) (pdst); \ + uint16_t val_m = (val); \ + \ + asm volatile ( \ + "sh %[val_m], %[pdst_sh_m] \n\t" \ + \ + : [pdst_sh_m] "=m" (*pdst_sh_m) \ + : [val_m] "r" (val_m) \ + ); \ + } + + #define SW(val, pdst) \ + { \ + uint8_t *pdst_sw_m = (uint8_t *) (pdst); \ + uint32_t val_m = (val); \ + \ + asm volatile ( \ + "sw %[val_m], %[pdst_sw_m] \n\t" \ + \ + : [pdst_sw_m] "=m" (*pdst_sw_m) \ + : [val_m] "r" (val_m) \ + ); \ + } + + #if (__mips == 64) + #define SD(val, pdst) \ + { \ + uint8_t *pdst_sd_m = (uint8_t *) (pdst); \ + uint64_t val_m = (val); \ + \ + asm volatile ( \ + "sd %[val_m], %[pdst_sd_m] \n\t" \ + \ + : [pdst_sd_m] "=m" (*pdst_sd_m) \ + : [val_m] "r" (val_m) \ + ); \ + } + #else + #define SD(val, pdst) \ + { \ + uint8_t *pdst_sd_m = (uint8_t *) (pdst); \ + uint32_t val0_m, val1_m; \ + \ + val0_m = (uint32_t) ((val) & 0x00000000FFFFFFFF); \ + val1_m = (uint32_t) (((val) >> 32) & 0x00000000FFFFFFFF); \ + \ + SW(val0_m, pdst_sd_m); \ + SW(val1_m, pdst_sd_m + 4); \ + } + #endif +#else // !(__mips_isa_rev >= 6) + #define LW(psrc) \ + ( { \ + uint8_t *psrc_lw_m = (uint8_t *) (psrc); \ + uint32_t val_m; \ + \ + asm volatile ( \ + "ulw %[val_m], %[psrc_lw_m] \n\t" \ + \ + : [val_m] "=r" (val_m) \ + : [psrc_lw_m] "m" (*psrc_lw_m) \ + ); \ + \ + val_m; \ + } ) + + #define SH(val, pdst) \ + { \ + uint8_t *pdst_sh_m = (uint8_t *) (pdst); \ + uint16_t val_m = (val); \ + \ + asm volatile ( \ + "ush %[val_m], %[pdst_sh_m] \n\t" \ + \ + : [pdst_sh_m] "=m" (*pdst_sh_m) \ + : [val_m] "r" (val_m) \ + ); \ + } + + #define SW(val, pdst) \ + { \ + uint8_t *pdst_sw_m = (uint8_t *) (pdst); \ + uint32_t val_m = (val); \ + \ + asm volatile ( \ + "usw %[val_m], %[pdst_sw_m] \n\t" \ + \ + : [pdst_sw_m] "=m" (*pdst_sw_m) \ + : [val_m] "r" (val_m) \ + ); \ + } + + #define SD(val, pdst) \ + { \ + uint8_t *pdst_sd_m = (uint8_t *) (pdst); \ + uint32_t val0_m, val1_m; \ + \ + val0_m = (uint32_t) ((val) & 0x00000000FFFFFFFF); \ + val1_m = (uint32_t) (((val) >> 32) & 0x00000000FFFFFFFF); \ + \ + SW(val0_m, pdst_sd_m); \ + SW(val1_m, pdst_sd_m + 4); \ + } + + #define SW_ZERO(pdst) \ + { \ + uint8_t *pdst_m = (uint8_t *) (pdst); \ + \ + asm volatile ( \ + "usw $0, %[pdst_m] \n\t" \ + \ + : [pdst_m] "=m" (*pdst_m) \ + : \ + ); \ + } +#endif // (__mips_isa_rev >= 6) +#endif + +#define LD_B(RTYPE, psrc) *((RTYPE *) (psrc)) +#define LD_UB(...) LD_B(v16u8, __VA_ARGS__) +#define LD_B2(RTYPE, psrc, stride, out0, out1) \ +{ \ + out0 = LD_B(RTYPE, (psrc)); \ + out1 = LD_B(RTYPE, (psrc) + stride); \ +} +#define LD_UB2(...) LD_B2(v16u8, __VA_ARGS__) +#define LD_B4(RTYPE, psrc, stride, out0, out1, out2, out3) \ +{ \ + LD_B2(RTYPE, (psrc), stride, out0, out1); \ + LD_B2(RTYPE, (psrc) + 2 * stride , stride, out2, out3); \ +} +#define LD_UB4(...) LD_B4(v16u8, __VA_ARGS__) + +#define ST_B(RTYPE, in, pdst) *((RTYPE *) (pdst)) = (in) +#define ST_UB(...) ST_B(v16u8, __VA_ARGS__) +#define ST_B2(RTYPE, in0, in1, pdst, stride) \ +{ \ + ST_B(RTYPE, in0, (pdst)); \ + ST_B(RTYPE, in1, (pdst) + stride); \ +} +#define ST_UB2(...) ST_B2(v16u8, __VA_ARGS__) +#define ST_B4(RTYPE, in0, in1, in2, in3, pdst, stride) \ +{ \ + ST_B2(RTYPE, in0, in1, (pdst), stride); \ + ST_B2(RTYPE, in2, in3, (pdst) + 2 * stride, stride); \ +} +#define ST_UB4(...) ST_B4(v16u8, __VA_ARGS__) + +#define ADD2(in0, in1, in2, in3, out0, out1) \ +{ \ + out0 = in0 + in1; \ + out1 = in2 + in3; \ +} +#define ADD3(in0, in1, in2, in3, in4, in5, \ + out0, out1, out2) \ +{ \ + ADD2(in0, in1, in2, in3, out0, out1); \ + out2 = in4 + in5; \ +} +#define ADD4(in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3) \ +{ \ + ADD2(in0, in1, in2, in3, out0, out1); \ + ADD2(in4, in5, in6, in7, out2, out3); \ +} + +#define ILVR_B2(RTYPE, in0, in1, in2, in3, out0, out1) \ +{ \ + out0 = (RTYPE) __msa_ilvr_b((v16i8) in0, (v16i8) in1); \ + out1 = (RTYPE) __msa_ilvr_b((v16i8) in2, (v16i8) in3); \ +} +#define ILVR_B2_SH(...) ILVR_B2(v8i16, __VA_ARGS__) + +#define HSUB_UB2(RTYPE, in0, in1, out0, out1) \ +{ \ + out0 = (RTYPE) __msa_hsub_u_h((v16u8) in0, (v16u8) in0); \ + out1 = (RTYPE) __msa_hsub_u_h((v16u8) in1, (v16u8) in1); \ +} +#define HSUB_UB2_SH(...) HSUB_UB2(v8i16, __VA_ARGS__) + +#define SLDI_B2_0(RTYPE, in0, in1, out0, out1, slide_val) \ +{ \ + v16i8 zero_m = { 0 }; \ + out0 = (RTYPE) __msa_sldi_b((v16i8) zero_m, (v16i8) in0, slide_val); \ + out1 = (RTYPE) __msa_sldi_b((v16i8) zero_m, (v16i8) in1, slide_val); \ +} +#define SLDI_B2_0_UB(...) SLDI_B2_0(v16u8, __VA_ARGS__) + +#define SLDI_B3_0(RTYPE, in0, in1, in2, out0, out1, out2, slide_val) \ +{ \ + v16i8 zero_m = { 0 }; \ + SLDI_B2_0(RTYPE, in0, in1, out0, out1, slide_val); \ + out2 = (RTYPE) __msa_sldi_b((v16i8) zero_m, (v16i8) in2, slide_val); \ +} +#define SLDI_B3_0_UB(...) SLDI_B3_0(v16u8, __VA_ARGS__) + +#define ILVEV_W2(RTYPE, in0, in1, in2, in3, out0, out1) \ +{ \ + out0 = (RTYPE) __msa_ilvev_w((v4i32) in1, (v4i32) in0); \ + out1 = (RTYPE) __msa_ilvev_w((v4i32) in3, (v4i32) in2); \ +} +#define ILVEV_W2_UB(...) ILVEV_W2(v16u8, __VA_ARGS__) + +#define ADD_ABS_H3(RTYPE, in0, in1, in2, out0, out1, out2) \ +{ \ + RTYPE zero = {0}; \ + \ + out0 = __msa_add_a_h((v8i16) zero, in0); \ + out1 = __msa_add_a_h((v8i16) zero, in1); \ + out2 = __msa_add_a_h((v8i16) zero, in2); \ +} +#define ADD_ABS_H3_SH(...) ADD_ABS_H3(v8i16, __VA_ARGS__) + +#define VSHF_B2(RTYPE, in0, in1, in2, in3, mask0, mask1, out0, out1) \ +{ \ + out0 = (RTYPE) __msa_vshf_b((v16i8) mask0, (v16i8) in1, (v16i8) in0); \ + out1 = (RTYPE) __msa_vshf_b((v16i8) mask1, (v16i8) in3, (v16i8) in2); \ +} +#define VSHF_B2_UB(...) VSHF_B2(v16u8, __VA_ARGS__) + +#define CMP_AND_SELECT(inp0, inp1, inp2, inp3, inp4, inp5, out0) \ +{ \ + v8i16 _sel_h0, _sel_h1; \ + v16u8 _sel_b0, _sel_b1; \ + _sel_h0 = (v8i16) __msa_clt_u_h((v8u16) inp1, (v8u16) inp0); \ + _sel_b0 = (v16u8) __msa_pckev_b((v16i8) _sel_h0, (v16i8) _sel_h0); \ + inp0 = (v8i16) __msa_bmnz_v((v16u8) inp0, (v16u8) inp1, (v16u8) _sel_h0); \ + inp4 = (v16u8) __msa_bmnz_v(inp3, inp4, _sel_b0); \ + _sel_h1 = (v8i16) __msa_clt_u_h((v8u16) inp2, (v8u16) inp0); \ + _sel_b1 = (v16u8) __msa_pckev_b((v16i8) _sel_h1, (v16i8) _sel_h1); \ + inp4 = (v16u8) __msa_bmnz_v(inp4, inp5, _sel_b1); \ + out0 += inp4; \ +} + +void png_read_filter_row_up_msa(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i, cnt, cnt16, cnt32; + png_size_t istop = row_info->rowbytes; + png_bytep rp = row; + png_const_bytep pp = prev_row; + v16u8 src0, src1, src2, src3, src4, src5, src6, src7; + + for (i = 0; i < (istop >> 6); i++) + { + LD_UB4(rp, 16, src0, src1, src2, src3); + LD_UB4(pp, 16, src4, src5, src6, src7); + pp += 64; + + ADD4(src0, src4, src1, src5, src2, src6, src3, src7, + src0, src1, src2, src3); + + ST_UB4(src0, src1, src2, src3, rp, 16); + rp += 64; + } + + if (istop & 0x3F) + { + cnt32 = istop & 0x20; + cnt16 = istop & 0x10; + cnt = istop & 0xF; + + if(cnt32) + { + if (cnt16 && cnt) + { + LD_UB4(rp, 16, src0, src1, src2, src3); + LD_UB4(pp, 16, src4, src5, src6, src7); + + ADD4(src0, src4, src1, src5, src2, src6, src3, src7, + src0, src1, src2, src3); + + ST_UB4(src0, src1, src2, src3, rp, 16); + rp += 64; + } + else if (cnt16 || cnt) + { + LD_UB2(rp, 16, src0, src1); + LD_UB2(pp, 16, src4, src5); + pp += 32; + src2 = LD_UB(rp + 32); + src6 = LD_UB(pp); + + ADD3(src0, src4, src1, src5, src2, src6, src0, src1, src2); + + ST_UB2(src0, src1, rp, 16); + rp += 32; + ST_UB(src2, rp); + rp += 16; + } + else + { + LD_UB2(rp, 16, src0, src1); + LD_UB2(pp, 16, src4, src5); + + ADD2(src0, src4, src1, src5, src0, src1); + + ST_UB2(src0, src1, rp, 16); + rp += 32; + } + } + else if (cnt16 && cnt) + { + LD_UB2(rp, 16, src0, src1); + LD_UB2(pp, 16, src4, src5); + + ADD2(src0, src4, src1, src5, src0, src1); + + ST_UB2(src0, src1, rp, 16); + rp += 32; + } + else if (cnt16 || cnt) + { + src0 = LD_UB(rp); + src4 = LD_UB(pp); + pp += 16; + + src0 += src4; + + ST_UB(src0, rp); + rp += 16; + } + } +} + +void png_read_filter_row_sub4_msa(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t count; + png_size_t istop = row_info->rowbytes; + png_bytep src = row; + png_bytep nxt = row + 4; + int32_t inp0; + v16u8 src0, src1, src2, src3, src4; + v16u8 dst0, dst1; + v16u8 zero = { 0 }; + + istop -= 4; + + inp0 = LW(src); + src += 4; + src0 = (v16u8) __msa_insert_w((v4i32) zero, 0, inp0); + + for (count = 0; count < istop; count += 16) + { + src1 = LD_UB(src); + src += 16; + + src2 = (v16u8) __msa_sldi_b((v16i8) zero, (v16i8) src1, 4); + src3 = (v16u8) __msa_sldi_b((v16i8) zero, (v16i8) src1, 8); + src4 = (v16u8) __msa_sldi_b((v16i8) zero, (v16i8) src1, 12); + src1 += src0; + src2 += src1; + src3 += src2; + src4 += src3; + src0 = src4; + ILVEV_W2_UB(src1, src2, src3, src4, dst0, dst1); + dst0 = (v16u8) __msa_pckev_d((v2i64) dst1, (v2i64) dst0); + + ST_UB(dst0, nxt); + nxt += 16; + } +} + +void png_read_filter_row_sub3_msa(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t count; + png_size_t istop = row_info->rowbytes; + png_bytep src = row; + png_bytep nxt = row + 3; + int64_t out0; + int32_t inp0, out1; + v16u8 src0, src1, src2, src3, src4, dst0, dst1; + v16u8 zero = { 0 }; + v16i8 mask0 = { 0, 1, 2, 16, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + v16i8 mask1 = { 0, 1, 2, 3, 4, 5, 16, 17, 18, 19, 20, 21, 0, 0, 0, 0 }; + + istop -= 3; + + inp0 = LW(src); + src += 3; + src0 = (v16u8) __msa_insert_w((v4i32) zero, 0, inp0); + + for (count = 0; count < istop; count += 12) + { + src1 = LD_UB(src); + src += 12; + + src2 = (v16u8) __msa_sldi_b((v16i8) zero, (v16i8) src1, 3); + src3 = (v16u8) __msa_sldi_b((v16i8) zero, (v16i8) src1, 6); + src4 = (v16u8) __msa_sldi_b((v16i8) zero, (v16i8) src1, 9); + src1 += src0; + src2 += src1; + src3 += src2; + src4 += src3; + src0 = src4; + VSHF_B2_UB(src1, src2, src3, src4, mask0, mask0, dst0, dst1); + dst0 = (v16u8) __msa_vshf_b(mask1, (v16i8) dst1, (v16i8) dst0); + out0 = __msa_copy_s_d((v2i64) dst0, 0); + out1 = __msa_copy_s_w((v4i32) dst0, 2); + + SD(out0, nxt); + nxt += 8; + SW(out1, nxt); + nxt += 4; + } +} + +void png_read_filter_row_avg4_msa(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i; + png_bytep src = row; + png_bytep nxt = row; + png_const_bytep pp = prev_row; + png_size_t istop = row_info->rowbytes - 4; + int32_t inp0, inp1, out0; + v16u8 src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, dst0, dst1; + v16u8 zero = { 0 }; + + inp0 = LW(pp); + pp += 4; + inp1 = LW(src); + src += 4; + src0 = (v16u8) __msa_insert_w((v4i32) zero, 0, inp0); + src1 = (v16u8) __msa_insert_w((v4i32) zero, 0, inp1); + src0 = (v16u8) MSA_SRLI_B(src0, 1); + src1 += src0; + out0 = __msa_copy_s_w((v4i32) src1, 0); + SW(out0, nxt); + nxt += 4; + + for (i = 0; i < istop; i += 16) + { + src2 = LD_UB(pp); + pp += 16; + src6 = LD_UB(src); + src += 16; + + SLDI_B2_0_UB(src2, src6, src3, src7, 4); + SLDI_B2_0_UB(src2, src6, src4, src8, 8); + SLDI_B2_0_UB(src2, src6, src5, src9, 12); + src2 = __msa_ave_u_b(src2, src1); + src6 += src2; + src3 = __msa_ave_u_b(src3, src6); + src7 += src3; + src4 = __msa_ave_u_b(src4, src7); + src8 += src4; + src5 = __msa_ave_u_b(src5, src8); + src9 += src5; + src1 = src9; + ILVEV_W2_UB(src6, src7, src8, src9, dst0, dst1); + dst0 = (v16u8) __msa_pckev_d((v2i64) dst1, (v2i64) dst0); + + ST_UB(dst0, nxt); + nxt += 16; + } +} + +void png_read_filter_row_avg3_msa(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i; + png_bytep src = row; + png_bytep nxt = row; + png_const_bytep pp = prev_row; + png_size_t istop = row_info->rowbytes - 3; + int64_t out0; + int32_t inp0, inp1, out1; + int16_t out2; + v16u8 src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, dst0, dst1; + v16u8 zero = { 0 }; + v16i8 mask0 = { 0, 1, 2, 16, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + v16i8 mask1 = { 0, 1, 2, 3, 4, 5, 16, 17, 18, 19, 20, 21, 0, 0, 0, 0 }; + + inp0 = LW(pp); + pp += 3; + inp1 = LW(src); + src += 3; + src0 = (v16u8) __msa_insert_w((v4i32) zero, 0, inp0); + src1 = (v16u8) __msa_insert_w((v4i32) zero, 0, inp1); + src0 = (v16u8) MSA_SRLI_B(src0, 1); + src1 += src0; + out2 = __msa_copy_s_h((v8i16) src1, 0); + SH(out2, nxt); + nxt += 2; + nxt[0] = src1[2]; + nxt++; + + for (i = 0; i < istop; i += 12) + { + src2 = LD_UB(pp); + pp += 12; + src6 = LD_UB(src); + src += 12; + + SLDI_B2_0_UB(src2, src6, src3, src7, 3); + SLDI_B2_0_UB(src2, src6, src4, src8, 6); + SLDI_B2_0_UB(src2, src6, src5, src9, 9); + src2 = __msa_ave_u_b(src2, src1); + src6 += src2; + src3 = __msa_ave_u_b(src3, src6); + src7 += src3; + src4 = __msa_ave_u_b(src4, src7); + src8 += src4; + src5 = __msa_ave_u_b(src5, src8); + src9 += src5; + src1 = src9; + VSHF_B2_UB(src6, src7, src8, src9, mask0, mask0, dst0, dst1); + dst0 = (v16u8) __msa_vshf_b(mask1, (v16i8) dst1, (v16i8) dst0); + out0 = __msa_copy_s_d((v2i64) dst0, 0); + out1 = __msa_copy_s_w((v4i32) dst0, 2); + + SD(out0, nxt); + nxt += 8; + SW(out1, nxt); + nxt += 4; + } +} + +void png_read_filter_row_paeth4_msa(png_row_infop row_info, + png_bytep row, + png_const_bytep prev_row) +{ + int32_t count, rp_end; + png_bytep nxt; + png_const_bytep prev_nxt; + int32_t inp0, inp1, res0; + v16u8 src0, src1, src2, src3, src4, src5, src6, src7, src8, src9; + v16u8 src10, src11, src12, src13, dst0, dst1; + v8i16 vec0, vec1, vec2; + v16u8 zero = { 0 }; + + nxt = row; + prev_nxt = prev_row; + + inp0 = LW(nxt); + inp1 = LW(prev_nxt); + prev_nxt += 4; + src0 = (v16u8) __msa_insert_w((v4i32) zero, 0, inp0); + src1 = (v16u8) __msa_insert_w((v4i32) zero, 0, inp1); + + src1 += src0; + res0 = __msa_copy_s_w((v4i32) src1, 0); + + SW(res0, nxt); + nxt += 4; + + /* Remainder */ + rp_end = row_info->rowbytes - 4; + + for (count = 0; count < rp_end; count += 16) + { + src2 = LD_UB(prev_nxt); + prev_nxt += 16; + src6 = LD_UB(prev_row); + prev_row += 16; + src10 = LD_UB(nxt); + + SLDI_B3_0_UB(src2, src6, src10, src3, src7, src11, 4); + SLDI_B3_0_UB(src2, src6, src10, src4, src8, src12, 8); + SLDI_B3_0_UB(src2, src6, src10, src5, src9, src13, 12); + ILVR_B2_SH(src2, src6, src1, src6, vec0, vec1); + HSUB_UB2_SH(vec0, vec1, vec0, vec1); + vec2 = vec0 + vec1; + ADD_ABS_H3_SH(vec0, vec1, vec2, vec0, vec1, vec2); + CMP_AND_SELECT(vec0, vec1, vec2, src1, src2, src6, src10); + ILVR_B2_SH(src3, src7, src10, src7, vec0, vec1); + HSUB_UB2_SH(vec0, vec1, vec0, vec1); + vec2 = vec0 + vec1; + ADD_ABS_H3_SH(vec0, vec1, vec2, vec0, vec1, vec2); + CMP_AND_SELECT(vec0, vec1, vec2, src10, src3, src7, src11); + ILVR_B2_SH(src4, src8, src11, src8, vec0, vec1); + HSUB_UB2_SH(vec0, vec1, vec0, vec1); + vec2 = vec0 + vec1; + ADD_ABS_H3_SH(vec0, vec1, vec2, vec0, vec1, vec2); + CMP_AND_SELECT(vec0, vec1, vec2, src11, src4, src8, src12); + ILVR_B2_SH(src5, src9, src12, src9, vec0, vec1); + HSUB_UB2_SH(vec0, vec1, vec0, vec1); + vec2 = vec0 + vec1; + ADD_ABS_H3_SH(vec0, vec1, vec2, vec0, vec1, vec2); + CMP_AND_SELECT(vec0, vec1, vec2, src12, src5, src9, src13); + src1 = src13; + ILVEV_W2_UB(src10, src11, src12, src1, dst0, dst1); + dst0 = (v16u8) __msa_pckev_d((v2i64) dst1, (v2i64) dst0); + + ST_UB(dst0, nxt); + nxt += 16; + } +} + +void png_read_filter_row_paeth3_msa(png_row_infop row_info, + png_bytep row, + png_const_bytep prev_row) +{ + int32_t count, rp_end; + png_bytep nxt; + png_const_bytep prev_nxt; + int64_t out0; + int32_t inp0, inp1, out1; + int16_t out2; + v16u8 src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, dst0, dst1; + v16u8 src10, src11, src12, src13; + v8i16 vec0, vec1, vec2; + v16u8 zero = { 0 }; + v16i8 mask0 = { 0, 1, 2, 16, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + v16i8 mask1 = { 0, 1, 2, 3, 4, 5, 16, 17, 18, 19, 20, 21, 0, 0, 0, 0 }; + + nxt = row; + prev_nxt = prev_row; + + inp0 = LW(nxt); + inp1 = LW(prev_nxt); + prev_nxt += 3; + src0 = (v16u8) __msa_insert_w((v4i32) zero, 0, inp0); + src1 = (v16u8) __msa_insert_w((v4i32) zero, 0, inp1); + + src1 += src0; + out2 = __msa_copy_s_h((v8i16) src1, 0); + + SH(out2, nxt); + nxt += 2; + nxt[0] = src1[2]; + nxt++; + + /* Remainder */ + rp_end = row_info->rowbytes - 3; + + for (count = 0; count < rp_end; count += 12) + { + src2 = LD_UB(prev_nxt); + prev_nxt += 12; + src6 = LD_UB(prev_row); + prev_row += 12; + src10 = LD_UB(nxt); + + SLDI_B3_0_UB(src2, src6, src10, src3, src7, src11, 3); + SLDI_B3_0_UB(src2, src6, src10, src4, src8, src12, 6); + SLDI_B3_0_UB(src2, src6, src10, src5, src9, src13, 9); + ILVR_B2_SH(src2, src6, src1, src6, vec0, vec1); + HSUB_UB2_SH(vec0, vec1, vec0, vec1); + vec2 = vec0 + vec1; + ADD_ABS_H3_SH(vec0, vec1, vec2, vec0, vec1, vec2); + CMP_AND_SELECT(vec0, vec1, vec2, src1, src2, src6, src10); + ILVR_B2_SH(src3, src7, src10, src7, vec0, vec1); + HSUB_UB2_SH(vec0, vec1, vec0, vec1); + vec2 = vec0 + vec1; + ADD_ABS_H3_SH(vec0, vec1, vec2, vec0, vec1, vec2); + CMP_AND_SELECT(vec0, vec1, vec2, src10, src3, src7, src11); + ILVR_B2_SH(src4, src8, src11, src8, vec0, vec1); + HSUB_UB2_SH(vec0, vec1, vec0, vec1); + vec2 = vec0 + vec1; + ADD_ABS_H3_SH(vec0, vec1, vec2, vec0, vec1, vec2); + CMP_AND_SELECT(vec0, vec1, vec2, src11, src4, src8, src12); + ILVR_B2_SH(src5, src9, src12, src9, vec0, vec1); + HSUB_UB2_SH(vec0, vec1, vec0, vec1); + vec2 = vec0 + vec1; + ADD_ABS_H3_SH(vec0, vec1, vec2, vec0, vec1, vec2); + CMP_AND_SELECT(vec0, vec1, vec2, src12, src5, src9, src13); + src1 = src13; + VSHF_B2_UB(src10, src11, src12, src13, mask0, mask0, dst0, dst1); + dst0 = (v16u8) __msa_vshf_b(mask1, (v16i8) dst1, (v16i8) dst0); + out0 = __msa_copy_s_d((v2i64) dst0, 0); + out1 = __msa_copy_s_w((v4i32) dst0, 2); + + SD(out0, nxt); + nxt += 8; + SW(out1, nxt); + nxt += 4; + } +} + +#endif /* PNG_MIPS_MSA_OPT > 0 */ +#endif /* PNG_MIPS_MSA_IMPLEMENTATION == 1 (intrinsics) */ +#endif /* READ */ diff --git a/src/png/libpng/mips/mips_init.c b/src/png/libpng/mips/mips_init.c new file mode 100644 index 0000000000..0bfb7a32e9 --- /dev/null +++ b/src/png/libpng/mips/mips_init.c @@ -0,0 +1,129 @@ + +/* mips_init.c - MSA optimised filter functions + * + * Copyright (c) 2016 Glenn Randers-Pehrson + * Written by Mandar Sahastrabuddhe, 2016. + * Last changed in libpng 1.6.25 [September 1, 2016] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ +/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are + * called. + */ +#define _POSIX_SOURCE 1 + +#include +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#if PNG_MIPS_MSA_OPT > 0 +#ifdef PNG_MIPS_MSA_CHECK_SUPPORTED /* Do run-time checks */ +/* WARNING: it is strongly recommended that you do not build libpng with + * run-time checks for CPU features if at all possible. In the case of the MIPS + * MSA instructions there is no processor-specific way of detecting the + * presence of the required support, therefore run-time detection is extremely + * OS specific. + * + * You may set the macro PNG_MIPS_MSA_FILE to the file name of file containing + * a fragment of C source code which defines the png_have_msa function. There + * are a number of implementations in contrib/mips-msa, but the only one that + * has partial support is contrib/mips-msa/linux.c - a generic Linux + * implementation which reads /proc/cpufino. + */ +#ifndef PNG_MIPS_MSA_FILE +# ifdef __linux__ +# define PNG_MIPS_MSA_FILE "contrib/mips-msa/linux.c" +# endif +#endif + +#ifdef PNG_MIPS_MSA_FILE + +#include /* for sig_atomic_t */ +static int png_have_msa(png_structp png_ptr); +#include PNG_MIPS_MSA_FILE + +#else /* PNG_MIPS_MSA_FILE */ +# error "PNG_MIPS_MSA_FILE undefined: no support for run-time MIPS MSA checks" +#endif /* PNG_MIPS_MSA_FILE */ +#endif /* PNG_MIPS_MSA_CHECK_SUPPORTED */ + +#ifndef PNG_ALIGNED_MEMORY_SUPPORTED +# error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED" +#endif + +void +png_init_filter_functions_msa(png_structp pp, unsigned int bpp) +{ + /* The switch statement is compiled in for MIPS_MSA_API, the call to + * png_have_msa is compiled in for MIPS_MSA_CHECK. If both are defined + * the check is only performed if the API has not set the MSA option on + * or off explicitly. In this case the check controls what happens. + */ + +#ifdef PNG_MIPS_MSA_API_SUPPORTED + switch ((pp->options >> PNG_MIPS_MSA) & 3) + { + case PNG_OPTION_UNSET: + /* Allow the run-time check to execute if it has been enabled - + * thus both API and CHECK can be turned on. If it isn't supported + * this case will fall through to the 'default' below, which just + * returns. + */ +#endif /* PNG_MIPS_MSA_API_SUPPORTED */ +#ifdef PNG_MIPS_MSA_CHECK_SUPPORTED + { + static volatile sig_atomic_t no_msa = -1; /* not checked */ + + if (no_msa < 0) + no_msa = !png_have_msa(pp); + + if (no_msa) + return; + } +#ifdef PNG_MIPS_MSA_API_SUPPORTED + break; +#endif +#endif /* PNG_MIPS_MSA_CHECK_SUPPORTED */ + +#ifdef PNG_MIPS_MSA_API_SUPPORTED + default: /* OFF or INVALID */ + return; + + case PNG_OPTION_ON: + /* Option turned on */ + break; + } +#endif + + /* IMPORTANT: any new external functions used here must be declared using + * PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the + * 'prefix' option to configure works: + * + * ./configure --with-libpng-prefix=foobar_ + * + * Verify you have got this right by running the above command, doing a build + * and examining pngprefix.h; it must contain a #define for every external + * function you add. (Notice that this happens automatically for the + * initialization function.) + */ + pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_msa; + + if (bpp == 3) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_msa; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_msa; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth3_msa; + } + + else if (bpp == 4) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_msa; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_msa; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth4_msa; + } +} +#endif /* PNG_MIPS_MSA_OPT > 0 */ +#endif /* READ */ diff --git a/src/png/libpng/png.c b/src/png/libpng/png.c new file mode 100644 index 0000000000..ff02c56518 --- /dev/null +++ b/src/png/libpng/png.c @@ -0,0 +1,4614 @@ + +/* png.c - location for general purpose libpng functions + * + * Last changed in libpng 1.6.33 [September 28, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef png_libpng_version_1_6_34 Your_png_h_is_not_version_1_6_34; + +#ifdef __GNUC__ +/* The version tests may need to be added to, but the problem warning has + * consistently been fixed in GCC versions which obtain wide-spread release. + * The problem is that many versions of GCC rearrange comparison expressions in + * the optimizer in such a way that the results of the comparison will change + * if signed integer overflow occurs. Such comparisons are not permitted in + * ANSI C90, however GCC isn't clever enough to work out that that do not occur + * below in png_ascii_from_fp and png_muldiv, so it produces a warning with + * -Wextra. Unfortunately this is highly dependent on the optimizer and the + * machine architecture so the warning comes and goes unpredictably and is + * impossible to "fix", even were that a good idea. + */ +#if __GNUC__ == 7 && __GNUC_MINOR__ == 1 +#define GCC_STRICT_OVERFLOW 1 +#endif /* GNU 7.1.x */ +#endif /* GNU */ +#ifndef GCC_STRICT_OVERFLOW +#define GCC_STRICT_OVERFLOW 0 +#endif + +/* Tells libpng that we have already handled the first "num_bytes" bytes + * of the PNG file signature. If the PNG data is embedded into another + * stream we can set num_bytes = 8 so that libpng will not attempt to read + * or write any of the magic bytes before it starts on the IHDR. + */ + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_set_sig_bytes(png_structrp png_ptr, int num_bytes) +{ + unsigned int nb = (unsigned int)num_bytes; + + png_debug(1, "in png_set_sig_bytes"); + + if (png_ptr == NULL) + return; + + if (num_bytes < 0) + nb = 0; + + if (nb > 8) + png_error(png_ptr, "Too many bytes for PNG signature"); + + png_ptr->sig_bytes = (png_byte)nb; +} + +/* Checks whether the supplied bytes match the PNG signature. We allow + * checking less than the full 8-byte signature so that those apps that + * already read the first few bytes of a file to determine the file type + * can simply check the remaining bytes for extra assurance. Returns + * an integer less than, equal to, or greater than zero if sig is found, + * respectively, to be less than, to match, or be greater than the correct + * PNG signature (this is the same behavior as strcmp, memcmp, etc). + */ +int PNGAPI +png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + + if (num_to_check > 8) + num_to_check = 8; + + else if (num_to_check < 1) + return (-1); + + if (start > 7) + return (-1); + + if (start + num_to_check > 8) + num_to_check = 8 - start; + + return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check))); +} + +#endif /* READ */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Function to allocate memory for zlib */ +PNG_FUNCTION(voidpf /* PRIVATE */, +png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED) +{ + png_alloc_size_t num_bytes = size; + + if (png_ptr == NULL) + return NULL; + + if (items >= (~(png_alloc_size_t)0)/size) + { + png_warning (png_voidcast(png_structrp, png_ptr), + "Potential overflow in png_zalloc()"); + return NULL; + } + + num_bytes *= items; + return png_malloc_warn(png_voidcast(png_structrp, png_ptr), num_bytes); +} + +/* Function to free memory for zlib */ +void /* PRIVATE */ +png_zfree(voidpf png_ptr, voidpf ptr) +{ + png_free(png_voidcast(png_const_structrp,png_ptr), ptr); +} + +/* Reset the CRC variable to 32 bits of 1's. Care must be taken + * in case CRC is > 32 bits to leave the top bits 0. + */ +void /* PRIVATE */ +png_reset_crc(png_structrp png_ptr) +{ + /* The cast is safe because the crc is a 32-bit value. */ + png_ptr->crc = (png_uint_32)crc32(0, Z_NULL, 0); +} + +/* Calculate the CRC over a section of data. We can only pass as + * much data to this routine as the largest single buffer size. We + * also check that this data will actually be used before going to the + * trouble of calculating it. + */ +void /* PRIVATE */ +png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, png_size_t length) +{ + int need_crc = 1; + + if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0) + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + + else /* critical */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0) + need_crc = 0; + } + + /* 'uLong' is defined in zlib.h as unsigned long; this means that on some + * systems it is a 64-bit value. crc32, however, returns 32 bits so the + * following cast is safe. 'uInt' may be no more than 16 bits, so it is + * necessary to perform a loop here. + */ + if (need_crc != 0 && length > 0) + { + uLong crc = png_ptr->crc; /* Should never issue a warning */ + + do + { + uInt safe_length = (uInt)length; +#ifndef __COVERITY__ + if (safe_length == 0) + safe_length = (uInt)-1; /* evil, but safe */ +#endif + + crc = crc32(crc, ptr, safe_length); + + /* The following should never issue compiler warnings; if they do the + * target system has characteristics that will probably violate other + * assumptions within the libpng code. + */ + ptr += safe_length; + length -= safe_length; + } + while (length > 0); + + /* And the following is always safe because the crc is only 32 bits. */ + png_ptr->crc = (png_uint_32)crc; + } +} + +/* Check a user supplied version number, called from both read and write + * functions that create a png_struct. + */ +int +png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver) +{ + /* Libpng versions 1.0.0 and later are binary compatible if the version + * string matches through the second '.'; we must recompile any + * applications that use any older library version. + */ + + if (user_png_ver != NULL) + { + int i = -1; + int found_dots = 0; + + do + { + i++; + if (user_png_ver[i] != PNG_LIBPNG_VER_STRING[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + if (user_png_ver[i] == '.') + found_dots++; + } while (found_dots < 2 && user_png_ver[i] != 0 && + PNG_LIBPNG_VER_STRING[i] != 0); + } + + else + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + + if ((png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) != 0) + { +#ifdef PNG_WARNINGS_SUPPORTED + size_t pos = 0; + char m[128]; + + pos = png_safecat(m, (sizeof m), pos, + "Application built with libpng-"); + pos = png_safecat(m, (sizeof m), pos, user_png_ver); + pos = png_safecat(m, (sizeof m), pos, " but running with "); + pos = png_safecat(m, (sizeof m), pos, PNG_LIBPNG_VER_STRING); + PNG_UNUSED(pos) + + png_warning(png_ptr, m); +#endif + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + + return 0; + } + + /* Success return. */ + return 1; +} + +/* Generic function to create a png_struct for either read or write - this + * contains the common initialization. + */ +PNG_FUNCTION(png_structp /* PRIVATE */, +png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) +{ + png_struct create_struct; +# ifdef PNG_SETJMP_SUPPORTED + jmp_buf create_jmp_buf; +# endif + + /* This temporary stack-allocated structure is used to provide a place to + * build enough context to allow the user provided memory allocator (if any) + * to be called. + */ + memset(&create_struct, 0, (sizeof create_struct)); + + /* Added at libpng-1.2.6 */ +# ifdef PNG_USER_LIMITS_SUPPORTED + create_struct.user_width_max = PNG_USER_WIDTH_MAX; + create_struct.user_height_max = PNG_USER_HEIGHT_MAX; + +# ifdef PNG_USER_CHUNK_CACHE_MAX + /* Added at libpng-1.2.43 and 1.4.0 */ + create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX; +# endif + +# ifdef PNG_USER_CHUNK_MALLOC_MAX + /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists + * in png_struct regardless. + */ + create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX; +# endif +# endif + + /* The following two API calls simply set fields in png_struct, so it is safe + * to do them now even though error handling is not yet set up. + */ +# ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(&create_struct, mem_ptr, malloc_fn, free_fn); +# else + PNG_UNUSED(mem_ptr) + PNG_UNUSED(malloc_fn) + PNG_UNUSED(free_fn) +# endif + + /* (*error_fn) can return control to the caller after the error_ptr is set, + * this will result in a memory leak unless the error_fn does something + * extremely sophisticated. The design lacks merit but is implicit in the + * API. + */ + png_set_error_fn(&create_struct, error_ptr, error_fn, warn_fn); + +# ifdef PNG_SETJMP_SUPPORTED + if (!setjmp(create_jmp_buf)) +# endif + { +# ifdef PNG_SETJMP_SUPPORTED + /* Temporarily fake out the longjmp information until we have + * successfully completed this function. This only works if we have + * setjmp() support compiled in, but it is safe - this stuff should + * never happen. + */ + create_struct.jmp_buf_ptr = &create_jmp_buf; + create_struct.jmp_buf_size = 0; /*stack allocation*/ + create_struct.longjmp_fn = longjmp; +# endif + /* Call the general version checker (shared with read and write code): + */ + if (png_user_version_check(&create_struct, user_png_ver) != 0) + { + png_structrp png_ptr = png_voidcast(png_structrp, + png_malloc_warn(&create_struct, (sizeof *png_ptr))); + + if (png_ptr != NULL) + { + /* png_ptr->zstream holds a back-pointer to the png_struct, so + * this can only be done now: + */ + create_struct.zstream.zalloc = png_zalloc; + create_struct.zstream.zfree = png_zfree; + create_struct.zstream.opaque = png_ptr; + +# ifdef PNG_SETJMP_SUPPORTED + /* Eliminate the local error handling: */ + create_struct.jmp_buf_ptr = NULL; + create_struct.jmp_buf_size = 0; + create_struct.longjmp_fn = 0; +# endif + + *png_ptr = create_struct; + + /* This is the successful return point */ + return png_ptr; + } + } + } + + /* A longjmp because of a bug in the application storage allocator or a + * simple failure to allocate the png_struct. + */ + return NULL; +} + +/* Allocate the memory for an info_struct for the application. */ +PNG_FUNCTION(png_infop,PNGAPI +png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED) +{ + png_inforp info_ptr; + + png_debug(1, "in png_create_info_struct"); + + if (png_ptr == NULL) + return NULL; + + /* Use the internal API that does not (or at least should not) error out, so + * that this call always returns ok. The application typically sets up the + * error handling *after* creating the info_struct because this is the way it + * has always been done in 'example.c'. + */ + info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr, + (sizeof *info_ptr))); + + if (info_ptr != NULL) + memset(info_ptr, 0, (sizeof *info_ptr)); + + return info_ptr; +} + +/* This function frees the memory associated with a single info struct. + * Normally, one would use either png_destroy_read_struct() or + * png_destroy_write_struct() to free an info struct, but this may be + * useful for some applications. From libpng 1.6.0 this function is also used + * internally to implement the png_info release part of the 'struct' destroy + * APIs. This ensures that all possible approaches free the same data (all of + * it). + */ +void PNGAPI +png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr) +{ + png_inforp info_ptr = NULL; + + png_debug(1, "in png_destroy_info_struct"); + + if (png_ptr == NULL) + return; + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + /* Do this first in case of an error below; if the app implements its own + * memory management this can lead to png_free calling png_error, which + * will abort this routine and return control to the app error handler. + * An infinite loop may result if it then tries to free the same info + * ptr. + */ + *info_ptr_ptr = NULL; + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + memset(info_ptr, 0, (sizeof *info_ptr)); + png_free(png_ptr, info_ptr); + } +} + +/* Initialize the info structure. This is now an internal function (0.89) + * and applications using it are urged to use png_create_info_struct() + * instead. Use deprecated in 1.6.0, internal use removed (used internally it + * is just a memset). + * + * NOTE: it is almost inconceivable that this API is used because it bypasses + * the user-memory mechanism and the user error handling/warning mechanisms in + * those cases where it does anything other than a memset. + */ +PNG_FUNCTION(void,PNGAPI +png_info_init_3,(png_infopp ptr_ptr, png_size_t png_info_struct_size), + PNG_DEPRECATED) +{ + png_inforp info_ptr = *ptr_ptr; + + png_debug(1, "in png_info_init_3"); + + if (info_ptr == NULL) + return; + + if ((sizeof (png_info)) > png_info_struct_size) + { + *ptr_ptr = NULL; + /* The following line is why this API should not be used: */ + free(info_ptr); + info_ptr = png_voidcast(png_inforp, png_malloc_base(NULL, + (sizeof *info_ptr))); + if (info_ptr == NULL) + return; + *ptr_ptr = info_ptr; + } + + /* Set everything to 0 */ + memset(info_ptr, 0, (sizeof *info_ptr)); +} + +/* The following API is not called internally */ +void PNGAPI +png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr, + int freer, png_uint_32 mask) +{ + png_debug(1, "in png_data_freer"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (freer == PNG_DESTROY_WILL_FREE_DATA) + info_ptr->free_me |= mask; + + else if (freer == PNG_USER_WILL_FREE_DATA) + info_ptr->free_me &= ~mask; + + else + png_error(png_ptr, "Unknown freer parameter in png_data_freer"); +} + +void PNGAPI +png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, + int num) +{ + png_debug(1, "in png_free_data"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + +#ifdef PNG_TEXT_SUPPORTED + /* Free text item num or (if num == -1) all text items */ + if (info_ptr->text != NULL && + ((mask & PNG_FREE_TEXT) & info_ptr->free_me) != 0) + { + if (num != -1) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + + else + { + int i; + + for (i = 0; i < info_ptr->num_text; i++) + png_free(png_ptr, info_ptr->text[i].key); + + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text = 0; + info_ptr->max_text = 0; + } + } +#endif + +#ifdef PNG_tRNS_SUPPORTED + /* Free any tRNS entry */ + if (((mask & PNG_FREE_TRNS) & info_ptr->free_me) != 0) + { + info_ptr->valid &= ~PNG_INFO_tRNS; + png_free(png_ptr, info_ptr->trans_alpha); + info_ptr->trans_alpha = NULL; + info_ptr->num_trans = 0; + } +#endif + +#ifdef PNG_sCAL_SUPPORTED + /* Free any sCAL entry */ + if (((mask & PNG_FREE_SCAL) & info_ptr->free_me) != 0) + { + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; + info_ptr->valid &= ~PNG_INFO_sCAL; + } +#endif + +#ifdef PNG_pCAL_SUPPORTED + /* Free any pCAL entry */ + if (((mask & PNG_FREE_PCAL) & info_ptr->free_me) != 0) + { + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + + if (info_ptr->pcal_params != NULL) + { + int i; + + for (i = 0; i < info_ptr->pcal_nparams; i++) + png_free(png_ptr, info_ptr->pcal_params[i]); + + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; + } +#endif + +#ifdef PNG_iCCP_SUPPORTED + /* Free any profile entry */ + if (((mask & PNG_FREE_ICCP) & info_ptr->free_me) != 0) + { + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; + } +#endif + +#ifdef PNG_sPLT_SUPPORTED + /* Free a given sPLT entry, or (if num == -1) all sPLT entries */ + if (info_ptr->splt_palettes != NULL && + ((mask & PNG_FREE_SPLT) & info_ptr->free_me) != 0) + { + if (num != -1) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + + else + { + int i; + + for (i = 0; i < info_ptr->splt_palettes_num; i++) + { + png_free(png_ptr, info_ptr->splt_palettes[i].name); + png_free(png_ptr, info_ptr->splt_palettes[i].entries); + } + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + info_ptr->valid &= ~PNG_INFO_sPLT; + } + } +#endif + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks != NULL && + ((mask & PNG_FREE_UNKN) & info_ptr->free_me) != 0) + { + if (num != -1) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + + else + { + int i; + + for (i = 0; i < info_ptr->unknown_chunks_num; i++) + png_free(png_ptr, info_ptr->unknown_chunks[i].data); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } +#endif + +#ifdef PNG_eXIf_SUPPORTED + /* Free any eXIf entry */ + if (((mask & PNG_FREE_EXIF) & info_ptr->free_me) != 0) + { +# ifdef PNG_READ_eXIf_SUPPORTED + if (info_ptr->eXIf_buf) + { + png_free(png_ptr, info_ptr->eXIf_buf); + info_ptr->eXIf_buf = NULL; + } +# endif + if (info_ptr->exif) + { + png_free(png_ptr, info_ptr->exif); + info_ptr->exif = NULL; + } + info_ptr->valid &= ~PNG_INFO_eXIf; + } +#endif + +#ifdef PNG_hIST_SUPPORTED + /* Free any hIST entry */ + if (((mask & PNG_FREE_HIST) & info_ptr->free_me) != 0) + { + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; + } +#endif + + /* Free any PLTE entry that was internally allocated */ + if (((mask & PNG_FREE_PLTE) & info_ptr->free_me) != 0) + { + png_free(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; + info_ptr->num_palette = 0; + } + +#ifdef PNG_INFO_IMAGE_SUPPORTED + /* Free any image bits attached to the info structure */ + if (((mask & PNG_FREE_ROWS) & info_ptr->free_me) != 0) + { + if (info_ptr->row_pointers != NULL) + { + png_uint_32 row; + for (row = 0; row < info_ptr->height; row++) + png_free(png_ptr, info_ptr->row_pointers[row]); + + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers = NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; + } +#endif + + if (num != -1) + mask &= ~PNG_FREE_MUL; + + info_ptr->free_me &= ~mask; +} +#endif /* READ || WRITE */ + +/* This function returns a pointer to the io_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy() or png_read_destroy() are called. + */ +png_voidp PNGAPI +png_get_io_ptr(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + + return (png_ptr->io_ptr); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +# ifdef PNG_STDIO_SUPPORTED +/* Initialize the default input/output functions for the PNG file. If you + * use your own read or write routines, you can call either png_set_read_fn() + * or png_set_write_fn() instead of png_init_io(). If you have defined + * PNG_NO_STDIO or otherwise disabled PNG_STDIO_SUPPORTED, you must use a + * function of your own because "FILE *" isn't necessarily available. + */ +void PNGAPI +png_init_io(png_structrp png_ptr, png_FILE_p fp) +{ + png_debug(1, "in png_init_io"); + + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = (png_voidp)fp; +} +# endif + +# ifdef PNG_SAVE_INT_32_SUPPORTED +/* PNG signed integers are saved in 32-bit 2's complement format. ANSI C-90 + * defines a cast of a signed integer to an unsigned integer either to preserve + * the value, if it is positive, or to calculate: + * + * (UNSIGNED_MAX+1) + integer + * + * Where UNSIGNED_MAX is the appropriate maximum unsigned value, so when the + * negative integral value is added the result will be an unsigned value + * correspnding to the 2's complement representation. + */ +void PNGAPI +png_save_int_32(png_bytep buf, png_int_32 i) +{ + png_save_uint_32(buf, (png_uint_32)i); +} +# endif + +# ifdef PNG_TIME_RFC1123_SUPPORTED +/* Convert the supplied time into an RFC 1123 string suitable for use in + * a "Creation Time" or other text-based time string. + */ +int PNGAPI +png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime) +{ + static PNG_CONST char short_months[12][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (out == NULL) + return 0; + + if (ptime->year > 9999 /* RFC1123 limitation */ || + ptime->month == 0 || ptime->month > 12 || + ptime->day == 0 || ptime->day > 31 || + ptime->hour > 23 || ptime->minute > 59 || + ptime->second > 60) + return 0; + + { + size_t pos = 0; + char number_buf[5]; /* enough for a four-digit year */ + +# define APPEND_STRING(string) pos = png_safecat(out, 29, pos, (string)) +# define APPEND_NUMBER(format, value)\ + APPEND_STRING(PNG_FORMAT_NUMBER(number_buf, format, (value))) +# define APPEND(ch) if (pos < 28) out[pos++] = (ch) + + APPEND_NUMBER(PNG_NUMBER_FORMAT_u, (unsigned)ptime->day); + APPEND(' '); + APPEND_STRING(short_months[(ptime->month - 1)]); + APPEND(' '); + APPEND_NUMBER(PNG_NUMBER_FORMAT_u, ptime->year); + APPEND(' '); + APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->hour); + APPEND(':'); + APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->minute); + APPEND(':'); + APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->second); + APPEND_STRING(" +0000"); /* This reliably terminates the buffer */ + PNG_UNUSED (pos) + +# undef APPEND +# undef APPEND_NUMBER +# undef APPEND_STRING + } + + return 1; +} + +# if PNG_LIBPNG_VER < 10700 +/* To do: remove the following from libpng-1.7 */ +/* Original API that uses a private buffer in png_struct. + * Deprecated because it causes png_struct to carry a spurious temporary + * buffer (png_struct::time_buffer), better to have the caller pass this in. + */ +png_const_charp PNGAPI +png_convert_to_rfc1123(png_structrp png_ptr, png_const_timep ptime) +{ + if (png_ptr != NULL) + { + /* The only failure above if png_ptr != NULL is from an invalid ptime */ + if (png_convert_to_rfc1123_buffer(png_ptr->time_buffer, ptime) == 0) + png_warning(png_ptr, "Ignoring invalid time value"); + + else + return png_ptr->time_buffer; + } + + return NULL; +} +# endif /* LIBPNG_VER < 10700 */ +# endif /* TIME_RFC1123 */ + +#endif /* READ || WRITE */ + +png_const_charp PNGAPI +png_get_copyright(png_const_structrp png_ptr) +{ + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ +#ifdef PNG_STRING_COPYRIGHT + return PNG_STRING_COPYRIGHT +#else +# ifdef __STDC__ + return PNG_STRING_NEWLINE \ + "libpng version 1.6.34 - September 29, 2017" PNG_STRING_NEWLINE \ + "Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson" \ + PNG_STRING_NEWLINE \ + "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ + "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ + PNG_STRING_NEWLINE; +# else + return "libpng version 1.6.34 - September 29, 2017\ + Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson\ + Copyright (c) 1996-1997 Andreas Dilger\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; +# endif +#endif +} + +/* The following return the library version as a short string in the + * format 1.0.0 through 99.99.99zz. To get the version of *.h files + * used with your application, print out PNG_LIBPNG_VER_STRING, which + * is defined in png.h. + * Note: now there is no difference between png_get_libpng_ver() and + * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, + * it is guaranteed that png.c uses the correct version of png.h. + */ +png_const_charp PNGAPI +png_get_libpng_ver(png_const_structrp png_ptr) +{ + /* Version of *.c files used when building libpng */ + return png_get_header_ver(png_ptr); +} + +png_const_charp PNGAPI +png_get_header_ver(png_const_structrp png_ptr) +{ + /* Version of *.h files used when building libpng */ + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ + return PNG_LIBPNG_VER_STRING; +} + +png_const_charp PNGAPI +png_get_header_version(png_const_structrp png_ptr) +{ + /* Returns longer string containing both version and date */ + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ +#ifdef __STDC__ + return PNG_HEADER_VERSION_STRING +# ifndef PNG_READ_SUPPORTED + " (NO READ SUPPORT)" +# endif + PNG_STRING_NEWLINE; +#else + return PNG_HEADER_VERSION_STRING; +#endif +} + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +/* NOTE: this routine is not used internally! */ +/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth + * large of png_color. This lets grayscale images be treated as + * paletted. Most useful for gamma correction and simplification + * of code. This API is not used internally. + */ +void PNGAPI +png_build_grayscale_palette(int bit_depth, png_colorp palette) +{ + int num_palette; + int color_inc; + int i; + int v; + + png_debug(1, "in png_do_build_grayscale_palette"); + + if (palette == NULL) + return; + + switch (bit_depth) + { + case 1: + num_palette = 2; + color_inc = 0xff; + break; + + case 2: + num_palette = 4; + color_inc = 0x55; + break; + + case 4: + num_palette = 16; + color_inc = 0x11; + break; + + case 8: + num_palette = 256; + color_inc = 1; + break; + + default: + num_palette = 0; + color_inc = 0; + break; + } + + for (i = 0, v = 0; i < num_palette; i++, v += color_inc) + { + palette[i].red = (png_byte)(v & 0xff); + palette[i].green = (png_byte)(v & 0xff); + palette[i].blue = (png_byte)(v & 0xff); + } +} +#endif + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +int PNGAPI +png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name) +{ + /* Check chunk_name and return "keep" value if it's on the list, else 0 */ + png_const_bytep p, p_end; + + if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list == 0) + return PNG_HANDLE_CHUNK_AS_DEFAULT; + + p_end = png_ptr->chunk_list; + p = p_end + png_ptr->num_chunk_list*5; /* beyond end */ + + /* The code is the fifth byte after each four byte string. Historically this + * code was always searched from the end of the list, this is no longer + * necessary because the 'set' routine handles duplicate entries correcty. + */ + do /* num_chunk_list > 0, so at least one */ + { + p -= 5; + + if (memcmp(chunk_name, p, 4) == 0) + return p[4]; + } + while (p > p_end); + + /* This means that known chunks should be processed and unknown chunks should + * be handled according to the value of png_ptr->unknown_default; this can be + * confusing because, as a result, there are two levels of defaulting for + * unknown chunks. + */ + return PNG_HANDLE_CHUNK_AS_DEFAULT; +} + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED) +int /* PRIVATE */ +png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name) +{ + png_byte chunk_string[5]; + + PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name); + return png_handle_as_unknown(png_ptr, chunk_string); +} +#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */ +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_READ_SUPPORTED +/* This function, added to libpng-1.0.6g, is untested. */ +int PNGAPI +png_reset_zstream(png_structrp png_ptr) +{ + if (png_ptr == NULL) + return Z_STREAM_ERROR; + + /* WARNING: this resets the window bits to the maximum! */ + return (inflateReset(&png_ptr->zstream)); +} +#endif /* READ */ + +/* This function was added to libpng-1.0.7 */ +png_uint_32 PNGAPI +png_access_version_number(void) +{ + /* Version of *.c files used when building libpng */ + return((png_uint_32)PNG_LIBPNG_VER); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Ensure that png_ptr->zstream.msg holds some appropriate error message string. + * If it doesn't 'ret' is used to set it to something appropriate, even in cases + * like Z_OK or Z_STREAM_END where the error code is apparently a success code. + */ +void /* PRIVATE */ +png_zstream_error(png_structrp png_ptr, int ret) +{ + /* Translate 'ret' into an appropriate error string, priority is given to the + * one in zstream if set. This always returns a string, even in cases like + * Z_OK or Z_STREAM_END where the error code is a success code. + */ + if (png_ptr->zstream.msg == NULL) switch (ret) + { + default: + case Z_OK: + png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code"); + break; + + case Z_STREAM_END: + /* Normal exit */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream"); + break; + + case Z_NEED_DICT: + /* This means the deflate stream did not have a dictionary; this + * indicates a bogus PNG. + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary"); + break; + + case Z_ERRNO: + /* gz APIs only: should not happen */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error"); + break; + + case Z_STREAM_ERROR: + /* internal libpng error */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib"); + break; + + case Z_DATA_ERROR: + png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream"); + break; + + case Z_MEM_ERROR: + png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory"); + break; + + case Z_BUF_ERROR: + /* End of input or output; not a problem if the caller is doing + * incremental read or write. + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated"); + break; + + case Z_VERSION_ERROR: + png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version"); + break; + + case PNG_UNEXPECTED_ZLIB_RETURN: + /* Compile errors here mean that zlib now uses the value co-opted in + * pngpriv.h for PNG_UNEXPECTED_ZLIB_RETURN; update the switch above + * and change pngpriv.h. Note that this message is "... return", + * whereas the default/Z_OK one is "... return code". + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return"); + break; + } +} + +/* png_convert_size: a PNGAPI but no longer in png.h, so deleted + * at libpng 1.5.5! + */ + +/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ +#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */ +static int +png_colorspace_check_gamma(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA, int from) + /* This is called to check a new gamma value against an existing one. The + * routine returns false if the new gamma value should not be written. + * + * 'from' says where the new gamma value comes from: + * + * 0: the new gamma value is the libpng estimate for an ICC profile + * 1: the new gamma value comes from a gAMA chunk + * 2: the new gamma value comes from an sRGB chunk + */ +{ + png_fixed_point gtest; + + if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && + (png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) == 0 || + png_gamma_significant(gtest) != 0)) + { + /* Either this is an sRGB image, in which case the calculated gamma + * approximation should match, or this is an image with a profile and the + * value libpng calculates for the gamma of the profile does not match the + * value recorded in the file. The former, sRGB, case is an error, the + * latter is just a warning. + */ + if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2) + { + png_chunk_report(png_ptr, "gamma value does not match sRGB", + PNG_CHUNK_ERROR); + /* Do not overwrite an sRGB value */ + return from == 2; + } + + else /* sRGB tag not involved */ + { + png_chunk_report(png_ptr, "gamma value does not match libpng estimate", + PNG_CHUNK_WARNING); + return from == 1; + } + } + + return 1; +} + +void /* PRIVATE */ +png_colorspace_set_gamma(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA) +{ + /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't + * occur. Since the fixed point representation is asymetrical it is + * possible for 1/gamma to overflow the limit of 21474 and this means the + * gamma value must be at least 5/100000 and hence at most 20000.0. For + * safety the limits here are a little narrower. The values are 0.00016 to + * 6250.0, which are truly ridiculous gamma values (and will produce + * displays that are all black or all white.) + * + * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk + * handling code, which only required the value to be >0. + */ + png_const_charp errmsg; + + if (gAMA < 16 || gAMA > 625000000) + errmsg = "gamma value out of range"; + +# ifdef PNG_READ_gAMA_SUPPORTED + /* Allow the application to set the gamma value more than once */ + else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0) + errmsg = "duplicate"; +# endif + + /* Do nothing if the colorspace is already invalid */ + else if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) + return; + + else + { + if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA, + 1/*from gAMA*/) != 0) + { + /* Store this gamma value. */ + colorspace->gamma = gAMA; + colorspace->flags |= + (PNG_COLORSPACE_HAVE_GAMMA | PNG_COLORSPACE_FROM_gAMA); + } + + /* At present if the check_gamma test fails the gamma of the colorspace is + * not updated however the colorspace is not invalidated. This + * corresponds to the case where the existing gamma comes from an sRGB + * chunk or profile. An error message has already been output. + */ + return; + } + + /* Error exit - errmsg has been set. */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR); +} + +void /* PRIVATE */ +png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr) +{ + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) + { + /* Everything is invalid */ + info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB| + PNG_INFO_iCCP); + +# ifdef PNG_COLORSPACE_SUPPORTED + /* Clean up the iCCP profile now if it won't be used. */ + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, -1/*not used*/); +# else + PNG_UNUSED(png_ptr) +# endif + } + + else + { +# ifdef PNG_COLORSPACE_SUPPORTED + /* Leave the INFO_iCCP flag set if the pngset.c code has already set + * it; this allows a PNG to contain a profile which matches sRGB and + * yet still have that profile retrievable by the application. + */ + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) != 0) + info_ptr->valid |= PNG_INFO_sRGB; + + else + info_ptr->valid &= ~PNG_INFO_sRGB; + + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + info_ptr->valid |= PNG_INFO_cHRM; + + else + info_ptr->valid &= ~PNG_INFO_cHRM; +# endif + + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0) + info_ptr->valid |= PNG_INFO_gAMA; + + else + info_ptr->valid &= ~PNG_INFO_gAMA; + } +} + +#ifdef PNG_READ_SUPPORTED +void /* PRIVATE */ +png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr) +{ + if (info_ptr == NULL) /* reduce code size; check here not in the caller */ + return; + + info_ptr->colorspace = png_ptr->colorspace; + png_colorspace_sync_info(png_ptr, info_ptr); +} +#endif +#endif /* GAMMA */ + +#ifdef PNG_COLORSPACE_SUPPORTED +/* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for + * cHRM, as opposed to using chromaticities. These internal APIs return + * non-zero on a parameter error. The X, Y and Z values are required to be + * positive and less than 1.0. + */ +static int +png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ) +{ + png_int_32 d, dwhite, whiteX, whiteY; + + d = XYZ->red_X + XYZ->red_Y + XYZ->red_Z; + if (png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d) == 0) + return 1; + if (png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d) == 0) + return 1; + dwhite = d; + whiteX = XYZ->red_X; + whiteY = XYZ->red_Y; + + d = XYZ->green_X + XYZ->green_Y + XYZ->green_Z; + if (png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d) == 0) + return 1; + if (png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d) == 0) + return 1; + dwhite += d; + whiteX += XYZ->green_X; + whiteY += XYZ->green_Y; + + d = XYZ->blue_X + XYZ->blue_Y + XYZ->blue_Z; + if (png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d) == 0) + return 1; + if (png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d) == 0) + return 1; + dwhite += d; + whiteX += XYZ->blue_X; + whiteY += XYZ->blue_Y; + + /* The reference white is simply the sum of the end-point (X,Y,Z) vectors, + * thus: + */ + if (png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite) == 0) + return 1; + if (png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite) == 0) + return 1; + + return 0; +} + +static int +png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) +{ + png_fixed_point red_inverse, green_inverse, blue_scale; + png_fixed_point left, right, denominator; + + /* Check xy and, implicitly, z. Note that wide gamut color spaces typically + * have end points with 0 tristimulus values (these are impossible end + * points, but they are used to cover the possible colors). We check + * xy->whitey against 5, not 0, to avoid a possible integer overflow. + */ + if (xy->redx < 0 || xy->redx > PNG_FP_1) return 1; + if (xy->redy < 0 || xy->redy > PNG_FP_1-xy->redx) return 1; + if (xy->greenx < 0 || xy->greenx > PNG_FP_1) return 1; + if (xy->greeny < 0 || xy->greeny > PNG_FP_1-xy->greenx) return 1; + if (xy->bluex < 0 || xy->bluex > PNG_FP_1) return 1; + if (xy->bluey < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1; + if (xy->whitex < 0 || xy->whitex > PNG_FP_1) return 1; + if (xy->whitey < 5 || xy->whitey > PNG_FP_1-xy->whitex) return 1; + + /* The reverse calculation is more difficult because the original tristimulus + * value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8 + * derived values were recorded in the cHRM chunk; + * (red,green,blue,white)x(x,y). This loses one degree of freedom and + * therefore an arbitrary ninth value has to be introduced to undo the + * original transformations. + * + * Think of the original end-points as points in (X,Y,Z) space. The + * chromaticity values (c) have the property: + * + * C + * c = --------- + * X + Y + Z + * + * For each c (x,y,z) from the corresponding original C (X,Y,Z). Thus the + * three chromaticity values (x,y,z) for each end-point obey the + * relationship: + * + * x + y + z = 1 + * + * This describes the plane in (X,Y,Z) space that intersects each axis at the + * value 1.0; call this the chromaticity plane. Thus the chromaticity + * calculation has scaled each end-point so that it is on the x+y+z=1 plane + * and chromaticity is the intersection of the vector from the origin to the + * (X,Y,Z) value with the chromaticity plane. + * + * To fully invert the chromaticity calculation we would need the three + * end-point scale factors, (red-scale, green-scale, blue-scale), but these + * were not recorded. Instead we calculated the reference white (X,Y,Z) and + * recorded the chromaticity of this. The reference white (X,Y,Z) would have + * given all three of the scale factors since: + * + * color-C = color-c * color-scale + * white-C = red-C + green-C + blue-C + * = red-c*red-scale + green-c*green-scale + blue-c*blue-scale + * + * But cHRM records only white-x and white-y, so we have lost the white scale + * factor: + * + * white-C = white-c*white-scale + * + * To handle this the inverse transformation makes an arbitrary assumption + * about white-scale: + * + * Assume: white-Y = 1.0 + * Hence: white-scale = 1/white-y + * Or: red-Y + green-Y + blue-Y = 1.0 + * + * Notice the last statement of the assumption gives an equation in three of + * the nine values we want to calculate. 8 more equations come from the + * above routine as summarised at the top above (the chromaticity + * calculation): + * + * Given: color-x = color-X / (color-X + color-Y + color-Z) + * Hence: (color-x - 1)*color-X + color.x*color-Y + color.x*color-Z = 0 + * + * This is 9 simultaneous equations in the 9 variables "color-C" and can be + * solved by Cramer's rule. Cramer's rule requires calculating 10 9x9 matrix + * determinants, however this is not as bad as it seems because only 28 of + * the total of 90 terms in the various matrices are non-zero. Nevertheless + * Cramer's rule is notoriously numerically unstable because the determinant + * calculation involves the difference of large, but similar, numbers. It is + * difficult to be sure that the calculation is stable for real world values + * and it is certain that it becomes unstable where the end points are close + * together. + * + * So this code uses the perhaps slightly less optimal but more + * understandable and totally obvious approach of calculating color-scale. + * + * This algorithm depends on the precision in white-scale and that is + * (1/white-y), so we can immediately see that as white-y approaches 0 the + * accuracy inherent in the cHRM chunk drops off substantially. + * + * libpng arithmetic: a simple inversion of the above equations + * ------------------------------------------------------------ + * + * white_scale = 1/white-y + * white-X = white-x * white-scale + * white-Y = 1.0 + * white-Z = (1 - white-x - white-y) * white_scale + * + * white-C = red-C + green-C + blue-C + * = red-c*red-scale + green-c*green-scale + blue-c*blue-scale + * + * This gives us three equations in (red-scale,green-scale,blue-scale) where + * all the coefficients are now known: + * + * red-x*red-scale + green-x*green-scale + blue-x*blue-scale + * = white-x/white-y + * red-y*red-scale + green-y*green-scale + blue-y*blue-scale = 1 + * red-z*red-scale + green-z*green-scale + blue-z*blue-scale + * = (1 - white-x - white-y)/white-y + * + * In the last equation color-z is (1 - color-x - color-y) so we can add all + * three equations together to get an alternative third: + * + * red-scale + green-scale + blue-scale = 1/white-y = white-scale + * + * So now we have a Cramer's rule solution where the determinants are just + * 3x3 - far more tractible. Unfortunately 3x3 determinants still involve + * multiplication of three coefficients so we can't guarantee to avoid + * overflow in the libpng fixed point representation. Using Cramer's rule in + * floating point is probably a good choice here, but it's not an option for + * fixed point. Instead proceed to simplify the first two equations by + * eliminating what is likely to be the largest value, blue-scale: + * + * blue-scale = white-scale - red-scale - green-scale + * + * Hence: + * + * (red-x - blue-x)*red-scale + (green-x - blue-x)*green-scale = + * (white-x - blue-x)*white-scale + * + * (red-y - blue-y)*red-scale + (green-y - blue-y)*green-scale = + * 1 - blue-y*white-scale + * + * And now we can trivially solve for (red-scale,green-scale): + * + * green-scale = + * (white-x - blue-x)*white-scale - (red-x - blue-x)*red-scale + * ----------------------------------------------------------- + * green-x - blue-x + * + * red-scale = + * 1 - blue-y*white-scale - (green-y - blue-y) * green-scale + * --------------------------------------------------------- + * red-y - blue-y + * + * Hence: + * + * red-scale = + * ( (green-x - blue-x) * (white-y - blue-y) - + * (green-y - blue-y) * (white-x - blue-x) ) / white-y + * ------------------------------------------------------------------------- + * (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x) + * + * green-scale = + * ( (red-y - blue-y) * (white-x - blue-x) - + * (red-x - blue-x) * (white-y - blue-y) ) / white-y + * ------------------------------------------------------------------------- + * (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x) + * + * Accuracy: + * The input values have 5 decimal digits of accuracy. The values are all in + * the range 0 < value < 1, so simple products are in the same range but may + * need up to 10 decimal digits to preserve the original precision and avoid + * underflow. Because we are using a 32-bit signed representation we cannot + * match this; the best is a little over 9 decimal digits, less than 10. + * + * The approach used here is to preserve the maximum precision within the + * signed representation. Because the red-scale calculation above uses the + * difference between two products of values that must be in the range -1..+1 + * it is sufficient to divide the product by 7; ceil(100,000/32767*2). The + * factor is irrelevant in the calculation because it is applied to both + * numerator and denominator. + * + * Note that the values of the differences of the products of the + * chromaticities in the above equations tend to be small, for example for + * the sRGB chromaticities they are: + * + * red numerator: -0.04751 + * green numerator: -0.08788 + * denominator: -0.2241 (without white-y multiplication) + * + * The resultant Y coefficients from the chromaticities of some widely used + * color space definitions are (to 15 decimal places): + * + * sRGB + * 0.212639005871510 0.715168678767756 0.072192315360734 + * Kodak ProPhoto + * 0.288071128229293 0.711843217810102 0.000085653960605 + * Adobe RGB + * 0.297344975250536 0.627363566255466 0.075291458493998 + * Adobe Wide Gamut RGB + * 0.258728243040113 0.724682314948566 0.016589442011321 + */ + /* By the argument, above overflow should be impossible here. The return + * value of 2 indicates an internal error to the caller. + */ + if (png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7) == 0) + return 2; + if (png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7) == 0) + return 2; + denominator = left - right; + + /* Now find the red numerator. */ + if (png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7) == 0) + return 2; + if (png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7) == 0) + return 2; + + /* Overflow is possible here and it indicates an extreme set of PNG cHRM + * chunk values. This calculation actually returns the reciprocal of the + * scale value because this allows us to delay the multiplication of white-y + * into the denominator, which tends to produce a small number. + */ + if (png_muldiv(&red_inverse, xy->whitey, denominator, left-right) == 0 || + red_inverse <= xy->whitey /* r+g+b scales = white scale */) + return 1; + + /* Similarly for green_inverse: */ + if (png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7) == 0) + return 2; + if (png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7) == 0) + return 2; + if (png_muldiv(&green_inverse, xy->whitey, denominator, left-right) == 0 || + green_inverse <= xy->whitey) + return 1; + + /* And the blue scale, the checks above guarantee this can't overflow but it + * can still produce 0 for extreme cHRM values. + */ + blue_scale = png_reciprocal(xy->whitey) - png_reciprocal(red_inverse) - + png_reciprocal(green_inverse); + if (blue_scale <= 0) + return 1; + + + /* And fill in the png_XYZ: */ + if (png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse) == 0) + return 1; + if (png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse) == 0) + return 1; + if (png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1, + red_inverse) == 0) + return 1; + + if (png_muldiv(&XYZ->green_X, xy->greenx, PNG_FP_1, green_inverse) == 0) + return 1; + if (png_muldiv(&XYZ->green_Y, xy->greeny, PNG_FP_1, green_inverse) == 0) + return 1; + if (png_muldiv(&XYZ->green_Z, PNG_FP_1 - xy->greenx - xy->greeny, PNG_FP_1, + green_inverse) == 0) + return 1; + + if (png_muldiv(&XYZ->blue_X, xy->bluex, blue_scale, PNG_FP_1) == 0) + return 1; + if (png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1) == 0) + return 1; + if (png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale, + PNG_FP_1) == 0) + return 1; + + return 0; /*success*/ +} + +static int +png_XYZ_normalize(png_XYZ *XYZ) +{ + png_int_32 Y; + + if (XYZ->red_Y < 0 || XYZ->green_Y < 0 || XYZ->blue_Y < 0 || + XYZ->red_X < 0 || XYZ->green_X < 0 || XYZ->blue_X < 0 || + XYZ->red_Z < 0 || XYZ->green_Z < 0 || XYZ->blue_Z < 0) + return 1; + + /* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1. + * IMPLEMENTATION NOTE: ANSI requires signed overflow not to occur, therefore + * relying on addition of two positive values producing a negative one is not + * safe. + */ + Y = XYZ->red_Y; + if (0x7fffffff - Y < XYZ->green_X) + return 1; + Y += XYZ->green_Y; + if (0x7fffffff - Y < XYZ->blue_X) + return 1; + Y += XYZ->blue_Y; + + if (Y != PNG_FP_1) + { + if (png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y) == 0) + return 1; + + if (png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y) == 0) + return 1; + + if (png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y) == 0) + return 1; + } + + return 0; +} + +static int +png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta) +{ + /* Allow an error of +/-0.01 (absolute value) on each chromaticity */ + if (PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) || + PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) || + PNG_OUT_OF_RANGE(xy1->redx, xy2->redx, delta) || + PNG_OUT_OF_RANGE(xy1->redy, xy2->redy, delta) || + PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) || + PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) || + PNG_OUT_OF_RANGE(xy1->bluex, xy2->bluex, delta) || + PNG_OUT_OF_RANGE(xy1->bluey, xy2->bluey, delta)) + return 0; + return 1; +} + +/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM + * chunk chromaticities. Earlier checks used to simply look for the overflow + * condition (where the determinant of the matrix to solve for XYZ ends up zero + * because the chromaticity values are not all distinct.) Despite this it is + * theoretically possible to produce chromaticities that are apparently valid + * but that rapidly degrade to invalid, potentially crashing, sets because of + * arithmetic inaccuracies when calculations are performed on them. The new + * check is to round-trip xy -> XYZ -> xy and then check that the result is + * within a small percentage of the original. + */ +static int +png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy) +{ + int result; + png_xy xy_test; + + /* As a side-effect this routine also returns the XYZ endpoints. */ + result = png_XYZ_from_xy(XYZ, xy); + if (result != 0) + return result; + + result = png_xy_from_XYZ(&xy_test, XYZ); + if (result != 0) + return result; + + if (png_colorspace_endpoints_match(xy, &xy_test, + 5/*actually, the math is pretty accurate*/) != 0) + return 0; + + /* Too much slip */ + return 1; +} + +/* This is the check going the other way. The XYZ is modified to normalize it + * (another side-effect) and the xy chromaticities are returned. + */ +static int +png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ) +{ + int result; + png_XYZ XYZtemp; + + result = png_XYZ_normalize(XYZ); + if (result != 0) + return result; + + result = png_xy_from_XYZ(xy, XYZ); + if (result != 0) + return result; + + XYZtemp = *XYZ; + return png_colorspace_check_xy(&XYZtemp, xy); +} + +/* Used to check for an endpoint match against sRGB */ +static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */ +{ + /* color x y */ + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000, + /* white */ 31270, 32900 +}; + +static int +png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ, + int preferred) +{ + if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) + return 0; + + /* The consistency check is performed on the chromaticities; this factors out + * variations because of the normalization (or not) of the end point Y + * values. + */ + if (preferred < 2 && + (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + { + /* The end points must be reasonably close to any we already have. The + * following allows an error of up to +/-.001 + */ + if (png_colorspace_endpoints_match(xy, &colorspace->end_points_xy, + 100) == 0) + { + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "inconsistent chromaticities"); + return 0; /* failed */ + } + + /* Only overwrite with preferred values */ + if (preferred == 0) + return 1; /* ok, but no change */ + } + + colorspace->end_points_xy = *xy; + colorspace->end_points_XYZ = *XYZ; + colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS; + + /* The end points are normally quoted to two decimal digits, so allow +/-0.01 + * on this test. + */ + if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000) != 0) + colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB; + + else + colorspace->flags &= PNG_COLORSPACE_CANCEL( + PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB); + + return 2; /* ok and changed */ +} + +int /* PRIVATE */ +png_colorspace_set_chromaticities(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_xy *xy, int preferred) +{ + /* We must check the end points to ensure they are reasonable - in the past + * color management systems have crashed as a result of getting bogus + * colorant values, while this isn't the fault of libpng it is the + * responsibility of libpng because PNG carries the bomb and libpng is in a + * position to protect against it. + */ + png_XYZ XYZ; + + switch (png_colorspace_check_xy(&XYZ, xy)) + { + case 0: /* success */ + return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ, + preferred); + + case 1: + /* We can't invert the chromaticities so we can't produce value XYZ + * values. Likely as not a color management system will fail too. + */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "invalid chromaticities"); + break; + + default: + /* libpng is broken; this should be a warning but if it happens we + * want error reports so for the moment it is an error. + */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_error(png_ptr, "internal error checking chromaticities"); + } + + return 0; /* failed */ +} + +int /* PRIVATE */ +png_colorspace_set_endpoints(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred) +{ + png_XYZ XYZ = *XYZ_in; + png_xy xy; + + switch (png_colorspace_check_XYZ(&xy, &XYZ)) + { + case 0: + return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ, + preferred); + + case 1: + /* End points are invalid. */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "invalid end points"); + break; + + default: + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_error(png_ptr, "internal error checking chromaticities"); + } + + return 0; /* failed */ +} + +#if defined(PNG_sRGB_SUPPORTED) || defined(PNG_iCCP_SUPPORTED) +/* Error message generation */ +static char +png_icc_tag_char(png_uint_32 byte) +{ + byte &= 0xff; + if (byte >= 32 && byte <= 126) + return (char)byte; + else + return '?'; +} + +static void +png_icc_tag_name(char *name, png_uint_32 tag) +{ + name[0] = '\''; + name[1] = png_icc_tag_char(tag >> 24); + name[2] = png_icc_tag_char(tag >> 16); + name[3] = png_icc_tag_char(tag >> 8); + name[4] = png_icc_tag_char(tag ); + name[5] = '\''; +} + +static int +is_ICC_signature_char(png_alloc_size_t it) +{ + return it == 32 || (it >= 48 && it <= 57) || (it >= 65 && it <= 90) || + (it >= 97 && it <= 122); +} + +static int +is_ICC_signature(png_alloc_size_t it) +{ + return is_ICC_signature_char(it >> 24) /* checks all the top bits */ && + is_ICC_signature_char((it >> 16) & 0xff) && + is_ICC_signature_char((it >> 8) & 0xff) && + is_ICC_signature_char(it & 0xff); +} + +static int +png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_alloc_size_t value, png_const_charp reason) +{ + size_t pos; + char message[196]; /* see below for calculation */ + + if (colorspace != NULL) + colorspace->flags |= PNG_COLORSPACE_INVALID; + + pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */ + pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */ + pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */ + if (is_ICC_signature(value) != 0) + { + /* So 'value' is at most 4 bytes and the following cast is safe */ + png_icc_tag_name(message+pos, (png_uint_32)value); + pos += 6; /* total +8; less than the else clause */ + message[pos++] = ':'; + message[pos++] = ' '; + } +# ifdef PNG_WARNINGS_SUPPORTED + else + { + char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114*/ + + pos = png_safecat(message, (sizeof message), pos, + png_format_number(number, number+(sizeof number), + PNG_NUMBER_FORMAT_x, value)); + pos = png_safecat(message, (sizeof message), pos, "h: "); /*+2 = 116*/ + } +# endif + /* The 'reason' is an arbitrary message, allow +79 maximum 195 */ + pos = png_safecat(message, (sizeof message), pos, reason); + PNG_UNUSED(pos) + + /* This is recoverable, but make it unconditionally an app_error on write to + * avoid writing invalid ICC profiles into PNG files (i.e., we handle them + * on read, with a warning, but on write unless the app turns off + * application errors the PNG won't be written.) + */ + png_chunk_report(png_ptr, message, + (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR); + + return 0; +} +#endif /* sRGB || iCCP */ + +#ifdef PNG_sRGB_SUPPORTED +int /* PRIVATE */ +png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, + int intent) +{ + /* sRGB sets known gamma, end points and (from the chunk) intent. */ + /* IMPORTANT: these are not necessarily the values found in an ICC profile + * because ICC profiles store values adapted to a D50 environment; it is + * expected that the ICC profile mediaWhitePointTag will be D50; see the + * checks and code elsewhere to understand this better. + * + * These XYZ values, which are accurate to 5dp, produce rgb to gray + * coefficients of (6968,23435,2366), which are reduced (because they add up + * to 32769 not 32768) to (6968,23434,2366). These are the values that + * libpng has traditionally used (and are the best values given the 15bit + * algorithm used by the rgb to gray code.) + */ + static const png_XYZ sRGB_XYZ = /* D65 XYZ (*not* the D50 adapted values!) */ + { + /* color X Y Z */ + /* red */ 41239, 21264, 1933, + /* green */ 35758, 71517, 11919, + /* blue */ 18048, 7219, 95053 + }; + + /* Do nothing if the colorspace is already invalidated. */ + if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) + return 0; + + /* Check the intent, then check for existing settings. It is valid for the + * PNG file to have cHRM or gAMA chunks along with sRGB, but the values must + * be consistent with the correct values. If, however, this function is + * called below because an iCCP chunk matches sRGB then it is quite + * conceivable that an older app recorded incorrect gAMA and cHRM because of + * an incorrect calculation based on the values in the profile - this does + * *not* invalidate the profile (though it still produces an error, which can + * be ignored.) + */ + if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) + return png_icc_profile_error(png_ptr, colorspace, "sRGB", + (png_alloc_size_t)intent, "invalid sRGB rendering intent"); + + if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 && + colorspace->rendering_intent != intent) + return png_icc_profile_error(png_ptr, colorspace, "sRGB", + (png_alloc_size_t)intent, "inconsistent rendering intents"); + + if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0) + { + png_benign_error(png_ptr, "duplicate sRGB information ignored"); + return 0; + } + + /* If the standard sRGB cHRM chunk does not match the one from the PNG file + * warn but overwrite the value with the correct one. + */ + if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 && + !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy, + 100)) + png_chunk_report(png_ptr, "cHRM chunk does not match sRGB", + PNG_CHUNK_ERROR); + + /* This check is just done for the error reporting - the routine always + * returns true when the 'from' argument corresponds to sRGB (2). + */ + (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE, + 2/*from sRGB*/); + + /* intent: bugs in GCC force 'int' to be used as the parameter type. */ + colorspace->rendering_intent = (png_uint_16)intent; + colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT; + + /* endpoints */ + colorspace->end_points_xy = sRGB_xy; + colorspace->end_points_XYZ = sRGB_XYZ; + colorspace->flags |= + (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB); + + /* gamma */ + colorspace->gamma = PNG_GAMMA_sRGB_INVERSE; + colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA; + + /* Finally record that we have an sRGB profile */ + colorspace->flags |= + (PNG_COLORSPACE_MATCHES_sRGB|PNG_COLORSPACE_FROM_sRGB); + + return 1; /* set */ +} +#endif /* sRGB */ + +#ifdef PNG_iCCP_SUPPORTED +/* Encoded value of D50 as an ICC XYZNumber. From the ICC 2010 spec the value + * is XYZ(0.9642,1.0,0.8249), which scales to: + * + * (63189.8112, 65536, 54060.6464) + */ +static const png_byte D50_nCIEXYZ[12] = + { 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d }; + +static int /* bool */ +icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length) +{ + if (profile_length < 132) + return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + "too short"); + return 1; +} + +#ifdef PNG_READ_iCCP_SUPPORTED +int /* PRIVATE */ +png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length) +{ + if (!icc_check_length(png_ptr, colorspace, name, profile_length)) + return 0; + + /* This needs to be here because the 'normal' check is in + * png_decompress_chunk, yet this happens after the attempt to + * png_malloc_base the required data. We only need this on read; on write + * the caller supplies the profile buffer so libpng doesn't allocate it. See + * the call to icc_check_length below (the write case). + */ +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + else if (png_ptr->user_chunk_malloc_max > 0 && + png_ptr->user_chunk_malloc_max < profile_length) + return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + "exceeds application limits"); +# elif PNG_USER_CHUNK_MALLOC_MAX > 0 + else if (PNG_USER_CHUNK_MALLOC_MAX < profile_length) + return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + "exceeds libpng limits"); +# else /* !SET_USER_LIMITS */ + /* This will get compiled out on all 32-bit and better systems. */ + else if (PNG_SIZE_MAX < profile_length) + return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + "exceeds system limits"); +# endif /* !SET_USER_LIMITS */ + + return 1; +} +#endif /* READ_iCCP */ + +int /* PRIVATE */ +png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile/* first 132 bytes only */, int color_type) +{ + png_uint_32 temp; + + /* Length check; this cannot be ignored in this code because profile_length + * is used later to check the tag table, so even if the profile seems over + * long profile_length from the caller must be correct. The caller can fix + * this up on read or write by just passing in the profile header length. + */ + temp = png_get_uint_32(profile); + if (temp != profile_length) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "length does not match profile"); + + temp = (png_uint_32) (*(profile+8)); + if (temp > 3 && (profile_length & 3)) + return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + "invalid length"); + + temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */ + if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */ + profile_length < 132+12*temp) /* truncated tag table */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "tag count too large"); + + /* The 'intent' must be valid or we can't store it, ICC limits the intent to + * 16 bits. + */ + temp = png_get_uint_32(profile+64); + if (temp >= 0xffff) /* The ICC limit */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid rendering intent"); + + /* This is just a warning because the profile may be valid in future + * versions. + */ + if (temp >= PNG_sRGB_INTENT_LAST) + (void)png_icc_profile_error(png_ptr, NULL, name, temp, + "intent outside defined range"); + + /* At this point the tag table can't be checked because it hasn't necessarily + * been loaded; however, various header fields can be checked. These checks + * are for values permitted by the PNG spec in an ICC profile; the PNG spec + * restricts the profiles that can be passed in an iCCP chunk (they must be + * appropriate to processing PNG data!) + */ + + /* Data checks (could be skipped). These checks must be independent of the + * version number; however, the version number doesn't accomodate changes in + * the header fields (just the known tags and the interpretation of the + * data.) + */ + temp = png_get_uint_32(profile+36); /* signature 'ascp' */ + if (temp != 0x61637370) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid signature"); + + /* Currently the PCS illuminant/adopted white point (the computational + * white point) are required to be D50, + * however the profile contains a record of the illuminant so perhaps ICC + * expects to be able to change this in the future (despite the rationale in + * the introduction for using a fixed PCS adopted white.) Consequently the + * following is just a warning. + */ + if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0) + (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/, + "PCS illuminant is not D50"); + + /* The PNG spec requires this: + * "If the iCCP chunk is present, the image samples conform to the colour + * space represented by the embedded ICC profile as defined by the + * International Color Consortium [ICC]. The colour space of the ICC profile + * shall be an RGB colour space for colour images (PNG colour types 2, 3, and + * 6), or a greyscale colour space for greyscale images (PNG colour types 0 + * and 4)." + * + * This checking code ensures the embedded profile (on either read or write) + * conforms to the specification requirements. Notice that an ICC 'gray' + * color-space profile contains the information to transform the monochrome + * data to XYZ or L*a*b (according to which PCS the profile uses) and this + * should be used in preference to the standard libpng K channel replication + * into R, G and B channels. + * + * Previously it was suggested that an RGB profile on grayscale data could be + * handled. However it it is clear that using an RGB profile in this context + * must be an error - there is no specification of what it means. Thus it is + * almost certainly more correct to ignore the profile. + */ + temp = png_get_uint_32(profile+16); /* data colour space field */ + switch (temp) + { + case 0x52474220: /* 'RGB ' */ + if ((color_type & PNG_COLOR_MASK_COLOR) == 0) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "RGB color space not permitted on grayscale PNG"); + break; + + case 0x47524159: /* 'GRAY' */ + if ((color_type & PNG_COLOR_MASK_COLOR) != 0) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "Gray color space not permitted on RGB PNG"); + break; + + default: + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid ICC profile color space"); + } + + /* It is up to the application to check that the profile class matches the + * application requirements; the spec provides no guidance, but it's pretty + * weird if the profile is not scanner ('scnr'), monitor ('mntr'), printer + * ('prtr') or 'spac' (for generic color spaces). Issue a warning in these + * cases. Issue an error for device link or abstract profiles - these don't + * contain the records necessary to transform the color-space to anything + * other than the target device (and not even that for an abstract profile). + * Profiles of these classes may not be embedded in images. + */ + temp = png_get_uint_32(profile+12); /* profile/device class */ + switch (temp) + { + case 0x73636e72: /* 'scnr' */ + case 0x6d6e7472: /* 'mntr' */ + case 0x70727472: /* 'prtr' */ + case 0x73706163: /* 'spac' */ + /* All supported */ + break; + + case 0x61627374: /* 'abst' */ + /* May not be embedded in an image */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid embedded Abstract ICC profile"); + + case 0x6c696e6b: /* 'link' */ + /* DeviceLink profiles cannot be interpreted in a non-device specific + * fashion, if an app uses the AToB0Tag in the profile the results are + * undefined unless the result is sent to the intended device, + * therefore a DeviceLink profile should not be found embedded in a + * PNG. + */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "unexpected DeviceLink ICC profile class"); + + case 0x6e6d636c: /* 'nmcl' */ + /* A NamedColor profile is also device specific, however it doesn't + * contain an AToB0 tag that is open to misinterpretation. Almost + * certainly it will fail the tests below. + */ + (void)png_icc_profile_error(png_ptr, NULL, name, temp, + "unexpected NamedColor ICC profile class"); + break; + + default: + /* To allow for future enhancements to the profile accept unrecognized + * profile classes with a warning, these then hit the test below on the + * tag content to ensure they are backward compatible with one of the + * understood profiles. + */ + (void)png_icc_profile_error(png_ptr, NULL, name, temp, + "unrecognized ICC profile class"); + break; + } + + /* For any profile other than a device link one the PCS must be encoded + * either in XYZ or Lab. + */ + temp = png_get_uint_32(profile+20); + switch (temp) + { + case 0x58595a20: /* 'XYZ ' */ + case 0x4c616220: /* 'Lab ' */ + break; + + default: + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "unexpected ICC PCS encoding"); + } + + return 1; +} + +int /* PRIVATE */ +png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */) +{ + png_uint_32 tag_count = png_get_uint_32(profile+128); + png_uint_32 itag; + png_const_bytep tag = profile+132; /* The first tag */ + + /* First scan all the tags in the table and add bits to the icc_info value + * (temporarily in 'tags'). + */ + for (itag=0; itag < tag_count; ++itag, tag += 12) + { + png_uint_32 tag_id = png_get_uint_32(tag+0); + png_uint_32 tag_start = png_get_uint_32(tag+4); /* must be aligned */ + png_uint_32 tag_length = png_get_uint_32(tag+8);/* not padded */ + + /* The ICC specification does not exclude zero length tags, therefore the + * start might actually be anywhere if there is no data, but this would be + * a clear abuse of the intent of the standard so the start is checked for + * being in range. All defined tag types have an 8 byte header - a 4 byte + * type signature then 0. + */ + + /* This is a hard error; potentially it can cause read outside the + * profile. + */ + if (tag_start > profile_length || tag_length > profile_length - tag_start) + return png_icc_profile_error(png_ptr, colorspace, name, tag_id, + "ICC profile tag outside profile"); + + if ((tag_start & 3) != 0) + { + /* CNHP730S.icc shipped with Microsoft Windows 64 violates this; it is + * only a warning here because libpng does not care about the + * alignment. + */ + (void)png_icc_profile_error(png_ptr, NULL, name, tag_id, + "ICC profile tag start not a multiple of 4"); + } + } + + return 1; /* success, maybe with warnings */ +} + +#ifdef PNG_sRGB_SUPPORTED +#if PNG_sRGB_PROFILE_CHECKS >= 0 +/* Information about the known ICC sRGB profiles */ +static const struct +{ + png_uint_32 adler, crc, length; + png_uint_32 md5[4]; + png_byte have_md5; + png_byte is_broken; + png_uint_16 intent; + +# define PNG_MD5(a,b,c,d) { a, b, c, d }, (a!=0)||(b!=0)||(c!=0)||(d!=0) +# define PNG_ICC_CHECKSUM(adler, crc, md5, intent, broke, date, length, fname)\ + { adler, crc, length, md5, broke, intent }, + +} png_sRGB_checks[] = +{ + /* This data comes from contrib/tools/checksum-icc run on downloads of + * all four ICC sRGB profiles from www.color.org. + */ + /* adler32, crc32, MD5[4], intent, date, length, file-name */ + PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9, + PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0, + "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc") + + /* ICC sRGB v2 perceptual no black-compensation: */ + PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21, + PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0, + "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc") + + PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae, + PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0, + "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc") + + /* ICC sRGB v4 perceptual */ + PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812, + PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0, + "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc") + + /* The following profiles have no known MD5 checksum. If there is a match + * on the (empty) MD5 the other fields are used to attempt a match and + * a warning is produced. The first two of these profiles have a 'cprt' tag + * which suggests that they were also made by Hewlett Packard. + */ + PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce, + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0, + "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc") + + /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not + * match the D50 PCS illuminant in the header (it is in fact the D65 values, + * so the white point is recorded as the un-adapted value.) The profiles + * below only differ in one byte - the intent - and are basically the same as + * the previous profile except for the mediaWhitePointTag error and a missing + * chromaticAdaptationTag. + */ + PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552, + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/, + "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual") + + PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d, + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/, + "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative") +}; + +static int +png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, + png_const_bytep profile, uLong adler) +{ + /* The quick check is to verify just the MD5 signature and trust the + * rest of the data. Because the profile has already been verified for + * correctness this is safe. png_colorspace_set_sRGB will check the 'intent' + * field too, so if the profile has been edited with an intent not defined + * by sRGB (but maybe defined by a later ICC specification) the read of + * the profile will fail at that point. + */ + + png_uint_32 length = 0; + png_uint_32 intent = 0x10000; /* invalid */ +#if PNG_sRGB_PROFILE_CHECKS > 1 + uLong crc = 0; /* the value for 0 length data */ +#endif + unsigned int i; + +#ifdef PNG_SET_OPTION_SUPPORTED + /* First see if PNG_SKIP_sRGB_CHECK_PROFILE has been set to "on" */ + if (((png_ptr->options >> PNG_SKIP_sRGB_CHECK_PROFILE) & 3) == + PNG_OPTION_ON) + return 0; +#endif + + for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i) + { + if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] && + png_get_uint_32(profile+88) == png_sRGB_checks[i].md5[1] && + png_get_uint_32(profile+92) == png_sRGB_checks[i].md5[2] && + png_get_uint_32(profile+96) == png_sRGB_checks[i].md5[3]) + { + /* This may be one of the old HP profiles without an MD5, in that + * case we can only use the length and Adler32 (note that these + * are not used by default if there is an MD5!) + */ +# if PNG_sRGB_PROFILE_CHECKS == 0 + if (png_sRGB_checks[i].have_md5 != 0) + return 1+png_sRGB_checks[i].is_broken; +# endif + + /* Profile is unsigned or more checks have been configured in. */ + if (length == 0) + { + length = png_get_uint_32(profile); + intent = png_get_uint_32(profile+64); + } + + /* Length *and* intent must match */ + if (length == (png_uint_32) png_sRGB_checks[i].length && + intent == (png_uint_32) png_sRGB_checks[i].intent) + { + /* Now calculate the adler32 if not done already. */ + if (adler == 0) + { + adler = adler32(0, NULL, 0); + adler = adler32(adler, profile, length); + } + + if (adler == png_sRGB_checks[i].adler) + { + /* These basic checks suggest that the data has not been + * modified, but if the check level is more than 1 perform + * our own crc32 checksum on the data. + */ +# if PNG_sRGB_PROFILE_CHECKS > 1 + if (crc == 0) + { + crc = crc32(0, NULL, 0); + crc = crc32(crc, profile, length); + } + + /* So this check must pass for the 'return' below to happen. + */ + if (crc == png_sRGB_checks[i].crc) +# endif + { + if (png_sRGB_checks[i].is_broken != 0) + { + /* These profiles are known to have bad data that may cause + * problems if they are used, therefore attempt to + * discourage their use, skip the 'have_md5' warning below, + * which is made irrelevant by this error. + */ + png_chunk_report(png_ptr, "known incorrect sRGB profile", + PNG_CHUNK_ERROR); + } + + /* Warn that this being done; this isn't even an error since + * the profile is perfectly valid, but it would be nice if + * people used the up-to-date ones. + */ + else if (png_sRGB_checks[i].have_md5 == 0) + { + png_chunk_report(png_ptr, + "out-of-date sRGB profile with no signature", + PNG_CHUNK_WARNING); + } + + return 1+png_sRGB_checks[i].is_broken; + } + } + +# if PNG_sRGB_PROFILE_CHECKS > 0 + /* The signature matched, but the profile had been changed in some + * way. This probably indicates a data error or uninformed hacking. + * Fall through to "no match". + */ + png_chunk_report(png_ptr, + "Not recognizing known sRGB profile that has been edited", + PNG_CHUNK_WARNING); + break; +# endif + } + } + } + + return 0; /* no match */ +} + +void /* PRIVATE */ +png_icc_set_sRGB(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_bytep profile, uLong adler) +{ + /* Is this profile one of the known ICC sRGB profiles? If it is, just set + * the sRGB information. + */ + if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler) != 0) + (void)png_colorspace_set_sRGB(png_ptr, colorspace, + (int)/*already checked*/png_get_uint_32(profile+64)); +} +#endif /* PNG_sRGB_PROFILE_CHECKS >= 0 */ +#endif /* sRGB */ + +int /* PRIVATE */ +png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, png_const_bytep profile, + int color_type) +{ + if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) + return 0; + + if (icc_check_length(png_ptr, colorspace, name, profile_length) != 0 && + png_icc_check_header(png_ptr, colorspace, name, profile_length, profile, + color_type) != 0 && + png_icc_check_tag_table(png_ptr, colorspace, name, profile_length, + profile) != 0) + { +# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0 + /* If no sRGB support, don't try storing sRGB information */ + png_icc_set_sRGB(png_ptr, colorspace, profile, 0); +# endif + return 1; + } + + /* Failure case */ + return 0; +} +#endif /* iCCP */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +void /* PRIVATE */ +png_colorspace_set_rgb_coefficients(png_structrp png_ptr) +{ + /* Set the rgb_to_gray coefficients from the colorspace. */ + if (png_ptr->rgb_to_gray_coefficients_set == 0 && + (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + { + /* png_set_background has not been called, get the coefficients from the Y + * values of the colorspace colorants. + */ + png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y; + png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y; + png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y; + png_fixed_point total = r+g+b; + + if (total > 0 && + r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 && + g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 && + b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 && + r+g+b <= 32769) + { + /* We allow 0 coefficients here. r+g+b may be 32769 if two or + * all of the coefficients were rounded up. Handle this by + * reducing the *largest* coefficient by 1; this matches the + * approach used for the default coefficients in pngrtran.c + */ + int add = 0; + + if (r+g+b > 32768) + add = -1; + else if (r+g+b < 32768) + add = 1; + + if (add != 0) + { + if (g >= r && g >= b) + g += add; + else if (r >= g && r >= b) + r += add; + else + b += add; + } + + /* Check for an internal error. */ + if (r+g+b != 32768) + png_error(png_ptr, + "internal error handling cHRM coefficients"); + + else + { + png_ptr->rgb_to_gray_red_coeff = (png_uint_16)r; + png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g; + } + } + + /* This is a png_error at present even though it could be ignored - + * it should never happen, but it is important that if it does, the + * bug is fixed. + */ + else + png_error(png_ptr, "internal error handling cHRM->XYZ"); + } +} +#endif /* READ_RGB_TO_GRAY */ + +#endif /* COLORSPACE */ + +#ifdef __GNUC__ +/* This exists solely to work round a warning from GNU C. */ +static int /* PRIVATE */ +png_gt(size_t a, size_t b) +{ + return a > b; +} +#else +# define png_gt(a,b) ((a) > (b)) +#endif + +void /* PRIVATE */ +png_check_IHDR(png_const_structrp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + int error = 0; + + /* Check for width and height valid values */ + if (width == 0) + { + png_warning(png_ptr, "Image width is zero in IHDR"); + error = 1; + } + + if (width > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image width in IHDR"); + error = 1; + } + + if (png_gt(((width + 7) & (~7U)), + ((PNG_SIZE_MAX + - 48 /* big_row_buf hack */ + - 1) /* filter byte */ + / 8) /* 8-byte RGBA pixels */ + - 1)) /* extra max_pixel_depth pad */ + { + /* The size of the row must be within the limits of this architecture. + * Because the read code can perform arbitrary transformations the + * maximum size is checked here. Because the code in png_read_start_row + * adds extra space "for safety's sake" in several places a conservative + * limit is used here. + * + * NOTE: it would be far better to check the size that is actually used, + * but the effect in the real world is minor and the changes are more + * extensive, therefore much more dangerous and much more difficult to + * write in a way that avoids compiler warnings. + */ + png_warning(png_ptr, "Image width is too large for this architecture"); + error = 1; + } + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max) +#else + if (width > PNG_USER_WIDTH_MAX) +#endif + { + png_warning(png_ptr, "Image width exceeds user limit in IHDR"); + error = 1; + } + + if (height == 0) + { + png_warning(png_ptr, "Image height is zero in IHDR"); + error = 1; + } + + if (height > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image height in IHDR"); + error = 1; + } + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (height > png_ptr->user_height_max) +#else + if (height > PNG_USER_HEIGHT_MAX) +#endif + { + png_warning(png_ptr, "Image height exceeds user limit in IHDR"); + error = 1; + } + + /* Check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + { + png_warning(png_ptr, "Invalid bit depth in IHDR"); + error = 1; + } + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + { + png_warning(png_ptr, "Invalid color type in IHDR"); + error = 1; + } + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + { + png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR"); + error = 1; + } + + if (interlace_type >= PNG_INTERLACE_LAST) + { + png_warning(png_ptr, "Unknown interlace method in IHDR"); + error = 1; + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Unknown compression method in IHDR"); + error = 1; + } + +#ifdef PNG_MNG_FEATURES_SUPPORTED + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 && + png_ptr->mng_features_permitted != 0) + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + + if (filter_type != PNG_FILTER_TYPE_BASE) + { + if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } + + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0) + { + png_warning(png_ptr, "Invalid filter method in IHDR"); + error = 1; + } + } + +#else + if (filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } +#endif + + if (error == 1) + png_error(png_ptr, "Invalid IHDR data"); +} + +#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) +/* ASCII to fp functions */ +/* Check an ASCII formated floating point value, see the more detailed + * comments in pngpriv.h + */ +/* The following is used internally to preserve the sticky flags */ +#define png_fp_add(state, flags) ((state) |= (flags)) +#define png_fp_set(state, value) ((state) = (value) | ((state) & PNG_FP_STICKY)) + +int /* PRIVATE */ +png_check_fp_number(png_const_charp string, png_size_t size, int *statep, + png_size_tp whereami) +{ + int state = *statep; + png_size_t i = *whereami; + + while (i < size) + { + int type; + /* First find the type of the next character */ + switch (string[i]) + { + case 43: type = PNG_FP_SAW_SIGN; break; + case 45: type = PNG_FP_SAW_SIGN + PNG_FP_NEGATIVE; break; + case 46: type = PNG_FP_SAW_DOT; break; + case 48: type = PNG_FP_SAW_DIGIT; break; + case 49: case 50: case 51: case 52: + case 53: case 54: case 55: case 56: + case 57: type = PNG_FP_SAW_DIGIT + PNG_FP_NONZERO; break; + case 69: + case 101: type = PNG_FP_SAW_E; break; + default: goto PNG_FP_End; + } + + /* Now deal with this type according to the current + * state, the type is arranged to not overlap the + * bits of the PNG_FP_STATE. + */ + switch ((state & PNG_FP_STATE) + (type & PNG_FP_SAW_ANY)) + { + case PNG_FP_INTEGER + PNG_FP_SAW_SIGN: + if ((state & PNG_FP_SAW_ANY) != 0) + goto PNG_FP_End; /* not a part of the number */ + + png_fp_add(state, type); + break; + + case PNG_FP_INTEGER + PNG_FP_SAW_DOT: + /* Ok as trailer, ok as lead of fraction. */ + if ((state & PNG_FP_SAW_DOT) != 0) /* two dots */ + goto PNG_FP_End; + + else if ((state & PNG_FP_SAW_DIGIT) != 0) /* trailing dot? */ + png_fp_add(state, type); + + else + png_fp_set(state, PNG_FP_FRACTION | type); + + break; + + case PNG_FP_INTEGER + PNG_FP_SAW_DIGIT: + if ((state & PNG_FP_SAW_DOT) != 0) /* delayed fraction */ + png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT); + + png_fp_add(state, type | PNG_FP_WAS_VALID); + + break; + + case PNG_FP_INTEGER + PNG_FP_SAW_E: + if ((state & PNG_FP_SAW_DIGIT) == 0) + goto PNG_FP_End; + + png_fp_set(state, PNG_FP_EXPONENT); + + break; + + /* case PNG_FP_FRACTION + PNG_FP_SAW_SIGN: + goto PNG_FP_End; ** no sign in fraction */ + + /* case PNG_FP_FRACTION + PNG_FP_SAW_DOT: + goto PNG_FP_End; ** Because SAW_DOT is always set */ + + case PNG_FP_FRACTION + PNG_FP_SAW_DIGIT: + png_fp_add(state, type | PNG_FP_WAS_VALID); + break; + + case PNG_FP_FRACTION + PNG_FP_SAW_E: + /* This is correct because the trailing '.' on an + * integer is handled above - so we can only get here + * with the sequence ".E" (with no preceding digits). + */ + if ((state & PNG_FP_SAW_DIGIT) == 0) + goto PNG_FP_End; + + png_fp_set(state, PNG_FP_EXPONENT); + + break; + + case PNG_FP_EXPONENT + PNG_FP_SAW_SIGN: + if ((state & PNG_FP_SAW_ANY) != 0) + goto PNG_FP_End; /* not a part of the number */ + + png_fp_add(state, PNG_FP_SAW_SIGN); + + break; + + /* case PNG_FP_EXPONENT + PNG_FP_SAW_DOT: + goto PNG_FP_End; */ + + case PNG_FP_EXPONENT + PNG_FP_SAW_DIGIT: + png_fp_add(state, PNG_FP_SAW_DIGIT | PNG_FP_WAS_VALID); + + break; + + /* case PNG_FP_EXPONEXT + PNG_FP_SAW_E: + goto PNG_FP_End; */ + + default: goto PNG_FP_End; /* I.e. break 2 */ + } + + /* The character seems ok, continue. */ + ++i; + } + +PNG_FP_End: + /* Here at the end, update the state and return the correct + * return code. + */ + *statep = state; + *whereami = i; + + return (state & PNG_FP_SAW_DIGIT) != 0; +} + + +/* The same but for a complete string. */ +int +png_check_fp_string(png_const_charp string, png_size_t size) +{ + int state=0; + png_size_t char_index=0; + + if (png_check_fp_number(string, size, &state, &char_index) != 0 && + (char_index == size || string[char_index] == 0)) + return state /* must be non-zero - see above */; + + return 0; /* i.e. fail */ +} +#endif /* pCAL || sCAL */ + +#ifdef PNG_sCAL_SUPPORTED +# ifdef PNG_FLOATING_POINT_SUPPORTED +/* Utility used below - a simple accurate power of ten from an integral + * exponent. + */ +static double +png_pow10(int power) +{ + int recip = 0; + double d = 1; + + /* Handle negative exponent with a reciprocal at the end because + * 10 is exact whereas .1 is inexact in base 2 + */ + if (power < 0) + { + if (power < DBL_MIN_10_EXP) return 0; + recip = 1; power = -power; + } + + if (power > 0) + { + /* Decompose power bitwise. */ + double mult = 10; + do + { + if (power & 1) d *= mult; + mult *= mult; + power >>= 1; + } + while (power > 0); + + if (recip != 0) d = 1/d; + } + /* else power is 0 and d is 1 */ + + return d; +} + +/* Function to format a floating point value in ASCII with a given + * precision. + */ +#if GCC_STRICT_OVERFLOW +#pragma GCC diagnostic push +/* The problem arises below with exp_b10, which can never overflow because it + * comes, originally, from frexp and is therefore limited to a range which is + * typically +/-710 (log2(DBL_MAX)/log2(DBL_MIN)). + */ +#pragma GCC diagnostic warning "-Wstrict-overflow=2" +#endif /* GCC_STRICT_OVERFLOW */ +void /* PRIVATE */ +png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, + double fp, unsigned int precision) +{ + /* We use standard functions from math.h, but not printf because + * that would require stdio. The caller must supply a buffer of + * sufficient size or we will png_error. The tests on size and + * the space in ascii[] consumed are indicated below. + */ + if (precision < 1) + precision = DBL_DIG; + + /* Enforce the limit of the implementation precision too. */ + if (precision > DBL_DIG+1) + precision = DBL_DIG+1; + + /* Basic sanity checks */ + if (size >= precision+5) /* See the requirements below. */ + { + if (fp < 0) + { + fp = -fp; + *ascii++ = 45; /* '-' PLUS 1 TOTAL 1 */ + --size; + } + + if (fp >= DBL_MIN && fp <= DBL_MAX) + { + int exp_b10; /* A base 10 exponent */ + double base; /* 10^exp_b10 */ + + /* First extract a base 10 exponent of the number, + * the calculation below rounds down when converting + * from base 2 to base 10 (multiply by log10(2) - + * 0.3010, but 77/256 is 0.3008, so exp_b10 needs to + * be increased. Note that the arithmetic shift + * performs a floor() unlike C arithmetic - using a + * C multiply would break the following for negative + * exponents. + */ + (void)frexp(fp, &exp_b10); /* exponent to base 2 */ + + exp_b10 = (exp_b10 * 77) >> 8; /* <= exponent to base 10 */ + + /* Avoid underflow here. */ + base = png_pow10(exp_b10); /* May underflow */ + + while (base < DBL_MIN || base < fp) + { + /* And this may overflow. */ + double test = png_pow10(exp_b10+1); + + if (test <= DBL_MAX) + { + ++exp_b10; base = test; + } + + else + break; + } + + /* Normalize fp and correct exp_b10, after this fp is in the + * range [.1,1) and exp_b10 is both the exponent and the digit + * *before* which the decimal point should be inserted + * (starting with 0 for the first digit). Note that this + * works even if 10^exp_b10 is out of range because of the + * test on DBL_MAX above. + */ + fp /= base; + while (fp >= 1) + { + fp /= 10; ++exp_b10; + } + + /* Because of the code above fp may, at this point, be + * less than .1, this is ok because the code below can + * handle the leading zeros this generates, so no attempt + * is made to correct that here. + */ + + { + unsigned int czero, clead, cdigits; + char exponent[10]; + + /* Allow up to two leading zeros - this will not lengthen + * the number compared to using E-n. + */ + if (exp_b10 < 0 && exp_b10 > -3) /* PLUS 3 TOTAL 4 */ + { + czero = 0U-exp_b10; /* PLUS 2 digits: TOTAL 3 */ + exp_b10 = 0; /* Dot added below before first output. */ + } + else + czero = 0; /* No zeros to add */ + + /* Generate the digit list, stripping trailing zeros and + * inserting a '.' before a digit if the exponent is 0. + */ + clead = czero; /* Count of leading zeros */ + cdigits = 0; /* Count of digits in list. */ + + do + { + double d; + + fp *= 10; + /* Use modf here, not floor and subtract, so that + * the separation is done in one step. At the end + * of the loop don't break the number into parts so + * that the final digit is rounded. + */ + if (cdigits+czero+1 < precision+clead) + fp = modf(fp, &d); + + else + { + d = floor(fp + .5); + + if (d > 9) + { + /* Rounding up to 10, handle that here. */ + if (czero > 0) + { + --czero; d = 1; + if (cdigits == 0) --clead; + } + else + { + while (cdigits > 0 && d > 9) + { + int ch = *--ascii; + + if (exp_b10 != (-1)) + ++exp_b10; + + else if (ch == 46) + { + ch = *--ascii; ++size; + /* Advance exp_b10 to '1', so that the + * decimal point happens after the + * previous digit. + */ + exp_b10 = 1; + } + + --cdigits; + d = ch - 47; /* I.e. 1+(ch-48) */ + } + + /* Did we reach the beginning? If so adjust the + * exponent but take into account the leading + * decimal point. + */ + if (d > 9) /* cdigits == 0 */ + { + if (exp_b10 == (-1)) + { + /* Leading decimal point (plus zeros?), if + * we lose the decimal point here it must + * be reentered below. + */ + int ch = *--ascii; + + if (ch == 46) + { + ++size; exp_b10 = 1; + } + + /* Else lost a leading zero, so 'exp_b10' is + * still ok at (-1) + */ + } + else + ++exp_b10; + + /* In all cases we output a '1' */ + d = 1; + } + } + } + fp = 0; /* Guarantees termination below. */ + } + + if (d == 0) + { + ++czero; + if (cdigits == 0) ++clead; + } + else + { + /* Included embedded zeros in the digit count. */ + cdigits += czero - clead; + clead = 0; + + while (czero > 0) + { + /* exp_b10 == (-1) means we just output the decimal + * place - after the DP don't adjust 'exp_b10' any + * more! + */ + if (exp_b10 != (-1)) + { + if (exp_b10 == 0) + { + *ascii++ = 46; --size; + } + /* PLUS 1: TOTAL 4 */ + --exp_b10; + } + *ascii++ = 48; --czero; + } + + if (exp_b10 != (-1)) + { + if (exp_b10 == 0) + { + *ascii++ = 46; --size; /* counted above */ + } + + --exp_b10; + } + *ascii++ = (char)(48 + (int)d); ++cdigits; + } + } + while (cdigits+czero < precision+clead && fp > DBL_MIN); + + /* The total output count (max) is now 4+precision */ + + /* Check for an exponent, if we don't need one we are + * done and just need to terminate the string. At + * this point exp_b10==(-1) is effectively a flag - it got + * to '-1' because of the decrement after outputting + * the decimal point above (the exponent required is + * *not* -1!) + */ + if (exp_b10 >= (-1) && exp_b10 <= 2) + { + /* The following only happens if we didn't output the + * leading zeros above for negative exponent, so this + * doesn't add to the digit requirement. Note that the + * two zeros here can only be output if the two leading + * zeros were *not* output, so this doesn't increase + * the output count. + */ + while (exp_b10-- > 0) *ascii++ = 48; + + *ascii = 0; + + /* Total buffer requirement (including the '\0') is + * 5+precision - see check at the start. + */ + return; + } + + /* Here if an exponent is required, adjust size for + * the digits we output but did not count. The total + * digit output here so far is at most 1+precision - no + * decimal point and no leading or trailing zeros have + * been output. + */ + size -= cdigits; + + *ascii++ = 69; --size; /* 'E': PLUS 1 TOTAL 2+precision */ + + /* The following use of an unsigned temporary avoids ambiguities in + * the signed arithmetic on exp_b10 and permits GCC at least to do + * better optimization. + */ + { + unsigned int uexp_b10; + + if (exp_b10 < 0) + { + *ascii++ = 45; --size; /* '-': PLUS 1 TOTAL 3+precision */ + uexp_b10 = 0U-exp_b10; + } + + else + uexp_b10 = 0U+exp_b10; + + cdigits = 0; + + while (uexp_b10 > 0) + { + exponent[cdigits++] = (char)(48 + uexp_b10 % 10); + uexp_b10 /= 10; + } + } + + /* Need another size check here for the exponent digits, so + * this need not be considered above. + */ + if (size > cdigits) + { + while (cdigits > 0) *ascii++ = exponent[--cdigits]; + + *ascii = 0; + + return; + } + } + } + else if (!(fp >= DBL_MIN)) + { + *ascii++ = 48; /* '0' */ + *ascii = 0; + return; + } + else + { + *ascii++ = 105; /* 'i' */ + *ascii++ = 110; /* 'n' */ + *ascii++ = 102; /* 'f' */ + *ascii = 0; + return; + } + } + + /* Here on buffer too small. */ + png_error(png_ptr, "ASCII conversion buffer too small"); +} +#if GCC_STRICT_OVERFLOW +#pragma GCC diagnostic pop +#endif /* GCC_STRICT_OVERFLOW */ + +# endif /* FLOATING_POINT */ + +# ifdef PNG_FIXED_POINT_SUPPORTED +/* Function to format a fixed point value in ASCII. + */ +void /* PRIVATE */ +png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii, + png_size_t size, png_fixed_point fp) +{ + /* Require space for 10 decimal digits, a decimal point, a minus sign and a + * trailing \0, 13 characters: + */ + if (size > 12) + { + png_uint_32 num; + + /* Avoid overflow here on the minimum integer. */ + if (fp < 0) + { + *ascii++ = 45; num = (png_uint_32)(-fp); + } + else + num = (png_uint_32)fp; + + if (num <= 0x80000000) /* else overflowed */ + { + unsigned int ndigits = 0, first = 16 /* flag value */; + char digits[10]; + + while (num) + { + /* Split the low digit off num: */ + unsigned int tmp = num/10; + num -= tmp*10; + digits[ndigits++] = (char)(48 + num); + /* Record the first non-zero digit, note that this is a number + * starting at 1, it's not actually the array index. + */ + if (first == 16 && num > 0) + first = ndigits; + num = tmp; + } + + if (ndigits > 0) + { + while (ndigits > 5) *ascii++ = digits[--ndigits]; + /* The remaining digits are fractional digits, ndigits is '5' or + * smaller at this point. It is certainly not zero. Check for a + * non-zero fractional digit: + */ + if (first <= 5) + { + unsigned int i; + *ascii++ = 46; /* decimal point */ + /* ndigits may be <5 for small numbers, output leading zeros + * then ndigits digits to first: + */ + i = 5; + while (ndigits < i) + { + *ascii++ = 48; --i; + } + while (ndigits >= first) *ascii++ = digits[--ndigits]; + /* Don't output the trailing zeros! */ + } + } + else + *ascii++ = 48; + + /* And null terminate the string: */ + *ascii = 0; + return; + } + } + + /* Here on buffer too small. */ + png_error(png_ptr, "ASCII conversion buffer too small"); +} +# endif /* FIXED_POINT */ +#endif /* SCAL */ + +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ + (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ + defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \ + (defined(PNG_sCAL_SUPPORTED) && \ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)) +png_fixed_point +png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text) +{ + double r = floor(100000 * fp + .5); + + if (r > 2147483647. || r < -2147483648.) + png_fixed_error(png_ptr, text); + +# ifndef PNG_ERROR_TEXT_SUPPORTED + PNG_UNUSED(text) +# endif + + return (png_fixed_point)r; +} +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_COLORSPACE_SUPPORTED) ||\ + defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED) +/* muldiv functions */ +/* This API takes signed arguments and rounds the result to the nearest + * integer (or, for a fixed point number - the standard argument - to + * the nearest .00001). Overflow and divide by zero are signalled in + * the result, a boolean - true on success, false on overflow. + */ +#if GCC_STRICT_OVERFLOW /* from above */ +/* It is not obvious which comparison below gets optimized in such a way that + * signed overflow would change the result; looking through the code does not + * reveal any tests which have the form GCC complains about, so presumably the + * optimizer is moving an add or subtract into the 'if' somewhere. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic warning "-Wstrict-overflow=2" +#endif /* GCC_STRICT_OVERFLOW */ +int +png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times, + png_int_32 divisor) +{ + /* Return a * times / divisor, rounded. */ + if (divisor != 0) + { + if (a == 0 || times == 0) + { + *res = 0; + return 1; + } + else + { +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = a; + r *= times; + r /= divisor; + r = floor(r+.5); + + /* A png_fixed_point is a 32-bit integer. */ + if (r <= 2147483647. && r >= -2147483648.) + { + *res = (png_fixed_point)r; + return 1; + } +#else + int negative = 0; + png_uint_32 A, T, D; + png_uint_32 s16, s32, s00; + + if (a < 0) + negative = 1, A = -a; + else + A = a; + + if (times < 0) + negative = !negative, T = -times; + else + T = times; + + if (divisor < 0) + negative = !negative, D = -divisor; + else + D = divisor; + + /* Following can't overflow because the arguments only + * have 31 bits each, however the result may be 32 bits. + */ + s16 = (A >> 16) * (T & 0xffff) + + (A & 0xffff) * (T >> 16); + /* Can't overflow because the a*times bit is only 30 + * bits at most. + */ + s32 = (A >> 16) * (T >> 16) + (s16 >> 16); + s00 = (A & 0xffff) * (T & 0xffff); + + s16 = (s16 & 0xffff) << 16; + s00 += s16; + + if (s00 < s16) + ++s32; /* carry */ + + if (s32 < D) /* else overflow */ + { + /* s32.s00 is now the 64-bit product, do a standard + * division, we know that s32 < D, so the maximum + * required shift is 31. + */ + int bitshift = 32; + png_fixed_point result = 0; /* NOTE: signed */ + + while (--bitshift >= 0) + { + png_uint_32 d32, d00; + + if (bitshift > 0) + d32 = D >> (32-bitshift), d00 = D << bitshift; + + else + d32 = 0, d00 = D; + + if (s32 > d32) + { + if (s00 < d00) --s32; /* carry */ + s32 -= d32, s00 -= d00, result += 1<= d00) + s32 = 0, s00 -= d00, result += 1<= (D >> 1)) + ++result; + + if (negative != 0) + result = -result; + + /* Check for overflow. */ + if ((negative != 0 && result <= 0) || + (negative == 0 && result >= 0)) + { + *res = result; + return 1; + } + } +#endif + } + } + + return 0; +} +#if GCC_STRICT_OVERFLOW +#pragma GCC diagnostic pop +#endif /* GCC_STRICT_OVERFLOW */ +#endif /* READ_GAMMA || INCH_CONVERSIONS */ + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) +/* The following is for when the caller doesn't much care about the + * result. + */ +png_fixed_point +png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times, + png_int_32 divisor) +{ + png_fixed_point result; + + if (png_muldiv(&result, a, times, divisor) != 0) + return result; + + png_warning(png_ptr, "fixed point overflow ignored"); + return 0; +} +#endif + +#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */ +/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ +png_fixed_point +png_reciprocal(png_fixed_point a) +{ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(1E10/a+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + png_fixed_point res; + + if (png_muldiv(&res, 100000, 100000, a) != 0) + return res; +#endif + + return 0; /* error/overflow */ +} + +/* This is the shared test on whether a gamma value is 'significant' - whether + * it is worth doing gamma correction. + */ +int /* PRIVATE */ +png_gamma_significant(png_fixed_point gamma_val) +{ + return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED || + gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED; +} +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +#ifdef PNG_16BIT_SUPPORTED +/* A local convenience routine. */ +static png_fixed_point +png_product2(png_fixed_point a, png_fixed_point b) +{ + /* The required result is 1/a * 1/b; the following preserves accuracy. */ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = a * 1E-5; + r *= b; + r = floor(r+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + png_fixed_point res; + + if (png_muldiv(&res, a, b, 100000) != 0) + return res; +#endif + + return 0; /* overflow */ +} +#endif /* 16BIT */ + +/* The inverse of the above. */ +png_fixed_point +png_reciprocal2(png_fixed_point a, png_fixed_point b) +{ + /* The required result is 1/a * 1/b; the following preserves accuracy. */ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + if (a != 0 && b != 0) + { + double r = 1E15/a; + r /= b; + r = floor(r+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; + } +#else + /* This may overflow because the range of png_fixed_point isn't symmetric, + * but this API is only used for the product of file and screen gamma so it + * doesn't matter that the smallest number it can produce is 1/21474, not + * 1/100000 + */ + png_fixed_point res = png_product2(a, b); + + if (res != 0) + return png_reciprocal(res); +#endif + + return 0; /* overflow */ +} +#endif /* READ_GAMMA */ + +#ifdef PNG_READ_GAMMA_SUPPORTED /* gamma table code */ +#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED +/* Fixed point gamma. + * + * The code to calculate the tables used below can be found in the shell script + * contrib/tools/intgamma.sh + * + * To calculate gamma this code implements fast log() and exp() calls using only + * fixed point arithmetic. This code has sufficient precision for either 8-bit + * or 16-bit sample values. + * + * The tables used here were calculated using simple 'bc' programs, but C double + * precision floating point arithmetic would work fine. + * + * 8-bit log table + * This is a table of -log(value/255)/log(2) for 'value' in the range 128 to + * 255, so it's the base 2 logarithm of a normalized 8-bit floating point + * mantissa. The numbers are 32-bit fractions. + */ +static const png_uint_32 +png_8bit_l2[128] = +{ + 4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U, + 3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U, + 3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U, + 3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U, + 3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U, + 2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U, + 2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U, + 2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U, + 2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U, + 2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U, + 1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U, + 1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U, + 1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U, + 1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U, + 1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U, + 971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U, + 803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U, + 639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U, + 479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U, + 324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U, + 172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U, + 24347096U, 0U + +#if 0 + /* The following are the values for 16-bit tables - these work fine for the + * 8-bit conversions but produce very slightly larger errors in the 16-bit + * log (about 1.2 as opposed to 0.7 absolute error in the final value). To + * use these all the shifts below must be adjusted appropriately. + */ + 65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054, + 57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803, + 50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068, + 43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782, + 37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887, + 31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339, + 25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098, + 20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132, + 15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415, + 10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523, + 6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495, + 1119, 744, 372 +#endif +}; + +static png_int_32 +png_log8bit(unsigned int x) +{ + unsigned int lg2 = 0; + /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log, + * because the log is actually negate that means adding 1. The final + * returned value thus has the range 0 (for 255 input) to 7.994 (for 1 + * input), return -1 for the overflow (log 0) case, - so the result is + * always at most 19 bits. + */ + if ((x &= 0xff) == 0) + return -1; + + if ((x & 0xf0) == 0) + lg2 = 4, x <<= 4; + + if ((x & 0xc0) == 0) + lg2 += 2, x <<= 2; + + if ((x & 0x80) == 0) + lg2 += 1, x <<= 1; + + /* result is at most 19 bits, so this cast is safe: */ + return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16)); +} + +/* The above gives exact (to 16 binary places) log2 values for 8-bit images, + * for 16-bit images we use the most significant 8 bits of the 16-bit value to + * get an approximation then multiply the approximation by a correction factor + * determined by the remaining up to 8 bits. This requires an additional step + * in the 16-bit case. + * + * We want log2(value/65535), we have log2(v'/255), where: + * + * value = v' * 256 + v'' + * = v' * f + * + * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128 + * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less + * than 258. The final factor also needs to correct for the fact that our 8-bit + * value is scaled by 255, whereas the 16-bit values must be scaled by 65535. + * + * This gives a final formula using a calculated value 'x' which is value/v' and + * scaling by 65536 to match the above table: + * + * log2(x/257) * 65536 + * + * Since these numbers are so close to '1' we can use simple linear + * interpolation between the two end values 256/257 (result -368.61) and 258/257 + * (result 367.179). The values used below are scaled by a further 64 to give + * 16-bit precision in the interpolation: + * + * Start (256): -23591 + * Zero (257): 0 + * End (258): 23499 + */ +#ifdef PNG_16BIT_SUPPORTED +static png_int_32 +png_log16bit(png_uint_32 x) +{ + unsigned int lg2 = 0; + + /* As above, but now the input has 16 bits. */ + if ((x &= 0xffff) == 0) + return -1; + + if ((x & 0xff00) == 0) + lg2 = 8, x <<= 8; + + if ((x & 0xf000) == 0) + lg2 += 4, x <<= 4; + + if ((x & 0xc000) == 0) + lg2 += 2, x <<= 2; + + if ((x & 0x8000) == 0) + lg2 += 1, x <<= 1; + + /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional + * value. + */ + lg2 <<= 28; + lg2 += (png_8bit_l2[(x>>8)-128]+8) >> 4; + + /* Now we need to interpolate the factor, this requires a division by the top + * 8 bits. Do this with maximum precision. + */ + x = ((x << 16) + (x >> 9)) / (x >> 8); + + /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24, + * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us exactly + * 16 bits to interpolate to get the low bits of the result. Round the + * answer. Note that the end point values are scaled by 64 to retain overall + * precision and that 'lg2' is current scaled by an extra 12 bits, so adjust + * the overall scaling by 6-12. Round at every step. + */ + x -= 1U << 24; + + if (x <= 65536U) /* <= '257' */ + lg2 += ((23591U * (65536U-x)) + (1U << (16+6-12-1))) >> (16+6-12); + + else + lg2 -= ((23499U * (x-65536U)) + (1U << (16+6-12-1))) >> (16+6-12); + + /* Safe, because the result can't have more than 20 bits: */ + return (png_int_32)((lg2 + 2048) >> 12); +} +#endif /* 16BIT */ + +/* The 'exp()' case must invert the above, taking a 20-bit fixed point + * logarithmic value and returning a 16 or 8-bit number as appropriate. In + * each case only the low 16 bits are relevant - the fraction - since the + * integer bits (the top 4) simply determine a shift. + * + * The worst case is the 16-bit distinction between 65535 and 65534. This + * requires perhaps spurious accuracy in the decoding of the logarithm to + * distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance + * of getting this accuracy in practice. + * + * To deal with this the following exp() function works out the exponent of the + * fractional part of the logarithm by using an accurate 32-bit value from the + * top four fractional bits then multiplying in the remaining bits. + */ +static const png_uint_32 +png_32bit_exp[16] = +{ + /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */ + 4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U, + 3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U, + 2553802834U, 2445529972U, 2341847524U, 2242560872U +}; + +/* Adjustment table; provided to explain the numbers in the code below. */ +#if 0 +for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"} + 11 44937.64284865548751208448 + 10 45180.98734845585101160448 + 9 45303.31936980687359311872 + 8 45364.65110595323018870784 + 7 45395.35850361789624614912 + 6 45410.72259715102037508096 + 5 45418.40724413220722311168 + 4 45422.25021786898173001728 + 3 45424.17186732298419044352 + 2 45425.13273269940811464704 + 1 45425.61317555035558641664 + 0 45425.85339951654943850496 +#endif + +static png_uint_32 +png_exp(png_fixed_point x) +{ + if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */ + { + /* Obtain a 4-bit approximation */ + png_uint_32 e = png_32bit_exp[(x >> 12) & 0x0f]; + + /* Incorporate the low 12 bits - these decrease the returned value by + * multiplying by a number less than 1 if the bit is set. The multiplier + * is determined by the above table and the shift. Notice that the values + * converge on 45426 and this is used to allow linear interpolation of the + * low bits. + */ + if (x & 0x800) + e -= (((e >> 16) * 44938U) + 16U) >> 5; + + if (x & 0x400) + e -= (((e >> 16) * 45181U) + 32U) >> 6; + + if (x & 0x200) + e -= (((e >> 16) * 45303U) + 64U) >> 7; + + if (x & 0x100) + e -= (((e >> 16) * 45365U) + 128U) >> 8; + + if (x & 0x080) + e -= (((e >> 16) * 45395U) + 256U) >> 9; + + if (x & 0x040) + e -= (((e >> 16) * 45410U) + 512U) >> 10; + + /* And handle the low 6 bits in a single block. */ + e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9; + + /* Handle the upper bits of x. */ + e >>= x >> 16; + return e; + } + + /* Check for overflow */ + if (x <= 0) + return png_32bit_exp[0]; + + /* Else underflow */ + return 0; +} + +static png_byte +png_exp8bit(png_fixed_point lg2) +{ + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..255 by multiplying by 256-1. Note that the + * second, rounding, step can't overflow because of the first, subtraction, + * step. + */ + x -= x >> 8; + return (png_byte)(((x + 0x7fffffU) >> 24) & 0xff); +} + +#ifdef PNG_16BIT_SUPPORTED +static png_uint_16 +png_exp16bit(png_fixed_point lg2) +{ + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */ + x -= x >> 16; + return (png_uint_16)((x + 32767U) >> 16); +} +#endif /* 16BIT */ +#endif /* FLOATING_ARITHMETIC */ + +png_byte +png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val) +{ + if (value > 0 && value < 255) + { +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + /* 'value' is unsigned, ANSI-C90 requires the compiler to correctly + * convert this to a floating point value. This includes values that + * would overflow if 'value' were to be converted to 'int'. + * + * Apparently GCC, however, does an intermediate conversion to (int) + * on some (ARM) but not all (x86) platforms, possibly because of + * hardware FP limitations. (E.g. if the hardware conversion always + * assumes the integer register contains a signed value.) This results + * in ANSI-C undefined behavior for large values. + * + * Other implementations on the same machine might actually be ANSI-C90 + * conformant and therefore compile spurious extra code for the large + * values. + * + * We can be reasonably sure that an unsigned to float conversion + * won't be faster than an int to float one. Therefore this code + * assumes responsibility for the undefined behavior, which it knows + * can't happen because of the check above. + * + * Note the argument to this routine is an (unsigned int) because, on + * 16-bit platforms, it is assigned a value which might be out of + * range for an (int); that would result in undefined behavior in the + * caller if the *argument* ('value') were to be declared (int). + */ + double r = floor(255*pow((int)/*SAFE*/value/255.,gamma_val*.00001)+.5); + return (png_byte)r; +# else + png_int_32 lg2 = png_log8bit(value); + png_fixed_point res; + + if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1) != 0) + return png_exp8bit(res); + + /* Overflow. */ + value = 0; +# endif + } + + return (png_byte)(value & 0xff); +} + +#ifdef PNG_16BIT_SUPPORTED +png_uint_16 +png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val) +{ + if (value > 0 && value < 65535) + { +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + /* The same (unsigned int)->(double) constraints apply here as above, + * however in this case the (unsigned int) to (int) conversion can + * overflow on an ANSI-C90 compliant system so the cast needs to ensure + * that this is not possible. + */ + double r = floor(65535*pow((png_int_32)value/65535., + gamma_val*.00001)+.5); + return (png_uint_16)r; +# else + png_int_32 lg2 = png_log16bit(value); + png_fixed_point res; + + if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1) != 0) + return png_exp16bit(res); + + /* Overflow. */ + value = 0; +# endif + } + + return (png_uint_16)value; +} +#endif /* 16BIT */ + +/* This does the right thing based on the bit_depth field of the + * png_struct, interpreting values as 8-bit or 16-bit. While the result + * is nominally a 16-bit value if bit depth is 8 then the result is + * 8-bit (as are the arguments.) + */ +png_uint_16 /* PRIVATE */ +png_gamma_correct(png_structrp png_ptr, unsigned int value, + png_fixed_point gamma_val) +{ + if (png_ptr->bit_depth == 8) + return png_gamma_8bit_correct(value, gamma_val); + +#ifdef PNG_16BIT_SUPPORTED + else + return png_gamma_16bit_correct(value, gamma_val); +#else + /* should not reach this */ + return 0; +#endif /* 16BIT */ +} + +#ifdef PNG_16BIT_SUPPORTED +/* Internal function to build a single 16-bit table - the table consists of + * 'num' 256 entry subtables, where 'num' is determined by 'shift' - the amount + * to shift the input values right (or 16-number_of_signifiant_bits). + * + * The caller is responsible for ensuring that the table gets cleaned up on + * png_error (i.e. if one of the mallocs below fails) - i.e. the *table argument + * should be somewhere that will be cleaned. + */ +static void +png_build_16bit_table(png_structrp png_ptr, png_uint_16pp *ptable, + PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val) +{ + /* Various values derived from 'shift': */ + PNG_CONST unsigned int num = 1U << (8U - shift); +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + /* CSE the division and work round wacky GCC warnings (see the comments + * in png_gamma_8bit_correct for where these come from.) + */ + PNG_CONST double fmax = 1./(((png_int_32)1 << (16U - shift))-1); +#endif + PNG_CONST unsigned int max = (1U << (16U - shift))-1U; + PNG_CONST unsigned int max_by_2 = 1U << (15U-shift); + unsigned int i; + + png_uint_16pp table = *ptable = + (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p))); + + for (i = 0; i < num; i++) + { + png_uint_16p sub_table = table[i] = + (png_uint_16p)png_malloc(png_ptr, 256 * (sizeof (png_uint_16))); + + /* The 'threshold' test is repeated here because it can arise for one of + * the 16-bit tables even if the others don't hit it. + */ + if (png_gamma_significant(gamma_val) != 0) + { + /* The old code would overflow at the end and this would cause the + * 'pow' function to return a result >1, resulting in an + * arithmetic error. This code follows the spec exactly; ig is + * the recovered input sample, it always has 8-16 bits. + * + * We want input * 65535/max, rounded, the arithmetic fits in 32 + * bits (unsigned) so long as max <= 32767. + */ + unsigned int j; + for (j = 0; j < 256; j++) + { + png_uint_32 ig = (j << (8-shift)) + i; +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + /* Inline the 'max' scaling operation: */ + /* See png_gamma_8bit_correct for why the cast to (int) is + * required here. + */ + double d = floor(65535.*pow(ig*fmax, gamma_val*.00001)+.5); + sub_table[j] = (png_uint_16)d; +# else + if (shift != 0) + ig = (ig * 65535U + max_by_2)/max; + + sub_table[j] = png_gamma_16bit_correct(ig, gamma_val); +# endif + } + } + else + { + /* We must still build a table, but do it the fast way. */ + unsigned int j; + + for (j = 0; j < 256; j++) + { + png_uint_32 ig = (j << (8-shift)) + i; + + if (shift != 0) + ig = (ig * 65535U + max_by_2)/max; + + sub_table[j] = (png_uint_16)ig; + } + } + } +} + +/* NOTE: this function expects the *inverse* of the overall gamma transformation + * required. + */ +static void +png_build_16to8_table(png_structrp png_ptr, png_uint_16pp *ptable, + PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val) +{ + PNG_CONST unsigned int num = 1U << (8U - shift); + PNG_CONST unsigned int max = (1U << (16U - shift))-1U; + unsigned int i; + png_uint_32 last; + + png_uint_16pp table = *ptable = + (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p))); + + /* 'num' is the number of tables and also the number of low bits of low + * bits of the input 16-bit value used to select a table. Each table is + * itself indexed by the high 8 bits of the value. + */ + for (i = 0; i < num; i++) + table[i] = (png_uint_16p)png_malloc(png_ptr, + 256 * (sizeof (png_uint_16))); + + /* 'gamma_val' is set to the reciprocal of the value calculated above, so + * pow(out,g) is an *input* value. 'last' is the last input value set. + * + * In the loop 'i' is used to find output values. Since the output is + * 8-bit there are only 256 possible values. The tables are set up to + * select the closest possible output value for each input by finding + * the input value at the boundary between each pair of output values + * and filling the table up to that boundary with the lower output + * value. + * + * The boundary values are 0.5,1.5..253.5,254.5. Since these are 9-bit + * values the code below uses a 16-bit value in i; the values start at + * 128.5 (for 0.5) and step by 257, for a total of 254 values (the last + * entries are filled with 255). Start i at 128 and fill all 'last' + * table entries <= 'max' + */ + last = 0; + for (i = 0; i < 255; ++i) /* 8-bit output value */ + { + /* Find the corresponding maximum input value */ + png_uint_16 out = (png_uint_16)(i * 257U); /* 16-bit output value */ + + /* Find the boundary value in 16 bits: */ + png_uint_32 bound = png_gamma_16bit_correct(out+128U, gamma_val); + + /* Adjust (round) to (16-shift) bits: */ + bound = (bound * max + 32768U)/65535U + 1U; + + while (last < bound) + { + table[last & (0xffU >> shift)][last >> (8U - shift)] = out; + last++; + } + } + + /* And fill in the final entries. */ + while (last < (num << 8)) + { + table[last & (0xff >> shift)][last >> (8U - shift)] = 65535U; + last++; + } +} +#endif /* 16BIT */ + +/* Build a single 8-bit table: same as the 16-bit case but much simpler (and + * typically much faster). Note that libpng currently does no sBIT processing + * (apparently contrary to the spec) so a 256-entry table is always generated. + */ +static void +png_build_8bit_table(png_structrp png_ptr, png_bytepp ptable, + PNG_CONST png_fixed_point gamma_val) +{ + unsigned int i; + png_bytep table = *ptable = (png_bytep)png_malloc(png_ptr, 256); + + if (png_gamma_significant(gamma_val) != 0) + for (i=0; i<256; i++) + table[i] = png_gamma_8bit_correct(i, gamma_val); + + else + for (i=0; i<256; ++i) + table[i] = (png_byte)(i & 0xff); +} + +/* Used from png_read_destroy and below to release the memory used by the gamma + * tables. + */ +void /* PRIVATE */ +png_destroy_gamma_table(png_structrp png_ptr) +{ + png_free(png_ptr, png_ptr->gamma_table); + png_ptr->gamma_table = NULL; + +#ifdef PNG_16BIT_SUPPORTED + if (png_ptr->gamma_16_table != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_table[i]); + } + png_free(png_ptr, png_ptr->gamma_16_table); + png_ptr->gamma_16_table = NULL; + } +#endif /* 16BIT */ + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_from_1); + png_ptr->gamma_from_1 = NULL; + png_free(png_ptr, png_ptr->gamma_to_1); + png_ptr->gamma_to_1 = NULL; + +#ifdef PNG_16BIT_SUPPORTED + if (png_ptr->gamma_16_from_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_from_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_from_1); + png_ptr->gamma_16_from_1 = NULL; + } + if (png_ptr->gamma_16_to_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_to_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_to_1); + png_ptr->gamma_16_to_1 = NULL; + } +#endif /* 16BIT */ +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +} + +/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit + * tables, we don't make a full table if we are reducing to 8-bit in + * the future. Note also how the gamma_16 tables are segmented so that + * we don't need to allocate > 64K chunks for a full 16-bit table. + */ +void /* PRIVATE */ +png_build_gamma_table(png_structrp png_ptr, int bit_depth) +{ + png_debug(1, "in png_build_gamma_table"); + + /* Remove any existing table; this copes with multiple calls to + * png_read_update_info. The warning is because building the gamma tables + * multiple times is a performance hit - it's harmless but the ability to + * call png_read_update_info() multiple times is new in 1.5.6 so it seems + * sensible to warn if the app introduces such a hit. + */ + if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL) + { + png_warning(png_ptr, "gamma table being rebuilt"); + png_destroy_gamma_table(png_ptr); + } + + if (bit_depth <= 8) + { + png_build_8bit_table(png_ptr, &png_ptr->gamma_table, + png_ptr->screen_gamma > 0 ? + png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma) : PNG_FP_1); + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0) + { + png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1, + png_reciprocal(png_ptr->colorspace.gamma)); + + png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1, + png_ptr->screen_gamma > 0 ? + png_reciprocal(png_ptr->screen_gamma) : + png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + } +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ + } +#ifdef PNG_16BIT_SUPPORTED + else + { + png_byte shift, sig_bit; + + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) + { + sig_bit = png_ptr->sig_bit.red; + + if (png_ptr->sig_bit.green > sig_bit) + sig_bit = png_ptr->sig_bit.green; + + if (png_ptr->sig_bit.blue > sig_bit) + sig_bit = png_ptr->sig_bit.blue; + } + else + sig_bit = png_ptr->sig_bit.gray; + + /* 16-bit gamma code uses this equation: + * + * ov = table[(iv & 0xff) >> gamma_shift][iv >> 8] + * + * Where 'iv' is the input color value and 'ov' is the output value - + * pow(iv, gamma). + * + * Thus the gamma table consists of up to 256 256-entry tables. The table + * is selected by the (8-gamma_shift) most significant of the low 8 bits + * of the color value then indexed by the upper 8 bits: + * + * table[low bits][high 8 bits] + * + * So the table 'n' corresponds to all those 'iv' of: + * + * ..<(n+1 << gamma_shift)-1> + * + */ + if (sig_bit > 0 && sig_bit < 16U) + /* shift == insignificant bits */ + shift = (png_byte)((16U - sig_bit) & 0xff); + + else + shift = 0; /* keep all 16 bits */ + + if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0) + { + /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively + * the significant bits in the *input* when the output will + * eventually be 8 bits. By default it is 11. + */ + if (shift < (16U - PNG_MAX_GAMMA_8)) + shift = (16U - PNG_MAX_GAMMA_8); + } + + if (shift > 8U) + shift = 8U; /* Guarantees at least one table! */ + + png_ptr->gamma_shift = shift; + + /* NOTE: prior to 1.5.4 this test used to include PNG_BACKGROUND (now + * PNG_COMPOSE). This effectively smashed the background calculation for + * 16-bit output because the 8-bit table assumes the result will be + * reduced to 8 bits. + */ + if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0) + png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift, + png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma) : PNG_FP_1); + + else + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift, + png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma) : PNG_FP_1); + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0) + { + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift, + png_reciprocal(png_ptr->colorspace.gamma)); + + /* Notice that the '16 from 1' table should be full precision, however + * the lookup on this table still uses gamma_shift, so it can't be. + * TODO: fix this. + */ + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift, + png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : + png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + } +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ + } +#endif /* 16BIT */ +} +#endif /* READ_GAMMA */ + +/* HARDWARE OR SOFTWARE OPTION SUPPORT */ +#ifdef PNG_SET_OPTION_SUPPORTED +int PNGAPI +png_set_option(png_structrp png_ptr, int option, int onoff) +{ + if (png_ptr != NULL && option >= 0 && option < PNG_OPTION_NEXT && + (option & 1) == 0) + { + png_uint_32 mask = 3U << option; + png_uint_32 setting = (2U + (onoff != 0)) << option; + png_uint_32 current = png_ptr->options; + + png_ptr->options = (png_uint_32)(((current & ~mask) | setting) & 0xff); + + return (int)(current & mask) >> option; + } + + return PNG_OPTION_INVALID; +} +#endif + +/* sRGB support */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +/* sRGB conversion tables; these are machine generated with the code in + * contrib/tools/makesRGB.c. The actual sRGB transfer curve defined in the + * specification (see the article at https://en.wikipedia.org/wiki/SRGB) + * is used, not the gamma=1/2.2 approximation use elsewhere in libpng. + * The sRGB to linear table is exact (to the nearest 16-bit linear fraction). + * The inverse (linear to sRGB) table has accuracies as follows: + * + * For all possible (255*65535+1) input values: + * + * error: -0.515566 - 0.625971, 79441 (0.475369%) of readings inexact + * + * For the input values corresponding to the 65536 16-bit values: + * + * error: -0.513727 - 0.607759, 308 (0.469978%) of readings inexact + * + * In all cases the inexact readings are only off by one. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* The convert-to-sRGB table is only currently required for read. */ +const png_uint_16 png_sRGB_table[256] = +{ + 0,20,40,60,80,99,119,139, + 159,179,199,219,241,264,288,313, + 340,367,396,427,458,491,526,562, + 599,637,677,718,761,805,851,898, + 947,997,1048,1101,1156,1212,1270,1330, + 1391,1453,1517,1583,1651,1720,1790,1863, + 1937,2013,2090,2170,2250,2333,2418,2504, + 2592,2681,2773,2866,2961,3058,3157,3258, + 3360,3464,3570,3678,3788,3900,4014,4129, + 4247,4366,4488,4611,4736,4864,4993,5124, + 5257,5392,5530,5669,5810,5953,6099,6246, + 6395,6547,6700,6856,7014,7174,7335,7500, + 7666,7834,8004,8177,8352,8528,8708,8889, + 9072,9258,9445,9635,9828,10022,10219,10417, + 10619,10822,11028,11235,11446,11658,11873,12090, + 12309,12530,12754,12980,13209,13440,13673,13909, + 14146,14387,14629,14874,15122,15371,15623,15878, + 16135,16394,16656,16920,17187,17456,17727,18001, + 18277,18556,18837,19121,19407,19696,19987,20281, + 20577,20876,21177,21481,21787,22096,22407,22721, + 23038,23357,23678,24002,24329,24658,24990,25325, + 25662,26001,26344,26688,27036,27386,27739,28094, + 28452,28813,29176,29542,29911,30282,30656,31033, + 31412,31794,32179,32567,32957,33350,33745,34143, + 34544,34948,35355,35764,36176,36591,37008,37429, + 37852,38278,38706,39138,39572,40009,40449,40891, + 41337,41785,42236,42690,43147,43606,44069,44534, + 45002,45473,45947,46423,46903,47385,47871,48359, + 48850,49344,49841,50341,50844,51349,51858,52369, + 52884,53401,53921,54445,54971,55500,56032,56567, + 57105,57646,58190,58737,59287,59840,60396,60955, + 61517,62082,62650,63221,63795,64372,64952,65535 +}; +#endif /* SIMPLIFIED_READ */ + +/* The base/delta tables are required for both read and write (but currently + * only the simplified versions.) + */ +const png_uint_16 png_sRGB_base[512] = +{ + 128,1782,3383,4644,5675,6564,7357,8074, + 8732,9346,9921,10463,10977,11466,11935,12384, + 12816,13233,13634,14024,14402,14769,15125,15473, + 15812,16142,16466,16781,17090,17393,17690,17981, + 18266,18546,18822,19093,19359,19621,19879,20133, + 20383,20630,20873,21113,21349,21583,21813,22041, + 22265,22487,22707,22923,23138,23350,23559,23767, + 23972,24175,24376,24575,24772,24967,25160,25352, + 25542,25730,25916,26101,26284,26465,26645,26823, + 27000,27176,27350,27523,27695,27865,28034,28201, + 28368,28533,28697,28860,29021,29182,29341,29500, + 29657,29813,29969,30123,30276,30429,30580,30730, + 30880,31028,31176,31323,31469,31614,31758,31902, + 32045,32186,32327,32468,32607,32746,32884,33021, + 33158,33294,33429,33564,33697,33831,33963,34095, + 34226,34357,34486,34616,34744,34873,35000,35127, + 35253,35379,35504,35629,35753,35876,35999,36122, + 36244,36365,36486,36606,36726,36845,36964,37083, + 37201,37318,37435,37551,37668,37783,37898,38013, + 38127,38241,38354,38467,38580,38692,38803,38915, + 39026,39136,39246,39356,39465,39574,39682,39790, + 39898,40005,40112,40219,40325,40431,40537,40642, + 40747,40851,40955,41059,41163,41266,41369,41471, + 41573,41675,41777,41878,41979,42079,42179,42279, + 42379,42478,42577,42676,42775,42873,42971,43068, + 43165,43262,43359,43456,43552,43648,43743,43839, + 43934,44028,44123,44217,44311,44405,44499,44592, + 44685,44778,44870,44962,45054,45146,45238,45329, + 45420,45511,45601,45692,45782,45872,45961,46051, + 46140,46229,46318,46406,46494,46583,46670,46758, + 46846,46933,47020,47107,47193,47280,47366,47452, + 47538,47623,47709,47794,47879,47964,48048,48133, + 48217,48301,48385,48468,48552,48635,48718,48801, + 48884,48966,49048,49131,49213,49294,49376,49458, + 49539,49620,49701,49782,49862,49943,50023,50103, + 50183,50263,50342,50422,50501,50580,50659,50738, + 50816,50895,50973,51051,51129,51207,51285,51362, + 51439,51517,51594,51671,51747,51824,51900,51977, + 52053,52129,52205,52280,52356,52432,52507,52582, + 52657,52732,52807,52881,52956,53030,53104,53178, + 53252,53326,53400,53473,53546,53620,53693,53766, + 53839,53911,53984,54056,54129,54201,54273,54345, + 54417,54489,54560,54632,54703,54774,54845,54916, + 54987,55058,55129,55199,55269,55340,55410,55480, + 55550,55620,55689,55759,55828,55898,55967,56036, + 56105,56174,56243,56311,56380,56448,56517,56585, + 56653,56721,56789,56857,56924,56992,57059,57127, + 57194,57261,57328,57395,57462,57529,57595,57662, + 57728,57795,57861,57927,57993,58059,58125,58191, + 58256,58322,58387,58453,58518,58583,58648,58713, + 58778,58843,58908,58972,59037,59101,59165,59230, + 59294,59358,59422,59486,59549,59613,59677,59740, + 59804,59867,59930,59993,60056,60119,60182,60245, + 60308,60370,60433,60495,60558,60620,60682,60744, + 60806,60868,60930,60992,61054,61115,61177,61238, + 61300,61361,61422,61483,61544,61605,61666,61727, + 61788,61848,61909,61969,62030,62090,62150,62211, + 62271,62331,62391,62450,62510,62570,62630,62689, + 62749,62808,62867,62927,62986,63045,63104,63163, + 63222,63281,63340,63398,63457,63515,63574,63632, + 63691,63749,63807,63865,63923,63981,64039,64097, + 64155,64212,64270,64328,64385,64443,64500,64557, + 64614,64672,64729,64786,64843,64900,64956,65013, + 65070,65126,65183,65239,65296,65352,65409,65465 +}; + +const png_byte png_sRGB_delta[512] = +{ + 207,201,158,129,113,100,90,82,77,72,68,64,61,59,56,54, + 52,50,49,47,46,45,43,42,41,40,39,39,38,37,36,36, + 35,34,34,33,33,32,32,31,31,30,30,30,29,29,28,28, + 28,27,27,27,27,26,26,26,25,25,25,25,24,24,24,24, + 23,23,23,23,23,22,22,22,22,22,22,21,21,21,21,21, + 21,20,20,20,20,20,20,20,20,19,19,19,19,19,19,19, + 19,18,18,18,18,18,18,18,18,18,18,17,17,17,17,17, + 17,17,17,17,17,17,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; +#endif /* SIMPLIFIED READ/WRITE sRGB support */ + +/* SIMPLIFIED READ/WRITE SUPPORT */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +static int +png_image_free_function(png_voidp argument) +{ + png_imagep image = png_voidcast(png_imagep, argument); + png_controlp cp = image->opaque; + png_control c; + + /* Double check that we have a png_ptr - it should be impossible to get here + * without one. + */ + if (cp->png_ptr == NULL) + return 0; + + /* First free any data held in the control structure. */ +# ifdef PNG_STDIO_SUPPORTED + if (cp->owned_file != 0) + { + FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr); + cp->owned_file = 0; + + /* Ignore errors here. */ + if (fp != NULL) + { + cp->png_ptr->io_ptr = NULL; + (void)fclose(fp); + } + } +# endif + + /* Copy the control structure so that the original, allocated, version can be + * safely freed. Notice that a png_error here stops the remainder of the + * cleanup, but this is probably fine because that would indicate bad memory + * problems anyway. + */ + c = *cp; + image->opaque = &c; + png_free(c.png_ptr, cp); + + /* Then the structures, calling the correct API. */ + if (c.for_write != 0) + { +# ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED + png_destroy_write_struct(&c.png_ptr, &c.info_ptr); +# else + png_error(c.png_ptr, "simplified write not supported"); +# endif + } + else + { +# ifdef PNG_SIMPLIFIED_READ_SUPPORTED + png_destroy_read_struct(&c.png_ptr, &c.info_ptr, NULL); +# else + png_error(c.png_ptr, "simplified read not supported"); +# endif + } + + /* Success. */ + return 1; +} + +void PNGAPI +png_image_free(png_imagep image) +{ + /* Safely call the real function, but only if doing so is safe at this point + * (if not inside an error handling context). Otherwise assume + * png_safe_execute will call this API after the return. + */ + if (image != NULL && image->opaque != NULL && + image->opaque->error_buf == NULL) + { + /* Ignore errors here: */ + (void)png_safe_execute(image, png_image_free_function, image); + image->opaque = NULL; + } +} + +int /* PRIVATE */ +png_image_error(png_imagep image, png_const_charp error_message) +{ + /* Utility to log an error. */ + png_safecat(image->message, (sizeof image->message), 0, error_message); + image->warning_or_error |= PNG_IMAGE_ERROR; + png_image_free(image); + return 0; +} + +#endif /* SIMPLIFIED READ/WRITE */ +#endif /* READ || WRITE */ diff --git a/src/png/libpng/png.h b/src/png/libpng/png.h new file mode 100644 index 0000000000..4c873f5c22 --- /dev/null +++ b/src/png/libpng/png.h @@ -0,0 +1,3278 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.34, September 29, 2017 + * + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license (See LICENSE, below) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.34, September 29, 2017: + * Glenn Randers-Pehrson. + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: + * + * If you modify libpng you may insert additional notices immediately following + * this sentence. + * + * This code is released under the libpng license. + * + * libpng versions 1.0.7, July 1, 2000 through 1.6.34, September 29, 2017 are + * Copyright (c) 2000-2002, 2004, 2006-2017 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of the + * library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is with + * the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the list + * of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing Authors + * and Group 42, Inc. disclaim all warranties, expressed or implied, + * including, without limitation, the warranties of merchantability and of + * fitness for any purpose. The Contributing Authors and Group 42, Inc. + * assume no liability for direct, indirect, incidental, special, exemplary, + * or consequential damages, which may result from the use of the PNG + * Reference Library, even if advised of the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, without + * fee, and encourage the use of this source code as a component to + * supporting the PNG file format in commercial products. If you use this + * source code in a product, acknowledgment is not required but would be + * appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK: + * + * The name "libpng" has not been registered by the Copyright owner + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owner claims "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + * + * OSI CERTIFICATION: + * + * Libpng is OSI Certified Open Source Software. OSI Certified Open Source is + * a certification mark of the Open Source Initiative. OSI has not addressed + * the additional disclaimers inserted at version 1.0.7. + * + * EXPORT CONTROL: + * + * The Copyright owner believes that the Export Control Classification + * Number (ECCN) for libpng is EAR99, which means not subject to export + * controls or International Traffic in Arms Regulations (ITAR) because + * it is open source, publicly available software, that does not contain + * any encryption software. See the EAR, paragraphs 734.3(b)(3) and + * 734.7(b). + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.19 10 10019 10.so.0.19[.0] + * ... + * 1.2.59 13 10257 12.so.0.59[.0] + * ... + * 1.5.30 15 10527 15.so.15.30[.0] + * ... + * 1.6.34 16 10633 16.so.16.34[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.34" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.34 - September 29, 2017\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 34 + +/* This should match the numeric part of the final component of + * PNG_LIBPNG_VER_STRING, omitting any leading zero: + */ + +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that would be octal. + * We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here (only + * version 1.0.0 was mis-numbered 100 instead of 10000). From + * version 1.0.1 it's xxyyzz, where x=major, y=minor, z=release + */ +#define PNG_LIBPNG_VER 10634 /* 1.6.34 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_34; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + png_size_t text_length; /* length of the text string */ + png_size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + png_size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((png_size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, png_size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, png_size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + png_size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + png_size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and reencode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceeded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the ouput gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WTIH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, png_size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, png_size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, const png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, const png_uint_32 num_exif, const png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WTIH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accomodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at https://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates the the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, png_size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occured during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilites, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/src/png/libpng/pngconf.h b/src/png/libpng/pngconf.h new file mode 100644 index 0000000000..d13b13e57a --- /dev/null +++ b/src/png/libpng/pngconf.h @@ -0,0 +1,622 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.6.34, September 29, 2017 + * + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0 it was possible to turn off 'const' in declarations using + * PNG_NO_CONST; this is no longer supported except for data declarations which + * apparently still cause problems in 2011 on some compilers. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit values + * from PNG files. It can be set on a per-app-file basis - it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows sytems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\ + defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0 it was possible to disable the use of size_t, 1.6.0, however, + * requires an ISOC90 compiler and relies on consistent behavior of sizeof. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than png_size_t, and no + * smaller than png_uint_32. Casts from png_size_t or png_uint_32 to + * png_alloc_size_t are not necessary; in fact, it is recommended not to use + * them at all so that the compiler can complain when something turns out to be + * problematic. + * + * Casts in the other direction (from png_alloc_size_t to png_size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef png_size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef png_size_t * png_size_tp; +typedef const png_size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/src/png/libpng/pngdebug.h b/src/png/libpng/pngdebug.h new file mode 100644 index 0000000000..15a7ed0c95 --- /dev/null +++ b/src/png/libpng/pngdebug.h @@ -0,0 +1,153 @@ + +/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c + * + * Last changed in libpng 1.6.8 [December 19, 2013] + * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + * + * png_debug[1-2]?(level, message ,arg{0-2}) + * Expands to a statement (either a simple expression or a compound + * do..while(0) statement) that outputs a message with parameter + * substitution if PNG_DEBUG is defined to 2 or more. If PNG_DEBUG + * is undefined, 0 or 1 every png_debug expands to a simple expression + * (actually ((void)0)). + * + * level: level of detail of message, starting at 0. A level 'n' + * message is preceded by 'n' 3-space indentations (not implemented + * on Microsoft compilers unless PNG_DEBUG_FILE is also + * defined, to allow debug DLL compilation with no standard IO). + * message: a printf(3) style text string. A trailing '\n' is added + * to the message. + * arg: 0 to 2 arguments for printf(3) style substitution in message. + */ +#ifndef PNGDEBUG_H +#define PNGDEBUG_H +/* These settings control the formatting of messages in png.c and pngerror.c */ +/* Moved to pngdebug.h at 1.5.0 */ +# ifndef PNG_LITERAL_SHARP +# define PNG_LITERAL_SHARP 0x23 +# endif +# ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET +# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b +# endif +# ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET +# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d +# endif +# ifndef PNG_STRING_NEWLINE +# define PNG_STRING_NEWLINE "\n" +# endif + +#ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +# include +# if (PNG_DEBUG > 1) +# ifndef _DEBUG +# define _DEBUG +# endif +# ifndef png_debug +# define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE) +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1) +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2) +# endif +# endif +# else /* PNG_DEBUG_FILE || !_MSC_VER */ +# ifndef PNG_STDIO_SUPPORTED +# include /* not included yet */ +# endif +# ifndef PNG_DEBUG_FILE +# define PNG_DEBUG_FILE stderr +# endif /* PNG_DEBUG_FILE */ + +# if (PNG_DEBUG > 1) +# ifdef __STDC__ +# ifndef png_debug +# define png_debug(l,m) \ + do { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \ + (num_tabs==2 ? " " : (num_tabs>2 ? " " : "")))); \ + } while (0) +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) \ + do { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \ + (num_tabs==2 ? " " : (num_tabs>2 ? " " : ""))),p1); \ + } while (0) +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + do { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \ + (num_tabs==2 ? " " : (num_tabs>2 ? " " : ""))),p1,p2);\ + } while (0) +# endif +# else /* __STDC __ */ +# ifndef png_debug +# define png_debug(l,m) \ + do { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format); \ + } while (0) +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) \ + do { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1); \ + } while (0) +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + do { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1,p2); \ + } while (0) +# endif +# endif /* __STDC __ */ +# endif /* (PNG_DEBUG > 1) */ + +# endif /* _MSC_VER */ +# endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +# define png_debug(l, m) ((void)0) +#endif +#ifndef png_debug1 +# define png_debug1(l, m, p1) ((void)0) +#endif +#ifndef png_debug2 +# define png_debug2(l, m, p1, p2) ((void)0) +#endif +#endif /* PNGDEBUG_H */ diff --git a/src/png/libpng/pngerror.c b/src/png/libpng/pngerror.c new file mode 100644 index 0000000000..ad48bfb986 --- /dev/null +++ b/src/png/libpng/pngerror.c @@ -0,0 +1,963 @@ + +/* pngerror.c - stub functions for i/o and memory allocation + * + * Last changed in libpng 1.6.31 [July 27, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all error handling. Users who + * need special error handling are expected to write replacement functions + * and use png_set_error_fn() to use those functions. See the instructions + * at each function. + */ + +#include "pngpriv.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr, + png_const_charp error_message)),PNG_NORETURN); + +#ifdef PNG_WARNINGS_SUPPORTED +static void /* PRIVATE */ +png_default_warning PNGARG((png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif /* WARNINGS */ + +/* This function is called whenever there is a fatal error. This function + * should not be changed. If there is a need to handle errors differently, + * you should supply a replacement error function and use png_set_error_fn() + * to replace the error function at run-time. + */ +#ifdef PNG_ERROR_TEXT_SUPPORTED +PNG_FUNCTION(void,PNGAPI +png_error,(png_const_structrp png_ptr, png_const_charp error_message), + PNG_NORETURN) +{ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + char msg[16]; + if (png_ptr != NULL) + { + if ((png_ptr->flags & + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0) + { + if (*error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + for (offset = 1; offset<15; offset++) + if (error_message[offset] == ' ') + break; + + if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0) + { + int i; + for (i = 0; i < offset - 1; i++) + msg[i] = error_message[i + 1]; + msg[i - 1] = '\0'; + error_message = msg; + } + + else + error_message += offset; + } + + else + { + if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0) + { + msg[0] = '0'; + msg[1] = '\0'; + error_message = msg; + } + } + } + } +#endif + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), + error_message); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, error_message); +} +#else +PNG_FUNCTION(void,PNGAPI +png_err,(png_const_structrp png_ptr),PNG_NORETURN) +{ + /* Prior to 1.5.2 the error_fn received a NULL pointer, expressed + * erroneously as '\0', instead of the empty string "". This was + * apparently an error, introduced in libpng-1.2.20, and png_default_error + * will crash in this case. + */ + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), ""); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, ""); +} +#endif /* ERROR_TEXT */ + +/* Utility to safely appends strings to a buffer. This never errors out so + * error checking is not required in the caller. + */ +size_t +png_safecat(png_charp buffer, size_t bufsize, size_t pos, + png_const_charp string) +{ + if (buffer != NULL && pos < bufsize) + { + if (string != NULL) + while (*string != '\0' && pos < bufsize-1) + buffer[pos++] = *string++; + + buffer[pos] = '\0'; + } + + return pos; +} + +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED) +/* Utility to dump an unsigned value into a buffer, given a start pointer and + * and end pointer (which should point just *beyond* the end of the buffer!) + * Returns the pointer to the start of the formatted string. + */ +png_charp +png_format_number(png_const_charp start, png_charp end, int format, + png_alloc_size_t number) +{ + int count = 0; /* number of digits output */ + int mincount = 1; /* minimum number required */ + int output = 0; /* digit output (for the fixed point format) */ + + *--end = '\0'; + + /* This is written so that the loop always runs at least once, even with + * number zero. + */ + while (end > start && (number != 0 || count < mincount)) + { + + static const char digits[] = "0123456789ABCDEF"; + + switch (format) + { + case PNG_NUMBER_FORMAT_fixed: + /* Needs five digits (the fraction) */ + mincount = 5; + if (output != 0 || number % 10 != 0) + { + *--end = digits[number % 10]; + output = 1; + } + number /= 10; + break; + + case PNG_NUMBER_FORMAT_02u: + /* Expects at least 2 digits. */ + mincount = 2; + /* FALLTHROUGH */ + + case PNG_NUMBER_FORMAT_u: + *--end = digits[number % 10]; + number /= 10; + break; + + case PNG_NUMBER_FORMAT_02x: + /* This format expects at least two digits */ + mincount = 2; + /* FALLTHROUGH */ + + case PNG_NUMBER_FORMAT_x: + *--end = digits[number & 0xf]; + number >>= 4; + break; + + default: /* an error */ + number = 0; + break; + } + + /* Keep track of the number of digits added */ + ++count; + + /* Float a fixed number here: */ + if ((format == PNG_NUMBER_FORMAT_fixed) && (count == 5) && (end > start)) + { + /* End of the fraction, but maybe nothing was output? In that case + * drop the decimal point. If the number is a true zero handle that + * here. + */ + if (output != 0) + *--end = '.'; + else if (number == 0) /* and !output */ + *--end = '0'; + } + } + + return end; +} +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* This function is called whenever there is a non-fatal error. This function + * should not be changed. If there is a need to handle warnings differently, + * you should supply a replacement warning function and use + * png_set_error_fn() to replace the warning function at run-time. + */ +void PNGAPI +png_warning(png_const_structrp png_ptr, png_const_charp warning_message) +{ + int offset = 0; + if (png_ptr != NULL) + { +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if ((png_ptr->flags & + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0) +#endif + { + if (*warning_message == PNG_LITERAL_SHARP) + { + for (offset = 1; offset < 15; offset++) + if (warning_message[offset] == ' ') + break; + } + } + } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr), + warning_message + offset); + else + png_default_warning(png_ptr, warning_message + offset); +} + +/* These functions support 'formatted' warning messages with up to + * PNG_WARNING_PARAMETER_COUNT parameters. In the format string the parameter + * is introduced by @, where 'number' starts at 1. This follows the + * standard established by X/Open for internationalizable error messages. + */ +void +png_warning_parameter(png_warning_parameters p, int number, + png_const_charp string) +{ + if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT) + (void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string); +} + +void +png_warning_parameter_unsigned(png_warning_parameters p, int number, int format, + png_alloc_size_t value) +{ + char buffer[PNG_NUMBER_BUFFER_SIZE]; + png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value)); +} + +void +png_warning_parameter_signed(png_warning_parameters p, int number, int format, + png_int_32 value) +{ + png_alloc_size_t u; + png_charp str; + char buffer[PNG_NUMBER_BUFFER_SIZE]; + + /* Avoid overflow by doing the negate in a png_alloc_size_t: */ + u = (png_alloc_size_t)value; + if (value < 0) + u = ~u + 1; + + str = PNG_FORMAT_NUMBER(buffer, format, u); + + if (value < 0 && str > buffer) + *--str = '-'; + + png_warning_parameter(p, number, str); +} + +void +png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p, + png_const_charp message) +{ + /* The internal buffer is just 192 bytes - enough for all our messages, + * overflow doesn't happen because this code checks! If someone figures + * out how to send us a message longer than 192 bytes, all that will + * happen is that the message will be truncated appropriately. + */ + size_t i = 0; /* Index in the msg[] buffer: */ + char msg[192]; + + /* Each iteration through the following loop writes at most one character + * to msg[i++] then returns here to validate that there is still space for + * the trailing '\0'. It may (in the case of a parameter) read more than + * one character from message[]; it must check for '\0' and continue to the + * test if it finds the end of string. + */ + while (i<(sizeof msg)-1 && *message != '\0') + { + /* '@' at end of string is now just printed (previously it was skipped); + * it is an error in the calling code to terminate the string with @. + */ + if (p != NULL && *message == '@' && message[1] != '\0') + { + int parameter_char = *++message; /* Consume the '@' */ + static const char valid_parameters[] = "123456789"; + int parameter = 0; + + /* Search for the parameter digit, the index in the string is the + * parameter to use. + */ + while (valid_parameters[parameter] != parameter_char && + valid_parameters[parameter] != '\0') + ++parameter; + + /* If the parameter digit is out of range it will just get printed. */ + if (parameter < PNG_WARNING_PARAMETER_COUNT) + { + /* Append this parameter */ + png_const_charp parm = p[parameter]; + png_const_charp pend = p[parameter] + (sizeof p[parameter]); + + /* No need to copy the trailing '\0' here, but there is no guarantee + * that parm[] has been initialized, so there is no guarantee of a + * trailing '\0': + */ + while (i<(sizeof msg)-1 && *parm != '\0' && parm < pend) + msg[i++] = *parm++; + + /* Consume the parameter digit too: */ + ++message; + continue; + } + + /* else not a parameter and there is a character after the @ sign; just + * copy that. This is known not to be '\0' because of the test above. + */ + } + + /* At this point *message can't be '\0', even in the bad parameter case + * above where there is a lone '@' at the end of the message string. + */ + msg[i++] = *message++; + } + + /* i is always less than (sizeof msg), so: */ + msg[i] = '\0'; + + /* And this is the formatted message. It may be larger than + * PNG_MAX_ERROR_TEXT, but that is only used for 'chunk' errors and these + * are not (currently) formatted. + */ + png_warning(png_ptr, msg); +} +#endif /* WARNINGS */ + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_benign_error(png_const_structrp png_ptr, png_const_charp error_message) +{ + if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0) + { +# ifdef PNG_READ_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + png_ptr->chunk_name != 0) + png_chunk_warning(png_ptr, error_message); + else +# endif + png_warning(png_ptr, error_message); + } + + else + { +# ifdef PNG_READ_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + png_ptr->chunk_name != 0) + png_chunk_error(png_ptr, error_message); + else +# endif + png_error(png_ptr, error_message); + } + +# ifndef PNG_ERROR_TEXT_SUPPORTED + PNG_UNUSED(error_message) +# endif +} + +void /* PRIVATE */ +png_app_warning(png_const_structrp png_ptr, png_const_charp error_message) +{ + if ((png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN) != 0) + png_warning(png_ptr, error_message); + else + png_error(png_ptr, error_message); + +# ifndef PNG_ERROR_TEXT_SUPPORTED + PNG_UNUSED(error_message) +# endif +} + +void /* PRIVATE */ +png_app_error(png_const_structrp png_ptr, png_const_charp error_message) +{ + if ((png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN) != 0) + png_warning(png_ptr, error_message); + else + png_error(png_ptr, error_message); + +# ifndef PNG_ERROR_TEXT_SUPPORTED + PNG_UNUSED(error_message) +# endif +} +#endif /* BENIGN_ERRORS */ + +#define PNG_MAX_ERROR_TEXT 196 /* Currently limited by profile_error in png.c */ +#if defined(PNG_WARNINGS_SUPPORTED) || \ + (defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)) +/* These utilities are used internally to build an error message that relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * which is used to prefix the message. The message is limited in length + * to 63 bytes. The name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) +static PNG_CONST char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static void /* PRIVATE */ +png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp + error_message) +{ + png_uint_32 chunk_name = png_ptr->chunk_name; + int iout = 0, ishift = 24; + + while (ishift >= 0) + { + int c = (int)(chunk_name >> ishift) & 0xff; + + ishift -= 8; + if (isnonalpha(c) != 0) + { + buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0x0f]; + buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET; + } + + else + { + buffer[iout++] = (char)c; + } + } + + if (error_message == NULL) + buffer[iout] = '\0'; + + else + { + int iin = 0; + + buffer[iout++] = ':'; + buffer[iout++] = ' '; + + while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0') + buffer[iout++] = error_message[iin++]; + + /* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */ + buffer[iout] = '\0'; + } +} +#endif /* WARNINGS || ERROR_TEXT */ + +#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) +PNG_FUNCTION(void,PNGAPI +png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message), + PNG_NORETURN) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_error(png_ptr, error_message); + + else + { + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); + } +} +#endif /* READ && ERROR_TEXT */ + +#ifdef PNG_WARNINGS_SUPPORTED +void PNGAPI +png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_warning(png_ptr, warning_message); + + else + { + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); + } +} +#endif /* WARNINGS */ + +#ifdef PNG_READ_SUPPORTED +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp + error_message) +{ + if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0) + png_chunk_warning(png_ptr, error_message); + + else + png_chunk_error(png_ptr, error_message); + +# ifndef PNG_ERROR_TEXT_SUPPORTED + PNG_UNUSED(error_message) +# endif +} +#endif +#endif /* READ */ + +void /* PRIVATE */ +png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error) +{ +# ifndef PNG_WARNINGS_SUPPORTED + PNG_UNUSED(message) +# endif + + /* This is always supported, but for just read or just write it + * unconditionally does the right thing. + */ +# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED) + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) +# endif + +# ifdef PNG_READ_SUPPORTED + { + if (error < PNG_CHUNK_ERROR) + png_chunk_warning(png_ptr, message); + + else + png_chunk_benign_error(png_ptr, message); + } +# endif + +# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED) + else if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0) +# endif + +# ifdef PNG_WRITE_SUPPORTED + { + if (error < PNG_CHUNK_WRITE_ERROR) + png_app_warning(png_ptr, message); + + else + png_app_error(png_ptr, message); + } +# endif +} + +#ifdef PNG_ERROR_TEXT_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_FUNCTION(void, +png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN) +{ +# define fixed_message "fixed point overflow in " +# define fixed_message_ln ((sizeof fixed_message)-1) + unsigned int iin; + char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT]; + memcpy(msg, fixed_message, fixed_message_ln); + iin = 0; + if (name != NULL) + while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0) + { + msg[fixed_message_ln + iin] = name[iin]; + ++iin; + } + msg[fixed_message_ln + iin] = 0; + png_error(png_ptr, msg); +} +#endif +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This API only exists if ANSI-C style error handling is used, + * otherwise it is necessary for png_default_error to be overridden. + */ +jmp_buf* PNGAPI +png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn, + size_t jmp_buf_size) +{ + /* From libpng 1.6.0 the app gets one chance to set a 'jmpbuf_size' value + * and it must not change after that. Libpng doesn't care how big the + * buffer is, just that it doesn't change. + * + * If the buffer size is no *larger* than the size of jmp_buf when libpng is + * compiled a built in jmp_buf is returned; this preserves the pre-1.6.0 + * semantics that this call will not fail. If the size is larger, however, + * the buffer is allocated and this may fail, causing the function to return + * NULL. + */ + if (png_ptr == NULL) + return NULL; + + if (png_ptr->jmp_buf_ptr == NULL) + { + png_ptr->jmp_buf_size = 0; /* not allocated */ + + if (jmp_buf_size <= (sizeof png_ptr->jmp_buf_local)) + png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; + + else + { + png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *, + png_malloc_warn(png_ptr, jmp_buf_size)); + + if (png_ptr->jmp_buf_ptr == NULL) + return NULL; /* new NULL return on OOM */ + + png_ptr->jmp_buf_size = jmp_buf_size; + } + } + + else /* Already allocated: check the size */ + { + size_t size = png_ptr->jmp_buf_size; + + if (size == 0) + { + size = (sizeof png_ptr->jmp_buf_local); + if (png_ptr->jmp_buf_ptr != &png_ptr->jmp_buf_local) + { + /* This is an internal error in libpng: somehow we have been left + * with a stack allocated jmp_buf when the application regained + * control. It's always possible to fix this up, but for the moment + * this is a png_error because that makes it easy to detect. + */ + png_error(png_ptr, "Libpng jmp_buf still allocated"); + /* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */ + } + } + + if (size != jmp_buf_size) + { + png_warning(png_ptr, "Application jmp_buf size changed"); + return NULL; /* caller will probably crash: no choice here */ + } + } + + /* Finally fill in the function, now we have a satisfactory buffer. It is + * valid to change the function on every call. + */ + png_ptr->longjmp_fn = longjmp_fn; + return png_ptr->jmp_buf_ptr; +} + +void /* PRIVATE */ +png_free_jmpbuf(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + jmp_buf *jb = png_ptr->jmp_buf_ptr; + + /* A size of 0 is used to indicate a local, stack, allocation of the + * pointer; used here and in png.c + */ + if (jb != NULL && png_ptr->jmp_buf_size > 0) + { + + /* This stuff is so that a failure to free the error control structure + * does not leave libpng in a state with no valid error handling: the + * free always succeeds, if there is an error it gets ignored. + */ + if (jb != &png_ptr->jmp_buf_local) + { + /* Make an internal, libpng, jmp_buf to return here */ + jmp_buf free_jmp_buf; + + if (!setjmp(free_jmp_buf)) + { + png_ptr->jmp_buf_ptr = &free_jmp_buf; /* come back here */ + png_ptr->jmp_buf_size = 0; /* stack allocation */ + png_ptr->longjmp_fn = longjmp; + png_free(png_ptr, jb); /* Return to setjmp on error */ + } + } + } + + /* *Always* cancel everything out: */ + png_ptr->jmp_buf_size = 0; + png_ptr->jmp_buf_ptr = NULL; + png_ptr->longjmp_fn = 0; + } +} +#endif + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static PNG_FUNCTION(void /* PRIVATE */, +png_default_error,(png_const_structrp png_ptr, png_const_charp error_message), + PNG_NORETURN) +{ +#ifdef PNG_CONSOLE_IO_SUPPORTED +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + /* Check on NULL only added in 1.5.4 */ + if (error_message != NULL && *error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + char error_number[16]; + for (offset = 0; offset<15; offset++) + { + error_number[offset] = error_message[offset + 1]; + if (error_message[offset] == ' ') + break; + } + + if ((offset > 1) && (offset < 15)) + { + error_number[offset - 1] = '\0'; + fprintf(stderr, "libpng error no. %s: %s", + error_number, error_message + offset + 1); + fprintf(stderr, PNG_STRING_NEWLINE); + } + + else + { + fprintf(stderr, "libpng error: %s, offset=%d", + error_message, offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + } + else +#endif + { + fprintf(stderr, "libpng error: %s", error_message ? error_message : + "undefined"); + fprintf(stderr, PNG_STRING_NEWLINE); + } +#else + PNG_UNUSED(error_message) /* Make compiler happy */ +#endif + png_longjmp(png_ptr, 1); +} + +PNG_FUNCTION(void,PNGAPI +png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN) +{ +#ifdef PNG_SETJMP_SUPPORTED + if (png_ptr != NULL && png_ptr->longjmp_fn != NULL && + png_ptr->jmp_buf_ptr != NULL) + png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val); +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(val) +#endif + + /* If control reaches this point, png_longjmp() must not return. The only + * choice is to terminate the whole process (or maybe the thread); to do + * this the ANSI-C abort() function is used unless a different method is + * implemented by overriding the default configuration setting for + * PNG_ABORT(). + */ + PNG_ABORT(); +} + +#ifdef PNG_WARNINGS_SUPPORTED +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want them to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void /* PRIVATE */ +png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message) +{ +#ifdef PNG_CONSOLE_IO_SUPPORTED +# ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*warning_message == PNG_LITERAL_SHARP) + { + int offset; + char warning_number[16]; + for (offset = 0; offset < 15; offset++) + { + warning_number[offset] = warning_message[offset + 1]; + if (warning_message[offset] == ' ') + break; + } + + if ((offset > 1) && (offset < 15)) + { + warning_number[offset + 1] = '\0'; + fprintf(stderr, "libpng warning no. %s: %s", + warning_number, warning_message + offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + + else + { + fprintf(stderr, "libpng warning: %s", + warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } + } + else +# endif + + { + fprintf(stderr, "libpng warning: %s", warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } +#else + PNG_UNUSED(warning_message) /* Make compiler happy */ +#endif + PNG_UNUSED(png_ptr) /* Make compiler happy */ +} +#endif /* WARNINGS */ + +/* This function is called when the application wants to use another method + * of handling errors and warnings. Note that the error function MUST NOT + * return to the calling routine or serious problems will occur. The return + * method used in the default routine calls longjmp(png_ptr->jmp_buf_ptr, 1) + */ +void PNGAPI +png_set_error_fn(png_structrp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->error_ptr = error_ptr; + png_ptr->error_fn = error_fn; +#ifdef PNG_WARNINGS_SUPPORTED + png_ptr->warning_fn = warning_fn; +#else + PNG_UNUSED(warning_fn) +#endif +} + + +/* This function returns a pointer to the error_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_error_ptr(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return NULL; + + return ((png_voidp)png_ptr->error_ptr); +} + + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +void PNGAPI +png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode) +{ + if (png_ptr != NULL) + { + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS | + PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + } +} +#endif + +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + /* Currently the above both depend on SETJMP_SUPPORTED, however it would be + * possible to implement without setjmp support just so long as there is some + * way to handle the error return here: + */ +PNG_FUNCTION(void /* PRIVATE */, (PNGCBAPI +png_safe_error),(png_structp png_nonconst_ptr, png_const_charp error_message), + PNG_NORETURN) +{ + const png_const_structrp png_ptr = png_nonconst_ptr; + png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr); + + /* An error is always logged here, overwriting anything (typically a warning) + * that is already there: + */ + if (image != NULL) + { + png_safecat(image->message, (sizeof image->message), 0, error_message); + image->warning_or_error |= PNG_IMAGE_ERROR; + + /* Retrieve the jmp_buf from within the png_control, making this work for + * C++ compilation too is pretty tricky: C++ wants a pointer to the first + * element of a jmp_buf, but C doesn't tell us the type of that. + */ + if (image->opaque != NULL && image->opaque->error_buf != NULL) + longjmp(png_control_jmp_buf(image->opaque), 1); + + /* Missing longjmp buffer, the following is to help debugging: */ + { + size_t pos = png_safecat(image->message, (sizeof image->message), 0, + "bad longjmp: "); + png_safecat(image->message, (sizeof image->message), pos, + error_message); + } + } + + /* Here on an internal programming error. */ + abort(); +} + +#ifdef PNG_WARNINGS_SUPPORTED +void /* PRIVATE */ PNGCBAPI +png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message) +{ + const png_const_structrp png_ptr = png_nonconst_ptr; + png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr); + + /* A warning is only logged if there is no prior warning or error. */ + if (image->warning_or_error == 0) + { + png_safecat(image->message, (sizeof image->message), 0, warning_message); + image->warning_or_error |= PNG_IMAGE_WARNING; + } +} +#endif + +int /* PRIVATE */ +png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg) +{ + volatile png_imagep image = image_in; + volatile int result; + volatile png_voidp saved_error_buf; + jmp_buf safe_jmpbuf; + + /* Safely execute function(arg) with png_error returning to this function. */ + saved_error_buf = image->opaque->error_buf; + result = setjmp(safe_jmpbuf) == 0; + + if (result != 0) + { + + image->opaque->error_buf = safe_jmpbuf; + result = function(arg); + } + + image->opaque->error_buf = saved_error_buf; + + /* And do the cleanup prior to any failure return. */ + if (result == 0) + png_image_free(image); + + return result; +} +#endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */ +#endif /* READ || WRITE */ diff --git a/src/png/libpng/pngget.c b/src/png/libpng/pngget.c new file mode 100644 index 0000000000..26e9fb1c35 --- /dev/null +++ b/src/png/libpng/pngget.c @@ -0,0 +1,1248 @@ + +/* pngget.c - retrieval of values from info struct + * + * Last changed in libpng 1.6.32 [August 24, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + */ + +#include "pngpriv.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +png_uint_32 PNGAPI +png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 flag) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->valid & flag); + + return(0); +} + +png_size_t PNGAPI +png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->rowbytes); + + return(0); +} + +#ifdef PNG_INFO_IMAGE_SUPPORTED +png_bytepp PNGAPI +png_get_rows(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->row_pointers); + + return(0); +} +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Easy access to info, added in libpng-0.99 */ +png_uint_32 PNGAPI +png_get_image_width(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->width; + + return (0); +} + +png_uint_32 PNGAPI +png_get_image_height(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->height; + + return (0); +} + +png_byte PNGAPI +png_get_bit_depth(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->bit_depth; + + return (0); +} + +png_byte PNGAPI +png_get_color_type(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->color_type; + + return (0); +} + +png_byte PNGAPI +png_get_filter_type(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->filter_type; + + return (0); +} + +png_byte PNGAPI +png_get_interlace_type(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->interlace_type; + + return (0); +} + +png_byte PNGAPI +png_get_compression_type(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->compression_type; + + return (0); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp + info_ptr) +{ +#ifdef PNG_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) + { + png_debug1(1, "in %s retrieval function", + "png_get_x_pixels_per_meter"); + + if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER) + return (info_ptr->x_pixels_per_unit); + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return (0); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp + info_ptr) +{ +#ifdef PNG_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) + { + png_debug1(1, "in %s retrieval function", + "png_get_y_pixels_per_meter"); + + if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER) + return (info_ptr->y_pixels_per_unit); + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return (0); +} + +png_uint_32 PNGAPI +png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) + { + png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter"); + + if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER && + info_ptr->x_pixels_per_unit == info_ptr->y_pixels_per_unit) + return (info_ptr->x_pixels_per_unit); + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return (0); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp + info_ptr) +{ +#ifdef PNG_READ_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) + { + png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio"); + + if (info_ptr->x_pixels_per_unit != 0) + return ((float)((float)info_ptr->y_pixels_per_unit + /(float)info_ptr->x_pixels_per_unit)); + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return ((float)0.0); +} +#endif + +#ifdef PNG_FIXED_POINT_SUPPORTED +png_fixed_point PNGAPI +png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr, + png_const_inforp info_ptr) +{ +#ifdef PNG_READ_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0 && + info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0 && + info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX && + info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX) + { + png_fixed_point res; + + png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio_fixed"); + + /* The following casts work because a PNG 4 byte integer only has a valid + * range of 0..2^31-1; otherwise the cast might overflow. + */ + if (png_muldiv(&res, (png_int_32)info_ptr->y_pixels_per_unit, PNG_FP_1, + (png_int_32)info_ptr->x_pixels_per_unit) != 0) + return res; + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return 0; +} +#endif + +png_int_32 PNGAPI +png_get_x_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_oFFs) != 0) + { + png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER) + return (info_ptr->x_offset); + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_oFFs) != 0) + { + png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER) + return (info_ptr->y_offset); + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return (0); +} + +png_int_32 PNGAPI +png_get_x_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_oFFs) != 0) + { + png_debug1(1, "in %s retrieval function", "png_get_x_offset_pixels"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL) + return (info_ptr->x_offset); + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_oFFs) != 0) + { + png_debug1(1, "in %s retrieval function", "png_get_y_offset_pixels"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL) + return (info_ptr->y_offset); + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return (0); +} + +#ifdef PNG_INCH_CONVERSIONS_SUPPORTED +static png_uint_32 +ppi_from_ppm(png_uint_32 ppm) +{ +#if 0 + /* The conversion is *(2.54/100), in binary (32 digits): + * .00000110100000001001110101001001 + */ + png_uint_32 t1001, t1101; + ppm >>= 1; /* .1 */ + t1001 = ppm + (ppm >> 3); /* .1001 */ + t1101 = t1001 + (ppm >> 1); /* .1101 */ + ppm >>= 20; /* .000000000000000000001 */ + t1101 += t1101 >> 15; /* .1101000000000001101 */ + t1001 >>= 11; /* .000000000001001 */ + t1001 += t1001 >> 12; /* .000000000001001000000001001 */ + ppm += t1001; /* .000000000001001000001001001 */ + ppm += t1101; /* .110100000001001110101001001 */ + return (ppm + 16) >> 5;/* .00000110100000001001110101001001 */ +#else + /* The argument is a PNG unsigned integer, so it is not permitted + * to be bigger than 2^31. + */ + png_fixed_point result; + if (ppm <= PNG_UINT_31_MAX && png_muldiv(&result, (png_int_32)ppm, 127, + 5000) != 0) + return (png_uint_32)result; + + /* Overflow. */ + return 0; +#endif +} + +png_uint_32 PNGAPI +png_get_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + return ppi_from_ppm(png_get_pixels_per_meter(png_ptr, info_ptr)); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + return ppi_from_ppm(png_get_x_pixels_per_meter(png_ptr, info_ptr)); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + return ppi_from_ppm(png_get_y_pixels_per_meter(png_ptr, info_ptr)); +} + +#ifdef PNG_FIXED_POINT_SUPPORTED +static png_fixed_point +png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns) +{ + /* Convert from metres * 1,000,000 to inches * 100,000, meters to + * inches is simply *(100/2.54), so we want *(10/2.54) == 500/127. + * Notice that this can overflow - a warning is output and 0 is + * returned. + */ + return png_muldiv_warn(png_ptr, microns, 500, 127); +} + +png_fixed_point PNGAPI +png_get_x_offset_inches_fixed(png_const_structrp png_ptr, + png_const_inforp info_ptr) +{ + return png_fixed_inches_from_microns(png_ptr, + png_get_x_offset_microns(png_ptr, info_ptr)); +} +#endif + +#ifdef PNG_FIXED_POINT_SUPPORTED +png_fixed_point PNGAPI +png_get_y_offset_inches_fixed(png_const_structrp png_ptr, + png_const_inforp info_ptr) +{ + return png_fixed_inches_from_microns(png_ptr, + png_get_y_offset_microns(png_ptr, info_ptr)); +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_x_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + /* To avoid the overflow do the conversion directly in floating + * point. + */ + return (float)(png_get_x_offset_microns(png_ptr, info_ptr) * .00003937); +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_y_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + /* To avoid the overflow do the conversion directly in floating + * point. + */ + return (float)(png_get_y_offset_microns(png_ptr, info_ptr) * .00003937); +} +#endif + +#ifdef PNG_pHYs_SUPPORTED +png_uint_32 PNGAPI +png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) + { + png_debug1(1, "in %s retrieval function", "pHYs"); + + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + + if (*unit_type == 1) + { + if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); + if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); + } + } + } + + return (retval); +} +#endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* png_get_channels really belongs in here, too, but it's been around longer */ + +#endif /* EASY_ACCESS */ + + +png_byte PNGAPI +png_get_channels(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->channels); + + return (0); +} + +#ifdef PNG_READ_SUPPORTED +png_const_bytep PNGAPI +png_get_signature(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->signature); + + return (NULL); +} +#endif + +#ifdef PNG_bKGD_SUPPORTED +png_uint_32 PNGAPI +png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, + png_color_16p *background) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_bKGD) != 0 && + background != NULL) + { + png_debug1(1, "in %s retrieval function", "bKGD"); + + *background = &(info_ptr->background); + return (PNG_INFO_bKGD); + } + + return (0); +} +#endif + +#ifdef PNG_cHRM_SUPPORTED +/* The XYZ APIs were added in 1.5.5 to take advantage of the code added at the + * same time to correct the rgb grayscale coefficient defaults obtained from the + * cHRM chunk in 1.5.4 + */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y) +{ + /* Quiet API change: this code used to only return the end points if a cHRM + * chunk was present, but the end points can also come from iCCP or sRGB + * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and + * the png_set_ APIs merely check that set end points are mutually + * consistent. + */ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + { + png_debug1(1, "in %s retrieval function", "cHRM"); + + if (white_x != NULL) + *white_x = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.whitex, "cHRM white X"); + if (white_y != NULL) + *white_y = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y"); + if (red_x != NULL) + *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx, + "cHRM red X"); + if (red_y != NULL) + *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy, + "cHRM red Y"); + if (green_x != NULL) + *green_x = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.greenx, "cHRM green X"); + if (green_y != NULL) + *green_y = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y"); + if (blue_x != NULL) + *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex, + "cHRM blue X"); + if (blue_y != NULL) + *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey, + "cHRM blue Y"); + return (PNG_INFO_cHRM); + } + + return (0); +} + +png_uint_32 PNGAPI +png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *red_X, double *red_Y, double *red_Z, double *green_X, + double *green_Y, double *green_Z, double *blue_X, double *blue_Y, + double *blue_Z) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + { + png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)"); + + if (red_X != NULL) + *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X, + "cHRM red X"); + if (red_Y != NULL) + *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y, + "cHRM red Y"); + if (red_Z != NULL) + *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z, + "cHRM red Z"); + if (green_X != NULL) + *green_X = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X"); + if (green_Y != NULL) + *green_Y = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y"); + if (green_Z != NULL) + *green_Z = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z"); + if (blue_X != NULL) + *blue_X = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X"); + if (blue_Y != NULL) + *blue_Y = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y"); + if (blue_Z != NULL) + *blue_Z = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z"); + return (PNG_INFO_cHRM); + } + + return (0); +} +# endif + +# ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + { + png_debug1(1, "in %s retrieval function", "cHRM_XYZ"); + + if (int_red_X != NULL) + *int_red_X = info_ptr->colorspace.end_points_XYZ.red_X; + if (int_red_Y != NULL) + *int_red_Y = info_ptr->colorspace.end_points_XYZ.red_Y; + if (int_red_Z != NULL) + *int_red_Z = info_ptr->colorspace.end_points_XYZ.red_Z; + if (int_green_X != NULL) + *int_green_X = info_ptr->colorspace.end_points_XYZ.green_X; + if (int_green_Y != NULL) + *int_green_Y = info_ptr->colorspace.end_points_XYZ.green_Y; + if (int_green_Z != NULL) + *int_green_Z = info_ptr->colorspace.end_points_XYZ.green_Z; + if (int_blue_X != NULL) + *int_blue_X = info_ptr->colorspace.end_points_XYZ.blue_X; + if (int_blue_Y != NULL) + *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blue_Y; + if (int_blue_Z != NULL) + *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blue_Z; + return (PNG_INFO_cHRM); + } + + return (0); +} + +png_uint_32 PNGAPI +png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, + png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y) +{ + png_debug1(1, "in %s retrieval function", "cHRM"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + { + if (white_x != NULL) + *white_x = info_ptr->colorspace.end_points_xy.whitex; + if (white_y != NULL) + *white_y = info_ptr->colorspace.end_points_xy.whitey; + if (red_x != NULL) + *red_x = info_ptr->colorspace.end_points_xy.redx; + if (red_y != NULL) + *red_y = info_ptr->colorspace.end_points_xy.redy; + if (green_x != NULL) + *green_x = info_ptr->colorspace.end_points_xy.greenx; + if (green_y != NULL) + *green_y = info_ptr->colorspace.end_points_xy.greeny; + if (blue_x != NULL) + *blue_x = info_ptr->colorspace.end_points_xy.bluex; + if (blue_y != NULL) + *blue_y = info_ptr->colorspace.end_points_xy.bluey; + return (PNG_INFO_cHRM); + } + + return (0); +} +# endif +#endif + +#ifdef PNG_gAMA_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *file_gamma) +{ + png_debug1(1, "in %s retrieval function", "gAMA"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && + file_gamma != NULL) + { + *file_gamma = info_ptr->colorspace.gamma; + return (PNG_INFO_gAMA); + } + + return (0); +} +# endif + +# ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *file_gamma) +{ + png_debug1(1, "in %s retrieval function", "gAMA(float)"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && + file_gamma != NULL) + { + *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma, + "png_get_gAMA"); + return (PNG_INFO_gAMA); + } + + return (0); +} +# endif +#endif + +#ifdef PNG_sRGB_SUPPORTED +png_uint_32 PNGAPI +png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *file_srgb_intent) +{ + png_debug1(1, "in %s retrieval function", "sRGB"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sRGB) != 0 && file_srgb_intent != NULL) + { + *file_srgb_intent = info_ptr->colorspace.rendering_intent; + return (PNG_INFO_sRGB); + } + + return (0); +} +#endif + +#ifdef PNG_iCCP_SUPPORTED +png_uint_32 PNGAPI +png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, + png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen) +{ + png_debug1(1, "in %s retrieval function", "iCCP"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_iCCP) != 0 && + name != NULL && compression_type != NULL && profile != NULL && + proflen != NULL) + { + *name = info_ptr->iccp_name; + *profile = info_ptr->iccp_profile; + *proflen = png_get_uint_32(info_ptr->iccp_profile); + /* This is somewhat irrelevant since the profile data returned has + * actually been uncompressed. + */ + *compression_type = PNG_COMPRESSION_TYPE_BASE; + return (PNG_INFO_iCCP); + } + + return (0); +} +#endif + +#ifdef PNG_sPLT_SUPPORTED +int PNGAPI +png_get_sPLT(png_const_structrp png_ptr, png_inforp info_ptr, + png_sPLT_tpp spalettes) +{ + if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) + { + *spalettes = info_ptr->splt_palettes; + return info_ptr->splt_palettes_num; + } + + return (0); +} +#endif + +#ifdef PNG_eXIf_SUPPORTED +png_uint_32 PNGAPI +png_get_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, + png_bytep *exif) +{ + png_warning(png_ptr, "png_get_eXIf does not work; use png_get_eXIf_1"); + PNG_UNUSED(info_ptr) + PNG_UNUSED(exif) + return 0; +} + +png_uint_32 PNGAPI +png_get_eXIf_1(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 *num_exif, png_bytep *exif) +{ + png_debug1(1, "in %s retrieval function", "eXIf"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_eXIf) != 0 && exif != NULL) + { + *num_exif = info_ptr->num_exif; + *exif = info_ptr->exif; + return (PNG_INFO_eXIf); + } + + return (0); +} +#endif + +#ifdef PNG_hIST_SUPPORTED +png_uint_32 PNGAPI +png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr, + png_uint_16p *hist) +{ + png_debug1(1, "in %s retrieval function", "hIST"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_hIST) != 0 && hist != NULL) + { + *hist = info_ptr->hist; + return (PNG_INFO_hIST); + } + + return (0); +} +#endif + +png_uint_32 PNGAPI +png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 *width, png_uint_32 *height, int *bit_depth, + int *color_type, int *interlace_type, int *compression_type, + int *filter_type) +{ + png_debug1(1, "in %s retrieval function", "IHDR"); + + if (png_ptr == NULL || info_ptr == NULL) + return (0); + + if (width != NULL) + *width = info_ptr->width; + + if (height != NULL) + *height = info_ptr->height; + + if (bit_depth != NULL) + *bit_depth = info_ptr->bit_depth; + + if (color_type != NULL) + *color_type = info_ptr->color_type; + + if (compression_type != NULL) + *compression_type = info_ptr->compression_type; + + if (filter_type != NULL) + *filter_type = info_ptr->filter_type; + + if (interlace_type != NULL) + *interlace_type = info_ptr->interlace_type; + + /* This is redundant if we can be sure that the info_ptr values were all + * assigned in png_set_IHDR(). We do the check anyhow in case an + * application has ignored our advice not to mess with the members + * of info_ptr directly. + */ + png_check_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + + return (1); +} + +#ifdef PNG_oFFs_SUPPORTED +png_uint_32 PNGAPI +png_get_oFFs(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) +{ + png_debug1(1, "in %s retrieval function", "oFFs"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_oFFs) != 0 && + offset_x != NULL && offset_y != NULL && unit_type != NULL) + { + *offset_x = info_ptr->x_offset; + *offset_y = info_ptr->y_offset; + *unit_type = (int)info_ptr->offset_unit_type; + return (PNG_INFO_oFFs); + } + + return (0); +} +#endif + +#ifdef PNG_pCAL_SUPPORTED +png_uint_32 PNGAPI +png_get_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, + png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, + png_charp *units, png_charpp *params) +{ + png_debug1(1, "in %s retrieval function", "pCAL"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pCAL) != 0 && + purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + nparams != NULL && units != NULL && params != NULL) + { + *purpose = info_ptr->pcal_purpose; + *X0 = info_ptr->pcal_X0; + *X1 = info_ptr->pcal_X1; + *type = (int)info_ptr->pcal_type; + *nparams = (int)info_ptr->pcal_nparams; + *units = info_ptr->pcal_units; + *params = info_ptr->pcal_params; + return (PNG_INFO_pCAL); + } + + return (0); +} +#endif + +#ifdef PNG_sCAL_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED +# if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *unit, png_fixed_point *width, png_fixed_point *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL) != 0) + { + *unit = info_ptr->scal_unit; + /*TODO: make this work without FP support; the API is currently eliminated + * if neither floating point APIs nor internal floating point arithmetic + * are enabled. + */ + *width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width"); + *height = png_fixed(png_ptr, atof(info_ptr->scal_s_height), + "sCAL height"); + return (PNG_INFO_sCAL); + } + + return(0); +} +# endif /* FLOATING_ARITHMETIC */ +# endif /* FIXED_POINT */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *unit, double *width, double *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL) != 0) + { + *unit = info_ptr->scal_unit; + *width = atof(info_ptr->scal_s_width); + *height = atof(info_ptr->scal_s_height); + return (PNG_INFO_sCAL); + } + + return(0); +} +# endif /* FLOATING POINT */ +png_uint_32 PNGAPI +png_get_sCAL_s(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *unit, png_charpp width, png_charpp height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL) != 0) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_s_width; + *height = info_ptr->scal_s_height; + return (PNG_INFO_sCAL); + } + + return(0); +} +#endif /* sCAL */ + +#ifdef PNG_pHYs_SUPPORTED +png_uint_32 PNGAPI +png_get_pHYs(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + png_debug1(1, "in %s retrieval function", "pHYs"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) + { + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + } + } + + return (retval); +} +#endif /* pHYs */ + +png_uint_32 PNGAPI +png_get_PLTE(png_const_structrp png_ptr, png_inforp info_ptr, + png_colorp *palette, int *num_palette) +{ + png_debug1(1, "in %s retrieval function", "PLTE"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_PLTE) != 0 && palette != NULL) + { + *palette = info_ptr->palette; + *num_palette = info_ptr->num_palette; + png_debug1(3, "num_palette = %d", *num_palette); + return (PNG_INFO_PLTE); + } + + return (0); +} + +#ifdef PNG_sBIT_SUPPORTED +png_uint_32 PNGAPI +png_get_sBIT(png_const_structrp png_ptr, png_inforp info_ptr, + png_color_8p *sig_bit) +{ + png_debug1(1, "in %s retrieval function", "sBIT"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sBIT) != 0 && sig_bit != NULL) + { + *sig_bit = &(info_ptr->sig_bit); + return (PNG_INFO_sBIT); + } + + return (0); +} +#endif + +#ifdef PNG_TEXT_SUPPORTED +int PNGAPI +png_get_text(png_const_structrp png_ptr, png_inforp info_ptr, + png_textp *text_ptr, int *num_text) +{ + if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) + { + png_debug1(1, "in 0x%lx retrieval function", + (unsigned long)png_ptr->chunk_name); + + if (text_ptr != NULL) + *text_ptr = info_ptr->text; + + if (num_text != NULL) + *num_text = info_ptr->num_text; + + return info_ptr->num_text; + } + + if (num_text != NULL) + *num_text = 0; + + return(0); +} +#endif + +#ifdef PNG_tIME_SUPPORTED +png_uint_32 PNGAPI +png_get_tIME(png_const_structrp png_ptr, png_inforp info_ptr, + png_timep *mod_time) +{ + png_debug1(1, "in %s retrieval function", "tIME"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_tIME) != 0 && mod_time != NULL) + { + *mod_time = &(info_ptr->mod_time); + return (PNG_INFO_tIME); + } + + return (0); +} +#endif + +#ifdef PNG_tRNS_SUPPORTED +png_uint_32 PNGAPI +png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr, + png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color) +{ + png_uint_32 retval = 0; + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_tRNS) != 0) + { + png_debug1(1, "in %s retrieval function", "tRNS"); + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (trans_alpha != NULL) + { + *trans_alpha = info_ptr->trans_alpha; + retval |= PNG_INFO_tRNS; + } + + if (trans_color != NULL) + *trans_color = &(info_ptr->trans_color); + } + + else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + { + if (trans_color != NULL) + { + *trans_color = &(info_ptr->trans_color); + retval |= PNG_INFO_tRNS; + } + + if (trans_alpha != NULL) + *trans_alpha = NULL; + } + + if (num_trans != NULL) + { + *num_trans = info_ptr->num_trans; + retval |= PNG_INFO_tRNS; + } + } + + return (retval); +} +#endif + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +int PNGAPI +png_get_unknown_chunks(png_const_structrp png_ptr, png_inforp info_ptr, + png_unknown_chunkpp unknowns) +{ + if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) + { + *unknowns = info_ptr->unknown_chunks; + return info_ptr->unknown_chunks_num; + } + + return (0); +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +png_byte PNGAPI +png_get_rgb_to_gray_status (png_const_structrp png_ptr) +{ + return (png_byte)(png_ptr ? png_ptr->rgb_to_gray_status : 0); +} +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +png_voidp PNGAPI +png_get_user_chunk_ptr(png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_chunk_ptr : NULL); +} +#endif + +png_size_t PNGAPI +png_get_compression_buffer_size(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return 0; + +#ifdef PNG_WRITE_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) +#endif + { +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED + return png_ptr->IDAT_read_size; +#else + return PNG_IDAT_READ_SIZE; +#endif + } + +#ifdef PNG_WRITE_SUPPORTED + else + return png_ptr->zbuffer_size; +#endif +} + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* These functions were added to libpng 1.2.6 and were enabled + * by default in libpng-1.4.0 */ +png_uint_32 PNGAPI +png_get_user_width_max (png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_width_max : 0); +} + +png_uint_32 PNGAPI +png_get_user_height_max (png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_height_max : 0); +} + +/* This function was added to libpng 1.4.0 */ +png_uint_32 PNGAPI +png_get_chunk_cache_max (png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_chunk_cache_max : 0); +} + +/* This function was added to libpng 1.4.1 */ +png_alloc_size_t PNGAPI +png_get_chunk_malloc_max (png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_chunk_malloc_max : 0); +} +#endif /* SET_USER_LIMITS */ + +/* These functions were added to libpng 1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +png_uint_32 PNGAPI +png_get_io_state (png_const_structrp png_ptr) +{ + return png_ptr->io_state; +} + +png_uint_32 PNGAPI +png_get_io_chunk_type (png_const_structrp png_ptr) +{ + return png_ptr->chunk_name; +} +#endif /* IO_STATE */ + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +int PNGAPI +png_get_palette_max(png_const_structp png_ptr, png_const_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return png_ptr->num_palette_max; + + return (-1); +} +# endif +#endif + +#endif /* READ || WRITE */ diff --git a/src/png/libpng/pnginfo.h b/src/png/libpng/pnginfo.h new file mode 100644 index 0000000000..d5f6149dbd --- /dev/null +++ b/src/png/libpng/pnginfo.h @@ -0,0 +1,267 @@ + +/* pnginfo.h - header file for PNG reference library + * + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + + /* png_info is a structure that holds the information in a PNG file so + * that the application can find out the characteristics of the image. + * If you are reading the file, this structure will tell you what is + * in the PNG file. If you are writing the file, fill in the information + * you want to put into the PNG file, using png_set_*() functions, then + * call png_write_info(). + * + * The names chosen should be very close to the PNG specification, so + * consult that document for information about the meaning of each field. + * + * With libpng < 0.95, it was only possible to directly set and read the + * the values in the png_info_struct, which meant that the contents and + * order of the values had to remain fixed. With libpng 0.95 and later, + * however, there are now functions that abstract the contents of + * png_info_struct from the application, so this makes it easier to use + * libpng with dynamic libraries, and even makes it possible to use + * libraries that don't have all of the libpng ancillary chunk-handing + * functionality. In libpng-1.5.0 this was moved into a separate private + * file that is not visible to applications. + * + * The following members may have allocated storage attached that should be + * cleaned up before the structure is discarded: palette, trans, text, + * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile, + * splt_palettes, scal_unit, row_pointers, and unknowns. By default, these + * are automatically freed when the info structure is deallocated, if they were + * allocated internally by libpng. This behavior can be changed by means + * of the png_data_freer() function. + * + * More allocation details: all the chunk-reading functions that + * change these members go through the corresponding png_set_* + * functions. A function to clear these members is available: see + * png_free_data(). The png_set_* functions do not depend on being + * able to point info structure members to any of the storage they are + * passed (they make their own copies), EXCEPT that the png_set_text + * functions use the same storage passed to them in the text_ptr or + * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns + * functions do not make their own copies. + */ +#ifndef PNGINFO_H +#define PNGINFO_H + +struct png_info_def +{ + /* The following are necessary for every PNG file */ + png_uint_32 width; /* width of image in pixels (from IHDR) */ + png_uint_32 height; /* height of image in pixels (from IHDR) */ + png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */ + png_size_t rowbytes; /* bytes needed to hold an untransformed row */ + png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */ + png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */ + png_uint_16 num_trans; /* number of transparent palette color (tRNS) */ + png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */ + png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */ + /* The following three should have been named *_method not *_type */ + png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */ + png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */ + png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + + /* The following are set by png_set_IHDR, called from the application on + * write, but the are never actually used by the write code. + */ + png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte spare_byte; /* to align the data, and for future use */ + +#ifdef PNG_READ_SUPPORTED + /* This is never set during write */ + png_byte signature[8]; /* magic bytes read by libpng from start of file */ +#endif + + /* The rest of the data is optional. If you are reading, check the + * valid field to see if the information in these are valid. If you + * are writing, set the valid field to those chunks you want written, + * and initialize the appropriate fields below. + */ + +#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) + /* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are + * defined. When COLORSPACE is switched on all the colorspace-defining + * chunks should be enabled, when GAMMA is switched on all the gamma-defining + * chunks should be enabled. If this is not done it becomes possible to read + * inconsistent PNG files and assign a probably incorrect interpretation to + * the information. (In other words, by carefully choosing which chunks to + * recognize the system configuration can select an interpretation for PNG + * files containing ambiguous data and this will result in inconsistent + * behavior between different libpng builds!) + */ + png_colorspace colorspace; +#endif + +#ifdef PNG_iCCP_SUPPORTED + /* iCCP chunk data. */ + png_charp iccp_name; /* profile name */ + png_bytep iccp_profile; /* International Color Consortium profile data */ + png_uint_32 iccp_proflen; /* ICC profile data length */ +#endif + +#ifdef PNG_TEXT_SUPPORTED + /* The tEXt, and zTXt chunks contain human-readable textual data in + * uncompressed, compressed, and optionally compressed forms, respectively. + * The data in "text" is an array of pointers to uncompressed, + * null-terminated C strings. Each chunk has a keyword that describes the + * textual data contained in that chunk. Keywords are not required to be + * unique, and the text string may be empty. Any number of text chunks may + * be in an image. + */ + int num_text; /* number of comments read or comments to write */ + int max_text; /* current size of text array */ + png_textp text; /* array of comments read or comments to write */ +#endif /* TEXT */ + +#ifdef PNG_tIME_SUPPORTED + /* The tIME chunk holds the last time the displayed image data was + * modified. See the png_time struct for the contents of this struct. + */ + png_time mod_time; +#endif + +#ifdef PNG_sBIT_SUPPORTED + /* The sBIT chunk specifies the number of significant high-order bits + * in the pixel data. Values are in the range [1, bit_depth], and are + * only specified for the channels in the pixel data. The contents of + * the low-order bits is not specified. Data is valid if + * (valid & PNG_INFO_sBIT) is non-zero. + */ + png_color_8 sig_bit; /* significant bits in color channels */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \ +defined(PNG_READ_BACKGROUND_SUPPORTED) + /* The tRNS chunk supplies transparency data for paletted images and + * other image types that don't need a full alpha channel. There are + * "num_trans" transparency values for a paletted image, stored in the + * same order as the palette colors, starting from index 0. Values + * for the data are in the range [0, 255], ranging from fully transparent + * to fully opaque, respectively. For non-paletted images, there is a + * single color specified that should be treated as fully transparent. + * Data is valid if (valid & PNG_INFO_tRNS) is non-zero. + */ + png_bytep trans_alpha; /* alpha values for paletted image */ + png_color_16 trans_color; /* transparent color for non-palette image */ +#endif + +#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + /* The bKGD chunk gives the suggested image background color if the + * display program does not have its own background color and the image + * is needs to composited onto a background before display. The colors + * in "background" are normally in the same color space/depth as the + * pixel data. Data is valid if (valid & PNG_INFO_bKGD) is non-zero. + */ + png_color_16 background; +#endif + +#ifdef PNG_oFFs_SUPPORTED + /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards + * and downwards from the top-left corner of the display, page, or other + * application-specific co-ordinate space. See the PNG_OFFSET_ defines + * below for the unit types. Valid if (valid & PNG_INFO_oFFs) non-zero. + */ + png_int_32 x_offset; /* x offset on page */ + png_int_32 y_offset; /* y offset on page */ + png_byte offset_unit_type; /* offset units type */ +#endif + +#ifdef PNG_pHYs_SUPPORTED + /* The pHYs chunk gives the physical pixel density of the image for + * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_ + * defines below). Data is valid if (valid & PNG_INFO_pHYs) is non-zero. + */ + png_uint_32 x_pixels_per_unit; /* horizontal pixel density */ + png_uint_32 y_pixels_per_unit; /* vertical pixel density */ + png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */ +#endif + +#ifdef PNG_eXIf_SUPPORTED + int num_exif; /* Added at libpng-1.6.31 */ + png_bytep exif; +# ifdef PNG_READ_eXIf_SUPPORTED + png_bytep eXIf_buf; /* Added at libpng-1.6.32 */ +# endif +#endif + +#ifdef PNG_hIST_SUPPORTED + /* The hIST chunk contains the relative frequency or importance of the + * various palette entries, so that a viewer can intelligently select a + * reduced-color palette, if required. Data is an array of "num_palette" + * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST) + * is non-zero. + */ + png_uint_16p hist; +#endif + +#ifdef PNG_pCAL_SUPPORTED + /* The pCAL chunk describes a transformation between the stored pixel + * values and original physical data values used to create the image. + * The integer range [0, 2^bit_depth - 1] maps to the floating-point + * range given by [pcal_X0, pcal_X1], and are further transformed by a + * (possibly non-linear) transformation function given by "pcal_type" + * and "pcal_params" into "pcal_units". Please see the PNG_EQUATION_ + * defines below, and the PNG-Group's PNG extensions document for a + * complete description of the transformations and how they should be + * implemented, and for a description of the ASCII parameter strings. + * Data values are valid if (valid & PNG_INFO_pCAL) non-zero. + */ + png_charp pcal_purpose; /* pCAL chunk description string */ + png_int_32 pcal_X0; /* minimum value */ + png_int_32 pcal_X1; /* maximum value */ + png_charp pcal_units; /* Latin-1 string giving physical units */ + png_charpp pcal_params; /* ASCII strings containing parameter values */ + png_byte pcal_type; /* equation type (see PNG_EQUATION_ below) */ + png_byte pcal_nparams; /* number of parameters given in pcal_params */ +#endif + +/* New members added in libpng-1.0.6 */ + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + /* Storage for unknown chunks that the library doesn't recognize. */ + png_unknown_chunkp unknown_chunks; + + /* The type of this field is limited by the type of + * png_struct::user_chunk_cache_max, else overflow can occur. + */ + int unknown_chunks_num; +#endif + +#ifdef PNG_sPLT_SUPPORTED + /* Data on sPLT chunks (there may be more than one). */ + png_sPLT_tp splt_palettes; + int splt_palettes_num; /* Match type returned by png_get API */ +#endif + +#ifdef PNG_sCAL_SUPPORTED + /* The sCAL chunk describes the actual physical dimensions of the + * subject matter of the graphic. The chunk contains a unit specification + * a byte value, and two ASCII strings representing floating-point + * values. The values are width and height corresponsing to one pixel + * in the image. Data values are valid if (valid & PNG_INFO_sCAL) is + * non-zero. + */ + png_byte scal_unit; /* unit of physical scale */ + png_charp scal_s_width; /* string containing height */ + png_charp scal_s_height; /* string containing width */ +#endif + +#ifdef PNG_INFO_IMAGE_SUPPORTED + /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS) + non-zero */ + /* Data valid if (valid & PNG_INFO_IDAT) non-zero */ + png_bytepp row_pointers; /* the image bits */ +#endif + +}; +#endif /* PNGINFO_H */ diff --git a/src/png/libpng/pngmem.c b/src/png/libpng/pngmem.c new file mode 100644 index 0000000000..ff3ef7e88c --- /dev/null +++ b/src/png/libpng/pngmem.c @@ -0,0 +1,284 @@ + +/* pngmem.c - stub functions for memory allocation + * + * Last changed in libpng 1.6.26 [October 20, 2016] + * Copyright (c) 1998-2002,2004,2006-2014,2016 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all memory allocation. Users who + * need special memory handling are expected to supply replacement + * functions for png_malloc() and png_free(), and to use + * png_create_read_struct_2() and png_create_write_struct_2() to + * identify the replacement functions. + */ + +#include "pngpriv.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Free a png_struct */ +void /* PRIVATE */ +png_destroy_png_struct(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + /* png_free might call png_error and may certainly call + * png_get_mem_ptr, so fake a temporary png_struct to support this. + */ + png_struct dummy_struct = *png_ptr; + memset(png_ptr, 0, (sizeof *png_ptr)); + png_free(&dummy_struct, png_ptr); + +# ifdef PNG_SETJMP_SUPPORTED + /* We may have a jmp_buf left to deallocate. */ + png_free_jmpbuf(&dummy_struct); +# endif + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more than 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + */ +PNG_FUNCTION(png_voidp,PNGAPI +png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) +{ + png_voidp ret; + + ret = png_malloc(png_ptr, size); + + if (ret != NULL) + memset(ret, 0, size); + + return ret; +} + +/* png_malloc_base, an internal function added at libpng 1.6.0, does the work of + * allocating memory, taking into account limits and PNG_USER_MEM_SUPPORTED. + * Checking and error handling must happen outside this routine; it returns NULL + * if the allocation cannot be done (for any reason.) + */ +PNG_FUNCTION(png_voidp /* PRIVATE */, +png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size), + PNG_ALLOCATED) +{ + /* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS + * allocators have also been removed in 1.6.0, so any 16-bit system now has + * to implement a user memory handler. This checks to be sure it isn't + * called with big numbers. + */ +#ifndef PNG_USER_MEM_SUPPORTED + PNG_UNUSED(png_ptr) +#endif + + /* Some compilers complain that this is always true. However, it + * can be false when integer overflow happens. + */ + if (size > 0 && size <= PNG_SIZE_MAX +# ifdef PNG_MAX_MALLOC_64K + && size <= 65536U +# endif + ) + { +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr != NULL && png_ptr->malloc_fn != NULL) + return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size); + + else +#endif + return malloc((size_t)size); /* checked for truncation above */ + } + + else + return NULL; +} + +#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\ + defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) +/* This is really here only to work round a spurious warning in GCC 4.6 and 4.7 + * that arises because of the checks in png_realloc_array that are repeated in + * png_malloc_array. + */ +static png_voidp +png_malloc_array_checked(png_const_structrp png_ptr, int nelements, + size_t element_size) +{ + png_alloc_size_t req = (png_alloc_size_t)nelements; /* known to be > 0 */ + + if (req <= PNG_SIZE_MAX/element_size) + return png_malloc_base(png_ptr, req * element_size); + + /* The failure case when the request is too large */ + return NULL; +} + +PNG_FUNCTION(png_voidp /* PRIVATE */, +png_malloc_array,(png_const_structrp png_ptr, int nelements, + size_t element_size),PNG_ALLOCATED) +{ + if (nelements <= 0 || element_size == 0) + png_error(png_ptr, "internal error: array alloc"); + + return png_malloc_array_checked(png_ptr, nelements, element_size); +} + +PNG_FUNCTION(png_voidp /* PRIVATE */, +png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array, + int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED) +{ + /* These are internal errors: */ + if (add_elements <= 0 || element_size == 0 || old_elements < 0 || + (old_array == NULL && old_elements > 0)) + png_error(png_ptr, "internal error: array realloc"); + + /* Check for overflow on the elements count (so the caller does not have to + * check.) + */ + if (add_elements <= INT_MAX - old_elements) + { + png_voidp new_array = png_malloc_array_checked(png_ptr, + old_elements+add_elements, element_size); + + if (new_array != NULL) + { + /* Because png_malloc_array worked the size calculations below cannot + * overflow. + */ + if (old_elements > 0) + memcpy(new_array, old_array, element_size*(unsigned)old_elements); + + memset((char*)new_array + element_size*(unsigned)old_elements, 0, + element_size*(unsigned)add_elements); + + return new_array; + } + } + + return NULL; /* error */ +} +#endif /* TEXT || sPLT || STORE_UNKNOWN_CHUNKS */ + +/* Various functions that have different error handling are derived from this. + * png_malloc always exists, but if PNG_USER_MEM_SUPPORTED is defined a separate + * function png_malloc_default is also provided. + */ +PNG_FUNCTION(png_voidp,PNGAPI +png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) +{ + png_voidp ret; + + if (png_ptr == NULL) + return NULL; + + ret = png_malloc_base(png_ptr, size); + + if (ret == NULL) + png_error(png_ptr, "Out of memory"); /* 'm' means png_malloc */ + + return ret; +} + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_FUNCTION(png_voidp,PNGAPI +png_malloc_default,(png_const_structrp png_ptr, png_alloc_size_t size), + PNG_ALLOCATED PNG_DEPRECATED) +{ + png_voidp ret; + + if (png_ptr == NULL) + return NULL; + + /* Passing 'NULL' here bypasses the application provided memory handler. */ + ret = png_malloc_base(NULL/*use malloc*/, size); + + if (ret == NULL) + png_error(png_ptr, "Out of Memory"); /* 'M' means png_malloc_default */ + + return ret; +} +#endif /* USER_MEM */ + +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will issue a png_warning and return NULL instead of issuing a + * png_error, if it fails to allocate the requested memory. + */ +PNG_FUNCTION(png_voidp,PNGAPI +png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size), + PNG_ALLOCATED) +{ + if (png_ptr != NULL) + { + png_voidp ret = png_malloc_base(png_ptr, size); + + if (ret != NULL) + return ret; + + png_warning(png_ptr, "Out of memory"); + } + + return NULL; +} + +/* Free a pointer allocated by png_malloc(). If ptr is NULL, return + * without taking any action. + */ +void PNGAPI +png_free(png_const_structrp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + png_ptr->free_fn(png_constcast(png_structrp,png_ptr), ptr); + + else + png_free_default(png_ptr, ptr); +} + +PNG_FUNCTION(void,PNGAPI +png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED) +{ + if (png_ptr == NULL || ptr == NULL) + return; +#endif /* USER_MEM */ + + free(ptr); +} + +#ifdef PNG_USER_MEM_SUPPORTED +/* This function is called when the application wants to use another method + * of allocating and freeing memory. + */ +void PNGAPI +png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr, png_malloc_ptr + malloc_fn, png_free_ptr free_fn) +{ + if (png_ptr != NULL) + { + png_ptr->mem_ptr = mem_ptr; + png_ptr->malloc_fn = malloc_fn; + png_ptr->free_fn = free_fn; + } +} + +/* This function returns a pointer to the mem_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_mem_ptr(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return NULL; + + return png_ptr->mem_ptr; +} +#endif /* USER_MEM */ +#endif /* READ || WRITE */ diff --git a/src/png/libpng/pngpread.c b/src/png/libpng/pngpread.c new file mode 100644 index 0000000000..fbe361dc34 --- /dev/null +++ b/src/png/libpng/pngpread.c @@ -0,0 +1,1096 @@ + +/* pngpread.c - read a png file in push mode + * + * Last changed in libpng 1.6.32 [August 24, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + +/* Push model modes */ +#define PNG_READ_SIG_MODE 0 +#define PNG_READ_CHUNK_MODE 1 +#define PNG_READ_IDAT_MODE 2 +#define PNG_READ_tEXt_MODE 4 +#define PNG_READ_zTXt_MODE 5 +#define PNG_READ_DONE_MODE 6 +#define PNG_READ_iTXt_MODE 7 +#define PNG_ERROR_MODE 8 + +#define PNG_PUSH_SAVE_BUFFER_IF_FULL \ +if (png_ptr->push_length + 4 > png_ptr->buffer_size) \ + { png_push_save_buffer(png_ptr); return; } +#define PNG_PUSH_SAVE_BUFFER_IF_LT(N) \ +if (png_ptr->buffer_size < N) \ + { png_push_save_buffer(png_ptr); return; } + +void PNGAPI +png_process_data(png_structrp png_ptr, png_inforp info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + { + png_process_some_data(png_ptr, info_ptr); + } +} + +png_size_t PNGAPI +png_process_data_pause(png_structrp png_ptr, int save) +{ + if (png_ptr != NULL) + { + /* It's easiest for the caller if we do the save; then the caller doesn't + * have to supply the same data again: + */ + if (save != 0) + png_push_save_buffer(png_ptr); + else + { + /* This includes any pending saved bytes: */ + png_size_t remaining = png_ptr->buffer_size; + png_ptr->buffer_size = 0; + + /* So subtract the saved buffer size, unless all the data + * is actually 'saved', in which case we just return 0 + */ + if (png_ptr->save_buffer_size < remaining) + return remaining - png_ptr->save_buffer_size; + } + } + + return 0; +} + +png_uint_32 PNGAPI +png_process_data_skip(png_structrp png_ptr) +{ +/* TODO: Deprecate and remove this API. + * Somewhere the implementation of this seems to have been lost, + * or abandoned. It was only to support some internal back-door access + * to png_struct) in libpng-1.4.x. + */ + png_app_warning(png_ptr, +"png_process_data_skip is not implemented in any current version of libpng"); + return 0; +} + +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +void /* PRIVATE */ +png_process_some_data(png_structrp png_ptr, png_inforp info_ptr) +{ + if (png_ptr == NULL) + return; + + switch (png_ptr->process_mode) + { + case PNG_READ_SIG_MODE: + { + png_push_read_sig(png_ptr, info_ptr); + break; + } + + case PNG_READ_CHUNK_MODE: + { + png_push_read_chunk(png_ptr, info_ptr); + break; + } + + case PNG_READ_IDAT_MODE: + { + png_push_read_IDAT(png_ptr); + break; + } + + default: + { + png_ptr->buffer_size = 0; + break; + } + } +} + +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +void /* PRIVATE */ +png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr) +{ + png_size_t num_checked = png_ptr->sig_bytes, /* SAFE, does not exceed 8 */ + num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + { + num_to_check = png_ptr->buffer_size; + } + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + else + { + if (png_ptr->sig_bytes >= 8) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + } +} + +void /* PRIVATE */ +png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) +{ + png_uint_32 chunk_name; +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int keep; /* unknown handling method */ +#endif + + /* First we make sure we have enough data for the 4-byte chunk name + * and the 4-byte chunk length before proceeding with decoding the + * chunk data. To fully decode each of these chunks, we also make + * sure we have enough data in the buffer for the 4-byte CRC at the + * end of every chunk (except IDAT, which is handled separately). + */ + if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0) + { + png_byte chunk_length[4]; + png_byte chunk_tag[4]; + + PNG_PUSH_SAVE_BUFFER_IF_LT(8) + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, chunk_tag, 4); + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + png_check_chunk_length(png_ptr, png_ptr->push_length); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + } + + chunk_name = png_ptr->chunk_name; + + if (chunk_name == png_IDAT) + { + if ((png_ptr->mode & PNG_AFTER_IDAT) != 0) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + /* If we reach an IDAT chunk, this means we have read all of the + * header chunks, and we can start reading the image (or if this + * is called after the image has been read - we have an error). + */ + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_error(png_ptr, "Missing IHDR before IDAT"); + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + (png_ptr->mode & PNG_HAVE_PLTE) == 0) + png_error(png_ptr, "Missing PLTE before IDAT"); + + png_ptr->process_mode = PNG_READ_IDAT_MODE; + + if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + if ((png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) == 0) + if (png_ptr->push_length == 0) + return; + + png_ptr->mode |= PNG_HAVE_IDAT; + + if ((png_ptr->mode & PNG_AFTER_IDAT) != 0) + png_benign_error(png_ptr, "Too many IDATs found"); + } + + if (chunk_name == png_IHDR) + { + if (png_ptr->push_length != 13) + png_error(png_ptr, "Invalid IHDR length"); + + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + } + + else if (chunk_name == png_IEND) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + + png_ptr->process_mode = PNG_READ_DONE_MODE; + png_push_have_end(png_ptr, info_ptr); + } + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, keep); + + if (chunk_name == png_PLTE) + png_ptr->mode |= PNG_HAVE_PLTE; + } +#endif + + else if (chunk_name == png_PLTE) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); + } + + else if (chunk_name == png_IDAT) + { + png_ptr->idat_size = png_ptr->push_length; + png_ptr->process_mode = PNG_READ_IDAT_MODE; + png_push_have_info(png_ptr, info_ptr); + png_ptr->zstream.avail_out = + (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + png_ptr->zstream.next_out = png_ptr->row_buf; + return; + } + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (png_ptr->chunk_name == png_gAMA) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + else if (png_ptr->chunk_name == png_sBIT) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + else if (png_ptr->chunk_name == png_cHRM) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + else if (chunk_name == png_sRGB) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + else if (png_ptr->chunk_name == png_iCCP) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + else if (chunk_name == png_sPLT) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + else if (chunk_name == png_tRNS) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_bKGD_SUPPORTED + else if (chunk_name == png_bKGD) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_hIST_SUPPORTED + else if (chunk_name == png_hIST) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + else if (chunk_name == png_pHYs) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + else if (chunk_name == png_oFFs) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (chunk_name == png_pCAL) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + else if (chunk_name == png_sCAL) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tIME_SUPPORTED + else if (chunk_name == png_tIME) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + else if (chunk_name == png_tEXt) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + else if (chunk_name == png_zTXt) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + else if (chunk_name == png_iTXt) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif + + else + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, + PNG_HANDLE_CHUNK_AS_DEFAULT); + } + + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; +} + +void PNGCBAPI +png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) +{ + png_bytep ptr; + + if (png_ptr == NULL) + return; + + ptr = buffer; + if (png_ptr->save_buffer_size != 0) + { + png_size_t save_size; + + if (length < png_ptr->save_buffer_size) + save_size = length; + + else + save_size = png_ptr->save_buffer_size; + + memcpy(ptr, png_ptr->save_buffer_ptr, save_size); + length -= save_size; + ptr += save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (length != 0 && png_ptr->current_buffer_size != 0) + { + png_size_t save_size; + + if (length < png_ptr->current_buffer_size) + save_size = length; + + else + save_size = png_ptr->current_buffer_size; + + memcpy(ptr, png_ptr->current_buffer_ptr, save_size); + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } +} + +void /* PRIVATE */ +png_push_save_buffer(png_structrp png_ptr) +{ + if (png_ptr->save_buffer_size != 0) + { + if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) + { + png_size_t i, istop; + png_bytep sp; + png_bytep dp; + + istop = png_ptr->save_buffer_size; + for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; + i < istop; i++, sp++, dp++) + { + *dp = *sp; + } + } + } + if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > + png_ptr->save_buffer_max) + { + png_size_t new_max; + png_bytep old_buffer; + + if (png_ptr->save_buffer_size > PNG_SIZE_MAX - + (png_ptr->current_buffer_size + 256)) + { + png_error(png_ptr, "Potential overflow of save_buffer"); + } + + new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; + old_buffer = png_ptr->save_buffer; + png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr, + (png_size_t)new_max); + + if (png_ptr->save_buffer == NULL) + { + png_free(png_ptr, old_buffer); + png_error(png_ptr, "Insufficient memory for save_buffer"); + } + + if (old_buffer) + memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); + else if (png_ptr->save_buffer_size) + png_error(png_ptr, "save_buffer error"); + png_free(png_ptr, old_buffer); + png_ptr->save_buffer_max = new_max; + } + if (png_ptr->current_buffer_size) + { + memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, + png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); + png_ptr->save_buffer_size += png_ptr->current_buffer_size; + png_ptr->current_buffer_size = 0; + } + png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_ptr->buffer_size = 0; +} + +void /* PRIVATE */ +png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + png_ptr->current_buffer = buffer; + png_ptr->current_buffer_size = buffer_length; + png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; + png_ptr->current_buffer_ptr = png_ptr->current_buffer; +} + +void /* PRIVATE */ +png_push_read_IDAT(png_structrp png_ptr) +{ + if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0) + { + png_byte chunk_length[4]; + png_byte chunk_tag[4]; + + /* TODO: this code can be commoned up with the same code in push_read */ + PNG_PUSH_SAVE_BUFFER_IF_LT(8) + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, chunk_tag, 4); + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + + if (png_ptr->chunk_name != png_IDAT) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0) + png_error(png_ptr, "Not enough compressed data"); + + return; + } + + png_ptr->idat_size = png_ptr->push_length; + } + + if (png_ptr->idat_size != 0 && png_ptr->save_buffer_size != 0) + { + png_size_t save_size = png_ptr->save_buffer_size; + png_uint_32 idat_size = png_ptr->idat_size; + + /* We want the smaller of 'idat_size' and 'current_buffer_size', but they + * are of different types and we don't know which variable has the fewest + * bits. Carefully select the smaller and cast it to the type of the + * larger - this cannot overflow. Do not cast in the following test - it + * will break on either 16-bit or 64-bit platforms. + */ + if (idat_size < save_size) + save_size = (png_size_t)idat_size; + + else + idat_size = (png_uint_32)save_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->idat_size -= idat_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + + if (png_ptr->idat_size != 0 && png_ptr->current_buffer_size != 0) + { + png_size_t save_size = png_ptr->current_buffer_size; + png_uint_32 idat_size = png_ptr->idat_size; + + /* We want the smaller of 'idat_size' and 'current_buffer_size', but they + * are of different types and we don't know which variable has the fewest + * bits. Carefully select the smaller and cast it to the type of the + * larger - this cannot overflow. + */ + if (idat_size < save_size) + save_size = (png_size_t)idat_size; + + else + idat_size = (png_uint_32)save_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->idat_size -= idat_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + + if (png_ptr->idat_size == 0) + { + PNG_PUSH_SAVE_BUFFER_IF_LT(4) + png_crc_finish(png_ptr, 0); + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->zowner = 0; + } +} + +void /* PRIVATE */ +png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + /* The caller checks for a non-zero buffer length. */ + if (!(buffer_length > 0) || buffer == NULL) + png_error(png_ptr, "No IDAT data (internal error)"); + + /* This routine must process all the data it has been given + * before returning, calling the row callback as required to + * handle the uncompressed results. + */ + png_ptr->zstream.next_in = buffer; + /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ + png_ptr->zstream.avail_in = (uInt)buffer_length; + + /* Keep going until the decompressed data is all processed + * or the stream marked as finished. + */ + while (png_ptr->zstream.avail_in > 0 && + (png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0) + { + int ret; + + /* We have data for zlib, but we must check that zlib + * has someplace to put the results. It doesn't matter + * if we don't expect any results -- it may be the input + * data is just the LZ end code. + */ + if (!(png_ptr->zstream.avail_out > 0)) + { + /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ + png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1); + + png_ptr->zstream.next_out = png_ptr->row_buf; + } + + /* Using Z_SYNC_FLUSH here means that an unterminated + * LZ stream (a stream with a missing end code) can still + * be handled, otherwise (Z_NO_FLUSH) a future zlib + * implementation might defer output and therefore + * change the current behavior (see comments in inflate.c + * for why this doesn't happen at present with zlib 1.2.5). + */ + ret = PNG_INFLATE(png_ptr, Z_SYNC_FLUSH); + + /* Check for any failure before proceeding. */ + if (ret != Z_OK && ret != Z_STREAM_END) + { + /* Terminate the decompression. */ + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + png_ptr->zowner = 0; + + /* This may be a truncated stream (missing or + * damaged end code). Treat that as a warning. + */ + if (png_ptr->row_number >= png_ptr->num_rows || + png_ptr->pass > 6) + png_warning(png_ptr, "Truncated compressed data in IDAT"); + + else + { + if (ret == Z_DATA_ERROR) + png_benign_error(png_ptr, "IDAT: ADLER32 checksum mismatch"); + else + png_error(png_ptr, "Decompression error in IDAT"); + } + + /* Skip the check on unprocessed input */ + return; + } + + /* Did inflate output any data? */ + if (png_ptr->zstream.next_out != png_ptr->row_buf) + { + /* Is this unexpected data after the last row? + * If it is, artificially terminate the LZ output + * here. + */ + if (png_ptr->row_number >= png_ptr->num_rows || + png_ptr->pass > 6) + { + /* Extra data. */ + png_warning(png_ptr, "Extra compressed data in IDAT"); + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + png_ptr->zowner = 0; + + /* Do no more processing; skip the unprocessed + * input check below. + */ + return; + } + + /* Do we have a complete row? */ + if (png_ptr->zstream.avail_out == 0) + png_push_process_row(png_ptr); + } + + /* And check for the end of the stream. */ + if (ret == Z_STREAM_END) + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + } + + /* All the data should have been processed, if anything + * is left at this point we have bytes of IDAT data + * after the zlib end code. + */ + if (png_ptr->zstream.avail_in > 0) + png_warning(png_ptr, "Extra compression data in IDAT"); +} + +void /* PRIVATE */ +png_push_process_row(png_structrp png_ptr) +{ + /* 1.5.6: row_info moved out of png_struct to a local here. */ + png_row_info row_info; + + row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */ + row_info.color_type = png_ptr->color_type; + row_info.bit_depth = png_ptr->bit_depth; + row_info.channels = png_ptr->channels; + row_info.pixel_depth = png_ptr->pixel_depth; + row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); + + if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) + { + if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) + png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, + png_ptr->prev_row + 1, png_ptr->row_buf[0]); + else + png_error(png_ptr, "bad adaptive filter value"); + } + + /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before + * 1.5.6, while the buffer really is this big in current versions of libpng + * it may not be in the future, so this was changed just to copy the + * interlaced row count: + */ + memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED + if (png_ptr->transformations != 0) + png_do_read_transformations(png_ptr, &row_info); +#endif + + /* The transformed pixel depth should match the depth now in row_info. */ + if (png_ptr->transformed_pixel_depth == 0) + { + png_ptr->transformed_pixel_depth = row_info.pixel_depth; + if (row_info.pixel_depth > png_ptr->maximum_pixel_depth) + png_error(png_ptr, "progressive row overflow"); + } + + else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth) + png_error(png_ptr, "internal progressive row size calculation error"); + + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Expand interlaced rows to full size */ + if (png_ptr->interlaced != 0 && + (png_ptr->transformations & PNG_INTERLACE) != 0) + { + if (png_ptr->pass < 6) + png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass, + png_ptr->transformations); + + switch (png_ptr->pass) + { + case 0: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 0; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */ + } + + if (png_ptr->pass == 2) /* Pass 1 might be empty */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + if (png_ptr->pass == 4 && png_ptr->height <= 4) + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + if (png_ptr->pass == 6 && png_ptr->height <= 4) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + + case 1: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 1; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 2) /* Skip top 4 generated rows */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 2: + { + int i; + + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 4) /* Pass 3 might be empty */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 3: + { + int i; + + for (i = 0; i < 4 && png_ptr->pass == 3; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 4) /* Skip top two generated rows */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 4: + { + int i; + + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 6) /* Pass 5 might be empty */ + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + + case 5: + { + int i; + + for (i = 0; i < 2 && png_ptr->pass == 5; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 6) /* Skip top generated row */ + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + + default: + case 6: + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + + if (png_ptr->pass != 6) + break; + + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + } + else +#endif + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } +} + +void /* PRIVATE */ +png_read_push_finish_row(png_structrp png_ptr) +{ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + + /* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + static PNG_CONST png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; + */ +#endif + + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced != 0) + { + png_ptr->row_number = 0; + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + do + { + png_ptr->pass++; + if ((png_ptr->pass == 1 && png_ptr->width < 5) || + (png_ptr->pass == 3 && png_ptr->width < 3) || + (png_ptr->pass == 5 && png_ptr->width < 2)) + png_ptr->pass++; + + if (png_ptr->pass > 7) + png_ptr->pass--; + + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + if ((png_ptr->transformations & PNG_INTERLACE) != 0) + break; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + } +#endif /* READ_INTERLACING */ +} + +void /* PRIVATE */ +png_push_have_info(png_structrp png_ptr, png_inforp info_ptr) +{ + if (png_ptr->info_fn != NULL) + (*(png_ptr->info_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_end(png_structrp png_ptr, png_inforp info_ptr) +{ + if (png_ptr->end_fn != NULL) + (*(png_ptr->end_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_row(png_structrp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, + (int)png_ptr->pass); +} + +#ifdef PNG_READ_INTERLACING_SUPPORTED +void PNGAPI +png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row, + png_const_bytep new_row) +{ + if (png_ptr == NULL) + return; + + /* new_row is a flag here - if it is NULL then the app callback was called + * from an empty row (see the calls to png_struct::row_fn below), otherwise + * it must be png_ptr->row_buf+1 + */ + if (new_row != NULL) + png_combine_row(png_ptr, old_row, 1/*blocky display*/); +} +#endif /* READ_INTERLACING */ + +void PNGAPI +png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->info_fn = info_fn; + png_ptr->row_fn = row_fn; + png_ptr->end_fn = end_fn; + + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); +} + +png_voidp PNGAPI +png_get_progressive_ptr(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + + return png_ptr->io_ptr; +} +#endif /* PROGRESSIVE_READ */ diff --git a/src/png/libpng/pngpriv.h b/src/png/libpng/pngpriv.h new file mode 100644 index 0000000000..1f2e90f2b3 --- /dev/null +++ b/src/png/libpng/pngpriv.h @@ -0,0 +1,2120 @@ + +/* pngpriv.h - private declarations for use inside libpng + * + * Last changed in libpng 1.6.32 [August 24, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* The symbols declared in this file (including the functions declared + * as extern) are PRIVATE. They are not part of the libpng public + * interface, and are not recommended for use by regular applications. + * Some of them may become public in the future; others may stay private, + * change in an incompatible way, or even disappear. + * Although the libpng users are not forbidden to include this header, + * they should be well aware of the issues that may arise from doing so. + */ + +#ifndef PNGPRIV_H +#define PNGPRIV_H + +/* Feature Test Macros. The following are defined here to ensure that correctly + * implemented libraries reveal the APIs libpng needs to build and hide those + * that are not needed and potentially damaging to the compilation. + * + * Feature Test Macros must be defined before any system header is included (see + * POSIX 1003.1 2.8.2 "POSIX Symbols." + * + * These macros only have an effect if the operating system supports either + * POSIX 1003.1 or C99, or both. On other operating systems (particularly + * Windows/Visual Studio) there is no effect; the OS specific tests below are + * still required (as of 2011-05-02.) + */ +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */ +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Standard library headers not required by png.h: */ +# include +# include +#endif + +#define PNGLIB_BUILD /*libpng is being built, not used*/ + +/* If HAVE_CONFIG_H is defined during the build then the build system must + * provide an appropriate "config.h" file on the include path. The header file + * must provide definitions as required below (search for "HAVE_CONFIG_H"); + * see configure.ac for more details of the requirements. The macro + * "PNG_NO_CONFIG_H" is provided for maintainers to test for dependencies on + * 'configure'; define this macro to prevent the configure build including the + * configure generated config.h. Libpng is expected to compile without *any* + * special build system support on a reasonably ANSI-C compliant system. + */ +#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H) +# include + + /* Pick up the definition of 'restrict' from config.h if it was read: */ +# define PNG_RESTRICT restrict +#endif + +/* To support symbol prefixing it is necessary to know *before* including png.h + * whether the fixed point (and maybe other) APIs are exported, because if they + * are not internal definitions may be required. This is handled below just + * before png.h is included, but load the configuration now if it is available. + */ +#ifndef PNGLCONF_H +# include "pnglibconf.h" +#endif + +/* Local renames may change non-exported API functions from png.h */ +#if defined(PNG_PREFIX) && !defined(PNGPREFIX_H) +# include "pngprefix.h" +#endif + +#ifdef PNG_USER_CONFIG +# include "pngusr.h" + /* These should have been defined in pngusr.h */ +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD "Custom libpng build" +# endif +# ifndef PNG_USER_DLLFNAME_POSTFIX +# define PNG_USER_DLLFNAME_POSTFIX "Cb" +# endif +#endif + +/* Compile time options. + * ===================== + * In a multi-arch build the compiler may compile the code several times for the + * same object module, producing different binaries for different architectures. + * When this happens configure-time setting of the target host options cannot be + * done and this interferes with the handling of the ARM NEON optimizations, and + * possibly other similar optimizations. Put additional tests here; in general + * this is needed when the same option can be changed at both compile time and + * run time depending on the target OS (i.e. iOS vs Android.) + * + * NOTE: symbol prefixing does not pass $(CFLAGS) to the preprocessor, because + * this is not possible with certain compilers (Oracle SUN OS CC), as a result + * it is necessary to ensure that all extern functions that *might* be used + * regardless of $(CFLAGS) get declared in this file. The test on __ARM_NEON__ + * below is one example of this behavior because it is controlled by the + * presence or not of -mfpu=neon on the GCC command line, it is possible to do + * this in $(CC), e.g. "CC=gcc -mfpu=neon", but people who build libpng rarely + * do this. + */ +#ifndef PNG_ARM_NEON_OPT + /* ARM NEON optimizations are being controlled by the compiler settings, + * typically the target FPU. If the FPU has been set to NEON (-mfpu=neon + * with GCC) then the compiler will define __ARM_NEON__ and we can rely + * unconditionally on NEON instructions not crashing, otherwise we must + * disable use of NEON instructions. + * + * NOTE: at present these optimizations depend on 'ALIGNED_MEMORY', so they + * can only be turned on automatically if that is supported too. If + * PNG_ARM_NEON_OPT is set in CPPFLAGS (to >0) then arm/arm_init.c will fail + * to compile with an appropriate #error if ALIGNED_MEMORY has been turned + * off. + * + * Note that gcc-4.9 defines __ARM_NEON instead of the deprecated + * __ARM_NEON__, so we check both variants. + * + * To disable ARM_NEON optimizations entirely, and skip compiling the + * associated assembler code, pass --enable-arm-neon=no to configure + * or put -DPNG_ARM_NEON_OPT=0 in CPPFLAGS. + */ +# if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && \ + defined(PNG_ALIGNED_MEMORY_SUPPORTED) +# define PNG_ARM_NEON_OPT 2 +# else +# define PNG_ARM_NEON_OPT 0 +# endif +#endif + +#if PNG_ARM_NEON_OPT > 0 + /* NEON optimizations are to be at least considered by libpng, so enable the + * callbacks to do this. + */ +# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_neon + + /* By default the 'intrinsics' code in arm/filter_neon_intrinsics.c is used + * if possible - if __ARM_NEON__ is set and the compiler version is not known + * to be broken. This is controlled by PNG_ARM_NEON_IMPLEMENTATION which can + * be: + * + * 1 The intrinsics code (the default with __ARM_NEON__) + * 2 The hand coded assembler (the default without __ARM_NEON__) + * + * It is possible to set PNG_ARM_NEON_IMPLEMENTATION in CPPFLAGS, however + * this is *NOT* supported and may cease to work even after a minor revision + * to libpng. It *is* valid to do this for testing purposes, e.g. speed + * testing or a new compiler, but the results should be communicated to the + * libpng implementation list for incorporation in the next minor release. + */ +# ifndef PNG_ARM_NEON_IMPLEMENTATION +# if defined(__ARM_NEON__) || defined(__ARM_NEON) +# if defined(__clang__) + /* At present it is unknown by the libpng developers which versions + * of clang support the intrinsics, however some or perhaps all + * versions do not work with the assembler so this may be + * irrelevant, so just use the default (do nothing here.) + */ +# elif defined(__GNUC__) + /* GCC 4.5.4 NEON support is known to be broken. 4.6.3 is known to + * work, so if this *is* GCC, or G++, look for a version >4.5 + */ +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6) +# define PNG_ARM_NEON_IMPLEMENTATION 2 +# endif /* no GNUC support */ +# endif /* __GNUC__ */ +# else /* !defined __ARM_NEON__ */ + /* The 'intrinsics' code simply won't compile without this -mfpu=neon: + */ +# define PNG_ARM_NEON_IMPLEMENTATION 2 +# endif /* __ARM_NEON__ */ +# endif /* !PNG_ARM_NEON_IMPLEMENTATION */ + +# ifndef PNG_ARM_NEON_IMPLEMENTATION + /* Use the intrinsics code by default. */ +# define PNG_ARM_NEON_IMPLEMENTATION 1 +# endif +#endif /* PNG_ARM_NEON_OPT > 0 */ + +#ifndef PNG_MIPS_MSA_OPT +# if defined(__mips_msa) && (__mips_isa_rev >= 5) && defined(PNG_ALIGNED_MEMORY_SUPPORTED) +# define PNG_MIPS_MSA_OPT 2 +# else +# define PNG_MIPS_MSA_OPT 0 +# endif +#endif + +#ifndef PNG_POWERPC_VSX_OPT +# if defined(__PPC64__) && defined(__ALTIVEC__) && defined(__VSX__) +# define PNG_POWERPC_VSX_OPT 2 +# else +# define PNG_POWERPC_VSX_OPT 0 +# endif +#endif + +#ifndef PNG_INTEL_SSE_OPT +# ifdef PNG_INTEL_SSE + /* Only check for SSE if the build configuration has been modified to + * enable SSE optimizations. This means that these optimizations will + * be off by default. See contrib/intel for more details. + */ +# if defined(__SSE4_1__) || defined(__AVX__) || defined(__SSSE3__) || \ + defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP >= 2) +# define PNG_INTEL_SSE_OPT 1 +# endif +# endif +#endif + +#if PNG_INTEL_SSE_OPT > 0 +# ifndef PNG_INTEL_SSE_IMPLEMENTATION +# if defined(__SSE4_1__) || defined(__AVX__) + /* We are not actually using AVX, but checking for AVX is the best + way we can detect SSE4.1 and SSSE3 on MSVC. + */ +# define PNG_INTEL_SSE_IMPLEMENTATION 3 +# elif defined(__SSSE3__) +# define PNG_INTEL_SSE_IMPLEMENTATION 2 +# elif defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP >= 2) +# define PNG_INTEL_SSE_IMPLEMENTATION 1 +# else +# define PNG_INTEL_SSE_IMPLEMENTATION 0 +# endif +# endif + +# if PNG_INTEL_SSE_IMPLEMENTATION > 0 +# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_sse2 +# endif +#endif + +#if PNG_MIPS_MSA_OPT > 0 +# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_msa +# ifndef PNG_MIPS_MSA_IMPLEMENTATION +# if defined(__mips_msa) +# if defined(__clang__) +# elif defined(__GNUC__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) +# define PNG_MIPS_MSA_IMPLEMENTATION 2 +# endif /* no GNUC support */ +# endif /* __GNUC__ */ +# else /* !defined __mips_msa */ +# define PNG_MIPS_MSA_IMPLEMENTATION 2 +# endif /* __mips_msa */ +# endif /* !PNG_MIPS_MSA_IMPLEMENTATION */ + +# ifndef PNG_MIPS_MSA_IMPLEMENTATION +# define PNG_MIPS_MSA_IMPLEMENTATION 1 +# endif +#endif /* PNG_MIPS_MSA_OPT > 0 */ + +#if PNG_POWERPC_VSX_OPT > 0 +# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_vsx +# define PNG_POWERPC_VSX_IMPLEMENTATION 1 +#endif + + +/* Is this a build of a DLL where compilation of the object modules requires + * different preprocessor settings to those required for a simple library? If + * so PNG_BUILD_DLL must be set. + * + * If libpng is used inside a DLL but that DLL does not export the libpng APIs + * PNG_BUILD_DLL must not be set. To avoid the code below kicking in build a + * static library of libpng then link the DLL against that. + */ +#ifndef PNG_BUILD_DLL +# ifdef DLL_EXPORT + /* This is set by libtool when files are compiled for a DLL; libtool + * always compiles twice, even on systems where it isn't necessary. Set + * PNG_BUILD_DLL in case it is necessary: + */ +# define PNG_BUILD_DLL +# else +# ifdef _WINDLL + /* This is set by the Microsoft Visual Studio IDE in projects that + * build a DLL. It can't easily be removed from those projects (it + * isn't visible in the Visual Studio UI) so it is a fairly reliable + * indication that PNG_IMPEXP needs to be set to the DLL export + * attributes. + */ +# define PNG_BUILD_DLL +# else +# ifdef __DLL__ + /* This is set by the Borland C system when compiling for a DLL + * (as above.) + */ +# define PNG_BUILD_DLL +# else + /* Add additional compiler cases here. */ +# endif +# endif +# endif +#endif /* Setting PNG_BUILD_DLL if required */ + +/* See pngconf.h for more details: the builder of the library may set this on + * the command line to the right thing for the specific compilation system or it + * may be automagically set above (at present we know of no system where it does + * need to be set on the command line.) + * + * PNG_IMPEXP must be set here when building the library to prevent pngconf.h + * setting it to the "import" setting for a DLL build. + */ +#ifndef PNG_IMPEXP +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP PNG_DLL_EXPORT +# else + /* Not building a DLL, or the DLL doesn't require specific export + * definitions. + */ +# define PNG_IMPEXP +# endif +#endif + +/* No warnings for private or deprecated functions in the build: */ +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE +#endif + +/* Symbol preprocessing support. + * + * To enable listing global, but internal, symbols the following macros should + * always be used to declare an extern data or function object in this file. + */ +#ifndef PNG_INTERNAL_DATA +# define PNG_INTERNAL_DATA(type, name, array) PNG_LINKAGE_DATA type name array +#endif + +#ifndef PNG_INTERNAL_FUNCTION +# define PNG_INTERNAL_FUNCTION(type, name, args, attributes)\ + PNG_LINKAGE_FUNCTION PNG_FUNCTION(type, name, args, PNG_EMPTY attributes) +#endif + +#ifndef PNG_INTERNAL_CALLBACK +# define PNG_INTERNAL_CALLBACK(type, name, args, attributes)\ + PNG_LINKAGE_CALLBACK PNG_FUNCTION(type, (PNGCBAPI name), args,\ + PNG_EMPTY attributes) +#endif + +/* If floating or fixed point APIs are disabled they may still be compiled + * internally. To handle this make sure they are declared as the appropriate + * internal extern function (otherwise the symbol prefixing stuff won't work and + * the functions will be used without definitions.) + * + * NOTE: although all the API functions are declared here they are not all + * actually built! Because the declarations are still made it is necessary to + * fake out types that they depend on. + */ +#ifndef PNG_FP_EXPORT +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY); +# ifndef PNG_VERSION_INFO_ONLY + typedef struct png_incomplete png_double; + typedef png_double* png_doublep; + typedef const png_double* png_const_doublep; + typedef png_double** png_doublepp; +# endif +# endif +#endif +#ifndef PNG_FIXED_EXPORT +# ifndef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY); +# endif +#endif + +#include "png.h" + +/* pngconf.h does not set PNG_DLL_EXPORT unless it is required, so: */ +#ifndef PNG_DLL_EXPORT +# define PNG_DLL_EXPORT +#endif + +/* This is a global switch to set the compilation for an installed system + * (a release build). It can be set for testing debug builds to ensure that + * they will compile when the build type is switched to RC or STABLE, the + * default is just to use PNG_LIBPNG_BUILD_BASE_TYPE. Set this in CPPFLAGS + * with either: + * + * -DPNG_RELEASE_BUILD Turns on the release compile path + * -DPNG_RELEASE_BUILD=0 Turns it off + * or in your pngusr.h with + * #define PNG_RELEASE_BUILD=1 Turns on the release compile path + * #define PNG_RELEASE_BUILD=0 Turns it off + */ +#ifndef PNG_RELEASE_BUILD +# define PNG_RELEASE_BUILD (PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC) +#endif + +/* SECURITY and SAFETY: + * + * libpng is built with support for internal limits on image dimensions and + * memory usage. These are documented in scripts/pnglibconf.dfa of the + * source and recorded in the machine generated header file pnglibconf.h. + */ + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. + * + * zlib provides 'MAXSEG_64K' which, if defined, indicates the + * same limit and pngconf.h (already included) sets the limit + * if certain operating systems are detected. + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +#ifndef PNG_UNUSED +/* Unused formal parameter warnings are silenced using the following macro + * which is expected to have no bad effects on performance (optimizing + * compilers will probably remove it entirely). Note that if you replace + * it with something other than whitespace, you must include the terminating + * semicolon. + */ +# define PNG_UNUSED(param) (void)param; +#endif + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + +/* If warnings or errors are turned off the code is disabled or redirected here. + * From 1.5.4 functions have been added to allow very limited formatting of + * error and warning messages - this code will also be disabled here. + */ +#ifdef PNG_WARNINGS_SUPPORTED +# define PNG_WARNING_PARAMETERS(p) png_warning_parameters p; +#else +# define png_warning_parameter(p,number,string) ((void)0) +# define png_warning_parameter_unsigned(p,number,format,value) ((void)0) +# define png_warning_parameter_signed(p,number,format,value) ((void)0) +# define png_formatted_warning(pp,p,message) ((void)(pp)) +# define PNG_WARNING_PARAMETERS(p) +#endif +#ifndef PNG_ERROR_TEXT_SUPPORTED +# define png_fixed_error(s1,s2) png_err(s1) +#endif + +/* Some fixed point APIs are still required even if not exported because + * they get used by the corresponding floating point APIs. This magic + * deals with this: + */ +#ifdef PNG_FIXED_POINT_SUPPORTED +# define PNGFAPI PNGAPI +#else +# define PNGFAPI /* PRIVATE */ +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ + +/* C allows up-casts from (void*) to any pointer and (const void*) to any + * pointer to a const object. C++ regards this as a type error and requires an + * explicit, static, cast and provides the static_cast<> rune to ensure that + * const is not cast away. + */ +#ifdef __cplusplus +# define png_voidcast(type, value) static_cast(value) +# define png_constcast(type, value) const_cast(value) +# define png_aligncast(type, value) \ + static_cast(static_cast(value)) +# define png_aligncastconst(type, value) \ + static_cast(static_cast(value)) +#else +# define png_voidcast(type, value) (value) +# ifdef _WIN64 +# ifdef __GNUC__ + typedef unsigned long long png_ptruint; +# else + typedef unsigned __int64 png_ptruint; +# endif +# else + typedef unsigned long png_ptruint; +# endif +# define png_constcast(type, value) ((type)(png_ptruint)(const void*)(value)) +# define png_aligncast(type, value) ((void*)(value)) +# define png_aligncastconst(type, value) ((const void*)(value)) +#endif /* __cplusplus */ + +#if defined(PNG_FLOATING_POINT_SUPPORTED) ||\ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) + /* png.c requires the following ANSI-C constants if the conversion of + * floating point to ASCII is implemented therein: + * + * DBL_DIG Maximum number of decimal digits (can be set to any constant) + * DBL_MIN Smallest normalized fp number (can be set to an arbitrary value) + * DBL_MAX Maximum floating point number (can be set to an arbitrary value) + */ +# include + +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +#if defined(WIN32) || defined(_Windows) || defined(_WINDOWS) || \ + defined(_WIN32) || defined(__WIN32__) +# include /* defines _WINDOWS_ macro */ +#endif +#endif /* PNG_VERSION_INFO_ONLY */ + +/* Moved here around 1.5.0beta36 from pngconf.h */ +/* Users may want to use these so they are not private. Any library + * functions that are passed far data must be model-independent. + */ + +/* Memory model/platform independent fns */ +#ifndef PNG_ABORT +# ifdef _WINDOWS_ +# define PNG_ABORT() ExitProcess(0) +# else +# define PNG_ABORT() abort() +# endif +#endif + +/* These macros may need to be architecture dependent. */ +#define PNG_ALIGN_NONE 0 /* do not use data alignment */ +#define PNG_ALIGN_ALWAYS 1 /* assume unaligned accesses are OK */ +#ifdef offsetof +# define PNG_ALIGN_OFFSET 2 /* use offsetof to determine alignment */ +#else +# define PNG_ALIGN_OFFSET -1 /* prevent the use of this */ +#endif +#define PNG_ALIGN_SIZE 3 /* use sizeof to determine alignment */ + +#ifndef PNG_ALIGN_TYPE + /* Default to using aligned access optimizations and requiring alignment to a + * multiple of the data type size. Override in a compiler specific fashion + * if necessary by inserting tests here: + */ +# define PNG_ALIGN_TYPE PNG_ALIGN_SIZE +#endif + +#if PNG_ALIGN_TYPE == PNG_ALIGN_SIZE + /* This is used because in some compiler implementations non-aligned + * structure members are supported, so the offsetof approach below fails. + * Set PNG_ALIGN_SIZE=0 for compiler combinations where unaligned access + * is good for performance. Do not do this unless you have tested the result + * and understand it. + */ +# define png_alignof(type) (sizeof (type)) +#else +# if PNG_ALIGN_TYPE == PNG_ALIGN_OFFSET +# define png_alignof(type) offsetof(struct{char c; type t;}, t) +# else +# if PNG_ALIGN_TYPE == PNG_ALIGN_ALWAYS +# define png_alignof(type) (1) +# endif + /* Else leave png_alignof undefined to prevent use thereof */ +# endif +#endif + +/* This implicitly assumes alignment is always to a power of 2. */ +#ifdef png_alignof +# define png_isaligned(ptr, type)\ + (((type)((const char*)ptr-(const char*)0) & \ + (type)(png_alignof(type)-1)) == 0) +#else +# define png_isaligned(ptr, type) 0 +#endif + +/* End of memory model/platform independent support */ +/* End of 1.5.0beta36 move from pngconf.h */ + +/* CONSTANTS and UTILITY MACROS + * These are used internally by libpng and not exposed in the API + */ + +/* Various modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. Three of these + * are defined in png.h because they need to be visible to applications + * that call png_set_unknown_chunk(). + */ +/* #define PNG_HAVE_IHDR 0x01U (defined in png.h) */ +/* #define PNG_HAVE_PLTE 0x02U (defined in png.h) */ +#define PNG_HAVE_IDAT 0x04U +/* #define PNG_AFTER_IDAT 0x08U (defined in png.h) */ +#define PNG_HAVE_IEND 0x10U + /* 0x20U (unused) */ + /* 0x40U (unused) */ + /* 0x80U (unused) */ +#define PNG_HAVE_CHUNK_HEADER 0x100U +#define PNG_WROTE_tIME 0x200U +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400U +#define PNG_BACKGROUND_IS_GRAY 0x800U +#define PNG_HAVE_PNG_SIGNATURE 0x1000U +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000U /* Have another chunk after IDAT */ + /* 0x4000U (unused) */ +#define PNG_IS_READ_STRUCT 0x8000U /* Else is a write struct */ + +/* Flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001U +#define PNG_INTERLACE 0x0002U +#define PNG_PACK 0x0004U +#define PNG_SHIFT 0x0008U +#define PNG_SWAP_BYTES 0x0010U +#define PNG_INVERT_MONO 0x0020U +#define PNG_QUANTIZE 0x0040U +#define PNG_COMPOSE 0x0080U /* Was PNG_BACKGROUND */ +#define PNG_BACKGROUND_EXPAND 0x0100U +#define PNG_EXPAND_16 0x0200U /* Added to libpng 1.5.2 */ +#define PNG_16_TO_8 0x0400U /* Becomes 'chop' in 1.5.4 */ +#define PNG_RGBA 0x0800U +#define PNG_EXPAND 0x1000U +#define PNG_GAMMA 0x2000U +#define PNG_GRAY_TO_RGB 0x4000U +#define PNG_FILLER 0x8000U +#define PNG_PACKSWAP 0x10000U +#define PNG_SWAP_ALPHA 0x20000U +#define PNG_STRIP_ALPHA 0x40000U +#define PNG_INVERT_ALPHA 0x80000U +#define PNG_USER_TRANSFORM 0x100000U +#define PNG_RGB_TO_GRAY_ERR 0x200000U +#define PNG_RGB_TO_GRAY_WARN 0x400000U +#define PNG_RGB_TO_GRAY 0x600000U /* two bits, RGB_TO_GRAY_ERR|WARN */ +#define PNG_ENCODE_ALPHA 0x800000U /* Added to libpng-1.5.4 */ +#define PNG_ADD_ALPHA 0x1000000U /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000U /* Added to libpng-1.2.9 */ +#define PNG_SCALE_16_TO_8 0x4000000U /* Added to libpng-1.5.4 */ + /* 0x8000000U unused */ + /* 0x10000000U unused */ + /* 0x20000000U unused */ + /* 0x40000000U unused */ +/* Flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001U +#define PNG_STRUCT_INFO 0x0002U + +/* Flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001U +#define PNG_FLAG_ZSTREAM_INITIALIZED 0x0002U /* Added to libpng-1.6.0 */ + /* 0x0004U unused */ +#define PNG_FLAG_ZSTREAM_ENDED 0x0008U /* Added to libpng-1.6.0 */ + /* 0x0010U unused */ + /* 0x0020U unused */ +#define PNG_FLAG_ROW_INIT 0x0040U +#define PNG_FLAG_FILLER_AFTER 0x0080U +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100U +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200U +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400U +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800U +#define PNG_FLAG_ASSUME_sRGB 0x1000U /* Added to libpng-1.5.4 */ +#define PNG_FLAG_OPTIMIZE_ALPHA 0x2000U /* Added to libpng-1.5.4 */ +#define PNG_FLAG_DETECT_UNINITIALIZED 0x4000U /* Added to libpng-1.5.4 */ +/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000U */ +/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000U */ +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000U +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000U +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000U +#define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000U /* Added to libpng-1.4.0 */ +#define PNG_FLAG_APP_WARNINGS_WARN 0x200000U /* Added to libpng-1.6.0 */ +#define PNG_FLAG_APP_ERRORS_WARN 0x400000U /* Added to libpng-1.6.0 */ + /* 0x800000U unused */ + /* 0x1000000U unused */ + /* 0x2000000U unused */ + /* 0x4000000U unused */ + /* 0x8000000U unused */ + /* 0x10000000U unused */ + /* 0x20000000U unused */ + /* 0x40000000U unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* Save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.6.0: scale a 16-bit value in the range 0..65535 to 0..255 + * by dividing by 257 *with rounding*. This macro is exact for the given range. + * See the discourse in pngrtran.c png_do_scale_16_to_8. The values in the + * macro were established by experiment (modifying the added value). The macro + * has a second variant that takes a value already scaled by 255 and divides by + * 65535 - this has a maximum error of .502. Over the range 0..65535*65535 it + * only gives off-by-one errors and only for 0.5% (1 in 200) of the values. + */ +#define PNG_DIV65535(v24) (((v24) + 32895) >> 16) +#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \ + (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) ) + +/* This returns the number of trailing bits in the last byte of a row, 0 if the + * last byte is completely full of pixels. It is, in principle, (pixel_bits x + * width) % 8, but that would overflow for large 'width'. The second macro is + * the same except that it returns the number of unused bits in the last byte; + * (8-TRAILBITS), but 0 when TRAILBITS is 0. + * + * NOTE: these macros are intended to be self-evidently correct and never + * overflow on the assumption that pixel_bits is in the range 0..255. The + * arguments are evaluated only once and they can be signed (e.g. as a result of + * the integral promotions). The result of the expression always has type + * (png_uint_32), however the compiler always knows it is in the range 0..7. + */ +#define PNG_TRAILBITS(pixel_bits, width) \ + (((pixel_bits) * ((width) % (png_uint_32)8)) % 8) + +#define PNG_PADBITS(pixel_bits, width) \ + ((8 - PNG_TRAILBITS(pixel_bits, width)) % 8) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + * ideal-delta..ideal+delta. Each argument is evaluated twice. + * "ideal" and "delta" should be constants, normally simple + * integers, "value" a variable. Added to libpng-1.2.6 JB + */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* Conversions between fixed and floating point, only defined if + * required (to make sure the code doesn't accidentally use float + * when it is supposedly disabled.) + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +/* The floating point conversion can't overflow, though it can and + * does lose accuracy relative to the original fixed point value. + * In practice this doesn't matter because png_fixed_point only + * stores numbers with very low precision. The png_ptr and s + * arguments are unused by default but are there in case error + * checking becomes a requirement. + */ +#define png_float(png_ptr, fixed, s) (.00001 * (fixed)) + +/* The fixed point conversion performs range checking and evaluates + * its argument multiple times, so must be used with care. The + * range checking uses the PNG specification values for a signed + * 32-bit fixed point value except that the values are deliberately + * rounded-to-zero to an integral value - 21474 (21474.83 is roughly + * (2^31-1) * 100000). 's' is a string that describes the value being + * converted. + * + * NOTE: this macro will raise a png_error if the range check fails, + * therefore it is normally only appropriate to use this on values + * that come from API calls or other sources where an out of range + * error indicates a programming error, not a data error! + * + * NOTE: by default this is off - the macro is not used - because the + * function call saves a lot of code. + */ +#ifdef PNG_FIXED_POINT_MACRO_SUPPORTED +#define png_fixed(png_ptr, fp, s) ((fp) <= 21474 && (fp) >= -21474 ?\ + ((png_fixed_point)(100000 * (fp))) : (png_fixed_error(png_ptr, s),0)) +#endif +/* else the corresponding function is defined below, inside the scope of the + * cplusplus test. + */ +#endif + +/* Constants for known chunk types. If you need to add a chunk, define the name + * here. For historical reasons these constants have the form png_; i.e. + * the prefix is lower case. Please use decimal values as the parameters to + * match the ISO PNG specification and to avoid relying on the C locale + * interpretation of character values. + * + * Prior to 1.5.6 these constants were strings, as of 1.5.6 png_uint_32 values + * are computed and a new macro (PNG_STRING_FROM_CHUNK) added to allow a string + * to be generated if required. + * + * PNG_32b correctly produces a value shifted by up to 24 bits, even on + * architectures where (int) is only 16 bits. + */ +#define PNG_32b(b,s) ((png_uint_32)(b) << (s)) +#define PNG_U32(b1,b2,b3,b4) \ + (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0)) + +/* Constants for known chunk types. + * + * MAINTAINERS: If you need to add a chunk, define the name here. + * For historical reasons these constants have the form png_; i.e. + * the prefix is lower case. Please use decimal values as the parameters to + * match the ISO PNG specification and to avoid relying on the C locale + * interpretation of character values. Please keep the list sorted. + * + * Notice that PNG_U32 is used to define a 32-bit value for the 4 byte chunk + * type. In fact the specification does not express chunk types this way, + * however using a 32-bit value means that the chunk type can be read from the + * stream using exactly the same code as used for a 32-bit unsigned value and + * can be examined far more efficiently (using one arithmetic compare). + * + * Prior to 1.5.6 the chunk type constants were expressed as C strings. The + * libpng API still uses strings for 'unknown' chunks and a macro, + * PNG_STRING_FROM_CHUNK, allows a string to be generated if required. Notice + * that for portable code numeric values must still be used; the string "IHDR" + * is not portable and neither is PNG_U32('I', 'H', 'D', 'R'). + * + * In 1.7.0 the definitions will be made public in png.h to avoid having to + * duplicate the same definitions in application code. + */ +#define png_IDAT PNG_U32( 73, 68, 65, 84) +#define png_IEND PNG_U32( 73, 69, 78, 68) +#define png_IHDR PNG_U32( 73, 72, 68, 82) +#define png_PLTE PNG_U32( 80, 76, 84, 69) +#define png_bKGD PNG_U32( 98, 75, 71, 68) +#define png_cHRM PNG_U32( 99, 72, 82, 77) +#define png_eXIf PNG_U32(101, 88, 73, 102) /* registered July 2017 */ +#define png_fRAc PNG_U32(102, 82, 65, 99) /* registered, not defined */ +#define png_gAMA PNG_U32(103, 65, 77, 65) +#define png_gIFg PNG_U32(103, 73, 70, 103) +#define png_gIFt PNG_U32(103, 73, 70, 116) /* deprecated */ +#define png_gIFx PNG_U32(103, 73, 70, 120) +#define png_hIST PNG_U32(104, 73, 83, 84) +#define png_iCCP PNG_U32(105, 67, 67, 80) +#define png_iTXt PNG_U32(105, 84, 88, 116) +#define png_oFFs PNG_U32(111, 70, 70, 115) +#define png_pCAL PNG_U32(112, 67, 65, 76) +#define png_pHYs PNG_U32(112, 72, 89, 115) +#define png_sBIT PNG_U32(115, 66, 73, 84) +#define png_sCAL PNG_U32(115, 67, 65, 76) +#define png_sPLT PNG_U32(115, 80, 76, 84) +#define png_sRGB PNG_U32(115, 82, 71, 66) +#define png_sTER PNG_U32(115, 84, 69, 82) +#define png_tEXt PNG_U32(116, 69, 88, 116) +#define png_tIME PNG_U32(116, 73, 77, 69) +#define png_tRNS PNG_U32(116, 82, 78, 83) +#define png_zTXt PNG_U32(122, 84, 88, 116) + +/* The following will work on (signed char*) strings, whereas the get_uint_32 + * macro will fail on top-bit-set values because of the sign extension. + */ +#define PNG_CHUNK_FROM_STRING(s)\ + PNG_U32(0xff & (s)[0], 0xff & (s)[1], 0xff & (s)[2], 0xff & (s)[3]) + +/* This uses (char), not (png_byte) to avoid warnings on systems where (char) is + * signed and the argument is a (char[]) This macro will fail miserably on + * systems where (char) is more than 8 bits. + */ +#define PNG_STRING_FROM_CHUNK(s,c)\ + (void)(((char*)(s))[0]=(char)(((c)>>24) & 0xff), \ + ((char*)(s))[1]=(char)(((c)>>16) & 0xff),\ + ((char*)(s))[2]=(char)(((c)>>8) & 0xff), \ + ((char*)(s))[3]=(char)((c & 0xff))) + +/* Do the same but terminate with a null character. */ +#define PNG_CSTRING_FROM_CHUNK(s,c)\ + (void)(PNG_STRING_FROM_CHUNK(s,c), ((char*)(s))[4] = 0) + +/* Test on flag values as defined in the spec (section 5.4): */ +#define PNG_CHUNK_ANCILLARY(c) (1 & ((c) >> 29)) +#define PNG_CHUNK_CRITICAL(c) (!PNG_CHUNK_ANCILLARY(c)) +#define PNG_CHUNK_PRIVATE(c) (1 & ((c) >> 21)) +#define PNG_CHUNK_RESERVED(c) (1 & ((c) >> 13)) +#define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >> 5)) + +/* Gamma values (new at libpng-1.5.4): */ +#define PNG_GAMMA_MAC_OLD 151724 /* Assume '1.8' is really 2.2/1.45! */ +#define PNG_GAMMA_MAC_INVERSE 65909 +#define PNG_GAMMA_sRGB_INVERSE 45455 + +/* Almost everything below is C specific; the #defines above can be used in + * non-C code (so long as it is C-preprocessed) the rest of this stuff cannot. + */ +#ifndef PNG_VERSION_INFO_ONLY + +#include "pngstruct.h" +#include "pnginfo.h" + +/* Validate the include paths - the include path used to generate pnglibconf.h + * must match that used in the build, or we must be using pnglibconf.h.prebuilt: + */ +#if PNG_ZLIB_VERNUM != 0 && PNG_ZLIB_VERNUM != ZLIB_VERNUM +# error ZLIB_VERNUM != PNG_ZLIB_VERNUM \ + "-I (include path) error: see the notes in pngpriv.h" + /* This means that when pnglibconf.h was built the copy of zlib.h that it + * used is not the same as the one being used here. Because the build of + * libpng makes decisions to use inflateInit2 and inflateReset2 based on the + * zlib version number and because this affects handling of certain broken + * PNG files the -I directives must match. + * + * The most likely explanation is that you passed a -I in CFLAGS. This will + * not work; all the preprocessor directories and in particular all the -I + * directives must be in CPPFLAGS. + */ +#endif + +/* This is used for 16-bit gamma tables -- only the top level pointers are + * const; this could be changed: + */ +typedef const png_uint_16p * png_const_uint_16pp; + +/* Added to libpng-1.5.7: sRGB conversion tables */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_table, [256]); + /* Convert from an sRGB encoded value 0..255 to a 16-bit linear value, + * 0..65535. This table gives the closest 16-bit answers (no errors). + */ +#endif + +PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_base, [512]); +PNG_INTERNAL_DATA(const png_byte, png_sRGB_delta, [512]); + +#define PNG_sRGB_FROM_LINEAR(linear) \ + ((png_byte)(0xff & ((png_sRGB_base[(linear)>>15] \ + + ((((linear) & 0x7fff)*png_sRGB_delta[(linear)>>15])>>12)) >> 8))) + /* Given a value 'linear' in the range 0..255*65535 calculate the 8-bit sRGB + * encoded value with maximum error 0.646365. Note that the input is not a + * 16-bit value; it has been multiplied by 255! */ +#endif /* SIMPLIFIED_READ/WRITE */ + + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Internal functions; these are not exported from a DLL however because they + * are used within several of the C source files they have to be C extern. + * + * All of these functions must be declared with PNG_INTERNAL_FUNCTION. + */ + +/* Zlib support */ +#define PNG_UNEXPECTED_ZLIB_RETURN (-7) +PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret), + PNG_EMPTY); + /* Used by the zlib handling functions to ensure that z_stream::msg is always + * set before they return. + */ + +#ifdef PNG_WRITE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr, + png_compression_bufferp *list),PNG_EMPTY); + /* Free the buffer list used by the compressed write code. */ +#endif + +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ + (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ + defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \ + (defined(PNG_sCAL_SUPPORTED) && \ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)) +PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr, + double fp, png_const_charp text),PNG_EMPTY); +#endif + +/* Check the user version string for compatibility, returns false if the version + * numbers aren't compatible. + */ +PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr, + png_const_charp user_png_ver),PNG_EMPTY); + +/* Internal base allocator - no messages, NULL on failure to allocate. This + * does, however, call the application provided allocator and that could call + * png_error (although that would be a bug in the application implementation.) + */ +PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_base,(png_const_structrp png_ptr, + png_alloc_size_t size),PNG_ALLOCATED); + +#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\ + defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) +/* Internal array allocator, outputs no error or warning messages on failure, + * just returns NULL. + */ +PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_array,(png_const_structrp png_ptr, + int nelements, size_t element_size),PNG_ALLOCATED); + +/* The same but an existing array is extended by add_elements. This function + * also memsets the new elements to 0 and copies the old elements. The old + * array is not freed or altered. + */ +PNG_INTERNAL_FUNCTION(png_voidp,png_realloc_array,(png_const_structrp png_ptr, + png_const_voidp array, int old_elements, int add_elements, + size_t element_size),PNG_ALLOCATED); +#endif /* text, sPLT or unknown chunks */ + +/* Magic to create a struct when there is no struct to call the user supplied + * memory allocators. Because error handling has not been set up the memory + * handlers can't safely call png_error, but this is an obscure and undocumented + * restriction so libpng has to assume that the 'free' handler, at least, might + * call png_error. + */ +PNG_INTERNAL_FUNCTION(png_structp,png_create_png_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn, + png_free_ptr free_fn),PNG_ALLOCATED); + +/* Free memory from internal libpng struct */ +PNG_INTERNAL_FUNCTION(void,png_destroy_png_struct,(png_structrp png_ptr), + PNG_EMPTY); + +/* Free an allocated jmp_buf (always succeeds) */ +PNG_INTERNAL_FUNCTION(void,png_free_jmpbuf,(png_structrp png_ptr),PNG_EMPTY); + +/* Function to allocate memory for zlib. PNGAPI is disallowed. */ +PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size), + PNG_ALLOCATED); + +/* Function to free memory for zlib. PNGAPI is disallowed. */ +PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY); + +/* Next four functions are used internally as callbacks. PNGCBAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3, changed to + * PNGCBAPI at 1.5.0 + */ + +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr, + png_bytep data, png_size_t length),PNG_EMPTY); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr, + png_bytep buffer, png_size_t length),PNG_EMPTY); +#endif + +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr, + png_bytep data, png_size_t length),PNG_EMPTY); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_STDIO_SUPPORTED +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr), + PNG_EMPTY); +# endif +#endif + +/* Reset the CRC variable */ +PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY); + +/* Write the "data" buffer to whatever output you are using */ +PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr, + png_const_bytep data, png_size_t length),PNG_EMPTY); + +/* Read and check the PNG file signature */ +PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); + +/* Read the chunk header (length + type name) */ +PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr), + PNG_EMPTY); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data, + png_size_t length),PNG_EMPTY); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf, + png_uint_32 length),PNG_EMPTY); + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr, + png_uint_32 skip),PNG_EMPTY); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY); + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr, + png_const_bytep ptr, png_size_t length),PNG_EMPTY); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY); +#endif + +/* Write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, + int compression_method, int filter_method, int interlace_method),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr, + png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr, + png_const_bytep row_data, png_alloc_size_t row_data_length, int flush), + PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY); + +#ifdef PNG_WRITE_gAMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_gAMA_fixed,(png_structrp png_ptr, + png_fixed_point file_gamma),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr, + png_const_color_8p sbit, int color_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr, + const png_xy *xy), PNG_EMPTY); + /* The xy value must have been previously validated */ +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr, + int intent),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_eXIf_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_eXIf,(png_structrp png_ptr, + png_bytep exif, int num_exif),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr, + png_const_charp name, png_const_bytep profile), PNG_EMPTY); + /* The profile must have been previously validated for correctness, the + * length comes from the first four bytes. Only the base, deflate, + * compression is supported. + */ +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sPLT,(png_structrp png_ptr, + png_const_sPLT_tp palette),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_tRNS,(png_structrp png_ptr, + png_const_bytep trans, png_const_color_16p values, int number, + int color_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_bKGD,(png_structrp png_ptr, + png_const_color_16p values, int color_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr, + png_const_uint_16p hist, int num_hist),PNG_EMPTY); +#endif + +/* Chunks that have keywords */ +#ifdef PNG_WRITE_tEXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr, + png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp + key, png_const_charp text, int compression),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_iTXt,(png_structrp png_ptr, + int compression, png_const_charp key, png_const_charp lang, + png_const_charp lang_key, png_const_charp text),PNG_EMPTY); +#endif + +#ifdef PNG_TEXT_SUPPORTED /* Added at version 1.0.14 and 1.2.4 */ +PNG_INTERNAL_FUNCTION(int,png_set_text_2,(png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_oFFs,(png_structrp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_pCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_pCAL,(png_structrp png_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_const_charp units, png_charpp params),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_pHYs,(png_structrp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_tIME,(png_structrp png_ptr, + png_const_timep mod_time),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr, + int unit, png_const_charp width, png_const_charp height),PNG_EMPTY); +#endif + +/* Called when finished processing a row of data */ +PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr), + PNG_EMPTY); + +/* Internal use only. Called before first row of data */ +PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr), + PNG_EMPTY); + +/* Combine a row of data, dealing with alpha, etc. if requested. 'row' is an + * array of png_ptr->width pixels. If the image is not interlaced or this + * is the final pass this just does a memcpy, otherwise the "display" flag + * is used to determine whether to copy pixels that are not in the current pass. + * + * Because 'png_do_read_interlace' (below) replicates pixels this allows this + * function to achieve the documented 'blocky' appearance during interlaced read + * if display is 1 and the 'sparkle' appearance, where existing pixels in 'row' + * are not changed if they are not in the current pass, when display is 0. + * + * 'display' must be 0 or 1, otherwise the memcpy will be done regardless. + * + * The API always reads from the png_struct row buffer and always assumes that + * it is full width (png_do_read_interlace has already been called.) + * + * This function is only ever used to write to row buffers provided by the + * caller of the relevant libpng API and the row must have already been + * transformed by the read transformations. + * + * The PNG_USE_COMPILE_TIME_MASKS option causes generation of pre-computed + * bitmasks for use within the code, otherwise runtime generated masks are used. + * The default is compile time masks. + */ +#ifndef PNG_USE_COMPILE_TIME_MASKS +# define PNG_USE_COMPILE_TIME_MASKS 1 +#endif +PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr, + png_bytep row, int display),PNG_EMPTY); + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* Expand an interlaced row: the 'row_info' describes the pass data that has + * been read in and must correspond to the pixels in 'row', the pixels are + * expanded (moved apart) in 'row' to match the final layout, when doing this + * the pixels are *replicated* to the intervening space. This is essential for + * the correct operation of png_combine_row, above. + */ +PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Grab pixels out of a row for an interlaced pass */ +PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info, + png_bytep row, int pass),PNG_EMPTY); +#endif + +/* Unfilter a row: check the filter value before calling this, there is no point + * calling it for PNG_FILTER_VALUE_NONE. + */ +PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop + row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY); + +#if PNG_ARM_NEON_OPT > 0 +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info, + png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +#endif + +#if PNG_MIPS_MSA_OPT > 0 +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_msa,(png_row_infop row_info, + png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_msa,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_msa,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_msa,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_msa,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_msa,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_msa,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +#endif + +#if PNG_POWERPC_VSX_OPT > 0 +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_vsx,(png_row_infop row_info, + png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_vsx,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_vsx,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_vsx,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_vsx,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_vsx,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_vsx,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +#endif + +#if PNG_INTEL_SSE_IMPLEMENTATION > 0 +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_sse2,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_sse2,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_sse2,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_sse2,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_sse2,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +#endif + +/* Choose the best filter to use and filter the row data */ +PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr, + png_row_infop row_info),PNG_EMPTY); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr, + png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY); + /* Read 'avail_out' bytes of data from the IDAT stream. If the output buffer + * is NULL the function checks, instead, for the end of the stream. In this + * case a benign error will be issued if the stream end is not found or if + * extra data has to be consumed. + */ +PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr), + PNG_EMPTY); + /* This cleans up when the IDAT LZ stream does not end when the last image + * byte is read; there is still some pending input. + */ + +PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr), + PNG_EMPTY); + /* Finish a row while reading, dealing with interlacing passes, etc. */ +#endif /* SEQUENTIAL_READ */ + +/* Initialize the row buffers, etc. */ +PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY); + +#if ZLIB_VERNUM >= 0x1240 +PNG_INTERNAL_FUNCTION(int,png_zlib_inflate,(png_structrp png_ptr, int flush), + PNG_EMPTY); +# define PNG_INFLATE(pp, flush) png_zlib_inflate(pp, flush) +#else /* Zlib < 1.2.4 */ +# define PNG_INFLATE(pp, flush) inflate(&(pp)->zstream, flush) +#endif /* Zlib < 1.2.4 */ + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +/* Optional call to update the users info structure */ +PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +#endif + +/* Shared transform functions, defined in pngtran.c */ +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info, + png_bytep row, int at_start),PNG_EMPTY); +#endif + +#ifdef PNG_16BIT_SUPPORTED +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* Decode the IHDR chunk */ +PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); + +#ifdef PNG_READ_bKGD_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_eXIf_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_eXIf,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_gAMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_hIST_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif /* READ_iCCP */ + +#ifdef PNG_READ_iTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_sPLT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif /* READ_sPLT */ + +#ifdef PNG_READ_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_tIME_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_tRNS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_const_structrp png_ptr, + const png_uint_32 chunk_name),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_check_chunk_length,(png_const_structrp png_ptr, + const png_uint_32 chunk_length),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY); + /* This is the function that gets called for unknown chunks. The 'keep' + * argument is either non-zero for a known chunk that has been set to be + * handled as unknown or zero for an unknown chunk. By default the function + * just skips the chunk or errors out if it is critical. + */ + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED) +PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling, + (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY); + /* Exactly as the API png_handle_as_unknown() except that the argument is a + * 32-bit chunk name, not a string. + */ +#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */ + +/* Handle the transformations for reading and writing */ +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr, + png_row_infop row_info),PNG_EMPTY); +#endif +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr, + png_row_infop row_info),PNG_EMPTY); +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr), + PNG_EMPTY); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr, + png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr, + png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr, + png_bytep row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr), + PNG_EMPTY); +# ifdef PNG_READ_tEXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +# endif +# ifdef PNG_READ_zTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +# endif +# ifdef PNG_READ_iTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +# endif + +#endif /* PROGRESSIVE_READ */ + +/* Added at libpng version 1.6.0 */ +#ifdef PNG_GAMMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA), PNG_EMPTY); + /* Set the colorspace gamma with a value provided by the application or by + * the gAMA chunk on read. The value will override anything set by an ICC + * profile. + */ + +PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr, + png_inforp info_ptr), PNG_EMPTY); + /* Synchronize the info 'valid' flags with the colorspace */ + +PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr, + png_inforp info_ptr), PNG_EMPTY); + /* Copy the png_struct colorspace to the info_struct and call the above to + * synchronize the flags. Checks for NULL info_ptr and does nothing. + */ +#endif + +/* Added at libpng version 1.4.0 */ +#ifdef PNG_COLORSPACE_SUPPORTED +/* These internal functions are for maintaining the colorspace structure within + * a png_info or png_struct (or, indeed, both). + */ +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_chromaticities, + (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_xy *xy, + int preferred), PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_endpoints, + (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_XYZ *XYZ, + int preferred), PNG_EMPTY); + +#ifdef PNG_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr, + png_colorspacerp colorspace, int intent), PNG_EMPTY); + /* This does set the colorspace gAMA and cHRM values too, but doesn't set the + * flags to write them, if it returns false there was a problem and an error + * message has already been output (but the colorspace may still need to be + * synced to record the invalid flag). + */ +#endif /* sRGB */ + +#ifdef PNG_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, png_const_bytep profile, int color_type), + PNG_EMPTY); + /* The 'name' is used for information only */ + +/* Routines for checking parts of an ICC profile. */ +#ifdef PNG_READ_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length), PNG_EMPTY); +#endif /* READ_iCCP */ +PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile /* first 132 bytes only */, int color_type), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY); +#ifdef PNG_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,( + png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_bytep profile, uLong adler), PNG_EMPTY); + /* 'adler' is the Adler32 checksum of the uncompressed profile data. It may + * be zero to indicate that it is not available. It is used, if provided, + * as a fast check on the profile when checking to see if it is sRGB. + */ +#endif +#endif /* iCCP */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients, + (png_structrp png_ptr), PNG_EMPTY); + /* Set the rgb_to_gray coefficients from the colorspace Y values */ +#endif /* READ_RGB_TO_GRAY */ +#endif /* COLORSPACE */ + +/* Added at libpng version 1.4.0 */ +PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type),PNG_EMPTY); + +/* Added at libpng version 1.5.10 */ +#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \ + defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes, + (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY); +#endif + +#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr, + png_const_charp name),PNG_NORETURN); +#endif + +/* Puts 'string' into 'buffer' at buffer[pos], taking care never to overwrite + * the end. Always leaves the buffer nul terminated. Never errors out (and + * there is no error code.) + */ +PNG_INTERNAL_FUNCTION(size_t,png_safecat,(png_charp buffer, size_t bufsize, + size_t pos, png_const_charp string),PNG_EMPTY); + +/* Various internal functions to handle formatted warning messages, currently + * only implemented for warnings. + */ +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED) +/* Utility to dump an unsigned value into a buffer, given a start pointer and + * and end pointer (which should point just *beyond* the end of the buffer!) + * Returns the pointer to the start of the formatted string. This utility only + * does unsigned values. + */ +PNG_INTERNAL_FUNCTION(png_charp,png_format_number,(png_const_charp start, + png_charp end, int format, png_alloc_size_t number),PNG_EMPTY); + +/* Convenience macro that takes an array: */ +#define PNG_FORMAT_NUMBER(buffer,format,number) \ + png_format_number(buffer, buffer + (sizeof buffer), format, number) + +/* Suggested size for a number buffer (enough for 64 bits and a sign!) */ +#define PNG_NUMBER_BUFFER_SIZE 24 + +/* These are the integer formats currently supported, the name is formed from + * the standard printf(3) format string. + */ +#define PNG_NUMBER_FORMAT_u 1 /* chose unsigned API! */ +#define PNG_NUMBER_FORMAT_02u 2 +#define PNG_NUMBER_FORMAT_d 1 /* chose signed API! */ +#define PNG_NUMBER_FORMAT_02d 2 +#define PNG_NUMBER_FORMAT_x 3 +#define PNG_NUMBER_FORMAT_02x 4 +#define PNG_NUMBER_FORMAT_fixed 5 /* choose the signed API */ +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* New defines and members adding in libpng-1.5.4 */ +# define PNG_WARNING_PARAMETER_SIZE 32 +# define PNG_WARNING_PARAMETER_COUNT 8 /* Maximum 9; see pngerror.c */ + +/* An l-value of this type has to be passed to the APIs below to cache the + * values of the parameters to a formatted warning message. + */ +typedef char png_warning_parameters[PNG_WARNING_PARAMETER_COUNT][ + PNG_WARNING_PARAMETER_SIZE]; + +PNG_INTERNAL_FUNCTION(void,png_warning_parameter,(png_warning_parameters p, + int number, png_const_charp string),PNG_EMPTY); + /* Parameters are limited in size to PNG_WARNING_PARAMETER_SIZE characters, + * including the trailing '\0'. + */ +PNG_INTERNAL_FUNCTION(void,png_warning_parameter_unsigned, + (png_warning_parameters p, int number, int format, png_alloc_size_t value), + PNG_EMPTY); + /* Use png_alloc_size_t because it is an unsigned type as big as any we + * need to output. Use the following for a signed value. + */ +PNG_INTERNAL_FUNCTION(void,png_warning_parameter_signed, + (png_warning_parameters p, int number, int format, png_int_32 value), + PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_formatted_warning,(png_const_structrp png_ptr, + png_warning_parameters p, png_const_charp message),PNG_EMPTY); + /* 'message' follows the X/Open approach of using @1, @2 to insert + * parameters previously supplied using the above functions. Errors in + * specifying the parameters will simply result in garbage substitutions. + */ +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Application errors (new in 1.6); use these functions (declared below) for + * errors in the parameters or order of API function calls on read. The + * 'warning' should be used for an error that can be handled completely; the + * 'error' for one which can be handled safely but which may lose application + * information or settings. + * + * By default these both result in a png_error call prior to release, while in a + * released version the 'warning' is just a warning. However if the application + * explicitly disables benign errors (explicitly permitting the code to lose + * information) they both turn into warnings. + * + * If benign errors aren't supported they end up as the corresponding base call + * (png_warning or png_error.) + */ +PNG_INTERNAL_FUNCTION(void,png_app_warning,(png_const_structrp png_ptr, + png_const_charp message),PNG_EMPTY); + /* The application provided invalid parameters to an API function or called + * an API function at the wrong time, libpng can completely recover. + */ + +PNG_INTERNAL_FUNCTION(void,png_app_error,(png_const_structrp png_ptr, + png_const_charp message),PNG_EMPTY); + /* As above but libpng will ignore the call, or attempt some other partial + * recovery from the error. + */ +#else +# define png_app_warning(pp,s) png_warning(pp,s) +# define png_app_error(pp,s) png_error(pp,s) +#endif + +PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr, + png_const_charp message, int error),PNG_EMPTY); + /* Report a recoverable issue in chunk data. On read this is used to report + * a problem found while reading a particular chunk and the + * png_chunk_benign_error or png_chunk_warning function is used as + * appropriate. On write this is used to report an error that comes from + * data set via an application call to a png_set_ API and png_app_error or + * png_app_warning is used as appropriate. + * + * The 'error' parameter must have one of the following values: + */ +#define PNG_CHUNK_WARNING 0 /* never an error */ +#define PNG_CHUNK_WRITE_ERROR 1 /* an error only on write */ +#define PNG_CHUNK_ERROR 2 /* always an error */ + +/* ASCII to FP interfaces, currently only implemented if sCAL + * support is required. + */ +#if defined(PNG_sCAL_SUPPORTED) +/* MAX_DIGITS is actually the maximum number of characters in an sCAL + * width or height, derived from the precision (number of significant + * digits - a build time settable option) and assumptions about the + * maximum ridiculous exponent. + */ +#define PNG_sCAL_MAX_DIGITS (PNG_sCAL_PRECISION+1/*.*/+1/*E*/+10/*exponent*/) + +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_ascii_from_fp,(png_const_structrp png_ptr, + png_charp ascii, png_size_t size, double fp, unsigned int precision), + PNG_EMPTY); +#endif /* FLOATING_POINT */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr, + png_charp ascii, png_size_t size, png_fixed_point fp),PNG_EMPTY); +#endif /* FIXED_POINT */ +#endif /* sCAL */ + +#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) +/* An internal API to validate the format of a floating point number. + * The result is the index of the next character. If the number is + * not valid it will be the index of a character in the supposed number. + * + * The format of a number is defined in the PNG extensions specification + * and this API is strictly conformant to that spec, not anyone elses! + * + * The format as a regular expression is: + * + * [+-]?[0-9]+.?([Ee][+-]?[0-9]+)? + * + * or: + * + * [+-]?.[0-9]+(.[0-9]+)?([Ee][+-]?[0-9]+)? + * + * The complexity is that either integer or fraction must be present and the + * fraction is permitted to have no digits only if the integer is present. + * + * NOTE: The dangling E problem. + * There is a PNG valid floating point number in the following: + * + * PNG floating point numbers are not greedy. + * + * Working this out requires *TWO* character lookahead (because of the + * sign), the parser does not do this - it will fail at the 'r' - this + * doesn't matter for PNG sCAL chunk values, but it requires more care + * if the value were ever to be embedded in something more complex. Use + * ANSI-C strtod if you need the lookahead. + */ +/* State table for the parser. */ +#define PNG_FP_INTEGER 0 /* before or in integer */ +#define PNG_FP_FRACTION 1 /* before or in fraction */ +#define PNG_FP_EXPONENT 2 /* before or in exponent */ +#define PNG_FP_STATE 3 /* mask for the above */ +#define PNG_FP_SAW_SIGN 4 /* Saw +/- in current state */ +#define PNG_FP_SAW_DIGIT 8 /* Saw a digit in current state */ +#define PNG_FP_SAW_DOT 16 /* Saw a dot in current state */ +#define PNG_FP_SAW_E 32 /* Saw an E (or e) in current state */ +#define PNG_FP_SAW_ANY 60 /* Saw any of the above 4 */ + +/* These three values don't affect the parser. They are set but not used. + */ +#define PNG_FP_WAS_VALID 64 /* Preceding substring is a valid fp number */ +#define PNG_FP_NEGATIVE 128 /* A negative number, including "-0" */ +#define PNG_FP_NONZERO 256 /* A non-zero value */ +#define PNG_FP_STICKY 448 /* The above three flags */ + +/* This is available for the caller to store in 'state' if required. Do not + * call the parser after setting it (the parser sometimes clears it.) + */ +#define PNG_FP_INVALID 512 /* Available for callers as a distinct value */ + +/* Result codes for the parser (boolean - true meants ok, false means + * not ok yet.) + */ +#define PNG_FP_MAYBE 0 /* The number may be valid in the future */ +#define PNG_FP_OK 1 /* The number is valid */ + +/* Tests on the sticky non-zero and negative flags. To pass these checks + * the state must also indicate that the whole number is valid - this is + * achieved by testing PNG_FP_SAW_DIGIT (see the implementation for why this + * is equivalent to PNG_FP_OK above.) + */ +#define PNG_FP_NZ_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NEGATIVE | PNG_FP_NONZERO) + /* NZ_MASK: the string is valid and a non-zero negative value */ +#define PNG_FP_Z_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NONZERO) + /* Z MASK: the string is valid and a non-zero value. */ + /* PNG_FP_SAW_DIGIT: the string is valid. */ +#define PNG_FP_IS_ZERO(state) (((state) & PNG_FP_Z_MASK) == PNG_FP_SAW_DIGIT) +#define PNG_FP_IS_POSITIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_Z_MASK) +#define PNG_FP_IS_NEGATIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_NZ_MASK) + +/* The actual parser. This can be called repeatedly. It updates + * the index into the string and the state variable (which must + * be initialized to 0). It returns a result code, as above. There + * is no point calling the parser any more if it fails to advance to + * the end of the string - it is stuck on an invalid character (or + * terminated by '\0'). + * + * Note that the pointer will consume an E or even an E+ and then leave + * a 'maybe' state even though a preceding integer.fraction is valid. + * The PNG_FP_WAS_VALID flag indicates that a preceding substring was + * a valid number. It's possible to recover from this by calling + * the parser again (from the start, with state 0) but with a string + * that omits the last character (i.e. set the size to the index of + * the problem character.) This has not been tested within libpng. + */ +PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string, + png_size_t size, int *statep, png_size_tp whereami),PNG_EMPTY); + +/* This is the same but it checks a complete string and returns true + * only if it just contains a floating point number. As of 1.5.4 this + * function also returns the state at the end of parsing the number if + * it was valid (otherwise it returns 0.) This can be used for testing + * for negative or zero values using the sticky flag. + */ +PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string, + png_size_t size),PNG_EMPTY); +#endif /* pCAL || sCAL */ + +#if defined(PNG_GAMMA_SUPPORTED) ||\ + defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED) +/* Added at libpng version 1.5.0 */ +/* This is a utility to provide a*times/div (rounded) and indicate + * if there is an overflow. The result is a boolean - false (0) + * for overflow, true (1) if no overflow, in which case *res + * holds the result. + */ +PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a, + png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) +/* Same deal, but issue a warning on overflow and return 0. */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn, + (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by, + png_int_32 divided_by),PNG_EMPTY); +#endif + +#ifdef PNG_GAMMA_SUPPORTED +/* Calculate a reciprocal - used for gamma values. This returns + * 0 if the argument is 0 in order to maintain an undefined value; + * there are no warnings. + */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a), + PNG_EMPTY); + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The same but gives a reciprocal of the product of two fixed point + * values. Accuracy is suitable for gamma calculations but this is + * not exact - use png_muldiv for that. Only required at present on read. + */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a, + png_fixed_point b),PNG_EMPTY); +#endif + +/* Return true if the gamma value is significantly different from 1.0 */ +PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value), + PNG_EMPTY); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Internal fixed point gamma correction. These APIs are called as + * required to convert single values - they don't need to be fast, + * they are not used when processing image pixel values. + * + * While the input is an 'unsigned' value it must actually be the + * correct bit value - 0..255 or 0..65535 as required. + */ +PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_correct,(png_structrp png_ptr, + unsigned int value, png_fixed_point gamma_value),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,(unsigned int value, + png_fixed_point gamma_value),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value, + png_fixed_point gamma_value),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr, + int bit_depth),PNG_EMPTY); +#endif + +/* SIMPLIFIED READ/WRITE SUPPORT */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +/* The internal structure that png_image::opaque points to. */ +typedef struct png_control +{ + png_structp png_ptr; + png_infop info_ptr; + png_voidp error_buf; /* Always a jmp_buf at present. */ + + png_const_bytep memory; /* Memory buffer. */ + png_size_t size; /* Size of the memory buffer. */ + + unsigned int for_write :1; /* Otherwise it is a read structure */ + unsigned int owned_file :1; /* We own the file in io_ptr */ +} png_control; + +/* Return the pointer to the jmp_buf from a png_control: necessary because C + * does not reveal the type of the elements of jmp_buf. + */ +#ifdef __cplusplus +# define png_control_jmp_buf(pc) (((jmp_buf*)((pc)->error_buf))[0]) +#else +# define png_control_jmp_buf(pc) ((pc)->error_buf) +#endif + +/* Utility to safely execute a piece of libpng code catching and logging any + * errors that might occur. Returns true on success, false on failure (either + * of the function or as a result of a png_error.) + */ +PNG_INTERNAL_CALLBACK(void,png_safe_error,(png_structp png_ptr, + png_const_charp error_message),PNG_NORETURN); + +#ifdef PNG_WARNINGS_SUPPORTED +PNG_INTERNAL_CALLBACK(void,png_safe_warning,(png_structp png_ptr, + png_const_charp warning_message),PNG_EMPTY); +#else +# define png_safe_warning 0/*dummy argument*/ +#endif + +PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image, + int (*function)(png_voidp), png_voidp arg),PNG_EMPTY); + +/* Utility to log an error; this also cleans up the png_image; the function + * always returns 0 (false). + */ +PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image, + png_const_charp error_message),PNG_EMPTY); + +#ifndef PNG_SIMPLIFIED_READ_SUPPORTED +/* png_image_free is used by the write code but not exported */ +PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY); +#endif /* !SIMPLIFIED_READ */ + +#endif /* SIMPLIFIED READ/WRITE */ + +/* These are initialization functions for hardware specific PNG filter + * optimizations; list these here then select the appropriate one at compile + * time using the macro PNG_FILTER_OPTIMIZATIONS. If the macro is not defined + * the generic code is used. + */ +#ifdef PNG_FILTER_OPTIMIZATIONS +PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS, (png_structp png_ptr, + unsigned int bpp), PNG_EMPTY); + /* Just declare the optimization that will be used */ +#else + /* List *all* the possible optimizations here - this branch is required if + * the builder of libpng passes the definition of PNG_FILTER_OPTIMIZATIONS in + * CFLAGS in place of CPPFLAGS *and* uses symbol prefixing. + */ +# if PNG_ARM_NEON_OPT > 0 +PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon, + (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); +#endif + +#if PNG_MIPS_MSA_OPT > 0 +PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_msa, + (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); +#endif + +# if PNG_INTEL_SSE_IMPLEMENTATION > 0 +PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_sse2, + (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); +# endif +#endif + +PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword, (png_structrp png_ptr, + png_const_charp key, png_bytep new_key), PNG_EMPTY); + +/* Maintainer: Put new private prototypes here ^ */ + +#include "pngdebug.h" + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +#endif /* PNGPRIV_H */ diff --git a/src/png/libpng/pngread.c b/src/png/libpng/pngread.c new file mode 100644 index 0000000000..da32e9ad9c --- /dev/null +++ b/src/png/libpng/pngread.c @@ -0,0 +1,4219 @@ + +/* pngread.c - read a PNG file + * + * Last changed in libpng 1.6.33 [September 28, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains routines that an application calls directly to + * read a PNG file or stream. + */ + +#include "pngpriv.h" +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) +# include +#endif + +#ifdef PNG_READ_SUPPORTED + +/* Create a PNG structure for reading, and allocate any memory needed. */ +PNG_FUNCTION(png_structp,PNGAPI +png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED) +{ +#ifndef PNG_USER_MEM_SUPPORTED + png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, NULL, NULL, NULL); +#else + return png_create_read_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, NULL, NULL, NULL); +} + +/* Alternate create PNG structure for reading, and allocate any memory + * needed. + */ +PNG_FUNCTION(png_structp,PNGAPI +png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) +{ + png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); +#endif /* USER_MEM */ + + if (png_ptr != NULL) + { + png_ptr->mode = PNG_IS_READ_STRUCT; + + /* Added in libpng-1.6.0; this can be used to detect a read structure if + * required (it will be zero in a write structure.) + */ +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE; +# endif + +# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; + + /* In stable builds only warn if an application error can be completely + * handled. + */ +# if PNG_RELEASE_BUILD + png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; +# endif +# endif + + /* TODO: delay this, it can be done in png_init_io (if the app doesn't + * do it itself) avoiding setting the default function if it is not + * required. + */ + png_set_read_fn(png_ptr, NULL, NULL); + } + + return png_ptr; +} + + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. This has been + * changed in v0.90 to allow reading a file that already has the magic + * bytes read from the stream. You can tell libpng how many bytes have + * been read from the beginning of the stream (up to the maximum of 8) + * via png_set_sig_bytes(), and we will only check the remaining bytes + * here. The application can then have access to the signature bytes we + * read if it is determined that this isn't a valid PNG file. + */ +void PNGAPI +png_read_info(png_structrp png_ptr, png_inforp info_ptr) +{ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int keep; +#endif + + png_debug(1, "in png_read_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Read and check the PNG file signature. */ + png_read_sig(png_ptr, info_ptr); + + for (;;) + { + png_uint_32 length = png_read_chunk_header(png_ptr); + png_uint_32 chunk_name = png_ptr->chunk_name; + + /* IDAT logic needs to happen here to simplify getting the two flags + * right. + */ + if (chunk_name == png_IDAT) + { + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "Missing IHDR before IDAT"); + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + (png_ptr->mode & PNG_HAVE_PLTE) == 0) + png_chunk_error(png_ptr, "Missing PLTE before IDAT"); + + else if ((png_ptr->mode & PNG_AFTER_IDAT) != 0) + png_chunk_benign_error(png_ptr, "Too many IDATs found"); + + png_ptr->mode |= PNG_HAVE_IDAT; + } + + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + png_ptr->mode |= PNG_AFTER_IDAT; + } + + /* This should be a binary subdivision search or a hash for + * matching the chunk name rather than a linear search. + */ + if (chunk_name == png_IHDR) + png_handle_IHDR(png_ptr, info_ptr, length); + + else if (chunk_name == png_IEND) + png_handle_IEND(png_ptr, info_ptr, length); + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) + { + png_handle_unknown(png_ptr, info_ptr, length, keep); + + if (chunk_name == png_PLTE) + png_ptr->mode |= PNG_HAVE_PLTE; + + else if (chunk_name == png_IDAT) + { + png_ptr->idat_size = 0; /* It has been consumed */ + break; + } + } +#endif + else if (chunk_name == png_PLTE) + png_handle_PLTE(png_ptr, info_ptr, length); + + else if (chunk_name == png_IDAT) + { + png_ptr->idat_size = length; + break; + } + +#ifdef PNG_READ_bKGD_SUPPORTED + else if (chunk_name == png_bKGD) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED + else if (chunk_name == png_cHRM) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_eXIf_SUPPORTED + else if (chunk_name == png_eXIf) + png_handle_eXIf(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (chunk_name == png_gAMA) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_hIST_SUPPORTED + else if (chunk_name == png_hIST) + png_handle_hIST(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED + else if (chunk_name == png_oFFs) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (chunk_name == png_pCAL) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED + else if (chunk_name == png_sCAL) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED + else if (chunk_name == png_pHYs) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED + else if (chunk_name == png_sBIT) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sRGB_SUPPORTED + else if (chunk_name == png_sRGB) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_iCCP_SUPPORTED + else if (chunk_name == png_iCCP) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sPLT_SUPPORTED + else if (chunk_name == png_sPLT) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED + else if (chunk_name == png_tEXt) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tIME_SUPPORTED + else if (chunk_name == png_tIME) + png_handle_tIME(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tRNS_SUPPORTED + else if (chunk_name == png_tRNS) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED + else if (chunk_name == png_zTXt) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_iTXt_SUPPORTED + else if (chunk_name == png_iTXt) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + + else + png_handle_unknown(png_ptr, info_ptr, length, + PNG_HANDLE_CHUNK_AS_DEFAULT); + } +} +#endif /* SEQUENTIAL_READ */ + +/* Optional call to update the users info_ptr structure */ +void PNGAPI +png_read_update_info(png_structrp png_ptr, png_inforp info_ptr) +{ + png_debug(1, "in png_read_update_info"); + + if (png_ptr != NULL) + { + if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) + { + png_read_start_row(png_ptr); + +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + png_read_transform_info(png_ptr, info_ptr); +# else + PNG_UNUSED(info_ptr) +# endif + } + + /* New in 1.6.0 this avoids the bug of doing the initializations twice */ + else + png_app_error(png_ptr, + "png_read_update_info/png_start_read_image: duplicate call"); + } +} + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Initialize palette, background, etc, after transformations + * are set, but before any reading takes place. This allows + * the user to obtain a gamma-corrected palette, for example. + * If the user doesn't call this, we will do it ourselves. + */ +void PNGAPI +png_start_read_image(png_structrp png_ptr) +{ + png_debug(1, "in png_start_read_image"); + + if (png_ptr != NULL) + { + if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) + png_read_start_row(png_ptr); + + /* New in 1.6.0 this avoids the bug of doing the initializations twice */ + else + png_app_error(png_ptr, + "png_start_read_image/png_read_update_info: duplicate call"); + } +} +#endif /* SEQUENTIAL_READ */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Undoes intrapixel differencing, + * NOTE: this is apparently only supported in the 'sequential' reader. + */ +static void +png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_intrapixel"); + + if ( + (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((256 + *rp + *(rp + 1)) & 0xff); + *(rp+2) = (png_byte)((256 + *(rp + 2) + *(rp + 1)) & 0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (png_uint_32)(*(rp ) << 8) | *(rp + 1); + png_uint_32 s1 = (png_uint_32)(*(rp + 2) << 8) | *(rp + 3); + png_uint_32 s2 = (png_uint_32)(*(rp + 4) << 8) | *(rp + 5); + png_uint_32 red = (s0 + s1 + 65536) & 0xffff; + png_uint_32 blue = (s2 + s1 + 65536) & 0xffff; + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp + 1) = (png_byte)(red & 0xff); + *(rp + 4) = (png_byte)((blue >> 8) & 0xff); + *(rp + 5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* MNG_FEATURES */ + +void PNGAPI +png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) +{ + png_row_info row_info; + + if (png_ptr == NULL) + return; + + png_debug2(1, "in png_read_row (row %lu, pass %d)", + (unsigned long)png_ptr->row_number, png_ptr->pass); + + /* png_read_start_row sets the information (in particular iwidth) for this + * interlace pass. + */ + if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) + png_read_start_row(png_ptr); + + /* 1.5.6: row_info moved out of png_struct to a local here. */ + row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */ + row_info.color_type = png_ptr->color_type; + row_info.bit_depth = png_ptr->bit_depth; + row_info.channels = png_ptr->channels; + row_info.pixel_depth = png_ptr->pixel_depth; + row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); + +#ifdef PNG_WARNINGS_SUPPORTED + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* Check for transforms that have been set but were defined out */ +#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) + if ((png_ptr->transformations & PNG_INVERT_MONO) != 0) + png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) + if ((png_ptr->transformations & PNG_FILLER) != 0) + png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + !defined(PNG_READ_PACKSWAP_SUPPORTED) + if ((png_ptr->transformations & PNG_PACKSWAP) != 0) + png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) != 0) + png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) + if ((png_ptr->transformations & PNG_SHIFT) != 0) + png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) + if ((png_ptr->transformations & PNG_BGR) != 0) + png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) + if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0) + png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined"); +#endif + } +#endif /* WARNINGS */ + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* If interlaced and we do not need a new row, combine row and return. + * Notice that the pixels we have from previous rows have been transformed + * already; we can only combine like with like (transformed or + * untransformed) and, because of the libpng API for interlaced images, this + * means we must transform before de-interlacing. + */ + if (png_ptr->interlaced != 0 && + (png_ptr->transformations & PNG_INTERLACE) != 0) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + png_read_finish_row(png_ptr); + return; + } + break; + + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + png_read_finish_row(png_ptr); + return; + } + break; + + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + if (dsp_row != NULL && (png_ptr->row_number & 4)) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + png_read_finish_row(png_ptr); + return; + } + break; + + case 3: + if ((png_ptr->row_number & 3) || png_ptr->width < 3) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + png_read_finish_row(png_ptr); + return; + } + break; + + case 4: + if ((png_ptr->row_number & 3) != 2) + { + if (dsp_row != NULL && (png_ptr->row_number & 2)) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + png_read_finish_row(png_ptr); + return; + } + break; + + case 5: + if ((png_ptr->row_number & 1) || png_ptr->width < 2) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + png_read_finish_row(png_ptr); + return; + } + break; + + default: + case 6: + if ((png_ptr->row_number & 1) == 0) + { + png_read_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + if ((png_ptr->mode & PNG_HAVE_IDAT) == 0) + png_error(png_ptr, "Invalid attempt to read row data"); + + /* Fill the row with IDAT data: */ + png_ptr->row_buf[0]=255; /* to force error if no data was found */ + png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1); + + if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) + { + if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) + png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, + png_ptr->prev_row + 1, png_ptr->row_buf[0]); + else + png_error(png_ptr, "bad adaptive filter value"); + } + + /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before + * 1.5.6, while the buffer really is this big in current versions of libpng + * it may not be in the future, so this was changed just to copy the + * interlaced count: + */ + memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_read_intrapixel(&row_info, png_ptr->row_buf + 1); + } +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED + if (png_ptr->transformations) + png_do_read_transformations(png_ptr, &row_info); +#endif + + /* The transformed pixel depth should match the depth now in row_info. */ + if (png_ptr->transformed_pixel_depth == 0) + { + png_ptr->transformed_pixel_depth = row_info.pixel_depth; + if (row_info.pixel_depth > png_ptr->maximum_pixel_depth) + png_error(png_ptr, "sequential row overflow"); + } + + else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth) + png_error(png_ptr, "internal sequential row size calculation error"); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Expand interlaced rows to full size */ + if (png_ptr->interlaced != 0 && + (png_ptr->transformations & PNG_INTERLACE) != 0) + { + if (png_ptr->pass < 6) + png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass, + png_ptr->transformations); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + if (row != NULL) + png_combine_row(png_ptr, row, 0/*row*/); + } + + else +#endif + { + if (row != NULL) + png_combine_row(png_ptr, row, -1/*ignored*/); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, -1/*ignored*/); + } + png_read_finish_row(png_ptr); + + if (png_ptr->read_row_fn != NULL) + (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); + +} +#endif /* SEQUENTIAL_READ */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. If the image is interlaced, + * and png_set_interlace_handling() has been called, the rows need to + * contain the contents of the rows from the previous pass. If the + * image has alpha or transparency, and png_handle_alpha()[*] has been + * called, the rows contents must be initialized to the contents of the + * screen. + * + * "row" holds the actual image, and pixels are placed in it + * as they arrive. If the image is displayed after each pass, it will + * appear to "sparkle" in. "display_row" can be used to display a + * "chunky" progressive image, with finer detail added as it becomes + * available. If you do not want this "chunky" display, you may pass + * NULL for display_row. If you do not want the sparkle display, and + * you have not called png_handle_alpha(), you may pass NULL for rows. + * If you have called png_handle_alpha(), and the image has either an + * alpha channel or a transparency chunk, you must provide a buffer for + * rows. In this case, you do not have to provide a display_row buffer + * also, but you may. If the image is not interlaced, or if you have + * not called png_set_interlace_handling(), the display_row buffer will + * be ignored, so pass NULL to it. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ + +void PNGAPI +png_read_rows(png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows) +{ + png_uint_32 i; + png_bytepp rp; + png_bytepp dp; + + png_debug(1, "in png_read_rows"); + + if (png_ptr == NULL) + return; + + rp = row; + dp = display_row; + if (rp != NULL && dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp++; + png_bytep dptr = *dp++; + + png_read_row(png_ptr, rptr, dptr); + } + + else if (rp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp; + png_read_row(png_ptr, rptr, NULL); + rp++; + } + + else if (dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep dptr = *dp; + png_read_row(png_ptr, NULL, dptr); + dp++; + } +} +#endif /* SEQUENTIAL_READ */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the entire image. If the image has an alpha channel or a tRNS + * chunk, and you have called png_handle_alpha()[*], you will need to + * initialize the image to the current image that PNG will be overlaying. + * We set the num_rows again here, in case it was incorrectly set in + * png_read_start_row() by a call to png_read_update_info() or + * png_start_read_image() if png_set_interlace_handling() wasn't called + * prior to either of these functions like it should have been. You can + * only call this function once. If you desire to have an image for + * each pass of a interlaced image, use png_read_rows() instead. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ +void PNGAPI +png_read_image(png_structrp png_ptr, png_bytepp image) +{ + png_uint_32 i, image_height; + int pass, j; + png_bytepp rp; + + png_debug(1, "in png_read_image"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) + { + pass = png_set_interlace_handling(png_ptr); + /* And make sure transforms are initialized. */ + png_start_read_image(png_ptr); + } + else + { + if (png_ptr->interlaced != 0 && + (png_ptr->transformations & PNG_INTERLACE) == 0) + { + /* Caller called png_start_read_image or png_read_update_info without + * first turning on the PNG_INTERLACE transform. We can fix this here, + * but the caller should do it! + */ + png_warning(png_ptr, "Interlace handling should be turned on when " + "using png_read_image"); + /* Make sure this is set correctly */ + png_ptr->num_rows = png_ptr->height; + } + + /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in + * the above error case. + */ + pass = png_set_interlace_handling(png_ptr); + } +#else + if (png_ptr->interlaced) + png_error(png_ptr, + "Cannot read interlaced image -- interlace handler disabled"); + + pass = 1; +#endif + + image_height=png_ptr->height; + + for (j = 0; j < pass; j++) + { + rp = image; + for (i = 0; i < image_height; i++) + { + png_read_row(png_ptr, *rp, NULL); + rp++; + } + } +} +#endif /* SEQUENTIAL_READ */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. Will not read past the end of the + * file, will verify the end is accurate, and will read any comments + * or time information at the end of the file, if info is not NULL. + */ +void PNGAPI +png_read_end(png_structrp png_ptr, png_inforp info_ptr) +{ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int keep; +#endif + + png_debug(1, "in png_read_end"); + + if (png_ptr == NULL) + return; + + /* If png_read_end is called in the middle of reading the rows there may + * still be pending IDAT data and an owned zstream. Deal with this here. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_chunk_unknown_handling(png_ptr, png_IDAT) == 0) +#endif + png_read_finish_IDAT(png_ptr); + +#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Report invalid palette index; added at libng-1.5.10 */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + png_ptr->num_palette_max > png_ptr->num_palette) + png_benign_error(png_ptr, "Read palette index exceeding num_palette"); +#endif + + do + { + png_uint_32 length = png_read_chunk_header(png_ptr); + png_uint_32 chunk_name = png_ptr->chunk_name; + + if (chunk_name != png_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (chunk_name == png_IEND) + png_handle_IEND(png_ptr, info_ptr, length); + + else if (chunk_name == png_IHDR) + png_handle_IHDR(png_ptr, info_ptr, length); + + else if (info_ptr == NULL) + png_crc_finish(png_ptr, length); + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) + { + if (chunk_name == png_IDAT) + { + if ((length > 0 && !(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) != 0) + png_benign_error(png_ptr, ".Too many IDATs found"); + } + png_handle_unknown(png_ptr, info_ptr, length, keep); + if (chunk_name == png_PLTE) + png_ptr->mode |= PNG_HAVE_PLTE; + } +#endif + + else if (chunk_name == png_IDAT) + { + /* Zero length IDATs are legal after the last IDAT has been + * read, but not after other chunks have been read. 1.6 does not + * always read all the deflate data; specifically it cannot be relied + * upon to read the Adler32 at the end. If it doesn't ignore IDAT + * chunks which are longer than zero as well: + */ + if ((length > 0 && !(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) != 0) + png_benign_error(png_ptr, "..Too many IDATs found"); + + png_crc_finish(png_ptr, length); + } + else if (chunk_name == png_PLTE) + png_handle_PLTE(png_ptr, info_ptr, length); + +#ifdef PNG_READ_bKGD_SUPPORTED + else if (chunk_name == png_bKGD) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED + else if (chunk_name == png_cHRM) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_eXIf_SUPPORTED + else if (chunk_name == png_eXIf) + png_handle_eXIf(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (chunk_name == png_gAMA) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_hIST_SUPPORTED + else if (chunk_name == png_hIST) + png_handle_hIST(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED + else if (chunk_name == png_oFFs) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (chunk_name == png_pCAL) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED + else if (chunk_name == png_sCAL) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED + else if (chunk_name == png_pHYs) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED + else if (chunk_name == png_sBIT) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sRGB_SUPPORTED + else if (chunk_name == png_sRGB) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_iCCP_SUPPORTED + else if (chunk_name == png_iCCP) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sPLT_SUPPORTED + else if (chunk_name == png_sPLT) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED + else if (chunk_name == png_tEXt) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tIME_SUPPORTED + else if (chunk_name == png_tIME) + png_handle_tIME(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tRNS_SUPPORTED + else if (chunk_name == png_tRNS) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED + else if (chunk_name == png_zTXt) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_iTXt_SUPPORTED + else if (chunk_name == png_iTXt) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + + else + png_handle_unknown(png_ptr, info_ptr, length, + PNG_HANDLE_CHUNK_AS_DEFAULT); + } while ((png_ptr->mode & PNG_HAVE_IEND) == 0); +} +#endif /* SEQUENTIAL_READ */ + +/* Free all memory used in the read struct */ +static void +png_read_destroy(png_structrp png_ptr) +{ + png_debug(1, "in png_read_destroy"); + +#ifdef PNG_READ_GAMMA_SUPPORTED + png_destroy_gamma_table(png_ptr); +#endif + + png_free(png_ptr, png_ptr->big_row_buf); + png_ptr->big_row_buf = NULL; + png_free(png_ptr, png_ptr->big_prev_row); + png_ptr->big_prev_row = NULL; + png_free(png_ptr, png_ptr->read_buffer); + png_ptr->read_buffer = NULL; + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + png_free(png_ptr, png_ptr->palette_lookup); + png_ptr->palette_lookup = NULL; + png_free(png_ptr, png_ptr->quantize_index); + png_ptr->quantize_index = NULL; +#endif + + if ((png_ptr->free_me & PNG_FREE_PLTE) != 0) + { + png_zfree(png_ptr, png_ptr->palette); + png_ptr->palette = NULL; + } + png_ptr->free_me &= ~PNG_FREE_PLTE; + +#if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if ((png_ptr->free_me & PNG_FREE_TRNS) != 0) + { + png_free(png_ptr, png_ptr->trans_alpha); + png_ptr->trans_alpha = NULL; + } + png_ptr->free_me &= ~PNG_FREE_TRNS; +#endif + + inflateEnd(&png_ptr->zstream); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_free(png_ptr, png_ptr->save_buffer); + png_ptr->save_buffer = NULL; +#endif + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) && \ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; +#endif + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list = NULL; +#endif + + /* NOTE: the 'setjmp' buffer may still be allocated and the memory and error + * callbacks are still set at this point. They are required to complete the + * destruction of the png_struct itself. + */ +} + +/* Free all memory used by the read */ +void PNGAPI +png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, + png_infopp end_info_ptr_ptr) +{ + png_structrp png_ptr = NULL; + + png_debug(1, "in png_destroy_read_struct"); + + if (png_ptr_ptr != NULL) + png_ptr = *png_ptr_ptr; + + if (png_ptr == NULL) + return; + + /* libpng 1.6.0: use the API to destroy info structs to ensure consistent + * behavior. Prior to 1.6.0 libpng did extra 'info' destruction in this API. + * The extra was, apparently, unnecessary yet this hides memory leak bugs. + */ + png_destroy_info_struct(png_ptr, end_info_ptr_ptr); + png_destroy_info_struct(png_ptr, info_ptr_ptr); + + *png_ptr_ptr = NULL; + png_read_destroy(png_ptr); + png_destroy_png_struct(png_ptr); +} + +void PNGAPI +png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->read_row_fn = read_row_fn; +} + + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_read_png(png_structrp png_ptr, png_inforp info_ptr, + int transforms, voidp params) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). + */ + png_read_info(png_ptr, info_ptr); + if (info_ptr->height > PNG_UINT_32_MAX/(sizeof (png_bytep))) + png_error(png_ptr, "Image is too high to process with png_read_png()"); + + /* -------------- image transformations start here ------------------- */ + /* libpng 1.6.10: add code to cause a png_app_error if a selected TRANSFORM + * is not implemented. This will only happen in de-configured (non-default) + * libpng builds. The results can be unexpected - png_read_png may return + * short or mal-formed rows because the transform is skipped. + */ + + /* Tell libpng to strip 16-bit/color files down to 8 bits per color. + */ + if ((transforms & PNG_TRANSFORM_SCALE_16) != 0) + /* Added at libpng-1.5.4. "strip_16" produces the same result that it + * did in earlier versions, while "scale_16" is now more accurate. + */ +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + png_set_scale_16(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_SCALE_16 not supported"); +#endif + + /* If both SCALE and STRIP are required pngrtran will effectively cancel the + * latter by doing SCALE first. This is ok and allows apps not to check for + * which is supported to get the right answer. + */ + if ((transforms & PNG_TRANSFORM_STRIP_16) != 0) +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + png_set_strip_16(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_16 not supported"); +#endif + + /* Strip alpha bytes from the input data without combining with + * the background (not recommended). + */ + if ((transforms & PNG_TRANSFORM_STRIP_ALPHA) != 0) +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + png_set_strip_alpha(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_ALPHA not supported"); +#endif + + /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). + */ + if ((transforms & PNG_TRANSFORM_PACKING) != 0) +#ifdef PNG_READ_PACK_SUPPORTED + png_set_packing(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_PACKING not supported"); +#endif + + /* Change the order of packed pixels to least significant bit first + * (not useful if you are using png_set_packing). + */ + if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0) +#ifdef PNG_READ_PACKSWAP_SUPPORTED + png_set_packswap(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_PACKSWAP not supported"); +#endif + + /* Expand paletted colors into true RGB triplets + * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel + * Expand paletted or RGB images with transparency to full alpha + * channels so the data will be available as RGBA quartets. + */ + if ((transforms & PNG_TRANSFORM_EXPAND) != 0) +#ifdef PNG_READ_EXPAND_SUPPORTED + png_set_expand(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_EXPAND not supported"); +#endif + + /* We don't handle background color or gamma transformation or quantizing. + */ + + /* Invert monochrome files to have 0 as white and 1 as black + */ + if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0) +#ifdef PNG_READ_INVERT_SUPPORTED + png_set_invert_mono(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_MONO not supported"); +#endif + + /* If you want to shift the pixel values from the range [0,255] or + * [0,65535] to the original [0,7] or [0,31], or whatever range the + * colors were originally in: + */ + if ((transforms & PNG_TRANSFORM_SHIFT) != 0) +#ifdef PNG_READ_SHIFT_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sBIT) != 0) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported"); +#endif + + /* Flip the RGB pixels to BGR (or RGBA to BGRA) */ + if ((transforms & PNG_TRANSFORM_BGR) != 0) +#ifdef PNG_READ_BGR_SUPPORTED + png_set_bgr(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_BGR not supported"); +#endif + + /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ + if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0) +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED + png_set_swap_alpha(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ALPHA not supported"); +#endif + + /* Swap bytes of 16-bit files to least significant byte first */ + if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0) +#ifdef PNG_READ_SWAP_SUPPORTED + png_set_swap(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ENDIAN not supported"); +#endif + +/* Added at libpng-1.2.41 */ + /* Invert the alpha channel from opacity to transparency */ + if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0) +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + png_set_invert_alpha(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_ALPHA not supported"); +#endif + +/* Added at libpng-1.2.41 */ + /* Expand grayscale image to RGB */ + if ((transforms & PNG_TRANSFORM_GRAY_TO_RGB) != 0) +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + png_set_gray_to_rgb(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_GRAY_TO_RGB not supported"); +#endif + +/* Added at libpng-1.5.4 */ + if ((transforms & PNG_TRANSFORM_EXPAND_16) != 0) +#ifdef PNG_READ_EXPAND_16_SUPPORTED + png_set_expand_16(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_EXPAND_16 not supported"); +#endif + + /* We don't handle adding filler bytes */ + + /* We use png_read_image and rely on that for interlace handling, but we also + * call png_read_update_info therefore must turn on interlace handling now: + */ + (void)png_set_interlace_handling(png_ptr); + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (i.e., you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + /* -------------- image transformations end here ------------------- */ + + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + if (info_ptr->row_pointers == NULL) + { + png_uint_32 iptr; + + info_ptr->row_pointers = png_voidcast(png_bytepp, png_malloc(png_ptr, + info_ptr->height * (sizeof (png_bytep)))); + + for (iptr=0; iptrheight; iptr++) + info_ptr->row_pointers[iptr] = NULL; + + info_ptr->free_me |= PNG_FREE_ROWS; + + for (iptr = 0; iptr < info_ptr->height; iptr++) + info_ptr->row_pointers[iptr] = png_voidcast(png_bytep, + png_malloc(png_ptr, info_ptr->rowbytes)); + } + + png_read_image(png_ptr, info_ptr->row_pointers); + info_ptr->valid |= PNG_INFO_IDAT; + + /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + PNG_UNUSED(params) +} +#endif /* INFO_IMAGE */ +#endif /* SEQUENTIAL_READ */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* SIMPLIFIED READ + * + * This code currently relies on the sequential reader, though it could easily + * be made to work with the progressive one. + */ +/* Arguments to png_image_finish_read: */ + +/* Encoding of PNG data (used by the color-map code) */ +# define P_NOTSET 0 /* File encoding not yet known */ +# define P_sRGB 1 /* 8-bit encoded to sRGB gamma */ +# define P_LINEAR 2 /* 16-bit linear: not encoded, NOT pre-multiplied! */ +# define P_FILE 3 /* 8-bit encoded to file gamma, not sRGB or linear */ +# define P_LINEAR8 4 /* 8-bit linear: only from a file value */ + +/* Color-map processing: after libpng has run on the PNG image further + * processing may be needed to convert the data to color-map indices. + */ +#define PNG_CMAP_NONE 0 +#define PNG_CMAP_GA 1 /* Process GA data to a color-map with alpha */ +#define PNG_CMAP_TRANS 2 /* Process GA data to a background index */ +#define PNG_CMAP_RGB 3 /* Process RGB data */ +#define PNG_CMAP_RGB_ALPHA 4 /* Process RGBA data */ + +/* The following document where the background is for each processing case. */ +#define PNG_CMAP_NONE_BACKGROUND 256 +#define PNG_CMAP_GA_BACKGROUND 231 +#define PNG_CMAP_TRANS_BACKGROUND 254 +#define PNG_CMAP_RGB_BACKGROUND 256 +#define PNG_CMAP_RGB_ALPHA_BACKGROUND 216 + +typedef struct +{ + /* Arguments: */ + png_imagep image; + png_voidp buffer; + png_int_32 row_stride; + png_voidp colormap; + png_const_colorp background; + /* Local variables: */ + png_voidp local_row; + png_voidp first_row; + ptrdiff_t row_bytes; /* step between rows */ + int file_encoding; /* E_ values above */ + png_fixed_point gamma_to_linear; /* For P_FILE, reciprocal of gamma */ + int colormap_processing; /* PNG_CMAP_ values above */ +} png_image_read_control; + +/* Do all the *safe* initialization - 'safe' means that png_error won't be + * called, so setting up the jmp_buf is not required. This means that anything + * called from here must *not* call png_malloc - it has to call png_malloc_warn + * instead so that control is returned safely back to this routine. + */ +static int +png_image_read_init(png_imagep image) +{ + if (image->opaque == NULL) + { + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, image, + png_safe_error, png_safe_warning); + + /* And set the rest of the structure to NULL to ensure that the various + * fields are consistent. + */ + memset(image, 0, (sizeof *image)); + image->version = PNG_IMAGE_VERSION; + + if (png_ptr != NULL) + { + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr != NULL) + { + png_controlp control = png_voidcast(png_controlp, + png_malloc_warn(png_ptr, (sizeof *control))); + + if (control != NULL) + { + memset(control, 0, (sizeof *control)); + + control->png_ptr = png_ptr; + control->info_ptr = info_ptr; + control->for_write = 0; + + image->opaque = control; + return 1; + } + + /* Error clean up */ + png_destroy_info_struct(png_ptr, &info_ptr); + } + + png_destroy_read_struct(&png_ptr, NULL, NULL); + } + + return png_image_error(image, "png_image_read: out of memory"); + } + + return png_image_error(image, "png_image_read: opaque pointer not NULL"); +} + +/* Utility to find the base format of a PNG file from a png_struct. */ +static png_uint_32 +png_image_format(png_structrp png_ptr) +{ + png_uint_32 format = 0; + + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) + format |= PNG_FORMAT_FLAG_COLOR; + + if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0) + format |= PNG_FORMAT_FLAG_ALPHA; + + /* Use png_ptr here, not info_ptr, because by examination png_handle_tRNS + * sets the png_struct fields; that's all we are interested in here. The + * precise interaction with an app call to png_set_tRNS and PNG file reading + * is unclear. + */ + else if (png_ptr->num_trans > 0) + format |= PNG_FORMAT_FLAG_ALPHA; + + if (png_ptr->bit_depth == 16) + format |= PNG_FORMAT_FLAG_LINEAR; + + if ((png_ptr->color_type & PNG_COLOR_MASK_PALETTE) != 0) + format |= PNG_FORMAT_FLAG_COLORMAP; + + return format; +} + +/* Is the given gamma significantly different from sRGB? The test is the same + * one used in pngrtran.c when deciding whether to do gamma correction. The + * arithmetic optimizes the division by using the fact that the inverse of the + * file sRGB gamma is 2.2 + */ +static int +png_gamma_not_sRGB(png_fixed_point g) +{ + if (g < PNG_FP_1) + { + /* An uninitialized gamma is assumed to be sRGB for the simplified API. */ + if (g == 0) + return 0; + + return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */); + } + + return 1; +} + +/* Do the main body of a 'png_image_begin_read' function; read the PNG file + * header and fill in all the information. This is executed in a safe context, + * unlike the init routine above. + */ +static int +png_image_read_header(png_voidp argument) +{ + png_imagep image = png_voidcast(png_imagep, argument); + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED + png_set_benign_errors(png_ptr, 1/*warn*/); +#endif + png_read_info(png_ptr, info_ptr); + + /* Do this the fast way; just read directly out of png_struct. */ + image->width = png_ptr->width; + image->height = png_ptr->height; + + { + png_uint_32 format = png_image_format(png_ptr); + + image->format = format; + +#ifdef PNG_COLORSPACE_SUPPORTED + /* Does the colorspace match sRGB? If there is no color endpoint + * (colorant) information assume yes, otherwise require the + * 'ENDPOINTS_MATCHP_sRGB' colorspace flag to have been set. If the + * colorspace has been determined to be invalid ignore it. + */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && ((png_ptr->colorspace.flags + & (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB| + PNG_COLORSPACE_INVALID)) == PNG_COLORSPACE_HAVE_ENDPOINTS)) + image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB; +#endif + } + + /* We need the maximum number of entries regardless of the format the + * application sets here. + */ + { + png_uint_32 cmap_entries; + + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + cmap_entries = 1U << png_ptr->bit_depth; + break; + + case PNG_COLOR_TYPE_PALETTE: + cmap_entries = (png_uint_32)png_ptr->num_palette; + break; + + default: + cmap_entries = 256; + break; + } + + if (cmap_entries > 256) + cmap_entries = 256; + + image->colormap_entries = cmap_entries; + } + + return 1; +} + +#ifdef PNG_STDIO_SUPPORTED +int PNGAPI +png_image_begin_read_from_stdio(png_imagep image, FILE* file) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file != NULL) + { + if (png_image_read_init(image) != 0) + { + /* This is slightly evil, but png_init_io doesn't do anything other + * than this and we haven't changed the standard IO functions so + * this saves a 'safe' function. + */ + image->opaque->png_ptr->io_ptr = file; + return png_safe_execute(image, png_image_read_header, image); + } + } + + else + return png_image_error(image, + "png_image_begin_read_from_stdio: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION"); + + return 0; +} + +int PNGAPI +png_image_begin_read_from_file(png_imagep image, const char *file_name) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file_name != NULL) + { + FILE *fp = fopen(file_name, "rb"); + + if (fp != NULL) + { + if (png_image_read_init(image) != 0) + { + image->opaque->png_ptr->io_ptr = fp; + image->opaque->owned_file = 1; + return png_safe_execute(image, png_image_read_header, image); + } + + /* Clean up: just the opened file. */ + (void)fclose(fp); + } + + else + return png_image_error(image, strerror(errno)); + } + + else + return png_image_error(image, + "png_image_begin_read_from_file: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION"); + + return 0; +} +#endif /* STDIO */ + +static void PNGCBAPI +png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need) +{ + if (png_ptr != NULL) + { + png_imagep image = png_voidcast(png_imagep, png_ptr->io_ptr); + if (image != NULL) + { + png_controlp cp = image->opaque; + if (cp != NULL) + { + png_const_bytep memory = cp->memory; + png_size_t size = cp->size; + + if (memory != NULL && size >= need) + { + memcpy(out, memory, need); + cp->memory = memory + need; + cp->size = size - need; + return; + } + + png_error(png_ptr, "read beyond end of data"); + } + } + + png_error(png_ptr, "invalid memory read"); + } +} + +int PNGAPI png_image_begin_read_from_memory(png_imagep image, + png_const_voidp memory, png_size_t size) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (memory != NULL && size > 0) + { + if (png_image_read_init(image) != 0) + { + /* Now set the IO functions to read from the memory buffer and + * store it into io_ptr. Again do this in-place to avoid calling a + * libpng function that requires error handling. + */ + image->opaque->memory = png_voidcast(png_const_bytep, memory); + image->opaque->size = size; + image->opaque->png_ptr->io_ptr = image; + image->opaque->png_ptr->read_data_fn = png_image_memory_read; + + return png_safe_execute(image, png_image_read_header, image); + } + } + + else + return png_image_error(image, + "png_image_begin_read_from_memory: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION"); + + return 0; +} + +/* Utility function to skip chunks that are not used by the simplified image + * read functions and an appropriate macro to call it. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +static void +png_image_skip_unused_chunks(png_structrp png_ptr) +{ + /* Prepare the reader to ignore all recognized chunks whose data will not + * be used, i.e., all chunks recognized by libpng except for those + * involved in basic image reading: + * + * IHDR, PLTE, IDAT, IEND + * + * Or image data handling: + * + * tRNS, bKGD, gAMA, cHRM, sRGB, [iCCP] and sBIT. + * + * This provides a small performance improvement and eliminates any + * potential vulnerability to security problems in the unused chunks. + * + * At present the iCCP chunk data isn't used, so iCCP chunk can be ignored + * too. This allows the simplified API to be compiled without iCCP support, + * however if the support is there the chunk is still checked to detect + * errors (which are unfortunately quite common.) + */ + { + static PNG_CONST png_byte chunks_to_process[] = { + 98, 75, 71, 68, '\0', /* bKGD */ + 99, 72, 82, 77, '\0', /* cHRM */ + 103, 65, 77, 65, '\0', /* gAMA */ +# ifdef PNG_READ_iCCP_SUPPORTED + 105, 67, 67, 80, '\0', /* iCCP */ +# endif + 115, 66, 73, 84, '\0', /* sBIT */ + 115, 82, 71, 66, '\0', /* sRGB */ + }; + + /* Ignore unknown chunks and all other chunks except for the + * IHDR, PLTE, tRNS, IDAT, and IEND chunks. + */ + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, + NULL, -1); + + /* But do not ignore image data handling chunks */ + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_AS_DEFAULT, + chunks_to_process, (int)/*SAFE*/(sizeof chunks_to_process)/5); + } +} + +# define PNG_SKIP_CHUNKS(p) png_image_skip_unused_chunks(p) +#else +# define PNG_SKIP_CHUNKS(p) ((void)0) +#endif /* HANDLE_AS_UNKNOWN */ + +/* The following macro gives the exact rounded answer for all values in the + * range 0..255 (it actually divides by 51.2, but the rounding still generates + * the correct numbers 0..5 + */ +#define PNG_DIV51(v8) (((v8) * 5 + 130) >> 8) + +/* Utility functions to make particular color-maps */ +static void +set_file_encoding(png_image_read_control *display) +{ + png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma; + if (png_gamma_significant(g) != 0) + { + if (png_gamma_not_sRGB(g) != 0) + { + display->file_encoding = P_FILE; + display->gamma_to_linear = png_reciprocal(g); + } + + else + display->file_encoding = P_sRGB; + } + + else + display->file_encoding = P_LINEAR8; +} + +static unsigned int +decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding) +{ + if (encoding == P_FILE) /* double check */ + encoding = display->file_encoding; + + if (encoding == P_NOTSET) /* must be the file encoding */ + { + set_file_encoding(display); + encoding = display->file_encoding; + } + + switch (encoding) + { + case P_FILE: + value = png_gamma_16bit_correct(value*257, display->gamma_to_linear); + break; + + case P_sRGB: + value = png_sRGB_table[value]; + break; + + case P_LINEAR: + break; + + case P_LINEAR8: + value *= 257; + break; + +#ifdef __GNUC__ + default: + png_error(display->image->opaque->png_ptr, + "unexpected encoding (internal error)"); +#endif + } + + return value; +} + +static png_uint_32 +png_colormap_compose(png_image_read_control *display, + png_uint_32 foreground, int foreground_encoding, png_uint_32 alpha, + png_uint_32 background, int encoding) +{ + /* The file value is composed on the background, the background has the given + * encoding and so does the result, the file is encoded with P_FILE and the + * file and alpha are 8-bit values. The (output) encoding will always be + * P_LINEAR or P_sRGB. + */ + png_uint_32 f = decode_gamma(display, foreground, foreground_encoding); + png_uint_32 b = decode_gamma(display, background, encoding); + + /* The alpha is always an 8-bit value (it comes from the palette), the value + * scaled by 255 is what PNG_sRGB_FROM_LINEAR requires. + */ + f = f * alpha + b * (255-alpha); + + if (encoding == P_LINEAR) + { + /* Scale to 65535; divide by 255, approximately (in fact this is extremely + * accurate, it divides by 255.00000005937181414556, with no overflow.) + */ + f *= 257; /* Now scaled by 65535 */ + f += f >> 16; + f = (f+32768) >> 16; + } + + else /* P_sRGB */ + f = PNG_sRGB_FROM_LINEAR(f); + + return f; +} + +/* NOTE: P_LINEAR values to this routine must be 16-bit, but P_FILE values must + * be 8-bit. + */ +static void +png_create_colormap_entry(png_image_read_control *display, + png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue, + png_uint_32 alpha, int encoding) +{ + png_imagep image = display->image; + const int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) != 0 ? + P_LINEAR : P_sRGB; + const int convert_to_Y = (image->format & PNG_FORMAT_FLAG_COLOR) == 0 && + (red != green || green != blue); + + if (ip > 255) + png_error(image->opaque->png_ptr, "color-map index out of range"); + + /* Update the cache with whether the file gamma is significantly different + * from sRGB. + */ + if (encoding == P_FILE) + { + if (display->file_encoding == P_NOTSET) + set_file_encoding(display); + + /* Note that the cached value may be P_FILE too, but if it is then the + * gamma_to_linear member has been set. + */ + encoding = display->file_encoding; + } + + if (encoding == P_FILE) + { + png_fixed_point g = display->gamma_to_linear; + + red = png_gamma_16bit_correct(red*257, g); + green = png_gamma_16bit_correct(green*257, g); + blue = png_gamma_16bit_correct(blue*257, g); + + if (convert_to_Y != 0 || output_encoding == P_LINEAR) + { + alpha *= 257; + encoding = P_LINEAR; + } + + else + { + red = PNG_sRGB_FROM_LINEAR(red * 255); + green = PNG_sRGB_FROM_LINEAR(green * 255); + blue = PNG_sRGB_FROM_LINEAR(blue * 255); + encoding = P_sRGB; + } + } + + else if (encoding == P_LINEAR8) + { + /* This encoding occurs quite frequently in test cases because PngSuite + * includes a gAMA 1.0 chunk with most images. + */ + red *= 257; + green *= 257; + blue *= 257; + alpha *= 257; + encoding = P_LINEAR; + } + + else if (encoding == P_sRGB && + (convert_to_Y != 0 || output_encoding == P_LINEAR)) + { + /* The values are 8-bit sRGB values, but must be converted to 16-bit + * linear. + */ + red = png_sRGB_table[red]; + green = png_sRGB_table[green]; + blue = png_sRGB_table[blue]; + alpha *= 257; + encoding = P_LINEAR; + } + + /* This is set if the color isn't gray but the output is. */ + if (encoding == P_LINEAR) + { + if (convert_to_Y != 0) + { + /* NOTE: these values are copied from png_do_rgb_to_gray */ + png_uint_32 y = (png_uint_32)6968 * red + (png_uint_32)23434 * green + + (png_uint_32)2366 * blue; + + if (output_encoding == P_LINEAR) + y = (y + 16384) >> 15; + + else + { + /* y is scaled by 32768, we need it scaled by 255: */ + y = (y + 128) >> 8; + y *= 255; + y = PNG_sRGB_FROM_LINEAR((y + 64) >> 7); + alpha = PNG_DIV257(alpha); + encoding = P_sRGB; + } + + blue = red = green = y; + } + + else if (output_encoding == P_sRGB) + { + red = PNG_sRGB_FROM_LINEAR(red * 255); + green = PNG_sRGB_FROM_LINEAR(green * 255); + blue = PNG_sRGB_FROM_LINEAR(blue * 255); + alpha = PNG_DIV257(alpha); + encoding = P_sRGB; + } + } + + if (encoding != output_encoding) + png_error(image->opaque->png_ptr, "bad encoding (internal error)"); + + /* Store the value. */ + { +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + const int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 && + (image->format & PNG_FORMAT_FLAG_ALPHA) != 0; +# else +# define afirst 0 +# endif +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int bgr = (image->format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0; +# else +# define bgr 0 +# endif + + if (output_encoding == P_LINEAR) + { + png_uint_16p entry = png_voidcast(png_uint_16p, display->colormap); + + entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format); + + /* The linear 16-bit values must be pre-multiplied by the alpha channel + * value, if less than 65535 (this is, effectively, composite on black + * if the alpha channel is removed.) + */ + switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format)) + { + case 4: + entry[afirst ? 0 : 3] = (png_uint_16)alpha; + /* FALLTHROUGH */ + + case 3: + if (alpha < 65535) + { + if (alpha > 0) + { + blue = (blue * alpha + 32767U)/65535U; + green = (green * alpha + 32767U)/65535U; + red = (red * alpha + 32767U)/65535U; + } + + else + red = green = blue = 0; + } + entry[afirst + (2 ^ bgr)] = (png_uint_16)blue; + entry[afirst + 1] = (png_uint_16)green; + entry[afirst + bgr] = (png_uint_16)red; + break; + + case 2: + entry[1 ^ afirst] = (png_uint_16)alpha; + /* FALLTHROUGH */ + + case 1: + if (alpha < 65535) + { + if (alpha > 0) + green = (green * alpha + 32767U)/65535U; + + else + green = 0; + } + entry[afirst] = (png_uint_16)green; + break; + + default: + break; + } + } + + else /* output encoding is P_sRGB */ + { + png_bytep entry = png_voidcast(png_bytep, display->colormap); + + entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format); + + switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format)) + { + case 4: + entry[afirst ? 0 : 3] = (png_byte)alpha; + /* FALLTHROUGH */ + case 3: + entry[afirst + (2 ^ bgr)] = (png_byte)blue; + entry[afirst + 1] = (png_byte)green; + entry[afirst + bgr] = (png_byte)red; + break; + + case 2: + entry[1 ^ afirst] = (png_byte)alpha; + /* FALLTHROUGH */ + case 1: + entry[afirst] = (png_byte)green; + break; + + default: + break; + } + } + +# ifdef afirst +# undef afirst +# endif +# ifdef bgr +# undef bgr +# endif + } +} + +static int +make_gray_file_colormap(png_image_read_control *display) +{ + unsigned int i; + + for (i=0; i<256; ++i) + png_create_colormap_entry(display, i, i, i, i, 255, P_FILE); + + return (int)i; +} + +static int +make_gray_colormap(png_image_read_control *display) +{ + unsigned int i; + + for (i=0; i<256; ++i) + png_create_colormap_entry(display, i, i, i, i, 255, P_sRGB); + + return (int)i; +} +#define PNG_GRAY_COLORMAP_ENTRIES 256 + +static int +make_ga_colormap(png_image_read_control *display) +{ + unsigned int i, a; + + /* Alpha is retained, the output will be a color-map with entries + * selected by six levels of alpha. One transparent entry, 6 gray + * levels for all the intermediate alpha values, leaving 230 entries + * for the opaque grays. The color-map entries are the six values + * [0..5]*51, the GA processing uses PNG_DIV51(value) to find the + * relevant entry. + * + * if (alpha > 229) // opaque + * { + * // The 231 entries are selected to make the math below work: + * base = 0; + * entry = (231 * gray + 128) >> 8; + * } + * else if (alpha < 26) // transparent + * { + * base = 231; + * entry = 0; + * } + * else // partially opaque + * { + * base = 226 + 6 * PNG_DIV51(alpha); + * entry = PNG_DIV51(gray); + * } + */ + i = 0; + while (i < 231) + { + unsigned int gray = (i * 256 + 115) / 231; + png_create_colormap_entry(display, i++, gray, gray, gray, 255, P_sRGB); + } + + /* 255 is used here for the component values for consistency with the code + * that undoes premultiplication in pngwrite.c. + */ + png_create_colormap_entry(display, i++, 255, 255, 255, 0, P_sRGB); + + for (a=1; a<5; ++a) + { + unsigned int g; + + for (g=0; g<6; ++g) + png_create_colormap_entry(display, i++, g*51, g*51, g*51, a*51, + P_sRGB); + } + + return (int)i; +} + +#define PNG_GA_COLORMAP_ENTRIES 256 + +static int +make_rgb_colormap(png_image_read_control *display) +{ + unsigned int i, r; + + /* Build a 6x6x6 opaque RGB cube */ + for (i=r=0; r<6; ++r) + { + unsigned int g; + + for (g=0; g<6; ++g) + { + unsigned int b; + + for (b=0; b<6; ++b) + png_create_colormap_entry(display, i++, r*51, g*51, b*51, 255, + P_sRGB); + } + } + + return (int)i; +} + +#define PNG_RGB_COLORMAP_ENTRIES 216 + +/* Return a palette index to the above palette given three 8-bit sRGB values. */ +#define PNG_RGB_INDEX(r,g,b) \ + ((png_byte)(6 * (6 * PNG_DIV51(r) + PNG_DIV51(g)) + PNG_DIV51(b))) + +static int +png_image_read_colormap(png_voidp argument) +{ + png_image_read_control *display = + png_voidcast(png_image_read_control*, argument); + const png_imagep image = display->image; + + const png_structrp png_ptr = image->opaque->png_ptr; + const png_uint_32 output_format = image->format; + const int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) != 0 ? + P_LINEAR : P_sRGB; + + unsigned int cmap_entries; + unsigned int output_processing; /* Output processing option */ + unsigned int data_encoding = P_NOTSET; /* Encoding libpng must produce */ + + /* Background information; the background color and the index of this color + * in the color-map if it exists (else 256). + */ + unsigned int background_index = 256; + png_uint_32 back_r, back_g, back_b; + + /* Flags to accumulate things that need to be done to the input. */ + int expand_tRNS = 0; + + /* Exclude the NYI feature of compositing onto a color-mapped buffer; it is + * very difficult to do, the results look awful, and it is difficult to see + * what possible use it is because the application can't control the + * color-map. + */ + if (((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0 || + png_ptr->num_trans > 0) /* alpha in input */ && + ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0) /* no alpha in output */) + { + if (output_encoding == P_LINEAR) /* compose on black */ + back_b = back_g = back_r = 0; + + else if (display->background == NULL /* no way to remove it */) + png_error(png_ptr, + "background color must be supplied to remove alpha/transparency"); + + /* Get a copy of the background color (this avoids repeating the checks + * below.) The encoding is 8-bit sRGB or 16-bit linear, depending on the + * output format. + */ + else + { + back_g = display->background->green; + if ((output_format & PNG_FORMAT_FLAG_COLOR) != 0) + { + back_r = display->background->red; + back_b = display->background->blue; + } + else + back_b = back_r = back_g; + } + } + + else if (output_encoding == P_LINEAR) + back_b = back_r = back_g = 65535; + + else + back_b = back_r = back_g = 255; + + /* Default the input file gamma if required - this is necessary because + * libpng assumes that if no gamma information is present the data is in the + * output format, but the simplified API deduces the gamma from the input + * format. + */ + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) == 0) + { + /* Do this directly, not using the png_colorspace functions, to ensure + * that it happens even if the colorspace is invalid (though probably if + * it is the setting will be ignored) Note that the same thing can be + * achieved at the application interface with png_set_gAMA. + */ + if (png_ptr->bit_depth == 16 && + (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) + png_ptr->colorspace.gamma = PNG_GAMMA_LINEAR; + + else + png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE; + + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } + + /* Decide what to do based on the PNG color type of the input data. The + * utility function png_create_colormap_entry deals with most aspects of the + * output transformations; this code works out how to produce bytes of + * color-map entries from the original format. + */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + if (png_ptr->bit_depth <= 8) + { + /* There at most 256 colors in the output, regardless of + * transparency. + */ + unsigned int step, i, val, trans = 256/*ignore*/, back_alpha = 0; + + cmap_entries = 1U << png_ptr->bit_depth; + if (cmap_entries > image->colormap_entries) + png_error(png_ptr, "gray[8] color-map: too few entries"); + + step = 255 / (cmap_entries - 1); + output_processing = PNG_CMAP_NONE; + + /* If there is a tRNS chunk then this either selects a transparent + * value or, if the output has no alpha, the background color. + */ + if (png_ptr->num_trans > 0) + { + trans = png_ptr->trans_color.gray; + + if ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0) + back_alpha = output_encoding == P_LINEAR ? 65535 : 255; + } + + /* png_create_colormap_entry just takes an RGBA and writes the + * corresponding color-map entry using the format from 'image', + * including the required conversion to sRGB or linear as + * appropriate. The input values are always either sRGB (if the + * gamma correction flag is 0) or 0..255 scaled file encoded values + * (if the function must gamma correct them). + */ + for (i=val=0; ibit_depth < 8) + png_set_packing(png_ptr); + } + + else /* bit depth is 16 */ + { + /* The 16-bit input values can be converted directly to 8-bit gamma + * encoded values; however, if a tRNS chunk is present 257 color-map + * entries are required. This means that the extra entry requires + * special processing; add an alpha channel, sacrifice gray level + * 254 and convert transparent (alpha==0) entries to that. + * + * Use libpng to chop the data to 8 bits. Convert it to sRGB at the + * same time to minimize quality loss. If a tRNS chunk is present + * this means libpng must handle it too; otherwise it is impossible + * to do the exact match on the 16-bit value. + * + * If the output has no alpha channel *and* the background color is + * gray then it is possible to let libpng handle the substitution by + * ensuring that the corresponding gray level matches the background + * color exactly. + */ + data_encoding = P_sRGB; + + if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "gray[16] color-map: too few entries"); + + cmap_entries = (unsigned int)make_gray_colormap(display); + + if (png_ptr->num_trans > 0) + { + unsigned int back_alpha; + + if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0) + back_alpha = 0; + + else + { + if (back_r == back_g && back_g == back_b) + { + /* Background is gray; no special processing will be + * required. + */ + png_color_16 c; + png_uint_32 gray = back_g; + + if (output_encoding == P_LINEAR) + { + gray = PNG_sRGB_FROM_LINEAR(gray * 255); + + /* And make sure the corresponding palette entry + * matches. + */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 65535, P_LINEAR); + } + + /* The background passed to libpng, however, must be the + * sRGB value. + */ + c.index = 0; /*unused*/ + c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + + /* NOTE: does this work without expanding tRNS to alpha? + * It should be the color->gray case below apparently + * doesn't. + */ + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + + output_processing = PNG_CMAP_NONE; + break; + } +#ifdef __COVERITY__ + /* Coverity claims that output_encoding cannot be 2 (P_LINEAR) + * here. + */ + back_alpha = 255; +#else + back_alpha = output_encoding == P_LINEAR ? 65535 : 255; +#endif + } + + /* output_processing means that the libpng-processed row will be + * 8-bit GA and it has to be processing to single byte color-map + * values. Entry 254 is replaced by either a completely + * transparent entry or by the background color at full + * precision (and the background color is not a simple gray + * level in this case.) + */ + expand_tRNS = 1; + output_processing = PNG_CMAP_TRANS; + background_index = 254; + + /* And set (overwrite) color-map entry 254 to the actual + * background color at full precision. + */ + png_create_colormap_entry(display, 254, back_r, back_g, back_b, + back_alpha, output_encoding); + } + + else + output_processing = PNG_CMAP_NONE; + } + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + /* 8-bit or 16-bit PNG with two channels - gray and alpha. A minimum + * of 65536 combinations. If, however, the alpha channel is to be + * removed there are only 256 possibilities if the background is gray. + * (Otherwise there is a subset of the 65536 possibilities defined by + * the triangle between black, white and the background color.) + * + * Reduce 16-bit files to 8-bit and sRGB encode the result. No need to + * worry about tRNS matching - tRNS is ignored if there is an alpha + * channel. + */ + data_encoding = P_sRGB; + + if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "gray+alpha color-map: too few entries"); + + cmap_entries = (unsigned int)make_ga_colormap(display); + + background_index = PNG_CMAP_GA_BACKGROUND; + output_processing = PNG_CMAP_GA; + } + + else /* alpha is removed */ + { + /* Alpha must be removed as the PNG data is processed when the + * background is a color because the G and A channels are + * independent and the vector addition (non-parallel vectors) is a + * 2-D problem. + * + * This can be reduced to the same algorithm as above by making a + * colormap containing gray levels (for the opaque grays), a + * background entry (for a transparent pixel) and a set of four six + * level color values, one set for each intermediate alpha value. + * See the comments in make_ga_colormap for how this works in the + * per-pixel processing. + * + * If the background is gray, however, we only need a 256 entry gray + * level color map. It is sufficient to make the entry generated + * for the background color be exactly the color specified. + */ + if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0 || + (back_r == back_g && back_g == back_b)) + { + /* Background is gray; no special processing will be required. */ + png_color_16 c; + png_uint_32 gray = back_g; + + if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "gray-alpha color-map: too few entries"); + + cmap_entries = (unsigned int)make_gray_colormap(display); + + if (output_encoding == P_LINEAR) + { + gray = PNG_sRGB_FROM_LINEAR(gray * 255); + + /* And make sure the corresponding palette entry matches. */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 65535, P_LINEAR); + } + + /* The background passed to libpng, however, must be the sRGB + * value. + */ + c.index = 0; /*unused*/ + c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + + output_processing = PNG_CMAP_NONE; + } + + else + { + png_uint_32 i, a; + + /* This is the same as png_make_ga_colormap, above, except that + * the entries are all opaque. + */ + if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "ga-alpha color-map: too few entries"); + + i = 0; + while (i < 231) + { + png_uint_32 gray = (i * 256 + 115) / 231; + png_create_colormap_entry(display, i++, gray, gray, gray, + 255, P_sRGB); + } + + /* NOTE: this preserves the full precision of the application + * background color. + */ + background_index = i; + png_create_colormap_entry(display, i++, back_r, back_g, back_b, +#ifdef __COVERITY__ + /* Coverity claims that output_encoding + * cannot be 2 (P_LINEAR) here. + */ 255U, +#else + output_encoding == P_LINEAR ? 65535U : 255U, +#endif + output_encoding); + + /* For non-opaque input composite on the sRGB background - this + * requires inverting the encoding for each component. The input + * is still converted to the sRGB encoding because this is a + * reasonable approximate to the logarithmic curve of human + * visual sensitivity, at least over the narrow range which PNG + * represents. Consequently 'G' is always sRGB encoded, while + * 'A' is linear. We need the linear background colors. + */ + if (output_encoding == P_sRGB) /* else already linear */ + { + /* This may produce a value not exactly matching the + * background, but that's ok because these numbers are only + * used when alpha != 0 + */ + back_r = png_sRGB_table[back_r]; + back_g = png_sRGB_table[back_g]; + back_b = png_sRGB_table[back_b]; + } + + for (a=1; a<5; ++a) + { + unsigned int g; + + /* PNG_sRGB_FROM_LINEAR expects a 16-bit linear value scaled + * by an 8-bit alpha value (0..255). + */ + png_uint_32 alpha = 51 * a; + png_uint_32 back_rx = (255-alpha) * back_r; + png_uint_32 back_gx = (255-alpha) * back_g; + png_uint_32 back_bx = (255-alpha) * back_b; + + for (g=0; g<6; ++g) + { + png_uint_32 gray = png_sRGB_table[g*51] * alpha; + + png_create_colormap_entry(display, i++, + PNG_sRGB_FROM_LINEAR(gray + back_rx), + PNG_sRGB_FROM_LINEAR(gray + back_gx), + PNG_sRGB_FROM_LINEAR(gray + back_bx), 255, P_sRGB); + } + } + + cmap_entries = i; + output_processing = PNG_CMAP_GA; + } + } + break; + + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + /* Exclude the case where the output is gray; we can always handle this + * with the cases above. + */ + if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0) + { + /* The color-map will be grayscale, so we may as well convert the + * input RGB values to a simple grayscale and use the grayscale + * code above. + * + * NOTE: calling this apparently damages the recognition of the + * transparent color in background color handling; call + * png_set_tRNS_to_alpha before png_set_background_fixed. + */ + png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, -1, + -1); + data_encoding = P_sRGB; + + /* The output will now be one or two 8-bit gray or gray+alpha + * channels. The more complex case arises when the input has alpha. + */ + if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) && + (output_format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + /* Both input and output have an alpha channel, so no background + * processing is required; just map the GA bytes to the right + * color-map entry. + */ + expand_tRNS = 1; + + if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "rgb[ga] color-map: too few entries"); + + cmap_entries = (unsigned int)make_ga_colormap(display); + background_index = PNG_CMAP_GA_BACKGROUND; + output_processing = PNG_CMAP_GA; + } + + else + { + /* Either the input or the output has no alpha channel, so there + * will be no non-opaque pixels in the color-map; it will just be + * grayscale. + */ + if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "rgb[gray] color-map: too few entries"); + + /* Ideally this code would use libpng to do the gamma correction, + * but if an input alpha channel is to be removed we will hit the + * libpng bug in gamma+compose+rgb-to-gray (the double gamma + * correction bug). Fix this by dropping the gamma correction in + * this case and doing it in the palette; this will result in + * duplicate palette entries, but that's better than the + * alternative of double gamma correction. + */ + if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) && + png_gamma_not_sRGB(png_ptr->colorspace.gamma) != 0) + { + cmap_entries = (unsigned int)make_gray_file_colormap(display); + data_encoding = P_FILE; + } + + else + cmap_entries = (unsigned int)make_gray_colormap(display); + + /* But if the input has alpha or transparency it must be removed + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) + { + png_color_16 c; + png_uint_32 gray = back_g; + + /* We need to ensure that the application background exists in + * the colormap and that completely transparent pixels map to + * it. Achieve this simply by ensuring that the entry + * selected for the background really is the background color. + */ + if (data_encoding == P_FILE) /* from the fixup above */ + { + /* The app supplied a gray which is in output_encoding, we + * need to convert it to a value of the input (P_FILE) + * encoding then set this palette entry to the required + * output encoding. + */ + if (output_encoding == P_sRGB) + gray = png_sRGB_table[gray]; /* now P_LINEAR */ + + gray = PNG_DIV257(png_gamma_16bit_correct(gray, + png_ptr->colorspace.gamma)); /* now P_FILE */ + + /* And make sure the corresponding palette entry contains + * exactly the required sRGB value. + */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 0/*unused*/, output_encoding); + } + + else if (output_encoding == P_LINEAR) + { + gray = PNG_sRGB_FROM_LINEAR(gray * 255); + + /* And make sure the corresponding palette entry matches. + */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 0/*unused*/, P_LINEAR); + } + + /* The background passed to libpng, however, must be the + * output (normally sRGB) value. + */ + c.index = 0; /*unused*/ + c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + + /* NOTE: the following is apparently a bug in libpng. Without + * it the transparent color recognition in + * png_set_background_fixed seems to go wrong. + */ + expand_tRNS = 1; + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + } + + output_processing = PNG_CMAP_NONE; + } + } + + else /* output is color */ + { + /* We could use png_quantize here so long as there is no transparent + * color or alpha; png_quantize ignores alpha. Easier overall just + * to do it once and using PNG_DIV51 on the 6x6x6 reduced RGB cube. + * Consequently we always want libpng to produce sRGB data. + */ + data_encoding = P_sRGB; + + /* Is there any transparency or alpha? */ + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) + { + /* Is there alpha in the output too? If so all four channels are + * processed into a special RGB cube with alpha support. + */ + if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + png_uint_32 r; + + if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries) + png_error(png_ptr, "rgb+alpha color-map: too few entries"); + + cmap_entries = (unsigned int)make_rgb_colormap(display); + + /* Add a transparent entry. */ + png_create_colormap_entry(display, cmap_entries, 255, 255, + 255, 0, P_sRGB); + + /* This is stored as the background index for the processing + * algorithm. + */ + background_index = cmap_entries++; + + /* Add 27 r,g,b entries each with alpha 0.5. */ + for (r=0; r<256; r = (r << 1) | 0x7f) + { + png_uint_32 g; + + for (g=0; g<256; g = (g << 1) | 0x7f) + { + png_uint_32 b; + + /* This generates components with the values 0, 127 and + * 255 + */ + for (b=0; b<256; b = (b << 1) | 0x7f) + png_create_colormap_entry(display, cmap_entries++, + r, g, b, 128, P_sRGB); + } + } + + expand_tRNS = 1; + output_processing = PNG_CMAP_RGB_ALPHA; + } + + else + { + /* Alpha/transparency must be removed. The background must + * exist in the color map (achieved by setting adding it after + * the 666 color-map). If the standard processing code will + * pick up this entry automatically that's all that is + * required; libpng can be called to do the background + * processing. + */ + unsigned int sample_size = + PNG_IMAGE_SAMPLE_SIZE(output_format); + png_uint_32 r, g, b; /* sRGB background */ + + if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries) + png_error(png_ptr, "rgb-alpha color-map: too few entries"); + + cmap_entries = (unsigned int)make_rgb_colormap(display); + + png_create_colormap_entry(display, cmap_entries, back_r, + back_g, back_b, 0/*unused*/, output_encoding); + + if (output_encoding == P_LINEAR) + { + r = PNG_sRGB_FROM_LINEAR(back_r * 255); + g = PNG_sRGB_FROM_LINEAR(back_g * 255); + b = PNG_sRGB_FROM_LINEAR(back_b * 255); + } + + else + { + r = back_r; + g = back_g; + b = back_g; + } + + /* Compare the newly-created color-map entry with the one the + * PNG_CMAP_RGB algorithm will use. If the two entries don't + * match, add the new one and set this as the background + * index. + */ + if (memcmp((png_const_bytep)display->colormap + + sample_size * cmap_entries, + (png_const_bytep)display->colormap + + sample_size * PNG_RGB_INDEX(r,g,b), + sample_size) != 0) + { + /* The background color must be added. */ + background_index = cmap_entries++; + + /* Add 27 r,g,b entries each with created by composing with + * the background at alpha 0.5. + */ + for (r=0; r<256; r = (r << 1) | 0x7f) + { + for (g=0; g<256; g = (g << 1) | 0x7f) + { + /* This generates components with the values 0, 127 + * and 255 + */ + for (b=0; b<256; b = (b << 1) | 0x7f) + png_create_colormap_entry(display, cmap_entries++, + png_colormap_compose(display, r, P_sRGB, 128, + back_r, output_encoding), + png_colormap_compose(display, g, P_sRGB, 128, + back_g, output_encoding), + png_colormap_compose(display, b, P_sRGB, 128, + back_b, output_encoding), + 0/*unused*/, output_encoding); + } + } + + expand_tRNS = 1; + output_processing = PNG_CMAP_RGB_ALPHA; + } + + else /* background color is in the standard color-map */ + { + png_color_16 c; + + c.index = 0; /*unused*/ + c.red = (png_uint_16)back_r; + c.gray = c.green = (png_uint_16)back_g; + c.blue = (png_uint_16)back_b; + + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + + output_processing = PNG_CMAP_RGB; + } + } + } + + else /* no alpha or transparency in the input */ + { + /* Alpha in the output is irrelevant, simply map the opaque input + * pixels to the 6x6x6 color-map. + */ + if (PNG_RGB_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "rgb color-map: too few entries"); + + cmap_entries = (unsigned int)make_rgb_colormap(display); + output_processing = PNG_CMAP_RGB; + } + } + break; + + case PNG_COLOR_TYPE_PALETTE: + /* It's already got a color-map. It may be necessary to eliminate the + * tRNS entries though. + */ + { + unsigned int num_trans = png_ptr->num_trans; + png_const_bytep trans = num_trans > 0 ? png_ptr->trans_alpha : NULL; + png_const_colorp colormap = png_ptr->palette; + const int do_background = trans != NULL && + (output_format & PNG_FORMAT_FLAG_ALPHA) == 0; + unsigned int i; + + /* Just in case: */ + if (trans == NULL) + num_trans = 0; + + output_processing = PNG_CMAP_NONE; + data_encoding = P_FILE; /* Don't change from color-map indices */ + cmap_entries = (unsigned int)png_ptr->num_palette; + if (cmap_entries > 256) + cmap_entries = 256; + + if (cmap_entries > (unsigned int)image->colormap_entries) + png_error(png_ptr, "palette color-map: too few entries"); + + for (i=0; i < cmap_entries; ++i) + { + if (do_background != 0 && i < num_trans && trans[i] < 255) + { + if (trans[i] == 0) + png_create_colormap_entry(display, i, back_r, back_g, + back_b, 0, output_encoding); + + else + { + /* Must compose the PNG file color in the color-map entry + * on the sRGB color in 'back'. + */ + png_create_colormap_entry(display, i, + png_colormap_compose(display, colormap[i].red, + P_FILE, trans[i], back_r, output_encoding), + png_colormap_compose(display, colormap[i].green, + P_FILE, trans[i], back_g, output_encoding), + png_colormap_compose(display, colormap[i].blue, + P_FILE, trans[i], back_b, output_encoding), + output_encoding == P_LINEAR ? trans[i] * 257U : + trans[i], + output_encoding); + } + } + + else + png_create_colormap_entry(display, i, colormap[i].red, + colormap[i].green, colormap[i].blue, + i < num_trans ? trans[i] : 255U, P_FILE/*8-bit*/); + } + + /* The PNG data may have indices packed in fewer than 8 bits, it + * must be expanded if so. + */ + if (png_ptr->bit_depth < 8) + png_set_packing(png_ptr); + } + break; + + default: + png_error(png_ptr, "invalid PNG color type"); + /*NOT REACHED*/ + } + + /* Now deal with the output processing */ + if (expand_tRNS != 0 && png_ptr->num_trans > 0 && + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0) + png_set_tRNS_to_alpha(png_ptr); + + switch (data_encoding) + { + case P_sRGB: + /* Change to 8-bit sRGB */ + png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, PNG_GAMMA_sRGB); + /* FALLTHROUGH */ + + case P_FILE: + if (png_ptr->bit_depth > 8) + png_set_scale_16(png_ptr); + break; + +#ifdef __GNUC__ + default: + png_error(png_ptr, "bad data option (internal error)"); +#endif + } + + if (cmap_entries > 256 || cmap_entries > image->colormap_entries) + png_error(png_ptr, "color map overflow (BAD internal error)"); + + image->colormap_entries = cmap_entries; + + /* Double check using the recorded background index */ + switch (output_processing) + { + case PNG_CMAP_NONE: + if (background_index != PNG_CMAP_NONE_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_GA: + if (background_index != PNG_CMAP_GA_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_TRANS: + if (background_index >= cmap_entries || + background_index != PNG_CMAP_TRANS_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_RGB: + if (background_index != PNG_CMAP_RGB_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_RGB_ALPHA: + if (background_index != PNG_CMAP_RGB_ALPHA_BACKGROUND) + goto bad_background; + break; + + default: + png_error(png_ptr, "bad processing option (internal error)"); + + bad_background: + png_error(png_ptr, "bad background index (internal error)"); + } + + display->colormap_processing = (int)output_processing; + + return 1/*ok*/; +} + +/* The final part of the color-map read called from png_image_finish_read. */ +static int +png_image_read_and_map(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + int passes; + + /* Called when the libpng data must be transformed into the color-mapped + * form. There is a local row buffer in display->local and this routine must + * do the interlace handling. + */ + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + png_error(png_ptr, "unknown interlace type"); + } + + { + png_uint_32 height = image->height; + png_uint_32 width = image->width; + int proc = display->colormap_processing; + png_bytep first_row = png_voidcast(png_bytep, display->first_row); + ptrdiff_t step_row = display->row_bytes; + int pass; + + for (pass = 0; pass < passes; ++pass) + { + unsigned int startx, stepx, stepy; + png_uint_32 y; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass); + stepx = PNG_PASS_COL_OFFSET(pass); + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = stepy = 1; + } + + for (; ylocal_row); + png_bytep outrow = first_row + y * step_row; + png_const_bytep end_row = outrow + width; + + /* Read read the libpng data into the temporary buffer. */ + png_read_row(png_ptr, inrow, NULL); + + /* Now process the row according to the processing option, note + * that the caller verifies that the format of the libpng output + * data is as required. + */ + outrow += startx; + switch (proc) + { + case PNG_CMAP_GA: + for (; outrow < end_row; outrow += stepx) + { + /* The data is always in the PNG order */ + unsigned int gray = *inrow++; + unsigned int alpha = *inrow++; + unsigned int entry; + + /* NOTE: this code is copied as a comment in + * make_ga_colormap above. Please update the + * comment if you change this code! + */ + if (alpha > 229) /* opaque */ + { + entry = (231 * gray + 128) >> 8; + } + else if (alpha < 26) /* transparent */ + { + entry = 231; + } + else /* partially opaque */ + { + entry = 226 + 6 * PNG_DIV51(alpha) + PNG_DIV51(gray); + } + + *outrow = (png_byte)entry; + } + break; + + case PNG_CMAP_TRANS: + for (; outrow < end_row; outrow += stepx) + { + png_byte gray = *inrow++; + png_byte alpha = *inrow++; + + if (alpha == 0) + *outrow = PNG_CMAP_TRANS_BACKGROUND; + + else if (gray != PNG_CMAP_TRANS_BACKGROUND) + *outrow = gray; + + else + *outrow = (png_byte)(PNG_CMAP_TRANS_BACKGROUND+1); + } + break; + + case PNG_CMAP_RGB: + for (; outrow < end_row; outrow += stepx) + { + *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], inrow[2]); + inrow += 3; + } + break; + + case PNG_CMAP_RGB_ALPHA: + for (; outrow < end_row; outrow += stepx) + { + unsigned int alpha = inrow[3]; + + /* Because the alpha entries only hold alpha==0.5 values + * split the processing at alpha==0.25 (64) and 0.75 + * (196). + */ + + if (alpha >= 196) + *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], + inrow[2]); + + else if (alpha < 64) + *outrow = PNG_CMAP_RGB_ALPHA_BACKGROUND; + + else + { + /* Likewise there are three entries for each of r, g + * and b. We could select the entry by popcount on + * the top two bits on those architectures that + * support it, this is what the code below does, + * crudely. + */ + unsigned int back_i = PNG_CMAP_RGB_ALPHA_BACKGROUND+1; + + /* Here are how the values map: + * + * 0x00 .. 0x3f -> 0 + * 0x40 .. 0xbf -> 1 + * 0xc0 .. 0xff -> 2 + * + * So, as above with the explicit alpha checks, the + * breakpoints are at 64 and 196. + */ + if (inrow[0] & 0x80) back_i += 9; /* red */ + if (inrow[0] & 0x40) back_i += 9; + if (inrow[0] & 0x80) back_i += 3; /* green */ + if (inrow[0] & 0x40) back_i += 3; + if (inrow[0] & 0x80) back_i += 1; /* blue */ + if (inrow[0] & 0x40) back_i += 1; + + *outrow = (png_byte)back_i; + } + + inrow += 4; + } + break; + + default: + break; + } + } + } + } + + return 1; +} + +static int +png_image_read_colormapped(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_controlp control = image->opaque; + png_structrp png_ptr = control->png_ptr; + png_inforp info_ptr = control->info_ptr; + + int passes = 0; /* As a flag */ + + PNG_SKIP_CHUNKS(png_ptr); + + /* Update the 'info' structure and make sure the result is as required; first + * make sure to turn on the interlace handling if it will be required + * (because it can't be turned on *after* the call to png_read_update_info!) + */ + if (display->colormap_processing == PNG_CMAP_NONE) + passes = png_set_interlace_handling(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + + /* The expected output can be deduced from the colormap_processing option. */ + switch (display->colormap_processing) + { + case PNG_CMAP_NONE: + /* Output must be one channel and one byte per pixel, the output + * encoding can be anything. + */ + if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + info_ptr->color_type == PNG_COLOR_TYPE_GRAY) && + info_ptr->bit_depth == 8) + break; + + goto bad_output; + + case PNG_CMAP_TRANS: + case PNG_CMAP_GA: + /* Output must be two channels and the 'G' one must be sRGB, the latter + * can be checked with an exact number because it should have been set + * to this number above! + */ + if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + info_ptr->bit_depth == 8 && + png_ptr->screen_gamma == PNG_GAMMA_sRGB && + image->colormap_entries == 256) + break; + + goto bad_output; + + case PNG_CMAP_RGB: + /* Output must be 8-bit sRGB encoded RGB */ + if (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + info_ptr->bit_depth == 8 && + png_ptr->screen_gamma == PNG_GAMMA_sRGB && + image->colormap_entries == 216) + break; + + goto bad_output; + + case PNG_CMAP_RGB_ALPHA: + /* Output must be 8-bit sRGB encoded RGBA */ + if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + info_ptr->bit_depth == 8 && + png_ptr->screen_gamma == PNG_GAMMA_sRGB && + image->colormap_entries == 244 /* 216 + 1 + 27 */) + break; + + goto bad_output; + + default: + bad_output: + png_error(png_ptr, "bad color-map processing (internal error)"); + } + + /* Now read the rows. Do this here if it is possible to read directly into + * the output buffer, otherwise allocate a local row buffer of the maximum + * size libpng requires and call the relevant processing routine safely. + */ + { + png_voidp first_row = display->buffer; + ptrdiff_t row_bytes = display->row_stride; + + /* The following expression is designed to work correctly whether it gives + * a signed or an unsigned result. + */ + if (row_bytes < 0) + { + char *ptr = png_voidcast(char*, first_row); + ptr += (image->height-1) * (-row_bytes); + first_row = png_voidcast(png_voidp, ptr); + } + + display->first_row = first_row; + display->row_bytes = row_bytes; + } + + if (passes == 0) + { + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_and_map, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + + else + { + png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes; + + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep row = png_voidcast(png_bytep, display->first_row); + + for (; y > 0; --y) + { + png_read_row(png_ptr, row, NULL); + row += row_bytes; + } + } + + return 1; + } +} + +/* Just the row reading part of png_image_read. */ +static int +png_image_read_composite(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + int passes; + + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + png_error(png_ptr, "unknown interlace type"); + } + + { + png_uint_32 height = image->height; + png_uint_32 width = image->width; + ptrdiff_t step_row = display->row_bytes; + unsigned int channels = + (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1; + int pass; + + for (pass = 0; pass < passes; ++pass) + { + unsigned int startx, stepx, stepy; + png_uint_32 y; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass) * channels; + stepx = PNG_PASS_COL_OFFSET(pass) * channels; + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = channels; + stepy = 1; + } + + for (; ylocal_row); + png_bytep outrow; + png_const_bytep end_row; + + /* Read the row, which is packed: */ + png_read_row(png_ptr, inrow, NULL); + + outrow = png_voidcast(png_bytep, display->first_row); + outrow += y * step_row; + end_row = outrow + width * channels; + + /* Now do the composition on each pixel in this row. */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_byte alpha = inrow[channels]; + + if (alpha > 0) /* else no change to the output */ + { + unsigned int c; + + for (c=0; cimage; + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + png_uint_32 height = image->height; + png_uint_32 width = image->width; + int pass, passes; + + /* Double check the convoluted logic below. We expect to get here with + * libpng doing rgb to gray and gamma correction but background processing + * left to the png_image_read_background function. The rows libpng produce + * might be 8 or 16-bit but should always have two channels; gray plus alpha. + */ + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) + png_error(png_ptr, "lost rgb to gray"); + + if ((png_ptr->transformations & PNG_COMPOSE) != 0) + png_error(png_ptr, "unexpected compose"); + + if (png_get_channels(png_ptr, info_ptr) != 2) + png_error(png_ptr, "lost/gained channels"); + + /* Expect the 8-bit case to always remove the alpha channel */ + if ((image->format & PNG_FORMAT_FLAG_LINEAR) == 0 && + (image->format & PNG_FORMAT_FLAG_ALPHA) != 0) + png_error(png_ptr, "unexpected 8-bit transformation"); + + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + png_error(png_ptr, "unknown interlace type"); + } + + /* Use direct access to info_ptr here because otherwise the simplified API + * would require PNG_EASY_ACCESS_SUPPORTED (just for this.) Note this is + * checking the value after libpng expansions, not the original value in the + * PNG. + */ + switch (info_ptr->bit_depth) + { + case 8: + /* 8-bit sRGB gray values with an alpha channel; the alpha channel is + * to be removed by composing on a background: either the row if + * display->background is NULL or display->background->green if not. + * Unlike the code above ALPHA_OPTIMIZED has *not* been done. + */ + { + png_bytep first_row = png_voidcast(png_bytep, display->first_row); + ptrdiff_t step_row = display->row_bytes; + + for (pass = 0; pass < passes; ++pass) + { + png_bytep row = png_voidcast(png_bytep, display->first_row); + unsigned int startx, stepx, stepy; + png_uint_32 y; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass); + stepx = PNG_PASS_COL_OFFSET(pass); + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = stepy = 1; + } + + if (display->background == NULL) + { + for (; ylocal_row); + png_bytep outrow = first_row + y * step_row; + png_const_bytep end_row = outrow + width; + + /* Read the row, which is packed: */ + png_read_row(png_ptr, inrow, NULL); + + /* Now do the composition on each pixel in this row. */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_byte alpha = inrow[1]; + + if (alpha > 0) /* else no change to the output */ + { + png_uint_32 component = inrow[0]; + + if (alpha < 255) /* else just use component */ + { + /* Since PNG_OPTIMIZED_ALPHA was not set it is + * necessary to invert the sRGB transfer + * function and multiply the alpha out. + */ + component = png_sRGB_table[component] * alpha; + component += png_sRGB_table[outrow[0]] * + (255-alpha); + component = PNG_sRGB_FROM_LINEAR(component); + } + + outrow[0] = (png_byte)component; + } + + inrow += 2; /* gray and alpha channel */ + } + } + } + + else /* constant background value */ + { + png_byte background8 = display->background->green; + png_uint_16 background = png_sRGB_table[background8]; + + for (; ylocal_row); + png_bytep outrow = first_row + y * step_row; + png_const_bytep end_row = outrow + width; + + /* Read the row, which is packed: */ + png_read_row(png_ptr, inrow, NULL); + + /* Now do the composition on each pixel in this row. */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_byte alpha = inrow[1]; + + if (alpha > 0) /* else use background */ + { + png_uint_32 component = inrow[0]; + + if (alpha < 255) /* else just use component */ + { + component = png_sRGB_table[component] * alpha; + component += background * (255-alpha); + component = PNG_sRGB_FROM_LINEAR(component); + } + + outrow[0] = (png_byte)component; + } + + else + outrow[0] = background8; + + inrow += 2; /* gray and alpha channel */ + } + + row += display->row_bytes; + } + } + } + } + break; + + case 16: + /* 16-bit linear with pre-multiplied alpha; the pre-multiplication must + * still be done and, maybe, the alpha channel removed. This code also + * handles the alpha-first option. + */ + { + png_uint_16p first_row = png_voidcast(png_uint_16p, + display->first_row); + /* The division by two is safe because the caller passed in a + * stride which was multiplied by 2 (below) to get row_bytes. + */ + ptrdiff_t step_row = display->row_bytes / 2; + unsigned int preserve_alpha = (image->format & + PNG_FORMAT_FLAG_ALPHA) != 0; + unsigned int outchannels = 1U+preserve_alpha; + int swap_alpha = 0; + +# ifdef PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED + if (preserve_alpha != 0 && + (image->format & PNG_FORMAT_FLAG_AFIRST) != 0) + swap_alpha = 1; +# endif + + for (pass = 0; pass < passes; ++pass) + { + unsigned int startx, stepx, stepy; + png_uint_32 y; + + /* The 'x' start and step are adjusted to output components here. + */ + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass) * outchannels; + stepx = PNG_PASS_COL_OFFSET(pass) * outchannels; + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = outchannels; + stepy = 1; + } + + for (; ylocal_row), NULL); + inrow = png_voidcast(png_const_uint_16p, display->local_row); + + /* Now do the pre-multiplication on each pixel in this row. + */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_uint_32 component = inrow[0]; + png_uint_16 alpha = inrow[1]; + + if (alpha > 0) /* else 0 */ + { + if (alpha < 65535) /* else just use component */ + { + component *= alpha; + component += 32767; + component /= 65535; + } + } + + else + component = 0; + + outrow[swap_alpha] = (png_uint_16)component; + if (preserve_alpha != 0) + outrow[1 ^ swap_alpha] = alpha; + + inrow += 2; /* components and alpha channel */ + } + } + } + } + break; + +#ifdef __GNUC__ + default: + png_error(png_ptr, "unexpected bit depth"); +#endif + } + + return 1; +} + +/* The guts of png_image_finish_read as a png_safe_execute callback. */ +static int +png_image_read_direct(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + + png_uint_32 format = image->format; + int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0; + int do_local_compose = 0; + int do_local_background = 0; /* to avoid double gamma correction bug */ + int passes = 0; + + /* Add transforms to ensure the correct output format is produced then check + * that the required implementation support is there. Always expand; always + * need 8 bits minimum, no palette and expanded tRNS. + */ + png_set_expand(png_ptr); + + /* Now check the format to see if it was modified. */ + { + png_uint_32 base_format = png_image_format(png_ptr) & + ~PNG_FORMAT_FLAG_COLORMAP /* removed by png_set_expand */; + png_uint_32 change = format ^ base_format; + png_fixed_point output_gamma; + int mode; /* alpha mode */ + + /* Do this first so that we have a record if rgb to gray is happening. */ + if ((change & PNG_FORMAT_FLAG_COLOR) != 0) + { + /* gray<->color transformation required. */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0) + png_set_gray_to_rgb(png_ptr); + + else + { + /* libpng can't do both rgb to gray and + * background/pre-multiplication if there is also significant gamma + * correction, because both operations require linear colors and + * the code only supports one transform doing the gamma correction. + * Handle this by doing the pre-multiplication or background + * operation in this code, if necessary. + * + * TODO: fix this by rewriting pngrtran.c (!) + * + * For the moment (given that fixing this in pngrtran.c is an + * enormous change) 'do_local_background' is used to indicate that + * the problem exists. + */ + if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0) + do_local_background = 1/*maybe*/; + + png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, + PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); + } + + change &= ~PNG_FORMAT_FLAG_COLOR; + } + + /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise. + */ + { + png_fixed_point input_gamma_default; + + if ((base_format & PNG_FORMAT_FLAG_LINEAR) != 0 && + (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) + input_gamma_default = PNG_GAMMA_LINEAR; + else + input_gamma_default = PNG_DEFAULT_sRGB; + + /* Call png_set_alpha_mode to set the default for the input gamma; the + * output gamma is set by a second call below. + */ + png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, input_gamma_default); + } + + if (linear != 0) + { + /* If there *is* an alpha channel in the input it must be multiplied + * out; use PNG_ALPHA_STANDARD, otherwise just use PNG_ALPHA_PNG. + */ + if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0) + mode = PNG_ALPHA_STANDARD; /* associated alpha */ + + else + mode = PNG_ALPHA_PNG; + + output_gamma = PNG_GAMMA_LINEAR; + } + + else + { + mode = PNG_ALPHA_PNG; + output_gamma = PNG_DEFAULT_sRGB; + } + + if ((change & PNG_FORMAT_FLAG_ASSOCIATED_ALPHA) != 0) + { + mode = PNG_ALPHA_OPTIMIZED; + change &= ~PNG_FORMAT_FLAG_ASSOCIATED_ALPHA; + } + + /* If 'do_local_background' is set check for the presence of gamma + * correction; this is part of the work-round for the libpng bug + * described above. + * + * TODO: fix libpng and remove this. + */ + if (do_local_background != 0) + { + png_fixed_point gtest; + + /* This is 'png_gamma_threshold' from pngrtran.c; the test used for + * gamma correction, the screen gamma hasn't been set on png_struct + * yet; it's set below. png_struct::gamma, however, is set to the + * final value. + */ + if (png_muldiv(>est, output_gamma, png_ptr->colorspace.gamma, + PNG_FP_1) != 0 && png_gamma_significant(gtest) == 0) + do_local_background = 0; + + else if (mode == PNG_ALPHA_STANDARD) + { + do_local_background = 2/*required*/; + mode = PNG_ALPHA_PNG; /* prevent libpng doing it */ + } + + /* else leave as 1 for the checks below */ + } + + /* If the bit-depth changes then handle that here. */ + if ((change & PNG_FORMAT_FLAG_LINEAR) != 0) + { + if (linear != 0 /*16-bit output*/) + png_set_expand_16(png_ptr); + + else /* 8-bit output */ + png_set_scale_16(png_ptr); + + change &= ~PNG_FORMAT_FLAG_LINEAR; + } + + /* Now the background/alpha channel changes. */ + if ((change & PNG_FORMAT_FLAG_ALPHA) != 0) + { + /* Removing an alpha channel requires composition for the 8-bit + * formats; for the 16-bit it is already done, above, by the + * pre-multiplication and the channel just needs to be stripped. + */ + if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + /* If RGB->gray is happening the alpha channel must be left and the + * operation completed locally. + * + * TODO: fix libpng and remove this. + */ + if (do_local_background != 0) + do_local_background = 2/*required*/; + + /* 16-bit output: just remove the channel */ + else if (linear != 0) /* compose on black (well, pre-multiply) */ + png_set_strip_alpha(png_ptr); + + /* 8-bit output: do an appropriate compose */ + else if (display->background != NULL) + { + png_color_16 c; + + c.index = 0; /*unused*/ + c.red = display->background->red; + c.green = display->background->green; + c.blue = display->background->blue; + c.gray = display->background->green; + + /* This is always an 8-bit sRGB value, using the 'green' channel + * for gray is much better than calculating the luminance here; + * we can get off-by-one errors in that calculation relative to + * the app expectations and that will show up in transparent + * pixels. + */ + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + } + + else /* compose on row: implemented below. */ + { + do_local_compose = 1; + /* This leaves the alpha channel in the output, so it has to be + * removed by the code below. Set the encoding to the 'OPTIMIZE' + * one so the code only has to hack on the pixels that require + * composition. + */ + mode = PNG_ALPHA_OPTIMIZED; + } + } + + else /* output needs an alpha channel */ + { + /* This is tricky because it happens before the swap operation has + * been accomplished; however, the swap does *not* swap the added + * alpha channel (weird API), so it must be added in the correct + * place. + */ + png_uint_32 filler; /* opaque filler */ + int where; + + if (linear != 0) + filler = 65535; + + else + filler = 255; + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED + if ((format & PNG_FORMAT_FLAG_AFIRST) != 0) + { + where = PNG_FILLER_BEFORE; + change &= ~PNG_FORMAT_FLAG_AFIRST; + } + + else +#endif + where = PNG_FILLER_AFTER; + + png_set_add_alpha(png_ptr, filler, where); + } + + /* This stops the (irrelevant) call to swap_alpha below. */ + change &= ~PNG_FORMAT_FLAG_ALPHA; + } + + /* Now set the alpha mode correctly; this is always done, even if there is + * no alpha channel in either the input or the output because it correctly + * sets the output gamma. + */ + png_set_alpha_mode_fixed(png_ptr, mode, output_gamma); + +# ifdef PNG_FORMAT_BGR_SUPPORTED + if ((change & PNG_FORMAT_FLAG_BGR) != 0) + { + /* Check only the output format; PNG is never BGR; don't do this if + * the output is gray, but fix up the 'format' value in that case. + */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0) + png_set_bgr(png_ptr); + + else + format &= ~PNG_FORMAT_FLAG_BGR; + + change &= ~PNG_FORMAT_FLAG_BGR; + } +# endif + +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if ((change & PNG_FORMAT_FLAG_AFIRST) != 0) + { + /* Only relevant if there is an alpha channel - it's particularly + * important to handle this correctly because do_local_compose may + * be set above and then libpng will keep the alpha channel for this + * code to remove. + */ + if ((format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + /* Disable this if doing a local background, + * TODO: remove this when local background is no longer required. + */ + if (do_local_background != 2) + png_set_swap_alpha(png_ptr); + } + + else + format &= ~PNG_FORMAT_FLAG_AFIRST; + + change &= ~PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* If the *output* is 16-bit then we need to check for a byte-swap on this + * architecture. + */ + if (linear != 0) + { + PNG_CONST png_uint_16 le = 0x0001; + + if ((*(png_const_bytep) & le) != 0) + png_set_swap(png_ptr); + } + + /* If change is not now 0 some transformation is missing - error out. */ + if (change != 0) + png_error(png_ptr, "png_read_image: unsupported transformation"); + } + + PNG_SKIP_CHUNKS(png_ptr); + + /* Update the 'info' structure and make sure the result is as required; first + * make sure to turn on the interlace handling if it will be required + * (because it can't be turned on *after* the call to png_read_update_info!) + * + * TODO: remove the do_local_background fixup below. + */ + if (do_local_compose == 0 && do_local_background != 2) + passes = png_set_interlace_handling(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + + { + png_uint_32 info_format = 0; + + if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) + info_format |= PNG_FORMAT_FLAG_COLOR; + + if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0) + { + /* do_local_compose removes this channel below. */ + if (do_local_compose == 0) + { + /* do_local_background does the same if required. */ + if (do_local_background != 2 || + (format & PNG_FORMAT_FLAG_ALPHA) != 0) + info_format |= PNG_FORMAT_FLAG_ALPHA; + } + } + + else if (do_local_compose != 0) /* internal error */ + png_error(png_ptr, "png_image_read: alpha channel lost"); + + if ((format & PNG_FORMAT_FLAG_ASSOCIATED_ALPHA) != 0) { + info_format |= PNG_FORMAT_FLAG_ASSOCIATED_ALPHA; + } + + if (info_ptr->bit_depth == 16) + info_format |= PNG_FORMAT_FLAG_LINEAR; + +#ifdef PNG_FORMAT_BGR_SUPPORTED + if ((png_ptr->transformations & PNG_BGR) != 0) + info_format |= PNG_FORMAT_FLAG_BGR; +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED + if (do_local_background == 2) + { + if ((format & PNG_FORMAT_FLAG_AFIRST) != 0) + info_format |= PNG_FORMAT_FLAG_AFIRST; + } + + if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0 || + ((png_ptr->transformations & PNG_ADD_ALPHA) != 0 && + (png_ptr->flags & PNG_FLAG_FILLER_AFTER) == 0)) + { + if (do_local_background == 2) + png_error(png_ptr, "unexpected alpha swap transformation"); + + info_format |= PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* This is actually an internal error. */ + if (info_format != format) + png_error(png_ptr, "png_read_image: invalid transformations"); + } + + /* Now read the rows. If do_local_compose is set then it is necessary to use + * a local row buffer. The output will be GA, RGBA or BGRA and must be + * converted to G, RGB or BGR as appropriate. The 'local_row' member of the + * display acts as a flag. + */ + { + png_voidp first_row = display->buffer; + ptrdiff_t row_bytes = display->row_stride; + + if (linear != 0) + row_bytes *= 2; + + /* The following expression is designed to work correctly whether it gives + * a signed or an unsigned result. + */ + if (row_bytes < 0) + { + char *ptr = png_voidcast(char*, first_row); + ptr += (image->height-1) * (-row_bytes); + first_row = png_voidcast(png_voidp, ptr); + } + + display->first_row = first_row; + display->row_bytes = row_bytes; + } + + if (do_local_compose != 0) + { + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_composite, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + + else if (do_local_background == 2) + { + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_background, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + + else + { + png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes; + + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep row = png_voidcast(png_bytep, display->first_row); + + for (; y > 0; --y) + { + png_read_row(png_ptr, row, NULL); + row += row_bytes; + } + } + + return 1; + } +} + +int PNGAPI +png_image_finish_read(png_imagep image, png_const_colorp background, + void *buffer, png_int_32 row_stride, void *colormap) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + /* Check for row_stride overflow. This check is not performed on the + * original PNG format because it may not occur in the output PNG format + * and libpng deals with the issues of reading the original. + */ + const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format); + + /* The following checks just the 'row_stride' calculation to ensure it + * fits in a signed 32-bit value. Because channels/components can be + * either 1 or 2 bytes in size the length of a row can still overflow 32 + * bits; this is just to verify that the 'row_stride' argument can be + * represented. + */ + if (image->width <= 0x7fffffffU/channels) /* no overflow */ + { + png_uint_32 check; + const png_uint_32 png_row_stride = image->width * channels; + + if (row_stride == 0) + row_stride = (png_int_32)/*SAFE*/png_row_stride; + + if (row_stride < 0) + check = (png_uint_32)(-row_stride); + + else + check = (png_uint_32)row_stride; + + /* This verifies 'check', the absolute value of the actual stride + * passed in and detects overflow in the application calculation (i.e. + * if the app did actually pass in a non-zero 'row_stride'. + */ + if (image->opaque != NULL && buffer != NULL && check >= png_row_stride) + { + /* Now check for overflow of the image buffer calculation; this + * limits the whole image size to 32 bits for API compatibility with + * the current, 32-bit, PNG_IMAGE_BUFFER_SIZE macro. + * + * The PNG_IMAGE_BUFFER_SIZE macro is: + * + * (PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)*height*(row_stride)) + * + * And the component size is always 1 or 2, so make sure that the + * number of *bytes* that the application is saying are available + * does actually fit into a 32-bit number. + * + * NOTE: this will be changed in 1.7 because PNG_IMAGE_BUFFER_SIZE + * will be changed to use png_alloc_size_t; bigger images can be + * accomodated on 64-bit systems. + */ + if (image->height <= + 0xffffffffU/PNG_IMAGE_PIXEL_COMPONENT_SIZE(image->format)/check) + { + if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 || + (image->colormap_entries > 0 && colormap != NULL)) + { + int result; + png_image_read_control display; + + memset(&display, 0, (sizeof display)); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.colormap = colormap; + display.background = background; + display.local_row = NULL; + + /* Choose the correct 'end' routine; for the color-map case + * all the setup has already been done. + */ + if ((image->format & PNG_FORMAT_FLAG_COLORMAP) != 0) + result = + png_safe_execute(image, + png_image_read_colormap, &display) && + png_safe_execute(image, + png_image_read_colormapped, &display); + + else + result = + png_safe_execute(image, + png_image_read_direct, &display); + + png_image_free(image); + return result; + } + + else + return png_image_error(image, + "png_image_finish_read[color-map]: no color-map"); + } + + else + return png_image_error(image, + "png_image_finish_read: image too large"); + } + + else + return png_image_error(image, + "png_image_finish_read: invalid argument"); + } + + else + return png_image_error(image, + "png_image_finish_read: row_stride too large"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_finish_read: damaged PNG_IMAGE_VERSION"); + + return 0; +} + +#endif /* SIMPLIFIED_READ */ +#endif /* READ */ diff --git a/src/png/libpng/pngrio.c b/src/png/libpng/pngrio.c new file mode 100644 index 0000000000..7e26e855ca --- /dev/null +++ b/src/png/libpng/pngrio.c @@ -0,0 +1,120 @@ + +/* pngrio.c - functions for data input + * + * Last changed in libpng 1.6.24 [August 4, 2016] + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all input. Users who need + * special handling are expected to write a function that has the same + * arguments as this and performs a similar function, but that possibly + * has a different input method. Note that you shouldn't change this + * function, but rather write a replacement function and then make + * libpng use it at run time with png_set_read_fn(...). + */ + +#include "pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +/* Read the data from whatever input you are using. The default routine + * reads from a file pointer. Note that this routine sometimes gets called + * with very small lengths, so you should implement some kind of simple + * buffering if you are using unbuffered reads. This should never be asked + * to read more than 64K on a 16-bit machine. + */ +void /* PRIVATE */ +png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length) +{ + png_debug1(4, "reading %d bytes", (int)length); + + if (png_ptr->read_data_fn != NULL) + (*(png_ptr->read_data_fn))(png_ptr, data, length); + + else + png_error(png_ptr, "Call to NULL read function"); +} + +#ifdef PNG_STDIO_SUPPORTED +/* This is the function that does the actual reading of data. If you are + * not reading from a standard C stream, you should create a replacement + * read_data function and use it at run time with png_set_read_fn(), rather + * than changing the library. + */ +void PNGCBAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + if (png_ptr == NULL) + return; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr)); + + if (check != length) + png_error(png_ptr, "Read Error"); +} +#endif + +/* This function allows the application to supply a new input function + * for libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * + * png_ptr - pointer to a png input data structure + * + * io_ptr - pointer to user supplied structure containing info about + * the input functions. May be NULL. + * + * read_data_fn - pointer to a new input function that takes as its + * arguments a pointer to a png_struct, a pointer to + * a location where input data can be stored, and a 32-bit + * unsigned int that is the number of bytes to be read. + * To exit and output any fatal error messages the new write + * function should call png_error(png_ptr, "Error msg"). + * May be NULL, in which case libpng's default function will + * be used. + */ +void PNGAPI +png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = io_ptr; + +#ifdef PNG_STDIO_SUPPORTED + if (read_data_fn != NULL) + png_ptr->read_data_fn = read_data_fn; + + else + png_ptr->read_data_fn = png_default_read_data; +#else + png_ptr->read_data_fn = read_data_fn; +#endif + +#ifdef PNG_WRITE_SUPPORTED + /* It is an error to write to a read device */ + if (png_ptr->write_data_fn != NULL) + { + png_ptr->write_data_fn = NULL; + png_warning(png_ptr, + "Can't set both read_data_fn and write_data_fn in the" + " same structure"); + } +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->output_flush_fn = NULL; +#endif +} +#endif /* READ */ diff --git a/src/png/libpng/pngrtran.c b/src/png/libpng/pngrtran.c new file mode 100644 index 0000000000..c189650313 --- /dev/null +++ b/src/png/libpng/pngrtran.c @@ -0,0 +1,5010 @@ + +/* pngrtran.c - transforms the data in a row for PNG readers + * + * Last changed in libpng 1.6.33 [September 28, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains functions optionally called by an application + * in order to tell libpng how to handle data when reading a PNG. + * Transformations that are used in both reading and writing are + * in pngtrans.c. + */ + +#include "pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +/* Set the action on getting a CRC error for an ancillary or critical chunk. */ +void PNGAPI +png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action) +{ + png_debug(1, "in png_set_crc_action"); + + if (png_ptr == NULL) + return; + + /* Tell libpng how we react to CRC errors in critical chunks */ + switch (crit_action) + { + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ + break; + + case PNG_CRC_WARN_USE: /* Warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; + break; + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | + PNG_FLAG_CRC_CRITICAL_IGNORE; + break; + + case PNG_CRC_WARN_DISCARD: /* Not a valid action for critical data */ + png_warning(png_ptr, + "Can't discard critical data on CRC error"); + /* FALLTHROUGH */ + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + break; + } + + /* Tell libpng how we react to CRC errors in ancillary chunks */ + switch (ancil_action) + { + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ + break; + + case PNG_CRC_WARN_USE: /* Warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; + break; + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | + PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + + case PNG_CRC_WARN_DISCARD: /* Warn/discard data */ + + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + break; + } +} + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +/* Is it OK to set a transformation now? Only if png_start_read_image or + * png_read_update_info have not been called. It is not necessary for the IHDR + * to have been read in all cases; the need_IHDR parameter allows for this + * check too. + */ +static int +png_rtran_ok(png_structrp png_ptr, int need_IHDR) +{ + if (png_ptr != NULL) + { + if ((png_ptr->flags & PNG_FLAG_ROW_INIT) != 0) + png_app_error(png_ptr, + "invalid after png_start_read_image or png_read_update_info"); + + else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_app_error(png_ptr, "invalid before the PNG header has been read"); + + else + { + /* Turn on failure to initialize correctly for all transforms. */ + png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED; + + return 1; /* Ok */ + } + } + + return 0; /* no png_error possible! */ +} +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS via a background color */ +void PNGFAPI +png_set_background_fixed(png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma) +{ + png_debug(1, "in png_set_background_fixed"); + + if (png_rtran_ok(png_ptr, 0) == 0 || background_color == NULL) + return; + + if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) + { + png_warning(png_ptr, "Application must supply a known background gamma"); + return; + } + + png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA; + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + + png_ptr->background = *background_color; + png_ptr->background_gamma = background_gamma; + png_ptr->background_gamma_type = (png_byte)(background_gamma_code); + if (need_expand != 0) + png_ptr->transformations |= PNG_BACKGROUND_EXPAND; + else + png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_background(png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) +{ + png_set_background_fixed(png_ptr, background_color, background_gamma_code, + need_expand, png_fixed(png_ptr, background_gamma, "png_set_background")); +} +# endif /* FLOATING_POINT */ +#endif /* READ_BACKGROUND */ + +/* Scale 16-bit depth files to 8-bit depth. If both of these are set then the + * one that pngrtran does first (scale) happens. This is necessary to allow the + * TRANSFORM and API behavior to be somewhat consistent, and it's simpler. + */ +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +void PNGAPI +png_set_scale_16(png_structrp png_ptr) +{ + png_debug(1, "in png_set_scale_16"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + png_ptr->transformations |= PNG_SCALE_16_TO_8; +} +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +/* Chop 16-bit depth files to 8-bit depth */ +void PNGAPI +png_set_strip_16(png_structrp png_ptr) +{ + png_debug(1, "in png_set_strip_16"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + png_ptr->transformations |= PNG_16_TO_8; +} +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +void PNGAPI +png_set_strip_alpha(png_structrp png_ptr) +{ + png_debug(1, "in png_set_strip_alpha"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + png_ptr->transformations |= PNG_STRIP_ALPHA; +} +#endif + +#if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED) +static png_fixed_point +translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma, + int is_screen) +{ + /* Check for flag values. The main reason for having the old Mac value as a + * flag is that it is pretty near impossible to work out what the correct + * value is from Apple documentation - a working Mac system is needed to + * discover the value! + */ + if (output_gamma == PNG_DEFAULT_sRGB || + output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB) + { + /* If there is no sRGB support this just sets the gamma to the standard + * sRGB value. (This is a side effect of using this function!) + */ +# ifdef PNG_READ_sRGB_SUPPORTED + png_ptr->flags |= PNG_FLAG_ASSUME_sRGB; +# else + PNG_UNUSED(png_ptr) +# endif + if (is_screen != 0) + output_gamma = PNG_GAMMA_sRGB; + else + output_gamma = PNG_GAMMA_sRGB_INVERSE; + } + + else if (output_gamma == PNG_GAMMA_MAC_18 || + output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18) + { + if (is_screen != 0) + output_gamma = PNG_GAMMA_MAC_OLD; + else + output_gamma = PNG_GAMMA_MAC_INVERSE; + } + + return output_gamma; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +static png_fixed_point +convert_gamma_value(png_structrp png_ptr, double output_gamma) +{ + /* The following silently ignores cases where fixed point (times 100,000) + * gamma values are passed to the floating point API. This is safe and it + * means the fixed point constants work just fine with the floating point + * API. The alternative would just lead to undetected errors and spurious + * bug reports. Negative values fail inside the _fixed API unless they + * correspond to the flag values. + */ + if (output_gamma > 0 && output_gamma < 128) + output_gamma *= PNG_FP_1; + + /* This preserves -1 and -2 exactly: */ + output_gamma = floor(output_gamma + .5); + + if (output_gamma > PNG_FP_MAX || output_gamma < PNG_FP_MIN) + png_fixed_error(png_ptr, "gamma value"); + + return (png_fixed_point)output_gamma; +} +# endif +#endif /* READ_ALPHA_MODE || READ_GAMMA */ + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +void PNGFAPI +png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, + png_fixed_point output_gamma) +{ + int compose = 0; + png_fixed_point file_gamma; + + png_debug(1, "in png_set_alpha_mode"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/); + + /* Validate the value to ensure it is in a reasonable range. The value + * is expected to be 1 or greater, but this range test allows for some + * viewing correction values. The intent is to weed out users of this API + * who use the inverse of the gamma value accidentally! Since some of these + * values are reasonable this may have to be changed: + * + * 1.6.x: changed from 0.07..3 to 0.01..100 (to accomodate the optimal 16-bit + * gamma of 36, and its reciprocal.) + */ + if (output_gamma < 1000 || output_gamma > 10000000) + png_error(png_ptr, "output gamma out of expected range"); + + /* The default file gamma is the inverse of the output gamma; the output + * gamma may be changed below so get the file value first: + */ + file_gamma = png_reciprocal(output_gamma); + + /* There are really 8 possibilities here, composed of any combination + * of: + * + * premultiply the color channels + * do not encode non-opaque pixels + * encode the alpha as well as the color channels + * + * The differences disappear if the input/output ('screen') gamma is 1.0, + * because then the encoding is a no-op and there is only the choice of + * premultiplying the color channels or not. + * + * png_set_alpha_mode and png_set_background interact because both use + * png_compose to do the work. Calling both is only useful when + * png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along + * with a default gamma value. Otherwise PNG_COMPOSE must not be set. + */ + switch (mode) + { + case PNG_ALPHA_PNG: /* default: png standard */ + /* No compose, but it may be set by png_set_background! */ + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + break; + + case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */ + compose = 1; + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + /* The output is linear: */ + output_gamma = PNG_FP_1; + break; + + case PNG_ALPHA_OPTIMIZED: /* associated, non-opaque pixels linear */ + compose = 1; + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA; + /* output_gamma records the encoding of opaque pixels! */ + break; + + case PNG_ALPHA_BROKEN: /* associated, non-linear, alpha encoded */ + compose = 1; + png_ptr->transformations |= PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + break; + + default: + png_error(png_ptr, "invalid alpha mode"); + } + + /* Only set the default gamma if the file gamma has not been set (this has + * the side effect that the gamma in a second call to png_set_alpha_mode will + * be ignored.) + */ + if (png_ptr->colorspace.gamma == 0) + { + png_ptr->colorspace.gamma = file_gamma; + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } + + /* But always set the output gamma: */ + png_ptr->screen_gamma = output_gamma; + + /* Finally, if pre-multiplying, set the background fields to achieve the + * desired result. + */ + if (compose != 0) + { + /* And obtain alpha pre-multiplication by composing on black: */ + memset(&png_ptr->background, 0, (sizeof png_ptr->background)); + png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */ + png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE; + png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; + + if ((png_ptr->transformations & PNG_COMPOSE) != 0) + png_error(png_ptr, + "conflicting calls to set alpha mode and background"); + + png_ptr->transformations |= PNG_COMPOSE; + } +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma) +{ + png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr, + output_gamma)); +} +# endif +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Dither file to 8-bit. Supply a palette, the current number + * of elements in the palette, the maximum number of elements + * allowed, and a histogram if possible. If the current number + * of colors is greater than the maximum number, the palette will be + * modified to fit in the maximum number. "full_quantize" indicates + * whether we need a quantizing cube set up for RGB images, or if we + * simply are reducing the number of colors in a paletted image. + */ + +typedef struct png_dsort_struct +{ + struct png_dsort_struct * next; + png_byte left; + png_byte right; +} png_dsort; +typedef png_dsort * png_dsortp; +typedef png_dsort * * png_dsortpp; + +void PNGAPI +png_set_quantize(png_structrp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_const_uint_16p histogram, + int full_quantize) +{ + png_debug(1, "in png_set_quantize"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + png_ptr->transformations |= PNG_QUANTIZE; + + if (full_quantize == 0) + { + int i; + + png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, + (png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte)))); + for (i = 0; i < num_palette; i++) + png_ptr->quantize_index[i] = (png_byte)i; + } + + if (num_palette > maximum_colors) + { + if (histogram != NULL) + { + /* This is easy enough, just throw out the least used colors. + * Perhaps not the best solution, but good enough. + */ + + int i; + + /* Initialize an array to sort colors */ + png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, + (png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte)))); + + /* Initialize the quantize_sort array */ + for (i = 0; i < num_palette; i++) + png_ptr->quantize_sort[i] = (png_byte)i; + + /* Find the least used palette entries by starting a + * bubble sort, and running it until we have sorted + * out enough colors. Note that we don't care about + * sorting all the colors, just finding which are + * least used. + */ + + for (i = num_palette - 1; i >= maximum_colors; i--) + { + int done; /* To stop early if the list is pre-sorted */ + int j; + + done = 1; + for (j = 0; j < i; j++) + { + if (histogram[png_ptr->quantize_sort[j]] + < histogram[png_ptr->quantize_sort[j + 1]]) + { + png_byte t; + + t = png_ptr->quantize_sort[j]; + png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; + png_ptr->quantize_sort[j + 1] = t; + done = 0; + } + } + + if (done != 0) + break; + } + + /* Swap the palette around, and set up a table, if necessary */ + if (full_quantize != 0) + { + int j = num_palette; + + /* Put all the useful colors within the max, but don't + * move the others. + */ + for (i = 0; i < maximum_colors; i++) + { + if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + { + do + j--; + while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + + palette[i] = palette[j]; + } + } + } + else + { + int j = num_palette; + + /* Move all the used colors inside the max limit, and + * develop a translation table. + */ + for (i = 0; i < maximum_colors; i++) + { + /* Only move the colors we need to */ + if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + { + png_color tmp_color; + + do + j--; + while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + + tmp_color = palette[j]; + palette[j] = palette[i]; + palette[i] = tmp_color; + /* Indicate where the color went */ + png_ptr->quantize_index[j] = (png_byte)i; + png_ptr->quantize_index[i] = (png_byte)j; + } + } + + /* Find closest color for those colors we are not using */ + for (i = 0; i < num_palette; i++) + { + if ((int)png_ptr->quantize_index[i] >= maximum_colors) + { + int min_d, k, min_k, d_index; + + /* Find the closest color to one we threw out */ + d_index = png_ptr->quantize_index[i]; + min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); + for (k = 1, min_k = 0; k < maximum_colors; k++) + { + int d; + + d = PNG_COLOR_DIST(palette[d_index], palette[k]); + + if (d < min_d) + { + min_d = d; + min_k = k; + } + } + /* Point to closest color */ + png_ptr->quantize_index[i] = (png_byte)min_k; + } + } + } + png_free(png_ptr, png_ptr->quantize_sort); + png_ptr->quantize_sort = NULL; + } + else + { + /* This is much harder to do simply (and quickly). Perhaps + * we need to go through a median cut routine, but those + * don't always behave themselves with only a few colors + * as input. So we will just find the closest two colors, + * and throw out one of them (chosen somewhat randomly). + * [We don't understand this at all, so if someone wants to + * work on improving it, be our guest - AED, GRP] + */ + int i; + int max_d; + int num_new_palette; + png_dsortp t; + png_dsortpp hash; + + t = NULL; + + /* Initialize palette index arrays */ + png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, + (png_alloc_size_t)((png_uint_32)num_palette * + (sizeof (png_byte)))); + png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, + (png_alloc_size_t)((png_uint_32)num_palette * + (sizeof (png_byte)))); + + /* Initialize the sort array */ + for (i = 0; i < num_palette; i++) + { + png_ptr->index_to_palette[i] = (png_byte)i; + png_ptr->palette_to_index[i] = (png_byte)i; + } + + hash = (png_dsortpp)png_calloc(png_ptr, (png_alloc_size_t)(769 * + (sizeof (png_dsortp)))); + + num_new_palette = num_palette; + + /* Initial wild guess at how far apart the farthest pixel + * pair we will be eliminating will be. Larger + * numbers mean more areas will be allocated, Smaller + * numbers run the risk of not saving enough data, and + * having to do this all over again. + * + * I have not done extensive checking on this number. + */ + max_d = 96; + + while (num_new_palette > maximum_colors) + { + for (i = 0; i < num_new_palette - 1; i++) + { + int j; + + for (j = i + 1; j < num_new_palette; j++) + { + int d; + + d = PNG_COLOR_DIST(palette[i], palette[j]); + + if (d <= max_d) + { + + t = (png_dsortp)png_malloc_warn(png_ptr, + (png_alloc_size_t)(sizeof (png_dsort))); + + if (t == NULL) + break; + + t->next = hash[d]; + t->left = (png_byte)i; + t->right = (png_byte)j; + hash[d] = t; + } + } + if (t == NULL) + break; + } + + if (t != NULL) + for (i = 0; i <= max_d; i++) + { + if (hash[i] != NULL) + { + png_dsortp p; + + for (p = hash[i]; p; p = p->next) + { + if ((int)png_ptr->index_to_palette[p->left] + < num_new_palette && + (int)png_ptr->index_to_palette[p->right] + < num_new_palette) + { + int j, next_j; + + if (num_new_palette & 0x01) + { + j = p->left; + next_j = p->right; + } + else + { + j = p->right; + next_j = p->left; + } + + num_new_palette--; + palette[png_ptr->index_to_palette[j]] + = palette[num_new_palette]; + if (full_quantize == 0) + { + int k; + + for (k = 0; k < num_palette; k++) + { + if (png_ptr->quantize_index[k] == + png_ptr->index_to_palette[j]) + png_ptr->quantize_index[k] = + png_ptr->index_to_palette[next_j]; + + if ((int)png_ptr->quantize_index[k] == + num_new_palette) + png_ptr->quantize_index[k] = + png_ptr->index_to_palette[j]; + } + } + + png_ptr->index_to_palette[png_ptr->palette_to_index + [num_new_palette]] = png_ptr->index_to_palette[j]; + + png_ptr->palette_to_index[png_ptr->index_to_palette[j]] + = png_ptr->palette_to_index[num_new_palette]; + + png_ptr->index_to_palette[j] = + (png_byte)num_new_palette; + + png_ptr->palette_to_index[num_new_palette] = + (png_byte)j; + } + if (num_new_palette <= maximum_colors) + break; + } + if (num_new_palette <= maximum_colors) + break; + } + } + + for (i = 0; i < 769; i++) + { + if (hash[i] != NULL) + { + png_dsortp p = hash[i]; + while (p) + { + t = p->next; + png_free(png_ptr, p); + p = t; + } + } + hash[i] = 0; + } + max_d += 96; + } + png_free(png_ptr, hash); + png_free(png_ptr, png_ptr->palette_to_index); + png_free(png_ptr, png_ptr->index_to_palette); + png_ptr->palette_to_index = NULL; + png_ptr->index_to_palette = NULL; + } + num_palette = maximum_colors; + } + if (png_ptr->palette == NULL) + { + png_ptr->palette = palette; + } + png_ptr->num_palette = (png_uint_16)num_palette; + + if (full_quantize != 0) + { + int i; + png_bytep distance; + int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS + + PNG_QUANTIZE_BLUE_BITS; + int num_red = (1 << PNG_QUANTIZE_RED_BITS); + int num_green = (1 << PNG_QUANTIZE_GREEN_BITS); + int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS); + png_size_t num_entries = ((png_size_t)1 << total_bits); + + png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr, + (png_alloc_size_t)(num_entries * (sizeof (png_byte)))); + + distance = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)(num_entries * + (sizeof (png_byte)))); + + memset(distance, 0xff, num_entries * (sizeof (png_byte))); + + for (i = 0; i < num_palette; i++) + { + int ir, ig, ib; + int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS)); + + for (ir = 0; ir < num_red; ir++) + { + /* int dr = abs(ir - r); */ + int dr = ((ir > r) ? ir - r : r - ir); + int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS + + PNG_QUANTIZE_GREEN_BITS)); + + for (ig = 0; ig < num_green; ig++) + { + /* int dg = abs(ig - g); */ + int dg = ((ig > g) ? ig - g : g - ig); + int dt = dr + dg; + int dm = ((dr > dg) ? dr : dg); + int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS); + + for (ib = 0; ib < num_blue; ib++) + { + int d_index = index_g | ib; + /* int db = abs(ib - b); */ + int db = ((ib > b) ? ib - b : b - ib); + int dmax = ((dm > db) ? dm : db); + int d = dmax + dt + db; + + if (d < (int)distance[d_index]) + { + distance[d_index] = (png_byte)d; + png_ptr->palette_lookup[d_index] = (png_byte)i; + } + } + } + } + } + + png_free(png_ptr, distance); + } +} +#endif /* READ_QUANTIZE */ + +#ifdef PNG_READ_GAMMA_SUPPORTED +void PNGFAPI +png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, + png_fixed_point file_gamma) +{ + png_debug(1, "in png_set_gamma_fixed"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + /* New in libpng-1.5.4 - reserve particular negative values as flags. */ + scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/); + file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/); + + /* Checking the gamma values for being >0 was added in 1.5.4 along with the + * premultiplied alpha support; this actually hides an undocumented feature + * of the previous implementation which allowed gamma processing to be + * disabled in background handling. There is no evidence (so far) that this + * was being used; however, png_set_background itself accepted and must still + * accept '0' for the gamma value it takes, because it isn't always used. + * + * Since this is an API change (albeit a very minor one that removes an + * undocumented API feature) the following checks were only enabled in + * libpng-1.6.0. + */ + if (file_gamma <= 0) + png_error(png_ptr, "invalid file gamma in png_set_gamma"); + + if (scrn_gamma <= 0) + png_error(png_ptr, "invalid screen gamma in png_set_gamma"); + + /* Set the gamma values unconditionally - this overrides the value in the PNG + * file if a gAMA chunk was present. png_set_alpha_mode provides a + * different, easier, way to default the file gamma. + */ + png_ptr->colorspace.gamma = file_gamma; + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + png_ptr->screen_gamma = scrn_gamma; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma) +{ + png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma), + convert_gamma_value(png_ptr, file_gamma)); +} +# endif /* FLOATING_POINT */ +#endif /* READ_GAMMA */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand paletted images to RGB, expand grayscale images of + * less than 8-bit depth to 8-bit depth, and expand tRNS chunks + * to alpha channels. + */ +void PNGAPI +png_set_expand(png_structrp png_ptr) +{ + png_debug(1, "in png_set_expand"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} + +/* GRR 19990627: the following three functions currently are identical + * to png_set_expand(). However, it is entirely reasonable that someone + * might wish to expand an indexed image to RGB but *not* expand a single, + * fully transparent palette entry to a full alpha channel--perhaps instead + * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace + * the transparent color with a particular RGB value, or drop tRNS entirely. + * IOW, a future version of the library may make the transformations flag + * a bit more fine-grained, with separate bits for each of these three + * functions. + * + * More to the point, these functions make it obvious what libpng will be + * doing, whereas "expand" can (and does) mean any number of things. + * + * GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified + * to expand only the sample depth but not to expand the tRNS to alpha + * and its name was changed to png_set_expand_gray_1_2_4_to_8(). + */ + +/* Expand paletted images to RGB. */ +void PNGAPI +png_set_palette_to_rgb(png_structrp png_ptr) +{ + png_debug(1, "in png_set_palette_to_rgb"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} + +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +void PNGAPI +png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr) +{ + png_debug(1, "in png_set_expand_gray_1_2_4_to_8"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + png_ptr->transformations |= PNG_EXPAND; +} + +/* Expand tRNS chunks to alpha channels. */ +void PNGAPI +png_set_tRNS_to_alpha(png_structrp png_ptr) +{ + png_debug(1, "in png_set_tRNS_to_alpha"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} +#endif /* READ_EXPAND */ + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise + * it may not work correctly.) + */ +void PNGAPI +png_set_expand_16(png_structrp png_ptr) +{ + png_debug(1, "in png_set_expand_16"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS); +} +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +void PNGAPI +png_set_gray_to_rgb(png_structrp png_ptr) +{ + png_debug(1, "in png_set_gray_to_rgb"); + + if (png_rtran_ok(png_ptr, 0) == 0) + return; + + /* Because rgb must be 8 bits or more: */ + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_ptr->transformations |= PNG_GRAY_TO_RGB; +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +void PNGFAPI +png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) +{ + png_debug(1, "in png_set_rgb_to_gray"); + + /* Need the IHDR here because of the check on color_type below. */ + /* TODO: fix this */ + if (png_rtran_ok(png_ptr, 1) == 0) + return; + + switch (error_action) + { + case PNG_ERROR_ACTION_NONE: + png_ptr->transformations |= PNG_RGB_TO_GRAY; + break; + + case PNG_ERROR_ACTION_WARN: + png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + break; + + case PNG_ERROR_ACTION_ERROR: + png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + break; + + default: + png_error(png_ptr, "invalid error action to rgb_to_gray"); + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#ifdef PNG_READ_EXPAND_SUPPORTED + png_ptr->transformations |= PNG_EXPAND; +#else + { + /* Make this an error in 1.6 because otherwise the application may assume + * that it just worked and get a memory overwrite. + */ + png_error(png_ptr, + "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED"); + + /* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */ + } +#endif + { + if (red >= 0 && green >= 0 && red + green <= PNG_FP_1) + { + png_uint_16 red_int, green_int; + + /* NOTE: this calculation does not round, but this behavior is retained + * for consistency; the inaccuracy is very small. The code here always + * overwrites the coefficients, regardless of whether they have been + * defaulted or set already. + */ + red_int = (png_uint_16)(((png_uint_32)red*32768)/100000); + green_int = (png_uint_16)(((png_uint_32)green*32768)/100000); + + png_ptr->rgb_to_gray_red_coeff = red_int; + png_ptr->rgb_to_gray_green_coeff = green_int; + png_ptr->rgb_to_gray_coefficients_set = 1; + } + + else + { + if (red >= 0 && green >= 0) + png_app_warning(png_ptr, + "ignoring out of range rgb_to_gray coefficients"); + + /* Use the defaults, from the cHRM chunk if set, else the historical + * values which are close to the sRGB/HDTV/ITU-Rec 709 values. See + * png_do_rgb_to_gray for more discussion of the values. In this case + * the coefficients are not marked as 'set' and are not overwritten if + * something has already provided a default. + */ + if (png_ptr->rgb_to_gray_red_coeff == 0 && + png_ptr->rgb_to_gray_green_coeff == 0) + { + png_ptr->rgb_to_gray_red_coeff = 6968; + png_ptr->rgb_to_gray_green_coeff = 23434; + /* png_ptr->rgb_to_gray_blue_coeff = 2366; */ + } + } + } +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +/* Convert a RGB image to a grayscale of the same width. This allows us, + * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image. + */ + +void PNGAPI +png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red, + double green) +{ + png_set_rgb_to_gray_fixed(png_ptr, error_action, + png_fixed(png_ptr, red, "rgb to gray red coefficient"), + png_fixed(png_ptr, green, "rgb to gray green coefficient")); +} +#endif /* FLOATING POINT */ + +#endif /* RGB_TO_GRAY */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr + read_user_transform_fn) +{ + png_debug(1, "in png_set_read_user_transform_fn"); + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->read_user_transform_fn = read_user_transform_fn; +#endif +} +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +#ifdef PNG_READ_GAMMA_SUPPORTED +/* In the case of gamma transformations only do transformations on images where + * the [file] gamma and screen_gamma are not close reciprocals, otherwise it + * slows things down slightly, and also needlessly introduces small errors. + */ +static int /* PRIVATE */ +png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma) +{ + /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma + * correction as a difference of the overall transform from 1.0 + * + * We want to compare the threshold with s*f - 1, if we get + * overflow here it is because of wacky gamma values so we + * turn on processing anyway. + */ + png_fixed_point gtest; + return !png_muldiv(>est, screen_gamma, file_gamma, PNG_FP_1) || + png_gamma_significant(gtest); +} +#endif + +/* Initialize everything needed for the read. This includes modifying + * the palette. + */ + +/* For the moment 'png_init_palette_transformations' and + * 'png_init_rgb_transformations' only do some flag canceling optimizations. + * The intent is that these two routines should have palette or rgb operations + * extracted from 'png_init_read_transformations'. + */ +static void /* PRIVATE */ +png_init_palette_transformations(png_structrp png_ptr) +{ + /* Called to handle the (input) palette case. In png_do_read_transformations + * the first step is to expand the palette if requested, so this code must + * take care to only make changes that are invariant with respect to the + * palette expansion, or only do them if there is no expansion. + * + * STRIP_ALPHA has already been handled in the caller (by setting num_trans + * to 0.) + */ + int input_has_alpha = 0; + int input_has_transparency = 0; + + if (png_ptr->num_trans > 0) + { + int i; + + /* Ignore if all the entries are opaque (unlikely!) */ + for (i=0; inum_trans; ++i) + { + if (png_ptr->trans_alpha[i] == 255) + continue; + else if (png_ptr->trans_alpha[i] == 0) + input_has_transparency = 1; + else + { + input_has_transparency = 1; + input_has_alpha = 1; + break; + } + } + } + + /* If no alpha we can optimize. */ + if (input_has_alpha == 0) + { + /* Any alpha means background and associative alpha processing is + * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA + * and ENCODE_ALPHA are irrelevant. + */ + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + + if (input_has_transparency == 0) + png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND); + } + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + /* png_set_background handling - deals with the complexity of whether the + * background color is in the file format or the screen format in the case + * where an 'expand' will happen. + */ + + /* The following code cannot be entered in the alpha pre-multiplication case + * because PNG_BACKGROUND_EXPAND is cancelled below. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0 && + (png_ptr->transformations & PNG_EXPAND) != 0) + { + { + png_ptr->background.red = + png_ptr->palette[png_ptr->background.index].red; + png_ptr->background.green = + png_ptr->palette[png_ptr->background.index].green; + png_ptr->background.blue = + png_ptr->palette[png_ptr->background.index].blue; + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0) + { + if ((png_ptr->transformations & PNG_EXPAND_tRNS) == 0) + { + /* Invert the alpha channel (in tRNS) unless the pixels are + * going to be expanded, in which case leave it for later + */ + int i, istop = png_ptr->num_trans; + + for (i=0; itrans_alpha[i] = (png_byte)(255 - + png_ptr->trans_alpha[i]); + } + } +#endif /* READ_INVERT_ALPHA */ + } + } /* background expand and (therefore) no alpha association. */ +#endif /* READ_EXPAND && READ_BACKGROUND */ +} + +static void /* PRIVATE */ +png_init_rgb_transformations(png_structrp png_ptr) +{ + /* Added to libpng-1.5.4: check the color type to determine whether there + * is any alpha or transparency in the image and simply cancel the + * background and alpha mode stuff if there isn't. + */ + int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0; + int input_has_transparency = png_ptr->num_trans > 0; + + /* If no alpha we can optimize. */ + if (input_has_alpha == 0) + { + /* Any alpha means background and associative alpha processing is + * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA + * and ENCODE_ALPHA are irrelevant. + */ +# ifdef PNG_READ_ALPHA_MODE_SUPPORTED + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; +# endif + + if (input_has_transparency == 0) + png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND); + } + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + /* png_set_background handling - deals with the complexity of whether the + * background color is in the file format or the screen format in the case + * where an 'expand' will happen. + */ + + /* The following code cannot be entered in the alpha pre-multiplication case + * because PNG_BACKGROUND_EXPAND is cancelled below. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0 && + (png_ptr->transformations & PNG_EXPAND) != 0 && + (png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) + /* i.e., GRAY or GRAY_ALPHA */ + { + { + /* Expand background and tRNS chunks */ + int gray = png_ptr->background.gray; + int trans_gray = png_ptr->trans_color.gray; + + switch (png_ptr->bit_depth) + { + case 1: + gray *= 0xff; + trans_gray *= 0xff; + break; + + case 2: + gray *= 0x55; + trans_gray *= 0x55; + break; + + case 4: + gray *= 0x11; + trans_gray *= 0x11; + break; + + default: + + case 8: + /* FALLTHROUGH */ /* (Already 8 bits) */ + + case 16: + /* Already a full 16 bits */ + break; + } + + png_ptr->background.red = png_ptr->background.green = + png_ptr->background.blue = (png_uint_16)gray; + + if ((png_ptr->transformations & PNG_EXPAND_tRNS) == 0) + { + png_ptr->trans_color.red = png_ptr->trans_color.green = + png_ptr->trans_color.blue = (png_uint_16)trans_gray; + } + } + } /* background expand and (therefore) no alpha association. */ +#endif /* READ_EXPAND && READ_BACKGROUND */ +} + +void /* PRIVATE */ +png_init_read_transformations(png_structrp png_ptr) +{ + png_debug(1, "in png_init_read_transformations"); + + /* This internal function is called from png_read_start_row in pngrutil.c + * and it is called before the 'rowbytes' calculation is done, so the code + * in here can change or update the transformations flags. + * + * First do updates that do not depend on the details of the PNG image data + * being processed. + */ + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds + * png_set_alpha_mode and this is another source for a default file gamma so + * the test needs to be performed later - here. In addition prior to 1.5.4 + * the tests were repeated for the PALETTE color type here - this is no + * longer necessary (and doesn't seem to have been necessary before.) + */ + { + /* The following temporary indicates if overall gamma correction is + * required. + */ + int gamma_correction = 0; + + if (png_ptr->colorspace.gamma != 0) /* has been set */ + { + if (png_ptr->screen_gamma != 0) /* screen set too */ + gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma, + png_ptr->screen_gamma); + + else + /* Assume the output matches the input; a long time default behavior + * of libpng, although the standard has nothing to say about this. + */ + png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma); + } + + else if (png_ptr->screen_gamma != 0) + /* The converse - assume the file matches the screen, note that this + * perhaps undesireable default can (from 1.5.4) be changed by calling + * png_set_alpha_mode (even if the alpha handling mode isn't required + * or isn't changed from the default.) + */ + png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma); + + else /* neither are set */ + /* Just in case the following prevents any processing - file and screen + * are both assumed to be linear and there is no way to introduce a + * third gamma value other than png_set_background with 'UNIQUE', and, + * prior to 1.5.4 + */ + png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1; + + /* We have a gamma value now. */ + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + + /* Now turn the gamma transformation on or off as appropriate. Notice + * that PNG_GAMMA just refers to the file->screen correction. Alpha + * composition may independently cause gamma correction because it needs + * linear data (e.g. if the file has a gAMA chunk but the screen gamma + * hasn't been specified.) In any case this flag may get turned off in + * the code immediately below if the transform can be handled outside the + * row loop. + */ + if (gamma_correction != 0) + png_ptr->transformations |= PNG_GAMMA; + + else + png_ptr->transformations &= ~PNG_GAMMA; + } +#endif + + /* Certain transformations have the effect of preventing other + * transformations that happen afterward in png_do_read_transformations; + * resolve the interdependencies here. From the code of + * png_do_read_transformations the order is: + * + * 1) PNG_EXPAND (including PNG_EXPAND_tRNS) + * 2) PNG_STRIP_ALPHA (if no compose) + * 3) PNG_RGB_TO_GRAY + * 4) PNG_GRAY_TO_RGB iff !PNG_BACKGROUND_IS_GRAY + * 5) PNG_COMPOSE + * 6) PNG_GAMMA + * 7) PNG_STRIP_ALPHA (if compose) + * 8) PNG_ENCODE_ALPHA + * 9) PNG_SCALE_16_TO_8 + * 10) PNG_16_TO_8 + * 11) PNG_QUANTIZE (converts to palette) + * 12) PNG_EXPAND_16 + * 13) PNG_GRAY_TO_RGB iff PNG_BACKGROUND_IS_GRAY + * 14) PNG_INVERT_MONO + * 15) PNG_INVERT_ALPHA + * 16) PNG_SHIFT + * 17) PNG_PACK + * 18) PNG_BGR + * 19) PNG_PACKSWAP + * 20) PNG_FILLER (includes PNG_ADD_ALPHA) + * 21) PNG_SWAP_ALPHA + * 22) PNG_SWAP_BYTES + * 23) PNG_USER_TRANSFORM [must be last] + */ +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 && + (png_ptr->transformations & PNG_COMPOSE) == 0) + { + /* Stripping the alpha channel happens immediately after the 'expand' + * transformations, before all other transformation, so it cancels out + * the alpha handling. It has the side effect negating the effect of + * PNG_EXPAND_tRNS too: + */ + png_ptr->transformations &= ~(PNG_BACKGROUND_EXPAND | PNG_ENCODE_ALPHA | + PNG_EXPAND_tRNS); + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + + /* Kill the tRNS chunk itself too. Prior to 1.5.4 this did not happen + * so transparency information would remain just so long as it wasn't + * expanded. This produces unexpected API changes if the set of things + * that do PNG_EXPAND_tRNS changes (perfectly possible given the + * documentation - which says ask for what you want, accept what you + * get.) This makes the behavior consistent from 1.5.4: + */ + png_ptr->num_trans = 0; + } +#endif /* STRIP_ALPHA supported, no COMPOSE */ + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED + /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA + * settings will have no effect. + */ + if (png_gamma_significant(png_ptr->screen_gamma) == 0) + { + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + } +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* Make sure the coefficients for the rgb to gray conversion are set + * appropriately. + */ + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) + png_colorspace_set_rgb_coefficients(png_ptr); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + /* Detect gray background and attempt to enable optimization for + * gray --> RGB case. + * + * Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or + * RGB_ALPHA (in which case need_expand is superfluous anyway), the + * background color might actually be gray yet not be flagged as such. + * This is not a problem for the current code, which uses + * PNG_BACKGROUND_IS_GRAY only to decide when to do the + * png_do_gray_to_rgb() transformation. + * + * TODO: this code needs to be revised to avoid the complexity and + * interdependencies. The color type of the background should be recorded in + * png_set_background, along with the bit depth, then the code has a record + * of exactly what color space the background is currently in. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0) + { + /* PNG_BACKGROUND_EXPAND: the background is in the file color space, so if + * the file was grayscale the background value is gray. + */ + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + } + + else if ((png_ptr->transformations & PNG_COMPOSE) != 0) + { + /* PNG_COMPOSE: png_set_background was called with need_expand false, + * so the color is in the color space of the output or png_set_alpha_mode + * was called and the color is black. Ignore RGB_TO_GRAY because that + * happens before GRAY_TO_RGB. + */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0) + { + if (png_ptr->background.red == png_ptr->background.green && + png_ptr->background.red == png_ptr->background.blue) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + png_ptr->background.gray = png_ptr->background.red; + } + } + } +#endif /* READ_EXPAND && READ_BACKGROUND */ +#endif /* READ_GRAY_TO_RGB */ + + /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations + * can be performed directly on the palette, and some (such as rgb to gray) + * can be optimized inside the palette. This is particularly true of the + * composite (background and alpha) stuff, which can be pretty much all done + * in the palette even if the result is expanded to RGB or gray afterward. + * + * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and + * earlier and the palette stuff is actually handled on the first row. This + * leads to the reported bug that the palette returned by png_get_PLTE is not + * updated. + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_init_palette_transformations(png_ptr); + + else + png_init_rgb_transformations(png_ptr); + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_READ_EXPAND_16_SUPPORTED) + if ((png_ptr->transformations & PNG_EXPAND_16) != 0 && + (png_ptr->transformations & PNG_COMPOSE) != 0 && + (png_ptr->transformations & PNG_BACKGROUND_EXPAND) == 0 && + png_ptr->bit_depth != 16) + { + /* TODO: fix this. Because the expand_16 operation is after the compose + * handling the background color must be 8, not 16, bits deep, but the + * application will supply a 16-bit value so reduce it here. + * + * The PNG_BACKGROUND_EXPAND code above does not expand to 16 bits at + * present, so that case is ok (until do_expand_16 is moved.) + * + * NOTE: this discards the low 16 bits of the user supplied background + * color, but until expand_16 works properly there is no choice! + */ +# define CHOP(x) (x)=((png_uint_16)PNG_DIV257(x)) + CHOP(png_ptr->background.red); + CHOP(png_ptr->background.green); + CHOP(png_ptr->background.blue); + CHOP(png_ptr->background.gray); +# undef CHOP + } +#endif /* READ_BACKGROUND && READ_EXPAND_16 */ + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + (defined(PNG_READ_SCALE_16_TO_8_SUPPORTED) || \ + defined(PNG_READ_STRIP_16_TO_8_SUPPORTED)) + if ((png_ptr->transformations & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) != 0 && + (png_ptr->transformations & PNG_COMPOSE) != 0 && + (png_ptr->transformations & PNG_BACKGROUND_EXPAND) == 0 && + png_ptr->bit_depth == 16) + { + /* On the other hand, if a 16-bit file is to be reduced to 8-bits per + * component this will also happen after PNG_COMPOSE and so the background + * color must be pre-expanded here. + * + * TODO: fix this too. + */ + png_ptr->background.red = (png_uint_16)(png_ptr->background.red * 257); + png_ptr->background.green = + (png_uint_16)(png_ptr->background.green * 257); + png_ptr->background.blue = (png_uint_16)(png_ptr->background.blue * 257); + png_ptr->background.gray = (png_uint_16)(png_ptr->background.gray * 257); + } +#endif + + /* NOTE: below 'PNG_READ_ALPHA_MODE_SUPPORTED' is presumed to also enable the + * background support (see the comments in scripts/pnglibconf.dfa), this + * allows pre-multiplication of the alpha channel to be implemented as + * compositing on black. This is probably sub-optimal and has been done in + * 1.5.4 betas simply to enable external critique and testing (i.e. to + * implement the new API quickly, without lots of internal changes.) + */ + +#ifdef PNG_READ_GAMMA_SUPPORTED +# ifdef PNG_READ_BACKGROUND_SUPPORTED + /* Includes ALPHA_MODE */ + png_ptr->background_1 = png_ptr->background; +# endif + + /* This needs to change - in the palette image case a whole set of tables are + * built when it would be quicker to just calculate the correct value for + * each palette entry directly. Also, the test is too tricky - why check + * PNG_RGB_TO_GRAY if PNG_GAMMA is not set? The answer seems to be that + * PNG_GAMMA is cancelled even if the gamma is known? The test excludes the + * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction + * the gamma tables will not be built even if composition is required on a + * gamma encoded value. + * + * In 1.5.4 this is addressed below by an additional check on the individual + * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the + * tables. + */ + if ((png_ptr->transformations & PNG_GAMMA) != 0 || + ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0 && + (png_gamma_significant(png_ptr->colorspace.gamma) != 0 || + png_gamma_significant(png_ptr->screen_gamma) != 0)) || + ((png_ptr->transformations & PNG_COMPOSE) != 0 && + (png_gamma_significant(png_ptr->colorspace.gamma) != 0 || + png_gamma_significant(png_ptr->screen_gamma) != 0 +# ifdef PNG_READ_BACKGROUND_SUPPORTED + || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE && + png_gamma_significant(png_ptr->background_gamma) != 0) +# endif + )) || ((png_ptr->transformations & PNG_ENCODE_ALPHA) != 0 && + png_gamma_significant(png_ptr->screen_gamma) != 0)) + { + png_build_gamma_table(png_ptr, png_ptr->bit_depth); + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if ((png_ptr->transformations & PNG_COMPOSE) != 0) + { + /* Issue a warning about this combination: because RGB_TO_GRAY is + * optimized to do the gamma transform if present yet do_background has + * to do the same thing if both options are set a + * double-gamma-correction happens. This is true in all versions of + * libpng to date. + */ + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) + png_warning(png_ptr, + "libpng does not support gamma+background+rgb_to_gray"); + + if ((png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) != 0) + { + /* We don't get to here unless there is a tRNS chunk with non-opaque + * entries - see the checking code at the start of this function. + */ + png_color back, back_1; + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + png_fixed_point g, gs; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = PNG_FP_1; + break; + + case PNG_BACKGROUND_GAMMA_FILE: + g = png_reciprocal(png_ptr->colorspace.gamma); + gs = png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma); + break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = png_reciprocal(png_ptr->background_gamma); + gs = png_reciprocal2(png_ptr->background_gamma, + png_ptr->screen_gamma); + break; + default: + g = PNG_FP_1; /* back_1 */ + gs = PNG_FP_1; /* back */ + break; + } + + if (png_gamma_significant(gs) != 0) + { + back.red = png_gamma_8bit_correct(png_ptr->background.red, + gs); + back.green = png_gamma_8bit_correct(png_ptr->background.green, + gs); + back.blue = png_gamma_8bit_correct(png_ptr->background.blue, + gs); + } + + else + { + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + } + + if (png_gamma_significant(g) != 0) + { + back_1.red = png_gamma_8bit_correct(png_ptr->background.red, + g); + back_1.green = png_gamma_8bit_correct( + png_ptr->background.green, g); + back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue, + g); + } + + else + { + back_1.red = (png_byte)png_ptr->background.red; + back_1.green = (png_byte)png_ptr->background.green; + back_1.blue = (png_byte)png_ptr->background.blue; + } + } + + for (i = 0; i < num_palette; i++) + { + if (i < (int)png_ptr->num_trans && + png_ptr->trans_alpha[i] != 0xff) + { + if (png_ptr->trans_alpha[i] == 0) + { + palette[i] = back; + } + else /* if (png_ptr->trans_alpha[i] != 0xff) */ + { + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, png_ptr->trans_alpha[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, png_ptr->trans_alpha[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + + /* Prevent the transformations being done again. + * + * NOTE: this is highly dubious; it removes the transformations in + * place. This seems inconsistent with the general treatment of the + * transformations elsewhere. + */ + png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA); + } /* color_type == PNG_COLOR_TYPE_PALETTE */ + + /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ + else /* color_type != PNG_COLOR_TYPE_PALETTE */ + { + int gs_sig, g_sig; + png_fixed_point g = PNG_FP_1; /* Correction to linear */ + png_fixed_point gs = PNG_FP_1; /* Correction to screen */ + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = png_ptr->screen_gamma; + /* gs = PNG_FP_1; */ + break; + + case PNG_BACKGROUND_GAMMA_FILE: + g = png_reciprocal(png_ptr->colorspace.gamma); + gs = png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma); + break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = png_reciprocal(png_ptr->background_gamma); + gs = png_reciprocal2(png_ptr->background_gamma, + png_ptr->screen_gamma); + break; + + default: + png_error(png_ptr, "invalid background gamma type"); + } + + g_sig = png_gamma_significant(g); + gs_sig = png_gamma_significant(gs); + + if (g_sig != 0) + png_ptr->background_1.gray = png_gamma_correct(png_ptr, + png_ptr->background.gray, g); + + if (gs_sig != 0) + png_ptr->background.gray = png_gamma_correct(png_ptr, + png_ptr->background.gray, gs); + + if ((png_ptr->background.red != png_ptr->background.green) || + (png_ptr->background.red != png_ptr->background.blue) || + (png_ptr->background.red != png_ptr->background.gray)) + { + /* RGB or RGBA with color background */ + if (g_sig != 0) + { + png_ptr->background_1.red = png_gamma_correct(png_ptr, + png_ptr->background.red, g); + + png_ptr->background_1.green = png_gamma_correct(png_ptr, + png_ptr->background.green, g); + + png_ptr->background_1.blue = png_gamma_correct(png_ptr, + png_ptr->background.blue, g); + } + + if (gs_sig != 0) + { + png_ptr->background.red = png_gamma_correct(png_ptr, + png_ptr->background.red, gs); + + png_ptr->background.green = png_gamma_correct(png_ptr, + png_ptr->background.green, gs); + + png_ptr->background.blue = png_gamma_correct(png_ptr, + png_ptr->background.blue, gs); + } + } + + else + { + /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ + png_ptr->background_1.red = png_ptr->background_1.green + = png_ptr->background_1.blue = png_ptr->background_1.gray; + + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + } + + /* The background is now in screen gamma: */ + png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_SCREEN; + } /* color_type != PNG_COLOR_TYPE_PALETTE */ + }/* png_ptr->transformations & PNG_BACKGROUND */ + + else + /* Transformation does not include PNG_BACKGROUND */ +#endif /* READ_BACKGROUND */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* RGB_TO_GRAY needs to have non-gamma-corrected values! */ + && ((png_ptr->transformations & PNG_EXPAND) == 0 || + (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) +#endif + ) + { + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + + /* NOTE: there are other transformations that should probably be in + * here too. + */ + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + + /* Done the gamma correction. */ + png_ptr->transformations &= ~PNG_GAMMA; + } /* color_type == PALETTE && !PNG_BACKGROUND transformation */ + } +#ifdef PNG_READ_BACKGROUND_SUPPORTED + else +#endif +#endif /* READ_GAMMA */ + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + /* No GAMMA transformation (see the hanging else 4 lines above) */ + if ((png_ptr->transformations & PNG_COMPOSE) != 0 && + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = (int)png_ptr->num_trans; + png_color back; + png_colorp palette = png_ptr->palette; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < istop; i++) + { + if (png_ptr->trans_alpha[i] == 0) + { + palette[i] = back; + } + + else if (png_ptr->trans_alpha[i] != 0xff) + { + /* The png_composite() macro is defined in png.h */ + png_composite(palette[i].red, palette[i].red, + png_ptr->trans_alpha[i], back.red); + + png_composite(palette[i].green, palette[i].green, + png_ptr->trans_alpha[i], back.green); + + png_composite(palette[i].blue, palette[i].blue, + png_ptr->trans_alpha[i], back.blue); + } + } + + png_ptr->transformations &= ~PNG_COMPOSE; + } +#endif /* READ_BACKGROUND */ + +#ifdef PNG_READ_SHIFT_SUPPORTED + if ((png_ptr->transformations & PNG_SHIFT) != 0 && + (png_ptr->transformations & PNG_EXPAND) == 0 && + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = png_ptr->num_palette; + int shift = 8 - png_ptr->sig_bit.red; + + png_ptr->transformations &= ~PNG_SHIFT; + + /* significant bits can be in the range 1 to 7 for a meaninful result, if + * the number of significant bits is 0 then no shift is done (this is an + * error condition which is silently ignored.) + */ + if (shift > 0 && shift < 8) + for (i=0; ipalette[i].red; + + component >>= shift; + png_ptr->palette[i].red = (png_byte)component; + } + + shift = 8 - png_ptr->sig_bit.green; + if (shift > 0 && shift < 8) + for (i=0; ipalette[i].green; + + component >>= shift; + png_ptr->palette[i].green = (png_byte)component; + } + + shift = 8 - png_ptr->sig_bit.blue; + if (shift > 0 && shift < 8) + for (i=0; ipalette[i].blue; + + component >>= shift; + png_ptr->palette[i].blue = (png_byte)component; + } + } +#endif /* READ_SHIFT */ +} + +/* Modify the info structure to reflect the transformations. The + * info should be updated so a PNG file could be written with it, + * assuming the transformations result in valid PNG data. + */ +void /* PRIVATE */ +png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr) +{ + png_debug(1, "in png_read_transform_info"); + +#ifdef PNG_READ_EXPAND_SUPPORTED + if ((png_ptr->transformations & PNG_EXPAND) != 0) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + /* This check must match what actually happens in + * png_do_expand_palette; if it ever checks the tRNS chunk to see if + * it is all opaque we must do the same (at present it does not.) + */ + if (png_ptr->num_trans > 0) + info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + + else + info_ptr->color_type = PNG_COLOR_TYPE_RGB; + + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + + if (png_ptr->palette == NULL) + png_error (png_ptr, "Palette is NULL in indexed image"); + } + else + { + if (png_ptr->num_trans != 0) + { + if ((png_ptr->transformations & PNG_EXPAND_tRNS) != 0) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + } + if (info_ptr->bit_depth < 8) + info_ptr->bit_depth = 8; + + info_ptr->num_trans = 0; + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + /* The following is almost certainly wrong unless the background value is in + * the screen space! + */ + if ((png_ptr->transformations & PNG_COMPOSE) != 0) + info_ptr->background = png_ptr->background; +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* The following used to be conditional on PNG_GAMMA (prior to 1.5.4), + * however it seems that the code in png_init_read_transformations, which has + * been called before this from png_read_update_info->png_read_start_row + * sometimes does the gamma transform and cancels the flag. + * + * TODO: this looks wrong; the info_ptr should end up with a gamma equal to + * the screen_gamma value. The following probably results in weirdness if + * the info_ptr is used by the app after the rows have been read. + */ + info_ptr->colorspace.gamma = png_ptr->colorspace.gamma; +#endif + + if (info_ptr->bit_depth == 16) + { +# ifdef PNG_READ_16BIT_SUPPORTED +# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + if ((png_ptr->transformations & PNG_SCALE_16_TO_8) != 0) + info_ptr->bit_depth = 8; +# endif + +# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + if ((png_ptr->transformations & PNG_16_TO_8) != 0) + info_ptr->bit_depth = 8; +# endif + +# else + /* No 16-bit support: force chopping 16-bit input down to 8, in this case + * the app program can chose if both APIs are available by setting the + * correct scaling to use. + */ +# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + /* For compatibility with previous versions use the strip method by + * default. This code works because if PNG_SCALE_16_TO_8 is already + * set the code below will do that in preference to the chop. + */ + png_ptr->transformations |= PNG_16_TO_8; + info_ptr->bit_depth = 8; +# else + +# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + png_ptr->transformations |= PNG_SCALE_16_TO_8; + info_ptr->bit_depth = 8; +# else + + CONFIGURATION ERROR: you must enable at least one 16 to 8 method +# endif +# endif +#endif /* !READ_16BIT */ + } + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0) + info_ptr->color_type = (png_byte)(info_ptr->color_type | + PNG_COLOR_MASK_COLOR); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) + info_ptr->color_type = (png_byte)(info_ptr->color_type & + ~PNG_COLOR_MASK_COLOR); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + if ((png_ptr->transformations & PNG_QUANTIZE) != 0) + { + if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && + png_ptr->palette_lookup != 0 && info_ptr->bit_depth == 8) + { + info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; + } + } +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED + if ((png_ptr->transformations & PNG_EXPAND_16) != 0 && + info_ptr->bit_depth == 8 && + info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + info_ptr->bit_depth = 16; + } +#endif + +#ifdef PNG_READ_PACK_SUPPORTED + if ((png_ptr->transformations & PNG_PACK) != 0 && + (info_ptr->bit_depth < 8)) + info_ptr->bit_depth = 8; +#endif + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + + else if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) + info_ptr->channels = 3; + + else + info_ptr->channels = 1; + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0) + { + info_ptr->color_type = (png_byte)(info_ptr->color_type & + ~PNG_COLOR_MASK_ALPHA); + info_ptr->num_trans = 0; + } +#endif + + if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0) + info_ptr->channels++; + +#ifdef PNG_READ_FILLER_SUPPORTED + /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ + if ((png_ptr->transformations & PNG_FILLER) != 0 && + (info_ptr->color_type == PNG_COLOR_TYPE_RGB || + info_ptr->color_type == PNG_COLOR_TYPE_GRAY)) + { + info_ptr->channels++; + /* If adding a true alpha channel not just filler */ + if ((png_ptr->transformations & PNG_ADD_ALPHA) != 0) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + } +#endif + +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ +defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0) + { + if (png_ptr->user_transform_depth != 0) + info_ptr->bit_depth = png_ptr->user_transform_depth; + + if (png_ptr->user_transform_channels != 0) + info_ptr->channels = png_ptr->user_transform_channels; + } +#endif + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * + info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width); + + /* Adding in 1.5.4: cache the above value in png_struct so that we can later + * check in png_rowbytes that the user buffer won't get overwritten. Note + * that the field is not always set - if png_read_update_info isn't called + * the application has to either not do any transforms or get the calculation + * right itself. + */ + png_ptr->info_rowbytes = info_ptr->rowbytes; + +#ifndef PNG_READ_EXPAND_SUPPORTED + if (png_ptr != NULL) + return; +#endif +} + +#ifdef PNG_READ_PACK_SUPPORTED +/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, + * without changing the actual values. Thus, if you had a row with + * a bit depth of 1, you would end up with bytes that only contained + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_do_shift() after this. + */ +static void +png_do_unpack(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_unpack"); + + if (row_info->bit_depth < 8) + { + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + switch (row_info->bit_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = 7U - ((row_width + 7U) & 0x07); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x01); + + if (shift == 7) + { + shift = 0; + sp--; + } + + else + shift++; + + dp--; + } + break; + } + + case 2: + { + + png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = ((3U - ((row_width + 3U) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x03); + + if (shift == 6) + { + shift = 0; + sp--; + } + + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = ((1U - ((row_width + 1U) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x0f); + + if (shift == 4) + { + shift = 0; + sp--; + } + + else + shift = 4; + + dp--; + } + break; + } + + default: + break; + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +static void +png_do_unshift(png_row_infop row_info, png_bytep row, + png_const_color_8p sig_bits) +{ + int color_type; + + png_debug(1, "in png_do_unshift"); + + /* The palette case has already been handled in the _init routine. */ + color_type = row_info->color_type; + + if (color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift[4]; + int channels = 0; + int bit_depth = row_info->bit_depth; + + if ((color_type & PNG_COLOR_MASK_COLOR) != 0) + { + shift[channels++] = bit_depth - sig_bits->red; + shift[channels++] = bit_depth - sig_bits->green; + shift[channels++] = bit_depth - sig_bits->blue; + } + + else + { + shift[channels++] = bit_depth - sig_bits->gray; + } + + if ((color_type & PNG_COLOR_MASK_ALPHA) != 0) + { + shift[channels++] = bit_depth - sig_bits->alpha; + } + + { + int c, have_shift; + + for (c = have_shift = 0; c < channels; ++c) + { + /* A shift of more than the bit depth is an error condition but it + * gets ignored here. + */ + if (shift[c] <= 0 || shift[c] >= bit_depth) + shift[c] = 0; + + else + have_shift = 1; + } + + if (have_shift == 0) + return; + } + + switch (bit_depth) + { + default: + /* Must be 1bpp gray: should not be here! */ + /* NOTREACHED */ + break; + + case 2: + /* Must be 2bpp gray */ + /* assert(channels == 1 && shift[0] == 1) */ + { + png_bytep bp = row; + png_bytep bp_end = bp + row_info->rowbytes; + + while (bp < bp_end) + { + int b = (*bp >> 1) & 0x55; + *bp++ = (png_byte)b; + } + break; + } + + case 4: + /* Must be 4bpp gray */ + /* assert(channels == 1) */ + { + png_bytep bp = row; + png_bytep bp_end = bp + row_info->rowbytes; + int gray_shift = shift[0]; + int mask = 0xf >> gray_shift; + + mask |= mask << 4; + + while (bp < bp_end) + { + int b = (*bp >> gray_shift) & mask; + *bp++ = (png_byte)b; + } + break; + } + + case 8: + /* Single byte components, G, GA, RGB, RGBA */ + { + png_bytep bp = row; + png_bytep bp_end = bp + row_info->rowbytes; + int channel = 0; + + while (bp < bp_end) + { + int b = *bp >> shift[channel]; + if (++channel >= channels) + channel = 0; + *bp++ = (png_byte)b; + } + break; + } + +#ifdef PNG_READ_16BIT_SUPPORTED + case 16: + /* Double byte components, G, GA, RGB, RGBA */ + { + png_bytep bp = row; + png_bytep bp_end = bp + row_info->rowbytes; + int channel = 0; + + while (bp < bp_end) + { + int value = (bp[0] << 8) + bp[1]; + + value >>= shift[channel]; + if (++channel >= channels) + channel = 0; + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)value; + } + break; + } +#endif + } + } +} +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale rows of bit depth 16 down to 8 accurately */ +static void +png_do_scale_16_to_8(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_scale_16_to_8"); + + if (row_info->bit_depth == 16) + { + png_bytep sp = row; /* source */ + png_bytep dp = row; /* destination */ + png_bytep ep = sp + row_info->rowbytes; /* end+1 */ + + while (sp < ep) + { + /* The input is an array of 16-bit components, these must be scaled to + * 8 bits each. For a 16-bit value V the required value (from the PNG + * specification) is: + * + * (V * 255) / 65535 + * + * This reduces to round(V / 257), or floor((V + 128.5)/257) + * + * Represent V as the two byte value vhi.vlo. Make a guess that the + * result is the top byte of V, vhi, then the correction to this value + * is: + * + * error = floor(((V-vhi.vhi) + 128.5) / 257) + * = floor(((vlo-vhi) + 128.5) / 257) + * + * This can be approximated using integer arithmetic (and a signed + * shift): + * + * error = (vlo-vhi+128) >> 8; + * + * The approximate differs from the exact answer only when (vlo-vhi) is + * 128; it then gives a correction of +1 when the exact correction is + * 0. This gives 128 errors. The exact answer (correct for all 16-bit + * input values) is: + * + * error = (vlo-vhi+128)*65535 >> 24; + * + * An alternative arithmetic calculation which also gives no errors is: + * + * (V * 255 + 32895) >> 16 + */ + + png_int_32 tmp = *sp++; /* must be signed! */ + tmp += (((int)*sp++ - tmp + 128) * 65535) >> 24; + *dp++ = (png_byte)tmp; + } + + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +static void +/* Simply discard the low byte. This was the default behavior prior + * to libpng-1.5.4. + */ +png_do_chop(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_chop"); + + if (row_info->bit_depth == 16) + { + png_bytep sp = row; /* source */ + png_bytep dp = row; /* destination */ + png_bytep ep = sp + row_info->rowbytes; /* end+1 */ + + while (sp < ep) + { + *dp++ = *sp; + sp += 2; /* skip low byte */ + } + + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED +static void +png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_swap_alpha"); + + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from RGBA to ARGB */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + /* This converts from RRGGBBAA to AARRGGBB */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } +#endif + } + + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from GA to AG */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + /* This converts from GGAA to AAGG */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } +#endif + } + } +} +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED +static void +png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_uint_32 row_width; + png_debug(1, "in png_do_read_invert_alpha"); + + row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This inverts the alpha channel in RGBA */ + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=3; + dp=sp; + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=6; + dp=sp; + } + } +#endif + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This inverts the alpha channel in GA */ + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = *(--sp); + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + else + { + /* This inverts the alpha channel in GGAA */ + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ + sp-=2; + dp=sp; + } + } +#endif + } +} +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED +/* Add filler channel if we have RGB color */ +static void +png_do_read_filler(png_row_infop row_info, png_bytep row, + png_uint_32 filler, png_uint_32 flags) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + +#ifdef PNG_READ_16BIT_SUPPORTED + png_byte hi_filler = (png_byte)(filler>>8); +#endif + png_byte lo_filler = (png_byte)filler; + + png_debug(1, "in png_do_read_filler"); + + if ( + row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + if ((flags & PNG_FLAG_FILLER_AFTER) != 0) + { + /* This changes the data from G to GX */ + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + + else + { + /* This changes the data from G to XG */ + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) + { + if ((flags & PNG_FLAG_FILLER_AFTER) != 0) + { + /* This changes the data from GG to GGXX */ + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = hi_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + *(--dp) = hi_filler; + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + + else + { + /* This changes the data from GG to XXGG */ + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + *(--dp) = hi_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } +#endif + } /* COLOR_TYPE == GRAY */ + else if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { + if ((flags & PNG_FLAG_FILLER_AFTER) != 0) + { + /* This changes the data from RGB to RGBX */ + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + + else + { + /* This changes the data from RGB to XRGB */ + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) + { + if ((flags & PNG_FLAG_FILLER_AFTER) != 0) + { + /* This changes the data from RRGGBB to RRGGBBXX */ + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = hi_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + *(--dp) = hi_filler; + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + + else + { + /* This changes the data from RRGGBB to XXRRGGBB */ + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + *(--dp) = hi_filler; + } + + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + } +#endif + } /* COLOR_TYPE == RGB */ +} +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand grayscale files to RGB, with or without alpha */ +static void +png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_gray_to_rgb"); + + if (row_info->bit_depth >= 8 && + (row_info->color_type & PNG_COLOR_MASK_COLOR) == 0) + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + /* This changes G to RGB */ + png_bytep sp = row + (png_size_t)row_width - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + + else + { + /* This changes GG to RRGGBB */ + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This changes GA to RGBA */ + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + + else + { + /* This changes GGAA to RRGGBBAA */ + png_bytep sp = row + (png_size_t)row_width * 4 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + row_info->channels = (png_byte)(row_info->channels + 2); + row_info->color_type |= PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ of 1998-01-04 at + * (THIS LINK IS DEAD June 2008 but + * versions dated 1998 through November 2002 have been archived at + * https://web.archive.org/web/20000816232553/www.inforamp.net/ + * ~poynton/notes/colour_and_gamma/ColorFAQ.txt ) + * Charles Poynton poynton at poynton.com + * + * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + * + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * Poynton's current link (as of January 2003 through July 2011): + * + * has changed the numbers slightly: + * + * Y = 0.2126*R + 0.7152*G + 0.0722*B + * + * which can be expressed with integers as + * + * Y = (6966 * R + 23436 * G + 2366 * B)/32768 + * + * Historically, however, libpng uses numbers derived from the ITU-R Rec 709 + * end point chromaticities and the D65 white point. Depending on the + * precision used for the D65 white point this produces a variety of different + * numbers, however if the four decimal place value used in ITU-R Rec 709 is + * used (0.3127,0.3290) the Y calculation would be: + * + * Y = (6968 * R + 23435 * G + 2366 * B)/32768 + * + * While this is correct the rounding results in an overflow for white, because + * the sum of the rounded coefficients is 32769, not 32768. Consequently + * libpng uses, instead, the closest non-overflowing approximation: + * + * Y = (6968 * R + 23434 * G + 2366 * B)/32768 + * + * Starting with libpng-1.5.5, if the image being converted has a cHRM chunk + * (including an sRGB chunk) then the chromaticities are used to calculate the + * coefficients. See the chunk handling in pngrutil.c for more information. + * + * In all cases the calculation is to be done in a linear colorspace. If no + * gamma information is available to correct the encoding of the original RGB + * values this results in an implicit assumption that the original PNG RGB + * values were linear. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). Because + * the API takes just red and green coefficients the blue coefficient is + * calculated to make the sum 32768. This will result in different rounding + * to that used above. + */ +static int +png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) + +{ + int rgb_error = 0; + + png_debug(1, "in png_do_rgb_to_gray"); + + if ((row_info->color_type & PNG_COLOR_MASK_PALETTE) == 0 && + (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0) + { + PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + PNG_CONST png_uint_32 bc = 32768 - rc - gc; + PNG_CONST png_uint_32 row_width = row_info->width; + PNG_CONST int have_alpha = + (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0; + + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + /* Notice that gamma to/from 1 are not necessarily inverses (if + * there is an overall gamma correction). Prior to 1.5.5 this code + * checked the linearized values for equality; this doesn't match + * the documentation, the original values must be checked. + */ + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + + if (red != green || red != blue) + { + red = png_ptr->gamma_to_1[red]; + green = png_ptr->gamma_to_1[green]; + blue = png_ptr->gamma_to_1[blue]; + + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red + gc*green + bc*blue + 16384)>>15]; + } + + else + { + /* If there is no overall correction the table will not be + * set. + */ + if (png_ptr->gamma_table != NULL) + red = png_ptr->gamma_table[red]; + + *(dp++) = red; + } + + if (have_alpha != 0) + *(dp++) = *(sp++); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + + if (red != green || red != blue) + { + rgb_error |= 1; + /* NOTE: this is the historical approach which simply + * truncates the results. + */ + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + } + + else + *(dp++) = red; + + if (have_alpha != 0) + *(dp++) = *(sp++); + } + } + } + + else /* RGB bit_depth == 16 */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + png_byte hi,lo; + + hi=*(sp)++; lo=*(sp)++; red = (png_uint_16)((hi << 8) | (lo)); + hi=*(sp)++; lo=*(sp)++; green = (png_uint_16)((hi << 8) | (lo)); + hi=*(sp)++; lo=*(sp)++; blue = (png_uint_16)((hi << 8) | (lo)); + + if (red == green && red == blue) + { + if (png_ptr->gamma_16_table != NULL) + w = png_ptr->gamma_16_table[(red & 0xff) + >> png_ptr->gamma_shift][red >> 8]; + + else + w = red; + } + + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red & 0xff) + >> png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = + png_ptr->gamma_16_to_1[(green & 0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue & 0xff) + >> png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1 + 16384)>>15); + w = png_ptr->gamma_16_from_1[(gray16 & 0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + + if (have_alpha != 0) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); + } + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + png_byte hi,lo; + + hi=*(sp)++; lo=*(sp)++; red = (png_uint_16)((hi << 8) | (lo)); + hi=*(sp)++; lo=*(sp)++; green = (png_uint_16)((hi << 8) | (lo)); + hi=*(sp)++; lo=*(sp)++; blue = (png_uint_16)((hi << 8) | (lo)); + + if (red != green || red != blue) + rgb_error |= 1; + + /* From 1.5.5 in the 16-bit case do the accurate conversion even + * in the 'fast' case - this is because this is where the code + * ends up when handling linear 16-bit data. + */ + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue + 16384) >> + 15); + *(dp++) = (png_byte)((gray16 >> 8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + + if (have_alpha != 0) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); + } + } + } + } + + row_info->channels = (png_byte)(row_info->channels - 2); + row_info->color_type = (png_byte)(row_info->color_type & + ~PNG_COLOR_MASK_COLOR); + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + return rgb_error; +} +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* Replace any alpha or transparency with the supplied background color. + * "background" is already in the screen gamma, while "background_1" is + * at a gamma of 1.0. Paletted files have already been taken care of. + */ +static void +png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) +{ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_const_bytep gamma_table = png_ptr->gamma_table; + png_const_bytep gamma_from_1 = png_ptr->gamma_from_1; + png_const_bytep gamma_to_1 = png_ptr->gamma_to_1; + png_const_uint_16pp gamma_16 = png_ptr->gamma_16_table; + png_const_uint_16pp gamma_16_from_1 = png_ptr->gamma_16_from_1; + png_const_uint_16pp gamma_16_to_1 = png_ptr->gamma_16_to_1; + int gamma_shift = png_ptr->gamma_shift; + int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0; +#endif + + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + int shift; + + png_debug(1, "in png_do_compose"); + + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_GRAY: + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row; + shift = 7; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x01) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0x7f7f >> (7 - shift)); + tmp |= + (unsigned int)(png_ptr->background.gray << shift); + *sp = (png_byte)(tmp & 0xff); + } + + if (shift == 0) + { + shift = 7; + sp++; + } + + else + shift--; + } + break; + } + + case 2: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= + (unsigned int)png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); + } + + else + { + unsigned int p = (*sp >> shift) & 0x03; + unsigned int g = (gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03; + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= (unsigned int)(g << shift); + *sp = (png_byte)(tmp & 0xff); + } + + if (shift == 0) + { + shift = 6; + sp++; + } + + else + shift -= 2; + } + } + + else +#endif + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= + (unsigned int)png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); + } + + if (shift == 0) + { + shift = 6; + sp++; + } + + else + shift -= 2; + } + } + break; + } + + case 4: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0x0f0f >> (4 - shift)); + tmp |= + (unsigned int)(png_ptr->background.gray << shift); + *sp = (png_byte)(tmp & 0xff); + } + + else + { + unsigned int p = (*sp >> shift) & 0x0f; + unsigned int g = (gamma_table[p | (p << 4)] >> 4) & + 0x0f; + unsigned int tmp = *sp & (0x0f0f >> (4 - shift)); + tmp |= (unsigned int)(g << shift); + *sp = (png_byte)(tmp & 0xff); + } + + if (shift == 0) + { + shift = 4; + sp++; + } + + else + shift -= 4; + } + } + + else +#endif + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0x0f0f >> (4 - shift)); + tmp |= + (unsigned int)(png_ptr->background.gray << shift); + *sp = (png_byte)(tmp & 0xff); + } + + if (shift == 0) + { + shift = 4; + sp++; + } + + else + shift -= 4; + } + } + break; + } + + case 8: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == png_ptr->trans_color.gray) + *sp = (png_byte)png_ptr->background.gray; + + else + *sp = gamma_table[*sp]; + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == png_ptr->trans_color.gray) + *sp = (png_byte)png_ptr->background.gray; + } + } + break; + } + + case 16: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + + if (v == png_ptr->trans_color.gray) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray + & 0xff); + } + + else + { + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + + if (v == png_ptr->trans_color.gray) + { + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray + & 0xff); + } + } + } + break; + } + + default: + break; + } + break; + } + + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == png_ptr->trans_color.red && + *(sp + 1) == png_ptr->trans_color.green && + *(sp + 2) == png_ptr->trans_color.blue) + { + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } + + else + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == png_ptr->trans_color.red && + *(sp + 1) == png_ptr->trans_color.green && + *(sp + 2) == png_ptr->trans_color.blue) + { + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + if (r == png_ptr->trans_color.red && + g == png_ptr->trans_color.green && + b == png_ptr->trans_color.blue) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } + + else + { + png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + if (r == png_ptr->trans_color.red && + g == png_ptr->trans_color.green && + b == png_ptr->trans_color.blue) + { + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } + } + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 a = *(sp + 1); + + if (a == 0xff) + *sp = gamma_table[*sp]; + + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)png_ptr->background.gray; + } + + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, png_ptr->background_1.gray); + if (optimize == 0) + w = gamma_from_1[w]; + *sp = w; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_byte a = *(sp + 1); + + if (a == 0) + *sp = (png_byte)png_ptr->background.gray; + + else if (a < 0xff) + png_composite(*sp, *sp, a, png_ptr->background.gray); + } + } + } + else /* if (png_ptr->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) + { + png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); + } + + else + { + png_uint_16 g, v, w; + + g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(v, g, a, png_ptr->background_1.gray); + if (optimize != 0) + w = v; + else + w = gamma_16_from_1[(v & 0xff) >> + gamma_shift][v >> 8]; + *sp = (png_byte)((w >> 8) & 0xff); + *(sp + 1) = (png_byte)(w & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) + { + png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + if (a == 0) + { + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); + } + + else if (a < 0xffff) + { + png_uint_16 g, v; + + g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_composite_16(v, g, a, png_ptr->background.gray); + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } + + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, png_ptr->background_1.red); + if (optimize == 0) w = gamma_from_1[w]; + *sp = w; + + v = gamma_to_1[*(sp + 1)]; + png_composite(w, v, a, png_ptr->background_1.green); + if (optimize == 0) w = gamma_from_1[w]; + *(sp + 1) = w; + + v = gamma_to_1[*(sp + 2)]; + png_composite(w, v, a, png_ptr->background_1.blue); + if (optimize == 0) w = gamma_from_1[w]; + *(sp + 2) = w; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) + { + png_byte a = *(sp + 3); + + if (a == 0) + { + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } + + else if (a < 0xff) + { + png_composite(*sp, *sp, a, png_ptr->background.red); + + png_composite(*(sp + 1), *(sp + 1), a, + png_ptr->background.green); + + png_composite(*(sp + 2), *(sp + 2), a, + png_ptr->background.blue); + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 8) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } + + else + { + png_uint_16 v, w; + + v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(w, v, a, png_ptr->background_1.red); + if (optimize == 0) + w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >> + 8]; + *sp = (png_byte)((w >> 8) & 0xff); + *(sp + 1) = (png_byte)(w & 0xff); + + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + png_composite_16(w, v, a, png_ptr->background_1.green); + if (optimize == 0) + w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >> + 8]; + + *(sp + 2) = (png_byte)((w >> 8) & 0xff); + *(sp + 3) = (png_byte)(w & 0xff); + + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + png_composite_16(w, v, a, png_ptr->background_1.blue); + if (optimize == 0) + w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >> + 8]; + + *(sp + 4) = (png_byte)((w >> 8) & 0xff); + *(sp + 5) = (png_byte)(w & 0xff); + } + } + } + + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 8) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + + if (a == 0) + { + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } + + else if (a < 0xffff) + { + png_uint_16 v; + + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + png_composite_16(v, r, a, png_ptr->background.red); + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + + png_composite_16(v, g, a, png_ptr->background.green); + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + + png_composite_16(v, b, a, png_ptr->background.blue); + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + + default: + break; + } + } +} +#endif /* READ_BACKGROUND || READ_ALPHA_MODE */ + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Gamma correct the image, avoiding the alpha channel. Make sure + * you do this after you deal with the transparency issue on grayscale + * or RGB images. If your bit depth is 8, use gamma_table, if it + * is 16, use gamma_16_table and gamma_shift. Build these with + * build_gamma_table(). + */ +static void +png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) +{ + png_const_bytep gamma_table = png_ptr->gamma_table; + png_const_uint_16pp gamma_16_table = png_ptr->gamma_16_table; + int gamma_shift = png_ptr->gamma_shift; + + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_gamma"); + + if (((row_info->bit_depth <= 8 && gamma_table != NULL) || + (row_info->bit_depth == 16 && gamma_16_table != NULL))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + } + } + + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + + *sp = gamma_table[*sp]; + sp++; + + *sp = gamma_table[*sp]; + sp++; + + sp++; + } + } + + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp += 2; + } + } + + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY: + { + if (row_info->bit_depth == 2) + { + sp = row; + for (i = 0; i < row_width; i += 4) + { + int a = *sp & 0xc0; + int b = *sp & 0x30; + int c = *sp & 0x0c; + int d = *sp & 0x03; + + *sp = (png_byte)( + ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| + ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| + ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| + ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); + sp++; + } + } + + if (row_info->bit_depth == 4) + { + sp = row; + for (i = 0; i < row_width; i += 2) + { + int msb = *sp & 0xf0; + int lsb = *sp & 0x0f; + + *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) + | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); + sp++; + } + } + + else if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + } + } + + else if (row_info->bit_depth == 16) + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + + default: + break; + } + } +} +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* Encode the alpha channel to the output gamma (the input channel is always + * linear.) Called only with color types that have an alpha channel. Needs the + * from_1 tables. + */ +static void +png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) +{ + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_encode_alpha"); + + if ((row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0) + { + if (row_info->bit_depth == 8) + { + PNG_CONST png_bytep table = png_ptr->gamma_from_1; + + if (table != NULL) + { + PNG_CONST int step = + (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2; + + /* The alpha channel is the last component: */ + row += step - 1; + + for (; row_width > 0; --row_width, row += step) + *row = table[*row]; + + return; + } + } + + else if (row_info->bit_depth == 16) + { + PNG_CONST png_uint_16pp table = png_ptr->gamma_16_from_1; + PNG_CONST int gamma_shift = png_ptr->gamma_shift; + + if (table != NULL) + { + PNG_CONST int step = + (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4; + + /* The alpha channel is the last component: */ + row += step - 2; + + for (; row_width > 0; --row_width, row += step) + { + png_uint_16 v; + + v = table[*(row + 1) >> gamma_shift][*row]; + *row = (png_byte)((v >> 8) & 0xff); + *(row + 1) = (png_byte)(v & 0xff); + } + + return; + } + } + } + + /* Only get to here if called with a weird row_info; no harm has been done, + * so just issue a warning. + */ + png_warning(png_ptr, "png_do_encode_alpha: unexpected call"); +} +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expands a palette row to an RGB or RGBA row depending + * upon whether you supply trans and num_trans. + */ +static void +png_do_expand_palette(png_row_infop row_info, png_bytep row, + png_const_colorp palette, png_const_bytep trans_alpha, int num_trans) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand_palette"); + + if (row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 1; + + else + *dp = 0; + + if (shift == 7) + { + shift = 0; + sp--; + } + + else + shift++; + + dp--; + } + break; + } + + case 2: + { + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)value; + if (shift == 6) + { + shift = 0; + sp--; + } + + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((row_width & 0x01) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)value; + if (shift == 4) + { + shift = 0; + sp--; + } + + else + shift += 4; + + dp--; + } + break; + } + + default: + break; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (row_info->bit_depth == 8) + { + { + if (num_trans > 0) + { + sp = row + (png_size_t)row_width - 1; + dp = row + ((png_size_t)row_width << 2) - 1; + + for (i = 0; i < row_width; i++) + { + if ((int)(*sp) >= num_trans) + *dp-- = 0xff; + + else + *dp-- = trans_alpha[*sp]; + + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + row_info->color_type = 6; + row_info->channels = 4; + } + + else + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width * 3) - 1; + + for (i = 0; i < row_width; i++) + { + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + + row_info->bit_depth = 8; + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + row_info->color_type = 2; + row_info->channels = 3; + } + } + } + } +} + +/* If the bit depth < 8, it is expanded to 8. Also, if the already + * expanded transparency value is supplied, an alpha channel is built. + */ +static void +png_do_expand(png_row_infop row_info, png_bytep row, + png_const_color_16p trans_color) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand"); + + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + unsigned int gray = trans_color != NULL ? trans_color->gray : 0; + + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + gray = (gray & 0x01) * 0xff; + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 0xff; + + else + *dp = 0; + + if (shift == 7) + { + shift = 0; + sp--; + } + + else + shift++; + + dp--; + } + break; + } + + case 2: + { + gray = (gray & 0x03) * 0x55; + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)(value | (value << 2) | (value << 4) | + (value << 6)); + if (shift == 6) + { + shift = 0; + sp--; + } + + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + gray = (gray & 0x0f) * 0x11; + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)(value | (value << 4)); + if (shift == 4) + { + shift = 0; + sp--; + } + + else + shift = 4; + + dp--; + } + break; + } + + default: + break; + } + + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (trans_color != NULL) + { + if (row_info->bit_depth == 8) + { + gray = gray & 0xff; + sp = row + (png_size_t)row_width - 1; + dp = row + ((png_size_t)row_width << 1) - 1; + + for (i = 0; i < row_width; i++) + { + if ((*sp & 0xffU) == gray) + *dp-- = 0; + + else + *dp-- = 0xff; + + *dp-- = *sp--; + } + } + + else if (row_info->bit_depth == 16) + { + unsigned int gray_high = (gray >> 8) & 0xff; + unsigned int gray_low = gray & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (row_info->rowbytes << 1) - 1; + for (i = 0; i < row_width; i++) + { + if ((*(sp - 1) & 0xffU) == gray_high && + (*(sp) & 0xffU) == gray_low) + { + *dp-- = 0; + *dp-- = 0; + } + + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + + *dp-- = *sp--; + *dp-- = *sp--; + } + } + + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + row_info->channels = 2; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_width); + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && + trans_color != NULL) + { + if (row_info->bit_depth == 8) + { + png_byte red = (png_byte)(trans_color->red & 0xff); + png_byte green = (png_byte)(trans_color->green & 0xff); + png_byte blue = (png_byte)(trans_color->blue & 0xff); + sp = row + (png_size_t)row_info->rowbytes - 1; + dp = row + ((png_size_t)row_width << 2) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) + *dp-- = 0; + + else + *dp-- = 0xff; + + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff); + png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff); + png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff); + png_byte red_low = (png_byte)(trans_color->red & 0xff); + png_byte green_low = (png_byte)(trans_color->green & 0xff); + png_byte blue_low = (png_byte)(trans_color->blue & 0xff); + sp = row + row_info->rowbytes - 1; + dp = row + ((png_size_t)row_width << 3) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 5) == red_high && + *(sp - 4) == red_low && + *(sp - 3) == green_high && + *(sp - 2) == green_low && + *(sp - 1) == blue_high && + *(sp ) == blue_low) + { + *dp-- = 0; + *dp-- = 0; + } + + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + row_info->channels = 4; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + } +} +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* If the bit depth is 8 and the color type is not a palette type expand the + * whole row to 16 bits. Has no effect otherwise. + */ +static void +png_do_expand_16(png_row_infop row_info, png_bytep row) +{ + if (row_info->bit_depth == 8 && + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + /* The row have a sequence of bytes containing [0..255] and we need + * to turn it into another row containing [0..65535], to do this we + * calculate: + * + * (input / 255) * 65535 + * + * Which happens to be exactly input * 257 and this can be achieved + * simply by byte replication in place (copying backwards). + */ + png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */ + png_byte *dp = sp + row_info->rowbytes; /* destination, end + 1 */ + while (dp > sp) + { + dp[-2] = dp[-1] = *--sp; dp -= 2; + } + + row_info->rowbytes *= 2; + row_info->bit_depth = 16; + row_info->pixel_depth = (png_byte)(row_info->channels * 16); + } +} +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +static void +png_do_quantize(png_row_infop row_info, png_bytep row, + png_const_bytep palette_lookup, png_const_bytep quantize_lookup) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_quantize"); + + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + + /* This looks real messy, but the compiler will reduce + * it down to a reasonable formula. For example, with + * 5 bits per color, we get: + * p = (((r >> 3) & 0x1f) << 10) | + * (((g >> 3) & 0x1f) << 5) | + * ((b >> 3) & 0x1f); + */ + p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & + ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << + (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | + (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & + ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << + (PNG_QUANTIZE_BLUE_BITS)) | + ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & + ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + palette_lookup != NULL) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + sp++; + + p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & + ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << + (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | + (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & + ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << + (PNG_QUANTIZE_BLUE_BITS)) | + ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & + ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + + else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + quantize_lookup) + { + sp = row; + + for (i = 0; i < row_width; i++, sp++) + { + *sp = quantize_lookup[*sp]; + } + } + } +} +#endif /* READ_QUANTIZE */ + +/* Transform the row. The order of transformations is significant, + * and is very touchy. If you add a transformation, take care to + * decide how it fits in with the other transformations here. + */ +void /* PRIVATE */ +png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) +{ + png_debug(1, "in png_do_read_transformations"); + + if (png_ptr->row_buf == NULL) + { + /* Prior to 1.5.4 this output row/pass where the NULL pointer is, but this + * error is incredibly rare and incredibly easy to debug without this + * information. + */ + png_error(png_ptr, "NULL row buffer"); + } + + /* The following is debugging; prior to 1.5.4 the code was never compiled in; + * in 1.5.4 PNG_FLAG_DETECT_UNINITIALIZED was added and the macro + * PNG_WARN_UNINITIALIZED_ROW removed. In 1.6 the new flag is set only for + * all transformations, however in practice the ROW_INIT always gets done on + * demand, if necessary. + */ + if ((png_ptr->flags & PNG_FLAG_DETECT_UNINITIALIZED) != 0 && + (png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) + { + /* Application has failed to call either png_read_start_image() or + * png_read_update_info() after setting transforms that expand pixels. + * This check added to libpng-1.2.19 (but not enabled until 1.5.4). + */ + png_error(png_ptr, "Uninitialized row"); + } + +#ifdef PNG_READ_EXPAND_SUPPORTED + if ((png_ptr->transformations & PNG_EXPAND) != 0) + { + if (row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_do_expand_palette(row_info, png_ptr->row_buf + 1, + png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans); + } + + else + { + if (png_ptr->num_trans != 0 && + (png_ptr->transformations & PNG_EXPAND_tRNS) != 0) + png_do_expand(row_info, png_ptr->row_buf + 1, + &(png_ptr->trans_color)); + + else + png_do_expand(row_info, png_ptr->row_buf + 1, + NULL); + } + } +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 && + (png_ptr->transformations & PNG_COMPOSE) == 0 && + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + png_do_strip_channel(row_info, png_ptr->row_buf + 1, + 0 /* at_start == false, because SWAP_ALPHA happens later */); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) + { + int rgb_error = + png_do_rgb_to_gray(png_ptr, row_info, + png_ptr->row_buf + 1); + + if (rgb_error != 0) + { + png_ptr->rgb_to_gray_status=1; + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_WARN) + png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_ERR) + png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + } + } +#endif + +/* From Andreas Dilger e-mail to png-implement, 26 March 1998: + * + * In most cases, the "simple transparency" should be done prior to doing + * gray-to-RGB, or you will have to test 3x as many bytes to check if a + * pixel is transparent. You would also need to make sure that the + * transparency information is upgraded to RGB. + * + * To summarize, the current flow is: + * - Gray + simple transparency -> compare 1 or 2 gray bytes and composite + * with background "in place" if transparent, + * convert to RGB if necessary + * - Gray + alpha -> composite with gray background and remove alpha bytes, + * convert to RGB if necessary + * + * To support RGB backgrounds for gray images we need: + * - Gray + simple transparency -> convert to RGB + simple transparency, + * compare 3 or 6 bytes and composite with + * background "in place" if transparent + * (3x compare/pixel compared to doing + * composite with gray bkgrnd) + * - Gray + alpha -> convert to RGB + alpha, composite with background and + * remove alpha bytes (3x float + * operations/pixel compared with composite + * on gray background) + * + * Greg's change will do this. The reason it wasn't done before is for + * performance, as this increases the per-pixel operations. If we would check + * in advance if the background was gray or RGB, and position the gray-to-RGB + * transform appropriately, then it would save a lot of work/time. + */ + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* If gray -> RGB, do so now only if background is non-gray; else do later + * for performance reasons + */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0 && + (png_ptr->mode & PNG_BACKGROUND_IS_GRAY) == 0) + png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + if ((png_ptr->transformations & PNG_COMPOSE) != 0) + png_do_compose(row_info, png_ptr->row_buf + 1, png_ptr); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + if ((png_ptr->transformations & PNG_GAMMA) != 0 && +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* Because RGB_TO_GRAY does the gamma transform. */ + (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0 && +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + /* Because PNG_COMPOSE does the gamma transform if there is something to + * do (if there is an alpha channel or transparency.) + */ + !((png_ptr->transformations & PNG_COMPOSE) != 0 && + ((png_ptr->num_trans != 0) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)) && +#endif + /* Because png_init_read_transformations transforms the palette, unless + * RGB_TO_GRAY will do the transform. + */ + (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) + png_do_gamma(row_info, png_ptr->row_buf + 1, png_ptr); +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 && + (png_ptr->transformations & PNG_COMPOSE) != 0 && + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + png_do_strip_channel(row_info, png_ptr->row_buf + 1, + 0 /* at_start == false, because SWAP_ALPHA happens later */); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED + if ((png_ptr->transformations & PNG_ENCODE_ALPHA) != 0 && + (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0) + png_do_encode_alpha(row_info, png_ptr->row_buf + 1, png_ptr); +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + if ((png_ptr->transformations & PNG_SCALE_16_TO_8) != 0) + png_do_scale_16_to_8(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + /* There is no harm in doing both of these because only one has any effect, + * by putting the 'scale' option first if the app asks for scale (either by + * calling the API or in a TRANSFORM flag) this is what happens. + */ + if ((png_ptr->transformations & PNG_16_TO_8) != 0) + png_do_chop(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + if ((png_ptr->transformations & PNG_QUANTIZE) != 0) + { + png_do_quantize(row_info, png_ptr->row_buf + 1, + png_ptr->palette_lookup, png_ptr->quantize_index); + + if (row_info->rowbytes == 0) + png_error(png_ptr, "png_do_quantize returned rowbytes=0"); + } +#endif /* READ_QUANTIZE */ + +#ifdef PNG_READ_EXPAND_16_SUPPORTED + /* Do the expansion now, after all the arithmetic has been done. Notice + * that previous transformations can handle the PNG_EXPAND_16 flag if this + * is efficient (particularly true in the case of gamma correction, where + * better accuracy results faster!) + */ + if ((png_ptr->transformations & PNG_EXPAND_16) != 0) + png_do_expand_16(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* NOTE: moved here in 1.5.4 (from much later in this list.) */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0 && + (png_ptr->mode & PNG_BACKGROUND_IS_GRAY) != 0) + png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_INVERT_SUPPORTED + if ((png_ptr->transformations & PNG_INVERT_MONO) != 0) + png_do_invert(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0) + png_do_read_invert_alpha(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED + if ((png_ptr->transformations & PNG_SHIFT) != 0) + png_do_unshift(row_info, png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#ifdef PNG_READ_PACK_SUPPORTED + if ((png_ptr->transformations & PNG_PACK) != 0) + png_do_unpack(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Added at libpng-1.5.10 */ + if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + png_ptr->num_palette_max >= 0) + png_do_check_palette_indexes(png_ptr, row_info); +#endif + +#ifdef PNG_READ_BGR_SUPPORTED + if ((png_ptr->transformations & PNG_BGR) != 0) + png_do_bgr(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if ((png_ptr->transformations & PNG_PACKSWAP) != 0) + png_do_packswap(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED + if ((png_ptr->transformations & PNG_FILLER) != 0) + png_do_read_filler(row_info, png_ptr->row_buf + 1, + (png_uint_32)png_ptr->filler, png_ptr->flags); +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0) + png_do_read_swap_alpha(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_16BIT_SUPPORTED +#ifdef PNG_READ_SWAP_SUPPORTED + if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0) + png_do_swap(row_info, png_ptr->row_buf + 1); +#endif +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0) + { + if (png_ptr->read_user_transform_fn != NULL) + (*(png_ptr->read_user_transform_fn)) /* User read transform function */ + (png_ptr, /* png_ptr */ + row_info, /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_size_t rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + if (png_ptr->user_transform_depth != 0) + row_info->bit_depth = png_ptr->user_transform_depth; + + if (png_ptr->user_transform_channels != 0) + row_info->channels = png_ptr->user_transform_channels; +#endif + row_info->pixel_depth = (png_byte)(row_info->bit_depth * + row_info->channels); + + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width); + } +#endif +} + +#endif /* READ_TRANSFORMS */ +#endif /* READ */ diff --git a/src/png/libpng/pngrutil.c b/src/png/libpng/pngrutil.c new file mode 100644 index 0000000000..8692933bd8 --- /dev/null +++ b/src/png/libpng/pngrutil.c @@ -0,0 +1,4661 @@ + +/* pngrutil.c - utilities to read a PNG file + * + * Last changed in libpng 1.6.33 [September 28, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains routines that are only called from within + * libpng itself during the course of reading an image. + */ + +#include "pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +png_uint_32 PNGAPI +png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf) +{ + png_uint_32 uval = png_get_uint_32(buf); + + if (uval > PNG_UINT_31_MAX) + png_error(png_ptr, "PNG unsigned integer out of range"); + + return (uval); +} + +#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED) +/* The following is a variation on the above for use with the fixed + * point values used for gAMA and cHRM. Instead of png_error it + * issues a warning and returns (-1) - an invalid value because both + * gAMA and cHRM use *unsigned* integers for fixed point values. + */ +#define PNG_FIXED_ERROR (-1) + +static png_fixed_point /* PRIVATE */ +png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf) +{ + png_uint_32 uval = png_get_uint_32(buf); + + if (uval <= PNG_UINT_31_MAX) + return (png_fixed_point)uval; /* known to be in range */ + + /* The caller can turn off the warning by passing NULL. */ + if (png_ptr != NULL) + png_warning(png_ptr, "PNG fixed point integer out of range"); + + return PNG_FIXED_ERROR; +} +#endif + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +/* NOTE: the read macros will obscure these definitions, so that if + * PNG_USE_READ_MACROS is set the library will not use them internally, + * but the APIs will still be available externally. + * + * The parentheses around "PNGAPI function_name" in the following three + * functions are necessary because they allow the macros to co-exist with + * these (unused but exported) functions. + */ + +/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ +png_uint_32 (PNGAPI +png_get_uint_32)(png_const_bytep buf) +{ + png_uint_32 uval = + ((png_uint_32)(*(buf )) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + ((png_uint_32)(*(buf + 3)) ) ; + + return uval; +} + +/* Grab a signed 32-bit integer from a buffer in big-endian format. The + * data is stored in the PNG file in two's complement format and there + * is no guarantee that a 'png_int_32' is exactly 32 bits, therefore + * the following code does a two's complement to native conversion. + */ +png_int_32 (PNGAPI +png_get_int_32)(png_const_bytep buf) +{ + png_uint_32 uval = png_get_uint_32(buf); + if ((uval & 0x80000000) == 0) /* non-negative */ + return (png_int_32)uval; + + uval = (uval ^ 0xffffffff) + 1; /* 2's complement: -x = ~x+1 */ + if ((uval & 0x80000000) == 0) /* no overflow */ + return -(png_int_32)uval; + /* The following has to be safe; this function only gets called on PNG data + * and if we get here that data is invalid. 0 is the most safe value and + * if not then an attacker would surely just generate a PNG with 0 instead. + */ + return 0; +} + +/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ +png_uint_16 (PNGAPI +png_get_uint_16)(png_const_bytep buf) +{ + /* ANSI-C requires an int value to accomodate at least 16 bits so this + * works and allows the compiler not to worry about possible narrowing + * on 32-bit systems. (Pre-ANSI systems did not make integers smaller + * than 16 bits either.) + */ + unsigned int val = + ((unsigned int)(*buf) << 8) + + ((unsigned int)(*(buf + 1))); + + return (png_uint_16)val; +} + +#endif /* READ_INT_FUNCTIONS */ + +/* Read and check the PNG file signature */ +void /* PRIVATE */ +png_read_sig(png_structrp png_ptr, png_inforp info_ptr) +{ + png_size_t num_checked, num_to_check; + + /* Exit if the user application does not expect a signature. */ + if (png_ptr->sig_bytes >= 8) + return; + + num_checked = png_ptr->sig_bytes; + num_to_check = 8 - num_checked; + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_SIGNATURE; +#endif + + /* The signature must be serialized in a single I/O call. */ + png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); + png_ptr->sig_bytes = 8; + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check) != 0) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + if (num_checked < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +/* Read the chunk header (length + type name). + * Put the type name into png_ptr->chunk_name, and return the length. + */ +png_uint_32 /* PRIVATE */ +png_read_chunk_header(png_structrp png_ptr) +{ + png_byte buf[8]; + png_uint_32 length; + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; +#endif + + /* Read the length and the chunk name. + * This must be performed in a single I/O call. + */ + png_read_data(png_ptr, buf, 8); + length = png_get_uint_31(png_ptr, buf); + + /* Put the chunk name into png_ptr->chunk_name. */ + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4); + + png_debug2(0, "Reading %lx chunk, length = %lu", + (unsigned long)png_ptr->chunk_name, (unsigned long)length); + + /* Reset the crc and run it over the chunk name. */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, buf + 4, 4); + + /* Check to see if chunk name is valid. */ + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + /* Check for too-large chunk length */ + png_check_chunk_length(png_ptr, length); + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; +#endif + + return length; +} + +/* Read data, and (optionally) run it through the CRC. */ +void /* PRIVATE */ +png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length) +{ + if (png_ptr == NULL) + return; + + png_read_data(png_ptr, buf, length); + png_calculate_crc(png_ptr, buf, length); +} + +/* Optionally skip data and then check the CRC. Depending on whether we + * are reading an ancillary or critical chunk, and how the program has set + * things up, we may calculate the CRC on the data and print a message. + * Returns '1' if there was a CRC error, '0' otherwise. + */ +int /* PRIVATE */ +png_crc_finish(png_structrp png_ptr, png_uint_32 skip) +{ + /* The size of the local buffer for inflate is a good guess as to a + * reasonable size to use for buffering reads from the application. + */ + while (skip > 0) + { + png_uint_32 len; + png_byte tmpbuf[PNG_INFLATE_BUF_SIZE]; + + len = (sizeof tmpbuf); + if (len > skip) + len = skip; + skip -= len; + + png_crc_read(png_ptr, tmpbuf, len); + } + + if (png_crc_error(png_ptr) != 0) + { + if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0 ? + (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0 : + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE) != 0) + { + png_chunk_warning(png_ptr, "CRC error"); + } + + else + png_chunk_error(png_ptr, "CRC error"); + + return (1); + } + + return (0); +} + +/* Compare the CRC stored in the PNG file with that calculated by libpng from + * the data it has read thus far. + */ +int /* PRIVATE */ +png_crc_error(png_structrp png_ptr) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0) + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + + else /* critical */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0) + need_crc = 0; + } + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; +#endif + + /* The chunk CRC must be serialized in a single I/O call. */ + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc != 0) + { + crc = png_get_uint_32(crc_bytes); + return ((int)(crc != png_ptr->crc)); + } + + else + return (0); +} + +#if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\ + defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_READ_sCAL_SUPPORTED) ||\ + defined(PNG_READ_sPLT_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) ||\ + defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_SEQUENTIAL_READ_SUPPORTED) +/* Manage the read buffer; this simply reallocates the buffer if it is not small + * enough (or if it is not allocated). The routine returns a pointer to the + * buffer; if an error occurs and 'warn' is set the routine returns NULL, else + * it will call png_error (via png_malloc) on failure. (warn == 2 means + * 'silent'). + */ +static png_bytep +png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) +{ + png_bytep buffer = png_ptr->read_buffer; + + if (buffer != NULL && new_size > png_ptr->read_buffer_size) + { + png_ptr->read_buffer = NULL; + png_ptr->read_buffer = NULL; + png_ptr->read_buffer_size = 0; + png_free(png_ptr, buffer); + buffer = NULL; + } + + if (buffer == NULL) + { + buffer = png_voidcast(png_bytep, png_malloc_base(png_ptr, new_size)); + + if (buffer != NULL) + { + memset(buffer, 0, new_size); /* just in case */ + png_ptr->read_buffer = buffer; + png_ptr->read_buffer_size = new_size; + } + + else if (warn < 2) /* else silent */ + { + if (warn != 0) + png_chunk_warning(png_ptr, "insufficient memory to read chunk"); + + else + png_chunk_error(png_ptr, "insufficient memory to read chunk"); + } + } + + return buffer; +} +#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|SEQUENTIAL_READ */ + +/* png_inflate_claim: claim the zstream for some nefarious purpose that involves + * decompression. Returns Z_OK on success, else a zlib error code. It checks + * the owner but, in final release builds, just issues a warning if some other + * chunk apparently owns the stream. Prior to release it does a png_error. + */ +static int +png_inflate_claim(png_structrp png_ptr, png_uint_32 owner) +{ + if (png_ptr->zowner != 0) + { + char msg[64]; + + PNG_STRING_FROM_CHUNK(msg, png_ptr->zowner); + /* So the message that results is " using zstream"; this is an + * internal error, but is very useful for debugging. i18n requirements + * are minimal. + */ + (void)png_safecat(msg, (sizeof msg), 4, " using zstream"); +#if PNG_RELEASE_BUILD + png_chunk_warning(png_ptr, msg); + png_ptr->zowner = 0; +#else + png_chunk_error(png_ptr, msg); +#endif + } + + /* Implementation note: unlike 'png_deflate_claim' this internal function + * does not take the size of the data as an argument. Some efficiency could + * be gained by using this when it is known *if* the zlib stream itself does + * not record the number; however, this is an illusion: the original writer + * of the PNG may have selected a lower window size, and we really must + * follow that because, for systems with with limited capabilities, we + * would otherwise reject the application's attempts to use a smaller window + * size (zlib doesn't have an interface to say "this or lower"!). + * + * inflateReset2 was added to zlib 1.2.4; before this the window could not be + * reset, therefore it is necessary to always allocate the maximum window + * size with earlier zlibs just in case later compressed chunks need it. + */ + { + int ret; /* zlib return code */ +#if ZLIB_VERNUM >= 0x1240 + int window_bits = 0; + +# if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_MAXIMUM_INFLATE_WINDOW) + if (((png_ptr->options >> PNG_MAXIMUM_INFLATE_WINDOW) & 3) == + PNG_OPTION_ON) + { + window_bits = 15; + png_ptr->zstream_start = 0; /* fixed window size */ + } + + else + { + png_ptr->zstream_start = 1; + } +# endif + +#endif /* ZLIB_VERNUM >= 0x1240 */ + + /* Set this for safety, just in case the previous owner left pointers to + * memory allocations. + */ + png_ptr->zstream.next_in = NULL; + png_ptr->zstream.avail_in = 0; + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0; + + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0) + { +#if ZLIB_VERNUM >= 0x1240 + ret = inflateReset2(&png_ptr->zstream, window_bits); +#else + ret = inflateReset(&png_ptr->zstream); +#endif + } + + else + { +#if ZLIB_VERNUM >= 0x1240 + ret = inflateInit2(&png_ptr->zstream, window_bits); +#else + ret = inflateInit(&png_ptr->zstream); +#endif + + if (ret == Z_OK) + png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; + } + +#if ZLIB_VERNUM >= 0x1290 && \ + defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_IGNORE_ADLER32) + if (((png_ptr->options >> PNG_IGNORE_ADLER32) & 3) == PNG_OPTION_ON) + /* Turn off validation of the ADLER32 checksum in IDAT chunks */ + ret = inflateValidate(&png_ptr->zstream, 0); +#endif + + if (ret == Z_OK) + png_ptr->zowner = owner; + + else + png_zstream_error(png_ptr, ret); + + return ret; + } + +#ifdef window_bits +# undef window_bits +#endif +} + +#if ZLIB_VERNUM >= 0x1240 +/* Handle the start of the inflate stream if we called inflateInit2(strm,0); + * in this case some zlib versions skip validation of the CINFO field and, in + * certain circumstances, libpng may end up displaying an invalid image, in + * contrast to implementations that call zlib in the normal way (e.g. libpng + * 1.5). + */ +int /* PRIVATE */ +png_zlib_inflate(png_structrp png_ptr, int flush) +{ + if (png_ptr->zstream_start && png_ptr->zstream.avail_in > 0) + { + if ((*png_ptr->zstream.next_in >> 4) > 7) + { + png_ptr->zstream.msg = "invalid window size (libpng)"; + return Z_DATA_ERROR; + } + + png_ptr->zstream_start = 0; + } + + return inflate(&png_ptr->zstream, flush); +} +#endif /* Zlib >= 1.2.4 */ + +#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED +#if defined(PNG_READ_zTXt_SUPPORTED) || defined (PNG_READ_iTXt_SUPPORTED) +/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to + * allow the caller to do multiple calls if required. If the 'finish' flag is + * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must + * be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and + * Z_OK or Z_STREAM_END will be returned on success. + * + * The input and output sizes are updated to the actual amounts of data consumed + * or written, not the amount available (as in a z_stream). The data pointers + * are not changed, so the next input is (data+input_size) and the next + * available output is (output+output_size). + */ +static int +png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, + /* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr, + /* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr) +{ + if (png_ptr->zowner == owner) /* Else not claimed */ + { + int ret; + png_alloc_size_t avail_out = *output_size_ptr; + png_uint_32 avail_in = *input_size_ptr; + + /* zlib can't necessarily handle more than 65535 bytes at once (i.e. it + * can't even necessarily handle 65536 bytes) because the type uInt is + * "16 bits or more". Consequently it is necessary to chunk the input to + * zlib. This code uses ZLIB_IO_MAX, from pngpriv.h, as the maximum (the + * maximum value that can be stored in a uInt.) It is possible to set + * ZLIB_IO_MAX to a lower value in pngpriv.h and this may sometimes have + * a performance advantage, because it reduces the amount of data accessed + * at each step and that may give the OS more time to page it in. + */ + png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input); + /* avail_in and avail_out are set below from 'size' */ + png_ptr->zstream.avail_in = 0; + png_ptr->zstream.avail_out = 0; + + /* Read directly into the output if it is available (this is set to + * a local buffer below if output is NULL). + */ + if (output != NULL) + png_ptr->zstream.next_out = output; + + do + { + uInt avail; + Byte local_buffer[PNG_INFLATE_BUF_SIZE]; + + /* zlib INPUT BUFFER */ + /* The setting of 'avail_in' used to be outside the loop; by setting it + * inside it is possible to chunk the input to zlib and simply rely on + * zlib to advance the 'next_in' pointer. This allows arbitrary + * amounts of data to be passed through zlib at the unavoidable cost of + * requiring a window save (memcpy of up to 32768 output bytes) + * every ZLIB_IO_MAX input bytes. + */ + avail_in += png_ptr->zstream.avail_in; /* not consumed last time */ + + avail = ZLIB_IO_MAX; + + if (avail_in < avail) + avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */ + + avail_in -= avail; + png_ptr->zstream.avail_in = avail; + + /* zlib OUTPUT BUFFER */ + avail_out += png_ptr->zstream.avail_out; /* not written last time */ + + avail = ZLIB_IO_MAX; /* maximum zlib can process */ + + if (output == NULL) + { + /* Reset the output buffer each time round if output is NULL and + * make available the full buffer, up to 'remaining_space' + */ + png_ptr->zstream.next_out = local_buffer; + if ((sizeof local_buffer) < avail) + avail = (sizeof local_buffer); + } + + if (avail_out < avail) + avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */ + + png_ptr->zstream.avail_out = avail; + avail_out -= avail; + + /* zlib inflate call */ + /* In fact 'avail_out' may be 0 at this point, that happens at the end + * of the read when the final LZ end code was not passed at the end of + * the previous chunk of input data. Tell zlib if we have reached the + * end of the output buffer. + */ + ret = PNG_INFLATE(png_ptr, avail_out > 0 ? Z_NO_FLUSH : + (finish ? Z_FINISH : Z_SYNC_FLUSH)); + } while (ret == Z_OK); + + /* For safety kill the local buffer pointer now */ + if (output == NULL) + png_ptr->zstream.next_out = NULL; + + /* Claw back the 'size' and 'remaining_space' byte counts. */ + avail_in += png_ptr->zstream.avail_in; + avail_out += png_ptr->zstream.avail_out; + + /* Update the input and output sizes; the updated values are the amount + * consumed or written, effectively the inverse of what zlib uses. + */ + if (avail_out > 0) + *output_size_ptr -= avail_out; + + if (avail_in > 0) + *input_size_ptr -= avail_in; + + /* Ensure png_ptr->zstream.msg is set (even in the success case!) */ + png_zstream_error(png_ptr, ret); + return ret; + } + + else + { + /* This is a bad internal error. The recovery assigns to the zstream msg + * pointer, which is not owned by the caller, but this is safe; it's only + * used on errors! + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed"); + return Z_STREAM_ERROR; + } +} + +/* + * Decompress trailing data in a chunk. The assumption is that read_buffer + * points at an allocated area holding the contents of a chunk with a + * trailing compressed part. What we get back is an allocated area + * holding the original prefix part and an uncompressed version of the + * trailing part (the malloc area passed in is freed). + */ +static int +png_decompress_chunk(png_structrp png_ptr, + png_uint_32 chunklength, png_uint_32 prefix_size, + png_alloc_size_t *newlength /* must be initialized to the maximum! */, + int terminate /*add a '\0' to the end of the uncompressed data*/) +{ + /* TODO: implement different limits for different types of chunk. + * + * The caller supplies *newlength set to the maximum length of the + * uncompressed data, but this routine allocates space for the prefix and + * maybe a '\0' terminator too. We have to assume that 'prefix_size' is + * limited only by the maximum chunk size. + */ + png_alloc_size_t limit = PNG_SIZE_MAX; + +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_malloc_max > 0 && + png_ptr->user_chunk_malloc_max < limit) + limit = png_ptr->user_chunk_malloc_max; +# elif PNG_USER_CHUNK_MALLOC_MAX > 0 + if (PNG_USER_CHUNK_MALLOC_MAX < limit) + limit = PNG_USER_CHUNK_MALLOC_MAX; +# endif + + if (limit >= prefix_size + (terminate != 0)) + { + int ret; + + limit -= prefix_size + (terminate != 0); + + if (limit < *newlength) + *newlength = limit; + + /* Now try to claim the stream. */ + ret = png_inflate_claim(png_ptr, png_ptr->chunk_name); + + if (ret == Z_OK) + { + png_uint_32 lzsize = chunklength - prefix_size; + + ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/, + /* input: */ png_ptr->read_buffer + prefix_size, &lzsize, + /* output: */ NULL, newlength); + + if (ret == Z_STREAM_END) + { + /* Use 'inflateReset' here, not 'inflateReset2' because this + * preserves the previously decided window size (otherwise it would + * be necessary to store the previous window size.) In practice + * this doesn't matter anyway, because png_inflate will call inflate + * with Z_FINISH in almost all cases, so the window will not be + * maintained. + */ + if (inflateReset(&png_ptr->zstream) == Z_OK) + { + /* Because of the limit checks above we know that the new, + * expanded, size will fit in a size_t (let alone an + * png_alloc_size_t). Use png_malloc_base here to avoid an + * extra OOM message. + */ + png_alloc_size_t new_size = *newlength; + png_alloc_size_t buffer_size = prefix_size + new_size + + (terminate != 0); + png_bytep text = png_voidcast(png_bytep, png_malloc_base(png_ptr, + buffer_size)); + + if (text != NULL) + { + memset(text, 0, buffer_size); + + ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/, + png_ptr->read_buffer + prefix_size, &lzsize, + text + prefix_size, newlength); + + if (ret == Z_STREAM_END) + { + if (new_size == *newlength) + { + if (terminate != 0) + text[prefix_size + *newlength] = 0; + + if (prefix_size > 0) + memcpy(text, png_ptr->read_buffer, prefix_size); + + { + png_bytep old_ptr = png_ptr->read_buffer; + + png_ptr->read_buffer = text; + png_ptr->read_buffer_size = buffer_size; + text = old_ptr; /* freed below */ + } + } + + else + { + /* The size changed on the second read, there can be no + * guarantee that anything is correct at this point. + * The 'msg' pointer has been set to "unexpected end of + * LZ stream", which is fine, but return an error code + * that the caller won't accept. + */ + ret = PNG_UNEXPECTED_ZLIB_RETURN; + } + } + + else if (ret == Z_OK) + ret = PNG_UNEXPECTED_ZLIB_RETURN; /* for safety */ + + /* Free the text pointer (this is the old read_buffer on + * success) + */ + png_free(png_ptr, text); + + /* This really is very benign, but it's still an error because + * the extra space may otherwise be used as a Trojan Horse. + */ + if (ret == Z_STREAM_END && + chunklength - prefix_size != lzsize) + png_chunk_benign_error(png_ptr, "extra compressed data"); + } + + else + { + /* Out of memory allocating the buffer */ + ret = Z_MEM_ERROR; + png_zstream_error(png_ptr, Z_MEM_ERROR); + } + } + + else + { + /* inflateReset failed, store the error message */ + png_zstream_error(png_ptr, ret); + ret = PNG_UNEXPECTED_ZLIB_RETURN; + } + } + + else if (ret == Z_OK) + ret = PNG_UNEXPECTED_ZLIB_RETURN; + + /* Release the claimed stream */ + png_ptr->zowner = 0; + } + + else /* the claim failed */ if (ret == Z_STREAM_END) /* impossible! */ + ret = PNG_UNEXPECTED_ZLIB_RETURN; + + return ret; + } + + else + { + /* Application/configuration limits exceeded */ + png_zstream_error(png_ptr, Z_MEM_ERROR); + return Z_MEM_ERROR; + } +} +#endif /* READ_zTXt || READ_iTXt */ +#endif /* READ_COMPRESSED_TEXT */ + +#ifdef PNG_READ_iCCP_SUPPORTED +/* Perform a partial read and decompress, producing 'avail_out' bytes and + * reading from the current chunk as required. + */ +static int +png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, + png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size, + int finish) +{ + if (png_ptr->zowner == png_ptr->chunk_name) + { + int ret; + + /* next_in and avail_in must have been initialized by the caller. */ + png_ptr->zstream.next_out = next_out; + png_ptr->zstream.avail_out = 0; /* set in the loop */ + + do + { + if (png_ptr->zstream.avail_in == 0) + { + if (read_size > *chunk_bytes) + read_size = (uInt)*chunk_bytes; + *chunk_bytes -= read_size; + + if (read_size > 0) + png_crc_read(png_ptr, read_buffer, read_size); + + png_ptr->zstream.next_in = read_buffer; + png_ptr->zstream.avail_in = read_size; + } + + if (png_ptr->zstream.avail_out == 0) + { + uInt avail = ZLIB_IO_MAX; + if (avail > *out_size) + avail = (uInt)*out_size; + *out_size -= avail; + + png_ptr->zstream.avail_out = avail; + } + + /* Use Z_SYNC_FLUSH when there is no more chunk data to ensure that all + * the available output is produced; this allows reading of truncated + * streams. + */ + ret = PNG_INFLATE(png_ptr, *chunk_bytes > 0 ? + Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH)); + } + while (ret == Z_OK && (*out_size > 0 || png_ptr->zstream.avail_out > 0)); + + *out_size += png_ptr->zstream.avail_out; + png_ptr->zstream.avail_out = 0; /* Should not be required, but is safe */ + + /* Ensure the error message pointer is always set: */ + png_zstream_error(png_ptr, ret); + return ret; + } + + else + { + png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed"); + return Z_STREAM_ERROR; + } +} +#endif /* READ_iCCP */ + +/* Read and check the IDHR chunk */ + +void /* PRIVATE */ +png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[13]; + png_uint_32 width, height; + int bit_depth, color_type, compression_type, filter_type; + int interlace_type; + + png_debug(1, "in png_handle_IHDR"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) != 0) + png_chunk_error(png_ptr, "out of place"); + + /* Check the length */ + if (length != 13) + png_chunk_error(png_ptr, "invalid"); + + png_ptr->mode |= PNG_HAVE_IHDR; + + png_crc_read(png_ptr, buf, 13); + png_crc_finish(png_ptr, 0); + + width = png_get_uint_31(png_ptr, buf); + height = png_get_uint_31(png_ptr, buf + 4); + bit_depth = buf[8]; + color_type = buf[9]; + compression_type = buf[10]; + filter_type = buf[11]; + interlace_type = buf[12]; + + /* Set internal variables */ + png_ptr->width = width; + png_ptr->height = height; + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->interlaced = (png_byte)interlace_type; + png_ptr->color_type = (png_byte)color_type; +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + + /* Find number of channels */ + switch (png_ptr->color_type) + { + default: /* invalid, png_set_IHDR calls png_error */ + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_PALETTE: + png_ptr->channels = 1; + break; + + case PNG_COLOR_TYPE_RGB: + png_ptr->channels = 3; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_ptr->channels = 2; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + png_ptr->channels = 4; + break; + } + + /* Set up other useful info */ + png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); + png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); + png_debug1(3, "channels = %d", png_ptr->channels); + png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes); + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, + color_type, interlace_type, compression_type, filter_type); +} + +/* Read and check the palette */ +void /* PRIVATE */ +png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_color palette[PNG_MAX_PALETTE_LENGTH]; + int max_palette_length, num, i; +#ifdef PNG_POINTER_INDEXING_SUPPORTED + png_colorp pal_ptr; +#endif + + png_debug(1, "in png_handle_PLTE"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + /* Moved to before the 'after IDAT' check below because otherwise duplicate + * PLTE chunks are potentially ignored (the spec says there shall not be more + * than one PLTE, the error is not treated as benign, so this check trumps + * the requirement that PLTE appears before IDAT.) + */ + else if ((png_ptr->mode & PNG_HAVE_PLTE) != 0) + png_chunk_error(png_ptr, "duplicate"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + /* This is benign because the non-benign error happened before, when an + * IDAT was encountered in a color-mapped image with no PLTE. + */ + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + png_ptr->mode |= PNG_HAVE_PLTE; + + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "ignored in grayscale PNG"); + return; + } + +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_crc_finish(png_ptr, length); + return; + } +#endif + + if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) + { + png_crc_finish(png_ptr, length); + + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + png_chunk_benign_error(png_ptr, "invalid"); + + else + png_chunk_error(png_ptr, "invalid"); + + return; + } + + /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */ + num = (int)length / 3; + + /* If the palette has 256 or fewer entries but is too large for the bit + * depth, we don't issue an error, to preserve the behavior of previous + * libpng versions. We silently truncate the unused extra palette entries + * here. + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + max_palette_length = (1 << png_ptr->bit_depth); + else + max_palette_length = PNG_MAX_PALETTE_LENGTH; + + if (num > max_palette_length) + num = max_palette_length; + +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + pal_ptr->red = buf[0]; + pal_ptr->green = buf[1]; + pal_ptr->blue = buf[2]; + } +#else + for (i = 0; i < num; i++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + /* Don't depend upon png_color being any order */ + palette[i].red = buf[0]; + palette[i].green = buf[1]; + palette[i].blue = buf[2]; + } +#endif + + /* If we actually need the PLTE chunk (ie for a paletted image), we do + * whatever the normal CRC configuration tells us. However, if we + * have an RGB image, the PLTE can be considered ancillary, so + * we will act as though it is. + */ +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#endif + { + png_crc_finish(png_ptr, (png_uint_32) (length - (unsigned int)num * 3)); + } + +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + else if (png_crc_error(png_ptr) != 0) /* Only if we have a CRC error */ + { + /* If we don't want to use the data from an ancillary chunk, + * we have two options: an error abort, or a warning and we + * ignore the data in this chunk (which should be OK, since + * it's considered ancillary for a RGB or RGBA image). + * + * IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the + * chunk type to determine whether to check the ancillary or the critical + * flags. + */ + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE) == 0) + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) != 0) + return; + + else + png_chunk_error(png_ptr, "CRC error"); + } + + /* Otherwise, we (optionally) emit a warning and use the chunk. */ + else if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0) + png_chunk_warning(png_ptr, "CRC error"); + } +#endif + + /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its + * own copy of the palette. This has the side effect that when png_start_row + * is called (this happens after any call to png_read_update_info) the + * info_ptr palette gets changed. This is extremely unexpected and + * confusing. + * + * Fix this by not sharing the palette in this way. + */ + png_set_PLTE(png_ptr, info_ptr, palette, num); + + /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before + * IDAT. Prior to 1.6.0 this was not checked; instead the code merely + * checked the apparent validity of a tRNS chunk inserted before PLTE on a + * palette PNG. 1.6.0 attempts to rigorously follow the standard and + * therefore does a benign error if the erroneous condition is detected *and* + * cancels the tRNS if the benign error returns. The alternative is to + * amend the standard since it would be rather hypocritical of the standards + * maintainers to ignore it. + */ +#ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->num_trans > 0 || + (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0)) + { + /* Cancel this because otherwise it would be used if the transforms + * require it. Don't cancel the 'valid' flag because this would prevent + * detection of duplicate chunks. + */ + png_ptr->num_trans = 0; + + if (info_ptr != NULL) + info_ptr->num_trans = 0; + + png_chunk_benign_error(png_ptr, "tRNS must be after"); + } +#endif + +#ifdef PNG_READ_hIST_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) + png_chunk_benign_error(png_ptr, "hIST must be after"); +#endif + +#ifdef PNG_READ_bKGD_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) + png_chunk_benign_error(png_ptr, "bKGD must be after"); +#endif +} + +void /* PRIVATE */ +png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_debug(1, "in png_handle_IEND"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0 || + (png_ptr->mode & PNG_HAVE_IDAT) == 0) + png_chunk_error(png_ptr, "out of place"); + + png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); + + png_crc_finish(png_ptr, length); + + if (length != 0) + png_chunk_benign_error(png_ptr, "invalid"); + + PNG_UNUSED(info_ptr) +} + +#ifdef PNG_READ_gAMA_SUPPORTED +void /* PRIVATE */ +png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_fixed_point igamma; + png_byte buf[4]; + + png_debug(1, "in png_handle_gAMA"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + if (length != 4) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 4); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + igamma = png_get_fixed_point(NULL, buf); + + png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma); + png_colorspace_sync(png_ptr, info_ptr); +} +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED +void /* PRIVATE */ +png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + unsigned int truelen, i; + png_byte sample_depth; + png_byte buf[4]; + + png_debug(1, "in png_handle_sBIT"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + truelen = 3; + sample_depth = 8; + } + + else + { + truelen = png_ptr->channels; + sample_depth = png_ptr->bit_depth; + } + + if (length != truelen || length > 4) + { + png_chunk_benign_error(png_ptr, "invalid"); + png_crc_finish(png_ptr, length); + return; + } + + buf[0] = buf[1] = buf[2] = buf[3] = sample_depth; + png_crc_read(png_ptr, buf, truelen); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + for (i=0; i sample_depth) + { + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + } + + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) + { + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[1]; + png_ptr->sig_bit.blue = buf[2]; + png_ptr->sig_bit.alpha = buf[3]; + } + + else + { + png_ptr->sig_bit.gray = buf[0]; + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[0]; + png_ptr->sig_bit.blue = buf[0]; + png_ptr->sig_bit.alpha = buf[1]; + } + + png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); +} +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED +void /* PRIVATE */ +png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[32]; + png_xy xy; + + png_debug(1, "in png_handle_cHRM"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + if (length != 32) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 32); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + xy.whitex = png_get_fixed_point(NULL, buf); + xy.whitey = png_get_fixed_point(NULL, buf + 4); + xy.redx = png_get_fixed_point(NULL, buf + 8); + xy.redy = png_get_fixed_point(NULL, buf + 12); + xy.greenx = png_get_fixed_point(NULL, buf + 16); + xy.greeny = png_get_fixed_point(NULL, buf + 20); + xy.bluex = png_get_fixed_point(NULL, buf + 24); + xy.bluey = png_get_fixed_point(NULL, buf + 28); + + if (xy.whitex == PNG_FIXED_ERROR || + xy.whitey == PNG_FIXED_ERROR || + xy.redx == PNG_FIXED_ERROR || + xy.redy == PNG_FIXED_ERROR || + xy.greenx == PNG_FIXED_ERROR || + xy.greeny == PNG_FIXED_ERROR || + xy.bluex == PNG_FIXED_ERROR || + xy.bluey == PNG_FIXED_ERROR) + { + png_chunk_benign_error(png_ptr, "invalid values"); + return; + } + + /* If a colorspace error has already been output skip this chunk */ + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) + return; + + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0) + { + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy, + 1/*prefer cHRM values*/); + png_colorspace_sync(png_ptr, info_ptr); +} +#endif + +#ifdef PNG_READ_sRGB_SUPPORTED +void /* PRIVATE */ +png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte intent; + + png_debug(1, "in png_handle_sRGB"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + if (length != 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, &intent, 1); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + /* If a colorspace error has already been output skip this chunk */ + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) + return; + + /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect + * this. + */ + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) != 0) + { + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + png_chunk_benign_error(png_ptr, "too many profiles"); + return; + } + + (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent); + png_colorspace_sync(png_ptr, info_ptr); +} +#endif /* READ_sRGB */ + +#ifdef PNG_READ_iCCP_SUPPORTED +void /* PRIVATE */ +png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +/* Note: this does not properly handle profiles that are > 64K under DOS */ +{ + png_const_charp errmsg = NULL; /* error message output, or no error */ + int finished = 0; /* crc checked */ + + png_debug(1, "in png_handle_iCCP"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + /* Consistent with all the above colorspace handling an obviously *invalid* + * chunk is just ignored, so does not invalidate the color space. An + * alternative is to set the 'invalid' flags at the start of this routine + * and only clear them in they were not set before and all the tests pass. + */ + + /* The keyword must be at least one character and there is a + * terminator (0) byte and the compression method byte, and the + * 'zlib' datastream is at least 11 bytes. + */ + if (length < 14) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too short"); + return; + } + + /* If a colorspace error has already been output skip this chunk */ + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) + { + png_crc_finish(png_ptr, length); + return; + } + + /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect + * this. + */ + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) == 0) + { + uInt read_length, keyword_length; + char keyword[81]; + + /* Find the keyword; the keyword plus separator and compression method + * bytes can be at most 81 characters long. + */ + read_length = 81; /* maximum */ + if (read_length > length) + read_length = (uInt)length; + + png_crc_read(png_ptr, (png_bytep)keyword, read_length); + length -= read_length; + + /* The minimum 'zlib' stream is assumed to be just the 2 byte header, + * 5 bytes minimum 'deflate' stream, and the 4 byte checksum. + */ + if (length < 11) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too short"); + return; + } + + keyword_length = 0; + while (keyword_length < 80 && keyword_length < read_length && + keyword[keyword_length] != 0) + ++keyword_length; + + /* TODO: make the keyword checking common */ + if (keyword_length >= 1 && keyword_length <= 79) + { + /* We only understand '0' compression - deflate - so if we get a + * different value we can't safely decode the chunk. + */ + if (keyword_length+1 < read_length && + keyword[keyword_length+1] == PNG_COMPRESSION_TYPE_BASE) + { + read_length -= keyword_length+2; + + if (png_inflate_claim(png_ptr, png_iCCP) == Z_OK) + { + Byte profile_header[132]={0}; + Byte local_buffer[PNG_INFLATE_BUF_SIZE]; + png_alloc_size_t size = (sizeof profile_header); + + png_ptr->zstream.next_in = (Bytef*)keyword + (keyword_length+2); + png_ptr->zstream.avail_in = read_length; + (void)png_inflate_read(png_ptr, local_buffer, + (sizeof local_buffer), &length, profile_header, &size, + 0/*finish: don't, because the output is too small*/); + + if (size == 0) + { + /* We have the ICC profile header; do the basic header checks. + */ + const png_uint_32 profile_length = + png_get_uint_32(profile_header); + + if (png_icc_check_length(png_ptr, &png_ptr->colorspace, + keyword, profile_length) != 0) + { + /* The length is apparently ok, so we can check the 132 + * byte header. + */ + if (png_icc_check_header(png_ptr, &png_ptr->colorspace, + keyword, profile_length, profile_header, + png_ptr->color_type) != 0) + { + /* Now read the tag table; a variable size buffer is + * needed at this point, allocate one for the whole + * profile. The header check has already validated + * that none of this stuff will overflow. + */ + const png_uint_32 tag_count = png_get_uint_32( + profile_header+128); + png_bytep profile = png_read_buffer(png_ptr, + profile_length, 2/*silent*/); + + if (profile != NULL) + { + memcpy(profile, profile_header, + (sizeof profile_header)); + + size = 12 * tag_count; + + (void)png_inflate_read(png_ptr, local_buffer, + (sizeof local_buffer), &length, + profile + (sizeof profile_header), &size, 0); + + /* Still expect a buffer error because we expect + * there to be some tag data! + */ + if (size == 0) + { + if (png_icc_check_tag_table(png_ptr, + &png_ptr->colorspace, keyword, profile_length, + profile) != 0) + { + /* The profile has been validated for basic + * security issues, so read the whole thing in. + */ + size = profile_length - (sizeof profile_header) + - 12 * tag_count; + + (void)png_inflate_read(png_ptr, local_buffer, + (sizeof local_buffer), &length, + profile + (sizeof profile_header) + + 12 * tag_count, &size, 1/*finish*/); + + if (length > 0 && !(png_ptr->flags & + PNG_FLAG_BENIGN_ERRORS_WARN)) + errmsg = "extra compressed data"; + + /* But otherwise allow extra data: */ + else if (size == 0) + { + if (length > 0) + { + /* This can be handled completely, so + * keep going. + */ + png_chunk_warning(png_ptr, + "extra compressed data"); + } + + png_crc_finish(png_ptr, length); + finished = 1; + +# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0 + /* Check for a match against sRGB */ + png_icc_set_sRGB(png_ptr, + &png_ptr->colorspace, profile, + png_ptr->zstream.adler); +# endif + + /* Steal the profile for info_ptr. */ + if (info_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, + PNG_FREE_ICCP, 0); + + info_ptr->iccp_name = png_voidcast(char*, + png_malloc_base(png_ptr, + keyword_length+1)); + if (info_ptr->iccp_name != NULL) + { + memcpy(info_ptr->iccp_name, keyword, + keyword_length+1); + info_ptr->iccp_proflen = + profile_length; + info_ptr->iccp_profile = profile; + png_ptr->read_buffer = NULL; /*steal*/ + info_ptr->free_me |= PNG_FREE_ICCP; + info_ptr->valid |= PNG_INFO_iCCP; + } + + else + { + png_ptr->colorspace.flags |= + PNG_COLORSPACE_INVALID; + errmsg = "out of memory"; + } + } + + /* else the profile remains in the read + * buffer which gets reused for subsequent + * chunks. + */ + + if (info_ptr != NULL) + png_colorspace_sync(png_ptr, info_ptr); + + if (errmsg == NULL) + { + png_ptr->zowner = 0; + return; + } + } + if (errmsg == NULL) + errmsg = png_ptr->zstream.msg; + } + /* else png_icc_check_tag_table output an error */ + } + else /* profile truncated */ + errmsg = png_ptr->zstream.msg; + } + + else + errmsg = "out of memory"; + } + + /* else png_icc_check_header output an error */ + } + + /* else png_icc_check_length output an error */ + } + + else /* profile truncated */ + errmsg = png_ptr->zstream.msg; + + /* Release the stream */ + png_ptr->zowner = 0; + } + + else /* png_inflate_claim failed */ + errmsg = png_ptr->zstream.msg; + } + + else + errmsg = "bad compression method"; /* or missing */ + } + + else + errmsg = "bad keyword"; + } + + else + errmsg = "too many profiles"; + + /* Failure: the reason is in 'errmsg' */ + if (finished == 0) + png_crc_finish(png_ptr, length); + + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + if (errmsg != NULL) /* else already output */ + png_chunk_benign_error(png_ptr, errmsg); +} +#endif /* READ_iCCP */ + +#ifdef PNG_READ_sPLT_SUPPORTED +void /* PRIVATE */ +png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_bytep entry_start, buffer; + png_sPLT_t new_palette; + png_sPLT_entryp pp; + png_uint_32 data_length; + int entry_size, i; + png_uint_32 skip = 0; + png_uint_32 dl; + png_size_t max_dl; + + png_debug(1, "in png_handle_sPLT"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for sPLT"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > 65535U) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too large to fit in memory"); + return; + } +#endif + + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + if (buffer == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + + /* WARNING: this may break if size_t is less than 32 bits; it is assumed + * that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a + * potential breakage point if the types in pngconf.h aren't exactly right. + */ + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, skip) != 0) + return; + + buffer[length] = 0; + + for (entry_start = buffer; *entry_start; entry_start++) + /* Empty loop to find end of name */ ; + + ++entry_start; + + /* A sample depth should follow the separator, and we should be on it */ + if (length < 2U || entry_start > buffer + (length - 2U)) + { + png_warning(png_ptr, "malformed sPLT chunk"); + return; + } + + new_palette.depth = *entry_start++; + entry_size = (new_palette.depth == 8 ? 6 : 10); + /* This must fit in a png_uint_32 because it is derived from the original + * chunk data length. + */ + data_length = length - (png_uint_32)(entry_start - buffer); + + /* Integrity-check the data length */ + if ((data_length % (unsigned int)entry_size) != 0) + { + png_warning(png_ptr, "sPLT chunk has bad length"); + return; + } + + dl = (png_uint_32)(data_length / (unsigned int)entry_size); + max_dl = PNG_SIZE_MAX / (sizeof (png_sPLT_entry)); + + if (dl > max_dl) + { + png_warning(png_ptr, "sPLT chunk too long"); + return; + } + + new_palette.nentries = (png_int_32)(data_length / (unsigned int)entry_size); + + new_palette.entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, + (png_alloc_size_t) new_palette.nentries * (sizeof (png_sPLT_entry))); + + if (new_palette.entries == NULL) + { + png_warning(png_ptr, "sPLT chunk requires too much memory"); + return; + } + +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0; i < new_palette.nentries; i++) + { + pp = new_palette.entries + i; + + if (new_palette.depth == 8) + { + pp->red = *entry_start++; + pp->green = *entry_start++; + pp->blue = *entry_start++; + pp->alpha = *entry_start++; + } + + else + { + pp->red = png_get_uint_16(entry_start); entry_start += 2; + pp->green = png_get_uint_16(entry_start); entry_start += 2; + pp->blue = png_get_uint_16(entry_start); entry_start += 2; + pp->alpha = png_get_uint_16(entry_start); entry_start += 2; + } + + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#else + pp = new_palette.entries; + + for (i = 0; i < new_palette.nentries; i++) + { + + if (new_palette.depth == 8) + { + pp[i].red = *entry_start++; + pp[i].green = *entry_start++; + pp[i].blue = *entry_start++; + pp[i].alpha = *entry_start++; + } + + else + { + pp[i].red = png_get_uint_16(entry_start); entry_start += 2; + pp[i].green = png_get_uint_16(entry_start); entry_start += 2; + pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; + pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; + } + + pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#endif + + /* Discard all chunk data except the name and stash that */ + new_palette.name = (png_charp)buffer; + + png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); + + png_free(png_ptr, new_palette.entries); +} +#endif /* READ_sPLT */ + +#ifdef PNG_READ_tRNS_SUPPORTED +void /* PRIVATE */ +png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_tRNS"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + png_byte buf[2]; + + if (length != 2) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 2); + png_ptr->num_trans = 1; + png_ptr->trans_color.gray = png_get_uint_16(buf); + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_byte buf[6]; + + if (length != 6) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, length); + png_ptr->num_trans = 1; + png_ptr->trans_color.red = png_get_uint_16(buf); + png_ptr->trans_color.green = png_get_uint_16(buf + 2); + png_ptr->trans_color.blue = png_get_uint_16(buf + 4); + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if ((png_ptr->mode & PNG_HAVE_PLTE) == 0) + { + /* TODO: is this actually an error in the ISO spec? */ + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + if (length > (unsigned int) png_ptr->num_palette || + length > (unsigned int) PNG_MAX_PALETTE_LENGTH || + length == 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, readbuf, length); + png_ptr->num_trans = (png_uint_16)length; + } + + else + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid with alpha channel"); + return; + } + + if (png_crc_finish(png_ptr, 0) != 0) + { + png_ptr->num_trans = 0; + return; + } + + /* TODO: this is a horrible side effect in the palette case because the + * png_struct ends up with a pointer to the tRNS buffer owned by the + * png_info. Fix this. + */ + png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, + &(png_ptr->trans_color)); +} +#endif + +#ifdef PNG_READ_bKGD_SUPPORTED +void /* PRIVATE */ +png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + unsigned int truelen; + png_byte buf[6]; + png_color_16 background; + + png_debug(1, "in png_handle_bKGD"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + (png_ptr->mode & PNG_HAVE_PLTE) == 0)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 1; + + else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) + truelen = 6; + + else + truelen = 2; + + if (length != truelen) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, truelen); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + /* We convert the index value into RGB components so that we can allow + * arbitrary RGB values for background when we have transparency, and + * so it is easy to determine the RGB values of the background color + * from the info_ptr struct. + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + background.index = buf[0]; + + if (info_ptr != NULL && info_ptr->num_palette != 0) + { + if (buf[0] >= info_ptr->num_palette) + { + png_chunk_benign_error(png_ptr, "invalid index"); + return; + } + + background.red = (png_uint_16)png_ptr->palette[buf[0]].red; + background.green = (png_uint_16)png_ptr->palette[buf[0]].green; + background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue; + } + + else + background.red = background.green = background.blue = 0; + + background.gray = 0; + } + + else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) /* GRAY */ + { + background.index = 0; + background.red = + background.green = + background.blue = + background.gray = png_get_uint_16(buf); + } + + else + { + background.index = 0; + background.red = png_get_uint_16(buf); + background.green = png_get_uint_16(buf + 2); + background.blue = png_get_uint_16(buf + 4); + background.gray = 0; + } + + png_set_bKGD(png_ptr, info_ptr, &background); +} +#endif + +#ifdef PNG_READ_eXIf_SUPPORTED +void /* PRIVATE */ +png_handle_eXIf(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + unsigned int i; + + png_debug(1, "in png_handle_eXIf"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + if (length < 2) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too short"); + return; + } + + else if (info_ptr == NULL || (info_ptr->valid & PNG_INFO_eXIf) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + info_ptr->free_me |= PNG_FREE_EXIF; + + info_ptr->eXIf_buf = png_voidcast(png_bytep, + png_malloc_warn(png_ptr, length)); + + if (info_ptr->eXIf_buf == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + for (i = 0; i < length; i++) + { + png_byte buf[1]; + png_crc_read(png_ptr, buf, 1); + info_ptr->eXIf_buf[i] = buf[0]; + if (i == 1 && buf[0] != 'M' && buf[0] != 'I' + && info_ptr->eXIf_buf[0] != buf[0]) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "incorrect byte-order specifier"); + png_free(png_ptr, info_ptr->eXIf_buf); + info_ptr->eXIf_buf = NULL; + return; + } + } + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + png_set_eXIf_1(png_ptr, info_ptr, length, info_ptr->eXIf_buf); + + png_free(png_ptr, info_ptr->eXIf_buf); + info_ptr->eXIf_buf = NULL; +} +#endif + +#ifdef PNG_READ_hIST_SUPPORTED +void /* PRIVATE */ +png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + unsigned int num, i; + png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_hIST"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 || + (png_ptr->mode & PNG_HAVE_PLTE) == 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + num = length / 2 ; + + if (num != (unsigned int) png_ptr->num_palette || + num > (unsigned int) PNG_MAX_PALETTE_LENGTH) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + for (i = 0; i < num; i++) + { + png_byte buf[2]; + + png_crc_read(png_ptr, buf, 2); + readbuf[i] = png_get_uint_16(buf); + } + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + png_set_hIST(png_ptr, info_ptr, readbuf); +} +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED +void /* PRIVATE */ +png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_uint_32 res_x, res_y; + int unit_type; + + png_debug(1, "in png_handle_pHYs"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (length != 9) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 9); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + res_x = png_get_uint_32(buf); + res_y = png_get_uint_32(buf + 4); + unit_type = buf[8]; + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); +} +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED +void /* PRIVATE */ +png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_int_32 offset_x, offset_y; + int unit_type; + + png_debug(1, "in png_handle_oFFs"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (length != 9) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 9); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + offset_x = png_get_int_32(buf); + offset_y = png_get_int_32(buf + 4); + unit_type = buf[8]; + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); +} +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED +/* Read the pCAL chunk (described in the PNG Extensions document) */ +void /* PRIVATE */ +png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_int_32 X0, X1; + png_byte type, nparams; + png_bytep buffer, buf, units, endptr; + png_charpp params; + int i; + + png_debug(1, "in png_handle_pCAL"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)", + length + 1); + + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + + if (buffer == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + buffer[length] = 0; /* Null terminate the last string */ + + png_debug(3, "Finding end of pCAL purpose string"); + for (buf = buffer; *buf; buf++) + /* Empty loop */ ; + + endptr = buffer + length; + + /* We need to have at least 12 bytes after the purpose string + * in order to get the parameter information. + */ + if (endptr - buf <= 12) + { + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_debug(3, "Reading pCAL X0, X1, type, nparams, and units"); + X0 = png_get_int_32((png_bytep)buf+1); + X1 = png_get_int_32((png_bytep)buf+5); + type = buf[9]; + nparams = buf[10]; + units = buf + 11; + + png_debug(3, "Checking pCAL equation type and number of parameters"); + /* Check that we have the right number of parameters for known + * equation types. + */ + if ((type == PNG_EQUATION_LINEAR && nparams != 2) || + (type == PNG_EQUATION_BASE_E && nparams != 3) || + (type == PNG_EQUATION_ARBITRARY && nparams != 3) || + (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) + { + png_chunk_benign_error(png_ptr, "invalid parameter count"); + return; + } + + else if (type >= PNG_EQUATION_LAST) + { + png_chunk_benign_error(png_ptr, "unrecognized equation type"); + } + + for (buf = units; *buf; buf++) + /* Empty loop to move past the units string. */ ; + + png_debug(3, "Allocating pCAL parameters array"); + + params = png_voidcast(png_charpp, png_malloc_warn(png_ptr, + nparams * (sizeof (png_charp)))); + + if (params == NULL) + { + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + /* Get pointers to the start of each parameter string. */ + for (i = 0; i < nparams; i++) + { + buf++; /* Skip the null string terminator from previous parameter. */ + + png_debug1(3, "Reading pCAL parameter %d", i); + + for (params[i] = (png_charp)buf; buf <= endptr && *buf != 0; buf++) + /* Empty loop to move past each parameter string */ ; + + /* Make sure we haven't run out of data yet */ + if (buf > endptr) + { + png_free(png_ptr, params); + png_chunk_benign_error(png_ptr, "invalid data"); + return; + } + } + + png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams, + (png_charp)units, params); + + png_free(png_ptr, params); +} +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED +/* Read the sCAL chunk */ +void /* PRIVATE */ +png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_bytep buffer; + png_size_t i; + int state; + + png_debug(1, "in png_handle_sCAL"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + /* Need unit type, width, \0, height: minimum 4 bytes */ + else if (length < 4) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)", + length + 1); + + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + + if (buffer == NULL) + { + png_chunk_benign_error(png_ptr, "out of memory"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buffer, length); + buffer[length] = 0; /* Null terminate the last string */ + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + /* Validate the unit. */ + if (buffer[0] != 1 && buffer[0] != 2) + { + png_chunk_benign_error(png_ptr, "invalid unit"); + return; + } + + /* Validate the ASCII numbers, need two ASCII numbers separated by + * a '\0' and they need to fit exactly in the chunk data. + */ + i = 1; + state = 0; + + if (png_check_fp_number((png_const_charp)buffer, length, &state, &i) == 0 || + i >= length || buffer[i++] != 0) + png_chunk_benign_error(png_ptr, "bad width format"); + + else if (PNG_FP_IS_POSITIVE(state) == 0) + png_chunk_benign_error(png_ptr, "non-positive width"); + + else + { + png_size_t heighti = i; + + state = 0; + if (png_check_fp_number((png_const_charp)buffer, length, + &state, &i) == 0 || i != length) + png_chunk_benign_error(png_ptr, "bad height format"); + + else if (PNG_FP_IS_POSITIVE(state) == 0) + png_chunk_benign_error(png_ptr, "non-positive height"); + + else + /* This is the (only) success case. */ + png_set_sCAL_s(png_ptr, info_ptr, buffer[0], + (png_charp)buffer+1, (png_charp)buffer+heighti); + } +} +#endif + +#ifdef PNG_READ_tIME_SUPPORTED +void /* PRIVATE */ +png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[7]; + png_time mod_time; + + png_debug(1, "in png_handle_tIME"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + png_ptr->mode |= PNG_AFTER_IDAT; + + if (length != 7) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 7); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + mod_time.second = buf[6]; + mod_time.minute = buf[5]; + mod_time.hour = buf[4]; + mod_time.day = buf[3]; + mod_time.month = buf[2]; + mod_time.year = png_get_uint_16(buf); + + png_set_tIME(png_ptr, info_ptr, &mod_time); +} +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED +/* Note: this does not properly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_text text_info; + png_bytep buffer; + png_charp key; + png_charp text; + png_uint_32 skip = 0; + + png_debug(1, "in png_handle_tEXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + return; + } + } +#endif + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + if (length > 65535U) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too large to fit in memory"); + return; + } +#endif + + buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + + if (buffer == NULL) + { + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, skip) != 0) + return; + + key = (png_charp)buffer; + key[length] = 0; + + for (text = key; *text; text++) + /* Empty loop to find end of key */ ; + + if (text != key + length) + text++; + + text_info.compression = PNG_TEXT_COMPRESSION_NONE; + text_info.key = key; + text_info.lang = NULL; + text_info.lang_key = NULL; + text_info.itxt_length = 0; + text_info.text = text; + text_info.text_length = strlen(text); + + if (png_set_text_2(png_ptr, info_ptr, &text_info, 1) != 0) + png_warning(png_ptr, "Insufficient memory to process text chunk"); +} +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +/* Note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_const_charp errmsg = NULL; + png_bytep buffer; + png_uint_32 keyword_length; + + png_debug(1, "in png_handle_zTXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + return; + } + } +#endif + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + png_ptr->mode |= PNG_AFTER_IDAT; + + /* Note, "length" is sufficient here; we won't be adding + * a null terminator later. + */ + buffer = png_read_buffer(png_ptr, length, 2/*silent*/); + + if (buffer == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + /* TODO: also check that the keyword contents match the spec! */ + for (keyword_length = 0; + keyword_length < length && buffer[keyword_length] != 0; + ++keyword_length) + /* Empty loop to find end of name */ ; + + if (keyword_length > 79 || keyword_length < 1) + errmsg = "bad keyword"; + + /* zTXt must have some LZ data after the keyword, although it may expand to + * zero bytes; we need a '\0' at the end of the keyword, the compression type + * then the LZ data: + */ + else if (keyword_length + 3 > length) + errmsg = "truncated"; + + else if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE) + errmsg = "unknown compression type"; + + else + { + png_alloc_size_t uncompressed_length = PNG_SIZE_MAX; + + /* TODO: at present png_decompress_chunk imposes a single application + * level memory limit, this should be split to different values for iCCP + * and text chunks. + */ + if (png_decompress_chunk(png_ptr, length, keyword_length+2, + &uncompressed_length, 1/*terminate*/) == Z_STREAM_END) + { + png_text text; + + if (png_ptr->read_buffer == NULL) + errmsg="Read failure in png_handle_zTXt"; + else + { + /* It worked; png_ptr->read_buffer now looks like a tEXt chunk + * except for the extra compression type byte and the fact that + * it isn't necessarily '\0' terminated. + */ + buffer = png_ptr->read_buffer; + buffer[uncompressed_length+(keyword_length+2)] = 0; + + text.compression = PNG_TEXT_COMPRESSION_zTXt; + text.key = (png_charp)buffer; + text.text = (png_charp)(buffer + keyword_length+2); + text.text_length = uncompressed_length; + text.itxt_length = 0; + text.lang = NULL; + text.lang_key = NULL; + + if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0) + errmsg = "insufficient memory"; + } + } + + else + errmsg = png_ptr->zstream.msg; + } + + if (errmsg != NULL) + png_chunk_benign_error(png_ptr, errmsg); +} +#endif + +#ifdef PNG_READ_iTXt_SUPPORTED +/* Note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_const_charp errmsg = NULL; + png_bytep buffer; + png_uint_32 prefix_length; + + png_debug(1, "in png_handle_iTXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + return; + } + } +#endif + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + png_ptr->mode |= PNG_AFTER_IDAT; + + buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + + if (buffer == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + /* First the keyword. */ + for (prefix_length=0; + prefix_length < length && buffer[prefix_length] != 0; + ++prefix_length) + /* Empty loop */ ; + + /* Perform a basic check on the keyword length here. */ + if (prefix_length > 79 || prefix_length < 1) + errmsg = "bad keyword"; + + /* Expect keyword, compression flag, compression type, language, translated + * keyword (both may be empty but are 0 terminated) then the text, which may + * be empty. + */ + else if (prefix_length + 5 > length) + errmsg = "truncated"; + + else if (buffer[prefix_length+1] == 0 || + (buffer[prefix_length+1] == 1 && + buffer[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE)) + { + int compressed = buffer[prefix_length+1] != 0; + png_uint_32 language_offset, translated_keyword_offset; + png_alloc_size_t uncompressed_length = 0; + + /* Now the language tag */ + prefix_length += 3; + language_offset = prefix_length; + + for (; prefix_length < length && buffer[prefix_length] != 0; + ++prefix_length) + /* Empty loop */ ; + + /* WARNING: the length may be invalid here, this is checked below. */ + translated_keyword_offset = ++prefix_length; + + for (; prefix_length < length && buffer[prefix_length] != 0; + ++prefix_length) + /* Empty loop */ ; + + /* prefix_length should now be at the trailing '\0' of the translated + * keyword, but it may already be over the end. None of this arithmetic + * can overflow because chunks are at most 2^31 bytes long, but on 16-bit + * systems the available allocation may overflow. + */ + ++prefix_length; + + if (compressed == 0 && prefix_length <= length) + uncompressed_length = length - prefix_length; + + else if (compressed != 0 && prefix_length < length) + { + uncompressed_length = PNG_SIZE_MAX; + + /* TODO: at present png_decompress_chunk imposes a single application + * level memory limit, this should be split to different values for + * iCCP and text chunks. + */ + if (png_decompress_chunk(png_ptr, length, prefix_length, + &uncompressed_length, 1/*terminate*/) == Z_STREAM_END) + buffer = png_ptr->read_buffer; + + else + errmsg = png_ptr->zstream.msg; + } + + else + errmsg = "truncated"; + + if (errmsg == NULL) + { + png_text text; + + buffer[uncompressed_length+prefix_length] = 0; + + if (compressed == 0) + text.compression = PNG_ITXT_COMPRESSION_NONE; + + else + text.compression = PNG_ITXT_COMPRESSION_zTXt; + + text.key = (png_charp)buffer; + text.lang = (png_charp)buffer + language_offset; + text.lang_key = (png_charp)buffer + translated_keyword_offset; + text.text = (png_charp)buffer + prefix_length; + text.text_length = 0; + text.itxt_length = uncompressed_length; + + if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0) + errmsg = "insufficient memory"; + } + } + + else + errmsg = "bad compression info"; + + if (errmsg != NULL) + png_chunk_benign_error(png_ptr, errmsg); +} +#endif + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +/* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */ +static int +png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) +{ + png_alloc_size_t limit = PNG_SIZE_MAX; + + if (png_ptr->unknown_chunk.data != NULL) + { + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_malloc_max > 0 && + png_ptr->user_chunk_malloc_max < limit) + limit = png_ptr->user_chunk_malloc_max; + +# elif PNG_USER_CHUNK_MALLOC_MAX > 0 + if (PNG_USER_CHUNK_MALLOC_MAX < limit) + limit = PNG_USER_CHUNK_MALLOC_MAX; +# endif + + if (length <= limit) + { + PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name); + /* The following is safe because of the PNG_SIZE_MAX init above */ + png_ptr->unknown_chunk.size = (png_size_t)length/*SAFE*/; + /* 'mode' is a flag array, only the bottom four bits matter here */ + png_ptr->unknown_chunk.location = (png_byte)png_ptr->mode/*SAFE*/; + + if (length == 0) + png_ptr->unknown_chunk.data = NULL; + + else + { + /* Do a 'warn' here - it is handled below. */ + png_ptr->unknown_chunk.data = png_voidcast(png_bytep, + png_malloc_warn(png_ptr, length)); + } + } + + if (png_ptr->unknown_chunk.data == NULL && length > 0) + { + /* This is benign because we clean up correctly */ + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "unknown chunk exceeds memory limits"); + return 0; + } + + else + { + if (length > 0) + png_crc_read(png_ptr, png_ptr->unknown_chunk.data, length); + png_crc_finish(png_ptr, 0); + return 1; + } +} +#endif /* READ_UNKNOWN_CHUNKS */ + +/* Handle an unknown, or known but disabled, chunk */ +void /* PRIVATE */ +png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, + png_uint_32 length, int keep) +{ + int handled = 0; /* the chunk was handled */ + + png_debug(1, "in png_handle_unknown"); + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + /* NOTE: this code is based on the code in libpng-1.4.12 except for fixing + * the bug which meant that setting a non-default behavior for a specific + * chunk would be ignored (the default was always used unless a user + * callback was installed). + * + * 'keep' is the value from the png_chunk_unknown_handling, the setting for + * this specific chunk_name, if PNG_HANDLE_AS_UNKNOWN_SUPPORTED, if not it + * will always be PNG_HANDLE_CHUNK_AS_DEFAULT and it needs to be set here. + * This is just an optimization to avoid multiple calls to the lookup + * function. + */ +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name); +# endif +# endif + + /* One of the following methods will read the chunk or skip it (at least one + * of these is always defined because this is the only way to switch on + * PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + */ +# ifdef PNG_READ_USER_CHUNKS_SUPPORTED + /* The user callback takes precedence over the chunk keep value, but the + * keep value is still required to validate a save of a critical chunk. + */ + if (png_ptr->read_user_chunk_fn != NULL) + { + if (png_cache_unknown_chunk(png_ptr, length) != 0) + { + /* Callback to user unknown chunk handler */ + int ret = (*(png_ptr->read_user_chunk_fn))(png_ptr, + &png_ptr->unknown_chunk); + + /* ret is: + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be discarded + * unless png_set_keep_unknown_chunks has been used to set + * a 'keep' behavior for this particular chunk, in which + * case that will be used. A critical chunk will cause an + * error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + */ + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + + else if (ret == 0) + { + /* If the keep value is 'default' or 'never' override it, but + * still error out on critical chunks unless the keep value is + * 'always' While this is weird it is the behavior in 1.4.12. + * A possible improvement would be to obey the value set for the + * chunk, but this would be an API change that would probably + * damage some applications. + * + * The png_app_warning below catches the case that matters, where + * the application has not set specific save or ignore for this + * chunk or global save or ignore. + */ + if (keep < PNG_HANDLE_CHUNK_IF_SAFE) + { +# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + if (png_ptr->unknown_default < PNG_HANDLE_CHUNK_IF_SAFE) + { + png_chunk_warning(png_ptr, "Saving unknown chunk:"); + png_app_warning(png_ptr, + "forcing save of an unhandled chunk;" + " please call png_set_keep_unknown_chunks"); + /* with keep = PNG_HANDLE_CHUNK_IF_SAFE */ + } +# endif + keep = PNG_HANDLE_CHUNK_IF_SAFE; + } + } + + else /* chunk was handled */ + { + handled = 1; + /* Critical chunks can be safely discarded at this point. */ + keep = PNG_HANDLE_CHUNK_NEVER; + } + } + + else + keep = PNG_HANDLE_CHUNK_NEVER; /* insufficient memory */ + } + + else + /* Use the SAVE_UNKNOWN_CHUNKS code or skip the chunk */ +# endif /* READ_USER_CHUNKS */ + +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + { + /* keep is currently just the per-chunk setting, if there was no + * setting change it to the global default now (not that this may + * still be AS_DEFAULT) then obtain the cache of the chunk if required, + * if not simply skip the chunk. + */ + if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT) + keep = png_ptr->unknown_default; + + if (keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_IF_SAFE && + PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))) + { + if (png_cache_unknown_chunk(png_ptr, length) == 0) + keep = PNG_HANDLE_CHUNK_NEVER; + } + + else + png_crc_finish(png_ptr, length); + } +# else +# ifndef PNG_READ_USER_CHUNKS_SUPPORTED +# error no method to support READ_UNKNOWN_CHUNKS +# endif + + { + /* If here there is no read callback pointer set and no support is + * compiled in to just save the unknown chunks, so simply skip this + * chunk. If 'keep' is something other than AS_DEFAULT or NEVER then + * the app has erroneously asked for unknown chunk saving when there + * is no support. + */ + if (keep > PNG_HANDLE_CHUNK_NEVER) + png_app_error(png_ptr, "no unknown chunk support available"); + + png_crc_finish(png_ptr, length); + } +# endif + +# ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + /* Now store the chunk in the chunk list if appropriate, and if the limits + * permit it. + */ + if (keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_IF_SAFE && + PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))) + { +# ifdef PNG_USER_LIMITS_SUPPORTED + switch (png_ptr->user_chunk_cache_max) + { + case 2: + png_ptr->user_chunk_cache_max = 1; + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + /* FALLTHROUGH */ + case 1: + /* NOTE: prior to 1.6.0 this case resulted in an unknown critical + * chunk being skipped, now there will be a hard error below. + */ + break; + + default: /* not at limit */ + --(png_ptr->user_chunk_cache_max); + /* FALLTHROUGH */ + case 0: /* no limit */ +# endif /* USER_LIMITS */ + /* Here when the limit isn't reached or when limits are compiled + * out; store the chunk. + */ + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + handled = 1; +# ifdef PNG_USER_LIMITS_SUPPORTED + break; + } +# endif + } +# else /* no store support: the chunk must be handled by the user callback */ + PNG_UNUSED(info_ptr) +# endif + + /* Regardless of the error handling below the cached data (if any) can be + * freed now. Notice that the data is not freed if there is a png_error, but + * it will be freed by destroy_read_struct. + */ + if (png_ptr->unknown_chunk.data != NULL) + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + +#else /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ + /* There is no support to read an unknown chunk, so just skip it. */ + png_crc_finish(png_ptr, length); + PNG_UNUSED(info_ptr) + PNG_UNUSED(keep) +#endif /* !READ_UNKNOWN_CHUNKS */ + + /* Check for unhandled critical chunks */ + if (handled == 0 && PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) + png_chunk_error(png_ptr, "unhandled critical chunk"); +} + +/* This function is called to verify that a chunk name is valid. + * This function can't have the "critical chunk check" incorporated + * into it, since in the future we will need to be able to call user + * functions to handle unknown critical chunks after we check that + * the chunk name itself is valid. + */ + +/* Bit hacking: the test for an invalid byte in the 4 byte chunk name is: + * + * ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + */ + +void /* PRIVATE */ +png_check_chunk_name(png_const_structrp png_ptr, const png_uint_32 chunk_name) +{ + int i; + png_uint_32 cn=chunk_name; + + png_debug(1, "in png_check_chunk_name"); + + for (i=1; i<=4; ++i) + { + int c = cn & 0xff; + + if (c < 65 || c > 122 || (c > 90 && c < 97)) + png_chunk_error(png_ptr, "invalid chunk type"); + + cn >>= 8; + } +} + +void /* PRIVATE */ +png_check_chunk_length(png_const_structrp png_ptr, const png_uint_32 length) +{ + png_alloc_size_t limit = PNG_UINT_31_MAX; + +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_malloc_max > 0 && + png_ptr->user_chunk_malloc_max < limit) + limit = png_ptr->user_chunk_malloc_max; +# elif PNG_USER_CHUNK_MALLOC_MAX > 0 + if (PNG_USER_CHUNK_MALLOC_MAX < limit) + limit = PNG_USER_CHUNK_MALLOC_MAX; +# endif + if (png_ptr->chunk_name == png_IDAT) + { + png_alloc_size_t idat_limit = PNG_UINT_31_MAX; + size_t row_factor = + (png_ptr->width * png_ptr->channels * (png_ptr->bit_depth > 8? 2: 1) + + 1 + (png_ptr->interlaced? 6: 0)); + if (png_ptr->height > PNG_UINT_32_MAX/row_factor) + idat_limit=PNG_UINT_31_MAX; + else + idat_limit = png_ptr->height * row_factor; + row_factor = row_factor > 32566? 32566 : row_factor; + idat_limit += 6 + 5*(idat_limit/row_factor+1); /* zlib+deflate overhead */ + idat_limit=idat_limit < PNG_UINT_31_MAX? idat_limit : PNG_UINT_31_MAX; + limit = limit < idat_limit? idat_limit : limit; + } + + if (length > limit) + { + png_debug2(0," length = %lu, limit = %lu", + (unsigned long)length,(unsigned long)limit); + png_chunk_error(png_ptr, "chunk data is too large"); + } +} + +/* Combines the row recently read in with the existing pixels in the row. This + * routine takes care of alpha and transparency if requested. This routine also + * handles the two methods of progressive display of interlaced images, + * depending on the 'display' value; if 'display' is true then the whole row + * (dp) is filled from the start by replicating the available pixels. If + * 'display' is false only those pixels present in the pass are filled in. + */ +void /* PRIVATE */ +png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) +{ + unsigned int pixel_depth = png_ptr->transformed_pixel_depth; + png_const_bytep sp = png_ptr->row_buf + 1; + png_alloc_size_t row_width = png_ptr->width; + unsigned int pass = png_ptr->pass; + png_bytep end_ptr = 0; + png_byte end_byte = 0; + unsigned int end_mask; + + png_debug(1, "in png_combine_row"); + + /* Added in 1.5.6: it should not be possible to enter this routine until at + * least one row has been read from the PNG data and transformed. + */ + if (pixel_depth == 0) + png_error(png_ptr, "internal row logic error"); + + /* Added in 1.5.4: the pixel depth should match the information returned by + * any call to png_read_update_info at this point. Do not continue if we got + * this wrong. + */ + if (png_ptr->info_rowbytes != 0 && png_ptr->info_rowbytes != + PNG_ROWBYTES(pixel_depth, row_width)) + png_error(png_ptr, "internal row size calculation error"); + + /* Don't expect this to ever happen: */ + if (row_width == 0) + png_error(png_ptr, "internal row width error"); + + /* Preserve the last byte in cases where only part of it will be overwritten, + * the multiply below may overflow, we don't care because ANSI-C guarantees + * we get the low bits. + */ + end_mask = (pixel_depth * row_width) & 7; + if (end_mask != 0) + { + /* end_ptr == NULL is a flag to say do nothing */ + end_ptr = dp + PNG_ROWBYTES(pixel_depth, row_width) - 1; + end_byte = *end_ptr; +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if ((png_ptr->transformations & PNG_PACKSWAP) != 0) + /* little-endian byte */ + end_mask = (unsigned int)(0xff << end_mask); + + else /* big-endian byte */ +# endif + end_mask = 0xff >> end_mask; + /* end_mask is now the bits to *keep* from the destination row */ + } + + /* For non-interlaced images this reduces to a memcpy(). A memcpy() + * will also happen if interlacing isn't supported or if the application + * does not call png_set_interlace_handling(). In the latter cases the + * caller just gets a sequence of the unexpanded rows from each interlace + * pass. + */ +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced != 0 && + (png_ptr->transformations & PNG_INTERLACE) != 0 && + pass < 6 && (display == 0 || + /* The following copies everything for 'display' on passes 0, 2 and 4. */ + (display == 1 && (pass & 1) != 0))) + { + /* Narrow images may have no bits in a pass; the caller should handle + * this, but this test is cheap: + */ + if (row_width <= PNG_PASS_START_COL(pass)) + return; + + if (pixel_depth < 8) + { + /* For pixel depths up to 4 bpp the 8-pixel mask can be expanded to fit + * into 32 bits, then a single loop over the bytes using the four byte + * values in the 32-bit mask can be used. For the 'display' option the + * expanded mask may also not require any masking within a byte. To + * make this work the PACKSWAP option must be taken into account - it + * simply requires the pixels to be reversed in each byte. + * + * The 'regular' case requires a mask for each of the first 6 passes, + * the 'display' case does a copy for the even passes in the range + * 0..6. This has already been handled in the test above. + * + * The masks are arranged as four bytes with the first byte to use in + * the lowest bits (little-endian) regardless of the order (PACKSWAP or + * not) of the pixels in each byte. + * + * NOTE: the whole of this logic depends on the caller of this function + * only calling it on rows appropriate to the pass. This function only + * understands the 'x' logic; the 'y' logic is handled by the caller. + * + * The following defines allow generation of compile time constant bit + * masks for each pixel depth and each possibility of swapped or not + * swapped bytes. Pass 'p' is in the range 0..6; 'x', a pixel index, + * is in the range 0..7; and the result is 1 if the pixel is to be + * copied in the pass, 0 if not. 'S' is for the sparkle method, 'B' + * for the block method. + * + * With some compilers a compile time expression of the general form: + * + * (shift >= 32) ? (a >> (shift-32)) : (b >> shift) + * + * Produces warnings with values of 'shift' in the range 33 to 63 + * because the right hand side of the ?: expression is evaluated by + * the compiler even though it isn't used. Microsoft Visual C (various + * versions) and the Intel C compiler are known to do this. To avoid + * this the following macros are used in 1.5.6. This is a temporary + * solution to avoid destabilizing the code during the release process. + */ +# if PNG_USE_COMPILE_TIME_MASKS +# define PNG_LSR(x,s) ((x)>>((s) & 0x1f)) +# define PNG_LSL(x,s) ((x)<<((s) & 0x1f)) +# else +# define PNG_LSR(x,s) ((x)>>(s)) +# define PNG_LSL(x,s) ((x)<<(s)) +# endif +# define S_COPY(p,x) (((p)<4 ? PNG_LSR(0x80088822,(3-(p))*8+(7-(x))) :\ + PNG_LSR(0xaa55ff00,(7-(p))*8+(7-(x)))) & 1) +# define B_COPY(p,x) (((p)<4 ? PNG_LSR(0xff0fff33,(3-(p))*8+(7-(x))) :\ + PNG_LSR(0xff55ff00,(7-(p))*8+(7-(x)))) & 1) + + /* Return a mask for pass 'p' pixel 'x' at depth 'd'. The mask is + * little endian - the first pixel is at bit 0 - however the extra + * parameter 's' can be set to cause the mask position to be swapped + * within each byte, to match the PNG format. This is done by XOR of + * the shift with 7, 6 or 4 for bit depths 1, 2 and 4. + */ +# define PIXEL_MASK(p,x,d,s) \ + (PNG_LSL(((PNG_LSL(1U,(d)))-1),(((x)*(d))^((s)?8-(d):0)))) + + /* Hence generate the appropriate 'block' or 'sparkle' pixel copy mask. + */ +# define S_MASKx(p,x,d,s) (S_COPY(p,x)?PIXEL_MASK(p,x,d,s):0) +# define B_MASKx(p,x,d,s) (B_COPY(p,x)?PIXEL_MASK(p,x,d,s):0) + + /* Combine 8 of these to get the full mask. For the 1-bpp and 2-bpp + * cases the result needs replicating, for the 4-bpp case the above + * generates a full 32 bits. + */ +# define MASK_EXPAND(m,d) ((m)*((d)==1?0x01010101:((d)==2?0x00010001:1))) + +# define S_MASK(p,d,s) MASK_EXPAND(S_MASKx(p,0,d,s) + S_MASKx(p,1,d,s) +\ + S_MASKx(p,2,d,s) + S_MASKx(p,3,d,s) + S_MASKx(p,4,d,s) +\ + S_MASKx(p,5,d,s) + S_MASKx(p,6,d,s) + S_MASKx(p,7,d,s), d) + +# define B_MASK(p,d,s) MASK_EXPAND(B_MASKx(p,0,d,s) + B_MASKx(p,1,d,s) +\ + B_MASKx(p,2,d,s) + B_MASKx(p,3,d,s) + B_MASKx(p,4,d,s) +\ + B_MASKx(p,5,d,s) + B_MASKx(p,6,d,s) + B_MASKx(p,7,d,s), d) + +#if PNG_USE_COMPILE_TIME_MASKS + /* Utility macros to construct all the masks for a depth/swap + * combination. The 's' parameter says whether the format is PNG + * (big endian bytes) or not. Only the three odd-numbered passes are + * required for the display/block algorithm. + */ +# define S_MASKS(d,s) { S_MASK(0,d,s), S_MASK(1,d,s), S_MASK(2,d,s),\ + S_MASK(3,d,s), S_MASK(4,d,s), S_MASK(5,d,s) } + +# define B_MASKS(d,s) { B_MASK(1,d,s), B_MASK(3,d,s), B_MASK(5,d,s) } + +# define DEPTH_INDEX(d) ((d)==1?0:((d)==2?1:2)) + + /* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and + * then pass: + */ + static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] = + { + /* Little-endian byte masks for PACKSWAP */ + { S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) }, + /* Normal (big-endian byte) masks - PNG format */ + { S_MASKS(1,1), S_MASKS(2,1), S_MASKS(4,1) } + }; + + /* display_mask has only three entries for the odd passes, so index by + * pass>>1. + */ + static PNG_CONST png_uint_32 display_mask[2][3][3] = + { + /* Little-endian byte masks for PACKSWAP */ + { B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) }, + /* Normal (big-endian byte) masks - PNG format */ + { B_MASKS(1,1), B_MASKS(2,1), B_MASKS(4,1) } + }; + +# define MASK(pass,depth,display,png)\ + ((display)?display_mask[png][DEPTH_INDEX(depth)][pass>>1]:\ + row_mask[png][DEPTH_INDEX(depth)][pass]) + +#else /* !PNG_USE_COMPILE_TIME_MASKS */ + /* This is the runtime alternative: it seems unlikely that this will + * ever be either smaller or faster than the compile time approach. + */ +# define MASK(pass,depth,display,png)\ + ((display)?B_MASK(pass,depth,png):S_MASK(pass,depth,png)) +#endif /* !USE_COMPILE_TIME_MASKS */ + + /* Use the appropriate mask to copy the required bits. In some cases + * the byte mask will be 0 or 0xff; optimize these cases. row_width is + * the number of pixels, but the code copies bytes, so it is necessary + * to special case the end. + */ + png_uint_32 pixels_per_byte = 8 / pixel_depth; + png_uint_32 mask; + +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if ((png_ptr->transformations & PNG_PACKSWAP) != 0) + mask = MASK(pass, pixel_depth, display, 0); + + else +# endif + mask = MASK(pass, pixel_depth, display, 1); + + for (;;) + { + png_uint_32 m; + + /* It doesn't matter in the following if png_uint_32 has more than + * 32 bits because the high bits always match those in m<<24; it is, + * however, essential to use OR here, not +, because of this. + */ + m = mask; + mask = (m >> 8) | (m << 24); /* rotate right to good compilers */ + m &= 0xff; + + if (m != 0) /* something to copy */ + { + if (m != 0xff) + *dp = (png_byte)((*dp & ~m) | (*sp & m)); + else + *dp = *sp; + } + + /* NOTE: this may overwrite the last byte with garbage if the image + * is not an exact number of bytes wide; libpng has always done + * this. + */ + if (row_width <= pixels_per_byte) + break; /* May need to restore part of the last byte */ + + row_width -= pixels_per_byte; + ++dp; + ++sp; + } + } + + else /* pixel_depth >= 8 */ + { + unsigned int bytes_to_copy, bytes_to_jump; + + /* Validate the depth - it must be a multiple of 8 */ + if (pixel_depth & 7) + png_error(png_ptr, "invalid user transform pixel depth"); + + pixel_depth >>= 3; /* now in bytes */ + row_width *= pixel_depth; + + /* Regardless of pass number the Adam 7 interlace always results in a + * fixed number of pixels to copy then to skip. There may be a + * different number of pixels to skip at the start though. + */ + { + unsigned int offset = PNG_PASS_START_COL(pass) * pixel_depth; + + row_width -= offset; + dp += offset; + sp += offset; + } + + /* Work out the bytes to copy. */ + if (display != 0) + { + /* When doing the 'block' algorithm the pixel in the pass gets + * replicated to adjacent pixels. This is why the even (0,2,4,6) + * passes are skipped above - the entire expanded row is copied. + */ + bytes_to_copy = (1<<((6-pass)>>1)) * pixel_depth; + + /* But don't allow this number to exceed the actual row width. */ + if (bytes_to_copy > row_width) + bytes_to_copy = (unsigned int)/*SAFE*/row_width; + } + + else /* normal row; Adam7 only ever gives us one pixel to copy. */ + bytes_to_copy = pixel_depth; + + /* In Adam7 there is a constant offset between where the pixels go. */ + bytes_to_jump = PNG_PASS_COL_OFFSET(pass) * pixel_depth; + + /* And simply copy these bytes. Some optimization is possible here, + * depending on the value of 'bytes_to_copy'. Special case the low + * byte counts, which we know to be frequent. + * + * Notice that these cases all 'return' rather than 'break' - this + * avoids an unnecessary test on whether to restore the last byte + * below. + */ + switch (bytes_to_copy) + { + case 1: + for (;;) + { + *dp = *sp; + + if (row_width <= bytes_to_jump) + return; + + dp += bytes_to_jump; + sp += bytes_to_jump; + row_width -= bytes_to_jump; + } + + case 2: + /* There is a possibility of a partial copy at the end here; this + * slows the code down somewhat. + */ + do + { + dp[0] = sp[0]; dp[1] = sp[1]; + + if (row_width <= bytes_to_jump) + return; + + sp += bytes_to_jump; + dp += bytes_to_jump; + row_width -= bytes_to_jump; + } + while (row_width > 1); + + /* And there can only be one byte left at this point: */ + *dp = *sp; + return; + + case 3: + /* This can only be the RGB case, so each copy is exactly one + * pixel and it is not necessary to check for a partial copy. + */ + for (;;) + { + dp[0] = sp[0]; dp[1] = sp[1]; dp[2] = sp[2]; + + if (row_width <= bytes_to_jump) + return; + + sp += bytes_to_jump; + dp += bytes_to_jump; + row_width -= bytes_to_jump; + } + + default: +#if PNG_ALIGN_TYPE != PNG_ALIGN_NONE + /* Check for double byte alignment and, if possible, use a + * 16-bit copy. Don't attempt this for narrow images - ones that + * are less than an interlace panel wide. Don't attempt it for + * wide bytes_to_copy either - use the memcpy there. + */ + if (bytes_to_copy < 16 /*else use memcpy*/ && + png_isaligned(dp, png_uint_16) && + png_isaligned(sp, png_uint_16) && + bytes_to_copy % (sizeof (png_uint_16)) == 0 && + bytes_to_jump % (sizeof (png_uint_16)) == 0) + { + /* Everything is aligned for png_uint_16 copies, but try for + * png_uint_32 first. + */ + if (png_isaligned(dp, png_uint_32) && + png_isaligned(sp, png_uint_32) && + bytes_to_copy % (sizeof (png_uint_32)) == 0 && + bytes_to_jump % (sizeof (png_uint_32)) == 0) + { + png_uint_32p dp32 = png_aligncast(png_uint_32p,dp); + png_const_uint_32p sp32 = png_aligncastconst( + png_const_uint_32p, sp); + size_t skip = (bytes_to_jump-bytes_to_copy) / + (sizeof (png_uint_32)); + + do + { + size_t c = bytes_to_copy; + do + { + *dp32++ = *sp32++; + c -= (sizeof (png_uint_32)); + } + while (c > 0); + + if (row_width <= bytes_to_jump) + return; + + dp32 += skip; + sp32 += skip; + row_width -= bytes_to_jump; + } + while (bytes_to_copy <= row_width); + + /* Get to here when the row_width truncates the final copy. + * There will be 1-3 bytes left to copy, so don't try the + * 16-bit loop below. + */ + dp = (png_bytep)dp32; + sp = (png_const_bytep)sp32; + do + *dp++ = *sp++; + while (--row_width > 0); + return; + } + + /* Else do it in 16-bit quantities, but only if the size is + * not too large. + */ + else + { + png_uint_16p dp16 = png_aligncast(png_uint_16p, dp); + png_const_uint_16p sp16 = png_aligncastconst( + png_const_uint_16p, sp); + size_t skip = (bytes_to_jump-bytes_to_copy) / + (sizeof (png_uint_16)); + + do + { + size_t c = bytes_to_copy; + do + { + *dp16++ = *sp16++; + c -= (sizeof (png_uint_16)); + } + while (c > 0); + + if (row_width <= bytes_to_jump) + return; + + dp16 += skip; + sp16 += skip; + row_width -= bytes_to_jump; + } + while (bytes_to_copy <= row_width); + + /* End of row - 1 byte left, bytes_to_copy > row_width: */ + dp = (png_bytep)dp16; + sp = (png_const_bytep)sp16; + do + *dp++ = *sp++; + while (--row_width > 0); + return; + } + } +#endif /* ALIGN_TYPE code */ + + /* The true default - use a memcpy: */ + for (;;) + { + memcpy(dp, sp, bytes_to_copy); + + if (row_width <= bytes_to_jump) + return; + + sp += bytes_to_jump; + dp += bytes_to_jump; + row_width -= bytes_to_jump; + if (bytes_to_copy > row_width) + bytes_to_copy = (unsigned int)/*SAFE*/row_width; + } + } + + /* NOT REACHED*/ + } /* pixel_depth >= 8 */ + + /* Here if pixel_depth < 8 to check 'end_ptr' below. */ + } + else +#endif /* READ_INTERLACING */ + + /* If here then the switch above wasn't used so just memcpy the whole row + * from the temporary row buffer (notice that this overwrites the end of the + * destination row if it is a partial byte.) + */ + memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width)); + + /* Restore the overwritten bits from the last byte if necessary. */ + if (end_ptr != NULL) + *end_ptr = (png_byte)((end_byte & end_mask) | (*end_ptr & ~end_mask)); +} + +#ifdef PNG_READ_INTERLACING_SUPPORTED +void /* PRIVATE */ +png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, + png_uint_32 transformations /* Because these may affect the byte layout */) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* Offset to next interlace block */ + static PNG_CONST unsigned int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + png_debug(1, "in png_do_read_interlace"); + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); + unsigned int sshift, dshift; + unsigned int s_start, s_end; + int s_inc; + int jstop = (int)png_pass_inc[pass]; + png_byte v; + png_uint_32 i; + int j; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if ((transformations & PNG_PACKSWAP) != 0) + { + sshift = ((row_info->width + 7) & 0x07); + dshift = ((final_width + 7) & 0x07); + s_start = 7; + s_end = 0; + s_inc = -1; + } + + else +#endif + { + sshift = 7 - ((row_info->width + 7) & 0x07); + dshift = 7 - ((final_width + 7) & 0x07); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = 0; i < row_info->width; i++) + { + v = (png_byte)((*sp >> sshift) & 0x01); + for (j = 0; j < jstop; j++) + { + unsigned int tmp = *dp & (0x7f7f >> (7 - dshift)); + tmp |= (unsigned int)(v << dshift); + *dp = (png_byte)(tmp & 0xff); + + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + + else + dshift = (unsigned int)((int)dshift + s_inc); + } + + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + + else + sshift = (unsigned int)((int)sshift + s_inc); + } + break; + } + + case 2: + { + png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); + png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); + unsigned int sshift, dshift; + unsigned int s_start, s_end; + int s_inc; + int jstop = (int)png_pass_inc[pass]; + png_uint_32 i; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if ((transformations & PNG_PACKSWAP) != 0) + { + sshift = (((row_info->width + 3) & 0x03) << 1); + dshift = (((final_width + 3) & 0x03) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + + else +#endif + { + sshift = ((3 - ((row_info->width + 3) & 0x03)) << 1); + dshift = ((3 - ((final_width + 3) & 0x03)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x03); + for (j = 0; j < jstop; j++) + { + unsigned int tmp = *dp & (0x3f3f >> (6 - dshift)); + tmp |= (unsigned int)(v << dshift); + *dp = (png_byte)(tmp & 0xff); + + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + + else + dshift = (unsigned int)((int)dshift + s_inc); + } + + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + + else + sshift = (unsigned int)((int)sshift + s_inc); + } + break; + } + + case 4: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); + unsigned int sshift, dshift; + unsigned int s_start, s_end; + int s_inc; + png_uint_32 i; + int jstop = (int)png_pass_inc[pass]; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if ((transformations & PNG_PACKSWAP) != 0) + { + sshift = (((row_info->width + 1) & 0x01) << 2); + dshift = (((final_width + 1) & 0x01) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + + else +#endif + { + sshift = ((1 - ((row_info->width + 1) & 0x01)) << 2); + dshift = ((1 - ((final_width + 1) & 0x01)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v = (png_byte)((*sp >> sshift) & 0x0f); + int j; + + for (j = 0; j < jstop; j++) + { + unsigned int tmp = *dp & (0xf0f >> (4 - dshift)); + tmp |= (unsigned int)(v << dshift); + *dp = (png_byte)(tmp & 0xff); + + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + + else + dshift = (unsigned int)((int)dshift + s_inc); + } + + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + + else + sshift = (unsigned int)((int)sshift + s_inc); + } + break; + } + + default: + { + png_size_t pixel_bytes = (row_info->pixel_depth >> 3); + + png_bytep sp = row + (png_size_t)(row_info->width - 1) + * pixel_bytes; + + png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; + + int jstop = (int)png_pass_inc[pass]; + png_uint_32 i; + + for (i = 0; i < row_info->width; i++) + { + png_byte v[8]; /* SAFE; pixel_depth does not exceed 64 */ + int j; + + memcpy(v, sp, pixel_bytes); + + for (j = 0; j < jstop; j++) + { + memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + + sp -= pixel_bytes; + } + break; + } + } + + row_info->width = final_width; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width); + } +#ifndef PNG_READ_PACKSWAP_SUPPORTED + PNG_UNUSED(transformations) /* Silence compiler warning */ +#endif +} +#endif /* READ_INTERLACING */ + +static void +png_read_filter_row_sub(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i; + png_size_t istop = row_info->rowbytes; + unsigned int bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + + PNG_UNUSED(prev_row) + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } +} + +static void +png_read_filter_row_up(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i; + png_size_t istop = row_info->rowbytes; + png_bytep rp = row; + png_const_bytep pp = prev_row; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } +} + +static void +png_read_filter_row_avg(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i; + png_bytep rp = row; + png_const_bytep pp = prev_row; + unsigned int bpp = (row_info->pixel_depth + 7) >> 3; + png_size_t istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } +} + +static void +png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_bytep rp_end = row + row_info->rowbytes; + int a, c; + + /* First pixel/byte */ + c = *prev_row++; + a = *row + c; + *row++ = (png_byte)a; + + /* Remainder */ + while (row < rp_end) + { + int b, pa, pb, pc, p; + + a &= 0xff; /* From previous iteration or start */ + b = *prev_row++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* Find the best predictor, the least of pa, pb, pc favoring the earlier + * ones in the case of a tie. + */ + if (pb < pa) + { + pa = pb; a = b; + } + if (pc < pa) a = c; + + /* Calculate the current pixel in a, and move the previous row pixel to c + * for the next time round the loop + */ + c = b; + a += *row; + *row++ = (png_byte)a; + } +} + +static void +png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + unsigned int bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp_end = row + bpp; + + /* Process the first pixel in the row completely (this is the same as 'up' + * because there is only one candidate predictor for the first row). + */ + while (row < rp_end) + { + int a = *row + *prev_row++; + *row++ = (png_byte)a; + } + + /* Remainder */ + rp_end = rp_end + (row_info->rowbytes - bpp); + + while (row < rp_end) + { + int a, b, c, pa, pb, pc, p; + + c = *(prev_row - bpp); + a = *(row - bpp); + b = *prev_row++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + if (pb < pa) + { + pa = pb; a = b; + } + if (pc < pa) a = c; + + a += *row; + *row++ = (png_byte)a; + } +} + +static void +png_init_filter_functions(png_structrp pp) + /* This function is called once for every PNG image (except for PNG images + * that only use PNG_FILTER_VALUE_NONE for all rows) to set the + * implementations required to reverse the filtering of PNG rows. Reversing + * the filter is the first transformation performed on the row data. It is + * performed in place, therefore an implementation can be selected based on + * the image pixel format. If the implementation depends on image width then + * take care to ensure that it works correctly if the image is interlaced - + * interlacing causes the actual row width to vary. + */ +{ + unsigned int bpp = (pp->pixel_depth + 7) >> 3; + + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub; + pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg; + if (bpp == 1) + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth_1byte_pixel; + else + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth_multibyte_pixel; + +#ifdef PNG_FILTER_OPTIMIZATIONS + /* To use this define PNG_FILTER_OPTIMIZATIONS as the name of a function to + * call to install hardware optimizations for the above functions; simply + * replace whatever elements of the pp->read_filter[] array with a hardware + * specific (or, for that matter, generic) optimization. + * + * To see an example of this examine what configure.ac does when + * --enable-arm-neon is specified on the command line. + */ + PNG_FILTER_OPTIMIZATIONS(pp, bpp); +#endif +} + +void /* PRIVATE */ +png_read_filter_row(png_structrp pp, png_row_infop row_info, png_bytep row, + png_const_bytep prev_row, int filter) +{ + /* OPTIMIZATION: DO NOT MODIFY THIS FUNCTION, instead #define + * PNG_FILTER_OPTIMIZATIONS to a function that overrides the generic + * implementations. See png_init_filter_functions above. + */ + if (filter > PNG_FILTER_VALUE_NONE && filter < PNG_FILTER_VALUE_LAST) + { + if (pp->read_filter[0] == NULL) + png_init_filter_functions(pp); + + pp->read_filter[filter-1](row_info, row, prev_row); + } +} + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +void /* PRIVATE */ +png_read_IDAT_data(png_structrp png_ptr, png_bytep output, + png_alloc_size_t avail_out) +{ + /* Loop reading IDATs and decompressing the result into output[avail_out] */ + png_ptr->zstream.next_out = output; + png_ptr->zstream.avail_out = 0; /* safety: set below */ + + if (output == NULL) + avail_out = 0; + + do + { + int ret; + png_byte tmpbuf[PNG_INFLATE_BUF_SIZE]; + + if (png_ptr->zstream.avail_in == 0) + { + uInt avail_in; + png_bytep buffer; + + while (png_ptr->idat_size == 0) + { + png_crc_finish(png_ptr, 0); + + png_ptr->idat_size = png_read_chunk_header(png_ptr); + /* This is an error even in the 'check' case because the code just + * consumed a non-IDAT header. + */ + if (png_ptr->chunk_name != png_IDAT) + png_error(png_ptr, "Not enough image data"); + } + + avail_in = png_ptr->IDAT_read_size; + + if (avail_in > png_ptr->idat_size) + avail_in = (uInt)png_ptr->idat_size; + + /* A PNG with a gradually increasing IDAT size will defeat this attempt + * to minimize memory usage by causing lots of re-allocs, but + * realistically doing IDAT_read_size re-allocs is not likely to be a + * big problem. + */ + buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/); + + png_crc_read(png_ptr, buffer, avail_in); + png_ptr->idat_size -= avail_in; + + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = avail_in; + } + + /* And set up the output side. */ + if (output != NULL) /* standard read */ + { + uInt out = ZLIB_IO_MAX; + + if (out > avail_out) + out = (uInt)avail_out; + + avail_out -= out; + png_ptr->zstream.avail_out = out; + } + + else /* after last row, checking for end */ + { + png_ptr->zstream.next_out = tmpbuf; + png_ptr->zstream.avail_out = (sizeof tmpbuf); + } + + /* Use NO_FLUSH; this gives zlib the maximum opportunity to optimize the + * process. If the LZ stream is truncated the sequential reader will + * terminally damage the stream, above, by reading the chunk header of the + * following chunk (it then exits with png_error). + * + * TODO: deal more elegantly with truncated IDAT lists. + */ + ret = PNG_INFLATE(png_ptr, Z_NO_FLUSH); + + /* Take the unconsumed output back. */ + if (output != NULL) + avail_out += png_ptr->zstream.avail_out; + + else /* avail_out counts the extra bytes */ + avail_out += (sizeof tmpbuf) - png_ptr->zstream.avail_out; + + png_ptr->zstream.avail_out = 0; + + if (ret == Z_STREAM_END) + { + /* Do this for safety; we won't read any more into this row. */ + png_ptr->zstream.next_out = NULL; + + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + + if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0) + png_chunk_benign_error(png_ptr, "Extra compressed data"); + break; + } + + if (ret != Z_OK) + { + png_zstream_error(png_ptr, ret); + + if (output != NULL) + png_chunk_error(png_ptr, png_ptr->zstream.msg); + + else /* checking */ + { + png_chunk_benign_error(png_ptr, png_ptr->zstream.msg); + return; + } + } + } while (avail_out > 0); + + if (avail_out > 0) + { + /* The stream ended before the image; this is the same as too few IDATs so + * should be handled the same way. + */ + if (output != NULL) + png_error(png_ptr, "Not enough image data"); + + else /* the deflate stream contained extra data */ + png_chunk_benign_error(png_ptr, "Too much image data"); + } +} + +void /* PRIVATE */ +png_read_finish_IDAT(png_structrp png_ptr) +{ + /* We don't need any more data and the stream should have ended, however the + * LZ end code may actually not have been processed. In this case we must + * read it otherwise stray unread IDAT data or, more likely, an IDAT chunk + * may still remain to be consumed. + */ + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0) + { + /* The NULL causes png_read_IDAT_data to swallow any remaining bytes in + * the compressed stream, but the stream may be damaged too, so even after + * this call we may need to terminate the zstream ownership. + */ + png_read_IDAT_data(png_ptr, NULL, 0); + png_ptr->zstream.next_out = NULL; /* safety */ + + /* Now clear everything out for safety; the following may not have been + * done. + */ + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0) + { + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + } + } + + /* If the zstream has not been released do it now *and* terminate the reading + * of the final IDAT chunk. + */ + if (png_ptr->zowner == png_IDAT) + { + /* Always do this; the pointers otherwise point into the read buffer. */ + png_ptr->zstream.next_in = NULL; + png_ptr->zstream.avail_in = 0; + + /* Now we no longer own the zstream. */ + png_ptr->zowner = 0; + + /* The slightly weird semantics of the sequential IDAT reading is that we + * are always in or at the end of an IDAT chunk, so we always need to do a + * crc_finish here. If idat_size is non-zero we also need to read the + * spurious bytes at the end of the chunk now. + */ + (void)png_crc_finish(png_ptr, png_ptr->idat_size); + } +} + +void /* PRIVATE */ +png_read_finish_row(png_structrp png_ptr) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; + + png_debug(1, "in png_read_finish_row"); + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + + if (png_ptr->interlaced != 0) + { + png_ptr->row_number = 0; + + /* TO DO: don't do this if prev_row isn't needed (requires + * read-ahead of the next row's filter byte. + */ + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + do + { + png_ptr->pass++; + + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + if ((png_ptr->transformations & PNG_INTERLACE) == 0) + { + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + } + + else /* if (png_ptr->transformations & PNG_INTERLACE) */ + break; /* libpng deinterlacing sees every row */ + + } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0); + + if (png_ptr->pass < 7) + return; + } + + /* Here after at the end of the last row of the last pass. */ + png_read_finish_IDAT(png_ptr); +} +#endif /* SEQUENTIAL_READ */ + +void /* PRIVATE */ +png_read_start_row(png_structrp png_ptr) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; + + unsigned int max_pixel_depth; + png_size_t row_bytes; + + png_debug(1, "in png_read_start_row"); + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED + png_init_read_transformations(png_ptr); +#endif + if (png_ptr->interlaced != 0) + { + if ((png_ptr->transformations & PNG_INTERLACE) == 0) + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + + else + png_ptr->num_rows = png_ptr->height; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + } + + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->iwidth = png_ptr->width; + } + + max_pixel_depth = (unsigned int)png_ptr->pixel_depth; + + /* WARNING: * png_read_transform_info (pngrtran.c) performs a simpler set of + * calculations to calculate the final pixel depth, then + * png_do_read_transforms actually does the transforms. This means that the + * code which effectively calculates this value is actually repeated in three + * separate places. They must all match. Innocent changes to the order of + * transformations can and will break libpng in a way that causes memory + * overwrites. + * + * TODO: fix this. + */ +#ifdef PNG_READ_PACK_SUPPORTED + if ((png_ptr->transformations & PNG_PACK) != 0 && png_ptr->bit_depth < 8) + max_pixel_depth = 8; +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED + if ((png_ptr->transformations & PNG_EXPAND) != 0) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans != 0) + max_pixel_depth = 32; + + else + max_pixel_depth = 24; + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth < 8) + max_pixel_depth = 8; + + if (png_ptr->num_trans != 0) + max_pixel_depth *= 2; + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (png_ptr->num_trans != 0) + { + max_pixel_depth *= 4; + max_pixel_depth /= 3; + } + } + } +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED + if ((png_ptr->transformations & PNG_EXPAND_16) != 0) + { +# ifdef PNG_READ_EXPAND_SUPPORTED + /* In fact it is an error if it isn't supported, but checking is + * the safe way. + */ + if ((png_ptr->transformations & PNG_EXPAND) != 0) + { + if (png_ptr->bit_depth < 16) + max_pixel_depth *= 2; + } + else +# endif + png_ptr->transformations &= ~PNG_EXPAND_16; + } +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED + if ((png_ptr->transformations & (PNG_FILLER)) != 0) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth <= 8) + max_pixel_depth = 16; + + else + max_pixel_depth = 32; + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB || + png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (max_pixel_depth <= 32) + max_pixel_depth = 32; + + else + max_pixel_depth = 64; + } + } +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0) + { + if ( +#ifdef PNG_READ_EXPAND_SUPPORTED + (png_ptr->num_trans != 0 && + (png_ptr->transformations & PNG_EXPAND) != 0) || +#endif +#ifdef PNG_READ_FILLER_SUPPORTED + (png_ptr->transformations & (PNG_FILLER)) != 0 || +#endif + png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (max_pixel_depth <= 16) + max_pixel_depth = 32; + + else + max_pixel_depth = 64; + } + + else + { + if (max_pixel_depth <= 8) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 32; + + else + max_pixel_depth = 24; + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 64; + + else + max_pixel_depth = 48; + } + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ +defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0) + { + unsigned int user_pixel_depth = png_ptr->user_transform_depth * + png_ptr->user_transform_channels; + + if (user_pixel_depth > max_pixel_depth) + max_pixel_depth = user_pixel_depth; + } +#endif + + /* This value is stored in png_struct and double checked in the row read + * code. + */ + png_ptr->maximum_pixel_depth = (png_byte)max_pixel_depth; + png_ptr->transformed_pixel_depth = 0; /* calculated on demand */ + + /* Align the width on the next larger 8 pixels. Mainly used + * for interlacing + */ + row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); + /* Calculate the maximum bytes needed, adding a byte and a pixel + * for safety's sake + */ + row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) + + 1 + ((max_pixel_depth + 7) >> 3U); + +#ifdef PNG_MAX_MALLOC_64K + if (row_bytes > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + + if (row_bytes + 48 > png_ptr->old_big_row_buf_size) + { + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->big_prev_row); + + if (png_ptr->interlaced != 0) + png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr, + row_bytes + 48); + + else + png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48); + + png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48); + +#ifdef PNG_ALIGNED_MEMORY_SUPPORTED + /* Use 16-byte aligned memory for row_buf with at least 16 bytes + * of padding before and after row_buf; treat prev_row similarly. + * NOTE: the alignment is to the start of the pixels, one beyond the start + * of the buffer, because of the filter byte. Prior to libpng 1.5.6 this + * was incorrect; the filter byte was aligned, which had the exact + * opposite effect of that intended. + */ + { + png_bytep temp = png_ptr->big_row_buf + 32; + int extra = (int)((temp - (png_bytep)0) & 0x0f); + png_ptr->row_buf = temp - extra - 1/*filter byte*/; + + temp = png_ptr->big_prev_row + 32; + extra = (int)((temp - (png_bytep)0) & 0x0f); + png_ptr->prev_row = temp - extra - 1/*filter byte*/; + } + +#else + /* Use 31 bytes of padding before and 17 bytes after row_buf. */ + png_ptr->row_buf = png_ptr->big_row_buf + 31; + png_ptr->prev_row = png_ptr->big_prev_row + 31; +#endif + png_ptr->old_big_row_buf_size = row_bytes + 48; + } + +#ifdef PNG_MAX_MALLOC_64K + if (png_ptr->rowbytes > 65535) + png_error(png_ptr, "This image requires a row greater than 64KB"); + +#endif + if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1)) + png_error(png_ptr, "Row has too many bytes to allocate in memory"); + + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + png_debug1(3, "width = %u,", png_ptr->width); + png_debug1(3, "height = %u,", png_ptr->height); + png_debug1(3, "iwidth = %u,", png_ptr->iwidth); + png_debug1(3, "num_rows = %u,", png_ptr->num_rows); + png_debug1(3, "rowbytes = %lu,", (unsigned long)png_ptr->rowbytes); + png_debug1(3, "irowbytes = %lu", + (unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); + + /* The sequential reader needs a buffer for IDAT, but the progressive reader + * does not, so free the read buffer now regardless; the sequential reader + * reallocates it on demand. + */ + if (png_ptr->read_buffer != NULL) + { + png_bytep buffer = png_ptr->read_buffer; + + png_ptr->read_buffer_size = 0; + png_ptr->read_buffer = NULL; + png_free(png_ptr, buffer); + } + + /* Finally claim the zstream for the inflate of the IDAT data, use the bits + * value from the stream (note that this will result in a fatal error if the + * IDAT stream has a bogus deflate header window_bits value, but this should + * not be happening any longer!) + */ + if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + + png_ptr->flags |= PNG_FLAG_ROW_INIT; +} +#endif /* READ */ diff --git a/src/png/libpng/pngset.c b/src/png/libpng/pngset.c new file mode 100644 index 0000000000..6f3a1ee11e --- /dev/null +++ b/src/png/libpng/pngset.c @@ -0,0 +1,1802 @@ + +/* pngset.c - storage of image information into info struct + * + * Last changed in libpng 1.6.32 [August 24, 2017] + * Copyright (c) 1998-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * The functions here are used during reads to store data from the file + * into the info struct, and during writes to store application data + * into the info struct for writing into the file. This abstracts the + * info struct and allows us to change the structure in the future. + */ + +#include "pngpriv.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#ifdef PNG_bKGD_SUPPORTED +void PNGAPI +png_set_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_color_16p background) +{ + png_debug1(1, "in %s storage function", "bKGD"); + + if (png_ptr == NULL || info_ptr == NULL || background == NULL) + return; + + info_ptr->background = *background; + info_ptr->valid |= PNG_INFO_bKGD; +} +#endif + +#ifdef PNG_cHRM_SUPPORTED +void PNGFAPI +png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + png_xy xy; + + png_debug1(1, "in %s storage function", "cHRM fixed"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + xy.redx = red_x; + xy.redy = red_y; + xy.greenx = green_x; + xy.greeny = green_y; + xy.bluex = blue_x; + xy.bluey = blue_y; + xy.whitex = white_x; + xy.whitey = white_y; + + if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy, + 2/* override with app values*/) != 0) + info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + + png_colorspace_sync_info(png_ptr, info_ptr); +} + +void PNGFAPI +png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z) +{ + png_XYZ XYZ; + + png_debug1(1, "in %s storage function", "cHRM XYZ fixed"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + XYZ.red_X = int_red_X; + XYZ.red_Y = int_red_Y; + XYZ.red_Z = int_red_Z; + XYZ.green_X = int_green_X; + XYZ.green_Y = int_green_Y; + XYZ.green_Z = int_green_Z; + XYZ.blue_X = int_blue_X; + XYZ.blue_Y = int_blue_Y; + XYZ.blue_Z = int_blue_Z; + + if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, + &XYZ, 2) != 0) + info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + + png_colorspace_sync_info(png_ptr, info_ptr); +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) +{ + png_set_cHRM_fixed(png_ptr, info_ptr, + png_fixed(png_ptr, white_x, "cHRM White X"), + png_fixed(png_ptr, white_y, "cHRM White Y"), + png_fixed(png_ptr, red_x, "cHRM Red X"), + png_fixed(png_ptr, red_y, "cHRM Red Y"), + png_fixed(png_ptr, green_x, "cHRM Green X"), + png_fixed(png_ptr, green_y, "cHRM Green Y"), + png_fixed(png_ptr, blue_x, "cHRM Blue X"), + png_fixed(png_ptr, blue_y, "cHRM Blue Y")); +} + +void PNGAPI +png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X, + double red_Y, double red_Z, double green_X, double green_Y, double green_Z, + double blue_X, double blue_Y, double blue_Z) +{ + png_set_cHRM_XYZ_fixed(png_ptr, info_ptr, + png_fixed(png_ptr, red_X, "cHRM Red X"), + png_fixed(png_ptr, red_Y, "cHRM Red Y"), + png_fixed(png_ptr, red_Z, "cHRM Red Z"), + png_fixed(png_ptr, green_X, "cHRM Green X"), + png_fixed(png_ptr, green_Y, "cHRM Green Y"), + png_fixed(png_ptr, green_Z, "cHRM Green Z"), + png_fixed(png_ptr, blue_X, "cHRM Blue X"), + png_fixed(png_ptr, blue_Y, "cHRM Blue Y"), + png_fixed(png_ptr, blue_Z, "cHRM Blue Z")); +} +# endif /* FLOATING_POINT */ + +#endif /* cHRM */ + +#ifdef PNG_eXIf_SUPPORTED +void PNGAPI +png_set_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, + const png_bytep eXIf_buf) +{ + png_warning(png_ptr, "png_set_eXIf does not work; use png_set_eXIf_1"); + PNG_UNUSED(info_ptr) + PNG_UNUSED(eXIf_buf) +} + +void PNGAPI +png_set_eXIf_1(png_const_structrp png_ptr, png_inforp info_ptr, + const png_uint_32 num_exif, const png_bytep eXIf_buf) +{ + int i; + + png_debug1(1, "in %s storage function", "eXIf"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->exif) + { + png_free(png_ptr, info_ptr->exif); + info_ptr->exif = NULL; + } + + info_ptr->num_exif = num_exif; + + info_ptr->exif = png_voidcast(png_bytep, png_malloc_warn(png_ptr, + info_ptr->num_exif)); + + if (info_ptr->exif == NULL) + { + png_warning(png_ptr, "Insufficient memory for eXIf chunk data"); + return; + } + + info_ptr->free_me |= PNG_FREE_EXIF; + + for (i = 0; i < (int) info_ptr->num_exif; i++) + info_ptr->exif[i] = eXIf_buf[i]; + + info_ptr->valid |= PNG_INFO_eXIf; +} +#endif /* eXIf */ + +#ifdef PNG_gAMA_SUPPORTED +void PNGFAPI +png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point file_gamma) +{ + png_debug1(1, "in %s storage function", "gAMA"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma); + png_colorspace_sync_info(png_ptr, info_ptr); +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gAMA(png_const_structrp png_ptr, png_inforp info_ptr, double file_gamma) +{ + png_set_gAMA_fixed(png_ptr, info_ptr, png_fixed(png_ptr, file_gamma, + "png_set_gAMA")); +} +# endif +#endif + +#ifdef PNG_hIST_SUPPORTED +void PNGAPI +png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_uint_16p hist) +{ + int i; + + png_debug1(1, "in %s storage function", "hIST"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->num_palette == 0 || info_ptr->num_palette + > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, + "Invalid palette size, hIST allocation skipped"); + + return; + } + + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); + + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in + * version 1.2.1 + */ + info_ptr->hist = png_voidcast(png_uint_16p, png_malloc_warn(png_ptr, + PNG_MAX_PALETTE_LENGTH * (sizeof (png_uint_16)))); + + if (info_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data"); + + return; + } + + info_ptr->free_me |= PNG_FREE_HIST; + + for (i = 0; i < info_ptr->num_palette; i++) + info_ptr->hist[i] = hist[i]; + + info_ptr->valid |= PNG_INFO_hIST; +} +#endif + +void PNGAPI +png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + png_debug1(1, "in %s storage function", "IHDR"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->width = width; + info_ptr->height = height; + info_ptr->bit_depth = (png_byte)bit_depth; + info_ptr->color_type = (png_byte)color_type; + info_ptr->compression_type = (png_byte)compression_type; + info_ptr->filter_type = (png_byte)filter_type; + info_ptr->interlace_type = (png_byte)interlace_type; + + png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + + else if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) + info_ptr->channels = 3; + + else + info_ptr->channels = 1; + + if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0) + info_ptr->channels++; + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); +} + +#ifdef PNG_oFFs_SUPPORTED +void PNGAPI +png_set_oFFs(png_const_structrp png_ptr, png_inforp info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "oFFs"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_offset = offset_x; + info_ptr->y_offset = offset_y; + info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_oFFs; +} +#endif + +#ifdef PNG_pCAL_SUPPORTED +void PNGAPI +png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type, + int nparams, png_const_charp units, png_charpp params) +{ + png_size_t length; + int i; + + png_debug1(1, "in %s storage function", "pCAL"); + + if (png_ptr == NULL || info_ptr == NULL || purpose == NULL || units == NULL + || (nparams > 0 && params == NULL)) + return; + + length = strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)", + (unsigned long)length); + + /* TODO: validate format of calibration name and unit name */ + + /* Check that the type matches the specification. */ + if (type < 0 || type > 3) + { + png_chunk_report(png_ptr, "Invalid pCAL equation type", + PNG_CHUNK_WRITE_ERROR); + return; + } + + if (nparams < 0 || nparams > 255) + { + png_chunk_report(png_ptr, "Invalid pCAL parameter count", + PNG_CHUNK_WRITE_ERROR); + return; + } + + /* Validate params[nparams] */ + for (i=0; ipcal_purpose = png_voidcast(png_charp, + png_malloc_warn(png_ptr, length)); + + if (info_ptr->pcal_purpose == NULL) + { + png_chunk_report(png_ptr, "Insufficient memory for pCAL purpose", + PNG_CHUNK_WRITE_ERROR); + return; + } + + memcpy(info_ptr->pcal_purpose, purpose, length); + + png_debug(3, "storing X0, X1, type, and nparams in info"); + info_ptr->pcal_X0 = X0; + info_ptr->pcal_X1 = X1; + info_ptr->pcal_type = (png_byte)type; + info_ptr->pcal_nparams = (png_byte)nparams; + + length = strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)", + (unsigned long)length); + + info_ptr->pcal_units = png_voidcast(png_charp, + png_malloc_warn(png_ptr, length)); + + if (info_ptr->pcal_units == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL units"); + + return; + } + + memcpy(info_ptr->pcal_units, units, length); + + info_ptr->pcal_params = png_voidcast(png_charpp, png_malloc_warn(png_ptr, + (png_size_t)(((unsigned int)nparams + 1) * (sizeof (png_charp))))); + + if (info_ptr->pcal_params == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL params"); + + return; + } + + memset(info_ptr->pcal_params, 0, ((unsigned int)nparams + 1) * + (sizeof (png_charp))); + + for (i = 0; i < nparams; i++) + { + length = strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)", i, + (unsigned long)length); + + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + + if (info_ptr->pcal_params[i] == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter"); + + return; + } + + memcpy(info_ptr->pcal_params[i], params[i], length); + } + + info_ptr->valid |= PNG_INFO_pCAL; + info_ptr->free_me |= PNG_FREE_PCAL; +} +#endif + +#ifdef PNG_sCAL_SUPPORTED +void PNGAPI +png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr, + int unit, png_const_charp swidth, png_const_charp sheight) +{ + png_size_t lengthw = 0, lengthh = 0; + + png_debug1(1, "in %s storage function", "sCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Double check the unit (should never get here with an invalid + * unit unless this is an API call.) + */ + if (unit != 1 && unit != 2) + png_error(png_ptr, "Invalid sCAL unit"); + + if (swidth == NULL || (lengthw = strlen(swidth)) == 0 || + swidth[0] == 45 /* '-' */ || !png_check_fp_string(swidth, lengthw)) + png_error(png_ptr, "Invalid sCAL width"); + + if (sheight == NULL || (lengthh = strlen(sheight)) == 0 || + sheight[0] == 45 /* '-' */ || !png_check_fp_string(sheight, lengthh)) + png_error(png_ptr, "Invalid sCAL height"); + + info_ptr->scal_unit = (png_byte)unit; + + ++lengthw; + + png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw); + + info_ptr->scal_s_width = png_voidcast(png_charp, + png_malloc_warn(png_ptr, lengthw)); + + if (info_ptr->scal_s_width == NULL) + { + png_warning(png_ptr, "Memory allocation failed while processing sCAL"); + + return; + } + + memcpy(info_ptr->scal_s_width, swidth, lengthw); + + ++lengthh; + + png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh); + + info_ptr->scal_s_height = png_voidcast(png_charp, + png_malloc_warn(png_ptr, lengthh)); + + if (info_ptr->scal_s_height == NULL) + { + png_free (png_ptr, info_ptr->scal_s_width); + info_ptr->scal_s_width = NULL; + + png_warning(png_ptr, "Memory allocation failed while processing sCAL"); + + return; + } + + memcpy(info_ptr->scal_s_height, sheight, lengthh); + + info_ptr->valid |= PNG_INFO_sCAL; + info_ptr->free_me |= PNG_FREE_SCAL; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_const_structrp png_ptr, png_inforp info_ptr, int unit, + double width, double height) +{ + png_debug1(1, "in %s storage function", "sCAL"); + + /* Check the arguments. */ + if (width <= 0) + png_warning(png_ptr, "Invalid sCAL width ignored"); + + else if (height <= 0) + png_warning(png_ptr, "Invalid sCAL height ignored"); + + else + { + /* Convert 'width' and 'height' to ASCII. */ + char swidth[PNG_sCAL_MAX_DIGITS+1]; + char sheight[PNG_sCAL_MAX_DIGITS+1]; + + png_ascii_from_fp(png_ptr, swidth, (sizeof swidth), width, + PNG_sCAL_PRECISION); + png_ascii_from_fp(png_ptr, sheight, (sizeof sheight), height, + PNG_sCAL_PRECISION); + + png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight); + } +} +# endif + +# ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_fixed(png_const_structrp png_ptr, png_inforp info_ptr, int unit, + png_fixed_point width, png_fixed_point height) +{ + png_debug1(1, "in %s storage function", "sCAL"); + + /* Check the arguments. */ + if (width <= 0) + png_warning(png_ptr, "Invalid sCAL width ignored"); + + else if (height <= 0) + png_warning(png_ptr, "Invalid sCAL height ignored"); + + else + { + /* Convert 'width' and 'height' to ASCII. */ + char swidth[PNG_sCAL_MAX_DIGITS+1]; + char sheight[PNG_sCAL_MAX_DIGITS+1]; + + png_ascii_from_fixed(png_ptr, swidth, (sizeof swidth), width); + png_ascii_from_fixed(png_ptr, sheight, (sizeof sheight), height); + + png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight); + } +} +# endif +#endif + +#ifdef PNG_pHYs_SUPPORTED +void PNGAPI +png_set_pHYs(png_const_structrp png_ptr, png_inforp info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "pHYs"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_pixels_per_unit = res_x; + info_ptr->y_pixels_per_unit = res_y; + info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_pHYs; +} +#endif + +void PNGAPI +png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, + png_const_colorp palette, int num_palette) +{ + + png_uint_32 max_palette_length; + + png_debug1(1, "in %s storage function", "PLTE"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + max_palette_length = (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ? + (1 << info_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH; + + if (num_palette < 0 || num_palette > (int) max_palette_length) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Invalid palette length"); + + else + { + png_warning(png_ptr, "Invalid palette length"); + + return; + } + } + + if ((num_palette > 0 && palette == NULL) || + (num_palette == 0 +# ifdef PNG_MNG_FEATURES_SUPPORTED + && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 +# endif + )) + { + png_error(png_ptr, "Invalid palette"); + } + + /* It may not actually be necessary to set png_ptr->palette here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + * + * 1.6.0: the above statement appears to be incorrect; something has to set + * the palette inside png_struct on read. + */ + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead + * of num_palette entries, in case of an invalid PNG file or incorrect + * call to png_set_PLTE() with too-large sample values. + */ + png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); + + if (num_palette > 0) + memcpy(png_ptr->palette, palette, (unsigned int)num_palette * + (sizeof (png_color))); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + + info_ptr->free_me |= PNG_FREE_PLTE; + + info_ptr->valid |= PNG_INFO_PLTE; +} + +#ifdef PNG_sBIT_SUPPORTED +void PNGAPI +png_set_sBIT(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_color_8p sig_bit) +{ + png_debug1(1, "in %s storage function", "sBIT"); + + if (png_ptr == NULL || info_ptr == NULL || sig_bit == NULL) + return; + + info_ptr->sig_bit = *sig_bit; + info_ptr->valid |= PNG_INFO_sBIT; +} +#endif + +#ifdef PNG_sRGB_SUPPORTED +void PNGAPI +png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent) +{ + png_debug1(1, "in %s storage function", "sRGB"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent); + png_colorspace_sync_info(png_ptr, info_ptr); +} + +void PNGAPI +png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, + int srgb_intent) +{ + png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, + srgb_intent) != 0) + { + /* This causes the gAMA and cHRM to be written too */ + info_ptr->colorspace.flags |= + PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; + } + + png_colorspace_sync_info(png_ptr, info_ptr); +} +#endif /* sRGB */ + + +#ifdef PNG_iCCP_SUPPORTED +void PNGAPI +png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen) +{ + png_charp new_iccp_name; + png_bytep new_iccp_profile; + png_size_t length; + + png_debug1(1, "in %s storage function", "iCCP"); + + if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) + return; + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_app_error(png_ptr, "Invalid iCCP compression method"); + + /* Set the colorspace first because this validates the profile; do not + * override previously set app cHRM or gAMA here (because likely as not the + * application knows better than libpng what the correct values are.) Pass + * the info_ptr color_type field to png_colorspace_set_ICC because in the + * write case it has not yet been stored in png_ptr. + */ + { + int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name, + proflen, profile, info_ptr->color_type); + + png_colorspace_sync_info(png_ptr, info_ptr); + + /* Don't do any of the copying if the profile was bad, or inconsistent. */ + if (result == 0) + return; + + /* But do write the gAMA and cHRM chunks from the profile. */ + info_ptr->colorspace.flags |= + PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; + } + + length = strlen(name)+1; + new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length)); + + if (new_iccp_name == NULL) + { + png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk"); + + return; + } + + memcpy(new_iccp_name, name, length); + new_iccp_profile = png_voidcast(png_bytep, + png_malloc_warn(png_ptr, proflen)); + + if (new_iccp_profile == NULL) + { + png_free(png_ptr, new_iccp_name); + png_benign_error(png_ptr, + "Insufficient memory to process iCCP profile"); + + return; + } + + memcpy(new_iccp_profile, profile, proflen); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); + + info_ptr->iccp_proflen = proflen; + info_ptr->iccp_name = new_iccp_name; + info_ptr->iccp_profile = new_iccp_profile; + info_ptr->free_me |= PNG_FREE_ICCP; + info_ptr->valid |= PNG_INFO_iCCP; +} +#endif + +#ifdef PNG_TEXT_SUPPORTED +void PNGAPI +png_set_text(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_textp text_ptr, int num_text) +{ + int ret; + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + + if (ret != 0) + png_error(png_ptr, "Insufficient memory to store text"); +} + +int /* PRIVATE */ +png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_textp text_ptr, int num_text) +{ + int i; + + png_debug1(1, "in %lx storage function", png_ptr == NULL ? 0xabadca11U : + (unsigned long)png_ptr->chunk_name); + + if (png_ptr == NULL || info_ptr == NULL || num_text <= 0 || text_ptr == NULL) + return(0); + + /* Make sure we have enough space in the "text" array in info_struct + * to hold all of the incoming text_ptr objects. This compare can't overflow + * because max_text >= num_text (anyway, subtract of two positive integers + * can't overflow in any case.) + */ + if (num_text > info_ptr->max_text - info_ptr->num_text) + { + int old_num_text = info_ptr->num_text; + int max_text; + png_textp new_text = NULL; + + /* Calculate an appropriate max_text, checking for overflow. */ + max_text = old_num_text; + if (num_text <= INT_MAX - max_text) + { + max_text += num_text; + + /* Round up to a multiple of 8 */ + if (max_text < INT_MAX-8) + max_text = (max_text + 8) & ~0x7; + + else + max_text = INT_MAX; + + /* Now allocate a new array and copy the old members in; this does all + * the overflow checks. + */ + new_text = png_voidcast(png_textp,png_realloc_array(png_ptr, + info_ptr->text, old_num_text, max_text-old_num_text, + sizeof *new_text)); + } + + if (new_text == NULL) + { + png_chunk_report(png_ptr, "too many text chunks", + PNG_CHUNK_WRITE_ERROR); + + return 1; + } + + png_free(png_ptr, info_ptr->text); + + info_ptr->text = new_text; + info_ptr->free_me |= PNG_FREE_TEXT; + info_ptr->max_text = max_text; + /* num_text is adjusted below as the entries are copied in */ + + png_debug1(3, "allocated %d entries for info_ptr->text", max_text); + } + + for (i = 0; i < num_text; i++) + { + size_t text_length, key_len; + size_t lang_len, lang_key_len; + png_textp textp = &(info_ptr->text[info_ptr->num_text]); + + if (text_ptr[i].key == NULL) + continue; + + if (text_ptr[i].compression < PNG_TEXT_COMPRESSION_NONE || + text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST) + { + png_chunk_report(png_ptr, "text compression mode is out of range", + PNG_CHUNK_WRITE_ERROR); + continue; + } + + key_len = strlen(text_ptr[i].key); + + if (text_ptr[i].compression <= 0) + { + lang_len = 0; + lang_key_len = 0; + } + + else +# ifdef PNG_iTXt_SUPPORTED + { + /* Set iTXt data */ + + if (text_ptr[i].lang != NULL) + lang_len = strlen(text_ptr[i].lang); + + else + lang_len = 0; + + if (text_ptr[i].lang_key != NULL) + lang_key_len = strlen(text_ptr[i].lang_key); + + else + lang_key_len = 0; + } +# else /* iTXt */ + { + png_chunk_report(png_ptr, "iTXt chunk not supported", + PNG_CHUNK_WRITE_ERROR); + continue; + } +# endif + + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') + { + text_length = 0; +# ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + textp->compression = PNG_ITXT_COMPRESSION_NONE; + + else +# endif + textp->compression = PNG_TEXT_COMPRESSION_NONE; + } + + else + { + text_length = strlen(text_ptr[i].text); + textp->compression = text_ptr[i].compression; + } + + textp->key = png_voidcast(png_charp,png_malloc_base(png_ptr, + key_len + text_length + lang_len + lang_key_len + 4)); + + if (textp->key == NULL) + { + png_chunk_report(png_ptr, "text chunk: out of memory", + PNG_CHUNK_WRITE_ERROR); + + return 1; + } + + png_debug2(2, "Allocated %lu bytes at %p in png_set_text", + (unsigned long)(png_uint_32) + (key_len + lang_len + lang_key_len + text_length + 4), + textp->key); + + memcpy(textp->key, text_ptr[i].key, key_len); + *(textp->key + key_len) = '\0'; + + if (text_ptr[i].compression > 0) + { + textp->lang = textp->key + key_len + 1; + memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang + lang_len) = '\0'; + textp->lang_key = textp->lang + lang_len + 1; + memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key + lang_key_len) = '\0'; + textp->text = textp->lang_key + lang_key_len + 1; + } + + else + { + textp->lang=NULL; + textp->lang_key=NULL; + textp->text = textp->key + key_len + 1; + } + + if (text_length != 0) + memcpy(textp->text, text_ptr[i].text, text_length); + + *(textp->text + text_length) = '\0'; + +# ifdef PNG_iTXt_SUPPORTED + if (textp->compression > 0) + { + textp->text_length = 0; + textp->itxt_length = text_length; + } + + else +# endif + { + textp->text_length = text_length; + textp->itxt_length = 0; + } + + info_ptr->num_text++; + png_debug1(3, "transferred text chunk %d", info_ptr->num_text); + } + + return(0); +} +#endif + +#ifdef PNG_tIME_SUPPORTED +void PNGAPI +png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_timep mod_time) +{ + png_debug1(1, "in %s storage function", "tIME"); + + if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL || + (png_ptr->mode & PNG_WROTE_tIME) != 0) + return; + + if (mod_time->month == 0 || mod_time->month > 12 || + mod_time->day == 0 || mod_time->day > 31 || + mod_time->hour > 23 || mod_time->minute > 59 || + mod_time->second > 60) + { + png_warning(png_ptr, "Ignoring invalid time value"); + + return; + } + + info_ptr->mod_time = *mod_time; + info_ptr->valid |= PNG_INFO_tIME; +} +#endif + +#ifdef PNG_tRNS_SUPPORTED +void PNGAPI +png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, + png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color) +{ + png_debug1(1, "in %s storage function", "tRNS"); + + if (png_ptr == NULL || info_ptr == NULL) + + return; + + if (trans_alpha != NULL) + { + /* It may not actually be necessary to set png_ptr->trans_alpha here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + * + * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively + * relies on png_set_tRNS storing the information in png_struct + * (otherwise it won't be there for the code in pngrtran.c). + */ + + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); + + if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) + { + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ + info_ptr->trans_alpha = png_voidcast(png_bytep, + png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); + memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans); + } + png_ptr->trans_alpha = info_ptr->trans_alpha; + } + + if (trans_color != NULL) + { +#ifdef PNG_WARNINGS_SUPPORTED + if (info_ptr->bit_depth < 16) + { + int sample_max = (1 << info_ptr->bit_depth) - 1; + + if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && + trans_color->gray > sample_max) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + (trans_color->red > sample_max || + trans_color->green > sample_max || + trans_color->blue > sample_max))) + png_warning(png_ptr, + "tRNS chunk has out-of-range samples for bit_depth"); + } +#endif + + info_ptr->trans_color = *trans_color; + + if (num_trans == 0) + num_trans = 1; + } + + info_ptr->num_trans = (png_uint_16)num_trans; + + if (num_trans != 0) + { + info_ptr->valid |= PNG_INFO_tRNS; + info_ptr->free_me |= PNG_FREE_TRNS; + } +} +#endif + +#ifdef PNG_sPLT_SUPPORTED +void PNGAPI +png_set_sPLT(png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries) +/* + * entries - array of png_sPLT_t structures + * to be added to the list of palettes + * in the info structure. + * + * nentries - number of palette structures to be + * added. + */ +{ + png_sPLT_tp np; + + if (png_ptr == NULL || info_ptr == NULL || nentries <= 0 || entries == NULL) + return; + + /* Use the internal realloc function, which checks for all the possible + * overflows. Notice that the parameters are (int) and (size_t) + */ + np = png_voidcast(png_sPLT_tp,png_realloc_array(png_ptr, + info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries, + sizeof *np)); + + if (np == NULL) + { + /* Out of memory or too many chunks */ + png_chunk_report(png_ptr, "too many sPLT chunks", PNG_CHUNK_WRITE_ERROR); + + return; + } + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = np; + info_ptr->free_me |= PNG_FREE_SPLT; + + np += info_ptr->splt_palettes_num; + + do + { + png_size_t length; + + /* Skip invalid input entries */ + if (entries->name == NULL || entries->entries == NULL) + { + /* png_handle_sPLT doesn't do this, so this is an app error */ + png_app_error(png_ptr, "png_set_sPLT: invalid sPLT"); + /* Just skip the invalid entry */ + continue; + } + + np->depth = entries->depth; + + /* In the event of out-of-memory just return - there's no point keeping + * on trying to add sPLT chunks. + */ + length = strlen(entries->name) + 1; + np->name = png_voidcast(png_charp, png_malloc_base(png_ptr, length)); + + if (np->name == NULL) + break; + + memcpy(np->name, entries->name, length); + + /* IMPORTANT: we have memory now that won't get freed if something else + * goes wrong; this code must free it. png_malloc_array produces no + * warnings; use a png_chunk_report (below) if there is an error. + */ + np->entries = png_voidcast(png_sPLT_entryp, png_malloc_array(png_ptr, + entries->nentries, sizeof (png_sPLT_entry))); + + if (np->entries == NULL) + { + png_free(png_ptr, np->name); + np->name = NULL; + break; + } + + np->nentries = entries->nentries; + /* This multiply can't overflow because png_malloc_array has already + * checked it when doing the allocation. + */ + memcpy(np->entries, entries->entries, + (unsigned int)entries->nentries * sizeof (png_sPLT_entry)); + + /* Note that 'continue' skips the advance of the out pointer and out + * count, so an invalid entry is not added. + */ + info_ptr->valid |= PNG_INFO_sPLT; + ++(info_ptr->splt_palettes_num); + ++np; + ++entries; + } + while (--nentries); + + if (nentries > 0) + png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR); +} +#endif /* sPLT */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +static png_byte +check_location(png_const_structrp png_ptr, int location) +{ + location &= (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT); + + /* New in 1.6.0; copy the location and check it. This is an API + * change; previously the app had to use the + * png_set_unknown_chunk_location API below for each chunk. + */ + if (location == 0 && (png_ptr->mode & PNG_IS_READ_STRUCT) == 0) + { + /* Write struct, so unknown chunks come from the app */ + png_app_warning(png_ptr, + "png_set_unknown_chunks now expects a valid location"); + /* Use the old behavior */ + location = (png_byte)(png_ptr->mode & + (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)); + } + + /* This need not be an internal error - if the app calls + * png_set_unknown_chunks on a read pointer it must get the location right. + */ + if (location == 0) + png_error(png_ptr, "invalid location in png_set_unknown_chunks"); + + /* Now reduce the location to the top-most set bit by removing each least + * significant bit in turn. + */ + while (location != (location & -location)) + location &= ~(location & -location); + + /* The cast is safe because 'location' is a bit mask and only the low four + * bits are significant. + */ + return (png_byte)location; +} + +void PNGAPI +png_set_unknown_chunks(png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns) +{ + png_unknown_chunkp np; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 || + unknowns == NULL) + return; + + /* Check for the failure cases where support has been disabled at compile + * time. This code is hardly ever compiled - it's here because + * STORE_UNKNOWN_CHUNKS is set by both read and write code (compiling in this + * code) but may be meaningless if the read or write handling of unknown + * chunks is not compiled in. + */ +# if !defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) && \ + defined(PNG_READ_SUPPORTED) + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) + { + png_app_error(png_ptr, "no unknown chunk support on read"); + + return; + } +# endif +# if !defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) && \ + defined(PNG_WRITE_SUPPORTED) + if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0) + { + png_app_error(png_ptr, "no unknown chunk support on write"); + + return; + } +# endif + + /* Prior to 1.6.0 this code used png_malloc_warn; however, this meant that + * unknown critical chunks could be lost with just a warning resulting in + * undefined behavior. Now png_chunk_report is used to provide behavior + * appropriate to read or write. + */ + np = png_voidcast(png_unknown_chunkp, png_realloc_array(png_ptr, + info_ptr->unknown_chunks, info_ptr->unknown_chunks_num, num_unknowns, + sizeof *np)); + + if (np == NULL) + { + png_chunk_report(png_ptr, "too many unknown chunks", + PNG_CHUNK_WRITE_ERROR); + + return; + } + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = np; /* safe because it is initialized */ + info_ptr->free_me |= PNG_FREE_UNKN; + + np += info_ptr->unknown_chunks_num; + + /* Increment unknown_chunks_num each time round the loop to protect the + * just-allocated chunk data. + */ + for (; num_unknowns > 0; --num_unknowns, ++unknowns) + { + memcpy(np->name, unknowns->name, (sizeof np->name)); + np->name[(sizeof np->name)-1] = '\0'; + np->location = check_location(png_ptr, unknowns->location); + + if (unknowns->size == 0) + { + np->data = NULL; + np->size = 0; + } + + else + { + np->data = png_voidcast(png_bytep, + png_malloc_base(png_ptr, unknowns->size)); + + if (np->data == NULL) + { + png_chunk_report(png_ptr, "unknown chunk: out of memory", + PNG_CHUNK_WRITE_ERROR); + /* But just skip storing the unknown chunk */ + continue; + } + + memcpy(np->data, unknowns->data, unknowns->size); + np->size = unknowns->size; + } + + /* These increments are skipped on out-of-memory for the data - the + * unknown chunk entry gets overwritten if the png_chunk_report returns. + * This is correct in the read case (the chunk is just dropped.) + */ + ++np; + ++(info_ptr->unknown_chunks_num); + } +} + +void PNGAPI +png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr, + int chunk, int location) +{ + /* This API is pretty pointless in 1.6.0 because the location can be set + * before the call to png_set_unknown_chunks. + * + * TODO: add a png_app_warning in 1.7 + */ + if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 && + chunk < info_ptr->unknown_chunks_num) + { + if ((location & (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)) == 0) + { + png_app_error(png_ptr, "invalid unknown chunk location"); + /* Fake out the pre 1.6.0 behavior: */ + if (((unsigned int)location & PNG_HAVE_IDAT) != 0) /* undocumented! */ + location = PNG_AFTER_IDAT; + + else + location = PNG_HAVE_IHDR; /* also undocumented */ + } + + info_ptr->unknown_chunks[chunk].location = + check_location(png_ptr, location); + } +} +#endif /* STORE_UNKNOWN_CHUNKS */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +png_uint_32 PNGAPI +png_permit_mng_features (png_structrp png_ptr, png_uint_32 mng_features) +{ + png_debug(1, "in png_permit_mng_features"); + + if (png_ptr == NULL) + return 0; + + png_ptr->mng_features_permitted = mng_features & PNG_ALL_MNG_FEATURES; + + return png_ptr->mng_features_permitted; +} +#endif + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +static unsigned int +add_one_chunk(png_bytep list, unsigned int count, png_const_bytep add, int keep) +{ + unsigned int i; + + /* Utility function: update the 'keep' state of a chunk if it is already in + * the list, otherwise add it to the list. + */ + for (i=0; i= PNG_HANDLE_CHUNK_LAST) + { + png_app_error(png_ptr, "png_set_keep_unknown_chunks: invalid keep"); + + return; + } + + if (num_chunks_in <= 0) + { + png_ptr->unknown_default = keep; + + /* '0' means just set the flags, so stop here */ + if (num_chunks_in == 0) + return; + } + + if (num_chunks_in < 0) + { + /* Ignore all unknown chunks and all chunks recognized by + * libpng except for IHDR, PLTE, tRNS, IDAT, and IEND + */ + static PNG_CONST png_byte chunks_to_ignore[] = { + 98, 75, 71, 68, '\0', /* bKGD */ + 99, 72, 82, 77, '\0', /* cHRM */ + 101, 88, 73, 102, '\0', /* eXIf */ + 103, 65, 77, 65, '\0', /* gAMA */ + 104, 73, 83, 84, '\0', /* hIST */ + 105, 67, 67, 80, '\0', /* iCCP */ + 105, 84, 88, 116, '\0', /* iTXt */ + 111, 70, 70, 115, '\0', /* oFFs */ + 112, 67, 65, 76, '\0', /* pCAL */ + 112, 72, 89, 115, '\0', /* pHYs */ + 115, 66, 73, 84, '\0', /* sBIT */ + 115, 67, 65, 76, '\0', /* sCAL */ + 115, 80, 76, 84, '\0', /* sPLT */ + 115, 84, 69, 82, '\0', /* sTER */ + 115, 82, 71, 66, '\0', /* sRGB */ + 116, 69, 88, 116, '\0', /* tEXt */ + 116, 73, 77, 69, '\0', /* tIME */ + 122, 84, 88, 116, '\0' /* zTXt */ + }; + + chunk_list = chunks_to_ignore; + num_chunks = (unsigned int)/*SAFE*/(sizeof chunks_to_ignore)/5U; + } + + else /* num_chunks_in > 0 */ + { + if (chunk_list == NULL) + { + /* Prior to 1.6.0 this was silently ignored, now it is an app_error + * which can be switched off. + */ + png_app_error(png_ptr, "png_set_keep_unknown_chunks: no chunk list"); + + return; + } + + num_chunks = (unsigned int)num_chunks_in; + } + + old_num_chunks = png_ptr->num_chunk_list; + if (png_ptr->chunk_list == NULL) + old_num_chunks = 0; + + /* Since num_chunks is always restricted to UINT_MAX/5 this can't overflow. + */ + if (num_chunks + old_num_chunks > UINT_MAX/5) + { + png_app_error(png_ptr, "png_set_keep_unknown_chunks: too many chunks"); + + return; + } + + /* If these chunks are being reset to the default then no more memory is + * required because add_one_chunk above doesn't extend the list if the 'keep' + * parameter is the default. + */ + if (keep != 0) + { + new_list = png_voidcast(png_bytep, png_malloc(png_ptr, + 5 * (num_chunks + old_num_chunks))); + + if (old_num_chunks > 0) + memcpy(new_list, png_ptr->chunk_list, 5*old_num_chunks); + } + + else if (old_num_chunks > 0) + new_list = png_ptr->chunk_list; + + else + new_list = NULL; + + /* Add the new chunks together with each one's handling code. If the chunk + * already exists the code is updated, otherwise the chunk is added to the + * end. (In libpng 1.6.0 order no longer matters because this code enforces + * the earlier convention that the last setting is the one that is used.) + */ + if (new_list != NULL) + { + png_const_bytep inlist; + png_bytep outlist; + unsigned int i; + + for (i=0; ichunk_list != new_list) + png_free(png_ptr, new_list); + + new_list = NULL; + } + } + + else + num_chunks = 0; + + png_ptr->num_chunk_list = num_chunks; + + if (png_ptr->chunk_list != new_list) + { + if (png_ptr->chunk_list != NULL) + png_free(png_ptr, png_ptr->chunk_list); + + png_ptr->chunk_list = new_list; + } +} +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +void PNGAPI +png_set_read_user_chunk_fn(png_structrp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) +{ + png_debug(1, "in png_set_read_user_chunk_fn"); + + if (png_ptr == NULL) + return; + + png_ptr->read_user_chunk_fn = read_user_chunk_fn; + png_ptr->user_chunk_ptr = user_chunk_ptr; +} +#endif + +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr, + png_bytepp row_pointers) +{ + png_debug1(1, "in %s storage function", "rows"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->row_pointers != NULL && + (info_ptr->row_pointers != row_pointers)) + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + + info_ptr->row_pointers = row_pointers; + + if (row_pointers != NULL) + info_ptr->valid |= PNG_INFO_IDAT; +} +#endif + +void PNGAPI +png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size) +{ + if (png_ptr == NULL) + return; + + if (size == 0 || size > PNG_UINT_31_MAX) + png_error(png_ptr, "invalid compression buffer size"); + +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) + { + png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */ + return; + } +# endif + +# ifdef PNG_WRITE_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0) + { + if (png_ptr->zowner != 0) + { + png_warning(png_ptr, + "Compression buffer size cannot be changed because it is in use"); + + return; + } + +#ifndef __COVERITY__ + /* Some compilers complain that this is always false. However, it + * can be true when integer overflow happens. + */ + if (size > ZLIB_IO_MAX) + { + png_warning(png_ptr, + "Compression buffer size limited to system maximum"); + size = ZLIB_IO_MAX; /* must fit */ + } +#endif + + if (size < 6) + { + /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH + * if this is permitted. + */ + png_warning(png_ptr, + "Compression buffer size cannot be reduced below 6"); + + return; + } + + if (png_ptr->zbuffer_size != size) + { + png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); + png_ptr->zbuffer_size = (uInt)size; + } + } +# endif +} + +void PNGAPI +png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask) +{ + if (png_ptr != NULL && info_ptr != NULL) + info_ptr->valid &= (unsigned int)(~mask); +} + + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* This function was added to libpng 1.2.6 */ +void PNGAPI +png_set_user_limits (png_structrp png_ptr, png_uint_32 user_width_max, + png_uint_32 user_height_max) +{ + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7fffffff. + */ + if (png_ptr == NULL) + return; + + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} + +/* This function was added to libpng 1.4.0 */ +void PNGAPI +png_set_chunk_cache_max (png_structrp png_ptr, png_uint_32 user_chunk_cache_max) +{ + if (png_ptr != NULL) + png_ptr->user_chunk_cache_max = user_chunk_cache_max; +} + +/* This function was added to libpng 1.4.1 */ +void PNGAPI +png_set_chunk_malloc_max (png_structrp png_ptr, + png_alloc_size_t user_chunk_malloc_max) +{ + if (png_ptr != NULL) + png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; +} +#endif /* ?SET_USER_LIMITS */ + + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_set_benign_errors(png_structrp png_ptr, int allowed) +{ + png_debug(1, "in png_set_benign_errors"); + + /* If allowed is 1, png_benign_error() is treated as a warning. + * + * If allowed is 0, png_benign_error() is treated as an error (which + * is the default behavior if png_set_benign_errors() is not called). + */ + + if (allowed != 0) + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN | + PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN; + + else + png_ptr->flags &= ~(PNG_FLAG_BENIGN_ERRORS_WARN | + PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN); +} +#endif /* BENIGN_ERRORS */ + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Whether to report invalid palette index; added at libng-1.5.10. + * It is possible for an indexed (color-type==3) PNG file to contain + * pixels with invalid (out-of-range) indexes if the PLTE chunk has + * fewer entries than the image's bit-depth would allow. We recover + * from this gracefully by filling any incomplete palette with zeros + * (opaque black). By default, when this occurs libpng will issue + * a benign error. This API can be used to override that behavior. + */ +void PNGAPI +png_set_check_for_invalid_index(png_structrp png_ptr, int allowed) +{ + png_debug(1, "in png_set_check_for_invalid_index"); + + if (allowed > 0) + png_ptr->num_palette_max = 0; + + else + png_ptr->num_palette_max = -1; +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The 'new_key' buffer must be 80 characters in size (for the keyword plus a + * trailing '\0'). If this routine returns 0 then there was no keyword, or a + * valid one could not be generated, and the caller must png_error. + */ +png_uint_32 /* PRIVATE */ +png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key) +{ +#ifdef PNG_WARNINGS_SUPPORTED + png_const_charp orig_key = key; +#endif + png_uint_32 key_len = 0; + int bad_character = 0; + int space = 1; + + png_debug(1, "in png_check_keyword"); + + if (key == NULL) + { + *new_key = 0; + return 0; + } + + while (*key && key_len < 79) + { + png_byte ch = (png_byte)*key++; + + if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/)) + { + *new_key++ = ch; ++key_len; space = 0; + } + + else if (space == 0) + { + /* A space or an invalid character when one wasn't seen immediately + * before; output just a space. + */ + *new_key++ = 32; ++key_len; space = 1; + + /* If the character was not a space then it is invalid. */ + if (ch != 32) + bad_character = ch; + } + + else if (bad_character == 0) + bad_character = ch; /* just skip it, record the first error */ + } + + if (key_len > 0 && space != 0) /* trailing space */ + { + --key_len; --new_key; + if (bad_character == 0) + bad_character = 32; + } + + /* Terminate the keyword */ + *new_key = 0; + + if (key_len == 0) + return 0; + +#ifdef PNG_WARNINGS_SUPPORTED + /* Try to only output one warning per keyword: */ + if (*key != 0) /* keyword too long */ + png_warning(png_ptr, "keyword truncated"); + + else if (bad_character != 0) + { + PNG_WARNING_PARAMETERS(p) + + png_warning_parameter(p, 1, orig_key); + png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_02x, bad_character); + + png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'"); + } +#else /* !WARNINGS */ + PNG_UNUSED(png_ptr) +#endif /* !WARNINGS */ + + return key_len; +} +#endif /* TEXT || pCAL || iCCP || sPLT */ +#endif /* READ || WRITE */ diff --git a/src/png/libpng/pngstruct.h b/src/png/libpng/pngstruct.h new file mode 100644 index 0000000000..d83f971253 --- /dev/null +++ b/src/png/libpng/pngstruct.h @@ -0,0 +1,483 @@ + +/* pngstruct.h - header file for PNG reference library + * + * Last changed in libpng 1.6.32 [August 24, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application. + */ + +#ifndef PNGSTRUCT_H +#define PNGSTRUCT_H +/* zlib.h defines the structure z_stream, an instance of which is included + * in this structure and is required for decompressing the LZ compressed + * data in PNG files. + */ +#ifndef ZLIB_CONST + /* We must ensure that zlib uses 'const' in declarations. */ +# define ZLIB_CONST +#endif +#include "zlib.h" +#ifdef const + /* zlib.h sometimes #defines const to nothing, undo this. */ +# undef const +#endif + +/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility + * with older builds. + */ +#if ZLIB_VERNUM < 0x1260 +# define PNGZ_MSG_CAST(s) png_constcast(char*,s) +# define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b) +#else +# define PNGZ_MSG_CAST(s) (s) +# define PNGZ_INPUT_CAST(b) (b) +#endif + +/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib + * can handle at once. This type need be no larger than 16 bits (so maximum of + * 65535), this define allows us to discover how big it is, but limited by the + * maximuum for png_size_t. The value can be overriden in a library build + * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably + * lower value (e.g. 255 works). A lower value may help memory usage (slightly) + * and may even improve performance on some systems (and degrade it on others.) + */ +#ifndef ZLIB_IO_MAX +# define ZLIB_IO_MAX ((uInt)-1) +#endif + +#ifdef PNG_WRITE_SUPPORTED +/* The type of a compression buffer list used by the write code. */ +typedef struct png_compression_buffer +{ + struct png_compression_buffer *next; + png_byte output[1]; /* actually zbuf_size */ +} png_compression_buffer, *png_compression_bufferp; + +#define PNG_COMPRESSION_BUFFER_SIZE(pp)\ + (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size) +#endif + +/* Colorspace support; structures used in png_struct, png_info and in internal + * functions to hold and communicate information about the color space. + * + * PNG_COLORSPACE_SUPPORTED is only required if the application will perform + * colorspace corrections, otherwise all the colorspace information can be + * skipped and the size of libpng can be reduced (significantly) by compiling + * out the colorspace support. + */ +#ifdef PNG_COLORSPACE_SUPPORTED +/* The chromaticities of the red, green and blue colorants and the chromaticity + * of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)). + */ +typedef struct png_xy +{ + png_fixed_point redx, redy; + png_fixed_point greenx, greeny; + png_fixed_point bluex, bluey; + png_fixed_point whitex, whitey; +} png_xy; + +/* The same data as above but encoded as CIE XYZ values. When this data comes + * from chromaticities the sum of the Y values is assumed to be 1.0 + */ +typedef struct png_XYZ +{ + png_fixed_point red_X, red_Y, red_Z; + png_fixed_point green_X, green_Y, green_Z; + png_fixed_point blue_X, blue_Y, blue_Z; +} png_XYZ; +#endif /* COLORSPACE */ + +#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) +/* A colorspace is all the above plus, potentially, profile information; + * however at present libpng does not use the profile internally so it is only + * stored in the png_info struct (if iCCP is supported.) The rendering intent + * is retained here and is checked. + * + * The file gamma encoding information is also stored here and gamma correction + * is done by libpng, whereas color correction must currently be done by the + * application. + */ +typedef struct png_colorspace +{ +#ifdef PNG_GAMMA_SUPPORTED + png_fixed_point gamma; /* File gamma */ +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED + png_xy end_points_xy; /* End points as chromaticities */ + png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */ + png_uint_16 rendering_intent; /* Rendering intent of a profile */ +#endif + + /* Flags are always defined to simplify the code. */ + png_uint_16 flags; /* As defined below */ +} png_colorspace, * PNG_RESTRICT png_colorspacerp; + +typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp; + +/* General flags for the 'flags' field */ +#define PNG_COLORSPACE_HAVE_GAMMA 0x0001 +#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002 +#define PNG_COLORSPACE_HAVE_INTENT 0x0004 +#define PNG_COLORSPACE_FROM_gAMA 0x0008 +#define PNG_COLORSPACE_FROM_cHRM 0x0010 +#define PNG_COLORSPACE_FROM_sRGB 0x0020 +#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040 +#define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */ +#define PNG_COLORSPACE_INVALID 0x8000 +#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags)) +#endif /* COLORSPACE || GAMMA */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmp_buf_local; /* New name in 1.6.0 for jmp_buf in png_struct */ + png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */ + jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */ + size_t jmp_buf_size; /* size of the above, if allocated */ +#endif + png_error_ptr error_fn; /* function for printing errors and aborting */ +#ifdef PNG_WARNINGS_SUPPORTED + png_error_ptr warning_fn; /* function for printing warnings */ +#endif + png_voidp error_ptr; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn; /* function for writing output data */ + png_rw_ptr read_data_fn; /* function for reading input data */ + png_voidp io_ptr; /* ptr to application struct for I/O functions */ + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr; /* user supplied struct for user transform */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations; /* which transformations to perform */ + + png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */ + z_stream zstream; /* decompression structure */ + +#ifdef PNG_WRITE_SUPPORTED + png_compression_bufferp zbuffer_list; /* Created on demand during write */ + uInt zbuffer_size; /* size of the actual buffer */ + + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ +#endif +/* Added at libpng 1.5.4 */ +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + int zlib_text_level; /* holds zlib compression level */ + int zlib_text_method; /* holds zlib compression method */ + int zlib_text_window_bits; /* holds zlib compression window bits */ + int zlib_text_mem_level; /* holds zlib compression memory level */ + int zlib_text_strategy; /* holds zlib compression strategy */ +#endif +/* End of material added at libpng 1.5.4 */ +/* Added at libpng 1.6.0 */ +#ifdef PNG_WRITE_SUPPORTED + int zlib_set_level; /* Actual values set into the zstream on write */ + int zlib_set_method; + int zlib_set_window_bits; + int zlib_set_mem_level; + int zlib_set_strategy; +#endif + + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_size_t rowbytes; /* size of row in bytes */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ + png_bytep prev_row; /* buffer to save previous (unfiltered) row. + * While reading this is a pointer into + * big_prev_row; while writing it is separately + * allocated if needed. + */ + png_bytep row_buf; /* buffer to save current (unfiltered) row. + * While reading, this is a pointer into + * big_row_buf; while writing it is separately + * allocated. + */ +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_bytep try_row; /* buffer to save trial row when filtering */ + png_bytep tst_row; /* buffer to save best trial row when filtering */ +#endif + png_size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */ + + png_uint_32 idat_size; /* current IDAT size for read */ + png_uint_32 crc; /* current chunk CRC value */ + png_colorp palette; /* palette from the input file */ + png_uint_16 num_palette; /* number of color entries in palette */ + +/* Added at libpng-1.5.10 */ +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + int num_palette_max; /* maximum palette index found in IDAT */ +#endif + + png_uint_16 num_trans; /* number of transparency values */ + png_byte compression; /* file compression type (always 0) */ + png_byte filter; /* file filter type (always 0) */ + png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass; /* current interlace pass (0 - 6) */ + png_byte do_filter; /* row filter flags (see PNG_FILTER_ in png.h ) */ + png_byte color_type; /* color type of file */ + png_byte bit_depth; /* bit depth of file */ + png_byte usr_bit_depth; /* bit depth of users row: write only */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte channels; /* number of channels in file */ +#ifdef PNG_WRITE_SUPPORTED + png_byte usr_channels; /* channels at start of write: write only */ +#endif + png_byte sig_bytes; /* magic bytes read/written from start of file */ + png_byte maximum_pixel_depth; + /* pixel depth used for the row buffers */ + png_byte transformed_pixel_depth; + /* pixel depth after read/write transforms */ +#if ZLIB_VERNUM >= 0x1240 + png_byte zstream_start; /* at start of an input zlib stream */ +#endif /* Zlib >= 1.2.4 */ +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif + +#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + png_byte background_gamma_type; + png_fixed_point background_gamma; + png_color_16 background; /* background color in screen gamma space */ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#endif /* bKGD */ + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_flush_ptr output_flush_fn; /* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */ + png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */ + + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_bytep gamma_to_1; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans_alpha; /* alpha values for paletted files */ + png_color_16 trans_color; /* transparent color for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after a prog. row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + png_size_t save_buffer_size; /* amount of data now in save_buffer */ + png_size_t save_buffer_max; /* total size of save_buffer */ + png_size_t buffer_size; /* total amount of available input data */ + png_size_t current_buffer_size; /* amount of data now in current_buffer */ + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ + +#endif /* PROGRESSIVE_READ */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* For the Borland special 64K segment handler */ + png_bytepp offset_table_ptr; + png_bytep offset_table; + png_uint_16 offset_table_number; + png_uint_16 offset_table_count; + png_uint_16 offset_table_count_free; +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + png_bytep palette_lookup; /* lookup table for quantizing */ + png_bytep quantize_index; /* index translation for palette files */ +#endif + +/* Options */ +#ifdef PNG_SET_OPTION_SUPPORTED + png_uint_32 options; /* On/off state (up to 16 options) */ +#endif + +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng-1.7 */ +#ifdef PNG_TIME_RFC1123_SUPPORTED + char time_buffer[29]; /* String to hold RFC 1123 time text */ +#endif +#endif + +/* New members added in libpng-1.0.6 */ + + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ + +#ifdef PNG_USER_CHUNKS_SUPPORTED + png_voidp user_chunk_ptr; +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif +#endif + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + int unknown_default; /* As PNG_HANDLE_* */ + unsigned int num_chunk_list; /* Number of entries in the list */ + png_bytep chunk_list; /* List of png_byte[5]; the textual chunk name + * followed by a PNG_HANDLE_* byte */ +#endif + +/* New members added in libpng-1.0.3 */ +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_byte rgb_to_gray_status; + /* Added in libpng 1.5.5 to record setting of coefficients: */ + png_byte rgb_to_gray_coefficients_set; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff; + png_uint_16 rgb_to_gray_green_coeff; + /* deleted in 1.5.5: rgb_to_gray_blue_coeff; */ +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* Changed from png_byte to png_uint_32 at version 1.2.0 */ + png_uint_32 mng_features_permitted; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_byte filter_type; +#endif + +/* New members added in libpng-1.2.0 */ + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn; /* function for allocating memory */ + png_free_ptr free_fn; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep quantize_sort; /* working sort array */ + png_bytep index_to_palette; /* where the original index currently is + in the palette */ + png_bytep palette_to_index; /* which original index points to this + palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type; + +#ifdef PNG_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max; + png_uint_32 user_height_max; + + /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown + * chunks that can be stored (0 means unlimited). + */ + png_uint_32 user_chunk_cache_max; + + /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk + * can occupy when decompressed. 0 means unlimited. + */ + png_alloc_size_t user_chunk_malloc_max; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + /* Temporary storage for unknown chunk that the library doesn't recognize, + * used while reading the chunk. + */ + png_unknown_chunk unknown_chunk; +#endif + +/* New member added in libpng-1.2.26 */ + png_size_t old_big_row_buf_size; + +#ifdef PNG_READ_SUPPORTED +/* New member added in libpng-1.2.30 */ + png_bytep read_buffer; /* buffer for reading chunk data */ + png_alloc_size_t read_buffer_size; /* current size of the buffer */ +#endif +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED + uInt IDAT_read_size; /* limit on read buffer size for IDAT */ +#endif + +#ifdef PNG_IO_STATE_SUPPORTED +/* New member added in libpng-1.4.0 */ + png_uint_32 io_state; +#endif + +/* New member added in libpng-1.5.6 */ + png_bytep big_prev_row; + +/* New member added in libpng-1.5.7 */ + void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info, + png_bytep row, png_const_bytep prev_row); + +#ifdef PNG_READ_SUPPORTED +#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) + png_colorspace colorspace; +#endif +#endif +}; +#endif /* PNGSTRUCT_H */ diff --git a/src/png/libpng/pngtest.c b/src/png/libpng/pngtest.c new file mode 100644 index 0000000000..9d5075791f --- /dev/null +++ b/src/png/libpng/pngtest.c @@ -0,0 +1,2156 @@ + +/* pngtest.c - a simple test program to test libpng + * + * Last changed in libpng 1.6.32 [August 24, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This program reads in a PNG image, writes it out again, and then + * compares the two files. If the files are identical, this shows that + * the basic chunk handling, filtering, and (de)compression code is working + * properly. It does not currently test all of the transforms, although + * it probably should. + * + * The program will report "FAIL" in certain legitimate cases: + * 1) when the compression level or filter selection method is changed. + * 2) when the maximum IDAT size (PNG_ZBUF_SIZE in pngconf.h) is not 8192. + * 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks + * exist in the input file. + * 4) others not listed here... + * In these cases, it is best to check with another tool such as "pngcheck" + * to see what the differences between the two files are. + * + * If a filename is given on the command-line, then this file is used + * for the input, rather than the default "pngtest.png". This allows + * testing a wide variety of files easily. You can also test a number + * of files at once by typing "pngtest -m file1.png file2.png ..." + */ + +#define _POSIX_SOURCE 1 + +#include +#include +#include + +/* Defined so I can write to a file on gui/windowing platforms */ +/* #define STDERR stderr */ +#define STDERR stdout /* For DOS */ + +#include "png.h" + +/* Known chunks that exist in pngtest.png must be supported or pngtest will fail + * simply as a result of re-ordering them. This may be fixed in 1.7 + * + * pngtest allocates a single row buffer for each row and overwrites it, + * therefore if the write side doesn't support the writing of interlaced images + * nothing can be done for an interlaced image (and the code below will fail + * horribly trying to write extra data after writing garbage). + */ +#if defined PNG_READ_SUPPORTED && /* else nothing can be done */\ + defined PNG_READ_bKGD_SUPPORTED &&\ + defined PNG_READ_cHRM_SUPPORTED &&\ + defined PNG_READ_gAMA_SUPPORTED &&\ + defined PNG_READ_oFFs_SUPPORTED &&\ + defined PNG_READ_pCAL_SUPPORTED &&\ + defined PNG_READ_pHYs_SUPPORTED &&\ + defined PNG_READ_sBIT_SUPPORTED &&\ + defined PNG_READ_sCAL_SUPPORTED &&\ + defined PNG_READ_sRGB_SUPPORTED &&\ + defined PNG_READ_sPLT_SUPPORTED &&\ + defined PNG_READ_tEXt_SUPPORTED &&\ + defined PNG_READ_tIME_SUPPORTED &&\ + defined PNG_READ_zTXt_SUPPORTED &&\ + (defined PNG_WRITE_INTERLACING_SUPPORTED || PNG_LIBPNG_VER >= 10700) + +#ifdef PNG_ZLIB_HEADER +# include PNG_ZLIB_HEADER /* defined by pnglibconf.h from 1.7 */ +#else +# include "zlib.h" +#endif + +/* Copied from pngpriv.h but only used in error messages below. */ +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif +#define FCLOSE(file) fclose(file) + +#ifndef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +/* Makes pngtest verbose so we can find problems. */ +#ifndef PNG_DEBUG +# define PNG_DEBUG 0 +#endif + +#if PNG_DEBUG > 1 +# define pngtest_debug(m) ((void)fprintf(stderr, m "\n")) +# define pngtest_debug1(m,p1) ((void)fprintf(stderr, m "\n", p1)) +# define pngtest_debug2(m,p1,p2) ((void)fprintf(stderr, m "\n", p1, p2)) +#else +# define pngtest_debug(m) ((void)0) +# define pngtest_debug1(m,p1) ((void)0) +# define pngtest_debug2(m,p1,p2) ((void)0) +#endif + +#if !PNG_DEBUG +# define SINGLE_ROWBUF_ALLOC /* Makes buffer overruns easier to nail */ +#endif + +#ifndef PNG_UNUSED +# define PNG_UNUSED(param) (void)param; +#endif + +/* Turn on CPU timing +#define PNGTEST_TIMING +*/ + +#ifndef PNG_FLOATING_POINT_SUPPORTED +#undef PNGTEST_TIMING +#endif + +#ifdef PNGTEST_TIMING +static float t_start, t_stop, t_decode, t_encode, t_misc; +#include +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED +#define PNG_tIME_STRING_LENGTH 29 +static int tIME_chunk_present = 0; +static char tIME_string[PNG_tIME_STRING_LENGTH] = "tIME chunk is not present"; + +#if PNG_LIBPNG_VER < 10619 +#define png_convert_to_rfc1123_buffer(ts, t) tIME_to_str(read_ptr, ts, t) + +static int +tIME_to_str(png_structp png_ptr, png_charp ts, png_const_timep t) +{ + png_const_charp str = png_convert_to_rfc1123(png_ptr, t); + + if (str == NULL) + return 0; + + strcpy(ts, str); + return 1; +} +#endif /* older libpng */ +#endif + +static int verbose = 0; +static int strict = 0; +static int relaxed = 0; +static int xfail = 0; +static int unsupported_chunks = 0; /* chunk unsupported by libpng in input */ +static int error_count = 0; /* count calls to png_error */ +static int warning_count = 0; /* count calls to png_warning */ + +/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) png_ptr->jmpbuf +#endif + +/* Defines for unknown chunk handling if required. */ +#ifndef PNG_HANDLE_CHUNK_ALWAYS +# define PNG_HANDLE_CHUNK_ALWAYS 3 +#endif +#ifndef PNG_HANDLE_CHUNK_IF_SAFE +# define PNG_HANDLE_CHUNK_IF_SAFE 2 +#endif + +/* Utility to save typing/errors, the argument must be a name */ +#define MEMZERO(var) ((void)memset(&var, 0, sizeof var)) + +/* Example of using row callbacks to make a simple progress meter */ +static int status_pass = 1; +static int status_dots_requested = 0; +static int status_dots = 1; + +static void PNGCBAPI +read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if (png_ptr == NULL || row_number > PNG_UINT_31_MAX) + return; + + if (status_pass != pass) + { + fprintf(stdout, "\n Pass %d: ", pass); + status_pass = pass; + status_dots = 31; + } + + status_dots--; + + if (status_dots == 0) + { + fprintf(stdout, "\n "); + status_dots=30; + } + + fprintf(stdout, "r"); +} + +#ifdef PNG_WRITE_SUPPORTED +static void PNGCBAPI +write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if (png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7) + return; + + fprintf(stdout, "w"); +} +#endif + + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +/* Example of using a user transform callback (doesn't do anything at present). + */ +static void PNGCBAPI +read_user_callback(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + PNG_UNUSED(png_ptr) + PNG_UNUSED(row_info) + PNG_UNUSED(data) +} +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +/* Example of using user transform callback (we don't transform anything, + * but merely count the zero samples) + */ + +static png_uint_32 zero_samples; + +static void PNGCBAPI +count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + png_bytep dp = data; + if (png_ptr == NULL) + return; + + /* Contents of row_info: + * png_uint_32 width width of row + * png_uint_32 rowbytes number of bytes in row + * png_byte color_type color type of pixels + * png_byte bit_depth bit depth of samples + * png_byte channels number of channels (1-4) + * png_byte pixel_depth bits per pixel (depth*channels) + */ + + /* Counts the number of zero samples (or zero pixels if color_type is 3 */ + + if (row_info->color_type == 0 || row_info->color_type == 3) + { + int pos = 0; + png_uint_32 n, nstop; + + for (n = 0, nstop=row_info->width; nbit_depth == 1) + { + if (((*dp << pos++ ) & 0x80) == 0) + zero_samples++; + + if (pos == 8) + { + pos = 0; + dp++; + } + } + + if (row_info->bit_depth == 2) + { + if (((*dp << (pos+=2)) & 0xc0) == 0) + zero_samples++; + + if (pos == 8) + { + pos = 0; + dp++; + } + } + + if (row_info->bit_depth == 4) + { + if (((*dp << (pos+=4)) & 0xf0) == 0) + zero_samples++; + + if (pos == 8) + { + pos = 0; + dp++; + } + } + + if (row_info->bit_depth == 8) + if (*dp++ == 0) + zero_samples++; + + if (row_info->bit_depth == 16) + { + if ((*dp | *(dp+1)) == 0) + zero_samples++; + dp+=2; + } + } + } + else /* Other color types */ + { + png_uint_32 n, nstop; + int channel; + int color_channels = row_info->channels; + if (row_info->color_type > 3) + color_channels--; + + for (n = 0, nstop=row_info->width; nbit_depth == 8) + if (*dp++ == 0) + zero_samples++; + + if (row_info->bit_depth == 16) + { + if ((*dp | *(dp+1)) == 0) + zero_samples++; + + dp+=2; + } + } + if (row_info->color_type > 3) + { + dp++; + if (row_info->bit_depth == 16) + dp++; + } + } + } +} +#endif /* WRITE_USER_TRANSFORM */ + +#ifndef PNG_STDIO_SUPPORTED +/* START of code to validate stdio-free compilation */ +/* These copies of the default read/write functions come from pngrio.c and + * pngwio.c. They allow "don't include stdio" testing of the library. + * This is the function that does the actual reading of data. If you are + * not reading from a standard C stream, you should create a replacement + * read_data function and use it at run time with png_set_read_fn(), rather + * than changing the library. + */ + +#ifdef PNG_IO_STATE_SUPPORTED +void +pngtest_check_io_state(png_structp png_ptr, png_size_t data_length, + png_uint_32 io_op); +void +pngtest_check_io_state(png_structp png_ptr, png_size_t data_length, + png_uint_32 io_op) +{ + png_uint_32 io_state = png_get_io_state(png_ptr); + int err = 0; + + /* Check if the current operation (reading / writing) is as expected. */ + if ((io_state & PNG_IO_MASK_OP) != io_op) + png_error(png_ptr, "Incorrect operation in I/O state"); + + /* Check if the buffer size specific to the current location + * (file signature / header / data / crc) is as expected. + */ + switch (io_state & PNG_IO_MASK_LOC) + { + case PNG_IO_SIGNATURE: + if (data_length > 8) + err = 1; + break; + case PNG_IO_CHUNK_HDR: + if (data_length != 8) + err = 1; + break; + case PNG_IO_CHUNK_DATA: + break; /* no restrictions here */ + case PNG_IO_CHUNK_CRC: + if (data_length != 4) + err = 1; + break; + default: + err = 1; /* uninitialized */ + } + if (err != 0) + png_error(png_ptr, "Bad I/O state or buffer size"); +} +#endif + +static void PNGCBAPI +pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check = 0; + png_voidp io_ptr; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + io_ptr = png_get_io_ptr(png_ptr); + if (io_ptr != NULL) + { + check = fread(data, 1, length, (png_FILE_p)io_ptr); + } + + if (check != length) + { + png_error(png_ptr, "Read Error"); + } + +#ifdef PNG_IO_STATE_SUPPORTED + pngtest_check_io_state(png_ptr, length, PNG_IO_READING); +#endif +} + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +static void PNGCBAPI +pngtest_flush(png_structp png_ptr) +{ + /* Do nothing; fflush() is said to be just a waste of energy. */ + PNG_UNUSED(png_ptr) /* Stifle compiler warning */ +} +#endif + +/* This is the function that does the actual writing of data. If you are + * not writing to a standard C stream, you should create a replacement + * write_data function and use it at run time with png_set_write_fn(), rather + * than changing the library. + */ +static void PNGCBAPI +pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + check = fwrite(data, 1, length, (png_FILE_p)png_get_io_ptr(png_ptr)); + + if (check != length) + { + png_error(png_ptr, "Write Error"); + } + +#ifdef PNG_IO_STATE_SUPPORTED + pngtest_check_io_state(png_ptr, length, PNG_IO_WRITING); +#endif +} +#endif /* !STDIO */ + +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +typedef struct +{ + PNG_CONST char *file_name; +} pngtest_error_parameters; + +static void PNGCBAPI +pngtest_warning(png_structp png_ptr, png_const_charp message) +{ + PNG_CONST char *name = "UNKNOWN (ERROR!)"; + pngtest_error_parameters *test = + (pngtest_error_parameters*)png_get_error_ptr(png_ptr); + + ++warning_count; + + if (test != NULL && test->file_name != NULL) + name = test->file_name; + + fprintf(STDERR, "\n%s: libpng warning: %s\n", name, message); +} + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void PNGCBAPI +pngtest_error(png_structp png_ptr, png_const_charp message) +{ + ++error_count; + + pngtest_warning(png_ptr, message); + /* We can return because png_error calls the default handler, which is + * actually OK in this case. + */ +} + +/* END of code to validate stdio-free compilation */ + +/* START of code to validate memory allocation and deallocation */ +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more than 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + * + * This piece of code can be compiled to validate max 64K allocations + * by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. + */ +typedef struct memory_information +{ + png_alloc_size_t size; + png_voidp pointer; + struct memory_information *next; +} memory_information; +typedef memory_information *memory_infop; + +static memory_infop pinformation = NULL; +static int current_allocation = 0; +static int maximum_allocation = 0; +static int total_allocation = 0; +static int num_allocations = 0; + +png_voidp PNGCBAPI png_debug_malloc PNGARG((png_structp png_ptr, + png_alloc_size_t size)); +void PNGCBAPI png_debug_free PNGARG((png_structp png_ptr, png_voidp ptr)); + +png_voidp +PNGCBAPI png_debug_malloc(png_structp png_ptr, png_alloc_size_t size) +{ + + /* png_malloc has already tested for NULL; png_create_struct calls + * png_debug_malloc directly, with png_ptr == NULL which is OK + */ + + if (size == 0) + return (NULL); + + /* This calls the library allocator twice, once to get the requested + buffer and once to get a new free list entry. */ + { + /* Disable malloc_fn and free_fn */ + memory_infop pinfo; + png_set_mem_fn(png_ptr, NULL, NULL, NULL); + pinfo = (memory_infop)png_malloc(png_ptr, + (sizeof *pinfo)); + pinfo->size = size; + current_allocation += size; + total_allocation += size; + num_allocations ++; + + if (current_allocation > maximum_allocation) + maximum_allocation = current_allocation; + + pinfo->pointer = png_malloc(png_ptr, size); + /* Restore malloc_fn and free_fn */ + + png_set_mem_fn(png_ptr, + NULL, png_debug_malloc, png_debug_free); + + if (size != 0 && pinfo->pointer == NULL) + { + current_allocation -= size; + total_allocation -= size; + png_error(png_ptr, + "out of memory in pngtest->png_debug_malloc"); + } + + pinfo->next = pinformation; + pinformation = pinfo; + /* Make sure the caller isn't assuming zeroed memory. */ + memset(pinfo->pointer, 0xdd, pinfo->size); + + if (verbose != 0) + printf("png_malloc %lu bytes at %p\n", (unsigned long)size, + pinfo->pointer); + + return (png_voidp)(pinfo->pointer); + } +} + +/* Free a pointer. It is removed from the list at the same time. */ +void PNGCBAPI +png_debug_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL) + fprintf(STDERR, "NULL pointer to png_debug_free.\n"); + + if (ptr == 0) + { +#if 0 /* This happens all the time. */ + fprintf(STDERR, "WARNING: freeing NULL pointer\n"); +#endif + return; + } + + /* Unlink the element from the list. */ + if (pinformation != NULL) + { + memory_infop *ppinfo = &pinformation; + + for (;;) + { + memory_infop pinfo = *ppinfo; + + if (pinfo->pointer == ptr) + { + *ppinfo = pinfo->next; + current_allocation -= pinfo->size; + if (current_allocation < 0) + fprintf(STDERR, "Duplicate free of memory\n"); + /* We must free the list element too, but first kill + the memory that is to be freed. */ + memset(ptr, 0x55, pinfo->size); + free(pinfo); + pinfo = NULL; + break; + } + + if (pinfo->next == NULL) + { + fprintf(STDERR, "Pointer %p not found\n", ptr); + break; + } + + ppinfo = &pinfo->next; + } + } + + /* Finally free the data. */ + if (verbose != 0) + printf("Freeing %p\n", ptr); + + if (ptr != NULL) + free(ptr); + ptr = NULL; +} +#endif /* USER_MEM && DEBUG */ +/* END of code to test memory allocation/deallocation */ + + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* Demonstration of user chunk support of the sTER and vpAg chunks */ + +/* (sTER is a public chunk not yet known by libpng. vpAg is a private +chunk used in ImageMagick to store "virtual page" size). */ + +static struct user_chunk_data +{ + png_const_infop info_ptr; + png_uint_32 vpAg_width, vpAg_height; + png_byte vpAg_units; + png_byte sTER_mode; + int location[2]; +} +user_chunk_data; + +/* Used for location and order; zero means nothing. */ +#define have_sTER 0x01 +#define have_vpAg 0x02 +#define before_PLTE 0x10 +#define before_IDAT 0x20 +#define after_IDAT 0x40 + +static void +init_callback_info(png_const_infop info_ptr) +{ + MEMZERO(user_chunk_data); + user_chunk_data.info_ptr = info_ptr; +} + +static int +set_location(png_structp png_ptr, struct user_chunk_data *data, int what) +{ + int location; + + if ((data->location[0] & what) != 0 || (data->location[1] & what) != 0) + return 0; /* already have one of these */ + + /* Find where we are (the code below zeroes info_ptr to indicate that the + * chunks before the first IDAT have been read.) + */ + if (data->info_ptr == NULL) /* after IDAT */ + location = what | after_IDAT; + + else if (png_get_valid(png_ptr, data->info_ptr, PNG_INFO_PLTE) != 0) + location = what | before_IDAT; + + else + location = what | before_PLTE; + + if (data->location[0] == 0) + data->location[0] = location; + + else + data->location[1] = location; + + return 1; /* handled */ +} + +static int PNGCBAPI +read_user_chunk_callback(png_struct *png_ptr, png_unknown_chunkp chunk) +{ + struct user_chunk_data *my_user_chunk_data = + (struct user_chunk_data*)png_get_user_chunk_ptr(png_ptr); + + if (my_user_chunk_data == NULL) + png_error(png_ptr, "lost user chunk pointer"); + + /* Return one of the following: + * return (-n); chunk had an error + * return (0); did not recognize + * return (n); success + * + * The unknown chunk structure contains the chunk data: + * png_byte name[5]; + * png_byte *data; + * png_size_t size; + * + * Note that libpng has already taken care of the CRC handling. + */ + + if (chunk->name[0] == 115 && chunk->name[1] == 84 && /* s T */ + chunk->name[2] == 69 && chunk->name[3] == 82) /* E R */ + { + /* Found sTER chunk */ + if (chunk->size != 1) + return (-1); /* Error return */ + + if (chunk->data[0] != 0 && chunk->data[0] != 1) + return (-1); /* Invalid mode */ + + if (set_location(png_ptr, my_user_chunk_data, have_sTER) != 0) + { + my_user_chunk_data->sTER_mode=chunk->data[0]; + return (1); + } + + else + return (0); /* duplicate sTER - give it to libpng */ + } + + if (chunk->name[0] != 118 || chunk->name[1] != 112 || /* v p */ + chunk->name[2] != 65 || chunk->name[3] != 103) /* A g */ + return (0); /* Did not recognize */ + + /* Found ImageMagick vpAg chunk */ + + if (chunk->size != 9) + return (-1); /* Error return */ + + if (set_location(png_ptr, my_user_chunk_data, have_vpAg) == 0) + return (0); /* duplicate vpAg */ + + my_user_chunk_data->vpAg_width = png_get_uint_31(png_ptr, chunk->data); + my_user_chunk_data->vpAg_height = png_get_uint_31(png_ptr, chunk->data + 4); + my_user_chunk_data->vpAg_units = chunk->data[8]; + + return (1); +} + +#ifdef PNG_WRITE_SUPPORTED +static void +write_sTER_chunk(png_structp write_ptr) +{ + png_byte sTER[5] = {115, 84, 69, 82, '\0'}; + + if (verbose != 0) + fprintf(STDERR, "\n stereo mode = %d\n", user_chunk_data.sTER_mode); + + png_write_chunk(write_ptr, sTER, &user_chunk_data.sTER_mode, 1); +} + +static void +write_vpAg_chunk(png_structp write_ptr) +{ + png_byte vpAg[5] = {118, 112, 65, 103, '\0'}; + + png_byte vpag_chunk_data[9]; + + if (verbose != 0) + fprintf(STDERR, " vpAg = %lu x %lu, units = %d\n", + (unsigned long)user_chunk_data.vpAg_width, + (unsigned long)user_chunk_data.vpAg_height, + user_chunk_data.vpAg_units); + + png_save_uint_32(vpag_chunk_data, user_chunk_data.vpAg_width); + png_save_uint_32(vpag_chunk_data + 4, user_chunk_data.vpAg_height); + vpag_chunk_data[8] = user_chunk_data.vpAg_units; + png_write_chunk(write_ptr, vpAg, vpag_chunk_data, 9); +} + +static void +write_chunks(png_structp write_ptr, int location) +{ + int i; + + /* Notice that this preserves the original chunk order, however chunks + * intercepted by the callback will be written *after* chunks passed to + * libpng. This will actually reverse a pair of sTER chunks or a pair of + * vpAg chunks, resulting in an error later. This is not worth worrying + * about - the chunks should not be duplicated! + */ + for (i=0; i<2; ++i) + { + if (user_chunk_data.location[i] == (location | have_sTER)) + write_sTER_chunk(write_ptr); + + else if (user_chunk_data.location[i] == (location | have_vpAg)) + write_vpAg_chunk(write_ptr); + } +} +#endif /* WRITE */ +#else /* !READ_USER_CHUNKS */ +# define write_chunks(pp,loc) ((void)0) +#endif +/* END of code to demonstrate user chunk support */ + +/* START of code to check that libpng has the required text support; this only + * checks for the write support because if read support is missing the chunk + * will simply not be reported back to pngtest. + */ +#ifdef PNG_TEXT_SUPPORTED +static void +pngtest_check_text_support(png_structp png_ptr, png_textp text_ptr, + int num_text) +{ + while (num_text > 0) + { + switch (text_ptr[--num_text].compression) + { + case PNG_TEXT_COMPRESSION_NONE: + break; + + case PNG_TEXT_COMPRESSION_zTXt: +# ifndef PNG_WRITE_zTXt_SUPPORTED + ++unsupported_chunks; + /* In libpng 1.7 this now does an app-error, so stop it: */ + text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE; +# endif + break; + + case PNG_ITXT_COMPRESSION_NONE: + case PNG_ITXT_COMPRESSION_zTXt: +# ifndef PNG_WRITE_iTXt_SUPPORTED + ++unsupported_chunks; + text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE; +# endif + break; + + default: + /* This is an error */ + png_error(png_ptr, "invalid text chunk compression field"); + break; + } + } +} +#endif +/* END of code to check that libpng has the required text support */ + +/* Test one file */ +static int +test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) +{ + static png_FILE_p fpin; + static png_FILE_p fpout; /* "static" prevents setjmp corruption */ + pngtest_error_parameters error_parameters; + png_structp read_ptr; + png_infop read_info_ptr, end_info_ptr; +#ifdef PNG_WRITE_SUPPORTED + png_structp write_ptr; + png_infop write_info_ptr; + png_infop write_end_info_ptr; +#ifdef PNG_WRITE_FILTER_SUPPORTED + int interlace_preserved = 1; +#endif /* WRITE_FILTER */ +#else /* !WRITE */ + png_structp write_ptr = NULL; + png_infop write_info_ptr = NULL; + png_infop write_end_info_ptr = NULL; +#endif /* !WRITE */ + png_bytep row_buf; + png_uint_32 y; + png_uint_32 width, height; + volatile int num_passes; + int pass; + int bit_depth, color_type; + + row_buf = NULL; + error_parameters.file_name = inname; + + if ((fpin = fopen(inname, "rb")) == NULL) + { + fprintf(STDERR, "Could not find input file %s\n", inname); + return (1); + } + + if ((fpout = fopen(outname, "wb")) == NULL) + { + fprintf(STDERR, "Could not open output file %s\n", outname); + FCLOSE(fpin); + return (1); + } + + pngtest_debug("Allocating read and write structures"); +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + read_ptr = + png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL, NULL, png_debug_malloc, png_debug_free); +#else + read_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); +#endif + png_set_error_fn(read_ptr, &error_parameters, pngtest_error, + pngtest_warning); + +#ifdef PNG_WRITE_SUPPORTED +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + write_ptr = + png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL, NULL, png_debug_malloc, png_debug_free); +#else + write_ptr = + png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); +#endif + png_set_error_fn(write_ptr, &error_parameters, pngtest_error, + pngtest_warning); +#endif + pngtest_debug("Allocating read_info, write_info and end_info structures"); + read_info_ptr = png_create_info_struct(read_ptr); + end_info_ptr = png_create_info_struct(read_ptr); +#ifdef PNG_WRITE_SUPPORTED + write_info_ptr = png_create_info_struct(write_ptr); + write_end_info_ptr = png_create_info_struct(write_ptr); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + init_callback_info(read_info_ptr); + png_set_read_user_chunk_fn(read_ptr, &user_chunk_data, + read_user_chunk_callback); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + pngtest_debug("Setting jmpbuf for read struct"); + if (setjmp(png_jmpbuf(read_ptr))) + { + fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); + png_free(read_ptr, row_buf); + row_buf = NULL; + if (verbose != 0) + fprintf(STDERR, " destroy read structs\n"); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + if (verbose != 0) + fprintf(STDERR, " destroy write structs\n"); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } + +#ifdef PNG_WRITE_SUPPORTED + pngtest_debug("Setting jmpbuf for write struct"); + + if (setjmp(png_jmpbuf(write_ptr))) + { + fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); + if (verbose != 0) + fprintf(STDERR, " destroying read structs\n"); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); + if (verbose != 0) + fprintf(STDERR, " destroying write structs\n"); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_destroy_write_struct(&write_ptr, &write_info_ptr); + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } +#endif +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED + if (strict != 0) + { + /* Treat png_benign_error() as errors on read */ + png_set_benign_errors(read_ptr, 0); + +# ifdef PNG_WRITE_SUPPORTED + /* Treat them as errors on write */ + png_set_benign_errors(write_ptr, 0); +# endif + + /* if strict is not set, then app warnings and errors are treated as + * warnings in release builds, but not in unstable builds; this can be + * changed with '--relaxed'. + */ + } + + else if (relaxed != 0) + { + /* Allow application (pngtest) errors and warnings to pass */ + png_set_benign_errors(read_ptr, 1); + + /* Turn off CRC checking while reading */ + png_set_crc_action(read_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); + +#ifdef PNG_IGNORE_ADLER32 + /* Turn off ADLER32 checking while reading */ + png_set_option(read_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); +#endif + +# ifdef PNG_WRITE_SUPPORTED + png_set_benign_errors(write_ptr, 1); +# endif + + } +#endif /* BENIGN_ERRORS */ + + pngtest_debug("Initializing input and output streams"); +#ifdef PNG_STDIO_SUPPORTED + png_init_io(read_ptr, fpin); +# ifdef PNG_WRITE_SUPPORTED + png_init_io(write_ptr, fpout); +# endif +#else + png_set_read_fn(read_ptr, (png_voidp)fpin, pngtest_read_data); +# ifdef PNG_WRITE_SUPPORTED + png_set_write_fn(write_ptr, (png_voidp)fpout, pngtest_write_data, +# ifdef PNG_WRITE_FLUSH_SUPPORTED + pngtest_flush); +# else + NULL); +# endif +# endif +#endif + + if (status_dots_requested == 1) + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, write_row_callback); +#endif + png_set_read_status_fn(read_ptr, read_row_callback); + } + + else + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, NULL); +#endif + png_set_read_status_fn(read_ptr, NULL); + } + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_set_read_user_transform_fn(read_ptr, read_user_callback); +#endif +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + zero_samples = 0; + png_set_write_user_transform_fn(write_ptr, count_zero_samples); +#endif + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + /* Preserve all the unknown chunks, if possible. If this is disabled then, + * even if the png_{get,set}_unknown_chunks stuff is enabled, we can't use + * libpng to *save* the unknown chunks on read (because we can't switch the + * save option on!) + * + * Notice that if SET_UNKNOWN_CHUNKS is *not* supported read will discard all + * unknown chunks and write will write them all. + */ +#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS, + NULL, 0); +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS, + NULL, 0); +#endif +#endif + + pngtest_debug("Reading info struct"); + png_read_info(read_ptr, read_info_ptr); + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + /* This is a bit of a hack; there is no obvious way in the callback function + * to determine that the chunks before the first IDAT have been read, so + * remove the info_ptr (which is only used to determine position relative to + * PLTE) here to indicate that we are after the IDAT. + */ + user_chunk_data.info_ptr = NULL; +#endif + + pngtest_debug("Transferring info struct"); + { + int interlace_type, compression_type, filter_type; + + if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, + &color_type, &interlace_type, &compression_type, &filter_type) != 0) + { + png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, + color_type, interlace_type, compression_type, filter_type); + /* num_passes may not be available below if interlace support is not + * provided by libpng for both read and write. + */ + switch (interlace_type) + { + case PNG_INTERLACE_NONE: + num_passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + num_passes = 7; + break; + + default: + png_error(read_ptr, "invalid interlace type"); + /*NOT REACHED*/ + } + } + + else + png_error(read_ptr, "png_get_IHDR failed"); + } +#ifdef PNG_FIXED_POINT_SUPPORTED +#ifdef PNG_cHRM_SUPPORTED + { + png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + + if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y, + &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y) != 0) + { + png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#ifdef PNG_gAMA_SUPPORTED + { + png_fixed_point gamma; + + if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma) != 0) + png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma); + } +#endif +#else /* Use floating point versions */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +#ifdef PNG_cHRM_SUPPORTED + { + double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + + if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y) != 0) + { + png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#ifdef PNG_gAMA_SUPPORTED + { + double gamma; + + if (png_get_gAMA(read_ptr, read_info_ptr, &gamma) != 0) + png_set_gAMA(write_ptr, write_info_ptr, gamma); + } +#endif +#endif /* Floating point */ +#endif /* Fixed point */ +#ifdef PNG_iCCP_SUPPORTED + { + png_charp name; + png_bytep profile; + png_uint_32 proflen; + int compression_type; + + if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type, + &profile, &proflen) != 0) + { + png_set_iCCP(write_ptr, write_info_ptr, name, compression_type, + profile, proflen); + } + } +#endif +#ifdef PNG_sRGB_SUPPORTED + { + int intent; + + if (png_get_sRGB(read_ptr, read_info_ptr, &intent) != 0) + png_set_sRGB(write_ptr, write_info_ptr, intent); + } +#endif + { + png_colorp palette; + int num_palette; + + if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette) != 0) + png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette); + } +#ifdef PNG_bKGD_SUPPORTED + { + png_color_16p background; + + if (png_get_bKGD(read_ptr, read_info_ptr, &background) != 0) + { + png_set_bKGD(write_ptr, write_info_ptr, background); + } + } +#endif +#ifdef PNG_READ_eXIf_SUPPORTED + { + png_bytep exif=NULL; + png_uint_32 exif_length; + + if (png_get_eXIf_1(read_ptr, read_info_ptr, &exif_length, &exif) != 0) + { + if (exif_length > 1) + fprintf(STDERR," eXIf type %c%c, %lu bytes\n",exif[0],exif[1], + (unsigned long)exif_length); +# ifdef PNG_WRITE_eXIf_SUPPORTED + png_set_eXIf_1(write_ptr, write_info_ptr, exif_length, exif); +# endif + } + } +#endif +#ifdef PNG_hIST_SUPPORTED + { + png_uint_16p hist; + + if (png_get_hIST(read_ptr, read_info_ptr, &hist) != 0) + png_set_hIST(write_ptr, write_info_ptr, hist); + } +#endif +#ifdef PNG_oFFs_SUPPORTED + { + png_int_32 offset_x, offset_y; + int unit_type; + + if (png_get_oFFs(read_ptr, read_info_ptr, &offset_x, &offset_y, + &unit_type) != 0) + { + png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type); + } + } +#endif +#ifdef PNG_pCAL_SUPPORTED + { + png_charp purpose, units; + png_charpp params; + png_int_32 X0, X1; + int type, nparams; + + if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type, + &nparams, &units, ¶ms) != 0) + { + png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type, + nparams, units, params); + } + } +#endif +#ifdef PNG_pHYs_SUPPORTED + { + png_uint_32 res_x, res_y; + int unit_type; + + if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, + &unit_type) != 0) + png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type); + } +#endif +#ifdef PNG_sBIT_SUPPORTED + { + png_color_8p sig_bit; + + if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit) != 0) + png_set_sBIT(write_ptr, write_info_ptr, sig_bit); + } +#endif +#ifdef PNG_sCAL_SUPPORTED +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) + { + int unit; + double scal_width, scal_height; + + if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height) != 0) + { + png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height); + } + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + { + int unit; + png_charp scal_width, scal_height; + + if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height) != 0) + { + png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, + scal_height); + } + } +#endif +#endif +#endif + +#ifdef PNG_sPLT_SUPPORTED + { + png_sPLT_tp entries; + + int num_entries = (int) png_get_sPLT(read_ptr, read_info_ptr, &entries); + if (num_entries) + { + png_set_sPLT(write_ptr, write_info_ptr, entries, num_entries); + } + } +#endif + +#ifdef PNG_TEXT_SUPPORTED + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0) + { + pngtest_debug1("Handling %d iTXt/tEXt/zTXt chunks", num_text); + + pngtest_check_text_support(read_ptr, text_ptr, num_text); + + if (verbose != 0) + { + int i; + + fprintf(STDERR,"\n"); + for (i=0; igray > sample_max) || + (color_type == PNG_COLOR_TYPE_RGB && + ((int)trans_color->red > sample_max || + (int)trans_color->green > sample_max || + (int)trans_color->blue > sample_max)))) + png_set_tRNS(write_ptr, write_info_ptr, trans_alpha, num_trans, + trans_color); + } + } +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + { + png_unknown_chunkp unknowns; + int num_unknowns = png_get_unknown_chunks(read_ptr, read_info_ptr, + &unknowns); + + if (num_unknowns != 0) + { + png_set_unknown_chunks(write_ptr, write_info_ptr, unknowns, + num_unknowns); +#if PNG_LIBPNG_VER < 10600 + /* Copy the locations from the read_info_ptr. The automatically + * generated locations in write_end_info_ptr are wrong prior to 1.6.0 + * because they are reset from the write pointer (removed in 1.6.0). + */ + { + int i; + for (i = 0; i < num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_info_ptr, i, + unknowns[i].location); + } +#endif + } + } +#endif + +#ifdef PNG_WRITE_SUPPORTED + pngtest_debug("Writing info struct"); + + /* Write the info in two steps so that if we write the 'unknown' chunks here + * they go to the correct place. + */ + png_write_info_before_PLTE(write_ptr, write_info_ptr); + + write_chunks(write_ptr, before_PLTE); /* before PLTE */ + + png_write_info(write_ptr, write_info_ptr); + + write_chunks(write_ptr, before_IDAT); /* after PLTE */ + + png_write_info(write_ptr, write_end_info_ptr); + + write_chunks(write_ptr, after_IDAT); /* after IDAT */ + +#ifdef PNG_COMPRESSION_COMPAT + /* Test the 'compatibility' setting here, if it is available. */ + png_set_compression(write_ptr, PNG_COMPRESSION_COMPAT); +#endif +#endif + +#ifdef SINGLE_ROWBUF_ALLOC + pngtest_debug("Allocating row buffer..."); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + + pngtest_debug1("\t0x%08lx", (unsigned long)row_buf); +#endif /* SINGLE_ROWBUF_ALLOC */ + pngtest_debug("Writing row data"); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) &&\ + defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* Both must be defined for libpng to be able to handle the interlace, + * otherwise it gets handled below by simply reading and writing the passes + * directly. + */ + if (png_set_interlace_handling(read_ptr) != num_passes) + png_error(write_ptr, + "png_set_interlace_handling(read): wrong pass count "); + if (png_set_interlace_handling(write_ptr) != num_passes) + png_error(write_ptr, + "png_set_interlace_handling(write): wrong pass count "); +#else /* png_set_interlace_handling not called on either read or write */ +# define calc_pass_height +#endif /* not using libpng interlace handling */ + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; +#endif + for (pass = 0; pass < num_passes; pass++) + { +# ifdef calc_pass_height + png_uint_32 pass_height; + + if (num_passes == 7) /* interlaced */ + { + if (PNG_PASS_COLS(width, pass) > 0) + pass_height = PNG_PASS_ROWS(height, pass); + + else + pass_height = 0; + } + + else /* not interlaced */ + pass_height = height; +# else +# define pass_height height +# endif + + pngtest_debug1("Writing row data for pass %d", pass); + for (y = 0; y < pass_height; y++) + { +#ifndef SINGLE_ROWBUF_ALLOC + pngtest_debug2("Allocating row buffer (pass %d, y = %u)...", pass, y); + + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + + pngtest_debug2("\t0x%08lx (%lu bytes)", (unsigned long)row_buf, + (unsigned long)png_get_rowbytes(read_ptr, read_info_ptr)); + +#endif /* !SINGLE_ROWBUF_ALLOC */ + png_read_rows(read_ptr, (png_bytepp)&row_buf, NULL, 1); + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_decode += (t_stop - t_start); + t_start = t_stop; +#endif + png_write_rows(write_ptr, (png_bytepp)&row_buf, 1); +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_encode += (t_stop - t_start); + t_start = t_stop; +#endif +#endif /* WRITE */ + +#ifndef SINGLE_ROWBUF_ALLOC + pngtest_debug2("Freeing row buffer (pass %d, y = %u)", pass, y); + png_free(read_ptr, row_buf); + row_buf = NULL; +#endif /* !SINGLE_ROWBUF_ALLOC */ + } + } + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1); +# endif +# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1); +# endif +#endif + + pngtest_debug("Reading and writing end_info data"); + + png_read_end(read_ptr, end_info_ptr); +#ifdef PNG_TEXT_SUPPORTED + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0) + { + pngtest_debug1("Handling %d iTXt/tEXt/zTXt chunks", num_text); + + pngtest_check_text_support(read_ptr, text_ptr, num_text); + + if (verbose != 0) + { + int i; + + fprintf(STDERR,"\n"); + for (i=0; i 1) + fprintf(STDERR," eXIf type %c%c, %lu bytes\n",exif[0],exif[1], + (unsigned long)exif_length); +# ifdef PNG_WRITE_eXIf_SUPPORTED + png_set_eXIf_1(write_ptr, write_end_info_ptr, exif_length, exif); +# endif + } + } +#endif +#ifdef PNG_tIME_SUPPORTED + { + png_timep mod_time; + + if (png_get_tIME(read_ptr, end_info_ptr, &mod_time) != 0) + { + png_set_tIME(write_ptr, write_end_info_ptr, mod_time); +#ifdef PNG_TIME_RFC1123_SUPPORTED + if (png_convert_to_rfc1123_buffer(tIME_string, mod_time) != 0) + tIME_string[(sizeof tIME_string) - 1] = '\0'; + + else + { + strncpy(tIME_string, "*** invalid time ***", sizeof tIME_string); + tIME_string[(sizeof tIME_string)-1] = '\0'; + } + + tIME_chunk_present++; +#endif /* TIME_RFC1123 */ + } + } +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + { + png_unknown_chunkp unknowns; + int num_unknowns = png_get_unknown_chunks(read_ptr, end_info_ptr, + &unknowns); + + if (num_unknowns != 0) + { + png_set_unknown_chunks(write_ptr, write_end_info_ptr, unknowns, + num_unknowns); +#if PNG_LIBPNG_VER < 10600 + /* Copy the locations from the read_info_ptr. The automatically + * generated locations in write_end_info_ptr are wrong prior to 1.6.0 + * because they are reset from the write pointer (removed in 1.6.0). + */ + { + int i; + for (i = 0; i < num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_end_info_ptr, i, + unknowns[i].location); + } +#endif + } + } +#endif + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + /* Normally one would use Z_DEFAULT_STRATEGY for text compression. + * This is here just to make pngtest replicate the results from libpng + * versions prior to 1.5.4, and to test this new API. + */ + png_set_text_compression_strategy(write_ptr, Z_FILTERED); +#endif + + /* When the unknown vpAg/sTER chunks are written by pngtest the only way to + * do it is to write them *before* calling png_write_end. When unknown + * chunks are written by libpng, however, they are written just before IEND. + * There seems to be no way round this, however vpAg/sTER are not expected + * after IDAT. + */ + write_chunks(write_ptr, after_IDAT); + + png_write_end(write_ptr, write_end_info_ptr); +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED + if (verbose != 0) + { + png_uint_32 iwidth, iheight; + iwidth = png_get_image_width(write_ptr, write_info_ptr); + iheight = png_get_image_height(write_ptr, write_info_ptr); + fprintf(STDERR, "\n Image width = %lu, height = %lu\n", + (unsigned long)iwidth, (unsigned long)iheight); + } +#endif + + pngtest_debug("Destroying data structs"); +#ifdef SINGLE_ROWBUF_ALLOC + pngtest_debug("destroying row_buf for read_ptr"); + png_free(read_ptr, row_buf); + row_buf = NULL; +#endif /* SINGLE_ROWBUF_ALLOC */ + pngtest_debug("destroying read_ptr, read_info_ptr, end_info_ptr"); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + pngtest_debug("destroying write_end_info_ptr"); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + pngtest_debug("destroying write_ptr, write_info_ptr"); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + pngtest_debug("Destruction complete."); + + FCLOSE(fpin); + FCLOSE(fpout); + + /* Summarize any warnings or errors and in 'strict' mode fail the test. + * Unsupported chunks can result in warnings, in that case ignore the strict + * setting, otherwise fail the test on warnings as well as errors. + */ + if (error_count > 0) + { + /* We don't really expect to get here because of the setjmp handling + * above, but this is safe. + */ + fprintf(STDERR, "\n %s: %d libpng errors found (%d warnings)", + inname, error_count, warning_count); + + if (strict != 0) + return (1); + } + +# ifdef PNG_WRITE_SUPPORTED + /* If there is no write support nothing was written! */ + else if (unsupported_chunks > 0) + { + fprintf(STDERR, "\n %s: unsupported chunks (%d)%s", + inname, unsupported_chunks, strict ? ": IGNORED --strict!" : ""); + } +# endif + + else if (warning_count > 0) + { + fprintf(STDERR, "\n %s: %d libpng warnings found", + inname, warning_count); + + if (strict != 0) + return (1); + } + + pngtest_debug("Opening files for comparison"); + if ((fpin = fopen(inname, "rb")) == NULL) + { + fprintf(STDERR, "Could not find file %s\n", inname); + return (1); + } + + if ((fpout = fopen(outname, "rb")) == NULL) + { + fprintf(STDERR, "Could not find file %s\n", outname); + FCLOSE(fpin); + return (1); + } + +#if defined (PNG_WRITE_SUPPORTED) /* else nothing was written */ &&\ + defined (PNG_WRITE_FILTER_SUPPORTED) + if (interlace_preserved != 0) /* else the files will be changed */ + { + for (;;) + { + static int wrote_question = 0; + png_size_t num_in, num_out; + char inbuf[256], outbuf[256]; + + num_in = fread(inbuf, 1, sizeof inbuf, fpin); + num_out = fread(outbuf, 1, sizeof outbuf, fpout); + + if (num_in != num_out) + { + fprintf(STDERR, "\nFiles %s and %s are of a different size\n", + inname, outname); + + if (wrote_question == 0 && unsupported_chunks == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT" + " chunk size (%d bytes),", + inname, PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question = 1; + } + + FCLOSE(fpin); + FCLOSE(fpout); + + if (strict != 0 && unsupported_chunks == 0) + return (1); + + else + return (0); + } + + if (num_in == 0) + break; + + if (memcmp(inbuf, outbuf, num_in)) + { + fprintf(STDERR, "\nFiles %s and %s are different\n", inname, + outname); + + if (wrote_question == 0 && unsupported_chunks == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum" + " IDAT chunk size (%d bytes),", + inname, PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question = 1; + } + + FCLOSE(fpin); + FCLOSE(fpout); + + /* NOTE: the unsupported_chunks escape is permitted here because + * unsupported text chunk compression will result in the compression + * mode being changed (to NONE) yet, in the test case, the result + * can be exactly the same size! + */ + if (strict != 0 && unsupported_chunks == 0) + return (1); + + else + return (0); + } + } + } +#endif /* WRITE && WRITE_FILTER */ + + FCLOSE(fpin); + FCLOSE(fpout); + + return (0); +} + +/* Input and output filenames */ +#ifdef RISCOS +static PNG_CONST char *inname = "pngtest/png"; +static PNG_CONST char *outname = "pngout/png"; +#else +static PNG_CONST char *inname = "pngtest.png"; +static PNG_CONST char *outname = "pngout.png"; +#endif + +int +main(int argc, char *argv[]) +{ + int multiple = 0; + int ierror = 0; + + png_structp dummy_ptr; + + fprintf(STDERR, "\n Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " with zlib version %s\n", ZLIB_VERSION); + fprintf(STDERR, "%s", png_get_copyright(NULL)); + /* Show the version of libpng used in building the library */ + fprintf(STDERR, " library (%lu):%s", + (unsigned long)png_access_version_number(), + png_get_header_version(NULL)); + + /* Show the version of libpng used in building the application */ + fprintf(STDERR, " pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER, + PNG_HEADER_VERSION_STRING); + + /* Do some consistency checking on the memory allocation settings, I'm + * not sure this matters, but it is nice to know, the first of these + * tests should be impossible because of the way the macros are set + * in pngconf.h + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n"); +#endif + /* I think the following can happen. */ +#if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n"); +#endif + + if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) + { + fprintf(STDERR, + "Warning: versions are different between png.h and png.c\n"); + fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); + ++ierror; + } + + if (argc > 1) + { + if (strcmp(argv[1], "-m") == 0) + { + multiple = 1; + status_dots_requested = 0; + } + + else if (strcmp(argv[1], "-mv") == 0 || + strcmp(argv[1], "-vm") == 0 ) + { + multiple = 1; + verbose = 1; + status_dots_requested = 1; + } + + else if (strcmp(argv[1], "-v") == 0) + { + verbose = 1; + status_dots_requested = 1; + inname = argv[2]; + } + + else if (strcmp(argv[1], "--strict") == 0) + { + status_dots_requested = 0; + verbose = 1; + inname = argv[2]; + strict++; + relaxed = 0; + multiple=1; + } + + else if (strcmp(argv[1], "--relaxed") == 0) + { + status_dots_requested = 0; + verbose = 1; + inname = argv[2]; + strict = 0; + relaxed++; + multiple=1; + } + else if (strcmp(argv[1], "--xfail") == 0) + { + status_dots_requested = 0; + verbose = 1; + inname = argv[2]; + strict = 0; + xfail++; + relaxed++; + multiple=1; + } + + else + { + inname = argv[1]; + status_dots_requested = 0; + } + } + + if (multiple == 0 && argc == 3 + verbose) + outname = argv[2 + verbose]; + + if ((multiple == 0 && argc > 3 + verbose) || + (multiple != 0 && argc < 2)) + { + fprintf(STDERR, + "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", + argv[0], argv[0]); + fprintf(STDERR, + " reads/writes one PNG file (without -m) or multiple files (-m)\n"); + fprintf(STDERR, + " with -m %s is used as a temporary file\n", outname); + exit(1); + } + + if (multiple != 0) + { + int i; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + for (i=2; i 0 + fprintf(STDERR, "\n"); +#endif + kerror = test_one_file(argv[i], outname); + if (kerror == 0) + { +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + fprintf(STDERR, "\n PASS (%lu zero samples)\n", + (unsigned long)zero_samples); +#else + fprintf(STDERR, " PASS\n"); +#endif +#ifdef PNG_TIME_RFC1123_SUPPORTED + if (tIME_chunk_present != 0) + fprintf(STDERR, " tIME = %s\n", tIME_string); + + tIME_chunk_present = 0; +#endif /* TIME_RFC1123 */ + } + + else + { + if (xfail) + fprintf(STDERR, " XFAIL\n"); + else + { + fprintf(STDERR, " FAIL\n"); + ierror += kerror; + } + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + if (allocation_now != current_allocation) + fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", + current_allocation - allocation_now); + + if (current_allocation != 0) + { + memory_infop pinfo = pinformation; + + fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", + current_allocation); + + while (pinfo != NULL) + { + fprintf(STDERR, " %lu bytes at %p\n", + (unsigned long)pinfo->size, + pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + + else + { + int i; + for (i = 0; i<3; ++i) + { + int kerror; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + if (i == 1) + status_dots_requested = 1; + + else if (verbose == 0) + status_dots_requested = 0; + + if (i == 0 || verbose == 1 || ierror != 0) + { + fprintf(STDERR, "\n Testing %s:", inname); +#if PNG_DEBUG > 0 + fprintf(STDERR, "\n"); +#endif + } + + kerror = test_one_file(inname, outname); + + if (kerror == 0) + { + if (verbose == 1 || i == 2) + { +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + fprintf(STDERR, "\n PASS (%lu zero samples)\n", + (unsigned long)zero_samples); +#else + fprintf(STDERR, " PASS\n"); +#endif +#ifdef PNG_TIME_RFC1123_SUPPORTED + if (tIME_chunk_present != 0) + fprintf(STDERR, " tIME = %s\n", tIME_string); +#endif /* TIME_RFC1123 */ + } + } + + else + { + if (verbose == 0 && i != 2) + { + fprintf(STDERR, "\n Testing %s:", inname); +#if PNG_DEBUG > 0 + fprintf(STDERR, "\n"); +#endif + } + + if (xfail) + fprintf(STDERR, " XFAIL\n"); + else + { + fprintf(STDERR, " FAIL\n"); + ierror += kerror; + } + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + if (allocation_now != current_allocation) + fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", + current_allocation - allocation_now); + + if (current_allocation != 0) + { + memory_infop pinfo = pinformation; + + fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", + current_allocation); + + while (pinfo != NULL) + { + fprintf(STDERR, " %lu bytes at %p\n", + (unsigned long)pinfo->size, pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; + fprintf(STDERR, " CPU time used = %.3f seconds", + (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC); + fprintf(STDERR, " (decoding %.3f,\n", + t_decode/(float)CLOCKS_PER_SEC); + fprintf(STDERR, " encoding %.3f ,", + t_encode/(float)CLOCKS_PER_SEC); + fprintf(STDERR, " other %.3f seconds)\n\n", + t_misc/(float)CLOCKS_PER_SEC); +#endif + + if (ierror == 0) + fprintf(STDERR, " libpng passes test\n"); + + else + fprintf(STDERR, " libpng FAILS test\n"); + + dummy_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + fprintf(STDERR, " Default limits:\n"); + fprintf(STDERR, " width_max = %lu\n", + (unsigned long) png_get_user_width_max(dummy_ptr)); + fprintf(STDERR, " height_max = %lu\n", + (unsigned long) png_get_user_height_max(dummy_ptr)); + if (png_get_chunk_cache_max(dummy_ptr) == 0) + fprintf(STDERR, " cache_max = unlimited\n"); + else + fprintf(STDERR, " cache_max = %lu\n", + (unsigned long) png_get_chunk_cache_max(dummy_ptr)); + if (png_get_chunk_malloc_max(dummy_ptr) == 0) + fprintf(STDERR, " malloc_max = unlimited\n"); + else + fprintf(STDERR, " malloc_max = %lu\n", + (unsigned long) png_get_chunk_malloc_max(dummy_ptr)); + png_destroy_read_struct(&dummy_ptr, NULL, NULL); + + return (int)(ierror != 0); +} +#else +int +main(void) +{ + fprintf(STDERR, + " test ignored because libpng was not built with read support\n"); + /* And skip this test */ + return PNG_LIBPNG_VER < 10600 ? 0 : 77; +} +#endif + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef png_libpng_version_1_6_34 Your_png_h_is_not_version_1_6_34; diff --git a/src/png/libpng/pngtrans.c b/src/png/libpng/pngtrans.c new file mode 100644 index 0000000000..6882f0fd7b --- /dev/null +++ b/src/png/libpng/pngtrans.c @@ -0,0 +1,864 @@ + +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * Last changed in libpng 1.6.33 [September 28, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structrp png_ptr) +{ + png_debug(1, "in png_set_bgr"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Turn on 16-bit byte swapping */ +void PNGAPI +png_set_swap(png_structrp png_ptr) +{ + png_debug(1, "in png_set_swap"); + + if (png_ptr == NULL) + return; + + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Turn on pixel packing */ +void PNGAPI +png_set_packing(png_structrp png_ptr) +{ + png_debug(1, "in png_set_packing"); + + if (png_ptr == NULL) + return; + + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; +# ifdef PNG_WRITE_SUPPORTED + png_ptr->usr_bit_depth = 8; +# endif + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structrp png_ptr) +{ + png_debug(1, "in png_set_packswap"); + + if (png_ptr == NULL) + return; + + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits) +{ + png_debug(1, "in png_set_shift"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structrp png_ptr) +{ + png_debug(1, "in png_set_interlace handling"); + + if (png_ptr != 0 && png_ptr->interlaced != 0) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler"); + + if (png_ptr == NULL) + return; + + /* In libpng 1.6 it is possible to determine whether this is a read or write + * operation and therefore to do more checking here for a valid call. + */ + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) + { +# ifdef PNG_READ_FILLER_SUPPORTED + /* On read png_set_filler is always valid, regardless of the base PNG + * format, because other transformations can give a format where the + * filler code can execute (basically an 8 or 16-bit component RGB or G + * format.) + * + * NOTE: usr_channels is not used by the read code! (This has led to + * confusion in the past.) The filler is only used in the read code. + */ + png_ptr->filler = (png_uint_16)filler; +# else + png_app_error(png_ptr, "png_set_filler not supported on read"); + PNG_UNUSED(filler) /* not used in the write case */ + return; +# endif + } + + else /* write */ + { +# ifdef PNG_WRITE_FILLER_SUPPORTED + /* On write the usr_channels parameter must be set correctly at the + * start to record the number of channels in the app-supplied data. + */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_RGB: + png_ptr->usr_channels = 4; + break; + + case PNG_COLOR_TYPE_GRAY: + if (png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + break; + } + + else + { + /* There simply isn't any code in libpng to strip out bits + * from bytes when the components are less than a byte in + * size! + */ + png_app_error(png_ptr, + "png_set_filler is invalid for" + " low bit depth gray output"); + return; + } + + default: + png_app_error(png_ptr, + "png_set_filler: inappropriate color type"); + return; + } +# else + png_app_error(png_ptr, "png_set_filler not supported on write"); + return; +# endif + } + + /* Here on success - libpng supports the operation, set the transformation + * and the flag to say where the filler channel is. + */ + png_ptr->transformations |= PNG_FILLER; + + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; +} + +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha"); + + if (png_ptr == NULL) + return; + + png_set_filler(png_ptr, filler, filler_loc); + /* The above may fail to do anything. */ + if ((png_ptr->transformations & PNG_FILLER) != 0) + png_ptr->transformations |= PNG_ADD_ALPHA; +} + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structrp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structrp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structrp png_ptr) +{ + png_debug(1, "in png_set_invert_mono"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* Invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert"); + + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_size_t i; + png_size_t istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_size_t i; + png_size_t istop = row_info->rowbytes; + + for (i = 0; i < istop; i += 2) + { + *rp = (png_byte)(~(*rp)); + rp += 2; + } + } + +#ifdef PNG_16BIT_SUPPORTED + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_size_t i; + png_size_t istop = row_info->rowbytes; + + for (i = 0; i < istop; i += 4) + { + *rp = (png_byte)(~(*rp)); + *(rp + 1) = (png_byte)(~(*(rp + 1))); + rp += 4; + } + } +#endif +} +#endif + +#ifdef PNG_16BIT_SUPPORTED +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swaps byte order on 16-bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap"); + + if (row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { +#ifdef PNG_BUILTIN_BSWAP16_SUPPORTED + /* Feature added to libpng-1.6.11 for testing purposes, not + * enabled by default. + */ + *(png_uint_16*)rp = __builtin_bswap16(*(png_uint_16*)rp); +#else + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; +#endif + } + } +} +#endif +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static PNG_CONST png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static PNG_CONST png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static PNG_CONST png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* Swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap"); + + if (row_info->bit_depth < 8) + { + png_bytep rp; + png_const_bytep end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = onebppswaptable; + + else if (row_info->bit_depth == 2) + table = twobppswaptable; + + else if (row_info->bit_depth == 4) + table = fourbppswaptable; + + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PACKSWAP || WRITE_PACKSWAP */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* Remove a channel - this used to be 'png_do_strip_filler' but it used a + * somewhat weird combination of flags to determine what to do. All the calls + * to png_do_strip_filler are changed in 1.5.2 to call this instead with the + * correct arguments. + * + * The routine isn't general - the channel must be the channel at the start or + * end (not in the middle) of each pixel. + */ +void /* PRIVATE */ +png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start) +{ + png_bytep sp = row; /* source pointer */ + png_bytep dp = row; /* destination pointer */ + png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */ + + /* At the start sp will point to the first byte to copy and dp to where + * it is copied to. ep always points just beyond the end of the row, so + * the loop simply copies (channels-1) channels until sp reaches ep. + * + * at_start: 0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc. + * nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc. + */ + + /* GA, GX, XG cases */ + if (row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + if (at_start != 0) /* Skip initial filler */ + ++sp; + else /* Skip initial channel and, for sp, the filler */ + { + sp += 2; ++dp; + } + + /* For a 1 pixel wide image there is nothing to do */ + while (sp < ep) + { + *dp++ = *sp; sp += 2; + } + + row_info->pixel_depth = 8; + } + + else if (row_info->bit_depth == 16) + { + if (at_start != 0) /* Skip initial filler */ + sp += 2; + else /* Skip initial channel and, for sp, the filler */ + { + sp += 4; dp += 2; + } + + while (sp < ep) + { + *dp++ = *sp++; *dp++ = *sp; sp += 3; + } + + row_info->pixel_depth = 16; + } + + else + return; /* bad bit depth */ + + row_info->channels = 1; + + /* Finally fix the color type if it records an alpha channel */ + if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + row_info->color_type = PNG_COLOR_TYPE_GRAY; + } + + /* RGBA, RGBX, XRGB cases */ + else if (row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + if (at_start != 0) /* Skip initial filler */ + ++sp; + else /* Skip initial channels and, for sp, the filler */ + { + sp += 4; dp += 3; + } + + /* Note that the loop adds 3 to dp and 4 to sp each time. */ + while (sp < ep) + { + *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp; sp += 2; + } + + row_info->pixel_depth = 24; + } + + else if (row_info->bit_depth == 16) + { + if (at_start != 0) /* Skip initial filler */ + sp += 2; + else /* Skip initial channels and, for sp, the filler */ + { + sp += 8; dp += 6; + } + + while (sp < ep) + { + /* Copy 6 bytes, skip 2 */ + *dp++ = *sp++; *dp++ = *sp++; + *dp++ = *sp++; *dp++ = *sp++; + *dp++ = *sp++; *dp++ = *sp; sp += 3; + } + + row_info->pixel_depth = 48; + } + + else + return; /* bad bit depth */ + + row_info->channels = 3; + + /* Finally fix the color type if it records an alpha channel */ + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + row_info->color_type = PNG_COLOR_TYPE_RGB; + } + + else + return; /* The filler channel has gone already */ + + /* Fix the rowbytes value. */ + row_info->rowbytes = (png_size_t)(dp-row); +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr"); + + if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + +#ifdef PNG_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } +#endif + } +} +#endif /* READ_BGR || WRITE_BGR */ + +#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \ + defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) +/* Added at libpng-1.5.10 */ +void /* PRIVATE */ +png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info) +{ + if (png_ptr->num_palette < (1 << row_info->bit_depth) && + png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */ + { + /* Calculations moved outside switch in an attempt to stop different + * compiler warnings. 'padding' is in *bits* within the last byte, it is + * an 'int' because pixel_depth becomes an 'int' in the expression below, + * and this calculation is used because it avoids warnings that other + * forms produced on either GCC or MSVC. + */ + int padding = PNG_PADBITS(row_info->pixel_depth, row_info->width); + png_bytep rp = png_ptr->row_buf + row_info->rowbytes - 1; + + switch (row_info->bit_depth) + { + case 1: + { + /* in this case, all bytes must be 0 so we don't need + * to unpack the pixels except for the rightmost one. + */ + for (; rp > png_ptr->row_buf; rp--) + { + if ((*rp >> padding) != 0) + png_ptr->num_palette_max = 1; + padding = 0; + } + + break; + } + + case 2: + { + for (; rp > png_ptr->row_buf; rp--) + { + int i = ((*rp >> padding) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 2) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 4) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 6) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + padding = 0; + } + + break; + } + + case 4: + { + for (; rp > png_ptr->row_buf; rp--) + { + int i = ((*rp >> padding) & 0x0f); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 4) & 0x0f); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + padding = 0; + } + + break; + } + + case 8: + { + for (; rp > png_ptr->row_buf; rp--) + { + if (*rp > png_ptr->num_palette_max) + png_ptr->num_palette_max = (int) *rp; + } + + break; + } + + default: + break; + } + } +} +#endif /* CHECK_FOR_INVALID_INDEX */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +void PNGAPI +png_set_user_transform_info(png_structrp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + (png_ptr->flags & PNG_FLAG_ROW_INIT) != 0) + { + png_app_error(png_ptr, + "info change after png_start_read_image or png_read_update_info"); + return; + } +#endif + + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +} +#endif + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +png_voidp PNGAPI +png_get_user_transform_ptr(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + + return png_ptr->user_transform_ptr; +} +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +png_uint_32 PNGAPI +png_get_current_row_number(png_const_structrp png_ptr) +{ + /* See the comments in png.h - this is the sub-image row when reading an + * interlaced image. + */ + if (png_ptr != NULL) + return png_ptr->row_number; + + return PNG_UINT_32_MAX; /* help the app not to fail silently */ +} + +png_byte PNGAPI +png_get_current_pass_number(png_const_structrp png_ptr) +{ + if (png_ptr != NULL) + return png_ptr->pass; + return 8; /* invalid */ +} +#endif /* USER_TRANSFORM_INFO */ +#endif /* READ_USER_TRANSFORM || WRITE_USER_TRANSFORM */ +#endif /* READ || WRITE */ diff --git a/src/png/libpng/pngusr.dfa b/src/png/libpng/pngusr.dfa new file mode 100644 index 0000000000..83067c38c0 --- /dev/null +++ b/src/png/libpng/pngusr.dfa @@ -0,0 +1,14 @@ +# pngusr.dfa +# +# Build time configuration of libpng +# +# Enter build configuration options in this file +# +# Security settings: by default these limits are unset, you can change them +# here by entering the appropriate values as #defines preceded by '@' (to cause, +# them to be passed through to the build of pnglibconf.h), for example: +# +# @# define PNG_USER_WIDTH_MAX 65535 +# @# define PNG_USER_HEIGHT_MAX 65535 +# @# define PNG_USER_CHUNK_CACHE_MAX 256 +# @# define PNG_USER_CHUNK_MALLOC_MAX 640000 diff --git a/src/png/libpng/pngwio.c b/src/png/libpng/pngwio.c new file mode 100644 index 0000000000..37c7c3a7f0 --- /dev/null +++ b/src/png/libpng/pngwio.c @@ -0,0 +1,168 @@ + +/* pngwio.c - functions for data output + * + * Last changed in libpng 1.6.24 [August 4, 2016] + * Copyright (c) 1998-2002,2004,2006-2014,2016 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all output. Users who need + * special handling are expected to write functions that have the same + * arguments as these and perform similar functions, but that possibly + * use different output methods. Note that you shouldn't change these + * functions, but rather write replacement functions and then change + * them at run time with png_set_write_fn(...). + */ + +#include "pngpriv.h" + +#ifdef PNG_WRITE_SUPPORTED + +/* Write the data to whatever output you are using. The default routine + * writes to a file pointer. Note that this routine sometimes gets called + * with very small lengths, so you should implement some kind of simple + * buffering if you are using unbuffered writes. This should never be asked + * to write more than 64K on a 16-bit machine. + */ + +void /* PRIVATE */ +png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length) +{ + /* NOTE: write_data_fn must not change the buffer! */ + if (png_ptr->write_data_fn != NULL ) + (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data), + length); + + else + png_error(png_ptr, "Call to NULL write function"); +} + +#ifdef PNG_STDIO_SUPPORTED +/* This is the function that does the actual writing of data. If you are + * not writing to a standard C stream, you should create a replacement + * write_data function and use it at run time with png_set_write_fn(), rather + * than changing the library. + */ +void PNGCBAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + if (png_ptr == NULL) + return; + + check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); + + if (check != length) + png_error(png_ptr, "Write Error"); +} +#endif + +/* This function is called to output any data pending writing (normally + * to disk). After png_flush is called, there should be no data pending + * writing in any buffers. + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +void /* PRIVATE */ +png_flush(png_structrp png_ptr) +{ + if (png_ptr->output_flush_fn != NULL) + (*(png_ptr->output_flush_fn))(png_ptr); +} + +# ifdef PNG_STDIO_SUPPORTED +void PNGCBAPI +png_default_flush(png_structp png_ptr) +{ + png_FILE_p io_ptr; + + if (png_ptr == NULL) + return; + + io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr)); + fflush(io_ptr); +} +# endif +#endif + +/* This function allows the application to supply new output functions for + * libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * png_ptr - pointer to a png output data structure + * io_ptr - pointer to user supplied structure containing info about + * the output functions. May be NULL. + * write_data_fn - pointer to a new output function that takes as its + * arguments a pointer to a png_struct, a pointer to + * data to be written, and a 32-bit unsigned int that is + * the number of bytes to be written. The new write + * function should call png_error(png_ptr, "Error msg") + * to exit and output any fatal error messages. May be + * NULL, in which case libpng's default function will + * be used. + * flush_data_fn - pointer to a new flush function that takes as its + * arguments a pointer to a png_struct. After a call to + * the flush function, there should be no data in any buffers + * or pending transmission. If the output method doesn't do + * any buffering of output, a function prototype must still be + * supplied although it doesn't have to do anything. If + * PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + * time, output_flush_fn will be ignored, although it must be + * supplied for compatibility. May be NULL, in which case + * libpng's default function will be used, if + * PNG_WRITE_FLUSH_SUPPORTED is defined. This is not + * a good idea if io_ptr does not point to a standard + * *FILE structure. + */ +void PNGAPI +png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = io_ptr; + +#ifdef PNG_STDIO_SUPPORTED + if (write_data_fn != NULL) + png_ptr->write_data_fn = write_data_fn; + + else + png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_STDIO_SUPPORTED + + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; + + else + png_ptr->output_flush_fn = png_default_flush; + +# else + png_ptr->output_flush_fn = output_flush_fn; +# endif +#else + PNG_UNUSED(output_flush_fn) +#endif /* WRITE_FLUSH */ + +#ifdef PNG_READ_SUPPORTED + /* It is an error to read while writing a png file */ + if (png_ptr->read_data_fn != NULL) + { + png_ptr->read_data_fn = NULL; + + png_warning(png_ptr, + "Can't set both read_data_fn and write_data_fn in the" + " same structure"); + } +#endif +} +#endif /* WRITE */ diff --git a/src/png/libpng/pngwrite.c b/src/png/libpng/pngwrite.c new file mode 100644 index 0000000000..a16d77ce00 --- /dev/null +++ b/src/png/libpng/pngwrite.c @@ -0,0 +1,2396 @@ + +/* pngwrite.c - general routines to write a PNG file + * + * Last changed in libpng 1.6.32 [August 24, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +# include +#endif /* SIMPLIFIED_WRITE_STDIO */ + +#ifdef PNG_WRITE_SUPPORTED + +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +/* Write out all the unknown chunks for the current given location */ +static void +write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr, + unsigned int where) +{ + if (info_ptr->unknown_chunks_num != 0) + { + png_const_unknown_chunkp up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + ++up) + if ((up->location & where) != 0) + { + /* If per-chunk unknown chunk handling is enabled use it, otherwise + * just write the chunks the application has set. + */ +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + int keep = png_handle_as_unknown(png_ptr, up->name); + + /* NOTE: this code is radically different from the read side in the + * matter of handling an ancillary unknown chunk. In the read side + * the default behavior is to discard it, in the code below the default + * behavior is to write it. Critical chunks are, however, only + * written if explicitly listed or if the default is set to write all + * unknown chunks. + * + * The default handling is also slightly weird - it is not possible to + * stop the writing of all unsafe-to-copy chunks! + * + * TODO: REVIEW: this would seem to be a bug. + */ + if (keep != PNG_HANDLE_CHUNK_NEVER && + ((up->name[3] & 0x20) /* safe-to-copy overrides everything */ || + keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_AS_DEFAULT && + png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS))) +#endif + { + /* TODO: review, what is wrong with a zero length unknown chunk? */ + if (up->size == 0) + png_warning(png_ptr, "Writing zero-length unknown chunk"); + + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +} +#endif /* WRITE_UNKNOWN_CHUNKS */ + +/* Writes all the PNG information. This is the suggested way to use the + * library. If you have a new chunk to add, make a function to write it, + * and put it in the correct location here. If you want the chunk written + * after the image data, put it in png_write_end(). I strongly encourage + * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing + * the chunk, as that will keep the code from breaking if you want to just + * write a plain PNG file. If you have long comments, I suggest writing + * them in png_write_end(), and compressing them. + */ +void PNGAPI +png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr) +{ + png_debug(1, "in png_write_info_before_PLTE"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0) + { + /* Write PNG signature */ + png_write_sig(png_ptr); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 && \ + png_ptr->mng_features_permitted != 0) + { + png_warning(png_ptr, + "MNG features are not allowed in a PNG datastream"); + png_ptr->mng_features_permitted = 0; + } +#endif + + /* Write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + info_ptr->interlace_type +#else + 0 +#endif + ); + + /* The rest of these check to see if the valid field has the appropriate + * flag set, and if it does, writes the chunk. + * + * 1.6.0: COLORSPACE support controls the writing of these chunks too, and + * the chunks will be written if the WRITE routine is there and + * information * is available in the COLORSPACE. (See + * png_colorspace_sync_info in png.c for where the valid flags get set.) + * + * Under certain circumstances the colorspace can be invalidated without + * syncing the info_struct 'valid' flags; this happens if libpng detects + * an error and calls png_error while the color space is being set, yet + * the application continues writing the PNG. So check the 'invalid' + * flag here too. + */ +#ifdef PNG_GAMMA_SUPPORTED +# ifdef PNG_WRITE_gAMA_SUPPORTED + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 && + (info_ptr->valid & PNG_INFO_gAMA) != 0) + png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma); +# endif +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED + /* Write only one of sRGB or an ICC profile. If a profile was supplied + * and it matches one of the known sRGB ones issue a warning. + */ +# ifdef PNG_WRITE_iCCP_SUPPORTED + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->valid & PNG_INFO_iCCP) != 0) + { +# ifdef PNG_WRITE_sRGB_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sRGB) != 0) + png_app_warning(png_ptr, + "profile matches sRGB but writing iCCP instead"); +# endif + + png_write_iCCP(png_ptr, info_ptr->iccp_name, + info_ptr->iccp_profile); + } +# ifdef PNG_WRITE_sRGB_SUPPORTED + else +# endif +# endif + +# ifdef PNG_WRITE_sRGB_SUPPORTED + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->valid & PNG_INFO_sRGB) != 0) + png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent); +# endif /* WRITE_sRGB */ +#endif /* COLORSPACE */ + +#ifdef PNG_WRITE_sBIT_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sBIT) != 0) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED +# ifdef PNG_WRITE_cHRM_SUPPORTED + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0 && + (info_ptr->valid & PNG_INFO_cHRM) != 0) + png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy); +# endif +#endif + +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR); +#endif + + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; + } +} + +void PNGAPI +png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) +{ +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) + int i; +#endif + + png_debug(1, "in png_write_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_write_info_before_PLTE(png_ptr, info_ptr); + + if ((info_ptr->valid & PNG_INFO_PLTE) != 0) + png_write_PLTE(png_ptr, info_ptr->palette, + (png_uint_32)info_ptr->num_palette); + + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images"); + +#ifdef PNG_WRITE_tRNS_SUPPORTED + if ((info_ptr->valid & PNG_INFO_tRNS) !=0) + { +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0 && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + int j, jend; + + jend = info_ptr->num_trans; + if (jend > PNG_MAX_PALETTE_LENGTH) + jend = PNG_MAX_PALETTE_LENGTH; + + for (j = 0; jtrans_alpha[j] = + (png_byte)(255 - info_ptr->trans_alpha[j]); + } +#endif + png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#ifdef PNG_WRITE_bKGD_SUPPORTED + if ((info_ptr->valid & PNG_INFO_bKGD) != 0) + png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); +#endif + +#ifdef PNG_WRITE_eXIf_SUPPORTED + if ((info_ptr->valid & PNG_INFO_eXIf) != 0) + png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif); +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED + if ((info_ptr->valid & PNG_INFO_hIST) != 0) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED + if ((info_ptr->valid & PNG_INFO_oFFs) != 0) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +#endif + +#ifdef PNG_WRITE_pCAL_SUPPORTED + if ((info_ptr->valid & PNG_INFO_pCAL) != 0) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sCAL) != 0) + png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_s_width, info_ptr->scal_s_height); +#endif /* sCAL */ + +#ifdef PNG_WRITE_pHYs_SUPPORTED + if ((info_ptr->valid & PNG_INFO_pHYs) != 0) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif /* pHYs */ + +#ifdef PNG_WRITE_tIME_SUPPORTED + if ((info_ptr->valid & PNG_INFO_tIME) != 0) + { + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + png_ptr->mode |= PNG_WROTE_tIME; + } +#endif /* tIME */ + +#ifdef PNG_WRITE_sPLT_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sPLT) != 0) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif /* sPLT */ + +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing header text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); + /* Mark this chunk as written */ + if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + else + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + } + + /* If we want a compressed text chunk */ + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) + { +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, info_ptr->text[i].compression); + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + } + + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, + 0); + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; +#else + /* Can't get here */ + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + } + } +#endif /* tEXt */ + +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE); +#endif +} + +/* Writes the end of the PNG file. If you don't want to write comments or + * time information, you can pass NULL for info. If you already wrote these + * in png_write_info(), do not write them again here. If you have long + * comments, I suggest writing them here, and compressing them. + */ +void PNGAPI +png_write_end(png_structrp png_ptr, png_inforp info_ptr) +{ + png_debug(1, "in png_write_end"); + + if (png_ptr == NULL) + return; + + if ((png_ptr->mode & PNG_HAVE_IDAT) == 0) + png_error(png_ptr, "No IDATs written into file"); + +#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + if (png_ptr->num_palette_max > png_ptr->num_palette) + png_benign_error(png_ptr, "Wrote palette index exceeding num_palette"); +#endif + + /* See if user wants us to write information chunks */ + if (info_ptr != NULL) + { +#ifdef PNG_WRITE_TEXT_SUPPORTED + int i; /* local index variable */ +#endif +#ifdef PNG_WRITE_tIME_SUPPORTED + /* Check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) != 0 && + (png_ptr->mode & PNG_WROTE_tIME) == 0) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + +#endif +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Loop through comment chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing trailer text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); + /* Mark this chunk as written */ + if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + else + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + } + + else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) + { +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, info_ptr->text[i].compression); + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + } + + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + } + } +#endif + +#ifdef PNG_WRITE_eXIf_SUPPORTED + if ((info_ptr->valid & PNG_INFO_eXIf) != 0) + png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif); +#endif + +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT); +#endif + } + + png_ptr->mode |= PNG_AFTER_IDAT; + + /* Write end of PNG file */ + png_write_IEND(png_ptr); + + /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, + * and restored again in libpng-1.2.30, may cause some applications that + * do not set png_ptr->output_flush_fn to crash. If your application + * experiences a problem, please try building libpng with + * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to + * png-mng-implement at lists.sf.net . + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED + png_flush(png_ptr); +# endif +#endif +} + +#ifdef PNG_CONVERT_tIME_SUPPORTED +void PNGAPI +png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime) +{ + png_debug(1, "in png_convert_from_struct_tm"); + + ptime->year = (png_uint_16)(1900 + ttime->tm_year); + ptime->month = (png_byte)(ttime->tm_mon + 1); + ptime->day = (png_byte)ttime->tm_mday; + ptime->hour = (png_byte)ttime->tm_hour; + ptime->minute = (png_byte)ttime->tm_min; + ptime->second = (png_byte)ttime->tm_sec; +} + +void PNGAPI +png_convert_from_time_t(png_timep ptime, time_t ttime) +{ + struct tm *tbuf; + + png_debug(1, "in png_convert_from_time_t"); + + tbuf = gmtime(&ttime); + png_convert_from_struct_tm(ptime, tbuf); +} +#endif + +/* Initialize png_ptr structure, and allocate any memory needed */ +PNG_FUNCTION(png_structp,PNGAPI +png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED) +{ +#ifndef PNG_USER_MEM_SUPPORTED + png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, NULL, NULL, NULL); +#else + return png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, NULL, NULL, NULL); +} + +/* Alternate initialize png_ptr structure, and allocate any memory needed */ +PNG_FUNCTION(png_structp,PNGAPI +png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) +{ + png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); +#endif /* USER_MEM */ + if (png_ptr != NULL) + { + /* Set the zlib control values to defaults; they can be overridden by the + * application after the struct has been created. + */ + png_ptr->zbuffer_size = PNG_ZBUF_SIZE; + + /* The 'zlib_strategy' setting is irrelevant because png_default_claim in + * pngwutil.c defaults it according to whether or not filters will be + * used, and ignores this setting. + */ + png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY; + png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION; + png_ptr->zlib_mem_level = 8; + png_ptr->zlib_window_bits = 15; + png_ptr->zlib_method = 8; + +#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED + png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY; + png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION; + png_ptr->zlib_text_mem_level = 8; + png_ptr->zlib_text_window_bits = 15; + png_ptr->zlib_text_method = 8; +#endif /* WRITE_COMPRESSED_TEXT */ + + /* This is a highly dubious configuration option; by default it is off, + * but it may be appropriate for private builds that are testing + * extensions not conformant to the current specification, or of + * applications that must not fail to write at all costs! + */ +#ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED + /* In stable builds only warn if an application error can be completely + * handled. + */ + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; +#endif + + /* App warnings are warnings in release (or release candidate) builds but + * are errors during development. + */ +#if PNG_RELEASE_BUILD + png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; +#endif + + /* TODO: delay this, it can be done in png_init_io() (if the app doesn't + * do it itself) avoiding setting the default function if it is not + * required. + */ + png_set_write_fn(png_ptr, NULL, NULL, NULL); + } + + return png_ptr; +} + + +/* Write a few rows of image data. If the image is interlaced, + * either you will have to write the 7 sub images, or, if you + * have called png_set_interlace_handling(), you will have to + * "write" the image seven times. + */ +void PNGAPI +png_write_rows(png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows) +{ + png_uint_32 i; /* row counter */ + png_bytepp rp; /* row pointer */ + + png_debug(1, "in png_write_rows"); + + if (png_ptr == NULL) + return; + + /* Loop through the rows */ + for (i = 0, rp = row; i < num_rows; i++, rp++) + { + png_write_row(png_ptr, *rp); + } +} + +/* Write the image. You only need to call this function once, even + * if you are writing an interlaced image. + */ +void PNGAPI +png_write_image(png_structrp png_ptr, png_bytepp image) +{ + png_uint_32 i; /* row index */ + int pass, num_pass; /* pass variables */ + png_bytepp rp; /* points to current row */ + + if (png_ptr == NULL) + return; + + png_debug(1, "in png_write_image"); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Initialize interlace handling. If image is not interlaced, + * this will set pass to 1 + */ + num_pass = png_set_interlace_handling(png_ptr); +#else + num_pass = 1; +#endif + /* Loop through passes */ + for (pass = 0; pass < num_pass; pass++) + { + /* Loop through image */ + for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + { + png_write_row(png_ptr, *rp); + } + } +} + +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Performs intrapixel differencing */ +static void +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel"); + + if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)(*rp - *(rp + 1)); + *(rp + 2) = (png_byte)(*(rp + 2) - *(rp + 1)); + } + } + +#ifdef PNG_WRITE_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (png_uint_32)(*(rp ) << 8) | *(rp + 1); + png_uint_32 s1 = (png_uint_32)(*(rp + 2) << 8) | *(rp + 3); + png_uint_32 s2 = (png_uint_32)(*(rp + 4) << 8) | *(rp + 5); + png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL); + *(rp ) = (png_byte)(red >> 8); + *(rp + 1) = (png_byte)red; + *(rp + 4) = (png_byte)(blue >> 8); + *(rp + 5) = (png_byte)blue; + } + } +#endif /* WRITE_16BIT */ + } +} +#endif /* MNG_FEATURES */ + +/* Called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structrp png_ptr, png_const_bytep row) +{ + /* 1.5.6: moved from png_struct to be a local structure: */ + png_row_info row_info; + + if (png_ptr == NULL) + return; + + png_debug2(1, "in png_write_row (row %u, pass %d)", + png_ptr->row_number, png_ptr->pass); + + /* Initialize transformations and other stuff if first time */ + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* Make sure we wrote the header info */ + if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0) + png_error(png_ptr, + "png_write_info was never called before png_write_row"); + + /* Check for transforms that have been set but were defined out */ +#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) + if ((png_ptr->transformations & PNG_INVERT_MONO) != 0) + png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined"); +#endif + +#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) + if ((png_ptr->transformations & PNG_FILLER) != 0) + png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined"); +#endif +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + defined(PNG_READ_PACKSWAP_SUPPORTED) + if ((png_ptr->transformations & PNG_PACKSWAP) != 0) + png_warning(png_ptr, + "PNG_WRITE_PACKSWAP_SUPPORTED is not defined"); +#endif + +#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) != 0) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined"); +#endif + +#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) + if ((png_ptr->transformations & PNG_SHIFT) != 0) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined"); +#endif + +#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) + if ((png_ptr->transformations & PNG_BGR) != 0) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined"); +#endif + +#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) + if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined"); +#endif + + png_write_start_row(png_ptr); + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced and not interested in row, return */ + if (png_ptr->interlaced != 0 && + (png_ptr->transformations & PNG_INTERLACE) != 0) + { + switch (png_ptr->pass) + { + case 0: + if ((png_ptr->row_number & 0x07) != 0) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 1: + if ((png_ptr->row_number & 0x07) != 0 || png_ptr->width < 5) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 3: + if ((png_ptr->row_number & 0x03) != 0 || png_ptr->width < 3) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 4: + if ((png_ptr->row_number & 0x03) != 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 5: + if ((png_ptr->row_number & 0x01) != 0 || png_ptr->width < 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 6: + if ((png_ptr->row_number & 0x01) == 0) + { + png_write_finish_row(png_ptr); + return; + } + break; + + default: /* error: ignore it */ + break; + } + } +#endif + + /* Set up row info for transformations */ + row_info.color_type = png_ptr->color_type; + row_info.width = png_ptr->usr_width; + row_info.channels = png_ptr->usr_channels; + row_info.bit_depth = png_ptr->usr_bit_depth; + row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels); + row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); + + png_debug1(3, "row_info->color_type = %d", row_info.color_type); + png_debug1(3, "row_info->width = %u", row_info.width); + png_debug1(3, "row_info->channels = %d", row_info.channels); + png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes); + + /* Copy user's row into buffer, leaving room for filter byte. */ + memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Handle interlacing */ + if (png_ptr->interlaced && png_ptr->pass < 6 && + (png_ptr->transformations & PNG_INTERLACE) != 0) + { + png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass); + /* This should always get caught above, but still ... */ + if (row_info.width == 0) + { + png_write_finish_row(png_ptr); + return; + } + } +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + /* Handle other transformations */ + if (png_ptr->transformations != 0) + png_do_write_transformations(png_ptr, &row_info); +#endif + + /* At this point the row_info pixel depth must match the 'transformed' depth, + * which is also the output depth. + */ + if (row_info.pixel_depth != png_ptr->pixel_depth || + row_info.pixel_depth != png_ptr->transformed_pixel_depth) + png_error(png_ptr, "internal write transform logic error"); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1); + } +#endif + +/* Added at libpng-1.5.10 */ +#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Check for out-of-range palette index */ + if (row_info.color_type == PNG_COLOR_TYPE_PALETTE && + png_ptr->num_palette_max >= 0) + png_do_check_palette_indexes(png_ptr, &row_info); +#endif + + /* Find a filter if necessary, filter the row and write it out. */ + png_write_find_filter(png_ptr, &row_info); + + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set the automatic flush interval or 0 to turn flushing off */ +void PNGAPI +png_set_flush(png_structrp png_ptr, int nrows) +{ + png_debug(1, "in png_set_flush"); + + if (png_ptr == NULL) + return; + + png_ptr->flush_dist = (nrows < 0 ? 0 : (png_uint_32)nrows); +} + +/* Flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structrp png_ptr) +{ + png_debug(1, "in png_write_flush"); + + if (png_ptr == NULL) + return; + + /* We have already written out all of the data */ + if (png_ptr->row_number >= png_ptr->num_rows) + return; + + png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH); + png_ptr->flush_rows = 0; + png_flush(png_ptr); +} +#endif /* WRITE_FLUSH */ + +/* Free any memory used in png_ptr struct without freeing the struct itself. */ +static void +png_write_destroy(png_structrp png_ptr) +{ + png_debug(1, "in png_write_destroy"); + + /* Free any memory zlib uses */ + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0) + deflateEnd(&png_ptr->zstream); + + /* Free our memory. png_free checks NULL for us. */ + png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); + png_free(png_ptr, png_ptr->row_buf); + png_ptr->row_buf = NULL; +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->try_row); + png_free(png_ptr, png_ptr->tst_row); + png_ptr->prev_row = NULL; + png_ptr->try_row = NULL; + png_ptr->tst_row = NULL; +#endif + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list = NULL; +#endif + + /* The error handling and memory handling information is left intact at this + * point: the jmp_buf may still have to be freed. See png_destroy_png_struct + * for how this happens. + */ +} + +/* Free all memory used by the write. + * In libpng 1.6.0 this API changed quietly to no longer accept a NULL value for + * *png_ptr_ptr. Prior to 1.6.0 it would accept such a value and it would free + * the passed in info_structs but it would quietly fail to free any of the data + * inside them. In 1.6.0 it quietly does nothing (it has to be quiet because it + * has no png_ptr.) + */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_debug(1, "in png_destroy_write_struct"); + + if (png_ptr_ptr != NULL) + { + png_structrp png_ptr = *png_ptr_ptr; + + if (png_ptr != NULL) /* added in libpng 1.6.0 */ + { + png_destroy_info_struct(png_ptr, info_ptr_ptr); + + *png_ptr_ptr = NULL; + png_write_destroy(png_ptr); + png_destroy_png_struct(png_ptr); + } + } +} + +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structrp png_ptr, int method, int filters) +{ + png_debug(1, "in png_set_filter"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; + +#endif + if (method == PNG_FILTER_TYPE_BASE) + { + switch (filters & (PNG_ALL_FILTERS | 0x07)) + { +#ifdef PNG_WRITE_FILTER_SUPPORTED + case 5: + case 6: + case 7: png_app_error(png_ptr, "Unknown row filter for method 0"); +#endif /* WRITE_FILTER */ + /* FALLTHROUGH */ + case PNG_FILTER_VALUE_NONE: + png_ptr->do_filter = PNG_FILTER_NONE; break; + +#ifdef PNG_WRITE_FILTER_SUPPORTED + case PNG_FILTER_VALUE_SUB: + png_ptr->do_filter = PNG_FILTER_SUB; break; + + case PNG_FILTER_VALUE_UP: + png_ptr->do_filter = PNG_FILTER_UP; break; + + case PNG_FILTER_VALUE_AVG: + png_ptr->do_filter = PNG_FILTER_AVG; break; + + case PNG_FILTER_VALUE_PAETH: + png_ptr->do_filter = PNG_FILTER_PAETH; break; + + default: + png_ptr->do_filter = (png_byte)filters; break; +#else + default: + png_app_error(png_ptr, "Unknown row filter for method 0"); +#endif /* WRITE_FILTER */ + } + +#ifdef PNG_WRITE_FILTER_SUPPORTED + /* If we have allocated the row_buf, this means we have already started + * with the image and we should have allocated all of the filter buffers + * that have been selected. If prev_row isn't already allocated, then + * it is too late to start using the filters that need it, since we + * will be missing the data in the previous row. If an application + * wants to start and stop using particular filters during compression, + * it should start out with all of the filters, and then remove them + * or add them back after the start of compression. + * + * NOTE: this is a nasty constraint on the code, because it means that the + * prev_row buffer must be maintained even if there are currently no + * 'prev_row' requiring filters active. + */ + if (png_ptr->row_buf != NULL) + { + int num_filters; + png_alloc_size_t buf_size; + + /* Repeat the checks in png_write_start_row; 1 pixel high or wide + * images cannot benefit from certain filters. If this isn't done here + * the check below will fire on 1 pixel high images. + */ + if (png_ptr->height == 1) + filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); + + if (png_ptr->width == 1) + filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH); + + if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0 + && png_ptr->prev_row == NULL) + { + /* This is the error case, however it is benign - the previous row + * is not available so the filter can't be used. Just warn here. + */ + png_app_warning(png_ptr, + "png_set_filter: UP/AVG/PAETH cannot be added after start"); + filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); + } + + num_filters = 0; + + if (filters & PNG_FILTER_SUB) + num_filters++; + + if (filters & PNG_FILTER_UP) + num_filters++; + + if (filters & PNG_FILTER_AVG) + num_filters++; + + if (filters & PNG_FILTER_PAETH) + num_filters++; + + /* Allocate needed row buffers if they have not already been + * allocated. + */ + buf_size = PNG_ROWBYTES(png_ptr->usr_channels * png_ptr->usr_bit_depth, + png_ptr->width) + 1; + + if (png_ptr->try_row == NULL) + png_ptr->try_row = png_voidcast(png_bytep, + png_malloc(png_ptr, buf_size)); + + if (num_filters > 1) + { + if (png_ptr->tst_row == NULL) + png_ptr->tst_row = png_voidcast(png_bytep, + png_malloc(png_ptr, buf_size)); + } + } + png_ptr->do_filter = (png_byte)filters; +#endif + } + else + png_error(png_ptr, "Unknown custom filter method"); +} + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +/* Provide floating and fixed point APIs */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method, + int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs) +{ + PNG_UNUSED(png_ptr) + PNG_UNUSED(heuristic_method) + PNG_UNUSED(num_weights) + PNG_UNUSED(filter_weights) + PNG_UNUSED(filter_costs) +} +#endif /* FLOATING_POINT */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method, + int num_weights, png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs) +{ + PNG_UNUSED(png_ptr) + PNG_UNUSED(heuristic_method) + PNG_UNUSED(num_weights) + PNG_UNUSED(filter_weights) + PNG_UNUSED(filter_costs) +} +#endif /* FIXED_POINT */ +#endif /* WRITE_WEIGHTED_FILTER */ + +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +void PNGAPI +png_set_compression_level(png_structrp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structrp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structrp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy"); + + if (png_ptr == NULL) + return; + + /* The flag setting here prevents the libpng dynamic selection of strategy. + */ + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +void PNGAPI +png_set_compression_window_bits(png_structrp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + + /* Prior to 1.6.0 this would warn but then set the window_bits value. This + * meant that negative window bits values could be selected that would cause + * libpng to write a non-standard PNG file with raw deflate or gzip + * compressed IDAT or ancillary chunks. Such files can be read and there is + * no warning on read, so this seems like a very bad idea. + */ + if (window_bits > 15) + { + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } + + else if (window_bits < 8) + { + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } + + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structrp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method"); + + if (png_ptr == NULL) + return; + + /* This would produce an invalid PNG file if it worked, but it doesn't and + * deflate will fault it, so it is harmless to just warn here. + */ + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + + png_ptr->zlib_method = method; +} +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +/* The following were added to libpng-1.5.4 */ +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +void PNGAPI +png_set_text_compression_level(png_structrp png_ptr, int level) +{ + png_debug(1, "in png_set_text_compression_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_level = level; +} + +void PNGAPI +png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_text_compression_mem_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_mem_level = mem_level; +} + +void PNGAPI +png_set_text_compression_strategy(png_structrp png_ptr, int strategy) +{ + png_debug(1, "in png_set_text_compression_strategy"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_strategy = strategy; +} + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +void PNGAPI +png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + + if (window_bits > 15) + { + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } + + else if (window_bits < 8) + { + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } + + png_ptr->zlib_text_window_bits = window_bits; +} + +void PNGAPI +png_set_text_compression_method(png_structrp png_ptr, int method) +{ + png_debug(1, "in png_set_text_compression_method"); + + if (png_ptr == NULL) + return; + + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + + png_ptr->zlib_text_method = method; +} +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +/* end of API added to libpng-1.5.4 */ + +void PNGAPI +png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->write_row_fn = write_row_fn; +} + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +void PNGAPI +png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + png_debug(1, "in png_set_write_user_transform_fn"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->write_user_transform_fn = write_user_transform_fn; +} +#endif + + +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_write_png(png_structrp png_ptr, png_inforp info_ptr, + int transforms, voidp params) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + if ((info_ptr->valid & PNG_INFO_IDAT) == 0) + { + png_app_error(png_ptr, "no rows for png_write_image to write"); + return; + } + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* ------ these transformations don't touch the info structure ------- */ + + /* Invert monochrome pixels */ + if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0) +#ifdef PNG_WRITE_INVERT_SUPPORTED + png_set_invert_mono(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_MONO not supported"); +#endif + + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + if ((transforms & PNG_TRANSFORM_SHIFT) != 0) +#ifdef PNG_WRITE_SHIFT_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sBIT) != 0) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported"); +#endif + + /* Pack pixels into bytes */ + if ((transforms & PNG_TRANSFORM_PACKING) != 0) +#ifdef PNG_WRITE_PACK_SUPPORTED + png_set_packing(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_PACKING not supported"); +#endif + + /* Swap location of alpha bytes from ARGB to RGBA */ + if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0) +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + png_set_swap_alpha(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ALPHA not supported"); +#endif + + /* Remove a filler (X) from XRGB/RGBX/AG/GA into to convert it into + * RGB, note that the code expects the input color type to be G or RGB; no + * alpha channel. + */ + if ((transforms & (PNG_TRANSFORM_STRIP_FILLER_AFTER| + PNG_TRANSFORM_STRIP_FILLER_BEFORE)) != 0) + { +#ifdef PNG_WRITE_FILLER_SUPPORTED + if ((transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) != 0) + { + if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0) + png_app_error(png_ptr, + "PNG_TRANSFORM_STRIP_FILLER: BEFORE+AFTER not supported"); + + /* Continue if ignored - this is the pre-1.6.10 behavior */ + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + } + + else if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_FILLER not supported"); +#endif + } + + /* Flip BGR pixels to RGB */ + if ((transforms & PNG_TRANSFORM_BGR) != 0) +#ifdef PNG_WRITE_BGR_SUPPORTED + png_set_bgr(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_BGR not supported"); +#endif + + /* Swap bytes of 16-bit files to most significant byte first */ + if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0) +#ifdef PNG_WRITE_SWAP_SUPPORTED + png_set_swap(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ENDIAN not supported"); +#endif + + /* Swap bits of 1-bit, 2-bit, 4-bit packed pixel formats */ + if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0) +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + png_set_packswap(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_PACKSWAP not supported"); +#endif + + /* Invert the alpha channel from opacity to transparency */ + if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0) +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + png_set_invert_alpha(png_ptr); +#else + png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_ALPHA not supported"); +#endif + + /* ----------------------- end of transformations ------------------- */ + + /* Write the bits */ + png_write_image(png_ptr, info_ptr->row_pointers); + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); + + PNG_UNUSED(params) +} +#endif + + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* Initialize the write structure - general purpose utility. */ +static int +png_image_write_init(png_imagep image) +{ + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image, + png_safe_error, png_safe_warning); + + if (png_ptr != NULL) + { + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr != NULL) + { + png_controlp control = png_voidcast(png_controlp, + png_malloc_warn(png_ptr, (sizeof *control))); + + if (control != NULL) + { + memset(control, 0, (sizeof *control)); + + control->png_ptr = png_ptr; + control->info_ptr = info_ptr; + control->for_write = 1; + + image->opaque = control; + return 1; + } + + /* Error clean up */ + png_destroy_info_struct(png_ptr, &info_ptr); + } + + png_destroy_write_struct(&png_ptr, NULL); + } + + return png_image_error(image, "png_image_write_: out of memory"); +} + +/* Arguments to png_image_write_main: */ +typedef struct +{ + /* Arguments: */ + png_imagep image; + png_const_voidp buffer; + png_int_32 row_stride; + png_const_voidp colormap; + int convert_to_8bit; + /* Local variables: */ + png_const_voidp first_row; + ptrdiff_t row_bytes; + png_voidp local_row; + /* Byte count for memory writing */ + png_bytep memory; + png_alloc_size_t memory_bytes; /* not used for STDIO */ + png_alloc_size_t output_bytes; /* running total */ +} png_image_write_control; + +/* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to + * do any necessary byte swapping. The component order is defined by the + * png_image format value. + */ +static int +png_write_image_16bit(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + + png_const_uint_16p input_row = png_voidcast(png_const_uint_16p, + display->first_row); + png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row); + png_uint_16p row_end; + const unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? + 3 : 1; + int aindex = 0; + png_uint_32 y = image->height; + + if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0) + { +# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED + if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0) + { + aindex = -1; + ++input_row; /* To point to the first component */ + ++output_row; + } + else + aindex = (int)channels; +# else + aindex = (int)channels; +# endif + } + + else + png_error(png_ptr, "png_write_image: internal call error"); + + /* Work out the output row end and count over this, note that the increment + * above to 'row' means that row_end can actually be beyond the end of the + * row; this is correct. + */ + row_end = output_row + image->width * (channels+1); + + for (; y > 0; --y) + { + png_const_uint_16p in_ptr = input_row; + png_uint_16p out_ptr = output_row; + + while (out_ptr < row_end) + { + const png_uint_16 alpha = in_ptr[aindex]; + png_uint_32 reciprocal = 0; + int c; + + out_ptr[aindex] = alpha; + + /* Calculate a reciprocal. The correct calculation is simply + * component/alpha*65535 << 15. (I.e. 15 bits of precision); this + * allows correct rounding by adding .5 before the shift. 'reciprocal' + * is only initialized when required. + */ + if (alpha > 0 && alpha < 65535) + reciprocal = ((0xffff<<15)+(alpha>>1))/alpha; + + c = (int)channels; + do /* always at least one channel */ + { + png_uint_16 component = *in_ptr++; + + /* The following gives 65535 for an alpha of 0, which is fine, + * otherwise if 0/0 is represented as some other value there is more + * likely to be a discontinuity which will probably damage + * compression when moving from a fully transparent area to a + * nearly transparent one. (The assumption here is that opaque + * areas tend not to be 0 intensity.) + */ + if (component >= alpha) + component = 65535; + + /* component 0 && alpha < 65535) + { + png_uint_32 calc = component * reciprocal; + calc += 16384; /* round to nearest */ + component = (png_uint_16)(calc >> 15); + } + + *out_ptr++ = component; + } + while (--c > 0); + + /* Skip to next component (skip the intervening alpha channel) */ + ++in_ptr; + ++out_ptr; + } + + png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row)); + input_row += (png_uint_16)display->row_bytes/(sizeof (png_uint_16)); + } + + return 1; +} + +/* Given 16-bit input (1 to 4 channels) write 8-bit output. If an alpha channel + * is present it must be removed from the components, the components are then + * written in sRGB encoding. No components are added or removed. + * + * Calculate an alpha reciprocal to reverse pre-multiplication. As above the + * calculation can be done to 15 bits of accuracy; however, the output needs to + * be scaled in the range 0..255*65535, so include that scaling here. + */ +# define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha) + +static png_byte +png_unpremultiply(png_uint_32 component, png_uint_32 alpha, + png_uint_32 reciprocal/*from the above macro*/) +{ + /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0 + * is represented as some other value there is more likely to be a + * discontinuity which will probably damage compression when moving from a + * fully transparent area to a nearly transparent one. (The assumption here + * is that opaque areas tend not to be 0 intensity.) + * + * There is a rounding problem here; if alpha is less than 128 it will end up + * as 0 when scaled to 8 bits. To avoid introducing spurious colors into the + * output change for this too. + */ + if (component >= alpha || alpha < 128) + return 255; + + /* component 0) + { + /* The test is that alpha/257 (rounded) is less than 255, the first value + * that becomes 255 is 65407. + * NOTE: this must agree with the PNG_DIV257 macro (which must, therefore, + * be exact!) [Could also test reciprocal != 0] + */ + if (alpha < 65407) + { + component *= reciprocal; + component += 64; /* round to nearest */ + component >>= 7; + } + + else + component *= 255; + + /* Convert the component to sRGB. */ + return (png_byte)PNG_sRGB_FROM_LINEAR(component); + } + + else + return 0; +} + +static int +png_write_image_8bit(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + + png_const_uint_16p input_row = png_voidcast(png_const_uint_16p, + display->first_row); + png_bytep output_row = png_voidcast(png_bytep, display->local_row); + png_uint_32 y = image->height; + const unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? + 3 : 1; + + if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + png_bytep row_end; + int aindex; + +# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED + if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0) + { + aindex = -1; + ++input_row; /* To point to the first component */ + ++output_row; + } + + else +# endif + aindex = (int)channels; + + /* Use row_end in place of a loop counter: */ + row_end = output_row + image->width * (channels+1); + + for (; y > 0; --y) + { + png_const_uint_16p in_ptr = input_row; + png_bytep out_ptr = output_row; + + while (out_ptr < row_end) + { + png_uint_16 alpha = in_ptr[aindex]; + png_byte alphabyte = (png_byte)PNG_DIV257(alpha); + png_uint_32 reciprocal = 0; + int c; + + /* Scale and write the alpha channel. */ + out_ptr[aindex] = alphabyte; + + if (alphabyte > 0 && alphabyte < 255) + reciprocal = UNP_RECIPROCAL(alpha); + + c = (int)channels; + do /* always at least one channel */ + *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal); + while (--c > 0); + + /* Skip to next component (skip the intervening alpha channel) */ + ++in_ptr; + ++out_ptr; + } /* while out_ptr < row_end */ + + png_write_row(png_ptr, png_voidcast(png_const_bytep, + display->local_row)); + input_row += (png_uint_16)display->row_bytes/(sizeof (png_uint_16)); + } /* while y */ + } + + else + { + /* No alpha channel, so the row_end really is the end of the row and it + * is sufficient to loop over the components one by one. + */ + png_bytep row_end = output_row + image->width * channels; + + for (; y > 0; --y) + { + png_const_uint_16p in_ptr = input_row; + png_bytep out_ptr = output_row; + + while (out_ptr < row_end) + { + png_uint_32 component = *in_ptr++; + + component *= 255; + *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component); + } + + png_write_row(png_ptr, output_row); + input_row += (png_uint_16)display->row_bytes/(sizeof (png_uint_16)); + } + } + + return 1; +} + +static void +png_image_set_PLTE(png_image_write_control *display) +{ + const png_imagep image = display->image; + const void *cmap = display->colormap; + const int entries = image->colormap_entries > 256 ? 256 : + (int)image->colormap_entries; + + /* NOTE: the caller must check for cmap != NULL and entries != 0 */ + const png_uint_32 format = image->format; + const unsigned int channels = PNG_IMAGE_SAMPLE_CHANNELS(format); + +# if defined(PNG_FORMAT_BGR_SUPPORTED) &&\ + defined(PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED) + const int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 && + (format & PNG_FORMAT_FLAG_ALPHA) != 0; +# else +# define afirst 0 +# endif + +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int bgr = (format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0; +# else +# define bgr 0 +# endif + + int i, num_trans; + png_color palette[256]; + png_byte tRNS[256]; + + memset(tRNS, 255, (sizeof tRNS)); + memset(palette, 0, (sizeof palette)); + + for (i=num_trans=0; i= 3) /* RGB */ + { + palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 * + entry[(2 ^ bgr)]); + palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 * + entry[1]); + palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 * + entry[bgr]); + } + + else /* Gray */ + palette[i].blue = palette[i].red = palette[i].green = + (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry); + } + + else /* alpha */ + { + png_uint_16 alpha = entry[afirst ? 0 : channels-1]; + png_byte alphabyte = (png_byte)PNG_DIV257(alpha); + png_uint_32 reciprocal = 0; + + /* Calculate a reciprocal, as in the png_write_image_8bit code above + * this is designed to produce a value scaled to 255*65535 when + * divided by 128 (i.e. asr 7). + */ + if (alphabyte > 0 && alphabyte < 255) + reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha; + + tRNS[i] = alphabyte; + if (alphabyte < 255) + num_trans = i+1; + + if (channels >= 3) /* RGB */ + { + palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)], + alpha, reciprocal); + palette[i].green = png_unpremultiply(entry[afirst + 1], alpha, + reciprocal); + palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha, + reciprocal); + } + + else /* gray */ + palette[i].blue = palette[i].red = palette[i].green = + png_unpremultiply(entry[afirst], alpha, reciprocal); + } + } + + else /* Color-map has sRGB values */ + { + png_const_bytep entry = png_voidcast(png_const_bytep, cmap); + + entry += (unsigned int)i * channels; + + switch (channels) + { + case 4: + tRNS[i] = entry[afirst ? 0 : 3]; + if (tRNS[i] < 255) + num_trans = i+1; + /* FALLTHROUGH */ + case 3: + palette[i].blue = entry[afirst + (2 ^ bgr)]; + palette[i].green = entry[afirst + 1]; + palette[i].red = entry[afirst + bgr]; + break; + + case 2: + tRNS[i] = entry[1 ^ afirst]; + if (tRNS[i] < 255) + num_trans = i+1; + /* FALLTHROUGH */ + case 1: + palette[i].blue = palette[i].red = palette[i].green = + entry[afirst]; + break; + + default: + break; + } + } + } + +# ifdef afirst +# undef afirst +# endif +# ifdef bgr +# undef bgr +# endif + + png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette, + entries); + + if (num_trans > 0) + png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS, + num_trans, NULL); + + image->colormap_entries = (png_uint_32)entries; +} + +static int +png_image_write_main(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + png_uint_32 format = image->format; + + /* The following four ints are actually booleans */ + int colormap = (format & PNG_FORMAT_FLAG_COLORMAP); + int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR); /* input */ + int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA); + int write_16bit = linear && (display->convert_to_8bit == 0); + +# ifdef PNG_BENIGN_ERRORS_SUPPORTED + /* Make sure we error out on any bad situation */ + png_set_benign_errors(png_ptr, 0/*error*/); +# endif + + /* Default the 'row_stride' parameter if required, also check the row stride + * and total image size to ensure that they are within the system limits. + */ + { + const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format); + + if (image->width <= 0x7fffffffU/channels) /* no overflow */ + { + png_uint_32 check; + const png_uint_32 png_row_stride = image->width * channels; + + if (display->row_stride == 0) + display->row_stride = (png_int_32)/*SAFE*/png_row_stride; + + if (display->row_stride < 0) + check = (png_uint_32)(-display->row_stride); + + else + check = (png_uint_32)display->row_stride; + + if (check >= png_row_stride) + { + /* Now check for overflow of the image buffer calculation; this + * limits the whole image size to 32 bits for API compatibility with + * the current, 32-bit, PNG_IMAGE_BUFFER_SIZE macro. + */ + if (image->height > 0xffffffffU/png_row_stride) + png_error(image->opaque->png_ptr, "memory image too large"); + } + + else + png_error(image->opaque->png_ptr, "supplied row stride too small"); + } + + else + png_error(image->opaque->png_ptr, "image row stride too large"); + } + + /* Set the required transforms then write the rows in the correct order. */ + if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0) + { + if (display->colormap != NULL && image->colormap_entries > 0) + { + png_uint_32 entries = image->colormap_entries; + + png_set_IHDR(png_ptr, info_ptr, image->width, image->height, + entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)), + PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_image_set_PLTE(display); + } + + else + png_error(image->opaque->png_ptr, + "no color-map for color-mapped image"); + } + + else + png_set_IHDR(png_ptr, info_ptr, image->width, image->height, + write_16bit ? 16 : 8, + ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) + + ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0), + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + /* Counter-intuitively the data transformations must be called *after* + * png_write_info, not before as in the read code, but the 'set' functions + * must still be called before. Just set the color space information, never + * write an interlaced image. + */ + + if (write_16bit != 0) + { + /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */ + png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR); + + if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0) + png_set_cHRM_fixed(png_ptr, info_ptr, + /* color x y */ + /* white */ 31270, 32900, + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000 + ); + } + + else if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0) + png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); + + /* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit + * space must still be gamma encoded. + */ + else + png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE); + + /* Write the file header. */ + png_write_info(png_ptr, info_ptr); + + /* Now set up the data transformations (*after* the header is written), + * remove the handled transformations from the 'format' flags for checking. + * + * First check for a little endian system if writing 16-bit files. + */ + if (write_16bit != 0) + { + PNG_CONST png_uint_16 le = 0x0001; + + if ((*(png_const_bytep) & le) != 0) + png_set_swap(png_ptr); + } + +# ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED + if ((format & PNG_FORMAT_FLAG_BGR) != 0) + { + if (colormap == 0 && (format & PNG_FORMAT_FLAG_COLOR) != 0) + png_set_bgr(png_ptr); + format &= ~PNG_FORMAT_FLAG_BGR; + } +# endif + +# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED + if ((format & PNG_FORMAT_FLAG_AFIRST) != 0) + { + if (colormap == 0 && (format & PNG_FORMAT_FLAG_ALPHA) != 0) + png_set_swap_alpha(png_ptr); + format &= ~PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* If there are 16 or fewer color-map entries we wrote a lower bit depth + * above, but the application data is still byte packed. + */ + if (colormap != 0 && image->colormap_entries <= 16) + png_set_packing(png_ptr); + + /* That should have handled all (both) the transforms. */ + if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR | + PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0) + png_error(png_ptr, "png_write_image: unsupported transformation"); + + { + png_const_bytep row = png_voidcast(png_const_bytep, display->buffer); + ptrdiff_t row_bytes = display->row_stride; + + if (linear != 0) + row_bytes *= (sizeof (png_uint_16)); + + if (row_bytes < 0) + row += (image->height-1) * (-row_bytes); + + display->first_row = row; + display->row_bytes = row_bytes; + } + + /* Apply 'fast' options if the flag is set. */ + if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0) + { + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS); + /* NOTE: determined by experiment using pngstest, this reflects some + * balance between the time to write the image once and the time to read + * it about 50 times. The speed-up in pngstest was about 10-20% of the + * total (user) time on a heavily loaded system. + */ +# ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED + png_set_compression_level(png_ptr, 3); +# endif + } + + /* Check for the cases that currently require a pre-transform on the row + * before it is written. This only applies when the input is 16-bit and + * either there is an alpha channel or it is converted to 8-bit. + */ + if ((linear != 0 && alpha != 0 ) || + (colormap == 0 && display->convert_to_8bit != 0)) + { + png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr))); + int result; + + display->local_row = row; + if (write_16bit != 0) + result = png_safe_execute(image, png_write_image_16bit, display); + else + result = png_safe_execute(image, png_write_image_8bit, display); + display->local_row = NULL; + + png_free(png_ptr, row); + + /* Skip the 'write_end' on error: */ + if (result == 0) + return 0; + } + + /* Otherwise this is the case where the input is in a format currently + * supported by the rest of the libpng write code; call it directly. + */ + else + { + png_const_bytep row = png_voidcast(png_const_bytep, display->first_row); + ptrdiff_t row_bytes = display->row_bytes; + png_uint_32 y = image->height; + + for (; y > 0; --y) + { + png_write_row(png_ptr, row); + row += row_bytes; + } + } + + png_write_end(png_ptr, info_ptr); + return 1; +} + + +static void (PNGCBAPI +image_memory_write)(png_structp png_ptr, png_bytep/*const*/ data, + png_size_t size) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + png_ptr->io_ptr/*backdoor: png_get_io_ptr(png_ptr)*/); + const png_alloc_size_t ob = display->output_bytes; + + /* Check for overflow; this should never happen: */ + if (size <= ((png_alloc_size_t)-1) - ob) + { + /* I don't think libpng ever does this, but just in case: */ + if (size > 0) + { + if (display->memory_bytes >= ob+size) /* writing */ + memcpy(display->memory+ob, data, size); + + /* Always update the size: */ + display->output_bytes = ob+size; + } + } + + else + png_error(png_ptr, "png_image_write_to_memory: PNG too big"); +} + +static void (PNGCBAPI +image_memory_flush)(png_structp png_ptr) +{ + PNG_UNUSED(png_ptr) +} + +static int +png_image_write_memory(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + + /* The rest of the memory-specific init and write_main in an error protected + * environment. This case needs to use callbacks for the write operations + * since libpng has no built in support for writing to memory. + */ + png_set_write_fn(display->image->opaque->png_ptr, display/*io_ptr*/, + image_memory_write, image_memory_flush); + + return png_image_write_main(display); +} + +int PNGAPI +png_image_write_to_memory(png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8bit, + const void *buffer, png_int_32 row_stride, const void *colormap) +{ + /* Write the image to the given buffer, or count the bytes if it is NULL */ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (memory_bytes != NULL && buffer != NULL) + { + /* This is to give the caller an easier error detection in the NULL + * case and guard against uninitialized variable problems: + */ + if (memory == NULL) + *memory_bytes = 0; + + if (png_image_write_init(image) != 0) + { + png_image_write_control display; + int result; + + memset(&display, 0, (sizeof display)); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.colormap = colormap; + display.convert_to_8bit = convert_to_8bit; + display.memory = png_voidcast(png_bytep, memory); + display.memory_bytes = *memory_bytes; + display.output_bytes = 0; + + result = png_safe_execute(image, png_image_write_memory, &display); + png_image_free(image); + + /* write_memory returns true even if we ran out of buffer. */ + if (result) + { + /* On out-of-buffer this function returns '0' but still updates + * memory_bytes: + */ + if (memory != NULL && display.output_bytes > *memory_bytes) + result = 0; + + *memory_bytes = display.output_bytes; + } + + return result; + } + + else + return 0; + } + + else + return png_image_error(image, + "png_image_write_to_memory: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_write_to_memory: incorrect PNG_IMAGE_VERSION"); + + else + return 0; +} + +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +int PNGAPI +png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, + const void *buffer, png_int_32 row_stride, const void *colormap) +{ + /* Write the image to the given (FILE*). */ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file != NULL && buffer != NULL) + { + if (png_image_write_init(image) != 0) + { + png_image_write_control display; + int result; + + /* This is slightly evil, but png_init_io doesn't do anything other + * than this and we haven't changed the standard IO functions so + * this saves a 'safe' function. + */ + image->opaque->png_ptr->io_ptr = file; + + memset(&display, 0, (sizeof display)); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.colormap = colormap; + display.convert_to_8bit = convert_to_8bit; + + result = png_safe_execute(image, png_image_write_main, &display); + png_image_free(image); + return result; + } + + else + return 0; + } + + else + return png_image_error(image, + "png_image_write_to_stdio: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION"); + + else + return 0; +} + +int PNGAPI +png_image_write_to_file(png_imagep image, const char *file_name, + int convert_to_8bit, const void *buffer, png_int_32 row_stride, + const void *colormap) +{ + /* Write the image to the named file. */ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file_name != NULL && buffer != NULL) + { + FILE *fp = fopen(file_name, "wb"); + + if (fp != NULL) + { + if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer, + row_stride, colormap) != 0) + { + int error; /* from fflush/fclose */ + + /* Make sure the file is flushed correctly. */ + if (fflush(fp) == 0 && ferror(fp) == 0) + { + if (fclose(fp) == 0) + return 1; + + error = errno; /* from fclose */ + } + + else + { + error = errno; /* from fflush or ferror */ + (void)fclose(fp); + } + + (void)remove(file_name); + /* The image has already been cleaned up; this is just used to + * set the error (because the original write succeeded). + */ + return png_image_error(image, strerror(error)); + } + + else + { + /* Clean up: just the opened file. */ + (void)fclose(fp); + (void)remove(file_name); + return 0; + } + } + + else + return png_image_error(image, strerror(errno)); + } + + else + return png_image_error(image, + "png_image_write_to_file: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_write_to_file: incorrect PNG_IMAGE_VERSION"); + + else + return 0; +} +#endif /* SIMPLIFIED_WRITE_STDIO */ +#endif /* SIMPLIFIED_WRITE */ +#endif /* WRITE */ diff --git a/src/png/libpng/pngwtran.c b/src/png/libpng/pngwtran.c new file mode 100644 index 0000000000..377b43e5ca --- /dev/null +++ b/src/png/libpng/pngwtran.c @@ -0,0 +1,576 @@ + +/* pngwtran.c - transforms the data in a row for PNG writers + * + * Last changed in libpng 1.6.26 [October 20, 2016] + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + +#ifdef PNG_WRITE_PACK_SUPPORTED +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +static void +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack"); + + if (row_info->bit_depth == 8 && + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + + sp++; + + if (mask > 1) + mask >>= 1; + + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + + if (mask != 0x80) + *dp = (png_byte)v; + + break; + } + + case 2: + { + png_bytep sp, dp; + unsigned int shift; + int v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + + else + shift -= 2; + + sp++; + } + + if (shift != 6) + *dp = (png_byte)v; + + break; + } + + case 4: + { + png_bytep sp, dp; + unsigned int shift; + int v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + + else + shift -= 4; + + sp++; + } + + if (shift != 4) + *dp = (png_byte)v; + + break; + } + + default: + break; + } + + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +static void +png_do_shift(png_row_infop row_info, png_bytep row, + png_const_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift"); + + if (row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + unsigned int channels = 0; + + if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + + if ((row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* With low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_size_t i; + unsigned int mask; + png_size_t row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + int j; + unsigned int v, out; + + v = *bp; + out = 0; + + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + out |= v << j; + + else + out |= (v >> (-j)) & mask; + } + + *bp = (png_byte)(out & 0xff); + } + } + + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + const unsigned int c = i%channels; + int j; + unsigned int v, out; + + v = *bp; + out = 0; + + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + out |= v << j; + + else + out |= v >> (-j); + } + + *bp = (png_byte)(out & 0xff); + } + } + + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + const unsigned int c = i%channels; + int j; + unsigned int value, v; + + v = png_get_uint_16(bp); + value = 0; + + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= v << j; + + else + value |= v >> (-j); + } + *bp++ = (png_byte)((value >> 8) & 0xff); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED +static void +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha"); + + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This converts from ARGB to RGBA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + +#ifdef PNG_WRITE_16BIT_SUPPORTED + else + { + /* This converts from AARRGGBB to RRGGBBAA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } +#endif /* WRITE_16BIT */ + } + + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This converts from AG to GA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + +#ifdef PNG_WRITE_16BIT_SUPPORTED + else + { + /* This converts from AAGG to GGAA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } +#endif /* WRITE_16BIT */ + } + } +} +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED +static void +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha"); + + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This inverts the alpha channel in RGBA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=3; dp = sp; + *dp = (png_byte)(255 - *(sp++)); + } + } + +#ifdef PNG_WRITE_16BIT_SUPPORTED + else + { + /* This inverts the alpha channel in RRGGBBAA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=6; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *dp = (png_byte)(255 - *(sp++)); + } + } +#endif /* WRITE_16BIT */ + } + + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This inverts the alpha channel in GA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + +#ifdef PNG_WRITE_16BIT_SUPPORTED + else + { + /* This inverts the alpha channel in GGAA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=2; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *dp = (png_byte)(255 - *(sp++)); + } + } +#endif /* WRITE_16BIT */ + } + } +} +#endif + +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info) +{ + png_debug(1, "in png_do_write_transformations"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0) + if (png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* User write transform + function */ + (png_ptr, /* png_ptr */ + row_info, /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_size_t rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif + +#ifdef PNG_WRITE_FILLER_SUPPORTED + if ((png_ptr->transformations & PNG_FILLER) != 0) + png_do_strip_channel(row_info, png_ptr->row_buf + 1, + !(png_ptr->flags & PNG_FLAG_FILLER_AFTER)); +#endif + +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + if ((png_ptr->transformations & PNG_PACKSWAP) != 0) + png_do_packswap(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED + if ((png_ptr->transformations & PNG_PACK) != 0) + png_do_pack(row_info, png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif + +#ifdef PNG_WRITE_SWAP_SUPPORTED +# ifdef PNG_16BIT_SUPPORTED + if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0) + png_do_swap(row_info, png_ptr->row_buf + 1); +# endif +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED + if ((png_ptr->transformations & PNG_SHIFT) != 0) + png_do_shift(row_info, png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0) + png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0) + png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_WRITE_BGR_SUPPORTED + if ((png_ptr->transformations & PNG_BGR) != 0) + png_do_bgr(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_WRITE_INVERT_SUPPORTED + if ((png_ptr->transformations & PNG_INVERT_MONO) != 0) + png_do_invert(row_info, png_ptr->row_buf + 1); +#endif +} +#endif /* WRITE_TRANSFORMS */ +#endif /* WRITE */ diff --git a/src/png/libpng/pngwutil.c b/src/png/libpng/pngwutil.c new file mode 100644 index 0000000000..0d4fb1336c --- /dev/null +++ b/src/png/libpng/pngwutil.c @@ -0,0 +1,2784 @@ + +/* pngwutil.c - utilities to write a PNG file + * + * Last changed in libpng 1.6.32 [August 24, 2017] + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" + +#ifdef PNG_WRITE_SUPPORTED + +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +/* Place a 32-bit number into a buffer in PNG byte order. We work + * with unsigned numbers for convenience, although one supported + * ancillary chunk uses signed (two's complement) numbers. + */ +void PNGAPI +png_save_uint_32(png_bytep buf, png_uint_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xffU); + buf[1] = (png_byte)((i >> 16) & 0xffU); + buf[2] = (png_byte)((i >> 8) & 0xffU); + buf[3] = (png_byte)( i & 0xffU); +} + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +void PNGAPI +png_save_uint_16(png_bytep buf, unsigned int i) +{ + buf[0] = (png_byte)((i >> 8) & 0xffU); + buf[1] = (png_byte)( i & 0xffU); +} +#endif + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void PNGAPI +png_write_sig(png_structrp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the signature is being written */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_SIGNATURE; +#endif + + /* Write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)(8 - png_ptr->sig_bytes)); + + if (png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +/* Write the start of a PNG chunk. The type is the chunk type. + * The total_length is the sum of the lengths of all the data you will be + * passing in png_write_chunk_data(). + */ +static void +png_write_chunk_header(png_structrp png_ptr, png_uint_32 chunk_name, + png_uint_32 length) +{ + png_byte buf[8]; + +#if defined(PNG_DEBUG) && (PNG_DEBUG > 0) + PNG_CSTRING_FROM_CHUNK(buf, chunk_name); + png_debug2(0, "Writing %s chunk, length = %lu", buf, (unsigned long)length); +#endif + + if (png_ptr == NULL) + return; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the chunk header is being written. + * PNG_IO_CHUNK_HDR requires a single I/O call. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_HDR; +#endif + + /* Write the length and the chunk name */ + png_save_uint_32(buf, length); + png_save_uint_32(buf + 4, chunk_name); + png_write_data(png_ptr, buf, 8); + + /* Put the chunk name into png_ptr->chunk_name */ + png_ptr->chunk_name = chunk_name; + + /* Reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + + png_calculate_crc(png_ptr, buf + 4, 4); + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that chunk data will (possibly) be written. + * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_DATA; +#endif +} + +void PNGAPI +png_write_chunk_start(png_structrp png_ptr, png_const_bytep chunk_string, + png_uint_32 length) +{ + png_write_chunk_header(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), length); +} + +/* Write the data of a PNG chunk started with png_write_chunk_header(). + * Note that multiple calls to this function are allowed, and that the + * sum of the lengths from these calls *must* add up to the total_length + * given to png_write_chunk_header(). + */ +void PNGAPI +png_write_chunk_data(png_structrp png_ptr, png_const_bytep data, + png_size_t length) +{ + /* Write the data, and run the CRC over it */ + if (png_ptr == NULL) + return; + + if (data != NULL && length > 0) + { + png_write_data(png_ptr, data, length); + + /* Update the CRC after writing the data, + * in case the user I/O routine alters it. + */ + png_calculate_crc(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_header(). */ +void PNGAPI +png_write_chunk_end(png_structrp png_ptr) +{ + png_byte buf[4]; + + if (png_ptr == NULL) return; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the chunk CRC is being written. + * PNG_IO_CHUNK_CRC requires a single I/O function call. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_CRC; +#endif + + /* Write the crc in a single operation */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} + +/* Write a PNG chunk all at once. The type is an array of ASCII characters + * representing the chunk name. The array must be at least 4 bytes in + * length, and does not need to be null terminated. To be safe, pass the + * pre-defined chunk names here, and if you need a new one, define it + * where the others are defined. The length is the length of the data. + * All the data must be present. If that is not possible, use the + * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() + * functions instead. + */ +static void +png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name, + png_const_bytep data, png_size_t length) +{ + if (png_ptr == NULL) + return; + + /* On 64-bit architectures 'length' may not fit in a png_uint_32. */ + if (length > PNG_UINT_31_MAX) + png_error(png_ptr, "length exceeds PNG maximum"); + + png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_data(png_ptr, data, length); + png_write_chunk_end(png_ptr); +} + +/* This is the API that calls the internal function above. */ +void PNGAPI +png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string, + png_const_bytep data, png_size_t length) +{ + png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data, + length); +} + +/* This is used below to find the size of an image to pass to png_deflate_claim, + * so it only needs to be accurate if the size is less than 16384 bytes (the + * point at which a lower LZ window size can be used.) + */ +static png_alloc_size_t +png_image_size(png_structrp png_ptr) +{ + /* Only return sizes up to the maximum of a png_uint_32; do this by limiting + * the width and height used to 15 bits. + */ + png_uint_32 h = png_ptr->height; + + if (png_ptr->rowbytes < 32768 && h < 32768) + { + if (png_ptr->interlaced != 0) + { + /* Interlacing makes the image larger because of the replication of + * both the filter byte and the padding to a byte boundary. + */ + png_uint_32 w = png_ptr->width; + unsigned int pd = png_ptr->pixel_depth; + png_alloc_size_t cb_base; + int pass; + + for (cb_base=0, pass=0; pass<=6; ++pass) + { + png_uint_32 pw = PNG_PASS_COLS(w, pass); + + if (pw > 0) + cb_base += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass); + } + + return cb_base; + } + + else + return (png_ptr->rowbytes+1) * h; + } + + else + return 0xffffffffU; +} + +#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + /* This is the code to hack the first two bytes of the deflate stream (the + * deflate header) to correct the windowBits value to match the actual data + * size. Note that the second argument is the *uncompressed* size but the + * first argument is the *compressed* data (and it must be deflate + * compressed.) + */ +static void +optimize_cmf(png_bytep data, png_alloc_size_t data_size) +{ + /* Optimize the CMF field in the zlib stream. The resultant zlib stream is + * still compliant to the stream specification. + */ + if (data_size <= 16384) /* else windowBits must be 15 */ + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + unsigned int z_cinfo; + unsigned int half_z_window_size; + + z_cinfo = z_cmf >> 4; + half_z_window_size = 1U << (z_cinfo + 7); + + if (data_size <= half_z_window_size) /* else no change */ + { + unsigned int tmp; + + do + { + half_z_window_size >>= 1; + --z_cinfo; + } + while (z_cinfo > 0 && data_size <= half_z_window_size); + + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + + data[0] = (png_byte)z_cmf; + tmp = data[1] & 0xe0; + tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f; + data[1] = (png_byte)tmp; + } + } + } +} +#endif /* WRITE_OPTIMIZE_CMF */ + +/* Initialize the compressor for the appropriate type of compression. */ +static int +png_deflate_claim(png_structrp png_ptr, png_uint_32 owner, + png_alloc_size_t data_size) +{ + if (png_ptr->zowner != 0) + { +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED) + char msg[64]; + + PNG_STRING_FROM_CHUNK(msg, owner); + msg[4] = ':'; + msg[5] = ' '; + PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner); + /* So the message that results is " using zstream"; this is an + * internal error, but is very useful for debugging. i18n requirements + * are minimal. + */ + (void)png_safecat(msg, (sizeof msg), 10, " using zstream"); +#endif +#if PNG_RELEASE_BUILD + png_warning(png_ptr, msg); + + /* Attempt sane error recovery */ + if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */ + { + png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT"); + return Z_STREAM_ERROR; + } + + png_ptr->zowner = 0; +#else + png_error(png_ptr, msg); +#endif + } + + { + int level = png_ptr->zlib_level; + int method = png_ptr->zlib_method; + int windowBits = png_ptr->zlib_window_bits; + int memLevel = png_ptr->zlib_mem_level; + int strategy; /* set below */ + int ret; /* zlib return code */ + + if (owner == png_IDAT) + { + if ((png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY) != 0) + strategy = png_ptr->zlib_strategy; + + else if (png_ptr->do_filter != PNG_FILTER_NONE) + strategy = PNG_Z_DEFAULT_STRATEGY; + + else + strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY; + } + + else + { +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + level = png_ptr->zlib_text_level; + method = png_ptr->zlib_text_method; + windowBits = png_ptr->zlib_text_window_bits; + memLevel = png_ptr->zlib_text_mem_level; + strategy = png_ptr->zlib_text_strategy; +#else + /* If customization is not supported the values all come from the + * IDAT values except for the strategy, which is fixed to the + * default. (This is the pre-1.6.0 behavior too, although it was + * implemented in a very different way.) + */ + strategy = Z_DEFAULT_STRATEGY; +#endif + } + + /* Adjust 'windowBits' down if larger than 'data_size'; to stop this + * happening just pass 32768 as the data_size parameter. Notice that zlib + * requires an extra 262 bytes in the window in addition to the data to be + * able to see the whole of the data, so if data_size+262 takes us to the + * next windowBits size we need to fix up the value later. (Because even + * though deflate needs the extra window, inflate does not!) + */ + if (data_size <= 16384) + { + /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to + * work round a Microsoft Visual C misbehavior which, contrary to C-90, + * widens the result of the following shift to 64-bits if (and, + * apparently, only if) it is used in a test. + */ + unsigned int half_window_size = 1U << (windowBits-1); + + while (data_size + 262 <= half_window_size) + { + half_window_size >>= 1; + --windowBits; + } + } + + /* Check against the previous initialized values, if any. */ + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0 && + (png_ptr->zlib_set_level != level || + png_ptr->zlib_set_method != method || + png_ptr->zlib_set_window_bits != windowBits || + png_ptr->zlib_set_mem_level != memLevel || + png_ptr->zlib_set_strategy != strategy)) + { + if (deflateEnd(&png_ptr->zstream) != Z_OK) + png_warning(png_ptr, "deflateEnd failed (ignored)"); + + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED; + } + + /* For safety clear out the input and output pointers (currently zlib + * doesn't use them on Init, but it might in the future). + */ + png_ptr->zstream.next_in = NULL; + png_ptr->zstream.avail_in = 0; + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0; + + /* Now initialize if required, setting the new parameters, otherwise just + * do a simple reset to the previous parameters. + */ + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0) + ret = deflateReset(&png_ptr->zstream); + + else + { + ret = deflateInit2(&png_ptr->zstream, level, method, windowBits, + memLevel, strategy); + + if (ret == Z_OK) + png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; + } + + /* The return code is from either deflateReset or deflateInit2; they have + * pretty much the same set of error codes. + */ + if (ret == Z_OK) + png_ptr->zowner = owner; + + else + png_zstream_error(png_ptr, ret); + + return ret; + } +} + +/* Clean up (or trim) a linked list of compression buffers. */ +void /* PRIVATE */ +png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp) +{ + png_compression_bufferp list = *listp; + + if (list != NULL) + { + *listp = NULL; + + do + { + png_compression_bufferp next = list->next; + + png_free(png_ptr, list); + list = next; + } + while (list != NULL); + } +} + +#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +/* This pair of functions encapsulates the operation of (a) compressing a + * text string, and (b) issuing it later as a series of chunk data writes. + * The compression_state structure is shared context for these functions + * set up by the caller to allow access to the relevant local variables. + * + * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size + * temporary buffers. From 1.6.0 it is retained in png_struct so that it will + * be correctly freed in the event of a write error (previous implementations + * just leaked memory.) + */ +typedef struct +{ + png_const_bytep input; /* The uncompressed input data */ + png_alloc_size_t input_len; /* Its length */ + png_uint_32 output_len; /* Final compressed length */ + png_byte output[1024]; /* First block of output */ +} compression_state; + +static void +png_text_compress_init(compression_state *comp, png_const_bytep input, + png_alloc_size_t input_len) +{ + comp->input = input; + comp->input_len = input_len; + comp->output_len = 0; +} + +/* Compress the data in the compression state input */ +static int +png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name, + compression_state *comp, png_uint_32 prefix_len) +{ + int ret; + + /* To find the length of the output it is necessary to first compress the + * input. The result is buffered rather than using the two-pass algorithm + * that is used on the inflate side; deflate is assumed to be slower and a + * PNG writer is assumed to have more memory available than a PNG reader. + * + * IMPLEMENTATION NOTE: the zlib API deflateBound() can be used to find an + * upper limit on the output size, but it is always bigger than the input + * size so it is likely to be more efficient to use this linked-list + * approach. + */ + ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len); + + if (ret != Z_OK) + return ret; + + /* Set up the compression buffers, we need a loop here to avoid overflowing a + * uInt. Use ZLIB_IO_MAX to limit the input. The output is always limited + * by the output buffer size, so there is no need to check that. Since this + * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits + * in size. + */ + { + png_compression_bufferp *end = &png_ptr->zbuffer_list; + png_alloc_size_t input_len = comp->input_len; /* may be zero! */ + png_uint_32 output_len; + + /* zlib updates these for us: */ + png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input); + png_ptr->zstream.avail_in = 0; /* Set below */ + png_ptr->zstream.next_out = comp->output; + png_ptr->zstream.avail_out = (sizeof comp->output); + + output_len = png_ptr->zstream.avail_out; + + do + { + uInt avail_in = ZLIB_IO_MAX; + + if (avail_in > input_len) + avail_in = (uInt)input_len; + + input_len -= avail_in; + + png_ptr->zstream.avail_in = avail_in; + + if (png_ptr->zstream.avail_out == 0) + { + png_compression_buffer *next; + + /* Chunk data is limited to 2^31 bytes in length, so the prefix + * length must be counted here. + */ + if (output_len + prefix_len > PNG_UINT_31_MAX) + { + ret = Z_MEM_ERROR; + break; + } + + /* Need a new (malloc'ed) buffer, but there may be one present + * already. + */ + next = *end; + if (next == NULL) + { + next = png_voidcast(png_compression_bufferp, png_malloc_base + (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr))); + + if (next == NULL) + { + ret = Z_MEM_ERROR; + break; + } + + /* Link in this buffer (so that it will be freed later) */ + next->next = NULL; + *end = next; + } + + png_ptr->zstream.next_out = next->output; + png_ptr->zstream.avail_out = png_ptr->zbuffer_size; + output_len += png_ptr->zstream.avail_out; + + /* Move 'end' to the next buffer pointer. */ + end = &next->next; + } + + /* Compress the data */ + ret = deflate(&png_ptr->zstream, + input_len > 0 ? Z_NO_FLUSH : Z_FINISH); + + /* Claw back input data that was not consumed (because avail_in is + * reset above every time round the loop). + */ + input_len += png_ptr->zstream.avail_in; + png_ptr->zstream.avail_in = 0; /* safety */ + } + while (ret == Z_OK); + + /* There may be some space left in the last output buffer. This needs to + * be subtracted from output_len. + */ + output_len -= png_ptr->zstream.avail_out; + png_ptr->zstream.avail_out = 0; /* safety */ + comp->output_len = output_len; + + /* Now double check the output length, put in a custom message if it is + * too long. Otherwise ensure the z_stream::msg pointer is set to + * something. + */ + if (output_len + prefix_len >= PNG_UINT_31_MAX) + { + png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long"); + ret = Z_MEM_ERROR; + } + + else + png_zstream_error(png_ptr, ret); + + /* Reset zlib for another zTXt/iTXt or image data */ + png_ptr->zowner = 0; + + /* The only success case is Z_STREAM_END, input_len must be 0; if not this + * is an internal error. + */ + if (ret == Z_STREAM_END && input_len == 0) + { +#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + /* Fix up the deflate header, if required */ + optimize_cmf(comp->output, comp->input_len); +#endif + /* But Z_OK is returned, not Z_STREAM_END; this allows the claim + * function above to return Z_STREAM_END on an error (though it never + * does in the current versions of zlib.) + */ + return Z_OK; + } + + else + return ret; + } +} + +/* Ship the compressed text out via chunk writes */ +static void +png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp) +{ + png_uint_32 output_len = comp->output_len; + png_const_bytep output = comp->output; + png_uint_32 avail = (sizeof comp->output); + png_compression_buffer *next = png_ptr->zbuffer_list; + + for (;;) + { + if (avail > output_len) + avail = output_len; + + png_write_chunk_data(png_ptr, output, avail); + + output_len -= avail; + + if (output_len == 0 || next == NULL) + break; + + avail = png_ptr->zbuffer_size; + output = next->output; + next = next->next; + } + + /* This is an internal error; 'next' must have been NULL! */ + if (output_len > 0) + png_error(png_ptr, "error writing ancillary chunked compressed data"); +} +#endif /* WRITE_COMPRESSED_TEXT */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. Note that the rest of this code depends upon this + * information being correct. + */ +void /* PRIVATE */ +png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) +{ + png_byte buf[13]; /* Buffer to store the IHDR info */ + int is_invalid_depth; + + png_debug(1, "in png_write_IHDR"); + + /* Check that we have valid input data from the application info */ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: +#ifdef PNG_WRITE_16BIT_SUPPORTED + case 16: +#endif + png_ptr->channels = 1; break; + + default: + png_error(png_ptr, + "Invalid bit depth for grayscale image"); + } + break; + + case PNG_COLOR_TYPE_RGB: + is_invalid_depth = (bit_depth != 8); +#ifdef PNG_WRITE_16BIT_SUPPORTED + is_invalid_depth = (is_invalid_depth && bit_depth != 16); +#endif + if (is_invalid_depth) + png_error(png_ptr, "Invalid bit depth for RGB image"); + + png_ptr->channels = 3; + break; + + case PNG_COLOR_TYPE_PALETTE: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: + png_ptr->channels = 1; + break; + + default: + png_error(png_ptr, "Invalid bit depth for paletted image"); + } + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + is_invalid_depth = (bit_depth != 8); +#ifdef PNG_WRITE_16BIT_SUPPORTED + is_invalid_depth = (is_invalid_depth && bit_depth != 16); +#endif + if (is_invalid_depth) + png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + + png_ptr->channels = 2; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + is_invalid_depth = (bit_depth != 8); +#ifdef PNG_WRITE_16BIT_SUPPORTED + is_invalid_depth = (is_invalid_depth && bit_depth != 16); +#endif + if (is_invalid_depth) + png_error(png_ptr, "Invalid bit depth for RGBA image"); + + png_ptr->channels = 4; + break; + + default: + png_error(png_ptr, "Invalid image color type specified"); + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Invalid compression type specified"); + compression_type = PNG_COMPRESSION_TYPE_BASE; + } + + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ( +#ifdef PNG_MNG_FEATURES_SUPPORTED + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#endif + filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Invalid filter type specified"); + filter_type = PNG_FILTER_TYPE_BASE; + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (interlace_type != PNG_INTERLACE_NONE && + interlace_type != PNG_INTERLACE_ADAM7) + { + png_warning(png_ptr, "Invalid interlace type specified"); + interlace_type = PNG_INTERLACE_ADAM7; + } +#else + interlace_type=PNG_INTERLACE_NONE; +#endif + + /* Save the relevant information */ + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->color_type = (png_byte)color_type; + png_ptr->interlaced = (png_byte)interlace_type; +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + png_ptr->width = width; + png_ptr->height = height; + + png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); + /* Set the usr info, so any transformations can modify it */ + png_ptr->usr_width = png_ptr->width; + png_ptr->usr_bit_depth = png_ptr->bit_depth; + png_ptr->usr_channels = png_ptr->channels; + + /* Pack the header information into the buffer */ + png_save_uint_32(buf, width); + png_save_uint_32(buf + 4, height); + buf[8] = (png_byte)bit_depth; + buf[9] = (png_byte)color_type; + buf[10] = (png_byte)compression_type; + buf[11] = (png_byte)filter_type; + buf[12] = (png_byte)interlace_type; + + /* Write the chunk */ + png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); + + if ((png_ptr->do_filter) == PNG_NO_FILTERS) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->do_filter = PNG_FILTER_NONE; + + else + png_ptr->do_filter = PNG_ALL_FILTERS; + } + + png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */ +} + +/* Write the palette. We are careful not to trust png_color to be in the + * correct order for PNG, so people can redefine it to any convenient + * structure. + */ +void /* PRIVATE */ +png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, + png_uint_32 num_pal) +{ + png_uint_32 max_palette_length, i; + png_const_colorp pal_ptr; + png_byte buf[3]; + + png_debug(1, "in png_write_PLTE"); + + max_palette_length = (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ? + (1 << png_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH; + + if (( +#ifdef PNG_MNG_FEATURES_SUPPORTED + (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 && +#endif + num_pal == 0) || num_pal > max_palette_length) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } + } + + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) + { + png_warning(png_ptr, + "Ignoring request to write a PLTE chunk in grayscale PNG"); + + return; + } + + png_ptr->num_palette = (png_uint_16)num_pal; + png_debug1(3, "num_palette = %d", png_ptr->num_palette); + + png_write_chunk_header(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3)); +#ifdef PNG_POINTER_INDEXING_SUPPORTED + + for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) + { + buf[0] = pal_ptr->red; + buf[1] = pal_ptr->green; + buf[2] = pal_ptr->blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } + +#else + /* This is a little slower but some buggy compilers need to do this + * instead + */ + pal_ptr=palette; + + for (i = 0; i < num_pal; i++) + { + buf[0] = pal_ptr[i].red; + buf[1] = pal_ptr[i].green; + buf[2] = pal_ptr[i].blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } + +#endif + png_write_chunk_end(png_ptr); + png_ptr->mode |= PNG_HAVE_PLTE; +} + +/* This is similar to png_text_compress, above, except that it does not require + * all of the data at once and, instead of buffering the compressed result, + * writes it as IDAT chunks. Unlike png_text_compress it *can* png_error out + * because it calls the write interface. As a result it does its own error + * reporting and does not return an error code. In the event of error it will + * just call png_error. The input data length may exceed 32-bits. The 'flush' + * parameter is exactly the same as that to deflate, with the following + * meanings: + * + * Z_NO_FLUSH: normal incremental output of compressed data + * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush + * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up + * + * The routine manages the acquire and release of the png_ptr->zstream by + * checking and (at the end) clearing png_ptr->zowner; it does some sanity + * checks on the 'mode' flags while doing this. + */ +void /* PRIVATE */ +png_compress_IDAT(png_structrp png_ptr, png_const_bytep input, + png_alloc_size_t input_len, int flush) +{ + if (png_ptr->zowner != png_IDAT) + { + /* First time. Ensure we have a temporary buffer for compression and + * trim the buffer list if it has more than one entry to free memory. + * If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been + * created at this point, but the check here is quick and safe. + */ + if (png_ptr->zbuffer_list == NULL) + { + png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp, + png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr))); + png_ptr->zbuffer_list->next = NULL; + } + + else + png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next); + + /* It is a terminal error if we can't claim the zstream. */ + if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + + /* The output state is maintained in png_ptr->zstream, so it must be + * initialized here after the claim. + */ + png_ptr->zstream.next_out = png_ptr->zbuffer_list->output; + png_ptr->zstream.avail_out = png_ptr->zbuffer_size; + } + + /* Now loop reading and writing until all the input is consumed or an error + * terminates the operation. The _out values are maintained across calls to + * this function, but the input must be reset each time. + */ + png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input); + png_ptr->zstream.avail_in = 0; /* set below */ + for (;;) + { + int ret; + + /* INPUT: from the row data */ + uInt avail = ZLIB_IO_MAX; + + if (avail > input_len) + avail = (uInt)input_len; /* safe because of the check */ + + png_ptr->zstream.avail_in = avail; + input_len -= avail; + + ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush); + + /* Include as-yet unconsumed input */ + input_len += png_ptr->zstream.avail_in; + png_ptr->zstream.avail_in = 0; + + /* OUTPUT: write complete IDAT chunks when avail_out drops to zero. Note + * that these two zstream fields are preserved across the calls, therefore + * there is no need to set these up on entry to the loop. + */ + if (png_ptr->zstream.avail_out == 0) + { + png_bytep data = png_ptr->zbuffer_list->output; + uInt size = png_ptr->zbuffer_size; + + /* Write an IDAT containing the data then reset the buffer. The + * first IDAT may need deflate header optimization. + */ +#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + optimize_cmf(data, png_image_size(png_ptr)); +#endif + + if (size > 0) + png_write_complete_chunk(png_ptr, png_IDAT, data, size); + png_ptr->mode |= PNG_HAVE_IDAT; + + png_ptr->zstream.next_out = data; + png_ptr->zstream.avail_out = size; + + /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with + * the same flush parameter until it has finished output, for NO_FLUSH + * it doesn't matter. + */ + if (ret == Z_OK && flush != Z_NO_FLUSH) + continue; + } + + /* The order of these checks doesn't matter much; it just affects which + * possible error might be detected if multiple things go wrong at once. + */ + if (ret == Z_OK) /* most likely return code! */ + { + /* If all the input has been consumed then just return. If Z_FINISH + * was used as the flush parameter something has gone wrong if we get + * here. + */ + if (input_len == 0) + { + if (flush == Z_FINISH) + png_error(png_ptr, "Z_OK on Z_FINISH with output space"); + + return; + } + } + + else if (ret == Z_STREAM_END && flush == Z_FINISH) + { + /* This is the end of the IDAT data; any pending output must be + * flushed. For small PNG files we may still be at the beginning. + */ + png_bytep data = png_ptr->zbuffer_list->output; + uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out; + +#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + optimize_cmf(data, png_image_size(png_ptr)); +#endif + + if (size > 0) + png_write_complete_chunk(png_ptr, png_IDAT, data, size); + png_ptr->zstream.avail_out = 0; + png_ptr->zstream.next_out = NULL; + png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT; + + png_ptr->zowner = 0; /* Release the stream */ + return; + } + + else + { + /* This is an error condition. */ + png_zstream_error(png_ptr, ret); + png_error(png_ptr, png_ptr->zstream.msg); + } + } +} + +/* Write an IEND chunk */ +void /* PRIVATE */ +png_write_IEND(png_structrp png_ptr) +{ + png_debug(1, "in png_write_IEND"); + + png_write_complete_chunk(png_ptr, png_IEND, NULL, (png_size_t)0); + png_ptr->mode |= PNG_HAVE_IEND; +} + +#ifdef PNG_WRITE_gAMA_SUPPORTED +/* Write a gAMA chunk */ +void /* PRIVATE */ +png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma) +{ + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA"); + + /* file_gamma is saved in 1/100,000ths */ + png_save_uint_32(buf, (png_uint_32)file_gamma); + png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); +} +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +/* Write a sRGB chunk */ +void /* PRIVATE */ +png_write_sRGB(png_structrp png_ptr, int srgb_intent) +{ + png_byte buf[1]; + + png_debug(1, "in png_write_sRGB"); + + if (srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + + buf[0]=(png_byte)srgb_intent; + png_write_complete_chunk(png_ptr, png_sRGB, buf, (png_size_t)1); +} +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +/* Write an iCCP chunk */ +void /* PRIVATE */ +png_write_iCCP(png_structrp png_ptr, png_const_charp name, + png_const_bytep profile) +{ + png_uint_32 name_len; + png_uint_32 profile_len; + png_byte new_name[81]; /* 1 byte for the compression byte */ + compression_state comp; + png_uint_32 temp; + + png_debug(1, "in png_write_iCCP"); + + /* These are all internal problems: the profile should have been checked + * before when it was stored. + */ + if (profile == NULL) + png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */ + + profile_len = png_get_uint_32(profile); + + if (profile_len < 132) + png_error(png_ptr, "ICC profile too short"); + + temp = (png_uint_32) (*(profile+8)); + if (temp > 3 && (profile_len & 0x03)) + png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)"); + + { + png_uint_32 embedded_profile_len = png_get_uint_32(profile); + + if (profile_len != embedded_profile_len) + png_error(png_ptr, "Profile length does not match profile"); + } + + name_len = png_check_keyword(png_ptr, name, new_name); + + if (name_len == 0) + png_error(png_ptr, "iCCP: invalid keyword"); + + new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE; + + /* Make sure we include the NULL after the name and the compression type */ + ++name_len; + + png_text_compress_init(&comp, profile, profile_len); + + /* Allow for keyword terminator and compression byte */ + if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + + png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len); + + png_write_chunk_data(png_ptr, new_name, name_len); + + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +/* Write a sPLT chunk */ +void /* PRIVATE */ +png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) +{ + png_uint_32 name_len; + png_byte new_name[80]; + png_byte entrybuf[10]; + png_size_t entry_size = (spalette->depth == 8 ? 6 : 10); + png_size_t palette_size = entry_size * (png_size_t)spalette->nentries; + png_sPLT_entryp ep; +#ifndef PNG_POINTER_INDEXING_SUPPORTED + int i; +#endif + + png_debug(1, "in png_write_sPLT"); + + name_len = png_check_keyword(png_ptr, spalette->name, new_name); + + if (name_len == 0) + png_error(png_ptr, "sPLT: invalid keyword"); + + /* Make sure we include the NULL after the name */ + png_write_chunk_header(png_ptr, png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 1)); + + png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1); + + /* Loop through each palette entry, writing appropriately */ +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (ep = spalette->entries; epentries + spalette->nentries; ep++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + + png_write_chunk_data(png_ptr, entrybuf, entry_size); + } +#else + ep=spalette->entries; + for (i = 0; i>spalette->nentries; i++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + + png_write_chunk_data(png_ptr, entrybuf, entry_size); + } +#endif + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +/* Write the sBIT chunk */ +void /* PRIVATE */ +png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) +{ + png_byte buf[4]; + png_size_t size; + + png_debug(1, "in png_write_sBIT"); + + /* Make sure we don't depend upon the order of PNG_COLOR_8 */ + if ((color_type & PNG_COLOR_MASK_COLOR) != 0) + { + png_byte maxbits; + + maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : + png_ptr->usr_bit_depth); + + if (sbit->red == 0 || sbit->red > maxbits || + sbit->green == 0 || sbit->green > maxbits || + sbit->blue == 0 || sbit->blue > maxbits) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + + buf[0] = sbit->red; + buf[1] = sbit->green; + buf[2] = sbit->blue; + size = 3; + } + + else + { + if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + + buf[0] = sbit->gray; + size = 1; + } + + if ((color_type & PNG_COLOR_MASK_ALPHA) != 0) + { + if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + + buf[size++] = sbit->alpha; + } + + png_write_complete_chunk(png_ptr, png_sBIT, buf, size); +} +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +/* Write the cHRM chunk */ +void /* PRIVATE */ +png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy) +{ + png_byte buf[32]; + + png_debug(1, "in png_write_cHRM"); + + /* Each value is saved in 1/100,000ths */ + png_save_int_32(buf, xy->whitex); + png_save_int_32(buf + 4, xy->whitey); + + png_save_int_32(buf + 8, xy->redx); + png_save_int_32(buf + 12, xy->redy); + + png_save_int_32(buf + 16, xy->greenx); + png_save_int_32(buf + 20, xy->greeny); + + png_save_int_32(buf + 24, xy->bluex); + png_save_int_32(buf + 28, xy->bluey); + + png_write_complete_chunk(png_ptr, png_cHRM, buf, 32); +} +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +/* Write the tRNS chunk */ +void /* PRIVATE */ +png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, + png_const_color_16p tran, int num_trans, int color_type) +{ + png_byte buf[6]; + + png_debug(1, "in png_write_tRNS"); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + { + png_app_warning(png_ptr, + "Invalid number of transparent colors specified"); + return; + } + + /* Write the chunk out as it is */ + png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha, + (png_size_t)num_trans); + } + + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + /* One 16-bit value */ + if (tran->gray >= (1 << png_ptr->bit_depth)) + { + png_app_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + + return; + } + + png_save_uint_16(buf, tran->gray); + png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2); + } + + else if (color_type == PNG_COLOR_TYPE_RGB) + { + /* Three 16-bit values */ + png_save_uint_16(buf, tran->red); + png_save_uint_16(buf + 2, tran->green); + png_save_uint_16(buf + 4, tran->blue); +#ifdef PNG_WRITE_16BIT_SUPPORTED + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]) != 0) +#else + if ((buf[0] | buf[2] | buf[4]) != 0) +#endif + { + png_app_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + + png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6); + } + + else + { + png_app_warning(png_ptr, "Can't write tRNS with an alpha channel"); + } +} +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +/* Write the background chunk */ +void /* PRIVATE */ +png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) +{ + png_byte buf[6]; + + png_debug(1, "in png_write_bKGD"); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if ( +#ifdef PNG_MNG_FEATURES_SUPPORTED + (png_ptr->num_palette != 0 || + (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0) && +#endif + back->index >= png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid background palette index"); + return; + } + + buf[0] = back->index; + png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1); + } + + else if ((color_type & PNG_COLOR_MASK_COLOR) != 0) + { + png_save_uint_16(buf, back->red); + png_save_uint_16(buf + 2, back->green); + png_save_uint_16(buf + 4, back->blue); +#ifdef PNG_WRITE_16BIT_SUPPORTED + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]) != 0) +#else + if ((buf[0] | buf[2] | buf[4]) != 0) +#endif + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit bKGD chunk " + "when bit_depth is 8"); + + return; + } + + png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)6); + } + + else + { + if (back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + + return; + } + + png_save_uint_16(buf, back->gray); + png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)2); + } +} +#endif + +#ifdef PNG_WRITE_eXIf_SUPPORTED +/* Write the Exif data */ +void /* PRIVATE */ +png_write_eXIf(png_structrp png_ptr, png_bytep exif, int num_exif) +{ + int i; + png_byte buf[1]; + + png_debug(1, "in png_write_eXIf"); + + png_write_chunk_header(png_ptr, png_eXIf, (png_uint_32)(num_exif)); + + for (i = 0; i < num_exif; i++) + { + buf[0] = exif[i]; + png_write_chunk_data(png_ptr, buf, (png_size_t)1); + } + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +/* Write the histogram */ +void /* PRIVATE */ +png_write_hIST(png_structrp png_ptr, png_const_uint_16p hist, int num_hist) +{ + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_hIST"); + + if (num_hist > (int)png_ptr->num_palette) + { + png_debug2(3, "num_hist = %d, num_palette = %d", num_hist, + png_ptr->num_palette); + + png_warning(png_ptr, "Invalid number of histogram entries specified"); + return; + } + + png_write_chunk_header(png_ptr, png_hIST, (png_uint_32)(num_hist * 2)); + + for (i = 0; i < num_hist; i++) + { + png_save_uint_16(buf, hist[i]); + png_write_chunk_data(png_ptr, buf, (png_size_t)2); + } + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_tEXt_SUPPORTED +/* Write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, + png_size_t text_len) +{ + png_uint_32 key_len; + png_byte new_key[80]; + + png_debug(1, "in png_write_tEXt"); + + key_len = png_check_keyword(png_ptr, key, new_key); + + if (key_len == 0) + png_error(png_ptr, "tEXt: invalid keyword"); + + if (text == NULL || *text == '\0') + text_len = 0; + + else + text_len = strlen(text); + + if (text_len > PNG_UINT_31_MAX - (key_len+1)) + png_error(png_ptr, "tEXt: text too long"); + + /* Make sure we include the 0 after the key */ + png_write_chunk_header(png_ptr, png_tEXt, + (png_uint_32)/*checked above*/(key_len + text_len + 1)); + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, new_key, key_len + 1); + + if (text_len != 0) + png_write_chunk_data(png_ptr, (png_const_bytep)text, text_len); + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +/* Write a compressed text chunk */ +void /* PRIVATE */ +png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, + int compression) +{ + png_uint_32 key_len; + png_byte new_key[81]; + compression_state comp; + + png_debug(1, "in png_write_zTXt"); + + if (compression == PNG_TEXT_COMPRESSION_NONE) + { + png_write_tEXt(png_ptr, key, text, 0); + return; + } + + if (compression != PNG_TEXT_COMPRESSION_zTXt) + png_error(png_ptr, "zTXt: invalid compression type"); + + key_len = png_check_keyword(png_ptr, key, new_key); + + if (key_len == 0) + png_error(png_ptr, "zTXt: invalid keyword"); + + /* Add the compression method and 1 for the keyword separator. */ + new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE; + ++key_len; + + /* Compute the compressed data; do it now for the length */ + png_text_compress_init(&comp, (png_const_bytep)text, + text == NULL ? 0 : strlen(text)); + + if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + + /* Write start of chunk */ + png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len); + + /* Write key */ + png_write_chunk_data(png_ptr, new_key, key_len); + + /* Write the compressed data */ + png_write_compressed_data_out(png_ptr, &comp); + + /* Close the chunk */ + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +/* Write an iTXt chunk */ +void /* PRIVATE */ +png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key, + png_const_charp lang, png_const_charp lang_key, png_const_charp text) +{ + png_uint_32 key_len, prefix_len; + png_size_t lang_len, lang_key_len; + png_byte new_key[82]; + compression_state comp; + + png_debug(1, "in png_write_iTXt"); + + key_len = png_check_keyword(png_ptr, key, new_key); + + if (key_len == 0) + png_error(png_ptr, "iTXt: invalid keyword"); + + /* Set the compression flag */ + switch (compression) + { + case PNG_ITXT_COMPRESSION_NONE: + case PNG_TEXT_COMPRESSION_NONE: + compression = new_key[++key_len] = 0; /* no compression */ + break; + + case PNG_TEXT_COMPRESSION_zTXt: + case PNG_ITXT_COMPRESSION_zTXt: + compression = new_key[++key_len] = 1; /* compressed */ + break; + + default: + png_error(png_ptr, "iTXt: invalid compression"); + } + + new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE; + ++key_len; /* for the keywod separator */ + + /* We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG, however, + * specifies that the text is UTF-8 and this really doesn't require any + * checking. + * + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + * + * TODO: validate the language tag correctly (see the spec.) + */ + if (lang == NULL) lang = ""; /* empty language is valid */ + lang_len = strlen(lang)+1; + if (lang_key == NULL) lang_key = ""; /* may be empty */ + lang_key_len = strlen(lang_key)+1; + if (text == NULL) text = ""; /* may be empty */ + + prefix_len = key_len; + if (lang_len > PNG_UINT_31_MAX-prefix_len) + prefix_len = PNG_UINT_31_MAX; + else + prefix_len = (png_uint_32)(prefix_len + lang_len); + + if (lang_key_len > PNG_UINT_31_MAX-prefix_len) + prefix_len = PNG_UINT_31_MAX; + else + prefix_len = (png_uint_32)(prefix_len + lang_key_len); + + png_text_compress_init(&comp, (png_const_bytep)text, strlen(text)); + + if (compression != 0) + { + if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + } + + else + { + if (comp.input_len > PNG_UINT_31_MAX-prefix_len) + png_error(png_ptr, "iTXt: uncompressed text too long"); + + /* So the string will fit in a chunk: */ + comp.output_len = (png_uint_32)/*SAFE*/comp.input_len; + } + + png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len); + + png_write_chunk_data(png_ptr, new_key, key_len); + + png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len); + + png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len); + + if (compression != 0) + png_write_compressed_data_out(png_ptr, &comp); + + else + png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.output_len); + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +/* Write the oFFs chunk */ +void /* PRIVATE */ +png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) +{ + png_byte buf[9]; + + png_debug(1, "in png_write_oFFs"); + + if (unit_type >= PNG_OFFSET_LAST) + png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); + + png_save_int_32(buf, x_offset); + png_save_int_32(buf + 4, y_offset); + buf[8] = (png_byte)unit_type; + + png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); +} +#endif +#ifdef PNG_WRITE_pCAL_SUPPORTED +/* Write the pCAL chunk (described in the PNG extensions document) */ +void /* PRIVATE */ +png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_const_charp units, + png_charpp params) +{ + png_uint_32 purpose_len; + png_size_t units_len, total_len; + png_size_tp params_len; + png_byte buf[10]; + png_byte new_purpose[80]; + int i; + + png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); + + if (type >= PNG_EQUATION_LAST) + png_error(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, new_purpose); + + if (purpose_len == 0) + png_error(png_ptr, "pCAL: invalid keyword"); + + ++purpose_len; /* terminator */ + + png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); + units_len = strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d", (int)units_len); + total_len = purpose_len + units_len + 10; + + params_len = (png_size_tp)png_malloc(png_ptr, + (png_alloc_size_t)((png_alloc_size_t)nparams * (sizeof (png_size_t)))); + + /* Find the length of each parameter, making sure we don't count the + * null terminator for the last parameter. + */ + for (i = 0; i < nparams; i++) + { + params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu", i, + (unsigned long)params_len[i]); + total_len += params_len[i]; + } + + png_debug1(3, "pCAL total length = %d", (int)total_len); + png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, new_purpose, purpose_len); + png_save_int_32(buf, X0); + png_save_int_32(buf + 4, X1); + buf[8] = (png_byte)type; + buf[9] = (png_byte)nparams; + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len); + + for (i = 0; i < nparams; i++) + { + png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]); + } + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +/* Write the sCAL chunk */ +void /* PRIVATE */ +png_write_sCAL_s(png_structrp png_ptr, int unit, png_const_charp width, + png_const_charp height) +{ + png_byte buf[64]; + png_size_t wlen, hlen, total_len; + + png_debug(1, "in png_write_sCAL_s"); + + wlen = strlen(width); + hlen = strlen(height); + total_len = wlen + hlen + 2; + + if (total_len > 64) + { + png_warning(png_ptr, "Can't write sCAL (buffer too small)"); + return; + } + + buf[0] = (png_byte)unit; + memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */ + memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */ + + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_complete_chunk(png_ptr, png_sCAL, buf, total_len); +} +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +/* Write the pHYs chunk */ +void /* PRIVATE */ +png_write_pHYs(png_structrp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) +{ + png_byte buf[9]; + + png_debug(1, "in png_write_pHYs"); + + if (unit_type >= PNG_RESOLUTION_LAST) + png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); + + png_save_uint_32(buf, x_pixels_per_unit); + png_save_uint_32(buf + 4, y_pixels_per_unit); + buf[8] = (png_byte)unit_type; + + png_write_complete_chunk(png_ptr, png_pHYs, buf, (png_size_t)9); +} +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +/* Write the tIME chunk. Use either png_convert_from_struct_tm() + * or png_convert_from_time_t(), or fill in the structure yourself. + */ +void /* PRIVATE */ +png_write_tIME(png_structrp png_ptr, png_const_timep mod_time) +{ + png_byte buf[7]; + + png_debug(1, "in png_write_tIME"); + + if (mod_time->month > 12 || mod_time->month < 1 || + mod_time->day > 31 || mod_time->day < 1 || + mod_time->hour > 23 || mod_time->second > 60) + { + png_warning(png_ptr, "Invalid time specified for tIME chunk"); + return; + } + + png_save_uint_16(buf, mod_time->year); + buf[2] = mod_time->month; + buf[3] = mod_time->day; + buf[4] = mod_time->hour; + buf[5] = mod_time->minute; + buf[6] = mod_time->second; + + png_write_complete_chunk(png_ptr, png_tIME, buf, (png_size_t)7); +} +#endif + +/* Initializes the row writing capability of libpng */ +void /* PRIVATE */ +png_write_start_row(png_structrp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_alloc_size_t buf_size; + int usr_pixel_depth; + +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_byte filters; +#endif + + png_debug(1, "in png_write_start_row"); + + usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth; + buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1; + + /* 1.5.6: added to allow checking in the row write code. */ + png_ptr->transformed_pixel_depth = png_ptr->pixel_depth; + png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth; + + /* Set up row buffer */ + png_ptr->row_buf = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); + + png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; + +#ifdef PNG_WRITE_FILTER_SUPPORTED + filters = png_ptr->do_filter; + + if (png_ptr->height == 1) + filters &= 0xff & ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); + + if (png_ptr->width == 1) + filters &= 0xff & ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH); + + if (filters == 0) + filters = PNG_FILTER_NONE; + + png_ptr->do_filter = filters; + + if (((filters & (PNG_FILTER_SUB | PNG_FILTER_UP | PNG_FILTER_AVG | + PNG_FILTER_PAETH)) != 0) && png_ptr->try_row == NULL) + { + int num_filters = 0; + + png_ptr->try_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); + + if (filters & PNG_FILTER_SUB) + num_filters++; + + if (filters & PNG_FILTER_UP) + num_filters++; + + if (filters & PNG_FILTER_AVG) + num_filters++; + + if (filters & PNG_FILTER_PAETH) + num_filters++; + + if (num_filters > 1) + png_ptr->tst_row = png_voidcast(png_bytep, png_malloc(png_ptr, + buf_size)); + } + + /* We only need to keep the previous row if we are using one of the following + * filters. + */ + if ((filters & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) != 0) + png_ptr->prev_row = png_voidcast(png_bytep, + png_calloc(png_ptr, buf_size)); +#endif /* WRITE_FILTER */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced, we need to set up width and height of pass */ + if (png_ptr->interlaced != 0) + { + if ((png_ptr->transformations & PNG_INTERLACE) == 0) + { + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - + png_pass_start[0]) / png_pass_inc[0]; + } + + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + } + + else +#endif + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } +} + +/* Internal use only. Called when finished processing a row of data. */ +void /* PRIVATE */ +png_write_finish_row(png_structrp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_debug(1, "in png_write_finish_row"); + + /* Next row */ + png_ptr->row_number++; + + /* See if we are done */ + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced, go to next pass */ + if (png_ptr->interlaced != 0) + { + png_ptr->row_number = 0; + if ((png_ptr->transformations & PNG_INTERLACE) != 0) + { + png_ptr->pass++; + } + + else + { + /* Loop until we find a non-zero width or height pass */ + do + { + png_ptr->pass++; + + if (png_ptr->pass >= 7) + break; + + png_ptr->usr_width = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + if ((png_ptr->transformations & PNG_INTERLACE) != 0) + break; + + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); + + } + + /* Reset the row above the image for the next pass */ + if (png_ptr->pass < 7) + { + if (png_ptr->prev_row != NULL) + memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth, png_ptr->width)) + 1); + + return; + } + } +#endif + + /* If we get here, we've just written the last row, so we need + to flush the compressor */ + png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH); +} + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ +void /* PRIVATE */ +png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + png_debug(1, "in png_do_write_interlace"); + + /* We don't have to do anything on the last pass (6) */ + if (pass < 6) + { + /* Each pixel depth is handled separately */ + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + unsigned int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + d = 0; + shift = 7; + + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 3); + value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; + d |= (value << shift); + + if (shift == 0) + { + shift = 7; + *dp++ = (png_byte)d; + d = 0; + } + + else + shift--; + + } + if (shift != 7) + *dp = (png_byte)d; + + break; + } + + case 2: + { + png_bytep sp; + png_bytep dp; + unsigned int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 6; + d = 0; + + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 2); + value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; + d |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp++ = (png_byte)d; + d = 0; + } + + else + shift -= 2; + } + if (shift != 6) + *dp = (png_byte)d; + + break; + } + + case 4: + { + png_bytep sp; + png_bytep dp; + unsigned int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 4; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 1); + value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; + d |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp++ = (png_byte)d; + d = 0; + } + + else + shift -= 4; + } + if (shift != 4) + *dp = (png_byte)d; + + break; + } + + default: + { + png_bytep sp; + png_bytep dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + png_size_t pixel_bytes; + + /* Start at the beginning */ + dp = row; + + /* Find out how many bytes each pixel takes up */ + pixel_bytes = (row_info->pixel_depth >> 3); + + /* Loop through the row, only looking at the pixels that matter */ + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + /* Find out where the original pixel is */ + sp = row + (png_size_t)i * pixel_bytes; + + /* Move the pixel */ + if (dp != sp) + memcpy(dp, sp, pixel_bytes); + + /* Next pixel */ + dp += pixel_bytes; + } + break; + } + } + /* Set new row width */ + row_info->width = (row_info->width + + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + + +/* This filters the row, chooses which filter to use, if it has not already + * been specified by the application, and then writes the row out with the + * chosen filter. + */ +static void /* PRIVATE */ +png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, + png_size_t row_bytes); + +#ifdef PNG_WRITE_FILTER_SUPPORTED +static png_size_t /* PRIVATE */ +png_setup_sub_row(png_structrp png_ptr, const png_uint_32 bpp, + const png_size_t row_bytes, const png_size_t lmins) +{ + png_bytep rp, dp, lp; + png_size_t i; + png_size_t sum = 0; + unsigned int v; + + png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB; + + for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1; i < bpp; + i++, rp++, dp++) + { + v = *dp = *rp; +#ifdef PNG_USE_ABS + sum += 128 - abs((int)v - 128); +#else + sum += (v < 128) ? v : 256 - v; +#endif + } + + for (lp = png_ptr->row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); +#ifdef PNG_USE_ABS + sum += 128 - abs((int)v - 128); +#else + sum += (v < 128) ? v : 256 - v; +#endif + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + + return (sum); +} + +static void /* PRIVATE */ +png_setup_sub_row_only(png_structrp png_ptr, const png_uint_32 bpp, + const png_size_t row_bytes) +{ + png_bytep rp, dp, lp; + png_size_t i; + + png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB; + + for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + + for (lp = png_ptr->row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } +} + +static png_size_t /* PRIVATE */ +png_setup_up_row(png_structrp png_ptr, const png_size_t row_bytes, + const png_size_t lmins) +{ + png_bytep rp, dp, pp; + png_size_t i; + png_size_t sum = 0; + unsigned int v; + + png_ptr->try_row[0] = PNG_FILTER_VALUE_UP; + + for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, + pp = png_ptr->prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); +#ifdef PNG_USE_ABS + sum += 128 - abs((int)v - 128); +#else + sum += (v < 128) ? v : 256 - v; +#endif + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + + return (sum); +} +static void /* PRIVATE */ +png_setup_up_row_only(png_structrp png_ptr, const png_size_t row_bytes) +{ + png_bytep rp, dp, pp; + png_size_t i; + + png_ptr->try_row[0] = PNG_FILTER_VALUE_UP; + + for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, + pp = png_ptr->prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } +} + +static png_size_t /* PRIVATE */ +png_setup_avg_row(png_structrp png_ptr, const png_uint_32 bpp, + const png_size_t row_bytes, const png_size_t lmins) +{ + png_bytep rp, dp, pp, lp; + png_uint_32 i; + png_size_t sum = 0; + unsigned int v; + + png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG; + + for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, + pp = png_ptr->prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + +#ifdef PNG_USE_ABS + sum += 128 - abs((int)v - 128); +#else + sum += (v < 128) ? v : 256 - v; +#endif + } + + for (lp = png_ptr->row_buf + 1; i < row_bytes; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + +#ifdef PNG_USE_ABS + sum += 128 - abs((int)v - 128); +#else + sum += (v < 128) ? v : 256 - v; +#endif + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + + return (sum); +} +static void /* PRIVATE */ +png_setup_avg_row_only(png_structrp png_ptr, const png_uint_32 bpp, + const png_size_t row_bytes) +{ + png_bytep rp, dp, pp, lp; + png_uint_32 i; + + png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG; + + for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, + pp = png_ptr->prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + + for (lp = png_ptr->row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } +} + +static png_size_t /* PRIVATE */ +png_setup_paeth_row(png_structrp png_ptr, const png_uint_32 bpp, + const png_size_t row_bytes, const png_size_t lmins) +{ + png_bytep rp, dp, pp, cp, lp; + png_size_t i; + png_size_t sum = 0; + unsigned int v; + + png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH; + + for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, + pp = png_ptr->prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + +#ifdef PNG_USE_ABS + sum += 128 - abs((int)v - 128); +#else + sum += (v < 128) ? v : 256 - v; +#endif + } + + for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes; + i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + +#ifdef PNG_USE_ABS + sum += 128 - abs((int)v - 128); +#else + sum += (v < 128) ? v : 256 - v; +#endif + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + + return (sum); +} +static void /* PRIVATE */ +png_setup_paeth_row_only(png_structrp png_ptr, const png_uint_32 bpp, + const png_size_t row_bytes) +{ + png_bytep rp, dp, pp, cp, lp; + png_size_t i; + + png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH; + + for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, + pp = png_ptr->prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes; + i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } +} +#endif /* WRITE_FILTER */ + +void /* PRIVATE */ +png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) +{ +#ifndef PNG_WRITE_FILTER_SUPPORTED + png_write_filtered_row(png_ptr, png_ptr->row_buf, row_info->rowbytes+1); +#else + unsigned int filter_to_do = png_ptr->do_filter; + png_bytep row_buf; + png_bytep best_row; + png_uint_32 bpp; + png_size_t mins; + png_size_t row_bytes = row_info->rowbytes; + + png_debug(1, "in png_write_find_filter"); + + /* Find out how many bytes offset each pixel is */ + bpp = (row_info->pixel_depth + 7) >> 3; + + row_buf = png_ptr->row_buf; + mins = PNG_SIZE_MAX - 256/* so we can detect potential overflow of the + running sum */; + + /* The prediction method we use is to find which method provides the + * smallest value when summing the absolute values of the distances + * from zero, using anything >= 128 as negative numbers. This is known + * as the "minimum sum of absolute differences" heuristic. Other + * heuristics are the "weighted minimum sum of absolute differences" + * (experimental and can in theory improve compression), and the "zlib + * predictive" method (not implemented yet), which does test compressions + * of lines using different filter methods, and then chooses the + * (series of) filter(s) that give minimum compressed data size (VERY + * computationally expensive). + * + * GRR 980525: consider also + * + * (1) minimum sum of absolute differences from running average (i.e., + * keep running sum of non-absolute differences & count of bytes) + * [track dispersion, too? restart average if dispersion too large?] + * + * (1b) minimum sum of absolute differences from sliding average, probably + * with window size <= deflate window (usually 32K) + * + * (2) minimum sum of squared differences from zero or running average + * (i.e., ~ root-mean-square approach) + */ + + + /* We don't need to test the 'no filter' case if this is the only filter + * that has been chosen, as it doesn't actually do anything to the data. + */ + best_row = png_ptr->row_buf; + + if (PNG_SIZE_MAX/128 <= row_bytes) + { + /* Overflow can occur in the calculation, just select the lowest set + * filter. + */ + filter_to_do &= 0U-filter_to_do; + } + else if ((filter_to_do & PNG_FILTER_NONE) != 0 && + filter_to_do != PNG_FILTER_NONE) + { + /* Overflow not possible and multiple filters in the list, including the + * 'none' filter. + */ + png_bytep rp; + png_size_t sum = 0; + png_size_t i; + unsigned int v; + + { + for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + { + v = *rp; +#ifdef PNG_USE_ABS + sum += 128 - abs((int)v - 128); +#else + sum += (v < 128) ? v : 256 - v; +#endif + } + } + + mins = sum; + } + + /* Sub filter */ + if (filter_to_do == PNG_FILTER_SUB) + /* It's the only filter so no testing is needed */ + { + png_setup_sub_row_only(png_ptr, bpp, row_bytes); + best_row = png_ptr->try_row; + } + + else if ((filter_to_do & PNG_FILTER_SUB) != 0) + { + png_size_t sum; + png_size_t lmins = mins; + + sum = png_setup_sub_row(png_ptr, bpp, row_bytes, lmins); + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->try_row; + if (png_ptr->tst_row != NULL) + { + png_ptr->try_row = png_ptr->tst_row; + png_ptr->tst_row = best_row; + } + } + } + + /* Up filter */ + if (filter_to_do == PNG_FILTER_UP) + { + png_setup_up_row_only(png_ptr, row_bytes); + best_row = png_ptr->try_row; + } + + else if ((filter_to_do & PNG_FILTER_UP) != 0) + { + png_size_t sum; + png_size_t lmins = mins; + + sum = png_setup_up_row(png_ptr, row_bytes, lmins); + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->try_row; + if (png_ptr->tst_row != NULL) + { + png_ptr->try_row = png_ptr->tst_row; + png_ptr->tst_row = best_row; + } + } + } + + /* Avg filter */ + if (filter_to_do == PNG_FILTER_AVG) + { + png_setup_avg_row_only(png_ptr, bpp, row_bytes); + best_row = png_ptr->try_row; + } + + else if ((filter_to_do & PNG_FILTER_AVG) != 0) + { + png_size_t sum; + png_size_t lmins = mins; + + sum= png_setup_avg_row(png_ptr, bpp, row_bytes, lmins); + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->try_row; + if (png_ptr->tst_row != NULL) + { + png_ptr->try_row = png_ptr->tst_row; + png_ptr->tst_row = best_row; + } + } + } + + /* Paeth filter */ + if (filter_to_do == PNG_FILTER_PAETH) + { + png_setup_paeth_row_only(png_ptr, bpp, row_bytes); + best_row = png_ptr->try_row; + } + + else if ((filter_to_do & PNG_FILTER_PAETH) != 0) + { + png_size_t sum; + png_size_t lmins = mins; + + sum = png_setup_paeth_row(png_ptr, bpp, row_bytes, lmins); + + if (sum < mins) + { + best_row = png_ptr->try_row; + if (png_ptr->tst_row != NULL) + { + png_ptr->try_row = png_ptr->tst_row; + png_ptr->tst_row = best_row; + } + } + } + + /* Do the actual writing of the filtered row data from the chosen filter. */ + png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1); + +#endif /* WRITE_FILTER */ +} + + +/* Do the actual writing of a previously filtered row. */ +static void +png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, + png_size_t full_row_length/*includes filter byte*/) +{ + png_debug(1, "in png_write_filtered_row"); + + png_debug1(2, "filter = %d", filtered_row[0]); + + png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH); + +#ifdef PNG_WRITE_FILTER_SUPPORTED + /* Swap the current and previous rows */ + if (png_ptr->prev_row != NULL) + { + png_bytep tptr; + + tptr = png_ptr->prev_row; + png_ptr->prev_row = png_ptr->row_buf; + png_ptr->row_buf = tptr; + } +#endif /* WRITE_FILTER */ + + /* Finish row - updates counters and flushes zlib if last row */ + png_write_finish_row(png_ptr); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->flush_rows++; + + if (png_ptr->flush_dist > 0 && + png_ptr->flush_rows >= png_ptr->flush_dist) + { + png_write_flush(png_ptr); + } +#endif /* WRITE_FLUSH */ +} +#endif /* WRITE */ diff --git a/src/png/libpng/powerpc/filter_vsx_intrinsics.c b/src/png/libpng/powerpc/filter_vsx_intrinsics.c new file mode 100644 index 0000000000..e3de496bdc --- /dev/null +++ b/src/png/libpng/powerpc/filter_vsx_intrinsics.c @@ -0,0 +1,767 @@ +/* filter_vsx_intrinsics.c - PowerPC optimised filter functions + * + * Copyright (c) 2017 Glenn Randers-Pehrson + * Written by Vadim Barkov, 2017. + * Last changed in libpng 1.6.29 [March 16, 2017] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ +#include +#include +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +/* This code requires -maltivec and -mvsx on the command line: */ +#if PNG_POWERPC_VSX_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */ + +#include + +#if PNG_POWERPC_VSX_OPT > 0 + +#ifndef __VSX__ +# error "This code requires VSX support (POWER7 and later). Please provide -mvsx compiler flag." +#endif + +#define vec_ld_unaligned(vec,data) vec = vec_vsx_ld(0,data) +#define vec_st_unaligned(vec,data) vec_vsx_st(vec,0,data) + + +/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d). + * They're positioned like this: + * prev: c b + * row: a d + * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be + * whichever of a, b, or c is closest to p=a+b-c. + * ( this is taken from ../intel/filter_sse2_intrinsics.c ) + */ + +#define vsx_declare_common_vars(row_info,row,prev_row,offset) \ + png_byte i;\ + png_bytep rp = row + offset;\ + png_const_bytep pp = prev_row;\ + png_size_t unaligned_top = 16 - (((png_size_t)rp % 16));\ + png_size_t istop;\ + if(unaligned_top == 16)\ + unaligned_top = 0;\ + istop = row_info->rowbytes;\ + if((unaligned_top < istop))\ + istop -= unaligned_top;\ + else{\ + unaligned_top = istop;\ + istop = 0;\ + } + +void png_read_filter_row_up_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + vector unsigned char rp_vec; + vector unsigned char pp_vec; + vsx_declare_common_vars(row_info,row,prev_row,0) + + /* Altivec operations require 16-byte aligned data + * but input can be unaligned. So we calculate + * unaligned part as usual. + */ + for (i = 0; i < unaligned_top; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + /* Using SIMD while we can */ + while( istop >= 16 ) + { + rp_vec = vec_ld(0,rp); + vec_ld_unaligned(pp_vec,pp); + + rp_vec = vec_add(rp_vec,pp_vec); + + vec_st(rp_vec,0,rp); + + pp += 16; + rp += 16; + istop -= 16; + } + + if(istop > 0) + { + /* If byte count of row is not divisible by 16 + * we will process remaining part as usual + */ + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } +} + +} + +static const vector unsigned char VSX_LEFTSHIFTED1_4 = {16,16,16,16, 0, 1, 2, 3,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_LEFTSHIFTED2_4 = {16,16,16,16,16,16,16,16, 4, 5, 6, 7,16,16,16,16}; +static const vector unsigned char VSX_LEFTSHIFTED3_4 = {16,16,16,16,16,16,16,16,16,16,16,16, 8, 9,10,11}; + +static const vector unsigned char VSX_LEFTSHIFTED1_3 = {16,16,16, 0, 1, 2,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_LEFTSHIFTED2_3 = {16,16,16,16,16,16, 3, 4, 5,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_LEFTSHIFTED3_3 = {16,16,16,16,16,16,16,16,16, 6, 7, 8,16,16,16,16}; +static const vector unsigned char VSX_LEFTSHIFTED4_3 = {16,16,16,16,16,16,16,16,16,16,16,16, 9,10,11,16}; + +static const vector unsigned char VSX_NOT_SHIFTED1_4 = {16,16,16,16, 4, 5, 6, 7,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_NOT_SHIFTED2_4 = {16,16,16,16,16,16,16,16, 8, 9,10,11,16,16,16,16}; +static const vector unsigned char VSX_NOT_SHIFTED3_4 = {16,16,16,16,16,16,16,16,16,16,16,16,12,13,14,15}; + +static const vector unsigned char VSX_NOT_SHIFTED1_3 = {16,16,16, 3, 4, 5,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_NOT_SHIFTED2_3 = {16,16,16,16,16,16, 6, 7, 8,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_NOT_SHIFTED3_3 = {16,16,16,16,16,16,16,16,16, 9,10,11,16,16,16,16}; +static const vector unsigned char VSX_NOT_SHIFTED4_3 = {16,16,16,16,16,16,16,16,16,16,16,16,12,13,14,16}; + +static const vector unsigned char VSX_CHAR_ZERO = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#ifdef __LITTLE_ENDIAN__ + +static const vector unsigned char VSX_CHAR_TO_SHORT1_4 = { 4,16, 5,16, 6,16, 7,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT2_4 = { 8,16, 9,16,10,16,11,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT3_4 = {12,16,13,16,14,16,15,16,16,16,16,16,16,16,16,16}; + +static const vector unsigned char VSX_SHORT_TO_CHAR1_4 = {16,16,16,16, 0, 2, 4, 6,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR2_4 = {16,16,16,16,16,16,16,16, 0, 2, 4, 6,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR3_4 = {16,16,16,16,16,16,16,16,16,16,16,16, 0, 2, 4, 6}; + +static const vector unsigned char VSX_CHAR_TO_SHORT1_3 = { 3,16, 4,16, 5,16,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT2_3 = { 6,16, 7,16, 8,16,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT3_3 = { 9,16,10,16,11,16,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT4_3 = {12,16,13,16,14,16,16,16,16,16,16,16,16,16,16,16}; + +static const vector unsigned char VSX_SHORT_TO_CHAR1_3 = {16,16,16, 0, 2, 4,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR2_3 = {16,16,16,16,16,16, 0, 2, 4,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR3_3 = {16,16,16,16,16,16,16,16,16, 0, 2, 4,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR4_3 = {16,16,16,16,16,16,16,16,16,16,16,16, 0, 2, 4,16}; + +#elif defined(__BIG_ENDIAN__) + +static const vector unsigned char VSX_CHAR_TO_SHORT1_4 = {16, 4,16, 5,16, 6,16, 7,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT2_4 = {16, 8,16, 9,16,10,16,11,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT3_4 = {16,12,16,13,16,14,16,15,16,16,16,16,16,16,16,16}; + +static const vector unsigned char VSX_SHORT_TO_CHAR1_4 = {16,16,16,16, 1, 3, 5, 7,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR2_4 = {16,16,16,16,16,16,16,16, 1, 3, 5, 7,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR3_4 = {16,16,16,16,16,16,16,16,16,16,16,16, 1, 3, 5, 7}; + +static const vector unsigned char VSX_CHAR_TO_SHORT1_3 = {16, 3,16, 4,16, 5,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT2_3 = {16, 6,16, 7,16, 8,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT3_3 = {16, 9,16,10,16,11,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT4_3 = {16,12,16,13,16,14,16,16,16,16,16,16,16,16,16,16}; + +static const vector unsigned char VSX_SHORT_TO_CHAR1_3 = {16,16,16, 1, 3, 5,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR2_3 = {16,16,16,16,16,16, 1, 3, 5,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR3_3 = {16,16,16,16,16,16,16,16,16, 1, 3, 5,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR4_3 = {16,16,16,16,16,16,16,16,16,16,16,16, 1, 3, 5,16}; + +#endif + +#define vsx_char_to_short(vec,offset,bpp) (vector unsigned short)vec_perm((vec),VSX_CHAR_ZERO,VSX_CHAR_TO_SHORT##offset##_##bpp) +#define vsx_short_to_char(vec,offset,bpp) vec_perm(((vector unsigned char)(vec)),VSX_CHAR_ZERO,VSX_SHORT_TO_CHAR##offset##_##bpp) + +#ifdef PNG_USE_ABS +# define vsx_abs(number) abs(number) +#else +# define vsx_abs(number) (number > 0) ? (number) : -(number) +#endif + +void png_read_filter_row_sub4_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + const png_byte bpp = 4; + + vector unsigned char rp_vec; + vector unsigned char part_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + + PNG_UNUSED(pp) + + /* Altivec operations require 16-byte aligned data + * but input can be unaligned. So we calculate + * unaligned part as usual. + */ + for (i = 0; i < unaligned_top; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } + + /* Using SIMD while we can */ + while( istop >= 16 ) + { + for(i=0;i < bpp ; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } + rp -= bpp; + + rp_vec = vec_ld(0,rp); + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_4); + rp_vec = vec_add(rp_vec,part_vec); + + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_4); + rp_vec = vec_add(rp_vec,part_vec); + + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_4); + rp_vec = vec_add(rp_vec,part_vec); + + vec_st(rp_vec,0,rp); + + rp += 16; + istop -= 16; + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp - bpp))) & 0xff); + rp++; + } + +} + +void png_read_filter_row_sub3_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + const png_byte bpp = 3; + + vector unsigned char rp_vec; + vector unsigned char part_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + + PNG_UNUSED(pp) + + /* Altivec operations require 16-byte aligned data + * but input can be unaligned. So we calculate + * unaligned part as usual. + */ + for (i = 0; i < unaligned_top; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } + + /* Using SIMD while we can */ + while( istop >= 16 ) + { + for(i=0;i < bpp ; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } + rp -= bpp; + + rp_vec = vec_ld(0,rp); + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_3); + rp_vec = vec_add(rp_vec,part_vec); + + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_3); + rp_vec = vec_add(rp_vec,part_vec); + + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_3); + rp_vec = vec_add(rp_vec,part_vec); + + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED4_3); + rp_vec = vec_add(rp_vec,part_vec); + + vec_st(rp_vec,0,rp); + rp += 15; + istop -= 16; + + /* Since 16 % bpp = 16 % 3 = 1, last element of array must + * be proceeded manually + */ + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } +} + +void png_read_filter_row_avg4_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + const png_byte bpp = 4; + + vector unsigned char rp_vec; + vector unsigned char pp_vec; + vector unsigned char pp_part_vec; + vector unsigned char rp_part_vec; + vector unsigned char avg_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + rp -= bpp; + if(istop >= bpp) + istop -= bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + + rp++; + } + + /* Altivec operations require 16-byte aligned data + * but input can be unaligned. So we calculate + * unaligned part as usual. + */ + for (i = 0; i < unaligned_top; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } + + /* Using SIMD while we can */ + while( istop >= 16 ) + { + for(i=0;i < bpp ; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } + rp -= bpp; + pp -= bpp; + + vec_ld_unaligned(pp_vec,pp); + rp_vec = vec_ld(0,rp); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_4); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED1_4); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_4); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED2_4); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_4); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED3_4); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + vec_st(rp_vec,0,rp); + + rp += 16; + pp += 16; + istop -= 16; + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } +} + +void png_read_filter_row_avg3_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + const png_byte bpp = 3; + + vector unsigned char rp_vec; + vector unsigned char pp_vec; + vector unsigned char pp_part_vec; + vector unsigned char rp_part_vec; + vector unsigned char avg_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + rp -= bpp; + if(istop >= bpp) + istop -= bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + + rp++; + } + + /* Altivec operations require 16-byte aligned data + * but input can be unaligned. So we calculate + * unaligned part as usual. + */ + for (i = 0; i < unaligned_top; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } + + /* Using SIMD while we can */ + while( istop >= 16 ) + { + for(i=0;i < bpp ; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } + rp -= bpp; + pp -= bpp; + + vec_ld_unaligned(pp_vec,pp); + rp_vec = vec_ld(0,rp); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_3); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED1_3); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_3); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED2_3); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_3); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED3_3); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED4_3); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED4_3); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + vec_st(rp_vec,0,rp); + + rp += 15; + pp += 15; + istop -= 16; + + /* Since 16 % bpp = 16 % 3 = 1, last element of array must + * be proceeded manually + */ + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + rp++; + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } +} + +/* Bytewise c ? t : e. */ +#define if_then_else(c,t,e) vec_sel(e,t,c) + +#define vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) {\ + c = *(pp - bpp);\ + a = *(rp - bpp);\ + b = *pp++;\ + p = b - c;\ + pc = a - c;\ + pa = vsx_abs(p);\ + pb = vsx_abs(pc);\ + pc = vsx_abs(p + pc);\ + if (pb < pa) pa = pb, a = b;\ + if (pc < pa) a = c;\ + a += *rp;\ + *rp++ = (png_byte)a;\ + } + +void png_read_filter_row_paeth4_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + const png_byte bpp = 4; + + int a, b, c, pa, pb, pc, p; + vector unsigned char rp_vec; + vector unsigned char pp_vec; + vector unsigned short a_vec,b_vec,c_vec,nearest_vec; + vector signed short pa_vec,pb_vec,pc_vec,smallest_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + rp -= bpp; + if(istop >= bpp) + istop -= bpp; + + /* Process the first pixel in the row completely (this is the same as 'up' + * because there is only one candidate predictor for the first row). + */ + for(i = 0; i < bpp ; i++) + { + *rp = (png_byte)( *rp + *pp); + rp++; + pp++; + } + + for(i = 0; i < unaligned_top ; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } + + while( istop >= 16) + { + for(i = 0; i < bpp ; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } + + rp -= bpp; + pp -= bpp; + rp_vec = vec_ld(0,rp); + vec_ld_unaligned(pp_vec,pp); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_4),1,4); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED1_4),1,4); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_4),1,4); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,1,4))); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_4),2,4); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED2_4),2,4); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_4),2,4); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,2,4))); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_4),3,4); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED3_4),3,4); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_4),3,4); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,3,4))); + + vec_st(rp_vec,0,rp); + + rp += 16; + pp += 16; + istop -= 16; + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } +} + +void png_read_filter_row_paeth3_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + const png_byte bpp = 3; + + int a, b, c, pa, pb, pc, p; + vector unsigned char rp_vec; + vector unsigned char pp_vec; + vector unsigned short a_vec,b_vec,c_vec,nearest_vec; + vector signed short pa_vec,pb_vec,pc_vec,smallest_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + rp -= bpp; + if(istop >= bpp) + istop -= bpp; + + /* Process the first pixel in the row completely (this is the same as 'up' + * because there is only one candidate predictor for the first row). + */ + for(i = 0; i < bpp ; i++) + { + *rp = (png_byte)( *rp + *pp); + rp++; + pp++; + } + + for(i = 0; i < unaligned_top ; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } + + while( istop >= 16) + { + for(i = 0; i < bpp ; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } + + rp -= bpp; + pp -= bpp; + rp_vec = vec_ld(0,rp); + vec_ld_unaligned(pp_vec,pp); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_3),1,3); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED1_3),1,3); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_3),1,3); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,1,3))); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_3),2,3); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED2_3),2,3); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_3),2,3); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,2,3))); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_3),3,3); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED3_3),3,3); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_3),3,3); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,3,3))); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED4_3),4,3); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED4_3),4,3); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED4_3),4,3); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,4,3))); + + vec_st(rp_vec,0,rp); + + rp += 15; + pp += 15; + istop -= 16; + + /* Since 16 % bpp = 16 % 3 = 1, last element of array must + * be proceeded manually + */ + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } +} + +#endif /* PNG_POWERPC_VSX_OPT > 0 */ +#endif /* PNG_POWERPC_VSX_IMPLEMENTATION == 1 (intrinsics) */ +#endif /* READ */ diff --git a/src/png/libpng/powerpc/powerpc_init.c b/src/png/libpng/powerpc/powerpc_init.c new file mode 100644 index 0000000000..07016177c5 --- /dev/null +++ b/src/png/libpng/powerpc/powerpc_init.c @@ -0,0 +1,125 @@ + +/* powerpc_init.c - POWERPC optimised filter functions + * + * Copyright (c) 2017 Glenn Randers-Pehrson + * Written by Vadim Barkov, 2017. + * Last changed in libpng 1.6.29 [March 16, 2017] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ +/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are + * called. + */ +#define _POSIX_SOURCE 1 + +#include +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#if PNG_POWERPC_VSX_OPT > 0 +#ifdef PNG_POWERPC_VSX_CHECK_SUPPORTED /* Do run-time checks */ +/* WARNING: it is strongly recommended that you do not build libpng with + * run-time checks for CPU features if at all possible. In the case of the PowerPC + * VSX instructions there is no processor-specific way of detecting the + * presence of the required support, therefore run-time detection is extremely + * OS specific. + * + * You may set the macro PNG_POWERPC_VSX_FILE to the file name of file containing + * a fragment of C source code which defines the png_have_vsx function. There + * are a number of implementations in contrib/powerpc-vsx, but the only one that + * has partial support is contrib/powerpc-vsx/linux.c - a generic Linux + * implementation which reads /proc/cpufino. + */ +#ifndef PNG_POWERPC_VSX_FILE +# ifdef __linux__ +# define PNG_POWERPC_VSX_FILE "contrib/powerpc-vsx/linux_aux.c" +# endif +#endif + +#ifdef PNG_POWERPC_VSX_FILE + +#include /* for sig_atomic_t */ +static int png_have_vsx(png_structp png_ptr); +#include PNG_POWERPC_VSX_FILE + +#else /* PNG_POWERPC_VSX_FILE */ +# error "PNG_POWERPC_VSX_FILE undefined: no support for run-time POWERPC VSX checks" +#endif /* PNG_POWERPC_VSX_FILE */ +#endif /* PNG_POWERPC_VSX_CHECK_SUPPORTED */ + +void +png_init_filter_functions_vsx(png_structp pp, unsigned int bpp) +{ + /* The switch statement is compiled in for POWERPC_VSX_API, the call to + * png_have_vsx is compiled in for POWERPC_VSX_CHECK. If both are defined + * the check is only performed if the API has not set the PowerPC option on + * or off explicitly. In this case the check controls what happens. + */ + +#ifdef PNG_POWERPC_VSX_API_SUPPORTED + switch ((pp->options >> PNG_POWERPC_VSX) & 3) + { + case PNG_OPTION_UNSET: + /* Allow the run-time check to execute if it has been enabled - + * thus both API and CHECK can be turned on. If it isn't supported + * this case will fall through to the 'default' below, which just + * returns. + */ +#endif /* PNG_POWERPC_VSX_API_SUPPORTED */ +#ifdef PNG_POWERPC_VSX_CHECK_SUPPORTED + { + static volatile sig_atomic_t no_vsx = -1; /* not checked */ + + if (no_vsx < 0) + no_vsx = !png_have_vsx(pp); + + if (no_vsx) + return; + } +#ifdef PNG_POWERPC_VSX_API_SUPPORTED + break; +#endif +#endif /* PNG_POWERPC_VSX_CHECK_SUPPORTED */ + +#ifdef PNG_POWERPC_VSX_API_SUPPORTED + default: /* OFF or INVALID */ + return; + + case PNG_OPTION_ON: + /* Option turned on */ + break; + } +#endif + + /* IMPORTANT: any new internal functions used here must be declared using + * PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the + * 'prefix' option to configure works: + * + * ./configure --with-libpng-prefix=foobar_ + * + * Verify you have got this right by running the above command, doing a build + * and examining pngprefix.h; it must contain a #define for every external + * function you add. (Notice that this happens automatically for the + * initialization function.) + */ + pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_vsx; + + if (bpp == 3) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_vsx; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_vsx; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth3_vsx; + } + + else if (bpp == 4) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_vsx; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_vsx; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth4_vsx; + } +} +#endif /* PNG_POWERPC_VSX_OPT > 0 */ +#endif /* READ */ diff --git a/src/png/libpng/scripts/checksym.awk b/src/png/libpng/scripts/checksym.awk new file mode 100644 index 0000000000..fe3af55e05 --- /dev/null +++ b/src/png/libpng/scripts/checksym.awk @@ -0,0 +1,173 @@ +#!/bin/awk -f +# Check a list of symbols against the master definition +# (official) list. Arguments: +# +# awk -f checksym.awk official-def list-to-check +# +# Output is a file in the current directory called 'symbols.new', +# the value of the awk variable "of" (which can be changed on the +# command line if required.) stdout holds error messages. Error +# code indicates success or failure. +# +# NOTE: this is a pure, old fashioned, awk script. It will +# work with any awk + +BEGIN{ + err=0 + master="" # master file + official[1] = "" # defined symbols from master file + symbol[1] = "" # defined symbols from png.h + removed[1] = "" # removed symbols from png.h + lasto = 0 # last ordinal value from png.h + mastero = 0 # highest ordinal in master file + symbolo = 0 # highest ordinal in png.h + missing = "error"# log an error on missing symbols + of="symbols.new" # default to a fixed name +} + +# Read existing definitions from the master file (the first +# file on the command line.) This must be a def file and it +# has definition lines (others are ignored) of the form: +# +# symbol @ordinal +# +master == "" { + master = FILENAME +} +FILENAME==master && NF==2 && $2~/^@/ && $1!~/^;/ { + o=0+substr($2,2) + if (o > 0) { + if (official[o] == "") { + official[o] = $1 + if (o > mastero) mastero = o + next + } else + print master ": duplicated symbol:", official[o] ":", $0 + } else + print master ": bad export line format:", $0 + err = 1 +} +FILENAME==master && $1==";missing" && NF==2{ + # This allows the master file to control how missing symbols + # are handled; symbols that aren't in either the master or + # the new file. Valid values are 'ignore', 'warning' and + # 'error' + missing = $2 +} +FILENAME==master { + next +} + +# Read new definitions, these are free form but the lines must +# just be symbol definitions. Lines will be commented out for +# 'removed' symbols, introduced in png.h using PNG_REMOVED rather +# than PNG_EXPORT. Use symbols.dfn or pngwin.dfn to generate the +# input file. +# +# symbol @ordinal # two fields, exported symbol +# ; symbol @ordinal # three fields, removed symbol +# ; @ordinal # two fields, the last ordinal +NF==2 && $1 == ";" && $2 ~ /^@[1-9][0-9]*$/ { # last ordinal + o=0+substr($2,2) + if (lasto == 0 || lasto == o) + lasto=o + else { + print "png.h: duplicated last ordinal:", lasto, o + err = 1 + } + next +} +NF==3 && $1 == ";" && $3 ~ /^@[1-9][0-9]*$/ { # removed symbol + o=0+substr($3,2) + if (removed[o] == "" || removed[o] == $2) { + removed[o] = $2 + if (o > symbolo) symbolo = o + } else { + print "png.h: duplicated removed symbol", o ": '" removed[o] "' != '" $2 "'" + err = 1 + } + next +} +NF==2 && $2 ~ /^@[1-9][0-9]*$/ { # exported symbol + o=0+substr($2,2) + if (symbol[o] == "" || symbol[o] == $1) { + symbol[o] = $1 + if (o > symbolo) symbolo = o + } else { + print "png.h: duplicated symbol", o ": '" symbol[o] "' != '" $1 "'" + err = 1 + } +} +{ + next # skip all other lines +} + +# At the end check for symbols marked as both duplicated and removed +END{ + if (symbolo > lasto) { + print "highest symbol ordinal in png.h,", symbolo ", exceeds last ordinal from png.h", lasto + err = 1 + } + if (mastero > lasto) { + print "highest symbol ordinal in", master ",", mastero ", exceeds last ordinal from png.h", lasto + err = 1 + } + unexported=0 + # Add a standard header to symbols.new: + print ";Version INSERT-VERSION-HERE" >of + print ";--------------------------------------------------------------" >of + print "; LIBPNG symbol list as a Win32 DEF file" >of + print "; Contains all the symbols that can be exported from libpng" >of + print ";--------------------------------------------------------------" >of + print "LIBRARY" >of + print "" >of + print "EXPORTS" >of + + for (o=1; o<=lasto; ++o) { + if (symbol[o] == "" && removed[o] == "") { + if (unexported == 0) unexported = o + if (official[o] == "") { + # missing in export list too, so ok + if (o < lasto) continue + } + } + if (unexported != 0) { + # Symbols in the .def but not in the new file are errors, but + # the 'unexported' symbols aren't in either. By default this + # is an error too (see the setting of 'missing' at the start), + # but this can be reset on the command line or by stuff in the + # file - see the comments above. + if (missing != "ignore") { + if (o-1 > unexported) + print "png.h:", missing ": missing symbols:", unexported "-" o-1 + else + print "png.h:", missing ": missing symbol:", unexported + if (missing != "warning") + err = 1 + } + unexported = 0 + } + if (symbol[o] != "" && removed[o] != "") { + print "png.h: symbol", o, "both exported as '" symbol[o] "' and removed as '" removed[o] "'" + err = 1 + } else if (symbol[o] != official[o]) { + # either the symbol is missing somewhere or it changed + err = 1 + if (symbol[o] == "") + print "png.h: symbol", o, "is exported as '" official[o] "' in", master + else if (official[o] == "") + print "png.h: exported symbol", o, "'" symbol[o] "' not present in", master + else + print "png.h: exported symbol", o, "'" symbol[o] "' exists as '" official[o] "' in", master + } + + # Finally generate symbols.new + if (symbol[o] != "") + print " " symbol[o], "@" o > of + } + + if (err != 0) { + print "*** A new list is in", of, "***" + exit 1 + } +} diff --git a/src/png/libpng/scripts/def.c b/src/png/libpng/scripts/def.c new file mode 100644 index 0000000000..0ffcbeb0c2 --- /dev/null +++ b/src/png/libpng/scripts/def.c @@ -0,0 +1,29 @@ +/* def.c - define format of libpng.def + * + * Last changed in libpng version 1.6.16 [December 22, 2014] + * Copyright (c) 2011-2014 Glenn Randers-Pehrson + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* Write the export file header: */ +PNG_DFN ";--------------------------------------------------------------" +PNG_DFN "; LIBPNG module definition file for OS/2" +PNG_DFN ";--------------------------------------------------------------" +PNG_DFN "" +PNG_DFN "; If you give the library an explicit name one or other files" +PNG_DFN "; may need modifying to support the new name on one or more" +PNG_DFN "; systems." +PNG_DFN "LIBRARY" +PNG_DFN "OS2 DESCRIPTION "PNG image compression library"" +PNG_DFN "OS2 CODE PRELOAD MOVEABLE DISCARDABLE" +PNG_DFN "" +PNG_DFN "EXPORTS" +PNG_DFN ";Version 1.6.35beta02" + +#define PNG_EXPORTA(ordinal, type, name, args, attributes)\ + PNG_DFN "@" SYMBOL_PREFIX "@@" name "@" + +#include "../png.h" diff --git a/src/png/libpng/scripts/dfn.awk b/src/png/libpng/scripts/dfn.awk new file mode 100644 index 0000000000..346b9db7d5 --- /dev/null +++ b/src/png/libpng/scripts/dfn.awk @@ -0,0 +1,203 @@ +#!/bin/awk -f +# scripts/dfn.awk - process a .dfn file +# +# last changed in libpng version 1.5.19 - August 21, 2014 +# +# Copyright (c) 2013-2014 Glenn Randers-Pehrson +# +# This code is released under the libpng license. +# For conditions of distribution and use, see the disclaimer +# and license in png.h + +# The output of this script is written to the file given by +# the variable 'out', which should be set on the command line. +# Error messages are printed to stdout and if any are printed +# the script will exit with error code 1. + +BEGIN{ + out="/dev/null" # as a flag + out_count=0 # count of output lines + err=0 # set if an error occurred + sort=0 # sort the output + array[""]="" +} + +# The output file must be specified before any input: +NR==1 && out == "/dev/null" { + print "out=output.file must be given on the command line" + # but continue without setting the error code; this allows the + # script to be checked easily +} + +# Output can be sorted; two lines are recognized +$1 == "PNG_DFN_START_SORT"{ + sort=0+$2 + next +} + +$1 ~ /^PNG_DFN_END_SORT/{ + # Do a very simple, slow, sort; notice that blank lines won't be + # output by this + for (entry in array) { + while (array[entry] != "") { + key = entry + value = array[key] + array[key] = "" + + for (alt in array) { + if (array[alt] != "" && alt < key) { + array[key] = value + value = array[alt] + key = alt + array[alt] = "" + } + } + + print value >out + } + } + sort=0 + next +} + +/^[^"]*PNG_DFN *".*"[^"]*$/{ + # A definition line, apparently correctly formatted; extract the + # definition then replace any doubled "" that remain with a single + # double quote. Notice that the original doubled double quotes + # may have been split by tokenization + # + # Sometimes GCC splits the PNG_DFN lines; we know this has happened + # if the quotes aren't closed and must read another line. In this + # case it is essential to reject lines that start with '#' because those + # are introduced #line directives. + orig=$0 + line=$0 + lineno=FNR + if (lineno == "") lineno=NR + + if (sub(/^[^"]*PNG_DFN *"/,"",line) != 1) { + print "line", lineno ": processing failed:" + print orig + err=1 + next + } else { + ++out_count + } + + # Now examine quotes within the value: + # + # @" - delete this and any following spaces + # "@ - delete this and any preceding spaces + # @' - replace this by a double quote + # + # This allows macro substitution by the C compiler thus: + # + # #define first_name John + # #define last_name Smith + # + # PNG_DFN"#define name @'@" first_name "@ @" last_name "@@'" + # + # Might get C preprocessed to: + # + # PNG_DFN "#define foo @'@" John "@ @" Smith "@@'" + # + # Which this script reduces to: + # + # #define name "John Smith" + # + while (1) { + # While there is an @" remove it and the next "@ + if (line ~ /@"/) { + if (line ~ /@".*"@/) { + # Do this special case first to avoid swallowing extra spaces + # before or after the @ stuff: + if (!sub(/@" *"@/, "", line)) { + # Ok, do it in pieces - there has to be a non-space between the + # two. NOTE: really weird things happen if a leading @" is + # lost - the code will error out below (I believe). + if (!sub(/@" */, "", line) || !sub(/ *"@/, "", line)) { + print "line", lineno, ": internal error:", orig + exit 1 + } + } + } + + # There is no matching "@. Assume a split line + else while (1) { + if (getline nextline) { + # If the line starts with '#' it is a preprocesor line directive + # from cc -E; skip it: + if (nextline !~ /^#/) { + line = line " " nextline + break + } + } else { + # This is end-of-input - probably a missing "@ on the first line: + print "line", lineno ": unbalanced @\" ... \"@ pair" + err=1 + next + } + } + + # Keep going until all the @" have gone + continue + } + + # Attempt to remove a trailing " (not preceded by '@') - if this can + # be done, stop now; if not assume a split line again + if (sub(/"[^"]*$/, "", line)) + break + + # Read another line + while (1) { + if (getline nextline) { + if (nextline !~ /^#/) { + line = line " " nextline + # Go back to stripping @" "@ pairs + break + } + } else { + print "line", lineno ": unterminated PNG_DFN string" + err=1 + next + } + } + } + + # Put any needed double quotes in (at the end, because these would otherwise + # interfere with the processing above.) + gsub(/@'/,"\"", line) + + # Remove any trailing spaces (not really required, but for + # editorial consistency + sub(/ *$/, "", line) + + # Remove trailing CR + sub(/ $/, "", line) + + if (sort) { + if (split(line, parts) < sort) { + print "line", lineno ": missing sort field:", line + err=1 + } else + array[parts[sort]] = line + } + + else + print line >out + next +} + +/PNG_DFN/{ + print "line", NR, "incorrectly formatted PNG_DFN line:" + print $0 + err = 1 +} + +END{ + if (out_count > 0 || err > 0) + exit err + + print "no definition lines found" + exit 1 +} diff --git a/src/png/libpng/scripts/genchk.cmake.in b/src/png/libpng/scripts/genchk.cmake.in new file mode 100644 index 0000000000..ab3b9d7469 --- /dev/null +++ b/src/png/libpng/scripts/genchk.cmake.in @@ -0,0 +1,37 @@ +# genchk.cmake.in +# Generate .chk from .out with awk (generic), based upon the automake logic. + +# Copyright (C) 2016 Glenn Randers-Pehrson +# Written by Roger Leigh, 2016 + +# This code is released under the libpng license. +# For conditions of distribution and use, see the disclaimer +# and license in png.h + +# Variables substituted from CMakeLists.txt +set(SRCDIR "@CMAKE_CURRENT_SOURCE_DIR@") + +set(AWK "@AWK@") + +get_filename_component(INPUTEXT "${INPUT}" EXT) +get_filename_component(OUTPUTEXT "${OUTPUT}" EXT) +get_filename_component(INPUTBASE "${INPUT}" NAME_WE) +get_filename_component(OUTPUTBASE "${OUTPUT}" NAME_WE) +get_filename_component(INPUTDIR "${INPUT}" PATH) +get_filename_component(OUTPUTDIR "${OUTPUT}" PATH) + +if("${INPUTEXT}" STREQUAL ".out" AND "${OUTPUTEXT}" STREQUAL ".chk") + # Generate .chk from .out with awk (generic) + file(REMOVE "${OUTPUT}" "${OUTPUTDIR}/${OUTPUTBASE}.new") + execute_process(COMMAND "${AWK}" -f "${SRCDIR}/scripts/checksym.awk" + "${SRCDIR}/scripts/${INPUTBASE}.def" + "of=${OUTPUTDIR}/${OUTPUTBASE}.new" + "${INPUT}" + RESULT_VARIABLE AWK_FAIL) + if(AWK_FAIL) + message(FATAL_ERROR "Failed to generate ${OUTPUTDIR}/${OUTPUTBASE}.new") + endif() + file(RENAME "${OUTPUTDIR}/${OUTPUTBASE}.new" "${OUTPUT}") +else() + message(FATAL_ERROR "Unsupported conversion: ${INPUTEXT} to ${OUTPUTEXT}") +endif() diff --git a/src/png/libpng/scripts/genout.cmake.in b/src/png/libpng/scripts/genout.cmake.in new file mode 100644 index 0000000000..01f12de2fc --- /dev/null +++ b/src/png/libpng/scripts/genout.cmake.in @@ -0,0 +1,93 @@ +# genout.cmake.in +# Generate .out from .c with awk (generic), based upon the automake logic. + +# Copyright (C) 2016 Glenn Randers-Pehrson +# Written by Roger Leigh, 2016 + +# This code is released under the libpng license. +# For conditions of distribution and use, see the disclaimer +# and license in png.h + +# Variables substituted from CMakeLists.txt +set(SRCDIR "@CMAKE_CURRENT_SOURCE_DIR@") +set(BINDIR "@CMAKE_CURRENT_BINARY_DIR@") + +set(AWK "@AWK@") +set(CMAKE_C_COMPILER "@CMAKE_C_COMPILER@") +set(CMAKE_C_FLAGS @CMAKE_C_FLAGS@) +set(INCDIR "@CMAKE_CURRENT_BINARY_DIR@") +set(PNG_PREFIX "@PNG_PREFIX@") +set(PNGLIB_MAJOR "@PNGLIB_MAJOR@") +set(PNGLIB_MINOR "@PNGLIB_MINOR@") +set(PNGLIB_VERSION "@PNGLIB_VERSION@") +set(ZLIBINCDIR "@ZLIB_INCLUDE_DIR@") + +set(PLATFORM_C_FLAGS) +if(APPLE) + set(CMAKE_OSX_ARCHITECTURES "@CMAKE_OSX_ARCHITECTURES@") + set(CMAKE_OSX_SYSROOT "@CMAKE_OSX_SYSROOT@") + if(CMAKE_OSX_ARCHITECTURES) + set(PLATFORM_C_FLAGS ${PLATFORM_C_FLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}) + endif() + if(CMAKE_OSX_SYSROOT) + set(PLATFORM_C_FLAGS ${PLATFORM_C_FLAGS} -isysroot ${CMAKE_OSX_SYSROOT}) + endif() +endif() + +get_filename_component(INPUTEXT "${INPUT}" EXT) +get_filename_component(OUTPUTEXT "${OUTPUT}" EXT) +get_filename_component(INPUTBASE "${INPUT}" NAME_WE) +get_filename_component(OUTPUTBASE "${OUTPUT}" NAME_WE) +get_filename_component(INPUTDIR "${INPUT}" PATH) +get_filename_component(OUTPUTDIR "${OUTPUT}" PATH) + +if ("${INPUTEXT}" STREQUAL ".c" AND "${OUTPUTEXT}" STREQUAL ".out") + get_filename_component(GENDIR "${OUTPUT}" PATH) + file(MAKE_DIRECTORY "${GENDIR}") + + file(REMOVE "${OUTPUT}.tf1" "${OUTPUT}.tf2") + + set(INCLUDES "-I${INCDIR}") + if(ZLIBINCDIR) + foreach(dir ${ZLIBINCDIR}) + list(APPEND INCLUDES "-I${dir}") + endforeach() + endif() + + if(PNG_PREFIX) + set(PNG_PREFIX_DEF "-DPNG_PREFIX=${PNG_PREFIX}") + endif() + + execute_process(COMMAND "${CMAKE_C_COMPILER}" "-E" + ${CMAKE_C_FLAGS} + ${PLATFORM_C_FLAGS} + "-I${SRCDIR}" + "-I${BINDIR}" + ${INCLUDES} + "-DPNGLIB_LIBNAME=PNG${PNGLIB_MAJOR}${PNGLIB_MINOR}_0" + "-DPNGLIB_VERSION=${PNGLIB_VERSION}" + "-DSYMBOL_PREFIX=${SYMBOL_PREFIX}" + "-DPNG_NO_USE_READ_MACROS" + "-DPNG_BUILDING_SYMBOL_TABLE" + ${PNG_PREFIX_DEF} + "${INPUT}" + OUTPUT_FILE "${OUTPUT}.tf1" + WORKING_DIRECTORY "${BINDIR}" + RESULT_VARIABLE CPP_FAIL) + if(CPP_FAIL) + message(FATAL_ERROR "Failed to generate ${OUTPUT}.tf1") + endif() + + execute_process(COMMAND "${AWK}" -f "${SRCDIR}/scripts/dfn.awk" + "out=${OUTPUT}.tf2" "${OUTPUT}.tf1" + WORKING_DIRECTORY "${BINDIR}" + RESULT_VARIABLE AWK_FAIL) + if(AWK_FAIL) + message(FATAL_ERROR "Failed to generate ${OUTPUT}.tf2") + endif() + + file(REMOVE "${OUTPUT}.tf1") + file(RENAME "${OUTPUT}.tf2" "${OUTPUT}") +else() + message(FATAL_ERROR "Unsupported conversion: ${INPUTEXT} to ${OUTPUTEXT}") +endif() diff --git a/src/png/libpng/scripts/gensrc.cmake.in b/src/png/libpng/scripts/gensrc.cmake.in new file mode 100644 index 0000000000..f28a622662 --- /dev/null +++ b/src/png/libpng/scripts/gensrc.cmake.in @@ -0,0 +1,138 @@ +# gensrc.cmake.in +# Generate source files with awk, based upon the automake logic. + +# Copyright (C) 2016 Glenn Randers-Pehrson +# Written by Roger Leigh, 2016 + +# This code is released under the libpng license. +# For conditions of distribution and use, see the disclaimer +# and license in png.h + +# Variables substituted from CMakeLists.txt +set(SRCDIR "@CMAKE_CURRENT_SOURCE_DIR@") +set(BINDIR "@CMAKE_CURRENT_BINARY_DIR@") + +set(AWK "@AWK@") +set(DFA_XTRA "@DFA_XTRA@") +set(PNG_PREFIX "@PNG_PREFIX@") +set(PNGLIB_VERSION "@PNGLIB_VERSION@") + +if("${OUTPUT}" STREQUAL "scripts/pnglibconf.c") + # Generate scripts/pnglibconf.c + + file(REMOVE "${BINDIR}/pnglibconf.tf6" "${BINDIR}/pnglibconf.tf7") + + execute_process(COMMAND "${CMAKE_COMMAND}" -E echo "com ${PNGLIB_VERSION} STANDARD API DEFINITION" + COMMAND "${AWK}" -f "${SRCDIR}/scripts/options.awk" + "out=pnglibconf.tf6" "logunsupported=1" "version=search" + "${SRCDIR}/pngconf.h" "-" + "${SRCDIR}/scripts/pnglibconf.dfa" + WORKING_DIRECTORY "${BINDIR}" + RESULT_VARIABLE AWK_FAIL) + if(AWK_FAIL) + message(FATAL_ERROR "Failed to generate pnglibconf.tf6") + endif() + + execute_process(COMMAND "${AWK}" -f "${SRCDIR}/scripts/options.awk" + "out=pnglibconf.tf7" "pnglibconf.tf6" + WORKING_DIRECTORY "${BINDIR}" + RESULT_VARIABLE AWK_FAIL) + if(AWK_FAIL) + message(FATAL_ERROR "Failed to generate pnglibconf.tf7") + endif() + + file(REMOVE "pnglibconf.tf6") + file(MAKE_DIRECTORY "${BINDIR}/scripts") + file(RENAME "pnglibconf.tf7" "${BINDIR}/scripts/pnglibconf.c") + +elseif ("${OUTPUT}" STREQUAL "pnglibconf.c") + # Generate pnglibconf.c + + file(REMOVE "${BINDIR}/pnglibconf.tf4" "${BINDIR}/pnglibconf.tf5") + + execute_process(COMMAND "${AWK}" -f "${SRCDIR}/scripts/options.awk" + out=pnglibconf.tf4 version=search + ${SRCDIR}/pngconf.h ${SRCDIR}/scripts/pnglibconf.dfa + ${SRCDIR}/pngusr.dfa ${DFA_XTRA} + WORKING_DIRECTORY "${BINDIR}" + RESULT_VARIABLE AWK_FAIL) + if(AWK_FAIL) + message(FATAL_ERROR "Failed to generate pnglibconf.tf4") + endif() + + execute_process(COMMAND "${AWK}" -f "${SRCDIR}/scripts/options.awk" + out=pnglibconf.tf5 pnglibconf.tf4 + WORKING_DIRECTORY "${BINDIR}" + RESULT_VARIABLE AWK_FAIL) + if(AWK_FAIL) + message(FATAL_ERROR "Failed to generate pnglibconf.tf5") + endif() + + file(REMOVE "pnglibconf.tf4") + file(MAKE_DIRECTORY "${BINDIR}/scripts") + file(RENAME "pnglibconf.tf5" "${BINDIR}/pnglibconf.c") + +elseif ("${OUTPUT}" STREQUAL "pnglibconf.h") + # Generate pnglibconf.h + + file(REMOVE "${BINDIR}/${OUTPUT}") + if(PNG_PREFIX) + file(REMOVE "pnglibconf.tf8") + + execute_process(COMMAND "${AWK}" "s==0 && NR>1{print prev} + s==0{prev=\$0} + s==1{print \"#define\", \$1, \"${PNG_PREFIX}\" \$1} + s==2{print \"#define ${PNG_PREFIX}png_\" \$1, \"PNG_\" \$1} + END{print prev}" s=0 pnglibconf.out s=1 "${BINDIR}/scripts/prefix.out" + s=2 "${SRCDIR}/scripts/macro.lst" + OUTPUT_FILE pnglibconf.tf8 + RESULT_VARIABLE AWK_FAIL) + if(AWK_FAIL) + message(FATAL_ERROR "Failed to generate pnglibconf.tf8") + endif() + + file(RENAME "pnglibconf.tf8" "${BINDIR}/${OUTPUT}") + else() + execute_process(COMMAND "${CMAKE_COMMAND}" -E copy "${BINDIR}/pnglibconf.out" + "${BINDIR}/${OUTPUT}" + RESULT_VARIABLE COPY_FAIL) + if(COPY_FAIL) + message(FATAL_ERROR "Failed to create pnglibconf.h") + endif() + endif() + +elseif ("${OUTPUT}" STREQUAL "pngprefix.h") + # Generate pngprefix.h + + file(REMOVE "${BINDIR}/${OUTPUT}") + + if(PNG_PREFIX) + file(REMOVE "pngprefix.tf1") + + execute_process(COMMAND "${AWK}" + "{print \"#define\", \$1, \"${PNG_PREFIX}\" \$1}" + "${BINDIR}/scripts/intprefix.out" + OUTPUT_FILE "pngprefix.tf1" + RESULT_VARIABLE AWK_FAIL) + if(AWK_FAIL) + message(FATAL_ERROR "Failed to generate pngprefix.tf1") + endif() + + file(RENAME "pngprefix.tf1" "${BINDIR}/${OUTPUT}") + else() + file(WRITE "${BINDIR}/${OUTPUT}" "/* No libpng symbol prefix configured. */") + endif() + +elseif("${OUTPUT}" STREQUAL "scripts/pnglibconf.h.prebuilt") + # Generate scripts/pnglibconf.h.prebuilt (fails build) + + message(STATUS "Attempting to build scripts/pnglibconf.h.prebuilt") + message(STATUS "This is a machine generated file, but if you want to make") + message(STATUS "a new one simply build the 'genfiles' target, and copy") + message(STATUS "scripts/pnglibconf.out to scripts/pnglibconf.h.prebuilt") + message(STATUS "AND set PNG_ZLIB_VERNUM to 0 (you MUST do this)") + message(FATAL_ERROR "Stopping build") + +else() + message(FATAL_ERROR "Unsupported output: ${OUTPUT}") +endif() diff --git a/src/png/libpng/scripts/intprefix.c b/src/png/libpng/scripts/intprefix.c new file mode 100644 index 0000000000..254f8e94b4 --- /dev/null +++ b/src/png/libpng/scripts/intprefix.c @@ -0,0 +1,22 @@ + +/* intprefix.c - generate an unprefixed internal symbol list + * + * Last changed in libpng version 1.6.16 [December 22, 2014] + * Copyright (c) 2013-2014 Glenn Randers-Pehrson + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_INTERNAL_DATA(type, name, array)\ + PNG_DFN "@" name "@" + +#define PNG_INTERNAL_FUNCTION(type, name, args, attributes)\ + PNG_DFN "@" name "@" + +#define PNG_INTERNAL_CALLBACK(type, name, args, attributes)\ + PNG_DFN "@" name "@" + +#define PNGPREFIX_H /* self generation */ +#include "../pngpriv.h" diff --git a/src/png/libpng/scripts/libpng-config-body.in b/src/png/libpng/scripts/libpng-config-body.in new file mode 100644 index 0000000000..b466432d54 --- /dev/null +++ b/src/png/libpng/scripts/libpng-config-body.in @@ -0,0 +1,96 @@ + +usage() +{ + cat <&2 +# awk -f scripts/options.awk out=options.dfn options.tmp 1>&2 +# +# Some options may be specified on the command line: +# +# deb=1 Causes debugging to be output +# logunsupported=1 Causes all options to be recorded in the output +# everything=off Causes all options to be disabled by default +# everything=on Causes all options to be enabled by default +# +# If awk fails on your platform, try nawk instead. +# +# These options may also be specified in the original input file (and +# are copied to the preprocessed file). + +BEGIN{ + out="" # intermediate, preprocessed, file + pre=-1 # preprocess (first line) + version="libpng version unknown" # version information + version_file="" # where to find the version + err=0 # in-line exit sets this + # The following definitions prevent the C preprocessor noticing the lines + # that will be in the final output file. Some C preprocessors tokenise + # the lines, for example by inserting spaces around operators, and all + # C preprocessors notice lines that start with '#', most remove comments. + # The technique adopted here is to make the final output lines into + # C strings (enclosed in double quotes), preceded by PNG_DFN. As a + # consequence the output cannot contain a 'raw' double quote - instead put + # @' in, this will be replaced by a single " afterward. See the parser + # script dfn.awk for more capabilities (not required here). Note that if + # you need a " in a 'setting' in pnglibconf.dfa it must also be @'! + dq="@'" # For a single double quote + start=" PNG_DFN \"" # Start stuff to output (can't contain a "!) + end="\" " # End stuff to output + subs="@\" " # Substitute start (substitute a C macro) + sube=" \"@" # Substitute end + comment=start "/*" # Comment start + cend="*/" end # Comment end + def=start "#define PNG_" # Arbitrary define + sup="_SUPPORTED" end # end supported option + und=comment "#undef PNG_" # Unsupported option + une="_SUPPORTED" cend # end unsupported option + error=start "ERROR:" # error message, terminate with 'end' + + # Variables + deb=0 # debug - set on command line + everything="" # do not override defaults + logunsupported=0 # write unsupported options too + + # Precreate arrays + # for each option: + option[""] = "" # list of all options: default enabled/disabled + done[""] = 1 # marks option as having been output + requires[""] = "" # requires by option + iffs[""] = "" # if by option + enabledby[""] = "" # options that enable it by option + sets[""] = "" # settings set by each option + setval[""] = "" # value to set (indexed: 'option sets[option]') + # for each setting: + setting[""] = "" # requires by setting + defaults[""] = "" # used for a defaulted value + doneset[""] = 1 # marks setting as having been output + r[""] = "" # Temporary array + + # For decorating the output file + protect = "" +} + +# The output file must be specified before any input: +out == "" { + print "out=output.file must be given on the command line" + err = 1 + exit 1 +} + +# The very first line indicates whether we are reading pre-processed +# input or not, this must come *first* because 'PREPROCESSED' needs +# to be the very first line in the temporary file. +pre == -1{ + if ($0 == "PREPROCESSED") { + pre = 0 + next + } else { + pre = 1 + print "PREPROCESSED" >out + # And fall through to continue processing + } +} + +# While pre-processing if version is set to "search" look for a version string +# in the following file. +pre && version == "search" && version_file == ""{ + version_file = FILENAME +} + +pre && version == "search" && version_file != FILENAME{ + print "version string not found in", version_file + err = 1 + exit 1 +} + +pre && version == "search" && $0 ~ /^ \* libpng version/{ + version = substr($0, 4) + print "version =", version >out + next +} + +pre && FILENAME == version_file{ + next +} + +# variable=value +# Sets the given variable to the given value (the syntax is fairly +# free form, except for deb (you are expected to understand how to +# set the debug variable...) +# +# This happens before the check on 'pre' below skips most of the +# rest of the actions, so the variable settings happen during +# preprocessing but are recorded in the END action too. This +# allows them to be set on the command line too. +$0 ~ /^[ ]*version[ ]*=/{ + sub(/^[ ]*version[ ]*=[ ]*/, "") + version = $0 + next +} +$0 ~ /^[ ]*everything[ =]*off[ ]*$/{ + everything = "off" + next +} +$0 ~ /^[ ]*everything[ =]*on[ ]*$/{ + everything = "on" + next +} +$0 ~ /^[ ]*logunsupported[ =]*0[ ]*$/{ + logunsupported = 0 + next +} +$0 ~ /^[ ]*logunsupported[ =]*1[ ]*$/{ + logunsupported = 1 + next +} +$1 == "deb" && $2 == "=" && NF == 3{ + deb = $3 + next +} + +# Preprocessing - this just copies the input file with lines +# that need preprocessing (just chunk at present) expanded +# The bare "pre" instead of "pre != 0" crashes under Sunos awk +pre && $1 != "chunk"{ + print >out + next +} + +# The first characters of the line determine how it is processed, +# leading spaces are ignored. In general tokens that are not +# keywords are the names of options. An option 'name' is +# controlled by the definition of the corresponding macros: +# +# PNG_name_SUPPORTED The option is turned on +# PNG_NO_name +# PNG_NO_name_SUPPORTED If the first macro is not defined +# either of these will turn the option off +# +# If none of these macros are defined the option is turned on, unless +# the keyword 'off' is given in a line relating to the option. The +# keyword 'on' can also be given, but it will be ignored (since it is +# the default.) +# +# In the syntax below a 'name' is indicated by "NAME", other macro +# values are indicated by "MACRO", as with "NAME" the leading "PNG_" +# is omitted, but in this case the "NO_" prefix and the "_SUPPORTED" +# suffix are never used. +# +# Each line is introduced by a keyword - the first non-space characters +# on the line. A line starting with a '#' is a comment - it is totally +# ignored. Keywords are as follows, a NAME, is simply a macro name +# without the leading PNG_, PNG_NO_ or the trailing _SUPPORTED. + +$1 ~ /^#/ || $0 ~ /^[ ]*$/{ + next +} + +# com +# The whole line is placed in the output file as a comment with +# the preceding 'com' removed +$1 == "com"{ + if (NF > 1) { + # sub(/^[ ]*com[ ]*/, "") + $1 = "" + print comment $0, cend >out + } else + print start end >out + next +} + +# version +# Inserts a version comment +$1 == "version" && NF == 1{ + if (version == "") { + print "ERROR: no version string set" + err = 1 # prevent END{} running + exit 1 + } + + print comment, version, cend >out + next +} + +# file output input protect +# Informational: the official name of the input file (without +# make generated local directories), the official name of the +# output file and, if required, a name to use in a protection +# macro for the contents. +$1 == "file" && NF >= 2{ + print comment, $2, cend >out + print comment, "Machine generated file: DO NOT EDIT", cend >out + if (NF >= 3) + print comment, "Derived from:", $3, cend >out + protect = $4 + if (protect != "") { + print start "#ifndef", protect end >out + print start "#define", protect end >out + } + next +} + +# option NAME ( (requires|enables|if) NAME* | on | off | disabled | +# sets SETTING VALUE+ )* +# +# Declares an option 'NAME' and describes its default setting (disabled) +# and its relationship to other options. The option is disabled +# unless *all* the options listed after 'requires' are set and at +# least one of the options listed after 'if' is set. If the +# option is set then it turns on all the options listed after 'enables'. +# +# Note that "enables" takes priority over the required/if/disabled/off +# setting of the target option. +# +# The definition file may list an option as 'disabled': off by default, +# otherwise the option is enabled: on by default. A later (and it must +# be later) entry may turn an option on or off explicitly. + +$1 == "option" && NF >= 2{ + opt = $2 + sub(/,$/,"",opt) + onoff = option[opt] # records current (and the default is "", enabled) + key = "" + istart = 3 + do { + if (istart == 1) { # continuation line + val = getline + + if (val != 1) { # error reading it + if (val == 0) + print "option", opt ": ERROR: missing continuation line" + else + print "option", opt ": ERROR: error reading continuation line" + + # This is a hard error + err = 1 # prevent END{} running + exit 1 + } + } + + for (i=istart; i<=NF; ++i) { + val=$(i) + sub(/,$/,"",val) + if (val == "on" || val == "off" || val == "disabled" || val =="enabled") { + key = "" + if (onoff != val) { + # on or off can zap disabled or enabled: + if (onoff == "" || (onoff == "disabled" || onoff == "enabled") && + (val == "on" || val == "off")) { + # It's easy to mis-spell the option when turning it + # on or off, so warn about it here: + if (onoff == "" && (val == "on" || val == "off")) { + print "option", opt ": ERROR: turning unrecognized option", val + # For the moment error out - it is safer + err = 1 # prevent END{} running + exit 1 + } + onoff = val + } else { + # Print a message, otherwise the error + # below is incomprehensible + print "option", opt ": currently", onoff ": attempt to turn", val + break + } + } + } else if (val == "requires" || val == "if" || val == "enables" || val =="sets") { + key = val + } else if (key == "requires") { + requires[opt] = requires[opt] " " val + } else if (key == "if") { + iffs[opt] = iffs[opt] " " val + } else if (key == "enables") { + enabledby[val] = enabledby[val] " " opt + } else if (key == "sets") { + sets[opt] = sets[opt] " " val + key = "setval" + set = val + } else if (key == "setval") { + setval[opt " " set] = setval[opt " " set] " " val + } else + break # bad line format + } + + istart = 1 + } while (i > NF && $0 ~ /,$/) + + if (i > NF) { + # Set the option, defaulting to 'enabled' + if (onoff == "") onoff = "enabled" + option[opt] = onoff + next + } + # Else fall through to the error handler +} + +# chunk NAME [requires OPT] [enables LIST] [on|off|disabled] +# Expands to the 'option' settings appropriate to the reading and +# writing of an ancillary PNG chunk 'NAME': +# +# option READ_NAME requires READ_ANCILLARY_CHUNKS [READ_OPT] +# option READ_NAME enables NAME LIST +# [option READ_NAME off] +# option WRITE_NAME requires WRITE_ANCILLARY_CHUNKS [WRITE_OPT] +# option WRITE_NAME enables NAME LIST +# [option WRITE_NAME off] + +pre != 0 && $1 == "chunk" && NF >= 2{ + # 'chunk' is handled on the first pass by writing appropriate + # 'option' lines into the intermediate file. + opt = $2 + sub(/,$/,"",opt) + onoff = "" + reqread = "" + reqwrite = "" + enables = "" + req = 0 + istart = 3 + do { + if (istart == 1) { # continuation line + val = getline + + if (val != 1) { # error reading it + if (val == 0) + print "chunk", opt ": ERROR: missing continuation line" + else + print "chunk", opt ": ERROR: error reading continuation line" + + # This is a hard error + err = 1 # prevent END{} running + exit 1 + } + } + + # read the keywords/additional OPTS + for (i=istart; i<=NF; ++i) { + val = $(i) + sub(/,$/,"",val) + if (val == "on" || val == "off" || val == "disabled") { + if (onoff != val) { + if (onoff == "") + onoff = val + else + break # on/off conflict + } + req = 0 + } else if (val == "requires") + req = 1 + else if (val == "enables") + req = 2 + else if (req == 1){ + reqread = reqread " READ_" val + reqwrite = reqwrite " WRITE_" val + } else if (req == 2) + enables = enables " " val + else + break # bad line: handled below + } + + istart = 1 + } while (i > NF && $0 ~ /,$/) + + if (i > NF) { + # Output new 'option' lines to the intermediate file (out) + print "option READ_" opt, "requires READ_ANCILLARY_CHUNKS" reqread, "enables", opt enables , onoff >out + print "option WRITE_" opt, "requires WRITE_ANCILLARY_CHUNKS" reqwrite, "enables", opt enables, onoff >out + next + } + # Else hit the error handler below - bad line format! +} + +# setting MACRO ( requires MACRO* )* [ default VALUE ] +# Behaves in a similar way to 'option' without looking for NO_ or +# _SUPPORTED; the macro is enabled if it is defined so long as all +# the 'requires' macros are also defined. The definitions may be +# empty, an error will be issued if the 'requires' macros are +# *not* defined. If given the 'default' value is used if the +# macro is not defined. The default value will be re-tokenised. +# (BTW: this is somewhat restrictive, it mainly exists for the +# support of non-standard configurations and numeric parameters, +# see the uses in scripts/options.dat + +$1 == "setting" && (NF == 2 || NF >= 3 && ($3 == "requires" || $3 == "default")){ + reqs = "" + deflt = "" + isdef = 0 + key = "" + for (i=3; i<=NF; ++i) + if ($(i) == "requires" || $(i) == "default") { + key = $(i) + if (key == "default") isdef = 1 + } else if (key == "requires") + reqs = reqs " " $(i) + else if (key == "default") + deflt = deflt " " $(i) + else + break # Format error, handled below + + setting[$2] = reqs + # NOTE: this overwrites a previous value silently + if (isdef && deflt == "") + deflt = " " # as a flag to force output + defaults[$2] = deflt + next +} + +# The order of the dependency lines (option, chunk, setting) is irrelevant +# - the 'enables', 'requires' and 'if' settings will be used to determine +# the correct order in the output and the final values in pnglibconf.h are +# not order dependent. 'requires' and 'if' entries take precedence over +# 'enables' from other options; if an option requires another option it +# won't be set regardless of any options that enable it unless the other +# option is also enabled. +# +# Similarly 'enables' trumps a NO_ definition in CFLAGS or pngusr.h +# +# For simplicity cycles in the definitions are regarded as errors, +# even if they are not ambiguous. +# A given NAME can be specified in as many 'option' lines as required, the +# definitions are additive. + +# For backwards compatibility equivalent macros may be listed thus: +# +# = [NO_]NAME MACRO +# Makes -DMACRO equivalent to -DPNG_NO_NAME or -DPNG_NAME_SUPPORTED +# as appropriate. +# +# The definition is injected into the C compiler input when encountered +# in the second pass (so all these definitions appear *after* the @ +# lines!) +# +# 'NAME' is as above, but 'MACRO' is the full text of the equivalent +# old, deprecated, macro. + +$1 == "=" && NF == 3{ + print "#ifdef PNG_" $3 >out + if ($2 ~ /^NO_/) + print "# define PNG_" $2 >out + else + print "# define PNG_" $2 "_SUPPORTED" >out + print "#endif" >out + next +} + +# Lines may be injected into the C compiler input by preceding them +# with an "@" character. The line is copied with just the leading +# @ removed. + +$1 ~ /^@/{ + # sub(/^[ ]*@/, "") + $1 = substr($1, 2) + print >out + next +} + +# Check for unrecognized lines, because of the preprocessing chunk +# format errors will be detected on the first pass independent of +# any other format errors. +{ + print "options.awk: bad line (" NR "):", $0 + err = 1 # prevent END{} running + exit 1 +} + +# For checking purposes names that start with "ok_" or "fail_" are +# not output to pnglibconf.h and must be either enabled or disabled +# respectively for the build to succeed. This allows interdependencies +# between options of the form "at least one of" or "at most one of" +# to be checked. For example: +# +# option FLOATING_POINT enables ok_math +# option FIXED_POINT enables ok_math +# This ensures that at least one of FLOATING_POINT and FIXED_POINT +# must be set for the build to succeed. +# +# option fail_math requires FLOATING_POINT FIXED_POINT +# This means the build will fail if *both* FLOATING_POINT and +# FIXED_POINT are set (this is an example; in fact both are allowed.) +# +# If all these options were given the build would require exactly one +# of the names to be enabled. + +END{ + # END{} gets run on an exit (a traditional awk feature) + if (err) exit 1 + + if (pre) { + # Record the final value of the variables + print "deb =", deb >out + if (everything != "") { + print "everything =", everything >out + } + print "logunsupported =", logunsupported >out + exit 0 + } + + # Do the options first (allowing options to set settings). The dependency + # tree is thus: + # + # name > name + # name requires name + # name if name + # name enabledby name + # + # First build a list 'tree' by option of all the things on which + # it depends. + print "" >out + print "/* OPTIONS */" >out + print comment, "options", cend >out + for (opt in enabledby) tree[opt] = 1 # may not be explicit options + for (opt in option) if (opt != "") { + o = option[opt] + # option should always be one of the following values + if (o != "on" && o != "off" && o != "disabled" && o != "enabled") { + print "internal option error (" o ")" + exit 1 + } + tree[opt] = "" # so unlisted options marked + } + for (opt in tree) if (opt != "") { + if (tree[opt] == 1) { + tree[opt] = "" + if (option[opt] != "") { + print "internal error (1)" + exit 1 + } + # Macros only listed in 'enables' remain off unless + # one of the enabling macros is on. + option[opt] = "disabled" + } + + split("", list) # clear 'list' + # Now add every requires, iffs or enabledby entry to 'list' + # so that we can add a unique list of requirements to tree[i] + split(requires[opt] iffs[opt] enabledby[opt], r) + for (i in r) list[r[i]] = 1 + for (i in list) tree[opt] = tree[opt] " " i + } + + # print the tree for extreme debugging + if (deb > 2) for (i in tree) if (i != "") print i, "depends-on" tree[i] + + # Ok, now check all options marked explicitly 'on' or 'off': + # + # If an option[opt] is 'on' then turn on all requires[opt] + # If an option[opt] is 'off' then turn off all enabledby[opt] + # + # Error out if we have to turn 'on' to an 'off' option or vice versa. + npending = 0 + for (opt in option) if (opt != "") { + if (option[opt] == "on" || option[opt] == "off") { + pending[++npending] = opt + } + } + + err = 0 # set on error + while (npending > 0) { + opt = pending[npending--] + if (option[opt] == "on") { + nreqs = split(requires[opt], r) + for (j=1; j<=nreqs; ++j) { + if (option[r[j]] == "off") { + print "option", opt, "turned on, but requirement", r[j], "is turned off" + err = 1 + } else if (option[r[j]] != "on") { + option[r[j]] = "on" + pending[++npending] = r[j] + } + } + } else { + if (option[opt] != "off") { + print "internal error (2)" + exit 1 + } + nreqs = split(enabledby[opt], r) + for (j=1; j<=nreqs; ++j) { + if (option[r[j]] == "on") { + print "option", opt, "turned off, but enabled by", r[j], "which is turned on" + err = 1 + } else if (option[r[j]] != "off") { + option[r[j]] = "off" + pending[++npending] = r[j] + } + } + } + } + if (err) exit 1 + + # Sort options: + print "PNG_DFN_START_SORT 2" >out + + # option[i] is now the complete list of all the tokens we may + # need to output, go through it as above, depth first. + finished = 0 + while (!finished) { + finished = 1 + movement = 0 # done nothing + for (i in option) if (!done[i]) { + nreqs = split(tree[i], r) + if (nreqs > 0) { + for (j=1; j<=nreqs; ++j) if (!done[r[j]]) { + break + } + if (j<=nreqs) { + finished = 0 + continue # next option + } + } + + # All the requirements have been processed, output + # this option. An option is _SUPPORTED if: + # + # all 'requires' are _SUPPORTED AND + # at least one of the 'if' options are _SUPPORTED AND + # EITHER: + # The name is _SUPPORTED (on the command line) + # OR: + # an 'enabledby' is _SUPPORTED + # OR: + # NO_name is not defined AND + # the option is not disabled; an option is disabled if: + # option == off + # option == disabled && everything != on + # option == "" && everything == off + if (deb) print "option", i + print "" >out + print "/* option:", i, option[i] >out + print " * requires: " requires[i] >out + print " * if: " iffs[i] >out + print " * enabled-by:" enabledby[i] >out + print " * sets: " sets[i], "*/" >out + print "#undef PNG_on" >out + print "#define PNG_on 1" >out + + # requires + nreqs = split(requires[i], r) + for (j=1; j<=nreqs; ++j) { + print "#ifndef PNG_" r[j] "_SUPPORTED" >out + print "# undef PNG_on /*!" r[j] "*/" >out + # This error appears in the final output if something + # was switched 'on' but the processing above to force + # the requires did not work + if (option[i] == "on") { + print error, i, "requires", r[j] end >out + } + print "#endif" >out + } + + # if + have_ifs = 0 + nreqs = split(iffs[i], r) + print "#undef PNG_no_if" >out + if (nreqs > 0) { + have_ifs = 1 + print "/* if" iffs[i], "*/" >out + print "#define PNG_no_if 1" >out + for (j=1; j<=nreqs; ++j) { + print "#ifdef PNG_" r[j] "_SUPPORTED" >out + print "# undef PNG_no_if /*" r[j] "*/" >out + print "#endif" >out + } + print "#ifdef PNG_no_if /*missing if*/" >out + print "# undef PNG_on" >out + # There is no checking above for this, because we + # don't know which 'if' to choose, so whine about + # it here: + if (option[i] == "on") { + print error, i, "needs one of:", iffs[i] end >out + } + print "#endif" >out + } + + print "#ifdef PNG_on /*requires, if*/" >out + # enables + print "# undef PNG_not_enabled" >out + print "# define PNG_not_enabled 1" >out + print " /* enabled by" enabledby[i], "*/" >out + nreqs = split(enabledby[i], r) + for (j=1; j<=nreqs; ++j) { + print "#ifdef PNG_" r[j] "_SUPPORTED" >out + print "# undef PNG_not_enabled /*" r[j] "*/" >out + # Oops, probably not intended (should be factored + # out by the checks above). + if (option[i] == "off") { + print error, i, "enabled by:", r[j] end >out + } + print "#endif" >out + } + + print "# ifndef PNG_" i "_SUPPORTED /*!command line*/" >out + print "# ifdef PNG_not_enabled /*!enabled*/" >out + # 'have_ifs' here means that everything = "off" still allows an 'if' on + # an otherwise enabled option to turn it on; otherwise the 'if' + # handling is effectively disabled by 'everything = off' + if (option[i] == "off" || option[i] == "disabled" && everything != "on" || option[i] == "enabled" && everything == "off" && !have_ifs) { + print "# undef PNG_on /*default off*/" >out + } else { + print "# ifdef PNG_NO_" i >out + print "# undef PNG_on /*turned off*/" >out + print "# endif" >out + print "# ifdef PNG_NO_" i "_SUPPORTED" >out + print "# undef PNG_on /*turned off*/" >out + print "# endif" >out + } + print "# endif /*!enabled*/" >out + print "# ifdef PNG_on" >out + # The _SUPPORTED macro must be defined so that dependent + # options output later work. + print "# define PNG_" i "_SUPPORTED" >out + print "# endif" >out + print "# endif /*!command line*/" >out + # If PNG_on is still set the option should be defined in + # pnglibconf.h + print "# ifdef PNG_on" >out + if (i ~ /^fail_/) { + print error, i, "is on: enabled by:" iffs[i] enabledby[i] ", requires" requires[i] end >out + } else if (i !~ /^ok_/) { + print def i sup >out + # Supported option, set required settings + nreqs = split(sets[i], r) + for (j=1; j<=nreqs; ++j) { + print "# ifdef PNG_set_" r[j] >out + # Some other option has already set a value: + print error, i, "sets", r[j] ": duplicate setting" end >out + print error, " previous value: " end "PNG_set_" r[j] >out + print "# else" >out + # Else set the default: note that this won't accept arbitrary + # values, the setval string must be acceptable to all the C + # compilers we use. That means it must be VERY simple; a number, + # a name or a string. + print "# define PNG_set_" r[j], setval[i " " r[j]] >out + print "# endif" >out + } + } + print "# endif /* definition */" >out + print "#endif /*requires, if*/" >out + if (logunsupported || i ~ /^ok_/) { + print "#ifndef PNG_on" >out + if (logunsupported) { + print und i une >out + } + if (i ~ /^ok_/) { + print error, i, "not enabled: requires:" requires[i] ", enabled by:" iffs[i] enabledby[i] end >out + } + print "#endif" >out + } + + done[i] = 1 + ++movement + } + + if (!finished && !movement) { + print "option: loop or missing option in dependency tree, cannot process:" + for (i in option) if (!done[i]) { + print " option", i, "depends on" tree[i], "needs:" + nreqs = split(tree[i], r) + if (nreqs > 0) for (j=1; j<=nreqs; ++j) if (!done[r[j]]) { + print " " r[j] + } + } + exit 1 + } + } + print "PNG_DFN_END_SORT" >out + print comment, "end of options", cend >out + + # Do the 'setting' values second, the algorithm the standard + # tree walk (O(1)) done in an O(2) while/for loop; iterations + # settings x depth, outputting the deepest required macros + # first. + print "" >out + print "/* SETTINGS */" >out + print comment, "settings", cend >out + # Sort (in dfn.awk) on field 2, the setting name + print "PNG_DFN_START_SORT 2" >out + finished = 0 + while (!finished) { + finished = 1 + movement = 0 # done nothing + for (i in setting) if (!doneset[i]) { + nreqs = split(setting[i], r) + if (nreqs > 0) { + # By default assume the requires values are options, but if there + # is no option with that name check for a setting + for (j=1; j<=nreqs; ++j) if (option[r[j]] == "" && !doneset[r[j]]) { + break + } + if (j<=nreqs) { + finished = 0 + continue # try a different setting + } + } + + # All the requirements have been processed, output + # this setting. + if (deb) print "setting", i + deflt = defaults[i] + # Remove any spurious trailing spaces + sub(/ *$/,"",deflt) + # A leading @ means leave it unquoted so the preprocessor + # can substitute the build time value + if (deflt ~ /^ @/) + deflt = " " subs substr(deflt, 3) sube + print "" >out + print "/* setting: ", i >out + print " * requires:" setting[i] >out + print " * default: ", defaults[i] deflt, "*/" >out + for (j=1; j<=nreqs; ++j) { + if (option[r[j]] != "") + print "#ifndef PNG_" r[j] "_SUPPORTED" >out + else + print "#ifndef PNG_" r[j] >out + print error, i, "requires", r[j] end >out + print "# endif" >out + } + # The precedence is: + # + # 1) External definition; trumps: + # 2) Option 'sets' value; trumps: + # 3) Setting 'default' + # + print "#ifdef PNG_" i >out + # PNG_ is defined, so substitute the value: + print def i, subs "PNG_" i sube end >out + print "#else /* use default */" >out + print "# ifdef PNG_set_" i >out + # Value from an option 'sets' argument + print def i, subs "PNG_set_" i sube end >out + # This is so that subsequent tests on the setting work: + print "# define PNG_" i, "1" >out + if (defaults[i] != "") { + print "# else /*default*/" >out + print def i deflt end >out + print "# define PNG_" i, "1" >out + } + print "# endif /* defaults */" >out + print "#endif /* setting", i, "*/" >out + + doneset[i] = 1 + ++movement + } + + if (!finished && !movement) { + print "setting: loop or missing setting in 'requires', cannot process:" + for (i in setting) if (!doneset[i]) { + print " setting", i, "requires" setting[i] + } + exit 1 + } + } + print "PNG_DFN_END_SORT" >out + print comment, "end of settings", cend >out + + # Regular end - everything looks ok + if (protect != "") { + print start "#endif", "/*", protect, "*/" end >out + } +} diff --git a/src/png/libpng/scripts/pnglibconf.dfa b/src/png/libpng/scripts/pnglibconf.dfa new file mode 100644 index 0000000000..b298a72f3b --- /dev/null +++ b/src/png/libpng/scripts/pnglibconf.dfa @@ -0,0 +1,919 @@ +# scripts/pnglibconf.dfa - library build configuration control +# +@/*- pnglibconf.dfn intermediate file +@ * generated from scripts/pnglibconf.dfa +@ */ +# +com pnglibconf.h - library build configuration +com +version +com +com Copyright (c) 1998-2017 Glenn Randers-Pehrson +com +com This code is released under the libpng license. +com For conditions of distribution and use, see the disclaimer +com and license in png.h +com + +file pnglibconf.h scripts/pnglibconf.dfa PNGLCONF_H + +# This file is preprocessed by scripts/options.awk and the +# C compiler to generate 'pnglibconf.h' - a list of all the +# configuration options. The file lists the various options +# that can *only* be specified during the libpng build; +# pnglibconf.h freezes the definitions selected for the specific +# build. +# +# The syntax is detailed in scripts/options.awk; this is a summary +# only: +# +# setting [requires ...] [default] +# #define PNG_ /* value comes from current setting */ +# option [requires ...] [if ...] [enables ...] [disabled] +# #define PNG__SUPPORTED if the requirements are met and +# enable the other options listed +# chunk [requires ...] [enables ...] [disabled] +# Enable chunk processing for the given ancillary chunk; any +# 'requires something' expands to READ_something for read and +# WRITE_something for write, but the enables list members are +# used as given (e.g. enables GAMMA just expands to that on the +# correspond READ_name and WRITE_name lines.) +# +# "," may be used to separate options on an 'option' line and is ignored; it +# doesn't change the meaning of the line. (NOT setting, where "," becomes +# part of the setting!) A comma at the end of an option line causes a +# continuation (the next line is included in the option too.) +# +# Note that the 'on' and 'off' keywords, while valid on both option +# and chunk, should not be used in this file because they force the +# relevant options on or off. + +#---------------------------------------------------------------------- + +# The following setting, option and chunk values can all be changed +# while building libpng: +# +# setting: change 'setting' lines to fine tune library performance; +# changes to the settings don't affect the libpng API functionally +# +# option: change 'option' lines to remove or add capabilities from +# or to the library; options change the library API +# +# chunk: change 'chunk' lines to remove capabilities to process +# optional ('ancillary') chunks. This does not prevent PNG +# decoding but does change the libpng API because some chunks +# will be ignored. +# +# There are three ways of disabling features, in no particular order: +# +# 1) Create 'pngusr.h', enter the required private build information +# detailed below and #define PNG_NO_

+ /// Implements the common functionality needed for all s + /// + /// + public abstract class ChecksumGeneratorBase : ChecksumGenerator + { + /// + /// The value of the current checksum + /// + protected uint _current; + + /// + /// Initializes a new instance of the checksum generator base - the current checksum is + /// set to zero + /// + public ChecksumGeneratorBase() + { + _current = 0; + } + + /// + /// Initializes a new instance of the checksum generator basewith a specified value + /// + /// The value to set the current checksum to + public ChecksumGeneratorBase(uint initialValue) + { + _current = initialValue; + } + + /// + /// Resets the current checksum to zero + /// + public void Reset() { _current = 0; } + + /// + /// Gets the current checksum value + /// + public uint Value { get { return _current; } } + + /// + /// Updates the current checksum with part of an array of bytes + /// + /// The data to update the checksum with + /// Where in data to start updating + /// The number of bytes from data to use + /// The sum of offset and count is larger than the length of data + /// data is a null reference + /// Offset or count is negative. + /// All the other Update methods are implmeneted in terms of this one. + /// This is therefore the only method a derived class has to implement + public abstract void Update(byte[] data, int offset, int count); + + /// + /// Updates the current checksum with an array of bytes. + /// + /// The data to update the checksum with + public void Update(byte[] data) + { + Update(data, 0, data.Length); + } + + /// + /// Updates the current checksum with the data from a string + /// + /// The string to update the checksum with + /// The characters in the string are converted by the UTF-8 encoding + public void Update(string data) + { + Update(Encoding.UTF8.GetBytes(data)); + } + + /// + /// Updates the current checksum with the data from a string, using a specific encoding + /// + /// The string to update the checksum with + /// The encoding to use + public void Update(string data, Encoding encoding) + { + Update(encoding.GetBytes(data)); + } + + } + #endregion + + #region CRC32 + /// + /// Implements a CRC32 checksum generator + /// + public sealed class CRC32Checksum : ChecksumGeneratorBase + { + #region DLL imports + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern uint crc32(uint crc, int data, uint length); + + #endregion + + /// + /// Initializes a new instance of the CRC32 checksum generator + /// + public CRC32Checksum() : base() {} + + /// + /// Initializes a new instance of the CRC32 checksum generator with a specified value + /// + /// The value to set the current checksum to + public CRC32Checksum(uint initialValue) : base(initialValue) {} + + /// + /// Updates the current checksum with part of an array of bytes + /// + /// The data to update the checksum with + /// Where in data to start updating + /// The number of bytes from data to use + /// The sum of offset and count is larger than the length of data + /// data is a null reference + /// Offset or count is negative. + public override void Update(byte[] data, int offset, int count) + { + if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); + if ((offset+count) > data.Length) throw new ArgumentException(); + GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned); + try + { + _current = crc32(_current, hData.AddrOfPinnedObject().ToInt32()+offset, (uint)count); + } + finally + { + hData.Free(); + } + } + + } + #endregion + + #region Adler + /// + /// Implements a checksum generator that computes the Adler checksum on data + /// + public sealed class AdlerChecksum : ChecksumGeneratorBase + { + #region DLL imports + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern uint adler32(uint adler, int data, uint length); + + #endregion + + /// + /// Initializes a new instance of the Adler checksum generator + /// + public AdlerChecksum() : base() {} + + /// + /// Initializes a new instance of the Adler checksum generator with a specified value + /// + /// The value to set the current checksum to + public AdlerChecksum(uint initialValue) : base(initialValue) {} + + /// + /// Updates the current checksum with part of an array of bytes + /// + /// The data to update the checksum with + /// Where in data to start updating + /// The number of bytes from data to use + /// The sum of offset and count is larger than the length of data + /// data is a null reference + /// Offset or count is negative. + public override void Update(byte[] data, int offset, int count) + { + if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); + if ((offset+count) > data.Length) throw new ArgumentException(); + GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned); + try + { + _current = adler32(_current, hData.AddrOfPinnedObject().ToInt32()+offset, (uint)count); + } + finally + { + hData.Free(); + } + } + + } + #endregion + +} \ No newline at end of file diff --git a/src/png/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs b/src/png/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs new file mode 100644 index 0000000000..9c8d601954 --- /dev/null +++ b/src/png/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs @@ -0,0 +1,83 @@ +// +// © Copyright Henrik Ravn 2004 +// +// Use, modification and distribution are subject to the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +using System; +using System.Diagnostics; + +namespace DotZLib +{ + + /// + /// This class implements a circular buffer + /// + internal class CircularBuffer + { + #region Private data + private int _capacity; + private int _head; + private int _tail; + private int _size; + private byte[] _buffer; + #endregion + + public CircularBuffer(int capacity) + { + Debug.Assert( capacity > 0 ); + _buffer = new byte[capacity]; + _capacity = capacity; + _head = 0; + _tail = 0; + _size = 0; + } + + public int Size { get { return _size; } } + + public int Put(byte[] source, int offset, int count) + { + Debug.Assert( count > 0 ); + int trueCount = Math.Min(count, _capacity - Size); + for (int i = 0; i < trueCount; ++i) + _buffer[(_tail+i) % _capacity] = source[offset+i]; + _tail += trueCount; + _tail %= _capacity; + _size += trueCount; + return trueCount; + } + + public bool Put(byte b) + { + if (Size == _capacity) // no room + return false; + _buffer[_tail++] = b; + _tail %= _capacity; + ++_size; + return true; + } + + public int Get(byte[] destination, int offset, int count) + { + int trueCount = Math.Min(count,Size); + for (int i = 0; i < trueCount; ++i) + destination[offset + i] = _buffer[(_head+i) % _capacity]; + _head += trueCount; + _head %= _capacity; + _size -= trueCount; + return trueCount; + } + + public int Get() + { + if (Size == 0) + return -1; + + int result = (int)_buffer[_head++ % _capacity]; + --_size; + return result; + } + + } +} diff --git a/src/png/zlib/contrib/dotzlib/DotZLib/CodecBase.cs b/src/png/zlib/contrib/dotzlib/DotZLib/CodecBase.cs new file mode 100644 index 0000000000..b0eb78a022 --- /dev/null +++ b/src/png/zlib/contrib/dotzlib/DotZLib/CodecBase.cs @@ -0,0 +1,198 @@ +// +// © Copyright Henrik Ravn 2004 +// +// Use, modification and distribution are subject to the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +using System; +using System.Runtime.InteropServices; + +namespace DotZLib +{ + /// + /// Implements the common functionality needed for all s + /// + public abstract class CodecBase : Codec, IDisposable + { + + #region Data members + + /// + /// Instance of the internal zlib buffer structure that is + /// passed to all functions in the zlib dll + /// + internal ZStream _ztream = new ZStream(); + + /// + /// True if the object instance has been disposed, false otherwise + /// + protected bool _isDisposed = false; + + /// + /// The size of the internal buffers + /// + protected const int kBufferSize = 16384; + + private byte[] _outBuffer = new byte[kBufferSize]; + private byte[] _inBuffer = new byte[kBufferSize]; + + private GCHandle _hInput; + private GCHandle _hOutput; + + private uint _checksum = 0; + + #endregion + + /// + /// Initializes a new instance of the CodeBase class. + /// + public CodecBase() + { + try + { + _hInput = GCHandle.Alloc(_inBuffer, GCHandleType.Pinned); + _hOutput = GCHandle.Alloc(_outBuffer, GCHandleType.Pinned); + } + catch (Exception) + { + CleanUp(false); + throw; + } + } + + + #region Codec Members + + /// + /// Occurs when more processed data are available. + /// + public event DataAvailableHandler DataAvailable; + + /// + /// Fires the event + /// + protected void OnDataAvailable() + { + if (_ztream.total_out > 0) + { + if (DataAvailable != null) + DataAvailable( _outBuffer, 0, (int)_ztream.total_out); + resetOutput(); + } + } + + /// + /// Adds more data to the codec to be processed. + /// + /// Byte array containing the data to be added to the codec + /// Adding data may, or may not, raise the DataAvailable event + public void Add(byte[] data) + { + Add(data,0,data.Length); + } + + /// + /// Adds more data to the codec to be processed. + /// + /// Byte array containing the data to be added to the codec + /// The index of the first byte to add from data + /// The number of bytes to add + /// Adding data may, or may not, raise the DataAvailable event + /// This must be implemented by a derived class + public abstract void Add(byte[] data, int offset, int count); + + /// + /// Finishes up any pending data that needs to be processed and handled. + /// + /// This must be implemented by a derived class + public abstract void Finish(); + + /// + /// Gets the checksum of the data that has been added so far + /// + public uint Checksum { get { return _checksum; } } + + #endregion + + #region Destructor & IDisposable stuff + + /// + /// Destroys this instance + /// + ~CodecBase() + { + CleanUp(false); + } + + /// + /// Releases any unmanaged resources and calls the method of the derived class + /// + public void Dispose() + { + CleanUp(true); + } + + /// + /// Performs any codec specific cleanup + /// + /// This must be implemented by a derived class + protected abstract void CleanUp(); + + // performs the release of the handles and calls the dereived CleanUp() + private void CleanUp(bool isDisposing) + { + if (!_isDisposed) + { + CleanUp(); + if (_hInput.IsAllocated) + _hInput.Free(); + if (_hOutput.IsAllocated) + _hOutput.Free(); + + _isDisposed = true; + } + } + + + #endregion + + #region Helper methods + + /// + /// Copies a number of bytes to the internal codec buffer - ready for proccesing + /// + /// The byte array that contains the data to copy + /// The index of the first byte to copy + /// The number of bytes to copy from data + protected void copyInput(byte[] data, int startIndex, int count) + { + Array.Copy(data, startIndex, _inBuffer,0, count); + _ztream.next_in = _hInput.AddrOfPinnedObject(); + _ztream.total_in = 0; + _ztream.avail_in = (uint)count; + + } + + /// + /// Resets the internal output buffers to a known state - ready for processing + /// + protected void resetOutput() + { + _ztream.total_out = 0; + _ztream.avail_out = kBufferSize; + _ztream.next_out = _hOutput.AddrOfPinnedObject(); + } + + /// + /// Updates the running checksum property + /// + /// The new checksum value + protected void setChecksum(uint newSum) + { + _checksum = newSum; + } + #endregion + + } +} diff --git a/src/png/zlib/contrib/dotzlib/DotZLib/Deflater.cs b/src/png/zlib/contrib/dotzlib/DotZLib/Deflater.cs new file mode 100644 index 0000000000..9039f41f66 --- /dev/null +++ b/src/png/zlib/contrib/dotzlib/DotZLib/Deflater.cs @@ -0,0 +1,106 @@ +// +// © Copyright Henrik Ravn 2004 +// +// Use, modification and distribution are subject to the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace DotZLib +{ + + /// + /// Implements a data compressor, using the deflate algorithm in the ZLib dll + /// + public sealed class Deflater : CodecBase + { + #region Dll imports + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)] + private static extern int deflateInit_(ref ZStream sz, int level, string vs, int size); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int deflate(ref ZStream sz, int flush); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int deflateReset(ref ZStream sz); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int deflateEnd(ref ZStream sz); + #endregion + + /// + /// Constructs an new instance of the Deflater + /// + /// The compression level to use for this Deflater + public Deflater(CompressLevel level) : base() + { + int retval = deflateInit_(ref _ztream, (int)level, Info.Version, Marshal.SizeOf(_ztream)); + if (retval != 0) + throw new ZLibException(retval, "Could not initialize deflater"); + + resetOutput(); + } + + /// + /// Adds more data to the codec to be processed. + /// + /// Byte array containing the data to be added to the codec + /// The index of the first byte to add from data + /// The number of bytes to add + /// Adding data may, or may not, raise the DataAvailable event + public override void Add(byte[] data, int offset, int count) + { + if (data == null) throw new ArgumentNullException(); + if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); + if ((offset+count) > data.Length) throw new ArgumentException(); + + int total = count; + int inputIndex = offset; + int err = 0; + + while (err >= 0 && inputIndex < total) + { + copyInput(data, inputIndex, Math.Min(total - inputIndex, kBufferSize)); + while (err >= 0 && _ztream.avail_in > 0) + { + err = deflate(ref _ztream, (int)FlushTypes.None); + if (err == 0) + while (_ztream.avail_out == 0) + { + OnDataAvailable(); + err = deflate(ref _ztream, (int)FlushTypes.None); + } + inputIndex += (int)_ztream.total_in; + } + } + setChecksum( _ztream.adler ); + } + + + /// + /// Finishes up any pending data that needs to be processed and handled. + /// + public override void Finish() + { + int err; + do + { + err = deflate(ref _ztream, (int)FlushTypes.Finish); + OnDataAvailable(); + } + while (err == 0); + setChecksum( _ztream.adler ); + deflateReset(ref _ztream); + resetOutput(); + } + + /// + /// Closes the internal zlib deflate stream + /// + protected override void CleanUp() { deflateEnd(ref _ztream); } + + } +} diff --git a/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.cs b/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.cs new file mode 100644 index 0000000000..90c7c3b380 --- /dev/null +++ b/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.cs @@ -0,0 +1,288 @@ +// +// © Copyright Henrik Ravn 2004 +// +// Use, modification and distribution are subject to the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + + +namespace DotZLib +{ + + #region Internal types + + /// + /// Defines constants for the various flush types used with zlib + /// + internal enum FlushTypes + { + None, Partial, Sync, Full, Finish, Block + } + + #region ZStream structure + // internal mapping of the zlib zstream structure for marshalling + [StructLayoutAttribute(LayoutKind.Sequential, Pack=4, Size=0, CharSet=CharSet.Ansi)] + internal struct ZStream + { + public IntPtr next_in; + public uint avail_in; + public uint total_in; + + public IntPtr next_out; + public uint avail_out; + public uint total_out; + + [MarshalAs(UnmanagedType.LPStr)] + string msg; + uint state; + + uint zalloc; + uint zfree; + uint opaque; + + int data_type; + public uint adler; + uint reserved; + } + + #endregion + + #endregion + + #region Public enums + /// + /// Defines constants for the available compression levels in zlib + /// + public enum CompressLevel : int + { + /// + /// The default compression level with a reasonable compromise between compression and speed + /// + Default = -1, + /// + /// No compression at all. The data are passed straight through. + /// + None = 0, + /// + /// The maximum compression rate available. + /// + Best = 9, + /// + /// The fastest available compression level. + /// + Fastest = 1 + } + #endregion + + #region Exception classes + /// + /// The exception that is thrown when an error occurs on the zlib dll + /// + public class ZLibException : ApplicationException + { + /// + /// Initializes a new instance of the class with a specified + /// error message and error code + /// + /// The zlib error code that caused the exception + /// A message that (hopefully) describes the error + public ZLibException(int errorCode, string msg) : base(String.Format("ZLib error {0} {1}", errorCode, msg)) + { + } + + /// + /// Initializes a new instance of the class with a specified + /// error code + /// + /// The zlib error code that caused the exception + public ZLibException(int errorCode) : base(String.Format("ZLib error {0}", errorCode)) + { + } + } + #endregion + + #region Interfaces + + /// + /// Declares methods and properties that enables a running checksum to be calculated + /// + public interface ChecksumGenerator + { + /// + /// Gets the current value of the checksum + /// + uint Value { get; } + + /// + /// Clears the current checksum to 0 + /// + void Reset(); + + /// + /// Updates the current checksum with an array of bytes + /// + /// The data to update the checksum with + void Update(byte[] data); + + /// + /// Updates the current checksum with part of an array of bytes + /// + /// The data to update the checksum with + /// Where in data to start updating + /// The number of bytes from data to use + /// The sum of offset and count is larger than the length of data + /// data is a null reference + /// Offset or count is negative. + void Update(byte[] data, int offset, int count); + + /// + /// Updates the current checksum with the data from a string + /// + /// The string to update the checksum with + /// The characters in the string are converted by the UTF-8 encoding + void Update(string data); + + /// + /// Updates the current checksum with the data from a string, using a specific encoding + /// + /// The string to update the checksum with + /// The encoding to use + void Update(string data, Encoding encoding); + } + + + /// + /// Represents the method that will be called from a codec when new data + /// are available. + /// + /// The byte array containing the processed data + /// The index of the first processed byte in data + /// The number of processed bytes available + /// On return from this method, the data may be overwritten, so grab it while you can. + /// You cannot assume that startIndex will be zero. + /// + public delegate void DataAvailableHandler(byte[] data, int startIndex, int count); + + /// + /// Declares methods and events for implementing compressors/decompressors + /// + public interface Codec + { + /// + /// Occurs when more processed data are available. + /// + event DataAvailableHandler DataAvailable; + + /// + /// Adds more data to the codec to be processed. + /// + /// Byte array containing the data to be added to the codec + /// Adding data may, or may not, raise the DataAvailable event + void Add(byte[] data); + + /// + /// Adds more data to the codec to be processed. + /// + /// Byte array containing the data to be added to the codec + /// The index of the first byte to add from data + /// The number of bytes to add + /// Adding data may, or may not, raise the DataAvailable event + void Add(byte[] data, int offset, int count); + + /// + /// Finishes up any pending data that needs to be processed and handled. + /// + void Finish(); + + /// + /// Gets the checksum of the data that has been added so far + /// + uint Checksum { get; } + + + } + + #endregion + + #region Classes + /// + /// Encapsulates general information about the ZLib library + /// + public class Info + { + #region DLL imports + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern uint zlibCompileFlags(); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern string zlibVersion(); + #endregion + + #region Private stuff + private uint _flags; + + // helper function that unpacks a bitsize mask + private static int bitSize(uint bits) + { + switch (bits) + { + case 0: return 16; + case 1: return 32; + case 2: return 64; + } + return -1; + } + #endregion + + /// + /// Constructs an instance of the Info class. + /// + public Info() + { + _flags = zlibCompileFlags(); + } + + /// + /// True if the library is compiled with debug info + /// + public bool HasDebugInfo { get { return 0 != (_flags & 0x100); } } + + /// + /// True if the library is compiled with assembly optimizations + /// + public bool UsesAssemblyCode { get { return 0 != (_flags & 0x200); } } + + /// + /// Gets the size of the unsigned int that was compiled into Zlib + /// + public int SizeOfUInt { get { return bitSize(_flags & 3); } } + + /// + /// Gets the size of the unsigned long that was compiled into Zlib + /// + public int SizeOfULong { get { return bitSize((_flags >> 2) & 3); } } + + /// + /// Gets the size of the pointers that were compiled into Zlib + /// + public int SizeOfPointer { get { return bitSize((_flags >> 4) & 3); } } + + /// + /// Gets the size of the z_off_t type that was compiled into Zlib + /// + public int SizeOfOffset { get { return bitSize((_flags >> 6) & 3); } } + + /// + /// Gets the version of ZLib as a string, e.g. "1.2.1" + /// + public static string Version { get { return zlibVersion(); } } + } + + #endregion + +} diff --git a/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj b/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj new file mode 100644 index 0000000000..dea7fb16a9 --- /dev/null +++ b/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/png/zlib/contrib/dotzlib/DotZLib/GZipStream.cs b/src/png/zlib/contrib/dotzlib/DotZLib/GZipStream.cs new file mode 100644 index 0000000000..f0eada1d24 --- /dev/null +++ b/src/png/zlib/contrib/dotzlib/DotZLib/GZipStream.cs @@ -0,0 +1,301 @@ +// +// © Copyright Henrik Ravn 2004 +// +// Use, modification and distribution are subject to the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace DotZLib +{ + /// + /// Implements a compressed , in GZip (.gz) format. + /// + public class GZipStream : Stream, IDisposable + { + #region Dll Imports + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)] + private static extern IntPtr gzopen(string name, string mode); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int gzclose(IntPtr gzFile); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int gzwrite(IntPtr gzFile, int data, int length); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int gzread(IntPtr gzFile, int data, int length); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int gzgetc(IntPtr gzFile); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int gzputc(IntPtr gzFile, int c); + + #endregion + + #region Private data + private IntPtr _gzFile; + private bool _isDisposed = false; + private bool _isWriting; + #endregion + + #region Constructors + /// + /// Creates a new file as a writeable GZipStream + /// + /// The name of the compressed file to create + /// The compression level to use when adding data + /// If an error occurred in the internal zlib function + public GZipStream(string fileName, CompressLevel level) + { + _isWriting = true; + _gzFile = gzopen(fileName, String.Format("wb{0}", (int)level)); + if (_gzFile == IntPtr.Zero) + throw new ZLibException(-1, "Could not open " + fileName); + } + + /// + /// Opens an existing file as a readable GZipStream + /// + /// The name of the file to open + /// If an error occurred in the internal zlib function + public GZipStream(string fileName) + { + _isWriting = false; + _gzFile = gzopen(fileName, "rb"); + if (_gzFile == IntPtr.Zero) + throw new ZLibException(-1, "Could not open " + fileName); + + } + #endregion + + #region Access properties + /// + /// Returns true of this stream can be read from, false otherwise + /// + public override bool CanRead + { + get + { + return !_isWriting; + } + } + + + /// + /// Returns false. + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Returns true if this tsream is writeable, false otherwise + /// + public override bool CanWrite + { + get + { + return _isWriting; + } + } + #endregion + + #region Destructor & IDispose stuff + + /// + /// Destroys this instance + /// + ~GZipStream() + { + cleanUp(false); + } + + /// + /// Closes the external file handle + /// + public void Dispose() + { + cleanUp(true); + } + + // Does the actual closing of the file handle. + private void cleanUp(bool isDisposing) + { + if (!_isDisposed) + { + gzclose(_gzFile); + _isDisposed = true; + } + } + #endregion + + #region Basic reading and writing + /// + /// Attempts to read a number of bytes from the stream. + /// + /// The destination data buffer + /// The index of the first destination byte in buffer + /// The number of bytes requested + /// The number of bytes read + /// If buffer is null + /// If count or offset are negative + /// If offset + count is > buffer.Length + /// If this stream is not readable. + /// If this stream has been disposed. + public override int Read(byte[] buffer, int offset, int count) + { + if (!CanRead) throw new NotSupportedException(); + if (buffer == null) throw new ArgumentNullException(); + if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); + if ((offset+count) > buffer.Length) throw new ArgumentException(); + if (_isDisposed) throw new ObjectDisposedException("GZipStream"); + + GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned); + int result; + try + { + result = gzread(_gzFile, h.AddrOfPinnedObject().ToInt32() + offset, count); + if (result < 0) + throw new IOException(); + } + finally + { + h.Free(); + } + return result; + } + + /// + /// Attempts to read a single byte from the stream. + /// + /// The byte that was read, or -1 in case of error or End-Of-File + public override int ReadByte() + { + if (!CanRead) throw new NotSupportedException(); + if (_isDisposed) throw new ObjectDisposedException("GZipStream"); + return gzgetc(_gzFile); + } + + /// + /// Writes a number of bytes to the stream + /// + /// + /// + /// + /// If buffer is null + /// If count or offset are negative + /// If offset + count is > buffer.Length + /// If this stream is not writeable. + /// If this stream has been disposed. + public override void Write(byte[] buffer, int offset, int count) + { + if (!CanWrite) throw new NotSupportedException(); + if (buffer == null) throw new ArgumentNullException(); + if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); + if ((offset+count) > buffer.Length) throw new ArgumentException(); + if (_isDisposed) throw new ObjectDisposedException("GZipStream"); + + GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned); + try + { + int result = gzwrite(_gzFile, h.AddrOfPinnedObject().ToInt32() + offset, count); + if (result < 0) + throw new IOException(); + } + finally + { + h.Free(); + } + } + + /// + /// Writes a single byte to the stream + /// + /// The byte to add to the stream. + /// If this stream is not writeable. + /// If this stream has been disposed. + public override void WriteByte(byte value) + { + if (!CanWrite) throw new NotSupportedException(); + if (_isDisposed) throw new ObjectDisposedException("GZipStream"); + + int result = gzputc(_gzFile, (int)value); + if (result < 0) + throw new IOException(); + } + #endregion + + #region Position & length stuff + /// + /// Not supported. + /// + /// + /// Always thrown + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// Not suppported. + /// + /// + /// + /// + /// Always thrown + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// + /// Flushes the GZipStream. + /// + /// In this implementation, this method does nothing. This is because excessive + /// flushing may degrade the achievable compression rates. + public override void Flush() + { + // left empty on purpose + } + + /// + /// Gets/sets the current position in the GZipStream. Not suppported. + /// + /// In this implementation this property is not supported + /// Always thrown + public override long Position + { + get + { + throw new NotSupportedException(); + } + set + { + throw new NotSupportedException(); + } + } + + /// + /// Gets the size of the stream. Not suppported. + /// + /// In this implementation this property is not supported + /// Always thrown + public override long Length + { + get + { + throw new NotSupportedException(); + } + } + #endregion + } +} diff --git a/src/png/zlib/contrib/dotzlib/DotZLib/Inflater.cs b/src/png/zlib/contrib/dotzlib/DotZLib/Inflater.cs new file mode 100644 index 0000000000..d295f26804 --- /dev/null +++ b/src/png/zlib/contrib/dotzlib/DotZLib/Inflater.cs @@ -0,0 +1,105 @@ +// +// © Copyright Henrik Ravn 2004 +// +// Use, modification and distribution are subject to the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace DotZLib +{ + + /// + /// Implements a data decompressor, using the inflate algorithm in the ZLib dll + /// + public class Inflater : CodecBase + { + #region Dll imports + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)] + private static extern int inflateInit_(ref ZStream sz, string vs, int size); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int inflate(ref ZStream sz, int flush); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int inflateReset(ref ZStream sz); + + [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] + private static extern int inflateEnd(ref ZStream sz); + #endregion + + /// + /// Constructs an new instance of the Inflater + /// + public Inflater() : base() + { + int retval = inflateInit_(ref _ztream, Info.Version, Marshal.SizeOf(_ztream)); + if (retval != 0) + throw new ZLibException(retval, "Could not initialize inflater"); + + resetOutput(); + } + + + /// + /// Adds more data to the codec to be processed. + /// + /// Byte array containing the data to be added to the codec + /// The index of the first byte to add from data + /// The number of bytes to add + /// Adding data may, or may not, raise the DataAvailable event + public override void Add(byte[] data, int offset, int count) + { + if (data == null) throw new ArgumentNullException(); + if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); + if ((offset+count) > data.Length) throw new ArgumentException(); + + int total = count; + int inputIndex = offset; + int err = 0; + + while (err >= 0 && inputIndex < total) + { + copyInput(data, inputIndex, Math.Min(total - inputIndex, kBufferSize)); + err = inflate(ref _ztream, (int)FlushTypes.None); + if (err == 0) + while (_ztream.avail_out == 0) + { + OnDataAvailable(); + err = inflate(ref _ztream, (int)FlushTypes.None); + } + + inputIndex += (int)_ztream.total_in; + } + setChecksum( _ztream.adler ); + } + + + /// + /// Finishes up any pending data that needs to be processed and handled. + /// + public override void Finish() + { + int err; + do + { + err = inflate(ref _ztream, (int)FlushTypes.Finish); + OnDataAvailable(); + } + while (err == 0); + setChecksum( _ztream.adler ); + inflateReset(ref _ztream); + resetOutput(); + } + + /// + /// Closes the internal zlib inflate stream + /// + protected override void CleanUp() { inflateEnd(ref _ztream); } + + + } +} diff --git a/src/png/zlib/contrib/dotzlib/DotZLib/UnitTests.cs b/src/png/zlib/contrib/dotzlib/DotZLib/UnitTests.cs new file mode 100644 index 0000000000..6d8aebb799 --- /dev/null +++ b/src/png/zlib/contrib/dotzlib/DotZLib/UnitTests.cs @@ -0,0 +1,274 @@ +// +// © Copyright Henrik Ravn 2004 +// +// Use, modification and distribution are subject to the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +using System; +using System.Collections; +using System.IO; + +// uncomment the define below to include unit tests +//#define nunit +#if nunit +using NUnit.Framework; + +// Unit tests for the DotZLib class library +// ---------------------------------------- +// +// Use this with NUnit 2 from http://www.nunit.org +// + +namespace DotZLibTests +{ + using DotZLib; + + // helper methods + internal class Utils + { + public static bool byteArrEqual( byte[] lhs, byte[] rhs ) + { + if (lhs.Length != rhs.Length) + return false; + for (int i = lhs.Length-1; i >= 0; --i) + if (lhs[i] != rhs[i]) + return false; + return true; + } + + } + + + [TestFixture] + public class CircBufferTests + { + #region Circular buffer tests + [Test] + public void SinglePutGet() + { + CircularBuffer buf = new CircularBuffer(10); + Assert.AreEqual( 0, buf.Size ); + Assert.AreEqual( -1, buf.Get() ); + + Assert.IsTrue(buf.Put( 1 )); + Assert.AreEqual( 1, buf.Size ); + Assert.AreEqual( 1, buf.Get() ); + Assert.AreEqual( 0, buf.Size ); + Assert.AreEqual( -1, buf.Get() ); + } + + [Test] + public void BlockPutGet() + { + CircularBuffer buf = new CircularBuffer(10); + byte[] arr = {1,2,3,4,5,6,7,8,9,10}; + Assert.AreEqual( 10, buf.Put(arr,0,10) ); + Assert.AreEqual( 10, buf.Size ); + Assert.IsFalse( buf.Put(11) ); + Assert.AreEqual( 1, buf.Get() ); + Assert.IsTrue( buf.Put(11) ); + + byte[] arr2 = (byte[])arr.Clone(); + Assert.AreEqual( 9, buf.Get(arr2,1,9) ); + Assert.IsTrue( Utils.byteArrEqual(arr,arr2) ); + } + + #endregion + } + + [TestFixture] + public class ChecksumTests + { + #region CRC32 Tests + [Test] + public void CRC32_Null() + { + CRC32Checksum crc32 = new CRC32Checksum(); + Assert.AreEqual( 0, crc32.Value ); + + crc32 = new CRC32Checksum(1); + Assert.AreEqual( 1, crc32.Value ); + + crc32 = new CRC32Checksum(556); + Assert.AreEqual( 556, crc32.Value ); + } + + [Test] + public void CRC32_Data() + { + CRC32Checksum crc32 = new CRC32Checksum(); + byte[] data = { 1,2,3,4,5,6,7 }; + crc32.Update(data); + Assert.AreEqual( 0x70e46888, crc32.Value ); + + crc32 = new CRC32Checksum(); + crc32.Update("penguin"); + Assert.AreEqual( 0x0e5c1a120, crc32.Value ); + + crc32 = new CRC32Checksum(1); + crc32.Update("penguin"); + Assert.AreEqual(0x43b6aa94, crc32.Value); + + } + #endregion + + #region Adler tests + + [Test] + public void Adler_Null() + { + AdlerChecksum adler = new AdlerChecksum(); + Assert.AreEqual(0, adler.Value); + + adler = new AdlerChecksum(1); + Assert.AreEqual( 1, adler.Value ); + + adler = new AdlerChecksum(556); + Assert.AreEqual( 556, adler.Value ); + } + + [Test] + public void Adler_Data() + { + AdlerChecksum adler = new AdlerChecksum(1); + byte[] data = { 1,2,3,4,5,6,7 }; + adler.Update(data); + Assert.AreEqual( 0x5b001d, adler.Value ); + + adler = new AdlerChecksum(); + adler.Update("penguin"); + Assert.AreEqual(0x0bcf02f6, adler.Value ); + + adler = new AdlerChecksum(1); + adler.Update("penguin"); + Assert.AreEqual(0x0bd602f7, adler.Value); + + } + #endregion + } + + [TestFixture] + public class InfoTests + { + #region Info tests + [Test] + public void Info_Version() + { + Info info = new Info(); + Assert.AreEqual("1.2.11", Info.Version); + Assert.AreEqual(32, info.SizeOfUInt); + Assert.AreEqual(32, info.SizeOfULong); + Assert.AreEqual(32, info.SizeOfPointer); + Assert.AreEqual(32, info.SizeOfOffset); + } + #endregion + } + + [TestFixture] + public class DeflateInflateTests + { + #region Deflate tests + [Test] + public void Deflate_Init() + { + using (Deflater def = new Deflater(CompressLevel.Default)) + { + } + } + + private ArrayList compressedData = new ArrayList(); + private uint adler1; + + private ArrayList uncompressedData = new ArrayList(); + private uint adler2; + + public void CDataAvail(byte[] data, int startIndex, int count) + { + for (int i = 0; i < count; ++i) + compressedData.Add(data[i+startIndex]); + } + + [Test] + public void Deflate_Compress() + { + compressedData.Clear(); + + byte[] testData = new byte[35000]; + for (int i = 0; i < testData.Length; ++i) + testData[i] = 5; + + using (Deflater def = new Deflater((CompressLevel)5)) + { + def.DataAvailable += new DataAvailableHandler(CDataAvail); + def.Add(testData); + def.Finish(); + adler1 = def.Checksum; + } + } + #endregion + + #region Inflate tests + [Test] + public void Inflate_Init() + { + using (Inflater inf = new Inflater()) + { + } + } + + private void DDataAvail(byte[] data, int startIndex, int count) + { + for (int i = 0; i < count; ++i) + uncompressedData.Add(data[i+startIndex]); + } + + [Test] + public void Inflate_Expand() + { + uncompressedData.Clear(); + + using (Inflater inf = new Inflater()) + { + inf.DataAvailable += new DataAvailableHandler(DDataAvail); + inf.Add((byte[])compressedData.ToArray(typeof(byte))); + inf.Finish(); + adler2 = inf.Checksum; + } + Assert.AreEqual( adler1, adler2 ); + } + #endregion + } + + [TestFixture] + public class GZipStreamTests + { + #region GZipStream test + [Test] + public void GZipStream_WriteRead() + { + using (GZipStream gzOut = new GZipStream("gzstream.gz", CompressLevel.Best)) + { + BinaryWriter writer = new BinaryWriter(gzOut); + writer.Write("hi there"); + writer.Write(Math.PI); + writer.Write(42); + } + + using (GZipStream gzIn = new GZipStream("gzstream.gz")) + { + BinaryReader reader = new BinaryReader(gzIn); + string s = reader.ReadString(); + Assert.AreEqual("hi there",s); + double d = reader.ReadDouble(); + Assert.AreEqual(Math.PI, d); + int i = reader.ReadInt32(); + Assert.AreEqual(42,i); + } + + } + #endregion + } +} + +#endif diff --git a/src/png/zlib/contrib/dotzlib/LICENSE_1_0.txt b/src/png/zlib/contrib/dotzlib/LICENSE_1_0.txt new file mode 100644 index 0000000000..127a5bc39b --- /dev/null +++ b/src/png/zlib/contrib/dotzlib/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/src/png/zlib/contrib/dotzlib/readme.txt b/src/png/zlib/contrib/dotzlib/readme.txt new file mode 100644 index 0000000000..4d8c2dd932 --- /dev/null +++ b/src/png/zlib/contrib/dotzlib/readme.txt @@ -0,0 +1,58 @@ +This directory contains a .Net wrapper class library for the ZLib1.dll + +The wrapper includes support for inflating/deflating memory buffers, +.Net streaming wrappers for the gz streams part of zlib, and wrappers +for the checksum parts of zlib. See DotZLib/UnitTests.cs for examples. + +Directory structure: +-------------------- + +LICENSE_1_0.txt - License file. +readme.txt - This file. +DotZLib.chm - Class library documentation +DotZLib.build - NAnt build file +DotZLib.sln - Microsoft Visual Studio 2003 solution file + +DotZLib\*.cs - Source files for the class library + +Unit tests: +----------- +The file DotZLib/UnitTests.cs contains unit tests for use with NUnit 2.1 or higher. +To include unit tests in the build, define nunit before building. + + +Build instructions: +------------------- + +1. Using Visual Studio.Net 2003: + Open DotZLib.sln in VS.Net and build from there. Output file (DotZLib.dll) + will be found ./DotZLib/bin/release or ./DotZLib/bin/debug, depending on + you are building the release or debug version of the library. Check + DotZLib/UnitTests.cs for instructions on how to include unit tests in the + build. + +2. Using NAnt: + Open a command prompt with access to the build environment and run nant + in the same directory as the DotZLib.build file. + You can define 2 properties on the nant command-line to control the build: + debug={true|false} to toggle between release/debug builds (default=true). + nunit={true|false} to include or esclude unit tests (default=true). + Also the target clean will remove binaries. + Output file (DotZLib.dll) will be found in either ./DotZLib/bin/release + or ./DotZLib/bin/debug, depending on whether you are building the release + or debug version of the library. + + Examples: + nant -D:debug=false -D:nunit=false + will build a release mode version of the library without unit tests. + nant + will build a debug version of the library with unit tests + nant clean + will remove all previously built files. + + +--------------------------------- +Copyright (c) Henrik Ravn 2004 + +Use, modification and distribution are subject to the Boost Software License, Version 1.0. +(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/src/png/zlib/contrib/gcc_gvmat64/gvmat64.S b/src/png/zlib/contrib/gcc_gvmat64/gvmat64.S new file mode 100644 index 0000000000..23309fa286 --- /dev/null +++ b/src/png/zlib/contrib/gcc_gvmat64/gvmat64.S @@ -0,0 +1,574 @@ +/* +;uInt longest_match_x64( +; deflate_state *s, +; IPos cur_match); // current match + +; gvmat64.S -- Asm portion of the optimized longest_match for 32 bits x86_64 +; (AMD64 on Athlon 64, Opteron, Phenom +; and Intel EM64T on Pentium 4 with EM64T, Pentium D, Core 2 Duo, Core I5/I7) +; this file is translation from gvmat64.asm to GCC 4.x (for Linux, Mac XCode) +; Copyright (C) 1995-2010 Jean-loup Gailly, Brian Raiter and Gilles Vollant. +; +; File written by Gilles Vollant, by converting to assembly the longest_match +; from Jean-loup Gailly in deflate.c of zLib and infoZip zip. +; and by taking inspiration on asm686 with masm, optimised assembly code +; from Brian Raiter, written 1998 +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software +; 3. This notice may not be removed or altered from any source distribution. +; +; http://www.zlib.net +; http://www.winimage.com/zLibDll +; http://www.muppetlabs.com/~breadbox/software/assembly.html +; +; to compile this file for zLib, I use option: +; gcc -c -arch x86_64 gvmat64.S + + +;uInt longest_match(s, cur_match) +; deflate_state *s; +; IPos cur_match; // current match / +; +; with XCode for Mac, I had strange error with some jump on intel syntax +; this is why BEFORE_JMP and AFTER_JMP are used + */ + + +#define BEFORE_JMP .att_syntax +#define AFTER_JMP .intel_syntax noprefix + +#ifndef NO_UNDERLINE +# define match_init _match_init +# define longest_match _longest_match +#endif + +.intel_syntax noprefix + +.globl match_init, longest_match +.text +longest_match: + + + +#define LocalVarsSize 96 +/* +; register used : rax,rbx,rcx,rdx,rsi,rdi,r8,r9,r10,r11,r12 +; free register : r14,r15 +; register can be saved : rsp +*/ + +#define chainlenwmask (rsp + 8 - LocalVarsSize) +#define nicematch (rsp + 16 - LocalVarsSize) + +#define save_rdi (rsp + 24 - LocalVarsSize) +#define save_rsi (rsp + 32 - LocalVarsSize) +#define save_rbx (rsp + 40 - LocalVarsSize) +#define save_rbp (rsp + 48 - LocalVarsSize) +#define save_r12 (rsp + 56 - LocalVarsSize) +#define save_r13 (rsp + 64 - LocalVarsSize) +#define save_r14 (rsp + 72 - LocalVarsSize) +#define save_r15 (rsp + 80 - LocalVarsSize) + + +/* +; all the +4 offsets are due to the addition of pending_buf_size (in zlib +; in the deflate_state structure since the asm code was first written +; (if you compile with zlib 1.0.4 or older, remove the +4). +; Note : these value are good with a 8 bytes boundary pack structure +*/ + +#define MAX_MATCH 258 +#define MIN_MATCH 3 +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) + +/* +;;; Offsets for fields in the deflate_state structure. These numbers +;;; are calculated from the definition of deflate_state, with the +;;; assumption that the compiler will dword-align the fields. (Thus, +;;; changing the definition of deflate_state could easily cause this +;;; program to crash horribly, without so much as a warning at +;;; compile time. Sigh.) + +; all the +zlib1222add offsets are due to the addition of fields +; in zlib in the deflate_state structure since the asm code was first written +; (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)"). +; (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0"). +; if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8"). +*/ + + + +/* you can check the structure offset by running + +#include +#include +#include "deflate.h" + +void print_depl() +{ +deflate_state ds; +deflate_state *s=&ds; +printf("size pointer=%u\n",(int)sizeof(void*)); + +printf("#define dsWSize %u\n",(int)(((char*)&(s->w_size))-((char*)s))); +printf("#define dsWMask %u\n",(int)(((char*)&(s->w_mask))-((char*)s))); +printf("#define dsWindow %u\n",(int)(((char*)&(s->window))-((char*)s))); +printf("#define dsPrev %u\n",(int)(((char*)&(s->prev))-((char*)s))); +printf("#define dsMatchLen %u\n",(int)(((char*)&(s->match_length))-((char*)s))); +printf("#define dsPrevMatch %u\n",(int)(((char*)&(s->prev_match))-((char*)s))); +printf("#define dsStrStart %u\n",(int)(((char*)&(s->strstart))-((char*)s))); +printf("#define dsMatchStart %u\n",(int)(((char*)&(s->match_start))-((char*)s))); +printf("#define dsLookahead %u\n",(int)(((char*)&(s->lookahead))-((char*)s))); +printf("#define dsPrevLen %u\n",(int)(((char*)&(s->prev_length))-((char*)s))); +printf("#define dsMaxChainLen %u\n",(int)(((char*)&(s->max_chain_length))-((char*)s))); +printf("#define dsGoodMatch %u\n",(int)(((char*)&(s->good_match))-((char*)s))); +printf("#define dsNiceMatch %u\n",(int)(((char*)&(s->nice_match))-((char*)s))); +} +*/ + +#define dsWSize 68 +#define dsWMask 76 +#define dsWindow 80 +#define dsPrev 96 +#define dsMatchLen 144 +#define dsPrevMatch 148 +#define dsStrStart 156 +#define dsMatchStart 160 +#define dsLookahead 164 +#define dsPrevLen 168 +#define dsMaxChainLen 172 +#define dsGoodMatch 188 +#define dsNiceMatch 192 + +#define window_size [ rcx + dsWSize] +#define WMask [ rcx + dsWMask] +#define window_ad [ rcx + dsWindow] +#define prev_ad [ rcx + dsPrev] +#define strstart [ rcx + dsStrStart] +#define match_start [ rcx + dsMatchStart] +#define Lookahead [ rcx + dsLookahead] //; 0ffffffffh on infozip +#define prev_length [ rcx + dsPrevLen] +#define max_chain_length [ rcx + dsMaxChainLen] +#define good_match [ rcx + dsGoodMatch] +#define nice_match [ rcx + dsNiceMatch] + +/* +; windows: +; parameter 1 in rcx(deflate state s), param 2 in rdx (cur match) + +; see http://weblogs.asp.net/oldnewthing/archive/2004/01/14/58579.aspx and +; http://msdn.microsoft.com/library/en-us/kmarch/hh/kmarch/64bitAMD_8e951dd2-ee77-4728-8702-55ce4b5dd24a.xml.asp +; +; All registers must be preserved across the call, except for +; rax, rcx, rdx, r8, r9, r10, and r11, which are scratch. + +; +; gcc on macosx-linux: +; see http://www.x86-64.org/documentation/abi-0.99.pdf +; param 1 in rdi, param 2 in rsi +; rbx, rsp, rbp, r12 to r15 must be preserved + +;;; Save registers that the compiler may be using, and adjust esp to +;;; make room for our stack frame. + + +;;; Retrieve the function arguments. r8d will hold cur_match +;;; throughout the entire function. edx will hold the pointer to the +;;; deflate_state structure during the function's setup (before +;;; entering the main loop. + +; ms: parameter 1 in rcx (deflate_state* s), param 2 in edx -> r8 (cur match) +; mac: param 1 in rdi, param 2 rsi +; this clear high 32 bits of r8, which can be garbage in both r8 and rdx +*/ + mov [save_rbx],rbx + mov [save_rbp],rbp + + + mov rcx,rdi + + mov r8d,esi + + + mov [save_r12],r12 + mov [save_r13],r13 + mov [save_r14],r14 + mov [save_r15],r15 + + +//;;; uInt wmask = s->w_mask; +//;;; unsigned chain_length = s->max_chain_length; +//;;; if (s->prev_length >= s->good_match) { +//;;; chain_length >>= 2; +//;;; } + + + mov edi, prev_length + mov esi, good_match + mov eax, WMask + mov ebx, max_chain_length + cmp edi, esi + jl LastMatchGood + shr ebx, 2 +LastMatchGood: + +//;;; chainlen is decremented once beforehand so that the function can +//;;; use the sign flag instead of the zero flag for the exit test. +//;;; It is then shifted into the high word, to make room for the wmask +//;;; value, which it will always accompany. + + dec ebx + shl ebx, 16 + or ebx, eax + +//;;; on zlib only +//;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + + + mov eax, nice_match + mov [chainlenwmask], ebx + mov r10d, Lookahead + cmp r10d, eax + cmovnl r10d, eax + mov [nicematch],r10d + + + +//;;; register Bytef *scan = s->window + s->strstart; + mov r10, window_ad + mov ebp, strstart + lea r13, [r10 + rbp] + +//;;; Determine how many bytes the scan ptr is off from being +//;;; dword-aligned. + + mov r9,r13 + neg r13 + and r13,3 + +//;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ? +//;;; s->strstart - (IPos)MAX_DIST(s) : NIL; + + + mov eax, window_size + sub eax, MIN_LOOKAHEAD + + + xor edi,edi + sub ebp, eax + + mov r11d, prev_length + + cmovng ebp,edi + +//;;; int best_len = s->prev_length; + + +//;;; Store the sum of s->window + best_len in esi locally, and in esi. + + lea rsi,[r10+r11] + +//;;; register ush scan_start = *(ushf*)scan; +//;;; register ush scan_end = *(ushf*)(scan+best_len-1); +//;;; Posf *prev = s->prev; + + movzx r12d,word ptr [r9] + movzx ebx, word ptr [r9 + r11 - 1] + + mov rdi, prev_ad + +//;;; Jump into the main loop. + + mov edx, [chainlenwmask] + + cmp bx,word ptr [rsi + r8 - 1] + jz LookupLoopIsZero + + + +LookupLoop1: + and r8d, edx + + movzx r8d, word ptr [rdi + r8*2] + cmp r8d, ebp + jbe LeaveNow + + + + sub edx, 0x00010000 + BEFORE_JMP + js LeaveNow + AFTER_JMP + +LoopEntry1: + cmp bx,word ptr [rsi + r8 - 1] + BEFORE_JMP + jz LookupLoopIsZero + AFTER_JMP + +LookupLoop2: + and r8d, edx + + movzx r8d, word ptr [rdi + r8*2] + cmp r8d, ebp + BEFORE_JMP + jbe LeaveNow + AFTER_JMP + sub edx, 0x00010000 + BEFORE_JMP + js LeaveNow + AFTER_JMP + +LoopEntry2: + cmp bx,word ptr [rsi + r8 - 1] + BEFORE_JMP + jz LookupLoopIsZero + AFTER_JMP + +LookupLoop4: + and r8d, edx + + movzx r8d, word ptr [rdi + r8*2] + cmp r8d, ebp + BEFORE_JMP + jbe LeaveNow + AFTER_JMP + sub edx, 0x00010000 + BEFORE_JMP + js LeaveNow + AFTER_JMP + +LoopEntry4: + + cmp bx,word ptr [rsi + r8 - 1] + BEFORE_JMP + jnz LookupLoop1 + jmp LookupLoopIsZero + AFTER_JMP +/* +;;; do { +;;; match = s->window + cur_match; +;;; if (*(ushf*)(match+best_len-1) != scan_end || +;;; *(ushf*)match != scan_start) continue; +;;; [...] +;;; } while ((cur_match = prev[cur_match & wmask]) > limit +;;; && --chain_length != 0); +;;; +;;; Here is the inner loop of the function. The function will spend the +;;; majority of its time in this loop, and majority of that time will +;;; be spent in the first ten instructions. +;;; +;;; Within this loop: +;;; ebx = scanend +;;; r8d = curmatch +;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask) +;;; esi = windowbestlen - i.e., (window + bestlen) +;;; edi = prev +;;; ebp = limit +*/ +.balign 16 +LookupLoop: + and r8d, edx + + movzx r8d, word ptr [rdi + r8*2] + cmp r8d, ebp + BEFORE_JMP + jbe LeaveNow + AFTER_JMP + sub edx, 0x00010000 + BEFORE_JMP + js LeaveNow + AFTER_JMP + +LoopEntry: + + cmp bx,word ptr [rsi + r8 - 1] + BEFORE_JMP + jnz LookupLoop1 + AFTER_JMP +LookupLoopIsZero: + cmp r12w, word ptr [r10 + r8] + BEFORE_JMP + jnz LookupLoop1 + AFTER_JMP + + +//;;; Store the current value of chainlen. + mov [chainlenwmask], edx +/* +;;; Point edi to the string under scrutiny, and esi to the string we +;;; are hoping to match it up with. In actuality, esi and edi are +;;; both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and edx is +;;; initialized to -(MAX_MATCH_8 - scanalign). +*/ + lea rsi,[r8+r10] + mov rdx, 0xfffffffffffffef8 //; -(MAX_MATCH_8) + lea rsi, [rsi + r13 + 0x0108] //;MAX_MATCH_8] + lea rdi, [r9 + r13 + 0x0108] //;MAX_MATCH_8] + + prefetcht1 [rsi+rdx] + prefetcht1 [rdi+rdx] + +/* +;;; Test the strings for equality, 8 bytes at a time. At the end, +;;; adjust rdx so that it is offset to the exact byte that mismatched. +;;; +;;; We already know at this point that the first three bytes of the +;;; strings match each other, and they can be safely passed over before +;;; starting the compare loop. So what this code does is skip over 0-3 +;;; bytes, as much as necessary in order to dword-align the edi +;;; pointer. (rsi will still be misaligned three times out of four.) +;;; +;;; It should be confessed that this loop usually does not represent +;;; much of the total running time. Replacing it with a more +;;; straightforward "rep cmpsb" would not drastically degrade +;;; performance. +*/ + +LoopCmps: + mov rax, [rsi + rdx] + xor rax, [rdi + rdx] + jnz LeaveLoopCmps + + mov rax, [rsi + rdx + 8] + xor rax, [rdi + rdx + 8] + jnz LeaveLoopCmps8 + + + mov rax, [rsi + rdx + 8+8] + xor rax, [rdi + rdx + 8+8] + jnz LeaveLoopCmps16 + + add rdx,8+8+8 + + BEFORE_JMP + jnz LoopCmps + jmp LenMaximum + AFTER_JMP + +LeaveLoopCmps16: add rdx,8 +LeaveLoopCmps8: add rdx,8 +LeaveLoopCmps: + + test eax, 0x0000FFFF + jnz LenLower + + test eax,0xffffffff + + jnz LenLower32 + + add rdx,4 + shr rax,32 + or ax,ax + BEFORE_JMP + jnz LenLower + AFTER_JMP + +LenLower32: + shr eax,16 + add rdx,2 + +LenLower: + sub al, 1 + adc rdx, 0 +//;;; Calculate the length of the match. If it is longer than MAX_MATCH, +//;;; then automatically accept it as the best possible match and leave. + + lea rax, [rdi + rdx] + sub rax, r9 + cmp eax, MAX_MATCH + BEFORE_JMP + jge LenMaximum + AFTER_JMP +/* +;;; If the length of the match is not longer than the best match we +;;; have so far, then forget it and return to the lookup loop. +;/////////////////////////////////// +*/ + cmp eax, r11d + jg LongerMatch + + lea rsi,[r10+r11] + + mov rdi, prev_ad + mov edx, [chainlenwmask] + BEFORE_JMP + jmp LookupLoop + AFTER_JMP +/* +;;; s->match_start = cur_match; +;;; best_len = len; +;;; if (len >= nice_match) break; +;;; scan_end = *(ushf*)(scan+best_len-1); +*/ +LongerMatch: + mov r11d, eax + mov match_start, r8d + cmp eax, [nicematch] + BEFORE_JMP + jge LeaveNow + AFTER_JMP + + lea rsi,[r10+rax] + + movzx ebx, word ptr [r9 + rax - 1] + mov rdi, prev_ad + mov edx, [chainlenwmask] + BEFORE_JMP + jmp LookupLoop + AFTER_JMP + +//;;; Accept the current string, with the maximum possible length. + +LenMaximum: + mov r11d,MAX_MATCH + mov match_start, r8d + +//;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len; +//;;; return s->lookahead; + +LeaveNow: + mov eax, Lookahead + cmp r11d, eax + cmovng eax, r11d + + + +//;;; Restore the stack and return from whence we came. + + +// mov rsi,[save_rsi] +// mov rdi,[save_rdi] + mov rbx,[save_rbx] + mov rbp,[save_rbp] + mov r12,[save_r12] + mov r13,[save_r13] + mov r14,[save_r14] + mov r15,[save_r15] + + + ret 0 +//; please don't remove this string ! +//; Your can freely use gvmat64 in any free or commercial app +//; but it is far better don't remove the string in the binary! + // db 0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998, converted to amd 64 by Gilles Vollant 2005",0dh,0ah,0 + + +match_init: + ret 0 + + diff --git a/src/png/zlib/contrib/infback9/README b/src/png/zlib/contrib/infback9/README new file mode 100644 index 0000000000..e75ed13294 --- /dev/null +++ b/src/png/zlib/contrib/infback9/README @@ -0,0 +1 @@ +See infback9.h for what this is and how to use it. diff --git a/src/png/zlib/contrib/infback9/infback9.c b/src/png/zlib/contrib/infback9/infback9.c new file mode 100644 index 0000000000..05fb3e3380 --- /dev/null +++ b/src/png/zlib/contrib/infback9/infback9.c @@ -0,0 +1,615 @@ +/* infback9.c -- inflate deflate64 data using a call-back interface + * Copyright (C) 1995-2008 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "infback9.h" +#include "inftree9.h" +#include "inflate9.h" + +#define WSIZE 65536UL + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + window is a user-supplied window and output buffer that is 64K bytes. + */ +int ZEXPORT inflateBack9Init_(strm, window, version, stream_size) +z_stream FAR *strm; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (voidpf)state; + state->window = window; + return Z_OK; +} + +/* + Build and output length and distance decoding tables for fixed code + decoding. + */ +#ifdef MAKEFIXED +#include + +void makefixed9(void) +{ + unsigned sym, bits, low, size; + code *next, *lenfix, *distfix; + struct inflate_state state; + code fixed[544]; + + /* literal/length table */ + sym = 0; + while (sym < 144) state.lens[sym++] = 8; + while (sym < 256) state.lens[sym++] = 9; + while (sym < 280) state.lens[sym++] = 7; + while (sym < 288) state.lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table9(LENS, state.lens, 288, &(next), &(bits), state.work); + + /* distance table */ + sym = 0; + while (sym < 32) state.lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table9(DISTS, state.lens, 32, &(next), &(bits), state.work); + + /* write tables */ + puts(" /* inffix9.h -- table for decoding deflate64 fixed codes"); + puts(" * Generated automatically by makefixed9()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", lenfix[low].op, lenfix[low].bits, + lenfix[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 5) == 0) printf("\n "); + printf("{%u,%u,%d}", distfix[low].op, distfix[low].bits, + distfix[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* Macros for inflateBack(): */ + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n <= 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = window; \ + left = WSIZE; \ + wrap = 1; \ + if (out(out_desc, put, (unsigned)left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack9(strm, in, in_desc, out, out_desc) +z_stream FAR *strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have; /* available input */ + unsigned long left; /* available output */ + inflate_mode mode; /* current inflate mode */ + int lastblock; /* true if processing last block */ + int wrap; /* true if the window has wrapped */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned extra; /* extra bits needed */ + unsigned long length; /* literal or length of data to copy */ + unsigned long offset; /* distance back to copy string from */ + unsigned long copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; +#include "inffix9.h" + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + mode = TYPE; + lastblock = 0; + wrap = 0; + window = state->window; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = window; + left = WSIZE; + lencode = Z_NULL; + distcode = Z_NULL; + + /* Inflate until end of block marked as last */ + for (;;) + switch (mode) { + case TYPE: + /* determine and dispatch block type */ + if (lastblock) { + BYTEBITS(); + mode = DONE; + break; + } + NEEDBITS(3); + lastblock = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + lastblock ? " (last)" : "")); + mode = STORED; + break; + case 1: /* fixed block */ + lencode = lenfix; + lenbits = 9; + distcode = distfix; + distbits = 5; + Tracev((stderr, "inflate: fixed codes block%s\n", + lastblock ? " (last)" : "")); + mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + lastblock ? " (last)" : "")); + mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + mode = BAD; + break; + } + length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %lu\n", + length)); + INITBITS(); + + /* copy stored block from input to output */ + while (length != 0) { + copy = length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); + if (state->nlen > 286) { + strm->msg = (char *)"too many length symbols"; + mode = BAD; + break; + } + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + lencode = (code const FAR *)(state->next); + lenbits = 7; + ret = inflate_table9(CODES, state->lens, 19, &(state->next), + &(lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = lencode[BITS(lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + NEEDBITS(here.bits); + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftree9.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + lencode = (code const FAR *)(state->next); + lenbits = 9; + ret = inflate_table9(LENS, state->lens, state->nlen, + &(state->next), &(lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + mode = BAD; + break; + } + distcode = (code const FAR *)(state->next); + distbits = 6; + ret = inflate_table9(DISTS, state->lens + state->nlen, + state->ndist, &(state->next), &(distbits), + state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + mode = LEN; + + case LEN: + /* get a literal, length, or end-of-block code */ + for (;;) { + here = lencode[BITS(lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + length = (unsigned)here.val; + + /* process literal */ + if (here.op == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + ROOM(); + *put++ = (unsigned char)(length); + left--; + mode = LEN; + break; + } + + /* process end of block */ + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + mode = TYPE; + break; + } + + /* invalid code */ + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + extra = (unsigned)(here.op) & 31; + if (extra != 0) { + NEEDBITS(extra); + length += BITS(extra); + DROPBITS(extra); + } + Tracevv((stderr, "inflate: length %lu\n", length)); + + /* get distance code */ + for (;;) { + here = distcode[BITS(distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + mode = BAD; + break; + } + offset = (unsigned)here.val; + + /* get distance extra bits, if any */ + extra = (unsigned)(here.op) & 15; + if (extra != 0) { + NEEDBITS(extra); + offset += BITS(extra); + DROPBITS(extra); + } + if (offset > WSIZE - (wrap ? 0: left)) { + strm->msg = (char *)"invalid distance too far back"; + mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %lu\n", offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = WSIZE - offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - offset; + copy = left; + } + if (copy > length) copy = length; + length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < WSIZE) { + if (out(out_desc, window, (unsigned)(WSIZE - left))) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBack9End(strm) +z_stream FAR *strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/src/png/zlib/contrib/infback9/infback9.h b/src/png/zlib/contrib/infback9/infback9.h new file mode 100644 index 0000000000..1073c0a38e --- /dev/null +++ b/src/png/zlib/contrib/infback9/infback9.h @@ -0,0 +1,37 @@ +/* infback9.h -- header for using inflateBack9 functions + * Copyright (C) 2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * This header file and associated patches provide a decoder for PKWare's + * undocumented deflate64 compression method (method 9). Use with infback9.c, + * inftree9.h, inftree9.c, and inffix9.h. These patches are not supported. + * This should be compiled with zlib, since it uses zutil.h and zutil.o. + * This code has not yet been tested on 16-bit architectures. See the + * comments in zlib.h for inflateBack() usage. These functions are used + * identically, except that there is no windowBits parameter, and a 64K + * window must be provided. Also if int's are 16 bits, then a zero for + * the third parameter of the "out" function actually means 65536UL. + * zlib.h must be included before this header file. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +ZEXTERN int ZEXPORT inflateBack9 OF((z_stream FAR *strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +ZEXTERN int ZEXPORT inflateBack9End OF((z_stream FAR *strm)); +ZEXTERN int ZEXPORT inflateBack9Init_ OF((z_stream FAR *strm, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define inflateBack9Init(strm, window) \ + inflateBack9Init_((strm), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + +#ifdef __cplusplus +} +#endif diff --git a/src/png/zlib/contrib/infback9/inffix9.h b/src/png/zlib/contrib/infback9/inffix9.h new file mode 100644 index 0000000000..ee5671d2df --- /dev/null +++ b/src/png/zlib/contrib/infback9/inffix9.h @@ -0,0 +1,107 @@ + /* inffix9.h -- table for decoding deflate64 fixed codes + * Generated automatically by makefixed9(). + */ + + /* WARNING: this file should *not* be used by applications. + It is part of the implementation of this library and is + subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{132,8,115},{130,7,31},{0,8,112}, + {0,8,48},{0,9,192},{128,7,10},{0,8,96},{0,8,32},{0,9,160}, + {0,8,0},{0,8,128},{0,8,64},{0,9,224},{128,7,6},{0,8,88}, + {0,8,24},{0,9,144},{131,7,59},{0,8,120},{0,8,56},{0,9,208}, + {129,7,17},{0,8,104},{0,8,40},{0,9,176},{0,8,8},{0,8,136}, + {0,8,72},{0,9,240},{128,7,4},{0,8,84},{0,8,20},{133,8,227}, + {131,7,43},{0,8,116},{0,8,52},{0,9,200},{129,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232}, + {128,7,8},{0,8,92},{0,8,28},{0,9,152},{132,7,83},{0,8,124}, + {0,8,60},{0,9,216},{130,7,23},{0,8,108},{0,8,44},{0,9,184}, + {0,8,12},{0,8,140},{0,8,76},{0,9,248},{128,7,3},{0,8,82}, + {0,8,18},{133,8,163},{131,7,35},{0,8,114},{0,8,50},{0,9,196}, + {129,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},{0,8,130}, + {0,8,66},{0,9,228},{128,7,7},{0,8,90},{0,8,26},{0,9,148}, + {132,7,67},{0,8,122},{0,8,58},{0,9,212},{130,7,19},{0,8,106}, + {0,8,42},{0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244}, + {128,7,5},{0,8,86},{0,8,22},{65,8,0},{131,7,51},{0,8,118}, + {0,8,54},{0,9,204},{129,7,15},{0,8,102},{0,8,38},{0,9,172}, + {0,8,6},{0,8,134},{0,8,70},{0,9,236},{128,7,9},{0,8,94}, + {0,8,30},{0,9,156},{132,7,99},{0,8,126},{0,8,62},{0,9,220}, + {130,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{133,8,131}, + {130,7,31},{0,8,113},{0,8,49},{0,9,194},{128,7,10},{0,8,97}, + {0,8,33},{0,9,162},{0,8,1},{0,8,129},{0,8,65},{0,9,226}, + {128,7,6},{0,8,89},{0,8,25},{0,9,146},{131,7,59},{0,8,121}, + {0,8,57},{0,9,210},{129,7,17},{0,8,105},{0,8,41},{0,9,178}, + {0,8,9},{0,8,137},{0,8,73},{0,9,242},{128,7,4},{0,8,85}, + {0,8,21},{144,8,3},{131,7,43},{0,8,117},{0,8,53},{0,9,202}, + {129,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133}, + {0,8,69},{0,9,234},{128,7,8},{0,8,93},{0,8,29},{0,9,154}, + {132,7,83},{0,8,125},{0,8,61},{0,9,218},{130,7,23},{0,8,109}, + {0,8,45},{0,9,186},{0,8,13},{0,8,141},{0,8,77},{0,9,250}, + {128,7,3},{0,8,83},{0,8,19},{133,8,195},{131,7,35},{0,8,115}, + {0,8,51},{0,9,198},{129,7,11},{0,8,99},{0,8,35},{0,9,166}, + {0,8,3},{0,8,131},{0,8,67},{0,9,230},{128,7,7},{0,8,91}, + {0,8,27},{0,9,150},{132,7,67},{0,8,123},{0,8,59},{0,9,214}, + {130,7,19},{0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139}, + {0,8,75},{0,9,246},{128,7,5},{0,8,87},{0,8,23},{77,8,0}, + {131,7,51},{0,8,119},{0,8,55},{0,9,206},{129,7,15},{0,8,103}, + {0,8,39},{0,9,174},{0,8,7},{0,8,135},{0,8,71},{0,9,238}, + {128,7,9},{0,8,95},{0,8,31},{0,9,158},{132,7,99},{0,8,127}, + {0,8,63},{0,9,222},{130,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80}, + {0,8,16},{132,8,115},{130,7,31},{0,8,112},{0,8,48},{0,9,193}, + {128,7,10},{0,8,96},{0,8,32},{0,9,161},{0,8,0},{0,8,128}, + {0,8,64},{0,9,225},{128,7,6},{0,8,88},{0,8,24},{0,9,145}, + {131,7,59},{0,8,120},{0,8,56},{0,9,209},{129,7,17},{0,8,104}, + {0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},{0,9,241}, + {128,7,4},{0,8,84},{0,8,20},{133,8,227},{131,7,43},{0,8,116}, + {0,8,52},{0,9,201},{129,7,13},{0,8,100},{0,8,36},{0,9,169}, + {0,8,4},{0,8,132},{0,8,68},{0,9,233},{128,7,8},{0,8,92}, + {0,8,28},{0,9,153},{132,7,83},{0,8,124},{0,8,60},{0,9,217}, + {130,7,23},{0,8,108},{0,8,44},{0,9,185},{0,8,12},{0,8,140}, + {0,8,76},{0,9,249},{128,7,3},{0,8,82},{0,8,18},{133,8,163}, + {131,7,35},{0,8,114},{0,8,50},{0,9,197},{129,7,11},{0,8,98}, + {0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {128,7,7},{0,8,90},{0,8,26},{0,9,149},{132,7,67},{0,8,122}, + {0,8,58},{0,9,213},{130,7,19},{0,8,106},{0,8,42},{0,9,181}, + {0,8,10},{0,8,138},{0,8,74},{0,9,245},{128,7,5},{0,8,86}, + {0,8,22},{65,8,0},{131,7,51},{0,8,118},{0,8,54},{0,9,205}, + {129,7,15},{0,8,102},{0,8,38},{0,9,173},{0,8,6},{0,8,134}, + {0,8,70},{0,9,237},{128,7,9},{0,8,94},{0,8,30},{0,9,157}, + {132,7,99},{0,8,126},{0,8,62},{0,9,221},{130,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253}, + {96,7,0},{0,8,81},{0,8,17},{133,8,131},{130,7,31},{0,8,113}, + {0,8,49},{0,9,195},{128,7,10},{0,8,97},{0,8,33},{0,9,163}, + {0,8,1},{0,8,129},{0,8,65},{0,9,227},{128,7,6},{0,8,89}, + {0,8,25},{0,9,147},{131,7,59},{0,8,121},{0,8,57},{0,9,211}, + {129,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},{0,8,137}, + {0,8,73},{0,9,243},{128,7,4},{0,8,85},{0,8,21},{144,8,3}, + {131,7,43},{0,8,117},{0,8,53},{0,9,203},{129,7,13},{0,8,101}, + {0,8,37},{0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235}, + {128,7,8},{0,8,93},{0,8,29},{0,9,155},{132,7,83},{0,8,125}, + {0,8,61},{0,9,219},{130,7,23},{0,8,109},{0,8,45},{0,9,187}, + {0,8,13},{0,8,141},{0,8,77},{0,9,251},{128,7,3},{0,8,83}, + {0,8,19},{133,8,195},{131,7,35},{0,8,115},{0,8,51},{0,9,199}, + {129,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{128,7,7},{0,8,91},{0,8,27},{0,9,151}, + {132,7,67},{0,8,123},{0,8,59},{0,9,215},{130,7,19},{0,8,107}, + {0,8,43},{0,9,183},{0,8,11},{0,8,139},{0,8,75},{0,9,247}, + {128,7,5},{0,8,87},{0,8,23},{77,8,0},{131,7,51},{0,8,119}, + {0,8,55},{0,9,207},{129,7,15},{0,8,103},{0,8,39},{0,9,175}, + {0,8,7},{0,8,135},{0,8,71},{0,9,239},{128,7,9},{0,8,95}, + {0,8,31},{0,9,159},{132,7,99},{0,8,127},{0,8,63},{0,9,223}, + {130,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143}, + {0,8,79},{0,9,255} + }; + + static const code distfix[32] = { + {128,5,1},{135,5,257},{131,5,17},{139,5,4097},{129,5,5}, + {137,5,1025},{133,5,65},{141,5,16385},{128,5,3},{136,5,513}, + {132,5,33},{140,5,8193},{130,5,9},{138,5,2049},{134,5,129}, + {142,5,32769},{128,5,2},{135,5,385},{131,5,25},{139,5,6145}, + {129,5,7},{137,5,1537},{133,5,97},{141,5,24577},{128,5,4}, + {136,5,769},{132,5,49},{140,5,12289},{130,5,13},{138,5,3073}, + {134,5,193},{142,5,49153} + }; diff --git a/src/png/zlib/contrib/infback9/inflate9.h b/src/png/zlib/contrib/infback9/inflate9.h new file mode 100644 index 0000000000..ee9a79394b --- /dev/null +++ b/src/png/zlib/contrib/infback9/inflate9.h @@ -0,0 +1,47 @@ +/* inflate9.h -- internal inflate state definition + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Possible inflate modes between inflate() calls */ +typedef enum { + TYPE, /* i: waiting for type bits, including last-flag bit */ + STORED, /* i: waiting for stored size (length and complement) */ + TABLE, /* i: waiting for dynamic block table lengths */ + LEN, /* i: waiting for length/lit code */ + DONE, /* finished check, done -- remain here until reset */ + BAD /* got a data error -- remain here until reset */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD mode -- not shown for clarity) + + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or DONE + STORED -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LEN or TYPE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + /* sliding window */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/src/png/zlib/contrib/infback9/inftree9.c b/src/png/zlib/contrib/infback9/inftree9.c new file mode 100644 index 0000000000..5f4a76798d --- /dev/null +++ b/src/png/zlib/contrib/infback9/inftree9.c @@ -0,0 +1,324 @@ +/* inftree9.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftree9.h" + +#define MAXBITS 15 + +const char inflate9_copyright[] = + " inflate9 1.2.11 Copyright 1995-2017 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table9(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, + 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, + 131, 163, 195, 227, 3, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, + 130, 130, 130, 130, 131, 131, 131, 131, 132, 132, 132, 132, + 133, 133, 133, 133, 144, 77, 202}; + static const unsigned short dbase[32] = { /* Distance codes 0..31 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, + 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, + 4097, 6145, 8193, 12289, 16385, 24577, 32769, 49153}; + static const unsigned short dext[32] = { /* Distance codes 0..31 extra */ + 128, 128, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, + 133, 133, 134, 134, 135, 135, 136, 136, 137, 137, 138, 138, + 139, 139, 140, 140, 141, 141, 142, 142}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) return -1; /* no codes! */ + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftree9.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += 1U << curr; + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + curr = root; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/src/png/zlib/contrib/infback9/inftree9.h b/src/png/zlib/contrib/infback9/inftree9.h new file mode 100644 index 0000000000..5ab21f0c6d --- /dev/null +++ b/src/png/zlib/contrib/infback9/inftree9.h @@ -0,0 +1,61 @@ +/* inftree9.h -- header to use inftree9.c + * Copyright (C) 1995-2008 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 100eeeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1446, which is the sum of 852 for literal/length codes and 594 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 32 6 15" for distance codes returns 594. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in infback9.c. If the root table size is changed, + then these maximum sizes would be need to be recalculated and updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 594 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table9() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table9 OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/src/png/zlib/contrib/inflate86/inffas86.c b/src/png/zlib/contrib/inflate86/inffas86.c new file mode 100644 index 0000000000..7292f67b75 --- /dev/null +++ b/src/png/zlib/contrib/inflate86/inffas86.c @@ -0,0 +1,1157 @@ +/* inffas86.c is a hand tuned assembler version of + * + * inffast.c -- fast decoding + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Copyright (C) 2003 Chris Anderson + * Please use the copyright conditions above. + * + * Dec-29-2003 -- I added AMD64 inflate asm support. This version is also + * slightly quicker on x86 systems because, instead of using rep movsb to copy + * data, it uses rep movsw, which moves data in 2-byte chunks instead of single + * bytes. I've tested the AMD64 code on a Fedora Core 1 + the x86_64 updates + * from http://fedora.linux.duke.edu/fc1_x86_64 + * which is running on an Athlon 64 3000+ / Gigabyte GA-K8VT800M system with + * 1GB ram. The 64-bit version is about 4% faster than the 32-bit version, + * when decompressing mozilla-source-1.3.tar.gz. + * + * Mar-13-2003 -- Most of this is derived from inffast.S which is derived from + * the gcc -S output of zlib-1.2.0/inffast.c. Zlib-1.2.0 is in beta release at + * the moment. I have successfully compiled and tested this code with gcc2.96, + * gcc3.2, icc5.0, msvc6.0. It is very close to the speed of inffast.S + * compiled with gcc -DNO_MMX, but inffast.S is still faster on the P3 with MMX + * enabled. I will attempt to merge the MMX code into this version. Newer + * versions of this and inffast.S can be found at + * http://www.eetbeetee.com/zlib/ and http://www.charm.net/~christop/zlib/ + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* Mark Adler's comments from inffast.c: */ + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + struct inffast_ar { +/* 64 32 x86 x86_64 */ +/* ar offset register */ +/* 0 0 */ void *esp; /* esp save */ +/* 8 4 */ void *ebp; /* ebp save */ +/* 16 8 */ unsigned char FAR *in; /* esi rsi local strm->next_in */ +/* 24 12 */ unsigned char FAR *last; /* r9 while in < last */ +/* 32 16 */ unsigned char FAR *out; /* edi rdi local strm->next_out */ +/* 40 20 */ unsigned char FAR *beg; /* inflate()'s init next_out */ +/* 48 24 */ unsigned char FAR *end; /* r10 while out < end */ +/* 56 28 */ unsigned char FAR *window;/* size of window, wsize!=0 */ +/* 64 32 */ code const FAR *lcode; /* ebp rbp local strm->lencode */ +/* 72 36 */ code const FAR *dcode; /* r11 local strm->distcode */ +/* 80 40 */ unsigned long hold; /* edx rdx local strm->hold */ +/* 88 44 */ unsigned bits; /* ebx rbx local strm->bits */ +/* 92 48 */ unsigned wsize; /* window size */ +/* 96 52 */ unsigned write; /* window write index */ +/*100 56 */ unsigned lmask; /* r12 mask for lcode */ +/*104 60 */ unsigned dmask; /* r13 mask for dcode */ +/*108 64 */ unsigned len; /* r14 match length */ +/*112 68 */ unsigned dist; /* r15 match distance */ +/*116 72 */ unsigned status; /* set when state chng*/ + } ar; + +#if defined( __GNUC__ ) && defined( __amd64__ ) && ! defined( __i386 ) +#define PAD_AVAIL_IN 6 +#define PAD_AVAIL_OUT 258 +#else +#define PAD_AVAIL_IN 5 +#define PAD_AVAIL_OUT 257 +#endif + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + ar.in = strm->next_in; + ar.last = ar.in + (strm->avail_in - PAD_AVAIL_IN); + ar.out = strm->next_out; + ar.beg = ar.out - (start - strm->avail_out); + ar.end = ar.out + (strm->avail_out - PAD_AVAIL_OUT); + ar.wsize = state->wsize; + ar.write = state->wnext; + ar.window = state->window; + ar.hold = state->hold; + ar.bits = state->bits; + ar.lcode = state->lencode; + ar.dcode = state->distcode; + ar.lmask = (1U << state->lenbits) - 1; + ar.dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + + /* align in on 1/2 hold size boundary */ + while (((unsigned long)(void *)ar.in & (sizeof(ar.hold) / 2 - 1)) != 0) { + ar.hold += (unsigned long)*ar.in++ << ar.bits; + ar.bits += 8; + } + +#if defined( __GNUC__ ) && defined( __amd64__ ) && ! defined( __i386 ) + __asm__ __volatile__ ( +" leaq %0, %%rax\n" +" movq %%rbp, 8(%%rax)\n" /* save regs rbp and rsp */ +" movq %%rsp, (%%rax)\n" +" movq %%rax, %%rsp\n" /* make rsp point to &ar */ +" movq 16(%%rsp), %%rsi\n" /* rsi = in */ +" movq 32(%%rsp), %%rdi\n" /* rdi = out */ +" movq 24(%%rsp), %%r9\n" /* r9 = last */ +" movq 48(%%rsp), %%r10\n" /* r10 = end */ +" movq 64(%%rsp), %%rbp\n" /* rbp = lcode */ +" movq 72(%%rsp), %%r11\n" /* r11 = dcode */ +" movq 80(%%rsp), %%rdx\n" /* rdx = hold */ +" movl 88(%%rsp), %%ebx\n" /* ebx = bits */ +" movl 100(%%rsp), %%r12d\n" /* r12d = lmask */ +" movl 104(%%rsp), %%r13d\n" /* r13d = dmask */ + /* r14d = len */ + /* r15d = dist */ +" cld\n" +" cmpq %%rdi, %%r10\n" +" je .L_one_time\n" /* if only one decode left */ +" cmpq %%rsi, %%r9\n" +" je .L_one_time\n" +" jmp .L_do_loop\n" + +".L_one_time:\n" +" movq %%r12, %%r8\n" /* r8 = lmask */ +" cmpb $32, %%bl\n" +" ja .L_get_length_code_one_time\n" + +" lodsl\n" /* eax = *(uint *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $32, %%bl\n" /* bits += 32 */ +" shlq %%cl, %%rax\n" +" orq %%rax, %%rdx\n" /* hold |= *((uint *)in)++ << bits */ +" jmp .L_get_length_code_one_time\n" + +".align 32,0x90\n" +".L_while_test:\n" +" cmpq %%rdi, %%r10\n" +" jbe .L_break_loop\n" +" cmpq %%rsi, %%r9\n" +" jbe .L_break_loop\n" + +".L_do_loop:\n" +" movq %%r12, %%r8\n" /* r8 = lmask */ +" cmpb $32, %%bl\n" +" ja .L_get_length_code\n" /* if (32 < bits) */ + +" lodsl\n" /* eax = *(uint *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $32, %%bl\n" /* bits += 32 */ +" shlq %%cl, %%rax\n" +" orq %%rax, %%rdx\n" /* hold |= *((uint *)in)++ << bits */ + +".L_get_length_code:\n" +" andq %%rdx, %%r8\n" /* r8 &= hold */ +" movl (%%rbp,%%r8,4), %%eax\n" /* eax = lcode[hold & lmask] */ + +" movb %%ah, %%cl\n" /* cl = this.bits */ +" subb %%ah, %%bl\n" /* bits -= this.bits */ +" shrq %%cl, %%rdx\n" /* hold >>= this.bits */ + +" testb %%al, %%al\n" +" jnz .L_test_for_length_base\n" /* if (op != 0) 45.7% */ + +" movq %%r12, %%r8\n" /* r8 = lmask */ +" shrl $16, %%eax\n" /* output this.val char */ +" stosb\n" + +".L_get_length_code_one_time:\n" +" andq %%rdx, %%r8\n" /* r8 &= hold */ +" movl (%%rbp,%%r8,4), %%eax\n" /* eax = lcode[hold & lmask] */ + +".L_dolen:\n" +" movb %%ah, %%cl\n" /* cl = this.bits */ +" subb %%ah, %%bl\n" /* bits -= this.bits */ +" shrq %%cl, %%rdx\n" /* hold >>= this.bits */ + +" testb %%al, %%al\n" +" jnz .L_test_for_length_base\n" /* if (op != 0) 45.7% */ + +" shrl $16, %%eax\n" /* output this.val char */ +" stosb\n" +" jmp .L_while_test\n" + +".align 32,0x90\n" +".L_test_for_length_base:\n" +" movl %%eax, %%r14d\n" /* len = this */ +" shrl $16, %%r14d\n" /* len = this.val */ +" movb %%al, %%cl\n" + +" testb $16, %%al\n" +" jz .L_test_for_second_level_length\n" /* if ((op & 16) == 0) 8% */ +" andb $15, %%cl\n" /* op &= 15 */ +" jz .L_decode_distance\n" /* if (!op) */ + +".L_add_bits_to_len:\n" +" subb %%cl, %%bl\n" +" xorl %%eax, %%eax\n" +" incl %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" shrq %%cl, %%rdx\n" +" addl %%eax, %%r14d\n" /* len += hold & mask[op] */ + +".L_decode_distance:\n" +" movq %%r13, %%r8\n" /* r8 = dmask */ +" cmpb $32, %%bl\n" +" ja .L_get_distance_code\n" /* if (32 < bits) */ + +" lodsl\n" /* eax = *(uint *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $32, %%bl\n" /* bits += 32 */ +" shlq %%cl, %%rax\n" +" orq %%rax, %%rdx\n" /* hold |= *((uint *)in)++ << bits */ + +".L_get_distance_code:\n" +" andq %%rdx, %%r8\n" /* r8 &= hold */ +" movl (%%r11,%%r8,4), %%eax\n" /* eax = dcode[hold & dmask] */ + +".L_dodist:\n" +" movl %%eax, %%r15d\n" /* dist = this */ +" shrl $16, %%r15d\n" /* dist = this.val */ +" movb %%ah, %%cl\n" +" subb %%ah, %%bl\n" /* bits -= this.bits */ +" shrq %%cl, %%rdx\n" /* hold >>= this.bits */ +" movb %%al, %%cl\n" /* cl = this.op */ + +" testb $16, %%al\n" /* if ((op & 16) == 0) */ +" jz .L_test_for_second_level_dist\n" +" andb $15, %%cl\n" /* op &= 15 */ +" jz .L_check_dist_one\n" + +".L_add_bits_to_dist:\n" +" subb %%cl, %%bl\n" +" xorl %%eax, %%eax\n" +" incl %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" /* (1 << op) - 1 */ +" andl %%edx, %%eax\n" /* eax &= hold */ +" shrq %%cl, %%rdx\n" +" addl %%eax, %%r15d\n" /* dist += hold & ((1 << op) - 1) */ + +".L_check_window:\n" +" movq %%rsi, %%r8\n" /* save in so from can use it's reg */ +" movq %%rdi, %%rax\n" +" subq 40(%%rsp), %%rax\n" /* nbytes = out - beg */ + +" cmpl %%r15d, %%eax\n" +" jb .L_clip_window\n" /* if (dist > nbytes) 4.2% */ + +" movl %%r14d, %%ecx\n" /* ecx = len */ +" movq %%rdi, %%rsi\n" +" subq %%r15, %%rsi\n" /* from = out - dist */ + +" sarl %%ecx\n" +" jnc .L_copy_two\n" /* if len % 2 == 0 */ + +" rep movsw\n" +" movb (%%rsi), %%al\n" +" movb %%al, (%%rdi)\n" +" incq %%rdi\n" + +" movq %%r8, %%rsi\n" /* move in back to %rsi, toss from */ +" jmp .L_while_test\n" + +".L_copy_two:\n" +" rep movsw\n" +" movq %%r8, %%rsi\n" /* move in back to %rsi, toss from */ +" jmp .L_while_test\n" + +".align 32,0x90\n" +".L_check_dist_one:\n" +" cmpl $1, %%r15d\n" /* if dist 1, is a memset */ +" jne .L_check_window\n" +" cmpq %%rdi, 40(%%rsp)\n" /* if out == beg, outside window */ +" je .L_check_window\n" + +" movl %%r14d, %%ecx\n" /* ecx = len */ +" movb -1(%%rdi), %%al\n" +" movb %%al, %%ah\n" + +" sarl %%ecx\n" +" jnc .L_set_two\n" +" movb %%al, (%%rdi)\n" +" incq %%rdi\n" + +".L_set_two:\n" +" rep stosw\n" +" jmp .L_while_test\n" + +".align 32,0x90\n" +".L_test_for_second_level_length:\n" +" testb $64, %%al\n" +" jnz .L_test_for_end_of_block\n" /* if ((op & 64) != 0) */ + +" xorl %%eax, %%eax\n" +" incl %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" addl %%r14d, %%eax\n" /* eax += len */ +" movl (%%rbp,%%rax,4), %%eax\n" /* eax = lcode[val+(hold&mask[op])]*/ +" jmp .L_dolen\n" + +".align 32,0x90\n" +".L_test_for_second_level_dist:\n" +" testb $64, %%al\n" +" jnz .L_invalid_distance_code\n" /* if ((op & 64) != 0) */ + +" xorl %%eax, %%eax\n" +" incl %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" addl %%r15d, %%eax\n" /* eax += dist */ +" movl (%%r11,%%rax,4), %%eax\n" /* eax = dcode[val+(hold&mask[op])]*/ +" jmp .L_dodist\n" + +".align 32,0x90\n" +".L_clip_window:\n" +" movl %%eax, %%ecx\n" /* ecx = nbytes */ +" movl 92(%%rsp), %%eax\n" /* eax = wsize, prepare for dist cmp */ +" negl %%ecx\n" /* nbytes = -nbytes */ + +" cmpl %%r15d, %%eax\n" +" jb .L_invalid_distance_too_far\n" /* if (dist > wsize) */ + +" addl %%r15d, %%ecx\n" /* nbytes = dist - nbytes */ +" cmpl $0, 96(%%rsp)\n" +" jne .L_wrap_around_window\n" /* if (write != 0) */ + +" movq 56(%%rsp), %%rsi\n" /* from = window */ +" subl %%ecx, %%eax\n" /* eax -= nbytes */ +" addq %%rax, %%rsi\n" /* from += wsize - nbytes */ + +" movl %%r14d, %%eax\n" /* eax = len */ +" cmpl %%ecx, %%r14d\n" +" jbe .L_do_copy\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* eax -= nbytes */ +" rep movsb\n" +" movq %%rdi, %%rsi\n" +" subq %%r15, %%rsi\n" /* from = &out[ -dist ] */ +" jmp .L_do_copy\n" + +".align 32,0x90\n" +".L_wrap_around_window:\n" +" movl 96(%%rsp), %%eax\n" /* eax = write */ +" cmpl %%eax, %%ecx\n" +" jbe .L_contiguous_in_window\n" /* if (write >= nbytes) */ + +" movl 92(%%rsp), %%esi\n" /* from = wsize */ +" addq 56(%%rsp), %%rsi\n" /* from += window */ +" addq %%rax, %%rsi\n" /* from += write */ +" subq %%rcx, %%rsi\n" /* from -= nbytes */ +" subl %%eax, %%ecx\n" /* nbytes -= write */ + +" movl %%r14d, %%eax\n" /* eax = len */ +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movq 56(%%rsp), %%rsi\n" /* from = window */ +" movl 96(%%rsp), %%ecx\n" /* nbytes = write */ +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movq %%rdi, %%rsi\n" +" subq %%r15, %%rsi\n" /* from = out - dist */ +" jmp .L_do_copy\n" + +".align 32,0x90\n" +".L_contiguous_in_window:\n" +" movq 56(%%rsp), %%rsi\n" /* rsi = window */ +" addq %%rax, %%rsi\n" +" subq %%rcx, %%rsi\n" /* from += write - nbytes */ + +" movl %%r14d, %%eax\n" /* eax = len */ +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movq %%rdi, %%rsi\n" +" subq %%r15, %%rsi\n" /* from = out - dist */ +" jmp .L_do_copy\n" /* if (nbytes >= len) */ + +".align 32,0x90\n" +".L_do_copy:\n" +" movl %%eax, %%ecx\n" /* ecx = len */ +" rep movsb\n" + +" movq %%r8, %%rsi\n" /* move in back to %esi, toss from */ +" jmp .L_while_test\n" + +".L_test_for_end_of_block:\n" +" testb $32, %%al\n" +" jz .L_invalid_literal_length_code\n" +" movl $1, 116(%%rsp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_literal_length_code:\n" +" movl $2, 116(%%rsp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_distance_code:\n" +" movl $3, 116(%%rsp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_distance_too_far:\n" +" movl $4, 116(%%rsp)\n" +" jmp .L_break_loop_with_status\n" + +".L_break_loop:\n" +" movl $0, 116(%%rsp)\n" + +".L_break_loop_with_status:\n" +/* put in, out, bits, and hold back into ar and pop esp */ +" movq %%rsi, 16(%%rsp)\n" /* in */ +" movq %%rdi, 32(%%rsp)\n" /* out */ +" movl %%ebx, 88(%%rsp)\n" /* bits */ +" movq %%rdx, 80(%%rsp)\n" /* hold */ +" movq (%%rsp), %%rax\n" /* restore rbp and rsp */ +" movq 8(%%rsp), %%rbp\n" +" movq %%rax, %%rsp\n" + : + : "m" (ar) + : "memory", "%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" + ); +#elif ( defined( __GNUC__ ) || defined( __ICC ) ) && defined( __i386 ) + __asm__ __volatile__ ( +" leal %0, %%eax\n" +" movl %%esp, (%%eax)\n" /* save esp, ebp */ +" movl %%ebp, 4(%%eax)\n" +" movl %%eax, %%esp\n" +" movl 8(%%esp), %%esi\n" /* esi = in */ +" movl 16(%%esp), %%edi\n" /* edi = out */ +" movl 40(%%esp), %%edx\n" /* edx = hold */ +" movl 44(%%esp), %%ebx\n" /* ebx = bits */ +" movl 32(%%esp), %%ebp\n" /* ebp = lcode */ + +" cld\n" +" jmp .L_do_loop\n" + +".align 32,0x90\n" +".L_while_test:\n" +" cmpl %%edi, 24(%%esp)\n" /* out < end */ +" jbe .L_break_loop\n" +" cmpl %%esi, 12(%%esp)\n" /* in < last */ +" jbe .L_break_loop\n" + +".L_do_loop:\n" +" cmpb $15, %%bl\n" +" ja .L_get_length_code\n" /* if (15 < bits) */ + +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ + +".L_get_length_code:\n" +" movl 56(%%esp), %%eax\n" /* eax = lmask */ +" andl %%edx, %%eax\n" /* eax &= hold */ +" movl (%%ebp,%%eax,4), %%eax\n" /* eax = lcode[hold & lmask] */ + +".L_dolen:\n" +" movb %%ah, %%cl\n" /* cl = this.bits */ +" subb %%ah, %%bl\n" /* bits -= this.bits */ +" shrl %%cl, %%edx\n" /* hold >>= this.bits */ + +" testb %%al, %%al\n" +" jnz .L_test_for_length_base\n" /* if (op != 0) 45.7% */ + +" shrl $16, %%eax\n" /* output this.val char */ +" stosb\n" +" jmp .L_while_test\n" + +".align 32,0x90\n" +".L_test_for_length_base:\n" +" movl %%eax, %%ecx\n" /* len = this */ +" shrl $16, %%ecx\n" /* len = this.val */ +" movl %%ecx, 64(%%esp)\n" /* save len */ +" movb %%al, %%cl\n" + +" testb $16, %%al\n" +" jz .L_test_for_second_level_length\n" /* if ((op & 16) == 0) 8% */ +" andb $15, %%cl\n" /* op &= 15 */ +" jz .L_decode_distance\n" /* if (!op) */ +" cmpb %%cl, %%bl\n" +" jae .L_add_bits_to_len\n" /* if (op <= bits) */ + +" movb %%cl, %%ch\n" /* stash op in ch, freeing cl */ +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ +" movb %%ch, %%cl\n" /* move op back to ecx */ + +".L_add_bits_to_len:\n" +" subb %%cl, %%bl\n" +" xorl %%eax, %%eax\n" +" incl %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" shrl %%cl, %%edx\n" +" addl %%eax, 64(%%esp)\n" /* len += hold & mask[op] */ + +".L_decode_distance:\n" +" cmpb $15, %%bl\n" +" ja .L_get_distance_code\n" /* if (15 < bits) */ + +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ + +".L_get_distance_code:\n" +" movl 60(%%esp), %%eax\n" /* eax = dmask */ +" movl 36(%%esp), %%ecx\n" /* ecx = dcode */ +" andl %%edx, %%eax\n" /* eax &= hold */ +" movl (%%ecx,%%eax,4), %%eax\n"/* eax = dcode[hold & dmask] */ + +".L_dodist:\n" +" movl %%eax, %%ebp\n" /* dist = this */ +" shrl $16, %%ebp\n" /* dist = this.val */ +" movb %%ah, %%cl\n" +" subb %%ah, %%bl\n" /* bits -= this.bits */ +" shrl %%cl, %%edx\n" /* hold >>= this.bits */ +" movb %%al, %%cl\n" /* cl = this.op */ + +" testb $16, %%al\n" /* if ((op & 16) == 0) */ +" jz .L_test_for_second_level_dist\n" +" andb $15, %%cl\n" /* op &= 15 */ +" jz .L_check_dist_one\n" +" cmpb %%cl, %%bl\n" +" jae .L_add_bits_to_dist\n" /* if (op <= bits) 97.6% */ + +" movb %%cl, %%ch\n" /* stash op in ch, freeing cl */ +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ +" movb %%ch, %%cl\n" /* move op back to ecx */ + +".L_add_bits_to_dist:\n" +" subb %%cl, %%bl\n" +" xorl %%eax, %%eax\n" +" incl %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" /* (1 << op) - 1 */ +" andl %%edx, %%eax\n" /* eax &= hold */ +" shrl %%cl, %%edx\n" +" addl %%eax, %%ebp\n" /* dist += hold & ((1 << op) - 1) */ + +".L_check_window:\n" +" movl %%esi, 8(%%esp)\n" /* save in so from can use it's reg */ +" movl %%edi, %%eax\n" +" subl 20(%%esp), %%eax\n" /* nbytes = out - beg */ + +" cmpl %%ebp, %%eax\n" +" jb .L_clip_window\n" /* if (dist > nbytes) 4.2% */ + +" movl 64(%%esp), %%ecx\n" /* ecx = len */ +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ + +" sarl %%ecx\n" +" jnc .L_copy_two\n" /* if len % 2 == 0 */ + +" rep movsw\n" +" movb (%%esi), %%al\n" +" movb %%al, (%%edi)\n" +" incl %%edi\n" + +" movl 8(%%esp), %%esi\n" /* move in back to %esi, toss from */ +" movl 32(%%esp), %%ebp\n" /* ebp = lcode */ +" jmp .L_while_test\n" + +".L_copy_two:\n" +" rep movsw\n" +" movl 8(%%esp), %%esi\n" /* move in back to %esi, toss from */ +" movl 32(%%esp), %%ebp\n" /* ebp = lcode */ +" jmp .L_while_test\n" + +".align 32,0x90\n" +".L_check_dist_one:\n" +" cmpl $1, %%ebp\n" /* if dist 1, is a memset */ +" jne .L_check_window\n" +" cmpl %%edi, 20(%%esp)\n" +" je .L_check_window\n" /* out == beg, if outside window */ + +" movl 64(%%esp), %%ecx\n" /* ecx = len */ +" movb -1(%%edi), %%al\n" +" movb %%al, %%ah\n" + +" sarl %%ecx\n" +" jnc .L_set_two\n" +" movb %%al, (%%edi)\n" +" incl %%edi\n" + +".L_set_two:\n" +" rep stosw\n" +" movl 32(%%esp), %%ebp\n" /* ebp = lcode */ +" jmp .L_while_test\n" + +".align 32,0x90\n" +".L_test_for_second_level_length:\n" +" testb $64, %%al\n" +" jnz .L_test_for_end_of_block\n" /* if ((op & 64) != 0) */ + +" xorl %%eax, %%eax\n" +" incl %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" addl 64(%%esp), %%eax\n" /* eax += len */ +" movl (%%ebp,%%eax,4), %%eax\n" /* eax = lcode[val+(hold&mask[op])]*/ +" jmp .L_dolen\n" + +".align 32,0x90\n" +".L_test_for_second_level_dist:\n" +" testb $64, %%al\n" +" jnz .L_invalid_distance_code\n" /* if ((op & 64) != 0) */ + +" xorl %%eax, %%eax\n" +" incl %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" addl %%ebp, %%eax\n" /* eax += dist */ +" movl 36(%%esp), %%ecx\n" /* ecx = dcode */ +" movl (%%ecx,%%eax,4), %%eax\n" /* eax = dcode[val+(hold&mask[op])]*/ +" jmp .L_dodist\n" + +".align 32,0x90\n" +".L_clip_window:\n" +" movl %%eax, %%ecx\n" +" movl 48(%%esp), %%eax\n" /* eax = wsize */ +" negl %%ecx\n" /* nbytes = -nbytes */ +" movl 28(%%esp), %%esi\n" /* from = window */ + +" cmpl %%ebp, %%eax\n" +" jb .L_invalid_distance_too_far\n" /* if (dist > wsize) */ + +" addl %%ebp, %%ecx\n" /* nbytes = dist - nbytes */ +" cmpl $0, 52(%%esp)\n" +" jne .L_wrap_around_window\n" /* if (write != 0) */ + +" subl %%ecx, %%eax\n" +" addl %%eax, %%esi\n" /* from += wsize - nbytes */ + +" movl 64(%%esp), %%eax\n" /* eax = len */ +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ +" jmp .L_do_copy\n" + +".align 32,0x90\n" +".L_wrap_around_window:\n" +" movl 52(%%esp), %%eax\n" /* eax = write */ +" cmpl %%eax, %%ecx\n" +" jbe .L_contiguous_in_window\n" /* if (write >= nbytes) */ + +" addl 48(%%esp), %%esi\n" /* from += wsize */ +" addl %%eax, %%esi\n" /* from += write */ +" subl %%ecx, %%esi\n" /* from -= nbytes */ +" subl %%eax, %%ecx\n" /* nbytes -= write */ + +" movl 64(%%esp), %%eax\n" /* eax = len */ +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl 28(%%esp), %%esi\n" /* from = window */ +" movl 52(%%esp), %%ecx\n" /* nbytes = write */ +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ +" jmp .L_do_copy\n" + +".align 32,0x90\n" +".L_contiguous_in_window:\n" +" addl %%eax, %%esi\n" +" subl %%ecx, %%esi\n" /* from += write - nbytes */ + +" movl 64(%%esp), %%eax\n" /* eax = len */ +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ +" jmp .L_do_copy\n" /* if (nbytes >= len) */ + +".align 32,0x90\n" +".L_do_copy:\n" +" movl %%eax, %%ecx\n" +" rep movsb\n" + +" movl 8(%%esp), %%esi\n" /* move in back to %esi, toss from */ +" movl 32(%%esp), %%ebp\n" /* ebp = lcode */ +" jmp .L_while_test\n" + +".L_test_for_end_of_block:\n" +" testb $32, %%al\n" +" jz .L_invalid_literal_length_code\n" +" movl $1, 72(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_literal_length_code:\n" +" movl $2, 72(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_distance_code:\n" +" movl $3, 72(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_distance_too_far:\n" +" movl 8(%%esp), %%esi\n" +" movl $4, 72(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_break_loop:\n" +" movl $0, 72(%%esp)\n" + +".L_break_loop_with_status:\n" +/* put in, out, bits, and hold back into ar and pop esp */ +" movl %%esi, 8(%%esp)\n" /* save in */ +" movl %%edi, 16(%%esp)\n" /* save out */ +" movl %%ebx, 44(%%esp)\n" /* save bits */ +" movl %%edx, 40(%%esp)\n" /* save hold */ +" movl 4(%%esp), %%ebp\n" /* restore esp, ebp */ +" movl (%%esp), %%esp\n" + : + : "m" (ar) + : "memory", "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi" + ); +#elif defined( _MSC_VER ) && ! defined( _M_AMD64 ) + __asm { + lea eax, ar + mov [eax], esp /* save esp, ebp */ + mov [eax+4], ebp + mov esp, eax + mov esi, [esp+8] /* esi = in */ + mov edi, [esp+16] /* edi = out */ + mov edx, [esp+40] /* edx = hold */ + mov ebx, [esp+44] /* ebx = bits */ + mov ebp, [esp+32] /* ebp = lcode */ + + cld + jmp L_do_loop + +ALIGN 4 +L_while_test: + cmp [esp+24], edi + jbe L_break_loop + cmp [esp+12], esi + jbe L_break_loop + +L_do_loop: + cmp bl, 15 + ja L_get_length_code /* if (15 < bits) */ + + xor eax, eax + lodsw /* al = *(ushort *)in++ */ + mov cl, bl /* cl = bits, needs it for shifting */ + add bl, 16 /* bits += 16 */ + shl eax, cl + or edx, eax /* hold |= *((ushort *)in)++ << bits */ + +L_get_length_code: + mov eax, [esp+56] /* eax = lmask */ + and eax, edx /* eax &= hold */ + mov eax, [ebp+eax*4] /* eax = lcode[hold & lmask] */ + +L_dolen: + mov cl, ah /* cl = this.bits */ + sub bl, ah /* bits -= this.bits */ + shr edx, cl /* hold >>= this.bits */ + + test al, al + jnz L_test_for_length_base /* if (op != 0) 45.7% */ + + shr eax, 16 /* output this.val char */ + stosb + jmp L_while_test + +ALIGN 4 +L_test_for_length_base: + mov ecx, eax /* len = this */ + shr ecx, 16 /* len = this.val */ + mov [esp+64], ecx /* save len */ + mov cl, al + + test al, 16 + jz L_test_for_second_level_length /* if ((op & 16) == 0) 8% */ + and cl, 15 /* op &= 15 */ + jz L_decode_distance /* if (!op) */ + cmp bl, cl + jae L_add_bits_to_len /* if (op <= bits) */ + + mov ch, cl /* stash op in ch, freeing cl */ + xor eax, eax + lodsw /* al = *(ushort *)in++ */ + mov cl, bl /* cl = bits, needs it for shifting */ + add bl, 16 /* bits += 16 */ + shl eax, cl + or edx, eax /* hold |= *((ushort *)in)++ << bits */ + mov cl, ch /* move op back to ecx */ + +L_add_bits_to_len: + sub bl, cl + xor eax, eax + inc eax + shl eax, cl + dec eax + and eax, edx /* eax &= hold */ + shr edx, cl + add [esp+64], eax /* len += hold & mask[op] */ + +L_decode_distance: + cmp bl, 15 + ja L_get_distance_code /* if (15 < bits) */ + + xor eax, eax + lodsw /* al = *(ushort *)in++ */ + mov cl, bl /* cl = bits, needs it for shifting */ + add bl, 16 /* bits += 16 */ + shl eax, cl + or edx, eax /* hold |= *((ushort *)in)++ << bits */ + +L_get_distance_code: + mov eax, [esp+60] /* eax = dmask */ + mov ecx, [esp+36] /* ecx = dcode */ + and eax, edx /* eax &= hold */ + mov eax, [ecx+eax*4]/* eax = dcode[hold & dmask] */ + +L_dodist: + mov ebp, eax /* dist = this */ + shr ebp, 16 /* dist = this.val */ + mov cl, ah + sub bl, ah /* bits -= this.bits */ + shr edx, cl /* hold >>= this.bits */ + mov cl, al /* cl = this.op */ + + test al, 16 /* if ((op & 16) == 0) */ + jz L_test_for_second_level_dist + and cl, 15 /* op &= 15 */ + jz L_check_dist_one + cmp bl, cl + jae L_add_bits_to_dist /* if (op <= bits) 97.6% */ + + mov ch, cl /* stash op in ch, freeing cl */ + xor eax, eax + lodsw /* al = *(ushort *)in++ */ + mov cl, bl /* cl = bits, needs it for shifting */ + add bl, 16 /* bits += 16 */ + shl eax, cl + or edx, eax /* hold |= *((ushort *)in)++ << bits */ + mov cl, ch /* move op back to ecx */ + +L_add_bits_to_dist: + sub bl, cl + xor eax, eax + inc eax + shl eax, cl + dec eax /* (1 << op) - 1 */ + and eax, edx /* eax &= hold */ + shr edx, cl + add ebp, eax /* dist += hold & ((1 << op) - 1) */ + +L_check_window: + mov [esp+8], esi /* save in so from can use it's reg */ + mov eax, edi + sub eax, [esp+20] /* nbytes = out - beg */ + + cmp eax, ebp + jb L_clip_window /* if (dist > nbytes) 4.2% */ + + mov ecx, [esp+64] /* ecx = len */ + mov esi, edi + sub esi, ebp /* from = out - dist */ + + sar ecx, 1 + jnc L_copy_two + + rep movsw + mov al, [esi] + mov [edi], al + inc edi + + mov esi, [esp+8] /* move in back to %esi, toss from */ + mov ebp, [esp+32] /* ebp = lcode */ + jmp L_while_test + +L_copy_two: + rep movsw + mov esi, [esp+8] /* move in back to %esi, toss from */ + mov ebp, [esp+32] /* ebp = lcode */ + jmp L_while_test + +ALIGN 4 +L_check_dist_one: + cmp ebp, 1 /* if dist 1, is a memset */ + jne L_check_window + cmp [esp+20], edi + je L_check_window /* out == beg, if outside window */ + + mov ecx, [esp+64] /* ecx = len */ + mov al, [edi-1] + mov ah, al + + sar ecx, 1 + jnc L_set_two + mov [edi], al /* memset out with from[-1] */ + inc edi + +L_set_two: + rep stosw + mov ebp, [esp+32] /* ebp = lcode */ + jmp L_while_test + +ALIGN 4 +L_test_for_second_level_length: + test al, 64 + jnz L_test_for_end_of_block /* if ((op & 64) != 0) */ + + xor eax, eax + inc eax + shl eax, cl + dec eax + and eax, edx /* eax &= hold */ + add eax, [esp+64] /* eax += len */ + mov eax, [ebp+eax*4] /* eax = lcode[val+(hold&mask[op])]*/ + jmp L_dolen + +ALIGN 4 +L_test_for_second_level_dist: + test al, 64 + jnz L_invalid_distance_code /* if ((op & 64) != 0) */ + + xor eax, eax + inc eax + shl eax, cl + dec eax + and eax, edx /* eax &= hold */ + add eax, ebp /* eax += dist */ + mov ecx, [esp+36] /* ecx = dcode */ + mov eax, [ecx+eax*4] /* eax = dcode[val+(hold&mask[op])]*/ + jmp L_dodist + +ALIGN 4 +L_clip_window: + mov ecx, eax + mov eax, [esp+48] /* eax = wsize */ + neg ecx /* nbytes = -nbytes */ + mov esi, [esp+28] /* from = window */ + + cmp eax, ebp + jb L_invalid_distance_too_far /* if (dist > wsize) */ + + add ecx, ebp /* nbytes = dist - nbytes */ + cmp dword ptr [esp+52], 0 + jne L_wrap_around_window /* if (write != 0) */ + + sub eax, ecx + add esi, eax /* from += wsize - nbytes */ + + mov eax, [esp+64] /* eax = len */ + cmp eax, ecx + jbe L_do_copy /* if (nbytes >= len) */ + + sub eax, ecx /* len -= nbytes */ + rep movsb + mov esi, edi + sub esi, ebp /* from = out - dist */ + jmp L_do_copy + +ALIGN 4 +L_wrap_around_window: + mov eax, [esp+52] /* eax = write */ + cmp ecx, eax + jbe L_contiguous_in_window /* if (write >= nbytes) */ + + add esi, [esp+48] /* from += wsize */ + add esi, eax /* from += write */ + sub esi, ecx /* from -= nbytes */ + sub ecx, eax /* nbytes -= write */ + + mov eax, [esp+64] /* eax = len */ + cmp eax, ecx + jbe L_do_copy /* if (nbytes >= len) */ + + sub eax, ecx /* len -= nbytes */ + rep movsb + mov esi, [esp+28] /* from = window */ + mov ecx, [esp+52] /* nbytes = write */ + cmp eax, ecx + jbe L_do_copy /* if (nbytes >= len) */ + + sub eax, ecx /* len -= nbytes */ + rep movsb + mov esi, edi + sub esi, ebp /* from = out - dist */ + jmp L_do_copy + +ALIGN 4 +L_contiguous_in_window: + add esi, eax + sub esi, ecx /* from += write - nbytes */ + + mov eax, [esp+64] /* eax = len */ + cmp eax, ecx + jbe L_do_copy /* if (nbytes >= len) */ + + sub eax, ecx /* len -= nbytes */ + rep movsb + mov esi, edi + sub esi, ebp /* from = out - dist */ + jmp L_do_copy + +ALIGN 4 +L_do_copy: + mov ecx, eax + rep movsb + + mov esi, [esp+8] /* move in back to %esi, toss from */ + mov ebp, [esp+32] /* ebp = lcode */ + jmp L_while_test + +L_test_for_end_of_block: + test al, 32 + jz L_invalid_literal_length_code + mov dword ptr [esp+72], 1 + jmp L_break_loop_with_status + +L_invalid_literal_length_code: + mov dword ptr [esp+72], 2 + jmp L_break_loop_with_status + +L_invalid_distance_code: + mov dword ptr [esp+72], 3 + jmp L_break_loop_with_status + +L_invalid_distance_too_far: + mov esi, [esp+4] + mov dword ptr [esp+72], 4 + jmp L_break_loop_with_status + +L_break_loop: + mov dword ptr [esp+72], 0 + +L_break_loop_with_status: +/* put in, out, bits, and hold back into ar and pop esp */ + mov [esp+8], esi /* save in */ + mov [esp+16], edi /* save out */ + mov [esp+44], ebx /* save bits */ + mov [esp+40], edx /* save hold */ + mov ebp, [esp+4] /* restore esp, ebp */ + mov esp, [esp] + } +#else +#error "x86 architecture not defined" +#endif + + if (ar.status > 1) { + if (ar.status == 2) + strm->msg = "invalid literal/length code"; + else if (ar.status == 3) + strm->msg = "invalid distance code"; + else + strm->msg = "invalid distance too far back"; + state->mode = BAD; + } + else if ( ar.status == 1 ) { + state->mode = TYPE; + } + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + ar.len = ar.bits >> 3; + ar.in -= ar.len; + ar.bits -= ar.len << 3; + ar.hold &= (1U << ar.bits) - 1; + + /* update state and return */ + strm->next_in = ar.in; + strm->next_out = ar.out; + strm->avail_in = (unsigned)(ar.in < ar.last ? + PAD_AVAIL_IN + (ar.last - ar.in) : + PAD_AVAIL_IN - (ar.in - ar.last)); + strm->avail_out = (unsigned)(ar.out < ar.end ? + PAD_AVAIL_OUT + (ar.end - ar.out) : + PAD_AVAIL_OUT - (ar.out - ar.end)); + state->hold = ar.hold; + state->bits = ar.bits; + return; +} + diff --git a/src/png/zlib/contrib/inflate86/inffast.S b/src/png/zlib/contrib/inflate86/inffast.S new file mode 100644 index 0000000000..2245a2905b --- /dev/null +++ b/src/png/zlib/contrib/inflate86/inffast.S @@ -0,0 +1,1368 @@ +/* + * inffast.S is a hand tuned assembler version of: + * + * inffast.c -- fast decoding + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Copyright (C) 2003 Chris Anderson + * Please use the copyright conditions above. + * + * This version (Jan-23-2003) of inflate_fast was coded and tested under + * GNU/Linux on a pentium 3, using the gcc-3.2 compiler distribution. On that + * machine, I found that gzip style archives decompressed about 20% faster than + * the gcc-3.2 -O3 -fomit-frame-pointer compiled version. Your results will + * depend on how large of a buffer is used for z_stream.next_in & next_out + * (8K-32K worked best for my 256K cpu cache) and how much overhead there is in + * stream processing I/O and crc32/addler32. In my case, this routine used + * 70% of the cpu time and crc32 used 20%. + * + * I am confident that this version will work in the general case, but I have + * not tested a wide variety of datasets or a wide variety of platforms. + * + * Jan-24-2003 -- Added -DUSE_MMX define for slightly faster inflating. + * It should be a runtime flag instead of compile time flag... + * + * Jan-26-2003 -- Added runtime check for MMX support with cpuid instruction. + * With -DUSE_MMX, only MMX code is compiled. With -DNO_MMX, only non-MMX code + * is compiled. Without either option, runtime detection is enabled. Runtime + * detection should work on all modern cpus and the recomended algorithm (flip + * ID bit on eflags and then use the cpuid instruction) is used in many + * multimedia applications. Tested under win2k with gcc-2.95 and gas-2.12 + * distributed with cygwin3. Compiling with gcc-2.95 -c inffast.S -o + * inffast.obj generates a COFF object which can then be linked with MSVC++ + * compiled code. Tested under FreeBSD 4.7 with gcc-2.95. + * + * Jan-28-2003 -- Tested Athlon XP... MMX mode is slower than no MMX (and + * slower than compiler generated code). Adjusted cpuid check to use the MMX + * code only for Pentiums < P4 until I have more data on the P4. Speed + * improvment is only about 15% on the Athlon when compared with code generated + * with MSVC++. Not sure yet, but I think the P4 will also be slower using the + * MMX mode because many of it's x86 ALU instructions execute in .5 cycles and + * have less latency than MMX ops. Added code to buffer the last 11 bytes of + * the input stream since the MMX code grabs bits in chunks of 32, which + * differs from the inffast.c algorithm. I don't think there would have been + * read overruns where a page boundary was crossed (a segfault), but there + * could have been overruns when next_in ends on unaligned memory (unintialized + * memory read). + * + * Mar-13-2003 -- P4 MMX is slightly slower than P4 NO_MMX. I created a C + * version of the non-MMX code so that it doesn't depend on zstrm and zstate + * structure offsets which are hard coded in this file. This was last tested + * with zlib-1.2.0 which is currently in beta testing, newer versions of this + * and inffas86.c can be found at http://www.eetbeetee.com/zlib/ and + * http://www.charm.net/~christop/zlib/ + */ + + +/* + * if you have underscore linking problems (_inflate_fast undefined), try + * using -DGAS_COFF + */ +#if ! defined( GAS_COFF ) && ! defined( GAS_ELF ) + +#if defined( WIN32 ) || defined( __CYGWIN__ ) +#define GAS_COFF /* windows object format */ +#else +#define GAS_ELF +#endif + +#endif /* ! GAS_COFF && ! GAS_ELF */ + + +#if defined( GAS_COFF ) + +/* coff externals have underscores */ +#define inflate_fast _inflate_fast +#define inflate_fast_use_mmx _inflate_fast_use_mmx + +#endif /* GAS_COFF */ + + +.file "inffast.S" + +.globl inflate_fast + +.text +.align 4,0 +.L_invalid_literal_length_code_msg: +.string "invalid literal/length code" + +.align 4,0 +.L_invalid_distance_code_msg: +.string "invalid distance code" + +.align 4,0 +.L_invalid_distance_too_far_msg: +.string "invalid distance too far back" + +#if ! defined( NO_MMX ) +.align 4,0 +.L_mask: /* mask[N] = ( 1 << N ) - 1 */ +.long 0 +.long 1 +.long 3 +.long 7 +.long 15 +.long 31 +.long 63 +.long 127 +.long 255 +.long 511 +.long 1023 +.long 2047 +.long 4095 +.long 8191 +.long 16383 +.long 32767 +.long 65535 +.long 131071 +.long 262143 +.long 524287 +.long 1048575 +.long 2097151 +.long 4194303 +.long 8388607 +.long 16777215 +.long 33554431 +.long 67108863 +.long 134217727 +.long 268435455 +.long 536870911 +.long 1073741823 +.long 2147483647 +.long 4294967295 +#endif /* NO_MMX */ + +.text + +/* + * struct z_stream offsets, in zlib.h + */ +#define next_in_strm 0 /* strm->next_in */ +#define avail_in_strm 4 /* strm->avail_in */ +#define next_out_strm 12 /* strm->next_out */ +#define avail_out_strm 16 /* strm->avail_out */ +#define msg_strm 24 /* strm->msg */ +#define state_strm 28 /* strm->state */ + +/* + * struct inflate_state offsets, in inflate.h + */ +#define mode_state 0 /* state->mode */ +#define wsize_state 32 /* state->wsize */ +#define write_state 40 /* state->write */ +#define window_state 44 /* state->window */ +#define hold_state 48 /* state->hold */ +#define bits_state 52 /* state->bits */ +#define lencode_state 68 /* state->lencode */ +#define distcode_state 72 /* state->distcode */ +#define lenbits_state 76 /* state->lenbits */ +#define distbits_state 80 /* state->distbits */ + +/* + * inflate_fast's activation record + */ +#define local_var_size 64 /* how much local space for vars */ +#define strm_sp 88 /* first arg: z_stream * (local_var_size + 24) */ +#define start_sp 92 /* second arg: unsigned int (local_var_size + 28) */ + +/* + * offsets for local vars on stack + */ +#define out 60 /* unsigned char* */ +#define window 56 /* unsigned char* */ +#define wsize 52 /* unsigned int */ +#define write 48 /* unsigned int */ +#define in 44 /* unsigned char* */ +#define beg 40 /* unsigned char* */ +#define buf 28 /* char[ 12 ] */ +#define len 24 /* unsigned int */ +#define last 20 /* unsigned char* */ +#define end 16 /* unsigned char* */ +#define dcode 12 /* code* */ +#define lcode 8 /* code* */ +#define dmask 4 /* unsigned int */ +#define lmask 0 /* unsigned int */ + +/* + * typedef enum inflate_mode consts, in inflate.h + */ +#define INFLATE_MODE_TYPE 11 /* state->mode flags enum-ed in inflate.h */ +#define INFLATE_MODE_BAD 26 + + +#if ! defined( USE_MMX ) && ! defined( NO_MMX ) + +#define RUN_TIME_MMX + +#define CHECK_MMX 1 +#define DO_USE_MMX 2 +#define DONT_USE_MMX 3 + +.globl inflate_fast_use_mmx + +.data + +.align 4,0 +inflate_fast_use_mmx: /* integer flag for run time control 1=check,2=mmx,3=no */ +.long CHECK_MMX + +#if defined( GAS_ELF ) +/* elf info */ +.type inflate_fast_use_mmx,@object +.size inflate_fast_use_mmx,4 +#endif + +#endif /* RUN_TIME_MMX */ + +#if defined( GAS_COFF ) +/* coff info: scl 2 = extern, type 32 = function */ +.def inflate_fast; .scl 2; .type 32; .endef +#endif + +.text + +.align 32,0x90 +inflate_fast: + pushl %edi + pushl %esi + pushl %ebp + pushl %ebx + pushf /* save eflags (strm_sp, state_sp assumes this is 32 bits) */ + subl $local_var_size, %esp + cld + +#define strm_r %esi +#define state_r %edi + + movl strm_sp(%esp), strm_r + movl state_strm(strm_r), state_r + + /* in = strm->next_in; + * out = strm->next_out; + * last = in + strm->avail_in - 11; + * beg = out - (start - strm->avail_out); + * end = out + (strm->avail_out - 257); + */ + movl avail_in_strm(strm_r), %edx + movl next_in_strm(strm_r), %eax + + addl %eax, %edx /* avail_in += next_in */ + subl $11, %edx /* avail_in -= 11 */ + + movl %eax, in(%esp) + movl %edx, last(%esp) + + movl start_sp(%esp), %ebp + movl avail_out_strm(strm_r), %ecx + movl next_out_strm(strm_r), %ebx + + subl %ecx, %ebp /* start -= avail_out */ + negl %ebp /* start = -start */ + addl %ebx, %ebp /* start += next_out */ + + subl $257, %ecx /* avail_out -= 257 */ + addl %ebx, %ecx /* avail_out += out */ + + movl %ebx, out(%esp) + movl %ebp, beg(%esp) + movl %ecx, end(%esp) + + /* wsize = state->wsize; + * write = state->write; + * window = state->window; + * hold = state->hold; + * bits = state->bits; + * lcode = state->lencode; + * dcode = state->distcode; + * lmask = ( 1 << state->lenbits ) - 1; + * dmask = ( 1 << state->distbits ) - 1; + */ + + movl lencode_state(state_r), %eax + movl distcode_state(state_r), %ecx + + movl %eax, lcode(%esp) + movl %ecx, dcode(%esp) + + movl $1, %eax + movl lenbits_state(state_r), %ecx + shll %cl, %eax + decl %eax + movl %eax, lmask(%esp) + + movl $1, %eax + movl distbits_state(state_r), %ecx + shll %cl, %eax + decl %eax + movl %eax, dmask(%esp) + + movl wsize_state(state_r), %eax + movl write_state(state_r), %ecx + movl window_state(state_r), %edx + + movl %eax, wsize(%esp) + movl %ecx, write(%esp) + movl %edx, window(%esp) + + movl hold_state(state_r), %ebp + movl bits_state(state_r), %ebx + +#undef strm_r +#undef state_r + +#define in_r %esi +#define from_r %esi +#define out_r %edi + + movl in(%esp), in_r + movl last(%esp), %ecx + cmpl in_r, %ecx + ja .L_align_long /* if in < last */ + + addl $11, %ecx /* ecx = &in[ avail_in ] */ + subl in_r, %ecx /* ecx = avail_in */ + movl $12, %eax + subl %ecx, %eax /* eax = 12 - avail_in */ + leal buf(%esp), %edi + rep movsb /* memcpy( buf, in, avail_in ) */ + movl %eax, %ecx + xorl %eax, %eax + rep stosb /* memset( &buf[ avail_in ], 0, 12 - avail_in ) */ + leal buf(%esp), in_r /* in = buf */ + movl in_r, last(%esp) /* last = in, do just one iteration */ + jmp .L_is_aligned + + /* align in_r on long boundary */ +.L_align_long: + testl $3, in_r + jz .L_is_aligned + xorl %eax, %eax + movb (in_r), %al + incl in_r + movl %ebx, %ecx + addl $8, %ebx + shll %cl, %eax + orl %eax, %ebp + jmp .L_align_long + +.L_is_aligned: + movl out(%esp), out_r + +#if defined( NO_MMX ) + jmp .L_do_loop +#endif + +#if defined( USE_MMX ) + jmp .L_init_mmx +#endif + +/*** Runtime MMX check ***/ + +#if defined( RUN_TIME_MMX ) +.L_check_mmx: + cmpl $DO_USE_MMX, inflate_fast_use_mmx + je .L_init_mmx + ja .L_do_loop /* > 2 */ + + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + pushf + movl (%esp), %eax /* copy eflags to eax */ + xorl $0x200000, (%esp) /* try toggling ID bit of eflags (bit 21) + * to see if cpu supports cpuid... + * ID bit method not supported by NexGen but + * bios may load a cpuid instruction and + * cpuid may be disabled on Cyrix 5-6x86 */ + popf + pushf + popl %edx /* copy new eflags to edx */ + xorl %eax, %edx /* test if ID bit is flipped */ + jz .L_dont_use_mmx /* not flipped if zero */ + xorl %eax, %eax + cpuid + cmpl $0x756e6547, %ebx /* check for GenuineIntel in ebx,ecx,edx */ + jne .L_dont_use_mmx + cmpl $0x6c65746e, %ecx + jne .L_dont_use_mmx + cmpl $0x49656e69, %edx + jne .L_dont_use_mmx + movl $1, %eax + cpuid /* get cpu features */ + shrl $8, %eax + andl $15, %eax + cmpl $6, %eax /* check for Pentium family, is 0xf for P4 */ + jne .L_dont_use_mmx + testl $0x800000, %edx /* test if MMX feature is set (bit 23) */ + jnz .L_use_mmx + jmp .L_dont_use_mmx +.L_use_mmx: + movl $DO_USE_MMX, inflate_fast_use_mmx + jmp .L_check_mmx_pop +.L_dont_use_mmx: + movl $DONT_USE_MMX, inflate_fast_use_mmx +.L_check_mmx_pop: + popl %edx + popl %ecx + popl %ebx + popl %eax + jmp .L_check_mmx +#endif + + +/*** Non-MMX code ***/ + +#if defined ( NO_MMX ) || defined( RUN_TIME_MMX ) + +#define hold_r %ebp +#define bits_r %bl +#define bitslong_r %ebx + +.align 32,0x90 +.L_while_test: + /* while (in < last && out < end) + */ + cmpl out_r, end(%esp) + jbe .L_break_loop /* if (out >= end) */ + + cmpl in_r, last(%esp) + jbe .L_break_loop + +.L_do_loop: + /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out + * + * do { + * if (bits < 15) { + * hold |= *((unsigned short *)in)++ << bits; + * bits += 16 + * } + * this = lcode[hold & lmask] + */ + cmpb $15, bits_r + ja .L_get_length_code /* if (15 < bits) */ + + xorl %eax, %eax + lodsw /* al = *(ushort *)in++ */ + movb bits_r, %cl /* cl = bits, needs it for shifting */ + addb $16, bits_r /* bits += 16 */ + shll %cl, %eax + orl %eax, hold_r /* hold |= *((ushort *)in)++ << bits */ + +.L_get_length_code: + movl lmask(%esp), %edx /* edx = lmask */ + movl lcode(%esp), %ecx /* ecx = lcode */ + andl hold_r, %edx /* edx &= hold */ + movl (%ecx,%edx,4), %eax /* eax = lcode[hold & lmask] */ + +.L_dolen: + /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out + * + * dolen: + * bits -= this.bits; + * hold >>= this.bits + */ + movb %ah, %cl /* cl = this.bits */ + subb %ah, bits_r /* bits -= this.bits */ + shrl %cl, hold_r /* hold >>= this.bits */ + + /* check if op is a literal + * if (op == 0) { + * PUP(out) = this.val; + * } + */ + testb %al, %al + jnz .L_test_for_length_base /* if (op != 0) 45.7% */ + + shrl $16, %eax /* output this.val char */ + stosb + jmp .L_while_test + +.L_test_for_length_base: + /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out, %edx = len + * + * else if (op & 16) { + * len = this.val + * op &= 15 + * if (op) { + * if (op > bits) { + * hold |= *((unsigned short *)in)++ << bits; + * bits += 16 + * } + * len += hold & mask[op]; + * bits -= op; + * hold >>= op; + * } + */ +#define len_r %edx + movl %eax, len_r /* len = this */ + shrl $16, len_r /* len = this.val */ + movb %al, %cl + + testb $16, %al + jz .L_test_for_second_level_length /* if ((op & 16) == 0) 8% */ + andb $15, %cl /* op &= 15 */ + jz .L_save_len /* if (!op) */ + cmpb %cl, bits_r + jae .L_add_bits_to_len /* if (op <= bits) */ + + movb %cl, %ch /* stash op in ch, freeing cl */ + xorl %eax, %eax + lodsw /* al = *(ushort *)in++ */ + movb bits_r, %cl /* cl = bits, needs it for shifting */ + addb $16, bits_r /* bits += 16 */ + shll %cl, %eax + orl %eax, hold_r /* hold |= *((ushort *)in)++ << bits */ + movb %ch, %cl /* move op back to ecx */ + +.L_add_bits_to_len: + movl $1, %eax + shll %cl, %eax + decl %eax + subb %cl, bits_r + andl hold_r, %eax /* eax &= hold */ + shrl %cl, hold_r + addl %eax, len_r /* len += hold & mask[op] */ + +.L_save_len: + movl len_r, len(%esp) /* save len */ +#undef len_r + +.L_decode_distance: + /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out, %edx = dist + * + * if (bits < 15) { + * hold |= *((unsigned short *)in)++ << bits; + * bits += 16 + * } + * this = dcode[hold & dmask]; + * dodist: + * bits -= this.bits; + * hold >>= this.bits; + * op = this.op; + */ + + cmpb $15, bits_r + ja .L_get_distance_code /* if (15 < bits) */ + + xorl %eax, %eax + lodsw /* al = *(ushort *)in++ */ + movb bits_r, %cl /* cl = bits, needs it for shifting */ + addb $16, bits_r /* bits += 16 */ + shll %cl, %eax + orl %eax, hold_r /* hold |= *((ushort *)in)++ << bits */ + +.L_get_distance_code: + movl dmask(%esp), %edx /* edx = dmask */ + movl dcode(%esp), %ecx /* ecx = dcode */ + andl hold_r, %edx /* edx &= hold */ + movl (%ecx,%edx,4), %eax /* eax = dcode[hold & dmask] */ + +#define dist_r %edx +.L_dodist: + movl %eax, dist_r /* dist = this */ + shrl $16, dist_r /* dist = this.val */ + movb %ah, %cl + subb %ah, bits_r /* bits -= this.bits */ + shrl %cl, hold_r /* hold >>= this.bits */ + + /* if (op & 16) { + * dist = this.val + * op &= 15 + * if (op > bits) { + * hold |= *((unsigned short *)in)++ << bits; + * bits += 16 + * } + * dist += hold & mask[op]; + * bits -= op; + * hold >>= op; + */ + movb %al, %cl /* cl = this.op */ + + testb $16, %al /* if ((op & 16) == 0) */ + jz .L_test_for_second_level_dist + andb $15, %cl /* op &= 15 */ + jz .L_check_dist_one + cmpb %cl, bits_r + jae .L_add_bits_to_dist /* if (op <= bits) 97.6% */ + + movb %cl, %ch /* stash op in ch, freeing cl */ + xorl %eax, %eax + lodsw /* al = *(ushort *)in++ */ + movb bits_r, %cl /* cl = bits, needs it for shifting */ + addb $16, bits_r /* bits += 16 */ + shll %cl, %eax + orl %eax, hold_r /* hold |= *((ushort *)in)++ << bits */ + movb %ch, %cl /* move op back to ecx */ + +.L_add_bits_to_dist: + movl $1, %eax + shll %cl, %eax + decl %eax /* (1 << op) - 1 */ + subb %cl, bits_r + andl hold_r, %eax /* eax &= hold */ + shrl %cl, hold_r + addl %eax, dist_r /* dist += hold & ((1 << op) - 1) */ + jmp .L_check_window + +.L_check_window: + /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist + * %ecx = nbytes + * + * nbytes = out - beg; + * if (dist <= nbytes) { + * from = out - dist; + * do { + * PUP(out) = PUP(from); + * } while (--len > 0) { + * } + */ + + movl in_r, in(%esp) /* save in so from can use it's reg */ + movl out_r, %eax + subl beg(%esp), %eax /* nbytes = out - beg */ + + cmpl dist_r, %eax + jb .L_clip_window /* if (dist > nbytes) 4.2% */ + + movl len(%esp), %ecx + movl out_r, from_r + subl dist_r, from_r /* from = out - dist */ + + subl $3, %ecx + movb (from_r), %al + movb %al, (out_r) + movb 1(from_r), %al + movb 2(from_r), %dl + addl $3, from_r + movb %al, 1(out_r) + movb %dl, 2(out_r) + addl $3, out_r + rep movsb + + movl in(%esp), in_r /* move in back to %esi, toss from */ + jmp .L_while_test + +.align 16,0x90 +.L_check_dist_one: + cmpl $1, dist_r + jne .L_check_window + cmpl out_r, beg(%esp) + je .L_check_window + + decl out_r + movl len(%esp), %ecx + movb (out_r), %al + subl $3, %ecx + + movb %al, 1(out_r) + movb %al, 2(out_r) + movb %al, 3(out_r) + addl $4, out_r + rep stosb + + jmp .L_while_test + +.align 16,0x90 +.L_test_for_second_level_length: + /* else if ((op & 64) == 0) { + * this = lcode[this.val + (hold & mask[op])]; + * } + */ + testb $64, %al + jnz .L_test_for_end_of_block /* if ((op & 64) != 0) */ + + movl $1, %eax + shll %cl, %eax + decl %eax + andl hold_r, %eax /* eax &= hold */ + addl %edx, %eax /* eax += this.val */ + movl lcode(%esp), %edx /* edx = lcode */ + movl (%edx,%eax,4), %eax /* eax = lcode[val + (hold&mask[op])] */ + jmp .L_dolen + +.align 16,0x90 +.L_test_for_second_level_dist: + /* else if ((op & 64) == 0) { + * this = dcode[this.val + (hold & mask[op])]; + * } + */ + testb $64, %al + jnz .L_invalid_distance_code /* if ((op & 64) != 0) */ + + movl $1, %eax + shll %cl, %eax + decl %eax + andl hold_r, %eax /* eax &= hold */ + addl %edx, %eax /* eax += this.val */ + movl dcode(%esp), %edx /* edx = dcode */ + movl (%edx,%eax,4), %eax /* eax = dcode[val + (hold&mask[op])] */ + jmp .L_dodist + +.align 16,0x90 +.L_clip_window: + /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist + * %ecx = nbytes + * + * else { + * if (dist > wsize) { + * invalid distance + * } + * from = window; + * nbytes = dist - nbytes; + * if (write == 0) { + * from += wsize - nbytes; + */ +#define nbytes_r %ecx + movl %eax, nbytes_r + movl wsize(%esp), %eax /* prepare for dist compare */ + negl nbytes_r /* nbytes = -nbytes */ + movl window(%esp), from_r /* from = window */ + + cmpl dist_r, %eax + jb .L_invalid_distance_too_far /* if (dist > wsize) */ + + addl dist_r, nbytes_r /* nbytes = dist - nbytes */ + cmpl $0, write(%esp) + jne .L_wrap_around_window /* if (write != 0) */ + + subl nbytes_r, %eax + addl %eax, from_r /* from += wsize - nbytes */ + + /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist + * %ecx = nbytes, %eax = len + * + * if (nbytes < len) { + * len -= nbytes; + * do { + * PUP(out) = PUP(from); + * } while (--nbytes); + * from = out - dist; + * } + * } + */ +#define len_r %eax + movl len(%esp), len_r + cmpl nbytes_r, len_r + jbe .L_do_copy1 /* if (nbytes >= len) */ + + subl nbytes_r, len_r /* len -= nbytes */ + rep movsb + movl out_r, from_r + subl dist_r, from_r /* from = out - dist */ + jmp .L_do_copy1 + + cmpl nbytes_r, len_r + jbe .L_do_copy1 /* if (nbytes >= len) */ + + subl nbytes_r, len_r /* len -= nbytes */ + rep movsb + movl out_r, from_r + subl dist_r, from_r /* from = out - dist */ + jmp .L_do_copy1 + +.L_wrap_around_window: + /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist + * %ecx = nbytes, %eax = write, %eax = len + * + * else if (write < nbytes) { + * from += wsize + write - nbytes; + * nbytes -= write; + * if (nbytes < len) { + * len -= nbytes; + * do { + * PUP(out) = PUP(from); + * } while (--nbytes); + * from = window; + * nbytes = write; + * if (nbytes < len) { + * len -= nbytes; + * do { + * PUP(out) = PUP(from); + * } while(--nbytes); + * from = out - dist; + * } + * } + * } + */ +#define write_r %eax + movl write(%esp), write_r + cmpl write_r, nbytes_r + jbe .L_contiguous_in_window /* if (write >= nbytes) */ + + addl wsize(%esp), from_r + addl write_r, from_r + subl nbytes_r, from_r /* from += wsize + write - nbytes */ + subl write_r, nbytes_r /* nbytes -= write */ +#undef write_r + + movl len(%esp), len_r + cmpl nbytes_r, len_r + jbe .L_do_copy1 /* if (nbytes >= len) */ + + subl nbytes_r, len_r /* len -= nbytes */ + rep movsb + movl window(%esp), from_r /* from = window */ + movl write(%esp), nbytes_r /* nbytes = write */ + cmpl nbytes_r, len_r + jbe .L_do_copy1 /* if (nbytes >= len) */ + + subl nbytes_r, len_r /* len -= nbytes */ + rep movsb + movl out_r, from_r + subl dist_r, from_r /* from = out - dist */ + jmp .L_do_copy1 + +.L_contiguous_in_window: + /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist + * %ecx = nbytes, %eax = write, %eax = len + * + * else { + * from += write - nbytes; + * if (nbytes < len) { + * len -= nbytes; + * do { + * PUP(out) = PUP(from); + * } while (--nbytes); + * from = out - dist; + * } + * } + */ +#define write_r %eax + addl write_r, from_r + subl nbytes_r, from_r /* from += write - nbytes */ +#undef write_r + + movl len(%esp), len_r + cmpl nbytes_r, len_r + jbe .L_do_copy1 /* if (nbytes >= len) */ + + subl nbytes_r, len_r /* len -= nbytes */ + rep movsb + movl out_r, from_r + subl dist_r, from_r /* from = out - dist */ + +.L_do_copy1: + /* regs: %esi = from, %esi = in, %ebp = hold, %bl = bits, %edi = out + * %eax = len + * + * while (len > 0) { + * PUP(out) = PUP(from); + * len--; + * } + * } + * } while (in < last && out < end); + */ +#undef nbytes_r +#define in_r %esi + movl len_r, %ecx + rep movsb + + movl in(%esp), in_r /* move in back to %esi, toss from */ + jmp .L_while_test + +#undef len_r +#undef dist_r + +#endif /* NO_MMX || RUN_TIME_MMX */ + + +/*** MMX code ***/ + +#if defined( USE_MMX ) || defined( RUN_TIME_MMX ) + +.align 32,0x90 +.L_init_mmx: + emms + +#undef bits_r +#undef bitslong_r +#define bitslong_r %ebp +#define hold_mm %mm0 + movd %ebp, hold_mm + movl %ebx, bitslong_r + +#define used_mm %mm1 +#define dmask2_mm %mm2 +#define lmask2_mm %mm3 +#define lmask_mm %mm4 +#define dmask_mm %mm5 +#define tmp_mm %mm6 + + movd lmask(%esp), lmask_mm + movq lmask_mm, lmask2_mm + movd dmask(%esp), dmask_mm + movq dmask_mm, dmask2_mm + pxor used_mm, used_mm + movl lcode(%esp), %ebx /* ebx = lcode */ + jmp .L_do_loop_mmx + +.align 32,0x90 +.L_while_test_mmx: + /* while (in < last && out < end) + */ + cmpl out_r, end(%esp) + jbe .L_break_loop /* if (out >= end) */ + + cmpl in_r, last(%esp) + jbe .L_break_loop + +.L_do_loop_mmx: + psrlq used_mm, hold_mm /* hold_mm >>= last bit length */ + + cmpl $32, bitslong_r + ja .L_get_length_code_mmx /* if (32 < bits) */ + + movd bitslong_r, tmp_mm + movd (in_r), %mm7 + addl $4, in_r + psllq tmp_mm, %mm7 + addl $32, bitslong_r + por %mm7, hold_mm /* hold_mm |= *((uint *)in)++ << bits */ + +.L_get_length_code_mmx: + pand hold_mm, lmask_mm + movd lmask_mm, %eax + movq lmask2_mm, lmask_mm + movl (%ebx,%eax,4), %eax /* eax = lcode[hold & lmask] */ + +.L_dolen_mmx: + movzbl %ah, %ecx /* ecx = this.bits */ + movd %ecx, used_mm + subl %ecx, bitslong_r /* bits -= this.bits */ + + testb %al, %al + jnz .L_test_for_length_base_mmx /* if (op != 0) 45.7% */ + + shrl $16, %eax /* output this.val char */ + stosb + jmp .L_while_test_mmx + +.L_test_for_length_base_mmx: +#define len_r %edx + movl %eax, len_r /* len = this */ + shrl $16, len_r /* len = this.val */ + + testb $16, %al + jz .L_test_for_second_level_length_mmx /* if ((op & 16) == 0) 8% */ + andl $15, %eax /* op &= 15 */ + jz .L_decode_distance_mmx /* if (!op) */ + + psrlq used_mm, hold_mm /* hold_mm >>= last bit length */ + movd %eax, used_mm + movd hold_mm, %ecx + subl %eax, bitslong_r + andl .L_mask(,%eax,4), %ecx + addl %ecx, len_r /* len += hold & mask[op] */ + +.L_decode_distance_mmx: + psrlq used_mm, hold_mm /* hold_mm >>= last bit length */ + + cmpl $32, bitslong_r + ja .L_get_dist_code_mmx /* if (32 < bits) */ + + movd bitslong_r, tmp_mm + movd (in_r), %mm7 + addl $4, in_r + psllq tmp_mm, %mm7 + addl $32, bitslong_r + por %mm7, hold_mm /* hold_mm |= *((uint *)in)++ << bits */ + +.L_get_dist_code_mmx: + movl dcode(%esp), %ebx /* ebx = dcode */ + pand hold_mm, dmask_mm + movd dmask_mm, %eax + movq dmask2_mm, dmask_mm + movl (%ebx,%eax,4), %eax /* eax = dcode[hold & lmask] */ + +.L_dodist_mmx: +#define dist_r %ebx + movzbl %ah, %ecx /* ecx = this.bits */ + movl %eax, dist_r + shrl $16, dist_r /* dist = this.val */ + subl %ecx, bitslong_r /* bits -= this.bits */ + movd %ecx, used_mm + + testb $16, %al /* if ((op & 16) == 0) */ + jz .L_test_for_second_level_dist_mmx + andl $15, %eax /* op &= 15 */ + jz .L_check_dist_one_mmx + +.L_add_bits_to_dist_mmx: + psrlq used_mm, hold_mm /* hold_mm >>= last bit length */ + movd %eax, used_mm /* save bit length of current op */ + movd hold_mm, %ecx /* get the next bits on input stream */ + subl %eax, bitslong_r /* bits -= op bits */ + andl .L_mask(,%eax,4), %ecx /* ecx = hold & mask[op] */ + addl %ecx, dist_r /* dist += hold & mask[op] */ + +.L_check_window_mmx: + movl in_r, in(%esp) /* save in so from can use it's reg */ + movl out_r, %eax + subl beg(%esp), %eax /* nbytes = out - beg */ + + cmpl dist_r, %eax + jb .L_clip_window_mmx /* if (dist > nbytes) 4.2% */ + + movl len_r, %ecx + movl out_r, from_r + subl dist_r, from_r /* from = out - dist */ + + subl $3, %ecx + movb (from_r), %al + movb %al, (out_r) + movb 1(from_r), %al + movb 2(from_r), %dl + addl $3, from_r + movb %al, 1(out_r) + movb %dl, 2(out_r) + addl $3, out_r + rep movsb + + movl in(%esp), in_r /* move in back to %esi, toss from */ + movl lcode(%esp), %ebx /* move lcode back to %ebx, toss dist */ + jmp .L_while_test_mmx + +.align 16,0x90 +.L_check_dist_one_mmx: + cmpl $1, dist_r + jne .L_check_window_mmx + cmpl out_r, beg(%esp) + je .L_check_window_mmx + + decl out_r + movl len_r, %ecx + movb (out_r), %al + subl $3, %ecx + + movb %al, 1(out_r) + movb %al, 2(out_r) + movb %al, 3(out_r) + addl $4, out_r + rep stosb + + movl lcode(%esp), %ebx /* move lcode back to %ebx, toss dist */ + jmp .L_while_test_mmx + +.align 16,0x90 +.L_test_for_second_level_length_mmx: + testb $64, %al + jnz .L_test_for_end_of_block /* if ((op & 64) != 0) */ + + andl $15, %eax + psrlq used_mm, hold_mm /* hold_mm >>= last bit length */ + movd hold_mm, %ecx + andl .L_mask(,%eax,4), %ecx + addl len_r, %ecx + movl (%ebx,%ecx,4), %eax /* eax = lcode[hold & lmask] */ + jmp .L_dolen_mmx + +.align 16,0x90 +.L_test_for_second_level_dist_mmx: + testb $64, %al + jnz .L_invalid_distance_code /* if ((op & 64) != 0) */ + + andl $15, %eax + psrlq used_mm, hold_mm /* hold_mm >>= last bit length */ + movd hold_mm, %ecx + andl .L_mask(,%eax,4), %ecx + movl dcode(%esp), %eax /* ecx = dcode */ + addl dist_r, %ecx + movl (%eax,%ecx,4), %eax /* eax = lcode[hold & lmask] */ + jmp .L_dodist_mmx + +.align 16,0x90 +.L_clip_window_mmx: +#define nbytes_r %ecx + movl %eax, nbytes_r + movl wsize(%esp), %eax /* prepare for dist compare */ + negl nbytes_r /* nbytes = -nbytes */ + movl window(%esp), from_r /* from = window */ + + cmpl dist_r, %eax + jb .L_invalid_distance_too_far /* if (dist > wsize) */ + + addl dist_r, nbytes_r /* nbytes = dist - nbytes */ + cmpl $0, write(%esp) + jne .L_wrap_around_window_mmx /* if (write != 0) */ + + subl nbytes_r, %eax + addl %eax, from_r /* from += wsize - nbytes */ + + cmpl nbytes_r, len_r + jbe .L_do_copy1_mmx /* if (nbytes >= len) */ + + subl nbytes_r, len_r /* len -= nbytes */ + rep movsb + movl out_r, from_r + subl dist_r, from_r /* from = out - dist */ + jmp .L_do_copy1_mmx + + cmpl nbytes_r, len_r + jbe .L_do_copy1_mmx /* if (nbytes >= len) */ + + subl nbytes_r, len_r /* len -= nbytes */ + rep movsb + movl out_r, from_r + subl dist_r, from_r /* from = out - dist */ + jmp .L_do_copy1_mmx + +.L_wrap_around_window_mmx: +#define write_r %eax + movl write(%esp), write_r + cmpl write_r, nbytes_r + jbe .L_contiguous_in_window_mmx /* if (write >= nbytes) */ + + addl wsize(%esp), from_r + addl write_r, from_r + subl nbytes_r, from_r /* from += wsize + write - nbytes */ + subl write_r, nbytes_r /* nbytes -= write */ +#undef write_r + + cmpl nbytes_r, len_r + jbe .L_do_copy1_mmx /* if (nbytes >= len) */ + + subl nbytes_r, len_r /* len -= nbytes */ + rep movsb + movl window(%esp), from_r /* from = window */ + movl write(%esp), nbytes_r /* nbytes = write */ + cmpl nbytes_r, len_r + jbe .L_do_copy1_mmx /* if (nbytes >= len) */ + + subl nbytes_r, len_r /* len -= nbytes */ + rep movsb + movl out_r, from_r + subl dist_r, from_r /* from = out - dist */ + jmp .L_do_copy1_mmx + +.L_contiguous_in_window_mmx: +#define write_r %eax + addl write_r, from_r + subl nbytes_r, from_r /* from += write - nbytes */ +#undef write_r + + cmpl nbytes_r, len_r + jbe .L_do_copy1_mmx /* if (nbytes >= len) */ + + subl nbytes_r, len_r /* len -= nbytes */ + rep movsb + movl out_r, from_r + subl dist_r, from_r /* from = out - dist */ + +.L_do_copy1_mmx: +#undef nbytes_r +#define in_r %esi + movl len_r, %ecx + rep movsb + + movl in(%esp), in_r /* move in back to %esi, toss from */ + movl lcode(%esp), %ebx /* move lcode back to %ebx, toss dist */ + jmp .L_while_test_mmx + +#undef hold_r +#undef bitslong_r + +#endif /* USE_MMX || RUN_TIME_MMX */ + + +/*** USE_MMX, NO_MMX, and RUNTIME_MMX from here on ***/ + +.L_invalid_distance_code: + /* else { + * strm->msg = "invalid distance code"; + * state->mode = BAD; + * } + */ + movl $.L_invalid_distance_code_msg, %ecx + movl $INFLATE_MODE_BAD, %edx + jmp .L_update_stream_state + +.L_test_for_end_of_block: + /* else if (op & 32) { + * state->mode = TYPE; + * break; + * } + */ + testb $32, %al + jz .L_invalid_literal_length_code /* if ((op & 32) == 0) */ + + movl $0, %ecx + movl $INFLATE_MODE_TYPE, %edx + jmp .L_update_stream_state + +.L_invalid_literal_length_code: + /* else { + * strm->msg = "invalid literal/length code"; + * state->mode = BAD; + * } + */ + movl $.L_invalid_literal_length_code_msg, %ecx + movl $INFLATE_MODE_BAD, %edx + jmp .L_update_stream_state + +.L_invalid_distance_too_far: + /* strm->msg = "invalid distance too far back"; + * state->mode = BAD; + */ + movl in(%esp), in_r /* from_r has in's reg, put in back */ + movl $.L_invalid_distance_too_far_msg, %ecx + movl $INFLATE_MODE_BAD, %edx + jmp .L_update_stream_state + +.L_update_stream_state: + /* set strm->msg = %ecx, strm->state->mode = %edx */ + movl strm_sp(%esp), %eax + testl %ecx, %ecx /* if (msg != NULL) */ + jz .L_skip_msg + movl %ecx, msg_strm(%eax) /* strm->msg = msg */ +.L_skip_msg: + movl state_strm(%eax), %eax /* state = strm->state */ + movl %edx, mode_state(%eax) /* state->mode = edx (BAD | TYPE) */ + jmp .L_break_loop + +.align 32,0x90 +.L_break_loop: + +/* + * Regs: + * + * bits = %ebp when mmx, and in %ebx when non-mmx + * hold = %hold_mm when mmx, and in %ebp when non-mmx + * in = %esi + * out = %edi + */ + +#if defined( USE_MMX ) || defined( RUN_TIME_MMX ) + +#if defined( RUN_TIME_MMX ) + + cmpl $DO_USE_MMX, inflate_fast_use_mmx + jne .L_update_next_in + +#endif /* RUN_TIME_MMX */ + + movl %ebp, %ebx + +.L_update_next_in: + +#endif + +#define strm_r %eax +#define state_r %edx + + /* len = bits >> 3; + * in -= len; + * bits -= len << 3; + * hold &= (1U << bits) - 1; + * state->hold = hold; + * state->bits = bits; + * strm->next_in = in; + * strm->next_out = out; + */ + movl strm_sp(%esp), strm_r + movl %ebx, %ecx + movl state_strm(strm_r), state_r + shrl $3, %ecx + subl %ecx, in_r + shll $3, %ecx + subl %ecx, %ebx + movl out_r, next_out_strm(strm_r) + movl %ebx, bits_state(state_r) + movl %ebx, %ecx + + leal buf(%esp), %ebx + cmpl %ebx, last(%esp) + jne .L_buf_not_used /* if buf != last */ + + subl %ebx, in_r /* in -= buf */ + movl next_in_strm(strm_r), %ebx + movl %ebx, last(%esp) /* last = strm->next_in */ + addl %ebx, in_r /* in += strm->next_in */ + movl avail_in_strm(strm_r), %ebx + subl $11, %ebx + addl %ebx, last(%esp) /* last = &strm->next_in[ avail_in - 11 ] */ + +.L_buf_not_used: + movl in_r, next_in_strm(strm_r) + + movl $1, %ebx + shll %cl, %ebx + decl %ebx + +#if defined( USE_MMX ) || defined( RUN_TIME_MMX ) + +#if defined( RUN_TIME_MMX ) + + cmpl $DO_USE_MMX, inflate_fast_use_mmx + jne .L_update_hold + +#endif /* RUN_TIME_MMX */ + + psrlq used_mm, hold_mm /* hold_mm >>= last bit length */ + movd hold_mm, %ebp + + emms + +.L_update_hold: + +#endif /* USE_MMX || RUN_TIME_MMX */ + + andl %ebx, %ebp + movl %ebp, hold_state(state_r) + +#define last_r %ebx + + /* strm->avail_in = in < last ? 11 + (last - in) : 11 - (in - last) */ + movl last(%esp), last_r + cmpl in_r, last_r + jbe .L_last_is_smaller /* if (in >= last) */ + + subl in_r, last_r /* last -= in */ + addl $11, last_r /* last += 11 */ + movl last_r, avail_in_strm(strm_r) + jmp .L_fixup_out +.L_last_is_smaller: + subl last_r, in_r /* in -= last */ + negl in_r /* in = -in */ + addl $11, in_r /* in += 11 */ + movl in_r, avail_in_strm(strm_r) + +#undef last_r +#define end_r %ebx + +.L_fixup_out: + /* strm->avail_out = out < end ? 257 + (end - out) : 257 - (out - end)*/ + movl end(%esp), end_r + cmpl out_r, end_r + jbe .L_end_is_smaller /* if (out >= end) */ + + subl out_r, end_r /* end -= out */ + addl $257, end_r /* end += 257 */ + movl end_r, avail_out_strm(strm_r) + jmp .L_done +.L_end_is_smaller: + subl end_r, out_r /* out -= end */ + negl out_r /* out = -out */ + addl $257, out_r /* out += 257 */ + movl out_r, avail_out_strm(strm_r) + +#undef end_r +#undef strm_r +#undef state_r + +.L_done: + addl $local_var_size, %esp + popf + popl %ebx + popl %ebp + popl %esi + popl %edi + ret + +#if defined( GAS_ELF ) +/* elf info */ +.type inflate_fast,@function +.size inflate_fast,.-inflate_fast +#endif diff --git a/src/png/zlib/contrib/iostream/test.cpp b/src/png/zlib/contrib/iostream/test.cpp new file mode 100644 index 0000000000..7d265b3b5c --- /dev/null +++ b/src/png/zlib/contrib/iostream/test.cpp @@ -0,0 +1,24 @@ + +#include "zfstream.h" + +int main() { + + // Construct a stream object with this filebuffer. Anything sent + // to this stream will go to standard out. + gzofstream os( 1, ios::out ); + + // This text is getting compressed and sent to stdout. + // To prove this, run 'test | zcat'. + os << "Hello, Mommy" << endl; + + os << setcompressionlevel( Z_NO_COMPRESSION ); + os << "hello, hello, hi, ho!" << endl; + + setcompressionlevel( os, Z_DEFAULT_COMPRESSION ) + << "I'm compressing again" << endl; + + os.close(); + + return 0; + +} diff --git a/src/png/zlib/contrib/iostream/zfstream.cpp b/src/png/zlib/contrib/iostream/zfstream.cpp new file mode 100644 index 0000000000..d0cd85faaf --- /dev/null +++ b/src/png/zlib/contrib/iostream/zfstream.cpp @@ -0,0 +1,329 @@ + +#include "zfstream.h" + +gzfilebuf::gzfilebuf() : + file(NULL), + mode(0), + own_file_descriptor(0) +{ } + +gzfilebuf::~gzfilebuf() { + + sync(); + if ( own_file_descriptor ) + close(); + +} + +gzfilebuf *gzfilebuf::open( const char *name, + int io_mode ) { + + if ( is_open() ) + return NULL; + + char char_mode[10]; + char *p = char_mode; + + if ( io_mode & ios::in ) { + mode = ios::in; + *p++ = 'r'; + } else if ( io_mode & ios::app ) { + mode = ios::app; + *p++ = 'a'; + } else { + mode = ios::out; + *p++ = 'w'; + } + + if ( io_mode & ios::binary ) { + mode |= ios::binary; + *p++ = 'b'; + } + + // Hard code the compression level + if ( io_mode & (ios::out|ios::app )) { + *p++ = '9'; + } + + // Put the end-of-string indicator + *p = '\0'; + + if ( (file = gzopen(name, char_mode)) == NULL ) + return NULL; + + own_file_descriptor = 1; + + return this; + +} + +gzfilebuf *gzfilebuf::attach( int file_descriptor, + int io_mode ) { + + if ( is_open() ) + return NULL; + + char char_mode[10]; + char *p = char_mode; + + if ( io_mode & ios::in ) { + mode = ios::in; + *p++ = 'r'; + } else if ( io_mode & ios::app ) { + mode = ios::app; + *p++ = 'a'; + } else { + mode = ios::out; + *p++ = 'w'; + } + + if ( io_mode & ios::binary ) { + mode |= ios::binary; + *p++ = 'b'; + } + + // Hard code the compression level + if ( io_mode & (ios::out|ios::app )) { + *p++ = '9'; + } + + // Put the end-of-string indicator + *p = '\0'; + + if ( (file = gzdopen(file_descriptor, char_mode)) == NULL ) + return NULL; + + own_file_descriptor = 0; + + return this; + +} + +gzfilebuf *gzfilebuf::close() { + + if ( is_open() ) { + + sync(); + gzclose( file ); + file = NULL; + + } + + return this; + +} + +int gzfilebuf::setcompressionlevel( int comp_level ) { + + return gzsetparams(file, comp_level, -2); + +} + +int gzfilebuf::setcompressionstrategy( int comp_strategy ) { + + return gzsetparams(file, -2, comp_strategy); + +} + + +streampos gzfilebuf::seekoff( streamoff off, ios::seek_dir dir, int which ) { + + return streampos(EOF); + +} + +int gzfilebuf::underflow() { + + // If the file hasn't been opened for reading, error. + if ( !is_open() || !(mode & ios::in) ) + return EOF; + + // if a buffer doesn't exists, allocate one. + if ( !base() ) { + + if ( (allocate()) == EOF ) + return EOF; + setp(0,0); + + } else { + + if ( in_avail() ) + return (unsigned char) *gptr(); + + if ( out_waiting() ) { + if ( flushbuf() == EOF ) + return EOF; + } + + } + + // Attempt to fill the buffer. + + int result = fillbuf(); + if ( result == EOF ) { + // disable get area + setg(0,0,0); + return EOF; + } + + return (unsigned char) *gptr(); + +} + +int gzfilebuf::overflow( int c ) { + + if ( !is_open() || !(mode & ios::out) ) + return EOF; + + if ( !base() ) { + if ( allocate() == EOF ) + return EOF; + setg(0,0,0); + } else { + if (in_avail()) { + return EOF; + } + if (out_waiting()) { + if (flushbuf() == EOF) + return EOF; + } + } + + int bl = blen(); + setp( base(), base() + bl); + + if ( c != EOF ) { + + *pptr() = c; + pbump(1); + + } + + return 0; + +} + +int gzfilebuf::sync() { + + if ( !is_open() ) + return EOF; + + if ( out_waiting() ) + return flushbuf(); + + return 0; + +} + +int gzfilebuf::flushbuf() { + + int n; + char *q; + + q = pbase(); + n = pptr() - q; + + if ( gzwrite( file, q, n) < n ) + return EOF; + + setp(0,0); + + return 0; + +} + +int gzfilebuf::fillbuf() { + + int required; + char *p; + + p = base(); + + required = blen(); + + int t = gzread( file, p, required ); + + if ( t <= 0) return EOF; + + setg( base(), base(), base()+t); + + return t; + +} + +gzfilestream_common::gzfilestream_common() : + ios( gzfilestream_common::rdbuf() ) +{ } + +gzfilestream_common::~gzfilestream_common() +{ } + +void gzfilestream_common::attach( int fd, int io_mode ) { + + if ( !buffer.attach( fd, io_mode) ) + clear( ios::failbit | ios::badbit ); + else + clear(); + +} + +void gzfilestream_common::open( const char *name, int io_mode ) { + + if ( !buffer.open( name, io_mode ) ) + clear( ios::failbit | ios::badbit ); + else + clear(); + +} + +void gzfilestream_common::close() { + + if ( !buffer.close() ) + clear( ios::failbit | ios::badbit ); + +} + +gzfilebuf *gzfilestream_common::rdbuf() +{ + return &buffer; +} + +gzifstream::gzifstream() : + ios( gzfilestream_common::rdbuf() ) +{ + clear( ios::badbit ); +} + +gzifstream::gzifstream( const char *name, int io_mode ) : + ios( gzfilestream_common::rdbuf() ) +{ + gzfilestream_common::open( name, io_mode ); +} + +gzifstream::gzifstream( int fd, int io_mode ) : + ios( gzfilestream_common::rdbuf() ) +{ + gzfilestream_common::attach( fd, io_mode ); +} + +gzifstream::~gzifstream() { } + +gzofstream::gzofstream() : + ios( gzfilestream_common::rdbuf() ) +{ + clear( ios::badbit ); +} + +gzofstream::gzofstream( const char *name, int io_mode ) : + ios( gzfilestream_common::rdbuf() ) +{ + gzfilestream_common::open( name, io_mode ); +} + +gzofstream::gzofstream( int fd, int io_mode ) : + ios( gzfilestream_common::rdbuf() ) +{ + gzfilestream_common::attach( fd, io_mode ); +} + +gzofstream::~gzofstream() { } diff --git a/src/png/zlib/contrib/iostream/zfstream.h b/src/png/zlib/contrib/iostream/zfstream.h new file mode 100644 index 0000000000..ed79098a3a --- /dev/null +++ b/src/png/zlib/contrib/iostream/zfstream.h @@ -0,0 +1,128 @@ + +#ifndef zfstream_h +#define zfstream_h + +#include +#include "zlib.h" + +class gzfilebuf : public streambuf { + +public: + + gzfilebuf( ); + virtual ~gzfilebuf(); + + gzfilebuf *open( const char *name, int io_mode ); + gzfilebuf *attach( int file_descriptor, int io_mode ); + gzfilebuf *close(); + + int setcompressionlevel( int comp_level ); + int setcompressionstrategy( int comp_strategy ); + + inline int is_open() const { return (file !=NULL); } + + virtual streampos seekoff( streamoff, ios::seek_dir, int ); + + virtual int sync(); + +protected: + + virtual int underflow(); + virtual int overflow( int = EOF ); + +private: + + gzFile file; + short mode; + short own_file_descriptor; + + int flushbuf(); + int fillbuf(); + +}; + +class gzfilestream_common : virtual public ios { + + friend class gzifstream; + friend class gzofstream; + friend gzofstream &setcompressionlevel( gzofstream &, int ); + friend gzofstream &setcompressionstrategy( gzofstream &, int ); + +public: + virtual ~gzfilestream_common(); + + void attach( int fd, int io_mode ); + void open( const char *name, int io_mode ); + void close(); + +protected: + gzfilestream_common(); + +private: + gzfilebuf *rdbuf(); + + gzfilebuf buffer; + +}; + +class gzifstream : public gzfilestream_common, public istream { + +public: + + gzifstream(); + gzifstream( const char *name, int io_mode = ios::in ); + gzifstream( int fd, int io_mode = ios::in ); + + virtual ~gzifstream(); + +}; + +class gzofstream : public gzfilestream_common, public ostream { + +public: + + gzofstream(); + gzofstream( const char *name, int io_mode = ios::out ); + gzofstream( int fd, int io_mode = ios::out ); + + virtual ~gzofstream(); + +}; + +template class gzomanip { + friend gzofstream &operator<<(gzofstream &, const gzomanip &); +public: + gzomanip(gzofstream &(*f)(gzofstream &, T), T v) : func(f), val(v) { } +private: + gzofstream &(*func)(gzofstream &, T); + T val; +}; + +template gzofstream &operator<<(gzofstream &s, const gzomanip &m) +{ + return (*m.func)(s, m.val); +} + +inline gzofstream &setcompressionlevel( gzofstream &s, int l ) +{ + (s.rdbuf())->setcompressionlevel(l); + return s; +} + +inline gzofstream &setcompressionstrategy( gzofstream &s, int l ) +{ + (s.rdbuf())->setcompressionstrategy(l); + return s; +} + +inline gzomanip setcompressionlevel(int l) +{ + return gzomanip(&setcompressionlevel,l); +} + +inline gzomanip setcompressionstrategy(int l) +{ + return gzomanip(&setcompressionstrategy,l); +} + +#endif diff --git a/src/png/zlib/contrib/iostream2/zstream.h b/src/png/zlib/contrib/iostream2/zstream.h new file mode 100644 index 0000000000..43d2332b79 --- /dev/null +++ b/src/png/zlib/contrib/iostream2/zstream.h @@ -0,0 +1,307 @@ +/* + * + * Copyright (c) 1997 + * Christian Michelsen Research AS + * Advanced Computing + * Fantoftvegen 38, 5036 BERGEN, Norway + * http://www.cmr.no + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Christian Michelsen Research AS makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + */ + +#ifndef ZSTREAM__H +#define ZSTREAM__H + +/* + * zstream.h - C++ interface to the 'zlib' general purpose compression library + * $Id: zstream.h 1.1 1997-06-25 12:00:56+02 tyge Exp tyge $ + */ + +#include +#include +#include +#include "zlib.h" + +#if defined(_WIN32) +# include +# include +# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif + +class zstringlen { +public: + zstringlen(class izstream&); + zstringlen(class ozstream&, const char*); + size_t value() const { return val.word; } +private: + struct Val { unsigned char byte; size_t word; } val; +}; + +// ----------------------------- izstream ----------------------------- + +class izstream +{ + public: + izstream() : m_fp(0) {} + izstream(FILE* fp) : m_fp(0) { open(fp); } + izstream(const char* name) : m_fp(0) { open(name); } + ~izstream() { close(); } + + /* Opens a gzip (.gz) file for reading. + * open() can be used to read a file which is not in gzip format; + * in this case read() will directly read from the file without + * decompression. errno can be checked to distinguish two error + * cases (if errno is zero, the zlib error is Z_MEM_ERROR). + */ + void open(const char* name) { + if (m_fp) close(); + m_fp = ::gzopen(name, "rb"); + } + + void open(FILE* fp) { + SET_BINARY_MODE(fp); + if (m_fp) close(); + m_fp = ::gzdopen(fileno(fp), "rb"); + } + + /* Flushes all pending input if necessary, closes the compressed file + * and deallocates all the (de)compression state. The return value is + * the zlib error number (see function error() below). + */ + int close() { + int r = ::gzclose(m_fp); + m_fp = 0; return r; + } + + /* Binary read the given number of bytes from the compressed file. + */ + int read(void* buf, size_t len) { + return ::gzread(m_fp, buf, len); + } + + /* Returns the error message for the last error which occurred on the + * given compressed file. errnum is set to zlib error number. If an + * error occurred in the file system and not in the compression library, + * errnum is set to Z_ERRNO and the application may consult errno + * to get the exact error code. + */ + const char* error(int* errnum) { + return ::gzerror(m_fp, errnum); + } + + gzFile fp() { return m_fp; } + + private: + gzFile m_fp; +}; + +/* + * Binary read the given (array of) object(s) from the compressed file. + * If the input file was not in gzip format, read() copies the objects number + * of bytes into the buffer. + * returns the number of uncompressed bytes actually read + * (0 for end of file, -1 for error). + */ +template +inline int read(izstream& zs, T* x, Items items) { + return ::gzread(zs.fp(), x, items*sizeof(T)); +} + +/* + * Binary input with the '>' operator. + */ +template +inline izstream& operator>(izstream& zs, T& x) { + ::gzread(zs.fp(), &x, sizeof(T)); + return zs; +} + + +inline zstringlen::zstringlen(izstream& zs) { + zs > val.byte; + if (val.byte == 255) zs > val.word; + else val.word = val.byte; +} + +/* + * Read length of string + the string with the '>' operator. + */ +inline izstream& operator>(izstream& zs, char* x) { + zstringlen len(zs); + ::gzread(zs.fp(), x, len.value()); + x[len.value()] = '\0'; + return zs; +} + +inline char* read_string(izstream& zs) { + zstringlen len(zs); + char* x = new char[len.value()+1]; + ::gzread(zs.fp(), x, len.value()); + x[len.value()] = '\0'; + return x; +} + +// ----------------------------- ozstream ----------------------------- + +class ozstream +{ + public: + ozstream() : m_fp(0), m_os(0) { + } + ozstream(FILE* fp, int level = Z_DEFAULT_COMPRESSION) + : m_fp(0), m_os(0) { + open(fp, level); + } + ozstream(const char* name, int level = Z_DEFAULT_COMPRESSION) + : m_fp(0), m_os(0) { + open(name, level); + } + ~ozstream() { + close(); + } + + /* Opens a gzip (.gz) file for writing. + * The compression level parameter should be in 0..9 + * errno can be checked to distinguish two error cases + * (if errno is zero, the zlib error is Z_MEM_ERROR). + */ + void open(const char* name, int level = Z_DEFAULT_COMPRESSION) { + char mode[4] = "wb\0"; + if (level != Z_DEFAULT_COMPRESSION) mode[2] = '0'+level; + if (m_fp) close(); + m_fp = ::gzopen(name, mode); + } + + /* open from a FILE pointer. + */ + void open(FILE* fp, int level = Z_DEFAULT_COMPRESSION) { + SET_BINARY_MODE(fp); + char mode[4] = "wb\0"; + if (level != Z_DEFAULT_COMPRESSION) mode[2] = '0'+level; + if (m_fp) close(); + m_fp = ::gzdopen(fileno(fp), mode); + } + + /* Flushes all pending output if necessary, closes the compressed file + * and deallocates all the (de)compression state. The return value is + * the zlib error number (see function error() below). + */ + int close() { + if (m_os) { + ::gzwrite(m_fp, m_os->str(), m_os->pcount()); + delete[] m_os->str(); delete m_os; m_os = 0; + } + int r = ::gzclose(m_fp); m_fp = 0; return r; + } + + /* Binary write the given number of bytes into the compressed file. + */ + int write(const void* buf, size_t len) { + return ::gzwrite(m_fp, (voidp) buf, len); + } + + /* Flushes all pending output into the compressed file. The parameter + * _flush is as in the deflate() function. The return value is the zlib + * error number (see function gzerror below). flush() returns Z_OK if + * the flush_ parameter is Z_FINISH and all output could be flushed. + * flush() should be called only when strictly necessary because it can + * degrade compression. + */ + int flush(int _flush) { + os_flush(); + return ::gzflush(m_fp, _flush); + } + + /* Returns the error message for the last error which occurred on the + * given compressed file. errnum is set to zlib error number. If an + * error occurred in the file system and not in the compression library, + * errnum is set to Z_ERRNO and the application may consult errno + * to get the exact error code. + */ + const char* error(int* errnum) { + return ::gzerror(m_fp, errnum); + } + + gzFile fp() { return m_fp; } + + ostream& os() { + if (m_os == 0) m_os = new ostrstream; + return *m_os; + } + + void os_flush() { + if (m_os && m_os->pcount()>0) { + ostrstream* oss = new ostrstream; + oss->fill(m_os->fill()); + oss->flags(m_os->flags()); + oss->precision(m_os->precision()); + oss->width(m_os->width()); + ::gzwrite(m_fp, m_os->str(), m_os->pcount()); + delete[] m_os->str(); delete m_os; m_os = oss; + } + } + + private: + gzFile m_fp; + ostrstream* m_os; +}; + +/* + * Binary write the given (array of) object(s) into the compressed file. + * returns the number of uncompressed bytes actually written + * (0 in case of error). + */ +template +inline int write(ozstream& zs, const T* x, Items items) { + return ::gzwrite(zs.fp(), (voidp) x, items*sizeof(T)); +} + +/* + * Binary output with the '<' operator. + */ +template +inline ozstream& operator<(ozstream& zs, const T& x) { + ::gzwrite(zs.fp(), (voidp) &x, sizeof(T)); + return zs; +} + +inline zstringlen::zstringlen(ozstream& zs, const char* x) { + val.byte = 255; val.word = ::strlen(x); + if (val.word < 255) zs < (val.byte = val.word); + else zs < val; +} + +/* + * Write length of string + the string with the '<' operator. + */ +inline ozstream& operator<(ozstream& zs, const char* x) { + zstringlen len(zs, x); + ::gzwrite(zs.fp(), (voidp) x, len.value()); + return zs; +} + +#ifdef _MSC_VER +inline ozstream& operator<(ozstream& zs, char* const& x) { + return zs < (const char*) x; +} +#endif + +/* + * Ascii write with the << operator; + */ +template +inline ostream& operator<<(ozstream& zs, const T& x) { + zs.os_flush(); + return zs.os() << x; +} + +#endif diff --git a/src/png/zlib/contrib/iostream2/zstream_test.cpp b/src/png/zlib/contrib/iostream2/zstream_test.cpp new file mode 100644 index 0000000000..6273f62d62 --- /dev/null +++ b/src/png/zlib/contrib/iostream2/zstream_test.cpp @@ -0,0 +1,25 @@ +#include "zstream.h" +#include +#include +#include + +void main() { + char h[256] = "Hello"; + char* g = "Goodbye"; + ozstream out("temp.gz"); + out < "This works well" < h < g; + out.close(); + + izstream in("temp.gz"); // read it back + char *x = read_string(in), *y = new char[256], z[256]; + in > y > z; + in.close(); + cout << x << endl << y << endl << z << endl; + + out.open("temp.gz"); // try ascii output; zcat temp.gz to see the results + out << setw(50) << setfill('#') << setprecision(20) << x << endl << y << endl << z << endl; + out << z << endl << y << endl << x << endl; + out << 1.1234567890123456789 << endl; + + delete[] x; delete[] y; +} diff --git a/src/png/zlib/contrib/iostream3/README b/src/png/zlib/contrib/iostream3/README new file mode 100644 index 0000000000..f7b319ab91 --- /dev/null +++ b/src/png/zlib/contrib/iostream3/README @@ -0,0 +1,35 @@ +These classes provide a C++ stream interface to the zlib library. It allows you +to do things like: + + gzofstream outf("blah.gz"); + outf << "These go into the gzip file " << 123 << endl; + +It does this by deriving a specialized stream buffer for gzipped files, which is +the way Stroustrup would have done it. :-> + +The gzifstream and gzofstream classes were originally written by Kevin Ruland +and made available in the zlib contrib/iostream directory. The older version still +compiles under gcc 2.xx, but not under gcc 3.xx, which sparked the development of +this version. + +The new classes are as standard-compliant as possible, closely following the +approach of the standard library's fstream classes. It compiles under gcc versions +3.2 and 3.3, but not under gcc 2.xx. This is mainly due to changes in the standard +library naming scheme. The new version of gzifstream/gzofstream/gzfilebuf differs +from the previous one in the following respects: +- added showmanyc +- added setbuf, with support for unbuffered output via setbuf(0,0) +- a few bug fixes of stream behavior +- gzipped output file opened with default compression level instead of maximum level +- setcompressionlevel()/strategy() members replaced by single setcompression() + +The code is provided "as is", with the permission to use, copy, modify, distribute +and sell it for any purpose without fee. + +Ludwig Schwardt + + +DSP Lab +Electrical & Electronic Engineering Department +University of Stellenbosch +South Africa diff --git a/src/png/zlib/contrib/iostream3/TODO b/src/png/zlib/contrib/iostream3/TODO new file mode 100644 index 0000000000..7032f97be0 --- /dev/null +++ b/src/png/zlib/contrib/iostream3/TODO @@ -0,0 +1,17 @@ +Possible upgrades to gzfilebuf: + +- The ability to do putback (e.g. putbackfail) + +- The ability to seek (zlib supports this, but could be slow/tricky) + +- Simultaneous read/write access (does it make sense?) + +- Support for ios_base::ate open mode + +- Locale support? + +- Check public interface to see which calls give problems + (due to dependence on library internals) + +- Override operator<<(ostream&, gzfilebuf*) to allow direct copying + of stream buffer to stream ( i.e. os << is.rdbuf(); ) diff --git a/src/png/zlib/contrib/iostream3/test.cc b/src/png/zlib/contrib/iostream3/test.cc new file mode 100644 index 0000000000..94235334f2 --- /dev/null +++ b/src/png/zlib/contrib/iostream3/test.cc @@ -0,0 +1,50 @@ +/* + * Test program for gzifstream and gzofstream + * + * by Ludwig Schwardt + * original version by Kevin Ruland + */ + +#include "zfstream.h" +#include // for cout + +int main() { + + gzofstream outf; + gzifstream inf; + char buf[80]; + + outf.open("test1.txt.gz"); + outf << "The quick brown fox sidestepped the lazy canine\n" + << 1.3 << "\nPlan " << 9 << std::endl; + outf.close(); + std::cout << "Wrote the following message to 'test1.txt.gz' (check with zcat or zless):\n" + << "The quick brown fox sidestepped the lazy canine\n" + << 1.3 << "\nPlan " << 9 << std::endl; + + std::cout << "\nReading 'test1.txt.gz' (buffered) produces:\n"; + inf.open("test1.txt.gz"); + while (inf.getline(buf,80,'\n')) { + std::cout << buf << "\t(" << inf.rdbuf()->in_avail() << " chars left in buffer)\n"; + } + inf.close(); + + outf.rdbuf()->pubsetbuf(0,0); + outf.open("test2.txt.gz"); + outf << setcompression(Z_NO_COMPRESSION) + << "The quick brown fox sidestepped the lazy canine\n" + << 1.3 << "\nPlan " << 9 << std::endl; + outf.close(); + std::cout << "\nWrote the same message to 'test2.txt.gz' in uncompressed form"; + + std::cout << "\nReading 'test2.txt.gz' (unbuffered) produces:\n"; + inf.rdbuf()->pubsetbuf(0,0); + inf.open("test2.txt.gz"); + while (inf.getline(buf,80,'\n')) { + std::cout << buf << "\t(" << inf.rdbuf()->in_avail() << " chars left in buffer)\n"; + } + inf.close(); + + return 0; + +} diff --git a/src/png/zlib/contrib/iostream3/zfstream.cc b/src/png/zlib/contrib/iostream3/zfstream.cc new file mode 100644 index 0000000000..94eb933444 --- /dev/null +++ b/src/png/zlib/contrib/iostream3/zfstream.cc @@ -0,0 +1,479 @@ +/* + * A C++ I/O streams interface to the zlib gz* functions + * + * by Ludwig Schwardt + * original version by Kevin Ruland + * + * This version is standard-compliant and compatible with gcc 3.x. + */ + +#include "zfstream.h" +#include // for strcpy, strcat, strlen (mode strings) +#include // for BUFSIZ + +// Internal buffer sizes (default and "unbuffered" versions) +#define BIGBUFSIZE BUFSIZ +#define SMALLBUFSIZE 1 + +/*****************************************************************************/ + +// Default constructor +gzfilebuf::gzfilebuf() +: file(NULL), io_mode(std::ios_base::openmode(0)), own_fd(false), + buffer(NULL), buffer_size(BIGBUFSIZE), own_buffer(true) +{ + // No buffers to start with + this->disable_buffer(); +} + +// Destructor +gzfilebuf::~gzfilebuf() +{ + // Sync output buffer and close only if responsible for file + // (i.e. attached streams should be left open at this stage) + this->sync(); + if (own_fd) + this->close(); + // Make sure internal buffer is deallocated + this->disable_buffer(); +} + +// Set compression level and strategy +int +gzfilebuf::setcompression(int comp_level, + int comp_strategy) +{ + return gzsetparams(file, comp_level, comp_strategy); +} + +// Open gzipped file +gzfilebuf* +gzfilebuf::open(const char *name, + std::ios_base::openmode mode) +{ + // Fail if file already open + if (this->is_open()) + return NULL; + // Don't support simultaneous read/write access (yet) + if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) + return NULL; + + // Build mode string for gzopen and check it [27.8.1.3.2] + char char_mode[6] = "\0\0\0\0\0"; + if (!this->open_mode(mode, char_mode)) + return NULL; + + // Attempt to open file + if ((file = gzopen(name, char_mode)) == NULL) + return NULL; + + // On success, allocate internal buffer and set flags + this->enable_buffer(); + io_mode = mode; + own_fd = true; + return this; +} + +// Attach to gzipped file +gzfilebuf* +gzfilebuf::attach(int fd, + std::ios_base::openmode mode) +{ + // Fail if file already open + if (this->is_open()) + return NULL; + // Don't support simultaneous read/write access (yet) + if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) + return NULL; + + // Build mode string for gzdopen and check it [27.8.1.3.2] + char char_mode[6] = "\0\0\0\0\0"; + if (!this->open_mode(mode, char_mode)) + return NULL; + + // Attempt to attach to file + if ((file = gzdopen(fd, char_mode)) == NULL) + return NULL; + + // On success, allocate internal buffer and set flags + this->enable_buffer(); + io_mode = mode; + own_fd = false; + return this; +} + +// Close gzipped file +gzfilebuf* +gzfilebuf::close() +{ + // Fail immediately if no file is open + if (!this->is_open()) + return NULL; + // Assume success + gzfilebuf* retval = this; + // Attempt to sync and close gzipped file + if (this->sync() == -1) + retval = NULL; + if (gzclose(file) < 0) + retval = NULL; + // File is now gone anyway (postcondition [27.8.1.3.8]) + file = NULL; + own_fd = false; + // Destroy internal buffer if it exists + this->disable_buffer(); + return retval; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// Convert int open mode to mode string +bool +gzfilebuf::open_mode(std::ios_base::openmode mode, + char* c_mode) const +{ + bool testb = mode & std::ios_base::binary; + bool testi = mode & std::ios_base::in; + bool testo = mode & std::ios_base::out; + bool testt = mode & std::ios_base::trunc; + bool testa = mode & std::ios_base::app; + + // Check for valid flag combinations - see [27.8.1.3.2] (Table 92) + // Original zfstream hardcoded the compression level to maximum here... + // Double the time for less than 1% size improvement seems + // excessive though - keeping it at the default level + // To change back, just append "9" to the next three mode strings + if (!testi && testo && !testt && !testa) + strcpy(c_mode, "w"); + if (!testi && testo && !testt && testa) + strcpy(c_mode, "a"); + if (!testi && testo && testt && !testa) + strcpy(c_mode, "w"); + if (testi && !testo && !testt && !testa) + strcpy(c_mode, "r"); + // No read/write mode yet +// if (testi && testo && !testt && !testa) +// strcpy(c_mode, "r+"); +// if (testi && testo && testt && !testa) +// strcpy(c_mode, "w+"); + + // Mode string should be empty for invalid combination of flags + if (strlen(c_mode) == 0) + return false; + if (testb) + strcat(c_mode, "b"); + return true; +} + +// Determine number of characters in internal get buffer +std::streamsize +gzfilebuf::showmanyc() +{ + // Calls to underflow will fail if file not opened for reading + if (!this->is_open() || !(io_mode & std::ios_base::in)) + return -1; + // Make sure get area is in use + if (this->gptr() && (this->gptr() < this->egptr())) + return std::streamsize(this->egptr() - this->gptr()); + else + return 0; +} + +// Fill get area from gzipped file +gzfilebuf::int_type +gzfilebuf::underflow() +{ + // If something is left in the get area by chance, return it + // (this shouldn't normally happen, as underflow is only supposed + // to be called when gptr >= egptr, but it serves as error check) + if (this->gptr() && (this->gptr() < this->egptr())) + return traits_type::to_int_type(*(this->gptr())); + + // If the file hasn't been opened for reading, produce error + if (!this->is_open() || !(io_mode & std::ios_base::in)) + return traits_type::eof(); + + // Attempt to fill internal buffer from gzipped file + // (buffer must be guaranteed to exist...) + int bytes_read = gzread(file, buffer, buffer_size); + // Indicates error or EOF + if (bytes_read <= 0) + { + // Reset get area + this->setg(buffer, buffer, buffer); + return traits_type::eof(); + } + // Make all bytes read from file available as get area + this->setg(buffer, buffer, buffer + bytes_read); + + // Return next character in get area + return traits_type::to_int_type(*(this->gptr())); +} + +// Write put area to gzipped file +gzfilebuf::int_type +gzfilebuf::overflow(int_type c) +{ + // Determine whether put area is in use + if (this->pbase()) + { + // Double-check pointer range + if (this->pptr() > this->epptr() || this->pptr() < this->pbase()) + return traits_type::eof(); + // Add extra character to buffer if not EOF + if (!traits_type::eq_int_type(c, traits_type::eof())) + { + *(this->pptr()) = traits_type::to_char_type(c); + this->pbump(1); + } + // Number of characters to write to file + int bytes_to_write = this->pptr() - this->pbase(); + // Overflow doesn't fail if nothing is to be written + if (bytes_to_write > 0) + { + // If the file hasn't been opened for writing, produce error + if (!this->is_open() || !(io_mode & std::ios_base::out)) + return traits_type::eof(); + // If gzipped file won't accept all bytes written to it, fail + if (gzwrite(file, this->pbase(), bytes_to_write) != bytes_to_write) + return traits_type::eof(); + // Reset next pointer to point to pbase on success + this->pbump(-bytes_to_write); + } + } + // Write extra character to file if not EOF + else if (!traits_type::eq_int_type(c, traits_type::eof())) + { + // If the file hasn't been opened for writing, produce error + if (!this->is_open() || !(io_mode & std::ios_base::out)) + return traits_type::eof(); + // Impromptu char buffer (allows "unbuffered" output) + char_type last_char = traits_type::to_char_type(c); + // If gzipped file won't accept this character, fail + if (gzwrite(file, &last_char, 1) != 1) + return traits_type::eof(); + } + + // If you got here, you have succeeded (even if c was EOF) + // The return value should therefore be non-EOF + if (traits_type::eq_int_type(c, traits_type::eof())) + return traits_type::not_eof(c); + else + return c; +} + +// Assign new buffer +std::streambuf* +gzfilebuf::setbuf(char_type* p, + std::streamsize n) +{ + // First make sure stuff is sync'ed, for safety + if (this->sync() == -1) + return NULL; + // If buffering is turned off on purpose via setbuf(0,0), still allocate one... + // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at + // least a buffer of size 1 (very inefficient though, therefore make it bigger?) + // This follows from [27.5.2.4.3]/12 (gptr needs to point at something, it seems) + if (!p || !n) + { + // Replace existing buffer (if any) with small internal buffer + this->disable_buffer(); + buffer = NULL; + buffer_size = 0; + own_buffer = true; + this->enable_buffer(); + } + else + { + // Replace existing buffer (if any) with external buffer + this->disable_buffer(); + buffer = p; + buffer_size = n; + own_buffer = false; + this->enable_buffer(); + } + return this; +} + +// Write put area to gzipped file (i.e. ensures that put area is empty) +int +gzfilebuf::sync() +{ + return traits_type::eq_int_type(this->overflow(), traits_type::eof()) ? -1 : 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// Allocate internal buffer +void +gzfilebuf::enable_buffer() +{ + // If internal buffer required, allocate one + if (own_buffer && !buffer) + { + // Check for buffered vs. "unbuffered" + if (buffer_size > 0) + { + // Allocate internal buffer + buffer = new char_type[buffer_size]; + // Get area starts empty and will be expanded by underflow as need arises + this->setg(buffer, buffer, buffer); + // Setup entire internal buffer as put area. + // The one-past-end pointer actually points to the last element of the buffer, + // so that overflow(c) can safely add the extra character c to the sequence. + // These pointers remain in place for the duration of the buffer + this->setp(buffer, buffer + buffer_size - 1); + } + else + { + // Even in "unbuffered" case, (small?) get buffer is still required + buffer_size = SMALLBUFSIZE; + buffer = new char_type[buffer_size]; + this->setg(buffer, buffer, buffer); + // "Unbuffered" means no put buffer + this->setp(0, 0); + } + } + else + { + // If buffer already allocated, reset buffer pointers just to make sure no + // stale chars are lying around + this->setg(buffer, buffer, buffer); + this->setp(buffer, buffer + buffer_size - 1); + } +} + +// Destroy internal buffer +void +gzfilebuf::disable_buffer() +{ + // If internal buffer exists, deallocate it + if (own_buffer && buffer) + { + // Preserve unbuffered status by zeroing size + if (!this->pbase()) + buffer_size = 0; + delete[] buffer; + buffer = NULL; + this->setg(0, 0, 0); + this->setp(0, 0); + } + else + { + // Reset buffer pointers to initial state if external buffer exists + this->setg(buffer, buffer, buffer); + if (buffer) + this->setp(buffer, buffer + buffer_size - 1); + else + this->setp(0, 0); + } +} + +/*****************************************************************************/ + +// Default constructor initializes stream buffer +gzifstream::gzifstream() +: std::istream(NULL), sb() +{ this->init(&sb); } + +// Initialize stream buffer and open file +gzifstream::gzifstream(const char* name, + std::ios_base::openmode mode) +: std::istream(NULL), sb() +{ + this->init(&sb); + this->open(name, mode); +} + +// Initialize stream buffer and attach to file +gzifstream::gzifstream(int fd, + std::ios_base::openmode mode) +: std::istream(NULL), sb() +{ + this->init(&sb); + this->attach(fd, mode); +} + +// Open file and go into fail() state if unsuccessful +void +gzifstream::open(const char* name, + std::ios_base::openmode mode) +{ + if (!sb.open(name, mode | std::ios_base::in)) + this->setstate(std::ios_base::failbit); + else + this->clear(); +} + +// Attach to file and go into fail() state if unsuccessful +void +gzifstream::attach(int fd, + std::ios_base::openmode mode) +{ + if (!sb.attach(fd, mode | std::ios_base::in)) + this->setstate(std::ios_base::failbit); + else + this->clear(); +} + +// Close file +void +gzifstream::close() +{ + if (!sb.close()) + this->setstate(std::ios_base::failbit); +} + +/*****************************************************************************/ + +// Default constructor initializes stream buffer +gzofstream::gzofstream() +: std::ostream(NULL), sb() +{ this->init(&sb); } + +// Initialize stream buffer and open file +gzofstream::gzofstream(const char* name, + std::ios_base::openmode mode) +: std::ostream(NULL), sb() +{ + this->init(&sb); + this->open(name, mode); +} + +// Initialize stream buffer and attach to file +gzofstream::gzofstream(int fd, + std::ios_base::openmode mode) +: std::ostream(NULL), sb() +{ + this->init(&sb); + this->attach(fd, mode); +} + +// Open file and go into fail() state if unsuccessful +void +gzofstream::open(const char* name, + std::ios_base::openmode mode) +{ + if (!sb.open(name, mode | std::ios_base::out)) + this->setstate(std::ios_base::failbit); + else + this->clear(); +} + +// Attach to file and go into fail() state if unsuccessful +void +gzofstream::attach(int fd, + std::ios_base::openmode mode) +{ + if (!sb.attach(fd, mode | std::ios_base::out)) + this->setstate(std::ios_base::failbit); + else + this->clear(); +} + +// Close file +void +gzofstream::close() +{ + if (!sb.close()) + this->setstate(std::ios_base::failbit); +} diff --git a/src/png/zlib/contrib/iostream3/zfstream.h b/src/png/zlib/contrib/iostream3/zfstream.h new file mode 100644 index 0000000000..8574479ae1 --- /dev/null +++ b/src/png/zlib/contrib/iostream3/zfstream.h @@ -0,0 +1,466 @@ +/* + * A C++ I/O streams interface to the zlib gz* functions + * + * by Ludwig Schwardt + * original version by Kevin Ruland + * + * This version is standard-compliant and compatible with gcc 3.x. + */ + +#ifndef ZFSTREAM_H +#define ZFSTREAM_H + +#include // not iostream, since we don't need cin/cout +#include +#include "zlib.h" + +/*****************************************************************************/ + +/** + * @brief Gzipped file stream buffer class. + * + * This class implements basic_filebuf for gzipped files. It doesn't yet support + * seeking (allowed by zlib but slow/limited), putback and read/write access + * (tricky). Otherwise, it attempts to be a drop-in replacement for the standard + * file streambuf. +*/ +class gzfilebuf : public std::streambuf +{ +public: + // Default constructor. + gzfilebuf(); + + // Destructor. + virtual + ~gzfilebuf(); + + /** + * @brief Set compression level and strategy on the fly. + * @param comp_level Compression level (see zlib.h for allowed values) + * @param comp_strategy Compression strategy (see zlib.h for allowed values) + * @return Z_OK on success, Z_STREAM_ERROR otherwise. + * + * Unfortunately, these parameters cannot be modified separately, as the + * previous zfstream version assumed. Since the strategy is seldom changed, + * it can default and setcompression(level) then becomes like the old + * setcompressionlevel(level). + */ + int + setcompression(int comp_level, + int comp_strategy = Z_DEFAULT_STRATEGY); + + /** + * @brief Check if file is open. + * @return True if file is open. + */ + bool + is_open() const { return (file != NULL); } + + /** + * @brief Open gzipped file. + * @param name File name. + * @param mode Open mode flags. + * @return @c this on success, NULL on failure. + */ + gzfilebuf* + open(const char* name, + std::ios_base::openmode mode); + + /** + * @brief Attach to already open gzipped file. + * @param fd File descriptor. + * @param mode Open mode flags. + * @return @c this on success, NULL on failure. + */ + gzfilebuf* + attach(int fd, + std::ios_base::openmode mode); + + /** + * @brief Close gzipped file. + * @return @c this on success, NULL on failure. + */ + gzfilebuf* + close(); + +protected: + /** + * @brief Convert ios open mode int to mode string used by zlib. + * @return True if valid mode flag combination. + */ + bool + open_mode(std::ios_base::openmode mode, + char* c_mode) const; + + /** + * @brief Number of characters available in stream buffer. + * @return Number of characters. + * + * This indicates number of characters in get area of stream buffer. + * These characters can be read without accessing the gzipped file. + */ + virtual std::streamsize + showmanyc(); + + /** + * @brief Fill get area from gzipped file. + * @return First character in get area on success, EOF on error. + * + * This actually reads characters from gzipped file to stream + * buffer. Always buffered. + */ + virtual int_type + underflow(); + + /** + * @brief Write put area to gzipped file. + * @param c Extra character to add to buffer contents. + * @return Non-EOF on success, EOF on error. + * + * This actually writes characters in stream buffer to + * gzipped file. With unbuffered output this is done one + * character at a time. + */ + virtual int_type + overflow(int_type c = traits_type::eof()); + + /** + * @brief Installs external stream buffer. + * @param p Pointer to char buffer. + * @param n Size of external buffer. + * @return @c this on success, NULL on failure. + * + * Call setbuf(0,0) to enable unbuffered output. + */ + virtual std::streambuf* + setbuf(char_type* p, + std::streamsize n); + + /** + * @brief Flush stream buffer to file. + * @return 0 on success, -1 on error. + * + * This calls underflow(EOF) to do the job. + */ + virtual int + sync(); + +// +// Some future enhancements +// +// virtual int_type uflow(); +// virtual int_type pbackfail(int_type c = traits_type::eof()); +// virtual pos_type +// seekoff(off_type off, +// std::ios_base::seekdir way, +// std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out); +// virtual pos_type +// seekpos(pos_type sp, +// std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out); + +private: + /** + * @brief Allocate internal buffer. + * + * This function is safe to call multiple times. It will ensure + * that a proper internal buffer exists if it is required. If the + * buffer already exists or is external, the buffer pointers will be + * reset to their original state. + */ + void + enable_buffer(); + + /** + * @brief Destroy internal buffer. + * + * This function is safe to call multiple times. It will ensure + * that the internal buffer is deallocated if it exists. In any + * case, it will also reset the buffer pointers. + */ + void + disable_buffer(); + + /** + * Underlying file pointer. + */ + gzFile file; + + /** + * Mode in which file was opened. + */ + std::ios_base::openmode io_mode; + + /** + * @brief True if this object owns file descriptor. + * + * This makes the class responsible for closing the file + * upon destruction. + */ + bool own_fd; + + /** + * @brief Stream buffer. + * + * For simplicity this remains allocated on the free store for the + * entire life span of the gzfilebuf object, unless replaced by setbuf. + */ + char_type* buffer; + + /** + * @brief Stream buffer size. + * + * Defaults to system default buffer size (typically 8192 bytes). + * Modified by setbuf. + */ + std::streamsize buffer_size; + + /** + * @brief True if this object owns stream buffer. + * + * This makes the class responsible for deleting the buffer + * upon destruction. + */ + bool own_buffer; +}; + +/*****************************************************************************/ + +/** + * @brief Gzipped file input stream class. + * + * This class implements ifstream for gzipped files. Seeking and putback + * is not supported yet. +*/ +class gzifstream : public std::istream +{ +public: + // Default constructor + gzifstream(); + + /** + * @brief Construct stream on gzipped file to be opened. + * @param name File name. + * @param mode Open mode flags (forced to contain ios::in). + */ + explicit + gzifstream(const char* name, + std::ios_base::openmode mode = std::ios_base::in); + + /** + * @brief Construct stream on already open gzipped file. + * @param fd File descriptor. + * @param mode Open mode flags (forced to contain ios::in). + */ + explicit + gzifstream(int fd, + std::ios_base::openmode mode = std::ios_base::in); + + /** + * Obtain underlying stream buffer. + */ + gzfilebuf* + rdbuf() const + { return const_cast(&sb); } + + /** + * @brief Check if file is open. + * @return True if file is open. + */ + bool + is_open() { return sb.is_open(); } + + /** + * @brief Open gzipped file. + * @param name File name. + * @param mode Open mode flags (forced to contain ios::in). + * + * Stream will be in state good() if file opens successfully; + * otherwise in state fail(). This differs from the behavior of + * ifstream, which never sets the state to good() and therefore + * won't allow you to reuse the stream for a second file unless + * you manually clear() the state. The choice is a matter of + * convenience. + */ + void + open(const char* name, + std::ios_base::openmode mode = std::ios_base::in); + + /** + * @brief Attach to already open gzipped file. + * @param fd File descriptor. + * @param mode Open mode flags (forced to contain ios::in). + * + * Stream will be in state good() if attach succeeded; otherwise + * in state fail(). + */ + void + attach(int fd, + std::ios_base::openmode mode = std::ios_base::in); + + /** + * @brief Close gzipped file. + * + * Stream will be in state fail() if close failed. + */ + void + close(); + +private: + /** + * Underlying stream buffer. + */ + gzfilebuf sb; +}; + +/*****************************************************************************/ + +/** + * @brief Gzipped file output stream class. + * + * This class implements ofstream for gzipped files. Seeking and putback + * is not supported yet. +*/ +class gzofstream : public std::ostream +{ +public: + // Default constructor + gzofstream(); + + /** + * @brief Construct stream on gzipped file to be opened. + * @param name File name. + * @param mode Open mode flags (forced to contain ios::out). + */ + explicit + gzofstream(const char* name, + std::ios_base::openmode mode = std::ios_base::out); + + /** + * @brief Construct stream on already open gzipped file. + * @param fd File descriptor. + * @param mode Open mode flags (forced to contain ios::out). + */ + explicit + gzofstream(int fd, + std::ios_base::openmode mode = std::ios_base::out); + + /** + * Obtain underlying stream buffer. + */ + gzfilebuf* + rdbuf() const + { return const_cast(&sb); } + + /** + * @brief Check if file is open. + * @return True if file is open. + */ + bool + is_open() { return sb.is_open(); } + + /** + * @brief Open gzipped file. + * @param name File name. + * @param mode Open mode flags (forced to contain ios::out). + * + * Stream will be in state good() if file opens successfully; + * otherwise in state fail(). This differs from the behavior of + * ofstream, which never sets the state to good() and therefore + * won't allow you to reuse the stream for a second file unless + * you manually clear() the state. The choice is a matter of + * convenience. + */ + void + open(const char* name, + std::ios_base::openmode mode = std::ios_base::out); + + /** + * @brief Attach to already open gzipped file. + * @param fd File descriptor. + * @param mode Open mode flags (forced to contain ios::out). + * + * Stream will be in state good() if attach succeeded; otherwise + * in state fail(). + */ + void + attach(int fd, + std::ios_base::openmode mode = std::ios_base::out); + + /** + * @brief Close gzipped file. + * + * Stream will be in state fail() if close failed. + */ + void + close(); + +private: + /** + * Underlying stream buffer. + */ + gzfilebuf sb; +}; + +/*****************************************************************************/ + +/** + * @brief Gzipped file output stream manipulator class. + * + * This class defines a two-argument manipulator for gzofstream. It is used + * as base for the setcompression(int,int) manipulator. +*/ +template + class gzomanip2 + { + public: + // Allows insertor to peek at internals + template + friend gzofstream& + operator<<(gzofstream&, + const gzomanip2&); + + // Constructor + gzomanip2(gzofstream& (*f)(gzofstream&, T1, T2), + T1 v1, + T2 v2); + private: + // Underlying manipulator function + gzofstream& + (*func)(gzofstream&, T1, T2); + + // Arguments for manipulator function + T1 val1; + T2 val2; + }; + +/*****************************************************************************/ + +// Manipulator function thunks through to stream buffer +inline gzofstream& +setcompression(gzofstream &gzs, int l, int s = Z_DEFAULT_STRATEGY) +{ + (gzs.rdbuf())->setcompression(l, s); + return gzs; +} + +// Manipulator constructor stores arguments +template + inline + gzomanip2::gzomanip2(gzofstream &(*f)(gzofstream &, T1, T2), + T1 v1, + T2 v2) + : func(f), val1(v1), val2(v2) + { } + +// Insertor applies underlying manipulator function to stream +template + inline gzofstream& + operator<<(gzofstream& s, const gzomanip2& m) + { return (*m.func)(s, m.val1, m.val2); } + +// Insert this onto stream to simplify setting of compression level +inline gzomanip2 +setcompression(int l, int s = Z_DEFAULT_STRATEGY) +{ return gzomanip2(&setcompression, l, s); } + +#endif // ZFSTREAM_H diff --git a/src/png/zlib/contrib/masmx64/bld_ml64.bat b/src/png/zlib/contrib/masmx64/bld_ml64.bat new file mode 100644 index 0000000000..f74bcef5b4 --- /dev/null +++ b/src/png/zlib/contrib/masmx64/bld_ml64.bat @@ -0,0 +1,2 @@ +ml64.exe /Flinffasx64 /c /Zi inffasx64.asm +ml64.exe /Flgvmat64 /c /Zi gvmat64.asm diff --git a/src/png/zlib/contrib/masmx64/gvmat64.asm b/src/png/zlib/contrib/masmx64/gvmat64.asm new file mode 100644 index 0000000000..c1817f1be9 --- /dev/null +++ b/src/png/zlib/contrib/masmx64/gvmat64.asm @@ -0,0 +1,553 @@ +;uInt longest_match_x64( +; deflate_state *s, +; IPos cur_match); /* current match */ + +; gvmat64.asm -- Asm portion of the optimized longest_match for 32 bits x86_64 +; (AMD64 on Athlon 64, Opteron, Phenom +; and Intel EM64T on Pentium 4 with EM64T, Pentium D, Core 2 Duo, Core I5/I7) +; Copyright (C) 1995-2010 Jean-loup Gailly, Brian Raiter and Gilles Vollant. +; +; File written by Gilles Vollant, by converting to assembly the longest_match +; from Jean-loup Gailly in deflate.c of zLib and infoZip zip. +; +; and by taking inspiration on asm686 with masm, optimised assembly code +; from Brian Raiter, written 1998 +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software +; 3. This notice may not be removed or altered from any source distribution. +; +; +; +; http://www.zlib.net +; http://www.winimage.com/zLibDll +; http://www.muppetlabs.com/~breadbox/software/assembly.html +; +; to compile this file for infozip Zip, I use option: +; ml64.exe /Flgvmat64 /c /Zi /DINFOZIP gvmat64.asm +; +; to compile this file for zLib, I use option: +; ml64.exe /Flgvmat64 /c /Zi gvmat64.asm +; Be carrefull to adapt zlib1222add below to your version of zLib +; (if you use a version of zLib before 1.0.4 or after 1.2.2.2, change +; value of zlib1222add later) +; +; This file compile with Microsoft Macro Assembler (x64) for AMD64 +; +; ml64.exe is given with Visual Studio 2005/2008/2010 and Windows WDK +; +; (you can get Windows WDK with ml64 for AMD64 from +; http://www.microsoft.com/whdc/Devtools/wdk/default.mspx for low price) +; + + +;uInt longest_match(s, cur_match) +; deflate_state *s; +; IPos cur_match; /* current match */ +.code +longest_match PROC + + +;LocalVarsSize equ 88 + LocalVarsSize equ 72 + +; register used : rax,rbx,rcx,rdx,rsi,rdi,r8,r9,r10,r11,r12 +; free register : r14,r15 +; register can be saved : rsp + + chainlenwmask equ rsp + 8 - LocalVarsSize ; high word: current chain len + ; low word: s->wmask +;window equ rsp + xx - LocalVarsSize ; local copy of s->window ; stored in r10 +;windowbestlen equ rsp + xx - LocalVarsSize ; s->window + bestlen , use r10+r11 +;scanstart equ rsp + xx - LocalVarsSize ; first two bytes of string ; stored in r12w +;scanend equ rsp + xx - LocalVarsSize ; last two bytes of string use ebx +;scanalign equ rsp + xx - LocalVarsSize ; dword-misalignment of string r13 +;bestlen equ rsp + xx - LocalVarsSize ; size of best match so far -> r11d +;scan equ rsp + xx - LocalVarsSize ; ptr to string wanting match -> r9 +IFDEF INFOZIP +ELSE + nicematch equ (rsp + 16 - LocalVarsSize) ; a good enough match size +ENDIF + +save_rdi equ rsp + 24 - LocalVarsSize +save_rsi equ rsp + 32 - LocalVarsSize +save_rbx equ rsp + 40 - LocalVarsSize +save_rbp equ rsp + 48 - LocalVarsSize +save_r12 equ rsp + 56 - LocalVarsSize +save_r13 equ rsp + 64 - LocalVarsSize +;save_r14 equ rsp + 72 - LocalVarsSize +;save_r15 equ rsp + 80 - LocalVarsSize + + +; summary of register usage +; scanend ebx +; scanendw bx +; chainlenwmask edx +; curmatch rsi +; curmatchd esi +; windowbestlen r8 +; scanalign r9 +; scanalignd r9d +; window r10 +; bestlen r11 +; bestlend r11d +; scanstart r12d +; scanstartw r12w +; scan r13 +; nicematch r14d +; limit r15 +; limitd r15d +; prev rcx + +; all the +4 offsets are due to the addition of pending_buf_size (in zlib +; in the deflate_state structure since the asm code was first written +; (if you compile with zlib 1.0.4 or older, remove the +4). +; Note : these value are good with a 8 bytes boundary pack structure + + + MAX_MATCH equ 258 + MIN_MATCH equ 3 + MIN_LOOKAHEAD equ (MAX_MATCH+MIN_MATCH+1) + + +;;; Offsets for fields in the deflate_state structure. These numbers +;;; are calculated from the definition of deflate_state, with the +;;; assumption that the compiler will dword-align the fields. (Thus, +;;; changing the definition of deflate_state could easily cause this +;;; program to crash horribly, without so much as a warning at +;;; compile time. Sigh.) + +; all the +zlib1222add offsets are due to the addition of fields +; in zlib in the deflate_state structure since the asm code was first written +; (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)"). +; (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0"). +; if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8"). + + +IFDEF INFOZIP + +_DATA SEGMENT +COMM window_size:DWORD +; WMask ; 7fff +COMM window:BYTE:010040H +COMM prev:WORD:08000H +; MatchLen : unused +; PrevMatch : unused +COMM strstart:DWORD +COMM match_start:DWORD +; Lookahead : ignore +COMM prev_length:DWORD ; PrevLen +COMM max_chain_length:DWORD +COMM good_match:DWORD +COMM nice_match:DWORD +prev_ad equ OFFSET prev +window_ad equ OFFSET window +nicematch equ nice_match +_DATA ENDS +WMask equ 07fffh + +ELSE + + IFNDEF zlib1222add + zlib1222add equ 8 + ENDIF +dsWSize equ 56+zlib1222add+(zlib1222add/2) +dsWMask equ 64+zlib1222add+(zlib1222add/2) +dsWindow equ 72+zlib1222add +dsPrev equ 88+zlib1222add +dsMatchLen equ 128+zlib1222add +dsPrevMatch equ 132+zlib1222add +dsStrStart equ 140+zlib1222add +dsMatchStart equ 144+zlib1222add +dsLookahead equ 148+zlib1222add +dsPrevLen equ 152+zlib1222add +dsMaxChainLen equ 156+zlib1222add +dsGoodMatch equ 172+zlib1222add +dsNiceMatch equ 176+zlib1222add + +window_size equ [ rcx + dsWSize] +WMask equ [ rcx + dsWMask] +window_ad equ [ rcx + dsWindow] +prev_ad equ [ rcx + dsPrev] +strstart equ [ rcx + dsStrStart] +match_start equ [ rcx + dsMatchStart] +Lookahead equ [ rcx + dsLookahead] ; 0ffffffffh on infozip +prev_length equ [ rcx + dsPrevLen] +max_chain_length equ [ rcx + dsMaxChainLen] +good_match equ [ rcx + dsGoodMatch] +nice_match equ [ rcx + dsNiceMatch] +ENDIF + +; parameter 1 in r8(deflate state s), param 2 in rdx (cur match) + +; see http://weblogs.asp.net/oldnewthing/archive/2004/01/14/58579.aspx and +; http://msdn.microsoft.com/library/en-us/kmarch/hh/kmarch/64bitAMD_8e951dd2-ee77-4728-8702-55ce4b5dd24a.xml.asp +; +; All registers must be preserved across the call, except for +; rax, rcx, rdx, r8, r9, r10, and r11, which are scratch. + + + +;;; Save registers that the compiler may be using, and adjust esp to +;;; make room for our stack frame. + + +;;; Retrieve the function arguments. r8d will hold cur_match +;;; throughout the entire function. edx will hold the pointer to the +;;; deflate_state structure during the function's setup (before +;;; entering the main loop. + +; parameter 1 in rcx (deflate_state* s), param 2 in edx -> r8 (cur match) + +; this clear high 32 bits of r8, which can be garbage in both r8 and rdx + + mov [save_rdi],rdi + mov [save_rsi],rsi + mov [save_rbx],rbx + mov [save_rbp],rbp +IFDEF INFOZIP + mov r8d,ecx +ELSE + mov r8d,edx +ENDIF + mov [save_r12],r12 + mov [save_r13],r13 +; mov [save_r14],r14 +; mov [save_r15],r15 + + +;;; uInt wmask = s->w_mask; +;;; unsigned chain_length = s->max_chain_length; +;;; if (s->prev_length >= s->good_match) { +;;; chain_length >>= 2; +;;; } + + mov edi, prev_length + mov esi, good_match + mov eax, WMask + mov ebx, max_chain_length + cmp edi, esi + jl LastMatchGood + shr ebx, 2 +LastMatchGood: + +;;; chainlen is decremented once beforehand so that the function can +;;; use the sign flag instead of the zero flag for the exit test. +;;; It is then shifted into the high word, to make room for the wmask +;;; value, which it will always accompany. + + dec ebx + shl ebx, 16 + or ebx, eax + +;;; on zlib only +;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + +IFDEF INFOZIP + mov [chainlenwmask], ebx +; on infozip nice_match = [nice_match] +ELSE + mov eax, nice_match + mov [chainlenwmask], ebx + mov r10d, Lookahead + cmp r10d, eax + cmovnl r10d, eax + mov [nicematch],r10d +ENDIF + +;;; register Bytef *scan = s->window + s->strstart; + mov r10, window_ad + mov ebp, strstart + lea r13, [r10 + rbp] + +;;; Determine how many bytes the scan ptr is off from being +;;; dword-aligned. + + mov r9,r13 + neg r13 + and r13,3 + +;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ? +;;; s->strstart - (IPos)MAX_DIST(s) : NIL; +IFDEF INFOZIP + mov eax,07efah ; MAX_DIST = (WSIZE-MIN_LOOKAHEAD) (0x8000-(3+8+1)) +ELSE + mov eax, window_size + sub eax, MIN_LOOKAHEAD +ENDIF + xor edi,edi + sub ebp, eax + + mov r11d, prev_length + + cmovng ebp,edi + +;;; int best_len = s->prev_length; + + +;;; Store the sum of s->window + best_len in esi locally, and in esi. + + lea rsi,[r10+r11] + +;;; register ush scan_start = *(ushf*)scan; +;;; register ush scan_end = *(ushf*)(scan+best_len-1); +;;; Posf *prev = s->prev; + + movzx r12d,word ptr [r9] + movzx ebx, word ptr [r9 + r11 - 1] + + mov rdi, prev_ad + +;;; Jump into the main loop. + + mov edx, [chainlenwmask] + + cmp bx,word ptr [rsi + r8 - 1] + jz LookupLoopIsZero + +LookupLoop1: + and r8d, edx + + movzx r8d, word ptr [rdi + r8*2] + cmp r8d, ebp + jbe LeaveNow + sub edx, 00010000h + js LeaveNow + +LoopEntry1: + cmp bx,word ptr [rsi + r8 - 1] + jz LookupLoopIsZero + +LookupLoop2: + and r8d, edx + + movzx r8d, word ptr [rdi + r8*2] + cmp r8d, ebp + jbe LeaveNow + sub edx, 00010000h + js LeaveNow + +LoopEntry2: + cmp bx,word ptr [rsi + r8 - 1] + jz LookupLoopIsZero + +LookupLoop4: + and r8d, edx + + movzx r8d, word ptr [rdi + r8*2] + cmp r8d, ebp + jbe LeaveNow + sub edx, 00010000h + js LeaveNow + +LoopEntry4: + + cmp bx,word ptr [rsi + r8 - 1] + jnz LookupLoop1 + jmp LookupLoopIsZero + + +;;; do { +;;; match = s->window + cur_match; +;;; if (*(ushf*)(match+best_len-1) != scan_end || +;;; *(ushf*)match != scan_start) continue; +;;; [...] +;;; } while ((cur_match = prev[cur_match & wmask]) > limit +;;; && --chain_length != 0); +;;; +;;; Here is the inner loop of the function. The function will spend the +;;; majority of its time in this loop, and majority of that time will +;;; be spent in the first ten instructions. +;;; +;;; Within this loop: +;;; ebx = scanend +;;; r8d = curmatch +;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask) +;;; esi = windowbestlen - i.e., (window + bestlen) +;;; edi = prev +;;; ebp = limit + +LookupLoop: + and r8d, edx + + movzx r8d, word ptr [rdi + r8*2] + cmp r8d, ebp + jbe LeaveNow + sub edx, 00010000h + js LeaveNow + +LoopEntry: + + cmp bx,word ptr [rsi + r8 - 1] + jnz LookupLoop1 +LookupLoopIsZero: + cmp r12w, word ptr [r10 + r8] + jnz LookupLoop1 + + +;;; Store the current value of chainlen. + mov [chainlenwmask], edx + +;;; Point edi to the string under scrutiny, and esi to the string we +;;; are hoping to match it up with. In actuality, esi and edi are +;;; both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and edx is +;;; initialized to -(MAX_MATCH_8 - scanalign). + + lea rsi,[r8+r10] + mov rdx, 0fffffffffffffef8h; -(MAX_MATCH_8) + lea rsi, [rsi + r13 + 0108h] ;MAX_MATCH_8] + lea rdi, [r9 + r13 + 0108h] ;MAX_MATCH_8] + + prefetcht1 [rsi+rdx] + prefetcht1 [rdi+rdx] + + +;;; Test the strings for equality, 8 bytes at a time. At the end, +;;; adjust rdx so that it is offset to the exact byte that mismatched. +;;; +;;; We already know at this point that the first three bytes of the +;;; strings match each other, and they can be safely passed over before +;;; starting the compare loop. So what this code does is skip over 0-3 +;;; bytes, as much as necessary in order to dword-align the edi +;;; pointer. (rsi will still be misaligned three times out of four.) +;;; +;;; It should be confessed that this loop usually does not represent +;;; much of the total running time. Replacing it with a more +;;; straightforward "rep cmpsb" would not drastically degrade +;;; performance. + + +LoopCmps: + mov rax, [rsi + rdx] + xor rax, [rdi + rdx] + jnz LeaveLoopCmps + + mov rax, [rsi + rdx + 8] + xor rax, [rdi + rdx + 8] + jnz LeaveLoopCmps8 + + + mov rax, [rsi + rdx + 8+8] + xor rax, [rdi + rdx + 8+8] + jnz LeaveLoopCmps16 + + add rdx,8+8+8 + + jnz short LoopCmps + jmp short LenMaximum +LeaveLoopCmps16: add rdx,8 +LeaveLoopCmps8: add rdx,8 +LeaveLoopCmps: + + test eax, 0000FFFFh + jnz LenLower + + test eax,0ffffffffh + + jnz LenLower32 + + add rdx,4 + shr rax,32 + or ax,ax + jnz LenLower + +LenLower32: + shr eax,16 + add rdx,2 +LenLower: sub al, 1 + adc rdx, 0 +;;; Calculate the length of the match. If it is longer than MAX_MATCH, +;;; then automatically accept it as the best possible match and leave. + + lea rax, [rdi + rdx] + sub rax, r9 + cmp eax, MAX_MATCH + jge LenMaximum + +;;; If the length of the match is not longer than the best match we +;;; have so far, then forget it and return to the lookup loop. +;/////////////////////////////////// + + cmp eax, r11d + jg LongerMatch + + lea rsi,[r10+r11] + + mov rdi, prev_ad + mov edx, [chainlenwmask] + jmp LookupLoop + +;;; s->match_start = cur_match; +;;; best_len = len; +;;; if (len >= nice_match) break; +;;; scan_end = *(ushf*)(scan+best_len-1); + +LongerMatch: + mov r11d, eax + mov match_start, r8d + cmp eax, [nicematch] + jge LeaveNow + + lea rsi,[r10+rax] + + movzx ebx, word ptr [r9 + rax - 1] + mov rdi, prev_ad + mov edx, [chainlenwmask] + jmp LookupLoop + +;;; Accept the current string, with the maximum possible length. + +LenMaximum: + mov r11d,MAX_MATCH + mov match_start, r8d + +;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len; +;;; return s->lookahead; + +LeaveNow: +IFDEF INFOZIP + mov eax,r11d +ELSE + mov eax, Lookahead + cmp r11d, eax + cmovng eax, r11d +ENDIF + +;;; Restore the stack and return from whence we came. + + + mov rsi,[save_rsi] + mov rdi,[save_rdi] + mov rbx,[save_rbx] + mov rbp,[save_rbp] + mov r12,[save_r12] + mov r13,[save_r13] +; mov r14,[save_r14] +; mov r15,[save_r15] + + + ret 0 +; please don't remove this string ! +; Your can freely use gvmat64 in any free or commercial app +; but it is far better don't remove the string in the binary! + db 0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998, converted to amd 64 by Gilles Vollant 2005",0dh,0ah,0 +longest_match ENDP + +match_init PROC + ret 0 +match_init ENDP + + +END diff --git a/src/png/zlib/contrib/masmx64/inffas8664.c b/src/png/zlib/contrib/masmx64/inffas8664.c new file mode 100644 index 0000000000..aa861a3339 --- /dev/null +++ b/src/png/zlib/contrib/masmx64/inffas8664.c @@ -0,0 +1,186 @@ +/* inffas8664.c is a hand tuned assembler version of inffast.c - fast decoding + * version for AMD64 on Windows using Microsoft C compiler + * + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Copyright (C) 2003 Chris Anderson + * Please use the copyright conditions above. + * + * 2005 - Adaptation to Microsoft C Compiler for AMD64 by Gilles Vollant + * + * inffas8664.c call function inffas8664fnc in inffasx64.asm + * inffasx64.asm is automatically convert from AMD64 portion of inffas86.c + * + * Dec-29-2003 -- I added AMD64 inflate asm support. This version is also + * slightly quicker on x86 systems because, instead of using rep movsb to copy + * data, it uses rep movsw, which moves data in 2-byte chunks instead of single + * bytes. I've tested the AMD64 code on a Fedora Core 1 + the x86_64 updates + * from http://fedora.linux.duke.edu/fc1_x86_64 + * which is running on an Athlon 64 3000+ / Gigabyte GA-K8VT800M system with + * 1GB ram. The 64-bit version is about 4% faster than the 32-bit version, + * when decompressing mozilla-source-1.3.tar.gz. + * + * Mar-13-2003 -- Most of this is derived from inffast.S which is derived from + * the gcc -S output of zlib-1.2.0/inffast.c. Zlib-1.2.0 is in beta release at + * the moment. I have successfully compiled and tested this code with gcc2.96, + * gcc3.2, icc5.0, msvc6.0. It is very close to the speed of inffast.S + * compiled with gcc -DNO_MMX, but inffast.S is still faster on the P3 with MMX + * enabled. I will attempt to merge the MMX code into this version. Newer + * versions of this and inffast.S can be found at + * http://www.eetbeetee.com/zlib/ and http://www.charm.net/~christop/zlib/ + * + */ + +#include +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* Mark Adler's comments from inffast.c: */ + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ + + + + typedef struct inffast_ar { +/* 64 32 x86 x86_64 */ +/* ar offset register */ +/* 0 0 */ void *esp; /* esp save */ +/* 8 4 */ void *ebp; /* ebp save */ +/* 16 8 */ unsigned char FAR *in; /* esi rsi local strm->next_in */ +/* 24 12 */ unsigned char FAR *last; /* r9 while in < last */ +/* 32 16 */ unsigned char FAR *out; /* edi rdi local strm->next_out */ +/* 40 20 */ unsigned char FAR *beg; /* inflate()'s init next_out */ +/* 48 24 */ unsigned char FAR *end; /* r10 while out < end */ +/* 56 28 */ unsigned char FAR *window;/* size of window, wsize!=0 */ +/* 64 32 */ code const FAR *lcode; /* ebp rbp local strm->lencode */ +/* 72 36 */ code const FAR *dcode; /* r11 local strm->distcode */ +/* 80 40 */ size_t /*unsigned long */hold; /* edx rdx local strm->hold */ +/* 88 44 */ unsigned bits; /* ebx rbx local strm->bits */ +/* 92 48 */ unsigned wsize; /* window size */ +/* 96 52 */ unsigned write; /* window write index */ +/*100 56 */ unsigned lmask; /* r12 mask for lcode */ +/*104 60 */ unsigned dmask; /* r13 mask for dcode */ +/*108 64 */ unsigned len; /* r14 match length */ +/*112 68 */ unsigned dist; /* r15 match distance */ +/*116 72 */ unsigned status; /* set when state chng*/ + } type_ar; +#ifdef ASMINF + +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + type_ar ar; + void inffas8664fnc(struct inffast_ar * par); + + + +#if (defined( __GNUC__ ) && defined( __amd64__ ) && ! defined( __i386 )) || (defined(_MSC_VER) && defined(_M_AMD64)) +#define PAD_AVAIL_IN 6 +#define PAD_AVAIL_OUT 258 +#else +#define PAD_AVAIL_IN 5 +#define PAD_AVAIL_OUT 257 +#endif + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + + ar.in = strm->next_in; + ar.last = ar.in + (strm->avail_in - PAD_AVAIL_IN); + ar.out = strm->next_out; + ar.beg = ar.out - (start - strm->avail_out); + ar.end = ar.out + (strm->avail_out - PAD_AVAIL_OUT); + ar.wsize = state->wsize; + ar.write = state->wnext; + ar.window = state->window; + ar.hold = state->hold; + ar.bits = state->bits; + ar.lcode = state->lencode; + ar.dcode = state->distcode; + ar.lmask = (1U << state->lenbits) - 1; + ar.dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + + /* align in on 1/2 hold size boundary */ + while (((size_t)(void *)ar.in & (sizeof(ar.hold) / 2 - 1)) != 0) { + ar.hold += (unsigned long)*ar.in++ << ar.bits; + ar.bits += 8; + } + + inffas8664fnc(&ar); + + if (ar.status > 1) { + if (ar.status == 2) + strm->msg = "invalid literal/length code"; + else if (ar.status == 3) + strm->msg = "invalid distance code"; + else + strm->msg = "invalid distance too far back"; + state->mode = BAD; + } + else if ( ar.status == 1 ) { + state->mode = TYPE; + } + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + ar.len = ar.bits >> 3; + ar.in -= ar.len; + ar.bits -= ar.len << 3; + ar.hold &= (1U << ar.bits) - 1; + + /* update state and return */ + strm->next_in = ar.in; + strm->next_out = ar.out; + strm->avail_in = (unsigned)(ar.in < ar.last ? + PAD_AVAIL_IN + (ar.last - ar.in) : + PAD_AVAIL_IN - (ar.in - ar.last)); + strm->avail_out = (unsigned)(ar.out < ar.end ? + PAD_AVAIL_OUT + (ar.end - ar.out) : + PAD_AVAIL_OUT - (ar.out - ar.end)); + state->hold = (unsigned long)ar.hold; + state->bits = ar.bits; + return; +} + +#endif diff --git a/src/png/zlib/contrib/masmx64/inffasx64.asm b/src/png/zlib/contrib/masmx64/inffasx64.asm new file mode 100644 index 0000000000..41ec82392e --- /dev/null +++ b/src/png/zlib/contrib/masmx64/inffasx64.asm @@ -0,0 +1,396 @@ +; inffasx64.asm is a hand tuned assembler version of inffast.c - fast decoding +; version for AMD64 on Windows using Microsoft C compiler +; +; inffasx64.asm is automatically convert from AMD64 portion of inffas86.c +; inffasx64.asm is called by inffas8664.c, which contain more info. + + +; to compile this file, I use option +; ml64.exe /Flinffasx64 /c /Zi inffasx64.asm +; with Microsoft Macro Assembler (x64) for AMD64 +; + +; This file compile with Microsoft Macro Assembler (x64) for AMD64 +; +; ml64.exe is given with Visual Studio 2005/2008/2010 and Windows WDK +; +; (you can get Windows WDK with ml64 for AMD64 from +; http://www.microsoft.com/whdc/Devtools/wdk/default.mspx for low price) +; + + +.code +inffas8664fnc PROC + +; see http://weblogs.asp.net/oldnewthing/archive/2004/01/14/58579.aspx and +; http://msdn.microsoft.com/library/en-us/kmarch/hh/kmarch/64bitAMD_8e951dd2-ee77-4728-8702-55ce4b5dd24a.xml.asp +; +; All registers must be preserved across the call, except for +; rax, rcx, rdx, r8, r-9, r10, and r11, which are scratch. + + + mov [rsp-8],rsi + mov [rsp-16],rdi + mov [rsp-24],r12 + mov [rsp-32],r13 + mov [rsp-40],r14 + mov [rsp-48],r15 + mov [rsp-56],rbx + + mov rax,rcx + + mov [rax+8], rbp ; /* save regs rbp and rsp */ + mov [rax], rsp + + mov rsp, rax ; /* make rsp point to &ar */ + + mov rsi, [rsp+16] ; /* rsi = in */ + mov rdi, [rsp+32] ; /* rdi = out */ + mov r9, [rsp+24] ; /* r9 = last */ + mov r10, [rsp+48] ; /* r10 = end */ + mov rbp, [rsp+64] ; /* rbp = lcode */ + mov r11, [rsp+72] ; /* r11 = dcode */ + mov rdx, [rsp+80] ; /* rdx = hold */ + mov ebx, [rsp+88] ; /* ebx = bits */ + mov r12d, [rsp+100] ; /* r12d = lmask */ + mov r13d, [rsp+104] ; /* r13d = dmask */ + ; /* r14d = len */ + ; /* r15d = dist */ + + + cld + cmp r10, rdi + je L_one_time ; /* if only one decode left */ + cmp r9, rsi + + jne L_do_loop + + +L_one_time: + mov r8, r12 ; /* r8 = lmask */ + cmp bl, 32 + ja L_get_length_code_one_time + + lodsd ; /* eax = *(uint *)in++ */ + mov cl, bl ; /* cl = bits, needs it for shifting */ + add bl, 32 ; /* bits += 32 */ + shl rax, cl + or rdx, rax ; /* hold |= *((uint *)in)++ << bits */ + jmp L_get_length_code_one_time + +ALIGN 4 +L_while_test: + cmp r10, rdi + jbe L_break_loop + cmp r9, rsi + jbe L_break_loop + +L_do_loop: + mov r8, r12 ; /* r8 = lmask */ + cmp bl, 32 + ja L_get_length_code ; /* if (32 < bits) */ + + lodsd ; /* eax = *(uint *)in++ */ + mov cl, bl ; /* cl = bits, needs it for shifting */ + add bl, 32 ; /* bits += 32 */ + shl rax, cl + or rdx, rax ; /* hold |= *((uint *)in)++ << bits */ + +L_get_length_code: + and r8, rdx ; /* r8 &= hold */ + mov eax, [rbp+r8*4] ; /* eax = lcode[hold & lmask] */ + + mov cl, ah ; /* cl = this.bits */ + sub bl, ah ; /* bits -= this.bits */ + shr rdx, cl ; /* hold >>= this.bits */ + + test al, al + jnz L_test_for_length_base ; /* if (op != 0) 45.7% */ + + mov r8, r12 ; /* r8 = lmask */ + shr eax, 16 ; /* output this.val char */ + stosb + +L_get_length_code_one_time: + and r8, rdx ; /* r8 &= hold */ + mov eax, [rbp+r8*4] ; /* eax = lcode[hold & lmask] */ + +L_dolen: + mov cl, ah ; /* cl = this.bits */ + sub bl, ah ; /* bits -= this.bits */ + shr rdx, cl ; /* hold >>= this.bits */ + + test al, al + jnz L_test_for_length_base ; /* if (op != 0) 45.7% */ + + shr eax, 16 ; /* output this.val char */ + stosb + jmp L_while_test + +ALIGN 4 +L_test_for_length_base: + mov r14d, eax ; /* len = this */ + shr r14d, 16 ; /* len = this.val */ + mov cl, al + + test al, 16 + jz L_test_for_second_level_length ; /* if ((op & 16) == 0) 8% */ + and cl, 15 ; /* op &= 15 */ + jz L_decode_distance ; /* if (!op) */ + +L_add_bits_to_len: + sub bl, cl + xor eax, eax + inc eax + shl eax, cl + dec eax + and eax, edx ; /* eax &= hold */ + shr rdx, cl + add r14d, eax ; /* len += hold & mask[op] */ + +L_decode_distance: + mov r8, r13 ; /* r8 = dmask */ + cmp bl, 32 + ja L_get_distance_code ; /* if (32 < bits) */ + + lodsd ; /* eax = *(uint *)in++ */ + mov cl, bl ; /* cl = bits, needs it for shifting */ + add bl, 32 ; /* bits += 32 */ + shl rax, cl + or rdx, rax ; /* hold |= *((uint *)in)++ << bits */ + +L_get_distance_code: + and r8, rdx ; /* r8 &= hold */ + mov eax, [r11+r8*4] ; /* eax = dcode[hold & dmask] */ + +L_dodist: + mov r15d, eax ; /* dist = this */ + shr r15d, 16 ; /* dist = this.val */ + mov cl, ah + sub bl, ah ; /* bits -= this.bits */ + shr rdx, cl ; /* hold >>= this.bits */ + mov cl, al ; /* cl = this.op */ + + test al, 16 ; /* if ((op & 16) == 0) */ + jz L_test_for_second_level_dist + and cl, 15 ; /* op &= 15 */ + jz L_check_dist_one + +L_add_bits_to_dist: + sub bl, cl + xor eax, eax + inc eax + shl eax, cl + dec eax ; /* (1 << op) - 1 */ + and eax, edx ; /* eax &= hold */ + shr rdx, cl + add r15d, eax ; /* dist += hold & ((1 << op) - 1) */ + +L_check_window: + mov r8, rsi ; /* save in so from can use it's reg */ + mov rax, rdi + sub rax, [rsp+40] ; /* nbytes = out - beg */ + + cmp eax, r15d + jb L_clip_window ; /* if (dist > nbytes) 4.2% */ + + mov ecx, r14d ; /* ecx = len */ + mov rsi, rdi + sub rsi, r15 ; /* from = out - dist */ + + sar ecx, 1 + jnc L_copy_two ; /* if len % 2 == 0 */ + + rep movsw + mov al, [rsi] + mov [rdi], al + inc rdi + + mov rsi, r8 ; /* move in back to %rsi, toss from */ + jmp L_while_test + +L_copy_two: + rep movsw + mov rsi, r8 ; /* move in back to %rsi, toss from */ + jmp L_while_test + +ALIGN 4 +L_check_dist_one: + cmp r15d, 1 ; /* if dist 1, is a memset */ + jne L_check_window + cmp [rsp+40], rdi ; /* if out == beg, outside window */ + je L_check_window + + mov ecx, r14d ; /* ecx = len */ + mov al, [rdi-1] + mov ah, al + + sar ecx, 1 + jnc L_set_two + mov [rdi], al + inc rdi + +L_set_two: + rep stosw + jmp L_while_test + +ALIGN 4 +L_test_for_second_level_length: + test al, 64 + jnz L_test_for_end_of_block ; /* if ((op & 64) != 0) */ + + xor eax, eax + inc eax + shl eax, cl + dec eax + and eax, edx ; /* eax &= hold */ + add eax, r14d ; /* eax += len */ + mov eax, [rbp+rax*4] ; /* eax = lcode[val+(hold&mask[op])]*/ + jmp L_dolen + +ALIGN 4 +L_test_for_second_level_dist: + test al, 64 + jnz L_invalid_distance_code ; /* if ((op & 64) != 0) */ + + xor eax, eax + inc eax + shl eax, cl + dec eax + and eax, edx ; /* eax &= hold */ + add eax, r15d ; /* eax += dist */ + mov eax, [r11+rax*4] ; /* eax = dcode[val+(hold&mask[op])]*/ + jmp L_dodist + +ALIGN 4 +L_clip_window: + mov ecx, eax ; /* ecx = nbytes */ + mov eax, [rsp+92] ; /* eax = wsize, prepare for dist cmp */ + neg ecx ; /* nbytes = -nbytes */ + + cmp eax, r15d + jb L_invalid_distance_too_far ; /* if (dist > wsize) */ + + add ecx, r15d ; /* nbytes = dist - nbytes */ + cmp dword ptr [rsp+96], 0 + jne L_wrap_around_window ; /* if (write != 0) */ + + mov rsi, [rsp+56] ; /* from = window */ + sub eax, ecx ; /* eax -= nbytes */ + add rsi, rax ; /* from += wsize - nbytes */ + + mov eax, r14d ; /* eax = len */ + cmp r14d, ecx + jbe L_do_copy ; /* if (nbytes >= len) */ + + sub eax, ecx ; /* eax -= nbytes */ + rep movsb + mov rsi, rdi + sub rsi, r15 ; /* from = &out[ -dist ] */ + jmp L_do_copy + +ALIGN 4 +L_wrap_around_window: + mov eax, [rsp+96] ; /* eax = write */ + cmp ecx, eax + jbe L_contiguous_in_window ; /* if (write >= nbytes) */ + + mov esi, [rsp+92] ; /* from = wsize */ + add rsi, [rsp+56] ; /* from += window */ + add rsi, rax ; /* from += write */ + sub rsi, rcx ; /* from -= nbytes */ + sub ecx, eax ; /* nbytes -= write */ + + mov eax, r14d ; /* eax = len */ + cmp eax, ecx + jbe L_do_copy ; /* if (nbytes >= len) */ + + sub eax, ecx ; /* len -= nbytes */ + rep movsb + mov rsi, [rsp+56] ; /* from = window */ + mov ecx, [rsp+96] ; /* nbytes = write */ + cmp eax, ecx + jbe L_do_copy ; /* if (nbytes >= len) */ + + sub eax, ecx ; /* len -= nbytes */ + rep movsb + mov rsi, rdi + sub rsi, r15 ; /* from = out - dist */ + jmp L_do_copy + +ALIGN 4 +L_contiguous_in_window: + mov rsi, [rsp+56] ; /* rsi = window */ + add rsi, rax + sub rsi, rcx ; /* from += write - nbytes */ + + mov eax, r14d ; /* eax = len */ + cmp eax, ecx + jbe L_do_copy ; /* if (nbytes >= len) */ + + sub eax, ecx ; /* len -= nbytes */ + rep movsb + mov rsi, rdi + sub rsi, r15 ; /* from = out - dist */ + jmp L_do_copy ; /* if (nbytes >= len) */ + +ALIGN 4 +L_do_copy: + mov ecx, eax ; /* ecx = len */ + rep movsb + + mov rsi, r8 ; /* move in back to %esi, toss from */ + jmp L_while_test + +L_test_for_end_of_block: + test al, 32 + jz L_invalid_literal_length_code + mov dword ptr [rsp+116], 1 + jmp L_break_loop_with_status + +L_invalid_literal_length_code: + mov dword ptr [rsp+116], 2 + jmp L_break_loop_with_status + +L_invalid_distance_code: + mov dword ptr [rsp+116], 3 + jmp L_break_loop_with_status + +L_invalid_distance_too_far: + mov dword ptr [rsp+116], 4 + jmp L_break_loop_with_status + +L_break_loop: + mov dword ptr [rsp+116], 0 + +L_break_loop_with_status: +; /* put in, out, bits, and hold back into ar and pop esp */ + mov [rsp+16], rsi ; /* in */ + mov [rsp+32], rdi ; /* out */ + mov [rsp+88], ebx ; /* bits */ + mov [rsp+80], rdx ; /* hold */ + + mov rax, [rsp] ; /* restore rbp and rsp */ + mov rbp, [rsp+8] + mov rsp, rax + + + + mov rsi,[rsp-8] + mov rdi,[rsp-16] + mov r12,[rsp-24] + mov r13,[rsp-32] + mov r14,[rsp-40] + mov r15,[rsp-48] + mov rbx,[rsp-56] + + ret 0 +; : +; : "m" (ar) +; : "memory", "%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi", +; "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" +; ); + +inffas8664fnc ENDP +;_TEXT ENDS +END diff --git a/src/png/zlib/contrib/masmx64/readme.txt b/src/png/zlib/contrib/masmx64/readme.txt new file mode 100644 index 0000000000..652571c7a5 --- /dev/null +++ b/src/png/zlib/contrib/masmx64/readme.txt @@ -0,0 +1,31 @@ +Summary +------- +This directory contains ASM implementations of the functions +longest_match() and inflate_fast(), for 64 bits x86 (both AMD64 and Intel EM64t), +for use with Microsoft Macro Assembler (x64) for AMD64 and Microsoft C++ 64 bits. + +gvmat64.asm is written by Gilles Vollant (2005), by using Brian Raiter 686/32 bits + assembly optimized version from Jean-loup Gailly original longest_match function + +inffasx64.asm and inffas8664.c were written by Chris Anderson, by optimizing + original function from Mark Adler + +Use instructions +---------------- +Assemble the .asm files using MASM and put the object files into the zlib source +directory. You can also get object files here: + + http://www.winimage.com/zLibDll/zlib124_masm_obj.zip + +define ASMV and ASMINF in your project. Include inffas8664.c in your source tree, +and inffasx64.obj and gvmat64.obj as object to link. + + +Build instructions +------------------ +run bld_64.bat with Microsoft Macro Assembler (x64) for AMD64 (ml64.exe) + +ml64.exe is given with Visual Studio 2005, Windows 2003 server DDK + +You can get Windows 2003 server DDK with ml64 and cl for AMD64 from + http://www.microsoft.com/whdc/devtools/ddk/default.mspx for low price) diff --git a/src/png/zlib/contrib/masmx86/bld_ml32.bat b/src/png/zlib/contrib/masmx86/bld_ml32.bat new file mode 100644 index 0000000000..fcf5755e46 --- /dev/null +++ b/src/png/zlib/contrib/masmx86/bld_ml32.bat @@ -0,0 +1,2 @@ +ml /coff /Zi /c /Flmatch686.lst match686.asm +ml /coff /Zi /c /Flinffas32.lst inffas32.asm diff --git a/src/png/zlib/contrib/masmx86/inffas32.asm b/src/png/zlib/contrib/masmx86/inffas32.asm new file mode 100644 index 0000000000..cb37a81e4e --- /dev/null +++ b/src/png/zlib/contrib/masmx86/inffas32.asm @@ -0,0 +1,1080 @@ +;/* inffas32.asm is a hand tuned assembler version of inffast.c -- fast decoding +; * +; * inffas32.asm is derivated from inffas86.c, with translation of assembly code +; * +; * Copyright (C) 1995-2003 Mark Adler +; * For conditions of distribution and use, see copyright notice in zlib.h +; * +; * Copyright (C) 2003 Chris Anderson +; * Please use the copyright conditions above. +; * +; * Mar-13-2003 -- Most of this is derived from inffast.S which is derived from +; * the gcc -S output of zlib-1.2.0/inffast.c. Zlib-1.2.0 is in beta release at +; * the moment. I have successfully compiled and tested this code with gcc2.96, +; * gcc3.2, icc5.0, msvc6.0. It is very close to the speed of inffast.S +; * compiled with gcc -DNO_MMX, but inffast.S is still faster on the P3 with MMX +; * enabled. I will attempt to merge the MMX code into this version. Newer +; * versions of this and inffast.S can be found at +; * http://www.eetbeetee.com/zlib/ and http://www.charm.net/~christop/zlib/ +; * +; * 2005 : modification by Gilles Vollant +; */ +; For Visual C++ 4.x and higher and ML 6.x and higher +; ml.exe is in directory \MASM611C of Win95 DDK +; ml.exe is also distributed in http://www.masm32.com/masmdl.htm +; and in VC++2003 toolkit at http://msdn.microsoft.com/visualc/vctoolkit2003/ +; +; +; compile with command line option +; ml /coff /Zi /c /Flinffas32.lst inffas32.asm + +; if you define NO_GZIP (see inflate.h), compile with +; ml /coff /Zi /c /Flinffas32.lst /DNO_GUNZIP inffas32.asm + + +; zlib122sup is 0 fort zlib 1.2.2.1 and lower +; zlib122sup is 8 fort zlib 1.2.2.2 and more (with addition of dmax and head +; in inflate_state in inflate.h) +zlib1222sup equ 8 + + +IFDEF GUNZIP + INFLATE_MODE_TYPE equ 11 + INFLATE_MODE_BAD equ 26 +ELSE + IFNDEF NO_GUNZIP + INFLATE_MODE_TYPE equ 11 + INFLATE_MODE_BAD equ 26 + ELSE + INFLATE_MODE_TYPE equ 3 + INFLATE_MODE_BAD equ 17 + ENDIF +ENDIF + + +; 75 "inffast.S" +;FILE "inffast.S" + +;;;GLOBAL _inflate_fast + +;;;SECTION .text + + + + .586p + .mmx + + name inflate_fast_x86 + .MODEL FLAT + +_DATA segment +inflate_fast_use_mmx: + dd 1 + + +_TEXT segment + + + +ALIGN 4 + db 'Fast decoding Code from Chris Anderson' + db 0 + +ALIGN 4 +invalid_literal_length_code_msg: + db 'invalid literal/length code' + db 0 + +ALIGN 4 +invalid_distance_code_msg: + db 'invalid distance code' + db 0 + +ALIGN 4 +invalid_distance_too_far_msg: + db 'invalid distance too far back' + db 0 + + +ALIGN 4 +inflate_fast_mask: +dd 0 +dd 1 +dd 3 +dd 7 +dd 15 +dd 31 +dd 63 +dd 127 +dd 255 +dd 511 +dd 1023 +dd 2047 +dd 4095 +dd 8191 +dd 16383 +dd 32767 +dd 65535 +dd 131071 +dd 262143 +dd 524287 +dd 1048575 +dd 2097151 +dd 4194303 +dd 8388607 +dd 16777215 +dd 33554431 +dd 67108863 +dd 134217727 +dd 268435455 +dd 536870911 +dd 1073741823 +dd 2147483647 +dd 4294967295 + + +mode_state equ 0 ;/* state->mode */ +wsize_state equ (32+zlib1222sup) ;/* state->wsize */ +write_state equ (36+4+zlib1222sup) ;/* state->write */ +window_state equ (40+4+zlib1222sup) ;/* state->window */ +hold_state equ (44+4+zlib1222sup) ;/* state->hold */ +bits_state equ (48+4+zlib1222sup) ;/* state->bits */ +lencode_state equ (64+4+zlib1222sup) ;/* state->lencode */ +distcode_state equ (68+4+zlib1222sup) ;/* state->distcode */ +lenbits_state equ (72+4+zlib1222sup) ;/* state->lenbits */ +distbits_state equ (76+4+zlib1222sup) ;/* state->distbits */ + + +;;SECTION .text +; 205 "inffast.S" +;GLOBAL inflate_fast_use_mmx + +;SECTION .data + + +; GLOBAL inflate_fast_use_mmx:object +;.size inflate_fast_use_mmx, 4 +; 226 "inffast.S" +;SECTION .text + +ALIGN 4 +_inflate_fast proc near +.FPO (16, 4, 0, 0, 1, 0) + push edi + push esi + push ebp + push ebx + pushfd + sub esp,64 + cld + + + + + mov esi, [esp+88] + mov edi, [esi+28] + + + + + + + + mov edx, [esi+4] + mov eax, [esi+0] + + add edx,eax + sub edx,11 + + mov [esp+44],eax + mov [esp+20],edx + + mov ebp, [esp+92] + mov ecx, [esi+16] + mov ebx, [esi+12] + + sub ebp,ecx + neg ebp + add ebp,ebx + + sub ecx,257 + add ecx,ebx + + mov [esp+60],ebx + mov [esp+40],ebp + mov [esp+16],ecx +; 285 "inffast.S" + mov eax, [edi+lencode_state] + mov ecx, [edi+distcode_state] + + mov [esp+8],eax + mov [esp+12],ecx + + mov eax,1 + mov ecx, [edi+lenbits_state] + shl eax,cl + dec eax + mov [esp+0],eax + + mov eax,1 + mov ecx, [edi+distbits_state] + shl eax,cl + dec eax + mov [esp+4],eax + + mov eax, [edi+wsize_state] + mov ecx, [edi+write_state] + mov edx, [edi+window_state] + + mov [esp+52],eax + mov [esp+48],ecx + mov [esp+56],edx + + mov ebp, [edi+hold_state] + mov ebx, [edi+bits_state] +; 321 "inffast.S" + mov esi, [esp+44] + mov ecx, [esp+20] + cmp ecx,esi + ja L_align_long + + add ecx,11 + sub ecx,esi + mov eax,12 + sub eax,ecx + lea edi, [esp+28] + rep movsb + mov ecx,eax + xor eax,eax + rep stosb + lea esi, [esp+28] + mov [esp+20],esi + jmp L_is_aligned + + +L_align_long: + test esi,3 + jz L_is_aligned + xor eax,eax + mov al, [esi] + inc esi + mov ecx,ebx + add ebx,8 + shl eax,cl + or ebp,eax + jmp L_align_long + +L_is_aligned: + mov edi, [esp+60] +; 366 "inffast.S" +L_check_mmx: + cmp dword ptr [inflate_fast_use_mmx],2 + je L_init_mmx + ja L_do_loop + + push eax + push ebx + push ecx + push edx + pushfd + mov eax, [esp] + xor dword ptr [esp],0200000h + + + + + popfd + pushfd + pop edx + xor edx,eax + jz L_dont_use_mmx + xor eax,eax + cpuid + cmp ebx,0756e6547h + jne L_dont_use_mmx + cmp ecx,06c65746eh + jne L_dont_use_mmx + cmp edx,049656e69h + jne L_dont_use_mmx + mov eax,1 + cpuid + shr eax,8 + and eax,15 + cmp eax,6 + jne L_dont_use_mmx + test edx,0800000h + jnz L_use_mmx + jmp L_dont_use_mmx +L_use_mmx: + mov dword ptr [inflate_fast_use_mmx],2 + jmp L_check_mmx_pop +L_dont_use_mmx: + mov dword ptr [inflate_fast_use_mmx],3 +L_check_mmx_pop: + pop edx + pop ecx + pop ebx + pop eax + jmp L_check_mmx +; 426 "inffast.S" +ALIGN 4 +L_do_loop: +; 437 "inffast.S" + cmp bl,15 + ja L_get_length_code + + xor eax,eax + lodsw + mov cl,bl + add bl,16 + shl eax,cl + or ebp,eax + +L_get_length_code: + mov edx, [esp+0] + mov ecx, [esp+8] + and edx,ebp + mov eax, [ecx+edx*4] + +L_dolen: + + + + + + + mov cl,ah + sub bl,ah + shr ebp,cl + + + + + + + test al,al + jnz L_test_for_length_base + + shr eax,16 + stosb + +L_while_test: + + + cmp [esp+16],edi + jbe L_break_loop + + cmp [esp+20],esi + ja L_do_loop + jmp L_break_loop + +L_test_for_length_base: +; 502 "inffast.S" + mov edx,eax + shr edx,16 + mov cl,al + + test al,16 + jz L_test_for_second_level_length + and cl,15 + jz L_save_len + cmp bl,cl + jae L_add_bits_to_len + + mov ch,cl + xor eax,eax + lodsw + mov cl,bl + add bl,16 + shl eax,cl + or ebp,eax + mov cl,ch + +L_add_bits_to_len: + mov eax,1 + shl eax,cl + dec eax + sub bl,cl + and eax,ebp + shr ebp,cl + add edx,eax + +L_save_len: + mov [esp+24],edx + + +L_decode_distance: +; 549 "inffast.S" + cmp bl,15 + ja L_get_distance_code + + xor eax,eax + lodsw + mov cl,bl + add bl,16 + shl eax,cl + or ebp,eax + +L_get_distance_code: + mov edx, [esp+4] + mov ecx, [esp+12] + and edx,ebp + mov eax, [ecx+edx*4] + + +L_dodist: + mov edx,eax + shr edx,16 + mov cl,ah + sub bl,ah + shr ebp,cl +; 584 "inffast.S" + mov cl,al + + test al,16 + jz L_test_for_second_level_dist + and cl,15 + jz L_check_dist_one + cmp bl,cl + jae L_add_bits_to_dist + + mov ch,cl + xor eax,eax + lodsw + mov cl,bl + add bl,16 + shl eax,cl + or ebp,eax + mov cl,ch + +L_add_bits_to_dist: + mov eax,1 + shl eax,cl + dec eax + sub bl,cl + and eax,ebp + shr ebp,cl + add edx,eax + jmp L_check_window + +L_check_window: +; 625 "inffast.S" + mov [esp+44],esi + mov eax,edi + sub eax, [esp+40] + + cmp eax,edx + jb L_clip_window + + mov ecx, [esp+24] + mov esi,edi + sub esi,edx + + sub ecx,3 + mov al, [esi] + mov [edi],al + mov al, [esi+1] + mov dl, [esi+2] + add esi,3 + mov [edi+1],al + mov [edi+2],dl + add edi,3 + rep movsb + + mov esi, [esp+44] + jmp L_while_test + +ALIGN 4 +L_check_dist_one: + cmp edx,1 + jne L_check_window + cmp [esp+40],edi + je L_check_window + + dec edi + mov ecx, [esp+24] + mov al, [edi] + sub ecx,3 + + mov [edi+1],al + mov [edi+2],al + mov [edi+3],al + add edi,4 + rep stosb + + jmp L_while_test + +ALIGN 4 +L_test_for_second_level_length: + + + + + test al,64 + jnz L_test_for_end_of_block + + mov eax,1 + shl eax,cl + dec eax + and eax,ebp + add eax,edx + mov edx, [esp+8] + mov eax, [edx+eax*4] + jmp L_dolen + +ALIGN 4 +L_test_for_second_level_dist: + + + + + test al,64 + jnz L_invalid_distance_code + + mov eax,1 + shl eax,cl + dec eax + and eax,ebp + add eax,edx + mov edx, [esp+12] + mov eax, [edx+eax*4] + jmp L_dodist + +ALIGN 4 +L_clip_window: +; 721 "inffast.S" + mov ecx,eax + mov eax, [esp+52] + neg ecx + mov esi, [esp+56] + + cmp eax,edx + jb L_invalid_distance_too_far + + add ecx,edx + cmp dword ptr [esp+48],0 + jne L_wrap_around_window + + sub eax,ecx + add esi,eax +; 749 "inffast.S" + mov eax, [esp+24] + cmp eax,ecx + jbe L_do_copy1 + + sub eax,ecx + rep movsb + mov esi,edi + sub esi,edx + jmp L_do_copy1 + + cmp eax,ecx + jbe L_do_copy1 + + sub eax,ecx + rep movsb + mov esi,edi + sub esi,edx + jmp L_do_copy1 + +L_wrap_around_window: +; 793 "inffast.S" + mov eax, [esp+48] + cmp ecx,eax + jbe L_contiguous_in_window + + add esi, [esp+52] + add esi,eax + sub esi,ecx + sub ecx,eax + + + mov eax, [esp+24] + cmp eax,ecx + jbe L_do_copy1 + + sub eax,ecx + rep movsb + mov esi, [esp+56] + mov ecx, [esp+48] + cmp eax,ecx + jbe L_do_copy1 + + sub eax,ecx + rep movsb + mov esi,edi + sub esi,edx + jmp L_do_copy1 + +L_contiguous_in_window: +; 836 "inffast.S" + add esi,eax + sub esi,ecx + + + mov eax, [esp+24] + cmp eax,ecx + jbe L_do_copy1 + + sub eax,ecx + rep movsb + mov esi,edi + sub esi,edx + +L_do_copy1: +; 862 "inffast.S" + mov ecx,eax + rep movsb + + mov esi, [esp+44] + jmp L_while_test +; 878 "inffast.S" +ALIGN 4 +L_init_mmx: + emms + + + + + + movd mm0,ebp + mov ebp,ebx +; 896 "inffast.S" + movd mm4,dword ptr [esp+0] + movq mm3,mm4 + movd mm5,dword ptr [esp+4] + movq mm2,mm5 + pxor mm1,mm1 + mov ebx, [esp+8] + jmp L_do_loop_mmx + +ALIGN 4 +L_do_loop_mmx: + psrlq mm0,mm1 + + cmp ebp,32 + ja L_get_length_code_mmx + + movd mm6,ebp + movd mm7,dword ptr [esi] + add esi,4 + psllq mm7,mm6 + add ebp,32 + por mm0,mm7 + +L_get_length_code_mmx: + pand mm4,mm0 + movd eax,mm4 + movq mm4,mm3 + mov eax, [ebx+eax*4] + +L_dolen_mmx: + movzx ecx,ah + movd mm1,ecx + sub ebp,ecx + + test al,al + jnz L_test_for_length_base_mmx + + shr eax,16 + stosb + +L_while_test_mmx: + + + cmp [esp+16],edi + jbe L_break_loop + + cmp [esp+20],esi + ja L_do_loop_mmx + jmp L_break_loop + +L_test_for_length_base_mmx: + + mov edx,eax + shr edx,16 + + test al,16 + jz L_test_for_second_level_length_mmx + and eax,15 + jz L_decode_distance_mmx + + psrlq mm0,mm1 + movd mm1,eax + movd ecx,mm0 + sub ebp,eax + and ecx, [inflate_fast_mask+eax*4] + add edx,ecx + +L_decode_distance_mmx: + psrlq mm0,mm1 + + cmp ebp,32 + ja L_get_dist_code_mmx + + movd mm6,ebp + movd mm7,dword ptr [esi] + add esi,4 + psllq mm7,mm6 + add ebp,32 + por mm0,mm7 + +L_get_dist_code_mmx: + mov ebx, [esp+12] + pand mm5,mm0 + movd eax,mm5 + movq mm5,mm2 + mov eax, [ebx+eax*4] + +L_dodist_mmx: + + movzx ecx,ah + mov ebx,eax + shr ebx,16 + sub ebp,ecx + movd mm1,ecx + + test al,16 + jz L_test_for_second_level_dist_mmx + and eax,15 + jz L_check_dist_one_mmx + +L_add_bits_to_dist_mmx: + psrlq mm0,mm1 + movd mm1,eax + movd ecx,mm0 + sub ebp,eax + and ecx, [inflate_fast_mask+eax*4] + add ebx,ecx + +L_check_window_mmx: + mov [esp+44],esi + mov eax,edi + sub eax, [esp+40] + + cmp eax,ebx + jb L_clip_window_mmx + + mov ecx,edx + mov esi,edi + sub esi,ebx + + sub ecx,3 + mov al, [esi] + mov [edi],al + mov al, [esi+1] + mov dl, [esi+2] + add esi,3 + mov [edi+1],al + mov [edi+2],dl + add edi,3 + rep movsb + + mov esi, [esp+44] + mov ebx, [esp+8] + jmp L_while_test_mmx + +ALIGN 4 +L_check_dist_one_mmx: + cmp ebx,1 + jne L_check_window_mmx + cmp [esp+40],edi + je L_check_window_mmx + + dec edi + mov ecx,edx + mov al, [edi] + sub ecx,3 + + mov [edi+1],al + mov [edi+2],al + mov [edi+3],al + add edi,4 + rep stosb + + mov ebx, [esp+8] + jmp L_while_test_mmx + +ALIGN 4 +L_test_for_second_level_length_mmx: + test al,64 + jnz L_test_for_end_of_block + + and eax,15 + psrlq mm0,mm1 + movd ecx,mm0 + and ecx, [inflate_fast_mask+eax*4] + add ecx,edx + mov eax, [ebx+ecx*4] + jmp L_dolen_mmx + +ALIGN 4 +L_test_for_second_level_dist_mmx: + test al,64 + jnz L_invalid_distance_code + + and eax,15 + psrlq mm0,mm1 + movd ecx,mm0 + and ecx, [inflate_fast_mask+eax*4] + mov eax, [esp+12] + add ecx,ebx + mov eax, [eax+ecx*4] + jmp L_dodist_mmx + +ALIGN 4 +L_clip_window_mmx: + + mov ecx,eax + mov eax, [esp+52] + neg ecx + mov esi, [esp+56] + + cmp eax,ebx + jb L_invalid_distance_too_far + + add ecx,ebx + cmp dword ptr [esp+48],0 + jne L_wrap_around_window_mmx + + sub eax,ecx + add esi,eax + + cmp edx,ecx + jbe L_do_copy1_mmx + + sub edx,ecx + rep movsb + mov esi,edi + sub esi,ebx + jmp L_do_copy1_mmx + + cmp edx,ecx + jbe L_do_copy1_mmx + + sub edx,ecx + rep movsb + mov esi,edi + sub esi,ebx + jmp L_do_copy1_mmx + +L_wrap_around_window_mmx: + + mov eax, [esp+48] + cmp ecx,eax + jbe L_contiguous_in_window_mmx + + add esi, [esp+52] + add esi,eax + sub esi,ecx + sub ecx,eax + + + cmp edx,ecx + jbe L_do_copy1_mmx + + sub edx,ecx + rep movsb + mov esi, [esp+56] + mov ecx, [esp+48] + cmp edx,ecx + jbe L_do_copy1_mmx + + sub edx,ecx + rep movsb + mov esi,edi + sub esi,ebx + jmp L_do_copy1_mmx + +L_contiguous_in_window_mmx: + + add esi,eax + sub esi,ecx + + + cmp edx,ecx + jbe L_do_copy1_mmx + + sub edx,ecx + rep movsb + mov esi,edi + sub esi,ebx + +L_do_copy1_mmx: + + + mov ecx,edx + rep movsb + + mov esi, [esp+44] + mov ebx, [esp+8] + jmp L_while_test_mmx +; 1174 "inffast.S" +L_invalid_distance_code: + + + + + + mov ecx, invalid_distance_code_msg + mov edx,INFLATE_MODE_BAD + jmp L_update_stream_state + +L_test_for_end_of_block: + + + + + + test al,32 + jz L_invalid_literal_length_code + + mov ecx,0 + mov edx,INFLATE_MODE_TYPE + jmp L_update_stream_state + +L_invalid_literal_length_code: + + + + + + mov ecx, invalid_literal_length_code_msg + mov edx,INFLATE_MODE_BAD + jmp L_update_stream_state + +L_invalid_distance_too_far: + + + + mov esi, [esp+44] + mov ecx, invalid_distance_too_far_msg + mov edx,INFLATE_MODE_BAD + jmp L_update_stream_state + +L_update_stream_state: + + mov eax, [esp+88] + test ecx,ecx + jz L_skip_msg + mov [eax+24],ecx +L_skip_msg: + mov eax, [eax+28] + mov [eax+mode_state],edx + jmp L_break_loop + +ALIGN 4 +L_break_loop: +; 1243 "inffast.S" + cmp dword ptr [inflate_fast_use_mmx],2 + jne L_update_next_in + + + + mov ebx,ebp + +L_update_next_in: +; 1266 "inffast.S" + mov eax, [esp+88] + mov ecx,ebx + mov edx, [eax+28] + shr ecx,3 + sub esi,ecx + shl ecx,3 + sub ebx,ecx + mov [eax+12],edi + mov [edx+bits_state],ebx + mov ecx,ebx + + lea ebx, [esp+28] + cmp [esp+20],ebx + jne L_buf_not_used + + sub esi,ebx + mov ebx, [eax+0] + mov [esp+20],ebx + add esi,ebx + mov ebx, [eax+4] + sub ebx,11 + add [esp+20],ebx + +L_buf_not_used: + mov [eax+0],esi + + mov ebx,1 + shl ebx,cl + dec ebx + + + + + + cmp dword ptr [inflate_fast_use_mmx],2 + jne L_update_hold + + + + psrlq mm0,mm1 + movd ebp,mm0 + + emms + +L_update_hold: + + + + and ebp,ebx + mov [edx+hold_state],ebp + + + + + mov ebx, [esp+20] + cmp ebx,esi + jbe L_last_is_smaller + + sub ebx,esi + add ebx,11 + mov [eax+4],ebx + jmp L_fixup_out +L_last_is_smaller: + sub esi,ebx + neg esi + add esi,11 + mov [eax+4],esi + + + + +L_fixup_out: + + mov ebx, [esp+16] + cmp ebx,edi + jbe L_end_is_smaller + + sub ebx,edi + add ebx,257 + mov [eax+16],ebx + jmp L_done +L_end_is_smaller: + sub edi,ebx + neg edi + add edi,257 + mov [eax+16],edi + + + + + +L_done: + add esp,64 + popfd + pop ebx + pop ebp + pop esi + pop edi + ret +_inflate_fast endp + +_TEXT ends +end diff --git a/src/png/zlib/contrib/masmx86/match686.asm b/src/png/zlib/contrib/masmx86/match686.asm new file mode 100644 index 0000000000..69e0eed01d --- /dev/null +++ b/src/png/zlib/contrib/masmx86/match686.asm @@ -0,0 +1,479 @@ +; match686.asm -- Asm portion of the optimized longest_match for 32 bits x86 +; Copyright (C) 1995-1996 Jean-loup Gailly, Brian Raiter and Gilles Vollant. +; File written by Gilles Vollant, by converting match686.S from Brian Raiter +; for MASM. This is as assembly version of longest_match +; from Jean-loup Gailly in deflate.c +; +; http://www.zlib.net +; http://www.winimage.com/zLibDll +; http://www.muppetlabs.com/~breadbox/software/assembly.html +; +; For Visual C++ 4.x and higher and ML 6.x and higher +; ml.exe is distributed in +; http://www.microsoft.com/downloads/details.aspx?FamilyID=7a1c9da0-0510-44a2-b042-7ef370530c64 +; +; this file contain two implementation of longest_match +; +; this longest_match was written by Brian raiter (1998), optimized for Pentium Pro +; (and the faster known version of match_init on modern Core 2 Duo and AMD Phenom) +; +; for using an assembly version of longest_match, you need define ASMV in project +; +; compile the asm file running +; ml /coff /Zi /c /Flmatch686.lst match686.asm +; and do not include match686.obj in your project +; +; note: contrib of zLib 1.2.3 and earlier contained both a deprecated version for +; Pentium (prior Pentium Pro) and this version for Pentium Pro and modern processor +; with autoselect (with cpu detection code) +; if you want support the old pentium optimization, you can still use these version +; +; this file is not optimized for old pentium, but it compatible with all x86 32 bits +; processor (starting 80386) +; +; +; see below : zlib1222add must be adjuster if you use a zlib version < 1.2.2.2 + +;uInt longest_match(s, cur_match) +; deflate_state *s; +; IPos cur_match; /* current match */ + + NbStack equ 76 + cur_match equ dword ptr[esp+NbStack-0] + str_s equ dword ptr[esp+NbStack-4] +; 5 dword on top (ret,ebp,esi,edi,ebx) + adrret equ dword ptr[esp+NbStack-8] + pushebp equ dword ptr[esp+NbStack-12] + pushedi equ dword ptr[esp+NbStack-16] + pushesi equ dword ptr[esp+NbStack-20] + pushebx equ dword ptr[esp+NbStack-24] + + chain_length equ dword ptr [esp+NbStack-28] + limit equ dword ptr [esp+NbStack-32] + best_len equ dword ptr [esp+NbStack-36] + window equ dword ptr [esp+NbStack-40] + prev equ dword ptr [esp+NbStack-44] + scan_start equ word ptr [esp+NbStack-48] + wmask equ dword ptr [esp+NbStack-52] + match_start_ptr equ dword ptr [esp+NbStack-56] + nice_match equ dword ptr [esp+NbStack-60] + scan equ dword ptr [esp+NbStack-64] + + windowlen equ dword ptr [esp+NbStack-68] + match_start equ dword ptr [esp+NbStack-72] + strend equ dword ptr [esp+NbStack-76] + NbStackAdd equ (NbStack-24) + + .386p + + name gvmatch + .MODEL FLAT + + + +; all the +zlib1222add offsets are due to the addition of fields +; in zlib in the deflate_state structure since the asm code was first written +; (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)"). +; (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0"). +; if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8"). + + zlib1222add equ 8 + +; Note : these value are good with a 8 bytes boundary pack structure + dep_chain_length equ 74h+zlib1222add + dep_window equ 30h+zlib1222add + dep_strstart equ 64h+zlib1222add + dep_prev_length equ 70h+zlib1222add + dep_nice_match equ 88h+zlib1222add + dep_w_size equ 24h+zlib1222add + dep_prev equ 38h+zlib1222add + dep_w_mask equ 2ch+zlib1222add + dep_good_match equ 84h+zlib1222add + dep_match_start equ 68h+zlib1222add + dep_lookahead equ 6ch+zlib1222add + + +_TEXT segment + +IFDEF NOUNDERLINE + public longest_match + public match_init +ELSE + public _longest_match + public _match_init +ENDIF + + MAX_MATCH equ 258 + MIN_MATCH equ 3 + MIN_LOOKAHEAD equ (MAX_MATCH+MIN_MATCH+1) + + + +MAX_MATCH equ 258 +MIN_MATCH equ 3 +MIN_LOOKAHEAD equ (MAX_MATCH + MIN_MATCH + 1) +MAX_MATCH_8_ equ ((MAX_MATCH + 7) AND 0FFF0h) + + +;;; stack frame offsets + +chainlenwmask equ esp + 0 ; high word: current chain len + ; low word: s->wmask +window equ esp + 4 ; local copy of s->window +windowbestlen equ esp + 8 ; s->window + bestlen +scanstart equ esp + 16 ; first two bytes of string +scanend equ esp + 12 ; last two bytes of string +scanalign equ esp + 20 ; dword-misalignment of string +nicematch equ esp + 24 ; a good enough match size +bestlen equ esp + 28 ; size of best match so far +scan equ esp + 32 ; ptr to string wanting match + +LocalVarsSize equ 36 +; saved ebx byte esp + 36 +; saved edi byte esp + 40 +; saved esi byte esp + 44 +; saved ebp byte esp + 48 +; return address byte esp + 52 +deflatestate equ esp + 56 ; the function arguments +curmatch equ esp + 60 + +;;; Offsets for fields in the deflate_state structure. These numbers +;;; are calculated from the definition of deflate_state, with the +;;; assumption that the compiler will dword-align the fields. (Thus, +;;; changing the definition of deflate_state could easily cause this +;;; program to crash horribly, without so much as a warning at +;;; compile time. Sigh.) + +dsWSize equ 36+zlib1222add +dsWMask equ 44+zlib1222add +dsWindow equ 48+zlib1222add +dsPrev equ 56+zlib1222add +dsMatchLen equ 88+zlib1222add +dsPrevMatch equ 92+zlib1222add +dsStrStart equ 100+zlib1222add +dsMatchStart equ 104+zlib1222add +dsLookahead equ 108+zlib1222add +dsPrevLen equ 112+zlib1222add +dsMaxChainLen equ 116+zlib1222add +dsGoodMatch equ 132+zlib1222add +dsNiceMatch equ 136+zlib1222add + + +;;; match686.asm -- Pentium-Pro-optimized version of longest_match() +;;; Written for zlib 1.1.2 +;;; Copyright (C) 1998 Brian Raiter +;;; You can look at http://www.muppetlabs.com/~breadbox/software/assembly.html +;;; +;; +;; This software is provided 'as-is', without any express or implied +;; warranty. In no event will the authors be held liable for any damages +;; arising from the use of this software. +;; +;; Permission is granted to anyone to use this software for any purpose, +;; including commercial applications, and to alter it and redistribute it +;; freely, subject to the following restrictions: +;; +;; 1. The origin of this software must not be misrepresented; you must not +;; claim that you wrote the original software. If you use this software +;; in a product, an acknowledgment in the product documentation would be +;; appreciated but is not required. +;; 2. Altered source versions must be plainly marked as such, and must not be +;; misrepresented as being the original software +;; 3. This notice may not be removed or altered from any source distribution. +;; + +;GLOBAL _longest_match, _match_init + + +;SECTION .text + +;;; uInt longest_match(deflate_state *deflatestate, IPos curmatch) + +;_longest_match: + IFDEF NOUNDERLINE + longest_match proc near + ELSE + _longest_match proc near + ENDIF +.FPO (9, 4, 0, 0, 1, 0) + +;;; Save registers that the compiler may be using, and adjust esp to +;;; make room for our stack frame. + + push ebp + push edi + push esi + push ebx + sub esp, LocalVarsSize + +;;; Retrieve the function arguments. ecx will hold cur_match +;;; throughout the entire function. edx will hold the pointer to the +;;; deflate_state structure during the function's setup (before +;;; entering the main loop. + + mov edx, [deflatestate] + mov ecx, [curmatch] + +;;; uInt wmask = s->w_mask; +;;; unsigned chain_length = s->max_chain_length; +;;; if (s->prev_length >= s->good_match) { +;;; chain_length >>= 2; +;;; } + + mov eax, [edx + dsPrevLen] + mov ebx, [edx + dsGoodMatch] + cmp eax, ebx + mov eax, [edx + dsWMask] + mov ebx, [edx + dsMaxChainLen] + jl LastMatchGood + shr ebx, 2 +LastMatchGood: + +;;; chainlen is decremented once beforehand so that the function can +;;; use the sign flag instead of the zero flag for the exit test. +;;; It is then shifted into the high word, to make room for the wmask +;;; value, which it will always accompany. + + dec ebx + shl ebx, 16 + or ebx, eax + mov [chainlenwmask], ebx + +;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + mov eax, [edx + dsNiceMatch] + mov ebx, [edx + dsLookahead] + cmp ebx, eax + jl LookaheadLess + mov ebx, eax +LookaheadLess: mov [nicematch], ebx + +;;; register Bytef *scan = s->window + s->strstart; + + mov esi, [edx + dsWindow] + mov [window], esi + mov ebp, [edx + dsStrStart] + lea edi, [esi + ebp] + mov [scan], edi + +;;; Determine how many bytes the scan ptr is off from being +;;; dword-aligned. + + mov eax, edi + neg eax + and eax, 3 + mov [scanalign], eax + +;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ? +;;; s->strstart - (IPos)MAX_DIST(s) : NIL; + + mov eax, [edx + dsWSize] + sub eax, MIN_LOOKAHEAD + sub ebp, eax + jg LimitPositive + xor ebp, ebp +LimitPositive: + +;;; int best_len = s->prev_length; + + mov eax, [edx + dsPrevLen] + mov [bestlen], eax + +;;; Store the sum of s->window + best_len in esi locally, and in esi. + + add esi, eax + mov [windowbestlen], esi + +;;; register ush scan_start = *(ushf*)scan; +;;; register ush scan_end = *(ushf*)(scan+best_len-1); +;;; Posf *prev = s->prev; + + movzx ebx, word ptr [edi] + mov [scanstart], ebx + movzx ebx, word ptr [edi + eax - 1] + mov [scanend], ebx + mov edi, [edx + dsPrev] + +;;; Jump into the main loop. + + mov edx, [chainlenwmask] + jmp short LoopEntry + +align 4 + +;;; do { +;;; match = s->window + cur_match; +;;; if (*(ushf*)(match+best_len-1) != scan_end || +;;; *(ushf*)match != scan_start) continue; +;;; [...] +;;; } while ((cur_match = prev[cur_match & wmask]) > limit +;;; && --chain_length != 0); +;;; +;;; Here is the inner loop of the function. The function will spend the +;;; majority of its time in this loop, and majority of that time will +;;; be spent in the first ten instructions. +;;; +;;; Within this loop: +;;; ebx = scanend +;;; ecx = curmatch +;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask) +;;; esi = windowbestlen - i.e., (window + bestlen) +;;; edi = prev +;;; ebp = limit + +LookupLoop: + and ecx, edx + movzx ecx, word ptr [edi + ecx*2] + cmp ecx, ebp + jbe LeaveNow + sub edx, 00010000h + js LeaveNow +LoopEntry: movzx eax, word ptr [esi + ecx - 1] + cmp eax, ebx + jnz LookupLoop + mov eax, [window] + movzx eax, word ptr [eax + ecx] + cmp eax, [scanstart] + jnz LookupLoop + +;;; Store the current value of chainlen. + + mov [chainlenwmask], edx + +;;; Point edi to the string under scrutiny, and esi to the string we +;;; are hoping to match it up with. In actuality, esi and edi are +;;; both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and edx is +;;; initialized to -(MAX_MATCH_8 - scanalign). + + mov esi, [window] + mov edi, [scan] + add esi, ecx + mov eax, [scanalign] + mov edx, 0fffffef8h; -(MAX_MATCH_8) + lea edi, [edi + eax + 0108h] ;MAX_MATCH_8] + lea esi, [esi + eax + 0108h] ;MAX_MATCH_8] + +;;; Test the strings for equality, 8 bytes at a time. At the end, +;;; adjust edx so that it is offset to the exact byte that mismatched. +;;; +;;; We already know at this point that the first three bytes of the +;;; strings match each other, and they can be safely passed over before +;;; starting the compare loop. So what this code does is skip over 0-3 +;;; bytes, as much as necessary in order to dword-align the edi +;;; pointer. (esi will still be misaligned three times out of four.) +;;; +;;; It should be confessed that this loop usually does not represent +;;; much of the total running time. Replacing it with a more +;;; straightforward "rep cmpsb" would not drastically degrade +;;; performance. + +LoopCmps: + mov eax, [esi + edx] + xor eax, [edi + edx] + jnz LeaveLoopCmps + mov eax, [esi + edx + 4] + xor eax, [edi + edx + 4] + jnz LeaveLoopCmps4 + add edx, 8 + jnz LoopCmps + jmp short LenMaximum +LeaveLoopCmps4: add edx, 4 +LeaveLoopCmps: test eax, 0000FFFFh + jnz LenLower + add edx, 2 + shr eax, 16 +LenLower: sub al, 1 + adc edx, 0 + +;;; Calculate the length of the match. If it is longer than MAX_MATCH, +;;; then automatically accept it as the best possible match and leave. + + lea eax, [edi + edx] + mov edi, [scan] + sub eax, edi + cmp eax, MAX_MATCH + jge LenMaximum + +;;; If the length of the match is not longer than the best match we +;;; have so far, then forget it and return to the lookup loop. + + mov edx, [deflatestate] + mov ebx, [bestlen] + cmp eax, ebx + jg LongerMatch + mov esi, [windowbestlen] + mov edi, [edx + dsPrev] + mov ebx, [scanend] + mov edx, [chainlenwmask] + jmp LookupLoop + +;;; s->match_start = cur_match; +;;; best_len = len; +;;; if (len >= nice_match) break; +;;; scan_end = *(ushf*)(scan+best_len-1); + +LongerMatch: mov ebx, [nicematch] + mov [bestlen], eax + mov [edx + dsMatchStart], ecx + cmp eax, ebx + jge LeaveNow + mov esi, [window] + add esi, eax + mov [windowbestlen], esi + movzx ebx, word ptr [edi + eax - 1] + mov edi, [edx + dsPrev] + mov [scanend], ebx + mov edx, [chainlenwmask] + jmp LookupLoop + +;;; Accept the current string, with the maximum possible length. + +LenMaximum: mov edx, [deflatestate] + mov dword ptr [bestlen], MAX_MATCH + mov [edx + dsMatchStart], ecx + +;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len; +;;; return s->lookahead; + +LeaveNow: + mov edx, [deflatestate] + mov ebx, [bestlen] + mov eax, [edx + dsLookahead] + cmp ebx, eax + jg LookaheadRet + mov eax, ebx +LookaheadRet: + +;;; Restore the stack and return from whence we came. + + add esp, LocalVarsSize + pop ebx + pop esi + pop edi + pop ebp + + ret +; please don't remove this string ! +; Your can freely use match686 in any free or commercial app if you don't remove the string in the binary! + db 0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998",0dh,0ah + + + IFDEF NOUNDERLINE + longest_match endp + ELSE + _longest_match endp + ENDIF + + IFDEF NOUNDERLINE + match_init proc near + ret + match_init endp + ELSE + _match_init proc near + ret + _match_init endp + ENDIF + + +_TEXT ends +end diff --git a/src/png/zlib/contrib/masmx86/readme.txt b/src/png/zlib/contrib/masmx86/readme.txt new file mode 100644 index 0000000000..3f8888679f --- /dev/null +++ b/src/png/zlib/contrib/masmx86/readme.txt @@ -0,0 +1,27 @@ + +Summary +------- +This directory contains ASM implementations of the functions +longest_match() and inflate_fast(). + + +Use instructions +---------------- +Assemble using MASM, and copy the object files into the zlib source +directory, then run the appropriate makefile, as suggested below. You can +donwload MASM from here: + + http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=7a1c9da0-0510-44a2-b042-7ef370530c64 + +You can also get objects files here: + + http://www.winimage.com/zLibDll/zlib124_masm_obj.zip + +Build instructions +------------------ +* With Microsoft C and MASM: +nmake -f win32/Makefile.msc LOC="-DASMV -DASMINF" OBJA="match686.obj inffas32.obj" + +* With Borland C and TASM: +make -f win32/Makefile.bor LOCAL_ZLIB="-DASMV -DASMINF" OBJA="match686.obj inffas32.obj" OBJPA="+match686c.obj+match686.obj+inffas32.obj" + diff --git a/src/png/zlib/contrib/minizip/Makefile b/src/png/zlib/contrib/minizip/Makefile new file mode 100644 index 0000000000..84eaad20d4 --- /dev/null +++ b/src/png/zlib/contrib/minizip/Makefile @@ -0,0 +1,25 @@ +CC=cc +CFLAGS=-O -I../.. + +UNZ_OBJS = miniunz.o unzip.o ioapi.o ../../libz.a +ZIP_OBJS = minizip.o zip.o ioapi.o ../../libz.a + +.c.o: + $(CC) -c $(CFLAGS) $*.c + +all: miniunz minizip + +miniunz: $(UNZ_OBJS) + $(CC) $(CFLAGS) -o $@ $(UNZ_OBJS) + +minizip: $(ZIP_OBJS) + $(CC) $(CFLAGS) -o $@ $(ZIP_OBJS) + +test: miniunz minizip + ./minizip test readme.txt + ./miniunz -l test.zip + mv readme.txt readme.old + ./miniunz test.zip + +clean: + /bin/rm -f *.o *~ minizip miniunz diff --git a/src/png/zlib/contrib/minizip/Makefile.am b/src/png/zlib/contrib/minizip/Makefile.am new file mode 100644 index 0000000000..d343011ebc --- /dev/null +++ b/src/png/zlib/contrib/minizip/Makefile.am @@ -0,0 +1,45 @@ +lib_LTLIBRARIES = libminizip.la + +if COND_DEMOS +bin_PROGRAMS = miniunzip minizip +endif + +zlib_top_srcdir = $(top_srcdir)/../.. +zlib_top_builddir = $(top_builddir)/../.. + +AM_CPPFLAGS = -I$(zlib_top_srcdir) +AM_LDFLAGS = -L$(zlib_top_builddir) + +if WIN32 +iowin32_src = iowin32.c +iowin32_h = iowin32.h +endif + +libminizip_la_SOURCES = \ + ioapi.c \ + mztools.c \ + unzip.c \ + zip.c \ + ${iowin32_src} + +libminizip_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:0:0 -lz + +minizip_includedir = $(includedir)/minizip +minizip_include_HEADERS = \ + crypt.h \ + ioapi.h \ + mztools.h \ + unzip.h \ + zip.h \ + ${iowin32_h} + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = minizip.pc + +EXTRA_PROGRAMS = miniunzip minizip + +miniunzip_SOURCES = miniunz.c +miniunzip_LDADD = libminizip.la + +minizip_SOURCES = minizip.c +minizip_LDADD = libminizip.la -lz diff --git a/src/png/zlib/contrib/minizip/MiniZip64_Changes.txt b/src/png/zlib/contrib/minizip/MiniZip64_Changes.txt new file mode 100644 index 0000000000..13a1bd91a9 --- /dev/null +++ b/src/png/zlib/contrib/minizip/MiniZip64_Changes.txt @@ -0,0 +1,6 @@ + +MiniZip 1.1 was derrived from MiniZip at version 1.01f + +Change in 1.0 (Okt 2009) + - **TODO - Add history** + diff --git a/src/png/zlib/contrib/minizip/MiniZip64_info.txt b/src/png/zlib/contrib/minizip/MiniZip64_info.txt new file mode 100644 index 0000000000..57d7152420 --- /dev/null +++ b/src/png/zlib/contrib/minizip/MiniZip64_info.txt @@ -0,0 +1,74 @@ +MiniZip - Copyright (c) 1998-2010 - by Gilles Vollant - version 1.1 64 bits from Mathias Svensson + +Introduction +--------------------- +MiniZip 1.1 is built from MiniZip 1.0 by Gilles Vollant ( http://www.winimage.com/zLibDll/minizip.html ) + +When adding ZIP64 support into minizip it would result into risk of breaking compatibility with minizip 1.0. +All possible work was done for compatibility. + + +Background +--------------------- +When adding ZIP64 support Mathias Svensson found that Even Rouault have added ZIP64 +support for unzip.c into minizip for a open source project called gdal ( http://www.gdal.org/ ) + +That was used as a starting point. And after that ZIP64 support was added to zip.c +some refactoring and code cleanup was also done. + + +Changed from MiniZip 1.0 to MiniZip 1.1 +--------------------------------------- +* Added ZIP64 support for unzip ( by Even Rouault ) +* Added ZIP64 support for zip ( by Mathias Svensson ) +* Reverted some changed that Even Rouault did. +* Bunch of patches received from Gulles Vollant that he received for MiniZip from various users. +* Added unzip patch for BZIP Compression method (patch create by Daniel Borca) +* Added BZIP Compress method for zip +* Did some refactoring and code cleanup + + +Credits + + Gilles Vollant - Original MiniZip author + Even Rouault - ZIP64 unzip Support + Daniel Borca - BZip Compression method support in unzip + Mathias Svensson - ZIP64 zip support + Mathias Svensson - BZip Compression method support in zip + + Resources + + ZipLayout http://result42.com/projects/ZipFileLayout + Command line tool for Windows that shows the layout and information of the headers in a zip archive. + Used when debugging and validating the creation of zip files using MiniZip64 + + + ZIP App Note http://www.pkware.com/documents/casestudies/APPNOTE.TXT + Zip File specification + + +Notes. + * To be able to use BZip compression method in zip64.c or unzip64.c the BZIP2 lib is needed and HAVE_BZIP2 need to be defined. + +License +---------------------------------------------------------- + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + +---------------------------------------------------------- + diff --git a/src/png/zlib/contrib/minizip/configure.ac b/src/png/zlib/contrib/minizip/configure.ac new file mode 100644 index 0000000000..5b11970977 --- /dev/null +++ b/src/png/zlib/contrib/minizip/configure.ac @@ -0,0 +1,32 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_INIT([minizip], [1.2.11], [bugzilla.redhat.com]) +AC_CONFIG_SRCDIR([minizip.c]) +AM_INIT_AUTOMAKE([foreign]) +LT_INIT + +AC_MSG_CHECKING([whether to build example programs]) +AC_ARG_ENABLE([demos], AC_HELP_STRING([--enable-demos], [build example programs])) +AM_CONDITIONAL([COND_DEMOS], [test "$enable_demos" = yes]) +if test "$enable_demos" = yes +then + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + +case "${host}" in + *-mingw* | mingw*) + WIN32="yes" + ;; + *) + ;; +esac +AM_CONDITIONAL([WIN32], [test "${WIN32}" = "yes"]) + + +AC_SUBST([HAVE_UNISTD_H], [0]) +AC_CHECK_HEADER([unistd.h], [HAVE_UNISTD_H=1], []) +AC_CONFIG_FILES([Makefile minizip.pc]) +AC_OUTPUT diff --git a/src/png/zlib/contrib/minizip/crypt.h b/src/png/zlib/contrib/minizip/crypt.h new file mode 100644 index 0000000000..1e9e8200b2 --- /dev/null +++ b/src/png/zlib/contrib/minizip/crypt.h @@ -0,0 +1,131 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const z_crc_t* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const z_crc_t* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(const char* passwd, /* password string */ + unsigned char* buf, /* where to write header */ + int bufSize, + unsigned long* pkeys, + const z_crc_t* pcrc_32_tab, + unsigned long crcForCrypting) +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/src/png/zlib/contrib/minizip/ioapi.c b/src/png/zlib/contrib/minizip/ioapi.c new file mode 100644 index 0000000000..7f5c191b2a --- /dev/null +++ b/src/png/zlib/contrib/minizip/ioapi.c @@ -0,0 +1,247 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS))) + #define _CRT_SECURE_NO_WARNINGS +#endif + +#if defined(__APPLE__) || defined(IOAPI_NO_64) +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + +#include "ioapi.h" + +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); + else + { + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); + } +} + +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + else + { + uLong offsetTruncated = (uLong)offset; + if (offsetTruncated != offset) + return -1; + else + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); + } +} + +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); + else + { + uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + if ((tell_uLong) == MAXU32) + return (ZPOS64_T)-1; + else + return tell_uLong; + } +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + + + +static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); +static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); +static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); +static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); + +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = FOPEN_FUNC((const char*)filename, mode_fopen); + return file; +} + + +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + + +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret; + ret = FTELLO_FUNC((FILE *)stream); + return ret; +} + +static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + if (fseek((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + + if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + + +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/png/zlib/contrib/minizip/ioapi.h b/src/png/zlib/contrib/minizip/ioapi.h new file mode 100644 index 0000000000..8dcbdb06e3 --- /dev/null +++ b/src/png/zlib/contrib/minizip/ioapi.h @@ -0,0 +1,208 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + + Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) + Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. + More if/def section may be needed to support other platforms + Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. + (but you should use iowin32.c for windows instead) + +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + + // Linux needs this to support file operation on files larger then 4+GB + // But might need better if/def to select just the platforms that needs them. + + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif + +#endif + +#include +#include +#include "zlib.h" + +#if defined(USE_FILE32API) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#ifdef __FreeBSD__ +#define fopen64 fopen +#define ftello64 ftello +#define fseeko64 fseeko +#endif +#ifdef _MSC_VER + #define fopen64 fopen + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else // old MSC + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif +#endif + +/* +#ifndef ZPOS64_T + #ifdef _WIN32 + #define ZPOS64_T fpos_t + #else + #include + #define ZPOS64_T uint64_t + #endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + +/* Maximum unsigned 32-bit value used as placeholder for zip64 */ +#define MAXU32 0xffffffff + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) + #define ZCALLBACK CALLBACK + #else + #define ZCALLBACK + #endif +#endif + + + + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) +//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); +long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/png/zlib/contrib/minizip/iowin32.c b/src/png/zlib/contrib/minizip/iowin32.c new file mode 100644 index 0000000000..274f39eb1d --- /dev/null +++ b/src/png/zlib/contrib/minizip/iowin32.c @@ -0,0 +1,462 @@ +/* iowin32.c -- IO base function header for compress/uncompress .zip + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#include + +#include "zlib.h" +#include "ioapi.h" +#include "iowin32.h" + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE (0xFFFFFFFF) +#endif + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + + +// see Include/shared/winapifamily.h in the Windows Kit +#if defined(WINAPI_FAMILY_PARTITION) && (!(defined(IOWIN32_USING_WINRT_API))) +#if WINAPI_FAMILY_ONE_PARTITION(WINAPI_FAMILY, WINAPI_PARTITION_APP) +#define IOWIN32_USING_WINRT_API 1 +#endif +#endif + +voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode)); +uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +uLong ZCALLBACK win32_write_file_func OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +ZPOS64_T ZCALLBACK win32_tell64_file_func OF((voidpf opaque, voidpf stream)); +long ZCALLBACK win32_seek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +int ZCALLBACK win32_close_file_func OF((voidpf opaque, voidpf stream)); +int ZCALLBACK win32_error_file_func OF((voidpf opaque, voidpf stream)); + +typedef struct +{ + HANDLE hf; + int error; +} WIN32FILE_IOWIN; + + +static void win32_translate_open_mode(int mode, + DWORD* lpdwDesiredAccess, + DWORD* lpdwCreationDisposition, + DWORD* lpdwShareMode, + DWORD* lpdwFlagsAndAttributes) +{ + *lpdwDesiredAccess = *lpdwShareMode = *lpdwFlagsAndAttributes = *lpdwCreationDisposition = 0; + + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + { + *lpdwDesiredAccess = GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + *lpdwShareMode = FILE_SHARE_READ; + } + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + } + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = CREATE_ALWAYS; + } +} + +static voidpf win32_build_iowin(HANDLE hFile) +{ + voidpf ret=NULL; + + if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + WIN32FILE_IOWIN w32fiow; + w32fiow.hf = hFile; + w32fiow.error = 0; + ret = malloc(sizeof(WIN32FILE_IOWIN)); + + if (ret==NULL) + CloseHandle(hFile); + else + *((WIN32FILE_IOWIN*)ret) = w32fiow; + } + return ret; +} + +voidpf ZCALLBACK win32_open64_file_func (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API +#ifdef UNICODE + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile2((LPCTSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + { + WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; + MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); + hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); + } +#endif +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open64_file_funcA (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API + if ((filename!=NULL) && (dwDesiredAccess != 0)) + { + WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; + MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); + hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); + } +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileA((LPCSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open64_file_funcW (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile2((LPCWSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition,NULL); +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileW((LPCWSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open_file_func (voidpf opaque,const char* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API +#ifdef UNICODE + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile2((LPCTSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + { + WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; + MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); + hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); + } +#endif +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +uLong ZCALLBACK win32_read_file_func (voidpf opaque, voidpf stream, void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!ReadFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + + +uLong ZCALLBACK win32_write_file_func (voidpf opaque,voidpf stream,const void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!WriteFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + +static BOOL MySetFilePointerEx(HANDLE hFile, LARGE_INTEGER pos, LARGE_INTEGER *newPos, DWORD dwMoveMethod) +{ +#ifdef IOWIN32_USING_WINRT_API + return SetFilePointerEx(hFile, pos, newPos, dwMoveMethod); +#else + LONG lHigh = pos.HighPart; + DWORD dwNewPos = SetFilePointer(hFile, pos.LowPart, &lHigh, dwMoveMethod); + BOOL fOk = TRUE; + if (dwNewPos == 0xFFFFFFFF) + if (GetLastError() != NO_ERROR) + fOk = FALSE; + if ((newPos != NULL) && (fOk)) + { + newPos->LowPart = dwNewPos; + newPos->HighPart = lHigh; + } + return fOk; +#endif +} + +long ZCALLBACK win32_tell_file_func (voidpf opaque,voidpf stream) +{ + long ret=-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + LARGE_INTEGER pos; + pos.QuadPart = 0; + + if (!MySetFilePointerEx(hFile, pos, &pos, FILE_CURRENT)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=(long)pos.LowPart; + } + return ret; +} + +ZPOS64_T ZCALLBACK win32_tell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret= (ZPOS64_T)-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + if (hFile) + { + LARGE_INTEGER pos; + pos.QuadPart = 0; + + if (!MySetFilePointerEx(hFile, pos, &pos, FILE_CURRENT)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = (ZPOS64_T)-1; + } + else + ret=pos.QuadPart; + } + return ret; +} + + +long ZCALLBACK win32_seek_file_func (voidpf opaque,voidpf stream,uLong offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + + long ret=-1; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile != NULL) + { + LARGE_INTEGER pos; + pos.QuadPart = offset; + if (!MySetFilePointerEx(hFile, pos, NULL, dwMoveMethod)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +long ZCALLBACK win32_seek64_file_func (voidpf opaque, voidpf stream,ZPOS64_T offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + long ret=-1; + + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile) + { + LARGE_INTEGER pos; + pos.QuadPart = offset; + if (!MySetFilePointerEx(hFile, pos, NULL, dwMoveMethod)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +int ZCALLBACK win32_close_file_func (voidpf opaque, voidpf stream) +{ + int ret=-1; + + if (stream!=NULL) + { + HANDLE hFile; + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + CloseHandle(hFile); + ret=0; + } + free(stream); + } + return ret; +} + +int ZCALLBACK win32_error_file_func (voidpf opaque,voidpf stream) +{ + int ret=-1; + if (stream!=NULL) + { + ret = ((WIN32FILE_IOWIN*)stream) -> error; + } + return ret; +} + +void fill_win32_filefunc (zlib_filefunc_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen_file = win32_open_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell_file = win32_tell_file_func; + pzlib_filefunc_def->zseek_file = win32_seek_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + + +void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcA; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + + +void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcW; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/png/zlib/contrib/minizip/iowin32.h b/src/png/zlib/contrib/minizip/iowin32.h new file mode 100644 index 0000000000..0ca0969a7d --- /dev/null +++ b/src/png/zlib/contrib/minizip/iowin32.h @@ -0,0 +1,28 @@ +/* iowin32.h -- IO base function header for compress/uncompress .zip + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); +void fill_win32_filefunc64 OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_win32_filefunc64A OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_win32_filefunc64W OF((zlib_filefunc64_def* pzlib_filefunc_def)); + +#ifdef __cplusplus +} +#endif diff --git a/src/png/zlib/contrib/minizip/make_vms.com b/src/png/zlib/contrib/minizip/make_vms.com new file mode 100644 index 0000000000..9ac13a98fa --- /dev/null +++ b/src/png/zlib/contrib/minizip/make_vms.com @@ -0,0 +1,25 @@ +$ if f$search("ioapi.h_orig") .eqs. "" then copy ioapi.h ioapi.h_orig +$ open/write zdef vmsdefs.h +$ copy sys$input: zdef +$ deck +#define unix +#define fill_zlib_filefunc64_32_def_from_filefunc32 fillzffunc64from +#define Write_Zip64EndOfCentralDirectoryLocator Write_Zip64EoDLocator +#define Write_Zip64EndOfCentralDirectoryRecord Write_Zip64EoDRecord +#define Write_EndOfCentralDirectoryRecord Write_EoDRecord +$ eod +$ close zdef +$ copy vmsdefs.h,ioapi.h_orig ioapi.h +$ cc/include=[--]/prefix=all ioapi.c +$ cc/include=[--]/prefix=all miniunz.c +$ cc/include=[--]/prefix=all unzip.c +$ cc/include=[--]/prefix=all minizip.c +$ cc/include=[--]/prefix=all zip.c +$ link miniunz,unzip,ioapi,[--]libz.olb/lib +$ link minizip,zip,ioapi,[--]libz.olb/lib +$ mcr []minizip test minizip_info.txt +$ mcr []miniunz -l test.zip +$ rename minizip_info.txt; minizip_info.txt_old +$ mcr []miniunz test.zip +$ delete test.zip;* +$exit diff --git a/src/png/zlib/contrib/minizip/miniunz.c b/src/png/zlib/contrib/minizip/miniunz.c new file mode 100644 index 0000000000..3d65401be5 --- /dev/null +++ b/src/png/zlib/contrib/minizip/miniunz.c @@ -0,0 +1,660 @@ +/* + miniunz.c + Version 1.1, February 14h, 2010 + sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) +*/ + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#ifdef __APPLE__ +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# include +# include +#endif + + +#include "unzip.h" + +#define CASESENSITIVITY (0) +#define WRITEBUFFERSIZE (8192) +#define MAXFILENAME (256) + +#ifdef _WIN32 +#define USEWIN32IOAPI +#include "iowin32.h" +#endif +/* + mini unzip, demo of unzip package + + usage : + Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir] + + list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT + if it exists +*/ + + +/* change_file_date : change the date/time of a file + filename : the filename of the file where date/time must be modified + dosdate : the new date at the MSDos format (4 bytes) + tmu_date : the SAME new date at the tm_unz format */ +void change_file_date(filename,dosdate,tmu_date) + const char *filename; + uLong dosdate; + tm_unz tmu_date; +{ +#ifdef _WIN32 + HANDLE hFile; + FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite; + + hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE, + 0,NULL,OPEN_EXISTING,0,NULL); + GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite); + DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal); + LocalFileTimeToFileTime(&ftLocal,&ftm); + SetFileTime(hFile,&ftm,&ftLastAcc,&ftm); + CloseHandle(hFile); +#else +#ifdef unix || __APPLE__ + struct utimbuf ut; + struct tm newdate; + newdate.tm_sec = tmu_date.tm_sec; + newdate.tm_min=tmu_date.tm_min; + newdate.tm_hour=tmu_date.tm_hour; + newdate.tm_mday=tmu_date.tm_mday; + newdate.tm_mon=tmu_date.tm_mon; + if (tmu_date.tm_year > 1900) + newdate.tm_year=tmu_date.tm_year - 1900; + else + newdate.tm_year=tmu_date.tm_year ; + newdate.tm_isdst=-1; + + ut.actime=ut.modtime=mktime(&newdate); + utime(filename,&ut); +#endif +#endif +} + + +/* mymkdir and change_file_date are not 100 % portable + As I don't know well Unix, I wait feedback for the unix portion */ + +int mymkdir(dirname) + const char* dirname; +{ + int ret=0; +#ifdef _WIN32 + ret = _mkdir(dirname); +#elif unix + ret = mkdir (dirname,0775); +#elif __APPLE__ + ret = mkdir (dirname,0775); +#endif + return ret; +} + +int makedir (newdir) + char *newdir; +{ + char *buffer ; + char *p; + int len = (int)strlen(newdir); + + if (len <= 0) + return 0; + + buffer = (char*)malloc(len+1); + if (buffer==NULL) + { + printf("Error allocating memory\n"); + return UNZ_INTERNALERROR; + } + strcpy(buffer,newdir); + + if (buffer[len-1] == '/') { + buffer[len-1] = '\0'; + } + if (mymkdir(buffer) == 0) + { + free(buffer); + return 1; + } + + p = buffer+1; + while (1) + { + char hold; + + while(*p && *p != '\\' && *p != '/') + p++; + hold = *p; + *p = 0; + if ((mymkdir(buffer) == -1) && (errno == ENOENT)) + { + printf("couldn't create directory %s\n",buffer); + free(buffer); + return 0; + } + if (hold == 0) + break; + *p++ = hold; + } + free(buffer); + return 1; +} + +void do_banner() +{ + printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n"); + printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n"); +} + +void do_help() +{ + printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \ + " -e Extract without pathname (junk paths)\n" \ + " -x Extract with pathname\n" \ + " -v list files\n" \ + " -l list files\n" \ + " -d directory to extract into\n" \ + " -o overwrite files without prompting\n" \ + " -p extract crypted file using password\n\n"); +} + +void Display64BitsSize(ZPOS64_T n, int size_char) +{ + /* to avoid compatibility problem , we do here the conversion */ + char number[21]; + int offset=19; + int pos_string = 19; + number[20]=0; + for (;;) { + number[offset]=(char)((n%10)+'0'); + if (number[offset] != '0') + pos_string=offset; + n/=10; + if (offset==0) + break; + offset--; + } + { + int size_display_string = 19-pos_string; + while (size_char > size_display_string) + { + size_char--; + printf(" "); + } + } + + printf("%s",&number[pos_string]); +} + +int do_list(uf) + unzFile uf; +{ + uLong i; + unz_global_info64 gi; + int err; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + printf(" Length Method Size Ratio Date Time CRC-32 Name\n"); + printf(" ------ ------ ---- ----- ---- ---- ------ ----\n"); + for (i=0;i0) + ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size); + + /* display a '*' if the file is crypted */ + if ((file_info.flag & 1) != 0) + charCrypt='*'; + + if (file_info.compression_method==0) + string_method="Stored"; + else + if (file_info.compression_method==Z_DEFLATED) + { + uInt iLevel=(uInt)((file_info.flag & 0x6)/2); + if (iLevel==0) + string_method="Defl:N"; + else if (iLevel==1) + string_method="Defl:X"; + else if ((iLevel==2) || (iLevel==3)) + string_method="Defl:F"; /* 2:fast , 3 : extra fast*/ + } + else + if (file_info.compression_method==Z_BZIP2ED) + { + string_method="BZip2 "; + } + else + string_method="Unkn. "; + + Display64BitsSize(file_info.uncompressed_size,7); + printf(" %6s%c",string_method,charCrypt); + Display64BitsSize(file_info.compressed_size,7); + printf(" %3lu%% %2.2lu-%2.2lu-%2.2lu %2.2lu:%2.2lu %8.8lx %s\n", + ratio, + (uLong)file_info.tmu_date.tm_mon + 1, + (uLong)file_info.tmu_date.tm_mday, + (uLong)file_info.tmu_date.tm_year % 100, + (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min, + (uLong)file_info.crc,filename_inzip); + if ((i+1)='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + } + + if (rep == 'N') + skip = 1; + + if (rep == 'A') + *popt_overwrite=1; + } + + if ((skip==0) && (err==UNZ_OK)) + { + fout=FOPEN_FUNC(write_filename,"wb"); + /* some zipfile don't contain directory alone before file */ + if ((fout==NULL) && ((*popt_extract_without_path)==0) && + (filename_withoutpath!=(char*)filename_inzip)) + { + char c=*(filename_withoutpath-1); + *(filename_withoutpath-1)='\0'; + makedir(write_filename); + *(filename_withoutpath-1)=c; + fout=FOPEN_FUNC(write_filename,"wb"); + } + + if (fout==NULL) + { + printf("error opening %s\n",write_filename); + } + } + + if (fout!=NULL) + { + printf(" extracting: %s\n",write_filename); + + do + { + err = unzReadCurrentFile(uf,buf,size_buf); + if (err<0) + { + printf("error %d with zipfile in unzReadCurrentFile\n",err); + break; + } + if (err>0) + if (fwrite(buf,err,1,fout)!=1) + { + printf("error in writing extracted file\n"); + err=UNZ_ERRNO; + break; + } + } + while (err>0); + if (fout) + fclose(fout); + + if (err==0) + change_file_date(write_filename,file_info.dosDate, + file_info.tmu_date); + } + + if (err==UNZ_OK) + { + err = unzCloseCurrentFile (uf); + if (err!=UNZ_OK) + { + printf("error %d with zipfile in unzCloseCurrentFile\n",err); + } + } + else + unzCloseCurrentFile(uf); /* don't lose the error */ + } + + free(buf); + return err; +} + + +int do_extract(uf,opt_extract_without_path,opt_overwrite,password) + unzFile uf; + int opt_extract_without_path; + int opt_overwrite; + const char* password; +{ + uLong i; + unz_global_info64 gi; + int err; + FILE* fout=NULL; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + + for (i=0;i insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +miniunzip - uncompress and examine ZIP archives +.SH SYNOPSIS +.B miniunzip +.RI [ -exvlo ] +zipfile [ files_to_extract ] [-d tempdir] +.SH DESCRIPTION +.B minizip +is a simple tool which allows the extraction of compressed file +archives in the ZIP format used by the MS-DOS utility PKZIP. It was +written as a demonstration of the +.IR zlib (3) +library and therefore lack many of the features of the +.IR unzip (1) +program. +.SH OPTIONS +A number of options are supported. With the exception of +.BI \-d\ tempdir +these must be supplied before any +other arguments and are: +.TP +.BI \-l\ ,\ \-\-v +List the files in the archive without extracting them. +.TP +.B \-o +Overwrite files without prompting for confirmation. +.TP +.B \-x +Extract files (default). +.PP +The +.I zipfile +argument is the name of the archive to process. The next argument can be used +to specify a single file to extract from the archive. + +Lastly, the following option can be specified at the end of the command-line: +.TP +.BI \-d\ tempdir +Extract the archive in the directory +.I tempdir +rather than the current directory. +.SH SEE ALSO +.BR minizip (1), +.BR zlib (3), +.BR unzip (1). +.SH AUTHOR +This program was written by Gilles Vollant. This manual page was +written by Mark Brown . The -d tempdir option +was added by Dirk Eddelbuettel . diff --git a/src/png/zlib/contrib/minizip/minizip.1 b/src/png/zlib/contrib/minizip/minizip.1 new file mode 100644 index 0000000000..1154484c1c --- /dev/null +++ b/src/png/zlib/contrib/minizip/minizip.1 @@ -0,0 +1,46 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH minizip 1 "May 2, 2001" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +minizip - create ZIP archives +.SH SYNOPSIS +.B minizip +.RI [ -o ] +zipfile [ " files" ... ] +.SH DESCRIPTION +.B minizip +is a simple tool which allows the creation of compressed file archives +in the ZIP format used by the MS-DOS utility PKZIP. It was written as +a demonstration of the +.IR zlib (3) +library and therefore lack many of the features of the +.IR zip (1) +program. +.SH OPTIONS +The first argument supplied is the name of the ZIP archive to create or +.RI -o +in which case it is ignored and the second argument treated as the +name of the ZIP file. If the ZIP file already exists it will be +overwritten. +.PP +Subsequent arguments specify a list of files to place in the ZIP +archive. If none are specified then an empty archive will be created. +.SH SEE ALSO +.BR miniunzip (1), +.BR zlib (3), +.BR zip (1). +.SH AUTHOR +This program was written by Gilles Vollant. This manual page was +written by Mark Brown . + diff --git a/src/png/zlib/contrib/minizip/minizip.c b/src/png/zlib/contrib/minizip/minizip.c new file mode 100644 index 0000000000..4288962ece --- /dev/null +++ b/src/png/zlib/contrib/minizip/minizip.c @@ -0,0 +1,520 @@ +/* + minizip.c + Version 1.1, February 14h, 2010 + sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) +*/ + + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#ifdef __APPLE__ +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include +# include +#endif + +#include "zip.h" + +#ifdef _WIN32 + #define USEWIN32IOAPI + #include "iowin32.h" +#endif + + + +#define WRITEBUFFERSIZE (16384) +#define MAXFILENAME (256) + +#ifdef _WIN32 +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + int ret = 0; + { + FILETIME ftLocal; + HANDLE hFind; + WIN32_FIND_DATAA ff32; + + hFind = FindFirstFileA(f,&ff32); + if (hFind != INVALID_HANDLE_VALUE) + { + FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal); + FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0); + FindClose(hFind); + ret = 1; + } + } + return ret; +} +#else +#ifdef unix || __APPLE__ +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + int ret=0; + struct stat s; /* results of stat() */ + struct tm* filedate; + time_t tm_t=0; + + if (strcmp(f,"-")!=0) + { + char name[MAXFILENAME+1]; + int len = strlen(f); + if (len > MAXFILENAME) + len = MAXFILENAME; + + strncpy(name, f,MAXFILENAME-1); + /* strncpy doesnt append the trailing NULL, of the string is too long. */ + name[ MAXFILENAME ] = '\0'; + + if (name[len - 1] == '/') + name[len - 1] = '\0'; + /* not all systems allow stat'ing a file with / appended */ + if (stat(name,&s)==0) + { + tm_t = s.st_mtime; + ret = 1; + } + } + filedate = localtime(&tm_t); + + tmzip->tm_sec = filedate->tm_sec; + tmzip->tm_min = filedate->tm_min; + tmzip->tm_hour = filedate->tm_hour; + tmzip->tm_mday = filedate->tm_mday; + tmzip->tm_mon = filedate->tm_mon ; + tmzip->tm_year = filedate->tm_year; + + return ret; +} +#else +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + return 0; +} +#endif +#endif + + + + +int check_exist_file(filename) + const char* filename; +{ + FILE* ftestexist; + int ret = 1; + ftestexist = FOPEN_FUNC(filename,"rb"); + if (ftestexist==NULL) + ret = 0; + else + fclose(ftestexist); + return ret; +} + +void do_banner() +{ + printf("MiniZip 1.1, demo of zLib + MiniZip64 package, written by Gilles Vollant\n"); + printf("more info on MiniZip at http://www.winimage.com/zLibDll/minizip.html\n\n"); +} + +void do_help() +{ + printf("Usage : minizip [-o] [-a] [-0 to -9] [-p password] [-j] file.zip [files_to_add]\n\n" \ + " -o Overwrite existing file.zip\n" \ + " -a Append to existing file.zip\n" \ + " -0 Store only\n" \ + " -1 Compress faster\n" \ + " -9 Compress better\n\n" \ + " -j exclude path. store only the file name.\n\n"); +} + +/* calculate the CRC32 of a file, + because to encrypt a file, we need known the CRC32 of the file before */ +int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc) +{ + unsigned long calculate_crc=0; + int err=ZIP_OK; + FILE * fin = FOPEN_FUNC(filenameinzip,"rb"); + + unsigned long size_read = 0; + unsigned long total_read = 0; + if (fin==NULL) + { + err = ZIP_ERRNO; + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + calculate_crc = crc32(calculate_crc,buf,size_read); + total_read += size_read; + + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + *result_crc=calculate_crc; + printf("file %s crc %lx\n", filenameinzip, calculate_crc); + return err; +} + +int isLargeFile(const char* filename) +{ + int largeFile = 0; + ZPOS64_T pos = 0; + FILE* pFile = FOPEN_FUNC(filename, "rb"); + + if(pFile != NULL) + { + int n = FSEEKO_FUNC(pFile, 0, SEEK_END); + pos = FTELLO_FUNC(pFile); + + printf("File : %s is %lld bytes\n", filename, pos); + + if(pos >= 0xffffffff) + largeFile = 1; + + fclose(pFile); + } + + return largeFile; +} + +int main(argc,argv) + int argc; + char *argv[]; +{ + int i; + int opt_overwrite=0; + int opt_compress_level=Z_DEFAULT_COMPRESSION; + int opt_exclude_path=0; + int zipfilenamearg = 0; + char filename_try[MAXFILENAME+16]; + int zipok; + int err=0; + int size_buf=0; + void* buf=NULL; + const char* password=NULL; + + + do_banner(); + if (argc==1) + { + do_help(); + return 0; + } + else + { + for (i=1;i='0') && (c<='9')) + opt_compress_level = c-'0'; + if ((c=='j') || (c=='J')) + opt_exclude_path = 1; + + if (((c=='p') || (c=='P')) && (i+1='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + if (rep=='N') + zipok = 0; + if (rep=='A') + opt_overwrite = 2; + } + } + + if (zipok==1) + { + zipFile zf; + int errclose; +# ifdef USEWIN32IOAPI + zlib_filefunc64_def ffunc; + fill_win32_filefunc64A(&ffunc); + zf = zipOpen2_64(filename_try,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc); +# else + zf = zipOpen64(filename_try,(opt_overwrite==2) ? 2 : 0); +# endif + + if (zf == NULL) + { + printf("error opening %s\n",filename_try); + err= ZIP_ERRNO; + } + else + printf("creating %s\n",filename_try); + + for (i=zipfilenamearg+1;(i='0') || (argv[i][1]<='9'))) && + (strlen(argv[i]) == 2))) + { + FILE * fin; + int size_read; + const char* filenameinzip = argv[i]; + const char *savefilenameinzip; + zip_fileinfo zi; + unsigned long crcFile=0; + int zip64 = 0; + + zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = + zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; + zi.dosDate = 0; + zi.internal_fa = 0; + zi.external_fa = 0; + filetime(filenameinzip,&zi.tmz_date,&zi.dosDate); + +/* + err = zipOpenNewFileInZip(zf,filenameinzip,&zi, + NULL,0,NULL,0,NULL / * comment * /, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level); +*/ + if ((password != NULL) && (err==ZIP_OK)) + err = getFileCrc(filenameinzip,buf,size_buf,&crcFile); + + zip64 = isLargeFile(filenameinzip); + + /* The path name saved, should not include a leading slash. */ + /*if it did, windows/xp and dynazip couldn't read the zip file. */ + savefilenameinzip = filenameinzip; + while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' ) + { + savefilenameinzip++; + } + + /*should the zip file contain any path at all?*/ + if( opt_exclude_path ) + { + const char *tmpptr; + const char *lastslash = 0; + for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++) + { + if( *tmpptr == '\\' || *tmpptr == '/') + { + lastslash = tmpptr; + } + } + if( lastslash != NULL ) + { + savefilenameinzip = lastslash+1; // base filename follows last slash. + } + } + + /**/ + err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi, + NULL,0,NULL,0,NULL /* comment*/, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level,0, + /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */ + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + password,crcFile, zip64); + + if (err != ZIP_OK) + printf("error in opening %s in zipfile\n",filenameinzip); + else + { + fin = FOPEN_FUNC(filenameinzip,"rb"); + if (fin==NULL) + { + err=ZIP_ERRNO; + printf("error in opening %s for reading\n",filenameinzip); + } + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + { + err = zipWriteInFileInZip (zf,buf,size_read); + if (err<0) + { + printf("error in writing %s in the zipfile\n", + filenameinzip); + } + + } + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + if (err<0) + err=ZIP_ERRNO; + else + { + err = zipCloseFileInZip(zf); + if (err!=ZIP_OK) + printf("error in closing %s in the zipfile\n", + filenameinzip); + } + } + } + errclose = zipClose(zf,NULL); + if (errclose != ZIP_OK) + printf("error in closing %s\n",filename_try); + } + else + { + do_help(); + } + + free(buf); + return 0; +} diff --git a/src/png/zlib/contrib/minizip/minizip.pc.in b/src/png/zlib/contrib/minizip/minizip.pc.in new file mode 100644 index 0000000000..69b5b7fdcb --- /dev/null +++ b/src/png/zlib/contrib/minizip/minizip.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/minizip + +Name: minizip +Description: Minizip zip file manipulation library +Requires: +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lminizip +Libs.private: -lz +Cflags: -I${includedir} diff --git a/src/png/zlib/contrib/minizip/mztools.c b/src/png/zlib/contrib/minizip/mztools.c new file mode 100644 index 0000000000..96891c2e0b --- /dev/null +++ b/src/png/zlib/contrib/minizip/mztools.c @@ -0,0 +1,291 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +/* Code */ +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#define READ_8(adr) ((unsigned char)*(adr)) +#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) ) +#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) ) + +#define WRITE_8(buff, n) do { \ + *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \ +} while(0) +#define WRITE_16(buff, n) do { \ + WRITE_8((unsigned char*)(buff), n); \ + WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \ +} while(0) +#define WRITE_32(buff, n) do { \ + WRITE_16((unsigned char*)(buff), (n) & 0xffff); \ + WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \ +} while(0) + +extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered) +const char* file; +const char* fileOut; +const char* fileOutTmp; +uLong* nRecovered; +uLong* bytesRecovered; +{ + int err = Z_OK; + FILE* fpZip = fopen(file, "rb"); + FILE* fpOut = fopen(fileOut, "wb"); + FILE* fpOutCD = fopen(fileOutTmp, "wb"); + if (fpZip != NULL && fpOut != NULL) { + int entries = 0; + uLong totalBytes = 0; + char header[30]; + char filename[1024]; + char extra[1024]; + int offset = 0; + int offsetCD = 0; + while ( fread(header, 1, 30, fpZip) == 30 ) { + int currentOffset = offset; + + /* File entry */ + if (READ_32(header) == 0x04034b50) { + unsigned int version = READ_16(header + 4); + unsigned int gpflag = READ_16(header + 6); + unsigned int method = READ_16(header + 8); + unsigned int filetime = READ_16(header + 10); + unsigned int filedate = READ_16(header + 12); + unsigned int crc = READ_32(header + 14); /* crc */ + unsigned int cpsize = READ_32(header + 18); /* compressed size */ + unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */ + unsigned int fnsize = READ_16(header + 26); /* file name length */ + unsigned int extsize = READ_16(header + 28); /* extra field length */ + filename[0] = extra[0] = '\0'; + + /* Header */ + if (fwrite(header, 1, 30, fpOut) == 30) { + offset += 30; + } else { + err = Z_ERRNO; + break; + } + + /* Filename */ + if (fnsize > 0) { + if (fnsize < sizeof(filename)) { + if (fread(filename, 1, fnsize, fpZip) == fnsize) { + if (fwrite(filename, 1, fnsize, fpOut) == fnsize) { + offset += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (extsize < sizeof(extra)) { + if (fread(extra, 1, extsize, fpZip) == extsize) { + if (fwrite(extra, 1, extsize, fpOut) == extsize) { + offset += extsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } + + /* Data */ + { + int dataSize = cpsize; + if (dataSize == 0) { + dataSize = uncpsize; + } + if (dataSize > 0) { + char* data = malloc(dataSize); + if (data != NULL) { + if ((int)fread(data, 1, dataSize, fpZip) == dataSize) { + if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) { + offset += dataSize; + totalBytes += dataSize; + } else { + err = Z_ERRNO; + } + } else { + err = Z_ERRNO; + } + free(data); + if (err != Z_OK) { + break; + } + } else { + err = Z_MEM_ERROR; + break; + } + } + } + + /* Central directory entry */ + { + char header[46]; + char* comment = ""; + int comsize = (int) strlen(comment); + WRITE_32(header, 0x02014b50); + WRITE_16(header + 4, version); + WRITE_16(header + 6, version); + WRITE_16(header + 8, gpflag); + WRITE_16(header + 10, method); + WRITE_16(header + 12, filetime); + WRITE_16(header + 14, filedate); + WRITE_32(header + 16, crc); + WRITE_32(header + 20, cpsize); + WRITE_32(header + 24, uncpsize); + WRITE_16(header + 28, fnsize); + WRITE_16(header + 30, extsize); + WRITE_16(header + 32, comsize); + WRITE_16(header + 34, 0); /* disk # */ + WRITE_16(header + 36, 0); /* int attrb */ + WRITE_32(header + 38, 0); /* ext attrb */ + WRITE_32(header + 42, currentOffset); + /* Header */ + if (fwrite(header, 1, 46, fpOutCD) == 46) { + offsetCD += 46; + + /* Filename */ + if (fnsize > 0) { + if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) { + offsetCD += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (fwrite(extra, 1, extsize, fpOutCD) == extsize) { + offsetCD += extsize; + } else { + err = Z_ERRNO; + break; + } + } + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) { + offsetCD += comsize; + } else { + err = Z_ERRNO; + break; + } + } + + + } else { + err = Z_ERRNO; + break; + } + } + + /* Success */ + entries++; + + } else { + break; + } + } + + /* Final central directory */ + { + int entriesZip = entries; + char header[22]; + char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools"; + int comsize = (int) strlen(comment); + if (entriesZip > 0xffff) { + entriesZip = 0xffff; + } + WRITE_32(header, 0x06054b50); + WRITE_16(header + 4, 0); /* disk # */ + WRITE_16(header + 6, 0); /* disk # */ + WRITE_16(header + 8, entriesZip); /* hack */ + WRITE_16(header + 10, entriesZip); /* hack */ + WRITE_32(header + 12, offsetCD); /* size of CD */ + WRITE_32(header + 16, offset); /* offset to CD */ + WRITE_16(header + 20, comsize); /* comment */ + + /* Header */ + if (fwrite(header, 1, 22, fpOutCD) == 22) { + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) { + err = Z_ERRNO; + } + } + + } else { + err = Z_ERRNO; + } + } + + /* Final merge (file + central directory) */ + fclose(fpOutCD); + if (err == Z_OK) { + fpOutCD = fopen(fileOutTmp, "rb"); + if (fpOutCD != NULL) { + int nRead; + char buffer[8192]; + while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) { + if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) { + err = Z_ERRNO; + break; + } + } + fclose(fpOutCD); + } + } + + /* Close */ + fclose(fpZip); + fclose(fpOut); + + /* Wipe temporary file */ + (void)remove(fileOutTmp); + + /* Number of recovered entries */ + if (err == Z_OK) { + if (nRecovered != NULL) { + *nRecovered = entries; + } + if (bytesRecovered != NULL) { + *bytesRecovered = totalBytes; + } + } + } else { + err = Z_STREAM_ERROR; + } + return err; +} diff --git a/src/png/zlib/contrib/minizip/mztools.h b/src/png/zlib/contrib/minizip/mztools.h new file mode 100644 index 0000000000..a49a426ec2 --- /dev/null +++ b/src/png/zlib/contrib/minizip/mztools.h @@ -0,0 +1,37 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +#ifndef _zip_tools_H +#define _zip_tools_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#include "unzip.h" + +/* Repair a ZIP file (missing central directory) + file: file to recover + fileOut: output file after recovery + fileOutTmp: temporary file name used for recovery +*/ +extern int ZEXPORT unzRepair(const char* file, + const char* fileOut, + const char* fileOutTmp, + uLong* nRecovered, + uLong* bytesRecovered); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/png/zlib/contrib/minizip/unzip.c b/src/png/zlib/contrib/minizip/unzip.c new file mode 100644 index 0000000000..bcfb9416ec --- /dev/null +++ b/src/png/zlib/contrib/minizip/unzip.c @@ -0,0 +1,2125 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + + ------------------------------------------------------------------------------------ + Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of + compatibility with older software. The following is from the original crypt.c. + Code woven in by Terry Thorsen 1/2003. + + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + ------------------------------------------------------------------------------------ + + Changes in unzip.c + + 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos + 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* + 2007-2008 - Even Rouault - Remove old C style function prototypes + 2007-2008 - Even Rouault - Add unzip support for ZIP64 + + Copyright (C) 2007-2008 Even Rouault + + + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G + should only read the compressed/uncompressed size from the Zip64 format if + the size from normal header was 0xFFFFFFFF + Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Patch created by Daniel Borca + + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + + Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + + +#include +#include +#include + +#ifndef NOUNCRYPT + #define NOUNCRYPT +#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip64_read_info_s; + + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t* pcrc_32_tab; +# endif +} unz64_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been successfully opened for reading. +*/ + + +local int unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == MAXU32) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == MAXU32) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if ((err==UNZ_OK) && (pfile_info != NULL)) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + + } + return err; +} +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; // offset in file + ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->read_buffer == NULL) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} diff --git a/src/png/zlib/contrib/minizip/unzip.h b/src/png/zlib/contrib/minizip/unzip.h new file mode 100644 index 0000000000..2104e39150 --- /dev/null +++ b/src/png/zlib/contrib/minizip/unzip.h @@ -0,0 +1,437 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------------- + + Changes + + See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality +*/ + + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzCloseCurrentFile before call unzClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +typedef struct unz64_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); + +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ diff --git a/src/png/zlib/contrib/minizip/zip.c b/src/png/zlib/contrib/minizip/zip.c new file mode 100644 index 0000000000..44e88a9cb9 --- /dev/null +++ b/src/png/zlib/contrib/minizip/zip.c @@ -0,0 +1,2007 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + Oct-2009 - Mathias Svensson - Remove old C style function prototypes + Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives + Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. + Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data + It is used when recreting zip archive with RAW when deleting items from a zip. + ZIP64 data is automatically added to items that needs it, and existing ZIP64 data need to be removed. + Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (64*1024) //(16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + + +// NOT sure that this work on ALL platform +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x6064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x7064b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignment */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + ZPOS64_T pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralExtra; + uLong size_centralheader; /* size of the central header for cur file */ + uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; + int zip64; /* Add ZIP64 extened information in the extra field */ + ZPOS64_T pos_zip64extrainfo; + ZPOS64_T totalCompressedData; + ZPOS64_T totalUncompressedData; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile64_info ci; /* info on the file curretly writing */ + + ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ + ZPOS64_T add_position_when_writing_offset; + ZPOS64_T number_entry; + +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif + +} zip64_internal; + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal* ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data* ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local void free_linkedlist(linkedlist_data* ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + + +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) +*/ + +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) +{ + unsigned char buf[8]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) +{ + uLong year = (uLong)ptm->tm_year; + if (year>=1980) + year-=1980; + else if (year>=80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); + +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); + + +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) +{ + ZPOS64_T x; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<24; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<32; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<40; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<48; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<56; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + { + // Signature "0x07064b50" Zip64 end of central directory locater + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + } + + if (uPosFound!=0) + break; + } + + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) + return 0; + + /* total number of disks */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto Zip64 end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' + return 0; + + return relativeOffset; +} + +int LoadCentralDirectoryRecord(zip64_internal* pziinit) +{ + int err=ZIP_OK; + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory */ + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry; + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong VersionMadeBy; + uLong VersionNeeded; + uLong size_comment; + + int hasZIP64Record = 0; + + // check first if we find a ZIP64 record + central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); + if(central_pos > 0) + { + hasZIP64Record = 1; + } + else if(central_pos == 0) + { + central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); + } + +/* disable to allow appending to empty ZIP archive + if (central_pos==0) + err=ZIP_ERRNO; +*/ + + if(hasZIP64Record) + { + ZPOS64_T sizeEndOfCentralDirectory; + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* size of zip64 end of central directory record */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version made by */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version needed to extract */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + // TODO.. + // read the comment from the standard central header. + size_comment = 0; + } + else + { + // Read End of central Directory info + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + number_entry = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry = uL; + + /* total number of entries in the central dir */ + number_entry_CD = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry_CD = uL; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + size_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + size_central_dir = uL; + + /* offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + offset_central_dir = uL; + + + /* zipfile global comment length */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + } + + if ((central_posz_filefunc, pziinit->filestream); + return ZIP_ERRNO; + } + + if (size_comment>0) + { + pziinit->globalcomment = (char*)ALLOC(size_comment+1); + if (pziinit->globalcomment) + { + size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); + pziinit->globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + pziinit->add_position_when_writing_offset = byte_before_the_zipfile; + + { + ZPOS64_T size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + ZPOS64_T read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); + + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + pziinit->begin_pos = byte_before_the_zipfile; + pziinit->number_entry = number_entry_CD; + + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + return err; +} + + +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + + +/************************************************************/ +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +{ + zip64_internal ziinit; + zip64_internal* zi; + int err=ZIP_OK; + + ziinit.z_filefunc.zseek32_file = NULL; + ziinit.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + else + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); + + ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writing_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi==NULL) + { + ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + // Read and Cache Central Directory Records + err = LoadCentralDirectoryRecord(&ziinit); + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + + + +extern zipFile ZEXPORT zipOpen (const char* pathname, int append) +{ + return zipOpen3((const void*)pathname,append,NULL,NULL); +} + +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) +{ + return zipOpen3(pathname,append,NULL,NULL); +} + +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +{ + /* write the local header */ + int err; + uInt size_filename = (uInt)strlen(filename); + uInt size_extrafield = size_extrafield_local; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); + + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + } + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if(zi->ci.zip64) + { + size_extrafield += 20; + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); + + if ((err==ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + + + if ((err==ZIP_OK) && (zi->ci.zip64)) + { + // write the Zip64 extended info + short HeaderID = 1; + short DataSize = 16; + ZPOS64_T CompressedSize = 0; + ZPOS64_T UncompressedSize = 0; + + // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) + zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); + } + + return err; +} + +/* + NOTE. + When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped + before calling this function it can be done with zipRemoveExtraInfoBlock + + It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize + unnecessary allocations. + */ +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) +{ + zip64_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + (crcForCrypting); + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + +#ifdef HAVE_BZIP2 + if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) + return ZIP_PARAMERROR; +#else + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; +#endif + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else + zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); + } + + zi->ci.flag = flagBase; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if (level==2) + zi->ci.flag |= 4; + if (level==1) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); + + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; + zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data + + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + + zi->ci.size_centralExtra = size_extrafield_global; + zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + if(zi->ci.pos_local_header >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writing_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + zi->ci.zip64 = zip64; + zi->ci.totalCompressedData = 0; + zi->ci.totalUncompressedData = 0; + zi->ci.pos_zip64extrainfo = 0; + + err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uInt)0; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + +#ifdef HAVE_BZIP2 + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) +#else + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) +#endif + { + if(zi->ci.method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } + else if(zi->ci.method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + // Init BZip stuff here + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); + if(err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; +#endif + } + + } + +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +local int zip64FlushWriteBuffer(zip64_internal* zi) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); +#endif + } + + if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + + zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED) + { + zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.totalUncompressedData += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) +{ + zip64_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) + { + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + + if(err != BZ_RUN_OK) + break; + + if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; +// uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; + } + } + + if(err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + if(uTotalOutBefore > zi->ci.stream.total_out) + { + int bBreak = 0; + bBreak++; + } + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + }// while(...) + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) +{ + return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) +{ + zip64_internal* zi; + ZPOS64_T compressed_size; + uLong invalidValue = 0xffffffff; + short datasize = 0; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + } + else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err==BZ_FINISH_OK) + { + uLong uTotalOutBefore; + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.bstream.total_out_lo32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if(err == BZ_STREAM_END) + err = Z_STREAM_END; + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); + } + + if(err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + { + if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + } + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + int tmp_err = deflateEnd(&zi->ci.stream); + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err==ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = zi->ci.totalUncompressedData; + } + compressed_size = zi->ci.totalCompressedData; + +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + // update Current Item crc and sizes, + if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) + { + /*version Made by*/ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); + /*version needed*/ + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); + + } + + zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + + + if(compressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ + + /// set internal file attributes field + if (zi->ci.stream.data_type == Z_ASCII) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + + if(uncompressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ + + // Add ZIP64 extra info field for uncompressed size + if(uncompressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for compressed size + if(compressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for relative offset to local file header of current file + if(zi->ci.pos_local_header >= 0xffffffff) + datasize += 8; + + if(datasize > 0) + { + char* p = NULL; + + if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) + { + // we can not write more data to the buffer that we have room for. + return ZIP_BADZIPFILE; + } + + p = zi->ci.central_header + zi->ci.size_centralheader; + + // Add Extra Information Header for 'ZIP64 information' + zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID + p += 2; + zip64local_putValue_inmemory(p, datasize, 2); // DataSize + p += 2; + + if(uncompressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, uncompressed_size, 8); + p += 8; + } + + if(compressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, compressed_size, 8); + p += 8; + } + + if(zi->ci.pos_local_header >= 0xffffffff) + { + zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); + p += 8; + } + + // Update how much extra free space we got in the memory buffer + // and increase the centralheader size so the new ZIP64 fields are included + // ( 4 below is the size of HeaderID and DataSize field ) + zi->ci.size_centralExtraFree -= datasize + 4; + zi->ci.size_centralheader += datasize + 4; + + // Update the extra info size field + zi->ci.size_centralExtra += datasize + 4; + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); + } + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); + + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + // Update the LocalFileHeader with the new values. + + ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if(uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff ) + { + if(zi->ci.pos_zip64extrainfo > 0) + { + // Update the size in the ZIP64 extended field. + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); + } + else + err = ZIP_BADZIPFILE; // Caller passed zip64 = 0, so no room for zip64 info -> fatal + } + else + { + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (zipFile file) +{ + return zipCloseFileInZipRaw (file,0,0); +} + +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +{ + int err = ZIP_OK; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writing_offset; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); + + /*num disks*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + /*relative offset*/ + if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); + + /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); + + return err; +} + +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + uLong Zip64DataSize = 44; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? + + if (err==ZIP_OK) /* version made by */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* version needed */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); + } + return err; +} +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + /*signature*/ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + { + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + } + + if (err==ZIP_OK) /* total number of entries in the central dir */ + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; + if(pos >= 0xffffffff) + { + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); + } + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writing_offset),4); + } + + return err; +} + +int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +{ + int err = ZIP_OK; + uInt size_global_comment = 0; + + if(global_comment != NULL) + size_global_comment = (uInt)strlen(global_comment); + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + return err; +} + +extern int ZEXPORT zipClose (zipFile file, const char* global_comment) +{ + zip64_internal* zi; + int err = 0; + uLong size_centraldir = 0; + ZPOS64_T centraldir_pos_inzip; + ZPOS64_T pos; + + if (file == NULL) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; + if(pos >= 0xffffffff || zi->number_entry > 0xFFFF) + { + ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); + Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); + } + + if (err==ZIP_OK) + err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + if(err == ZIP_OK) + err = Write_GlobalComment(zi, global_comment); + + if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) +{ + char* p = pData; + int size = 0; + char* pNewHeader; + char* pTmp; + short header; + short dataSize; + + int retVal = ZIP_OK; + + if(pData == NULL || *dataLen < 4) + return ZIP_PARAMERROR; + + pNewHeader = (char*)ALLOC(*dataLen); + pTmp = pNewHeader; + + while(p < (pData + *dataLen)) + { + header = *(short*)p; + dataSize = *(((short*)p)+1); + + if( header == sHeader ) // Header found. + { + p += dataSize + 4; // skip it. do not copy to temp buffer + } + else + { + // Extra Info block should not be removed, So copy it to the temp buffer. + memcpy(pTmp, p, dataSize + 4); + p += dataSize + 4; + size += dataSize + 4; + } + + } + + if(size < *dataLen) + { + // clean old extra info block. + memset(pData,0, *dataLen); + + // copy the new extra info block over the old + if(size > 0) + memcpy(pData, pNewHeader, size); + + // set the new extra info size + *dataLen = size; + + retVal = ZIP_OK; + } + else + retVal = ZIP_ERRNO; + + TRYFREE(pNewHeader); + + return retVal; +} diff --git a/src/png/zlib/contrib/minizip/zip.h b/src/png/zlib/contrib/minizip/zip.h new file mode 100644 index 0000000000..8aaebb6234 --- /dev/null +++ b/src/png/zlib/contrib/minizip/zip.h @@ -0,0 +1,362 @@ +/* zip.h -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------- + + Changes + + See header of zip.h + +*/ + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#define HAVE_BZIP2 + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); +/* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + +/* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + + +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +/* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ diff --git a/src/png/zlib/contrib/pascal/example.pas b/src/png/zlib/contrib/pascal/example.pas new file mode 100644 index 0000000000..5518b36a73 --- /dev/null +++ b/src/png/zlib/contrib/pascal/example.pas @@ -0,0 +1,599 @@ +(* example.c -- usage example of the zlib compression library + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Pascal translation + * Copyright (C) 1998 by Jacques Nomssi Nzali. + * For conditions of distribution and use, see copyright notice in readme.txt + * + * Adaptation to the zlibpas interface + * Copyright (C) 2003 by Cosmin Truta. + * For conditions of distribution and use, see copyright notice in readme.txt + *) + +program example; + +{$DEFINE TEST_COMPRESS} +{DO NOT $DEFINE TEST_GZIO} +{$DEFINE TEST_DEFLATE} +{$DEFINE TEST_INFLATE} +{$DEFINE TEST_FLUSH} +{$DEFINE TEST_SYNC} +{$DEFINE TEST_DICT} + +uses SysUtils, zlibpas; + +const TESTFILE = 'foo.gz'; + +(* "hello world" would be more standard, but the repeated "hello" + * stresses the compression code better, sorry... + *) +const hello: PChar = 'hello, hello!'; + +const dictionary: PChar = 'hello'; + +var dictId: LongInt; (* Adler32 value of the dictionary *) + +procedure CHECK_ERR(err: Integer; msg: String); +begin + if err <> Z_OK then + begin + WriteLn(msg, ' error: ', err); + Halt(1); + end; +end; + +procedure EXIT_ERR(const msg: String); +begin + WriteLn('Error: ', msg); + Halt(1); +end; + +(* =========================================================================== + * Test compress and uncompress + *) +{$IFDEF TEST_COMPRESS} +procedure test_compress(compr: Pointer; comprLen: LongInt; + uncompr: Pointer; uncomprLen: LongInt); +var err: Integer; + len: LongInt; +begin + len := StrLen(hello)+1; + + err := compress(compr, comprLen, hello, len); + CHECK_ERR(err, 'compress'); + + StrCopy(PChar(uncompr), 'garbage'); + + err := uncompress(uncompr, uncomprLen, compr, comprLen); + CHECK_ERR(err, 'uncompress'); + + if StrComp(PChar(uncompr), hello) <> 0 then + EXIT_ERR('bad uncompress') + else + WriteLn('uncompress(): ', PChar(uncompr)); +end; +{$ENDIF} + +(* =========================================================================== + * Test read/write of .gz files + *) +{$IFDEF TEST_GZIO} +procedure test_gzio(const fname: PChar; (* compressed file name *) + uncompr: Pointer; + uncomprLen: LongInt); +var err: Integer; + len: Integer; + zfile: gzFile; + pos: LongInt; +begin + len := StrLen(hello)+1; + + zfile := gzopen(fname, 'wb'); + if zfile = NIL then + begin + WriteLn('gzopen error'); + Halt(1); + end; + gzputc(zfile, 'h'); + if gzputs(zfile, 'ello') <> 4 then + begin + WriteLn('gzputs err: ', gzerror(zfile, err)); + Halt(1); + end; + {$IFDEF GZ_FORMAT_STRING} + if gzprintf(zfile, ', %s!', 'hello') <> 8 then + begin + WriteLn('gzprintf err: ', gzerror(zfile, err)); + Halt(1); + end; + {$ELSE} + if gzputs(zfile, ', hello!') <> 8 then + begin + WriteLn('gzputs err: ', gzerror(zfile, err)); + Halt(1); + end; + {$ENDIF} + gzseek(zfile, 1, SEEK_CUR); (* add one zero byte *) + gzclose(zfile); + + zfile := gzopen(fname, 'rb'); + if zfile = NIL then + begin + WriteLn('gzopen error'); + Halt(1); + end; + + StrCopy(PChar(uncompr), 'garbage'); + + if gzread(zfile, uncompr, uncomprLen) <> len then + begin + WriteLn('gzread err: ', gzerror(zfile, err)); + Halt(1); + end; + if StrComp(PChar(uncompr), hello) <> 0 then + begin + WriteLn('bad gzread: ', PChar(uncompr)); + Halt(1); + end + else + WriteLn('gzread(): ', PChar(uncompr)); + + pos := gzseek(zfile, -8, SEEK_CUR); + if (pos <> 6) or (gztell(zfile) <> pos) then + begin + WriteLn('gzseek error, pos=', pos, ', gztell=', gztell(zfile)); + Halt(1); + end; + + if gzgetc(zfile) <> ' ' then + begin + WriteLn('gzgetc error'); + Halt(1); + end; + + if gzungetc(' ', zfile) <> ' ' then + begin + WriteLn('gzungetc error'); + Halt(1); + end; + + gzgets(zfile, PChar(uncompr), uncomprLen); + uncomprLen := StrLen(PChar(uncompr)); + if uncomprLen <> 7 then (* " hello!" *) + begin + WriteLn('gzgets err after gzseek: ', gzerror(zfile, err)); + Halt(1); + end; + if StrComp(PChar(uncompr), hello + 6) <> 0 then + begin + WriteLn('bad gzgets after gzseek'); + Halt(1); + end + else + WriteLn('gzgets() after gzseek: ', PChar(uncompr)); + + gzclose(zfile); +end; +{$ENDIF} + +(* =========================================================================== + * Test deflate with small buffers + *) +{$IFDEF TEST_DEFLATE} +procedure test_deflate(compr: Pointer; comprLen: LongInt); +var c_stream: z_stream; (* compression stream *) + err: Integer; + len: LongInt; +begin + len := StrLen(hello)+1; + + c_stream.zalloc := NIL; + c_stream.zfree := NIL; + c_stream.opaque := NIL; + + err := deflateInit(c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, 'deflateInit'); + + c_stream.next_in := hello; + c_stream.next_out := compr; + + while (c_stream.total_in <> len) and + (c_stream.total_out < comprLen) do + begin + c_stream.avail_out := 1; { force small buffers } + c_stream.avail_in := 1; + err := deflate(c_stream, Z_NO_FLUSH); + CHECK_ERR(err, 'deflate'); + end; + + (* Finish the stream, still forcing small buffers: *) + while TRUE do + begin + c_stream.avail_out := 1; + err := deflate(c_stream, Z_FINISH); + if err = Z_STREAM_END then + break; + CHECK_ERR(err, 'deflate'); + end; + + err := deflateEnd(c_stream); + CHECK_ERR(err, 'deflateEnd'); +end; +{$ENDIF} + +(* =========================================================================== + * Test inflate with small buffers + *) +{$IFDEF TEST_INFLATE} +procedure test_inflate(compr: Pointer; comprLen : LongInt; + uncompr: Pointer; uncomprLen : LongInt); +var err: Integer; + d_stream: z_stream; (* decompression stream *) +begin + StrCopy(PChar(uncompr), 'garbage'); + + d_stream.zalloc := NIL; + d_stream.zfree := NIL; + d_stream.opaque := NIL; + + d_stream.next_in := compr; + d_stream.avail_in := 0; + d_stream.next_out := uncompr; + + err := inflateInit(d_stream); + CHECK_ERR(err, 'inflateInit'); + + while (d_stream.total_out < uncomprLen) and + (d_stream.total_in < comprLen) do + begin + d_stream.avail_out := 1; (* force small buffers *) + d_stream.avail_in := 1; + err := inflate(d_stream, Z_NO_FLUSH); + if err = Z_STREAM_END then + break; + CHECK_ERR(err, 'inflate'); + end; + + err := inflateEnd(d_stream); + CHECK_ERR(err, 'inflateEnd'); + + if StrComp(PChar(uncompr), hello) <> 0 then + EXIT_ERR('bad inflate') + else + WriteLn('inflate(): ', PChar(uncompr)); +end; +{$ENDIF} + +(* =========================================================================== + * Test deflate with large buffers and dynamic change of compression level + *) +{$IFDEF TEST_DEFLATE} +procedure test_large_deflate(compr: Pointer; comprLen: LongInt; + uncompr: Pointer; uncomprLen: LongInt); +var c_stream: z_stream; (* compression stream *) + err: Integer; +begin + c_stream.zalloc := NIL; + c_stream.zfree := NIL; + c_stream.opaque := NIL; + + err := deflateInit(c_stream, Z_BEST_SPEED); + CHECK_ERR(err, 'deflateInit'); + + c_stream.next_out := compr; + c_stream.avail_out := Integer(comprLen); + + (* At this point, uncompr is still mostly zeroes, so it should compress + * very well: + *) + c_stream.next_in := uncompr; + c_stream.avail_in := Integer(uncomprLen); + err := deflate(c_stream, Z_NO_FLUSH); + CHECK_ERR(err, 'deflate'); + if c_stream.avail_in <> 0 then + EXIT_ERR('deflate not greedy'); + + (* Feed in already compressed data and switch to no compression: *) + deflateParams(c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + c_stream.next_in := compr; + c_stream.avail_in := Integer(comprLen div 2); + err := deflate(c_stream, Z_NO_FLUSH); + CHECK_ERR(err, 'deflate'); + + (* Switch back to compressing mode: *) + deflateParams(c_stream, Z_BEST_COMPRESSION, Z_FILTERED); + c_stream.next_in := uncompr; + c_stream.avail_in := Integer(uncomprLen); + err := deflate(c_stream, Z_NO_FLUSH); + CHECK_ERR(err, 'deflate'); + + err := deflate(c_stream, Z_FINISH); + if err <> Z_STREAM_END then + EXIT_ERR('deflate should report Z_STREAM_END'); + + err := deflateEnd(c_stream); + CHECK_ERR(err, 'deflateEnd'); +end; +{$ENDIF} + +(* =========================================================================== + * Test inflate with large buffers + *) +{$IFDEF TEST_INFLATE} +procedure test_large_inflate(compr: Pointer; comprLen: LongInt; + uncompr: Pointer; uncomprLen: LongInt); +var err: Integer; + d_stream: z_stream; (* decompression stream *) +begin + StrCopy(PChar(uncompr), 'garbage'); + + d_stream.zalloc := NIL; + d_stream.zfree := NIL; + d_stream.opaque := NIL; + + d_stream.next_in := compr; + d_stream.avail_in := Integer(comprLen); + + err := inflateInit(d_stream); + CHECK_ERR(err, 'inflateInit'); + + while TRUE do + begin + d_stream.next_out := uncompr; (* discard the output *) + d_stream.avail_out := Integer(uncomprLen); + err := inflate(d_stream, Z_NO_FLUSH); + if err = Z_STREAM_END then + break; + CHECK_ERR(err, 'large inflate'); + end; + + err := inflateEnd(d_stream); + CHECK_ERR(err, 'inflateEnd'); + + if d_stream.total_out <> 2 * uncomprLen + comprLen div 2 then + begin + WriteLn('bad large inflate: ', d_stream.total_out); + Halt(1); + end + else + WriteLn('large_inflate(): OK'); +end; +{$ENDIF} + +(* =========================================================================== + * Test deflate with full flush + *) +{$IFDEF TEST_FLUSH} +procedure test_flush(compr: Pointer; var comprLen : LongInt); +var c_stream: z_stream; (* compression stream *) + err: Integer; + len: Integer; +begin + len := StrLen(hello)+1; + + c_stream.zalloc := NIL; + c_stream.zfree := NIL; + c_stream.opaque := NIL; + + err := deflateInit(c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, 'deflateInit'); + + c_stream.next_in := hello; + c_stream.next_out := compr; + c_stream.avail_in := 3; + c_stream.avail_out := Integer(comprLen); + err := deflate(c_stream, Z_FULL_FLUSH); + CHECK_ERR(err, 'deflate'); + + Inc(PByteArray(compr)^[3]); (* force an error in first compressed block *) + c_stream.avail_in := len - 3; + + err := deflate(c_stream, Z_FINISH); + if err <> Z_STREAM_END then + CHECK_ERR(err, 'deflate'); + + err := deflateEnd(c_stream); + CHECK_ERR(err, 'deflateEnd'); + + comprLen := c_stream.total_out; +end; +{$ENDIF} + +(* =========================================================================== + * Test inflateSync() + *) +{$IFDEF TEST_SYNC} +procedure test_sync(compr: Pointer; comprLen: LongInt; + uncompr: Pointer; uncomprLen : LongInt); +var err: Integer; + d_stream: z_stream; (* decompression stream *) +begin + StrCopy(PChar(uncompr), 'garbage'); + + d_stream.zalloc := NIL; + d_stream.zfree := NIL; + d_stream.opaque := NIL; + + d_stream.next_in := compr; + d_stream.avail_in := 2; (* just read the zlib header *) + + err := inflateInit(d_stream); + CHECK_ERR(err, 'inflateInit'); + + d_stream.next_out := uncompr; + d_stream.avail_out := Integer(uncomprLen); + + inflate(d_stream, Z_NO_FLUSH); + CHECK_ERR(err, 'inflate'); + + d_stream.avail_in := Integer(comprLen-2); (* read all compressed data *) + err := inflateSync(d_stream); (* but skip the damaged part *) + CHECK_ERR(err, 'inflateSync'); + + err := inflate(d_stream, Z_FINISH); + if err <> Z_DATA_ERROR then + EXIT_ERR('inflate should report DATA_ERROR'); + (* Because of incorrect adler32 *) + + err := inflateEnd(d_stream); + CHECK_ERR(err, 'inflateEnd'); + + WriteLn('after inflateSync(): hel', PChar(uncompr)); +end; +{$ENDIF} + +(* =========================================================================== + * Test deflate with preset dictionary + *) +{$IFDEF TEST_DICT} +procedure test_dict_deflate(compr: Pointer; comprLen: LongInt); +var c_stream: z_stream; (* compression stream *) + err: Integer; +begin + c_stream.zalloc := NIL; + c_stream.zfree := NIL; + c_stream.opaque := NIL; + + err := deflateInit(c_stream, Z_BEST_COMPRESSION); + CHECK_ERR(err, 'deflateInit'); + + err := deflateSetDictionary(c_stream, dictionary, StrLen(dictionary)); + CHECK_ERR(err, 'deflateSetDictionary'); + + dictId := c_stream.adler; + c_stream.next_out := compr; + c_stream.avail_out := Integer(comprLen); + + c_stream.next_in := hello; + c_stream.avail_in := StrLen(hello)+1; + + err := deflate(c_stream, Z_FINISH); + if err <> Z_STREAM_END then + EXIT_ERR('deflate should report Z_STREAM_END'); + + err := deflateEnd(c_stream); + CHECK_ERR(err, 'deflateEnd'); +end; +{$ENDIF} + +(* =========================================================================== + * Test inflate with a preset dictionary + *) +{$IFDEF TEST_DICT} +procedure test_dict_inflate(compr: Pointer; comprLen: LongInt; + uncompr: Pointer; uncomprLen: LongInt); +var err: Integer; + d_stream: z_stream; (* decompression stream *) +begin + StrCopy(PChar(uncompr), 'garbage'); + + d_stream.zalloc := NIL; + d_stream.zfree := NIL; + d_stream.opaque := NIL; + + d_stream.next_in := compr; + d_stream.avail_in := Integer(comprLen); + + err := inflateInit(d_stream); + CHECK_ERR(err, 'inflateInit'); + + d_stream.next_out := uncompr; + d_stream.avail_out := Integer(uncomprLen); + + while TRUE do + begin + err := inflate(d_stream, Z_NO_FLUSH); + if err = Z_STREAM_END then + break; + if err = Z_NEED_DICT then + begin + if d_stream.adler <> dictId then + EXIT_ERR('unexpected dictionary'); + err := inflateSetDictionary(d_stream, dictionary, StrLen(dictionary)); + end; + CHECK_ERR(err, 'inflate with dict'); + end; + + err := inflateEnd(d_stream); + CHECK_ERR(err, 'inflateEnd'); + + if StrComp(PChar(uncompr), hello) <> 0 then + EXIT_ERR('bad inflate with dict') + else + WriteLn('inflate with dictionary: ', PChar(uncompr)); +end; +{$ENDIF} + +var compr, uncompr: Pointer; + comprLen, uncomprLen: LongInt; + +begin + if zlibVersion^ <> ZLIB_VERSION[1] then + EXIT_ERR('Incompatible zlib version'); + + WriteLn('zlib version: ', zlibVersion); + WriteLn('zlib compile flags: ', Format('0x%x', [zlibCompileFlags])); + + comprLen := 10000 * SizeOf(Integer); (* don't overflow on MSDOS *) + uncomprLen := comprLen; + GetMem(compr, comprLen); + GetMem(uncompr, uncomprLen); + if (compr = NIL) or (uncompr = NIL) then + EXIT_ERR('Out of memory'); + (* compr and uncompr are cleared to avoid reading uninitialized + * data and to ensure that uncompr compresses well. + *) + FillChar(compr^, comprLen, 0); + FillChar(uncompr^, uncomprLen, 0); + + {$IFDEF TEST_COMPRESS} + WriteLn('** Testing compress'); + test_compress(compr, comprLen, uncompr, uncomprLen); + {$ENDIF} + + {$IFDEF TEST_GZIO} + WriteLn('** Testing gzio'); + if ParamCount >= 1 then + test_gzio(ParamStr(1), uncompr, uncomprLen) + else + test_gzio(TESTFILE, uncompr, uncomprLen); + {$ENDIF} + + {$IFDEF TEST_DEFLATE} + WriteLn('** Testing deflate with small buffers'); + test_deflate(compr, comprLen); + {$ENDIF} + {$IFDEF TEST_INFLATE} + WriteLn('** Testing inflate with small buffers'); + test_inflate(compr, comprLen, uncompr, uncomprLen); + {$ENDIF} + + {$IFDEF TEST_DEFLATE} + WriteLn('** Testing deflate with large buffers'); + test_large_deflate(compr, comprLen, uncompr, uncomprLen); + {$ENDIF} + {$IFDEF TEST_INFLATE} + WriteLn('** Testing inflate with large buffers'); + test_large_inflate(compr, comprLen, uncompr, uncomprLen); + {$ENDIF} + + {$IFDEF TEST_FLUSH} + WriteLn('** Testing deflate with full flush'); + test_flush(compr, comprLen); + {$ENDIF} + {$IFDEF TEST_SYNC} + WriteLn('** Testing inflateSync'); + test_sync(compr, comprLen, uncompr, uncomprLen); + {$ENDIF} + comprLen := uncomprLen; + + {$IFDEF TEST_DICT} + WriteLn('** Testing deflate and inflate with preset dictionary'); + test_dict_deflate(compr, comprLen); + test_dict_inflate(compr, comprLen, uncompr, uncomprLen); + {$ENDIF} + + FreeMem(compr, comprLen); + FreeMem(uncompr, uncomprLen); +end. diff --git a/src/png/zlib/contrib/pascal/readme.txt b/src/png/zlib/contrib/pascal/readme.txt new file mode 100644 index 0000000000..60e87c8a33 --- /dev/null +++ b/src/png/zlib/contrib/pascal/readme.txt @@ -0,0 +1,76 @@ + +This directory contains a Pascal (Delphi, Kylix) interface to the +zlib data compression library. + + +Directory listing +================= + +zlibd32.mak makefile for Borland C++ +example.pas usage example of zlib +zlibpas.pas the Pascal interface to zlib +readme.txt this file + + +Compatibility notes +=================== + +- Although the name "zlib" would have been more normal for the + zlibpas unit, this name is already taken by Borland's ZLib unit. + This is somehow unfortunate, because that unit is not a genuine + interface to the full-fledged zlib functionality, but a suite of + class wrappers around zlib streams. Other essential features, + such as checksums, are missing. + It would have been more appropriate for that unit to have a name + like "ZStreams", or something similar. + +- The C and zlib-supplied types int, uInt, long, uLong, etc. are + translated directly into Pascal types of similar sizes (Integer, + LongInt, etc.), to avoid namespace pollution. In particular, + there is no conversion of unsigned int into a Pascal unsigned + integer. The Word type is non-portable and has the same size + (16 bits) both in a 16-bit and in a 32-bit environment, unlike + Integer. Even if there is a 32-bit Cardinal type, there is no + real need for unsigned int in zlib under a 32-bit environment. + +- Except for the callbacks, the zlib function interfaces are + assuming the calling convention normally used in Pascal + (__pascal for DOS and Windows16, __fastcall for Windows32). + Since the cdecl keyword is used, the old Turbo Pascal does + not work with this interface. + +- The gz* function interfaces are not translated, to avoid + interfacing problems with the C runtime library. Besides, + gzprintf(gzFile file, const char *format, ...) + cannot be translated into Pascal. + + +Legal issues +============ + +The zlibpas interface is: + Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler. + Copyright (C) 1998 by Bob Dellaca. + Copyright (C) 2003 by Cosmin Truta. + +The example program is: + Copyright (C) 1995-2003 by Jean-loup Gailly. + Copyright (C) 1998,1999,2000 by Jacques Nomssi Nzali. + Copyright (C) 2003 by Cosmin Truta. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + diff --git a/src/png/zlib/contrib/pascal/zlibd32.mak b/src/png/zlib/contrib/pascal/zlibd32.mak new file mode 100644 index 0000000000..9bb00b7cc4 --- /dev/null +++ b/src/png/zlib/contrib/pascal/zlibd32.mak @@ -0,0 +1,99 @@ +# Makefile for zlib +# For use with Delphi and C++ Builder under Win32 +# Updated for zlib 1.2.x by Cosmin Truta + +# ------------ Borland C++ ------------ + +# This project uses the Delphi (fastcall/register) calling convention: +LOC = -DZEXPORT=__fastcall -DZEXPORTVA=__cdecl + +CC = bcc32 +LD = bcc32 +AR = tlib +# do not use "-pr" in CFLAGS +CFLAGS = -a -d -k- -O2 $(LOC) +LDFLAGS = + + +# variables +ZLIB_LIB = zlib.lib + +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj +OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj +OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj + + +# targets +all: $(ZLIB_LIB) example.exe minigzip.exe + +.c.obj: + $(CC) -c $(CFLAGS) $*.c + +adler32.obj: adler32.c zlib.h zconf.h + +compress.obj: compress.c zlib.h zconf.h + +crc32.obj: crc32.c zlib.h zconf.h crc32.h + +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h + +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h + +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h + +gzread.obj: gzread.c zlib.h zconf.h gzguts.h + +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h + +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h + +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h + +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h + +uncompr.obj: uncompr.c zlib.h zconf.h + +zutil.obj: zutil.c zutil.h zlib.h zconf.h + +example.obj: test/example.c zlib.h zconf.h + +minigzip.obj: test/minigzip.c zlib.h zconf.h + + +# For the sake of the old Borland make, +# the command line is cut to fit in the MS-DOS 128 byte limit: +$(ZLIB_LIB): $(OBJ1) $(OBJ2) + -del $(ZLIB_LIB) + $(AR) $(ZLIB_LIB) $(OBJP1) + $(AR) $(ZLIB_LIB) $(OBJP2) + + +# testing +test: example.exe minigzip.exe + example + echo hello world | minigzip | minigzip -d + +example.exe: example.obj $(ZLIB_LIB) + $(LD) $(LDFLAGS) example.obj $(ZLIB_LIB) + +minigzip.exe: minigzip.obj $(ZLIB_LIB) + $(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB) + + +# cleanup +clean: + -del *.obj + -del *.exe + -del *.lib + -del *.tds + -del zlib.bak + -del foo.gz + diff --git a/src/png/zlib/contrib/pascal/zlibpas.pas b/src/png/zlib/contrib/pascal/zlibpas.pas new file mode 100644 index 0000000000..a0dff11b50 --- /dev/null +++ b/src/png/zlib/contrib/pascal/zlibpas.pas @@ -0,0 +1,276 @@ +(* zlibpas -- Pascal interface to the zlib data compression library + * + * Copyright (C) 2003 Cosmin Truta. + * Derived from original sources by Bob Dellaca. + * For conditions of distribution and use, see copyright notice in readme.txt + *) + +unit zlibpas; + +interface + +const + ZLIB_VERSION = '1.2.11'; + ZLIB_VERNUM = $12a0; + +type + alloc_func = function(opaque: Pointer; items, size: Integer): Pointer; + cdecl; + free_func = procedure(opaque, address: Pointer); + cdecl; + + in_func = function(opaque: Pointer; var buf: PByte): Integer; + cdecl; + out_func = function(opaque: Pointer; buf: PByte; size: Integer): Integer; + cdecl; + + z_streamp = ^z_stream; + z_stream = packed record + next_in: PChar; (* next input byte *) + avail_in: Integer; (* number of bytes available at next_in *) + total_in: LongInt; (* total nb of input bytes read so far *) + + next_out: PChar; (* next output byte should be put there *) + avail_out: Integer; (* remaining free space at next_out *) + total_out: LongInt; (* total nb of bytes output so far *) + + msg: PChar; (* last error message, NULL if no error *) + state: Pointer; (* not visible by applications *) + + zalloc: alloc_func; (* used to allocate the internal state *) + zfree: free_func; (* used to free the internal state *) + opaque: Pointer; (* private data object passed to zalloc and zfree *) + + data_type: Integer; (* best guess about the data type: ascii or binary *) + adler: LongInt; (* adler32 value of the uncompressed data *) + reserved: LongInt; (* reserved for future use *) + end; + + gz_headerp = ^gz_header; + gz_header = packed record + text: Integer; (* true if compressed data believed to be text *) + time: LongInt; (* modification time *) + xflags: Integer; (* extra flags (not used when writing a gzip file) *) + os: Integer; (* operating system *) + extra: PChar; (* pointer to extra field or Z_NULL if none *) + extra_len: Integer; (* extra field length (valid if extra != Z_NULL) *) + extra_max: Integer; (* space at extra (only when reading header) *) + name: PChar; (* pointer to zero-terminated file name or Z_NULL *) + name_max: Integer; (* space at name (only when reading header) *) + comment: PChar; (* pointer to zero-terminated comment or Z_NULL *) + comm_max: Integer; (* space at comment (only when reading header) *) + hcrc: Integer; (* true if there was or will be a header crc *) + done: Integer; (* true when done reading gzip header *) + end; + +(* constants *) +const + Z_NO_FLUSH = 0; + Z_PARTIAL_FLUSH = 1; + Z_SYNC_FLUSH = 2; + Z_FULL_FLUSH = 3; + Z_FINISH = 4; + Z_BLOCK = 5; + Z_TREES = 6; + + Z_OK = 0; + Z_STREAM_END = 1; + Z_NEED_DICT = 2; + Z_ERRNO = -1; + Z_STREAM_ERROR = -2; + Z_DATA_ERROR = -3; + Z_MEM_ERROR = -4; + Z_BUF_ERROR = -5; + Z_VERSION_ERROR = -6; + + Z_NO_COMPRESSION = 0; + Z_BEST_SPEED = 1; + Z_BEST_COMPRESSION = 9; + Z_DEFAULT_COMPRESSION = -1; + + Z_FILTERED = 1; + Z_HUFFMAN_ONLY = 2; + Z_RLE = 3; + Z_FIXED = 4; + Z_DEFAULT_STRATEGY = 0; + + Z_BINARY = 0; + Z_TEXT = 1; + Z_ASCII = 1; + Z_UNKNOWN = 2; + + Z_DEFLATED = 8; + +(* basic functions *) +function zlibVersion: PChar; +function deflateInit(var strm: z_stream; level: Integer): Integer; +function deflate(var strm: z_stream; flush: Integer): Integer; +function deflateEnd(var strm: z_stream): Integer; +function inflateInit(var strm: z_stream): Integer; +function inflate(var strm: z_stream; flush: Integer): Integer; +function inflateEnd(var strm: z_stream): Integer; + +(* advanced functions *) +function deflateInit2(var strm: z_stream; level, method, windowBits, + memLevel, strategy: Integer): Integer; +function deflateSetDictionary(var strm: z_stream; const dictionary: PChar; + dictLength: Integer): Integer; +function deflateCopy(var dest, source: z_stream): Integer; +function deflateReset(var strm: z_stream): Integer; +function deflateParams(var strm: z_stream; level, strategy: Integer): Integer; +function deflateTune(var strm: z_stream; good_length, max_lazy, nice_length, max_chain: Integer): Integer; +function deflateBound(var strm: z_stream; sourceLen: LongInt): LongInt; +function deflatePending(var strm: z_stream; var pending: Integer; var bits: Integer): Integer; +function deflatePrime(var strm: z_stream; bits, value: Integer): Integer; +function deflateSetHeader(var strm: z_stream; head: gz_header): Integer; +function inflateInit2(var strm: z_stream; windowBits: Integer): Integer; +function inflateSetDictionary(var strm: z_stream; const dictionary: PChar; + dictLength: Integer): Integer; +function inflateSync(var strm: z_stream): Integer; +function inflateCopy(var dest, source: z_stream): Integer; +function inflateReset(var strm: z_stream): Integer; +function inflateReset2(var strm: z_stream; windowBits: Integer): Integer; +function inflatePrime(var strm: z_stream; bits, value: Integer): Integer; +function inflateMark(var strm: z_stream): LongInt; +function inflateGetHeader(var strm: z_stream; var head: gz_header): Integer; +function inflateBackInit(var strm: z_stream; + windowBits: Integer; window: PChar): Integer; +function inflateBack(var strm: z_stream; in_fn: in_func; in_desc: Pointer; + out_fn: out_func; out_desc: Pointer): Integer; +function inflateBackEnd(var strm: z_stream): Integer; +function zlibCompileFlags: LongInt; + +(* utility functions *) +function compress(dest: PChar; var destLen: LongInt; + const source: PChar; sourceLen: LongInt): Integer; +function compress2(dest: PChar; var destLen: LongInt; + const source: PChar; sourceLen: LongInt; + level: Integer): Integer; +function compressBound(sourceLen: LongInt): LongInt; +function uncompress(dest: PChar; var destLen: LongInt; + const source: PChar; sourceLen: LongInt): Integer; + +(* checksum functions *) +function adler32(adler: LongInt; const buf: PChar; len: Integer): LongInt; +function adler32_combine(adler1, adler2, len2: LongInt): LongInt; +function crc32(crc: LongInt; const buf: PChar; len: Integer): LongInt; +function crc32_combine(crc1, crc2, len2: LongInt): LongInt; + +(* various hacks, don't look :) *) +function deflateInit_(var strm: z_stream; level: Integer; + const version: PChar; stream_size: Integer): Integer; +function inflateInit_(var strm: z_stream; const version: PChar; + stream_size: Integer): Integer; +function deflateInit2_(var strm: z_stream; + level, method, windowBits, memLevel, strategy: Integer; + const version: PChar; stream_size: Integer): Integer; +function inflateInit2_(var strm: z_stream; windowBits: Integer; + const version: PChar; stream_size: Integer): Integer; +function inflateBackInit_(var strm: z_stream; + windowBits: Integer; window: PChar; + const version: PChar; stream_size: Integer): Integer; + + +implementation + +{$L adler32.obj} +{$L compress.obj} +{$L crc32.obj} +{$L deflate.obj} +{$L infback.obj} +{$L inffast.obj} +{$L inflate.obj} +{$L inftrees.obj} +{$L trees.obj} +{$L uncompr.obj} +{$L zutil.obj} + +function adler32; external; +function adler32_combine; external; +function compress; external; +function compress2; external; +function compressBound; external; +function crc32; external; +function crc32_combine; external; +function deflate; external; +function deflateBound; external; +function deflateCopy; external; +function deflateEnd; external; +function deflateInit_; external; +function deflateInit2_; external; +function deflateParams; external; +function deflatePending; external; +function deflatePrime; external; +function deflateReset; external; +function deflateSetDictionary; external; +function deflateSetHeader; external; +function deflateTune; external; +function inflate; external; +function inflateBack; external; +function inflateBackEnd; external; +function inflateBackInit_; external; +function inflateCopy; external; +function inflateEnd; external; +function inflateGetHeader; external; +function inflateInit_; external; +function inflateInit2_; external; +function inflateMark; external; +function inflatePrime; external; +function inflateReset; external; +function inflateReset2; external; +function inflateSetDictionary; external; +function inflateSync; external; +function uncompress; external; +function zlibCompileFlags; external; +function zlibVersion; external; + +function deflateInit(var strm: z_stream; level: Integer): Integer; +begin + Result := deflateInit_(strm, level, ZLIB_VERSION, sizeof(z_stream)); +end; + +function deflateInit2(var strm: z_stream; level, method, windowBits, memLevel, + strategy: Integer): Integer; +begin + Result := deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + ZLIB_VERSION, sizeof(z_stream)); +end; + +function inflateInit(var strm: z_stream): Integer; +begin + Result := inflateInit_(strm, ZLIB_VERSION, sizeof(z_stream)); +end; + +function inflateInit2(var strm: z_stream; windowBits: Integer): Integer; +begin + Result := inflateInit2_(strm, windowBits, ZLIB_VERSION, sizeof(z_stream)); +end; + +function inflateBackInit(var strm: z_stream; + windowBits: Integer; window: PChar): Integer; +begin + Result := inflateBackInit_(strm, windowBits, window, + ZLIB_VERSION, sizeof(z_stream)); +end; + +function _malloc(Size: Integer): Pointer; cdecl; +begin + GetMem(Result, Size); +end; + +procedure _free(Block: Pointer); cdecl; +begin + FreeMem(Block); +end; + +procedure _memset(P: Pointer; B: Byte; count: Integer); cdecl; +begin + FillChar(P^, count, B); +end; + +procedure _memcpy(dest, source: Pointer; count: Integer); cdecl; +begin + Move(source^, dest^, count); +end; + +end. diff --git a/src/png/zlib/contrib/puff/Makefile b/src/png/zlib/contrib/puff/Makefile new file mode 100644 index 0000000000..0e2594c808 --- /dev/null +++ b/src/png/zlib/contrib/puff/Makefile @@ -0,0 +1,42 @@ +CFLAGS=-O + +puff: puff.o pufftest.o + +puff.o: puff.h + +pufftest.o: puff.h + +test: puff + puff zeros.raw + +puft: puff.c puff.h pufftest.o + cc -fprofile-arcs -ftest-coverage -o puft puff.c pufftest.o + +# puff full coverage test (should say 100%) +cov: puft + @rm -f *.gcov *.gcda + @puft -w zeros.raw 2>&1 | cat > /dev/null + @echo '04' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2 + @echo '00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2 + @echo '00 00 00 00 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 254 + @echo '00 01 00 fe ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2 + @echo '01 01 00 fe ff 0a' | xxd -r -p | puft -f 2>&1 | cat > /dev/null + @echo '02 7e ff ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 246 + @echo '02' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2 + @echo '04 80 49 92 24 49 92 24 0f b4 ff ff c3 04' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2 + @echo '04 80 49 92 24 49 92 24 71 ff ff 93 11 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 249 + @echo '04 c0 81 08 00 00 00 00 20 7f eb 0b 00 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 246 + @echo '0b 00 00' | xxd -r -p | puft -f 2>&1 | cat > /dev/null + @echo '1a 07' | xxd -r -p | puft 2> /dev/null || test $$? -eq 246 + @echo '0c c0 81 00 00 00 00 00 90 ff 6b 04' | xxd -r -p | puft 2> /dev/null || test $$? -eq 245 + @puft -f zeros.raw 2>&1 | cat > /dev/null + @echo 'fc 00 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 253 + @echo '04 00 fe ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 252 + @echo '04 00 24 49' | xxd -r -p | puft 2> /dev/null || test $$? -eq 251 + @echo '04 80 49 92 24 49 92 24 0f b4 ff ff c3 84' | xxd -r -p | puft 2> /dev/null || test $$? -eq 248 + @echo '04 00 24 e9 ff ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 250 + @echo '04 00 24 e9 ff 6d' | xxd -r -p | puft 2> /dev/null || test $$? -eq 247 + @gcov -n puff.c + +clean: + rm -f puff puft *.o *.gc* diff --git a/src/png/zlib/contrib/puff/README b/src/png/zlib/contrib/puff/README new file mode 100644 index 0000000000..bbc4cb595e --- /dev/null +++ b/src/png/zlib/contrib/puff/README @@ -0,0 +1,63 @@ +Puff -- A Simple Inflate +3 Mar 2003 +Mark Adler +madler@alumni.caltech.edu + +What this is -- + +puff.c provides the routine puff() to decompress the deflate data format. It +does so more slowly than zlib, but the code is about one-fifth the size of the +inflate code in zlib, and written to be very easy to read. + +Why I wrote this -- + +puff.c was written to document the deflate format unambiguously, by virtue of +being working C code. It is meant to supplement RFC 1951, which formally +describes the deflate format. I have received many questions on details of the +deflate format, and I hope that reading this code will answer those questions. +puff.c is heavily commented with details of the deflate format, especially +those little nooks and cranies of the format that might not be obvious from a +specification. + +puff.c may also be useful in applications where code size or memory usage is a +very limited resource, and speed is not as important. + +How to use it -- + +Well, most likely you should just be reading puff.c and using zlib for actual +applications, but if you must ... + +Include puff.h in your code, which provides this prototype: + +int puff(unsigned char *dest, /* pointer to destination pointer */ + unsigned long *destlen, /* amount of output space */ + unsigned char *source, /* pointer to source data pointer */ + unsigned long *sourcelen); /* amount of input available */ + +Then you can call puff() to decompress a deflate stream that is in memory in +its entirety at source, to a sufficiently sized block of memory for the +decompressed data at dest. puff() is the only external symbol in puff.c The +only C library functions that puff.c needs are setjmp() and longjmp(), which +are used to simplify error checking in the code to improve readabilty. puff.c +does no memory allocation, and uses less than 2K bytes off of the stack. + +If destlen is not enough space for the uncompressed data, then inflate will +return an error without writing more than destlen bytes. Note that this means +that in order to decompress the deflate data successfully, you need to know +the size of the uncompressed data ahead of time. + +If needed, puff() can determine the size of the uncompressed data with no +output space. This is done by passing dest equal to (unsigned char *)0. Then +the initial value of *destlen is ignored and *destlen is set to the length of +the uncompressed data. So if the size of the uncompressed data is not known, +then two passes of puff() can be used--first to determine the size, and second +to do the actual inflation after allocating the appropriate memory. Not +pretty, but it works. (This is one of the reasons you should be using zlib.) + +The deflate format is self-terminating. If the deflate stream does not end +in *sourcelen bytes, puff() will return an error without reading at or past +endsource. + +On return, *sourcelen is updated to the amount of input data consumed, and +*destlen is updated to the size of the uncompressed data. See the comments +in puff.c for the possible return codes for puff(). diff --git a/src/png/zlib/contrib/puff/puff.c b/src/png/zlib/contrib/puff/puff.c new file mode 100644 index 0000000000..c6c90d7142 --- /dev/null +++ b/src/png/zlib/contrib/puff/puff.c @@ -0,0 +1,840 @@ +/* + * puff.c + * Copyright (C) 2002-2013 Mark Adler + * For conditions of distribution and use, see copyright notice in puff.h + * version 2.3, 21 Jan 2013 + * + * puff.c is a simple inflate written to be an unambiguous way to specify the + * deflate format. It is not written for speed but rather simplicity. As a + * side benefit, this code might actually be useful when small code is more + * important than speed, such as bootstrap applications. For typical deflate + * data, zlib's inflate() is about four times as fast as puff(). zlib's + * inflate compiles to around 20K on my machine, whereas puff.c compiles to + * around 4K on my machine (a PowerPC using GNU cc). If the faster decode() + * function here is used, then puff() is only twice as slow as zlib's + * inflate(). + * + * All dynamically allocated memory comes from the stack. The stack required + * is less than 2K bytes. This code is compatible with 16-bit int's and + * assumes that long's are at least 32 bits. puff.c uses the short data type, + * assumed to be 16 bits, for arrays in order to conserve memory. The code + * works whether integers are stored big endian or little endian. + * + * In the comments below are "Format notes" that describe the inflate process + * and document some of the less obvious aspects of the format. This source + * code is meant to supplement RFC 1951, which formally describes the deflate + * format: + * + * http://www.zlib.org/rfc-deflate.html + */ + +/* + * Change history: + * + * 1.0 10 Feb 2002 - First version + * 1.1 17 Feb 2002 - Clarifications of some comments and notes + * - Update puff() dest and source pointers on negative + * errors to facilitate debugging deflators + * - Remove longest from struct huffman -- not needed + * - Simplify offs[] index in construct() + * - Add input size and checking, using longjmp() to + * maintain easy readability + * - Use short data type for large arrays + * - Use pointers instead of long to specify source and + * destination sizes to avoid arbitrary 4 GB limits + * 1.2 17 Mar 2002 - Add faster version of decode(), doubles speed (!), + * but leave simple version for readabilty + * - Make sure invalid distances detected if pointers + * are 16 bits + * - Fix fixed codes table error + * - Provide a scanning mode for determining size of + * uncompressed data + * 1.3 20 Mar 2002 - Go back to lengths for puff() parameters [Gailly] + * - Add a puff.h file for the interface + * - Add braces in puff() for else do [Gailly] + * - Use indexes instead of pointers for readability + * 1.4 31 Mar 2002 - Simplify construct() code set check + * - Fix some comments + * - Add FIXLCODES #define + * 1.5 6 Apr 2002 - Minor comment fixes + * 1.6 7 Aug 2002 - Minor format changes + * 1.7 3 Mar 2003 - Added test code for distribution + * - Added zlib-like license + * 1.8 9 Jan 2004 - Added some comments on no distance codes case + * 1.9 21 Feb 2008 - Fix bug on 16-bit integer architectures [Pohland] + * - Catch missing end-of-block symbol error + * 2.0 25 Jul 2008 - Add #define to permit distance too far back + * - Add option in TEST code for puff to write the data + * - Add option in TEST code to skip input bytes + * - Allow TEST code to read from piped stdin + * 2.1 4 Apr 2010 - Avoid variable initialization for happier compilers + * - Avoid unsigned comparisons for even happier compilers + * 2.2 25 Apr 2010 - Fix bug in variable initializations [Oberhumer] + * - Add const where appropriate [Oberhumer] + * - Split if's and ?'s for coverage testing + * - Break out test code to separate file + * - Move NIL to puff.h + * - Allow incomplete code only if single code length is 1 + * - Add full code coverage test to Makefile + * 2.3 21 Jan 2013 - Check for invalid code length codes in dynamic blocks + */ + +#include /* for setjmp(), longjmp(), and jmp_buf */ +#include "puff.h" /* prototype for puff() */ + +#define local static /* for local function definitions */ + +/* + * Maximums for allocations and loops. It is not useful to change these -- + * they are fixed by the deflate format. + */ +#define MAXBITS 15 /* maximum bits in a code */ +#define MAXLCODES 286 /* maximum number of literal/length codes */ +#define MAXDCODES 30 /* maximum number of distance codes */ +#define MAXCODES (MAXLCODES+MAXDCODES) /* maximum codes lengths to read */ +#define FIXLCODES 288 /* number of fixed literal/length codes */ + +/* input and output state */ +struct state { + /* output state */ + unsigned char *out; /* output buffer */ + unsigned long outlen; /* available space at out */ + unsigned long outcnt; /* bytes written to out so far */ + + /* input state */ + const unsigned char *in; /* input buffer */ + unsigned long inlen; /* available input at in */ + unsigned long incnt; /* bytes read so far */ + int bitbuf; /* bit buffer */ + int bitcnt; /* number of bits in bit buffer */ + + /* input limit error return state for bits() and decode() */ + jmp_buf env; +}; + +/* + * Return need bits from the input stream. This always leaves less than + * eight bits in the buffer. bits() works properly for need == 0. + * + * Format notes: + * + * - Bits are stored in bytes from the least significant bit to the most + * significant bit. Therefore bits are dropped from the bottom of the bit + * buffer, using shift right, and new bytes are appended to the top of the + * bit buffer, using shift left. + */ +local int bits(struct state *s, int need) +{ + long val; /* bit accumulator (can use up to 20 bits) */ + + /* load at least need bits into val */ + val = s->bitbuf; + while (s->bitcnt < need) { + if (s->incnt == s->inlen) + longjmp(s->env, 1); /* out of input */ + val |= (long)(s->in[s->incnt++]) << s->bitcnt; /* load eight bits */ + s->bitcnt += 8; + } + + /* drop need bits and update buffer, always zero to seven bits left */ + s->bitbuf = (int)(val >> need); + s->bitcnt -= need; + + /* return need bits, zeroing the bits above that */ + return (int)(val & ((1L << need) - 1)); +} + +/* + * Process a stored block. + * + * Format notes: + * + * - After the two-bit stored block type (00), the stored block length and + * stored bytes are byte-aligned for fast copying. Therefore any leftover + * bits in the byte that has the last bit of the type, as many as seven, are + * discarded. The value of the discarded bits are not defined and should not + * be checked against any expectation. + * + * - The second inverted copy of the stored block length does not have to be + * checked, but it's probably a good idea to do so anyway. + * + * - A stored block can have zero length. This is sometimes used to byte-align + * subsets of the compressed data for random access or partial recovery. + */ +local int stored(struct state *s) +{ + unsigned len; /* length of stored block */ + + /* discard leftover bits from current byte (assumes s->bitcnt < 8) */ + s->bitbuf = 0; + s->bitcnt = 0; + + /* get length and check against its one's complement */ + if (s->incnt + 4 > s->inlen) + return 2; /* not enough input */ + len = s->in[s->incnt++]; + len |= s->in[s->incnt++] << 8; + if (s->in[s->incnt++] != (~len & 0xff) || + s->in[s->incnt++] != ((~len >> 8) & 0xff)) + return -2; /* didn't match complement! */ + + /* copy len bytes from in to out */ + if (s->incnt + len > s->inlen) + return 2; /* not enough input */ + if (s->out != NIL) { + if (s->outcnt + len > s->outlen) + return 1; /* not enough output space */ + while (len--) + s->out[s->outcnt++] = s->in[s->incnt++]; + } + else { /* just scanning */ + s->outcnt += len; + s->incnt += len; + } + + /* done with a valid stored block */ + return 0; +} + +/* + * Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of + * each length, which for a canonical code are stepped through in order. + * symbol[] are the symbol values in canonical order, where the number of + * entries is the sum of the counts in count[]. The decoding process can be + * seen in the function decode() below. + */ +struct huffman { + short *count; /* number of symbols of each length */ + short *symbol; /* canonically ordered symbols */ +}; + +/* + * Decode a code from the stream s using huffman table h. Return the symbol or + * a negative value if there is an error. If all of the lengths are zero, i.e. + * an empty code, or if the code is incomplete and an invalid code is received, + * then -10 is returned after reading MAXBITS bits. + * + * Format notes: + * + * - The codes as stored in the compressed data are bit-reversed relative to + * a simple integer ordering of codes of the same lengths. Hence below the + * bits are pulled from the compressed data one at a time and used to + * build the code value reversed from what is in the stream in order to + * permit simple integer comparisons for decoding. A table-based decoding + * scheme (as used in zlib) does not need to do this reversal. + * + * - The first code for the shortest length is all zeros. Subsequent codes of + * the same length are simply integer increments of the previous code. When + * moving up a length, a zero bit is appended to the code. For a complete + * code, the last code of the longest length will be all ones. + * + * - Incomplete codes are handled by this decoder, since they are permitted + * in the deflate format. See the format notes for fixed() and dynamic(). + */ +#ifdef SLOW +local int decode(struct state *s, const struct huffman *h) +{ + int len; /* current number of bits in code */ + int code; /* len bits being decoded */ + int first; /* first code of length len */ + int count; /* number of codes of length len */ + int index; /* index of first code of length len in symbol table */ + + code = first = index = 0; + for (len = 1; len <= MAXBITS; len++) { + code |= bits(s, 1); /* get next bit */ + count = h->count[len]; + if (code - count < first) /* if length len, return symbol */ + return h->symbol[index + (code - first)]; + index += count; /* else update for next length */ + first += count; + first <<= 1; + code <<= 1; + } + return -10; /* ran out of codes */ +} + +/* + * A faster version of decode() for real applications of this code. It's not + * as readable, but it makes puff() twice as fast. And it only makes the code + * a few percent larger. + */ +#else /* !SLOW */ +local int decode(struct state *s, const struct huffman *h) +{ + int len; /* current number of bits in code */ + int code; /* len bits being decoded */ + int first; /* first code of length len */ + int count; /* number of codes of length len */ + int index; /* index of first code of length len in symbol table */ + int bitbuf; /* bits from stream */ + int left; /* bits left in next or left to process */ + short *next; /* next number of codes */ + + bitbuf = s->bitbuf; + left = s->bitcnt; + code = first = index = 0; + len = 1; + next = h->count + 1; + while (1) { + while (left--) { + code |= bitbuf & 1; + bitbuf >>= 1; + count = *next++; + if (code - count < first) { /* if length len, return symbol */ + s->bitbuf = bitbuf; + s->bitcnt = (s->bitcnt - len) & 7; + return h->symbol[index + (code - first)]; + } + index += count; /* else update for next length */ + first += count; + first <<= 1; + code <<= 1; + len++; + } + left = (MAXBITS+1) - len; + if (left == 0) + break; + if (s->incnt == s->inlen) + longjmp(s->env, 1); /* out of input */ + bitbuf = s->in[s->incnt++]; + if (left > 8) + left = 8; + } + return -10; /* ran out of codes */ +} +#endif /* SLOW */ + +/* + * Given the list of code lengths length[0..n-1] representing a canonical + * Huffman code for n symbols, construct the tables required to decode those + * codes. Those tables are the number of codes of each length, and the symbols + * sorted by length, retaining their original order within each length. The + * return value is zero for a complete code set, negative for an over- + * subscribed code set, and positive for an incomplete code set. The tables + * can be used if the return value is zero or positive, but they cannot be used + * if the return value is negative. If the return value is zero, it is not + * possible for decode() using that table to return an error--any stream of + * enough bits will resolve to a symbol. If the return value is positive, then + * it is possible for decode() using that table to return an error for received + * codes past the end of the incomplete lengths. + * + * Not used by decode(), but used for error checking, h->count[0] is the number + * of the n symbols not in the code. So n - h->count[0] is the number of + * codes. This is useful for checking for incomplete codes that have more than + * one symbol, which is an error in a dynamic block. + * + * Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS + * This is assured by the construction of the length arrays in dynamic() and + * fixed() and is not verified by construct(). + * + * Format notes: + * + * - Permitted and expected examples of incomplete codes are one of the fixed + * codes and any code with a single symbol which in deflate is coded as one + * bit instead of zero bits. See the format notes for fixed() and dynamic(). + * + * - Within a given code length, the symbols are kept in ascending order for + * the code bits definition. + */ +local int construct(struct huffman *h, const short *length, int n) +{ + int symbol; /* current symbol when stepping through length[] */ + int len; /* current length when stepping through h->count[] */ + int left; /* number of possible codes left of current length */ + short offs[MAXBITS+1]; /* offsets in symbol table for each length */ + + /* count number of codes of each length */ + for (len = 0; len <= MAXBITS; len++) + h->count[len] = 0; + for (symbol = 0; symbol < n; symbol++) + (h->count[length[symbol]])++; /* assumes lengths are within bounds */ + if (h->count[0] == n) /* no codes! */ + return 0; /* complete, but decode() will fail */ + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; /* one possible code of zero length */ + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; /* one more bit, double codes left */ + left -= h->count[len]; /* deduct count from possible codes */ + if (left < 0) + return left; /* over-subscribed--return negative */ + } /* left > 0 means incomplete */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + h->count[len]; + + /* + * put symbols in table sorted by length, by symbol order within each + * length + */ + for (symbol = 0; symbol < n; symbol++) + if (length[symbol] != 0) + h->symbol[offs[length[symbol]]++] = symbol; + + /* return zero for complete set, positive for incomplete set */ + return left; +} + +/* + * Decode literal/length and distance codes until an end-of-block code. + * + * Format notes: + * + * - Compressed data that is after the block type if fixed or after the code + * description if dynamic is a combination of literals and length/distance + * pairs terminated by and end-of-block code. Literals are simply Huffman + * coded bytes. A length/distance pair is a coded length followed by a + * coded distance to represent a string that occurs earlier in the + * uncompressed data that occurs again at the current location. + * + * - Literals, lengths, and the end-of-block code are combined into a single + * code of up to 286 symbols. They are 256 literals (0..255), 29 length + * symbols (257..285), and the end-of-block symbol (256). + * + * - There are 256 possible lengths (3..258), and so 29 symbols are not enough + * to represent all of those. Lengths 3..10 and 258 are in fact represented + * by just a length symbol. Lengths 11..257 are represented as a symbol and + * some number of extra bits that are added as an integer to the base length + * of the length symbol. The number of extra bits is determined by the base + * length symbol. These are in the static arrays below, lens[] for the base + * lengths and lext[] for the corresponding number of extra bits. + * + * - The reason that 258 gets its own symbol is that the longest length is used + * often in highly redundant files. Note that 258 can also be coded as the + * base value 227 plus the maximum extra value of 31. While a good deflate + * should never do this, it is not an error, and should be decoded properly. + * + * - If a length is decoded, including its extra bits if any, then it is + * followed a distance code. There are up to 30 distance symbols. Again + * there are many more possible distances (1..32768), so extra bits are added + * to a base value represented by the symbol. The distances 1..4 get their + * own symbol, but the rest require extra bits. The base distances and + * corresponding number of extra bits are below in the static arrays dist[] + * and dext[]. + * + * - Literal bytes are simply written to the output. A length/distance pair is + * an instruction to copy previously uncompressed bytes to the output. The + * copy is from distance bytes back in the output stream, copying for length + * bytes. + * + * - Distances pointing before the beginning of the output data are not + * permitted. + * + * - Overlapped copies, where the length is greater than the distance, are + * allowed and common. For example, a distance of one and a length of 258 + * simply copies the last byte 258 times. A distance of four and a length of + * twelve copies the last four bytes three times. A simple forward copy + * ignoring whether the length is greater than the distance or not implements + * this correctly. You should not use memcpy() since its behavior is not + * defined for overlapped arrays. You should not use memmove() or bcopy() + * since though their behavior -is- defined for overlapping arrays, it is + * defined to do the wrong thing in this case. + */ +local int codes(struct state *s, + const struct huffman *lencode, + const struct huffman *distcode) +{ + int symbol; /* decoded symbol */ + int len; /* length for copy */ + unsigned dist; /* distance for copy */ + static const short lens[29] = { /* Size base for length codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}; + static const short lext[29] = { /* Extra bits for length codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; + static const short dists[30] = { /* Offset base for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; + static const short dext[30] = { /* Extra bits for distance codes 0..29 */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + /* decode literals and length/distance pairs */ + do { + symbol = decode(s, lencode); + if (symbol < 0) + return symbol; /* invalid symbol */ + if (symbol < 256) { /* literal: symbol is the byte */ + /* write out the literal */ + if (s->out != NIL) { + if (s->outcnt == s->outlen) + return 1; + s->out[s->outcnt] = symbol; + } + s->outcnt++; + } + else if (symbol > 256) { /* length */ + /* get and compute length */ + symbol -= 257; + if (symbol >= 29) + return -10; /* invalid fixed code */ + len = lens[symbol] + bits(s, lext[symbol]); + + /* get and check distance */ + symbol = decode(s, distcode); + if (symbol < 0) + return symbol; /* invalid symbol */ + dist = dists[symbol] + bits(s, dext[symbol]); +#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (dist > s->outcnt) + return -11; /* distance too far back */ +#endif + + /* copy length bytes from distance bytes back */ + if (s->out != NIL) { + if (s->outcnt + len > s->outlen) + return 1; + while (len--) { + s->out[s->outcnt] = +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + dist > s->outcnt ? + 0 : +#endif + s->out[s->outcnt - dist]; + s->outcnt++; + } + } + else + s->outcnt += len; + } + } while (symbol != 256); /* end of block symbol */ + + /* done with a valid fixed or dynamic block */ + return 0; +} + +/* + * Process a fixed codes block. + * + * Format notes: + * + * - This block type can be useful for compressing small amounts of data for + * which the size of the code descriptions in a dynamic block exceeds the + * benefit of custom codes for that block. For fixed codes, no bits are + * spent on code descriptions. Instead the code lengths for literal/length + * codes and distance codes are fixed. The specific lengths for each symbol + * can be seen in the "for" loops below. + * + * - The literal/length code is complete, but has two symbols that are invalid + * and should result in an error if received. This cannot be implemented + * simply as an incomplete code since those two symbols are in the "middle" + * of the code. They are eight bits long and the longest literal/length\ + * code is nine bits. Therefore the code must be constructed with those + * symbols, and the invalid symbols must be detected after decoding. + * + * - The fixed distance codes also have two invalid symbols that should result + * in an error if received. Since all of the distance codes are the same + * length, this can be implemented as an incomplete code. Then the invalid + * codes are detected while decoding. + */ +local int fixed(struct state *s) +{ + static int virgin = 1; + static short lencnt[MAXBITS+1], lensym[FIXLCODES]; + static short distcnt[MAXBITS+1], distsym[MAXDCODES]; + static struct huffman lencode, distcode; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + int symbol; + short lengths[FIXLCODES]; + + /* construct lencode and distcode */ + lencode.count = lencnt; + lencode.symbol = lensym; + distcode.count = distcnt; + distcode.symbol = distsym; + + /* literal/length table */ + for (symbol = 0; symbol < 144; symbol++) + lengths[symbol] = 8; + for (; symbol < 256; symbol++) + lengths[symbol] = 9; + for (; symbol < 280; symbol++) + lengths[symbol] = 7; + for (; symbol < FIXLCODES; symbol++) + lengths[symbol] = 8; + construct(&lencode, lengths, FIXLCODES); + + /* distance table */ + for (symbol = 0; symbol < MAXDCODES; symbol++) + lengths[symbol] = 5; + construct(&distcode, lengths, MAXDCODES); + + /* do this just once */ + virgin = 0; + } + + /* decode data until end-of-block code */ + return codes(s, &lencode, &distcode); +} + +/* + * Process a dynamic codes block. + * + * Format notes: + * + * - A dynamic block starts with a description of the literal/length and + * distance codes for that block. New dynamic blocks allow the compressor to + * rapidly adapt to changing data with new codes optimized for that data. + * + * - The codes used by the deflate format are "canonical", which means that + * the actual bits of the codes are generated in an unambiguous way simply + * from the number of bits in each code. Therefore the code descriptions + * are simply a list of code lengths for each symbol. + * + * - The code lengths are stored in order for the symbols, so lengths are + * provided for each of the literal/length symbols, and for each of the + * distance symbols. + * + * - If a symbol is not used in the block, this is represented by a zero as + * as the code length. This does not mean a zero-length code, but rather + * that no code should be created for this symbol. There is no way in the + * deflate format to represent a zero-length code. + * + * - The maximum number of bits in a code is 15, so the possible lengths for + * any code are 1..15. + * + * - The fact that a length of zero is not permitted for a code has an + * interesting consequence. Normally if only one symbol is used for a given + * code, then in fact that code could be represented with zero bits. However + * in deflate, that code has to be at least one bit. So for example, if + * only a single distance base symbol appears in a block, then it will be + * represented by a single code of length one, in particular one 0 bit. This + * is an incomplete code, since if a 1 bit is received, it has no meaning, + * and should result in an error. So incomplete distance codes of one symbol + * should be permitted, and the receipt of invalid codes should be handled. + * + * - It is also possible to have a single literal/length code, but that code + * must be the end-of-block code, since every dynamic block has one. This + * is not the most efficient way to create an empty block (an empty fixed + * block is fewer bits), but it is allowed by the format. So incomplete + * literal/length codes of one symbol should also be permitted. + * + * - If there are only literal codes and no lengths, then there are no distance + * codes. This is represented by one distance code with zero bits. + * + * - The list of up to 286 length/literal lengths and up to 30 distance lengths + * are themselves compressed using Huffman codes and run-length encoding. In + * the list of code lengths, a 0 symbol means no code, a 1..15 symbol means + * that length, and the symbols 16, 17, and 18 are run-length instructions. + * Each of 16, 17, and 18 are follwed by extra bits to define the length of + * the run. 16 copies the last length 3 to 6 times. 17 represents 3 to 10 + * zero lengths, and 18 represents 11 to 138 zero lengths. Unused symbols + * are common, hence the special coding for zero lengths. + * + * - The symbols for 0..18 are Huffman coded, and so that code must be + * described first. This is simply a sequence of up to 19 three-bit values + * representing no code (0) or the code length for that symbol (1..7). + * + * - A dynamic block starts with three fixed-size counts from which is computed + * the number of literal/length code lengths, the number of distance code + * lengths, and the number of code length code lengths (ok, you come up with + * a better name!) in the code descriptions. For the literal/length and + * distance codes, lengths after those provided are considered zero, i.e. no + * code. The code length code lengths are received in a permuted order (see + * the order[] array below) to make a short code length code length list more + * likely. As it turns out, very short and very long codes are less likely + * to be seen in a dynamic code description, hence what may appear initially + * to be a peculiar ordering. + * + * - Given the number of literal/length code lengths (nlen) and distance code + * lengths (ndist), then they are treated as one long list of nlen + ndist + * code lengths. Therefore run-length coding can and often does cross the + * boundary between the two sets of lengths. + * + * - So to summarize, the code description at the start of a dynamic block is + * three counts for the number of code lengths for the literal/length codes, + * the distance codes, and the code length codes. This is followed by the + * code length code lengths, three bits each. This is used to construct the + * code length code which is used to read the remainder of the lengths. Then + * the literal/length code lengths and distance lengths are read as a single + * set of lengths using the code length codes. Codes are constructed from + * the resulting two sets of lengths, and then finally you can start + * decoding actual compressed data in the block. + * + * - For reference, a "typical" size for the code description in a dynamic + * block is around 80 bytes. + */ +local int dynamic(struct state *s) +{ + int nlen, ndist, ncode; /* number of lengths in descriptor */ + int index; /* index of lengths[] */ + int err; /* construct() return value */ + short lengths[MAXCODES]; /* descriptor code lengths */ + short lencnt[MAXBITS+1], lensym[MAXLCODES]; /* lencode memory */ + short distcnt[MAXBITS+1], distsym[MAXDCODES]; /* distcode memory */ + struct huffman lencode, distcode; /* length and distance codes */ + static const short order[19] = /* permutation of code length codes */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* construct lencode and distcode */ + lencode.count = lencnt; + lencode.symbol = lensym; + distcode.count = distcnt; + distcode.symbol = distsym; + + /* get number of lengths in each table, check lengths */ + nlen = bits(s, 5) + 257; + ndist = bits(s, 5) + 1; + ncode = bits(s, 4) + 4; + if (nlen > MAXLCODES || ndist > MAXDCODES) + return -3; /* bad counts */ + + /* read code length code lengths (really), missing lengths are zero */ + for (index = 0; index < ncode; index++) + lengths[order[index]] = bits(s, 3); + for (; index < 19; index++) + lengths[order[index]] = 0; + + /* build huffman table for code lengths codes (use lencode temporarily) */ + err = construct(&lencode, lengths, 19); + if (err != 0) /* require complete code set here */ + return -4; + + /* read length/literal and distance code length tables */ + index = 0; + while (index < nlen + ndist) { + int symbol; /* decoded value */ + int len; /* last length to repeat */ + + symbol = decode(s, &lencode); + if (symbol < 0) + return symbol; /* invalid symbol */ + if (symbol < 16) /* length in 0..15 */ + lengths[index++] = symbol; + else { /* repeat instruction */ + len = 0; /* assume repeating zeros */ + if (symbol == 16) { /* repeat last length 3..6 times */ + if (index == 0) + return -5; /* no last length! */ + len = lengths[index - 1]; /* last length */ + symbol = 3 + bits(s, 2); + } + else if (symbol == 17) /* repeat zero 3..10 times */ + symbol = 3 + bits(s, 3); + else /* == 18, repeat zero 11..138 times */ + symbol = 11 + bits(s, 7); + if (index + symbol > nlen + ndist) + return -6; /* too many lengths! */ + while (symbol--) /* repeat last or zero symbol times */ + lengths[index++] = len; + } + } + + /* check for end-of-block code -- there better be one! */ + if (lengths[256] == 0) + return -9; + + /* build huffman table for literal/length codes */ + err = construct(&lencode, lengths, nlen); + if (err && (err < 0 || nlen != lencode.count[0] + lencode.count[1])) + return -7; /* incomplete code ok only for single length 1 code */ + + /* build huffman table for distance codes */ + err = construct(&distcode, lengths + nlen, ndist); + if (err && (err < 0 || ndist != distcode.count[0] + distcode.count[1])) + return -8; /* incomplete code ok only for single length 1 code */ + + /* decode data until end-of-block code */ + return codes(s, &lencode, &distcode); +} + +/* + * Inflate source to dest. On return, destlen and sourcelen are updated to the + * size of the uncompressed data and the size of the deflate data respectively. + * On success, the return value of puff() is zero. If there is an error in the + * source data, i.e. it is not in the deflate format, then a negative value is + * returned. If there is not enough input available or there is not enough + * output space, then a positive error is returned. In that case, destlen and + * sourcelen are not updated to facilitate retrying from the beginning with the + * provision of more input data or more output space. In the case of invalid + * inflate data (a negative error), the dest and source pointers are updated to + * facilitate the debugging of deflators. + * + * puff() also has a mode to determine the size of the uncompressed output with + * no output written. For this dest must be (unsigned char *)0. In this case, + * the input value of *destlen is ignored, and on return *destlen is set to the + * size of the uncompressed output. + * + * The return codes are: + * + * 2: available inflate data did not terminate + * 1: output space exhausted before completing inflate + * 0: successful inflate + * -1: invalid block type (type == 3) + * -2: stored block length did not match one's complement + * -3: dynamic block code description: too many length or distance codes + * -4: dynamic block code description: code lengths codes incomplete + * -5: dynamic block code description: repeat lengths with no first length + * -6: dynamic block code description: repeat more than specified lengths + * -7: dynamic block code description: invalid literal/length code lengths + * -8: dynamic block code description: invalid distance code lengths + * -9: dynamic block code description: missing end-of-block code + * -10: invalid literal/length or distance code in fixed or dynamic block + * -11: distance is too far back in fixed or dynamic block + * + * Format notes: + * + * - Three bits are read for each block to determine the kind of block and + * whether or not it is the last block. Then the block is decoded and the + * process repeated if it was not the last block. + * + * - The leftover bits in the last byte of the deflate data after the last + * block (if it was a fixed or dynamic block) are undefined and have no + * expected values to check. + */ +int puff(unsigned char *dest, /* pointer to destination pointer */ + unsigned long *destlen, /* amount of output space */ + const unsigned char *source, /* pointer to source data pointer */ + unsigned long *sourcelen) /* amount of input available */ +{ + struct state s; /* input/output state */ + int last, type; /* block information */ + int err; /* return value */ + + /* initialize output state */ + s.out = dest; + s.outlen = *destlen; /* ignored if dest is NIL */ + s.outcnt = 0; + + /* initialize input state */ + s.in = source; + s.inlen = *sourcelen; + s.incnt = 0; + s.bitbuf = 0; + s.bitcnt = 0; + + /* return if bits() or decode() tries to read past available input */ + if (setjmp(s.env) != 0) /* if came back here via longjmp() */ + err = 2; /* then skip do-loop, return error */ + else { + /* process blocks until last block or error */ + do { + last = bits(&s, 1); /* one if last block */ + type = bits(&s, 2); /* block type 0..3 */ + err = type == 0 ? + stored(&s) : + (type == 1 ? + fixed(&s) : + (type == 2 ? + dynamic(&s) : + -1)); /* type == 3, invalid */ + if (err != 0) + break; /* return with error */ + } while (!last); + } + + /* update the lengths and return */ + if (err <= 0) { + *destlen = s.outcnt; + *sourcelen = s.incnt; + } + return err; +} diff --git a/src/png/zlib/contrib/puff/puff.h b/src/png/zlib/contrib/puff/puff.h new file mode 100644 index 0000000000..e23a245431 --- /dev/null +++ b/src/png/zlib/contrib/puff/puff.h @@ -0,0 +1,35 @@ +/* puff.h + Copyright (C) 2002-2013 Mark Adler, all rights reserved + version 2.3, 21 Jan 2013 + + This software is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Mark Adler madler@alumni.caltech.edu + */ + + +/* + * See puff.c for purpose and usage. + */ +#ifndef NIL +# define NIL ((unsigned char *)0) /* for no output option */ +#endif + +int puff(unsigned char *dest, /* pointer to destination pointer */ + unsigned long *destlen, /* amount of output space */ + const unsigned char *source, /* pointer to source data pointer */ + unsigned long *sourcelen); /* amount of input available */ diff --git a/src/png/zlib/contrib/puff/pufftest.c b/src/png/zlib/contrib/puff/pufftest.c new file mode 100644 index 0000000000..776481488c --- /dev/null +++ b/src/png/zlib/contrib/puff/pufftest.c @@ -0,0 +1,165 @@ +/* + * pufftest.c + * Copyright (C) 2002-2013 Mark Adler + * For conditions of distribution and use, see copyright notice in puff.h + * version 2.3, 21 Jan 2013 + */ + +/* Example of how to use puff(). + + Usage: puff [-w] [-f] [-nnn] file + ... | puff [-w] [-f] [-nnn] + + where file is the input file with deflate data, nnn is the number of bytes + of input to skip before inflating (e.g. to skip a zlib or gzip header), and + -w is used to write the decompressed data to stdout. -f is for coverage + testing, and causes pufftest to fail with not enough output space (-f does + a write like -w, so -w is not required). */ + +#include +#include +#include "puff.h" + +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) +# include +# include +# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif + +#define local static + +/* Return size times approximately the cube root of 2, keeping the result as 1, + 3, or 5 times a power of 2 -- the result is always > size, until the result + is the maximum value of an unsigned long, where it remains. This is useful + to keep reallocations less than ~33% over the actual data. */ +local size_t bythirds(size_t size) +{ + int n; + size_t m; + + m = size; + for (n = 0; m; n++) + m >>= 1; + if (n < 3) + return size + 1; + n -= 3; + m = size >> n; + m += m == 6 ? 2 : 1; + m <<= n; + return m > size ? m : (size_t)(-1); +} + +/* Read the input file *name, or stdin if name is NULL, into allocated memory. + Reallocate to larger buffers until the entire file is read in. Return a + pointer to the allocated data, or NULL if there was a memory allocation + failure. *len is the number of bytes of data read from the input file (even + if load() returns NULL). If the input file was empty or could not be opened + or read, *len is zero. */ +local void *load(const char *name, size_t *len) +{ + size_t size; + void *buf, *swap; + FILE *in; + + *len = 0; + buf = malloc(size = 4096); + if (buf == NULL) + return NULL; + in = name == NULL ? stdin : fopen(name, "rb"); + if (in != NULL) { + for (;;) { + *len += fread((char *)buf + *len, 1, size - *len, in); + if (*len < size) break; + size = bythirds(size); + if (size == *len || (swap = realloc(buf, size)) == NULL) { + free(buf); + buf = NULL; + break; + } + buf = swap; + } + fclose(in); + } + return buf; +} + +int main(int argc, char **argv) +{ + int ret, put = 0, fail = 0; + unsigned skip = 0; + char *arg, *name = NULL; + unsigned char *source = NULL, *dest; + size_t len = 0; + unsigned long sourcelen, destlen; + + /* process arguments */ + while (arg = *++argv, --argc) + if (arg[0] == '-') { + if (arg[1] == 'w' && arg[2] == 0) + put = 1; + else if (arg[1] == 'f' && arg[2] == 0) + fail = 1, put = 1; + else if (arg[1] >= '0' && arg[1] <= '9') + skip = (unsigned)atoi(arg + 1); + else { + fprintf(stderr, "invalid option %s\n", arg); + return 3; + } + } + else if (name != NULL) { + fprintf(stderr, "only one file name allowed\n"); + return 3; + } + else + name = arg; + source = load(name, &len); + if (source == NULL) { + fprintf(stderr, "memory allocation failure\n"); + return 4; + } + if (len == 0) { + fprintf(stderr, "could not read %s, or it was empty\n", + name == NULL ? "" : name); + free(source); + return 3; + } + if (skip >= len) { + fprintf(stderr, "skip request of %d leaves no input\n", skip); + free(source); + return 3; + } + + /* test inflate data with offset skip */ + len -= skip; + sourcelen = (unsigned long)len; + ret = puff(NIL, &destlen, source + skip, &sourcelen); + if (ret) + fprintf(stderr, "puff() failed with return code %d\n", ret); + else { + fprintf(stderr, "puff() succeeded uncompressing %lu bytes\n", destlen); + if (sourcelen < len) fprintf(stderr, "%lu compressed bytes unused\n", + len - sourcelen); + } + + /* if requested, inflate again and write decompressd data to stdout */ + if (put && ret == 0) { + if (fail) + destlen >>= 1; + dest = malloc(destlen); + if (dest == NULL) { + fprintf(stderr, "memory allocation failure\n"); + free(source); + return 4; + } + puff(dest, &destlen, source + skip, &sourcelen); + SET_BINARY_MODE(stdout); + fwrite(dest, 1, destlen, stdout); + free(dest); + } + + /* clean up */ + free(source); + return ret; +} diff --git a/src/png/zlib/contrib/puff/zeros.raw b/src/png/zlib/contrib/puff/zeros.raw new file mode 100644 index 0000000000..0a90e76b30 Binary files /dev/null and b/src/png/zlib/contrib/puff/zeros.raw differ diff --git a/src/png/zlib/contrib/testzlib/testzlib.c b/src/png/zlib/contrib/testzlib/testzlib.c new file mode 100644 index 0000000000..8626c92ad1 --- /dev/null +++ b/src/png/zlib/contrib/testzlib/testzlib.c @@ -0,0 +1,275 @@ +#include +#include +#include + +#include "zlib.h" + + +void MyDoMinus64(LARGE_INTEGER *R,LARGE_INTEGER A,LARGE_INTEGER B) +{ + R->HighPart = A.HighPart - B.HighPart; + if (A.LowPart >= B.LowPart) + R->LowPart = A.LowPart - B.LowPart; + else + { + R->LowPart = A.LowPart - B.LowPart; + R->HighPart --; + } +} + +#ifdef _M_X64 +// see http://msdn2.microsoft.com/library/twchhe95(en-us,vs.80).aspx for __rdtsc +unsigned __int64 __rdtsc(void); +void BeginCountRdtsc(LARGE_INTEGER * pbeginTime64) +{ + // printf("rdtsc = %I64x\n",__rdtsc()); + pbeginTime64->QuadPart=__rdtsc(); +} + +LARGE_INTEGER GetResRdtsc(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf) +{ + LARGE_INTEGER LIres; + unsigned _int64 res=__rdtsc()-((unsigned _int64)(beginTime64.QuadPart)); + LIres.QuadPart=res; + // printf("rdtsc = %I64x\n",__rdtsc()); + return LIres; +} +#else +#ifdef _M_IX86 +void myGetRDTSC32(LARGE_INTEGER * pbeginTime64) +{ + DWORD dwEdx,dwEax; + _asm + { + rdtsc + mov dwEax,eax + mov dwEdx,edx + } + pbeginTime64->LowPart=dwEax; + pbeginTime64->HighPart=dwEdx; +} + +void BeginCountRdtsc(LARGE_INTEGER * pbeginTime64) +{ + myGetRDTSC32(pbeginTime64); +} + +LARGE_INTEGER GetResRdtsc(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf) +{ + LARGE_INTEGER LIres,endTime64; + myGetRDTSC32(&endTime64); + + LIres.LowPart=LIres.HighPart=0; + MyDoMinus64(&LIres,endTime64,beginTime64); + return LIres; +} +#else +void myGetRDTSC32(LARGE_INTEGER * pbeginTime64) +{ +} + +void BeginCountRdtsc(LARGE_INTEGER * pbeginTime64) +{ +} + +LARGE_INTEGER GetResRdtsc(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf) +{ + LARGE_INTEGER lr; + lr.QuadPart=0; + return lr; +} +#endif +#endif + +void BeginCountPerfCounter(LARGE_INTEGER * pbeginTime64,BOOL fComputeTimeQueryPerf) +{ + if ((!fComputeTimeQueryPerf) || (!QueryPerformanceCounter(pbeginTime64))) + { + pbeginTime64->LowPart = GetTickCount(); + pbeginTime64->HighPart = 0; + } +} + +DWORD GetMsecSincePerfCounter(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf) +{ + LARGE_INTEGER endTime64,ticksPerSecond,ticks; + DWORDLONG ticksShifted,tickSecShifted; + DWORD dwLog=16+0; + DWORD dwRet; + if ((!fComputeTimeQueryPerf) || (!QueryPerformanceCounter(&endTime64))) + dwRet = (GetTickCount() - beginTime64.LowPart)*1; + else + { + MyDoMinus64(&ticks,endTime64,beginTime64); + QueryPerformanceFrequency(&ticksPerSecond); + + + { + ticksShifted = Int64ShrlMod32(*(DWORDLONG*)&ticks,dwLog); + tickSecShifted = Int64ShrlMod32(*(DWORDLONG*)&ticksPerSecond,dwLog); + + } + + dwRet = (DWORD)((((DWORD)ticksShifted)*1000)/(DWORD)(tickSecShifted)); + dwRet *=1; + } + return dwRet; +} + +int ReadFileMemory(const char* filename,long* plFileSize,unsigned char** pFilePtr) +{ + FILE* stream; + unsigned char* ptr; + int retVal=1; + stream=fopen(filename, "rb"); + if (stream==NULL) + return 0; + + fseek(stream,0,SEEK_END); + + *plFileSize=ftell(stream); + fseek(stream,0,SEEK_SET); + ptr=malloc((*plFileSize)+1); + if (ptr==NULL) + retVal=0; + else + { + if (fread(ptr, 1, *plFileSize,stream) != (*plFileSize)) + retVal=0; + } + fclose(stream); + *pFilePtr=ptr; + return retVal; +} + +int main(int argc, char *argv[]) +{ + int BlockSizeCompress=0x8000; + int BlockSizeUncompress=0x8000; + int cprLevel=Z_DEFAULT_COMPRESSION ; + long lFileSize; + unsigned char* FilePtr; + long lBufferSizeCpr; + long lBufferSizeUncpr; + long lCompressedSize=0; + unsigned char* CprPtr; + unsigned char* UncprPtr; + long lSizeCpr,lSizeUncpr; + DWORD dwGetTick,dwMsecQP; + LARGE_INTEGER li_qp,li_rdtsc,dwResRdtsc; + + if (argc<=1) + { + printf("run TestZlib [BlockSizeCompress] [BlockSizeUncompress] [compres. level]\n"); + return 0; + } + + if (ReadFileMemory(argv[1],&lFileSize,&FilePtr)==0) + { + printf("error reading %s\n",argv[1]); + return 1; + } + else printf("file %s read, %u bytes\n",argv[1],lFileSize); + + if (argc>=3) + BlockSizeCompress=atol(argv[2]); + + if (argc>=4) + BlockSizeUncompress=atol(argv[3]); + + if (argc>=5) + cprLevel=(int)atol(argv[4]); + + lBufferSizeCpr = lFileSize + (lFileSize/0x10) + 0x200; + lBufferSizeUncpr = lBufferSizeCpr; + + CprPtr=(unsigned char*)malloc(lBufferSizeCpr + BlockSizeCompress); + + BeginCountPerfCounter(&li_qp,TRUE); + dwGetTick=GetTickCount(); + BeginCountRdtsc(&li_rdtsc); + { + z_stream zcpr; + int ret=Z_OK; + long lOrigToDo = lFileSize; + long lOrigDone = 0; + int step=0; + memset(&zcpr,0,sizeof(z_stream)); + deflateInit(&zcpr,cprLevel); + + zcpr.next_in = FilePtr; + zcpr.next_out = CprPtr; + + + do + { + long all_read_before = zcpr.total_in; + zcpr.avail_in = min(lOrigToDo,BlockSizeCompress); + zcpr.avail_out = BlockSizeCompress; + ret=deflate(&zcpr,(zcpr.avail_in==lOrigToDo) ? Z_FINISH : Z_SYNC_FLUSH); + lOrigDone += (zcpr.total_in-all_read_before); + lOrigToDo -= (zcpr.total_in-all_read_before); + step++; + } while (ret==Z_OK); + + lSizeCpr=zcpr.total_out; + deflateEnd(&zcpr); + dwGetTick=GetTickCount()-dwGetTick; + dwMsecQP=GetMsecSincePerfCounter(li_qp,TRUE); + dwResRdtsc=GetResRdtsc(li_rdtsc,TRUE); + printf("total compress size = %u, in %u step\n",lSizeCpr,step); + printf("time = %u msec = %f sec\n",dwGetTick,dwGetTick/(double)1000.); + printf("defcpr time QP = %u msec = %f sec\n",dwMsecQP,dwMsecQP/(double)1000.); + printf("defcpr result rdtsc = %I64x\n\n",dwResRdtsc.QuadPart); + } + + CprPtr=(unsigned char*)realloc(CprPtr,lSizeCpr); + UncprPtr=(unsigned char*)malloc(lBufferSizeUncpr + BlockSizeUncompress); + + BeginCountPerfCounter(&li_qp,TRUE); + dwGetTick=GetTickCount(); + BeginCountRdtsc(&li_rdtsc); + { + z_stream zcpr; + int ret=Z_OK; + long lOrigToDo = lSizeCpr; + long lOrigDone = 0; + int step=0; + memset(&zcpr,0,sizeof(z_stream)); + inflateInit(&zcpr); + + zcpr.next_in = CprPtr; + zcpr.next_out = UncprPtr; + + + do + { + long all_read_before = zcpr.total_in; + zcpr.avail_in = min(lOrigToDo,BlockSizeUncompress); + zcpr.avail_out = BlockSizeUncompress; + ret=inflate(&zcpr,Z_SYNC_FLUSH); + lOrigDone += (zcpr.total_in-all_read_before); + lOrigToDo -= (zcpr.total_in-all_read_before); + step++; + } while (ret==Z_OK); + + lSizeUncpr=zcpr.total_out; + inflateEnd(&zcpr); + dwGetTick=GetTickCount()-dwGetTick; + dwMsecQP=GetMsecSincePerfCounter(li_qp,TRUE); + dwResRdtsc=GetResRdtsc(li_rdtsc,TRUE); + printf("total uncompress size = %u, in %u step\n",lSizeUncpr,step); + printf("time = %u msec = %f sec\n",dwGetTick,dwGetTick/(double)1000.); + printf("uncpr time QP = %u msec = %f sec\n",dwMsecQP,dwMsecQP/(double)1000.); + printf("uncpr result rdtsc = %I64x\n\n",dwResRdtsc.QuadPart); + } + + if (lSizeUncpr==lFileSize) + { + if (memcmp(FilePtr,UncprPtr,lFileSize)==0) + printf("compare ok\n"); + + } + + return 0; +} diff --git a/src/png/zlib/contrib/testzlib/testzlib.txt b/src/png/zlib/contrib/testzlib/testzlib.txt new file mode 100644 index 0000000000..e508bb22ff --- /dev/null +++ b/src/png/zlib/contrib/testzlib/testzlib.txt @@ -0,0 +1,10 @@ +To build testzLib with Visual Studio 2005: + +copy to a directory file from : +- root of zLib tree +- contrib/testzlib +- contrib/masmx86 +- contrib/masmx64 +- contrib/vstudio/vc7 + +and open testzlib8.sln \ No newline at end of file diff --git a/src/png/zlib/contrib/untgz/Makefile b/src/png/zlib/contrib/untgz/Makefile new file mode 100644 index 0000000000..b54266fba2 --- /dev/null +++ b/src/png/zlib/contrib/untgz/Makefile @@ -0,0 +1,14 @@ +CC=cc +CFLAGS=-g + +untgz: untgz.o ../../libz.a + $(CC) $(CFLAGS) -o untgz untgz.o -L../.. -lz + +untgz.o: untgz.c ../../zlib.h + $(CC) $(CFLAGS) -c -I../.. untgz.c + +../../libz.a: + cd ../..; ./configure; make + +clean: + rm -f untgz untgz.o *~ diff --git a/src/png/zlib/contrib/untgz/Makefile.msc b/src/png/zlib/contrib/untgz/Makefile.msc new file mode 100644 index 0000000000..77b8602213 --- /dev/null +++ b/src/png/zlib/contrib/untgz/Makefile.msc @@ -0,0 +1,17 @@ +CC=cl +CFLAGS=-MD + +untgz.exe: untgz.obj ..\..\zlib.lib + $(CC) $(CFLAGS) untgz.obj ..\..\zlib.lib + +untgz.obj: untgz.c ..\..\zlib.h + $(CC) $(CFLAGS) -c -I..\.. untgz.c + +..\..\zlib.lib: + cd ..\.. + $(MAKE) -f win32\makefile.msc + cd contrib\untgz + +clean: + -del untgz.obj + -del untgz.exe diff --git a/src/png/zlib/contrib/untgz/untgz.c b/src/png/zlib/contrib/untgz/untgz.c new file mode 100644 index 0000000000..2c391e5986 --- /dev/null +++ b/src/png/zlib/contrib/untgz/untgz.c @@ -0,0 +1,674 @@ +/* + * untgz.c -- Display contents and extract files from a gzip'd TAR file + * + * written by Pedro A. Aranda Gutierrez + * adaptation to Unix by Jean-loup Gailly + * various fixes by Cosmin Truta + */ + +#include +#include +#include +#include +#include + +#include "zlib.h" + +#ifdef unix +# include +#else +# include +# include +#endif + +#ifdef WIN32 +#include +# ifndef F_OK +# define F_OK 0 +# endif +# define mkdir(dirname,mode) _mkdir(dirname) +# ifdef _MSC_VER +# define access(path,mode) _access(path,mode) +# define chmod(path,mode) _chmod(path,mode) +# define strdup(str) _strdup(str) +# endif +#else +# include +#endif + + +/* values used in typeflag field */ + +#define REGTYPE '0' /* regular file */ +#define AREGTYPE '\0' /* regular file */ +#define LNKTYPE '1' /* link */ +#define SYMTYPE '2' /* reserved */ +#define CHRTYPE '3' /* character special */ +#define BLKTYPE '4' /* block special */ +#define DIRTYPE '5' /* directory */ +#define FIFOTYPE '6' /* FIFO special */ +#define CONTTYPE '7' /* reserved */ + +/* GNU tar extensions */ + +#define GNUTYPE_DUMPDIR 'D' /* file names from dumped directory */ +#define GNUTYPE_LONGLINK 'K' /* long link name */ +#define GNUTYPE_LONGNAME 'L' /* long file name */ +#define GNUTYPE_MULTIVOL 'M' /* continuation of file from another volume */ +#define GNUTYPE_NAMES 'N' /* file name that does not fit into main hdr */ +#define GNUTYPE_SPARSE 'S' /* sparse file */ +#define GNUTYPE_VOLHDR 'V' /* tape/volume header */ + + +/* tar header */ + +#define BLOCKSIZE 512 +#define SHORTNAMESIZE 100 + +struct tar_header +{ /* byte offset */ + char name[100]; /* 0 */ + char mode[8]; /* 100 */ + char uid[8]; /* 108 */ + char gid[8]; /* 116 */ + char size[12]; /* 124 */ + char mtime[12]; /* 136 */ + char chksum[8]; /* 148 */ + char typeflag; /* 156 */ + char linkname[100]; /* 157 */ + char magic[6]; /* 257 */ + char version[2]; /* 263 */ + char uname[32]; /* 265 */ + char gname[32]; /* 297 */ + char devmajor[8]; /* 329 */ + char devminor[8]; /* 337 */ + char prefix[155]; /* 345 */ + /* 500 */ +}; + +union tar_buffer +{ + char buffer[BLOCKSIZE]; + struct tar_header header; +}; + +struct attr_item +{ + struct attr_item *next; + char *fname; + int mode; + time_t time; +}; + +enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID }; + +char *TGZfname OF((const char *)); +void TGZnotfound OF((const char *)); + +int getoct OF((char *, int)); +char *strtime OF((time_t *)); +int setfiletime OF((char *, time_t)); +void push_attr OF((struct attr_item **, char *, int, time_t)); +void restore_attr OF((struct attr_item **)); + +int ExprMatch OF((char *, char *)); + +int makedir OF((char *)); +int matchname OF((int, int, char **, char *)); + +void error OF((const char *)); +int tar OF((gzFile, int, int, int, char **)); + +void help OF((int)); +int main OF((int, char **)); + +char *prog; + +const char *TGZsuffix[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL }; + +/* return the file name of the TGZ archive */ +/* or NULL if it does not exist */ + +char *TGZfname (const char *arcname) +{ + static char buffer[1024]; + int origlen,i; + + strcpy(buffer,arcname); + origlen = strlen(buffer); + + for (i=0; TGZsuffix[i]; i++) + { + strcpy(buffer+origlen,TGZsuffix[i]); + if (access(buffer,F_OK) == 0) + return buffer; + } + return NULL; +} + + +/* error message for the filename */ + +void TGZnotfound (const char *arcname) +{ + int i; + + fprintf(stderr,"%s: Couldn't find ",prog); + for (i=0;TGZsuffix[i];i++) + fprintf(stderr,(TGZsuffix[i+1]) ? "%s%s, " : "or %s%s\n", + arcname, + TGZsuffix[i]); + exit(1); +} + + +/* convert octal digits to int */ +/* on error return -1 */ + +int getoct (char *p,int width) +{ + int result = 0; + char c; + + while (width--) + { + c = *p++; + if (c == 0) + break; + if (c == ' ') + continue; + if (c < '0' || c > '7') + return -1; + result = result * 8 + (c - '0'); + } + return result; +} + + +/* convert time_t to string */ +/* use the "YYYY/MM/DD hh:mm:ss" format */ + +char *strtime (time_t *t) +{ + struct tm *local; + static char result[32]; + + local = localtime(t); + sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d", + local->tm_year+1900, local->tm_mon+1, local->tm_mday, + local->tm_hour, local->tm_min, local->tm_sec); + return result; +} + + +/* set file time */ + +int setfiletime (char *fname,time_t ftime) +{ +#ifdef WIN32 + static int isWinNT = -1; + SYSTEMTIME st; + FILETIME locft, modft; + struct tm *loctm; + HANDLE hFile; + int result; + + loctm = localtime(&ftime); + if (loctm == NULL) + return -1; + + st.wYear = (WORD)loctm->tm_year + 1900; + st.wMonth = (WORD)loctm->tm_mon + 1; + st.wDayOfWeek = (WORD)loctm->tm_wday; + st.wDay = (WORD)loctm->tm_mday; + st.wHour = (WORD)loctm->tm_hour; + st.wMinute = (WORD)loctm->tm_min; + st.wSecond = (WORD)loctm->tm_sec; + st.wMilliseconds = 0; + if (!SystemTimeToFileTime(&st, &locft) || + !LocalFileTimeToFileTime(&locft, &modft)) + return -1; + + if (isWinNT < 0) + isWinNT = (GetVersion() < 0x80000000) ? 1 : 0; + hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + (isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0), + NULL); + if (hFile == INVALID_HANDLE_VALUE) + return -1; + result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1; + CloseHandle(hFile); + return result; +#else + struct utimbuf settime; + + settime.actime = settime.modtime = ftime; + return utime(fname,&settime); +#endif +} + + +/* push file attributes */ + +void push_attr(struct attr_item **list,char *fname,int mode,time_t time) +{ + struct attr_item *item; + + item = (struct attr_item *)malloc(sizeof(struct attr_item)); + if (item == NULL) + error("Out of memory"); + item->fname = strdup(fname); + item->mode = mode; + item->time = time; + item->next = *list; + *list = item; +} + + +/* restore file attributes */ + +void restore_attr(struct attr_item **list) +{ + struct attr_item *item, *prev; + + for (item = *list; item != NULL; ) + { + setfiletime(item->fname,item->time); + chmod(item->fname,item->mode); + prev = item; + item = item->next; + free(prev); + } + *list = NULL; +} + + +/* match regular expression */ + +#define ISSPECIAL(c) (((c) == '*') || ((c) == '/')) + +int ExprMatch (char *string,char *expr) +{ + while (1) + { + if (ISSPECIAL(*expr)) + { + if (*expr == '/') + { + if (*string != '\\' && *string != '/') + return 0; + string ++; expr++; + } + else if (*expr == '*') + { + if (*expr ++ == 0) + return 1; + while (*++string != *expr) + if (*string == 0) + return 0; + } + } + else + { + if (*string != *expr) + return 0; + if (*expr++ == 0) + return 1; + string++; + } + } +} + + +/* recursive mkdir */ +/* abort on ENOENT; ignore other errors like "directory already exists" */ +/* return 1 if OK */ +/* 0 on error */ + +int makedir (char *newdir) +{ + char *buffer = strdup(newdir); + char *p; + int len = strlen(buffer); + + if (len <= 0) { + free(buffer); + return 0; + } + if (buffer[len-1] == '/') { + buffer[len-1] = '\0'; + } + if (mkdir(buffer, 0755) == 0) + { + free(buffer); + return 1; + } + + p = buffer+1; + while (1) + { + char hold; + + while(*p && *p != '\\' && *p != '/') + p++; + hold = *p; + *p = 0; + if ((mkdir(buffer, 0755) == -1) && (errno == ENOENT)) + { + fprintf(stderr,"%s: Couldn't create directory %s\n",prog,buffer); + free(buffer); + return 0; + } + if (hold == 0) + break; + *p++ = hold; + } + free(buffer); + return 1; +} + + +int matchname (int arg,int argc,char **argv,char *fname) +{ + if (arg == argc) /* no arguments given (untgz tgzarchive) */ + return 1; + + while (arg < argc) + if (ExprMatch(fname,argv[arg++])) + return 1; + + return 0; /* ignore this for the moment being */ +} + + +/* tar file list or extract */ + +int tar (gzFile in,int action,int arg,int argc,char **argv) +{ + union tar_buffer buffer; + int len; + int err; + int getheader = 1; + int remaining = 0; + FILE *outfile = NULL; + char fname[BLOCKSIZE]; + int tarmode; + time_t tartime; + struct attr_item *attributes = NULL; + + if (action == TGZ_LIST) + printf(" date time size file\n" + " ---------- -------- --------- -------------------------------------\n"); + while (1) + { + len = gzread(in, &buffer, BLOCKSIZE); + if (len < 0) + error(gzerror(in, &err)); + /* + * Always expect complete blocks to process + * the tar information. + */ + if (len != BLOCKSIZE) + { + action = TGZ_INVALID; /* force error exit */ + remaining = 0; /* force I/O cleanup */ + } + + /* + * If we have to get a tar header + */ + if (getheader >= 1) + { + /* + * if we met the end of the tar + * or the end-of-tar block, + * we are done + */ + if (len == 0 || buffer.header.name[0] == 0) + break; + + tarmode = getoct(buffer.header.mode,8); + tartime = (time_t)getoct(buffer.header.mtime,12); + if (tarmode == -1 || tartime == (time_t)-1) + { + buffer.header.name[0] = 0; + action = TGZ_INVALID; + } + + if (getheader == 1) + { + strncpy(fname,buffer.header.name,SHORTNAMESIZE); + if (fname[SHORTNAMESIZE-1] != 0) + fname[SHORTNAMESIZE] = 0; + } + else + { + /* + * The file name is longer than SHORTNAMESIZE + */ + if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0) + error("bad long name"); + getheader = 1; + } + + /* + * Act according to the type flag + */ + switch (buffer.header.typeflag) + { + case DIRTYPE: + if (action == TGZ_LIST) + printf(" %s %s\n",strtime(&tartime),fname); + if (action == TGZ_EXTRACT) + { + makedir(fname); + push_attr(&attributes,fname,tarmode,tartime); + } + break; + case REGTYPE: + case AREGTYPE: + remaining = getoct(buffer.header.size,12); + if (remaining == -1) + { + action = TGZ_INVALID; + break; + } + if (action == TGZ_LIST) + printf(" %s %9d %s\n",strtime(&tartime),remaining,fname); + else if (action == TGZ_EXTRACT) + { + if (matchname(arg,argc,argv,fname)) + { + outfile = fopen(fname,"wb"); + if (outfile == NULL) { + /* try creating directory */ + char *p = strrchr(fname, '/'); + if (p != NULL) { + *p = '\0'; + makedir(fname); + *p = '/'; + outfile = fopen(fname,"wb"); + } + } + if (outfile != NULL) + printf("Extracting %s\n",fname); + else + fprintf(stderr, "%s: Couldn't create %s",prog,fname); + } + else + outfile = NULL; + } + getheader = 0; + break; + case GNUTYPE_LONGLINK: + case GNUTYPE_LONGNAME: + remaining = getoct(buffer.header.size,12); + if (remaining < 0 || remaining >= BLOCKSIZE) + { + action = TGZ_INVALID; + break; + } + len = gzread(in, fname, BLOCKSIZE); + if (len < 0) + error(gzerror(in, &err)); + if (fname[BLOCKSIZE-1] != 0 || (int)strlen(fname) > remaining) + { + action = TGZ_INVALID; + break; + } + getheader = 2; + break; + default: + if (action == TGZ_LIST) + printf(" %s <---> %s\n",strtime(&tartime),fname); + break; + } + } + else + { + unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining; + + if (outfile != NULL) + { + if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes) + { + fprintf(stderr, + "%s: Error writing %s -- skipping\n",prog,fname); + fclose(outfile); + outfile = NULL; + remove(fname); + } + } + remaining -= bytes; + } + + if (remaining == 0) + { + getheader = 1; + if (outfile != NULL) + { + fclose(outfile); + outfile = NULL; + if (action != TGZ_INVALID) + push_attr(&attributes,fname,tarmode,tartime); + } + } + + /* + * Abandon if errors are found + */ + if (action == TGZ_INVALID) + { + error("broken archive"); + break; + } + } + + /* + * Restore file modes and time stamps + */ + restore_attr(&attributes); + + if (gzclose(in) != Z_OK) + error("failed gzclose"); + + return 0; +} + + +/* ============================================================ */ + +void help(int exitval) +{ + printf("untgz version 0.2.1\n" + " using zlib version %s\n\n", + zlibVersion()); + printf("Usage: untgz file.tgz extract all files\n" + " untgz file.tgz fname ... extract selected files\n" + " untgz -l file.tgz list archive contents\n" + " untgz -h display this help\n"); + exit(exitval); +} + +void error(const char *msg) +{ + fprintf(stderr, "%s: %s\n", prog, msg); + exit(1); +} + + +/* ============================================================ */ + +#if defined(WIN32) && defined(__GNUC__) +int _CRT_glob = 0; /* disable argument globbing in MinGW */ +#endif + +int main(int argc,char **argv) +{ + int action = TGZ_EXTRACT; + int arg = 1; + char *TGZfile; + gzFile *f; + + prog = strrchr(argv[0],'\\'); + if (prog == NULL) + { + prog = strrchr(argv[0],'/'); + if (prog == NULL) + { + prog = strrchr(argv[0],':'); + if (prog == NULL) + prog = argv[0]; + else + prog++; + } + else + prog++; + } + else + prog++; + + if (argc == 1) + help(0); + + if (strcmp(argv[arg],"-l") == 0) + { + action = TGZ_LIST; + if (argc == ++arg) + help(0); + } + else if (strcmp(argv[arg],"-h") == 0) + { + help(0); + } + + if ((TGZfile = TGZfname(argv[arg])) == NULL) + TGZnotfound(argv[arg]); + + ++arg; + if ((action == TGZ_LIST) && (arg != argc)) + help(1); + +/* + * Process the TGZ file + */ + switch(action) + { + case TGZ_LIST: + case TGZ_EXTRACT: + f = gzopen(TGZfile,"rb"); + if (f == NULL) + { + fprintf(stderr,"%s: Couldn't gzopen %s\n",prog,TGZfile); + return 1; + } + exit(tar(f, action, arg, argc, argv)); + break; + + default: + error("Unknown option"); + exit(1); + } + + return 0; +} diff --git a/src/png/zlib/contrib/vstudio/readme.txt b/src/png/zlib/contrib/vstudio/readme.txt new file mode 100644 index 0000000000..48cccc0d2a --- /dev/null +++ b/src/png/zlib/contrib/vstudio/readme.txt @@ -0,0 +1,78 @@ +Building instructions for the DLL versions of Zlib 1.2.11 +======================================================== + +This directory contains projects that build zlib and minizip using +Microsoft Visual C++ 9.0/10.0. + +You don't need to build these projects yourself. You can download the +binaries from: + http://www.winimage.com/zLibDll + +More information can be found at this site. + + + + + +Build instructions for Visual Studio 2008 (32 bits or 64 bits) +-------------------------------------------------------------- +- Decompress current zlib, including all contrib/* files +- Compile assembly code (with Visual Studio Command Prompt) by running: + bld_ml64.bat (in contrib\masmx64) + bld_ml32.bat (in contrib\masmx86) +- Open contrib\vstudio\vc9\zlibvc.sln with Microsoft Visual C++ 2008 +- Or run: vcbuild /rebuild contrib\vstudio\vc9\zlibvc.sln "Release|Win32" + +Build instructions for Visual Studio 2010 (32 bits or 64 bits) +-------------------------------------------------------------- +- Decompress current zlib, including all contrib/* files +- Open contrib\vstudio\vc10\zlibvc.sln with Microsoft Visual C++ 2010 + +Build instructions for Visual Studio 2012 (32 bits or 64 bits) +-------------------------------------------------------------- +- Decompress current zlib, including all contrib/* files +- Open contrib\vstudio\vc11\zlibvc.sln with Microsoft Visual C++ 2012 + +Build instructions for Visual Studio 2013 (32 bits or 64 bits) +-------------------------------------------------------------- +- Decompress current zlib, including all contrib/* files +- Open contrib\vstudio\vc12\zlibvc.sln with Microsoft Visual C++ 2013 + +Build instructions for Visual Studio 2015 (32 bits or 64 bits) +-------------------------------------------------------------- +- Decompress current zlib, including all contrib/* files +- Open contrib\vstudio\vc14\zlibvc.sln with Microsoft Visual C++ 2015 + + +Important +--------- +- To use zlibwapi.dll in your application, you must define the + macro ZLIB_WINAPI when compiling your application's source files. + + +Additional notes +---------------- +- This DLL, named zlibwapi.dll, is compatible to the old zlib.dll built + by Gilles Vollant from the zlib 1.1.x sources, and distributed at + http://www.winimage.com/zLibDll + It uses the WINAPI calling convention for the exported functions, and + includes the minizip functionality. If your application needs that + particular build of zlib.dll, you can rename zlibwapi.dll to zlib.dll. + +- The new DLL was renamed because there exist several incompatible + versions of zlib.dll on the Internet. + +- There is also an official DLL build of zlib, named zlib1.dll. This one + is exporting the functions using the CDECL convention. See the file + win32\DLL_FAQ.txt found in this zlib distribution. + +- There used to be a ZLIB_DLL macro in zlib 1.1.x, but now this symbol + has a slightly different effect. To avoid compatibility problems, do + not define it here. + + +Gilles Vollant +info@winimage.com + +Visual Studio 2013 and 2015 Projects from Sean Hunt +seandhunt_7@yahoo.com diff --git a/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj b/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj new file mode 100644 index 0000000000..1b3624215a --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj @@ -0,0 +1,310 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {C52F9E7B-498A-42BE-8DB4-85A15694382A} + Win32Proj + + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\MiniUnzip$(Configuration)\ + x86\MiniUnzip$(Configuration)\Tmp\ + true + false + x86\MiniUnzip$(Configuration)\ + x86\MiniUnzip$(Configuration)\Tmp\ + false + false + x64\MiniUnzip$(Configuration)\ + x64\MiniUnzip$(Configuration)\Tmp\ + true + false + ia64\MiniUnzip$(Configuration)\ + ia64\MiniUnzip$(Configuration)\Tmp\ + true + false + x64\MiniUnzip$(Configuration)\ + x64\MiniUnzip$(Configuration)\Tmp\ + false + false + ia64\MiniUnzip$(Configuration)\ + ia64\MiniUnzip$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebug + false + + + $(IntDir) + Level3 + EditAndContinue + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters new file mode 100644 index 0000000000..0bd12210ca --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {048af943-022b-4db6-beeb-a54c34774ee2} + cpp;c;cxx;def;odl;idl;hpj;bat;asm + + + {c1d600d2-888f-4aea-b73e-8b0dd9befa0c} + h;hpp;hxx;hm;inl;inc + + + {0844199a-966b-4f19-81db-1e0125e141b9} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + Source Files + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj b/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj new file mode 100644 index 0000000000..ccd3651df6 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj @@ -0,0 +1,307 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B} + Win32Proj + + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\MiniZip$(Configuration)\ + x86\MiniZip$(Configuration)\Tmp\ + true + false + x86\MiniZip$(Configuration)\ + x86\MiniZip$(Configuration)\Tmp\ + false + x64\$(Configuration)\ + x64\$(Configuration)\ + true + false + ia64\$(Configuration)\ + ia64\$(Configuration)\ + true + false + x64\$(Configuration)\ + x64\$(Configuration)\ + false + ia64\$(Configuration)\ + ia64\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebug + false + + + $(IntDir) + Level3 + EditAndContinue + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters new file mode 100644 index 0000000000..7076d76ff6 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {c0419b40-bf50-40da-b153-ff74215b79de} + cpp;c;cxx;def;odl;idl;hpj;bat;asm + + + {bb87b070-735b-478e-92ce-7383abb2f36c} + h;hpp;hxx;hm;inl;inc + + + {f46ab6a6-548f-43cb-ae96-681abb5bd5db} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + Source Files + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj b/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj new file mode 100644 index 0000000000..476b8ea453 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj @@ -0,0 +1,420 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B} + testzlib + Win32Proj + + + + Application + MultiByte + true + + + Application + MultiByte + true + + + Application + MultiByte + + + Application + MultiByte + true + + + Application + MultiByte + true + + + Application + MultiByte + + + Application + true + + + Application + true + + + Application + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + true + false + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + false + false + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + false + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + true + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + false + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebug + false + + + AssemblyAndSourceCode + $(IntDir) + Level3 + EditAndContinue + + + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)testzlib.exe + true + $(OutDir)testzlib.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)testzlib.exe + true + Console + true + true + false + + + MachineX86 + + + + + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDebugDLL + false + $(IntDir) + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + + + + + Itanium + + + Disabled + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + AssemblyAndSourceCode + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + $(OutDir)testzlib.pdb + Console + MachineIA64 + + + + + ..\..\..;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDLL + false + $(IntDir) + + + %(AdditionalDependencies) + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + MachineIA64 + + + + + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDLL + false + $(IntDir) + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + MachineIA64 + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters new file mode 100644 index 0000000000..3276491039 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters @@ -0,0 +1,58 @@ + + + + + {c1f6a2e3-5da5-4955-8653-310d3efe05a9} + cpp;c;cxx;def;odl;idl;hpj;bat;asm + + + {c2aaffdc-2c95-4d6f-8466-4bec5890af2c} + h;hpp;hxx;hm;inl;inc + + + {c274fe07-05f2-461c-964b-f6341e4e7eb5} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj b/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj new file mode 100644 index 0000000000..8e38876fa3 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj @@ -0,0 +1,310 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {C52F9E7B-498A-42BE-8DB4-85A15694366A} + Win32Proj + + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\TestZlibDll$(Configuration)\ + x86\TestZlibDll$(Configuration)\Tmp\ + true + false + x86\TestZlibDll$(Configuration)\ + x86\TestZlibDll$(Configuration)\Tmp\ + false + false + x64\TestZlibDll$(Configuration)\ + x64\TestZlibDll$(Configuration)\Tmp\ + true + false + ia64\TestZlibDll$(Configuration)\ + ia64\TestZlibDll$(Configuration)\Tmp\ + true + false + x64\TestZlibDll$(Configuration)\ + x64\TestZlibDll$(Configuration)\Tmp\ + false + false + ia64\TestZlibDll$(Configuration)\ + ia64\TestZlibDll$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebug + false + + + $(IntDir) + Level3 + EditAndContinue + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters new file mode 100644 index 0000000000..ab87f09f47 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {fa61a89f-93fc-4c89-b29e-36224b7592f4} + cpp;c;cxx;def;odl;idl;hpj;bat;asm + + + {d4b85da0-2ba2-4934-b57f-e2584e3848ee} + h;hpp;hxx;hm;inl;inc + + + {e573e075-00bd-4a7d-bd67-a8cc9bfc5aca} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + Source Files + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/zlib.rc b/src/png/zlib/contrib/vstudio/vc10/zlib.rc new file mode 100644 index 0000000000..c4e4b016e9 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/zlib.rc @@ -0,0 +1,32 @@ +#include + +#define IDR_VERSION1 1 +IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE + FILEVERSION 1, 2, 11, 0 + PRODUCTVERSION 1, 2, 11, 0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS_DOS_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0 // not used +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, char set = Windows, Multilingual + + BEGIN + VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0" + VALUE "FileVersion", "1.2.11\0" + VALUE "InternalName", "zlib\0" + VALUE "OriginalFilename", "zlibwapi.dll\0" + VALUE "ProductName", "ZLib.DLL\0" + VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" + VALUE "LegalCopyright", "(C) 1995-2017 Jean-loup Gailly & Mark Adler\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1252 + END +END diff --git a/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj b/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj new file mode 100644 index 0000000000..45389a3521 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj @@ -0,0 +1,473 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8} + + + + StaticLibrary + false + + + StaticLibrary + false + + + StaticLibrary + false + + + StaticLibrary + false + + + StaticLibrary + false + + + StaticLibrary + false + + + StaticLibrary + false + + + StaticLibrary + false + + + StaticLibrary + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDebug + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + cd ..\..\masmx86 +bld_ml32.bat + + + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)zlibstat.lib + true + + + cd ..\..\masmx86 +bld_ml32.bat + + + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + cd ..\..\masmx64 +bld_ml64.bat + + + + + Itanium + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + $(OutDir)zlibstat.lib + true + + + cd ..\..\masmx64 +bld_ml64.bat + + + + + Itanium + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + Itanium + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters new file mode 100644 index 0000000000..0c8b2501cc --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters @@ -0,0 +1,77 @@ + + + + + {174213f6-7f66-4ae8-a3a8-a1e0a1e6ffdd} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/zlibvc.def b/src/png/zlib/contrib/vstudio/vc10/zlibvc.def new file mode 100644 index 0000000000..f876c3bcab --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/zlibvc.def @@ -0,0 +1,153 @@ +LIBRARY +; zlib data compression and ZIP file I/O library + +VERSION 1.2 + +EXPORTS + adler32 @1 + compress @2 + crc32 @3 + deflate @4 + deflateCopy @5 + deflateEnd @6 + deflateInit2_ @7 + deflateInit_ @8 + deflateParams @9 + deflateReset @10 + deflateSetDictionary @11 + gzclose @12 + gzdopen @13 + gzerror @14 + gzflush @15 + gzopen @16 + gzread @17 + gzwrite @18 + inflate @19 + inflateEnd @20 + inflateInit2_ @21 + inflateInit_ @22 + inflateReset @23 + inflateSetDictionary @24 + inflateSync @25 + uncompress @26 + zlibVersion @27 + gzprintf @28 + gzputc @29 + gzgetc @30 + gzseek @31 + gzrewind @32 + gztell @33 + gzeof @34 + gzsetparams @35 + zError @36 + inflateSyncPoint @37 + get_crc_table @38 + compress2 @39 + gzputs @40 + gzgets @41 + inflateCopy @42 + inflateBackInit_ @43 + inflateBack @44 + inflateBackEnd @45 + compressBound @46 + deflateBound @47 + gzclearerr @48 + gzungetc @49 + zlibCompileFlags @50 + deflatePrime @51 + deflatePending @52 + + unzOpen @61 + unzClose @62 + unzGetGlobalInfo @63 + unzGetCurrentFileInfo @64 + unzGoToFirstFile @65 + unzGoToNextFile @66 + unzOpenCurrentFile @67 + unzReadCurrentFile @68 + unzOpenCurrentFile3 @69 + unztell @70 + unzeof @71 + unzCloseCurrentFile @72 + unzGetGlobalComment @73 + unzStringFileNameCompare @74 + unzLocateFile @75 + unzGetLocalExtrafield @76 + unzOpen2 @77 + unzOpenCurrentFile2 @78 + unzOpenCurrentFilePassword @79 + + zipOpen @80 + zipOpenNewFileInZip @81 + zipWriteInFileInZip @82 + zipCloseFileInZip @83 + zipClose @84 + zipOpenNewFileInZip2 @86 + zipCloseFileInZipRaw @87 + zipOpen2 @88 + zipOpenNewFileInZip3 @89 + + unzGetFilePos @100 + unzGoToFilePos @101 + + fill_win32_filefunc @110 + +; zlibwapi v1.2.4 added: + fill_win32_filefunc64 @111 + fill_win32_filefunc64A @112 + fill_win32_filefunc64W @113 + + unzOpen64 @120 + unzOpen2_64 @121 + unzGetGlobalInfo64 @122 + unzGetCurrentFileInfo64 @124 + unzGetCurrentFileZStreamPos64 @125 + unztell64 @126 + unzGetFilePos64 @127 + unzGoToFilePos64 @128 + + zipOpen64 @130 + zipOpen2_64 @131 + zipOpenNewFileInZip64 @132 + zipOpenNewFileInZip2_64 @133 + zipOpenNewFileInZip3_64 @134 + zipOpenNewFileInZip4_64 @135 + zipCloseFileInZipRaw64 @136 + +; zlib1 v1.2.4 added: + adler32_combine @140 + crc32_combine @142 + deflateSetHeader @144 + deflateTune @145 + gzbuffer @146 + gzclose_r @147 + gzclose_w @148 + gzdirect @149 + gzoffset @150 + inflateGetHeader @156 + inflateMark @157 + inflatePrime @158 + inflateReset2 @159 + inflateUndermine @160 + +; zlib1 v1.2.6 added: + gzgetc_ @161 + inflateResetKeep @163 + deflateResetKeep @164 + +; zlib1 v1.2.7 added: + gzopen_w @165 + +; zlib1 v1.2.8 added: + inflateGetDictionary @166 + gzvprintf @167 + +; zlib1 v1.2.9 added: + inflateCodesUsed @168 + inflateValidate @169 + uncompress2 @170 + gzfread @171 + gzfwrite @172 + deflateGetDictionary @173 + adler32_z @174 + crc32_z @175 diff --git a/src/png/zlib/contrib/vstudio/vc10/zlibvc.sln b/src/png/zlib/contrib/vstudio/vc10/zlibvc.sln new file mode 100644 index 0000000000..649f40c7ea --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/zlibvc.sln @@ -0,0 +1,135 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcxproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcxproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcxproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlibdll", "testzlibdll.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcxproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Itanium = Debug|Itanium + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Itanium = Release|Itanium + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium + ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32 + ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.Build.0 = Debug|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.Build.0 = Release|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.Build.0 = Debug|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.Build.0 = Release|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.Build.0 = Debug|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.Build.0 = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.Build.0 = Debug|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.Build.0 = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj b/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj new file mode 100644 index 0000000000..7d7c49a6dd --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj @@ -0,0 +1,657 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {8FD826F8-3739-44E6-8CC8-997122E53B8D} + + + + DynamicLibrary + false + true + + + DynamicLibrary + false + true + + + DynamicLibrary + false + + + DynamicLibrary + false + true + + + DynamicLibrary + false + true + + + DynamicLibrary + false + + + DynamicLibrary + false + true + + + DynamicLibrary + false + true + + + DynamicLibrary + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + true + false + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + false + false + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + false + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + true + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + true + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + false + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + false + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + false + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + zlibwapid + zlibwapi + zlibwapi + zlibwapid + zlibwapi + zlibwapi + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions) + + + MultiThreadedDebug + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + true + .\zlibvc.def + true + true + Windows + false + + + + + cd ..\..\masmx86 +bld_ml32.bat + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + true + false + .\zlibvc.def + true + Windows + false + + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + true + false + .\zlibvc.def + true + Windows + false + + + + + cd ..\..\masmx86 +bld_ml32.bat + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + true + .\zlibvc.def + true + true + Windows + MachineX64 + + + cd ..\..\masmx64 +bld_ml64.bat + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + .\zlibvc.def + true + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + true + false + .\zlibvc.def + true + Windows + MachineX64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + true + false + .\zlibvc.def + true + Windows + MachineX64 + + + cd ..\..\masmx64 +bld_ml64.bat + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters new file mode 100644 index 0000000000..22786824fc --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters @@ -0,0 +1,118 @@ + + + + + {07934a85-8b61-443d-a0ee-b2eedb74f3cd} + cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90 + + + {1d99675b-433d-4a21-9e50-ed4ab8b19762} + h;hpp;hxx;hm;inl;fi;fd + + + {431c0958-fa71-44d0-9084-2d19d100c0cc} + ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc11/miniunz.vcxproj b/src/png/zlib/contrib/vstudio/vc11/miniunz.vcxproj new file mode 100644 index 0000000000..99be63d69c --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc11/miniunz.vcxproj @@ -0,0 +1,314 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {C52F9E7B-498A-42BE-8DB4-85A15694382A} + Win32Proj + + + + Application + MultiByte + v110 + + + Application + Unicode + v110 + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + v110 + + + Application + MultiByte + v110 + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\MiniUnzip$(Configuration)\ + x86\MiniUnzip$(Configuration)\Tmp\ + true + false + x86\MiniUnzip$(Configuration)\ + x86\MiniUnzip$(Configuration)\Tmp\ + false + false + x64\MiniUnzip$(Configuration)\ + x64\MiniUnzip$(Configuration)\Tmp\ + true + false + ia64\MiniUnzip$(Configuration)\ + ia64\MiniUnzip$(Configuration)\Tmp\ + true + false + x64\MiniUnzip$(Configuration)\ + x64\MiniUnzip$(Configuration)\Tmp\ + false + false + ia64\MiniUnzip$(Configuration)\ + ia64\MiniUnzip$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc11/minizip.vcxproj b/src/png/zlib/contrib/vstudio/vc11/minizip.vcxproj new file mode 100644 index 0000000000..d6e98f4d57 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc11/minizip.vcxproj @@ -0,0 +1,311 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B} + Win32Proj + + + + Application + MultiByte + v110 + + + Application + Unicode + v110 + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + v110 + + + Application + MultiByte + v110 + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\MiniZip$(Configuration)\ + x86\MiniZip$(Configuration)\Tmp\ + true + false + x86\MiniZip$(Configuration)\ + x86\MiniZip$(Configuration)\Tmp\ + false + x64\$(Configuration)\ + x64\$(Configuration)\ + true + false + ia64\$(Configuration)\ + ia64\$(Configuration)\ + true + false + x64\$(Configuration)\ + x64\$(Configuration)\ + false + ia64\$(Configuration)\ + ia64\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc11/testzlib.vcxproj b/src/png/zlib/contrib/vstudio/vc11/testzlib.vcxproj new file mode 100644 index 0000000000..0115dd17b9 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc11/testzlib.vcxproj @@ -0,0 +1,426 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B} + testzlib + Win32Proj + + + + Application + MultiByte + true + v110 + + + Application + MultiByte + true + v110 + + + Application + Unicode + v110 + + + Application + MultiByte + true + + + Application + MultiByte + true + + + Application + MultiByte + + + Application + true + v110 + + + Application + true + v110 + + + Application + v110 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + true + false + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + false + false + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + false + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + true + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + false + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + AssemblyAndSourceCode + $(IntDir) + Level3 + ProgramDatabase + + + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)testzlib.exe + true + $(OutDir)testzlib.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)testzlib.exe + true + Console + true + true + false + + + MachineX86 + + + + + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDebugDLL + false + $(IntDir) + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + + + + + Itanium + + + Disabled + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + AssemblyAndSourceCode + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + $(OutDir)testzlib.pdb + Console + MachineIA64 + + + + + ..\..\..;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDLL + false + $(IntDir) + + + %(AdditionalDependencies) + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + MachineIA64 + + + + + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDLL + false + $(IntDir) + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + MachineIA64 + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj b/src/png/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj new file mode 100644 index 0000000000..9d36336eb8 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj @@ -0,0 +1,314 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {C52F9E7B-498A-42BE-8DB4-85A15694366A} + Win32Proj + + + + Application + MultiByte + v110 + + + Application + Unicode + v110 + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + v110 + + + Application + MultiByte + v110 + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\TestZlibDll$(Configuration)\ + x86\TestZlibDll$(Configuration)\Tmp\ + true + false + x86\TestZlibDll$(Configuration)\ + x86\TestZlibDll$(Configuration)\Tmp\ + false + false + x64\TestZlibDll$(Configuration)\ + x64\TestZlibDll$(Configuration)\Tmp\ + true + false + ia64\TestZlibDll$(Configuration)\ + ia64\TestZlibDll$(Configuration)\Tmp\ + true + false + x64\TestZlibDll$(Configuration)\ + x64\TestZlibDll$(Configuration)\Tmp\ + false + false + ia64\TestZlibDll$(Configuration)\ + ia64\TestZlibDll$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc11/zlib.rc b/src/png/zlib/contrib/vstudio/vc11/zlib.rc new file mode 100644 index 0000000000..c4e4b016e9 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc11/zlib.rc @@ -0,0 +1,32 @@ +#include + +#define IDR_VERSION1 1 +IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE + FILEVERSION 1, 2, 11, 0 + PRODUCTVERSION 1, 2, 11, 0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS_DOS_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0 // not used +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, char set = Windows, Multilingual + + BEGIN + VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0" + VALUE "FileVersion", "1.2.11\0" + VALUE "InternalName", "zlib\0" + VALUE "OriginalFilename", "zlibwapi.dll\0" + VALUE "ProductName", "ZLib.DLL\0" + VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" + VALUE "LegalCopyright", "(C) 1995-2017 Jean-loup Gailly & Mark Adler\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1252 + END +END diff --git a/src/png/zlib/contrib/vstudio/vc11/zlibstat.vcxproj b/src/png/zlib/contrib/vstudio/vc11/zlibstat.vcxproj new file mode 100644 index 0000000000..64b4d869dc --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc11/zlibstat.vcxproj @@ -0,0 +1,464 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8} + + + + StaticLibrary + false + v110 + + + StaticLibrary + false + v110 + + + StaticLibrary + false + v110 + Unicode + + + StaticLibrary + false + + + StaticLibrary + false + + + StaticLibrary + false + + + StaticLibrary + false + v110 + + + StaticLibrary + false + v110 + + + StaticLibrary + false + v110 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)zlibstat.lib + true + + + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + Itanium + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + $(OutDir)zlibstat.lib + true + + + + + Itanium + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + Itanium + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc11/zlibvc.def b/src/png/zlib/contrib/vstudio/vc11/zlibvc.def new file mode 100644 index 0000000000..f876c3bcab --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc11/zlibvc.def @@ -0,0 +1,153 @@ +LIBRARY +; zlib data compression and ZIP file I/O library + +VERSION 1.2 + +EXPORTS + adler32 @1 + compress @2 + crc32 @3 + deflate @4 + deflateCopy @5 + deflateEnd @6 + deflateInit2_ @7 + deflateInit_ @8 + deflateParams @9 + deflateReset @10 + deflateSetDictionary @11 + gzclose @12 + gzdopen @13 + gzerror @14 + gzflush @15 + gzopen @16 + gzread @17 + gzwrite @18 + inflate @19 + inflateEnd @20 + inflateInit2_ @21 + inflateInit_ @22 + inflateReset @23 + inflateSetDictionary @24 + inflateSync @25 + uncompress @26 + zlibVersion @27 + gzprintf @28 + gzputc @29 + gzgetc @30 + gzseek @31 + gzrewind @32 + gztell @33 + gzeof @34 + gzsetparams @35 + zError @36 + inflateSyncPoint @37 + get_crc_table @38 + compress2 @39 + gzputs @40 + gzgets @41 + inflateCopy @42 + inflateBackInit_ @43 + inflateBack @44 + inflateBackEnd @45 + compressBound @46 + deflateBound @47 + gzclearerr @48 + gzungetc @49 + zlibCompileFlags @50 + deflatePrime @51 + deflatePending @52 + + unzOpen @61 + unzClose @62 + unzGetGlobalInfo @63 + unzGetCurrentFileInfo @64 + unzGoToFirstFile @65 + unzGoToNextFile @66 + unzOpenCurrentFile @67 + unzReadCurrentFile @68 + unzOpenCurrentFile3 @69 + unztell @70 + unzeof @71 + unzCloseCurrentFile @72 + unzGetGlobalComment @73 + unzStringFileNameCompare @74 + unzLocateFile @75 + unzGetLocalExtrafield @76 + unzOpen2 @77 + unzOpenCurrentFile2 @78 + unzOpenCurrentFilePassword @79 + + zipOpen @80 + zipOpenNewFileInZip @81 + zipWriteInFileInZip @82 + zipCloseFileInZip @83 + zipClose @84 + zipOpenNewFileInZip2 @86 + zipCloseFileInZipRaw @87 + zipOpen2 @88 + zipOpenNewFileInZip3 @89 + + unzGetFilePos @100 + unzGoToFilePos @101 + + fill_win32_filefunc @110 + +; zlibwapi v1.2.4 added: + fill_win32_filefunc64 @111 + fill_win32_filefunc64A @112 + fill_win32_filefunc64W @113 + + unzOpen64 @120 + unzOpen2_64 @121 + unzGetGlobalInfo64 @122 + unzGetCurrentFileInfo64 @124 + unzGetCurrentFileZStreamPos64 @125 + unztell64 @126 + unzGetFilePos64 @127 + unzGoToFilePos64 @128 + + zipOpen64 @130 + zipOpen2_64 @131 + zipOpenNewFileInZip64 @132 + zipOpenNewFileInZip2_64 @133 + zipOpenNewFileInZip3_64 @134 + zipOpenNewFileInZip4_64 @135 + zipCloseFileInZipRaw64 @136 + +; zlib1 v1.2.4 added: + adler32_combine @140 + crc32_combine @142 + deflateSetHeader @144 + deflateTune @145 + gzbuffer @146 + gzclose_r @147 + gzclose_w @148 + gzdirect @149 + gzoffset @150 + inflateGetHeader @156 + inflateMark @157 + inflatePrime @158 + inflateReset2 @159 + inflateUndermine @160 + +; zlib1 v1.2.6 added: + gzgetc_ @161 + inflateResetKeep @163 + deflateResetKeep @164 + +; zlib1 v1.2.7 added: + gzopen_w @165 + +; zlib1 v1.2.8 added: + inflateGetDictionary @166 + gzvprintf @167 + +; zlib1 v1.2.9 added: + inflateCodesUsed @168 + inflateValidate @169 + uncompress2 @170 + gzfread @171 + gzfwrite @172 + deflateGetDictionary @173 + adler32_z @174 + crc32_z @175 diff --git a/src/png/zlib/contrib/vstudio/vc11/zlibvc.sln b/src/png/zlib/contrib/vstudio/vc11/zlibvc.sln new file mode 100644 index 0000000000..b7e3812661 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc11/zlibvc.sln @@ -0,0 +1,117 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcxproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcxproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcxproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlibdll", "testzlibdll.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcxproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Itanium = Debug|Itanium + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Itanium = Release|Itanium + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium + ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32 + ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/png/zlib/contrib/vstudio/vc11/zlibvc.vcxproj b/src/png/zlib/contrib/vstudio/vc11/zlibvc.vcxproj new file mode 100644 index 0000000000..c4cffccf1d --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc11/zlibvc.vcxproj @@ -0,0 +1,688 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {8FD826F8-3739-44E6-8CC8-997122E53B8D} + + + + DynamicLibrary + false + true + v110 + + + DynamicLibrary + false + true + v110 + + + DynamicLibrary + false + v110 + Unicode + + + DynamicLibrary + false + true + + + DynamicLibrary + false + true + + + DynamicLibrary + false + + + DynamicLibrary + false + true + v110 + + + DynamicLibrary + false + true + v110 + + + DynamicLibrary + false + v110 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + true + false + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + false + false + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + false + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + true + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + true + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + false + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + false + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + false + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + zlibwapi + zlibwapi + zlibwapi + zlibwapi + zlibwapi + zlibwapi + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + .\zlibvc.def + true + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + false + + + $(OutDir)zlibwapi.lib + + + cd ..\..\masmx86 +bld_ml32.bat + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + false + + + $(OutDir)zlibwapi.lib + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + false + + + $(OutDir)zlibwapi.lib + + + cd ..\..\masmx86 +bld_ml32.bat + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + .\zlibvc.def + true + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineX64 + + + cd ..\..\contrib\masmx64 +bld_ml64.bat + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + .\zlibvc.def + true + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineX64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineX64 + + + cd ..\..\masmx64 +bld_ml64.bat + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc12/miniunz.vcxproj b/src/png/zlib/contrib/vstudio/vc12/miniunz.vcxproj new file mode 100644 index 0000000000..d88ac7fc7d --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc12/miniunz.vcxproj @@ -0,0 +1,316 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {C52F9E7B-498A-42BE-8DB4-85A15694382A} + Win32Proj + + + + Application + MultiByte + v120 + + + Application + Unicode + v120 + + + Application + MultiByte + v120 + + + Application + MultiByte + v120 + + + Application + MultiByte + v120 + + + Application + MultiByte + v120 + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\MiniUnzip$(Configuration)\ + x86\MiniUnzip$(Configuration)\Tmp\ + true + false + x86\MiniUnzip$(Configuration)\ + x86\MiniUnzip$(Configuration)\Tmp\ + false + false + x64\MiniUnzip$(Configuration)\ + x64\MiniUnzip$(Configuration)\Tmp\ + true + false + ia64\MiniUnzip$(Configuration)\ + ia64\MiniUnzip$(Configuration)\Tmp\ + true + false + x64\MiniUnzip$(Configuration)\ + x64\MiniUnzip$(Configuration)\Tmp\ + false + false + ia64\MiniUnzip$(Configuration)\ + ia64\MiniUnzip$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc12/minizip.vcxproj b/src/png/zlib/contrib/vstudio/vc12/minizip.vcxproj new file mode 100644 index 0000000000..f1f239c9e0 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc12/minizip.vcxproj @@ -0,0 +1,313 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B} + Win32Proj + + + + Application + MultiByte + v120 + + + Application + Unicode + v120 + + + Application + MultiByte + v120 + + + Application + MultiByte + v120 + + + Application + MultiByte + v120 + + + Application + MultiByte + v120 + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\MiniZip$(Configuration)\ + x86\MiniZip$(Configuration)\Tmp\ + true + false + x86\MiniZip$(Configuration)\ + x86\MiniZip$(Configuration)\Tmp\ + false + x64\$(Configuration)\ + x64\$(Configuration)\ + true + false + ia64\$(Configuration)\ + ia64\$(Configuration)\ + true + false + x64\$(Configuration)\ + x64\$(Configuration)\ + false + ia64\$(Configuration)\ + ia64\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc12/testzlib.vcxproj b/src/png/zlib/contrib/vstudio/vc12/testzlib.vcxproj new file mode 100644 index 0000000000..64b2cbe34a --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc12/testzlib.vcxproj @@ -0,0 +1,430 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B} + testzlib + Win32Proj + + + + Application + MultiByte + true + v120 + + + Application + MultiByte + true + v120 + + + Application + Unicode + v120 + + + Application + MultiByte + true + v120 + + + Application + MultiByte + true + v120 + + + Application + MultiByte + v120 + + + Application + true + v120 + + + Application + true + v120 + + + Application + v120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + true + false + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + false + false + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + false + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + true + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + false + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + AssemblyAndSourceCode + $(IntDir) + Level3 + ProgramDatabase + + + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)testzlib.exe + true + $(OutDir)testzlib.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)testzlib.exe + true + Console + true + true + false + + + MachineX86 + false + + + + + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDebugDLL + false + $(IntDir) + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + + + + + Itanium + + + Disabled + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + AssemblyAndSourceCode + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + $(OutDir)testzlib.pdb + Console + MachineIA64 + + + + + ..\..\..;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDLL + false + $(IntDir) + + + %(AdditionalDependencies) + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + MachineIA64 + + + + + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDLL + false + $(IntDir) + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + MachineIA64 + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc12/testzlibdll.vcxproj b/src/png/zlib/contrib/vstudio/vc12/testzlibdll.vcxproj new file mode 100644 index 0000000000..c66573a8bf --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc12/testzlibdll.vcxproj @@ -0,0 +1,316 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {C52F9E7B-498A-42BE-8DB4-85A15694366A} + Win32Proj + + + + Application + MultiByte + v120 + + + Application + Unicode + v120 + + + Application + MultiByte + v120 + + + Application + MultiByte + v120 + + + Application + MultiByte + v120 + + + Application + MultiByte + v120 + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\TestZlibDll$(Configuration)\ + x86\TestZlibDll$(Configuration)\Tmp\ + true + false + x86\TestZlibDll$(Configuration)\ + x86\TestZlibDll$(Configuration)\Tmp\ + false + false + x64\TestZlibDll$(Configuration)\ + x64\TestZlibDll$(Configuration)\Tmp\ + true + false + ia64\TestZlibDll$(Configuration)\ + ia64\TestZlibDll$(Configuration)\Tmp\ + true + false + x64\TestZlibDll$(Configuration)\ + x64\TestZlibDll$(Configuration)\Tmp\ + false + false + ia64\TestZlibDll$(Configuration)\ + ia64\TestZlibDll$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc12/zlib.rc b/src/png/zlib/contrib/vstudio/vc12/zlib.rc new file mode 100644 index 0000000000..c4e4b016e9 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc12/zlib.rc @@ -0,0 +1,32 @@ +#include + +#define IDR_VERSION1 1 +IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE + FILEVERSION 1, 2, 11, 0 + PRODUCTVERSION 1, 2, 11, 0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS_DOS_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0 // not used +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, char set = Windows, Multilingual + + BEGIN + VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0" + VALUE "FileVersion", "1.2.11\0" + VALUE "InternalName", "zlib\0" + VALUE "OriginalFilename", "zlibwapi.dll\0" + VALUE "ProductName", "ZLib.DLL\0" + VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" + VALUE "LegalCopyright", "(C) 1995-2017 Jean-loup Gailly & Mark Adler\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1252 + END +END diff --git a/src/png/zlib/contrib/vstudio/vc12/zlibstat.vcxproj b/src/png/zlib/contrib/vstudio/vc12/zlibstat.vcxproj new file mode 100644 index 0000000000..3fdee7c507 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc12/zlibstat.vcxproj @@ -0,0 +1,467 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8} + + + + StaticLibrary + false + v120 + + + StaticLibrary + false + v120 + + + StaticLibrary + false + v120 + Unicode + + + StaticLibrary + false + v120 + + + StaticLibrary + false + v120 + + + StaticLibrary + false + v120 + + + StaticLibrary + false + v120 + + + StaticLibrary + false + v120 + + + StaticLibrary + false + v120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)zlibstat.lib + true + + + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + Itanium + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + $(OutDir)zlibstat.lib + true + + + + + Itanium + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + Itanium + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc12/zlibvc.def b/src/png/zlib/contrib/vstudio/vc12/zlibvc.def new file mode 100644 index 0000000000..f876c3bcab --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc12/zlibvc.def @@ -0,0 +1,153 @@ +LIBRARY +; zlib data compression and ZIP file I/O library + +VERSION 1.2 + +EXPORTS + adler32 @1 + compress @2 + crc32 @3 + deflate @4 + deflateCopy @5 + deflateEnd @6 + deflateInit2_ @7 + deflateInit_ @8 + deflateParams @9 + deflateReset @10 + deflateSetDictionary @11 + gzclose @12 + gzdopen @13 + gzerror @14 + gzflush @15 + gzopen @16 + gzread @17 + gzwrite @18 + inflate @19 + inflateEnd @20 + inflateInit2_ @21 + inflateInit_ @22 + inflateReset @23 + inflateSetDictionary @24 + inflateSync @25 + uncompress @26 + zlibVersion @27 + gzprintf @28 + gzputc @29 + gzgetc @30 + gzseek @31 + gzrewind @32 + gztell @33 + gzeof @34 + gzsetparams @35 + zError @36 + inflateSyncPoint @37 + get_crc_table @38 + compress2 @39 + gzputs @40 + gzgets @41 + inflateCopy @42 + inflateBackInit_ @43 + inflateBack @44 + inflateBackEnd @45 + compressBound @46 + deflateBound @47 + gzclearerr @48 + gzungetc @49 + zlibCompileFlags @50 + deflatePrime @51 + deflatePending @52 + + unzOpen @61 + unzClose @62 + unzGetGlobalInfo @63 + unzGetCurrentFileInfo @64 + unzGoToFirstFile @65 + unzGoToNextFile @66 + unzOpenCurrentFile @67 + unzReadCurrentFile @68 + unzOpenCurrentFile3 @69 + unztell @70 + unzeof @71 + unzCloseCurrentFile @72 + unzGetGlobalComment @73 + unzStringFileNameCompare @74 + unzLocateFile @75 + unzGetLocalExtrafield @76 + unzOpen2 @77 + unzOpenCurrentFile2 @78 + unzOpenCurrentFilePassword @79 + + zipOpen @80 + zipOpenNewFileInZip @81 + zipWriteInFileInZip @82 + zipCloseFileInZip @83 + zipClose @84 + zipOpenNewFileInZip2 @86 + zipCloseFileInZipRaw @87 + zipOpen2 @88 + zipOpenNewFileInZip3 @89 + + unzGetFilePos @100 + unzGoToFilePos @101 + + fill_win32_filefunc @110 + +; zlibwapi v1.2.4 added: + fill_win32_filefunc64 @111 + fill_win32_filefunc64A @112 + fill_win32_filefunc64W @113 + + unzOpen64 @120 + unzOpen2_64 @121 + unzGetGlobalInfo64 @122 + unzGetCurrentFileInfo64 @124 + unzGetCurrentFileZStreamPos64 @125 + unztell64 @126 + unzGetFilePos64 @127 + unzGoToFilePos64 @128 + + zipOpen64 @130 + zipOpen2_64 @131 + zipOpenNewFileInZip64 @132 + zipOpenNewFileInZip2_64 @133 + zipOpenNewFileInZip3_64 @134 + zipOpenNewFileInZip4_64 @135 + zipCloseFileInZipRaw64 @136 + +; zlib1 v1.2.4 added: + adler32_combine @140 + crc32_combine @142 + deflateSetHeader @144 + deflateTune @145 + gzbuffer @146 + gzclose_r @147 + gzclose_w @148 + gzdirect @149 + gzoffset @150 + inflateGetHeader @156 + inflateMark @157 + inflatePrime @158 + inflateReset2 @159 + inflateUndermine @160 + +; zlib1 v1.2.6 added: + gzgetc_ @161 + inflateResetKeep @163 + deflateResetKeep @164 + +; zlib1 v1.2.7 added: + gzopen_w @165 + +; zlib1 v1.2.8 added: + inflateGetDictionary @166 + gzvprintf @167 + +; zlib1 v1.2.9 added: + inflateCodesUsed @168 + inflateValidate @169 + uncompress2 @170 + gzfread @171 + gzfwrite @172 + deflateGetDictionary @173 + adler32_z @174 + crc32_z @175 diff --git a/src/png/zlib/contrib/vstudio/vc12/zlibvc.sln b/src/png/zlib/contrib/vstudio/vc12/zlibvc.sln new file mode 100644 index 0000000000..dcda229849 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc12/zlibvc.sln @@ -0,0 +1,119 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcxproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcxproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcxproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlibdll", "testzlibdll.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcxproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Itanium = Debug|Itanium + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Itanium = Release|Itanium + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium + ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32 + ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/png/zlib/contrib/vstudio/vc12/zlibvc.vcxproj b/src/png/zlib/contrib/vstudio/vc12/zlibvc.vcxproj new file mode 100644 index 0000000000..ab2b6c3603 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc12/zlibvc.vcxproj @@ -0,0 +1,692 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {8FD826F8-3739-44E6-8CC8-997122E53B8D} + + + + DynamicLibrary + false + true + v120 + + + DynamicLibrary + false + true + v120 + + + DynamicLibrary + false + v120 + Unicode + + + DynamicLibrary + false + true + v120 + + + DynamicLibrary + false + true + v120 + + + DynamicLibrary + false + v120 + + + DynamicLibrary + false + true + v120 + + + DynamicLibrary + false + true + v120 + + + DynamicLibrary + false + v120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + true + false + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + false + false + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + false + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + true + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + true + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + false + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + false + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + false + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + zlibwapi + zlibwapi + zlibwapi + zlibwapi + zlibwapi + zlibwapi + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + .\zlibvc.def + true + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + false + + + $(OutDir)zlibwapi.lib + + + cd ..\..\masmx86 +bld_ml32.bat + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + false + + + $(OutDir)zlibwapi.lib + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + false + + + $(OutDir)zlibwapi.lib + false + + + cd ..\..\masmx86 +bld_ml32.bat + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + .\zlibvc.def + true + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineX64 + + + cd ..\..\contrib\masmx64 +bld_ml64.bat + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + .\zlibvc.def + true + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineX64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineX64 + + + cd ..\..\masmx64 +bld_ml64.bat + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc14/miniunz.vcxproj b/src/png/zlib/contrib/vstudio/vc14/miniunz.vcxproj new file mode 100644 index 0000000000..9b5c07587d --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc14/miniunz.vcxproj @@ -0,0 +1,316 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {C52F9E7B-498A-42BE-8DB4-85A15694382A} + Win32Proj + + + + Application + MultiByte + v140 + + + Application + Unicode + v140 + + + Application + MultiByte + v140 + + + Application + MultiByte + v140 + + + Application + MultiByte + v140 + + + Application + MultiByte + v140 + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\MiniUnzip$(Configuration)\ + x86\MiniUnzip$(Configuration)\Tmp\ + true + false + x86\MiniUnzip$(Configuration)\ + x86\MiniUnzip$(Configuration)\Tmp\ + false + false + x64\MiniUnzip$(Configuration)\ + x64\MiniUnzip$(Configuration)\Tmp\ + true + false + ia64\MiniUnzip$(Configuration)\ + ia64\MiniUnzip$(Configuration)\Tmp\ + true + false + x64\MiniUnzip$(Configuration)\ + x64\MiniUnzip$(Configuration)\Tmp\ + false + false + ia64\MiniUnzip$(Configuration)\ + ia64\MiniUnzip$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + $(OutDir)miniunz.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)miniunz.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc14/minizip.vcxproj b/src/png/zlib/contrib/vstudio/vc14/minizip.vcxproj new file mode 100644 index 0000000000..968a410a12 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc14/minizip.vcxproj @@ -0,0 +1,313 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B} + Win32Proj + + + + Application + MultiByte + v140 + + + Application + Unicode + v140 + + + Application + MultiByte + v140 + + + Application + MultiByte + v140 + + + Application + MultiByte + v140 + + + Application + MultiByte + v140 + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\MiniZip$(Configuration)\ + x86\MiniZip$(Configuration)\Tmp\ + true + false + x86\MiniZip$(Configuration)\ + x86\MiniZip$(Configuration)\Tmp\ + false + x64\$(Configuration)\ + x64\$(Configuration)\ + true + false + ia64\$(Configuration)\ + ia64\$(Configuration)\ + true + false + x64\$(Configuration)\ + x64\$(Configuration)\ + false + ia64\$(Configuration)\ + ia64\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + $(OutDir)minizip.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)minizip.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc14/testzlib.vcxproj b/src/png/zlib/contrib/vstudio/vc14/testzlib.vcxproj new file mode 100644 index 0000000000..2c371252ae --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc14/testzlib.vcxproj @@ -0,0 +1,430 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B} + testzlib + Win32Proj + + + + Application + MultiByte + true + v140 + + + Application + MultiByte + true + v140 + + + Application + Unicode + v140 + + + Application + MultiByte + true + v140 + + + Application + MultiByte + true + v140 + + + Application + MultiByte + v140 + + + Application + true + v140 + + + Application + true + v140 + + + Application + v140 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + true + false + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + false + false + x86\TestZlib$(Configuration)\ + x86\TestZlib$(Configuration)\Tmp\ + false + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + true + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + false + false + x64\TestZlib$(Configuration)\ + x64\TestZlib$(Configuration)\Tmp\ + false + ia64\TestZlib$(Configuration)\ + ia64\TestZlib$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + AssemblyAndSourceCode + $(IntDir) + Level3 + ProgramDatabase + + + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)testzlib.exe + true + $(OutDir)testzlib.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)testzlib.exe + true + Console + true + true + false + + + MachineX86 + false + + + + + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDebugDLL + false + $(IntDir) + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + + + + + Itanium + + + Disabled + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + AssemblyAndSourceCode + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + $(OutDir)testzlib.pdb + Console + MachineIA64 + + + + + ..\..\..;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDLL + false + $(IntDir) + + + %(AdditionalDependencies) + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + MachineIA64 + + + + + ..\..\..;%(AdditionalIncludeDirectories) + ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + Default + MultiThreadedDLL + false + $(IntDir) + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + $(OutDir)testzlib.exe + true + Console + true + true + MachineIA64 + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc14/testzlibdll.vcxproj b/src/png/zlib/contrib/vstudio/vc14/testzlibdll.vcxproj new file mode 100644 index 0000000000..d87474dec6 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc14/testzlibdll.vcxproj @@ -0,0 +1,316 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {C52F9E7B-498A-42BE-8DB4-85A15694366A} + Win32Proj + + + + Application + MultiByte + v140 + + + Application + Unicode + v140 + + + Application + MultiByte + v140 + + + Application + MultiByte + v140 + + + Application + MultiByte + v140 + + + Application + MultiByte + v140 + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\TestZlibDll$(Configuration)\ + x86\TestZlibDll$(Configuration)\Tmp\ + true + false + x86\TestZlibDll$(Configuration)\ + x86\TestZlibDll$(Configuration)\Tmp\ + false + false + x64\TestZlibDll$(Configuration)\ + x64\TestZlibDll$(Configuration)\Tmp\ + true + false + ia64\TestZlibDll$(Configuration)\ + ia64\TestZlibDll$(Configuration)\Tmp\ + true + false + x64\TestZlibDll$(Configuration)\ + x64\TestZlibDll$(Configuration)\Tmp\ + false + false + ia64\TestZlibDll$(Configuration)\ + ia64\TestZlibDll$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + false + + + MachineX86 + + + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + MultiThreaded + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + MachineX64 + + + + + Itanium + + + Disabled + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDebugDLL + false + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + $(OutDir)testzlib.pdb + Console + MachineIA64 + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + MachineX64 + + + + + Itanium + + + MaxSpeed + OnlyExplicitInline + true + ..\..\..;..\..\minizip;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions) + true + Default + MultiThreadedDLL + false + true + + + $(IntDir) + Level3 + ProgramDatabase + + + ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies) + $(OutDir)testzlibdll.exe + true + Console + true + true + MachineIA64 + + + + + + + + {8fd826f8-3739-44e6-8cc8-997122e53b8d} + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc14/zlib.rc b/src/png/zlib/contrib/vstudio/vc14/zlib.rc new file mode 100644 index 0000000000..c4e4b016e9 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc14/zlib.rc @@ -0,0 +1,32 @@ +#include + +#define IDR_VERSION1 1 +IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE + FILEVERSION 1, 2, 11, 0 + PRODUCTVERSION 1, 2, 11, 0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS_DOS_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0 // not used +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, char set = Windows, Multilingual + + BEGIN + VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0" + VALUE "FileVersion", "1.2.11\0" + VALUE "InternalName", "zlib\0" + VALUE "OriginalFilename", "zlibwapi.dll\0" + VALUE "ProductName", "ZLib.DLL\0" + VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" + VALUE "LegalCopyright", "(C) 1995-2017 Jean-loup Gailly & Mark Adler\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1252 + END +END diff --git a/src/png/zlib/contrib/vstudio/vc14/zlibstat.vcxproj b/src/png/zlib/contrib/vstudio/vc14/zlibstat.vcxproj new file mode 100644 index 0000000000..3e4b986392 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc14/zlibstat.vcxproj @@ -0,0 +1,467 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8} + + + + StaticLibrary + false + v140 + + + StaticLibrary + false + v140 + + + StaticLibrary + false + v140 + Unicode + + + StaticLibrary + false + v140 + + + StaticLibrary + false + v140 + + + StaticLibrary + false + v140 + + + StaticLibrary + false + v140 + + + StaticLibrary + false + v140 + + + StaticLibrary + false + v140 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x86\ZlibStat$(Configuration)\ + x86\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + x64\ZlibStat$(Configuration)\ + x64\ZlibStat$(Configuration)\Tmp\ + ia64\ZlibStat$(Configuration)\ + ia64\ZlibStat$(Configuration)\Tmp\ + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)zlibstat.lib + true + + + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + Itanium + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + OldStyle + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + $(OutDir)zlibstat.lib + true + + + + + Itanium + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + X64 + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + Itanium + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibstat.pch + $(IntDir) + $(IntDir) + $(OutDir) + Level3 + true + + + 0x040c + + + /MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions) + $(OutDir)zlibstat.lib + true + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc14/zlibvc.def b/src/png/zlib/contrib/vstudio/vc14/zlibvc.def new file mode 100644 index 0000000000..f876c3bcab --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc14/zlibvc.def @@ -0,0 +1,153 @@ +LIBRARY +; zlib data compression and ZIP file I/O library + +VERSION 1.2 + +EXPORTS + adler32 @1 + compress @2 + crc32 @3 + deflate @4 + deflateCopy @5 + deflateEnd @6 + deflateInit2_ @7 + deflateInit_ @8 + deflateParams @9 + deflateReset @10 + deflateSetDictionary @11 + gzclose @12 + gzdopen @13 + gzerror @14 + gzflush @15 + gzopen @16 + gzread @17 + gzwrite @18 + inflate @19 + inflateEnd @20 + inflateInit2_ @21 + inflateInit_ @22 + inflateReset @23 + inflateSetDictionary @24 + inflateSync @25 + uncompress @26 + zlibVersion @27 + gzprintf @28 + gzputc @29 + gzgetc @30 + gzseek @31 + gzrewind @32 + gztell @33 + gzeof @34 + gzsetparams @35 + zError @36 + inflateSyncPoint @37 + get_crc_table @38 + compress2 @39 + gzputs @40 + gzgets @41 + inflateCopy @42 + inflateBackInit_ @43 + inflateBack @44 + inflateBackEnd @45 + compressBound @46 + deflateBound @47 + gzclearerr @48 + gzungetc @49 + zlibCompileFlags @50 + deflatePrime @51 + deflatePending @52 + + unzOpen @61 + unzClose @62 + unzGetGlobalInfo @63 + unzGetCurrentFileInfo @64 + unzGoToFirstFile @65 + unzGoToNextFile @66 + unzOpenCurrentFile @67 + unzReadCurrentFile @68 + unzOpenCurrentFile3 @69 + unztell @70 + unzeof @71 + unzCloseCurrentFile @72 + unzGetGlobalComment @73 + unzStringFileNameCompare @74 + unzLocateFile @75 + unzGetLocalExtrafield @76 + unzOpen2 @77 + unzOpenCurrentFile2 @78 + unzOpenCurrentFilePassword @79 + + zipOpen @80 + zipOpenNewFileInZip @81 + zipWriteInFileInZip @82 + zipCloseFileInZip @83 + zipClose @84 + zipOpenNewFileInZip2 @86 + zipCloseFileInZipRaw @87 + zipOpen2 @88 + zipOpenNewFileInZip3 @89 + + unzGetFilePos @100 + unzGoToFilePos @101 + + fill_win32_filefunc @110 + +; zlibwapi v1.2.4 added: + fill_win32_filefunc64 @111 + fill_win32_filefunc64A @112 + fill_win32_filefunc64W @113 + + unzOpen64 @120 + unzOpen2_64 @121 + unzGetGlobalInfo64 @122 + unzGetCurrentFileInfo64 @124 + unzGetCurrentFileZStreamPos64 @125 + unztell64 @126 + unzGetFilePos64 @127 + unzGoToFilePos64 @128 + + zipOpen64 @130 + zipOpen2_64 @131 + zipOpenNewFileInZip64 @132 + zipOpenNewFileInZip2_64 @133 + zipOpenNewFileInZip3_64 @134 + zipOpenNewFileInZip4_64 @135 + zipCloseFileInZipRaw64 @136 + +; zlib1 v1.2.4 added: + adler32_combine @140 + crc32_combine @142 + deflateSetHeader @144 + deflateTune @145 + gzbuffer @146 + gzclose_r @147 + gzclose_w @148 + gzdirect @149 + gzoffset @150 + inflateGetHeader @156 + inflateMark @157 + inflatePrime @158 + inflateReset2 @159 + inflateUndermine @160 + +; zlib1 v1.2.6 added: + gzgetc_ @161 + inflateResetKeep @163 + deflateResetKeep @164 + +; zlib1 v1.2.7 added: + gzopen_w @165 + +; zlib1 v1.2.8 added: + inflateGetDictionary @166 + gzvprintf @167 + +; zlib1 v1.2.9 added: + inflateCodesUsed @168 + inflateValidate @169 + uncompress2 @170 + gzfread @171 + gzfwrite @172 + deflateGetDictionary @173 + adler32_z @174 + crc32_z @175 diff --git a/src/png/zlib/contrib/vstudio/vc14/zlibvc.sln b/src/png/zlib/contrib/vstudio/vc14/zlibvc.sln new file mode 100644 index 0000000000..6f4a1076a4 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc14/zlibvc.sln @@ -0,0 +1,119 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcxproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcxproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcxproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlibdll", "testzlibdll.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcxproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Itanium = Debug|Itanium + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Itanium = Release|Itanium + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium + ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32 + ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/png/zlib/contrib/vstudio/vc14/zlibvc.vcxproj b/src/png/zlib/contrib/vstudio/vc14/zlibvc.vcxproj new file mode 100644 index 0000000000..f8f673cb05 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc14/zlibvc.vcxproj @@ -0,0 +1,692 @@ + + + + + Debug + Itanium + + + Debug + Win32 + + + Debug + x64 + + + ReleaseWithoutAsm + Itanium + + + ReleaseWithoutAsm + Win32 + + + ReleaseWithoutAsm + x64 + + + Release + Itanium + + + Release + Win32 + + + Release + x64 + + + + {8FD826F8-3739-44E6-8CC8-997122E53B8D} + + + + DynamicLibrary + false + true + v140 + + + DynamicLibrary + false + true + v140 + + + DynamicLibrary + false + v140 + Unicode + + + DynamicLibrary + false + true + v140 + + + DynamicLibrary + false + true + v140 + + + DynamicLibrary + false + v140 + + + DynamicLibrary + false + true + v140 + + + DynamicLibrary + false + true + v140 + + + DynamicLibrary + false + v140 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30128.1 + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + true + false + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + false + false + x86\ZlibDll$(Configuration)\ + x86\ZlibDll$(Configuration)\Tmp\ + false + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + true + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + true + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + false + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + false + false + x64\ZlibDll$(Configuration)\ + x64\ZlibDll$(Configuration)\Tmp\ + false + false + ia64\ZlibDll$(Configuration)\ + ia64\ZlibDll$(Configuration)\Tmp\ + false + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + zlibwapi + zlibwapi + zlibwapi + zlibwapi + zlibwapi + zlibwapi + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + .\zlibvc.def + true + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + false + + + $(OutDir)zlibwapi.lib + + + cd ..\..\masmx86 +bld_ml32.bat + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + false + + + $(OutDir)zlibwapi.lib + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + /MACHINE:I386 %(AdditionalOptions) + ..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + false + + + $(OutDir)zlibwapi.lib + false + + + cd ..\..\masmx86 +bld_ml32.bat + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + .\zlibvc.def + true + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineX64 + + + cd ..\..\contrib\masmx64 +bld_ml64.bat + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + Disabled + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + + + MultiThreadedDebugDLL + false + $(IntDir)zlibvc.pch + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + .\zlibvc.def + true + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineX64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + ..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies) + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineX64 + + + cd ..\..\masmx64 +bld_ml64.bat + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Itanium + $(OutDir)zlibvc.tlb + + + OnlyExplicitInline + ..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories) + _CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions) + true + + + MultiThreadedDLL + false + true + $(IntDir)zlibvc.pch + All + $(IntDir) + $(IntDir) + $(OutDir) + + + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x040c + + + $(OutDir)zlibwapi.dll + true + false + .\zlibvc.def + $(OutDir)zlibwapi.pdb + true + $(OutDir)zlibwapi.map + Windows + $(OutDir)zlibwapi.lib + MachineIA64 + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + ZLIB_INTERNAL;%(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/png/zlib/contrib/vstudio/vc9/miniunz.vcproj b/src/png/zlib/contrib/vstudio/vc9/miniunz.vcproj new file mode 100644 index 0000000000..038a9e5faf --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc9/miniunz.vcproj @@ -0,0 +1,565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/png/zlib/contrib/vstudio/vc9/minizip.vcproj b/src/png/zlib/contrib/vstudio/vc9/minizip.vcproj new file mode 100644 index 0000000000..ad40239914 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc9/minizip.vcproj @@ -0,0 +1,562 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/png/zlib/contrib/vstudio/vc9/testzlib.vcproj b/src/png/zlib/contrib/vstudio/vc9/testzlib.vcproj new file mode 100644 index 0000000000..c9f19d24ef --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc9/testzlib.vcproj @@ -0,0 +1,852 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/png/zlib/contrib/vstudio/vc9/testzlibdll.vcproj b/src/png/zlib/contrib/vstudio/vc9/testzlibdll.vcproj new file mode 100644 index 0000000000..d7530fd7dc --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc9/testzlibdll.vcproj @@ -0,0 +1,565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/png/zlib/contrib/vstudio/vc9/zlib.rc b/src/png/zlib/contrib/vstudio/vc9/zlib.rc new file mode 100644 index 0000000000..c4e4b016e9 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc9/zlib.rc @@ -0,0 +1,32 @@ +#include + +#define IDR_VERSION1 1 +IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE + FILEVERSION 1, 2, 11, 0 + PRODUCTVERSION 1, 2, 11, 0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS_DOS_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0 // not used +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, char set = Windows, Multilingual + + BEGIN + VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0" + VALUE "FileVersion", "1.2.11\0" + VALUE "InternalName", "zlib\0" + VALUE "OriginalFilename", "zlibwapi.dll\0" + VALUE "ProductName", "ZLib.DLL\0" + VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" + VALUE "LegalCopyright", "(C) 1995-2017 Jean-loup Gailly & Mark Adler\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1252 + END +END diff --git a/src/png/zlib/contrib/vstudio/vc9/zlibstat.vcproj b/src/png/zlib/contrib/vstudio/vc9/zlibstat.vcproj new file mode 100644 index 0000000000..d4ffb46b24 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc9/zlibstat.vcproj @@ -0,0 +1,835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/png/zlib/contrib/vstudio/vc9/zlibvc.def b/src/png/zlib/contrib/vstudio/vc9/zlibvc.def new file mode 100644 index 0000000000..f876c3bcab --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc9/zlibvc.def @@ -0,0 +1,153 @@ +LIBRARY +; zlib data compression and ZIP file I/O library + +VERSION 1.2 + +EXPORTS + adler32 @1 + compress @2 + crc32 @3 + deflate @4 + deflateCopy @5 + deflateEnd @6 + deflateInit2_ @7 + deflateInit_ @8 + deflateParams @9 + deflateReset @10 + deflateSetDictionary @11 + gzclose @12 + gzdopen @13 + gzerror @14 + gzflush @15 + gzopen @16 + gzread @17 + gzwrite @18 + inflate @19 + inflateEnd @20 + inflateInit2_ @21 + inflateInit_ @22 + inflateReset @23 + inflateSetDictionary @24 + inflateSync @25 + uncompress @26 + zlibVersion @27 + gzprintf @28 + gzputc @29 + gzgetc @30 + gzseek @31 + gzrewind @32 + gztell @33 + gzeof @34 + gzsetparams @35 + zError @36 + inflateSyncPoint @37 + get_crc_table @38 + compress2 @39 + gzputs @40 + gzgets @41 + inflateCopy @42 + inflateBackInit_ @43 + inflateBack @44 + inflateBackEnd @45 + compressBound @46 + deflateBound @47 + gzclearerr @48 + gzungetc @49 + zlibCompileFlags @50 + deflatePrime @51 + deflatePending @52 + + unzOpen @61 + unzClose @62 + unzGetGlobalInfo @63 + unzGetCurrentFileInfo @64 + unzGoToFirstFile @65 + unzGoToNextFile @66 + unzOpenCurrentFile @67 + unzReadCurrentFile @68 + unzOpenCurrentFile3 @69 + unztell @70 + unzeof @71 + unzCloseCurrentFile @72 + unzGetGlobalComment @73 + unzStringFileNameCompare @74 + unzLocateFile @75 + unzGetLocalExtrafield @76 + unzOpen2 @77 + unzOpenCurrentFile2 @78 + unzOpenCurrentFilePassword @79 + + zipOpen @80 + zipOpenNewFileInZip @81 + zipWriteInFileInZip @82 + zipCloseFileInZip @83 + zipClose @84 + zipOpenNewFileInZip2 @86 + zipCloseFileInZipRaw @87 + zipOpen2 @88 + zipOpenNewFileInZip3 @89 + + unzGetFilePos @100 + unzGoToFilePos @101 + + fill_win32_filefunc @110 + +; zlibwapi v1.2.4 added: + fill_win32_filefunc64 @111 + fill_win32_filefunc64A @112 + fill_win32_filefunc64W @113 + + unzOpen64 @120 + unzOpen2_64 @121 + unzGetGlobalInfo64 @122 + unzGetCurrentFileInfo64 @124 + unzGetCurrentFileZStreamPos64 @125 + unztell64 @126 + unzGetFilePos64 @127 + unzGoToFilePos64 @128 + + zipOpen64 @130 + zipOpen2_64 @131 + zipOpenNewFileInZip64 @132 + zipOpenNewFileInZip2_64 @133 + zipOpenNewFileInZip3_64 @134 + zipOpenNewFileInZip4_64 @135 + zipCloseFileInZipRaw64 @136 + +; zlib1 v1.2.4 added: + adler32_combine @140 + crc32_combine @142 + deflateSetHeader @144 + deflateTune @145 + gzbuffer @146 + gzclose_r @147 + gzclose_w @148 + gzdirect @149 + gzoffset @150 + inflateGetHeader @156 + inflateMark @157 + inflatePrime @158 + inflateReset2 @159 + inflateUndermine @160 + +; zlib1 v1.2.6 added: + gzgetc_ @161 + inflateResetKeep @163 + deflateResetKeep @164 + +; zlib1 v1.2.7 added: + gzopen_w @165 + +; zlib1 v1.2.8 added: + inflateGetDictionary @166 + gzvprintf @167 + +; zlib1 v1.2.9 added: + inflateCodesUsed @168 + inflateValidate @169 + uncompress2 @170 + gzfread @171 + gzfwrite @172 + deflateGetDictionary @173 + adler32_z @174 + crc32_z @175 diff --git a/src/png/zlib/contrib/vstudio/vc9/zlibvc.sln b/src/png/zlib/contrib/vstudio/vc9/zlibvc.sln new file mode 100644 index 0000000000..75c64c3f4a --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc9/zlibvc.sln @@ -0,0 +1,144 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestZlibDll", "testzlibdll.vcproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}" + ProjectSection(ProjectDependencies) = postProject + {8FD826F8-3739-44E6-8CC8-997122E53B8D} = {8FD826F8-3739-44E6-8CC8-997122E53B8D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" + ProjectSection(ProjectDependencies) = postProject + {8FD826F8-3739-44E6-8CC8-997122E53B8D} = {8FD826F8-3739-44E6-8CC8-997122E53B8D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}" + ProjectSection(ProjectDependencies) = postProject + {8FD826F8-3739-44E6-8CC8-997122E53B8D} = {8FD826F8-3739-44E6-8CC8-997122E53B8D} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Itanium = Debug|Itanium + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Itanium = Release|Itanium + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium + ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32 + ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.Build.0 = Debug|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.Build.0 = Release|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.Build.0 = Debug|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.Build.0 = Release|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 + {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.Build.0 = Debug|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.Build.0 = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.Build.0 = Debug|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.Build.0 = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 + {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/png/zlib/contrib/vstudio/vc9/zlibvc.vcproj b/src/png/zlib/contrib/vstudio/vc9/zlibvc.vcproj new file mode 100644 index 0000000000..95bb241f30 --- /dev/null +++ b/src/png/zlib/contrib/vstudio/vc9/zlibvc.vcproj @@ -0,0 +1,1156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/png/zlib/crc32.c b/src/png/zlib/crc32.c new file mode 100644 index 0000000000..9580440c0e --- /dev/null +++ b/src/png/zlib/crc32.c @@ -0,0 +1,442 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + + DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +/* Definitions for doing the crc four data bytes at a time. */ +#if !defined(NOBYFOUR) && defined(Z_U4) +# define BYFOUR +#endif +#ifdef BYFOUR + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, z_size_t)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, z_size_t)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); + + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local z_crc_t FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const z_crc_t FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + z_crc_t c; + int n, k; + z_crc_t poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0; + for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) + poly |= (z_crc_t)1 << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (z_crc_t)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = ZSWAP32(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = ZSWAP32(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const z_crc_t FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const z_crc_t FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", + (unsigned long)(table[n]), + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const z_crc_t FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const z_crc_t FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32_z(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + z_size_t len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + z_crc_t endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + return crc32_z(crc, buf, len); +} + +#ifdef BYFOUR + +/* + This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit + integer pointer type. This violates the strict aliasing rule, where a + compiler can assume, for optimization purposes, that two pointers to + fundamentally different types won't ever point to the same memory. This can + manifest as a problem only if one of the pointers is written to. This code + only reads from those pointers. So long as this code remains isolated in + this compilation unit, there won't be a problem. For this reason, this code + should not be copied and pasted into a compilation unit in which other code + writes to the buffer that is passed to these routines. + */ + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + z_size_t len; +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = (z_crc_t)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *buf4++; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + z_size_t len; +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = ZSWAP32((z_crc_t)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(ZSWAP32(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +local uLong crc32_combine_(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/src/png/zlib/crc32.h b/src/png/zlib/crc32.h new file mode 100644 index 0000000000..9e0c778102 --- /dev/null +++ b/src/png/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const z_crc_t FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/src/png/zlib/deflate.c b/src/png/zlib/deflate.c new file mode 100644 index 0000000000..1ec761448d --- /dev/null +++ b/src/png/zlib/deflate.c @@ -0,0 +1,2163 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://tools.ietf.org/html/rfc1951 + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.11 Copyright 1995-2017 Jean-loup Gailly and Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local int deflateStateCheck OF((z_streamp strm)); +local void slide_hash OF((deflate_state *s)); +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local block_state deflate_rle OF((deflate_state *s, int flush)); +local block_state deflate_huff OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV +# pragma message("Assembler code may have bugs -- use at your own risk") + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef ZLIB_DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ +#define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0)) + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to UPDATE_HASH are made with consecutive input + * characters, so that a running hash key can be computed from the previous + * key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to INSERT_STRING are made with consecutive input + * characters and the first MIN_MATCH bytes of str are valid (except for + * the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* =========================================================================== + * Slide the hash table when sliding the window down (could be avoided with 32 + * bit values at the expense of memory usage). We slide even when level == 0 to + * keep the hash table consistent if we switch back to level > 0 later. + */ +local void slide_hash(s) + deflate_state *s; +{ + unsigned n, m; + Posf *p; + uInt wsize = s->w_size; + + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m - wsize : NIL); + } while (--n); + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m - wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif +} + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + s->status = INIT_STATE; /* to pass state test in deflateReset() */ + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = (uInt)windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = (uInt)memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->high_water = 0; /* nothing written to s->window yet */ + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= + * Check for a valid deflate stream state. Return 0 if ok, 1 if not. + */ +local int deflateStateCheck (strm) + z_streamp strm; +{ + deflate_state *s; + if (strm == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) + return 1; + s = strm->state; + if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE && +#ifdef GZIP + s->status != GZIP_STATE && +#endif + s->status != EXTRA_STATE && + s->status != NAME_STATE && + s->status != COMMENT_STATE && + s->status != HCRC_STATE && + s->status != BUSY_STATE && + s->status != FINISH_STATE)) + return 1; + return 0; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt str, n; + int wrap; + unsigned avail; + z_const unsigned char *next; + + if (deflateStateCheck(strm) || dictionary == Z_NULL) + return Z_STREAM_ERROR; + s = strm->state; + wrap = s->wrap; + if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) + return Z_STREAM_ERROR; + + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap == 1) + strm->adler = adler32(strm->adler, dictionary, dictLength); + s->wrap = 0; /* avoid computing Adler-32 in read_buf */ + + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s->w_size) { + if (wrap == 0) { /* already empty otherwise */ + CLEAR_HASH(s); + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + dictionary += dictLength - s->w_size; /* use the tail */ + dictLength = s->w_size; + } + + /* insert dictionary into window and hash */ + avail = strm->avail_in; + next = strm->next_in; + strm->avail_in = dictLength; + strm->next_in = (z_const Bytef *)dictionary; + fill_window(s); + while (s->lookahead >= MIN_MATCH) { + str = s->strstart; + n = s->lookahead - (MIN_MATCH-1); + do { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + } while (--n); + s->strstart = str; + s->lookahead = MIN_MATCH-1; + fill_window(s); + } + s->strstart += s->lookahead; + s->block_start = (long)s->strstart; + s->insert = s->lookahead; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + strm->next_in = next; + strm->avail_in = avail; + s->wrap = wrap; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength) + z_streamp strm; + Bytef *dictionary; + uInt *dictLength; +{ + deflate_state *s; + uInt len; + + if (deflateStateCheck(strm)) + return Z_STREAM_ERROR; + s = strm->state; + len = s->strstart + s->lookahead; + if (len > s->w_size) + len = s->w_size; + if (dictionary != Z_NULL && len) + zmemcpy(dictionary, s->window + s->strstart + s->lookahead - len, len); + if (dictLength != Z_NULL) + *dictLength = len; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateResetKeep (strm) + z_streamp strm; +{ + deflate_state *s; + + if (deflateStateCheck(strm)) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = +#ifdef GZIP + s->wrap == 2 ? GZIP_STATE : +#endif + s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + int ret; + + ret = deflateResetKeep(strm); + if (ret == Z_OK) + lm_init(strm->state); + return ret; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (deflateStateCheck(strm) || strm->state->wrap != 2) + return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePending (strm, pending, bits) + unsigned *pending; + int *bits; + z_streamp strm; +{ + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; + if (pending != Z_NULL) + *pending = strm->state->pending; + if (bits != Z_NULL) + *bits = strm->state->bi_valid; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + deflate_state *s; + int put; + + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; + s = strm->state; + if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) + return Z_BUF_ERROR; + do { + put = Buf_size - s->bi_valid; + if (put > bits) + put = bits; + s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); + s->bi_valid += put; + _tr_flush_bits(s); + value >>= put; + bits -= put; + } while (bits); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if ((strategy != s->strategy || func != configuration_table[level].func) && + s->high_water) { + /* Flush the last buffer: */ + int err = deflate(strm, Z_BLOCK); + if (err == Z_STREAM_ERROR) + return err; + if (strm->avail_out == 0) + return Z_BUF_ERROR; + } + if (s->level != level) { + if (s->level == 0 && s->matches != 0) { + if (s->matches == 1) + slide_hash(s); + else + CLEAR_HASH(s); + s->matches = 0; + } + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = (uInt)good_length; + s->max_lazy_match = (uInt)max_lazy; + s->nice_match = nice_length; + s->max_chain_length = (uInt)max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds for + * every combination of windowBits and memLevel. But even the conservative + * upper bound of about 14% expansion does not seem onerous for output buffer + * allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong complen, wraplen; + + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + + /* if can't get parameters, return conservative bound plus zlib wrapper */ + if (deflateStateCheck(strm)) + return complen + 6; + + /* compute wrapper length */ + s = strm->state; + switch (s->wrap) { + case 0: /* raw deflate */ + wraplen = 0; + break; + case 1: /* zlib wrapper */ + wraplen = 6 + (s->strstart ? 4 : 0); + break; +#ifdef GZIP + case 2: /* gzip wrapper */ + wraplen = 18; + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + Bytef *str; + if (s->gzhead->extra != Z_NULL) + wraplen += 2 + s->gzhead->extra_len; + str = s->gzhead->name; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + str = s->gzhead->comment; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + if (s->gzhead->hcrc) + wraplen += 2; + } + break; +#endif + default: /* for compiler happiness */ + wraplen = 6; + } + + /* if not default parameters, return conservative bound */ + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return complen + wraplen; + + /* default settings: return tight bound for that case */ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output, except for + * some deflate_stored() output, goes through this function so some + * applications may wish to modify it to avoid allocating a large + * strm->next_out buffer and copying into it. (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len; + deflate_state *s = strm->state; + + _tr_flush_bits(s); + len = s->pending; + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, s->pending_out, len); + strm->next_out += len; + s->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + s->pending -= len; + if (s->pending == 0) { + s->pending_out = s->pending_buf; + } +} + +/* =========================================================================== + * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1]. + */ +#define HCRC_UPDATE(beg) \ + do { \ + if (s->gzhead->hcrc && s->pending > (beg)) \ + strm->adler = crc32(strm->adler, s->pending_buf + (beg), \ + s->pending - (beg)); \ + } while (0) + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->avail_in != 0 && strm->next_in == Z_NULL) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + old_flush = s->last_flush; + s->last_flush = flush; + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Write the header */ + if (s->status == INIT_STATE) { + /* zlib header */ + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } +#ifdef GZIP + if (s->status == GZIP_STATE) { + /* gzip header */ + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != Z_NULL) { + ulg beg = s->pending; /* start of bytes to update crc */ + uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex; + while (s->pending + left > s->pending_buf_size) { + uInt copy = s->pending_buf_size - s->pending; + zmemcpy(s->pending_buf + s->pending, + s->gzhead->extra + s->gzindex, copy); + s->pending = s->pending_buf_size; + HCRC_UPDATE(beg); + s->gzindex += copy; + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + beg = 0; + left -= copy; + } + zmemcpy(s->pending_buf + s->pending, + s->gzhead->extra + s->gzindex, left); + s->pending += left; + HCRC_UPDATE(beg); + s->gzindex = 0; + } + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != Z_NULL) { + ulg beg = s->pending; /* start of bytes to update crc */ + int val; + do { + if (s->pending == s->pending_buf_size) { + HCRC_UPDATE(beg); + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + beg = 0; + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + HCRC_UPDATE(beg); + s->gzindex = 0; + } + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != Z_NULL) { + ulg beg = s->pending; /* start of bytes to update crc */ + int val; + do { + if (s->pending == s->pending_buf_size) { + HCRC_UPDATE(beg); + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + beg = 0; + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + HCRC_UPDATE(beg); + } + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) { + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + } + s->status = BUSY_STATE; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } +#endif + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = s->level == 0 ? deflate_stored(s, flush) : + s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; + + status = strm->state->status; + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (deflateStateCheck(source) || dest == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local unsigned read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + zmemcpy(buf, strm->next_in, len); + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, buf, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, buf, len); + } +#endif + strm->next_in += len; + strm->total_in += len; + + return len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->insert = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = (int)s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#else /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for FASTEST only + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#endif /* FASTEST */ + +#ifdef ZLIB_DEBUG + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* ZLIB_DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + unsigned n; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + slide_hash(s); + more += wsize; + } + if (s->strm->avail_in == 0) break; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead + s->insert >= MIN_MATCH) { + uInt str = s->strstart - s->insert; + s->ins_h = s->window[str]; + UPDATE_HASH(s, s->ins_h, s->window[str + 1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + while (s->insert) { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + s->insert--; + if (s->lookahead + s->insert < MIN_MATCH) + break; + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } + + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "not enough room for search"); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, last) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (last)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, last) { \ + FLUSH_BLOCK_ONLY(s, last); \ + if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ +} + +/* Maximum stored block length in deflate format (not including header). */ +#define MAX_STORED 65535 + +/* Minimum of a and b. */ +#define MIN(a, b) ((a) > (b) ? (b) : (a)) + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * + * In case deflateParams() is used to later switch to a non-zero compression + * level, s->matches (otherwise unused when storing) keeps track of the number + * of hash table slides to perform. If s->matches is 1, then one hash table + * slide will be done when switching. If s->matches is 2, the maximum value + * allowed here, then the hash table will be cleared, since two or more slides + * is the same as a clear. + * + * deflate_stored() is written to minimize the number of times an input byte is + * copied. It is most efficient with large input and output buffers, which + * maximizes the opportunites to have a single copy from next_in to next_out. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Smallest worthy block size when not flushing or finishing. By default + * this is 32K. This can be as small as 507 bytes for memLevel == 1. For + * large input and output buffers, the stored block size will be larger. + */ + unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size); + + /* Copy as many min_block or larger stored blocks directly to next_out as + * possible. If flushing, copy the remaining available input to next_out as + * stored blocks, if there is enough space. + */ + unsigned len, left, have, last = 0; + unsigned used = s->strm->avail_in; + do { + /* Set len to the maximum size block that we can copy directly with the + * available input data and output space. Set left to how much of that + * would be copied from what's left in the window. + */ + len = MAX_STORED; /* maximum deflate stored block length */ + have = (s->bi_valid + 42) >> 3; /* number of header bytes */ + if (s->strm->avail_out < have) /* need room for header */ + break; + /* maximum stored block length that will fit in avail_out: */ + have = s->strm->avail_out - have; + left = s->strstart - s->block_start; /* bytes left in window */ + if (len > (ulg)left + s->strm->avail_in) + len = left + s->strm->avail_in; /* limit len to the input */ + if (len > have) + len = have; /* limit len to the output */ + + /* If the stored block would be less than min_block in length, or if + * unable to copy all of the available input when flushing, then try + * copying to the window and the pending buffer instead. Also don't + * write an empty block when flushing -- deflate() does that. + */ + if (len < min_block && ((len == 0 && flush != Z_FINISH) || + flush == Z_NO_FLUSH || + len != left + s->strm->avail_in)) + break; + + /* Make a dummy stored block in pending to get the header bytes, + * including any pending bits. This also updates the debugging counts. + */ + last = flush == Z_FINISH && len == left + s->strm->avail_in ? 1 : 0; + _tr_stored_block(s, (char *)0, 0L, last); + + /* Replace the lengths in the dummy stored block with len. */ + s->pending_buf[s->pending - 4] = len; + s->pending_buf[s->pending - 3] = len >> 8; + s->pending_buf[s->pending - 2] = ~len; + s->pending_buf[s->pending - 1] = ~len >> 8; + + /* Write the stored block header bytes. */ + flush_pending(s->strm); + +#ifdef ZLIB_DEBUG + /* Update debugging counts for the data about to be copied. */ + s->compressed_len += len << 3; + s->bits_sent += len << 3; +#endif + + /* Copy uncompressed bytes from the window to next_out. */ + if (left) { + if (left > len) + left = len; + zmemcpy(s->strm->next_out, s->window + s->block_start, left); + s->strm->next_out += left; + s->strm->avail_out -= left; + s->strm->total_out += left; + s->block_start += left; + len -= left; + } + + /* Copy uncompressed bytes directly from next_in to next_out, updating + * the check value. + */ + if (len) { + read_buf(s->strm, s->strm->next_out, len); + s->strm->next_out += len; + s->strm->avail_out -= len; + s->strm->total_out += len; + } + } while (last == 0); + + /* Update the sliding window with the last s->w_size bytes of the copied + * data, or append all of the copied data to the existing window if less + * than s->w_size bytes were copied. Also update the number of bytes to + * insert in the hash tables, in the event that deflateParams() switches to + * a non-zero compression level. + */ + used -= s->strm->avail_in; /* number of input bytes directly copied */ + if (used) { + /* If any input was used, then no unused input remains in the window, + * therefore s->block_start == s->strstart. + */ + if (used >= s->w_size) { /* supplant the previous history */ + s->matches = 2; /* clear hash */ + zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); + s->strstart = s->w_size; + } + else { + if (s->window_size - s->strstart <= used) { + /* Slide the window down. */ + s->strstart -= s->w_size; + zmemcpy(s->window, s->window + s->w_size, s->strstart); + if (s->matches < 2) + s->matches++; /* add a pending slide_hash() */ + } + zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); + s->strstart += used; + } + s->block_start = s->strstart; + s->insert += MIN(used, s->w_size - s->insert); + } + if (s->high_water < s->strstart) + s->high_water = s->strstart; + + /* If the last block was written to next_out, then done. */ + if (last) + return finish_done; + + /* If flushing and all input has been consumed, then done. */ + if (flush != Z_NO_FLUSH && flush != Z_FINISH && + s->strm->avail_in == 0 && (long)s->strstart == s->block_start) + return block_done; + + /* Fill the window with any remaining input. */ + have = s->window_size - s->strstart - 1; + if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) { + /* Slide the window down. */ + s->block_start -= s->w_size; + s->strstart -= s->w_size; + zmemcpy(s->window, s->window + s->w_size, s->strstart); + if (s->matches < 2) + s->matches++; /* add a pending slide_hash() */ + have += s->w_size; /* more space now */ + } + if (have > s->strm->avail_in) + have = s->strm->avail_in; + if (have) { + read_buf(s->strm, s->window + s->strstart, have); + s->strstart += have; + } + if (s->high_water < s->strstart) + s->high_water = s->strstart; + + /* There was not enough avail_out to write a complete worthy or flushed + * stored block to next_out. Write a stored block to pending instead, if we + * have enough input for a worthy block, or if flushing and there is enough + * room for the remaining input as a stored block in the pending buffer. + */ + have = (s->bi_valid + 42) >> 3; /* number of header bytes */ + /* maximum stored block length that will fit in pending: */ + have = MIN(s->pending_buf_size - have, MAX_STORED); + min_block = MIN(have, s->w_size); + left = s->strstart - s->block_start; + if (left >= min_block || + ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH && + s->strm->avail_in == 0 && left <= have)) { + len = MIN(left, have); + last = flush == Z_FINISH && s->strm->avail_in == 0 && + len == left ? 1 : 0; + _tr_stored_block(s, (charf *)s->window + s->block_start, len, last); + s->block_start += len; + flush_pending(s->strm); + } + + /* We've done all we can with the available input and output. */ + return last ? finish_started : need_more; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} +#endif /* FASTEST */ + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt prev; /* byte at distance one to match */ + Bytef *scan, *strend; /* scan goes up to strend for length of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest run, plus one for the unrolled loop. + */ + if (s->lookahead <= MAX_MATCH) { + fill_window(s); + if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s->match_length = 0; + if (s->lookahead >= MIN_MATCH && s->strstart > 0) { + scan = s->window + s->strstart - 1; + prev = *scan; + if (prev == *++scan && prev == *++scan && prev == *++scan) { + strend = s->window + s->strstart + MAX_MATCH; + do { + } while (prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + s->match_length = MAX_MATCH - (uInt)(strend - scan); + if (s->match_length > s->lookahead) + s->match_length = s->lookahead; + } + Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, s->match_length); + + _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + s->strstart += s->match_length; + s->match_length = 0; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +local block_state deflate_huff(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s->lookahead == 0) { + fill_window(s); + if (s->lookahead == 0) { + if (flush == Z_NO_FLUSH) + return need_more; + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s->match_length = 0; + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} diff --git a/src/png/zlib/deflate.h b/src/png/zlib/deflate.h new file mode 100644 index 0000000000..23ecdd312b --- /dev/null +++ b/src/png/zlib/deflate.h @@ -0,0 +1,349 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2016 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define Buf_size 16 +/* size of bit buffer in bi_buf */ + +#define INIT_STATE 42 /* zlib header -> BUSY_STATE */ +#ifdef GZIP +# define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ +#endif +#define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ +#define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ +#define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ +#define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ +#define BUSY_STATE 113 /* deflate -> FINISH_STATE */ +#define FINISH_STATE 666 /* stream complete */ +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + const static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + ulg pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + ulg gzindex; /* where in extra, name, or comment */ + Byte method; /* can only be DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to suppress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + uInt insert; /* bytes at end of window left to insert */ + +#ifdef ZLIB_DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + + /* in trees.c */ +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef ZLIB_DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; +#else + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (uch)(length); \ + ush dist = (ush)(distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/src/png/zlib/gzclose.c b/src/png/zlib/gzclose.c new file mode 100644 index 0000000000..caeb99a317 --- /dev/null +++ b/src/png/zlib/gzclose.c @@ -0,0 +1,25 @@ +/* gzclose.c -- zlib gzclose() function + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* gzclose() is in a separate file so that it is linked in only if it is used. + That way the other gzclose functions can be used instead to avoid linking in + unneeded compression or decompression routines. */ +int ZEXPORT gzclose(file) + gzFile file; +{ +#ifndef NO_GZCOMPRESS + gz_statep state; + + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); +#else + return gzclose_r(file); +#endif +} diff --git a/src/png/zlib/gzguts.h b/src/png/zlib/gzguts.h new file mode 100644 index 0000000000..990a4d2514 --- /dev/null +++ b/src/png/zlib/gzguts.h @@ -0,0 +1,218 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif + +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif +#include + +#ifdef _WIN32 +# include +#endif + +#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) +# include +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +# define WIDECHAR +#endif + +#ifdef WINAPI_FAMILY +# define open _open +# define read _read +# define write _write +# define close _close +#endif + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS +/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 +/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +# ifdef VMS +# define NO_vsnprintf +# endif +# ifdef __OS400__ +# define NO_vsnprintf +# endif +# ifdef __MVS__ +# define NO_vsnprintf +# endif +#endif + +/* unlike snprintf (which is required in C99), _snprintf does not guarantee + null termination of the result -- however this is only used in gzlib.c where + the result is assured to fit in the space provided */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf _snprintf +#endif + +#ifndef local +# define local static +#endif +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifndef NO_STRERROR +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + +/* default i/o buffer size -- double this for output when reading (this and + twice this must be able to fit in an unsigned type) */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* exposed contents for gzgetc() macro */ + struct gzFile_s x; /* "x" for exposed */ + /* x.have: number of bytes available at x.next */ + /* x.next: next output data to deliver or write */ + /* x.pos: current position in uncompressed data */ + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer (double-sized when writing) */ + unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ + /* just for reading */ + int how; /* 0: get header, 1: copy, 2: decompress */ + z_off64_t start; /* where the gzip data started, for rewinding */ + int eof; /* true if end of input file reached */ + int past; /* true if read requested past end */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/src/png/zlib/gzlib.c b/src/png/zlib/gzlib.c new file mode 100644 index 0000000000..4105e6aff9 --- /dev/null +++ b/src/png/zlib/gzlib.c @@ -0,0 +1,637 @@ +/* gzlib.c -- zlib functions common to reading and writing gzip files + * Copyright (C) 2004-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +#if defined(_WIN32) && !defined(__BORLANDC__) && !defined(__MINGW32__) +# define LSEEK _lseeki64 +#else +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define LSEEK lseek64 +#else +# define LSEEK lseek +#endif +#endif + +/* Local functions */ +local void gz_reset OF((gz_statep)); +local gzFile gz_open OF((const void *, int, const char *)); + +#if defined UNDER_CE + +/* Map the Windows error number in ERROR to a locale-dependent error message + string and return a pointer to it. Typically, the values for ERROR come + from GetLastError. + + The string pointed to shall not be modified by the application, but may be + overwritten by a subsequent call to gz_strwinerror + + The gz_strwinerror function does not change the current setting of + GetLastError. */ +char ZLIB_INTERNAL *gz_strwinerror (error) + DWORD error; +{ + static char buf[1024]; + + wchar_t *msgbuf; + DWORD lasterr = GetLastError(); + DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + error, + 0, /* Default language */ + (LPVOID)&msgbuf, + 0, + NULL); + if (chars != 0) { + /* If there is an \r\n appended, zap it. */ + if (chars >= 2 + && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { + chars -= 2; + msgbuf[chars] = 0; + } + + if (chars > sizeof (buf) - 1) { + chars = sizeof (buf) - 1; + msgbuf[chars] = 0; + } + + wcstombs(buf, msgbuf, chars + 1); + LocalFree(msgbuf); + } + else { + sprintf(buf, "unknown win32 error (%ld)", error); + } + + SetLastError(lasterr); + return buf; +} + +#endif /* UNDER_CE */ + +/* Reset gzip file state */ +local void gz_reset(state) + gz_statep state; +{ + state->x.have = 0; /* no output data available */ + if (state->mode == GZ_READ) { /* for reading ... */ + state->eof = 0; /* not at end of file */ + state->past = 0; /* have not read past end yet */ + state->how = LOOK; /* look for gzip header */ + } + state->seek = 0; /* no seek request pending */ + gz_error(state, Z_OK, NULL); /* clear error */ + state->x.pos = 0; /* no uncompressed data yet */ + state->strm.avail_in = 0; /* no input data yet */ +} + +/* Open a gzip file either by name or file descriptor. */ +local gzFile gz_open(path, fd, mode) + const void *path; + int fd; + const char *mode; +{ + gz_statep state; + z_size_t len; + int oflag; +#ifdef O_CLOEXEC + int cloexec = 0; +#endif +#ifdef O_EXCL + int exclusive = 0; +#endif + + /* check input */ + if (path == NULL) + return NULL; + + /* allocate gzFile structure to return */ + state = (gz_statep)malloc(sizeof(gz_state)); + if (state == NULL) + return NULL; + state->size = 0; /* no buffers allocated yet */ + state->want = GZBUFSIZE; /* requested buffer size */ + state->msg = NULL; /* no error message yet */ + + /* interpret mode */ + state->mode = GZ_NONE; + state->level = Z_DEFAULT_COMPRESSION; + state->strategy = Z_DEFAULT_STRATEGY; + state->direct = 0; + while (*mode) { + if (*mode >= '0' && *mode <= '9') + state->level = *mode - '0'; + else + switch (*mode) { + case 'r': + state->mode = GZ_READ; + break; +#ifndef NO_GZCOMPRESS + case 'w': + state->mode = GZ_WRITE; + break; + case 'a': + state->mode = GZ_APPEND; + break; +#endif + case '+': /* can't read and write at the same time */ + free(state); + return NULL; + case 'b': /* ignore -- will request binary anyway */ + break; +#ifdef O_CLOEXEC + case 'e': + cloexec = 1; + break; +#endif +#ifdef O_EXCL + case 'x': + exclusive = 1; + break; +#endif + case 'f': + state->strategy = Z_FILTERED; + break; + case 'h': + state->strategy = Z_HUFFMAN_ONLY; + break; + case 'R': + state->strategy = Z_RLE; + break; + case 'F': + state->strategy = Z_FIXED; + break; + case 'T': + state->direct = 1; + break; + default: /* could consider as an error, but just ignore */ + ; + } + mode++; + } + + /* must provide an "r", "w", or "a" */ + if (state->mode == GZ_NONE) { + free(state); + return NULL; + } + + /* can't force transparent read */ + if (state->mode == GZ_READ) { + if (state->direct) { + free(state); + return NULL; + } + state->direct = 1; /* for empty file */ + } + + /* save the path name for error messages */ +#ifdef WIDECHAR + if (fd == -2) { + len = wcstombs(NULL, path, 0); + if (len == (z_size_t)-1) + len = 0; + } + else +#endif + len = strlen((const char *)path); + state->path = (char *)malloc(len + 1); + if (state->path == NULL) { + free(state); + return NULL; + } +#ifdef WIDECHAR + if (fd == -2) + if (len) + wcstombs(state->path, path, len + 1); + else + *(state->path) = 0; + else +#endif +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + (void)snprintf(state->path, len + 1, "%s", (const char *)path); +#else + strcpy(state->path, path); +#endif + + /* compute the flags for open() */ + oflag = +#ifdef O_LARGEFILE + O_LARGEFILE | +#endif +#ifdef O_BINARY + O_BINARY | +#endif +#ifdef O_CLOEXEC + (cloexec ? O_CLOEXEC : 0) | +#endif + (state->mode == GZ_READ ? + O_RDONLY : + (O_WRONLY | O_CREAT | +#ifdef O_EXCL + (exclusive ? O_EXCL : 0) | +#endif + (state->mode == GZ_WRITE ? + O_TRUNC : + O_APPEND))); + + /* open the file with the appropriate flags (or just use fd) */ + state->fd = fd > -1 ? fd : ( +#ifdef WIDECHAR + fd == -2 ? _wopen(path, oflag, 0666) : +#endif + open((const char *)path, oflag, 0666)); + if (state->fd == -1) { + free(state->path); + free(state); + return NULL; + } + if (state->mode == GZ_APPEND) { + LSEEK(state->fd, 0, SEEK_END); /* so gzoffset() is correct */ + state->mode = GZ_WRITE; /* simplify later checks */ + } + + /* save the current position for rewinding (only if reading) */ + if (state->mode == GZ_READ) { + state->start = LSEEK(state->fd, 0, SEEK_CUR); + if (state->start == -1) state->start = 0; + } + + /* initialize stream */ + gz_reset(state); + + /* return stream */ + return (gzFile)state; +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen64(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzdopen(fd, mode) + int fd; + const char *mode; +{ + char *path; /* identifier for error messages */ + gzFile gz; + + if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) + return NULL; +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + (void)snprintf(path, 7 + 3 * sizeof(int), "", fd); +#else + sprintf(path, "", fd); /* for debugging */ +#endif + gz = gz_open(path, fd, mode); + free(path); + return gz; +} + +/* -- see zlib.h -- */ +#ifdef WIDECHAR +gzFile ZEXPORT gzopen_w(path, mode) + const wchar_t *path; + const char *mode; +{ + return gz_open(path, -2, mode); +} +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzbuffer(file, size) + gzFile file; + unsigned size; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* make sure we haven't already allocated memory */ + if (state->size != 0) + return -1; + + /* check and set requested size */ + if ((size << 1) < size) + return -1; /* need to be able to double it */ + if (size < 2) + size = 2; /* need two bytes to check magic header */ + state->want = size; + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzrewind(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* back up and start over */ + if (LSEEK(state->fd, state->start, SEEK_SET) == -1) + return -1; + gz_reset(state); + return 0; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzseek64(file, offset, whence) + gzFile file; + z_off64_t offset; + int whence; +{ + unsigned n; + z_off64_t ret; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* check that there's no error */ + if (state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + + /* can only seek from start or relative to current position */ + if (whence != SEEK_SET && whence != SEEK_CUR) + return -1; + + /* normalize offset to a SEEK_CUR specification */ + if (whence == SEEK_SET) + offset -= state->x.pos; + else if (state->seek) + offset += state->skip; + state->seek = 0; + + /* if within raw area while reading, just go there */ + if (state->mode == GZ_READ && state->how == COPY && + state->x.pos + offset >= 0) { + ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR); + if (ret == -1) + return -1; + state->x.have = 0; + state->eof = 0; + state->past = 0; + state->seek = 0; + gz_error(state, Z_OK, NULL); + state->strm.avail_in = 0; + state->x.pos += offset; + return state->x.pos; + } + + /* calculate skip amount, rewinding if needed for back seek when reading */ + if (offset < 0) { + if (state->mode != GZ_READ) /* writing -- can't go backwards */ + return -1; + offset += state->x.pos; + if (offset < 0) /* before start of file! */ + return -1; + if (gzrewind(file) == -1) /* rewind, then skip to offset */ + return -1; + } + + /* if reading, skip what's in output buffer (one less gzgetc() check) */ + if (state->mode == GZ_READ) { + n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ? + (unsigned)offset : state->x.have; + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + offset -= n; + } + + /* request skip (if not zero) */ + if (offset) { + state->seek = 1; + state->skip = offset; + } + return state->x.pos + offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzseek(file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + z_off64_t ret; + + ret = gzseek64(file, (z_off64_t)offset, whence); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gztell64(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* return position */ + return state->x.pos + (state->seek ? state->skip : 0); +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gztell(file) + gzFile file; +{ + z_off64_t ret; + + ret = gztell64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzoffset64(file) + gzFile file; +{ + z_off64_t offset; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* compute and return effective offset in file */ + offset = LSEEK(state->fd, 0, SEEK_CUR); + if (offset == -1) + return -1; + if (state->mode == GZ_READ) /* reading */ + offset -= state->strm.avail_in; /* don't count buffered input */ + return offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzoffset(file) + gzFile file; +{ + z_off64_t ret; + + ret = gzoffset64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzeof(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return 0; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return 0; + + /* return end-of-file state */ + return state->mode == GZ_READ ? state->past : 0; +} + +/* -- see zlib.h -- */ +const char * ZEXPORT gzerror(file, errnum) + gzFile file; + int *errnum; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return NULL; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return NULL; + + /* return error information */ + if (errnum != NULL) + *errnum = state->err; + return state->err == Z_MEM_ERROR ? "out of memory" : + (state->msg == NULL ? "" : state->msg); +} + +/* -- see zlib.h -- */ +void ZEXPORT gzclearerr(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return; + + /* clear error and end-of-file */ + if (state->mode == GZ_READ) { + state->eof = 0; + state->past = 0; + } + gz_error(state, Z_OK, NULL); +} + +/* Create an error message in allocated memory and set state->err and + state->msg accordingly. Free any previous error message already there. Do + not try to free or allocate space if the error is Z_MEM_ERROR (out of + memory). Simply save the error message as a static string. If there is an + allocation failure constructing the error message, then convert the error to + out of memory. */ +void ZLIB_INTERNAL gz_error(state, err, msg) + gz_statep state; + int err; + const char *msg; +{ + /* free previously allocated message and clear */ + if (state->msg != NULL) { + if (state->err != Z_MEM_ERROR) + free(state->msg); + state->msg = NULL; + } + + /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */ + if (err != Z_OK && err != Z_BUF_ERROR) + state->x.have = 0; + + /* set error code, and if no message, then done */ + state->err = err; + if (msg == NULL) + return; + + /* for an out of memory error, return literal string when requested */ + if (err == Z_MEM_ERROR) + return; + + /* construct error message with path */ + if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) == + NULL) { + state->err = Z_MEM_ERROR; + return; + } +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, + "%s%s%s", state->path, ": ", msg); +#else + strcpy(state->msg, state->path); + strcat(state->msg, ": "); + strcat(state->msg, msg); +#endif +} + +#ifndef INT_MAX +/* portably return maximum value for an int (when limits.h presumed not + available) -- we need to do this to cover cases where 2's complement not + used, since C standard permits 1's complement and sign-bit representations, + otherwise we could just use ((unsigned)-1) >> 1 */ +unsigned ZLIB_INTERNAL gz_intmax() +{ + unsigned p, q; + + p = 1; + do { + q = p; + p <<= 1; + p++; + } while (p > q); + return q >> 1; +} +#endif diff --git a/src/png/zlib/gzread.c b/src/png/zlib/gzread.c new file mode 100644 index 0000000000..956b91ea7d --- /dev/null +++ b/src/png/zlib/gzread.c @@ -0,0 +1,654 @@ +/* gzread.c -- zlib functions for reading gzip files + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); +local int gz_avail OF((gz_statep)); +local int gz_look OF((gz_statep)); +local int gz_decomp OF((gz_statep)); +local int gz_fetch OF((gz_statep)); +local int gz_skip OF((gz_statep, z_off64_t)); +local z_size_t gz_read OF((gz_statep, voidp, z_size_t)); + +/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from + state->fd, and update state->eof, state->err, and state->msg as appropriate. + This function needs to loop on read(), since read() is not guaranteed to + read the number of bytes requested, depending on the type of descriptor. */ +local int gz_load(state, buf, len, have) + gz_statep state; + unsigned char *buf; + unsigned len; + unsigned *have; +{ + int ret; + unsigned get, max = ((unsigned)-1 >> 2) + 1; + + *have = 0; + do { + get = len - *have; + if (get > max) + get = max; + ret = read(state->fd, buf + *have, get); + if (ret <= 0) + break; + *have += (unsigned)ret; + } while (*have < len); + if (ret < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (ret == 0) + state->eof = 1; + return 0; +} + +/* Load up input buffer and set eof flag if last data loaded -- return -1 on + error, 0 otherwise. Note that the eof flag is set when the end of the input + file is reached, even though there may be unused data in the buffer. Once + that data has been used, no more attempts will be made to read the file. + If strm->avail_in != 0, then the current data is moved to the beginning of + the input buffer, and then the remainder of the buffer is loaded with the + available data from the input file. */ +local int gz_avail(state) + gz_statep state; +{ + unsigned got; + z_streamp strm = &(state->strm); + + if (state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + if (state->eof == 0) { + if (strm->avail_in) { /* copy what's there to the start */ + unsigned char *p = state->in; + unsigned const char *q = strm->next_in; + unsigned n = strm->avail_in; + do { + *p++ = *q++; + } while (--n); + } + if (gz_load(state, state->in + strm->avail_in, + state->size - strm->avail_in, &got) == -1) + return -1; + strm->avail_in += got; + strm->next_in = state->in; + } + return 0; +} + +/* Look for gzip header, set up for inflate or copy. state->x.have must be 0. + If this is the first time in, allocate required memory. state->how will be + left unchanged if there is no more input data available, will be set to COPY + if there is no gzip header and direct copying will be performed, or it will + be set to GZIP for decompression. If direct copying, then leftover input + data from the input buffer will be copied to the output buffer. In that + case, all further file reads will be directly to either the output buffer or + a user buffer. If decompressing, the inflate state will be initialized. + gz_look() will return 0 on success or -1 on failure. */ +local int gz_look(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + /* allocate read buffers and inflate memory */ + if (state->size == 0) { + /* allocate buffers */ + state->in = (unsigned char *)malloc(state->want); + state->out = (unsigned char *)malloc(state->want << 1); + if (state->in == NULL || state->out == NULL) { + free(state->out); + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + state->size = state->want; + + /* allocate inflate memory */ + state->strm.zalloc = Z_NULL; + state->strm.zfree = Z_NULL; + state->strm.opaque = Z_NULL; + state->strm.avail_in = 0; + state->strm.next_in = Z_NULL; + if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */ + free(state->out); + free(state->in); + state->size = 0; + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* get at least the magic bytes in the input buffer */ + if (strm->avail_in < 2) { + if (gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) + return 0; + } + + /* look for gzip magic bytes -- if there, do gzip decoding (note: there is + a logical dilemma here when considering the case of a partially written + gzip file, to wit, if a single 31 byte is written, then we cannot tell + whether this is a single-byte file, or just a partially written gzip + file -- for here we assume that if a gzip file is being written, then + the header will be written in a single operation, so that reading a + single byte is sufficient indication that it is not a gzip file) */ + if (strm->avail_in > 1 && + strm->next_in[0] == 31 && strm->next_in[1] == 139) { + inflateReset(strm); + state->how = GZIP; + state->direct = 0; + return 0; + } + + /* no gzip header -- if we were decoding gzip before, then this is trailing + garbage. Ignore the trailing garbage and finish. */ + if (state->direct == 0) { + strm->avail_in = 0; + state->eof = 1; + state->x.have = 0; + return 0; + } + + /* doing raw i/o, copy any leftover input to output -- this assumes that + the output buffer is larger than the input buffer, which also assures + space for gzungetc() */ + state->x.next = state->out; + if (strm->avail_in) { + memcpy(state->x.next, strm->next_in, strm->avail_in); + state->x.have = strm->avail_in; + strm->avail_in = 0; + } + state->how = COPY; + state->direct = 1; + return 0; +} + +/* Decompress from input to the provided next_out and avail_out in the state. + On return, state->x.have and state->x.next point to the just decompressed + data. If the gzip stream completes, state->how is reset to LOOK to look for + the next gzip stream or raw data, once state->x.have is depleted. Returns 0 + on success, -1 on failure. */ +local int gz_decomp(state) + gz_statep state; +{ + int ret = Z_OK; + unsigned had; + z_streamp strm = &(state->strm); + + /* fill output buffer up to end of deflate stream */ + had = strm->avail_out; + do { + /* get more input for inflate() */ + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) { + gz_error(state, Z_BUF_ERROR, "unexpected end of file"); + break; + } + + /* decompress and handle errors */ + ret = inflate(strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { + gz_error(state, Z_STREAM_ERROR, + "internal error: inflate stream corrupt"); + return -1; + } + if (ret == Z_MEM_ERROR) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ + gz_error(state, Z_DATA_ERROR, + strm->msg == NULL ? "compressed data error" : strm->msg); + return -1; + } + } while (strm->avail_out && ret != Z_STREAM_END); + + /* update available output */ + state->x.have = had - strm->avail_out; + state->x.next = strm->next_out - state->x.have; + + /* if the gzip stream completed successfully, look for another */ + if (ret == Z_STREAM_END) + state->how = LOOK; + + /* good decompression */ + return 0; +} + +/* Fetch data and put it in the output buffer. Assumes state->x.have is 0. + Data is either copied from the input file or decompressed from the input + file depending on state->how. If state->how is LOOK, then a gzip header is + looked for to determine whether to copy or decompress. Returns -1 on error, + otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the + end of the input file has been reached and all data has been processed. */ +local int gz_fetch(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + do { + switch(state->how) { + case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */ + if (gz_look(state) == -1) + return -1; + if (state->how == LOOK) + return 0; + break; + case COPY: /* -> COPY */ + if (gz_load(state, state->out, state->size << 1, &(state->x.have)) + == -1) + return -1; + state->x.next = state->out; + return 0; + case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */ + strm->avail_out = state->size << 1; + strm->next_out = state->out; + if (gz_decomp(state) == -1) + return -1; + } + } while (state->x.have == 0 && (!state->eof || strm->avail_in)); + return 0; +} + +/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ +local int gz_skip(state, len) + gz_statep state; + z_off64_t len; +{ + unsigned n; + + /* skip over len bytes or reach end-of-file, whichever comes first */ + while (len) + /* skip over whatever is in output buffer */ + if (state->x.have) { + n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ? + (unsigned)len : state->x.have; + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + len -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) + break; + + /* need more data to skip -- load up output buffer */ + else { + /* get more output, looking for header if required */ + if (gz_fetch(state) == -1) + return -1; + } + return 0; +} + +/* Read len bytes into buf from file, or less than len up to the end of the + input. Return the number of bytes read. If zero is returned, either the + end of file was reached, or there was an error. state->err must be + consulted in that case to determine which. */ +local z_size_t gz_read(state, buf, len) + gz_statep state; + voidp buf; + z_size_t len; +{ + z_size_t got; + unsigned n; + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return 0; + } + + /* get len bytes to buf, or less than len if at the end */ + got = 0; + do { + /* set n to the maximum amount of len that fits in an unsigned int */ + n = -1; + if (n > len) + n = len; + + /* first just try copying data from the output buffer */ + if (state->x.have) { + if (state->x.have < n) + n = state->x.have; + memcpy(buf, state->x.next, n); + state->x.next += n; + state->x.have -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) { + state->past = 1; /* tried to read past end */ + break; + } + + /* need output data -- for small len or new stream load up our output + buffer */ + else if (state->how == LOOK || n < (state->size << 1)) { + /* get more output, looking for header if required */ + if (gz_fetch(state) == -1) + return 0; + continue; /* no progress yet -- go back to copy above */ + /* the copy above assures that we will leave with space in the + output buffer, allowing at least one gzungetc() to succeed */ + } + + /* large len -- read directly into user buffer */ + else if (state->how == COPY) { /* read directly */ + if (gz_load(state, (unsigned char *)buf, n, &n) == -1) + return 0; + } + + /* large len -- decompress directly into user buffer */ + else { /* state->how == GZIP */ + state->strm.avail_out = n; + state->strm.next_out = (unsigned char *)buf; + if (gz_decomp(state) == -1) + return 0; + n = state->x.have; + state->x.have = 0; + } + + /* update progress */ + len -= n; + buf = (char *)buf + n; + got += n; + state->x.pos += n; + } while (len); + + /* return number of bytes read into user buffer */ + return got; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids a flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in an int"); + return -1; + } + + /* read len or fewer bytes to buf */ + len = gz_read(state, buf, len); + + /* check for an error */ + if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + + /* return the number of bytes read (this is assured to fit in an int) */ + return (int)len; +} + +/* -- see zlib.h -- */ +z_size_t ZEXPORT gzfread(buf, size, nitems, file) + voidp buf; + z_size_t size; + z_size_t nitems; + gzFile file; +{ + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return 0; + + /* compute bytes to read -- error on overflow */ + len = nitems * size; + if (size && len / size != nitems) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); + return 0; + } + + /* read len or fewer bytes to buf, return the number of full items read */ + return len ? gz_read(state, buf, len) / size : 0; +} + +/* -- see zlib.h -- */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +#else +# undef gzgetc +#endif +int ZEXPORT gzgetc(file) + gzFile file; +{ + int ret; + unsigned char buf[1]; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* try output buffer (no need to check for skip request) */ + if (state->x.have) { + state->x.have--; + state->x.pos++; + return *(state->x.next)++; + } + + /* nothing there -- try gz_read() */ + ret = gz_read(state, buf, 1); + return ret < 1 ? -1 : buf[0]; +} + +int ZEXPORT gzgetc_(file) +gzFile file; +{ + return gzgetc(file); +} + +/* -- see zlib.h -- */ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* can't push EOF */ + if (c < 0) + return -1; + + /* if output buffer empty, put byte at end (allows more pushing) */ + if (state->x.have == 0) { + state->x.have = 1; + state->x.next = state->out + (state->size << 1) - 1; + state->x.next[0] = (unsigned char)c; + state->x.pos--; + state->past = 0; + return c; + } + + /* if no room, give up (must have already done a gzungetc()) */ + if (state->x.have == (state->size << 1)) { + gz_error(state, Z_DATA_ERROR, "out of room to push characters"); + return -1; + } + + /* slide output data if needed and insert byte before existing data */ + if (state->x.next == state->out) { + unsigned char *src = state->out + state->x.have; + unsigned char *dest = state->out + (state->size << 1); + while (src > state->out) + *--dest = *--src; + state->x.next = dest; + } + state->x.have++; + state->x.next--; + state->x.next[0] = (unsigned char)c; + state->x.pos--; + state->past = 0; + return c; +} + +/* -- see zlib.h -- */ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + unsigned left, n; + char *str; + unsigned char *eol; + gz_statep state; + + /* check parameters and get internal structure */ + if (file == NULL || buf == NULL || len < 1) + return NULL; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return NULL; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return NULL; + } + + /* copy output bytes up to new line or len - 1, whichever comes first -- + append a terminating zero to the string (we don't check for a zero in + the contents, let the user worry about that) */ + str = buf; + left = (unsigned)len - 1; + if (left) do { + /* assure that something is in the output buffer */ + if (state->x.have == 0 && gz_fetch(state) == -1) + return NULL; /* error */ + if (state->x.have == 0) { /* end of file */ + state->past = 1; /* read past end */ + break; /* return what we have */ + } + + /* look for end-of-line in current output buffer */ + n = state->x.have > left ? left : state->x.have; + eol = (unsigned char *)memchr(state->x.next, '\n', n); + if (eol != NULL) + n = (unsigned)(eol - state->x.next) + 1; + + /* copy through end-of-line, or remainder if not found */ + memcpy(buf, state->x.next, n); + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + left -= n; + buf += n; + } while (left && eol == NULL); + + /* return terminated string, or if nothing, end of file */ + if (buf == str) + return NULL; + buf[0] = 0; + return str; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzdirect(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* if the state is not known, but we can find out, then do so (this is + mainly for right after a gzopen() or gzdopen()) */ + if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) + (void)gz_look(state); + + /* return 1 if transparent, 0 if processing a gzip stream */ + return state->direct; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_r(file) + gzFile file; +{ + int ret, err; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return Z_STREAM_ERROR; + + /* free memory and close file */ + if (state->size) { + inflateEnd(&(state->strm)); + free(state->out); + free(state->in); + } + err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK; + gz_error(state, Z_OK, NULL); + free(state->path); + ret = close(state->fd); + free(state); + return ret ? Z_ERRNO : err; +} diff --git a/src/png/zlib/gzwrite.c b/src/png/zlib/gzwrite.c new file mode 100644 index 0000000000..c7b5651d70 --- /dev/null +++ b/src/png/zlib/gzwrite.c @@ -0,0 +1,665 @@ +/* gzwrite.c -- zlib functions for writing gzip files + * Copyright (C) 2004-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_init OF((gz_statep)); +local int gz_comp OF((gz_statep, int)); +local int gz_zero OF((gz_statep, z_off64_t)); +local z_size_t gz_write OF((gz_statep, voidpc, z_size_t)); + +/* Initialize state for writing a gzip file. Mark initialization by setting + state->size to non-zero. Return -1 on a memory allocation failure, or 0 on + success. */ +local int gz_init(state) + gz_statep state; +{ + int ret; + z_streamp strm = &(state->strm); + + /* allocate input buffer (double size for gzprintf) */ + state->in = (unsigned char *)malloc(state->want << 1); + if (state->in == NULL) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* only need output buffer and deflate state if compressing */ + if (!state->direct) { + /* allocate output buffer */ + state->out = (unsigned char *)malloc(state->want); + if (state->out == NULL) { + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* allocate deflate memory, set up for gzip compression */ + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + ret = deflateInit2(strm, state->level, Z_DEFLATED, + MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy); + if (ret != Z_OK) { + free(state->out); + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + strm->next_in = NULL; + } + + /* mark state as initialized */ + state->size = state->want; + + /* initialize write buffer if compressing */ + if (!state->direct) { + strm->avail_out = state->size; + strm->next_out = state->out; + state->x.next = strm->next_out; + } + return 0; +} + +/* Compress whatever is at avail_in and next_in and write to the output file. + Return -1 if there is an error writing to the output file or if gz_init() + fails to allocate memory, otherwise 0. flush is assumed to be a valid + deflate() flush value. If flush is Z_FINISH, then the deflate() state is + reset to start a new gzip stream. If gz->direct is true, then simply write + to the output file without compressing, and ignore flush. */ +local int gz_comp(state, flush) + gz_statep state; + int flush; +{ + int ret, writ; + unsigned have, put, max = ((unsigned)-1 >> 2) + 1; + z_streamp strm = &(state->strm); + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return -1; + + /* write directly if requested */ + if (state->direct) { + while (strm->avail_in) { + put = strm->avail_in > max ? max : strm->avail_in; + writ = write(state->fd, strm->next_in, put); + if (writ < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + strm->avail_in -= (unsigned)writ; + strm->next_in += writ; + } + return 0; + } + + /* run deflate() on provided input until it produces no more output */ + ret = Z_OK; + do { + /* write out current buffer contents if full, or if flushing, but if + doing Z_FINISH then don't write until we get to Z_STREAM_END */ + if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && + (flush != Z_FINISH || ret == Z_STREAM_END))) { + while (strm->next_out > state->x.next) { + put = strm->next_out - state->x.next > (int)max ? max : + (unsigned)(strm->next_out - state->x.next); + writ = write(state->fd, state->x.next, put); + if (writ < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + state->x.next += writ; + } + if (strm->avail_out == 0) { + strm->avail_out = state->size; + strm->next_out = state->out; + state->x.next = state->out; + } + } + + /* compress */ + have = strm->avail_out; + ret = deflate(strm, flush); + if (ret == Z_STREAM_ERROR) { + gz_error(state, Z_STREAM_ERROR, + "internal error: deflate stream corrupt"); + return -1; + } + have -= strm->avail_out; + } while (have); + + /* if that completed a deflate stream, allow another to start */ + if (flush == Z_FINISH) + deflateReset(strm); + + /* all done, no errors */ + return 0; +} + +/* Compress len zeros to output. Return -1 on a write error or memory + allocation failure by gz_comp(), or 0 on success. */ +local int gz_zero(state, len) + gz_statep state; + z_off64_t len; +{ + int first; + unsigned n; + z_streamp strm = &(state->strm); + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + + /* compress len zeros (len guaranteed > 0) */ + first = 1; + while (len) { + n = GT_OFF(state->size) || (z_off64_t)state->size > len ? + (unsigned)len : state->size; + if (first) { + memset(state->in, 0, n); + first = 0; + } + strm->avail_in = n; + strm->next_in = state->in; + state->x.pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + len -= n; + } + return 0; +} + +/* Write len bytes from buf to file. Return the number of bytes written. If + the returned value is less than len, then there was an error. */ +local z_size_t gz_write(state, buf, len) + gz_statep state; + voidpc buf; + z_size_t len; +{ + z_size_t put = len; + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* for small len, copy to input buffer, otherwise compress directly */ + if (len < state->size) { + /* copy to input buffer, compress when full */ + do { + unsigned have, copy; + + if (state->strm.avail_in == 0) + state->strm.next_in = state->in; + have = (unsigned)((state->strm.next_in + state->strm.avail_in) - + state->in); + copy = state->size - have; + if (copy > len) + copy = len; + memcpy(state->in + have, buf, copy); + state->strm.avail_in += copy; + state->x.pos += copy; + buf = (const char *)buf + copy; + len -= copy; + if (len && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } while (len); + } + else { + /* consume whatever's left in the input buffer */ + if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* directly compress user buffer to file */ + state->strm.next_in = (z_const Bytef *)buf; + do { + unsigned n = (unsigned)-1; + if (n > len) + n = len; + state->strm.avail_in = n; + state->x.pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + len -= n; + } while (len); + } + + /* input was all buffered or compressed */ + return put; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids a flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return 0; + } + + /* write len bytes from buf (the return value will fit in an int) */ + return (int)gz_write(state, buf, len); +} + +/* -- see zlib.h -- */ +z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) + voidpc buf; + z_size_t size; + z_size_t nitems; + gzFile file; +{ + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* compute bytes to read -- error on overflow */ + len = nitems * size; + if (size && len / size != nitems) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); + return 0; + } + + /* write len bytes to buf, return the number of full items written */ + return len ? gz_write(state, buf, len) / size : 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned have; + unsigned char buf[1]; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* try writing to input buffer for speed (state->size == 0 if buffer not + initialized) */ + if (state->size) { + if (strm->avail_in == 0) + strm->next_in = state->in; + have = (unsigned)((strm->next_in + strm->avail_in) - state->in); + if (have < state->size) { + state->in[have] = (unsigned char)c; + strm->avail_in++; + state->x.pos++; + return c & 0xff; + } + } + + /* no room in buffer or not initialized, use gz_write() */ + buf[0] = (unsigned char)c; + if (gz_write(state, buf, 1) != 1) + return -1; + return c & 0xff; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputs(file, str) + gzFile file; + const char *str; +{ + int ret; + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; + + /* write string */ + len = strlen(str); + ret = gz_write(state, str, len); + return ret == 0 && len != 0 ? -1 : ret; +} + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +#include + +/* -- see zlib.h -- */ +int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) +{ + int len; + unsigned left; + char *next; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return state->err; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return state->err; + } + + /* do the printf() into the input buffer, put length in len -- the input + buffer is double-sized just for this function, so there is guaranteed to + be state->size bytes available after the current contents */ + if (strm->avail_in == 0) + strm->next_in = state->in; + next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in); + next[state->size - 1] = 0; +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(next, format, va); + for (len = 0; len < state->size; len++) + if (next[len] == 0) break; +# else + len = vsprintf(next, format, va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(next, state->size, format, va); + len = strlen(next); +# else + len = vsnprintf(next, state->size, format, va); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0) + return 0; + + /* update buffer and position, compress first half if past that */ + strm->avail_in += (unsigned)len; + state->x.pos += len; + if (strm->avail_in >= state->size) { + left = strm->avail_in - state->size; + strm->avail_in = state->size; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return state->err; + memcpy(state->in, state->in + state->size, left); + strm->next_in = state->in; + strm->avail_in = left; + } + return len; +} + +int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) +{ + va_list va; + int ret; + + va_start(va, format); + ret = gzvprintf(file, format, va); + va_end(va); + return ret; +} + +#else /* !STDC && !Z_HAVE_STDARG_H */ + +/* -- see zlib.h -- */ +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + unsigned len, left; + char *next; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that can really pass pointer in ints */ + if (sizeof(int) != sizeof(void *)) + return Z_STREAM_ERROR; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return state->error; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return state->error; + } + + /* do the printf() into the input buffer, put length in len -- the input + buffer is double-sized just for this function, so there is guaranteed to + be state->size bytes available after the current contents */ + if (strm->avail_in == 0) + strm->next_in = state->in; + next = (char *)(strm->next_in + strm->avail_in); + next[state->size - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < size; len++) + if (next[len] == 0) + break; +# else + len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, + a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(next); +# else + len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len == 0 || len >= state->size || next[state->size - 1] != 0) + return 0; + + /* update buffer and position, compress first half if past that */ + strm->avail_in += len; + state->x.pos += len; + if (strm->avail_in >= state->size) { + left = strm->avail_in - state->size; + strm->avail_in = state->size; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return state->err; + memcpy(state->in, state->in + state->size, left); + strm->next_in = state->in; + strm->avail_in = left; + } + return (int)len; +} + +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzflush(file, flush) + gzFile file; + int flush; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* check flush parameter */ + if (flush < 0 || flush > Z_FINISH) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return state->err; + } + + /* compress remaining data with requested flush */ + (void)gz_comp(state, flush); + return state->err; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzsetparams(file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* if no change is requested, then do nothing */ + if (level == state->level && strategy == state->strategy) + return Z_OK; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return state->err; + } + + /* change compression parameters for subsequent input */ + if (state->size) { + /* flush previous input with previous parameters before changing */ + if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1) + return state->err; + deflateParams(strm, level, strategy); + } + state->level = level; + state->strategy = strategy; + return Z_OK; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_w(file) + gzFile file; +{ + int ret = Z_OK; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're writing */ + if (state->mode != GZ_WRITE) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + ret = state->err; + } + + /* flush, free memory, and close file */ + if (gz_comp(state, Z_FINISH) == -1) + ret = state->err; + if (state->size) { + if (!state->direct) { + (void)deflateEnd(&(state->strm)); + free(state->out); + } + free(state->in); + } + gz_error(state, Z_OK, NULL); + free(state->path); + if (close(state->fd) == -1) + ret = Z_ERRNO; + free(state); + return ret; +} diff --git a/src/png/zlib/infback.c b/src/png/zlib/infback.c new file mode 100644 index 0000000000..59679ecbfc --- /dev/null +++ b/src/png/zlib/infback.c @@ -0,0 +1,640 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = (uInt)windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->wnext = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + state->length = (unsigned)here.val; + + /* process literal */ + if (here.op == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/src/png/zlib/inffast.c b/src/png/zlib/inffast.c new file mode 100644 index 0000000000..0dbd1dbc09 --- /dev/null +++ b/src/png/zlib/inffast.c @@ -0,0 +1,323 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef ASMINF +# pragma message("Assembler code may have bugs -- use at your own risk") +#else + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void ZLIB_INTERNAL inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *in; /* local strm->next_in */ + z_const unsigned char FAR *last; /* have enough input while in < last */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code here; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in; + last = in + (strm->avail_in - 5); + out = strm->next_out; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + wnext = state->wnext; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(*in++) << bits; + bits += 8; + hold += (unsigned long)(*in++) << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op == 0) { /* literal */ + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + *out++ = (unsigned char)(here.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(*in++) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(*in++) << bits; + bits += 8; + hold += (unsigned long)(*in++) << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(*in++) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(*in++) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + *out++ = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + *out++ = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + *out++ = *from++; + } while (--len); + continue; + } +#endif + } + from = window; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + *out++ = *from++; + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + *out++ = *from++; + } while (--op); + from = window; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + *out++ = *from++; + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + *out++ = *from++; + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + *out++ = *from++; + *out++ = *from++; + *out++ = *from++; + len -= 3; + } + if (len) { + *out++ = *from++; + if (len > 1) + *out++ = *from++; + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + *out++ = *from++; + *out++ = *from++; + *out++ = *from++; + len -= 3; + } while (len > 2); + if (len) { + *out++ = *from++; + if (len > 1) + *out++ = *from++; + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[here.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[here.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in; + strm->next_out = out; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/src/png/zlib/inffast.h b/src/png/zlib/inffast.h new file mode 100644 index 0000000000..e5c1aa4ca8 --- /dev/null +++ b/src/png/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/src/png/zlib/inffixed.h b/src/png/zlib/inffixed.h new file mode 100644 index 0000000000..d628327769 --- /dev/null +++ b/src/png/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. + It is part of the implementation of this library and is + subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/src/png/zlib/inflate.c b/src/png/zlib/inflate.c new file mode 100644 index 0000000000..ac333e8c2e --- /dev/null +++ b/src/png/zlib/inflate.c @@ -0,0 +1,1561 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common wnext == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local int inflateStateCheck OF((z_streamp strm)); +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, + unsigned copy)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, + unsigned len)); + +local int inflateStateCheck(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) + return 1; + state = (struct inflate_state FAR *)strm->state; + if (state == Z_NULL || state->strm != strm || + state->mode < HEAD || state->mode > SYNC) + return 1; + return 0; +} + +int ZEXPORT inflateResetKeep(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + if (state->wrap) /* to support ill-conceived Java test suite */ + strm->adler = state->wrap & 1; + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + return inflateResetKeep(strm); +} + +int ZEXPORT inflateReset2(strm, windowBits) +z_streamp strm; +int windowBits; +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 5; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + int ret; + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->strm = strm; + state->window = Z_NULL; + state->mode = HEAD; /* to pass state test in inflateReset2() */ + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { + ZFREE(strm, state); + strm->state = Z_NULL; + } + return ret; +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += (unsigned)value << state->bits; + state->bits += (uInt)bits; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, + state.lencode[low].bits, state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, end, copy) +z_streamp strm; +const Bytef *end; +unsigned copy; +{ + struct inflate_state FAR *state; + unsigned dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state->wsize) { + zmemcpy(state->window, end - state->wsize, state->wsize); + state->wnext = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->wnext; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->wnext, end - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, end - copy, copy); + state->wnext = copy; + state->whave = state->wsize; + } + else { + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (inflateStateCheck(strm) || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + if (state->wbits == 0) + state->wbits = 15; + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (state->wbits == 0) + state->wbits = len; + if (len > 15 || len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if ((state->flags & 0x0200) && (state->wrap & 4)) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = (Bytef)len; + } while (len && copy < have); + if ((state->flags & 0x0200) && (state->wrap & 4)) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = (Bytef)len; + } while (len && copy < have); + if ((state->flags & 0x0200) && (state->wrap & 4)) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if ((state->wrap & 4) && hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = ZSWAP32(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (const code FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + if (state->mode == TYPE) + state->back = -1; + break; + } + state->back = 0; + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + state->mode = LIT; + break; + } + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; + state->mode = TYPE; + break; + } + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(here.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; + state->mode = DIST; + case DIST: + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->wnext - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if ((state->wrap & 4) && out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if ((state->wrap & 4) && ( +#ifdef GUNZIP + state->flags ? hold : +#endif + ZSWAP32(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (out != strm->avail_out && state->mode < BAD && + (state->mode < CHECK || flush != Z_FINISH))) + if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if ((state->wrap & 4) && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = (int)state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (inflateStateCheck(strm)) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) +z_streamp strm; +Bytef *dictionary; +uInt *dictLength; +{ + struct inflate_state FAR *state; + + /* check state */ + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* copy dictionary */ + if (state->whave && dictionary != Z_NULL) { + zmemcpy(dictionary, state->window + state->wnext, + state->whave - state->wnext); + zmemcpy(dictionary + state->whave - state->wnext, + state->window, state->wnext); + } + if (dictLength != Z_NULL) + *dictLength = state->whave; + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long dictid; + int ret; + + /* check state */ + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary identifier */ + if (state->mode == DICT) { + dictid = adler32(0L, Z_NULL, 0); + dictid = adler32(dictid, dictionary, dictLength); + if (dictid != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary + dictLength, dictLength); + if (ret) { + state->mode = MEM; + return Z_MEM_ERROR; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +const unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (inflateStateCheck(source) || dest == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); + copy->strm = dest; + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} + +int ZEXPORT inflateUndermine(strm, subvert) +z_streamp strm; +int subvert; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + state->sane = !subvert; + return Z_OK; +#else + (void)subvert; + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +int ZEXPORT inflateValidate(strm, check) +z_streamp strm; +int check; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (check) + state->wrap |= 4; + else + state->wrap &= ~4; + return Z_OK; +} + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) + return -(1L << 16); + state = (struct inflate_state FAR *)strm->state; + return (long)(((unsigned long)((long)state->back)) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} + +unsigned long ZEXPORT inflateCodesUsed(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (inflateStateCheck(strm)) return (unsigned long)-1; + state = (struct inflate_state FAR *)strm->state; + return (unsigned long)(state->next - state->codes); +} diff --git a/src/png/zlib/inflate.h b/src/png/zlib/inflate.h new file mode 100644 index 0000000000..a46cce6b6d --- /dev/null +++ b/src/png/zlib/inflate.h @@ -0,0 +1,125 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD = 16180, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* State maintained between inflate() calls -- approximately 7K bytes, not + including the allocated sliding window, which is up to 32K bytes. */ +struct inflate_state { + z_streamp strm; /* pointer back to this zlib stream */ + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip, + bit 2 true to validate check value */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; diff --git a/src/png/zlib/inftrees.c b/src/png/zlib/inftrees.c new file mode 100644 index 0000000000..2ea08fc13e --- /dev/null +++ b/src/png/zlib/inftrees.c @@ -0,0 +1,304 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.11 Copyright 1995-2017 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code here; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + unsigned match; /* use base and extra for symbol >= match */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 77, 202}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + match = 20; + break; + case LENS: + base = lbase; + extra = lext; + match = 257; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + match = 0; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here.bits = (unsigned char)(len - drop); + if (work[sym] + 1U < match) { + here.op = (unsigned char)0; + here.val = work[sym]; + } + else if (work[sym] >= match) { + here.op = (unsigned char)(extra[work[sym] - match]); + here.val = base[work[sym] - match]; + } + else { + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff != 0) { + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + next[huff] = here; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/src/png/zlib/inftrees.h b/src/png/zlib/inftrees.h new file mode 100644 index 0000000000..baa53a0b1a --- /dev/null +++ b/src/png/zlib/inftrees.h @@ -0,0 +1,62 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/src/png/zlib/make_vms.com b/src/png/zlib/make_vms.com new file mode 100644 index 0000000000..65e9d0cbc8 --- /dev/null +++ b/src/png/zlib/make_vms.com @@ -0,0 +1,867 @@ +$! make libz under VMS written by +$! Martin P.J. Zinser +$! +$! In case of problems with the install you might contact me at +$! zinser@zinser.no-ip.info(preferred) or +$! martin.zinser@eurexchange.com (work) +$! +$! Make procedure history for Zlib +$! +$!------------------------------------------------------------------------------ +$! Version history +$! 0.01 20060120 First version to receive a number +$! 0.02 20061008 Adapt to new Makefile.in +$! 0.03 20091224 Add support for large file check +$! 0.04 20100110 Add new gzclose, gzlib, gzread, gzwrite +$! 0.05 20100221 Exchange zlibdefs.h by zconf.h.in +$! 0.06 20120111 Fix missing amiss_err, update zconf_h.in, fix new exmples +$! subdir path, update module search in makefile.in +$! 0.07 20120115 Triggered by work done by Alexey Chupahin completly redesigned +$! shared image creation +$! 0.08 20120219 Make it work on VAX again, pre-load missing symbols to shared +$! image +$! 0.09 20120305 SMS. P1 sets builder ("MMK", "MMS", " " (built-in)). +$! "" -> automatic, preference: MMK, MMS, built-in. +$! +$ on error then goto err_exit +$! +$ true = 1 +$ false = 0 +$ tmpnam = "temp_" + f$getjpi("","pid") +$ tt = tmpnam + ".txt" +$ tc = tmpnam + ".c" +$ th = tmpnam + ".h" +$ define/nolog tconfig 'th' +$ its_decc = false +$ its_vaxc = false +$ its_gnuc = false +$ s_case = False +$! +$! Setup variables holding "config" information +$! +$ Make = "''p1'" +$ name = "Zlib" +$ version = "?.?.?" +$ v_string = "ZLIB_VERSION" +$ v_file = "zlib.h" +$ ccopt = "/include = []" +$ lopts = "" +$ dnsrl = "" +$ aconf_in_file = "zconf.h.in#zconf.h_in#zconf_h.in" +$ conf_check_string = "" +$ linkonly = false +$ optfile = name + ".opt" +$ mapfile = name + ".map" +$ libdefs = "" +$ vax = f$getsyi("HW_MODEL").lt.1024 +$ axp = f$getsyi("HW_MODEL").ge.1024 .and. f$getsyi("HW_MODEL").lt.4096 +$ ia64 = f$getsyi("HW_MODEL").ge.4096 +$! +$! 2012-03-05 SMS. +$! Why is this needed? And if it is needed, why not simply ".not. vax"? +$! +$!!! if axp .or. ia64 then set proc/parse=extended +$! +$ whoami = f$parse(f$environment("Procedure"),,,,"NO_CONCEAL") +$ mydef = F$parse(whoami,,,"DEVICE") +$ mydir = f$parse(whoami,,,"DIRECTORY") - "][" +$ myproc = f$parse(whoami,,,"Name") + f$parse(whoami,,,"type") +$! +$! Check for MMK/MMS +$! +$ if (Make .eqs. "") +$ then +$ If F$Search ("Sys$System:MMS.EXE") .nes. "" Then Make = "MMS" +$ If F$Type (MMK) .eqs. "STRING" Then Make = "MMK" +$ else +$ Make = f$edit( Make, "trim") +$ endif +$! +$ gosub find_version +$! +$ open/write topt tmp.opt +$ open/write optf 'optfile' +$! +$ gosub check_opts +$! +$! Look for the compiler used +$! +$ gosub check_compiler +$ close topt +$ close optf +$! +$ if its_decc +$ then +$ ccopt = "/prefix=all" + ccopt +$ if f$trnlnm("SYS") .eqs. "" +$ then +$ if axp +$ then +$ define sys sys$library: +$ else +$ ccopt = "/decc" + ccopt +$ define sys decc$library_include: +$ endif +$ endif +$! +$! 2012-03-05 SMS. +$! Why /NAMES = AS_IS? Why not simply ".not. vax"? And why not on VAX? +$! +$ if axp .or. ia64 +$ then +$ ccopt = ccopt + "/name=as_is/opt=(inline=speed)" +$ s_case = true +$ endif +$ endif +$ if its_vaxc .or. its_gnuc +$ then +$ if f$trnlnm("SYS").eqs."" then define sys sys$library: +$ endif +$! +$! Build a fake configure input header +$! +$ open/write conf_hin config.hin +$ write conf_hin "#undef _LARGEFILE64_SOURCE" +$ close conf_hin +$! +$! +$ i = 0 +$FIND_ACONF: +$ fname = f$element(i,"#",aconf_in_file) +$ if fname .eqs. "#" then goto AMISS_ERR +$ if f$search(fname) .eqs. "" +$ then +$ i = i + 1 +$ goto find_aconf +$ endif +$ open/read/err=aconf_err aconf_in 'fname' +$ open/write aconf zconf.h +$ACONF_LOOP: +$ read/end_of_file=aconf_exit aconf_in line +$ work = f$edit(line, "compress,trim") +$ if f$extract(0,6,work) .nes. "#undef" +$ then +$ if f$extract(0,12,work) .nes. "#cmakedefine" +$ then +$ write aconf line +$ endif +$ else +$ cdef = f$element(1," ",work) +$ gosub check_config +$ endif +$ goto aconf_loop +$ACONF_EXIT: +$ write aconf "" +$ write aconf "/* VMS specifics added by make_vms.com: */" +$ write aconf "#define VMS 1" +$ write aconf "#include " +$ write aconf "#include " +$ write aconf "#ifdef _LARGEFILE" +$ write aconf "# define off64_t __off64_t" +$ write aconf "# define fopen64 fopen" +$ write aconf "# define fseeko64 fseeko" +$ write aconf "# define lseek64 lseek" +$ write aconf "# define ftello64 ftell" +$ write aconf "#endif" +$ write aconf "#if !defined( __VAX) && (__CRTL_VER >= 70312000)" +$ write aconf "# define HAVE_VSNPRINTF" +$ write aconf "#endif" +$ close aconf_in +$ close aconf +$ if f$search("''th'") .nes. "" then delete 'th';* +$! Build the thing plain or with mms +$! +$ write sys$output "Compiling Zlib sources ..." +$ if make.eqs."" +$ then +$ if (f$search( "example.obj;*") .nes. "") then delete example.obj;* +$ if (f$search( "minigzip.obj;*") .nes. "") then delete minigzip.obj;* +$ CALL MAKE adler32.OBJ "CC ''CCOPT' adler32" - + adler32.c zlib.h zconf.h +$ CALL MAKE compress.OBJ "CC ''CCOPT' compress" - + compress.c zlib.h zconf.h +$ CALL MAKE crc32.OBJ "CC ''CCOPT' crc32" - + crc32.c zlib.h zconf.h +$ CALL MAKE deflate.OBJ "CC ''CCOPT' deflate" - + deflate.c deflate.h zutil.h zlib.h zconf.h +$ CALL MAKE gzclose.OBJ "CC ''CCOPT' gzclose" - + gzclose.c zutil.h zlib.h zconf.h +$ CALL MAKE gzlib.OBJ "CC ''CCOPT' gzlib" - + gzlib.c zutil.h zlib.h zconf.h +$ CALL MAKE gzread.OBJ "CC ''CCOPT' gzread" - + gzread.c zutil.h zlib.h zconf.h +$ CALL MAKE gzwrite.OBJ "CC ''CCOPT' gzwrite" - + gzwrite.c zutil.h zlib.h zconf.h +$ CALL MAKE infback.OBJ "CC ''CCOPT' infback" - + infback.c zutil.h inftrees.h inflate.h inffast.h inffixed.h +$ CALL MAKE inffast.OBJ "CC ''CCOPT' inffast" - + inffast.c zutil.h zlib.h zconf.h inffast.h +$ CALL MAKE inflate.OBJ "CC ''CCOPT' inflate" - + inflate.c zutil.h zlib.h zconf.h infblock.h +$ CALL MAKE inftrees.OBJ "CC ''CCOPT' inftrees" - + inftrees.c zutil.h zlib.h zconf.h inftrees.h +$ CALL MAKE trees.OBJ "CC ''CCOPT' trees" - + trees.c deflate.h zutil.h zlib.h zconf.h +$ CALL MAKE uncompr.OBJ "CC ''CCOPT' uncompr" - + uncompr.c zlib.h zconf.h +$ CALL MAKE zutil.OBJ "CC ''CCOPT' zutil" - + zutil.c zutil.h zlib.h zconf.h +$ write sys$output "Building Zlib ..." +$ CALL MAKE libz.OLB "lib/crea libz.olb *.obj" *.OBJ +$ write sys$output "Building example..." +$ CALL MAKE example.OBJ "CC ''CCOPT' [.test]example" - + [.test]example.c zlib.h zconf.h +$ call make example.exe "LINK example,libz.olb/lib" example.obj libz.olb +$ write sys$output "Building minigzip..." +$ CALL MAKE minigzip.OBJ "CC ''CCOPT' [.test]minigzip" - + [.test]minigzip.c zlib.h zconf.h +$ call make minigzip.exe - + "LINK minigzip,libz.olb/lib" - + minigzip.obj libz.olb +$ else +$ gosub crea_mms +$ write sys$output "Make ''name' ''version' with ''Make' " +$ 'make' +$ endif +$! +$! Create shareable image +$! +$ gosub crea_olist +$ write sys$output "Creating libzshr.exe" +$ call map_2_shopt 'mapfile' 'optfile' +$ LINK_'lopts'/SHARE=libzshr.exe modules.opt/opt,'optfile'/opt +$ write sys$output "Zlib build completed" +$ delete/nolog tmp.opt;* +$ exit +$AMISS_ERR: +$ write sys$output "No source for config.hin found." +$ write sys$output "Tried any of ''aconf_in_file'" +$ goto err_exit +$CC_ERR: +$ write sys$output "C compiler required to build ''name'" +$ goto err_exit +$ERR_EXIT: +$ set message/facil/ident/sever/text +$ close/nolog optf +$ close/nolog topt +$ close/nolog aconf_in +$ close/nolog aconf +$ close/nolog out +$ close/nolog min +$ close/nolog mod +$ close/nolog h_in +$ write sys$output "Exiting..." +$ exit 2 +$! +$! +$MAKE: SUBROUTINE !SUBROUTINE TO CHECK DEPENDENCIES +$ V = 'F$Verify(0) +$! P1 = What we are trying to make +$! P2 = Command to make it +$! P3 - P8 What it depends on +$ +$ If F$Search(P1) .Eqs. "" Then Goto Makeit +$ Time = F$CvTime(F$File(P1,"RDT")) +$arg=3 +$Loop: +$ Argument = P'arg +$ If Argument .Eqs. "" Then Goto Exit +$ El=0 +$Loop2: +$ File = F$Element(El," ",Argument) +$ If File .Eqs. " " Then Goto Endl +$ AFile = "" +$Loop3: +$ OFile = AFile +$ AFile = F$Search(File) +$ If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl +$ If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit +$ Goto Loop3 +$NextEL: +$ El = El + 1 +$ Goto Loop2 +$EndL: +$ arg=arg+1 +$ If arg .Le. 8 Then Goto Loop +$ Goto Exit +$ +$Makeit: +$ VV=F$VERIFY(0) +$ write sys$output P2 +$ 'P2 +$ VV='F$Verify(VV) +$Exit: +$ If V Then Set Verify +$ENDSUBROUTINE +$!------------------------------------------------------------------------------ +$! +$! Check command line options and set symbols accordingly +$! +$!------------------------------------------------------------------------------ +$! Version history +$! 0.01 20041206 First version to receive a number +$! 0.02 20060126 Add new "HELP" target +$ CHECK_OPTS: +$ i = 1 +$ OPT_LOOP: +$ if i .lt. 9 +$ then +$ cparm = f$edit(p'i',"upcase") +$! +$! Check if parameter actually contains something +$! +$ if f$edit(cparm,"trim") .nes. "" +$ then +$ if cparm .eqs. "DEBUG" +$ then +$ ccopt = ccopt + "/noopt/deb" +$ lopts = lopts + "/deb" +$ endif +$ if f$locate("CCOPT=",cparm) .lt. f$length(cparm) +$ then +$ start = f$locate("=",cparm) + 1 +$ len = f$length(cparm) - start +$ ccopt = ccopt + f$extract(start,len,cparm) +$ if f$locate("AS_IS",f$edit(ccopt,"UPCASE")) .lt. f$length(ccopt) - + then s_case = true +$ endif +$ if cparm .eqs. "LINK" then linkonly = true +$ if f$locate("LOPTS=",cparm) .lt. f$length(cparm) +$ then +$ start = f$locate("=",cparm) + 1 +$ len = f$length(cparm) - start +$ lopts = lopts + f$extract(start,len,cparm) +$ endif +$ if f$locate("CC=",cparm) .lt. f$length(cparm) +$ then +$ start = f$locate("=",cparm) + 1 +$ len = f$length(cparm) - start +$ cc_com = f$extract(start,len,cparm) + if (cc_com .nes. "DECC") .and. - + (cc_com .nes. "VAXC") .and. - + (cc_com .nes. "GNUC") +$ then +$ write sys$output "Unsupported compiler choice ''cc_com' ignored" +$ write sys$output "Use DECC, VAXC, or GNUC instead" +$ else +$ if cc_com .eqs. "DECC" then its_decc = true +$ if cc_com .eqs. "VAXC" then its_vaxc = true +$ if cc_com .eqs. "GNUC" then its_gnuc = true +$ endif +$ endif +$ if f$locate("MAKE=",cparm) .lt. f$length(cparm) +$ then +$ start = f$locate("=",cparm) + 1 +$ len = f$length(cparm) - start +$ mmks = f$extract(start,len,cparm) +$ if (mmks .eqs. "MMK") .or. (mmks .eqs. "MMS") +$ then +$ make = mmks +$ else +$ write sys$output "Unsupported make choice ''mmks' ignored" +$ write sys$output "Use MMK or MMS instead" +$ endif +$ endif +$ if cparm .eqs. "HELP" then gosub bhelp +$ endif +$ i = i + 1 +$ goto opt_loop +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! Look for the compiler used +$! +$! Version history +$! 0.01 20040223 First version to receive a number +$! 0.02 20040229 Save/set value of decc$no_rooted_search_lists +$! 0.03 20060202 Extend handling of GNU C +$! 0.04 20090402 Compaq -> hp +$CHECK_COMPILER: +$ if (.not. (its_decc .or. its_vaxc .or. its_gnuc)) +$ then +$ its_decc = (f$search("SYS$SYSTEM:DECC$COMPILER.EXE") .nes. "") +$ its_vaxc = .not. its_decc .and. (F$Search("SYS$System:VAXC.Exe") .nes. "") +$ its_gnuc = .not. (its_decc .or. its_vaxc) .and. (f$trnlnm("gnu_cc") .nes. "") +$ endif +$! +$! Exit if no compiler available +$! +$ if (.not. (its_decc .or. its_vaxc .or. its_gnuc)) +$ then goto CC_ERR +$ else +$ if its_decc +$ then +$ write sys$output "CC compiler check ... hp C" +$ if f$trnlnm("decc$no_rooted_search_lists") .nes. "" +$ then +$ dnrsl = f$trnlnm("decc$no_rooted_search_lists") +$ endif +$ define/nolog decc$no_rooted_search_lists 1 +$ else +$ if its_vaxc then write sys$output "CC compiler check ... VAX C" +$ if its_gnuc +$ then +$ write sys$output "CC compiler check ... GNU C" +$ if f$trnlnm(topt) then write topt "gnu_cc:[000000]gcclib.olb/lib" +$ if f$trnlnm(optf) then write optf "gnu_cc:[000000]gcclib.olb/lib" +$ cc = "gcc" +$ endif +$ if f$trnlnm(topt) then write topt "sys$share:vaxcrtl.exe/share" +$ if f$trnlnm(optf) then write optf "sys$share:vaxcrtl.exe/share" +$ endif +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! If MMS/MMK are available dump out the descrip.mms if required +$! +$CREA_MMS: +$ write sys$output "Creating descrip.mms..." +$ create descrip.mms +$ open/append out descrip.mms +$ copy sys$input: out +$ deck +# descrip.mms: MMS description file for building zlib on VMS +# written by Martin P.J. Zinser +# + +OBJS = adler32.obj, compress.obj, crc32.obj, gzclose.obj, gzlib.obj\ + gzread.obj, gzwrite.obj, uncompr.obj, infback.obj\ + deflate.obj, trees.obj, zutil.obj, inflate.obj, \ + inftrees.obj, inffast.obj + +$ eod +$ write out "CFLAGS=", ccopt +$ write out "LOPTS=", lopts +$ write out "all : example.exe minigzip.exe libz.olb" +$ copy sys$input: out +$ deck + @ write sys$output " Example applications available" + +libz.olb : libz.olb($(OBJS)) + @ write sys$output " libz available" + +example.exe : example.obj libz.olb + link $(LOPTS) example,libz.olb/lib + +minigzip.exe : minigzip.obj libz.olb + link $(LOPTS) minigzip,libz.olb/lib + +clean : + delete *.obj;*,libz.olb;*,*.opt;*,*.exe;* + + +# Other dependencies. +adler32.obj : adler32.c zutil.h zlib.h zconf.h +compress.obj : compress.c zlib.h zconf.h +crc32.obj : crc32.c zutil.h zlib.h zconf.h +deflate.obj : deflate.c deflate.h zutil.h zlib.h zconf.h +example.obj : [.test]example.c zlib.h zconf.h +gzclose.obj : gzclose.c zutil.h zlib.h zconf.h +gzlib.obj : gzlib.c zutil.h zlib.h zconf.h +gzread.obj : gzread.c zutil.h zlib.h zconf.h +gzwrite.obj : gzwrite.c zutil.h zlib.h zconf.h +inffast.obj : inffast.c zutil.h zlib.h zconf.h inftrees.h inffast.h +inflate.obj : inflate.c zutil.h zlib.h zconf.h +inftrees.obj : inftrees.c zutil.h zlib.h zconf.h inftrees.h +minigzip.obj : [.test]minigzip.c zlib.h zconf.h +trees.obj : trees.c deflate.h zutil.h zlib.h zconf.h +uncompr.obj : uncompr.c zlib.h zconf.h +zutil.obj : zutil.c zutil.h zlib.h zconf.h +infback.obj : infback.c zutil.h inftrees.h inflate.h inffast.h inffixed.h +$ eod +$ close out +$ return +$!------------------------------------------------------------------------------ +$! +$! Read list of core library sources from makefile.in and create options +$! needed to build shareable image +$! +$CREA_OLIST: +$ open/read min makefile.in +$ open/write mod modules.opt +$ src_check_list = "OBJZ =#OBJG =" +$MRLOOP: +$ read/end=mrdone min rec +$ i = 0 +$SRC_CHECK_LOOP: +$ src_check = f$element(i, "#", src_check_list) +$ i = i+1 +$ if src_check .eqs. "#" then goto mrloop +$ if (f$extract(0,6,rec) .nes. src_check) then goto src_check_loop +$ rec = rec - src_check +$ gosub extra_filnam +$ if (f$element(1,"\",rec) .eqs. "\") then goto mrloop +$MRSLOOP: +$ read/end=mrdone min rec +$ gosub extra_filnam +$ if (f$element(1,"\",rec) .nes. "\") then goto mrsloop +$MRDONE: +$ close min +$ close mod +$ return +$!------------------------------------------------------------------------------ +$! +$! Take record extracted in crea_olist and split it into single filenames +$! +$EXTRA_FILNAM: +$ myrec = f$edit(rec - "\", "trim,compress") +$ i = 0 +$FELOOP: +$ srcfil = f$element(i," ", myrec) +$ if (srcfil .nes. " ") +$ then +$ write mod f$parse(srcfil,,,"NAME"), ".obj" +$ i = i + 1 +$ goto feloop +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! Find current Zlib version number +$! +$FIND_VERSION: +$ open/read h_in 'v_file' +$hloop: +$ read/end=hdone h_in rec +$ rec = f$edit(rec,"TRIM") +$ if (f$extract(0,1,rec) .nes. "#") then goto hloop +$ rec = f$edit(rec - "#", "TRIM") +$ if f$element(0," ",rec) .nes. "define" then goto hloop +$ if f$element(1," ",rec) .eqs. v_string +$ then +$ version = 'f$element(2," ",rec)' +$ goto hdone +$ endif +$ goto hloop +$hdone: +$ close h_in +$ return +$!------------------------------------------------------------------------------ +$! +$CHECK_CONFIG: +$! +$ in_ldef = f$locate(cdef,libdefs) +$ if (in_ldef .lt. f$length(libdefs)) +$ then +$ write aconf "#define ''cdef' 1" +$ libdefs = f$extract(0,in_ldef,libdefs) + - + f$extract(in_ldef + f$length(cdef) + 1, - + f$length(libdefs) - in_ldef - f$length(cdef) - 1, - + libdefs) +$ else +$ if (f$type('cdef') .eqs. "INTEGER") +$ then +$ write aconf "#define ''cdef' ", 'cdef' +$ else +$ if (f$type('cdef') .eqs. "STRING") +$ then +$ write aconf "#define ''cdef' ", """", '''cdef'', """" +$ else +$ gosub check_cc_def +$ endif +$ endif +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! Check if this is a define relating to the properties of the C/C++ +$! compiler +$! +$ CHECK_CC_DEF: +$ if (cdef .eqs. "_LARGEFILE64_SOURCE") +$ then +$ copy sys$input: 'tc' +$ deck +#include "tconfig" +#define _LARGEFILE +#include + +int main(){ +FILE *fp; + fp = fopen("temp.txt","r"); + fseeko(fp,1,SEEK_SET); + fclose(fp); +} + +$ eod +$ test_inv = false +$ comm_h = false +$ gosub cc_prop_check +$ return +$ endif +$ write aconf "/* ", line, " */" +$ return +$!------------------------------------------------------------------------------ +$! +$! Check for properties of C/C++ compiler +$! +$! Version history +$! 0.01 20031020 First version to receive a number +$! 0.02 20031022 Added logic for defines with value +$! 0.03 20040309 Make sure local config file gets not deleted +$! 0.04 20041230 Also write include for configure run +$! 0.05 20050103 Add processing of "comment defines" +$CC_PROP_CHECK: +$ cc_prop = true +$ is_need = false +$ is_need = (f$extract(0,4,cdef) .eqs. "NEED") .or. (test_inv .eq. true) +$ if f$search(th) .eqs. "" then create 'th' +$ set message/nofac/noident/nosever/notext +$ on error then continue +$ cc 'tmpnam' +$ if .not. ($status) then cc_prop = false +$ on error then continue +$! The headers might lie about the capabilities of the RTL +$ link 'tmpnam',tmp.opt/opt +$ if .not. ($status) then cc_prop = false +$ set message/fac/ident/sever/text +$ on error then goto err_exit +$ delete/nolog 'tmpnam'.*;*/exclude='th' +$ if (cc_prop .and. .not. is_need) .or. - + (.not. cc_prop .and. is_need) +$ then +$ write sys$output "Checking for ''cdef'... yes" +$ if f$type('cdef_val'_yes) .nes. "" +$ then +$ if f$type('cdef_val'_yes) .eqs. "INTEGER" - + then call write_config f$fao("#define !AS !UL",cdef,'cdef_val'_yes) +$ if f$type('cdef_val'_yes) .eqs. "STRING" - + then call write_config f$fao("#define !AS !AS",cdef,'cdef_val'_yes) +$ else +$ call write_config f$fao("#define !AS 1",cdef) +$ endif +$ if (cdef .eqs. "HAVE_FSEEKO") .or. (cdef .eqs. "_LARGE_FILES") .or. - + (cdef .eqs. "_LARGEFILE64_SOURCE") then - + call write_config f$string("#define _LARGEFILE 1") +$ else +$ write sys$output "Checking for ''cdef'... no" +$ if (comm_h) +$ then + call write_config f$fao("/* !AS */",line) +$ else +$ if f$type('cdef_val'_no) .nes. "" +$ then +$ if f$type('cdef_val'_no) .eqs. "INTEGER" - + then call write_config f$fao("#define !AS !UL",cdef,'cdef_val'_no) +$ if f$type('cdef_val'_no) .eqs. "STRING" - + then call write_config f$fao("#define !AS !AS",cdef,'cdef_val'_no) +$ else +$ call write_config f$fao("#undef !AS",cdef) +$ endif +$ endif +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! Check for properties of C/C++ compiler with multiple result values +$! +$! Version history +$! 0.01 20040127 First version +$! 0.02 20050103 Reconcile changes from cc_prop up to version 0.05 +$CC_MPROP_CHECK: +$ cc_prop = true +$ i = 1 +$ idel = 1 +$ MT_LOOP: +$ if f$type(result_'i') .eqs. "STRING" +$ then +$ set message/nofac/noident/nosever/notext +$ on error then continue +$ cc 'tmpnam'_'i' +$ if .not. ($status) then cc_prop = false +$ on error then continue +$! The headers might lie about the capabilities of the RTL +$ link 'tmpnam'_'i',tmp.opt/opt +$ if .not. ($status) then cc_prop = false +$ set message/fac/ident/sever/text +$ on error then goto err_exit +$ delete/nolog 'tmpnam'_'i'.*;* +$ if (cc_prop) +$ then +$ write sys$output "Checking for ''cdef'... ", mdef_'i' +$ if f$type(mdef_'i') .eqs. "INTEGER" - + then call write_config f$fao("#define !AS !UL",cdef,mdef_'i') +$ if f$type('cdef_val'_yes) .eqs. "STRING" - + then call write_config f$fao("#define !AS !AS",cdef,mdef_'i') +$ goto msym_clean +$ else +$ i = i + 1 +$ goto mt_loop +$ endif +$ endif +$ write sys$output "Checking for ''cdef'... no" +$ call write_config f$fao("#undef !AS",cdef) +$ MSYM_CLEAN: +$ if (idel .le. msym_max) +$ then +$ delete/sym mdef_'idel' +$ idel = idel + 1 +$ goto msym_clean +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! Write configuration to both permanent and temporary config file +$! +$! Version history +$! 0.01 20031029 First version to receive a number +$! +$WRITE_CONFIG: SUBROUTINE +$ write aconf 'p1' +$ open/append confh 'th' +$ write confh 'p1' +$ close confh +$ENDSUBROUTINE +$!------------------------------------------------------------------------------ +$! +$! Analyze the project map file and create the symbol vector for a shareable +$! image from it +$! +$! Version history +$! 0.01 20120128 First version +$! 0.02 20120226 Add pre-load logic +$! +$ MAP_2_SHOPT: Subroutine +$! +$ SAY := "WRITE_ SYS$OUTPUT" +$! +$ IF F$SEARCH("''P1'") .EQS. "" +$ THEN +$ SAY "MAP_2_SHOPT-E-NOSUCHFILE: Error, inputfile ''p1' not available" +$ goto exit_m2s +$ ENDIF +$ IF "''P2'" .EQS. "" +$ THEN +$ SAY "MAP_2_SHOPT: Error, no output file provided" +$ goto exit_m2s +$ ENDIF +$! +$ module1 = "deflate#deflateEnd#deflateInit_#deflateParams#deflateSetDictionary" +$ module2 = "gzclose#gzerror#gzgetc#gzgets#gzopen#gzprintf#gzputc#gzputs#gzread" +$ module3 = "gzseek#gztell#inflate#inflateEnd#inflateInit_#inflateSetDictionary" +$ module4 = "inflateSync#uncompress#zlibVersion#compress" +$ open/read map 'p1 +$ if axp .or. ia64 +$ then +$ open/write aopt a.opt +$ open/write bopt b.opt +$ write aopt " CASE_SENSITIVE=YES" +$ write bopt "SYMBOL_VECTOR= (-" +$ mod_sym_num = 1 +$ MOD_SYM_LOOP: +$ if f$type(module'mod_sym_num') .nes. "" +$ then +$ mod_in = 0 +$ MOD_SYM_IN: +$ shared_proc = f$element(mod_in, "#", module'mod_sym_num') +$ if shared_proc .nes. "#" +$ then +$ write aopt f$fao(" symbol_vector=(!AS/!AS=PROCEDURE)",- + f$edit(shared_proc,"upcase"),shared_proc) +$ write bopt f$fao("!AS=PROCEDURE,-",shared_proc) +$ mod_in = mod_in + 1 +$ goto mod_sym_in +$ endif +$ mod_sym_num = mod_sym_num + 1 +$ goto mod_sym_loop +$ endif +$MAP_LOOP: +$ read/end=map_end map line +$ if (f$locate("{",line).lt. f$length(line)) .or. - + (f$locate("global:", line) .lt. f$length(line)) +$ then +$ proc = true +$ goto map_loop +$ endif +$ if f$locate("}",line).lt. f$length(line) then proc = false +$ if f$locate("local:", line) .lt. f$length(line) then proc = false +$ if proc +$ then +$ shared_proc = f$edit(line,"collapse") +$ chop_semi = f$locate(";", shared_proc) +$ if chop_semi .lt. f$length(shared_proc) then - + shared_proc = f$extract(0, chop_semi, shared_proc) +$ write aopt f$fao(" symbol_vector=(!AS/!AS=PROCEDURE)",- + f$edit(shared_proc,"upcase"),shared_proc) +$ write bopt f$fao("!AS=PROCEDURE,-",shared_proc) +$ endif +$ goto map_loop +$MAP_END: +$ close/nolog aopt +$ close/nolog bopt +$ open/append libopt 'p2' +$ open/read aopt a.opt +$ open/read bopt b.opt +$ALOOP: +$ read/end=aloop_end aopt line +$ write libopt line +$ goto aloop +$ALOOP_END: +$ close/nolog aopt +$ sv = "" +$BLOOP: +$ read/end=bloop_end bopt svn +$ if (svn.nes."") +$ then +$ if (sv.nes."") then write libopt sv +$ sv = svn +$ endif +$ goto bloop +$BLOOP_END: +$ write libopt f$extract(0,f$length(sv)-2,sv), "-" +$ write libopt ")" +$ close/nolog bopt +$ delete/nolog/noconf a.opt;*,b.opt;* +$ else +$ if vax +$ then +$ open/append libopt 'p2' +$ mod_sym_num = 1 +$ VMOD_SYM_LOOP: +$ if f$type(module'mod_sym_num') .nes. "" +$ then +$ mod_in = 0 +$ VMOD_SYM_IN: +$ shared_proc = f$element(mod_in, "#", module'mod_sym_num') +$ if shared_proc .nes. "#" +$ then +$ write libopt f$fao("UNIVERSAL=!AS",- + f$edit(shared_proc,"upcase")) +$ mod_in = mod_in + 1 +$ goto vmod_sym_in +$ endif +$ mod_sym_num = mod_sym_num + 1 +$ goto vmod_sym_loop +$ endif +$VMAP_LOOP: +$ read/end=vmap_end map line +$ if (f$locate("{",line).lt. f$length(line)) .or. - + (f$locate("global:", line) .lt. f$length(line)) +$ then +$ proc = true +$ goto vmap_loop +$ endif +$ if f$locate("}",line).lt. f$length(line) then proc = false +$ if f$locate("local:", line) .lt. f$length(line) then proc = false +$ if proc +$ then +$ shared_proc = f$edit(line,"collapse") +$ chop_semi = f$locate(";", shared_proc) +$ if chop_semi .lt. f$length(shared_proc) then - + shared_proc = f$extract(0, chop_semi, shared_proc) +$ write libopt f$fao("UNIVERSAL=!AS",- + f$edit(shared_proc,"upcase")) +$ endif +$ goto vmap_loop +$VMAP_END: +$ else +$ write sys$output "Unknown Architecture (Not VAX, AXP, or IA64)" +$ write sys$output "No options file created" +$ endif +$ endif +$ EXIT_M2S: +$ close/nolog map +$ close/nolog libopt +$ endsubroutine diff --git a/src/png/zlib/msdos/Makefile.bor b/src/png/zlib/msdos/Makefile.bor new file mode 100644 index 0000000000..3d12a2c252 --- /dev/null +++ b/src/png/zlib/msdos/Makefile.bor @@ -0,0 +1,115 @@ +# Makefile for zlib +# Borland C++ +# Last updated: 15-Mar-2003 + +# To use, do "make -fmakefile.bor" +# To compile in small model, set below: MODEL=s + +# WARNING: the small model is supported but only for small values of +# MAX_WBITS and MAX_MEM_LEVEL. For example: +# -DMAX_WBITS=11 -DDEF_WBITS=11 -DMAX_MEM_LEVEL=3 +# If you wish to reduce the memory requirements (default 256K for big +# objects plus a few K), you can add to the LOC macro below: +# -DMAX_MEM_LEVEL=7 -DMAX_WBITS=14 +# See zconf.h for details about the memory requirements. + +# ------------ Turbo C++, Borland C++ ------------ + +# Optional nonstandard preprocessor flags (e.g. -DMAX_MEM_LEVEL=7) +# should be added to the environment via "set LOCAL_ZLIB=-DFOO" or added +# to the declaration of LOC here: +LOC = $(LOCAL_ZLIB) + +# type for CPU required: 0: 8086, 1: 80186, 2: 80286, 3: 80386, etc. +CPU_TYP = 0 + +# memory model: one of s, m, c, l (small, medium, compact, large) +MODEL=l + +# replace bcc with tcc for Turbo C++ 1.0, with bcc32 for the 32 bit version +CC=bcc +LD=bcc +AR=tlib + +# compiler flags +# replace "-O2" by "-O -G -a -d" for Turbo C++ 1.0 +CFLAGS=-O2 -Z -m$(MODEL) $(LOC) + +LDFLAGS=-m$(MODEL) -f- + + +# variables +ZLIB_LIB = zlib_$(MODEL).lib + +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj +OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj +OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj + + +# targets +all: $(ZLIB_LIB) example.exe minigzip.exe + +.c.obj: + $(CC) -c $(CFLAGS) $*.c + +adler32.obj: adler32.c zlib.h zconf.h + +compress.obj: compress.c zlib.h zconf.h + +crc32.obj: crc32.c zlib.h zconf.h crc32.h + +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h + +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h + +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h + +gzread.obj: gzread.c zlib.h zconf.h gzguts.h + +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h + +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h + +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h + +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h + +uncompr.obj: uncompr.c zlib.h zconf.h + +zutil.obj: zutil.c zutil.h zlib.h zconf.h + +example.obj: test/example.c zlib.h zconf.h + +minigzip.obj: test/minigzip.c zlib.h zconf.h + + +# the command line is cut to fit in the MS-DOS 128 byte limit: +$(ZLIB_LIB): $(OBJ1) $(OBJ2) + -del $(ZLIB_LIB) + $(AR) $(ZLIB_LIB) $(OBJP1) + $(AR) $(ZLIB_LIB) $(OBJP2) + +example.exe: example.obj $(ZLIB_LIB) + $(LD) $(LDFLAGS) example.obj $(ZLIB_LIB) + +minigzip.exe: minigzip.obj $(ZLIB_LIB) + $(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB) + +test: example.exe minigzip.exe + example + echo hello world | minigzip | minigzip -d + +clean: + -del *.obj + -del *.lib + -del *.exe + -del zlib_*.bak + -del foo.gz diff --git a/src/png/zlib/msdos/Makefile.dj2 b/src/png/zlib/msdos/Makefile.dj2 new file mode 100644 index 0000000000..59d2037d69 --- /dev/null +++ b/src/png/zlib/msdos/Makefile.dj2 @@ -0,0 +1,104 @@ +# Makefile for zlib. Modified for djgpp v2.0 by F. J. Donahoe, 3/15/96. +# Copyright (C) 1995-1998 Jean-loup Gailly. +# For conditions of distribution and use, see copyright notice in zlib.h + +# To compile, or to compile and test, type: +# +# make -fmakefile.dj2; make test -fmakefile.dj2 +# +# To install libz.a, zconf.h and zlib.h in the djgpp directories, type: +# +# make install -fmakefile.dj2 +# +# after first defining LIBRARY_PATH and INCLUDE_PATH in djgpp.env as +# in the sample below if the pattern of the DJGPP distribution is to +# be followed. Remember that, while 'es around <=> are ignored in +# makefiles, they are *not* in batch files or in djgpp.env. +# - - - - - +# [make] +# INCLUDE_PATH=%\>;INCLUDE_PATH%%\DJDIR%\include +# LIBRARY_PATH=%\>;LIBRARY_PATH%%\DJDIR%\lib +# BUTT=-m486 +# - - - - - +# Alternately, these variables may be defined below, overriding the values +# in djgpp.env, as +# INCLUDE_PATH=c:\usr\include +# LIBRARY_PATH=c:\usr\lib + +CC=gcc + +#CFLAGS=-MMD -O +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 +#CFLAGS=-MMD -g -DZLIB_DEBUG +CFLAGS=-MMD -O3 $(BUTT) -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ + -Wstrict-prototypes -Wmissing-prototypes + +# If cp.exe is available, replace "copy /Y" with "cp -fp" . +CP=copy /Y +# If gnu install.exe is available, replace $(CP) with ginstall. +INSTALL=$(CP) +# The default value of RM is "rm -f." If "rm.exe" is found, comment out: +RM=del +LDLIBS=-L. -lz +LD=$(CC) -s -o +LDSHARED=$(CC) + +INCL=zlib.h zconf.h +LIBS=libz.a + +AR=ar rcs + +prefix=/usr/local +exec_prefix = $(prefix) + +OBJS = adler32.o compress.o crc32.o gzclose.o gzlib.o gzread.o gzwrite.o \ + uncompr.o deflate.o trees.o zutil.o inflate.o infback.o inftrees.o inffast.o + +OBJA = +# to use the asm code: make OBJA=match.o + +TEST_OBJS = example.o minigzip.o + +all: example.exe minigzip.exe + +check: test +test: all + ./example + echo hello world | .\minigzip | .\minigzip -d + +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +libz.a: $(OBJS) $(OBJA) + $(AR) $@ $(OBJS) $(OBJA) + +%.exe : %.o $(LIBS) + $(LD) $@ $< $(LDLIBS) + +# INCLUDE_PATH and LIBRARY_PATH were set for [make] in djgpp.env . + +.PHONY : uninstall clean + +install: $(INCL) $(LIBS) + -@if not exist $(INCLUDE_PATH)\nul mkdir $(INCLUDE_PATH) + -@if not exist $(LIBRARY_PATH)\nul mkdir $(LIBRARY_PATH) + $(INSTALL) zlib.h $(INCLUDE_PATH) + $(INSTALL) zconf.h $(INCLUDE_PATH) + $(INSTALL) libz.a $(LIBRARY_PATH) + +uninstall: + $(RM) $(INCLUDE_PATH)\zlib.h + $(RM) $(INCLUDE_PATH)\zconf.h + $(RM) $(LIBRARY_PATH)\libz.a + +clean: + $(RM) *.d + $(RM) *.o + $(RM) *.exe + $(RM) libz.a + $(RM) foo.gz + +DEPS := $(wildcard *.d) +ifneq ($(DEPS),) +include $(DEPS) +endif diff --git a/src/png/zlib/msdos/Makefile.emx b/src/png/zlib/msdos/Makefile.emx new file mode 100644 index 0000000000..e30f67bed6 --- /dev/null +++ b/src/png/zlib/msdos/Makefile.emx @@ -0,0 +1,69 @@ +# Makefile for zlib. Modified for emx 0.9c by Chr. Spieler, 6/17/98. +# Copyright (C) 1995-1998 Jean-loup Gailly. +# For conditions of distribution and use, see copyright notice in zlib.h + +# To compile, or to compile and test, type: +# +# make -fmakefile.emx; make test -fmakefile.emx +# + +CC=gcc + +#CFLAGS=-MMD -O +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 +#CFLAGS=-MMD -g -DZLIB_DEBUG +CFLAGS=-MMD -O3 $(BUTT) -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ + -Wstrict-prototypes -Wmissing-prototypes + +# If cp.exe is available, replace "copy /Y" with "cp -fp" . +CP=copy /Y +# If gnu install.exe is available, replace $(CP) with ginstall. +INSTALL=$(CP) +# The default value of RM is "rm -f." If "rm.exe" is found, comment out: +RM=del +LDLIBS=-L. -lzlib +LD=$(CC) -s -o +LDSHARED=$(CC) + +INCL=zlib.h zconf.h +LIBS=zlib.a + +AR=ar rcs + +prefix=/usr/local +exec_prefix = $(prefix) + +OBJS = adler32.o compress.o crc32.o gzclose.o gzlib.o gzread.o gzwrite.o \ + uncompr.o deflate.o trees.o zutil.o inflate.o infback.o inftrees.o inffast.o + +TEST_OBJS = example.o minigzip.o + +all: example.exe minigzip.exe + +test: all + ./example + echo hello world | .\minigzip | .\minigzip -d + +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +zlib.a: $(OBJS) + $(AR) $@ $(OBJS) + +%.exe : %.o $(LIBS) + $(LD) $@ $< $(LDLIBS) + + +.PHONY : clean + +clean: + $(RM) *.d + $(RM) *.o + $(RM) *.exe + $(RM) zlib.a + $(RM) foo.gz + +DEPS := $(wildcard *.d) +ifneq ($(DEPS),) +include $(DEPS) +endif diff --git a/src/png/zlib/msdos/Makefile.msc b/src/png/zlib/msdos/Makefile.msc new file mode 100644 index 0000000000..ae8378615e --- /dev/null +++ b/src/png/zlib/msdos/Makefile.msc @@ -0,0 +1,112 @@ +# Makefile for zlib +# Microsoft C 5.1 or later +# Last updated: 19-Mar-2003 + +# To use, do "make makefile.msc" +# To compile in small model, set below: MODEL=S + +# If you wish to reduce the memory requirements (default 256K for big +# objects plus a few K), you can add to the LOC macro below: +# -DMAX_MEM_LEVEL=7 -DMAX_WBITS=14 +# See zconf.h for details about the memory requirements. + +# ------------- Microsoft C 5.1 and later ------------- + +# Optional nonstandard preprocessor flags (e.g. -DMAX_MEM_LEVEL=7) +# should be added to the environment via "set LOCAL_ZLIB=-DFOO" or added +# to the declaration of LOC here: +LOC = $(LOCAL_ZLIB) + +# Type for CPU required: 0: 8086, 1: 80186, 2: 80286, 3: 80386, etc. +CPU_TYP = 0 + +# Memory model: one of S, M, C, L (small, medium, compact, large) +MODEL=L + +CC=cl +CFLAGS=-nologo -A$(MODEL) -G$(CPU_TYP) -W3 -Oait -Gs $(LOC) +#-Ox generates bad code with MSC 5.1 +LIB_CFLAGS=-Zl $(CFLAGS) + +LD=link +LDFLAGS=/noi/e/st:0x1500/noe/farcall/packcode +# "/farcall/packcode" are only useful for `large code' memory models +# but should be a "no-op" for small code models. + + +# variables +ZLIB_LIB = zlib_$(MODEL).lib + +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj + + +# targets +all: $(ZLIB_LIB) example.exe minigzip.exe + +.c.obj: + $(CC) -c $(LIB_CFLAGS) $*.c + +adler32.obj: adler32.c zlib.h zconf.h + +compress.obj: compress.c zlib.h zconf.h + +crc32.obj: crc32.c zlib.h zconf.h crc32.h + +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h + +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h + +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h + +gzread.obj: gzread.c zlib.h zconf.h gzguts.h + +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h + +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h + +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h + +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h + +uncompr.obj: uncompr.c zlib.h zconf.h + +zutil.obj: zutil.c zutil.h zlib.h zconf.h + +example.obj: test/example.c zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +minigzip.obj: test/minigzip.c zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + + +# the command line is cut to fit in the MS-DOS 128 byte limit: +$(ZLIB_LIB): $(OBJ1) $(OBJ2) + if exist $(ZLIB_LIB) del $(ZLIB_LIB) + lib $(ZLIB_LIB) $(OBJ1); + lib $(ZLIB_LIB) $(OBJ2); + +example.exe: example.obj $(ZLIB_LIB) + $(LD) $(LDFLAGS) example.obj,,,$(ZLIB_LIB); + +minigzip.exe: minigzip.obj $(ZLIB_LIB) + $(LD) $(LDFLAGS) minigzip.obj,,,$(ZLIB_LIB); + +test: example.exe minigzip.exe + example + echo hello world | minigzip | minigzip -d + +clean: + -del *.obj + -del *.lib + -del *.exe + -del *.map + -del zlib_*.bak + -del foo.gz diff --git a/src/png/zlib/msdos/Makefile.tc b/src/png/zlib/msdos/Makefile.tc new file mode 100644 index 0000000000..5aec82a9d5 --- /dev/null +++ b/src/png/zlib/msdos/Makefile.tc @@ -0,0 +1,100 @@ +# Makefile for zlib +# Turbo C 2.01, Turbo C++ 1.01 +# Last updated: 15-Mar-2003 + +# To use, do "make -fmakefile.tc" +# To compile in small model, set below: MODEL=s + +# WARNING: the small model is supported but only for small values of +# MAX_WBITS and MAX_MEM_LEVEL. For example: +# -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3 +# If you wish to reduce the memory requirements (default 256K for big +# objects plus a few K), you can add to CFLAGS below: +# -DMAX_MEM_LEVEL=7 -DMAX_WBITS=14 +# See zconf.h for details about the memory requirements. + +# ------------ Turbo C 2.01, Turbo C++ 1.01 ------------ +MODEL=l +CC=tcc +LD=tcc +AR=tlib +# CFLAGS=-O2 -G -Z -m$(MODEL) -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3 +CFLAGS=-O2 -G -Z -m$(MODEL) +LDFLAGS=-m$(MODEL) -f- + + +# variables +ZLIB_LIB = zlib_$(MODEL).lib + +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj +OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj +OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj + + +# targets +all: $(ZLIB_LIB) example.exe minigzip.exe + +.c.obj: + $(CC) -c $(CFLAGS) $*.c + +adler32.obj: adler32.c zlib.h zconf.h + +compress.obj: compress.c zlib.h zconf.h + +crc32.obj: crc32.c zlib.h zconf.h crc32.h + +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h + +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h + +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h + +gzread.obj: gzread.c zlib.h zconf.h gzguts.h + +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h + +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h + +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h + +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h + +uncompr.obj: uncompr.c zlib.h zconf.h + +zutil.obj: zutil.c zutil.h zlib.h zconf.h + +example.obj: test/example.c zlib.h zconf.h + +minigzip.obj: test/minigzip.c zlib.h zconf.h + + +# the command line is cut to fit in the MS-DOS 128 byte limit: +$(ZLIB_LIB): $(OBJ1) $(OBJ2) + -del $(ZLIB_LIB) + $(AR) $(ZLIB_LIB) $(OBJP1) + $(AR) $(ZLIB_LIB) $(OBJP2) + +example.exe: example.obj $(ZLIB_LIB) + $(LD) $(LDFLAGS) example.obj $(ZLIB_LIB) + +minigzip.exe: minigzip.obj $(ZLIB_LIB) + $(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB) + +test: example.exe minigzip.exe + example + echo hello world | minigzip | minigzip -d + +clean: + -del *.obj + -del *.lib + -del *.exe + -del zlib_*.bak + -del foo.gz diff --git a/src/png/zlib/nintendods/Makefile b/src/png/zlib/nintendods/Makefile new file mode 100644 index 0000000000..21337d01ab --- /dev/null +++ b/src/png/zlib/nintendods/Makefile @@ -0,0 +1,126 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#--------------------------------------------------------------------------------- +TARGET := $(shell basename $(CURDIR)) +BUILD := build +SOURCES := ../../ +DATA := data +INCLUDES := include + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb -mthumb-interwork + +CFLAGS := -Wall -O2\ + -march=armv5te -mtune=arm946e-s \ + -fomit-frame-pointer -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM9 +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +ASFLAGS := $(ARCH) -march=armv5te -mtune=arm946e-s +LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBNDS) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/lib/libz.a + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + @[ -d $@ ] || mkdir -p include + @cp ../../*.h include + +lib: + @[ -d $@ ] || mkdir -p $@ + +$(BUILD): lib + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) lib + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/src/png/zlib/nintendods/README b/src/png/zlib/nintendods/README new file mode 100644 index 0000000000..ba7a37dbe8 --- /dev/null +++ b/src/png/zlib/nintendods/README @@ -0,0 +1,5 @@ +This Makefile requires devkitARM (http://www.devkitpro.org/category/devkitarm/) and works inside "contrib/nds". It is based on a devkitARM template. + +Eduardo Costa +January 3, 2009 + diff --git a/src/png/zlib/old/Makefile.emx b/src/png/zlib/old/Makefile.emx new file mode 100644 index 0000000000..612b037915 --- /dev/null +++ b/src/png/zlib/old/Makefile.emx @@ -0,0 +1,69 @@ +# Makefile for zlib. Modified for emx/rsxnt by Chr. Spieler, 6/16/98. +# Copyright (C) 1995-1998 Jean-loup Gailly. +# For conditions of distribution and use, see copyright notice in zlib.h + +# To compile, or to compile and test, type: +# +# make -fmakefile.emx; make test -fmakefile.emx +# + +CC=gcc -Zwin32 + +#CFLAGS=-MMD -O +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 +#CFLAGS=-MMD -g -DZLIB_DEBUG +CFLAGS=-MMD -O3 $(BUTT) -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ + -Wstrict-prototypes -Wmissing-prototypes + +# If cp.exe is available, replace "copy /Y" with "cp -fp" . +CP=copy /Y +# If gnu install.exe is available, replace $(CP) with ginstall. +INSTALL=$(CP) +# The default value of RM is "rm -f." If "rm.exe" is found, comment out: +RM=del +LDLIBS=-L. -lzlib +LD=$(CC) -s -o +LDSHARED=$(CC) + +INCL=zlib.h zconf.h +LIBS=zlib.a + +AR=ar rcs + +prefix=/usr/local +exec_prefix = $(prefix) + +OBJS = adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o \ + gzwrite.o infback.o inffast.o inflate.o inftrees.o trees.o uncompr.o zutil.o + +TEST_OBJS = example.o minigzip.o + +all: example.exe minigzip.exe + +test: all + ./example + echo hello world | .\minigzip | .\minigzip -d + +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +zlib.a: $(OBJS) + $(AR) $@ $(OBJS) + +%.exe : %.o $(LIBS) + $(LD) $@ $< $(LDLIBS) + + +.PHONY : clean + +clean: + $(RM) *.d + $(RM) *.o + $(RM) *.exe + $(RM) zlib.a + $(RM) foo.gz + +DEPS := $(wildcard *.d) +ifneq ($(DEPS),) +include $(DEPS) +endif diff --git a/src/png/zlib/old/Makefile.riscos b/src/png/zlib/old/Makefile.riscos new file mode 100644 index 0000000000..57e29d3fba --- /dev/null +++ b/src/png/zlib/old/Makefile.riscos @@ -0,0 +1,151 @@ +# Project: zlib_1_03 +# Patched for zlib 1.1.2 rw@shadow.org.uk 19980430 +# test works out-of-the-box, installs `somewhere' on demand + +# Toolflags: +CCflags = -c -depend !Depend -IC: -g -throwback -DRISCOS -fah +C++flags = -c -depend !Depend -IC: -throwback +Linkflags = -aif -c++ -o $@ +ObjAsmflags = -throwback -NoCache -depend !Depend +CMHGflags = +LibFileflags = -c -l -o $@ +Squeezeflags = -o $@ + +# change the line below to where _you_ want the library installed. +libdest = lib:zlib + +# Final targets: +@.lib: @.o.adler32 @.o.compress @.o.crc32 @.o.deflate @.o.gzio \ + @.o.infblock @.o.infcodes @.o.inffast @.o.inflate @.o.inftrees @.o.infutil @.o.trees \ + @.o.uncompr @.o.zutil + LibFile $(LibFileflags) @.o.adler32 @.o.compress @.o.crc32 @.o.deflate \ + @.o.gzio @.o.infblock @.o.infcodes @.o.inffast @.o.inflate @.o.inftrees @.o.infutil \ + @.o.trees @.o.uncompr @.o.zutil +test: @.minigzip @.example @.lib + @copy @.lib @.libc A~C~DF~L~N~P~Q~RS~TV + @echo running tests: hang on. + @/@.minigzip -f -9 libc + @/@.minigzip -d libc-gz + @/@.minigzip -f -1 libc + @/@.minigzip -d libc-gz + @/@.minigzip -h -9 libc + @/@.minigzip -d libc-gz + @/@.minigzip -h -1 libc + @/@.minigzip -d libc-gz + @/@.minigzip -9 libc + @/@.minigzip -d libc-gz + @/@.minigzip -1 libc + @/@.minigzip -d libc-gz + @diff @.lib @.libc + @echo that should have reported '@.lib and @.libc identical' if you have diff. + @/@.example @.fred @.fred + @echo that will have given lots of hello!'s. + +@.minigzip: @.o.minigzip @.lib C:o.Stubs + Link $(Linkflags) @.o.minigzip @.lib C:o.Stubs +@.example: @.o.example @.lib C:o.Stubs + Link $(Linkflags) @.o.example @.lib C:o.Stubs + +install: @.lib + cdir $(libdest) + cdir $(libdest).h + @copy @.h.zlib $(libdest).h.zlib A~C~DF~L~N~P~Q~RS~TV + @copy @.h.zconf $(libdest).h.zconf A~C~DF~L~N~P~Q~RS~TV + @copy @.lib $(libdest).lib A~C~DF~L~N~P~Q~RS~TV + @echo okay, installed zlib in $(libdest) + +clean:; remove @.minigzip + remove @.example + remove @.libc + -wipe @.o.* F~r~cV + remove @.fred + +# User-editable dependencies: +.c.o: + cc $(ccflags) -o $@ $< + +# Static dependencies: + +# Dynamic dependencies: +o.example: c.example +o.example: h.zlib +o.example: h.zconf +o.minigzip: c.minigzip +o.minigzip: h.zlib +o.minigzip: h.zconf +o.adler32: c.adler32 +o.adler32: h.zlib +o.adler32: h.zconf +o.compress: c.compress +o.compress: h.zlib +o.compress: h.zconf +o.crc32: c.crc32 +o.crc32: h.zlib +o.crc32: h.zconf +o.deflate: c.deflate +o.deflate: h.deflate +o.deflate: h.zutil +o.deflate: h.zlib +o.deflate: h.zconf +o.gzio: c.gzio +o.gzio: h.zutil +o.gzio: h.zlib +o.gzio: h.zconf +o.infblock: c.infblock +o.infblock: h.zutil +o.infblock: h.zlib +o.infblock: h.zconf +o.infblock: h.infblock +o.infblock: h.inftrees +o.infblock: h.infcodes +o.infblock: h.infutil +o.infcodes: c.infcodes +o.infcodes: h.zutil +o.infcodes: h.zlib +o.infcodes: h.zconf +o.infcodes: h.inftrees +o.infcodes: h.infblock +o.infcodes: h.infcodes +o.infcodes: h.infutil +o.infcodes: h.inffast +o.inffast: c.inffast +o.inffast: h.zutil +o.inffast: h.zlib +o.inffast: h.zconf +o.inffast: h.inftrees +o.inffast: h.infblock +o.inffast: h.infcodes +o.inffast: h.infutil +o.inffast: h.inffast +o.inflate: c.inflate +o.inflate: h.zutil +o.inflate: h.zlib +o.inflate: h.zconf +o.inflate: h.infblock +o.inftrees: c.inftrees +o.inftrees: h.zutil +o.inftrees: h.zlib +o.inftrees: h.zconf +o.inftrees: h.inftrees +o.inftrees: h.inffixed +o.infutil: c.infutil +o.infutil: h.zutil +o.infutil: h.zlib +o.infutil: h.zconf +o.infutil: h.infblock +o.infutil: h.inftrees +o.infutil: h.infcodes +o.infutil: h.infutil +o.trees: c.trees +o.trees: h.deflate +o.trees: h.zutil +o.trees: h.zlib +o.trees: h.zconf +o.trees: h.trees +o.uncompr: c.uncompr +o.uncompr: h.zlib +o.uncompr: h.zconf +o.zutil: c.zutil +o.zutil: h.zutil +o.zutil: h.zlib +o.zutil: h.zconf diff --git a/src/png/zlib/old/README b/src/png/zlib/old/README new file mode 100644 index 0000000000..800bf07982 --- /dev/null +++ b/src/png/zlib/old/README @@ -0,0 +1,3 @@ +This directory contains files that have not been updated for zlib 1.2.x + +(Volunteers are encouraged to help clean this up. Thanks.) diff --git a/src/png/zlib/old/descrip.mms b/src/png/zlib/old/descrip.mms new file mode 100644 index 0000000000..7066da5b55 --- /dev/null +++ b/src/png/zlib/old/descrip.mms @@ -0,0 +1,48 @@ +# descrip.mms: MMS description file for building zlib on VMS +# written by Martin P.J. Zinser + +cc_defs = +c_deb = + +.ifdef __DECC__ +pref = /prefix=all +.endif + +OBJS = adler32.obj, compress.obj, crc32.obj, gzio.obj, uncompr.obj,\ + deflate.obj, trees.obj, zutil.obj, inflate.obj, infblock.obj,\ + inftrees.obj, infcodes.obj, infutil.obj, inffast.obj + +CFLAGS= $(C_DEB) $(CC_DEFS) $(PREF) + +all : example.exe minigzip.exe + @ write sys$output " Example applications available" +libz.olb : libz.olb($(OBJS)) + @ write sys$output " libz available" + +example.exe : example.obj libz.olb + link example,libz.olb/lib + +minigzip.exe : minigzip.obj libz.olb + link minigzip,libz.olb/lib,x11vms:xvmsutils.olb/lib + +clean : + delete *.obj;*,libz.olb;* + + +# Other dependencies. +adler32.obj : zutil.h zlib.h zconf.h +compress.obj : zlib.h zconf.h +crc32.obj : zutil.h zlib.h zconf.h +deflate.obj : deflate.h zutil.h zlib.h zconf.h +example.obj : zlib.h zconf.h +gzio.obj : zutil.h zlib.h zconf.h +infblock.obj : zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h +infcodes.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h infcodes.h inffast.h +inffast.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h inffast.h +inflate.obj : zutil.h zlib.h zconf.h infblock.h +inftrees.obj : zutil.h zlib.h zconf.h inftrees.h +infutil.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h +minigzip.obj : zlib.h zconf.h +trees.obj : deflate.h zutil.h zlib.h zconf.h +uncompr.obj : zlib.h zconf.h +zutil.obj : zutil.h zlib.h zconf.h diff --git a/src/png/zlib/old/os2/Makefile.os2 b/src/png/zlib/old/os2/Makefile.os2 new file mode 100644 index 0000000000..bb426c0d8e --- /dev/null +++ b/src/png/zlib/old/os2/Makefile.os2 @@ -0,0 +1,136 @@ +# Makefile for zlib under OS/2 using GCC (PGCC) +# For conditions of distribution and use, see copyright notice in zlib.h + +# To compile and test, type: +# cp Makefile.os2 .. +# cd .. +# make -f Makefile.os2 test + +# This makefile will build a static library z.lib, a shared library +# z.dll and a import library zdll.lib. You can use either z.lib or +# zdll.lib by specifying either -lz or -lzdll on gcc's command line + +CC=gcc -Zomf -s + +CFLAGS=-O6 -Wall +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 +#CFLAGS=-g -DZLIB_DEBUG +#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ +# -Wstrict-prototypes -Wmissing-prototypes + +#################### BUG WARNING: ##################### +## infcodes.c hits a bug in pgcc-1.0, so you have to use either +## -O# where # <= 4 or one of (-fno-ommit-frame-pointer or -fno-force-mem) +## This bug is reportedly fixed in pgcc >1.0, but this was not tested +CFLAGS+=-fno-force-mem + +LDFLAGS=-s -L. -lzdll -Zcrtdll +LDSHARED=$(CC) -s -Zomf -Zdll -Zcrtdll + +VER=1.1.0 +ZLIB=z.lib +SHAREDLIB=z.dll +SHAREDLIBIMP=zdll.lib +LIBS=$(ZLIB) $(SHAREDLIB) $(SHAREDLIBIMP) + +AR=emxomfar cr +IMPLIB=emximp +RANLIB=echo +TAR=tar +SHELL=bash + +prefix=/usr/local +exec_prefix = $(prefix) + +OBJS = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \ + zutil.o inflate.o infblock.o inftrees.o infcodes.o infutil.o inffast.o + +TEST_OBJS = example.o minigzip.o + +DISTFILES = README INDEX ChangeLog configure Make*[a-z0-9] *.[ch] descrip.mms \ + algorithm.txt zlib.3 msdos/Make*[a-z0-9] msdos/zlib.def msdos/zlib.rc \ + nt/Makefile.nt nt/zlib.dnt contrib/README.contrib contrib/*.txt \ + contrib/asm386/*.asm contrib/asm386/*.c \ + contrib/asm386/*.bat contrib/asm386/zlibvc.d?? contrib/iostream/*.cpp \ + contrib/iostream/*.h contrib/iostream2/*.h contrib/iostream2/*.cpp \ + contrib/untgz/Makefile contrib/untgz/*.c contrib/untgz/*.w32 + +all: example.exe minigzip.exe + +test: all + @LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \ + echo hello world | ./minigzip | ./minigzip -d || \ + echo ' *** minigzip test FAILED ***' ; \ + if ./example; then \ + echo ' *** zlib test OK ***'; \ + else \ + echo ' *** zlib test FAILED ***'; \ + fi + +$(ZLIB): $(OBJS) + $(AR) $@ $(OBJS) + -@ ($(RANLIB) $@ || true) >/dev/null 2>&1 + +$(SHAREDLIB): $(OBJS) os2/z.def + $(LDSHARED) -o $@ $^ + +$(SHAREDLIBIMP): os2/z.def + $(IMPLIB) -o $@ $^ + +example.exe: example.o $(LIBS) + $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) + +minigzip.exe: minigzip.o $(LIBS) + $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS) + +clean: + rm -f *.o *~ example minigzip libz.a libz.so* foo.gz + +distclean: clean + +zip: + mv Makefile Makefile~; cp -p Makefile.in Makefile + rm -f test.c ztest*.c + v=`sed -n -e 's/\.//g' -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`;\ + zip -ul9 zlib$$v $(DISTFILES) + mv Makefile~ Makefile + +dist: + mv Makefile Makefile~; cp -p Makefile.in Makefile + rm -f test.c ztest*.c + d=zlib-`sed -n '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`;\ + rm -f $$d.tar.gz; \ + if test ! -d ../$$d; then rm -f ../$$d; ln -s `pwd` ../$$d; fi; \ + files=""; \ + for f in $(DISTFILES); do files="$$files $$d/$$f"; done; \ + cd ..; \ + GZIP=-9 $(TAR) chofz $$d/$$d.tar.gz $$files; \ + if test ! -d $$d; then rm -f $$d; fi + mv Makefile~ Makefile + +tags: + etags *.[ch] + +depend: + makedepend -- $(CFLAGS) -- *.[ch] + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +adler32.o: zlib.h zconf.h +compress.o: zlib.h zconf.h +crc32.o: zlib.h zconf.h +deflate.o: deflate.h zutil.h zlib.h zconf.h +example.o: zlib.h zconf.h +gzio.o: zutil.h zlib.h zconf.h +infblock.o: infblock.h inftrees.h infcodes.h infutil.h zutil.h zlib.h zconf.h +infcodes.o: zutil.h zlib.h zconf.h +infcodes.o: inftrees.h infblock.h infcodes.h infutil.h inffast.h +inffast.o: zutil.h zlib.h zconf.h inftrees.h +inffast.o: infblock.h infcodes.h infutil.h inffast.h +inflate.o: zutil.h zlib.h zconf.h infblock.h +inftrees.o: zutil.h zlib.h zconf.h inftrees.h +infutil.o: zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h +minigzip.o: zlib.h zconf.h +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h +uncompr.o: zlib.h zconf.h +zutil.o: zutil.h zlib.h zconf.h diff --git a/src/png/zlib/old/os2/zlib.def b/src/png/zlib/old/os2/zlib.def new file mode 100644 index 0000000000..4c753f1a3b --- /dev/null +++ b/src/png/zlib/old/os2/zlib.def @@ -0,0 +1,51 @@ +; +; Slightly modified version of ../nt/zlib.dnt :-) +; + +LIBRARY Z +DESCRIPTION "Zlib compression library for OS/2" +CODE PRELOAD MOVEABLE DISCARDABLE +DATA PRELOAD MOVEABLE MULTIPLE + +EXPORTS + adler32 + compress + crc32 + deflate + deflateCopy + deflateEnd + deflateInit2_ + deflateInit_ + deflateParams + deflateReset + deflateSetDictionary + gzclose + gzdopen + gzerror + gzflush + gzopen + gzread + gzwrite + inflate + inflateEnd + inflateInit2_ + inflateInit_ + inflateReset + inflateSetDictionary + inflateSync + uncompress + zlibVersion + gzprintf + gzputc + gzgetc + gzseek + gzrewind + gztell + gzeof + gzsetparams + zError + inflateSyncPoint + get_crc_table + compress2 + gzputs + gzgets diff --git a/src/png/zlib/old/visual-basic.txt b/src/png/zlib/old/visual-basic.txt new file mode 100644 index 0000000000..57efe58124 --- /dev/null +++ b/src/png/zlib/old/visual-basic.txt @@ -0,0 +1,160 @@ +See below some functions declarations for Visual Basic. + +Frequently Asked Question: + +Q: Each time I use the compress function I get the -5 error (not enough + room in the output buffer). + +A: Make sure that the length of the compressed buffer is passed by + reference ("as any"), not by value ("as long"). Also check that + before the call of compress this length is equal to the total size of + the compressed buffer and not zero. + + +From: "Jon Caruana" +Subject: Re: How to port zlib declares to vb? +Date: Mon, 28 Oct 1996 18:33:03 -0600 + +Got the answer! (I haven't had time to check this but it's what I got, and +looks correct): + +He has the following routines working: + compress + uncompress + gzopen + gzwrite + gzread + gzclose + +Declares follow: (Quoted from Carlos Rios , in Vb4 form) + +#If Win16 Then 'Use Win16 calls. +Declare Function compress Lib "ZLIB.DLL" (ByVal compr As + String, comprLen As Any, ByVal buf As String, ByVal buflen + As Long) As Integer +Declare Function uncompress Lib "ZLIB.DLL" (ByVal uncompr + As String, uncomprLen As Any, ByVal compr As String, ByVal + lcompr As Long) As Integer +Declare Function gzopen Lib "ZLIB.DLL" (ByVal filePath As + String, ByVal mode As String) As Long +Declare Function gzread Lib "ZLIB.DLL" (ByVal file As + Long, ByVal uncompr As String, ByVal uncomprLen As Integer) + As Integer +Declare Function gzwrite Lib "ZLIB.DLL" (ByVal file As + Long, ByVal uncompr As String, ByVal uncomprLen As Integer) + As Integer +Declare Function gzclose Lib "ZLIB.DLL" (ByVal file As + Long) As Integer +#Else +Declare Function compress Lib "ZLIB32.DLL" + (ByVal compr As String, comprLen As Any, ByVal buf As + String, ByVal buflen As Long) As Integer +Declare Function uncompress Lib "ZLIB32.DLL" + (ByVal uncompr As String, uncomprLen As Any, ByVal compr As + String, ByVal lcompr As Long) As Long +Declare Function gzopen Lib "ZLIB32.DLL" + (ByVal file As String, ByVal mode As String) As Long +Declare Function gzread Lib "ZLIB32.DLL" + (ByVal file As Long, ByVal uncompr As String, ByVal + uncomprLen As Long) As Long +Declare Function gzwrite Lib "ZLIB32.DLL" + (ByVal file As Long, ByVal uncompr As String, ByVal + uncomprLen As Long) As Long +Declare Function gzclose Lib "ZLIB32.DLL" + (ByVal file As Long) As Long +#End If + +-Jon Caruana +jon-net@usa.net +Microsoft Sitebuilder Network Level 1 Member - HTML Writer's Guild Member + + +Here is another example from Michael that he +says conforms to the VB guidelines, and that solves the problem of not +knowing the uncompressed size by storing it at the end of the file: + +'Calling the functions: +'bracket meaning: [optional] {Range of possible values} +'Call subCompressFile( [, , [level of compression {1..9}]]) +'Call subUncompressFile() + +Option Explicit +Private lngpvtPcnSml As Long 'Stores value for 'lngPercentSmaller' +Private Const SUCCESS As Long = 0 +Private Const strFilExt As String = ".cpr" +Private Declare Function lngfncCpr Lib "zlib.dll" Alias "compress2" (ByRef +dest As Any, ByRef destLen As Any, ByRef src As Any, ByVal srcLen As Long, +ByVal level As Integer) As Long +Private Declare Function lngfncUcp Lib "zlib.dll" Alias "uncompress" (ByRef +dest As Any, ByRef destLen As Any, ByRef src As Any, ByVal srcLen As Long) +As Long + +Public Sub subCompressFile(ByVal strargOriFilPth As String, Optional ByVal +strargCprFilPth As String, Optional ByVal intLvl As Integer = 9) + Dim strCprPth As String + Dim lngOriSiz As Long + Dim lngCprSiz As Long + Dim bytaryOri() As Byte + Dim bytaryCpr() As Byte + lngOriSiz = FileLen(strargOriFilPth) + ReDim bytaryOri(lngOriSiz - 1) + Open strargOriFilPth For Binary Access Read As #1 + Get #1, , bytaryOri() + Close #1 + strCprPth = IIf(strargCprFilPth = "", strargOriFilPth, strargCprFilPth) +'Select file path and name + strCprPth = strCprPth & IIf(Right(strCprPth, Len(strFilExt)) = +strFilExt, "", strFilExt) 'Add file extension if not exists + lngCprSiz = (lngOriSiz * 1.01) + 12 'Compression needs temporary a bit +more space then original file size + ReDim bytaryCpr(lngCprSiz - 1) + If lngfncCpr(bytaryCpr(0), lngCprSiz, bytaryOri(0), lngOriSiz, intLvl) = +SUCCESS Then + lngpvtPcnSml = (1# - (lngCprSiz / lngOriSiz)) * 100 + ReDim Preserve bytaryCpr(lngCprSiz - 1) + Open strCprPth For Binary Access Write As #1 + Put #1, , bytaryCpr() + Put #1, , lngOriSiz 'Add the the original size value to the end +(last 4 bytes) + Close #1 + Else + MsgBox "Compression error" + End If + Erase bytaryCpr + Erase bytaryOri +End Sub + +Public Sub subUncompressFile(ByVal strargFilPth As String) + Dim bytaryCpr() As Byte + Dim bytaryOri() As Byte + Dim lngOriSiz As Long + Dim lngCprSiz As Long + Dim strOriPth As String + lngCprSiz = FileLen(strargFilPth) + ReDim bytaryCpr(lngCprSiz - 1) + Open strargFilPth For Binary Access Read As #1 + Get #1, , bytaryCpr() + Close #1 + 'Read the original file size value: + lngOriSiz = bytaryCpr(lngCprSiz - 1) * (2 ^ 24) _ + + bytaryCpr(lngCprSiz - 2) * (2 ^ 16) _ + + bytaryCpr(lngCprSiz - 3) * (2 ^ 8) _ + + bytaryCpr(lngCprSiz - 4) + ReDim Preserve bytaryCpr(lngCprSiz - 5) 'Cut of the original size value + ReDim bytaryOri(lngOriSiz - 1) + If lngfncUcp(bytaryOri(0), lngOriSiz, bytaryCpr(0), lngCprSiz) = SUCCESS +Then + strOriPth = Left(strargFilPth, Len(strargFilPth) - Len(strFilExt)) + Open strOriPth For Binary Access Write As #1 + Put #1, , bytaryOri() + Close #1 + Else + MsgBox "Uncompression error" + End If + Erase bytaryCpr + Erase bytaryOri +End Sub +Public Property Get lngPercentSmaller() As Long + lngPercentSmaller = lngpvtPcnSml +End Property diff --git a/src/png/zlib/os400/README400 b/src/png/zlib/os400/README400 new file mode 100644 index 0000000000..4f98334f5a --- /dev/null +++ b/src/png/zlib/os400/README400 @@ -0,0 +1,48 @@ + ZLIB version 1.2.11 for OS/400 installation instructions + +1) Download and unpack the zlib tarball to some IFS directory. + (i.e.: /path/to/the/zlib/ifs/source/directory) + + If the installed IFS command suppors gzip format, this is straightforward, +else you have to unpack first to some directory on a system supporting it, +then move the whole directory to the IFS via the network (via SMB or FTP). + +2) Edit the configuration parameters in the compilation script. + + EDTF STMF('/path/to/the/zlib/ifs/source/directory/os400/make.sh') + +Tune the parameters according to your needs if not matching the defaults. +Save the file and exit after edition. + +3) Enter qshell, then work in the zlib OS/400 specific directory. + + QSH + cd /path/to/the/zlib/ifs/source/directory/os400 + +4) Compile and install + + sh make.sh + +The script will: +- create the libraries, objects and IFS directories for the zlib environment, +- compile all modules, +- create a service program, +- create a static and a dynamic binding directory, +- install header files for C/C++ and for ILE/RPG, both for compilation in + DB2 and IFS environments. + +That's all. + + +Notes: For OS/400 ILE RPG programmers, a /copy member defining the ZLIB + API prototypes for ILE RPG can be found in ZLIB/H(ZLIB.INC). + In the ILE environment, the same definitions are available from + file zlib.inc located in the same IFS include directory as the + C/C++ header files. + Please read comments in this member for more information. + + Remember that most foreign textual data are ASCII coded: this + implementation does not handle conversion from/to ASCII, so + text data code conversions must be done explicitely. + + Mainly for the reason above, always open zipped files in binary mode. diff --git a/src/png/zlib/os400/bndsrc b/src/png/zlib/os400/bndsrc new file mode 100644 index 0000000000..5e6e0a2f0a --- /dev/null +++ b/src/png/zlib/os400/bndsrc @@ -0,0 +1,119 @@ +STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('ZLIB') + +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ +/* Version 1.1.3 entry points. */ +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ + + EXPORT SYMBOL("adler32") + EXPORT SYMBOL("compress") + EXPORT SYMBOL("compress2") + EXPORT SYMBOL("crc32") + EXPORT SYMBOL("get_crc_table") + EXPORT SYMBOL("deflate") + EXPORT SYMBOL("deflateEnd") + EXPORT SYMBOL("deflateSetDictionary") + EXPORT SYMBOL("deflateCopy") + EXPORT SYMBOL("deflateReset") + EXPORT SYMBOL("deflateParams") + EXPORT SYMBOL("deflatePrime") + EXPORT SYMBOL("deflateInit_") + EXPORT SYMBOL("deflateInit2_") + EXPORT SYMBOL("gzopen") + EXPORT SYMBOL("gzdopen") + EXPORT SYMBOL("gzsetparams") + EXPORT SYMBOL("gzread") + EXPORT SYMBOL("gzwrite") + EXPORT SYMBOL("gzprintf") + EXPORT SYMBOL("gzputs") + EXPORT SYMBOL("gzgets") + EXPORT SYMBOL("gzputc") + EXPORT SYMBOL("gzgetc") + EXPORT SYMBOL("gzflush") + EXPORT SYMBOL("gzseek") + EXPORT SYMBOL("gzrewind") + EXPORT SYMBOL("gztell") + EXPORT SYMBOL("gzeof") + EXPORT SYMBOL("gzclose") + EXPORT SYMBOL("gzerror") + EXPORT SYMBOL("inflate") + EXPORT SYMBOL("inflateEnd") + EXPORT SYMBOL("inflateSetDictionary") + EXPORT SYMBOL("inflateSync") + EXPORT SYMBOL("inflateReset") + EXPORT SYMBOL("inflateInit_") + EXPORT SYMBOL("inflateInit2_") + EXPORT SYMBOL("inflateSyncPoint") + EXPORT SYMBOL("uncompress") + EXPORT SYMBOL("zlibVersion") + EXPORT SYMBOL("zError") + EXPORT SYMBOL("z_errmsg") + +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ +/* Version 1.2.1 additional entry points. */ +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ + + EXPORT SYMBOL("compressBound") + EXPORT SYMBOL("deflateBound") + EXPORT SYMBOL("deflatePending") + EXPORT SYMBOL("gzungetc") + EXPORT SYMBOL("gzclearerr") + EXPORT SYMBOL("inflateBack") + EXPORT SYMBOL("inflateBackEnd") + EXPORT SYMBOL("inflateBackInit_") + EXPORT SYMBOL("inflateCopy") + EXPORT SYMBOL("zlibCompileFlags") + +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ +/* Version 1.2.4 additional entry points. */ +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ + + EXPORT SYMBOL("adler32_combine") + EXPORT SYMBOL("adler32_combine64") + EXPORT SYMBOL("crc32_combine") + EXPORT SYMBOL("crc32_combine64") + EXPORT SYMBOL("deflateSetHeader") + EXPORT SYMBOL("deflateTune") + EXPORT SYMBOL("gzbuffer") + EXPORT SYMBOL("gzclose_r") + EXPORT SYMBOL("gzclose_w") + EXPORT SYMBOL("gzdirect") + EXPORT SYMBOL("gzoffset") + EXPORT SYMBOL("gzoffset64") + EXPORT SYMBOL("gzopen64") + EXPORT SYMBOL("gzseek64") + EXPORT SYMBOL("gztell64") + EXPORT SYMBOL("inflateGetHeader") + EXPORT SYMBOL("inflateMark") + EXPORT SYMBOL("inflatePrime") + EXPORT SYMBOL("inflateReset2") + EXPORT SYMBOL("inflateUndermine") + +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ +/* Version 1.2.6 additional entry points. */ +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ + + EXPORT SYMBOL("deflateResetKeep") + EXPORT SYMBOL("gzgetc_") + EXPORT SYMBOL("inflateResetKeep") + +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ +/* Version 1.2.8 additional entry points. */ +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ + + EXPORT SYMBOL("gzvprintf") + EXPORT SYMBOL("inflateGetDictionary") + +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ +/* Version 1.2.9 additional entry points. */ +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ + + EXPORT SYMBOL("adler32_z") + EXPORT SYMBOL("crc32_z") + EXPORT SYMBOL("deflateGetDictionary") + EXPORT SYMBOL("gzfread") + EXPORT SYMBOL("gzfwrite") + EXPORT SYMBOL("inflateCodesUsed") + EXPORT SYMBOL("inflateValidate") + EXPORT SYMBOL("uncompress2") + +ENDPGMEXP diff --git a/src/png/zlib/os400/make.sh b/src/png/zlib/os400/make.sh new file mode 100644 index 0000000000..19eec117a6 --- /dev/null +++ b/src/png/zlib/os400/make.sh @@ -0,0 +1,366 @@ +#!/bin/sh +# +# ZLIB compilation script for the OS/400. +# +# +# This is a shell script since make is not a standard component of OS/400. + + +################################################################################ +# +# Tunable configuration parameters. +# +################################################################################ + +TARGETLIB='ZLIB' # Target OS/400 program library +STATBNDDIR='ZLIB_A' # Static binding directory. +DYNBNDDIR='ZLIB' # Dynamic binding directory. +SRVPGM="ZLIB" # Service program. +IFSDIR='/zlib' # IFS support base directory. +TGTCCSID='500' # Target CCSID of objects +DEBUG='*NONE' # Debug level +OPTIMIZE='40' # Optimisation level +OUTPUT='*NONE' # Compilation output option. +TGTRLS='V6R1M0' # Target OS release + +export TARGETLIB STATBNDDIR DYNBNDDIR SRVPGM IFSDIR +export TGTCCSID DEBUG OPTIMIZE OUTPUT TGTRLS + + +################################################################################ +# +# OS/400 specific definitions. +# +################################################################################ + +LIBIFSNAME="/QSYS.LIB/${TARGETLIB}.LIB" + + +################################################################################ +# +# Procedures. +# +################################################################################ + +# action_needed dest [src] +# +# dest is an object to build +# if specified, src is an object on which dest depends. +# +# exit 0 (succeeds) if some action has to be taken, else 1. + +action_needed() + +{ + [ ! -e "${1}" ] && return 0 + [ "${2}" ] || return 1 + [ "${1}" -ot "${2}" ] && return 0 + return 1 +} + + +# make_module module_name source_name [additional_definitions] +# +# Compile source name into module if needed. +# As side effect, append the module name to variable MODULES. +# Set LINK to "YES" if the module has been compiled. + +make_module() + +{ + MODULES="${MODULES} ${1}" + MODIFSNAME="${LIBIFSNAME}/${1}.MODULE" + CSRC="`basename \"${2}\"`" + + if action_needed "${MODIFSNAME}" "${2}" + then : + elif [ ! "`sed -e \"//,/<\\\\/source>/!d\" \ + -e '/ tmphdrfile + + # Need to translate to target CCSID. + + CMD="CPY OBJ('`pwd`/tmphdrfile') TOOBJ('${DEST}')" + CMD="${CMD} TOCCSID(${TGTCCSID}) DTAFMT(*TEXT) REPLACE(*YES)" + system "${CMD}" + # touch -r "${HFILE}" "${DEST}" + rm -f tmphdrfile + fi + + IFSFILE="${IFSDIR}/include/`basename \"${HFILE}\"`" + + if action_needed "${IFSFILE}" "${DEST}" + then rm -f "${IFSFILE}" + ln -s "${DEST}" "${IFSFILE}" + fi +done + + +# Install the ILE/RPG header file. + + +HFILE="${SCRIPTDIR}/zlib.inc" +DEST="${SRCPF}/ZLIB.INC.MBR" + +if action_needed "${DEST}" "${HFILE}" +then CMD="CPY OBJ('${HFILE}') TOOBJ('${DEST}')" + CMD="${CMD} TOCCSID(${TGTCCSID}) DTAFMT(*TEXT) REPLACE(*YES)" + system "${CMD}" + # touch -r "${HFILE}" "${DEST}" +fi + +IFSFILE="${IFSDIR}/include/`basename \"${HFILE}\"`" + +if action_needed "${IFSFILE}" "${DEST}" +then rm -f "${IFSFILE}" + ln -s "${DEST}" "${IFSFILE}" +fi + + +# Create and compile the identification source file. + +echo '#pragma comment(user, "ZLIB version '"${VERSION}"'")' > os400.c +echo '#pragma comment(user, __DATE__)' >> os400.c +echo '#pragma comment(user, __TIME__)' >> os400.c +echo '#pragma comment(copyright, "Copyright (C) 1995-2017 Jean-Loup Gailly, Mark Adler. OS/400 version by P. Monnerat.")' >> os400.c +make_module OS400 os400.c +LINK= # No need to rebuild service program yet. +MODULES= + + +# Get source list. + +CSOURCES=`sed -e '/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Library + + Medium + + 2.0 + + + + zlib + zlib + alain.bonnefoy@icbt.com + Public + public + www.gzip.org/zlib + + + Jean-Loup Gailly,Mark Adler + www.gzip.org/zlib + + zlib@gzip.org + + + A massively spiffy yet delicately unobtrusive compression library. + zlib is designed to be a free, general-purpose, legally unencumbered, lossless data compression library for use on virtually any computer hardware and operating system. + http://www.gzip.org/zlib + + + + + 1.2.11 + Medium + Stable + + + + + + + No License + + + + Software Development/Libraries and Extensions/C Libraries + zlib,compression + qnx6 + qnx6 + None + Developer + + + + + + + + + + + + + + Install + Post + No + Ignore + + No + Optional + + + + + + + + + + + + + InstallOver + zlib + + + + + + + + + + + + + InstallOver + zlib-dev + + + + + + + + + diff --git a/src/png/zlib/treebuild.xml b/src/png/zlib/treebuild.xml new file mode 100644 index 0000000000..fd75525f99 --- /dev/null +++ b/src/png/zlib/treebuild.xml @@ -0,0 +1,116 @@ + + + + zip compression library + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/png/zlib/trees.c b/src/png/zlib/trees.c new file mode 100644 index 0000000000..50cf4b4571 --- /dev/null +++ b/src/png/zlib/trees.c @@ -0,0 +1,1203 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2017 Jean-loup Gailly + * detect_data_type() function provided freely by Cosmin Truta, 2006 + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef ZLIB_DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local const static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local const static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local const static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, const ct_data *ltree, + const ct_data *dtree)); +local int detect_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef ZLIB_DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* !ZLIB_DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef ZLIB_DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (ush)value << s->bi_valid; + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= (ush)value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !ZLIB_DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = (int)value;\ + s->bi_buf |= (ush)val << s->bi_valid;\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* ZLIB_DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ +#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef ZLIB_DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, + "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void ZLIB_INTERNAL _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef ZLIB_DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (unsigned)(bits + xbits); + if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits); + } + if (overflow == 0) return; + + Tracev((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Tracev((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((ulg)bits - tree[m].Len) * tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + unsigned code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + code = (code + bl_count[bits-1]) << 1; + next_code[bits] = (ush)code; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*((ulg)max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ + bi_windup(s); /* align on byte boundary */ + put_short(s, (ush)stored_len); + put_short(s, (ush)~stored_len); + zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len); + s->pending += stored_len; +#ifdef ZLIB_DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; + s->bits_sent += 2*16; + s->bits_sent += stored_len<<3; +#endif +} + +/* =========================================================================== + * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) + */ +void ZLIB_INTERNAL _tr_flush_bits(s) + deflate_state *s; +{ + bi_flush(s); +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + */ +void ZLIB_INTERNAL _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef ZLIB_DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and write out the encoded block. + */ +void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (s->strm->data_type == Z_UNKNOWN) + s->strm->data_type = detect_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+last, 3); + compress_block(s, (const ct_data *)static_ltree, + (const ct_data *)static_dtree); +#ifdef ZLIB_DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+last, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (const ct_data *)s->dyn_ltree, + (const ct_data *)s->dyn_dtree); +#ifdef ZLIB_DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); +#ifdef ZLIB_DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*last)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ZLIB_INTERNAL _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + const ct_data *ltree; /* literal tree */ + const ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= (unsigned)base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(s) + deflate_state *s; +{ + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>= 1) + if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("white-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef ZLIB_DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} diff --git a/src/png/zlib/trees.h b/src/png/zlib/trees.h new file mode 100644 index 0000000000..d35639d82a --- /dev/null +++ b/src/png/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/src/png/zlib/uncompr.c b/src/png/zlib/uncompr.c new file mode 100644 index 0000000000..f03a1a865e --- /dev/null +++ b/src/png/zlib/uncompr.c @@ -0,0 +1,93 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. *sourceLen is + the byte length of the source buffer. Upon entry, *destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, + *destLen is the size of the decompressed data and *sourceLen is the number + of source bytes consumed. Upon return, source + *sourceLen points to the + first unused input byte. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, or + Z_DATA_ERROR if the input data was corrupted, including if the input data is + an incomplete zlib stream. +*/ +int ZEXPORT uncompress2 (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong *sourceLen; +{ + z_stream stream; + int err; + const uInt max = (uInt)-1; + uLong len, left; + Byte buf[1]; /* for detection of incomplete stream when *destLen == 0 */ + + len = *sourceLen; + if (*destLen) { + left = *destLen; + *destLen = 0; + } + else { + left = 1; + dest = buf; + } + + stream.next_in = (z_const Bytef *)source; + stream.avail_in = 0; + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + stream.next_out = dest; + stream.avail_out = 0; + + do { + if (stream.avail_out == 0) { + stream.avail_out = left > (uLong)max ? max : (uInt)left; + left -= stream.avail_out; + } + if (stream.avail_in == 0) { + stream.avail_in = len > (uLong)max ? max : (uInt)len; + len -= stream.avail_in; + } + err = inflate(&stream, Z_NO_FLUSH); + } while (err == Z_OK); + + *sourceLen -= len + stream.avail_in; + if (dest != buf) + *destLen = stream.total_out; + else if (stream.total_out && err == Z_BUF_ERROR) + left = 1; + + inflateEnd(&stream); + return err == Z_STREAM_END ? Z_OK : + err == Z_NEED_DICT ? Z_DATA_ERROR : + err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR : + err; +} + +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return uncompress2(dest, destLen, source, &sourceLen); +} diff --git a/src/png/zlib/watcom/watcom_f.mak b/src/png/zlib/watcom/watcom_f.mak new file mode 100644 index 0000000000..37f4d74c19 --- /dev/null +++ b/src/png/zlib/watcom/watcom_f.mak @@ -0,0 +1,43 @@ +# Makefile for zlib +# OpenWatcom flat model +# Last updated: 28-Dec-2005 + +# To use, do "wmake -f watcom_f.mak" + +C_SOURCE = adler32.c compress.c crc32.c deflate.c & + gzclose.c gzlib.c gzread.c gzwrite.c & + infback.c inffast.c inflate.c inftrees.c & + trees.c uncompr.c zutil.c + +OBJS = adler32.obj compress.obj crc32.obj deflate.obj & + gzclose.obj gzlib.obj gzread.obj gzwrite.obj & + infback.obj inffast.obj inflate.obj inftrees.obj & + trees.obj uncompr.obj zutil.obj + +CC = wcc386 +LINKER = wcl386 +CFLAGS = -zq -mf -3r -fp3 -s -bt=dos -oilrtfm -fr=nul -wx +ZLIB_LIB = zlib_f.lib + +.C.OBJ: + $(CC) $(CFLAGS) $[@ + +all: $(ZLIB_LIB) example.exe minigzip.exe + +$(ZLIB_LIB): $(OBJS) + wlib -b -c $(ZLIB_LIB) -+adler32.obj -+compress.obj -+crc32.obj + wlib -b -c $(ZLIB_LIB) -+gzclose.obj -+gzlib.obj -+gzread.obj -+gzwrite.obj + wlib -b -c $(ZLIB_LIB) -+deflate.obj -+infback.obj + wlib -b -c $(ZLIB_LIB) -+inffast.obj -+inflate.obj -+inftrees.obj + wlib -b -c $(ZLIB_LIB) -+trees.obj -+uncompr.obj -+zutil.obj + +example.exe: $(ZLIB_LIB) example.obj + $(LINKER) -ldos32a -fe=example.exe example.obj $(ZLIB_LIB) + +minigzip.exe: $(ZLIB_LIB) minigzip.obj + $(LINKER) -ldos32a -fe=minigzip.exe minigzip.obj $(ZLIB_LIB) + +clean: .SYMBOLIC + del *.obj + del $(ZLIB_LIB) + @echo Cleaning done diff --git a/src/png/zlib/watcom/watcom_l.mak b/src/png/zlib/watcom/watcom_l.mak new file mode 100644 index 0000000000..193eed7b31 --- /dev/null +++ b/src/png/zlib/watcom/watcom_l.mak @@ -0,0 +1,43 @@ +# Makefile for zlib +# OpenWatcom large model +# Last updated: 28-Dec-2005 + +# To use, do "wmake -f watcom_l.mak" + +C_SOURCE = adler32.c compress.c crc32.c deflate.c & + gzclose.c gzlib.c gzread.c gzwrite.c & + infback.c inffast.c inflate.c inftrees.c & + trees.c uncompr.c zutil.c + +OBJS = adler32.obj compress.obj crc32.obj deflate.obj & + gzclose.obj gzlib.obj gzread.obj gzwrite.obj & + infback.obj inffast.obj inflate.obj inftrees.obj & + trees.obj uncompr.obj zutil.obj + +CC = wcc +LINKER = wcl +CFLAGS = -zq -ml -s -bt=dos -oilrtfm -fr=nul -wx +ZLIB_LIB = zlib_l.lib + +.C.OBJ: + $(CC) $(CFLAGS) $[@ + +all: $(ZLIB_LIB) example.exe minigzip.exe + +$(ZLIB_LIB): $(OBJS) + wlib -b -c $(ZLIB_LIB) -+adler32.obj -+compress.obj -+crc32.obj + wlib -b -c $(ZLIB_LIB) -+gzclose.obj -+gzlib.obj -+gzread.obj -+gzwrite.obj + wlib -b -c $(ZLIB_LIB) -+deflate.obj -+infback.obj + wlib -b -c $(ZLIB_LIB) -+inffast.obj -+inflate.obj -+inftrees.obj + wlib -b -c $(ZLIB_LIB) -+trees.obj -+uncompr.obj -+zutil.obj + +example.exe: $(ZLIB_LIB) example.obj + $(LINKER) -fe=example.exe example.obj $(ZLIB_LIB) + +minigzip.exe: $(ZLIB_LIB) minigzip.obj + $(LINKER) -fe=minigzip.exe minigzip.obj $(ZLIB_LIB) + +clean: .SYMBOLIC + del *.obj + del $(ZLIB_LIB) + @echo Cleaning done diff --git a/src/png/zlib/win32/DLL_FAQ.txt b/src/png/zlib/win32/DLL_FAQ.txt new file mode 100644 index 0000000000..12c009018c --- /dev/null +++ b/src/png/zlib/win32/DLL_FAQ.txt @@ -0,0 +1,397 @@ + + Frequently Asked Questions about ZLIB1.DLL + + +This document describes the design, the rationale, and the usage +of the official DLL build of zlib, named ZLIB1.DLL. If you have +general questions about zlib, you should see the file "FAQ" found +in the zlib distribution, or at the following location: + http://www.gzip.org/zlib/zlib_faq.html + + + 1. What is ZLIB1.DLL, and how can I get it? + + - ZLIB1.DLL is the official build of zlib as a DLL. + (Please remark the character '1' in the name.) + + Pointers to a precompiled ZLIB1.DLL can be found in the zlib + web site at: + http://www.zlib.net/ + + Applications that link to ZLIB1.DLL can rely on the following + specification: + + * The exported symbols are exclusively defined in the source + files "zlib.h" and "zlib.def", found in an official zlib + source distribution. + * The symbols are exported by name, not by ordinal. + * The exported names are undecorated. + * The calling convention of functions is "C" (CDECL). + * The ZLIB1.DLL binary is linked to MSVCRT.DLL. + + The archive in which ZLIB1.DLL is bundled contains compiled + test programs that must run with a valid build of ZLIB1.DLL. + It is recommended to download the prebuilt DLL from the zlib + web site, instead of building it yourself, to avoid potential + incompatibilities that could be introduced by your compiler + and build settings. If you do build the DLL yourself, please + make sure that it complies with all the above requirements, + and it runs with the precompiled test programs, bundled with + the original ZLIB1.DLL distribution. + + If, for any reason, you need to build an incompatible DLL, + please use a different file name. + + + 2. Why did you change the name of the DLL to ZLIB1.DLL? + What happened to the old ZLIB.DLL? + + - The old ZLIB.DLL, built from zlib-1.1.4 or earlier, required + compilation settings that were incompatible to those used by + a static build. The DLL settings were supposed to be enabled + by defining the macro ZLIB_DLL, before including "zlib.h". + Incorrect handling of this macro was silently accepted at + build time, resulting in two major problems: + + * ZLIB_DLL was missing from the old makefile. When building + the DLL, not all people added it to the build options. In + consequence, incompatible incarnations of ZLIB.DLL started + to circulate around the net. + + * When switching from using the static library to using the + DLL, applications had to define the ZLIB_DLL macro and + to recompile all the sources that contained calls to zlib + functions. Failure to do so resulted in creating binaries + that were unable to run with the official ZLIB.DLL build. + + The only possible solution that we could foresee was to make + a binary-incompatible change in the DLL interface, in order to + remove the dependency on the ZLIB_DLL macro, and to release + the new DLL under a different name. + + We chose the name ZLIB1.DLL, where '1' indicates the major + zlib version number. We hope that we will not have to break + the binary compatibility again, at least not as long as the + zlib-1.x series will last. + + There is still a ZLIB_DLL macro, that can trigger a more + efficient build and use of the DLL, but compatibility no + longer dependents on it. + + + 3. Can I build ZLIB.DLL from the new zlib sources, and replace + an old ZLIB.DLL, that was built from zlib-1.1.4 or earlier? + + - In principle, you can do it by assigning calling convention + keywords to the macros ZEXPORT and ZEXPORTVA. In practice, + it depends on what you mean by "an old ZLIB.DLL", because the + old DLL exists in several mutually-incompatible versions. + You have to find out first what kind of calling convention is + being used in your particular ZLIB.DLL build, and to use the + same one in the new build. If you don't know what this is all + about, you might be better off if you would just leave the old + DLL intact. + + + 4. Can I compile my application using the new zlib interface, and + link it to an old ZLIB.DLL, that was built from zlib-1.1.4 or + earlier? + + - The official answer is "no"; the real answer depends again on + what kind of ZLIB.DLL you have. Even if you are lucky, this + course of action is unreliable. + + If you rebuild your application and you intend to use a newer + version of zlib (post- 1.1.4), it is strongly recommended to + link it to the new ZLIB1.DLL. + + + 5. Why are the zlib symbols exported by name, and not by ordinal? + + - Although exporting symbols by ordinal is a little faster, it + is risky. Any single glitch in the maintenance or use of the + DEF file that contains the ordinals can result in incompatible + builds and frustrating crashes. Simply put, the benefits of + exporting symbols by ordinal do not justify the risks. + + Technically, it should be possible to maintain ordinals in + the DEF file, and still export the symbols by name. Ordinals + exist in every DLL, and even if the dynamic linking performed + at the DLL startup is searching for names, ordinals serve as + hints, for a faster name lookup. However, if the DEF file + contains ordinals, the Microsoft linker automatically builds + an implib that will cause the executables linked to it to use + those ordinals, and not the names. It is interesting to + notice that the GNU linker for Win32 does not suffer from this + problem. + + It is possible to avoid the DEF file if the exported symbols + are accompanied by a "__declspec(dllexport)" attribute in the + source files. You can do this in zlib by predefining the + ZLIB_DLL macro. + + + 6. I see that the ZLIB1.DLL functions use the "C" (CDECL) calling + convention. Why not use the STDCALL convention? + STDCALL is the standard convention in Win32, and I need it in + my Visual Basic project! + + (For readability, we use CDECL to refer to the convention + triggered by the "__cdecl" keyword, STDCALL to refer to + the convention triggered by "__stdcall", and FASTCALL to + refer to the convention triggered by "__fastcall".) + + - Most of the native Windows API functions (without varargs) use + indeed the WINAPI convention (which translates to STDCALL in + Win32), but the standard C functions use CDECL. If a user + application is intrinsically tied to the Windows API (e.g. + it calls native Windows API functions such as CreateFile()), + sometimes it makes sense to decorate its own functions with + WINAPI. But if ANSI C or POSIX portability is a goal (e.g. + it calls standard C functions such as fopen()), it is not a + sound decision to request the inclusion of , or to + use non-ANSI constructs, for the sole purpose to make the user + functions STDCALL-able. + + The functionality offered by zlib is not in the category of + "Windows functionality", but is more like "C functionality". + + Technically, STDCALL is not bad; in fact, it is slightly + faster than CDECL, and it works with variable-argument + functions, just like CDECL. It is unfortunate that, in spite + of using STDCALL in the Windows API, it is not the default + convention used by the C compilers that run under Windows. + The roots of the problem reside deep inside the unsafety of + the K&R-style function prototypes, where the argument types + are not specified; but that is another story for another day. + + The remaining fact is that CDECL is the default convention. + Even if an explicit convention is hard-coded into the function + prototypes inside C headers, problems may appear. The + necessity to expose the convention in users' callbacks is one + of these problems. + + The calling convention issues are also important when using + zlib in other programming languages. Some of them, like Ada + (GNAT) and Fortran (GNU G77), have C bindings implemented + initially on Unix, and relying on the C calling convention. + On the other hand, the pre- .NET versions of Microsoft Visual + Basic require STDCALL, while Borland Delphi prefers, although + it does not require, FASTCALL. + + In fairness to all possible uses of zlib outside the C + programming language, we choose the default "C" convention. + Anyone interested in different bindings or conventions is + encouraged to maintain specialized projects. The "contrib/" + directory from the zlib distribution already holds a couple + of foreign bindings, such as Ada, C++, and Delphi. + + + 7. I need a DLL for my Visual Basic project. What can I do? + + - Define the ZLIB_WINAPI macro before including "zlib.h", when + building both the DLL and the user application (except that + you don't need to define anything when using the DLL in Visual + Basic). The ZLIB_WINAPI macro will switch on the WINAPI + (STDCALL) convention. The name of this DLL must be different + than the official ZLIB1.DLL. + + Gilles Vollant has contributed a build named ZLIBWAPI.DLL, + with the ZLIB_WINAPI macro turned on, and with the minizip + functionality built in. For more information, please read + the notes inside "contrib/vstudio/readme.txt", found in the + zlib distribution. + + + 8. I need to use zlib in my Microsoft .NET project. What can I + do? + + - Henrik Ravn has contributed a .NET wrapper around zlib. Look + into contrib/dotzlib/, inside the zlib distribution. + + + 9. If my application uses ZLIB1.DLL, should I link it to + MSVCRT.DLL? Why? + + - It is not required, but it is recommended to link your + application to MSVCRT.DLL, if it uses ZLIB1.DLL. + + The executables (.EXE, .DLL, etc.) that are involved in the + same process and are using the C run-time library (i.e. they + are calling standard C functions), must link to the same + library. There are several libraries in the Win32 system: + CRTDLL.DLL, MSVCRT.DLL, the static C libraries, etc. + Since ZLIB1.DLL is linked to MSVCRT.DLL, the executables that + depend on it should also be linked to MSVCRT.DLL. + + +10. Why are you saying that ZLIB1.DLL and my application should + be linked to the same C run-time (CRT) library? I linked my + application and my DLLs to different C libraries (e.g. my + application to a static library, and my DLLs to MSVCRT.DLL), + and everything works fine. + + - If a user library invokes only pure Win32 API (accessible via + and the related headers), its DLL build will work + in any context. But if this library invokes standard C API, + things get more complicated. + + There is a single Win32 library in a Win32 system. Every + function in this library resides in a single DLL module, that + is safe to call from anywhere. On the other hand, there are + multiple versions of the C library, and each of them has its + own separate internal state. Standalone executables and user + DLLs that call standard C functions must link to a C run-time + (CRT) library, be it static or shared (DLL). Intermixing + occurs when an executable (not necessarily standalone) and a + DLL are linked to different CRTs, and both are running in the + same process. + + Intermixing multiple CRTs is possible, as long as their + internal states are kept intact. The Microsoft Knowledge Base + articles KB94248 "HOWTO: Use the C Run-Time" and KB140584 + "HOWTO: Link with the Correct C Run-Time (CRT) Library" + mention the potential problems raised by intermixing. + + If intermixing works for you, it's because your application + and DLLs are avoiding the corruption of each of the CRTs' + internal states, maybe by careful design, or maybe by fortune. + + Also note that linking ZLIB1.DLL to non-Microsoft CRTs, such + as those provided by Borland, raises similar problems. + + +11. Why are you linking ZLIB1.DLL to MSVCRT.DLL? + + - MSVCRT.DLL exists on every Windows 95 with a new service pack + installed, or with Microsoft Internet Explorer 4 or later, and + on all other Windows 4.x or later (Windows 98, Windows NT 4, + or later). It is freely distributable; if not present in the + system, it can be downloaded from Microsoft or from other + software provider for free. + + The fact that MSVCRT.DLL does not exist on a virgin Windows 95 + is not so problematic. Windows 95 is scarcely found nowadays, + Microsoft ended its support a long time ago, and many recent + applications from various vendors, including Microsoft, do not + even run on it. Furthermore, no serious user should run + Windows 95 without a proper update installed. + + +12. Why are you not linking ZLIB1.DLL to + <> ? + + - We considered and abandoned the following alternatives: + + * Linking ZLIB1.DLL to a static C library (LIBC.LIB, or + LIBCMT.LIB) is not a good option. People are using the DLL + mainly to save disk space. If you are linking your program + to a static C library, you may as well consider linking zlib + in statically, too. + + * Linking ZLIB1.DLL to CRTDLL.DLL looks appealing, because + CRTDLL.DLL is present on every Win32 installation. + Unfortunately, it has a series of problems: it does not + work properly with Microsoft's C++ libraries, it does not + provide support for 64-bit file offsets, (and so on...), + and Microsoft discontinued its support a long time ago. + + * Linking ZLIB1.DLL to MSVCR70.DLL or MSVCR71.DLL, supplied + with the Microsoft .NET platform, and Visual C++ 7.0/7.1, + raises problems related to the status of ZLIB1.DLL as a + system component. According to the Microsoft Knowledge Base + article KB326922 "INFO: Redistribution of the Shared C + Runtime Component in Visual C++ .NET", MSVCR70.DLL and + MSVCR71.DLL are not supposed to function as system DLLs, + because they may clash with MSVCRT.DLL. Instead, the + application's installer is supposed to put these DLLs + (if needed) in the application's private directory. + If ZLIB1.DLL depends on a non-system runtime, it cannot + function as a redistributable system component. + + * Linking ZLIB1.DLL to non-Microsoft runtimes, such as + Borland's, or Cygwin's, raises problems related to the + reliable presence of these runtimes on Win32 systems. + It's easier to let the DLL build of zlib up to the people + who distribute these runtimes, and who may proceed as + explained in the answer to Question 14. + + +13. If ZLIB1.DLL cannot be linked to MSVCR70.DLL or MSVCR71.DLL, + how can I build/use ZLIB1.DLL in Microsoft Visual C++ 7.0 + (Visual Studio .NET) or newer? + + - Due to the problems explained in the Microsoft Knowledge Base + article KB326922 (see the previous answer), the C runtime that + comes with the VC7 environment is no longer considered a + system component. That is, it should not be assumed that this + runtime exists, or may be installed in a system directory. + Since ZLIB1.DLL is supposed to be a system component, it may + not depend on a non-system component. + + In order to link ZLIB1.DLL and your application to MSVCRT.DLL + in VC7, you need the library of Visual C++ 6.0 or older. If + you don't have this library at hand, it's probably best not to + use ZLIB1.DLL. + + We are hoping that, in the future, Microsoft will provide a + way to build applications linked to a proper system runtime, + from the Visual C++ environment. Until then, you have a + couple of alternatives, such as linking zlib in statically. + If your application requires dynamic linking, you may proceed + as explained in the answer to Question 14. + + +14. I need to link my own DLL build to a CRT different than + MSVCRT.DLL. What can I do? + + - Feel free to rebuild the DLL from the zlib sources, and link + it the way you want. You should, however, clearly state that + your build is unofficial. You should give it a different file + name, and/or install it in a private directory that can be + accessed by your application only, and is not visible to the + others (i.e. it's neither in the PATH, nor in the SYSTEM or + SYSTEM32 directories). Otherwise, your build may clash with + applications that link to the official build. + + For example, in Cygwin, zlib is linked to the Cygwin runtime + CYGWIN1.DLL, and it is distributed under the name CYGZ.DLL. + + +15. May I include additional pieces of code that I find useful, + link them in ZLIB1.DLL, and export them? + + - No. A legitimate build of ZLIB1.DLL must not include code + that does not originate from the official zlib source code. + But you can make your own private DLL build, under a different + file name, as suggested in the previous answer. + + For example, zlib is a part of the VCL library, distributed + with Borland Delphi and C++ Builder. The DLL build of VCL + is a redistributable file, named VCLxx.DLL. + + +16. May I remove some functionality out of ZLIB1.DLL, by enabling + macros like NO_GZCOMPRESS or NO_GZIP at compile time? + + - No. A legitimate build of ZLIB1.DLL must provide the complete + zlib functionality, as implemented in the official zlib source + code. But you can make your own private DLL build, under a + different file name, as suggested in the previous answer. + + +17. I made my own ZLIB1.DLL build. Can I test it for compliance? + + - We prefer that you download the official DLL from the zlib + web site. If you need something peculiar from this DLL, you + can send your suggestion to the zlib mailing list. + + However, in case you do rebuild the DLL yourself, you can run + it with the test programs found in the DLL distribution. + Running these test programs is not a guarantee of compliance, + but a failure can imply a detected problem. + +** + +This document is written and maintained by +Cosmin Truta diff --git a/src/png/zlib/win32/Makefile.bor b/src/png/zlib/win32/Makefile.bor new file mode 100644 index 0000000000..d152bbb7ff --- /dev/null +++ b/src/png/zlib/win32/Makefile.bor @@ -0,0 +1,110 @@ +# Makefile for zlib +# Borland C++ for Win32 +# +# Usage: +# make -f win32/Makefile.bor +# make -f win32/Makefile.bor LOCAL_ZLIB=-DASMV OBJA=match.obj OBJPA=+match.obj + +# ------------ Borland C++ ------------ + +# Optional nonstandard preprocessor flags (e.g. -DMAX_MEM_LEVEL=7) +# should be added to the environment via "set LOCAL_ZLIB=-DFOO" or +# added to the declaration of LOC here: +LOC = $(LOCAL_ZLIB) + +CC = bcc32 +AS = bcc32 +LD = bcc32 +AR = tlib +CFLAGS = -a -d -k- -O2 $(LOC) +ASFLAGS = $(LOC) +LDFLAGS = $(LOC) + + +# variables +ZLIB_LIB = zlib.lib + +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj +#OBJA = +OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj +OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj +#OBJPA= + + +# targets +all: $(ZLIB_LIB) example.exe minigzip.exe + +.c.obj: + $(CC) -c $(CFLAGS) $< + +.asm.obj: + $(AS) -c $(ASFLAGS) $< + +adler32.obj: adler32.c zlib.h zconf.h + +compress.obj: compress.c zlib.h zconf.h + +crc32.obj: crc32.c zlib.h zconf.h crc32.h + +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h + +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h + +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h + +gzread.obj: gzread.c zlib.h zconf.h gzguts.h + +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h + +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h + +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h + +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h + +uncompr.obj: uncompr.c zlib.h zconf.h + +zutil.obj: zutil.c zutil.h zlib.h zconf.h + +example.obj: test/example.c zlib.h zconf.h + +minigzip.obj: test/minigzip.c zlib.h zconf.h + + +# For the sake of the old Borland make, +# the command line is cut to fit in the MS-DOS 128 byte limit: +$(ZLIB_LIB): $(OBJ1) $(OBJ2) $(OBJA) + -del $(ZLIB_LIB) + $(AR) $(ZLIB_LIB) $(OBJP1) + $(AR) $(ZLIB_LIB) $(OBJP2) + $(AR) $(ZLIB_LIB) $(OBJPA) + + +# testing +test: example.exe minigzip.exe + example + echo hello world | minigzip | minigzip -d + +example.exe: example.obj $(ZLIB_LIB) + $(LD) $(LDFLAGS) example.obj $(ZLIB_LIB) + +minigzip.exe: minigzip.obj $(ZLIB_LIB) + $(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB) + + +# cleanup +clean: + -del $(ZLIB_LIB) + -del *.obj + -del *.exe + -del *.tds + -del zlib.bak + -del foo.gz diff --git a/src/png/zlib/win32/Makefile.gcc b/src/png/zlib/win32/Makefile.gcc new file mode 100644 index 0000000000..305be50afe --- /dev/null +++ b/src/png/zlib/win32/Makefile.gcc @@ -0,0 +1,182 @@ +# Makefile for zlib, derived from Makefile.dj2. +# Modified for mingw32 by C. Spieler, 6/16/98. +# Updated for zlib 1.2.x by Christian Spieler and Cosmin Truta, Mar-2003. +# Last updated: Mar 2012. +# Tested under Cygwin and MinGW. + +# Copyright (C) 1995-2003 Jean-loup Gailly. +# For conditions of distribution and use, see copyright notice in zlib.h + +# To compile, or to compile and test, type from the top level zlib directory: +# +# make -fwin32/Makefile.gcc; make test testdll -fwin32/Makefile.gcc +# +# To use the asm code, type: +# cp contrib/asm?86/match.S ./match.S +# make LOC=-DASMV OBJA=match.o -fwin32/Makefile.gcc +# +# To install libz.a, zconf.h and zlib.h in the system directories, type: +# +# make install -fwin32/Makefile.gcc +# +# BINARY_PATH, INCLUDE_PATH and LIBRARY_PATH must be set. +# +# To install the shared lib, append SHARED_MODE=1 to the make command : +# +# make install -fwin32/Makefile.gcc SHARED_MODE=1 + +# Note: +# If the platform is *not* MinGW (e.g. it is Cygwin or UWIN), +# the DLL name should be changed from "zlib1.dll". + +STATICLIB = libz.a +SHAREDLIB = zlib1.dll +IMPLIB = libz.dll.a + +# +# Set to 1 if shared object needs to be installed +# +SHARED_MODE=0 + +#LOC = -DASMV +#LOC = -DZLIB_DEBUG -g + +PREFIX = +CC = $(PREFIX)gcc +CFLAGS = $(LOC) -O3 -Wall + +AS = $(CC) +ASFLAGS = $(LOC) -Wall + +LD = $(CC) +LDFLAGS = $(LOC) + +AR = $(PREFIX)ar +ARFLAGS = rcs + +RC = $(PREFIX)windres +RCFLAGS = --define GCC_WINDRES + +STRIP = $(PREFIX)strip + +CP = cp -fp +# If GNU install is available, replace $(CP) with install. +INSTALL = $(CP) +RM = rm -f + +prefix ?= /usr/local +exec_prefix = $(prefix) + +OBJS = adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o \ + gzwrite.o infback.o inffast.o inflate.o inftrees.o trees.o uncompr.o zutil.o +OBJA = + +all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) example.exe minigzip.exe example_d.exe minigzip_d.exe + +test: example.exe minigzip.exe + ./example + echo hello world | ./minigzip | ./minigzip -d + +testdll: example_d.exe minigzip_d.exe + ./example_d + echo hello world | ./minigzip_d | ./minigzip_d -d + +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +.S.o: + $(AS) $(ASFLAGS) -c -o $@ $< + +$(STATICLIB): $(OBJS) $(OBJA) + $(AR) $(ARFLAGS) $@ $(OBJS) $(OBJA) + +$(IMPLIB): $(SHAREDLIB) + +$(SHAREDLIB): win32/zlib.def $(OBJS) $(OBJA) zlibrc.o + $(CC) -shared -Wl,--out-implib,$(IMPLIB) $(LDFLAGS) \ + -o $@ win32/zlib.def $(OBJS) $(OBJA) zlibrc.o + $(STRIP) $@ + +example.exe: example.o $(STATICLIB) + $(LD) $(LDFLAGS) -o $@ example.o $(STATICLIB) + $(STRIP) $@ + +minigzip.exe: minigzip.o $(STATICLIB) + $(LD) $(LDFLAGS) -o $@ minigzip.o $(STATICLIB) + $(STRIP) $@ + +example_d.exe: example.o $(IMPLIB) + $(LD) $(LDFLAGS) -o $@ example.o $(IMPLIB) + $(STRIP) $@ + +minigzip_d.exe: minigzip.o $(IMPLIB) + $(LD) $(LDFLAGS) -o $@ minigzip.o $(IMPLIB) + $(STRIP) $@ + +example.o: test/example.c zlib.h zconf.h + $(CC) $(CFLAGS) -I. -c -o $@ test/example.c + +minigzip.o: test/minigzip.c zlib.h zconf.h + $(CC) $(CFLAGS) -I. -c -o $@ test/minigzip.c + +zlibrc.o: win32/zlib1.rc + $(RC) $(RCFLAGS) -o $@ win32/zlib1.rc + +.PHONY: install uninstall clean + +install: zlib.h zconf.h $(STATICLIB) $(IMPLIB) + @if test -z "$(DESTDIR)$(INCLUDE_PATH)" -o -z "$(DESTDIR)$(LIBRARY_PATH)" -o -z "$(DESTDIR)$(BINARY_PATH)"; then \ + echo INCLUDE_PATH, LIBRARY_PATH, and BINARY_PATH must be specified; \ + exit 1; \ + fi + -@mkdir -p '$(DESTDIR)$(INCLUDE_PATH)' + -@mkdir -p '$(DESTDIR)$(LIBRARY_PATH)' '$(DESTDIR)$(LIBRARY_PATH)'/pkgconfig + -if [ "$(SHARED_MODE)" = "1" ]; then \ + mkdir -p '$(DESTDIR)$(BINARY_PATH)'; \ + $(INSTALL) $(SHAREDLIB) '$(DESTDIR)$(BINARY_PATH)'; \ + $(INSTALL) $(IMPLIB) '$(DESTDIR)$(LIBRARY_PATH)'; \ + fi + -$(INSTALL) zlib.h '$(DESTDIR)$(INCLUDE_PATH)' + -$(INSTALL) zconf.h '$(DESTDIR)$(INCLUDE_PATH)' + -$(INSTALL) $(STATICLIB) '$(DESTDIR)$(LIBRARY_PATH)' + sed \ + -e 's|@prefix@|${prefix}|g' \ + -e 's|@exec_prefix@|${exec_prefix}|g' \ + -e 's|@libdir@|$(LIBRARY_PATH)|g' \ + -e 's|@sharedlibdir@|$(LIBRARY_PATH)|g' \ + -e 's|@includedir@|$(INCLUDE_PATH)|g' \ + -e 's|@VERSION@|'`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' zlib.h`'|g' \ + zlib.pc.in > '$(DESTDIR)$(LIBRARY_PATH)'/pkgconfig/zlib.pc + +uninstall: + -if [ "$(SHARED_MODE)" = "1" ]; then \ + $(RM) '$(DESTDIR)$(BINARY_PATH)'/$(SHAREDLIB); \ + $(RM) '$(DESTDIR)$(LIBRARY_PATH)'/$(IMPLIB); \ + fi + -$(RM) '$(DESTDIR)$(INCLUDE_PATH)'/zlib.h + -$(RM) '$(DESTDIR)$(INCLUDE_PATH)'/zconf.h + -$(RM) '$(DESTDIR)$(LIBRARY_PATH)'/$(STATICLIB) + +clean: + -$(RM) $(STATICLIB) + -$(RM) $(SHAREDLIB) + -$(RM) $(IMPLIB) + -$(RM) *.o + -$(RM) *.exe + -$(RM) foo.gz + +adler32.o: zlib.h zconf.h +compress.o: zlib.h zconf.h +crc32.o: crc32.h zlib.h zconf.h +deflate.o: deflate.h zutil.h zlib.h zconf.h +gzclose.o: zlib.h zconf.h gzguts.h +gzlib.o: zlib.h zconf.h gzguts.h +gzread.o: zlib.h zconf.h gzguts.h +gzwrite.o: zlib.h zconf.h gzguts.h +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +inftrees.o: zutil.h zlib.h zconf.h inftrees.h +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h +uncompr.o: zlib.h zconf.h +zutil.o: zutil.h zlib.h zconf.h diff --git a/src/png/zlib/win32/Makefile.msc b/src/png/zlib/win32/Makefile.msc new file mode 100644 index 0000000000..6831882de4 --- /dev/null +++ b/src/png/zlib/win32/Makefile.msc @@ -0,0 +1,163 @@ +# Makefile for zlib using Microsoft (Visual) C +# zlib is copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler +# +# Usage: +# nmake -f win32/Makefile.msc (standard build) +# nmake -f win32/Makefile.msc LOC=-DFOO (nonstandard build) +# nmake -f win32/Makefile.msc LOC="-DASMV -DASMINF" \ +# OBJA="inffas32.obj match686.obj" (use ASM code, x86) +# nmake -f win32/Makefile.msc AS=ml64 LOC="-DASMV -DASMINF -I." \ +# OBJA="inffasx64.obj gvmat64.obj inffas8664.obj" (use ASM code, x64) + +# The toplevel directory of the source tree. +# +TOP = . + +# optional build flags +LOC = + +# variables +STATICLIB = zlib.lib +SHAREDLIB = zlib1.dll +IMPLIB = zdll.lib + +CC = cl +AS = ml +LD = link +AR = lib +RC = rc +CFLAGS = -nologo -MD -W3 -O2 -Oy- -Zi -Fd"zlib" $(LOC) +WFLAGS = -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE +ASFLAGS = -coff -Zi $(LOC) +LDFLAGS = -nologo -debug -incremental:no -opt:ref +ARFLAGS = -nologo +RCFLAGS = /dWIN32 /r + +OBJS = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj \ + gzwrite.obj infback.obj inflate.obj inftrees.obj inffast.obj trees.obj uncompr.obj zutil.obj +OBJA = + + +# targets +all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) \ + example.exe minigzip.exe example_d.exe minigzip_d.exe + +$(STATICLIB): $(OBJS) $(OBJA) + $(AR) $(ARFLAGS) -out:$@ $(OBJS) $(OBJA) + +$(IMPLIB): $(SHAREDLIB) + +$(SHAREDLIB): $(TOP)/win32/zlib.def $(OBJS) $(OBJA) zlib1.res + $(LD) $(LDFLAGS) -def:$(TOP)/win32/zlib.def -dll -implib:$(IMPLIB) \ + -out:$@ -base:0x5A4C0000 $(OBJS) $(OBJA) zlib1.res + if exist $@.manifest \ + mt -nologo -manifest $@.manifest -outputresource:$@;2 + +example.exe: example.obj $(STATICLIB) + $(LD) $(LDFLAGS) example.obj $(STATICLIB) + if exist $@.manifest \ + mt -nologo -manifest $@.manifest -outputresource:$@;1 + +minigzip.exe: minigzip.obj $(STATICLIB) + $(LD) $(LDFLAGS) minigzip.obj $(STATICLIB) + if exist $@.manifest \ + mt -nologo -manifest $@.manifest -outputresource:$@;1 + +example_d.exe: example.obj $(IMPLIB) + $(LD) $(LDFLAGS) -out:$@ example.obj $(IMPLIB) + if exist $@.manifest \ + mt -nologo -manifest $@.manifest -outputresource:$@;1 + +minigzip_d.exe: minigzip.obj $(IMPLIB) + $(LD) $(LDFLAGS) -out:$@ minigzip.obj $(IMPLIB) + if exist $@.manifest \ + mt -nologo -manifest $@.manifest -outputresource:$@;1 + +{$(TOP)}.c.obj: + $(CC) -c $(WFLAGS) $(CFLAGS) $< + +{$(TOP)/test}.c.obj: + $(CC) -c -I$(TOP) $(WFLAGS) $(CFLAGS) $< + +{$(TOP)/contrib/masmx64}.c.obj: + $(CC) -c $(WFLAGS) $(CFLAGS) $< + +{$(TOP)/contrib/masmx64}.asm.obj: + $(AS) -c $(ASFLAGS) $< + +{$(TOP)/contrib/masmx86}.asm.obj: + $(AS) -c $(ASFLAGS) $< + +adler32.obj: $(TOP)/adler32.c $(TOP)/zlib.h $(TOP)/zconf.h + +compress.obj: $(TOP)/compress.c $(TOP)/zlib.h $(TOP)/zconf.h + +crc32.obj: $(TOP)/crc32.c $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/crc32.h + +deflate.obj: $(TOP)/deflate.c $(TOP)/deflate.h $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h + +gzclose.obj: $(TOP)/gzclose.c $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/gzguts.h + +gzlib.obj: $(TOP)/gzlib.c $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/gzguts.h + +gzread.obj: $(TOP)/gzread.c $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/gzguts.h + +gzwrite.obj: $(TOP)/gzwrite.c $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/gzguts.h + +infback.obj: $(TOP)/infback.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/inftrees.h $(TOP)/inflate.h \ + $(TOP)/inffast.h $(TOP)/inffixed.h + +inffast.obj: $(TOP)/inffast.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/inftrees.h $(TOP)/inflate.h \ + $(TOP)/inffast.h + +inflate.obj: $(TOP)/inflate.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/inftrees.h $(TOP)/inflate.h \ + $(TOP)/inffast.h $(TOP)/inffixed.h + +inftrees.obj: $(TOP)/inftrees.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/inftrees.h + +trees.obj: $(TOP)/trees.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/deflate.h $(TOP)/trees.h + +uncompr.obj: $(TOP)/uncompr.c $(TOP)/zlib.h $(TOP)/zconf.h + +zutil.obj: $(TOP)/zutil.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h + +gvmat64.obj: $(TOP)/contrib\masmx64\gvmat64.asm + +inffasx64.obj: $(TOP)/contrib\masmx64\inffasx64.asm + +inffas8664.obj: $(TOP)/contrib\masmx64\inffas8664.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h \ + $(TOP)/inftrees.h $(TOP)/inflate.h $(TOP)/inffast.h + +inffas32.obj: $(TOP)/contrib\masmx86\inffas32.asm + +match686.obj: $(TOP)/contrib\masmx86\match686.asm + +example.obj: $(TOP)/test/example.c $(TOP)/zlib.h $(TOP)/zconf.h + +minigzip.obj: $(TOP)/test/minigzip.c $(TOP)/zlib.h $(TOP)/zconf.h + +zlib1.res: $(TOP)/win32/zlib1.rc + $(RC) $(RCFLAGS) /fo$@ $(TOP)/win32/zlib1.rc + +# testing +test: example.exe minigzip.exe + example + echo hello world | minigzip | minigzip -d + +testdll: example_d.exe minigzip_d.exe + example_d + echo hello world | minigzip_d | minigzip_d -d + + +# cleanup +clean: + -del $(STATICLIB) + -del $(SHAREDLIB) + -del $(IMPLIB) + -del *.obj + -del *.res + -del *.exp + -del *.exe + -del *.pdb + -del *.manifest + -del foo.gz diff --git a/src/png/zlib/win32/README-WIN32.txt b/src/png/zlib/win32/README-WIN32.txt new file mode 100644 index 0000000000..df7ab7f4b3 --- /dev/null +++ b/src/png/zlib/win32/README-WIN32.txt @@ -0,0 +1,103 @@ +ZLIB DATA COMPRESSION LIBRARY + +zlib 1.2.11 is a general purpose data compression library. All the code is +thread safe. The data format used by the zlib library is described by RFCs +(Request for Comments) 1950 to 1952 in the files +http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) +and rfc1952.txt (gzip format). + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact zlib@gzip.org). Two compiled +examples are distributed in this package, example and minigzip. The example_d +and minigzip_d flavors validate that the zlib1.dll file is working correctly. + +Questions about zlib should be sent to . The zlib home page +is http://zlib.net/ . Before reporting a problem, please check this site to +verify that you have the latest version of zlib; otherwise get the latest +version and check whether the problem still exists or not. + +PLEASE read DLL_FAQ.txt, and the the zlib FAQ http://zlib.net/zlib_faq.html +before asking for help. + + +Manifest: + +The package zlib-1.2.11-win32-x86.zip will contain the following files: + + README-WIN32.txt This document + ChangeLog Changes since previous zlib packages + DLL_FAQ.txt Frequently asked questions about zlib1.dll + zlib.3.pdf Documentation of this library in Adobe Acrobat format + + example.exe A statically-bound example (using zlib.lib, not the dll) + example.pdb Symbolic information for debugging example.exe + + example_d.exe A zlib1.dll bound example (using zdll.lib) + example_d.pdb Symbolic information for debugging example_d.exe + + minigzip.exe A statically-bound test program (using zlib.lib, not the dll) + minigzip.pdb Symbolic information for debugging minigzip.exe + + minigzip_d.exe A zlib1.dll bound test program (using zdll.lib) + minigzip_d.pdb Symbolic information for debugging minigzip_d.exe + + zlib.h Install these files into the compilers' INCLUDE path to + zconf.h compile programs which use zlib.lib or zdll.lib + + zdll.lib Install these files into the compilers' LIB path if linking + zdll.exp a compiled program to the zlib1.dll binary + + zlib.lib Install these files into the compilers' LIB path to link zlib + zlib.pdb into compiled programs, without zlib1.dll runtime dependency + (zlib.pdb provides debugging info to the compile time linker) + + zlib1.dll Install this binary shared library into the system PATH, or + the program's runtime directory (where the .exe resides) + zlib1.pdb Install in the same directory as zlib1.dll, in order to debug + an application crash using WinDbg or similar tools. + +All .pdb files above are entirely optional, but are very useful to a developer +attempting to diagnose program misbehavior or a crash. Many additional +important files for developers can be found in the zlib127.zip source package +available from http://zlib.net/ - review that package's README file for details. + + +Acknowledgments: + +The deflate format used by zlib was defined by Phil Katz. The deflate and +zlib specifications were written by L. Peter Deutsch. Thanks to all the +people who reported problems and suggested various improvements in zlib; they +are too numerous to cite here. + + +Copyright notice: + + (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* receiving +lengthy legal documents to sign. The sources are provided for free but without +warranty of any kind. The library has been entirely written by Jean-loup +Gailly and Mark Adler; it does not include third-party code. + +If you redistribute modified sources, we would appreciate that you include in +the file ChangeLog history information documenting your changes. Please read +the FAQ for more information on the distribution of modified source versions. diff --git a/src/png/zlib/win32/VisualC.txt b/src/png/zlib/win32/VisualC.txt new file mode 100644 index 0000000000..1005b21941 --- /dev/null +++ b/src/png/zlib/win32/VisualC.txt @@ -0,0 +1,3 @@ + +To build zlib using the Microsoft Visual C++ environment, +use the appropriate project from the contrib/vstudio/ directory. diff --git a/src/png/zlib/win32/zlib.def b/src/png/zlib/win32/zlib.def new file mode 100644 index 0000000000..a2188b0006 --- /dev/null +++ b/src/png/zlib/win32/zlib.def @@ -0,0 +1,94 @@ +; zlib data compression library +EXPORTS +; basic functions + zlibVersion + deflate + deflateEnd + inflate + inflateEnd +; advanced functions + deflateSetDictionary + deflateGetDictionary + deflateCopy + deflateReset + deflateParams + deflateTune + deflateBound + deflatePending + deflatePrime + deflateSetHeader + inflateSetDictionary + inflateGetDictionary + inflateSync + inflateCopy + inflateReset + inflateReset2 + inflatePrime + inflateMark + inflateGetHeader + inflateBack + inflateBackEnd + zlibCompileFlags +; utility functions + compress + compress2 + compressBound + uncompress + uncompress2 + gzopen + gzdopen + gzbuffer + gzsetparams + gzread + gzfread + gzwrite + gzfwrite + gzprintf + gzvprintf + gzputs + gzgets + gzputc + gzgetc + gzungetc + gzflush + gzseek + gzrewind + gztell + gzoffset + gzeof + gzdirect + gzclose + gzclose_r + gzclose_w + gzerror + gzclearerr +; large file functions + gzopen64 + gzseek64 + gztell64 + gzoffset64 + adler32_combine64 + crc32_combine64 +; checksum functions + adler32 + adler32_z + crc32 + crc32_z + adler32_combine + crc32_combine +; various hacks, don't look :) + deflateInit_ + deflateInit2_ + inflateInit_ + inflateInit2_ + inflateBackInit_ + gzgetc_ + zError + inflateSyncPoint + get_crc_table + inflateUndermine + inflateValidate + inflateCodesUsed + inflateResetKeep + deflateResetKeep + gzopen_w diff --git a/src/png/zlib/win32/zlib1.rc b/src/png/zlib/win32/zlib1.rc new file mode 100644 index 0000000000..234e641c32 --- /dev/null +++ b/src/png/zlib/win32/zlib1.rc @@ -0,0 +1,40 @@ +#include +#include "../zlib.h" + +#ifdef GCC_WINDRES +VS_VERSION_INFO VERSIONINFO +#else +VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE +#endif + FILEVERSION ZLIB_VER_MAJOR,ZLIB_VER_MINOR,ZLIB_VER_REVISION,0 + PRODUCTVERSION ZLIB_VER_MAJOR,ZLIB_VER_MINOR,ZLIB_VER_REVISION,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS 1 +#else + FILEFLAGS 0 +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0 // not used +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, char set = Windows, Multilingual + BEGIN + VALUE "FileDescription", "zlib data compression library\0" + VALUE "FileVersion", ZLIB_VERSION "\0" + VALUE "InternalName", "zlib1.dll\0" + VALUE "LegalCopyright", "(C) 1995-2017 Jean-loup Gailly & Mark Adler\0" + VALUE "OriginalFilename", "zlib1.dll\0" + VALUE "ProductName", "zlib\0" + VALUE "ProductVersion", ZLIB_VERSION "\0" + VALUE "Comments", "For more information visit http://www.zlib.net/\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1252 + END +END diff --git a/src/png/zlib/zconf.h.cmakein b/src/png/zlib/zconf.h.cmakein new file mode 100644 index 0000000000..a7f24cce60 --- /dev/null +++ b/src/png/zlib/zconf.h.cmakein @@ -0,0 +1,536 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H +#cmakedefine Z_PREFIX +#cmakedefine Z_HAVE_UNISTD_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/png/zlib/zconf.h.in b/src/png/zlib/zconf.h.in new file mode 100644 index 0000000000..5e1d68a004 --- /dev/null +++ b/src/png/zlib/zconf.h.in @@ -0,0 +1,534 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/png/zlib/zconf.h.included b/src/png/zlib/zconf.h.included new file mode 100644 index 0000000000..5e1d68a004 --- /dev/null +++ b/src/png/zlib/zconf.h.included @@ -0,0 +1,534 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/png/zlib/zlib.3 b/src/png/zlib/zlib.3 new file mode 100644 index 0000000000..bda4eb0737 --- /dev/null +++ b/src/png/zlib/zlib.3 @@ -0,0 +1,149 @@ +.TH ZLIB 3 "15 Jan 2017" +.SH NAME +zlib \- compression/decompression library +.SH SYNOPSIS +[see +.I zlib.h +for full description] +.SH DESCRIPTION +The +.I zlib +library is a general purpose data compression library. +The code is thread safe, assuming that the standard library functions +used are thread safe, such as memory allocation routines. +It provides in-memory compression and decompression functions, +including integrity checks of the uncompressed data. +This version of the library supports only one compression method (deflation) +but other algorithms may be added later +with the same stream interface. +.LP +Compression can be done in a single step if the buffers are large enough +or can be done by repeated calls of the compression function. +In the latter case, +the application must provide more input and/or consume the output +(providing more output space) before each call. +.LP +The library also supports reading and writing files in +.IR gzip (1) +(.gz) format +with an interface similar to that of stdio. +.LP +The library does not install any signal handler. +The decoder checks the consistency of the compressed data, +so the library should never crash even in the case of corrupted input. +.LP +All functions of the compression library are documented in the file +.IR zlib.h . +The distribution source includes examples of use of the library +in the files +.I test/example.c +and +.IR test/minigzip.c, +as well as other examples in the +.IR examples/ +directory. +.LP +Changes to this version are documented in the file +.I ChangeLog +that accompanies the source. +.LP +.I zlib +is built in to many languages and operating systems, including but not limited to +Java, Python, .NET, PHP, Perl, Ruby, Swift, and Go. +.LP +An experimental package to read and write files in the .zip format, +written on top of +.I zlib +by Gilles Vollant (info@winimage.com), +is available at: +.IP +http://www.winimage.com/zLibDll/minizip.html +and also in the +.I contrib/minizip +directory of the main +.I zlib +source distribution. +.SH "SEE ALSO" +The +.I zlib +web site can be found at: +.IP +http://zlib.net/ +.LP +The data format used by the +.I zlib +library is described by RFC +(Request for Comments) 1950 to 1952 in the files: +.IP +http://tools.ietf.org/html/rfc1950 (for the zlib header and trailer format) +.br +http://tools.ietf.org/html/rfc1951 (for the deflate compressed data format) +.br +http://tools.ietf.org/html/rfc1952 (for the gzip header and trailer format) +.LP +Mark Nelson wrote an article about +.I zlib +for the Jan. 1997 issue of Dr. Dobb's Journal; +a copy of the article is available at: +.IP +http://marknelson.us/1997/01/01/zlib-engine/ +.SH "REPORTING PROBLEMS" +Before reporting a problem, +please check the +.I zlib +web site to verify that you have the latest version of +.IR zlib ; +otherwise, +obtain the latest version and see if the problem still exists. +Please read the +.I zlib +FAQ at: +.IP +http://zlib.net/zlib_faq.html +.LP +before asking for help. +Send questions and/or comments to zlib@gzip.org, +or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). +.SH AUTHORS AND LICENSE +Version 1.2.11 +.LP +Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler +.LP +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. +.LP +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: +.LP +.nr step 1 1 +.IP \n[step]. 3 +The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. +.IP \n+[step]. +Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. +.IP \n+[step]. +This notice may not be removed or altered from any source distribution. +.LP +Jean-loup Gailly Mark Adler +.br +jloup@gzip.org madler@alumni.caltech.edu +.LP +The deflate format used by +.I zlib +was defined by Phil Katz. +The deflate and +.I zlib +specifications were written by L. Peter Deutsch. +Thanks to all the people who reported problems and suggested various +improvements in +.IR zlib ; +who are too numerous to cite here. +.LP +UNIX manual page by R. P. C. Rodgers, +U.S. National Library of Medicine (rodgers@nlm.nih.gov). +.\" end of man page diff --git a/src/png/zlib/zlib.3.pdf b/src/png/zlib/zlib.3.pdf new file mode 100644 index 0000000000..6fa519c5bd Binary files /dev/null and b/src/png/zlib/zlib.3.pdf differ diff --git a/src/png/zlib/zlib.h b/src/png/zlib/zlib.h new file mode 100644 index 0000000000..f09cdaf1e0 --- /dev/null +++ b/src/png/zlib/zlib.h @@ -0,0 +1,1912 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/src/png/zlib/zlib.map b/src/png/zlib/zlib.map new file mode 100644 index 0000000000..82ce98cf7d --- /dev/null +++ b/src/png/zlib/zlib.map @@ -0,0 +1,94 @@ +ZLIB_1.2.0 { + global: + compressBound; + deflateBound; + inflateBack; + inflateBackEnd; + inflateBackInit_; + inflateCopy; + local: + deflate_copyright; + inflate_copyright; + inflate_fast; + inflate_table; + zcalloc; + zcfree; + z_errmsg; + gz_error; + gz_intmax; + _*; +}; + +ZLIB_1.2.0.2 { + gzclearerr; + gzungetc; + zlibCompileFlags; +} ZLIB_1.2.0; + +ZLIB_1.2.0.8 { + deflatePrime; +} ZLIB_1.2.0.2; + +ZLIB_1.2.2 { + adler32_combine; + crc32_combine; + deflateSetHeader; + inflateGetHeader; +} ZLIB_1.2.0.8; + +ZLIB_1.2.2.3 { + deflateTune; + gzdirect; +} ZLIB_1.2.2; + +ZLIB_1.2.2.4 { + inflatePrime; +} ZLIB_1.2.2.3; + +ZLIB_1.2.3.3 { + adler32_combine64; + crc32_combine64; + gzopen64; + gzseek64; + gztell64; + inflateUndermine; +} ZLIB_1.2.2.4; + +ZLIB_1.2.3.4 { + inflateReset2; + inflateMark; +} ZLIB_1.2.3.3; + +ZLIB_1.2.3.5 { + gzbuffer; + gzoffset; + gzoffset64; + gzclose_r; + gzclose_w; +} ZLIB_1.2.3.4; + +ZLIB_1.2.5.1 { + deflatePending; +} ZLIB_1.2.3.5; + +ZLIB_1.2.5.2 { + deflateResetKeep; + gzgetc_; + inflateResetKeep; +} ZLIB_1.2.5.1; + +ZLIB_1.2.7.1 { + inflateGetDictionary; + gzvprintf; +} ZLIB_1.2.5.2; + +ZLIB_1.2.9 { + inflateCodesUsed; + inflateValidate; + uncompress2; + gzfread; + gzfwrite; + deflateGetDictionary; + adler32_z; + crc32_z; +} ZLIB_1.2.7.1; diff --git a/src/png/zlib/zlib.pc.cmakein b/src/png/zlib/zlib.pc.cmakein new file mode 100644 index 0000000000..a5e642938c --- /dev/null +++ b/src/png/zlib/zlib.pc.cmakein @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@INSTALL_LIB_DIR@ +sharedlibdir=@INSTALL_LIB_DIR@ +includedir=@INSTALL_INC_DIR@ + +Name: zlib +Description: zlib compression library +Version: @VERSION@ + +Requires: +Libs: -L${libdir} -L${sharedlibdir} -lz +Cflags: -I${includedir} diff --git a/src/png/zlib/zlib.pc.in b/src/png/zlib/zlib.pc.in new file mode 100644 index 0000000000..7e5acf9c77 --- /dev/null +++ b/src/png/zlib/zlib.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +sharedlibdir=@sharedlibdir@ +includedir=@includedir@ + +Name: zlib +Description: zlib compression library +Version: @VERSION@ + +Requires: +Libs: -L${libdir} -L${sharedlibdir} -lz +Cflags: -I${includedir} diff --git a/src/png/zlib/zlib2ansi b/src/png/zlib/zlib2ansi new file mode 100644 index 0000000000..15e3e165f3 --- /dev/null +++ b/src/png/zlib/zlib2ansi @@ -0,0 +1,152 @@ +#!/usr/bin/perl + +# Transform K&R C function definitions into ANSI equivalent. +# +# Author: Paul Marquess +# Version: 1.0 +# Date: 3 October 2006 + +# TODO +# +# Asumes no function pointer parameters. unless they are typedefed. +# Assumes no literal strings that look like function definitions +# Assumes functions start at the beginning of a line + +use strict; +use warnings; + +local $/; +$_ = <>; + +my $sp = qr{ \s* (?: /\* .*? \*/ )? \s* }x; # assume no nested comments + +my $d1 = qr{ $sp (?: [\w\*\s]+ $sp)* $sp \w+ $sp [\[\]\s]* $sp }x ; +my $decl = qr{ $sp (?: \w+ $sp )+ $d1 }xo ; +my $dList = qr{ $sp $decl (?: $sp , $d1 )* $sp ; $sp }xo ; + + +while (s/^ + ( # Start $1 + ( # Start $2 + .*? # Minimal eat content + ( ^ \w [\w\s\*]+ ) # $3 -- function name + \s* # optional whitespace + ) # $2 - Matched up to before parameter list + + \( \s* # Literal "(" + optional whitespace + ( [^\)]+ ) # $4 - one or more anythings except ")" + \s* \) # optional whitespace surrounding a Literal ")" + + ( (?: $dList )+ ) # $5 + + $sp ^ { # literal "{" at start of line + ) # Remember to $1 + //xsom + ) +{ + my $all = $1 ; + my $prefix = $2; + my $param_list = $4 ; + my $params = $5; + + StripComments($params); + StripComments($param_list); + $param_list =~ s/^\s+//; + $param_list =~ s/\s+$//; + + my $i = 0 ; + my %pList = map { $_ => $i++ } + split /\s*,\s*/, $param_list; + my $pMatch = '(\b' . join('|', keys %pList) . '\b)\W*$' ; + + my @params = split /\s*;\s*/, $params; + my @outParams = (); + foreach my $p (@params) + { + if ($p =~ /,/) + { + my @bits = split /\s*,\s*/, $p; + my $first = shift @bits; + $first =~ s/^\s*//; + push @outParams, $first; + $first =~ /^(\w+\s*)/; + my $type = $1 ; + push @outParams, map { $type . $_ } @bits; + } + else + { + $p =~ s/^\s+//; + push @outParams, $p; + } + } + + + my %tmp = map { /$pMatch/; $_ => $pList{$1} } + @outParams ; + + @outParams = map { " $_" } + sort { $tmp{$a} <=> $tmp{$b} } + @outParams ; + + print $prefix ; + print "(\n" . join(",\n", @outParams) . ")\n"; + print "{" ; + +} + +# Output any trailing code. +print ; +exit 0; + + +sub StripComments +{ + + no warnings; + + # Strip C & C++ coments + # From the perlfaq + $_[0] =~ + + s{ + /\* ## Start of /* ... */ comment + [^*]*\*+ ## Non-* followed by 1-or-more *'s + ( + [^/*][^*]*\*+ + )* ## 0-or-more things which don't start with / + ## but do end with '*' + / ## End of /* ... */ comment + + | ## OR C++ Comment + // ## Start of C++ comment // + [^\n]* ## followed by 0-or-more non end of line characters + + | ## OR various things which aren't comments: + + ( + " ## Start of " ... " string + ( + \\. ## Escaped char + | ## OR + [^"\\] ## Non "\ + )* + " ## End of " ... " string + + | ## OR + + ' ## Start of ' ... ' string + ( + \\. ## Escaped char + | ## OR + [^'\\] ## Non '\ + )* + ' ## End of ' ... ' string + + | ## OR + + . ## Anything other char + [^/"'\\]* ## Chars which doesn't start a comment, string or escape + ) + }{$2}gxs; + +} diff --git a/src/png/zlib/zutil.c b/src/png/zlib/zutil.c new file mode 100644 index 0000000000..a76c6b0c7e --- /dev/null +++ b/src/png/zlib/zutil.c @@ -0,0 +1,325 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2017 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" +#ifndef Z_SOLO +# include "gzguts.h" +#endif + +z_const char * const z_errmsg[10] = { + (z_const char *)"need dictionary", /* Z_NEED_DICT 2 */ + (z_const char *)"stream end", /* Z_STREAM_END 1 */ + (z_const char *)"", /* Z_OK 0 */ + (z_const char *)"file error", /* Z_ERRNO (-1) */ + (z_const char *)"stream error", /* Z_STREAM_ERROR (-2) */ + (z_const char *)"data error", /* Z_DATA_ERROR (-3) */ + (z_const char *)"insufficient memory", /* Z_MEM_ERROR (-4) */ + (z_const char *)"buffer error", /* Z_BUF_ERROR (-5) */ + (z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */ + (z_const char *)"" +}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch ((int)(sizeof(uInt))) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch ((int)(sizeof(uLong))) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch ((int)(sizeof(voidpf))) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch ((int)(sizeof(z_off_t))) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef ZLIB_DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef ZLIB_DEBUG +#include +# ifndef verbose +# define verbose 0 +# endif +int ZLIB_INTERNAL z_verbose = verbose; + +void ZLIB_INTERNAL z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void ZLIB_INTERNAL zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int ZLIB_INTERNAL zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void ZLIB_INTERNAL zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#ifndef Z_SOLO + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf; + ulg bsize = (ulg)items*size; + + (void)opaque; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + int n; + + (void)opaque; + + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +{ + (void)opaque; + return _halloc((long)items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + (void)opaque; + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + (void)opaque; + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void ZLIB_INTERNAL zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + (void)opaque; + free(ptr); +} + +#endif /* MY_ZCALLOC */ + +#endif /* !Z_SOLO */ diff --git a/src/png/zlib/zutil.h b/src/png/zlib/zutil.h new file mode 100644 index 0000000000..b079ea6a80 --- /dev/null +++ b/src/png/zlib/zutil.h @@ -0,0 +1,271 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#if defined(STDC) && !defined(Z_SOLO) +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifdef Z_SOLO + typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ +#endif + +#ifndef local +# define local static +#endif +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# ifndef Z_SOLO +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 1 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 2 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#ifdef __370__ +# if __TARGET_LIB__ < 0x20000000 +# define OS_CODE 4 +# elif __TARGET_LIB__ < 0x40000000 +# define OS_CODE 11 +# else +# define OS_CODE 8 +# endif +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 5 +#endif + +#ifdef OS2 +# define OS_CODE 6 +# if defined(M_I86) && !defined(Z_SOLO) +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 7 +# ifndef Z_SOLO +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +# endif +#endif + +#ifdef __acorn +# define OS_CODE 13 +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) +# define OS_CODE 10 +#endif + +#ifdef _BEOS_ +# define OS_CODE 16 +#endif + +#ifdef __TOS_OS400__ +# define OS_CODE 18 +#endif + +#ifdef __APPLE__ +# define OS_CODE 19 +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) && !defined(MSDOS) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_WIN32) && \ + (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 3 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(pyr) || defined(Z_SOLO) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef ZLIB_DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#ifndef Z_SOLO + voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); + void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#endif + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +/* Reverse the bytes in a 32-bit value */ +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +#endif /* ZUTIL_H */ diff --git a/src/poly2tri/CMakeLists.txt b/src/poly2tri/CMakeLists.txt new file mode 100644 index 0000000000..3cdff1221d --- /dev/null +++ b/src/poly2tri/CMakeLists.txt @@ -0,0 +1,17 @@ +project(poly2tri) +cmake_minimum_required(VERSION 2.6) + +add_library(poly2tri STATIC + common/shapes.cc + common/shapes.h + common/utils.h + poly2tri.h + sweep/advancing_front.cc + sweep/advancing_front.h + sweep/cdt.cc + sweep/cdt.h + sweep/sweep.cc + sweep/sweep.h + sweep/sweep_context.cc + sweep/sweep_context.h +) diff --git a/xs/src/poly2tri/common/shapes.cc b/src/poly2tri/common/shapes.cc similarity index 100% rename from xs/src/poly2tri/common/shapes.cc rename to src/poly2tri/common/shapes.cc diff --git a/xs/src/poly2tri/common/shapes.h b/src/poly2tri/common/shapes.h similarity index 100% rename from xs/src/poly2tri/common/shapes.h rename to src/poly2tri/common/shapes.h diff --git a/xs/src/poly2tri/common/utils.h b/src/poly2tri/common/utils.h similarity index 97% rename from xs/src/poly2tri/common/utils.h rename to src/poly2tri/common/utils.h index ffb713f58f..4bcb763614 100644 --- a/xs/src/poly2tri/common/utils.h +++ b/src/poly2tri/common/utils.h @@ -33,7 +33,9 @@ #define UTILS_H // Otherwise #defines like M_PI are undeclared under Visual Studio -#define _USE_MATH_DEFINES +#ifndef _USE_MATH_DEFINES + #define _USE_MATH_DEFINES +#endif /* _USE_MATH_DEFINES */ #include #include diff --git a/xs/src/poly2tri/poly2tri.h b/src/poly2tri/poly2tri.h similarity index 100% rename from xs/src/poly2tri/poly2tri.h rename to src/poly2tri/poly2tri.h diff --git a/xs/src/poly2tri/sweep/advancing_front.cc b/src/poly2tri/sweep/advancing_front.cc similarity index 100% rename from xs/src/poly2tri/sweep/advancing_front.cc rename to src/poly2tri/sweep/advancing_front.cc diff --git a/xs/src/poly2tri/sweep/advancing_front.h b/src/poly2tri/sweep/advancing_front.h similarity index 100% rename from xs/src/poly2tri/sweep/advancing_front.h rename to src/poly2tri/sweep/advancing_front.h diff --git a/xs/src/poly2tri/sweep/cdt.cc b/src/poly2tri/sweep/cdt.cc similarity index 100% rename from xs/src/poly2tri/sweep/cdt.cc rename to src/poly2tri/sweep/cdt.cc diff --git a/xs/src/poly2tri/sweep/cdt.h b/src/poly2tri/sweep/cdt.h similarity index 100% rename from xs/src/poly2tri/sweep/cdt.h rename to src/poly2tri/sweep/cdt.h diff --git a/xs/src/poly2tri/sweep/sweep.cc b/src/poly2tri/sweep/sweep.cc similarity index 100% rename from xs/src/poly2tri/sweep/sweep.cc rename to src/poly2tri/sweep/sweep.cc diff --git a/xs/src/poly2tri/sweep/sweep.h b/src/poly2tri/sweep/sweep.h similarity index 100% rename from xs/src/poly2tri/sweep/sweep.h rename to src/poly2tri/sweep/sweep.h diff --git a/xs/src/poly2tri/sweep/sweep_context.cc b/src/poly2tri/sweep/sweep_context.cc similarity index 100% rename from xs/src/poly2tri/sweep/sweep_context.cc rename to src/poly2tri/sweep/sweep_context.cc diff --git a/xs/src/poly2tri/sweep/sweep_context.h b/src/poly2tri/sweep/sweep_context.h similarity index 100% rename from xs/src/poly2tri/sweep/sweep_context.h rename to src/poly2tri/sweep/sweep_context.h diff --git a/src/polypartition/CMakeLists.txt b/src/polypartition/CMakeLists.txt new file mode 100644 index 0000000000..07b92840b8 --- /dev/null +++ b/src/polypartition/CMakeLists.txt @@ -0,0 +1,7 @@ +project(polypartition) +cmake_minimum_required(VERSION 2.6) + +add_library(polypartition STATIC + polypartition.cpp + polypartition.h +) diff --git a/xs/src/polypartition.cpp b/src/polypartition/polypartition.cpp similarity index 96% rename from xs/src/polypartition.cpp rename to src/polypartition/polypartition.cpp index 700cd09743..9c59172514 100644 --- a/xs/src/polypartition.cpp +++ b/src/polypartition/polypartition.cpp @@ -1,1563 +1,1563 @@ -//Copyright (C) 2011 by Ivan Fratric -// -//Permission is hereby granted, free of charge, to any person obtaining a copy -//of this software and associated documentation files (the "Software"), to 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 -#include -#include -#include -#include -#include - -using namespace std; - -#include "polypartition.h" - -#define TPPL_VERTEXTYPE_REGULAR 0 -#define TPPL_VERTEXTYPE_START 1 -#define TPPL_VERTEXTYPE_END 2 -#define TPPL_VERTEXTYPE_SPLIT 3 -#define TPPL_VERTEXTYPE_MERGE 4 - -TPPLPoly::TPPLPoly() { - hole = false; - numpoints = 0; - points = NULL; -} - -TPPLPoly::~TPPLPoly() { - if(points) delete [] points; -} - -void TPPLPoly::Clear() { - if(points) delete [] points; - hole = false; - numpoints = 0; - points = NULL; -} - -void TPPLPoly::Init(long numpoints) { - Clear(); - this->numpoints = numpoints; - points = new TPPLPoint[numpoints]; -} - -void TPPLPoly::Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3) { - Init(3); - points[0] = p1; - points[1] = p2; - points[2] = p3; -} - -TPPLPoly::TPPLPoly(const TPPLPoly &src) { - hole = src.hole; - numpoints = src.numpoints; - points = new TPPLPoint[numpoints]; - memcpy(points, src.points, numpoints*sizeof(TPPLPoint)); -} - -TPPLPoly& TPPLPoly::operator=(const TPPLPoly &src) { - if(&src != this) { - Clear(); - hole = src.hole; - numpoints = src.numpoints; - points = new TPPLPoint[numpoints]; - memcpy(points, src.points, numpoints*sizeof(TPPLPoint)); - } - return *this; -} - -int TPPLPoly::GetOrientation() const { - long i1,i2; - tppl_float area = 0; - for(i1=0; i10) return TPPL_CCW; - if(area<0) return TPPL_CW; - return 0; -} - -void TPPLPoly::SetOrientation(int orientation) { - int polyorientation = GetOrientation(); - if(polyorientation&&(polyorientation!=orientation)) { - Invert(); - } -} - -void TPPLPoly::Invert() { - long i; - TPPLPoint *invpoints; - - invpoints = new TPPLPoint[numpoints]; - for(i=0;i0) return 0; - if(dot21*dot22>0) return 0; - - return 1; -} - -//removes holes from inpolys by merging them with non-holes -int TPPLPartition::RemoveHoles(list *inpolys, list *outpolys) { - list polys; - list::iterator holeiter,polyiter,iter,iter2; - long i,i2,holepointindex,polypointindex = 0; - TPPLPoint holepoint,polypoint,bestpolypoint; - TPPLPoint linep1,linep2; - TPPLPoint v1,v2; - TPPLPoly newpoly; - bool hasholes; - bool pointvisible; - bool pointfound; - - //check for trivial case (no holes) - hasholes = false; - for(iter = inpolys->begin(); iter!=inpolys->end(); ++iter) { - if(iter->IsHole()) { - hasholes = true; - break; - } - } - if(!hasholes) { - for(iter = inpolys->begin(); iter!=inpolys->end(); ++iter) { - outpolys->push_back(*iter); - } - return 1; - } - - polys = *inpolys; - - while(1) { - //find the hole point with the largest x - hasholes = false; - for(iter = polys.begin(); iter!=polys.end(); ++iter) { - if(!iter->IsHole()) continue; - - if(!hasholes) { - hasholes = true; - holeiter = iter; - holepointindex = 0; - } - - for(i=0; i < iter->GetNumPoints(); i++) { - if(iter->GetPoint(i).x > holeiter->GetPoint(holepointindex).x) { - holeiter = iter; - holepointindex = i; - } - } - } - if(!hasholes) break; - holepoint = holeiter->GetPoint(holepointindex); - - pointfound = false; - for(iter = polys.begin(); iter!=polys.end(); ++iter) { - if(iter->IsHole()) continue; - for(i=0; i < iter->GetNumPoints(); i++) { - if(iter->GetPoint(i).x <= holepoint.x) continue; - if(!InCone(iter->GetPoint((i+iter->GetNumPoints()-1)%(iter->GetNumPoints())), - iter->GetPoint(i), - iter->GetPoint((i+1)%(iter->GetNumPoints())), - holepoint)) - continue; - polypoint = iter->GetPoint(i); - if(pointfound) { - v1 = Normalize(polypoint-holepoint); - v2 = Normalize(bestpolypoint-holepoint); - if(v2.x > v1.x) continue; - } - pointvisible = true; - for(iter2 = polys.begin(); iter2!=polys.end(); ++iter2) { - if(iter2->IsHole()) continue; - for(i2=0; i2 < iter2->GetNumPoints(); i2++) { - linep1 = iter2->GetPoint(i2); - linep2 = iter2->GetPoint((i2+1)%(iter2->GetNumPoints())); - if(Intersects(holepoint,polypoint,linep1,linep2)) { - pointvisible = false; - break; - } - } - if(!pointvisible) break; - } - if(pointvisible) { - pointfound = true; - bestpolypoint = polypoint; - polyiter = iter; - polypointindex = i; - } - } - } - - if(!pointfound) return 0; - - newpoly.Init(holeiter->GetNumPoints() + polyiter->GetNumPoints() + 2); - i2 = 0; - for(i=0;i<=polypointindex;i++) { - newpoly[i2] = polyiter->GetPoint(i); - i2++; - } - for(i=0;i<=holeiter->GetNumPoints();i++) { - newpoly[i2] = holeiter->GetPoint((i+holepointindex)%holeiter->GetNumPoints()); - i2++; - } - for(i=polypointindex;iGetNumPoints();i++) { - newpoly[i2] = polyiter->GetPoint(i); - i2++; - } - - polys.erase(holeiter); - polys.erase(polyiter); - polys.push_back(newpoly); - } - - for(iter = polys.begin(); iter!=polys.end(); ++iter) { - outpolys->push_back(*iter); - } - - return 1; -} - -bool TPPLPartition::IsConvex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3) { - tppl_float tmp; - tmp = (p3.y-p1.y)*(p2.x-p1.x)-(p3.x-p1.x)*(p2.y-p1.y); - if(tmp>0) return 1; - else return 0; -} - -bool TPPLPartition::IsReflex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3) { - tppl_float tmp; - tmp = (p3.y-p1.y)*(p2.x-p1.x)-(p3.x-p1.x)*(p2.y-p1.y); - if(tmp<0) return 1; - else return 0; -} - -bool TPPLPartition::IsInside(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3, TPPLPoint &p) { - if(IsConvex(p1,p,p2)) return false; - if(IsConvex(p2,p,p3)) return false; - if(IsConvex(p3,p,p1)) return false; - return true; -} - -bool TPPLPartition::InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p) { - bool convex; - - convex = IsConvex(p1,p2,p3); - - if(convex) { - if(!IsConvex(p1,p2,p)) return false; - if(!IsConvex(p2,p3,p)) return false; - return true; - } else { - if(IsConvex(p1,p2,p)) return true; - if(IsConvex(p2,p3,p)) return true; - return false; - } -} - -bool TPPLPartition::InCone(PartitionVertex *v, TPPLPoint &p) { - TPPLPoint p1,p2,p3; - - p1 = v->previous->p; - p2 = v->p; - p3 = v->next->p; - - return InCone(p1,p2,p3,p); -} - -void TPPLPartition::UpdateVertexReflexity(PartitionVertex *v) { - PartitionVertex *v1,*v3; - v1 = v->previous; - v3 = v->next; - v->isConvex = !IsReflex(v1->p,v->p,v3->p); -} - -void TPPLPartition::UpdateVertex(PartitionVertex *v, PartitionVertex *vertices, long numvertices) { - long i; - PartitionVertex *v1,*v3; - TPPLPoint vec1,vec3; - - v1 = v->previous; - v3 = v->next; - - v->isConvex = IsConvex(v1->p,v->p,v3->p); - - vec1 = Normalize(v1->p - v->p); - vec3 = Normalize(v3->p - v->p); - v->angle = vec1.x*vec3.x + vec1.y*vec3.y; - - if(v->isConvex) { - v->isEar = true; - for(i=0;ip.x)&&(vertices[i].p.y==v->p.y)) continue; - if((vertices[i].p.x==v1->p.x)&&(vertices[i].p.y==v1->p.y)) continue; - if((vertices[i].p.x==v3->p.x)&&(vertices[i].p.y==v3->p.y)) continue; - if(IsInside(v1->p,v->p,v3->p,vertices[i].p)) { - v->isEar = false; - break; - } - } - } else { - v->isEar = false; - } -} - -//triangulation by ear removal -int TPPLPartition::Triangulate_EC(TPPLPoly *poly, list *triangles) { - long numvertices; - PartitionVertex *vertices; - PartitionVertex *ear; - TPPLPoly triangle; - long i,j; - bool earfound; - - if(poly->GetNumPoints() < 3) return 0; - if(poly->GetNumPoints() == 3) { - triangles->push_back(*poly); - return 1; - } - - numvertices = poly->GetNumPoints(); - - vertices = new PartitionVertex[numvertices]; - for(i=0;iGetPoint(i); - if(i==(numvertices-1)) vertices[i].next=&(vertices[0]); - else vertices[i].next=&(vertices[i+1]); - if(i==0) vertices[i].previous = &(vertices[numvertices-1]); - else vertices[i].previous = &(vertices[i-1]); - } - for(i=0;i ear->angle) { - ear = &(vertices[j]); - } - } - } - if(!earfound) { - delete [] vertices; - return 0; - } - - triangle.Triangle(ear->previous->p,ear->p,ear->next->p); - triangles->push_back(triangle); - - ear->isActive = false; - ear->previous->next = ear->next; - ear->next->previous = ear->previous; - - if(i==numvertices-4) break; - - UpdateVertex(ear->previous,vertices,numvertices); - UpdateVertex(ear->next,vertices,numvertices); - } - for(i=0;ip,vertices[i].p,vertices[i].next->p); - triangles->push_back(triangle); - break; - } - } - - delete [] vertices; - - return 1; -} - -int TPPLPartition::Triangulate_EC(list *inpolys, list *triangles) { - list outpolys; - list::iterator iter; - - if(!RemoveHoles(inpolys,&outpolys)) return 0; - for(iter=outpolys.begin();iter!=outpolys.end();++iter) { - if(!Triangulate_EC(&(*iter),triangles)) return 0; - } - return 1; -} - -int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, list *parts) { - list triangles; - list::iterator iter1,iter2; - TPPLPoly *poly1,*poly2; - TPPLPoly newpoly; - TPPLPoint d1,d2,p1,p2,p3; - long i11,i12,i21,i22,i13,i23,j,k; - bool isdiagonal; - long numreflex; - - //check if the poly is already convex - numreflex = 0; - for(i11=0;i11GetNumPoints();i11++) { - if(i11==0) i12 = poly->GetNumPoints()-1; - else i12=i11-1; - if(i11==(poly->GetNumPoints()-1)) i13=0; - else i13=i11+1; - if(IsReflex(poly->GetPoint(i12),poly->GetPoint(i11),poly->GetPoint(i13))) { - numreflex = 1; - break; - } - } - if(numreflex == 0) { - parts->push_back(*poly); - return 1; - } - - if(!Triangulate_EC(poly,&triangles)) return 0; - - for(iter1 = triangles.begin(); iter1 != triangles.end(); ++iter1) { - poly1 = &(*iter1); - for(i11=0;i11GetNumPoints();i11++) { - d1 = poly1->GetPoint(i11); - i12 = (i11+1)%(poly1->GetNumPoints()); - d2 = poly1->GetPoint(i12); - - isdiagonal = false; - for(iter2 = iter1; iter2 != triangles.end(); ++iter2) { - if(iter1 == iter2) continue; - poly2 = &(*iter2); - - for(i21=0;i21GetNumPoints();i21++) { - if((d2.x != poly2->GetPoint(i21).x)||(d2.y != poly2->GetPoint(i21).y)) continue; - i22 = (i21+1)%(poly2->GetNumPoints()); - if((d1.x != poly2->GetPoint(i22).x)||(d1.y != poly2->GetPoint(i22).y)) continue; - isdiagonal = true; - break; - } - if(isdiagonal) break; - } - - if(!isdiagonal) continue; - - p2 = poly1->GetPoint(i11); - if(i11 == 0) i13 = poly1->GetNumPoints()-1; - else i13 = i11-1; - p1 = poly1->GetPoint(i13); - if(i22 == (poly2->GetNumPoints()-1)) i23 = 0; - else i23 = i22+1; - p3 = poly2->GetPoint(i23); - - if(!IsConvex(p1,p2,p3)) continue; - - p2 = poly1->GetPoint(i12); - if(i12 == (poly1->GetNumPoints()-1)) i13 = 0; - else i13 = i12+1; - p3 = poly1->GetPoint(i13); - if(i21 == 0) i23 = poly2->GetNumPoints()-1; - else i23 = i21-1; - p1 = poly2->GetPoint(i23); - - if(!IsConvex(p1,p2,p3)) continue; - - newpoly.Init(poly1->GetNumPoints()+poly2->GetNumPoints()-2); - k = 0; - for(j=i12;j!=i11;j=(j+1)%(poly1->GetNumPoints())) { - newpoly[k] = poly1->GetPoint(j); - k++; - } - for(j=i22;j!=i21;j=(j+1)%(poly2->GetNumPoints())) { - newpoly[k] = poly2->GetPoint(j); - k++; - } - - triangles.erase(iter2); - *iter1 = newpoly; - poly1 = &(*iter1); - i11 = -1; - - continue; - } - } - - for(iter1 = triangles.begin(); iter1 != triangles.end(); ++iter1) { - parts->push_back(*iter1); - } - - return 1; -} - -int TPPLPartition::ConvexPartition_HM(list *inpolys, list *parts) { - list outpolys; - list::iterator iter; - - if(!RemoveHoles(inpolys,&outpolys)) return 0; - for(iter=outpolys.begin();iter!=outpolys.end();++iter) { - if(!ConvexPartition_HM(&(*iter),parts)) return 0; - } - return 1; -} - -//minimum-weight polygon triangulation by dynamic programming -//O(n^3) time complexity -//O(n^2) space complexity -int TPPLPartition::Triangulate_OPT(TPPLPoly *poly, list *triangles) { - long i,j,k,gap,n; - DPState **dpstates; - TPPLPoint p1,p2,p3,p4; - long bestvertex; - tppl_float weight,minweight,d1,d2; - Diagonal diagonal,newdiagonal; - list diagonals; - TPPLPoly triangle; - int ret = 1; - - n = poly->GetNumPoints(); - dpstates = new DPState *[n]; - for(i=1;iGetPoint(i); - for(j=i+1;jGetPoint(j); - - //visibility check - if(i==0) p3 = poly->GetPoint(n-1); - else p3 = poly->GetPoint(i-1); - if(i==(n-1)) p4 = poly->GetPoint(0); - else p4 = poly->GetPoint(i+1); - if(!InCone(p3,p1,p4,p2)) { - dpstates[j][i].visible = false; - continue; - } - - if(j==0) p3 = poly->GetPoint(n-1); - else p3 = poly->GetPoint(j-1); - if(j==(n-1)) p4 = poly->GetPoint(0); - else p4 = poly->GetPoint(j+1); - if(!InCone(p3,p2,p4,p1)) { - dpstates[j][i].visible = false; - continue; - } - - for(k=0;kGetPoint(k); - if(k==(n-1)) p4 = poly->GetPoint(0); - else p4 = poly->GetPoint(k+1); - if(Intersects(p1,p2,p3,p4)) { - dpstates[j][i].visible = false; - break; - } - } - } - } - } - dpstates[n-1][0].visible = true; - dpstates[n-1][0].weight = 0; - dpstates[n-1][0].bestvertex = -1; - - for(gap = 2; gapGetPoint(i),poly->GetPoint(k)); - if(j<=(k+1)) d2=0; - else d2 = Distance(poly->GetPoint(k),poly->GetPoint(j)); - - weight = dpstates[k][i].weight + dpstates[j][k].weight + d1 + d2; - - if((bestvertex == -1)||(weightGetPoint(diagonal.index1),poly->GetPoint(bestvertex),poly->GetPoint(diagonal.index2)); - triangles->push_back(triangle); - if(bestvertex > (diagonal.index1+1)) { - newdiagonal.index1 = diagonal.index1; - newdiagonal.index2 = bestvertex; - diagonals.push_back(newdiagonal); - } - if(diagonal.index2 > (bestvertex+1)) { - newdiagonal.index1 = bestvertex; - newdiagonal.index2 = diagonal.index2; - diagonals.push_back(newdiagonal); - } - } - - for(i=1;i *pairs; - long w2; - - w2 = dpstates[a][b].weight; - if(w>w2) return; - - pairs = &(dpstates[a][b].pairs); - newdiagonal.index1 = i; - newdiagonal.index2 = j; - - if(wclear(); - pairs->push_front(newdiagonal); - dpstates[a][b].weight = w; - } else { - if((!pairs->empty())&&(i <= pairs->begin()->index1)) return; - while((!pairs->empty())&&(pairs->begin()->index2 >= j)) pairs->pop_front(); - pairs->push_front(newdiagonal); - } -} - -void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) { - list *pairs; - list::iterator iter,lastiter; - long top; - long w; - - if(!dpstates[i][j].visible) return; - top = j; - w = dpstates[i][j].weight; - if(k-j > 1) { - if (!dpstates[j][k].visible) return; - w += dpstates[j][k].weight + 1; - } - if(j-i > 1) { - pairs = &(dpstates[i][j].pairs); - iter = pairs->end(); - lastiter = pairs->end(); - while(iter!=pairs->begin()) { - --iter; - if(!IsReflex(vertices[iter->index2].p,vertices[j].p,vertices[k].p)) lastiter = iter; - else break; - } - if(lastiter == pairs->end()) w++; - else { - if(IsReflex(vertices[k].p,vertices[i].p,vertices[lastiter->index1].p)) w++; - else top = lastiter->index1; - } - } - UpdateState(i,k,w,top,j,dpstates); -} - -void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) { - list *pairs; - list::iterator iter,lastiter; - long top; - long w; - - if(!dpstates[j][k].visible) return; - top = j; - w = dpstates[j][k].weight; - - if (j-i > 1) { - if (!dpstates[i][j].visible) return; - w += dpstates[i][j].weight + 1; - } - if (k-j > 1) { - pairs = &(dpstates[j][k].pairs); - - iter = pairs->begin(); - if((!pairs->empty())&&(!IsReflex(vertices[i].p,vertices[j].p,vertices[iter->index1].p))) { - lastiter = iter; - while(iter!=pairs->end()) { - if(!IsReflex(vertices[i].p,vertices[j].p,vertices[iter->index1].p)) { - lastiter = iter; - ++iter; - } - else break; - } - if(IsReflex(vertices[lastiter->index2].p,vertices[k].p,vertices[i].p)) w++; - else top = lastiter->index2; - } else w++; - } - UpdateState(i,k,w,j,top,dpstates); -} - -int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list *parts) { - TPPLPoint p1,p2,p3,p4; - PartitionVertex *vertices; - DPState2 **dpstates; - long i,j,k,n,gap; - list diagonals,diagonals2; - Diagonal diagonal,newdiagonal; - list *pairs,*pairs2; - list::iterator iter,iter2; - int ret; - TPPLPoly newpoly; - list indices; - list::iterator iiter; - bool ijreal,jkreal; - - n = poly->GetNumPoints(); - vertices = new PartitionVertex[n]; - - dpstates = new DPState2 *[n]; - for(i=0;iGetPoint(i); - vertices[i].isActive = true; - if(i==0) vertices[i].previous = &(vertices[n-1]); - else vertices[i].previous = &(vertices[i-1]); - if(i==(poly->GetNumPoints()-1)) vertices[i].next = &(vertices[0]); - else vertices[i].next = &(vertices[i+1]); - } - for(i=1;iGetPoint(i); - for(j=i+1;jGetPoint(j); - - //visibility check - if(!InCone(&vertices[i],p2)) { - dpstates[i][j].visible = false; - continue; - } - if(!InCone(&vertices[j],p1)) { - dpstates[i][j].visible = false; - continue; - } - - for(k=0;kGetPoint(k); - if(k==(n-1)) p4 = poly->GetPoint(0); - else p4 = poly->GetPoint(k+1); - if(Intersects(p1,p2,p3,p4)) { - dpstates[i][j].visible = false; - break; - } - } - } - } - } - for(i=0;i<(n-2);i++) { - j = i+2; - if(dpstates[i][j].visible) { - dpstates[i][j].weight = 0; - newdiagonal.index1 = i+1; - newdiagonal.index2 = i+1; - dpstates[i][j].pairs.push_back(newdiagonal); - } - } - - dpstates[0][n-1].visible = true; - vertices[0].isConvex = false; //by convention - - for(gap=3; gapempty()) { - ret = 0; - break; - } - if(!vertices[diagonal.index1].isConvex) { - iter = pairs->end(); - --iter; - j = iter->index2; - newdiagonal.index1 = j; - newdiagonal.index2 = diagonal.index2; - diagonals.push_front(newdiagonal); - if((j - diagonal.index1)>1) { - if(iter->index1 != iter->index2) { - pairs2 = &(dpstates[diagonal.index1][j].pairs); - while(1) { - if(pairs2->empty()) { - ret = 0; - break; - } - iter2 = pairs2->end(); - --iter2; - if(iter->index1 != iter2->index1) pairs2->pop_back(); - else break; - } - if(ret == 0) break; - } - newdiagonal.index1 = diagonal.index1; - newdiagonal.index2 = j; - diagonals.push_front(newdiagonal); - } - } else { - iter = pairs->begin(); - j = iter->index1; - newdiagonal.index1 = diagonal.index1; - newdiagonal.index2 = j; - diagonals.push_front(newdiagonal); - if((diagonal.index2 - j) > 1) { - if(iter->index1 != iter->index2) { - pairs2 = &(dpstates[j][diagonal.index2].pairs); - while(1) { - if(pairs2->empty()) { - ret = 0; - break; - } - iter2 = pairs2->begin(); - if(iter->index2 != iter2->index2) pairs2->pop_front(); - else break; - } - if(ret == 0) break; - } - newdiagonal.index1 = j; - newdiagonal.index2 = diagonal.index2; - diagonals.push_front(newdiagonal); - } - } - } - - if(ret == 0) { - for(i=0;iend(); - --iter; - j = iter->index2; - if(iter->index1 != iter->index2) ijreal = false; - } else { - iter = pairs->begin(); - j = iter->index1; - if(iter->index1 != iter->index2) jkreal = false; - } - - newdiagonal.index1 = diagonal.index1; - newdiagonal.index2 = j; - if(ijreal) { - diagonals.push_back(newdiagonal); - } else { - diagonals2.push_back(newdiagonal); - } - - newdiagonal.index1 = j; - newdiagonal.index2 = diagonal.index2; - if(jkreal) { - diagonals.push_back(newdiagonal); - } else { - diagonals2.push_back(newdiagonal); - } - - indices.push_back(j); - } - - indices.sort(); - newpoly.Init((long)indices.size()); - k=0; - for(iiter = indices.begin();iiter!=indices.end(); ++iiter) { - newpoly[k] = vertices[*iiter].p; - k++; - } - parts->push_back(newpoly); - } - - for(i=0;i *inpolys, list *monotonePolys) { - list::iterator iter; - MonotoneVertex *vertices; - long i,numvertices,vindex,vindex2,newnumvertices,maxnumvertices; - long polystartindex, polyendindex; - TPPLPoly *poly; - MonotoneVertex *v,*v2,*vprev,*vnext; - ScanLineEdge newedge; - bool error = false; - - numvertices = 0; - for(iter = inpolys->begin(); iter != inpolys->end(); ++iter) { - numvertices += iter->GetNumPoints(); - } - - maxnumvertices = numvertices*3; - vertices = new MonotoneVertex[maxnumvertices]; - newnumvertices = numvertices; - - polystartindex = 0; - for(iter = inpolys->begin(); iter != inpolys->end(); ++iter) { - poly = &(*iter); - polyendindex = polystartindex + poly->GetNumPoints()-1; - for(i=0;iGetNumPoints();i++) { - vertices[i+polystartindex].p = poly->GetPoint(i); - if(i==0) vertices[i+polystartindex].previous = polyendindex; - else vertices[i+polystartindex].previous = i+polystartindex-1; - if(i==(poly->GetNumPoints()-1)) vertices[i+polystartindex].next = polystartindex; - else vertices[i+polystartindex].next = i+polystartindex+1; - } - polystartindex = polyendindex+1; - } - - //construct the priority queue - long *priority = new long [numvertices]; - for(i=0;iprevious]); - vnext = &(vertices[v->next]); - - if(Below(vprev->p,v->p)&&Below(vnext->p,v->p)) { - if(IsConvex(vnext->p,vprev->p,v->p)) { - vertextypes[i] = TPPL_VERTEXTYPE_START; - } else { - vertextypes[i] = TPPL_VERTEXTYPE_SPLIT; - } - } else if(Below(v->p,vprev->p)&&Below(v->p,vnext->p)) { - if(IsConvex(vnext->p,vprev->p,v->p)) - { - vertextypes[i] = TPPL_VERTEXTYPE_END; - } else { - vertextypes[i] = TPPL_VERTEXTYPE_MERGE; - } - } else { - vertextypes[i] = TPPL_VERTEXTYPE_REGULAR; - } - } - - //helpers - long *helpers = new long[maxnumvertices]; - - //binary search tree that holds edges intersecting the scanline - //note that while set doesn't actually have to be implemented as a tree - //complexity requirements for operations are the same as for the balanced binary search tree - set edgeTree; - //store iterators to the edge tree elements - //this makes deleting existing edges much faster - set::iterator *edgeTreeIterators,edgeIter; - edgeTreeIterators = new set::iterator[maxnumvertices]; - pair::iterator,bool> edgeTreeRet; - - //for each vertex - for(i=0;ip; - newedge.p2 = vertices[v->next].p; - newedge.index = vindex; - edgeTreeRet = edgeTree.insert(newedge); - edgeTreeIterators[vindex] = edgeTreeRet.first; - helpers[vindex] = vindex; - break; - - case TPPL_VERTEXTYPE_END: - //if helper(ei-1) is a merge vertex - if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) { - //Insert the diagonal connecting vi to helper(ei-1) in D. - AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]); - vertextypes[newnumvertices-2] = vertextypes[vindex]; - edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex]; - helpers[newnumvertices-2] = helpers[vindex]; - vertextypes[newnumvertices-1] = vertextypes[helpers[v->previous]]; - edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[v->previous]]; - helpers[newnumvertices-1] = helpers[helpers[v->previous]]; - } - //Delete ei-1 from T - edgeTree.erase(edgeTreeIterators[v->previous]); - break; - - case TPPL_VERTEXTYPE_SPLIT: - //Search in T to find the edge e j directly left of vi. - newedge.p1 = v->p; - newedge.p2 = v->p; - edgeIter = edgeTree.lower_bound(newedge); - if(edgeIter == edgeTree.begin()) { - error = true; - break; - } - --edgeIter; - //Insert the diagonal connecting vi to helper(ej) in D. - AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index]); - vertextypes[newnumvertices-2] = vertextypes[vindex]; - edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex]; - helpers[newnumvertices-2] = helpers[vindex]; - vertextypes[newnumvertices-1] = vertextypes[helpers[edgeIter->index]]; - edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[edgeIter->index]]; - helpers[newnumvertices-1] = helpers[helpers[edgeIter->index]]; - vindex2 = newnumvertices-2; - v2 = &(vertices[vindex2]); - //helper(e j)�vi - helpers[edgeIter->index] = vindex; - //Insert ei in T and set helper(ei) to vi. - newedge.p1 = v2->p; - newedge.p2 = vertices[v2->next].p; - newedge.index = vindex2; - edgeTreeRet = edgeTree.insert(newedge); - edgeTreeIterators[vindex2] = edgeTreeRet.first; - helpers[vindex2] = vindex2; - break; - - case TPPL_VERTEXTYPE_MERGE: - //if helper(ei-1) is a merge vertex - if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) { - //Insert the diagonal connecting vi to helper(ei-1) in D. - AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]); - vertextypes[newnumvertices-2] = vertextypes[vindex]; - edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex]; - helpers[newnumvertices-2] = helpers[vindex]; - vertextypes[newnumvertices-1] = vertextypes[helpers[v->previous]]; - edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[v->previous]]; - helpers[newnumvertices-1] = helpers[helpers[v->previous]]; - vindex2 = newnumvertices-2; - v2 = &(vertices[vindex2]); - } - //Delete ei-1 from T. - edgeTree.erase(edgeTreeIterators[v->previous]); - //Search in T to find the edge e j directly left of vi. - newedge.p1 = v->p; - newedge.p2 = v->p; - edgeIter = edgeTree.lower_bound(newedge); - if(edgeIter == edgeTree.begin()) { - error = true; - break; - } - --edgeIter; - //if helper(ej) is a merge vertex - if(vertextypes[helpers[edgeIter->index]]==TPPL_VERTEXTYPE_MERGE) { - //Insert the diagonal connecting vi to helper(e j) in D. - AddDiagonal(vertices,&newnumvertices,vindex2,helpers[edgeIter->index]); - vertextypes[newnumvertices-2] = vertextypes[vindex2]; - edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex2]; - helpers[newnumvertices-2] = helpers[vindex2]; - vertextypes[newnumvertices-1] = vertextypes[helpers[edgeIter->index]]; - edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[edgeIter->index]]; - helpers[newnumvertices-1] = helpers[helpers[edgeIter->index]]; - } - //helper(e j)�vi - helpers[edgeIter->index] = vindex2; - break; - - case TPPL_VERTEXTYPE_REGULAR: - //if the interior of P lies to the right of vi - if(Below(v->p,vertices[v->previous].p)) { - //if helper(ei-1) is a merge vertex - if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) { - //Insert the diagonal connecting vi to helper(ei-1) in D. - AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]); - vertextypes[newnumvertices-2] = vertextypes[vindex]; - edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex]; - helpers[newnumvertices-2] = helpers[vindex]; - vertextypes[newnumvertices-1] = vertextypes[helpers[v->previous]]; - edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[v->previous]]; - helpers[newnumvertices-1] = helpers[helpers[v->previous]]; - vindex2 = newnumvertices-2; - v2 = &(vertices[vindex2]); - } - //Delete ei-1 from T. - edgeTree.erase(edgeTreeIterators[v->previous]); - //Insert ei in T and set helper(ei) to vi. - newedge.p1 = v2->p; - newedge.p2 = vertices[v2->next].p; - newedge.index = vindex2; - edgeTreeRet = edgeTree.insert(newedge); - edgeTreeIterators[vindex2] = edgeTreeRet.first; - helpers[vindex2] = vindex; - } else { - //Search in T to find the edge ej directly left of vi. - newedge.p1 = v->p; - newedge.p2 = v->p; - edgeIter = edgeTree.lower_bound(newedge); - if(edgeIter == edgeTree.begin()) { - error = true; - break; - } - --edgeIter; - //if helper(ej) is a merge vertex - if(vertextypes[helpers[edgeIter->index]]==TPPL_VERTEXTYPE_MERGE) { - //Insert the diagonal connecting vi to helper(e j) in D. - AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index]); - vertextypes[newnumvertices-2] = vertextypes[vindex]; - edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex]; - helpers[newnumvertices-2] = helpers[vindex]; - vertextypes[newnumvertices-1] = vertextypes[helpers[edgeIter->index]]; - edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[edgeIter->index]]; - helpers[newnumvertices-1] = helpers[helpers[edgeIter->index]]; - } - //helper(e j)�vi - helpers[edgeIter->index] = vindex; - } - break; - } - - if(error) break; - } - - char *used = new char[newnumvertices]; - memset(used,0,newnumvertices*sizeof(char)); - - if(!error) { - //return result - long size; - TPPLPoly mpoly; - for(i=0;inext]); - size = 1; - while(vnext!=v) { - vnext = &(vertices[vnext->next]); - size++; - } - mpoly.Init(size); - v = &(vertices[i]); - mpoly[0] = v->p; - vnext = &(vertices[v->next]); - size = 1; - used[i] = 1; - used[v->next] = 1; - while(vnext!=v) { - mpoly[size] = vnext->p; - used[vnext->next] = 1; - vnext = &(vertices[vnext->next]); - size++; - } - monotonePolys->push_back(mpoly); - } - } - - //cleanup - delete [] vertices; - delete [] priority; - delete [] vertextypes; - delete [] edgeTreeIterators; - delete [] helpers; - delete [] used; - - if(error) { - return 0; - } else { - return 1; - } -} - -//adds a diagonal to the doubly-connected list of vertices -void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2) { - long newindex1,newindex2; - - newindex1 = *numvertices; - (*numvertices)++; - newindex2 = *numvertices; - (*numvertices)++; - - vertices[newindex1].p = vertices[index1].p; - vertices[newindex2].p = vertices[index2].p; - - vertices[newindex2].next = vertices[index2].next; - vertices[newindex1].next = vertices[index1].next; - - vertices[vertices[index2].next].previous = newindex2; - vertices[vertices[index1].next].previous = newindex1; - - vertices[index1].next = newindex2; - vertices[newindex2].previous = index1; - - vertices[index2].next = newindex1; - vertices[newindex1].previous = index2; -} - -bool TPPLPartition::Below(TPPLPoint &p1, TPPLPoint &p2) { - if(p1.y < p2.y) return true; - else if(p1.y == p2.y) { - if(p1.x < p2.x) return true; - } - return false; -} - -//sorts in the falling order of y values, if y is equal, x is used instead -bool TPPLPartition::VertexSorter::operator() (long index1, long index2) const { - if(vertices[index1].p.y > vertices[index2].p.y) return true; - else if(vertices[index1].p.y == vertices[index2].p.y) { - if(vertices[index1].p.x > vertices[index2].p.x) return true; - } - return false; -} - -bool TPPLPartition::ScanLineEdge::IsConvex(const TPPLPoint& p1, const TPPLPoint& p2, const TPPLPoint& p3) const { - tppl_float tmp; - tmp = (p3.y-p1.y)*(p2.x-p1.x)-(p3.x-p1.x)*(p2.y-p1.y); - if(tmp>0) return 1; - else return 0; -} - -bool TPPLPartition::ScanLineEdge::operator < (const ScanLineEdge & other) const { - if(other.p1.y == other.p2.y) { - if(p1.y == p2.y) { - if(p1.y < other.p1.y) return true; - else return false; - } - if(IsConvex(p1,p2,other.p1)) return true; - else return false; - } else if(p1.y == p2.y) { - if(IsConvex(other.p1,other.p2,p1)) return false; - else return true; - } else if(p1.y < other.p1.y) { - if(IsConvex(other.p1,other.p2,p1)) return false; - else return true; - } else { - if(IsConvex(p1,p2,other.p1)) return true; - else return false; - } -} - -//triangulates monotone polygon -//O(n) time, O(n) space complexity -int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, list *triangles) { - long i,i2,j,topindex,bottomindex,leftindex,rightindex,vindex; - TPPLPoint *points; - long numpoints; - TPPLPoly triangle; - - numpoints = inPoly->GetNumPoints(); - points = inPoly->GetPoints(); - - //trivial calses - if(numpoints < 3) return 0; - if(numpoints == 3) { - triangles->push_back(*inPoly); - } - - topindex = 0; bottomindex=0; - for(i=1;i=numpoints) i2 = 0; - if(!Below(points[i2],points[i])) return 0; - i = i2; - } - i = bottomindex; - while(i!=topindex) { - i2 = i+1; if(i2>=numpoints) i2 = 0; - if(!Below(points[i],points[i2])) return 0; - i = i2; - } - - char *vertextypes = new char[numpoints]; - long *priority = new long[numpoints]; - - //merge left and right vertex chains - priority[0] = topindex; - vertextypes[topindex] = 0; - leftindex = topindex+1; if(leftindex>=numpoints) leftindex = 0; - rightindex = topindex-1; if(rightindex<0) rightindex = numpoints-1; - for(i=1;i<(numpoints-1);i++) { - if(leftindex==bottomindex) { - priority[i] = rightindex; - rightindex--; if(rightindex<0) rightindex = numpoints-1; - vertextypes[priority[i]] = -1; - } else if(rightindex==bottomindex) { - priority[i] = leftindex; - leftindex++; if(leftindex>=numpoints) leftindex = 0; - vertextypes[priority[i]] = 1; - } else { - if(Below(points[leftindex],points[rightindex])) { - priority[i] = rightindex; - rightindex--; if(rightindex<0) rightindex = numpoints-1; - vertextypes[priority[i]] = -1; - } else { - priority[i] = leftindex; - leftindex++; if(leftindex>=numpoints) leftindex = 0; - vertextypes[priority[i]] = 1; - } - } - } - priority[i] = bottomindex; - vertextypes[bottomindex] = 0; - - long *stack = new long[numpoints]; - long stackptr = 0; - - stack[0] = priority[0]; - stack[1] = priority[1]; - stackptr = 2; - - //for each vertex from top to bottom trim as many triangles as possible - for(i=2;i<(numpoints-1);i++) { - vindex = priority[i]; - if(vertextypes[vindex]!=vertextypes[stack[stackptr-1]]) { - for(j=0;j<(stackptr-1);j++) { - if(vertextypes[vindex]==1) { - triangle.Triangle(points[stack[j+1]],points[stack[j]],points[vindex]); - } else { - triangle.Triangle(points[stack[j]],points[stack[j+1]],points[vindex]); - } - triangles->push_back(triangle); - } - stack[0] = priority[i-1]; - stack[1] = priority[i]; - stackptr = 2; - } else { - stackptr--; - while(stackptr>0) { - if(vertextypes[vindex]==1) { - if(IsConvex(points[vindex],points[stack[stackptr-1]],points[stack[stackptr]])) { - triangle.Triangle(points[vindex],points[stack[stackptr-1]],points[stack[stackptr]]); - triangles->push_back(triangle); - stackptr--; - } else { - break; - } - } else { - if(IsConvex(points[vindex],points[stack[stackptr]],points[stack[stackptr-1]])) { - triangle.Triangle(points[vindex],points[stack[stackptr]],points[stack[stackptr-1]]); - triangles->push_back(triangle); - stackptr--; - } else { - break; - } - } - } - stackptr++; - stack[stackptr] = vindex; - stackptr++; - } - } - vindex = priority[i]; - for(j=0;j<(stackptr-1);j++) { - if(vertextypes[stack[j+1]]==1) { - triangle.Triangle(points[stack[j]],points[stack[j+1]],points[vindex]); - } else { - triangle.Triangle(points[stack[j+1]],points[stack[j]],points[vindex]); - } - triangles->push_back(triangle); - } - - delete [] priority; - delete [] vertextypes; - delete [] stack; - - return 1; -} - -int TPPLPartition::Triangulate_MONO(list *inpolys, list *triangles) { - list monotone; - list::iterator iter; - - if(!MonotonePartition(inpolys,&monotone)) return 0; - for(iter = monotone.begin(); iter!=monotone.end(); ++iter) { - if(!TriangulateMonotone(&(*iter),triangles)) return 0; - } - return 1; -} - -int TPPLPartition::Triangulate_MONO(TPPLPoly *poly, list *triangles) { - list polys; - polys.push_back(*poly); - - return Triangulate_MONO(&polys, triangles); -} +//Copyright (C) 2011 by Ivan Fratric +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to 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 +#include +#include +#include +#include +#include + +using namespace std; + +#include "polypartition.h" + +#define TPPL_VERTEXTYPE_REGULAR 0 +#define TPPL_VERTEXTYPE_START 1 +#define TPPL_VERTEXTYPE_END 2 +#define TPPL_VERTEXTYPE_SPLIT 3 +#define TPPL_VERTEXTYPE_MERGE 4 + +TPPLPoly::TPPLPoly() { + hole = false; + numpoints = 0; + points = NULL; +} + +TPPLPoly::~TPPLPoly() { + if(points) delete [] points; +} + +void TPPLPoly::Clear() { + if(points) delete [] points; + hole = false; + numpoints = 0; + points = NULL; +} + +void TPPLPoly::Init(long numpoints) { + Clear(); + this->numpoints = numpoints; + points = new TPPLPoint[numpoints]; +} + +void TPPLPoly::Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3) { + Init(3); + points[0] = p1; + points[1] = p2; + points[2] = p3; +} + +TPPLPoly::TPPLPoly(const TPPLPoly &src) { + hole = src.hole; + numpoints = src.numpoints; + points = new TPPLPoint[numpoints]; + memcpy(points, src.points, numpoints*sizeof(TPPLPoint)); +} + +TPPLPoly& TPPLPoly::operator=(const TPPLPoly &src) { + if(&src != this) { + Clear(); + hole = src.hole; + numpoints = src.numpoints; + points = new TPPLPoint[numpoints]; + memcpy(points, src.points, numpoints*sizeof(TPPLPoint)); + } + return *this; +} + +int TPPLPoly::GetOrientation() const { + long i1,i2; + tppl_float area = 0; + for(i1=0; i10) return TPPL_CCW; + if(area<0) return TPPL_CW; + return 0; +} + +void TPPLPoly::SetOrientation(int orientation) { + int polyorientation = GetOrientation(); + if(polyorientation&&(polyorientation!=orientation)) { + Invert(); + } +} + +void TPPLPoly::Invert() { + long i; + TPPLPoint *invpoints; + + invpoints = new TPPLPoint[numpoints]; + for(i=0;i0) return 0; + if(dot21*dot22>0) return 0; + + return 1; +} + +//removes holes from inpolys by merging them with non-holes +int TPPLPartition::RemoveHoles(list *inpolys, list *outpolys) { + list polys; + list::iterator holeiter,polyiter,iter,iter2; + long i,i2,holepointindex,polypointindex = 0; + TPPLPoint holepoint,polypoint,bestpolypoint; + TPPLPoint linep1,linep2; + TPPLPoint v1,v2; + TPPLPoly newpoly; + bool hasholes; + bool pointvisible; + bool pointfound; + + //check for trivial case (no holes) + hasholes = false; + for(iter = inpolys->begin(); iter!=inpolys->end(); ++iter) { + if(iter->IsHole()) { + hasholes = true; + break; + } + } + if(!hasholes) { + for(iter = inpolys->begin(); iter!=inpolys->end(); ++iter) { + outpolys->push_back(*iter); + } + return 1; + } + + polys = *inpolys; + + while(1) { + //find the hole point with the largest x + hasholes = false; + for(iter = polys.begin(); iter!=polys.end(); ++iter) { + if(!iter->IsHole()) continue; + + if(!hasholes) { + hasholes = true; + holeiter = iter; + holepointindex = 0; + } + + for(i=0; i < iter->GetNumPoints(); i++) { + if(iter->GetPoint(i).x > holeiter->GetPoint(holepointindex).x) { + holeiter = iter; + holepointindex = i; + } + } + } + if(!hasholes) break; + holepoint = holeiter->GetPoint(holepointindex); + + pointfound = false; + for(iter = polys.begin(); iter!=polys.end(); ++iter) { + if(iter->IsHole()) continue; + for(i=0; i < iter->GetNumPoints(); i++) { + if(iter->GetPoint(i).x <= holepoint.x) continue; + if(!InCone(iter->GetPoint((i+iter->GetNumPoints()-1)%(iter->GetNumPoints())), + iter->GetPoint(i), + iter->GetPoint((i+1)%(iter->GetNumPoints())), + holepoint)) + continue; + polypoint = iter->GetPoint(i); + if(pointfound) { + v1 = Normalize(polypoint-holepoint); + v2 = Normalize(bestpolypoint-holepoint); + if(v2.x > v1.x) continue; + } + pointvisible = true; + for(iter2 = polys.begin(); iter2!=polys.end(); ++iter2) { + if(iter2->IsHole()) continue; + for(i2=0; i2 < iter2->GetNumPoints(); i2++) { + linep1 = iter2->GetPoint(i2); + linep2 = iter2->GetPoint((i2+1)%(iter2->GetNumPoints())); + if(Intersects(holepoint,polypoint,linep1,linep2)) { + pointvisible = false; + break; + } + } + if(!pointvisible) break; + } + if(pointvisible) { + pointfound = true; + bestpolypoint = polypoint; + polyiter = iter; + polypointindex = i; + } + } + } + + if(!pointfound) return 0; + + newpoly.Init(holeiter->GetNumPoints() + polyiter->GetNumPoints() + 2); + i2 = 0; + for(i=0;i<=polypointindex;i++) { + newpoly[i2] = polyiter->GetPoint(i); + i2++; + } + for(i=0;i<=holeiter->GetNumPoints();i++) { + newpoly[i2] = holeiter->GetPoint((i+holepointindex)%holeiter->GetNumPoints()); + i2++; + } + for(i=polypointindex;iGetNumPoints();i++) { + newpoly[i2] = polyiter->GetPoint(i); + i2++; + } + + polys.erase(holeiter); + polys.erase(polyiter); + polys.push_back(newpoly); + } + + for(iter = polys.begin(); iter!=polys.end(); ++iter) { + outpolys->push_back(*iter); + } + + return 1; +} + +bool TPPLPartition::IsConvex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3) { + tppl_float tmp; + tmp = (p3.y-p1.y)*(p2.x-p1.x)-(p3.x-p1.x)*(p2.y-p1.y); + if(tmp>0) return 1; + else return 0; +} + +bool TPPLPartition::IsReflex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3) { + tppl_float tmp; + tmp = (p3.y-p1.y)*(p2.x-p1.x)-(p3.x-p1.x)*(p2.y-p1.y); + if(tmp<0) return 1; + else return 0; +} + +bool TPPLPartition::IsInside(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3, TPPLPoint &p) { + if(IsConvex(p1,p,p2)) return false; + if(IsConvex(p2,p,p3)) return false; + if(IsConvex(p3,p,p1)) return false; + return true; +} + +bool TPPLPartition::InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p) { + bool convex; + + convex = IsConvex(p1,p2,p3); + + if(convex) { + if(!IsConvex(p1,p2,p)) return false; + if(!IsConvex(p2,p3,p)) return false; + return true; + } else { + if(IsConvex(p1,p2,p)) return true; + if(IsConvex(p2,p3,p)) return true; + return false; + } +} + +bool TPPLPartition::InCone(PartitionVertex *v, TPPLPoint &p) { + TPPLPoint p1,p2,p3; + + p1 = v->previous->p; + p2 = v->p; + p3 = v->next->p; + + return InCone(p1,p2,p3,p); +} + +void TPPLPartition::UpdateVertexReflexity(PartitionVertex *v) { + PartitionVertex *v1,*v3; + v1 = v->previous; + v3 = v->next; + v->isConvex = !IsReflex(v1->p,v->p,v3->p); +} + +void TPPLPartition::UpdateVertex(PartitionVertex *v, PartitionVertex *vertices, long numvertices) { + long i; + PartitionVertex *v1,*v3; + TPPLPoint vec1,vec3; + + v1 = v->previous; + v3 = v->next; + + v->isConvex = IsConvex(v1->p,v->p,v3->p); + + vec1 = Normalize(v1->p - v->p); + vec3 = Normalize(v3->p - v->p); + v->angle = vec1.x*vec3.x + vec1.y*vec3.y; + + if(v->isConvex) { + v->isEar = true; + for(i=0;ip.x)&&(vertices[i].p.y==v->p.y)) continue; + if((vertices[i].p.x==v1->p.x)&&(vertices[i].p.y==v1->p.y)) continue; + if((vertices[i].p.x==v3->p.x)&&(vertices[i].p.y==v3->p.y)) continue; + if(IsInside(v1->p,v->p,v3->p,vertices[i].p)) { + v->isEar = false; + break; + } + } + } else { + v->isEar = false; + } +} + +//triangulation by ear removal +int TPPLPartition::Triangulate_EC(TPPLPoly *poly, list *triangles) { + long numvertices; + PartitionVertex *vertices; + PartitionVertex *ear; + TPPLPoly triangle; + long i,j; + bool earfound; + + if(poly->GetNumPoints() < 3) return 0; + if(poly->GetNumPoints() == 3) { + triangles->push_back(*poly); + return 1; + } + + numvertices = poly->GetNumPoints(); + + vertices = new PartitionVertex[numvertices]; + for(i=0;iGetPoint(i); + if(i==(numvertices-1)) vertices[i].next=&(vertices[0]); + else vertices[i].next=&(vertices[i+1]); + if(i==0) vertices[i].previous = &(vertices[numvertices-1]); + else vertices[i].previous = &(vertices[i-1]); + } + for(i=0;i ear->angle) { + ear = &(vertices[j]); + } + } + } + if(!earfound) { + delete [] vertices; + return 0; + } + + triangle.Triangle(ear->previous->p,ear->p,ear->next->p); + triangles->push_back(triangle); + + ear->isActive = false; + ear->previous->next = ear->next; + ear->next->previous = ear->previous; + + if(i==numvertices-4) break; + + UpdateVertex(ear->previous,vertices,numvertices); + UpdateVertex(ear->next,vertices,numvertices); + } + for(i=0;ip,vertices[i].p,vertices[i].next->p); + triangles->push_back(triangle); + break; + } + } + + delete [] vertices; + + return 1; +} + +int TPPLPartition::Triangulate_EC(list *inpolys, list *triangles) { + list outpolys; + list::iterator iter; + + if(!RemoveHoles(inpolys,&outpolys)) return 0; + for(iter=outpolys.begin();iter!=outpolys.end();++iter) { + if(!Triangulate_EC(&(*iter),triangles)) return 0; + } + return 1; +} + +int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, list *parts) { + list triangles; + list::iterator iter1,iter2; + TPPLPoly *poly1,*poly2; + TPPLPoly newpoly; + TPPLPoint d1,d2,p1,p2,p3; + long i11,i12,i21,i22,i13,i23,j,k; + bool isdiagonal; + long numreflex; + + //check if the poly is already convex + numreflex = 0; + for(i11=0;i11GetNumPoints();i11++) { + if(i11==0) i12 = poly->GetNumPoints()-1; + else i12=i11-1; + if(i11==(poly->GetNumPoints()-1)) i13=0; + else i13=i11+1; + if(IsReflex(poly->GetPoint(i12),poly->GetPoint(i11),poly->GetPoint(i13))) { + numreflex = 1; + break; + } + } + if(numreflex == 0) { + parts->push_back(*poly); + return 1; + } + + if(!Triangulate_EC(poly,&triangles)) return 0; + + for(iter1 = triangles.begin(); iter1 != triangles.end(); ++iter1) { + poly1 = &(*iter1); + for(i11=0;i11GetNumPoints();i11++) { + d1 = poly1->GetPoint(i11); + i12 = (i11+1)%(poly1->GetNumPoints()); + d2 = poly1->GetPoint(i12); + + isdiagonal = false; + for(iter2 = iter1; iter2 != triangles.end(); ++iter2) { + if(iter1 == iter2) continue; + poly2 = &(*iter2); + + for(i21=0;i21GetNumPoints();i21++) { + if((d2.x != poly2->GetPoint(i21).x)||(d2.y != poly2->GetPoint(i21).y)) continue; + i22 = (i21+1)%(poly2->GetNumPoints()); + if((d1.x != poly2->GetPoint(i22).x)||(d1.y != poly2->GetPoint(i22).y)) continue; + isdiagonal = true; + break; + } + if(isdiagonal) break; + } + + if(!isdiagonal) continue; + + p2 = poly1->GetPoint(i11); + if(i11 == 0) i13 = poly1->GetNumPoints()-1; + else i13 = i11-1; + p1 = poly1->GetPoint(i13); + if(i22 == (poly2->GetNumPoints()-1)) i23 = 0; + else i23 = i22+1; + p3 = poly2->GetPoint(i23); + + if(!IsConvex(p1,p2,p3)) continue; + + p2 = poly1->GetPoint(i12); + if(i12 == (poly1->GetNumPoints()-1)) i13 = 0; + else i13 = i12+1; + p3 = poly1->GetPoint(i13); + if(i21 == 0) i23 = poly2->GetNumPoints()-1; + else i23 = i21-1; + p1 = poly2->GetPoint(i23); + + if(!IsConvex(p1,p2,p3)) continue; + + newpoly.Init(poly1->GetNumPoints()+poly2->GetNumPoints()-2); + k = 0; + for(j=i12;j!=i11;j=(j+1)%(poly1->GetNumPoints())) { + newpoly[k] = poly1->GetPoint(j); + k++; + } + for(j=i22;j!=i21;j=(j+1)%(poly2->GetNumPoints())) { + newpoly[k] = poly2->GetPoint(j); + k++; + } + + triangles.erase(iter2); + *iter1 = newpoly; + poly1 = &(*iter1); + i11 = -1; + + continue; + } + } + + for(iter1 = triangles.begin(); iter1 != triangles.end(); ++iter1) { + parts->push_back(*iter1); + } + + return 1; +} + +int TPPLPartition::ConvexPartition_HM(list *inpolys, list *parts) { + list outpolys; + list::iterator iter; + + if(!RemoveHoles(inpolys,&outpolys)) return 0; + for(iter=outpolys.begin();iter!=outpolys.end();++iter) { + if(!ConvexPartition_HM(&(*iter),parts)) return 0; + } + return 1; +} + +//minimum-weight polygon triangulation by dynamic programming +//O(n^3) time complexity +//O(n^2) space complexity +int TPPLPartition::Triangulate_OPT(TPPLPoly *poly, list *triangles) { + long i,j,k,gap,n; + DPState **dpstates; + TPPLPoint p1,p2,p3,p4; + long bestvertex; + tppl_float weight,minweight,d1,d2; + Diagonal diagonal,newdiagonal; + list diagonals; + TPPLPoly triangle; + int ret = 1; + + n = poly->GetNumPoints(); + dpstates = new DPState *[n]; + for(i=1;iGetPoint(i); + for(j=i+1;jGetPoint(j); + + //visibility check + if(i==0) p3 = poly->GetPoint(n-1); + else p3 = poly->GetPoint(i-1); + if(i==(n-1)) p4 = poly->GetPoint(0); + else p4 = poly->GetPoint(i+1); + if(!InCone(p3,p1,p4,p2)) { + dpstates[j][i].visible = false; + continue; + } + + if(j==0) p3 = poly->GetPoint(n-1); + else p3 = poly->GetPoint(j-1); + if(j==(n-1)) p4 = poly->GetPoint(0); + else p4 = poly->GetPoint(j+1); + if(!InCone(p3,p2,p4,p1)) { + dpstates[j][i].visible = false; + continue; + } + + for(k=0;kGetPoint(k); + if(k==(n-1)) p4 = poly->GetPoint(0); + else p4 = poly->GetPoint(k+1); + if(Intersects(p1,p2,p3,p4)) { + dpstates[j][i].visible = false; + break; + } + } + } + } + } + dpstates[n-1][0].visible = true; + dpstates[n-1][0].weight = 0; + dpstates[n-1][0].bestvertex = -1; + + for(gap = 2; gapGetPoint(i),poly->GetPoint(k)); + if(j<=(k+1)) d2=0; + else d2 = Distance(poly->GetPoint(k),poly->GetPoint(j)); + + weight = dpstates[k][i].weight + dpstates[j][k].weight + d1 + d2; + + if((bestvertex == -1)||(weightGetPoint(diagonal.index1),poly->GetPoint(bestvertex),poly->GetPoint(diagonal.index2)); + triangles->push_back(triangle); + if(bestvertex > (diagonal.index1+1)) { + newdiagonal.index1 = diagonal.index1; + newdiagonal.index2 = bestvertex; + diagonals.push_back(newdiagonal); + } + if(diagonal.index2 > (bestvertex+1)) { + newdiagonal.index1 = bestvertex; + newdiagonal.index2 = diagonal.index2; + diagonals.push_back(newdiagonal); + } + } + + for(i=1;i *pairs; + long w2; + + w2 = dpstates[a][b].weight; + if(w>w2) return; + + pairs = &(dpstates[a][b].pairs); + newdiagonal.index1 = i; + newdiagonal.index2 = j; + + if(wclear(); + pairs->push_front(newdiagonal); + dpstates[a][b].weight = w; + } else { + if((!pairs->empty())&&(i <= pairs->begin()->index1)) return; + while((!pairs->empty())&&(pairs->begin()->index2 >= j)) pairs->pop_front(); + pairs->push_front(newdiagonal); + } +} + +void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) { + list *pairs; + list::iterator iter,lastiter; + long top; + long w; + + if(!dpstates[i][j].visible) return; + top = j; + w = dpstates[i][j].weight; + if(k-j > 1) { + if (!dpstates[j][k].visible) return; + w += dpstates[j][k].weight + 1; + } + if(j-i > 1) { + pairs = &(dpstates[i][j].pairs); + iter = pairs->end(); + lastiter = pairs->end(); + while(iter!=pairs->begin()) { + --iter; + if(!IsReflex(vertices[iter->index2].p,vertices[j].p,vertices[k].p)) lastiter = iter; + else break; + } + if(lastiter == pairs->end()) w++; + else { + if(IsReflex(vertices[k].p,vertices[i].p,vertices[lastiter->index1].p)) w++; + else top = lastiter->index1; + } + } + UpdateState(i,k,w,top,j,dpstates); +} + +void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) { + list *pairs; + list::iterator iter,lastiter; + long top; + long w; + + if(!dpstates[j][k].visible) return; + top = j; + w = dpstates[j][k].weight; + + if (j-i > 1) { + if (!dpstates[i][j].visible) return; + w += dpstates[i][j].weight + 1; + } + if (k-j > 1) { + pairs = &(dpstates[j][k].pairs); + + iter = pairs->begin(); + if((!pairs->empty())&&(!IsReflex(vertices[i].p,vertices[j].p,vertices[iter->index1].p))) { + lastiter = iter; + while(iter!=pairs->end()) { + if(!IsReflex(vertices[i].p,vertices[j].p,vertices[iter->index1].p)) { + lastiter = iter; + ++iter; + } + else break; + } + if(IsReflex(vertices[lastiter->index2].p,vertices[k].p,vertices[i].p)) w++; + else top = lastiter->index2; + } else w++; + } + UpdateState(i,k,w,j,top,dpstates); +} + +int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list *parts) { + TPPLPoint p1,p2,p3,p4; + PartitionVertex *vertices; + DPState2 **dpstates; + long i,j,k,n,gap; + list diagonals,diagonals2; + Diagonal diagonal,newdiagonal; + list *pairs,*pairs2; + list::iterator iter,iter2; + int ret; + TPPLPoly newpoly; + list indices; + list::iterator iiter; + bool ijreal,jkreal; + + n = poly->GetNumPoints(); + vertices = new PartitionVertex[n]; + + dpstates = new DPState2 *[n]; + for(i=0;iGetPoint(i); + vertices[i].isActive = true; + if(i==0) vertices[i].previous = &(vertices[n-1]); + else vertices[i].previous = &(vertices[i-1]); + if(i==(poly->GetNumPoints()-1)) vertices[i].next = &(vertices[0]); + else vertices[i].next = &(vertices[i+1]); + } + for(i=1;iGetPoint(i); + for(j=i+1;jGetPoint(j); + + //visibility check + if(!InCone(&vertices[i],p2)) { + dpstates[i][j].visible = false; + continue; + } + if(!InCone(&vertices[j],p1)) { + dpstates[i][j].visible = false; + continue; + } + + for(k=0;kGetPoint(k); + if(k==(n-1)) p4 = poly->GetPoint(0); + else p4 = poly->GetPoint(k+1); + if(Intersects(p1,p2,p3,p4)) { + dpstates[i][j].visible = false; + break; + } + } + } + } + } + for(i=0;i<(n-2);i++) { + j = i+2; + if(dpstates[i][j].visible) { + dpstates[i][j].weight = 0; + newdiagonal.index1 = i+1; + newdiagonal.index2 = i+1; + dpstates[i][j].pairs.push_back(newdiagonal); + } + } + + dpstates[0][n-1].visible = true; + vertices[0].isConvex = false; //by convention + + for(gap=3; gapempty()) { + ret = 0; + break; + } + if(!vertices[diagonal.index1].isConvex) { + iter = pairs->end(); + --iter; + j = iter->index2; + newdiagonal.index1 = j; + newdiagonal.index2 = diagonal.index2; + diagonals.push_front(newdiagonal); + if((j - diagonal.index1)>1) { + if(iter->index1 != iter->index2) { + pairs2 = &(dpstates[diagonal.index1][j].pairs); + while(1) { + if(pairs2->empty()) { + ret = 0; + break; + } + iter2 = pairs2->end(); + --iter2; + if(iter->index1 != iter2->index1) pairs2->pop_back(); + else break; + } + if(ret == 0) break; + } + newdiagonal.index1 = diagonal.index1; + newdiagonal.index2 = j; + diagonals.push_front(newdiagonal); + } + } else { + iter = pairs->begin(); + j = iter->index1; + newdiagonal.index1 = diagonal.index1; + newdiagonal.index2 = j; + diagonals.push_front(newdiagonal); + if((diagonal.index2 - j) > 1) { + if(iter->index1 != iter->index2) { + pairs2 = &(dpstates[j][diagonal.index2].pairs); + while(1) { + if(pairs2->empty()) { + ret = 0; + break; + } + iter2 = pairs2->begin(); + if(iter->index2 != iter2->index2) pairs2->pop_front(); + else break; + } + if(ret == 0) break; + } + newdiagonal.index1 = j; + newdiagonal.index2 = diagonal.index2; + diagonals.push_front(newdiagonal); + } + } + } + + if(ret == 0) { + for(i=0;iend(); + --iter; + j = iter->index2; + if(iter->index1 != iter->index2) ijreal = false; + } else { + iter = pairs->begin(); + j = iter->index1; + if(iter->index1 != iter->index2) jkreal = false; + } + + newdiagonal.index1 = diagonal.index1; + newdiagonal.index2 = j; + if(ijreal) { + diagonals.push_back(newdiagonal); + } else { + diagonals2.push_back(newdiagonal); + } + + newdiagonal.index1 = j; + newdiagonal.index2 = diagonal.index2; + if(jkreal) { + diagonals.push_back(newdiagonal); + } else { + diagonals2.push_back(newdiagonal); + } + + indices.push_back(j); + } + + indices.sort(); + newpoly.Init((long)indices.size()); + k=0; + for(iiter = indices.begin();iiter!=indices.end(); ++iiter) { + newpoly[k] = vertices[*iiter].p; + k++; + } + parts->push_back(newpoly); + } + + for(i=0;i *inpolys, list *monotonePolys) { + list::iterator iter; + MonotoneVertex *vertices; + long i,numvertices,vindex,vindex2,newnumvertices,maxnumvertices; + long polystartindex, polyendindex; + TPPLPoly *poly; + MonotoneVertex *v,*v2,*vprev,*vnext; + ScanLineEdge newedge; + bool error = false; + + numvertices = 0; + for(iter = inpolys->begin(); iter != inpolys->end(); ++iter) { + numvertices += iter->GetNumPoints(); + } + + maxnumvertices = numvertices*3; + vertices = new MonotoneVertex[maxnumvertices]; + newnumvertices = numvertices; + + polystartindex = 0; + for(iter = inpolys->begin(); iter != inpolys->end(); ++iter) { + poly = &(*iter); + polyendindex = polystartindex + poly->GetNumPoints()-1; + for(i=0;iGetNumPoints();i++) { + vertices[i+polystartindex].p = poly->GetPoint(i); + if(i==0) vertices[i+polystartindex].previous = polyendindex; + else vertices[i+polystartindex].previous = i+polystartindex-1; + if(i==(poly->GetNumPoints()-1)) vertices[i+polystartindex].next = polystartindex; + else vertices[i+polystartindex].next = i+polystartindex+1; + } + polystartindex = polyendindex+1; + } + + //construct the priority queue + long *priority = new long [numvertices]; + for(i=0;iprevious]); + vnext = &(vertices[v->next]); + + if(Below(vprev->p,v->p)&&Below(vnext->p,v->p)) { + if(IsConvex(vnext->p,vprev->p,v->p)) { + vertextypes[i] = TPPL_VERTEXTYPE_START; + } else { + vertextypes[i] = TPPL_VERTEXTYPE_SPLIT; + } + } else if(Below(v->p,vprev->p)&&Below(v->p,vnext->p)) { + if(IsConvex(vnext->p,vprev->p,v->p)) + { + vertextypes[i] = TPPL_VERTEXTYPE_END; + } else { + vertextypes[i] = TPPL_VERTEXTYPE_MERGE; + } + } else { + vertextypes[i] = TPPL_VERTEXTYPE_REGULAR; + } + } + + //helpers + long *helpers = new long[maxnumvertices]; + + //binary search tree that holds edges intersecting the scanline + //note that while set doesn't actually have to be implemented as a tree + //complexity requirements for operations are the same as for the balanced binary search tree + set edgeTree; + //store iterators to the edge tree elements + //this makes deleting existing edges much faster + set::iterator *edgeTreeIterators,edgeIter; + edgeTreeIterators = new set::iterator[maxnumvertices]; + pair::iterator,bool> edgeTreeRet; + + //for each vertex + for(i=0;ip; + newedge.p2 = vertices[v->next].p; + newedge.index = vindex; + edgeTreeRet = edgeTree.insert(newedge); + edgeTreeIterators[vindex] = edgeTreeRet.first; + helpers[vindex] = vindex; + break; + + case TPPL_VERTEXTYPE_END: + //if helper(ei-1) is a merge vertex + if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) { + //Insert the diagonal connecting vi to helper(ei-1) in D. + AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]); + vertextypes[newnumvertices-2] = vertextypes[vindex]; + edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex]; + helpers[newnumvertices-2] = helpers[vindex]; + vertextypes[newnumvertices-1] = vertextypes[helpers[v->previous]]; + edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[v->previous]]; + helpers[newnumvertices-1] = helpers[helpers[v->previous]]; + } + //Delete ei-1 from T + edgeTree.erase(edgeTreeIterators[v->previous]); + break; + + case TPPL_VERTEXTYPE_SPLIT: + //Search in T to find the edge e j directly left of vi. + newedge.p1 = v->p; + newedge.p2 = v->p; + edgeIter = edgeTree.lower_bound(newedge); + if(edgeIter == edgeTree.begin()) { + error = true; + break; + } + --edgeIter; + //Insert the diagonal connecting vi to helper(ej) in D. + AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index]); + vertextypes[newnumvertices-2] = vertextypes[vindex]; + edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex]; + helpers[newnumvertices-2] = helpers[vindex]; + vertextypes[newnumvertices-1] = vertextypes[helpers[edgeIter->index]]; + edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[edgeIter->index]]; + helpers[newnumvertices-1] = helpers[helpers[edgeIter->index]]; + vindex2 = newnumvertices-2; + v2 = &(vertices[vindex2]); + //helper(e j)�vi + helpers[edgeIter->index] = vindex; + //Insert ei in T and set helper(ei) to vi. + newedge.p1 = v2->p; + newedge.p2 = vertices[v2->next].p; + newedge.index = vindex2; + edgeTreeRet = edgeTree.insert(newedge); + edgeTreeIterators[vindex2] = edgeTreeRet.first; + helpers[vindex2] = vindex2; + break; + + case TPPL_VERTEXTYPE_MERGE: + //if helper(ei-1) is a merge vertex + if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) { + //Insert the diagonal connecting vi to helper(ei-1) in D. + AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]); + vertextypes[newnumvertices-2] = vertextypes[vindex]; + edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex]; + helpers[newnumvertices-2] = helpers[vindex]; + vertextypes[newnumvertices-1] = vertextypes[helpers[v->previous]]; + edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[v->previous]]; + helpers[newnumvertices-1] = helpers[helpers[v->previous]]; + vindex2 = newnumvertices-2; + v2 = &(vertices[vindex2]); + } + //Delete ei-1 from T. + edgeTree.erase(edgeTreeIterators[v->previous]); + //Search in T to find the edge e j directly left of vi. + newedge.p1 = v->p; + newedge.p2 = v->p; + edgeIter = edgeTree.lower_bound(newedge); + if(edgeIter == edgeTree.begin()) { + error = true; + break; + } + --edgeIter; + //if helper(ej) is a merge vertex + if(vertextypes[helpers[edgeIter->index]]==TPPL_VERTEXTYPE_MERGE) { + //Insert the diagonal connecting vi to helper(e j) in D. + AddDiagonal(vertices,&newnumvertices,vindex2,helpers[edgeIter->index]); + vertextypes[newnumvertices-2] = vertextypes[vindex2]; + edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex2]; + helpers[newnumvertices-2] = helpers[vindex2]; + vertextypes[newnumvertices-1] = vertextypes[helpers[edgeIter->index]]; + edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[edgeIter->index]]; + helpers[newnumvertices-1] = helpers[helpers[edgeIter->index]]; + } + //helper(e j)�vi + helpers[edgeIter->index] = vindex2; + break; + + case TPPL_VERTEXTYPE_REGULAR: + //if the interior of P lies to the right of vi + if(Below(v->p,vertices[v->previous].p)) { + //if helper(ei-1) is a merge vertex + if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) { + //Insert the diagonal connecting vi to helper(ei-1) in D. + AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]); + vertextypes[newnumvertices-2] = vertextypes[vindex]; + edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex]; + helpers[newnumvertices-2] = helpers[vindex]; + vertextypes[newnumvertices-1] = vertextypes[helpers[v->previous]]; + edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[v->previous]]; + helpers[newnumvertices-1] = helpers[helpers[v->previous]]; + vindex2 = newnumvertices-2; + v2 = &(vertices[vindex2]); + } + //Delete ei-1 from T. + edgeTree.erase(edgeTreeIterators[v->previous]); + //Insert ei in T and set helper(ei) to vi. + newedge.p1 = v2->p; + newedge.p2 = vertices[v2->next].p; + newedge.index = vindex2; + edgeTreeRet = edgeTree.insert(newedge); + edgeTreeIterators[vindex2] = edgeTreeRet.first; + helpers[vindex2] = vindex; + } else { + //Search in T to find the edge ej directly left of vi. + newedge.p1 = v->p; + newedge.p2 = v->p; + edgeIter = edgeTree.lower_bound(newedge); + if(edgeIter == edgeTree.begin()) { + error = true; + break; + } + --edgeIter; + //if helper(ej) is a merge vertex + if(vertextypes[helpers[edgeIter->index]]==TPPL_VERTEXTYPE_MERGE) { + //Insert the diagonal connecting vi to helper(e j) in D. + AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index]); + vertextypes[newnumvertices-2] = vertextypes[vindex]; + edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex]; + helpers[newnumvertices-2] = helpers[vindex]; + vertextypes[newnumvertices-1] = vertextypes[helpers[edgeIter->index]]; + edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[edgeIter->index]]; + helpers[newnumvertices-1] = helpers[helpers[edgeIter->index]]; + } + //helper(e j)�vi + helpers[edgeIter->index] = vindex; + } + break; + } + + if(error) break; + } + + char *used = new char[newnumvertices]; + memset(used,0,newnumvertices*sizeof(char)); + + if(!error) { + //return result + long size; + TPPLPoly mpoly; + for(i=0;inext]); + size = 1; + while(vnext!=v) { + vnext = &(vertices[vnext->next]); + size++; + } + mpoly.Init(size); + v = &(vertices[i]); + mpoly[0] = v->p; + vnext = &(vertices[v->next]); + size = 1; + used[i] = 1; + used[v->next] = 1; + while(vnext!=v) { + mpoly[size] = vnext->p; + used[vnext->next] = 1; + vnext = &(vertices[vnext->next]); + size++; + } + monotonePolys->push_back(mpoly); + } + } + + //cleanup + delete [] vertices; + delete [] priority; + delete [] vertextypes; + delete [] edgeTreeIterators; + delete [] helpers; + delete [] used; + + if(error) { + return 0; + } else { + return 1; + } +} + +//adds a diagonal to the doubly-connected list of vertices +void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2) { + long newindex1,newindex2; + + newindex1 = *numvertices; + (*numvertices)++; + newindex2 = *numvertices; + (*numvertices)++; + + vertices[newindex1].p = vertices[index1].p; + vertices[newindex2].p = vertices[index2].p; + + vertices[newindex2].next = vertices[index2].next; + vertices[newindex1].next = vertices[index1].next; + + vertices[vertices[index2].next].previous = newindex2; + vertices[vertices[index1].next].previous = newindex1; + + vertices[index1].next = newindex2; + vertices[newindex2].previous = index1; + + vertices[index2].next = newindex1; + vertices[newindex1].previous = index2; +} + +bool TPPLPartition::Below(TPPLPoint &p1, TPPLPoint &p2) { + if(p1.y < p2.y) return true; + else if(p1.y == p2.y) { + if(p1.x < p2.x) return true; + } + return false; +} + +//sorts in the falling order of y values, if y is equal, x is used instead +bool TPPLPartition::VertexSorter::operator() (long index1, long index2) const { + if(vertices[index1].p.y > vertices[index2].p.y) return true; + else if(vertices[index1].p.y == vertices[index2].p.y) { + if(vertices[index1].p.x > vertices[index2].p.x) return true; + } + return false; +} + +bool TPPLPartition::ScanLineEdge::IsConvex(const TPPLPoint& p1, const TPPLPoint& p2, const TPPLPoint& p3) const { + tppl_float tmp; + tmp = (p3.y-p1.y)*(p2.x-p1.x)-(p3.x-p1.x)*(p2.y-p1.y); + if(tmp>0) return 1; + else return 0; +} + +bool TPPLPartition::ScanLineEdge::operator < (const ScanLineEdge & other) const { + if(other.p1.y == other.p2.y) { + if(p1.y == p2.y) { + if(p1.y < other.p1.y) return true; + else return false; + } + if(IsConvex(p1,p2,other.p1)) return true; + else return false; + } else if(p1.y == p2.y) { + if(IsConvex(other.p1,other.p2,p1)) return false; + else return true; + } else if(p1.y < other.p1.y) { + if(IsConvex(other.p1,other.p2,p1)) return false; + else return true; + } else { + if(IsConvex(p1,p2,other.p1)) return true; + else return false; + } +} + +//triangulates monotone polygon +//O(n) time, O(n) space complexity +int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, list *triangles) { + long i,i2,j,topindex,bottomindex,leftindex,rightindex,vindex; + TPPLPoint *points; + long numpoints; + TPPLPoly triangle; + + numpoints = inPoly->GetNumPoints(); + points = inPoly->GetPoints(); + + //trivial calses + if(numpoints < 3) return 0; + if(numpoints == 3) { + triangles->push_back(*inPoly); + } + + topindex = 0; bottomindex=0; + for(i=1;i=numpoints) i2 = 0; + if(!Below(points[i2],points[i])) return 0; + i = i2; + } + i = bottomindex; + while(i!=topindex) { + i2 = i+1; if(i2>=numpoints) i2 = 0; + if(!Below(points[i],points[i2])) return 0; + i = i2; + } + + char *vertextypes = new char[numpoints]; + long *priority = new long[numpoints]; + + //merge left and right vertex chains + priority[0] = topindex; + vertextypes[topindex] = 0; + leftindex = topindex+1; if(leftindex>=numpoints) leftindex = 0; + rightindex = topindex-1; if(rightindex<0) rightindex = numpoints-1; + for(i=1;i<(numpoints-1);i++) { + if(leftindex==bottomindex) { + priority[i] = rightindex; + rightindex--; if(rightindex<0) rightindex = numpoints-1; + vertextypes[priority[i]] = -1; + } else if(rightindex==bottomindex) { + priority[i] = leftindex; + leftindex++; if(leftindex>=numpoints) leftindex = 0; + vertextypes[priority[i]] = 1; + } else { + if(Below(points[leftindex],points[rightindex])) { + priority[i] = rightindex; + rightindex--; if(rightindex<0) rightindex = numpoints-1; + vertextypes[priority[i]] = -1; + } else { + priority[i] = leftindex; + leftindex++; if(leftindex>=numpoints) leftindex = 0; + vertextypes[priority[i]] = 1; + } + } + } + priority[i] = bottomindex; + vertextypes[bottomindex] = 0; + + long *stack = new long[numpoints]; + long stackptr = 0; + + stack[0] = priority[0]; + stack[1] = priority[1]; + stackptr = 2; + + //for each vertex from top to bottom trim as many triangles as possible + for(i=2;i<(numpoints-1);i++) { + vindex = priority[i]; + if(vertextypes[vindex]!=vertextypes[stack[stackptr-1]]) { + for(j=0;j<(stackptr-1);j++) { + if(vertextypes[vindex]==1) { + triangle.Triangle(points[stack[j+1]],points[stack[j]],points[vindex]); + } else { + triangle.Triangle(points[stack[j]],points[stack[j+1]],points[vindex]); + } + triangles->push_back(triangle); + } + stack[0] = priority[i-1]; + stack[1] = priority[i]; + stackptr = 2; + } else { + stackptr--; + while(stackptr>0) { + if(vertextypes[vindex]==1) { + if(IsConvex(points[vindex],points[stack[stackptr-1]],points[stack[stackptr]])) { + triangle.Triangle(points[vindex],points[stack[stackptr-1]],points[stack[stackptr]]); + triangles->push_back(triangle); + stackptr--; + } else { + break; + } + } else { + if(IsConvex(points[vindex],points[stack[stackptr]],points[stack[stackptr-1]])) { + triangle.Triangle(points[vindex],points[stack[stackptr]],points[stack[stackptr-1]]); + triangles->push_back(triangle); + stackptr--; + } else { + break; + } + } + } + stackptr++; + stack[stackptr] = vindex; + stackptr++; + } + } + vindex = priority[i]; + for(j=0;j<(stackptr-1);j++) { + if(vertextypes[stack[j+1]]==1) { + triangle.Triangle(points[stack[j]],points[stack[j+1]],points[vindex]); + } else { + triangle.Triangle(points[stack[j+1]],points[stack[j]],points[vindex]); + } + triangles->push_back(triangle); + } + + delete [] priority; + delete [] vertextypes; + delete [] stack; + + return 1; +} + +int TPPLPartition::Triangulate_MONO(list *inpolys, list *triangles) { + list monotone; + list::iterator iter; + + if(!MonotonePartition(inpolys,&monotone)) return 0; + for(iter = monotone.begin(); iter!=monotone.end(); ++iter) { + if(!TriangulateMonotone(&(*iter),triangles)) return 0; + } + return 1; +} + +int TPPLPartition::Triangulate_MONO(TPPLPoly *poly, list *triangles) { + list polys; + polys.push_back(*poly); + + return Triangulate_MONO(&polys, triangles); +} diff --git a/xs/src/polypartition.h b/src/polypartition/polypartition.h similarity index 97% rename from xs/src/polypartition.h rename to src/polypartition/polypartition.h index 2596243ae4..20ec0e24f6 100644 --- a/xs/src/polypartition.h +++ b/src/polypartition/polypartition.h @@ -1,343 +1,343 @@ -//Copyright (C) 2011 by Ivan Fratric -// -//Permission is hereby granted, free of charge, to any person obtaining a copy -//of this software and associated documentation files (the "Software"), to 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 -using namespace std; - -typedef double tppl_float; - -#define TPPL_CCW 1 -#define TPPL_CW -1 - -//2D point structure -struct TPPLPoint { - tppl_float x; - tppl_float y; - - TPPLPoint operator + (const TPPLPoint& p) const { - TPPLPoint r; - r.x = x + p.x; - r.y = y + p.y; - return r; - } - - TPPLPoint operator - (const TPPLPoint& p) const { - TPPLPoint r; - r.x = x - p.x; - r.y = y - p.y; - return r; - } - - TPPLPoint operator * (const tppl_float f ) const { - TPPLPoint r; - r.x = x*f; - r.y = y*f; - return r; - } - - TPPLPoint operator / (const tppl_float f ) const { - TPPLPoint r; - r.x = x/f; - r.y = y/f; - return r; - } - - bool operator==(const TPPLPoint& p) const { - if((x == p.x)&&(y==p.y)) return true; - else return false; - } - - bool operator!=(const TPPLPoint& p) const { - if((x == p.x)&&(y==p.y)) return false; - else return true; - } -}; - -//Polygon implemented as an array of points with a 'hole' flag -class TPPLPoly { -protected: - - TPPLPoint *points; - long numpoints; - bool hole; - -public: - - //constructors/destructors - TPPLPoly(); - ~TPPLPoly(); - - TPPLPoly(const TPPLPoly &src); - TPPLPoly& operator=(const TPPLPoly &src); - - //getters and setters - long GetNumPoints() const { - return numpoints; - } - - bool IsHole() const { - return hole; - } - - void SetHole(bool hole) { - this->hole = hole; - } - - TPPLPoint &GetPoint(long i) { - return points[i]; - } - - TPPLPoint *GetPoints() { - return points; - } - - TPPLPoint& operator[] (int i) { - return points[i]; - } - - //clears the polygon points - void Clear(); - - //inits the polygon with numpoints vertices - void Init(long numpoints); - - //creates a triangle with points p1,p2,p3 - void Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3); - - //inverts the orfer of vertices - void Invert(); - - //returns the orientation of the polygon - //possible values: - // TPPL_CCW : polygon vertices are in counter-clockwise order - // TPPL_CW : polygon vertices are in clockwise order - // 0 : the polygon has no (measurable) area - int GetOrientation() const; - - //sets the polygon orientation - //orientation can be - // TPPL_CCW : sets vertices in counter-clockwise order - // TPPL_CW : sets vertices in clockwise order - void SetOrientation(int orientation); -}; - -class TPPLPartition { -protected: - struct PartitionVertex { - bool isActive; - bool isConvex; - bool isEar; - - TPPLPoint p; - tppl_float angle; - PartitionVertex *previous; - PartitionVertex *next; - }; - - struct MonotoneVertex { - TPPLPoint p; - long previous; - long next; - }; - - class VertexSorter{ - MonotoneVertex *vertices; - public: - VertexSorter(MonotoneVertex *v) : vertices(v) {} - bool operator() (long index1, long index2) const; - }; - - struct Diagonal { - long index1; - long index2; - }; - - //dynamic programming state for minimum-weight triangulation - struct DPState { - bool visible; - tppl_float weight; - long bestvertex; - }; - - //dynamic programming state for convex partitioning - struct DPState2 { - bool visible; - long weight; - list pairs; - }; - - //edge that intersects the scanline - struct ScanLineEdge { - long index; - TPPLPoint p1; - TPPLPoint p2; - - //determines if the edge is to the left of another edge - bool operator< (const ScanLineEdge & other) const; - - bool IsConvex(const TPPLPoint& p1, const TPPLPoint& p2, const TPPLPoint& p3) const; - }; - - //standard helper functions - bool IsConvex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3); - bool IsReflex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3); - bool IsInside(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3, TPPLPoint &p); - - bool InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p); - bool InCone(PartitionVertex *v, TPPLPoint &p); - - int Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TPPLPoint &p22); - - TPPLPoint Normalize(const TPPLPoint &p); - tppl_float Distance(const TPPLPoint &p1, const TPPLPoint &p2); - - //helper functions for Triangulate_EC - void UpdateVertexReflexity(PartitionVertex *v); - void UpdateVertex(PartitionVertex *v,PartitionVertex *vertices, long numvertices); - - //helper functions for ConvexPartition_OPT - void UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates); - void TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates); - void TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates); - - //helper functions for MonotonePartition - bool Below(TPPLPoint &p1, TPPLPoint &p2); - void AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2); - - //triangulates a monotone polygon, used in Triangulate_MONO - int TriangulateMonotone(TPPLPoly *inPoly, list *triangles); - -public: - - //simple heuristic procedure for removing holes from a list of polygons - //works by creating a diagonal from the rightmost hole vertex to some visible vertex - //time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices - //space complexity: O(n) - //params: - // inpolys : a list of polygons that can contain holes - // vertices of all non-hole polys have to be in counter-clockwise order - // vertices of all hole polys have to be in clockwise order - // outpolys : a list of polygons without holes - //returns 1 on success, 0 on failure - int RemoveHoles(list *inpolys, list *outpolys); - - //triangulates a polygon by ear clipping - //time complexity O(n^2), n is the number of vertices - //space complexity: O(n) - //params: - // poly : an input polygon to be triangulated - // vertices have to be in counter-clockwise order - // triangles : a list of triangles (result) - //returns 1 on success, 0 on failure - int Triangulate_EC(TPPLPoly *poly, list *triangles); - - //triangulates a list of polygons that may contain holes by ear clipping algorithm - //first calls RemoveHoles to get rid of the holes, and then Triangulate_EC for each resulting polygon - //time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices - //space complexity: O(n) - //params: - // inpolys : a list of polygons to be triangulated (can contain holes) - // vertices of all non-hole polys have to be in counter-clockwise order - // vertices of all hole polys have to be in clockwise order - // triangles : a list of triangles (result) - //returns 1 on success, 0 on failure - int Triangulate_EC(list *inpolys, list *triangles); - - //creates an optimal polygon triangulation in terms of minimal edge length - //time complexity: O(n^3), n is the number of vertices - //space complexity: O(n^2) - //params: - // poly : an input polygon to be triangulated - // vertices have to be in counter-clockwise order - // triangles : a list of triangles (result) - //returns 1 on success, 0 on failure - int Triangulate_OPT(TPPLPoly *poly, list *triangles); - - //triangulates a polygons by firstly partitioning it into monotone polygons - //time complexity: O(n*log(n)), n is the number of vertices - //space complexity: O(n) - //params: - // poly : an input polygon to be triangulated - // vertices have to be in counter-clockwise order - // triangles : a list of triangles (result) - //returns 1 on success, 0 on failure - int Triangulate_MONO(TPPLPoly *poly, list *triangles); - - //triangulates a list of polygons by firstly partitioning them into monotone polygons - //time complexity: O(n*log(n)), n is the number of vertices - //space complexity: O(n) - //params: - // inpolys : a list of polygons to be triangulated (can contain holes) - // vertices of all non-hole polys have to be in counter-clockwise order - // vertices of all hole polys have to be in clockwise order - // triangles : a list of triangles (result) - //returns 1 on success, 0 on failure - int Triangulate_MONO(list *inpolys, list *triangles); - - //creates a monotone partition of a list of polygons that can contain holes - //time complexity: O(n*log(n)), n is the number of vertices - //space complexity: O(n) - //params: - // inpolys : a list of polygons to be triangulated (can contain holes) - // vertices of all non-hole polys have to be in counter-clockwise order - // vertices of all hole polys have to be in clockwise order - // monotonePolys : a list of monotone polygons (result) - //returns 1 on success, 0 on failure - int MonotonePartition(list *inpolys, list *monotonePolys); - - //partitions a polygon into convex polygons by using Hertel-Mehlhorn algorithm - //the algorithm gives at most four times the number of parts as the optimal algorithm - //however, in practice it works much better than that and often gives optimal partition - //uses triangulation obtained by ear clipping as intermediate result - //time complexity O(n^2), n is the number of vertices - //space complexity: O(n) - //params: - // poly : an input polygon to be partitioned - // vertices have to be in counter-clockwise order - // parts : resulting list of convex polygons - //returns 1 on success, 0 on failure - int ConvexPartition_HM(TPPLPoly *poly, list *parts); - - //partitions a list of polygons into convex parts by using Hertel-Mehlhorn algorithm - //the algorithm gives at most four times the number of parts as the optimal algorithm - //however, in practice it works much better than that and often gives optimal partition - //uses triangulation obtained by ear clipping as intermediate result - //time complexity O(n^2), n is the number of vertices - //space complexity: O(n) - //params: - // inpolys : an input list of polygons to be partitioned - // vertices of all non-hole polys have to be in counter-clockwise order - // vertices of all hole polys have to be in clockwise order - // parts : resulting list of convex polygons - //returns 1 on success, 0 on failure - int ConvexPartition_HM(list *inpolys, list *parts); - - //optimal convex partitioning (in terms of number of resulting convex polygons) - //using the Keil-Snoeyink algorithm - //M. Keil, J. Snoeyink, "On the time bound for convex decomposition of simple polygons", 1998 - //time complexity O(n^3), n is the number of vertices - //space complexity: O(n^3) - // poly : an input polygon to be partitioned - // vertices have to be in counter-clockwise order - // parts : resulting list of convex polygons - //returns 1 on success, 0 on failure - int ConvexPartition_OPT(TPPLPoly *poly, list *parts); -}; +//Copyright (C) 2011 by Ivan Fratric +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to 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 +using namespace std; + +typedef double tppl_float; + +#define TPPL_CCW 1 +#define TPPL_CW -1 + +//2D point structure +struct TPPLPoint { + tppl_float x; + tppl_float y; + + TPPLPoint operator + (const TPPLPoint& p) const { + TPPLPoint r; + r.x = x + p.x; + r.y = y + p.y; + return r; + } + + TPPLPoint operator - (const TPPLPoint& p) const { + TPPLPoint r; + r.x = x - p.x; + r.y = y - p.y; + return r; + } + + TPPLPoint operator * (const tppl_float f ) const { + TPPLPoint r; + r.x = x*f; + r.y = y*f; + return r; + } + + TPPLPoint operator / (const tppl_float f ) const { + TPPLPoint r; + r.x = x/f; + r.y = y/f; + return r; + } + + bool operator==(const TPPLPoint& p) const { + if((x == p.x)&&(y==p.y)) return true; + else return false; + } + + bool operator!=(const TPPLPoint& p) const { + if((x == p.x)&&(y==p.y)) return false; + else return true; + } +}; + +//Polygon implemented as an array of points with a 'hole' flag +class TPPLPoly { +protected: + + TPPLPoint *points; + long numpoints; + bool hole; + +public: + + //constructors/destructors + TPPLPoly(); + ~TPPLPoly(); + + TPPLPoly(const TPPLPoly &src); + TPPLPoly& operator=(const TPPLPoly &src); + + //getters and setters + long GetNumPoints() const { + return numpoints; + } + + bool IsHole() const { + return hole; + } + + void SetHole(bool hole) { + this->hole = hole; + } + + TPPLPoint &GetPoint(long i) { + return points[i]; + } + + TPPLPoint *GetPoints() { + return points; + } + + TPPLPoint& operator[] (int i) { + return points[i]; + } + + //clears the polygon points + void Clear(); + + //inits the polygon with numpoints vertices + void Init(long numpoints); + + //creates a triangle with points p1,p2,p3 + void Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3); + + //inverts the orfer of vertices + void Invert(); + + //returns the orientation of the polygon + //possible values: + // TPPL_CCW : polygon vertices are in counter-clockwise order + // TPPL_CW : polygon vertices are in clockwise order + // 0 : the polygon has no (measurable) area + int GetOrientation() const; + + //sets the polygon orientation + //orientation can be + // TPPL_CCW : sets vertices in counter-clockwise order + // TPPL_CW : sets vertices in clockwise order + void SetOrientation(int orientation); +}; + +class TPPLPartition { +protected: + struct PartitionVertex { + bool isActive; + bool isConvex; + bool isEar; + + TPPLPoint p; + tppl_float angle; + PartitionVertex *previous; + PartitionVertex *next; + }; + + struct MonotoneVertex { + TPPLPoint p; + long previous; + long next; + }; + + class VertexSorter{ + MonotoneVertex *vertices; + public: + VertexSorter(MonotoneVertex *v) : vertices(v) {} + bool operator() (long index1, long index2) const; + }; + + struct Diagonal { + long index1; + long index2; + }; + + //dynamic programming state for minimum-weight triangulation + struct DPState { + bool visible; + tppl_float weight; + long bestvertex; + }; + + //dynamic programming state for convex partitioning + struct DPState2 { + bool visible; + long weight; + list pairs; + }; + + //edge that intersects the scanline + struct ScanLineEdge { + long index; + TPPLPoint p1; + TPPLPoint p2; + + //determines if the edge is to the left of another edge + bool operator< (const ScanLineEdge & other) const; + + bool IsConvex(const TPPLPoint& p1, const TPPLPoint& p2, const TPPLPoint& p3) const; + }; + + //standard helper functions + bool IsConvex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3); + bool IsReflex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3); + bool IsInside(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3, TPPLPoint &p); + + bool InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p); + bool InCone(PartitionVertex *v, TPPLPoint &p); + + int Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TPPLPoint &p22); + + TPPLPoint Normalize(const TPPLPoint &p); + tppl_float Distance(const TPPLPoint &p1, const TPPLPoint &p2); + + //helper functions for Triangulate_EC + void UpdateVertexReflexity(PartitionVertex *v); + void UpdateVertex(PartitionVertex *v,PartitionVertex *vertices, long numvertices); + + //helper functions for ConvexPartition_OPT + void UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates); + void TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates); + void TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates); + + //helper functions for MonotonePartition + bool Below(TPPLPoint &p1, TPPLPoint &p2); + void AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2); + + //triangulates a monotone polygon, used in Triangulate_MONO + int TriangulateMonotone(TPPLPoly *inPoly, list *triangles); + +public: + + //simple heuristic procedure for removing holes from a list of polygons + //works by creating a diagonal from the rightmost hole vertex to some visible vertex + //time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices + //space complexity: O(n) + //params: + // inpolys : a list of polygons that can contain holes + // vertices of all non-hole polys have to be in counter-clockwise order + // vertices of all hole polys have to be in clockwise order + // outpolys : a list of polygons without holes + //returns 1 on success, 0 on failure + int RemoveHoles(list *inpolys, list *outpolys); + + //triangulates a polygon by ear clipping + //time complexity O(n^2), n is the number of vertices + //space complexity: O(n) + //params: + // poly : an input polygon to be triangulated + // vertices have to be in counter-clockwise order + // triangles : a list of triangles (result) + //returns 1 on success, 0 on failure + int Triangulate_EC(TPPLPoly *poly, list *triangles); + + //triangulates a list of polygons that may contain holes by ear clipping algorithm + //first calls RemoveHoles to get rid of the holes, and then Triangulate_EC for each resulting polygon + //time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices + //space complexity: O(n) + //params: + // inpolys : a list of polygons to be triangulated (can contain holes) + // vertices of all non-hole polys have to be in counter-clockwise order + // vertices of all hole polys have to be in clockwise order + // triangles : a list of triangles (result) + //returns 1 on success, 0 on failure + int Triangulate_EC(list *inpolys, list *triangles); + + //creates an optimal polygon triangulation in terms of minimal edge length + //time complexity: O(n^3), n is the number of vertices + //space complexity: O(n^2) + //params: + // poly : an input polygon to be triangulated + // vertices have to be in counter-clockwise order + // triangles : a list of triangles (result) + //returns 1 on success, 0 on failure + int Triangulate_OPT(TPPLPoly *poly, list *triangles); + + //triangulates a polygons by firstly partitioning it into monotone polygons + //time complexity: O(n*log(n)), n is the number of vertices + //space complexity: O(n) + //params: + // poly : an input polygon to be triangulated + // vertices have to be in counter-clockwise order + // triangles : a list of triangles (result) + //returns 1 on success, 0 on failure + int Triangulate_MONO(TPPLPoly *poly, list *triangles); + + //triangulates a list of polygons by firstly partitioning them into monotone polygons + //time complexity: O(n*log(n)), n is the number of vertices + //space complexity: O(n) + //params: + // inpolys : a list of polygons to be triangulated (can contain holes) + // vertices of all non-hole polys have to be in counter-clockwise order + // vertices of all hole polys have to be in clockwise order + // triangles : a list of triangles (result) + //returns 1 on success, 0 on failure + int Triangulate_MONO(list *inpolys, list *triangles); + + //creates a monotone partition of a list of polygons that can contain holes + //time complexity: O(n*log(n)), n is the number of vertices + //space complexity: O(n) + //params: + // inpolys : a list of polygons to be triangulated (can contain holes) + // vertices of all non-hole polys have to be in counter-clockwise order + // vertices of all hole polys have to be in clockwise order + // monotonePolys : a list of monotone polygons (result) + //returns 1 on success, 0 on failure + int MonotonePartition(list *inpolys, list *monotonePolys); + + //partitions a polygon into convex polygons by using Hertel-Mehlhorn algorithm + //the algorithm gives at most four times the number of parts as the optimal algorithm + //however, in practice it works much better than that and often gives optimal partition + //uses triangulation obtained by ear clipping as intermediate result + //time complexity O(n^2), n is the number of vertices + //space complexity: O(n) + //params: + // poly : an input polygon to be partitioned + // vertices have to be in counter-clockwise order + // parts : resulting list of convex polygons + //returns 1 on success, 0 on failure + int ConvexPartition_HM(TPPLPoly *poly, list *parts); + + //partitions a list of polygons into convex parts by using Hertel-Mehlhorn algorithm + //the algorithm gives at most four times the number of parts as the optimal algorithm + //however, in practice it works much better than that and often gives optimal partition + //uses triangulation obtained by ear clipping as intermediate result + //time complexity O(n^2), n is the number of vertices + //space complexity: O(n) + //params: + // inpolys : an input list of polygons to be partitioned + // vertices of all non-hole polys have to be in counter-clockwise order + // vertices of all hole polys have to be in clockwise order + // parts : resulting list of convex polygons + //returns 1 on success, 0 on failure + int ConvexPartition_HM(list *inpolys, list *parts); + + //optimal convex partitioning (in terms of number of resulting convex polygons) + //using the Keil-Snoeyink algorithm + //M. Keil, J. Snoeyink, "On the time bound for convex decomposition of simple polygons", 1998 + //time complexity O(n^3), n is the number of vertices + //space complexity: O(n^3) + // poly : an input polygon to be partitioned + // vertices have to be in counter-clockwise order + // parts : resulting list of convex polygons + //returns 1 on success, 0 on failure + int ConvexPartition_OPT(TPPLPoly *poly, list *parts); +}; diff --git a/xs/src/qhull/Announce.txt b/src/qhull/Announce.txt similarity index 100% rename from xs/src/qhull/Announce.txt rename to src/qhull/Announce.txt diff --git a/xs/src/qhull/CMakeLists.txt b/src/qhull/CMakeLists.txt similarity index 98% rename from xs/src/qhull/CMakeLists.txt rename to src/qhull/CMakeLists.txt index d798b018fa..e25da35e3e 100644 --- a/xs/src/qhull/CMakeLists.txt +++ b/src/qhull/CMakeLists.txt @@ -80,11 +80,9 @@ set(libqhull_SOURCES src/libqhull_r/mem_r.c src/libqhull_r/random_r.c src/libqhull_r/usermem_r.c - src/libqhull_r/userprintf_r.c src/libqhull_r/io_r.c src/libqhull_r/user_r.c src/libqhull_r/rboxlib_r.c - src/libqhull_r/userprintf_rbox_r.c # C++ interface to reentrant Qhull SOURCES: src/libqhullcpp/Coordinates.cpp diff --git a/xs/src/qhull/COPYING.txt b/src/qhull/COPYING.txt similarity index 100% rename from xs/src/qhull/COPYING.txt rename to src/qhull/COPYING.txt diff --git a/xs/src/qhull/README.txt b/src/qhull/README.txt similarity index 100% rename from xs/src/qhull/README.txt rename to src/qhull/README.txt diff --git a/xs/src/qhull/REGISTER.txt b/src/qhull/REGISTER.txt similarity index 100% rename from xs/src/qhull/REGISTER.txt rename to src/qhull/REGISTER.txt diff --git a/xs/src/qhull/html/index.htm b/src/qhull/html/index.htm similarity index 100% rename from xs/src/qhull/html/index.htm rename to src/qhull/html/index.htm diff --git a/xs/src/qhull/html/normal_voronoi_knauss_oesterle.jpg b/src/qhull/html/normal_voronoi_knauss_oesterle.jpg similarity index 100% rename from xs/src/qhull/html/normal_voronoi_knauss_oesterle.jpg rename to src/qhull/html/normal_voronoi_knauss_oesterle.jpg diff --git a/xs/src/qhull/html/qconvex.htm b/src/qhull/html/qconvex.htm similarity index 100% rename from xs/src/qhull/html/qconvex.htm rename to src/qhull/html/qconvex.htm diff --git a/xs/src/qhull/html/qdelau_f.htm b/src/qhull/html/qdelau_f.htm similarity index 100% rename from xs/src/qhull/html/qdelau_f.htm rename to src/qhull/html/qdelau_f.htm diff --git a/xs/src/qhull/html/qdelaun.htm b/src/qhull/html/qdelaun.htm similarity index 100% rename from xs/src/qhull/html/qdelaun.htm rename to src/qhull/html/qdelaun.htm diff --git a/xs/src/qhull/html/qh--4d.gif b/src/qhull/html/qh--4d.gif similarity index 100% rename from xs/src/qhull/html/qh--4d.gif rename to src/qhull/html/qh--4d.gif diff --git a/xs/src/qhull/html/qh--cone.gif b/src/qhull/html/qh--cone.gif similarity index 100% rename from xs/src/qhull/html/qh--cone.gif rename to src/qhull/html/qh--cone.gif diff --git a/xs/src/qhull/html/qh--dt.gif b/src/qhull/html/qh--dt.gif similarity index 100% rename from xs/src/qhull/html/qh--dt.gif rename to src/qhull/html/qh--dt.gif diff --git a/xs/src/qhull/html/qh--geom.gif b/src/qhull/html/qh--geom.gif similarity index 100% rename from xs/src/qhull/html/qh--geom.gif rename to src/qhull/html/qh--geom.gif diff --git a/xs/src/qhull/html/qh--half.gif b/src/qhull/html/qh--half.gif similarity index 100% rename from xs/src/qhull/html/qh--half.gif rename to src/qhull/html/qh--half.gif diff --git a/xs/src/qhull/html/qh--rand.gif b/src/qhull/html/qh--rand.gif similarity index 100% rename from xs/src/qhull/html/qh--rand.gif rename to src/qhull/html/qh--rand.gif diff --git a/xs/src/qhull/html/qh-code.htm b/src/qhull/html/qh-code.htm similarity index 100% rename from xs/src/qhull/html/qh-code.htm rename to src/qhull/html/qh-code.htm diff --git a/xs/src/qhull/html/qh-eg.htm b/src/qhull/html/qh-eg.htm similarity index 100% rename from xs/src/qhull/html/qh-eg.htm rename to src/qhull/html/qh-eg.htm diff --git a/xs/src/qhull/html/qh-faq.htm b/src/qhull/html/qh-faq.htm similarity index 100% rename from xs/src/qhull/html/qh-faq.htm rename to src/qhull/html/qh-faq.htm diff --git a/xs/src/qhull/html/qh-get.htm b/src/qhull/html/qh-get.htm similarity index 100% rename from xs/src/qhull/html/qh-get.htm rename to src/qhull/html/qh-get.htm diff --git a/xs/src/qhull/html/qh-impre.htm b/src/qhull/html/qh-impre.htm similarity index 100% rename from xs/src/qhull/html/qh-impre.htm rename to src/qhull/html/qh-impre.htm diff --git a/xs/src/qhull/html/qh-optc.htm b/src/qhull/html/qh-optc.htm similarity index 100% rename from xs/src/qhull/html/qh-optc.htm rename to src/qhull/html/qh-optc.htm diff --git a/xs/src/qhull/html/qh-optf.htm b/src/qhull/html/qh-optf.htm similarity index 100% rename from xs/src/qhull/html/qh-optf.htm rename to src/qhull/html/qh-optf.htm diff --git a/xs/src/qhull/html/qh-optg.htm b/src/qhull/html/qh-optg.htm similarity index 100% rename from xs/src/qhull/html/qh-optg.htm rename to src/qhull/html/qh-optg.htm diff --git a/xs/src/qhull/html/qh-opto.htm b/src/qhull/html/qh-opto.htm similarity index 100% rename from xs/src/qhull/html/qh-opto.htm rename to src/qhull/html/qh-opto.htm diff --git a/xs/src/qhull/html/qh-optp.htm b/src/qhull/html/qh-optp.htm similarity index 100% rename from xs/src/qhull/html/qh-optp.htm rename to src/qhull/html/qh-optp.htm diff --git a/xs/src/qhull/html/qh-optq.htm b/src/qhull/html/qh-optq.htm similarity index 100% rename from xs/src/qhull/html/qh-optq.htm rename to src/qhull/html/qh-optq.htm diff --git a/xs/src/qhull/html/qh-optt.htm b/src/qhull/html/qh-optt.htm similarity index 100% rename from xs/src/qhull/html/qh-optt.htm rename to src/qhull/html/qh-optt.htm diff --git a/xs/src/qhull/html/qh-quick.htm b/src/qhull/html/qh-quick.htm similarity index 100% rename from xs/src/qhull/html/qh-quick.htm rename to src/qhull/html/qh-quick.htm diff --git a/xs/src/qhull/html/qhalf.htm b/src/qhull/html/qhalf.htm similarity index 100% rename from xs/src/qhull/html/qhalf.htm rename to src/qhull/html/qhalf.htm diff --git a/xs/src/qhull/html/qhull-cpp.xml b/src/qhull/html/qhull-cpp.xml similarity index 100% rename from xs/src/qhull/html/qhull-cpp.xml rename to src/qhull/html/qhull-cpp.xml diff --git a/xs/src/qhull/html/qhull.htm b/src/qhull/html/qhull.htm similarity index 100% rename from xs/src/qhull/html/qhull.htm rename to src/qhull/html/qhull.htm diff --git a/xs/src/qhull/html/qhull.man b/src/qhull/html/qhull.man similarity index 100% rename from xs/src/qhull/html/qhull.man rename to src/qhull/html/qhull.man diff --git a/xs/src/qhull/html/qhull.txt b/src/qhull/html/qhull.txt similarity index 100% rename from xs/src/qhull/html/qhull.txt rename to src/qhull/html/qhull.txt diff --git a/xs/src/qhull/html/qvoron_f.htm b/src/qhull/html/qvoron_f.htm similarity index 100% rename from xs/src/qhull/html/qvoron_f.htm rename to src/qhull/html/qvoron_f.htm diff --git a/xs/src/qhull/html/qvoronoi.htm b/src/qhull/html/qvoronoi.htm similarity index 100% rename from xs/src/qhull/html/qvoronoi.htm rename to src/qhull/html/qvoronoi.htm diff --git a/xs/src/qhull/html/rbox.htm b/src/qhull/html/rbox.htm similarity index 100% rename from xs/src/qhull/html/rbox.htm rename to src/qhull/html/rbox.htm diff --git a/xs/src/qhull/html/rbox.man b/src/qhull/html/rbox.man similarity index 100% rename from xs/src/qhull/html/rbox.man rename to src/qhull/html/rbox.man diff --git a/xs/src/qhull/html/rbox.txt b/src/qhull/html/rbox.txt similarity index 100% rename from xs/src/qhull/html/rbox.txt rename to src/qhull/html/rbox.txt diff --git a/xs/src/qhull/index.htm b/src/qhull/index.htm similarity index 100% rename from xs/src/qhull/index.htm rename to src/qhull/index.htm diff --git a/xs/src/qhull/origCMakeLists.txt b/src/qhull/origCMakeLists.txt similarity index 100% rename from xs/src/qhull/origCMakeLists.txt rename to src/qhull/origCMakeLists.txt diff --git a/xs/src/qhull/src/Changes.txt b/src/qhull/src/Changes.txt similarity index 100% rename from xs/src/qhull/src/Changes.txt rename to src/qhull/src/Changes.txt diff --git a/xs/src/qhull/src/libqhull/DEPRECATED.txt b/src/qhull/src/libqhull/DEPRECATED.txt similarity index 100% rename from xs/src/qhull/src/libqhull/DEPRECATED.txt rename to src/qhull/src/libqhull/DEPRECATED.txt diff --git a/xs/src/qhull/src/libqhull/Makefile b/src/qhull/src/libqhull/Makefile similarity index 100% rename from xs/src/qhull/src/libqhull/Makefile rename to src/qhull/src/libqhull/Makefile diff --git a/xs/src/qhull/src/libqhull/Mborland b/src/qhull/src/libqhull/Mborland similarity index 100% rename from xs/src/qhull/src/libqhull/Mborland rename to src/qhull/src/libqhull/Mborland diff --git a/xs/src/qhull/src/libqhull/geom.c b/src/qhull/src/libqhull/geom.c similarity index 100% rename from xs/src/qhull/src/libqhull/geom.c rename to src/qhull/src/libqhull/geom.c diff --git a/xs/src/qhull/src/libqhull/geom.h b/src/qhull/src/libqhull/geom.h similarity index 100% rename from xs/src/qhull/src/libqhull/geom.h rename to src/qhull/src/libqhull/geom.h diff --git a/xs/src/qhull/src/libqhull/geom2.c b/src/qhull/src/libqhull/geom2.c similarity index 100% rename from xs/src/qhull/src/libqhull/geom2.c rename to src/qhull/src/libqhull/geom2.c diff --git a/xs/src/qhull/src/libqhull/global.c b/src/qhull/src/libqhull/global.c similarity index 100% rename from xs/src/qhull/src/libqhull/global.c rename to src/qhull/src/libqhull/global.c diff --git a/xs/src/qhull/src/libqhull/index.htm b/src/qhull/src/libqhull/index.htm similarity index 100% rename from xs/src/qhull/src/libqhull/index.htm rename to src/qhull/src/libqhull/index.htm diff --git a/xs/src/qhull/src/libqhull/io.c b/src/qhull/src/libqhull/io.c similarity index 100% rename from xs/src/qhull/src/libqhull/io.c rename to src/qhull/src/libqhull/io.c diff --git a/xs/src/qhull/src/libqhull/io.h b/src/qhull/src/libqhull/io.h similarity index 100% rename from xs/src/qhull/src/libqhull/io.h rename to src/qhull/src/libqhull/io.h diff --git a/xs/src/qhull/src/libqhull/libqhull.c b/src/qhull/src/libqhull/libqhull.c similarity index 100% rename from xs/src/qhull/src/libqhull/libqhull.c rename to src/qhull/src/libqhull/libqhull.c diff --git a/xs/src/qhull/src/libqhull/libqhull.h b/src/qhull/src/libqhull/libqhull.h similarity index 100% rename from xs/src/qhull/src/libqhull/libqhull.h rename to src/qhull/src/libqhull/libqhull.h diff --git a/xs/src/qhull/src/libqhull/libqhull.pro b/src/qhull/src/libqhull/libqhull.pro similarity index 100% rename from xs/src/qhull/src/libqhull/libqhull.pro rename to src/qhull/src/libqhull/libqhull.pro diff --git a/xs/src/qhull/src/libqhull/mem.c b/src/qhull/src/libqhull/mem.c similarity index 100% rename from xs/src/qhull/src/libqhull/mem.c rename to src/qhull/src/libqhull/mem.c diff --git a/xs/src/qhull/src/libqhull/mem.h b/src/qhull/src/libqhull/mem.h similarity index 100% rename from xs/src/qhull/src/libqhull/mem.h rename to src/qhull/src/libqhull/mem.h diff --git a/xs/src/qhull/src/libqhull/merge.c b/src/qhull/src/libqhull/merge.c similarity index 100% rename from xs/src/qhull/src/libqhull/merge.c rename to src/qhull/src/libqhull/merge.c diff --git a/xs/src/qhull/src/libqhull/merge.h b/src/qhull/src/libqhull/merge.h similarity index 100% rename from xs/src/qhull/src/libqhull/merge.h rename to src/qhull/src/libqhull/merge.h diff --git a/xs/src/qhull/src/libqhull/poly.c b/src/qhull/src/libqhull/poly.c similarity index 100% rename from xs/src/qhull/src/libqhull/poly.c rename to src/qhull/src/libqhull/poly.c diff --git a/xs/src/qhull/src/libqhull/poly.h b/src/qhull/src/libqhull/poly.h similarity index 100% rename from xs/src/qhull/src/libqhull/poly.h rename to src/qhull/src/libqhull/poly.h diff --git a/xs/src/qhull/src/libqhull/poly2.c b/src/qhull/src/libqhull/poly2.c similarity index 100% rename from xs/src/qhull/src/libqhull/poly2.c rename to src/qhull/src/libqhull/poly2.c diff --git a/xs/src/qhull/src/libqhull/qh-geom.htm b/src/qhull/src/libqhull/qh-geom.htm similarity index 100% rename from xs/src/qhull/src/libqhull/qh-geom.htm rename to src/qhull/src/libqhull/qh-geom.htm diff --git a/xs/src/qhull/src/libqhull/qh-globa.htm b/src/qhull/src/libqhull/qh-globa.htm similarity index 100% rename from xs/src/qhull/src/libqhull/qh-globa.htm rename to src/qhull/src/libqhull/qh-globa.htm diff --git a/xs/src/qhull/src/libqhull/qh-io.htm b/src/qhull/src/libqhull/qh-io.htm similarity index 100% rename from xs/src/qhull/src/libqhull/qh-io.htm rename to src/qhull/src/libqhull/qh-io.htm diff --git a/xs/src/qhull/src/libqhull/qh-mem.htm b/src/qhull/src/libqhull/qh-mem.htm similarity index 100% rename from xs/src/qhull/src/libqhull/qh-mem.htm rename to src/qhull/src/libqhull/qh-mem.htm diff --git a/xs/src/qhull/src/libqhull/qh-merge.htm b/src/qhull/src/libqhull/qh-merge.htm similarity index 100% rename from xs/src/qhull/src/libqhull/qh-merge.htm rename to src/qhull/src/libqhull/qh-merge.htm diff --git a/xs/src/qhull/src/libqhull/qh-poly.htm b/src/qhull/src/libqhull/qh-poly.htm similarity index 100% rename from xs/src/qhull/src/libqhull/qh-poly.htm rename to src/qhull/src/libqhull/qh-poly.htm diff --git a/xs/src/qhull/src/libqhull/qh-qhull.htm b/src/qhull/src/libqhull/qh-qhull.htm similarity index 100% rename from xs/src/qhull/src/libqhull/qh-qhull.htm rename to src/qhull/src/libqhull/qh-qhull.htm diff --git a/xs/src/qhull/src/libqhull/qh-set.htm b/src/qhull/src/libqhull/qh-set.htm similarity index 100% rename from xs/src/qhull/src/libqhull/qh-set.htm rename to src/qhull/src/libqhull/qh-set.htm diff --git a/xs/src/qhull/src/libqhull/qh-stat.htm b/src/qhull/src/libqhull/qh-stat.htm similarity index 100% rename from xs/src/qhull/src/libqhull/qh-stat.htm rename to src/qhull/src/libqhull/qh-stat.htm diff --git a/xs/src/qhull/src/libqhull/qh-user.htm b/src/qhull/src/libqhull/qh-user.htm similarity index 100% rename from xs/src/qhull/src/libqhull/qh-user.htm rename to src/qhull/src/libqhull/qh-user.htm diff --git a/xs/src/qhull/src/libqhull/qhull-exports.def b/src/qhull/src/libqhull/qhull-exports.def similarity index 100% rename from xs/src/qhull/src/libqhull/qhull-exports.def rename to src/qhull/src/libqhull/qhull-exports.def diff --git a/xs/src/qhull/src/libqhull/qhull_a.h b/src/qhull/src/libqhull/qhull_a.h similarity index 100% rename from xs/src/qhull/src/libqhull/qhull_a.h rename to src/qhull/src/libqhull/qhull_a.h diff --git a/xs/src/qhull/src/libqhull/qhull_p-exports.def b/src/qhull/src/libqhull/qhull_p-exports.def similarity index 100% rename from xs/src/qhull/src/libqhull/qhull_p-exports.def rename to src/qhull/src/libqhull/qhull_p-exports.def diff --git a/xs/src/qhull/src/libqhull/qset.c b/src/qhull/src/libqhull/qset.c similarity index 100% rename from xs/src/qhull/src/libqhull/qset.c rename to src/qhull/src/libqhull/qset.c diff --git a/xs/src/qhull/src/libqhull/qset.h b/src/qhull/src/libqhull/qset.h similarity index 100% rename from xs/src/qhull/src/libqhull/qset.h rename to src/qhull/src/libqhull/qset.h diff --git a/xs/src/qhull/src/libqhull/random.c b/src/qhull/src/libqhull/random.c similarity index 100% rename from xs/src/qhull/src/libqhull/random.c rename to src/qhull/src/libqhull/random.c diff --git a/xs/src/qhull/src/libqhull/random.h b/src/qhull/src/libqhull/random.h similarity index 100% rename from xs/src/qhull/src/libqhull/random.h rename to src/qhull/src/libqhull/random.h diff --git a/xs/src/qhull/src/libqhull/rboxlib.c b/src/qhull/src/libqhull/rboxlib.c similarity index 100% rename from xs/src/qhull/src/libqhull/rboxlib.c rename to src/qhull/src/libqhull/rboxlib.c diff --git a/xs/src/qhull/src/libqhull/stat.c b/src/qhull/src/libqhull/stat.c similarity index 100% rename from xs/src/qhull/src/libqhull/stat.c rename to src/qhull/src/libqhull/stat.c diff --git a/xs/src/qhull/src/libqhull/stat.h b/src/qhull/src/libqhull/stat.h similarity index 100% rename from xs/src/qhull/src/libqhull/stat.h rename to src/qhull/src/libqhull/stat.h diff --git a/xs/src/qhull/src/libqhull/user.c b/src/qhull/src/libqhull/user.c similarity index 100% rename from xs/src/qhull/src/libqhull/user.c rename to src/qhull/src/libqhull/user.c diff --git a/xs/src/qhull/src/libqhull/user.h b/src/qhull/src/libqhull/user.h similarity index 100% rename from xs/src/qhull/src/libqhull/user.h rename to src/qhull/src/libqhull/user.h diff --git a/xs/src/qhull/src/libqhull/usermem.c b/src/qhull/src/libqhull/usermem.c similarity index 100% rename from xs/src/qhull/src/libqhull/usermem.c rename to src/qhull/src/libqhull/usermem.c diff --git a/xs/src/qhull/src/libqhull/userprintf.c b/src/qhull/src/libqhull/userprintf.c similarity index 100% rename from xs/src/qhull/src/libqhull/userprintf.c rename to src/qhull/src/libqhull/userprintf.c diff --git a/xs/src/qhull/src/libqhull/userprintf_rbox.c b/src/qhull/src/libqhull/userprintf_rbox.c similarity index 100% rename from xs/src/qhull/src/libqhull/userprintf_rbox.c rename to src/qhull/src/libqhull/userprintf_rbox.c diff --git a/xs/src/qhull/src/libqhull_r/Makefile b/src/qhull/src/libqhull_r/Makefile similarity index 100% rename from xs/src/qhull/src/libqhull_r/Makefile rename to src/qhull/src/libqhull_r/Makefile diff --git a/xs/src/qhull/src/libqhull_r/geom2_r.c b/src/qhull/src/libqhull_r/geom2_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/geom2_r.c rename to src/qhull/src/libqhull_r/geom2_r.c diff --git a/xs/src/qhull/src/libqhull_r/geom_r.c b/src/qhull/src/libqhull_r/geom_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/geom_r.c rename to src/qhull/src/libqhull_r/geom_r.c diff --git a/xs/src/qhull/src/libqhull_r/geom_r.h b/src/qhull/src/libqhull_r/geom_r.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/geom_r.h rename to src/qhull/src/libqhull_r/geom_r.h diff --git a/xs/src/qhull/src/libqhull_r/global_r.c b/src/qhull/src/libqhull_r/global_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/global_r.c rename to src/qhull/src/libqhull_r/global_r.c diff --git a/xs/src/qhull/src/libqhull_r/index.htm b/src/qhull/src/libqhull_r/index.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/index.htm rename to src/qhull/src/libqhull_r/index.htm diff --git a/xs/src/qhull/src/libqhull_r/io_r.c b/src/qhull/src/libqhull_r/io_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/io_r.c rename to src/qhull/src/libqhull_r/io_r.c diff --git a/xs/src/qhull/src/libqhull_r/io_r.h b/src/qhull/src/libqhull_r/io_r.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/io_r.h rename to src/qhull/src/libqhull_r/io_r.h diff --git a/xs/src/qhull/src/libqhull_r/libqhull_r.c b/src/qhull/src/libqhull_r/libqhull_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/libqhull_r.c rename to src/qhull/src/libqhull_r/libqhull_r.c diff --git a/xs/src/qhull/src/libqhull_r/libqhull_r.h b/src/qhull/src/libqhull_r/libqhull_r.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/libqhull_r.h rename to src/qhull/src/libqhull_r/libqhull_r.h diff --git a/xs/src/qhull/src/libqhull_r/libqhull_r.pro b/src/qhull/src/libqhull_r/libqhull_r.pro similarity index 100% rename from xs/src/qhull/src/libqhull_r/libqhull_r.pro rename to src/qhull/src/libqhull_r/libqhull_r.pro diff --git a/xs/src/qhull/src/libqhull_r/mem_r.c b/src/qhull/src/libqhull_r/mem_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/mem_r.c rename to src/qhull/src/libqhull_r/mem_r.c diff --git a/xs/src/qhull/src/libqhull_r/mem_r.h b/src/qhull/src/libqhull_r/mem_r.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/mem_r.h rename to src/qhull/src/libqhull_r/mem_r.h diff --git a/xs/src/qhull/src/libqhull_r/merge_r.c b/src/qhull/src/libqhull_r/merge_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/merge_r.c rename to src/qhull/src/libqhull_r/merge_r.c diff --git a/xs/src/qhull/src/libqhull_r/merge_r.h b/src/qhull/src/libqhull_r/merge_r.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/merge_r.h rename to src/qhull/src/libqhull_r/merge_r.h diff --git a/xs/src/qhull/src/libqhull_r/poly2_r.c b/src/qhull/src/libqhull_r/poly2_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/poly2_r.c rename to src/qhull/src/libqhull_r/poly2_r.c diff --git a/xs/src/qhull/src/libqhull_r/poly_r.c b/src/qhull/src/libqhull_r/poly_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/poly_r.c rename to src/qhull/src/libqhull_r/poly_r.c diff --git a/xs/src/qhull/src/libqhull_r/poly_r.h b/src/qhull/src/libqhull_r/poly_r.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/poly_r.h rename to src/qhull/src/libqhull_r/poly_r.h diff --git a/xs/src/qhull/src/libqhull_r/qh-geom_r.htm b/src/qhull/src/libqhull_r/qh-geom_r.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/qh-geom_r.htm rename to src/qhull/src/libqhull_r/qh-geom_r.htm diff --git a/xs/src/qhull/src/libqhull_r/qh-globa_r.htm b/src/qhull/src/libqhull_r/qh-globa_r.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/qh-globa_r.htm rename to src/qhull/src/libqhull_r/qh-globa_r.htm diff --git a/xs/src/qhull/src/libqhull_r/qh-io_r.htm b/src/qhull/src/libqhull_r/qh-io_r.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/qh-io_r.htm rename to src/qhull/src/libqhull_r/qh-io_r.htm diff --git a/xs/src/qhull/src/libqhull_r/qh-mem_r.htm b/src/qhull/src/libqhull_r/qh-mem_r.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/qh-mem_r.htm rename to src/qhull/src/libqhull_r/qh-mem_r.htm diff --git a/xs/src/qhull/src/libqhull_r/qh-merge_r.htm b/src/qhull/src/libqhull_r/qh-merge_r.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/qh-merge_r.htm rename to src/qhull/src/libqhull_r/qh-merge_r.htm diff --git a/xs/src/qhull/src/libqhull_r/qh-poly_r.htm b/src/qhull/src/libqhull_r/qh-poly_r.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/qh-poly_r.htm rename to src/qhull/src/libqhull_r/qh-poly_r.htm diff --git a/xs/src/qhull/src/libqhull_r/qh-qhull_r.htm b/src/qhull/src/libqhull_r/qh-qhull_r.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/qh-qhull_r.htm rename to src/qhull/src/libqhull_r/qh-qhull_r.htm diff --git a/xs/src/qhull/src/libqhull_r/qh-set_r.htm b/src/qhull/src/libqhull_r/qh-set_r.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/qh-set_r.htm rename to src/qhull/src/libqhull_r/qh-set_r.htm diff --git a/xs/src/qhull/src/libqhull_r/qh-stat_r.htm b/src/qhull/src/libqhull_r/qh-stat_r.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/qh-stat_r.htm rename to src/qhull/src/libqhull_r/qh-stat_r.htm diff --git a/xs/src/qhull/src/libqhull_r/qh-user_r.htm b/src/qhull/src/libqhull_r/qh-user_r.htm similarity index 100% rename from xs/src/qhull/src/libqhull_r/qh-user_r.htm rename to src/qhull/src/libqhull_r/qh-user_r.htm diff --git a/xs/src/qhull/src/libqhull_r/qhull_r-exports.def b/src/qhull/src/libqhull_r/qhull_r-exports.def similarity index 100% rename from xs/src/qhull/src/libqhull_r/qhull_r-exports.def rename to src/qhull/src/libqhull_r/qhull_r-exports.def diff --git a/xs/src/qhull/src/libqhull_r/qhull_ra.h b/src/qhull/src/libqhull_r/qhull_ra.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/qhull_ra.h rename to src/qhull/src/libqhull_r/qhull_ra.h diff --git a/xs/src/qhull/src/libqhull_r/qset_r.c b/src/qhull/src/libqhull_r/qset_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/qset_r.c rename to src/qhull/src/libqhull_r/qset_r.c diff --git a/xs/src/qhull/src/libqhull_r/qset_r.h b/src/qhull/src/libqhull_r/qset_r.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/qset_r.h rename to src/qhull/src/libqhull_r/qset_r.h diff --git a/xs/src/qhull/src/libqhull_r/random_r.c b/src/qhull/src/libqhull_r/random_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/random_r.c rename to src/qhull/src/libqhull_r/random_r.c diff --git a/xs/src/qhull/src/libqhull_r/random_r.h b/src/qhull/src/libqhull_r/random_r.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/random_r.h rename to src/qhull/src/libqhull_r/random_r.h diff --git a/xs/src/qhull/src/libqhull_r/rboxlib_r.c b/src/qhull/src/libqhull_r/rboxlib_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/rboxlib_r.c rename to src/qhull/src/libqhull_r/rboxlib_r.c diff --git a/xs/src/qhull/src/libqhull_r/stat_r.c b/src/qhull/src/libqhull_r/stat_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/stat_r.c rename to src/qhull/src/libqhull_r/stat_r.c diff --git a/xs/src/qhull/src/libqhull_r/stat_r.h b/src/qhull/src/libqhull_r/stat_r.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/stat_r.h rename to src/qhull/src/libqhull_r/stat_r.h diff --git a/xs/src/qhull/src/libqhull_r/user_r.c b/src/qhull/src/libqhull_r/user_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/user_r.c rename to src/qhull/src/libqhull_r/user_r.c diff --git a/xs/src/qhull/src/libqhull_r/user_r.h b/src/qhull/src/libqhull_r/user_r.h similarity index 100% rename from xs/src/qhull/src/libqhull_r/user_r.h rename to src/qhull/src/libqhull_r/user_r.h diff --git a/xs/src/qhull/src/libqhull_r/usermem_r.c b/src/qhull/src/libqhull_r/usermem_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/usermem_r.c rename to src/qhull/src/libqhull_r/usermem_r.c diff --git a/xs/src/qhull/src/libqhull_r/userprintf_r.c b/src/qhull/src/libqhull_r/userprintf_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/userprintf_r.c rename to src/qhull/src/libqhull_r/userprintf_r.c diff --git a/xs/src/qhull/src/libqhull_r/userprintf_rbox_r.c b/src/qhull/src/libqhull_r/userprintf_rbox_r.c similarity index 100% rename from xs/src/qhull/src/libqhull_r/userprintf_rbox_r.c rename to src/qhull/src/libqhull_r/userprintf_rbox_r.c diff --git a/xs/src/qhull/src/libqhullcpp/Coordinates.cpp b/src/qhull/src/libqhullcpp/Coordinates.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/Coordinates.cpp rename to src/qhull/src/libqhullcpp/Coordinates.cpp diff --git a/xs/src/qhull/src/libqhullcpp/Coordinates.h b/src/qhull/src/libqhullcpp/Coordinates.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/Coordinates.h rename to src/qhull/src/libqhullcpp/Coordinates.h diff --git a/xs/src/qhull/src/libqhullcpp/PointCoordinates.cpp b/src/qhull/src/libqhullcpp/PointCoordinates.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/PointCoordinates.cpp rename to src/qhull/src/libqhullcpp/PointCoordinates.cpp diff --git a/xs/src/qhull/src/libqhullcpp/PointCoordinates.h b/src/qhull/src/libqhullcpp/PointCoordinates.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/PointCoordinates.h rename to src/qhull/src/libqhullcpp/PointCoordinates.h diff --git a/xs/src/qhull/src/libqhullcpp/Qhull.cpp b/src/qhull/src/libqhullcpp/Qhull.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/Qhull.cpp rename to src/qhull/src/libqhullcpp/Qhull.cpp diff --git a/xs/src/qhull/src/libqhullcpp/Qhull.h b/src/qhull/src/libqhullcpp/Qhull.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/Qhull.h rename to src/qhull/src/libqhullcpp/Qhull.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullError.h b/src/qhull/src/libqhullcpp/QhullError.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullError.h rename to src/qhull/src/libqhullcpp/QhullError.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacet.cpp b/src/qhull/src/libqhullcpp/QhullFacet.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullFacet.cpp rename to src/qhull/src/libqhullcpp/QhullFacet.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacet.h b/src/qhull/src/libqhullcpp/QhullFacet.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullFacet.h rename to src/qhull/src/libqhullcpp/QhullFacet.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetList.cpp b/src/qhull/src/libqhullcpp/QhullFacetList.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullFacetList.cpp rename to src/qhull/src/libqhullcpp/QhullFacetList.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetList.h b/src/qhull/src/libqhullcpp/QhullFacetList.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullFacetList.h rename to src/qhull/src/libqhullcpp/QhullFacetList.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetSet.cpp b/src/qhull/src/libqhullcpp/QhullFacetSet.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullFacetSet.cpp rename to src/qhull/src/libqhullcpp/QhullFacetSet.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetSet.h b/src/qhull/src/libqhullcpp/QhullFacetSet.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullFacetSet.h rename to src/qhull/src/libqhullcpp/QhullFacetSet.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullHyperplane.cpp b/src/qhull/src/libqhullcpp/QhullHyperplane.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullHyperplane.cpp rename to src/qhull/src/libqhullcpp/QhullHyperplane.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullHyperplane.h b/src/qhull/src/libqhullcpp/QhullHyperplane.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullHyperplane.h rename to src/qhull/src/libqhullcpp/QhullHyperplane.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullIterator.h b/src/qhull/src/libqhullcpp/QhullIterator.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullIterator.h rename to src/qhull/src/libqhullcpp/QhullIterator.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullLinkedList.h b/src/qhull/src/libqhullcpp/QhullLinkedList.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullLinkedList.h rename to src/qhull/src/libqhullcpp/QhullLinkedList.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoint.cpp b/src/qhull/src/libqhullcpp/QhullPoint.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullPoint.cpp rename to src/qhull/src/libqhullcpp/QhullPoint.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoint.h b/src/qhull/src/libqhullcpp/QhullPoint.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullPoint.h rename to src/qhull/src/libqhullcpp/QhullPoint.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullPointSet.cpp b/src/qhull/src/libqhullcpp/QhullPointSet.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullPointSet.cpp rename to src/qhull/src/libqhullcpp/QhullPointSet.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullPointSet.h b/src/qhull/src/libqhullcpp/QhullPointSet.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullPointSet.h rename to src/qhull/src/libqhullcpp/QhullPointSet.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoints.cpp b/src/qhull/src/libqhullcpp/QhullPoints.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullPoints.cpp rename to src/qhull/src/libqhullcpp/QhullPoints.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoints.h b/src/qhull/src/libqhullcpp/QhullPoints.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullPoints.h rename to src/qhull/src/libqhullcpp/QhullPoints.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullQh.cpp b/src/qhull/src/libqhullcpp/QhullQh.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullQh.cpp rename to src/qhull/src/libqhullcpp/QhullQh.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullQh.h b/src/qhull/src/libqhullcpp/QhullQh.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullQh.h rename to src/qhull/src/libqhullcpp/QhullQh.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullRidge.cpp b/src/qhull/src/libqhullcpp/QhullRidge.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullRidge.cpp rename to src/qhull/src/libqhullcpp/QhullRidge.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullRidge.h b/src/qhull/src/libqhullcpp/QhullRidge.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullRidge.h rename to src/qhull/src/libqhullcpp/QhullRidge.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullSet.cpp b/src/qhull/src/libqhullcpp/QhullSet.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullSet.cpp rename to src/qhull/src/libqhullcpp/QhullSet.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullSet.h b/src/qhull/src/libqhullcpp/QhullSet.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullSet.h rename to src/qhull/src/libqhullcpp/QhullSet.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullSets.h b/src/qhull/src/libqhullcpp/QhullSets.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullSets.h rename to src/qhull/src/libqhullcpp/QhullSets.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullStat.cpp b/src/qhull/src/libqhullcpp/QhullStat.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullStat.cpp rename to src/qhull/src/libqhullcpp/QhullStat.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullStat.h b/src/qhull/src/libqhullcpp/QhullStat.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullStat.h rename to src/qhull/src/libqhullcpp/QhullStat.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertex.cpp b/src/qhull/src/libqhullcpp/QhullVertex.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullVertex.cpp rename to src/qhull/src/libqhullcpp/QhullVertex.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertex.h b/src/qhull/src/libqhullcpp/QhullVertex.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullVertex.h rename to src/qhull/src/libqhullcpp/QhullVertex.h diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertexSet.cpp b/src/qhull/src/libqhullcpp/QhullVertexSet.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullVertexSet.cpp rename to src/qhull/src/libqhullcpp/QhullVertexSet.cpp diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertexSet.h b/src/qhull/src/libqhullcpp/QhullVertexSet.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/QhullVertexSet.h rename to src/qhull/src/libqhullcpp/QhullVertexSet.h diff --git a/xs/src/qhull/src/libqhullcpp/RboxPoints.cpp b/src/qhull/src/libqhullcpp/RboxPoints.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/RboxPoints.cpp rename to src/qhull/src/libqhullcpp/RboxPoints.cpp diff --git a/xs/src/qhull/src/libqhullcpp/RboxPoints.h b/src/qhull/src/libqhullcpp/RboxPoints.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/RboxPoints.h rename to src/qhull/src/libqhullcpp/RboxPoints.h diff --git a/xs/src/qhull/src/libqhullcpp/RoadError.cpp b/src/qhull/src/libqhullcpp/RoadError.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/RoadError.cpp rename to src/qhull/src/libqhullcpp/RoadError.cpp diff --git a/xs/src/qhull/src/libqhullcpp/RoadError.h b/src/qhull/src/libqhullcpp/RoadError.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/RoadError.h rename to src/qhull/src/libqhullcpp/RoadError.h diff --git a/xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp b/src/qhull/src/libqhullcpp/RoadLogEvent.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp rename to src/qhull/src/libqhullcpp/RoadLogEvent.cpp diff --git a/xs/src/qhull/src/libqhullcpp/RoadLogEvent.h b/src/qhull/src/libqhullcpp/RoadLogEvent.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/RoadLogEvent.h rename to src/qhull/src/libqhullcpp/RoadLogEvent.h diff --git a/xs/src/qhull/src/libqhullcpp/functionObjects.h b/src/qhull/src/libqhullcpp/functionObjects.h similarity index 100% rename from xs/src/qhull/src/libqhullcpp/functionObjects.h rename to src/qhull/src/libqhullcpp/functionObjects.h diff --git a/xs/src/qhull/src/libqhullcpp/libqhullcpp.pro b/src/qhull/src/libqhullcpp/libqhullcpp.pro similarity index 100% rename from xs/src/qhull/src/libqhullcpp/libqhullcpp.pro rename to src/qhull/src/libqhullcpp/libqhullcpp.pro diff --git a/xs/src/qhull/src/libqhullcpp/qt-qhull.cpp b/src/qhull/src/libqhullcpp/qt-qhull.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/qt-qhull.cpp rename to src/qhull/src/libqhullcpp/qt-qhull.cpp diff --git a/xs/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp b/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp similarity index 100% rename from xs/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp rename to src/qhull/src/libqhullcpp/usermem_r-cpp.cpp diff --git a/xs/src/qhull/src/libqhullstatic/libqhullstatic.pro b/src/qhull/src/libqhullstatic/libqhullstatic.pro similarity index 100% rename from xs/src/qhull/src/libqhullstatic/libqhullstatic.pro rename to src/qhull/src/libqhullstatic/libqhullstatic.pro diff --git a/xs/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro b/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro similarity index 100% rename from xs/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro rename to src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro diff --git a/xs/src/qhull/src/qconvex/qconvex.c b/src/qhull/src/qconvex/qconvex.c similarity index 100% rename from xs/src/qhull/src/qconvex/qconvex.c rename to src/qhull/src/qconvex/qconvex.c diff --git a/xs/src/qhull/src/qconvex/qconvex.pro b/src/qhull/src/qconvex/qconvex.pro similarity index 100% rename from xs/src/qhull/src/qconvex/qconvex.pro rename to src/qhull/src/qconvex/qconvex.pro diff --git a/xs/src/qhull/src/qconvex/qconvex_r.c b/src/qhull/src/qconvex/qconvex_r.c similarity index 100% rename from xs/src/qhull/src/qconvex/qconvex_r.c rename to src/qhull/src/qconvex/qconvex_r.c diff --git a/xs/src/qhull/src/qdelaunay/qdelaun.c b/src/qhull/src/qdelaunay/qdelaun.c similarity index 100% rename from xs/src/qhull/src/qdelaunay/qdelaun.c rename to src/qhull/src/qdelaunay/qdelaun.c diff --git a/xs/src/qhull/src/qdelaunay/qdelaun_r.c b/src/qhull/src/qdelaunay/qdelaun_r.c similarity index 100% rename from xs/src/qhull/src/qdelaunay/qdelaun_r.c rename to src/qhull/src/qdelaunay/qdelaun_r.c diff --git a/xs/src/qhull/src/qdelaunay/qdelaunay.pro b/src/qhull/src/qdelaunay/qdelaunay.pro similarity index 100% rename from xs/src/qhull/src/qdelaunay/qdelaunay.pro rename to src/qhull/src/qdelaunay/qdelaunay.pro diff --git a/xs/src/qhull/src/qhalf/qhalf.c b/src/qhull/src/qhalf/qhalf.c similarity index 100% rename from xs/src/qhull/src/qhalf/qhalf.c rename to src/qhull/src/qhalf/qhalf.c diff --git a/xs/src/qhull/src/qhalf/qhalf.pro b/src/qhull/src/qhalf/qhalf.pro similarity index 100% rename from xs/src/qhull/src/qhalf/qhalf.pro rename to src/qhull/src/qhalf/qhalf.pro diff --git a/xs/src/qhull/src/qhalf/qhalf_r.c b/src/qhull/src/qhalf/qhalf_r.c similarity index 100% rename from xs/src/qhull/src/qhalf/qhalf_r.c rename to src/qhull/src/qhalf/qhalf_r.c diff --git a/xs/src/qhull/src/qhull-all.pro b/src/qhull/src/qhull-all.pro similarity index 100% rename from xs/src/qhull/src/qhull-all.pro rename to src/qhull/src/qhull-all.pro diff --git a/xs/src/qhull/src/qhull-app-c.pri b/src/qhull/src/qhull-app-c.pri similarity index 100% rename from xs/src/qhull/src/qhull-app-c.pri rename to src/qhull/src/qhull-app-c.pri diff --git a/xs/src/qhull/src/qhull-app-c_r.pri b/src/qhull/src/qhull-app-c_r.pri similarity index 100% rename from xs/src/qhull/src/qhull-app-c_r.pri rename to src/qhull/src/qhull-app-c_r.pri diff --git a/xs/src/qhull/src/qhull-app-cpp.pri b/src/qhull/src/qhull-app-cpp.pri similarity index 100% rename from xs/src/qhull/src/qhull-app-cpp.pri rename to src/qhull/src/qhull-app-cpp.pri diff --git a/xs/src/qhull/src/qhull-app-shared.pri b/src/qhull/src/qhull-app-shared.pri similarity index 100% rename from xs/src/qhull/src/qhull-app-shared.pri rename to src/qhull/src/qhull-app-shared.pri diff --git a/xs/src/qhull/src/qhull-app-shared_r.pri b/src/qhull/src/qhull-app-shared_r.pri similarity index 100% rename from xs/src/qhull/src/qhull-app-shared_r.pri rename to src/qhull/src/qhull-app-shared_r.pri diff --git a/xs/src/qhull/src/qhull-libqhull-src.pri b/src/qhull/src/qhull-libqhull-src.pri similarity index 100% rename from xs/src/qhull/src/qhull-libqhull-src.pri rename to src/qhull/src/qhull-libqhull-src.pri diff --git a/xs/src/qhull/src/qhull-libqhull-src_r.pri b/src/qhull/src/qhull-libqhull-src_r.pri similarity index 100% rename from xs/src/qhull/src/qhull-libqhull-src_r.pri rename to src/qhull/src/qhull-libqhull-src_r.pri diff --git a/xs/src/qhull/src/qhull-warn.pri b/src/qhull/src/qhull-warn.pri similarity index 100% rename from xs/src/qhull/src/qhull-warn.pri rename to src/qhull/src/qhull-warn.pri diff --git a/xs/src/qhull/src/qhull/qhull.pro b/src/qhull/src/qhull/qhull.pro similarity index 100% rename from xs/src/qhull/src/qhull/qhull.pro rename to src/qhull/src/qhull/qhull.pro diff --git a/xs/src/qhull/src/qhull/unix.c b/src/qhull/src/qhull/unix.c similarity index 100% rename from xs/src/qhull/src/qhull/unix.c rename to src/qhull/src/qhull/unix.c diff --git a/xs/src/qhull/src/qhull/unix_r.c b/src/qhull/src/qhull/unix_r.c similarity index 100% rename from xs/src/qhull/src/qhull/unix_r.c rename to src/qhull/src/qhull/unix_r.c diff --git a/xs/src/qhull/src/qhulltest/Coordinates_test.cpp b/src/qhull/src/qhulltest/Coordinates_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/Coordinates_test.cpp rename to src/qhull/src/qhulltest/Coordinates_test.cpp diff --git a/xs/src/qhull/src/qhulltest/PointCoordinates_test.cpp b/src/qhull/src/qhulltest/PointCoordinates_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/PointCoordinates_test.cpp rename to src/qhull/src/qhulltest/PointCoordinates_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullFacetList_test.cpp b/src/qhull/src/qhulltest/QhullFacetList_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullFacetList_test.cpp rename to src/qhull/src/qhulltest/QhullFacetList_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullFacetSet_test.cpp b/src/qhull/src/qhulltest/QhullFacetSet_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullFacetSet_test.cpp rename to src/qhull/src/qhulltest/QhullFacetSet_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullFacet_test.cpp b/src/qhull/src/qhulltest/QhullFacet_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullFacet_test.cpp rename to src/qhull/src/qhulltest/QhullFacet_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullHyperplane_test.cpp b/src/qhull/src/qhulltest/QhullHyperplane_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullHyperplane_test.cpp rename to src/qhull/src/qhulltest/QhullHyperplane_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullLinkedList_test.cpp b/src/qhull/src/qhulltest/QhullLinkedList_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullLinkedList_test.cpp rename to src/qhull/src/qhulltest/QhullLinkedList_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullPointSet_test.cpp b/src/qhull/src/qhulltest/QhullPointSet_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullPointSet_test.cpp rename to src/qhull/src/qhulltest/QhullPointSet_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullPoint_test.cpp b/src/qhull/src/qhulltest/QhullPoint_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullPoint_test.cpp rename to src/qhull/src/qhulltest/QhullPoint_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullPoints_test.cpp b/src/qhull/src/qhulltest/QhullPoints_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullPoints_test.cpp rename to src/qhull/src/qhulltest/QhullPoints_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullRidge_test.cpp b/src/qhull/src/qhulltest/QhullRidge_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullRidge_test.cpp rename to src/qhull/src/qhulltest/QhullRidge_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullSet_test.cpp b/src/qhull/src/qhulltest/QhullSet_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullSet_test.cpp rename to src/qhull/src/qhulltest/QhullSet_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullVertexSet_test.cpp b/src/qhull/src/qhulltest/QhullVertexSet_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullVertexSet_test.cpp rename to src/qhull/src/qhulltest/QhullVertexSet_test.cpp diff --git a/xs/src/qhull/src/qhulltest/QhullVertex_test.cpp b/src/qhull/src/qhulltest/QhullVertex_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/QhullVertex_test.cpp rename to src/qhull/src/qhulltest/QhullVertex_test.cpp diff --git a/xs/src/qhull/src/qhulltest/Qhull_test.cpp b/src/qhull/src/qhulltest/Qhull_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/Qhull_test.cpp rename to src/qhull/src/qhulltest/Qhull_test.cpp diff --git a/xs/src/qhull/src/qhulltest/RboxPoints_test.cpp b/src/qhull/src/qhulltest/RboxPoints_test.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/RboxPoints_test.cpp rename to src/qhull/src/qhulltest/RboxPoints_test.cpp diff --git a/xs/src/qhull/src/qhulltest/RoadTest.cpp b/src/qhull/src/qhulltest/RoadTest.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/RoadTest.cpp rename to src/qhull/src/qhulltest/RoadTest.cpp diff --git a/xs/src/qhull/src/qhulltest/RoadTest.h b/src/qhull/src/qhulltest/RoadTest.h similarity index 100% rename from xs/src/qhull/src/qhulltest/RoadTest.h rename to src/qhull/src/qhulltest/RoadTest.h diff --git a/xs/src/qhull/src/qhulltest/qhulltest.cpp b/src/qhull/src/qhulltest/qhulltest.cpp similarity index 100% rename from xs/src/qhull/src/qhulltest/qhulltest.cpp rename to src/qhull/src/qhulltest/qhulltest.cpp diff --git a/xs/src/qhull/src/qhulltest/qhulltest.pro b/src/qhull/src/qhulltest/qhulltest.pro similarity index 100% rename from xs/src/qhull/src/qhulltest/qhulltest.pro rename to src/qhull/src/qhulltest/qhulltest.pro diff --git a/xs/src/qhull/src/qvoronoi/qvoronoi.c b/src/qhull/src/qvoronoi/qvoronoi.c similarity index 100% rename from xs/src/qhull/src/qvoronoi/qvoronoi.c rename to src/qhull/src/qvoronoi/qvoronoi.c diff --git a/xs/src/qhull/src/qvoronoi/qvoronoi.pro b/src/qhull/src/qvoronoi/qvoronoi.pro similarity index 100% rename from xs/src/qhull/src/qvoronoi/qvoronoi.pro rename to src/qhull/src/qvoronoi/qvoronoi.pro diff --git a/xs/src/qhull/src/qvoronoi/qvoronoi_r.c b/src/qhull/src/qvoronoi/qvoronoi_r.c similarity index 100% rename from xs/src/qhull/src/qvoronoi/qvoronoi_r.c rename to src/qhull/src/qvoronoi/qvoronoi_r.c diff --git a/xs/src/qhull/src/rbox/rbox.c b/src/qhull/src/rbox/rbox.c similarity index 100% rename from xs/src/qhull/src/rbox/rbox.c rename to src/qhull/src/rbox/rbox.c diff --git a/xs/src/qhull/src/rbox/rbox.pro b/src/qhull/src/rbox/rbox.pro similarity index 100% rename from xs/src/qhull/src/rbox/rbox.pro rename to src/qhull/src/rbox/rbox.pro diff --git a/xs/src/qhull/src/rbox/rbox_r.c b/src/qhull/src/rbox/rbox_r.c similarity index 100% rename from xs/src/qhull/src/rbox/rbox_r.c rename to src/qhull/src/rbox/rbox_r.c diff --git a/xs/src/qhull/src/testqset/testqset.c b/src/qhull/src/testqset/testqset.c similarity index 100% rename from xs/src/qhull/src/testqset/testqset.c rename to src/qhull/src/testqset/testqset.c diff --git a/xs/src/qhull/src/testqset/testqset.pro b/src/qhull/src/testqset/testqset.pro similarity index 100% rename from xs/src/qhull/src/testqset/testqset.pro rename to src/qhull/src/testqset/testqset.pro diff --git a/xs/src/qhull/src/testqset_r/testqset_r.c b/src/qhull/src/testqset_r/testqset_r.c similarity index 100% rename from xs/src/qhull/src/testqset_r/testqset_r.c rename to src/qhull/src/testqset_r/testqset_r.c diff --git a/xs/src/qhull/src/testqset_r/testqset_r.pro b/src/qhull/src/testqset_r/testqset_r.pro similarity index 100% rename from xs/src/qhull/src/testqset_r/testqset_r.pro rename to src/qhull/src/testqset_r/testqset_r.pro diff --git a/xs/src/qhull/src/user_eg/user_eg.c b/src/qhull/src/user_eg/user_eg.c similarity index 100% rename from xs/src/qhull/src/user_eg/user_eg.c rename to src/qhull/src/user_eg/user_eg.c diff --git a/xs/src/qhull/src/user_eg/user_eg.pro b/src/qhull/src/user_eg/user_eg.pro similarity index 100% rename from xs/src/qhull/src/user_eg/user_eg.pro rename to src/qhull/src/user_eg/user_eg.pro diff --git a/xs/src/qhull/src/user_eg/user_eg_r.c b/src/qhull/src/user_eg/user_eg_r.c similarity index 100% rename from xs/src/qhull/src/user_eg/user_eg_r.c rename to src/qhull/src/user_eg/user_eg_r.c diff --git a/xs/src/qhull/src/user_eg2/user_eg2.c b/src/qhull/src/user_eg2/user_eg2.c similarity index 100% rename from xs/src/qhull/src/user_eg2/user_eg2.c rename to src/qhull/src/user_eg2/user_eg2.c diff --git a/xs/src/qhull/src/user_eg2/user_eg2.pro b/src/qhull/src/user_eg2/user_eg2.pro similarity index 100% rename from xs/src/qhull/src/user_eg2/user_eg2.pro rename to src/qhull/src/user_eg2/user_eg2.pro diff --git a/xs/src/qhull/src/user_eg2/user_eg2_r.c b/src/qhull/src/user_eg2/user_eg2_r.c similarity index 100% rename from xs/src/qhull/src/user_eg2/user_eg2_r.c rename to src/qhull/src/user_eg2/user_eg2_r.c diff --git a/xs/src/qhull/src/user_eg3/user_eg3.pro b/src/qhull/src/user_eg3/user_eg3.pro similarity index 100% rename from xs/src/qhull/src/user_eg3/user_eg3.pro rename to src/qhull/src/user_eg3/user_eg3.pro diff --git a/xs/src/qhull/src/user_eg3/user_eg3_r.cpp b/src/qhull/src/user_eg3/user_eg3_r.cpp similarity index 100% rename from xs/src/qhull/src/user_eg3/user_eg3_r.cpp rename to src/qhull/src/user_eg3/user_eg3_r.cpp diff --git a/src/semver/CMakeLists.txt b/src/semver/CMakeLists.txt new file mode 100644 index 0000000000..e3457bf291 --- /dev/null +++ b/src/semver/CMakeLists.txt @@ -0,0 +1,7 @@ +project(semver) +cmake_minimum_required(VERSION 2.6) + +add_library(semver STATIC + semver.c + semver.h +) diff --git a/xs/src/semver/semver.c b/src/semver/semver.c similarity index 100% rename from xs/src/semver/semver.c rename to src/semver/semver.c diff --git a/xs/src/semver/semver.h b/src/semver/semver.h similarity index 100% rename from xs/src/semver/semver.h rename to src/semver/semver.h diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 4cff9b38bb..376ade95a4 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -1,8 +1,26 @@ +#ifdef WIN32 + // Why? + #define _WIN32_WINNT 0x0502 + // The standard Windows includes. + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #include + #include + // Let the NVIDIA and AMD know we want to use their graphics card + // on a dual graphics card system. + __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; + __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; +#endif /* WIN32 */ + +#include "libslic3r/libslic3r_version.h.in" #include "Config.hpp" #include "Geometry.hpp" #include "Model.hpp" +#include "Print.hpp" #include "TriangleMesh.hpp" +#include "Format/3mf.hpp" #include "libslic3r.h" +#include "Utils.hpp" #include #include #include @@ -10,41 +28,90 @@ #include #include #include +#include #include +#ifdef USE_WX +// #include "GUI/GUI.hpp" +#endif +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_App.hpp" + using namespace Slic3r; -void confess_at(const char *file, int line, const char *func, const char *pat, ...){} +/// utility function for displaying CLI usage +void printUsage(); -int -main(int argc, char **argv) +#ifdef _MSC_VER +int slic3r_main_(int argc, char **argv) +#else +int main(int argc, char **argv) +#endif { - // Convert arguments to UTF-8 (needed on Windows). - // argv then points to memory owned by a. - boost::nowide::args a(argc, argv); - -#if 0 + { + const char *loglevel = boost::nowide::getenv("SLIC3R_LOGLEVEL"); + if (loglevel != nullptr) { + if (loglevel[0] >= '0' && loglevel[0] <= '9' && loglevel[1] == 0) + set_logging_level(loglevel[0] - '0'); + else + boost::nowide::cerr << "Invalid SLIC3R_LOGLEVEL environment variable: " << loglevel << std::endl; + } + } + // parse all command line options into a DynamicConfig - ConfigDef config_def; - config_def.merge(cli_config_def); - config_def.merge(print_config_def); - DynamicConfig config(&config_def); + DynamicPrintAndCLIConfig config; t_config_option_keys input_files; - config.read_cli(argc, argv, &input_files); - + // if any option is unsupported, print usage and abort immediately + if (! config.read_cli(argc, argv, &input_files)) { + printUsage(); + return 0; + } + + boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]); + + // Path from the Slic3r binary to its resources. +#ifdef __APPLE__ + // The application is packed in the .dmg archive as 'Slic3r.app/Contents/MacOS/Slic3r' + // The resources are packed to 'Slic3r.app/Contents/Resources' + boost::filesystem::path path_resources = path_to_binary.parent_path() / "../Resources"; +#elif defined _WIN32 + // The application is packed in the .zip archive in the root, + // The resources are packed to 'resources' + // Path from Slic3r binary to resources: + boost::filesystem::path path_resources = path_to_binary.parent_path() / "resources"; +#else + // The application is packed in the .tar.bz archive (or in AppImage) as 'bin/slic3r', + // The resources are packed to 'resources' + // Path from Slic3r binary to resources: + boost::filesystem::path path_resources = path_to_binary.parent_path() / "../resources"; +#endif + + set_resources_dir(path_resources.string()); + set_var_dir((path_resources / "icons").string()); + set_local_dir((path_resources / "localization").string()); + // apply command line options to a more handy CLIConfig CLIConfig cli_config; cli_config.apply(config, true); - + set_data_dir(cli_config.datadir.value); + DynamicPrintConfig print_config; - + + if ((argc == 1 || cli_config.gui.value) && ! cli_config.no_gui.value && ! cli_config.help.value && cli_config.save.value.empty()) { +#if 1 + GUI::GUI_App *gui = new GUI::GUI_App(); + GUI::GUI_App::SetInstance(gui); + wxEntry(argc, argv); +#else + std::cout << "GUI support has not been built." << "\n"; +#endif + } // load config files supplied via --load for (const std::string &file : cli_config.load.values) { if (!boost::filesystem::exists(file)) { boost::nowide::cout << "No such file: " << file << std::endl; exit(1); } - DynamicPrintConfig c; try { c.load(file); @@ -59,124 +126,144 @@ main(int argc, char **argv) // apply command line options to a more specific DynamicPrintConfig which provides normalize() // (command line options override --load files) print_config.apply(config, true); - print_config.normalize(); // write config if requested - if (!cli_config.save.value.empty()) print_config.save(cli_config.save.value); - + if (! cli_config.save.value.empty()) { + print_config.normalize(); + print_config.save(cli_config.save.value); + } + + if (cli_config.help) { + printUsage(); + return 0; + } + // read input file(s) if any std::vector models; for (const t_config_option_key &file : input_files) { - if (!boost::filesystem::exists(file)) { + if (! boost::filesystem::exists(file)) { boost::nowide::cerr << "No such file: " << file << std::endl; exit(1); } - Model model; try { - model = Model::read_from_file(file); + model = Model::read_from_file(file, &print_config, true); } catch (std::exception &e) { boost::nowide::cerr << file << ": " << e.what() << std::endl; exit(1); } - if (model.objects.empty()) { boost::nowide::cerr << "Error: file is empty: " << file << std::endl; continue; } - - model.add_default_instances(); - + model.add_default_instances(); // apply command line transform options for (ModelObject* o : model.objects) { +/* if (cli_config.scale_to_fit.is_positive_volume()) o->scale_to_fit(cli_config.scale_to_fit.value); - +*/ // TODO: honor option order? o->scale(cli_config.scale.value); o->rotate(Geometry::deg2rad(cli_config.rotate_x.value), X); o->rotate(Geometry::deg2rad(cli_config.rotate_y.value), Y); o->rotate(Geometry::deg2rad(cli_config.rotate.value), Z); } - // TODO: handle --merge models.push_back(model); } - + for (Model &model : models) { if (cli_config.info) { // --info works on unrepaired model model.print_info(); - } else if (cli_config.export_obj) { + } else if (cli_config.export_3mf) { std::string outfile = cli_config.output.value; - if (outfile.empty()) outfile = model.objects.front()->input_file + ".obj"; - - TriangleMesh mesh = model.mesh(); - mesh.repair(); - IO::OBJ::write(mesh, outfile); - boost::nowide::cout << "File exported to " << outfile << std::endl; - } else if (cli_config.export_pov) { - std::string outfile = cli_config.output.value; - if (outfile.empty()) outfile = model.objects.front()->input_file + ".pov"; - - TriangleMesh mesh = model.mesh(); - mesh.repair(); - IO::POV::write(mesh, outfile); - boost::nowide::cout << "File exported to " << outfile << std::endl; - } else if (cli_config.export_svg) { - std::string outfile = cli_config.output.value; - if (outfile.empty()) outfile = model.objects.front()->input_file + ".svg"; - - SLAPrint print(&model); - print.config.apply(print_config, true); - print.slice(); - print.write_svg(outfile); - boost::nowide::cout << "SVG file exported to " << outfile << std::endl; - } else if (cli_config.cut_x > 0 || cli_config.cut_y > 0 || cli_config.cut > 0) { + if (outfile.empty()) outfile = model.objects.front()->input_file; + // Check if the file is already a 3mf. + if(outfile.substr(outfile.find_last_of('.'), outfile.length()) == ".3mf") + outfile = outfile.substr(0, outfile.find_last_of('.')) + "_2" + ".3mf"; + else + // Remove the previous extension and add .3mf extention. + outfile = outfile.substr(0, outfile.find_last_of('.')) + ".3mf"; + store_3mf(outfile.c_str(), &model, nullptr); + boost::nowide::cout << "File file exported to " << outfile << std::endl; + } else if (cli_config.cut > 0) { model.repair(); - model.translate(0, 0, -model.bounding_box().min.z); - - if (!model.objects.empty()) { - // FIXME: cut all objects - Model out; - if (cli_config.cut_x > 0) { - model.objects.front()->cut(X, cli_config.cut_x, &out); - } else if (cli_config.cut_y > 0) { - model.objects.front()->cut(Y, cli_config.cut_y, &out); - } else { - model.objects.front()->cut(Z, cli_config.cut, &out); - } - - ModelObject &upper = *out.objects[0]; - ModelObject &lower = *out.objects[1]; - - if (upper.facets_count() > 0) { - TriangleMesh m = upper.mesh(); - IO::STL::write(m, upper.input_file + "_upper.stl"); - } - if (lower.facets_count() > 0) { - TriangleMesh m = lower.mesh(); - IO::STL::write(m, lower.input_file + "_lower.stl"); - } + model.translate(0, 0, - model.bounding_box().min(2)); + if (! model.objects.empty()) { + // XXX + // Model out; + // model.objects.front()->cut(cli_config.cut, &out); + // ModelObject &upper = *out.objects[0]; + // ModelObject &lower = *out.objects[1]; + // // Use the input name and trim off the extension. + // std::string outfile = cli_config.output.value; + // if (outfile.empty()) + // outfile = model.objects.front()->input_file; + // outfile = outfile.substr(0, outfile.find_last_of('.')); + // std::cerr << outfile << "\n"; + // if (upper.facets_count() > 0) + // upper.mesh().write_binary((outfile + "_upper.stl").c_str()); + // if (lower.facets_count() > 0) + // lower.mesh().write_binary((outfile + "_lower.stl").c_str()); } - } else if (cli_config.cut_grid.value.x > 0 && cli_config.cut_grid.value.y > 0) { - TriangleMesh mesh = model.mesh(); - mesh.repair(); - - TriangleMeshPtrs meshes = mesh.cut_by_grid(cli_config.cut_grid.value); - size_t i = 0; - for (TriangleMesh* m : meshes) { - std::ostringstream ss; - ss << model.objects.front()->input_file << "_" << i++ << ".stl"; - IO::STL::write(*m, ss.str()); - delete m; + } else if (cli_config.slice) { + std::string outfile = cli_config.output.value; + Print print; + if (! cli_config.dont_arrange) { + model.arrange_objects(print.config().min_object_distance()); + model.center_instances_around_point(cli_config.print_center); } + if (outfile.empty()) + outfile = model.objects.front()->input_file + ".gcode"; + for (auto* mo : model.objects) + print.auto_assign_extruders(mo); + print_config.normalize(); + print.apply(model, print_config); + std::string err = print.validate(); + if (err.empty()) + print.export_gcode(outfile, nullptr); + else + std::cerr << err << "\n"; } else { boost::nowide::cerr << "error: command not supported" << std::endl; return 1; } } -#endif return 0; } + +void printUsage() +{ + std::cout << "Slic3r " << SLIC3R_VERSION << " is a STL-to-GCODE translator for RepRap 3D printers" << "\n" + << "written by Alessandro Ranellucci - http://slic3r.org/ - https://github.com/slic3r/Slic3r" << "\n" +// << "Git Version " << BUILD_COMMIT << "\n\n" + << "Usage: ./slic3r [ OPTIONS ] [ file.stl ] [ file2.stl ] ..." << "\n"; + // CLI Options + std::cout << "** CLI OPTIONS **\n"; + print_cli_options(boost::nowide::cout); + std::cout << "****\n"; + // Print options + std::cout << "** PRINT OPTIONS **\n"; + print_print_options(boost::nowide::cout); + std::cout << "****\n"; +} + +#ifdef _MSC_VER +extern "C" { + __declspec(dllexport) int __stdcall slic3r_main(int argc, wchar_t **argv) + { + // Convert wchar_t arguments to UTF8. + std::vector argv_narrow; + std::vector argv_ptrs(argc + 1, nullptr); + for (size_t i = 0; i < argc; ++ i) + argv_narrow.emplace_back(boost::nowide::narrow(argv[i])); + for (size_t i = 0; i < argc; ++ i) + argv_ptrs[i] = const_cast(argv_narrow[i].data()); + // Call the UTF8 main. + return slic3r_main_(argc, argv_ptrs.data()); + } +} +#endif /* _MSC_VER */ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt new file mode 100644 index 0000000000..f4bea7d36f --- /dev/null +++ b/src/slic3r/CMakeLists.txt @@ -0,0 +1,129 @@ +project(libslic3r_gui) +cmake_minimum_required(VERSION 2.6) + +include(PrecompiledHeader) + +add_library(libslic3r_gui STATIC + pchheader.cpp + pchheader.hpp + GUI/AboutDialog.cpp + GUI/AboutDialog.hpp + GUI/SysInfoDialog.cpp + GUI/SysInfoDialog.hpp + GUI/AppConfig.cpp + GUI/AppConfig.hpp + GUI/BackgroundSlicingProcess.cpp + GUI/BackgroundSlicingProcess.hpp + GUI/BitmapCache.cpp + GUI/BitmapCache.hpp + GUI/ConfigSnapshotDialog.cpp + GUI/ConfigSnapshotDialog.hpp + GUI/3DScene.cpp + GUI/3DScene.hpp + GUI/GLShader.cpp + GUI/GLShader.hpp + GUI/GLCanvas3D.hpp + GUI/GLCanvas3D.cpp + GUI/GLCanvas3DManager.hpp + GUI/GLCanvas3DManager.cpp + GUI/GLGizmo.hpp + GUI/GLGizmo.cpp + GUI/GLTexture.hpp + GUI/GLTexture.cpp + GUI/GLToolbar.hpp + GUI/GLToolbar.cpp + GUI/Preferences.cpp + GUI/Preferences.hpp + GUI/Preset.cpp + GUI/Preset.hpp + GUI/PresetBundle.cpp + GUI/PresetBundle.hpp + GUI/PresetHints.cpp + GUI/PresetHints.hpp + GUI/GUI.cpp + GUI/GUI.hpp + GUI/GUI_Preview.cpp + GUI/GUI_Preview.hpp + GUI/GUI_PreviewIface.cpp + GUI/GUI_PreviewIface.hpp + GUI/GUI_App.cpp + GUI/GUI_App.hpp + GUI/GUI_Utils.cpp + GUI/GUI_Utils.hpp + GUI/MainFrame.cpp + GUI/MainFrame.hpp + GUI/Plater.cpp + GUI/Plater.hpp + GUI/GUI_ObjectList.cpp + GUI/GUI_ObjectList.hpp + GUI/GUI_ObjectManipulation.cpp + GUI/GUI_ObjectManipulation.hpp + GUI/GUI_ObjectSettings.cpp + GUI/GUI_ObjectSettings.hpp + GUI/LambdaObjectDialog.cpp + GUI/LambdaObjectDialog.hpp + GUI/Tab.cpp + GUI/Tab.hpp + GUI/TabIface.cpp + GUI/TabIface.hpp + GUI/Field.cpp + GUI/Field.hpp + GUI/OptionsGroup.cpp + GUI/OptionsGroup.hpp + GUI/BedShapeDialog.cpp + GUI/BedShapeDialog.hpp + GUI/2DBed.cpp + GUI/2DBed.hpp + GUI/wxExtensions.cpp + GUI/wxExtensions.hpp + GUI/WipeTowerDialog.cpp + GUI/WipeTowerDialog.hpp + GUI/RammingChart.cpp + GUI/RammingChart.hpp + GUI/BonjourDialog.cpp + GUI/BonjourDialog.hpp + GUI/ButtonsDescription.cpp + GUI/ButtonsDescription.hpp + Config/Snapshot.cpp + Config/Snapshot.hpp + Config/Version.cpp + Config/Version.hpp + Utils/ASCIIFolding.cpp + Utils/ASCIIFolding.hpp + Utils/Serial.cpp + Utils/Serial.hpp + GUI/ConfigWizard.cpp + GUI/ConfigWizard.hpp + GUI/MsgDialog.cpp + GUI/MsgDialog.hpp + GUI/UpdateDialogs.cpp + GUI/UpdateDialogs.hpp + GUI/FirmwareDialog.cpp + GUI/FirmwareDialog.hpp + GUI/ProgressIndicator.hpp + GUI/ProgressStatusBar.hpp + GUI/ProgressStatusBar.cpp + Utils/Http.cpp + Utils/Http.hpp + Utils/FixModelByWin10.cpp + Utils/FixModelByWin10.hpp + Utils/PrintHostSendDialog.cpp + Utils/PrintHostSendDialog.hpp + Utils/OctoPrint.cpp + Utils/OctoPrint.hpp + Utils/Duet.cpp + Utils/Duet.hpp + Utils/PrintHost.cpp + Utils/PrintHost.hpp + Utils/Bonjour.cpp + Utils/Bonjour.hpp + Utils/PresetUpdater.cpp + Utils/PresetUpdater.hpp + Utils/Time.cpp + Utils/Time.hpp + Utils/HexFile.cpp + Utils/HexFile.hpp +) + +target_link_libraries(libslic3r_gui libslic3r avrdude) +add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) diff --git a/xs/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp similarity index 99% rename from xs/src/slic3r/Config/Snapshot.cpp rename to src/slic3r/Config/Snapshot.cpp index 704fbcfa1b..e92c829c0a 100644 --- a/xs/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -406,7 +406,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: cfg.version.min_slic3r_version = it->min_slic3r_version; cfg.version.max_slic3r_version = it->max_slic3r_version; } - } catch (const std::runtime_error &err) { + } catch (const std::runtime_error & /* err */) { } snapshot.vendor_configs.emplace_back(std::move(cfg)); } @@ -521,7 +521,7 @@ SnapshotDB& SnapshotDB::singleton() // Update the min / max slic3r versions compatible with the configurations stored inside the snapshots // based on the min / max slic3r versions defined by the vendor specific config indices. instance.update_slic3r_versions(index_db); - } catch (std::exception &ex) { + } catch (std::exception & /* ex */) { } } return instance; diff --git a/xs/src/slic3r/Config/Snapshot.hpp b/src/slic3r/Config/Snapshot.hpp similarity index 100% rename from xs/src/slic3r/Config/Snapshot.hpp rename to src/slic3r/Config/Snapshot.hpp diff --git a/xs/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp similarity index 100% rename from xs/src/slic3r/Config/Version.cpp rename to src/slic3r/Config/Version.cpp diff --git a/xs/src/slic3r/Config/Version.hpp b/src/slic3r/Config/Version.hpp similarity index 100% rename from xs/src/slic3r/Config/Version.hpp rename to src/slic3r/Config/Version.hpp diff --git a/xs/src/slic3r/GUI/2DBed.cpp b/src/slic3r/GUI/2DBed.cpp similarity index 54% rename from xs/src/slic3r/GUI/2DBed.cpp rename to src/slic3r/GUI/2DBed.cpp index 6d788cf340..d0f640433e 100644 --- a/xs/src/slic3r/GUI/2DBed.cpp +++ b/src/slic3r/GUI/2DBed.cpp @@ -32,17 +32,17 @@ void Bed_2D::repaint() cw--; ch--; - auto cbb = BoundingBoxf(Pointf(0, 0),Pointf(cw, ch)); + auto cbb = BoundingBoxf(Vec2d(0, 0),Vec2d(cw, ch)); // leave space for origin point - cbb.min.translate(4, 0); - cbb.max.translate(-4, -4); + cbb.min(0) += 4; + cbb.max -= Vec2d(4., 4.); // leave space for origin label - cbb.max.translate(0, -13); + cbb.max(1) -= 13; // read new size - cw = cbb.size().x; - ch = cbb.size().y; + cw = cbb.size()(0); + ch = cbb.size()(1); auto ccenter = cbb.center(); @@ -50,20 +50,20 @@ void Bed_2D::repaint() auto bed_shape = m_bed_shape; auto bed_polygon = Polygon::new_scale(m_bed_shape); auto bb = BoundingBoxf(m_bed_shape); - bb.merge(Pointf(0, 0)); // origin needs to be in the visible area - auto bw = bb.size().x; - auto bh = bb.size().y; + bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area + auto bw = bb.size()(0); + auto bh = bb.size()(1); auto bcenter = bb.center(); // calculate the scaling factor for fitting bed shape in canvas area auto sfactor = std::min(cw/bw, ch/bh); - auto shift = Pointf( - ccenter.x - bcenter.x * sfactor, - ccenter.y - bcenter.y * sfactor + auto shift = Vec2d( + ccenter(0) - bcenter(0) * sfactor, + ccenter(1) - bcenter(1) * sfactor ); m_scale_factor = sfactor; - m_shift = Pointf(shift.x + cbb.min.x, - shift.y - (cbb.max.y - GetSize().GetHeight())); + m_shift = Vec2d(shift(0) + cbb.min(0), + shift(1) - (cbb.max(1) - GetSize().GetHeight())); // draw bed fill dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxSOLID)); @@ -71,29 +71,28 @@ void Bed_2D::repaint() for (auto pt: m_bed_shape) { Point pt_pix = to_pixels(pt); - pt_list.push_back(new wxPoint(pt_pix.x, pt_pix.y)); + pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1))); } dc.DrawPolygon(&pt_list, 0, 0); // draw grid auto step = 10; // 1cm grid Polylines polylines; - for (auto x = bb.min.x - fmod(bb.min.x, step) + step; x < bb.max.x; x += step) { - Polyline pl = Polyline::new_scale({ Pointf(x, bb.min.y), Pointf(x, bb.max.y) }); - polylines.push_back(pl); + for (auto x = bb.min(0) - fmod(bb.min(0), step) + step; x < bb.max(0); x += step) { + polylines.push_back(Polyline::new_scale({ Vec2d(x, bb.min(1)), Vec2d(x, bb.max(1)) })); } - for (auto y = bb.min.y - fmod(bb.min.y, step) + step; y < bb.max.y; y += step) { - polylines.push_back(Polyline::new_scale({ Pointf(bb.min.x, y), Pointf(bb.max.x, y) })); + for (auto y = bb.min(1) - fmod(bb.min(1), step) + step; y < bb.max(1); y += step) { + polylines.push_back(Polyline::new_scale({ Vec2d(bb.min(0), y), Vec2d(bb.max(0), y) })); } polylines = intersection_pl(polylines, bed_polygon); dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxSOLID)); for (auto pl : polylines) { - for (size_t i = 0; i < pl.points.size()-1; i++){ - Point pt1 = to_pixels(Pointf::new_unscale(pl.points[i])); - Point pt2 = to_pixels(Pointf::new_unscale(pl.points[i+1])); - dc.DrawLine(pt1.x, pt1.y, pt2.x, pt2.y); + for (size_t i = 0; i < pl.points.size()-1; i++) { + Point pt1 = to_pixels(unscale(pl.points[i])); + Point pt2 = to_pixels(unscale(pl.points[i+1])); + dc.DrawLine(pt1(0), pt1(1), pt2(0), pt2(1)); } } @@ -102,73 +101,69 @@ void Bed_2D::repaint() dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxTRANSPARENT)); dc.DrawPolygon(&pt_list, 0, 0); - auto origin_px = to_pixels(Pointf(0, 0)); + auto origin_px = to_pixels(Vec2d(0, 0)); // draw axes auto axes_len = 50; auto arrow_len = 6; auto arrow_angle = Geometry::deg2rad(45.0); dc.SetPen(wxPen(wxColour(255, 0, 0), 2, wxSOLID)); // red - auto x_end = Pointf(origin_px.x + axes_len, origin_px.y); - dc.DrawLine(wxPoint(origin_px.x, origin_px.y), wxPoint(x_end.x, x_end.y)); - for (auto angle : { -arrow_angle, arrow_angle }){ - auto end = x_end; - end.translate(-arrow_len, 0); - end.rotate(angle, x_end); - dc.DrawLine(wxPoint(x_end.x, x_end.y), wxPoint(end.x, end.y)); + auto x_end = Vec2d(origin_px(0) + axes_len, origin_px(1)); + dc.DrawLine(wxPoint(origin_px(0), origin_px(1)), wxPoint(x_end(0), x_end(1))); + for (auto angle : { -arrow_angle, arrow_angle }) { + auto end = Eigen::Translation2d(x_end) * Eigen::Rotation2Dd(angle) * Eigen::Translation2d(- x_end) * Eigen::Vector2d(x_end(0) - arrow_len, x_end(1)); + dc.DrawLine(wxPoint(x_end(0), x_end(1)), wxPoint(end(0), end(1))); } dc.SetPen(wxPen(wxColour(0, 255, 0), 2, wxSOLID)); // green - auto y_end = Pointf(origin_px.x, origin_px.y - axes_len); - dc.DrawLine(wxPoint(origin_px.x, origin_px.y), wxPoint(y_end.x, y_end.y)); + auto y_end = Vec2d(origin_px(0), origin_px(1) - axes_len); + dc.DrawLine(wxPoint(origin_px(0), origin_px(1)), wxPoint(y_end(0), y_end(1))); for (auto angle : { -arrow_angle, arrow_angle }) { - auto end = y_end; - end.translate(0, +arrow_len); - end.rotate(angle, y_end); - dc.DrawLine(wxPoint(y_end.x, y_end.y), wxPoint(end.x, end.y)); + auto end = Eigen::Translation2d(y_end) * Eigen::Rotation2Dd(angle) * Eigen::Translation2d(- y_end) * Eigen::Vector2d(y_end(0), y_end(1) + arrow_len); + dc.DrawLine(wxPoint(y_end(0), y_end(1)), wxPoint(end(0), end(1))); } // draw origin dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxSOLID)); dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxSOLID)); - dc.DrawCircle(origin_px.x, origin_px.y, 3); + dc.DrawCircle(origin_px(0), origin_px(1), 3); static const auto origin_label = wxString("(0,0)"); dc.SetTextForeground(wxColour(0, 0, 0)); dc.SetFont(wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL)); auto extent = dc.GetTextExtent(origin_label); - const auto origin_label_x = origin_px.x <= cw / 2 ? origin_px.x + 1 : origin_px.x - 1 - extent.GetWidth(); - const auto origin_label_y = origin_px.y <= ch / 2 ? origin_px.y + 1 : origin_px.y - 1 - extent.GetHeight(); + const auto origin_label_x = origin_px(0) <= cw / 2 ? origin_px(0) + 1 : origin_px(0) - 1 - extent.GetWidth(); + const auto origin_label_y = origin_px(1) <= ch / 2 ? origin_px(1) + 1 : origin_px(1) - 1 - extent.GetHeight(); dc.DrawText(origin_label, origin_label_x, origin_label_y); // draw current position - if (m_pos!= Pointf(0, 0)) { + if (m_pos!= Vec2d(0, 0)) { auto pos_px = to_pixels(m_pos); dc.SetPen(wxPen(wxColour(200, 0, 0), 2, wxSOLID)); dc.SetBrush(wxBrush(wxColour(200, 0, 0), wxTRANSPARENT)); - dc.DrawCircle(pos_px.x, pos_px.y, 5); + dc.DrawCircle(pos_px(0), pos_px(1), 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); + dc.DrawLine(pos_px(0) - 15, pos_px(1), pos_px(0) + 15, pos_px(1)); + dc.DrawLine(pos_px(0), pos_px(1) - 15, pos_px(0), pos_px(1) + 15); } m_painted = true; } // convert G - code coordinates into pixels -Point Bed_2D::to_pixels(Pointf point){ - auto p = Pointf(point); - p.scale(m_scale_factor); - p.translate(m_shift); - return Point(p.x, GetSize().GetHeight() - p.y); +Point Bed_2D::to_pixels(Vec2d point) +{ + auto p = point * m_scale_factor + m_shift; + return Point(p(0), GetSize().GetHeight() - p(1)); } -void Bed_2D::mouse_event(wxMouseEvent event){ +void Bed_2D::mouse_event(wxMouseEvent event) +{ if (!m_interactive) return; if (!m_painted) return; auto pos = event.GetPosition(); - auto point = to_units(Point(pos.x, pos.y)); + auto point = to_units(Point(pos.x, pos.y)); if (event.LeftDown() || event.Dragging()) { if (m_on_move) m_on_move(point) ; @@ -177,14 +172,13 @@ void Bed_2D::mouse_event(wxMouseEvent event){ } // convert pixels into G - code coordinates -Pointf Bed_2D::to_units(Point point){ - auto p = Pointf(point.x, GetSize().GetHeight() - point.y); - p.translate(m_shift.negative()); - p.scale(1 / m_scale_factor); - return p; +Vec2d Bed_2D::to_units(Point point) +{ + return (Vec2d(point(0), GetSize().GetHeight() - point(1)) - m_shift) * (1. / m_scale_factor); } -void Bed_2D::set_pos(Pointf pos){ +void Bed_2D::set_pos(Vec2d pos) +{ m_pos = pos; Refresh(); } diff --git a/xs/src/slic3r/GUI/2DBed.hpp b/src/slic3r/GUI/2DBed.hpp similarity index 63% rename from xs/src/slic3r/GUI/2DBed.hpp rename to src/slic3r/GUI/2DBed.hpp index 4b14986a2b..5df5961364 100644 --- a/xs/src/slic3r/GUI/2DBed.hpp +++ b/src/slic3r/GUI/2DBed.hpp @@ -14,15 +14,15 @@ class Bed_2D : public wxPanel bool m_painted = false; bool m_interactive = false; double m_scale_factor; - Pointf m_shift; - Pointf m_pos; - std::function m_on_move = nullptr; + Vec2d m_shift = Vec2d::Zero(); + Vec2d m_pos = Vec2d::Zero(); + std::function m_on_move = nullptr; - Point to_pixels(Pointf point); - Pointf to_units(Point point); + Point to_pixels(Vec2d point); + Vec2d to_units(Point point); void repaint(); void mouse_event(wxMouseEvent event); - void set_pos(Pointf pos); + void set_pos(Vec2d pos); public: Bed_2D(wxWindow* parent) @@ -34,14 +34,14 @@ public: #endif /*__APPLE__*/ Bind(wxEVT_PAINT, ([this](wxPaintEvent e) { repaint(); })); // EVT_ERASE_BACKGROUND($self, sub{}) if $self->{user_drawn_background}; -// Bind(EVT_MOUSE_EVENTS, ([this](wxMouseEvent event){/*mouse_event()*/; })); - Bind(wxEVT_LEFT_DOWN, ([this](wxMouseEvent event){ mouse_event(event); })); - Bind(wxEVT_MOTION, ([this](wxMouseEvent event){ mouse_event(event); })); +// Bind(EVT_MOUSE_EVENTS, ([this](wxMouseEvent event) {/*mouse_event()*/; })); + Bind(wxEVT_LEFT_DOWN, ([this](wxMouseEvent event) { mouse_event(event); })); + Bind(wxEVT_MOTION, ([this](wxMouseEvent event) { mouse_event(event); })); Bind(wxEVT_SIZE, ([this](wxSizeEvent e) { Refresh(); })); } - ~Bed_2D(){} + ~Bed_2D() {} - std::vector m_bed_shape; + std::vector m_bed_shape; }; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp similarity index 78% rename from xs/src/slic3r/GUI/3DScene.cpp rename to src/slic3r/GUI/3DScene.cpp index 1901aa3a77..2350cdffcc 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -2,13 +2,14 @@ #include "3DScene.hpp" -#include "../../libslic3r/ExtrusionEntity.hpp" -#include "../../libslic3r/ExtrusionEntityCollection.hpp" -#include "../../libslic3r/Geometry.hpp" -#include "../../libslic3r/GCode/PreviewData.hpp" -#include "../../libslic3r/Print.hpp" -#include "../../libslic3r/Slicing.hpp" -#include "../../slic3r/GUI/PresetBundle.hpp" +#include "libslic3r/ExtrusionEntity.hpp" +#include "libslic3r/ExtrusionEntityCollection.hpp" +#include "libslic3r/Geometry.hpp" +#include "libslic3r/GCode/PreviewData.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/Slicing.hpp" +#include "slic3r/GUI/PresetBundle.hpp" #include "GCode/Analyzer.hpp" #include @@ -26,11 +27,6 @@ #include "GUI.hpp" -static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - namespace Slic3r { void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh) @@ -41,10 +37,10 @@ void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh) this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); - for (int i = 0; i < mesh.stl.stats.number_of_facets; ++ i) { + for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) { const stl_facet &facet = mesh.stl.facet_start[i]; for (int j = 0; j < 3; ++ j) - this->push_geometry(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z, facet.normal.x, facet.normal.y, facet.normal.z); + this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); } } @@ -57,10 +53,10 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh) this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); unsigned int vertices_count = 0; - for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { + for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) { const stl_facet &facet = mesh.stl.facet_start[i]; for (int j = 0; j < 3; ++j) - this->push_geometry(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z, facet.normal.x, facet.normal.y, facet.normal.z); + this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2); vertices_count += 3; @@ -198,18 +194,28 @@ const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; +const float GLVolume::DISABLED_COLOR[4] = { 0.25f, 0.25f, 0.25f, 1.0f }; GLVolume::GLVolume(float r, float g, float b, float a) - : m_angle_z(0.0f) - , m_scale_factor(1.0f) +#if ENABLE_MODELVOLUME_TRANSFORM + : m_transformed_bounding_box_dirty(true) +#else + : m_offset(Vec3d::Zero()) + , m_rotation(Vec3d::Zero()) + , m_scaling_factor(Vec3d::Ones()) + , m_mirror(Vec3d::Ones()) + , m_world_matrix(Transform3f::Identity()) + , m_world_matrix_dirty(true) , m_transformed_bounding_box_dirty(true) +#endif // ENABLE_MODELVOLUME_TRANSFORM , m_transformed_convex_hull_bounding_box_dirty(true) , m_convex_hull(nullptr) - , composite_id(-1) - , select_group_id(-1) - , drag_group_id(-1) + , object_id(-1) + , volume_id(-1) + , instance_id(-1) , extruder_id(0) , selected(false) + , disabled(false) , is_active(true) , zoom_to_volumes(true) , shader_outside_printer_detection_enabled(false) @@ -239,7 +245,7 @@ void GLVolume::set_render_color(float r, float g, float b, float a) void GLVolume::set_render_color(const float* rgba, unsigned int size) { size = std::min((unsigned int)4, size); - for (int i = 0; i < size; ++i) + for (unsigned int i = 0; i < size; ++i) { render_color[i] = rgba[i]; } @@ -251,82 +257,156 @@ void GLVolume::set_render_color() set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); else if (hover) set_render_color(HOVER_COLOR, 4); + else if (disabled) + set_render_color(DISABLED_COLOR, 4); else if (is_outside && shader_outside_printer_detection_enabled) set_render_color(OUTSIDE_COLOR, 4); else set_render_color(color, 4); } -const Pointf3& GLVolume::get_origin() const +#if !ENABLE_MODELVOLUME_TRANSFORM +const Vec3d& GLVolume::get_rotation() const { - return m_origin; + return m_rotation; } -void GLVolume::set_origin(const Pointf3& origin) +void GLVolume::set_rotation(const Vec3d& rotation) { - if (m_origin != origin) + static const double TWO_PI = 2.0 * (double)PI; + + if (m_rotation != rotation) { - m_origin = origin; + m_rotation = rotation; + for (int i = 0; i < 3; ++i) + { + while (m_rotation(i) < 0.0) + { + m_rotation(i) += TWO_PI; + } + while (TWO_PI < m_rotation(i)) + { + m_rotation(i) -= TWO_PI; + } + } + m_world_matrix_dirty = true; m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } } -void GLVolume::set_angle_z(float angle_z) +const Vec3d& GLVolume::get_offset() const { - if (m_angle_z != angle_z) + return m_offset; +} + +void GLVolume::set_offset(const Vec3d& offset) +{ + if (m_offset != offset) { - m_angle_z = angle_z; + m_offset = offset; + m_world_matrix_dirty = true; m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } } -void GLVolume::set_scale_factor(float scale_factor) +const Vec3d& GLVolume::get_scaling_factor() const { - if (m_scale_factor != scale_factor) + return m_scaling_factor; +} + +void GLVolume::set_scaling_factor(const Vec3d& scaling_factor) +{ + if (m_scaling_factor != scaling_factor) { - m_scale_factor = scale_factor; + m_scaling_factor = scaling_factor; + m_world_matrix_dirty = true; m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } } +const Vec3d& GLVolume::get_mirror() const +{ + return m_mirror; +} + +double GLVolume::get_mirror(Axis axis) const +{ + return m_mirror(axis); +} + +void GLVolume::set_mirror(const Vec3d& mirror) +{ + if (m_mirror != mirror) + { + m_mirror = mirror; + m_world_matrix_dirty = true; + m_transformed_bounding_box_dirty = true; + m_transformed_convex_hull_bounding_box_dirty = true; + } +} + +void GLVolume::set_mirror(Axis axis, double mirror) +{ + if (m_mirror(axis) != mirror) + { + m_mirror(axis) = mirror; + m_world_matrix_dirty = true; + m_transformed_bounding_box_dirty = true; + m_transformed_convex_hull_bounding_box_dirty = true; + } +} +#endif // !ENABLE_MODELVOLUME_TRANSFORM + void GLVolume::set_convex_hull(const TriangleMesh& convex_hull) { m_convex_hull = &convex_hull; } -std::vector GLVolume::world_matrix() const +#if !ENABLE_MODELVOLUME_TRANSFORM +const Transform3f& GLVolume::world_matrix() const { - std::vector world_mat(UNIT_MATRIX, std::end(UNIT_MATRIX)); - Eigen::Transform m = Eigen::Transform::Identity(); - m.translate(Eigen::Vector3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z)); - m.rotate(Eigen::AngleAxisf(m_angle_z, Eigen::Vector3f::UnitZ())); - m.scale(m_scale_factor); - ::memcpy((void*)world_mat.data(), (const void*)m.data(), 16 * sizeof(float)); - return world_mat; + if (m_world_matrix_dirty) + { + m_world_matrix = Geometry::assemble_transform(m_offset, m_rotation, m_scaling_factor, m_mirror).cast(); + m_world_matrix_dirty = false; + } + return m_world_matrix; } +#endif // !ENABLE_MODELVOLUME_TRANSFORM -BoundingBoxf3 GLVolume::transformed_bounding_box() const +const BoundingBoxf3& GLVolume::transformed_bounding_box() const { if (m_transformed_bounding_box_dirty) { +#if ENABLE_MODELVOLUME_TRANSFORM m_transformed_bounding_box = bounding_box.transformed(world_matrix()); +#else + m_transformed_bounding_box = bounding_box.transformed(world_matrix().cast()); +#endif // ENABLE_MODELVOLUME_TRANSFORM m_transformed_bounding_box_dirty = false; } return m_transformed_bounding_box; } -BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box() const +const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const { if (m_transformed_convex_hull_bounding_box_dirty) { +#if ENABLE_MODELVOLUME_TRANSFORM if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0)) m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix()); else m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix()); +#else + if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0)) + m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix().cast()); + else + m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix().cast()); +#endif // ENABLE_MODELVOLUME_TRANSFORM m_transformed_convex_hull_bounding_box_dirty = false; } @@ -376,9 +456,12 @@ void GLVolume::render() const ::glCullFace(GL_BACK); ::glPushMatrix(); - ::glTranslated(m_origin.x, m_origin.y, m_origin.z); - ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f); - ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor); + +#if ENABLE_MODELVOLUME_TRANSFORM + ::glMultMatrixd(world_matrix().data()); +#else + ::glMultMatrixf(world_matrix().data()); +#endif // ENABLE_MODELVOLUME_TRANSFORM if (this->indexed_vertex_array.indexed()) this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); else @@ -410,13 +493,17 @@ void GLVolume::render_using_layer_height() const glUniform1f(z_texture_row_to_normalized_id, (GLfloat)(1.0f / layer_height_texture_height())); if (z_cursor_id >= 0) - glUniform1f(z_cursor_id, (GLfloat)(layer_height_texture_data.print_object->model_object()->bounding_box().max.z * layer_height_texture_data.z_cursor_relative)); + glUniform1f(z_cursor_id, (GLfloat)(layer_height_texture_data.print_object->model_object()->bounding_box().max(2) * layer_height_texture_data.z_cursor_relative)); if (z_cursor_band_width_id >= 0) glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width); if (world_matrix_id >= 0) +#if ENABLE_MODELVOLUME_TRANSFORM + ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast().data()); +#else ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); +#endif // ENABLE_MODELVOLUME_TRANSFORM GLsizei w = (GLsizei)layer_height_texture_width(); GLsizei h = (GLsizei)layer_height_texture_height(); @@ -470,13 +557,17 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glUniform4fv(color_id, 1, (const GLfloat*)color); } else - ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + ::glColor4fv(render_color); if (detection_id != -1) ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0); if (worldmatrix_id != -1) +#if ENABLE_MODELVOLUME_TRANSFORM + ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast().data()); +#else ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); +#endif // ENABLE_MODELVOLUME_TRANSFORM render(); @@ -489,22 +580,29 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c if (color_id >= 0) ::glUniform4fv(color_id, 1, (const GLfloat*)render_color); else - ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + ::glColor4fv(render_color); if (detection_id != -1) ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0); if (worldmatrix_id != -1) +#if ENABLE_MODELVOLUME_TRANSFORM + ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast().data()); +#else ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); +#endif // ENABLE_MODELVOLUME_TRANSFORM ::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); ::glPushMatrix(); - ::glTranslated(m_origin.x, m_origin.y, m_origin.z); - ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f); - ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor); + +#if ENABLE_MODELVOLUME_TRANSFORM + ::glMultMatrixd(world_matrix().data()); +#else + ::glMultMatrixf(world_matrix().data()); +#endif // ENABLE_MODELVOLUME_TRANSFORM if (n_triangles > 0) { @@ -533,7 +631,7 @@ void GLVolume::render_legacy() const ::glDisableClientState(GL_VERTEX_ARRAY); ::glDisableClientState(GL_NORMAL_ARRAY); - ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + ::glColor4fv(render_color); render(); ::glEnableClientState(GL_VERTEX_ARRAY); @@ -542,14 +640,17 @@ void GLVolume::render_legacy() const return; } - ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + ::glColor4fv(render_color); ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3); ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data()); ::glPushMatrix(); - ::glTranslated(m_origin.x, m_origin.y, m_origin.z); - ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f); - ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor); + +#if ENABLE_MODELVOLUME_TRANSFORM + ::glMultMatrixd(world_matrix().data()); +#else + ::glMultMatrixf(world_matrix().data()); +#endif // ENABLE_MODELVOLUME_TRANSFORM if (n_triangles > 0) ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first); @@ -562,10 +663,10 @@ void GLVolume::render_legacy() const double GLVolume::layer_height_texture_z_to_row_id() const { - return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max.z); + return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max(2)); } -void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool force) +void GLVolume::generate_layer_height_texture(const PrintObject *print_object, bool force) { LayersTexture *tex = this->layer_height_texture.get(); if (tex == nullptr) @@ -573,7 +674,7 @@ void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool for return; // Always try to update the layer height profile. - bool update = print_object->update_layer_height_profile(print_object->model_object()->layer_height_profile) || force; + bool update = print_object->update_layer_height_profile(const_cast(print_object->model_object())->layer_height_profile) || force; // Update if the layer height profile was changed, or when the texture is not valid. if (! update && ! tex->data.empty() && tex->cells > 0) // Texture is valid, don't update. @@ -599,12 +700,10 @@ void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool for #define LAYER_HEIGHT_TEXTURE_HEIGHT 1024 std::vector GLVolumeCollection::load_object( - const ModelObject *model_object, + const ModelObject *model_object, int obj_idx, const std::vector &instance_idxs, const std::string &color_by, - const std::string &select_by, - const std::string &drag_by, bool use_VBOs) { static float colors[4][4] = { @@ -631,7 +730,11 @@ std::vector GLVolumeCollection::load_object( for (int instance_idx : instance_idxs) { const ModelInstance *instance = model_object->instances[instance_idx]; +#if ENABLE_MODELVOLUME_TRANSFORM + const TriangleMesh& mesh = model_volume->mesh; +#else TriangleMesh mesh = model_volume->mesh; +#endif // ENABLE_MODELVOLUME_TRANSFORM volumes_idx.push_back(int(this->volumes.size())); float color[4]; memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); @@ -655,18 +758,9 @@ std::vector GLVolumeCollection::load_object( // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); - v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; - if (select_by == "object") - v.select_group_id = obj_idx * 1000000; - else if (select_by == "volume") - v.select_group_id = obj_idx * 1000000 + volume_idx * 1000; - else if (select_by == "instance") - v.select_group_id = v.composite_id; - if (drag_by == "object") - v.drag_group_id = obj_idx * 1000; - else if (drag_by == "instance") - v.drag_group_id = obj_idx * 1000 + instance_idx; - + v.object_id = obj_idx; + v.volume_id = volume_idx; + v.instance_id = instance_idx; if (model_volume->is_model_part()) { v.set_convex_hull(model_volume->get_convex_hull()); @@ -676,15 +770,76 @@ std::vector GLVolumeCollection::load_object( } v.is_modifier = ! model_volume->is_model_part(); v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); - v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0)); - v.set_angle_z(instance->rotation); - v.set_scale_factor(instance->scaling_factor); +#if ENABLE_MODELVOLUME_TRANSFORM + v.set_instance_transformation(instance->get_transformation()); + v.set_volume_transformation(model_volume->get_transformation()); +#else + v.set_offset(instance->get_offset()); + v.set_rotation(instance->get_rotation()); + v.set_scaling_factor(instance->get_scaling_factor()); + v.set_mirror(instance->get_mirror()); +#endif // ENABLE_MODELVOLUME_TRANSFORM } } return volumes_idx; } +// Load SLA auxiliary GLVolumes (for support trees or pad). +std::vector GLVolumeCollection::load_object_auxiliary( + const ModelObject *model_object, + const SLAPrintObject *print_object, + int obj_idx, + SLAPrintObjectStep milestone, + bool use_VBOs) +{ + std::vector volumes_idx; + // Find the SLAPrintObject's instance to it. + if (print_object->is_step_done(milestone)) { + // Get the support mesh. + TriangleMesh mesh; + switch (milestone) { + case slaposSupportTree: mesh = print_object->support_mesh(); break; + case slaposBasePool: mesh = print_object->pad_mesh(); break; + default: + assert(false); + } + // Convex hull is required for out of print bed detection. + TriangleMesh convex_hull = mesh.convex_hull_3d(); + const std::vector &instances = print_object->instances(); + std::map map_instances; + for (int i = 0; i < (int)model_object->instances.size(); ++ i) + map_instances[model_object->instances[i]->id()] = i; + for (const SLAPrintObject::Instance &instance : instances) { + auto model_instance_it = map_instances.find(instance.instance_id); + assert(model_instance_it != map_instances.end()); + const int instance_idx = model_instance_it->second; + const ModelInstance *model_instance = model_object->instances[instance_idx]; + volumes_idx.push_back(int(this->volumes.size())); + float color[4] { 0.f, 0.f, 1.f, 1.f }; + this->volumes.emplace_back(new GLVolume(color)); + GLVolume &v = *this->volumes.back(); + if (use_VBOs) + v.indexed_vertex_array.load_mesh_full_shading(mesh); + else + v.indexed_vertex_array.load_mesh_flat_shading(mesh); + // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). + v.bounding_box = v.indexed_vertex_array.bounding_box(); + v.indexed_vertex_array.finalize_geometry(use_VBOs); + v.object_id = obj_idx; + v.volume_id = -1; // SLA supports + v.instance_id = instance_idx; + v.set_convex_hull(convex_hull); + v.is_modifier = false; + v.shader_outside_printer_detection_enabled = true; + v.set_instance_transformation(model_instance->get_transformation()); + // Leave the volume transformation at identity. + // v.set_volume_transformation(model_volume->get_transformation()); + } + } + + return volumes_idx; +} int GLVolumeCollection::load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width) @@ -707,16 +862,16 @@ int GLVolumeCollection::load_wipe_tower_preview( // We'll now create the box with jagged edge. y-coordinates of the pre-generated model are shifted so that the front // edge has y=0 and centerline of the back edge has y=depth: Pointf3s points; - std::vector facets; - float out_points_idx[][3] = {{0, -depth, 0}, {0, 0, 0}, {38.453, 0, 0}, {61.547, 0, 0}, {100, 0, 0}, {100, -depth, 0}, {55.7735, -10, 0}, {44.2265, 10, 0}, - {38.453, 0, 1}, {0, 0, 1}, {0, -depth, 1}, {100, -depth, 1}, {100, 0, 1}, {61.547, 0, 1}, {55.7735, -10, 1}, {44.2265, 10, 1}}; - int out_facets_idx[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 5, 0}, {3, 5, 6}, {6, 2, 7}, {6, 0, 2}, {8, 9, 10}, {11, 12, 13}, {10, 11, 14}, {14, 11, 13}, {15, 8, 14}, + std::vector facets; + float out_points_idx[][3] = { { 0, -depth, 0 }, { 0, 0, 0 }, { 38.453f, 0, 0 }, { 61.547f, 0, 0 }, { 100.0f, 0, 0 }, { 100.0f, -depth, 0 }, { 55.7735f, -10.0f, 0 }, { 44.2265f, 10.0f, 0 }, + { 38.453f, 0, 1 }, { 0, 0, 1 }, { 0, -depth, 1 }, { 100.0f, -depth, 1 }, { 100.0f, 0, 1 }, { 61.547f, 0, 1 }, { 55.7735f, -10.0f, 1 }, { 44.2265f, 10.0f, 1 } }; + int out_facets_idx[][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 }, {8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8}, {0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11}}; for (int i=0;i<16;++i) - points.push_back(Pointf3(out_points_idx[i][0] / (100.f/min_width), out_points_idx[i][1] + depth, out_points_idx[i][2])); + points.push_back(Vec3d(out_points_idx[i][0] / (100.f/min_width), out_points_idx[i][1] + depth, out_points_idx[i][2])); for (int i=0;i<28;++i) - facets.push_back(Point3(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2])); + facets.push_back(Vec3crd(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2])); TriangleMesh tooth_mesh(points, facets); // We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to @@ -727,7 +882,7 @@ int GLVolumeCollection::load_wipe_tower_preview( tooth_mesh.translate(min_width, 0.f, 0.f); } - mesh.scale(Pointf3(width/(n*min_width), 1.f, height)); // Scaling to proper width + mesh.scale(Vec3d(width/(n*min_width), 1.f, height)); // Scaling to proper width } else mesh = make_cube(width, depth, height); @@ -747,14 +902,18 @@ int GLVolumeCollection::load_wipe_tower_preview( else v.indexed_vertex_array.load_mesh_flat_shading(mesh); - v.set_origin(Pointf3(pos_x, pos_y, 0.)); +#if ENABLE_MODELVOLUME_TRANSFORM + v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); +#else + v.set_offset(Vec3d(pos_x, pos_y, 0.0)); +#endif // ENABLE_MODELVOLUME_TRANSFORM // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); - v.composite_id = obj_idx * 1000000; - v.select_group_id = obj_idx * 1000000; - v.drag_group_id = obj_idx * 1000; + v.object_id = obj_idx; + v.volume_id = 0; + v.instance_id = 0; v.is_wipe_tower = true; v.shader_outside_printer_detection_enabled = ! size_unknown; return int(this->volumes.size() - 1); @@ -833,9 +992,9 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M return false; BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); - BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height"))); + BoundingBoxf3 print_volume(Vec3d(unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0), Vec3d(unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config->opt_float("max_print_height"))); // Allow the objects to protrude below the print bed - print_volume.min.z = -1e10; + print_volume.min(2) = -1e10; ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside; bool all_contained = true; @@ -936,7 +1095,7 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con continue; int extruder_id = volume->extruder_id - 1; - if ((extruder_id < 0) || ((unsigned int)colors.size() <= extruder_id)) + if ((extruder_id < 0) || ((int)colors.size() <= extruder_id)) extruder_id = 0; const Color& color = colors[extruder_id]; @@ -998,8 +1157,8 @@ static void thick_lines_to_indexed_vertex_array( // right, left, top, bottom int idx_prev[4] = { -1, -1, -1, -1 }; double bottom_z_prev = 0.; - Pointf b1_prev; - Vectorf v_prev; + Vec2d b1_prev(Vec2d::Zero()); + Vec2d v_prev(Vec2d::Zero()); int idx_initial[4] = { -1, -1, -1, -1 }; double width_initial = 0.; double bottom_z_initial = 0.0; @@ -1009,7 +1168,7 @@ static void thick_lines_to_indexed_vertex_array( for (size_t ii = 0; ii < lines_end; ++ ii) { size_t i = (ii == lines.size()) ? 0 : ii; const Line &line = lines[i]; - double len = unscale(line.length()); + double len = unscale(line.length()); double inv_len = 1.0 / len; double bottom_z = top_z - heights[i]; double middle_z = 0.5 * (top_z + bottom_z); @@ -1019,29 +1178,29 @@ static void thick_lines_to_indexed_vertex_array( bool is_last = (ii == lines_end - 1); bool is_closing = closed && is_last; - Vectorf v = Vectorf::new_unscale(line.vector()); - v.scale(inv_len); + Vec2d v = unscale(line.vector()); + v *= inv_len; - Pointf a = Pointf::new_unscale(line.a); - Pointf b = Pointf::new_unscale(line.b); - Pointf a1 = a; - Pointf a2 = a; - Pointf b1 = b; - Pointf b2 = b; + Vec2d a = unscale(line.a); + Vec2d b = unscale(line.b); + Vec2d a1 = a; + Vec2d a2 = a; + Vec2d b1 = b; + Vec2d b2 = b; { double dist = 0.5 * width; // scaled - double dx = dist * v.x; - double dy = dist * v.y; - a1.translate(+dy, -dx); - a2.translate(-dy, +dx); - b1.translate(+dy, -dx); - b2.translate(-dy, +dx); + double dx = dist * v(0); + double dy = dist * v(1); + a1 += Vec2d(+dy, -dx); + a2 += Vec2d(-dy, +dx); + b1 += Vec2d(+dy, -dx); + b2 += Vec2d(-dy, +dx); } // calculate new XY normals Vector n = line.normal(); - Vectorf3 xy_right_normal = Vectorf3::new_unscale(n.x, n.y, 0); - xy_right_normal.scale(inv_len); + Vec3d xy_right_normal = unscale(n(0), n(1), 0); + xy_right_normal *= inv_len; int idx_a[4]; int idx_b[4]; @@ -1059,7 +1218,7 @@ static void thick_lines_to_indexed_vertex_array( // Share top / bottom vertices if possible. if (is_first) { idx_a[TOP] = idx_last++; - volume.push_geometry(a.x, a.y, top_z , 0., 0., 1.); + volume.push_geometry(a(0), a(1), top_z , 0., 0., 1.); } else { idx_a[TOP] = idx_prev[TOP]; } @@ -1067,11 +1226,11 @@ static void thick_lines_to_indexed_vertex_array( if (is_first || bottom_z_different) { // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z. idx_a[BOTTOM] = idx_last ++; - volume.push_geometry(a.x, a.y, bottom_z, 0., 0., -1.); + volume.push_geometry(a(0), a(1), bottom_z, 0., 0., -1.); idx_a[LEFT ] = idx_last ++; - volume.push_geometry(a2.x, a2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z); + volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); idx_a[RIGHT] = idx_last ++; - volume.push_geometry(a1.x, a1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z); + volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); } else { idx_a[BOTTOM] = idx_prev[BOTTOM]; @@ -1085,16 +1244,16 @@ static void thick_lines_to_indexed_vertex_array( } else { // Continuing a previous segment. // Share left / right vertices if possible. - double v_dot = dot(v_prev, v); + double v_dot = v_prev.dot(v); bool sharp = v_dot < 0.707; // sin(45 degrees) if (sharp) { if (!bottom_z_different) { // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. idx_a[RIGHT] = idx_last++; - volume.push_geometry(a1.x, a1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z); + volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); idx_a[LEFT] = idx_last++; - volume.push_geometry(a2.x, a2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z); + volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); } } if (v_dot > 0.9) { @@ -1110,32 +1269,32 @@ static void thick_lines_to_indexed_vertex_array( { // Create a sharp corner with an overshot and average the left / right normals. // At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc. - Pointf intersection; + Vec2d intersection(Vec2d::Zero()); Geometry::ray_ray_intersection(b1_prev, v_prev, a1, v, intersection); a1 = intersection; a2 = 2. * a - intersection; - assert(length(a1.vector_to(a)) < width); - assert(length(a2.vector_to(a)) < width); + assert((a - a1).norm() < width); + assert((a - a2).norm() < width); float *n_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6; float *p_left_prev = n_left_prev + 3; float *n_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6; float *p_right_prev = n_right_prev + 3; - p_left_prev [0] = float(a2.x); - p_left_prev [1] = float(a2.y); - p_right_prev[0] = float(a1.x); - p_right_prev[1] = float(a1.y); - xy_right_normal.x += n_right_prev[0]; - xy_right_normal.y += n_right_prev[1]; - xy_right_normal.scale(1. / length(xy_right_normal)); - n_left_prev [0] = float(-xy_right_normal.x); - n_left_prev [1] = float(-xy_right_normal.y); - n_right_prev[0] = float( xy_right_normal.x); - n_right_prev[1] = float( xy_right_normal.y); + p_left_prev [0] = float(a2(0)); + p_left_prev [1] = float(a2(1)); + p_right_prev[0] = float(a1(0)); + p_right_prev[1] = float(a1(1)); + xy_right_normal(0) += n_right_prev[0]; + xy_right_normal(1) += n_right_prev[1]; + xy_right_normal *= 1. / xy_right_normal.norm(); + n_left_prev [0] = float(-xy_right_normal(0)); + n_left_prev [1] = float(-xy_right_normal(1)); + n_right_prev[0] = float( xy_right_normal(0)); + n_right_prev[1] = float( xy_right_normal(1)); idx_a[LEFT ] = idx_prev[LEFT ]; idx_a[RIGHT] = idx_prev[RIGHT]; } } - else if (cross(v_prev, v) > 0.) { + else if (cross2(v_prev, v) > 0.) { // Right turn. Fill in the right turn wedge. volume.push_triangle(idx_prev[RIGHT], idx_a [RIGHT], idx_prev[TOP] ); volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a [RIGHT] ); @@ -1171,20 +1330,20 @@ static void thick_lines_to_indexed_vertex_array( idx_b[TOP] = idx_initial[TOP]; } else { idx_b[TOP] = idx_last ++; - volume.push_geometry(b.x, b.y, top_z , 0., 0., 1.); + volume.push_geometry(b(0), b(1), top_z , 0., 0., 1.); } if (is_closing && (width == width_initial) && (bottom_z == bottom_z_initial)) { idx_b[BOTTOM] = idx_initial[BOTTOM]; } else { idx_b[BOTTOM] = idx_last ++; - volume.push_geometry(b.x, b.y, bottom_z, 0., 0., -1.); + volume.push_geometry(b(0), b(1), bottom_z, 0., 0., -1.); } // Generate new vertices for the end of this line segment. idx_b[LEFT ] = idx_last ++; - volume.push_geometry(b2.x, b2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z); + volume.push_geometry(b2(0), b2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); idx_b[RIGHT ] = idx_last ++; - volume.push_geometry(b1.x, b1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z); + volume.push_geometry(b1(0), b1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); memcpy(idx_prev, idx_b, 4 * sizeof(int)); bottom_z_prev = bottom_z; @@ -1243,15 +1402,15 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, int idx_initial[4] = { -1, -1, -1, -1 }; int idx_prev[4] = { -1, -1, -1, -1 }; double z_prev = 0.0; - Vectorf3 n_right_prev; - Vectorf3 n_top_prev; - Vectorf3 unit_v_prev; + Vec3d n_right_prev = Vec3d::Zero(); + Vec3d n_top_prev = Vec3d::Zero(); + Vec3d unit_v_prev = Vec3d::Zero(); double width_initial = 0.0; // new vertices around the line endpoints // left, right, top, bottom - Pointf3 a[4]; - Pointf3 b[4]; + Vec3d a[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; + Vec3d b[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; // loop once more in case of closed loops size_t lines_end = closed ? (lines.size() + 1) : lines.size(); @@ -1263,29 +1422,29 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, double height = heights[i]; double width = widths[i]; - Vectorf3 unit_v = normalize(Vectorf3::new_unscale(line.vector())); + Vec3d unit_v = unscale(line.vector()).normalized(); - Vectorf3 n_top; - Vectorf3 n_right; - Vectorf3 unit_positive_z(0.0, 0.0, 1.0); + Vec3d n_top = Vec3d::Zero(); + Vec3d n_right = Vec3d::Zero(); + Vec3d unit_positive_z(0.0, 0.0, 1.0); - if ((line.a.x == line.b.x) && (line.a.y == line.b.y)) + if ((line.a(0) == line.b(0)) && (line.a(1) == line.b(1))) { // vertical segment - n_right = (line.a.z < line.b.z) ? Vectorf3(-1.0, 0.0, 0.0) : Vectorf3(1.0, 0.0, 0.0); - n_top = Vectorf3(0.0, 1.0, 0.0); + n_right = (line.a(2) < line.b(2)) ? Vec3d(-1.0, 0.0, 0.0) : Vec3d(1.0, 0.0, 0.0); + n_top = Vec3d(0.0, 1.0, 0.0); } else { // generic segment - n_right = normalize(cross(unit_v, unit_positive_z)); - n_top = normalize(cross(n_right, unit_v)); + n_right = unit_v.cross(unit_positive_z).normalized(); + n_top = n_right.cross(unit_v).normalized(); } - Vectorf3 rl_displacement = 0.5 * width * n_right; - Vectorf3 tb_displacement = 0.5 * height * n_top; - Pointf3 l_a = Pointf3::new_unscale(line.a); - Pointf3 l_b = Pointf3::new_unscale(line.b); + Vec3d rl_displacement = 0.5 * width * n_right; + Vec3d tb_displacement = 0.5 * height * n_top; + Vec3d l_a = unscale(line.a); + Vec3d l_b = unscale(line.b); a[RIGHT] = l_a + rl_displacement; a[LEFT] = l_a - rl_displacement; @@ -1296,15 +1455,15 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, b[TOP] = l_b + tb_displacement; b[BOTTOM] = l_b - tb_displacement; - Vectorf3 n_bottom = -n_top; - Vectorf3 n_left = -n_right; + Vec3d n_bottom = -n_top; + Vec3d n_left = -n_right; int idx_a[4]; int idx_b[4]; int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); - bool z_different = (z_prev != l_a.z); - z_prev = l_b.z; + bool z_different = (z_prev != l_a(2)); + z_prev = l_b(2); // Share top / bottom vertices if possible. if (ii == 0) @@ -1338,9 +1497,9 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, { // Continuing a previous segment. // Share left / right vertices if possible. - double v_dot = dot(unit_v_prev, unit_v); + double v_dot = unit_v_prev.dot(unit_v); bool is_sharp = v_dot < 0.707; // sin(45 degrees) - bool is_right_turn = dot(n_top_prev, cross(unit_v_prev, unit_v)) > 0.0; + bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0; if (is_sharp) { @@ -1363,9 +1522,9 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, // At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc. // averages normals - Vectorf3 average_n_right = normalize(0.5 * (n_right + n_right_prev)); - Vectorf3 average_n_left = -average_n_right; - Vectorf3 average_rl_displacement = 0.5 * width * average_n_right; + Vec3d average_n_right = 0.5 * (n_right + n_right_prev).normalized(); + Vec3d average_n_left = -average_n_right; + Vec3d average_rl_displacement = 0.5 * width * average_n_right; // updates vertices around a a[RIGHT] = l_a + average_rl_displacement; @@ -1373,25 +1532,25 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, // updates previous line normals float* normal_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT] * 6; - normal_left_prev[0] = float(average_n_left.x); - normal_left_prev[1] = float(average_n_left.y); - normal_left_prev[2] = float(average_n_left.z); + normal_left_prev[0] = float(average_n_left(0)); + normal_left_prev[1] = float(average_n_left(1)); + normal_left_prev[2] = float(average_n_left(2)); float* normal_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6; - normal_right_prev[0] = float(average_n_right.x); - normal_right_prev[1] = float(average_n_right.y); - normal_right_prev[2] = float(average_n_right.z); + normal_right_prev[0] = float(average_n_right(0)); + normal_right_prev[1] = float(average_n_right(1)); + normal_right_prev[2] = float(average_n_right(2)); // updates previous line's vertices around b float* b_left_prev = normal_left_prev + 3; - b_left_prev[0] = float(a[LEFT].x); - b_left_prev[1] = float(a[LEFT].y); - b_left_prev[2] = float(a[LEFT].z); + b_left_prev[0] = float(a[LEFT](0)); + b_left_prev[1] = float(a[LEFT](1)); + b_left_prev[2] = float(a[LEFT](2)); float* b_right_prev = normal_right_prev + 3; - b_right_prev[0] = float(a[RIGHT].x); - b_right_prev[1] = float(a[RIGHT].y); - b_right_prev[2] = float(a[RIGHT].z); + b_right_prev[0] = float(a[RIGHT](0)); + b_right_prev[1] = float(a[RIGHT](1)); + b_right_prev[2] = float(a[RIGHT](2)); idx_a[LEFT] = idx_prev[LEFT]; idx_a[RIGHT] = idx_prev[RIGHT]; @@ -1488,14 +1647,14 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, #undef BOTTOM } -static void point_to_indexed_vertex_array(const Point3& point, +static void point_to_indexed_vertex_array(const Vec3crd& point, double width, double height, GLIndexedVertexArray& volume) { // builds a double piramid, with vertices on the local axes, around the point - Pointf3 center = Pointf3::new_unscale(point); + Vec3d center = unscale(point); double scale_factor = 1.0; double w = scale_factor * width; @@ -1509,13 +1668,13 @@ static void point_to_indexed_vertex_array(const Point3& point, idxs[i] = idx_last + i; } - Vectorf3 displacement_x(w, 0.0, 0.0); - Vectorf3 displacement_y(0.0, w, 0.0); - Vectorf3 displacement_z(0.0, 0.0, h); + Vec3d displacement_x(w, 0.0, 0.0); + Vec3d displacement_y(0.0, w, 0.0); + Vec3d displacement_z(0.0, 0.0, h); - Vectorf3 unit_x(1.0, 0.0, 0.0); - Vectorf3 unit_y(0.0, 1.0, 0.0); - Vectorf3 unit_z(0.0, 0.0, 1.0); + Vec3d unit_x(1.0, 0.0, 0.0); + Vec3d unit_y(0.0, 1.0, 0.0); + Vec3d unit_z(0.0, 0.0, 1.0); // vertices volume.push_geometry(center - displacement_x, -unit_x); // idxs[0] @@ -1558,7 +1717,7 @@ void _3DScene::thick_lines_to_verts(const Lines3& lines, thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, volume.indexed_vertex_array); } -static void thick_point_to_verts(const Point3& point, +static void thick_point_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume) @@ -1648,7 +1807,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, if (extrusion_entity_collection != nullptr) extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume); else { - CONFESS("Unexpected extrusion_entity type in to_verts()"); + throw std::runtime_error("Unexpected extrusion_entity type in to_verts()"); } } } @@ -1664,7 +1823,7 @@ void _3DScene::polyline3_to_verts(const Polyline3& polyline, double width, doubl thick_lines_to_verts(lines, widths, heights, false, volume); } -void _3DScene::point3_to_verts(const Point3& point, double width, double height, GLVolume& volume) +void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume) { thick_point_to_verts(point, width, height, volume); } @@ -1721,39 +1880,14 @@ void _3DScene::reset_volumes(wxGLCanvas* canvas) s_canvas_mgr.reset_volumes(canvas); } -void _3DScene::deselect_volumes(wxGLCanvas* canvas) -{ - s_canvas_mgr.deselect_volumes(canvas); -} - -void _3DScene::select_volume(wxGLCanvas* canvas, unsigned int id) -{ - s_canvas_mgr.select_volume(canvas, id); -} - -void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections) -{ - s_canvas_mgr.update_volumes_selection(canvas, selections); -} - int _3DScene::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) { return s_canvas_mgr.check_volumes_outside_state(canvas, config); } -bool _3DScene::move_volume_up(wxGLCanvas* canvas, unsigned int id) +GUI::GLCanvas3D* _3DScene::get_canvas(wxGLCanvas* canvas) { - return s_canvas_mgr.move_volume_up(canvas, id); -} - -bool _3DScene::move_volume_down(wxGLCanvas* canvas, unsigned int id) -{ - return s_canvas_mgr.move_volume_down(canvas, id); -} - -void _3DScene::set_objects_selections(wxGLCanvas* canvas, const std::vector& selections) -{ - s_canvas_mgr.set_objects_selections(canvas, selections); + return s_canvas_mgr.get_canvas(canvas); } void _3DScene::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) @@ -1766,6 +1900,11 @@ void _3DScene::set_print(wxGLCanvas* canvas, Print* print) s_canvas_mgr.set_print(canvas, print); } +void _3DScene::set_SLA_print(wxGLCanvas* canvas, SLAPrint* print) +{ + s_canvas_mgr.set_SLA_print(canvas, print); +} + void _3DScene::set_model(wxGLCanvas* canvas, Model* model) { s_canvas_mgr.set_model(canvas, model); @@ -1773,42 +1912,12 @@ void _3DScene::set_model(wxGLCanvas* canvas, Model* model) void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { - return s_canvas_mgr.set_bed_shape(canvas, shape); -} - -void _3DScene::set_auto_bed_shape(wxGLCanvas* canvas) -{ - return s_canvas_mgr.set_auto_bed_shape(canvas); -} - -BoundingBoxf3 _3DScene::get_volumes_bounding_box(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_volumes_bounding_box(canvas); -} - -void _3DScene::set_axes_length(wxGLCanvas* canvas, float length) -{ - s_canvas_mgr.set_axes_length(canvas, length); -} - -void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) -{ - return s_canvas_mgr.set_cutting_plane(canvas, z, polygons); + s_canvas_mgr.set_bed_shape(canvas, shape); } void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value) { - return s_canvas_mgr.set_color_by(canvas, value); -} - -void _3DScene::set_select_by(wxGLCanvas* canvas, const std::string& value) -{ - return s_canvas_mgr.set_select_by(canvas, value); -} - -void _3DScene::set_drag_by(wxGLCanvas* canvas, const std::string& value) -{ - return s_canvas_mgr.set_drag_by(canvas, value); + s_canvas_mgr.set_color_by(canvas, value); } bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) @@ -1821,11 +1930,6 @@ bool _3DScene::is_layers_editing_allowed(wxGLCanvas* canvas) return s_canvas_mgr.is_layers_editing_allowed(canvas); } -bool _3DScene::is_shader_enabled(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_shader_enabled(canvas); -} - bool _3DScene::is_reload_delayed(wxGLCanvas* canvas) { return s_canvas_mgr.is_reload_delayed(canvas); @@ -1861,6 +1965,11 @@ void _3DScene::enable_gizmos(wxGLCanvas* canvas, bool enable) s_canvas_mgr.enable_gizmos(canvas, enable); } +void _3DScene::enable_toolbar(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_toolbar(canvas, enable); +} + void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_shader(canvas, enable); @@ -1881,6 +1990,16 @@ void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow) s_canvas_mgr.allow_multisample(canvas, allow); } +void _3DScene::enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable) +{ + s_canvas_mgr.enable_toolbar_item(canvas, name, enable); +} + +bool _3DScene::is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name) +{ + return s_canvas_mgr.is_toolbar_item_pressed(canvas, name); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); @@ -1906,16 +2025,16 @@ void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas) s_canvas_mgr.update_volumes_colors_by_extruder(canvas); } -void _3DScene::update_gizmos_data(wxGLCanvas* canvas) -{ - s_canvas_mgr.update_gizmos_data(canvas); -} - void _3DScene::render(wxGLCanvas* canvas) { s_canvas_mgr.render(canvas); } +void _3DScene::delete_selected(wxGLCanvas* canvas) +{ + s_canvas_mgr.delete_selected(canvas); +} + std::vector _3DScene::get_current_print_zs(wxGLCanvas* canvas, bool active_only) { return s_canvas_mgr.get_current_print_zs(canvas, active_only); @@ -1926,96 +2045,6 @@ void _3DScene::set_toolpaths_range(wxGLCanvas* canvas, double low, double high) s_canvas_mgr.set_toolpaths_range(canvas, low, high); } -void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); -} - -void _3DScene::register_on_double_click_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_double_click_callback(canvas, callback); -} - -void _3DScene::register_on_right_click_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_right_click_callback(canvas, callback); -} - -void _3DScene::register_on_select_object_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_select_object_callback(canvas, callback); -} - -void _3DScene::register_on_model_update_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_model_update_callback(canvas, callback); -} - -void _3DScene::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_remove_object_callback(canvas, callback); -} - -void _3DScene::register_on_arrange_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_arrange_callback(canvas, callback); -} - -void _3DScene::register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_rotate_object_left_callback(canvas, callback); -} - -void _3DScene::register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_rotate_object_right_callback(canvas, callback); -} - -void _3DScene::register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_scale_object_uniformly_callback(canvas, callback); -} - -void _3DScene::register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_increase_objects_callback(canvas, callback); -} - -void _3DScene::register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_decrease_objects_callback(canvas, callback); -} - -void _3DScene::register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_instance_moved_callback(canvas, callback); -} - -void _3DScene::register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_wipe_tower_moved_callback(canvas, callback); -} - -void _3DScene::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_enable_action_buttons_callback(canvas, callback); -} - -void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_gizmo_scale_uniformly_callback(canvas, callback); -} - -void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback); -} - -void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback); -} - static inline int hex_digit_to_int(const char c) { return @@ -2053,6 +2082,11 @@ std::vector _3DScene::load_object(wxGLCanvas* canvas, const Model* model, i return s_canvas_mgr.load_object(canvas, model, obj_idx); } +void _3DScene::mirror_selection(wxGLCanvas* canvas, Axis axis) +{ + s_canvas_mgr.mirror_selection(canvas, axis); +} + void _3DScene::reload_scene(wxGLCanvas* canvas, bool force) { s_canvas_mgr.reload_scene(canvas, force); @@ -2068,9 +2102,9 @@ void _3DScene::load_preview(wxGLCanvas* canvas, const std::vector& s_canvas_mgr.load_preview(canvas, str_tool_colors); } -void _3DScene::reset_legend_texture() +void _3DScene::reset_legend_texture(wxGLCanvas* canvas) { - s_canvas_mgr.reset_legend_texture(); + s_canvas_mgr.reset_legend_texture(canvas); } } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp similarity index 68% rename from xs/src/slic3r/GUI/3DScene.hpp rename to src/slic3r/GUI/3DScene.hpp index 5cd144c680..16523af686 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -1,13 +1,13 @@ #ifndef slic3r_3DScene_hpp_ #define slic3r_3DScene_hpp_ -#include "../../libslic3r/libslic3r.h" -#include "../../libslic3r/Point.hpp" -#include "../../libslic3r/Line.hpp" -#include "../../libslic3r/TriangleMesh.hpp" -#include "../../libslic3r/Utils.hpp" -#include "../../libslic3r/Model.hpp" -#include "../../slic3r/GUI/GLCanvas3DManager.hpp" +#include "libslic3r/libslic3r.h" +#include "libslic3r/Point.hpp" +#include "libslic3r/Line.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/Utils.hpp" +#include "libslic3r/Model.hpp" +#include "slic3r/GUI/GLCanvas3DManager.hpp" class wxBitmap; class wxWindow; @@ -16,6 +16,9 @@ namespace Slic3r { class Print; class PrintObject; +class SLAPrint; +class SLAPrintObject; +enum SLAPrintObjectStep : unsigned int; class Model; class ModelObject; class GCodePreviewData; @@ -119,8 +122,8 @@ public: push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz)); } - inline void push_geometry(const Pointf3& p, const Vectorf3& n) { - push_geometry(p.x, p.y, p.z, n.x, n.y, n.z); + inline void push_geometry(const Vec3d& p, const Vec3d& n) { + push_geometry(p(0), p(1), p(2), n(0), n(1), n(2)); } inline void push_triangle(int idx1, int idx2, int idx3) { @@ -176,17 +179,17 @@ public: BoundingBoxf3 bbox; if (! this->vertices_and_normals_interleaved.empty()) { bbox.defined = true; - bbox.min.x = bbox.max.x = this->vertices_and_normals_interleaved[3]; - bbox.min.y = bbox.max.y = this->vertices_and_normals_interleaved[4]; - bbox.min.z = bbox.max.z = this->vertices_and_normals_interleaved[5]; + bbox.min(0) = bbox.max(0) = this->vertices_and_normals_interleaved[3]; + bbox.min(1) = bbox.max(1) = this->vertices_and_normals_interleaved[4]; + bbox.min(2) = bbox.max(2) = this->vertices_and_normals_interleaved[5]; for (size_t i = 9; i < this->vertices_and_normals_interleaved.size(); i += 6) { const float *verts = this->vertices_and_normals_interleaved.data() + i; - bbox.min.x = std::min(bbox.min.x, verts[0]); - bbox.min.y = std::min(bbox.min.y, verts[1]); - bbox.min.z = std::min(bbox.min.z, verts[2]); - bbox.max.x = std::max(bbox.max.x, verts[0]); - bbox.max.y = std::max(bbox.max.y, verts[1]); - bbox.max.z = std::max(bbox.max.z, verts[2]); + bbox.min(0) = std::min(bbox.min(0), verts[0]); + bbox.min(1) = std::min(bbox.min(1), verts[1]); + bbox.min(2) = std::min(bbox.min(2), verts[2]); + bbox.max(0) = std::max(bbox.max(0), verts[0]); + bbox.max(1) = std::max(bbox.max(1), verts[1]); + bbox.max(2) = std::max(bbox.max(2), verts[2]); } } return bbox; @@ -225,7 +228,7 @@ class GLVolume { // ID of the shader used to render with the layer height texture unsigned int shader_id; // The print object to update when generating the layer height texture - PrintObject* print_object; + const PrintObject* print_object; float z_cursor_relative; float edit_band_width; @@ -249,17 +252,29 @@ public: static const float HOVER_COLOR[4]; static const float OUTSIDE_COLOR[4]; static const float SELECTED_OUTSIDE_COLOR[4]; + static const float DISABLED_COLOR[4]; GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} private: +#if ENABLE_MODELVOLUME_TRANSFORM + Geometry::Transformation m_instance_transformation; + Geometry::Transformation m_volume_transformation; +#else // Offset of the volume to be rendered. - Pointf3 m_origin; - // Rotation around Z axis of the volume to be rendered. - float m_angle_z; - // Scale factor of the volume to be rendered. - float m_scale_factor; + Vec3d m_offset; + // Rotation around three axes of the volume to be rendered. + Vec3d m_rotation; + // Scale factor along the three axes of the volume to be rendered. + Vec3d m_scaling_factor; + // Mirroring along the three axes of the volume to be rendered. + Vec3d m_mirror; + // World matrix of the volume to be rendered. + mutable Transform3f m_world_matrix; + // Whether or not is needed to recalculate the world matrix. + mutable bool m_world_matrix_dirty; +#endif // ENABLE_MODELVOLUME_TRANSFORM // Bounding box of this volume, in unscaled coordinates. mutable BoundingBoxf3 m_transformed_bounding_box; // Whether or not is needed to recalculate the transformed bounding box. @@ -272,23 +287,27 @@ private: mutable bool m_transformed_convex_hull_bounding_box_dirty; public: - // Bounding box of this volume, in unscaled coordinates. BoundingBoxf3 bounding_box; // Color of the triangles / quads held by this volume. float color[4]; // Color used to render this volume. float render_color[4]; - // An ID containing the object ID, volume ID and instance ID. - int composite_id; - // An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance. - int select_group_id; - // An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance. - int drag_group_id; + // Object ID, which is equal to the index of the respective ModelObject in Model.objects array. + int object_id; + // Volume ID, which is equal to the index of the respective ModelVolume in ModelObject.volumes array. + // If negative, it is an index of a geometry produced by the PrintObject for the respective ModelObject, + // and which has no associated ModelVolume in ModelObject.volumes. For example, SLA supports. + // Volume with a negative volume_id cannot be picked independently, it will pick the associated instance. + int volume_id; + // Instance ID, which is equal to the index of the respective ModelInstance in ModelObject.instances array. + int instance_id; // An ID containing the extruder ID (used to select color). int extruder_id; // Is this object selected? bool selected; + // Is this object disabled from selection? + bool disabled; // Whether or not this volume is active for rendering bool is_active; // Whether or not to use this volume when applying zoom_to_volumes() @@ -323,19 +342,89 @@ public: // Sets render color in dependence of current state void set_render_color(); - const Pointf3& get_origin() const; - void set_origin(const Pointf3& origin); - void set_angle_z(float angle_z); - void set_scale_factor(float scale_factor); +#if ENABLE_MODELVOLUME_TRANSFORM + const Geometry::Transformation& get_instance_transformation() const { return m_instance_transformation; } + void set_instance_transformation(const Geometry::Transformation& transformation) { m_instance_transformation = transformation; set_bounding_boxes_as_dirty(); } + + const Vec3d& get_instance_offset() const { return m_instance_transformation.get_offset(); } + double get_instance_offset(Axis axis) const { return m_instance_transformation.get_offset(axis); } + + void set_instance_offset(const Vec3d& offset) { m_instance_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } + void set_instance_offset(Axis axis, double offset) { m_instance_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } + + const Vec3d& get_instance_rotation() const { return m_instance_transformation.get_rotation(); } + double get_instance_rotation(Axis axis) const { return m_instance_transformation.get_rotation(axis); } + + void set_instance_rotation(const Vec3d& rotation) { m_instance_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } + void set_instance_rotation(Axis axis, double rotation) { m_instance_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } + + Vec3d get_instance_scaling_factor() const { return m_instance_transformation.get_scaling_factor(); } + double get_instance_scaling_factor(Axis axis) const { return m_instance_transformation.get_scaling_factor(axis); } + + void set_instance_scaling_factor(const Vec3d& scaling_factor) { m_instance_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } + void set_instance_scaling_factor(Axis axis, double scaling_factor) { m_instance_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } + + const Vec3d& get_instance_mirror() const { return m_instance_transformation.get_mirror(); } + double get_instance_mirror(Axis axis) const { return m_instance_transformation.get_mirror(axis); } + + void set_instance_mirror(const Vec3d& mirror) { m_instance_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } + void set_instance_mirror(Axis axis, double mirror) { m_instance_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } + + const Geometry::Transformation& get_volume_transformation() const { return m_volume_transformation; } + void set_volume_transformation(const Geometry::Transformation& transformation) { m_volume_transformation = transformation; set_bounding_boxes_as_dirty(); } + + const Vec3d& get_volume_offset() const { return m_volume_transformation.get_offset(); } + double get_volume_offset(Axis axis) const { return m_volume_transformation.get_offset(axis); } + + void set_volume_offset(const Vec3d& offset) { m_volume_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } + void set_volume_offset(Axis axis, double offset) { m_volume_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } + + const Vec3d& get_volume_rotation() const { return m_volume_transformation.get_rotation(); } + double get_volume_rotation(Axis axis) const { return m_volume_transformation.get_rotation(axis); } + + void set_volume_rotation(const Vec3d& rotation) { m_volume_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } + void set_volume_rotation(Axis axis, double rotation) { m_volume_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } + + Vec3d get_volume_scaling_factor() const { return m_volume_transformation.get_scaling_factor(); } + double get_volume_scaling_factor(Axis axis) const { return m_volume_transformation.get_scaling_factor(axis); } + + void set_volume_scaling_factor(const Vec3d& scaling_factor) { m_volume_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } + void set_volume_scaling_factor(Axis axis, double scaling_factor) { m_volume_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } + + const Vec3d& get_volume_mirror() const { return m_volume_transformation.get_mirror(); } + double get_volume_mirror(Axis axis) const { return m_volume_transformation.get_mirror(axis); } + + void set_volume_mirror(const Vec3d& mirror) { m_volume_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } + void set_volume_mirror(Axis axis, double mirror) { m_volume_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } +#else + const Vec3d& get_rotation() const; + void set_rotation(const Vec3d& rotation); + + const Vec3d& get_scaling_factor() const; + void set_scaling_factor(const Vec3d& scaling_factor); + + const Vec3d& get_mirror() const; + double get_mirror(Axis axis) const; + void set_mirror(const Vec3d& mirror); + void set_mirror(Axis axis, double mirror); + + const Vec3d& get_offset() const; + void set_offset(const Vec3d& offset); +#endif // ENABLE_MODELVOLUME_TRANSFORM + void set_convex_hull(const TriangleMesh& convex_hull); - int object_idx() const { return this->composite_id / 1000000; } - int volume_idx() const { return (this->composite_id / 1000) % 1000; } - int instance_idx() const { return this->composite_id % 1000; } + int object_idx() const { return this->object_id; } + int volume_idx() const { return this->volume_id; } + int instance_idx() const { return this->instance_id; } - std::vector world_matrix() const; - BoundingBoxf3 transformed_bounding_box() const; - BoundingBoxf3 transformed_convex_hull_bounding_box() const; +#if ENABLE_MODELVOLUME_TRANSFORM + Transform3d world_matrix() const { return m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); } +#else + const Transform3f& world_matrix() const; +#endif // ENABLE_MODELVOLUME_TRANSFORM + const BoundingBoxf3& transformed_bounding_box() const; + const BoundingBoxf3& transformed_convex_hull_bounding_box() const; bool empty() const { return this->indexed_vertex_array.empty(); } bool indexed() const { return this->indexed_vertex_array.indexed(); } @@ -371,9 +460,9 @@ public: (void*)(layer_height_texture->data.data() + layer_height_texture->width * layer_height_texture->height * 4); } double layer_height_texture_z_to_row_id() const; - void generate_layer_height_texture(PrintObject *print_object, bool force); + void generate_layer_height_texture(const PrintObject *print_object, bool force); - void set_layer_height_texture_data(unsigned int texture_id, unsigned int shader_id, PrintObject* print_object, float z_cursor_relative, float edit_band_width) + void set_layer_height_texture_data(unsigned int texture_id, unsigned int shader_id, const PrintObject* print_object, float z_cursor_relative, float edit_band_width) { layer_height_texture_data.texture_id = texture_id; layer_height_texture_data.shader_id = shader_id; @@ -383,8 +472,14 @@ public: } void reset_layer_height_texture_data() { layer_height_texture_data.reset(); } + +#if ENABLE_MODELVOLUME_TRANSFORM + void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } +#endif // ENABLE_MODELVOLUME_TRANSFORM }; +typedef std::vector GLVolumePtrs; + class GLVolumeCollection { // min and max vertex of the print box volume @@ -392,8 +487,8 @@ class GLVolumeCollection float print_box_max[3]; public: - std::vector volumes; - + GLVolumePtrs volumes; + GLVolumeCollection() {}; ~GLVolumeCollection() { clear(); }; @@ -402,8 +497,14 @@ public: int obj_idx, const std::vector &instance_idxs, const std::string &color_by, - const std::string &select_by, - const std::string &drag_by, + bool use_VBOs); + + // Load SLA auxiliary GLVolumes (for support trees or pad). + std::vector load_object_auxiliary( + const ModelObject *model_object, + const SLAPrintObject *print_object, + int obj_idx, + SLAPrintObjectStep milestone, bool use_VBOs); int load_wipe_tower_preview( @@ -465,35 +566,21 @@ public: static unsigned int get_volumes_count(wxGLCanvas* canvas); static void reset_volumes(wxGLCanvas* canvas); - static void deselect_volumes(wxGLCanvas* canvas); - static void select_volume(wxGLCanvas* canvas, unsigned int id); - static void update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections); static int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config); - static bool move_volume_up(wxGLCanvas* canvas, unsigned int id); - static bool move_volume_down(wxGLCanvas* canvas, unsigned int id); - static void set_objects_selections(wxGLCanvas* canvas, const std::vector& selections); + static GUI::GLCanvas3D* get_canvas(wxGLCanvas* canvas); static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); static void set_print(wxGLCanvas* canvas, Print* print); + static void set_SLA_print(wxGLCanvas* canvas, SLAPrint* print); static void set_model(wxGLCanvas* canvas, Model* model); static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); - static void set_auto_bed_shape(wxGLCanvas* canvas); - - static BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); - - static void set_axes_length(wxGLCanvas* canvas, float length); - - static void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); static void set_color_by(wxGLCanvas* canvas, const std::string& value); - static void set_select_by(wxGLCanvas* canvas, const std::string& value); - static void set_drag_by(wxGLCanvas* canvas, const std::string& value); static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_layers_editing_allowed(wxGLCanvas* canvas); - static bool is_shader_enabled(wxGLCanvas* canvas); static bool is_reload_delayed(wxGLCanvas* canvas); @@ -503,52 +590,40 @@ public: static void enable_picking(wxGLCanvas* canvas, bool enable); static void enable_moving(wxGLCanvas* canvas, bool enable); static void enable_gizmos(wxGLCanvas* canvas, bool enable); + static void enable_toolbar(wxGLCanvas* canvas, bool enable); static void enable_shader(wxGLCanvas* canvas, bool enable); static void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); static void enable_dynamic_background(wxGLCanvas* canvas, bool enable); static void allow_multisample(wxGLCanvas* canvas, bool allow); + static void enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable); + static bool is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name); + static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); static void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other); static void update_volumes_colors_by_extruder(wxGLCanvas* canvas); - static void update_gizmos_data(wxGLCanvas* canvas); static void render(wxGLCanvas* canvas); + static void delete_selected(wxGLCanvas* canvas); + static std::vector get_current_print_zs(wxGLCanvas* canvas, bool active_only); static void set_toolpaths_range(wxGLCanvas* canvas, double low, double high); - static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); - static void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); - static void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); - static void register_on_select_object_callback(wxGLCanvas* canvas, void* callback); - static void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); - static void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback); - static void register_on_arrange_callback(wxGLCanvas* canvas, void* callback); - static void register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback); - static void register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback); - static void register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback); - static void register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback); - static void register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback); - static void register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback); - static void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); - static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); - static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); - static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); - static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); - static std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); static std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); + static void mirror_selection(wxGLCanvas* canvas, Axis axis); + static void reload_scene(wxGLCanvas* canvas, bool force); static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); static void load_preview(wxGLCanvas* canvas, const std::vector& str_tool_colors); - static void reset_legend_texture(); + static void reset_legend_texture(wxGLCanvas* canvas); static void thick_lines_to_verts(const Lines& lines, const std::vector& widths, const std::vector& heights, bool closed, double top_z, GLVolume& volume); static void thick_lines_to_verts(const Lines3& lines, const std::vector& widths, const std::vector& heights, bool closed, GLVolume& volume); @@ -559,7 +634,7 @@ public: static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume); static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume); - static void point3_to_verts(const Point3& point, double width, double height, GLVolume& volume); + static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume); }; } diff --git a/xs/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp similarity index 98% rename from xs/src/slic3r/GUI/AboutDialog.cpp rename to src/slic3r/GUI/AboutDialog.cpp index 0fed8d1751..cf196c36d3 100644 --- a/xs/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -41,7 +41,7 @@ AboutDialog::AboutDialog() // logo wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG); auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(logo_bmp)); - hsizer->Add(logo, 1, wxALIGN_CENTRE_VERTICAL | wxEXPAND | wxTOP | wxBOTTOM, 35); + hsizer->Add(logo, 1, wxEXPAND | wxTOP | wxBOTTOM, 35); wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); #ifdef __WXMSW__ diff --git a/xs/src/slic3r/GUI/AboutDialog.hpp b/src/slic3r/GUI/AboutDialog.hpp similarity index 100% rename from xs/src/slic3r/GUI/AboutDialog.hpp rename to src/slic3r/GUI/AboutDialog.hpp diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp similarity index 95% rename from xs/src/slic3r/GUI/AppConfig.cpp rename to src/slic3r/GUI/AppConfig.cpp index c2ae0bf0b8..7a277b1191 100644 --- a/xs/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -16,8 +16,6 @@ #include #include #include -#include - namespace Slic3r { @@ -127,14 +125,8 @@ void AppConfig::load() void AppConfig::save() { - // The config is first written to a file with a PID suffix and then moved - // to avoid race conditions with multiple instances of Slic3r - - const auto path = config_path(); - std::string path_pid = (boost::format("%1%.%2%") % path % get_current_pid()).str(); - boost::nowide::ofstream c; - c.open(path_pid, std::ios::out | std::ios::trunc); + c.open(AppConfig::config_path(), std::ios::out | std::ios::trunc); c << "# " << Slic3r::header_slic3r_generated() << std::endl; // Make sure the "no" category is written first. for (const std::pair &kvp : m_storage[""]) @@ -163,9 +155,6 @@ void AppConfig::save() } } c.close(); - - rename_file(path_pid, path); - m_dirty = false; } @@ -252,6 +241,8 @@ void AppConfig::reset_selections() if (it != m_storage.end()) { it->second.erase("print"); it->second.erase("filament"); + it->second.erase("sla_print"); + it->second.erase("sla_material"); it->second.erase("printer"); m_dirty = true; } diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp similarity index 100% rename from xs/src/slic3r/GUI/AppConfig.hpp rename to src/slic3r/GUI/AppConfig.hpp diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp new file mode 100644 index 0000000000..5dcef44a12 --- /dev/null +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -0,0 +1,368 @@ +#include "BackgroundSlicingProcess.hpp" +#include "GUI_App.hpp" + +#include +#include +#include +#include + +// For zipped archive creation +#include +#include +#include + +// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. +#include "libslic3r/Print.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/Utils.hpp" +#include "libslic3r/GCode/PostProcessor.hpp" + +//#undef NDEBUG +#include +#include + +#include +#include +#include + +namespace Slic3r { + +BackgroundSlicingProcess::BackgroundSlicingProcess() +{ + boost::filesystem::path temp_path(wxStandardPaths::Get().GetTempDir().utf8_str().data()); + temp_path /= (boost::format(".%1%.gcode") % get_current_pid()).str(); + m_temp_output_path = temp_path.string(); +} + +BackgroundSlicingProcess::~BackgroundSlicingProcess() +{ + this->stop(); + this->join_background_thread(); + boost::nowide::remove(m_temp_output_path.c_str()); +} + +void BackgroundSlicingProcess::select_technology(PrinterTechnology tech) +{ + if (m_print == nullptr || m_print->technology() != tech) { + if (m_print != nullptr) + this->reset(); + switch (tech) { + case ptFFF: m_print = m_fff_print; break; + case ptSLA: m_print = m_sla_print; break; + } + } + assert(m_print != nullptr); +} + +// This function may one day be merged into the Print, but historically the print was separated +// from the G-code generator. +void BackgroundSlicingProcess::process_fff() +{ + assert(m_print == m_fff_print); + m_print->process(); + if (! m_print->canceled()) { + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_sliced_id)); + m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); + if (! m_print->canceled() && ! this->is_step_done(bspsGCodeFinalize)) { + this->set_step_started(bspsGCodeFinalize); + if (! m_export_path.empty()) { + //FIXME localize the messages + if (copy_file(m_temp_output_path, m_export_path) != 0) + throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); + m_print->set_status(95, "Running post-processing scripts"); + run_post_process_scripts(m_export_path, m_fff_print->config()); + m_print->set_status(100, "G-code file exported to " + m_export_path); + } else { + m_print->set_status(100, "Slicing complete"); + } + this->set_step_done(bspsGCodeFinalize); + } + } +} + +// Pseudo type for specializing LayerWriter trait class +struct SLAZipFmt {}; + +// The implementation of creating zipped archives with wxWidgets +template<> class LayerWriter { + wxFileName fpath; + wxFFileOutputStream zipfile; + wxZipOutputStream zipstream; + wxStdOutputStream pngstream; + +public: + + inline LayerWriter(const std::string& zipfile_path): + fpath(zipfile_path), + zipfile(zipfile_path), + zipstream(zipfile), + pngstream(zipstream) + { + if(!zipfile.IsOk()) + throw std::runtime_error("Cannot create zip file."); + } + + inline void next_entry(const std::string& fname) { + zipstream.PutNextEntry(fname); + } + + inline std::string get_name() const { + return fpath.GetName().ToStdString(); + } + + template inline LayerWriter& operator<<(const T& arg) { + pngstream << arg; return *this; + } + + inline void close() { + zipstream.Close(); + zipfile.Close(); + } +}; + +void BackgroundSlicingProcess::process_sla() { + assert(m_print == m_sla_print); + m_print->process(); + if(!m_print->canceled() && ! this->is_step_done(bspsGCodeFinalize)) { + this->set_step_started(bspsGCodeFinalize); + if (! m_export_path.empty()) { + m_sla_print->export_raster(m_export_path); + m_print->set_status(100, "Zip file exported to " + m_export_path); + } + this->set_step_done(bspsGCodeFinalize); + } +} + +void BackgroundSlicingProcess::thread_proc() +{ + assert(m_print != nullptr); + assert(m_print == m_fff_print || m_print == m_sla_print); + std::unique_lock lck(m_mutex); + // Let the caller know we are ready to run the background processing task. + m_state = STATE_IDLE; + lck.unlock(); + m_condition.notify_one(); + for (;;) { + assert(m_state == STATE_IDLE || m_state == STATE_CANCELED || m_state == STATE_FINISHED); + // Wait until a new task is ready to be executed, or this thread should be finished. + lck.lock(); + m_condition.wait(lck, [this](){ return m_state == STATE_STARTED || m_state == STATE_EXIT; }); + if (m_state == STATE_EXIT) + // Exiting this thread. + break; + // Process the background slicing task. + m_state = STATE_RUNNING; + lck.unlock(); + std::string error; + try { + assert(m_print != nullptr); + switch(m_print->technology()) { + case ptFFF: this->process_fff(); break; + case ptSLA: this->process_sla(); break; + default: m_print->process(); break; + } + } catch (CanceledException & /* ex */) { + // Canceled, this is all right. + assert(m_print->canceled()); + } catch (std::exception &ex) { + error = ex.what(); + } catch (...) { + error = "Unknown C++ exception."; + } + lck.lock(); + m_state = m_print->canceled() ? STATE_CANCELED : STATE_FINISHED; + if (m_print->cancel_status() != Print::CANCELED_INTERNAL) { + // Only post the canceled event, if canceled by user. + // Don't post the canceled event, if canceled from Print::apply(). + wxCommandEvent evt(m_event_finished_id); + evt.SetString(error); + evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0)); + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone()); + } + m_print->restart(); + lck.unlock(); + // Let the UI thread wake up if it is waiting for the background task to finish. + m_condition.notify_one(); + // Let the UI thread see the result. + } + m_state = STATE_EXITED; + lck.unlock(); + // End of the background processing thread. The UI thread should join m_thread now. +} + +void BackgroundSlicingProcess::thread_proc_safe() +{ + try { + this->thread_proc(); + } catch (...) { + wxTheApp->OnUnhandledException(); + } +} + +void BackgroundSlicingProcess::join_background_thread() +{ + std::unique_lock lck(m_mutex); + if (m_state == STATE_INITIAL) { + // Worker thread has not been started yet. + assert(! m_thread.joinable()); + } else { + assert(m_state == STATE_IDLE); + assert(m_thread.joinable()); + // Notify the worker thread to exit. + m_state = STATE_EXIT; + lck.unlock(); + m_condition.notify_one(); + // Wait until the worker thread exits. + m_thread.join(); + } +} + +bool BackgroundSlicingProcess::start() +{ + if (m_print->empty()) + // The print is empty (no object in Model, or all objects are out of the print bed). + return false; + + std::unique_lock lck(m_mutex); + if (m_state == STATE_INITIAL) { + // The worker thread is not running yet. Start it. + assert(! m_thread.joinable()); + m_thread = std::thread([this]{this->thread_proc_safe();}); + // Wait until the worker thread is ready to execute the background processing task. + m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); + } + assert(m_state == STATE_IDLE || this->running()); + if (this->running()) + // The background processing thread is already running. + return false; + if (! this->idle()) + throw std::runtime_error("Cannot start a background task, the worker thread is not idle."); + m_state = STATE_STARTED; + m_print->set_cancel_callback([this](){ this->stop_internal(); }); + lck.unlock(); + m_condition.notify_one(); + return true; +} + +bool BackgroundSlicingProcess::stop() +{ + std::unique_lock lck(m_mutex); + if (m_state == STATE_INITIAL) { +// this->m_export_path.clear(); + return false; + } +// assert(this->running()); + if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { + m_print->cancel(); + // Wait until the background processing stops by being canceled. + m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; }); + // In the "Canceled" state. Reset the state to "Idle". + m_state = STATE_IDLE; + m_print->set_cancel_callback([](){}); + } else if (m_state == STATE_FINISHED || m_state == STATE_CANCELED) { + // In the "Finished" or "Canceled" state. Reset the state to "Idle". + m_state = STATE_IDLE; + m_print->set_cancel_callback([](){}); + } +// this->m_export_path.clear(); + return true; +} + +bool BackgroundSlicingProcess::reset() +{ + bool stopped = this->stop(); + this->reset_export(); + m_print->clear(); + this->invalidate_all_steps(); + return stopped; +} + +// To be called by Print::apply() through the Print::m_cancel_callback to stop the background +// processing before changing any data of running or finalized milestones. +// This function shall not trigger any UI update through the wxWidgets event. +void BackgroundSlicingProcess::stop_internal() +{ + std::unique_lock lck(m_mutex); + assert(m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED); + if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { + m_print->cancel_internal(); + // Wait until the background processing stops by being canceled. + m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; }); + } + // In the "Canceled" state. Reset the state to "Idle". + m_state = STATE_IDLE; + m_print->set_cancel_callback([](){}); +} + +bool BackgroundSlicingProcess::empty() const +{ + assert(m_print != nullptr); + return m_print->empty(); +} + +std::string BackgroundSlicingProcess::validate() +{ + assert(m_print != nullptr); + return m_print->validate(); +} + +// Apply config over the print. Returns false, if the new config values caused any of the already +// processed steps to be invalidated, therefore the task will need to be restarted. +Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const DynamicPrintConfig &config) +{ + assert(m_print != nullptr); + assert(config.opt_enum("printer_technology") == m_print->technology()); + Print::ApplyStatus invalidated = m_print->apply(model, config); + return invalidated; +} + +// Set the output path of the G-code. +void BackgroundSlicingProcess::schedule_export(const std::string &path) +{ + assert(m_export_path.empty()); + if (! m_export_path.empty()) + return; + + // Guard against entering the export step before changing the export path. + tbb::mutex::scoped_lock lock(m_step_state_mutex); + this->invalidate_step(bspsGCodeFinalize); + m_export_path = path; +} + +void BackgroundSlicingProcess::reset_export() +{ + assert(! this->running()); + if (! this->running()) { + m_export_path.clear(); + // invalidate_step expects the mutex to be locked. + tbb::mutex::scoped_lock lock(m_step_state_mutex); + this->invalidate_step(bspsGCodeFinalize); + } +} + +void BackgroundSlicingProcess::set_step_started(BackgroundSlicingProcessStep step) +{ + m_step_state.set_started(step, m_step_state_mutex); + if (m_print->canceled()) + throw CanceledException(); +} + +void BackgroundSlicingProcess::set_step_done(BackgroundSlicingProcessStep step) +{ + m_step_state.set_done(step, m_step_state_mutex); + if (m_print->canceled()) + throw CanceledException(); +} + +bool BackgroundSlicingProcess::invalidate_step(BackgroundSlicingProcessStep step) +{ + bool invalidated = m_step_state.invalidate(step, m_step_state_mutex, [this](){ this->stop(); }); + return invalidated; +} + +bool BackgroundSlicingProcess::invalidate_all_steps() +{ + return m_step_state.invalidate_all(m_step_state_mutex, [this](){ this->stop(); }); +} + +}; // namespace Slic3r diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp new file mode 100644 index 0000000000..92bc512d74 --- /dev/null +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -0,0 +1,142 @@ +#ifndef slic3r_GUI_BackgroundSlicingProcess_hpp_ +#define slic3r_GUI_BackgroundSlicingProcess_hpp_ + +#include +#include +#include +#include + +#include "Print.hpp" + +namespace Slic3r { + +class DynamicPrintConfig; +class GCodePreviewData; +class Model; +class SLAPrint; + +// Print step IDs for keeping track of the print state. +enum BackgroundSlicingProcessStep { + bspsGCodeFinalize, bspsCount, +}; + +// Support for the GUI background processing (Slicing and G-code generation). +// As of now this class is not declared in Slic3r::GUI due to the Perl bindings limits. +class BackgroundSlicingProcess +{ +public: + BackgroundSlicingProcess(); + // Stop the background processing and finalize the bacgkround processing thread, remove temp files. + ~BackgroundSlicingProcess(); + + void set_fff_print(Print *print) { m_fff_print = print; } + void set_sla_print(SLAPrint *print) { m_sla_print = print; } + void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } + // The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished + // and the background processing will transition into G-code export. + // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed. + void set_sliced_event(int event_id) { m_event_sliced_id = event_id; } + // The following wxCommandEvent will be sent to the UI thread / Platter window, when the G-code export is finished. + // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed. + void set_finished_event(int event_id) { m_event_finished_id = event_id; } + + // Activate either m_fff_print or m_sla_print. + void select_technology(PrinterTechnology tech); + // Start the background processing. Returns false if the background processing was already running. + bool start(); + // Cancel the background processing. Returns false if the background processing was not running. + // A stopped background processing may be restarted with start(). + bool stop(); + // Cancel the background processing and reset the print. Returns false if the background processing was not running. + // Useful when the Model or configuration is being changed drastically. + bool reset(); + + // Apply config over the print. Returns false, if the new config values caused any of the already + // processed steps to be invalidated, therefore the task will need to be restarted. + Print::ApplyStatus apply(const Model &model, const DynamicPrintConfig &config); + // After calling apply, the empty() call will report whether there is anything to slice. + bool empty() const; + // Validate the print. Returns an empty string if valid, returns an error message if invalid. + // Call validate before calling start(). + std::string validate(); + + // Set the export path of the G-code. + // Once the path is set, the G-code + void schedule_export(const std::string &path); + // Clear m_export_path. + void reset_export(); + // Once the G-code export is scheduled, the apply() methods will do nothing. + bool is_export_scheduled() const { return ! m_export_path.empty(); } + + enum State { + // m_thread is not running yet, or it did not reach the STATE_IDLE yet (it does not wait on the condition yet). + STATE_INITIAL = 0, + // m_thread is waiting for the task to execute. + STATE_IDLE, + STATE_STARTED, + // m_thread is executing a task. + STATE_RUNNING, + // m_thread finished executing a task, and it is waiting until the UI thread picks up the results. + STATE_FINISHED, + // m_thread finished executing a task, the task has been canceled by the UI thread, therefore the UI thread will not be notified. + STATE_CANCELED, + // m_thread exited the loop and it is going to finish. The UI thread should join on m_thread. + STATE_EXIT, + STATE_EXITED, + }; + State state() const { return m_state; } + bool idle() const { return m_state == STATE_IDLE; } + bool running() const { return m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED; } + +private: + void thread_proc(); + void thread_proc_safe(); + void join_background_thread(); + // To be called by Print::apply() through the Print::m_cancel_callback to stop the background + // processing before changing any data of running or finalized milestones. + // This function shall not trigger any UI update through the wxWidgets event. + void stop_internal(); + + // Helper to wrap the FFF slicing & G-code generation. + void process_fff(); + + // Temporary: for mimicking the fff file export behavior with the raster output + void process_sla(); + + // Currently active print. It is one of m_fff_print and m_sla_print. + PrintBase *m_print = nullptr; + // Non-owned pointers to Print instances. + Print *m_fff_print = nullptr; + SLAPrint *m_sla_print = nullptr; + // Data structure, to which the G-code export writes its annotations. + GCodePreviewData *m_gcode_preview_data = nullptr; + // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. + std::string m_temp_output_path; + // Output path provided by the user. The output path may be set even if the slicing is running, + // but once set, it cannot be re-set. + std::string m_export_path; + // Thread, on which the background processing is executed. The thread will always be present + // and ready to execute the slicing process. + std::thread m_thread; + // Mutex and condition variable to synchronize m_thread with the UI thread. + std::mutex m_mutex; + std::condition_variable m_condition; + State m_state = STATE_INITIAL; + + PrintState m_step_state; + mutable tbb::mutex m_step_state_mutex; + void set_step_started(BackgroundSlicingProcessStep step); + void set_step_done(BackgroundSlicingProcessStep step); + bool is_step_done(BackgroundSlicingProcessStep step) const { return m_step_state.is_done(step); } + bool invalidate_step(BackgroundSlicingProcessStep step); + bool invalidate_all_steps(); + + // wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue. + int m_event_sliced_id = 0; + // wxWidgets command ID to be sent to the platter to inform that the task finished. + int m_event_finished_id = 0; +}; + +}; // namespace Slic3r + +#endif /* slic3r_GUI_BackgroundSlicingProcess_hpp_ */ diff --git a/xs/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp similarity index 79% rename from xs/src/slic3r/GUI/BedShapeDialog.cpp rename to src/slic3r/GUI/BedShapeDialog.cpp index d525355897..66aedf16d2 100644 --- a/xs/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -28,7 +28,7 @@ void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt) main_sizer->SetSizeHints(this); // needed to actually free memory - this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent e){ + this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent e) { EndModal(wxID_OK); Destroy(); })); @@ -48,14 +48,14 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt) auto optgroup = init_shape_options_page(_(L("Rectangular"))); ConfigOptionDef def; def.type = coPoints; - def.default_value = new ConfigOptionPoints{ Pointf(200, 200) }; + def.default_value = new ConfigOptionPoints{ Vec2d(200, 200) }; def.label = L("Size"); def.tooltip = L("Size in X and Y of the rectangular plate."); Option option(def, "rect_size"); optgroup->append_single_option_line(option); def.type = coPoints; - def.default_value = new ConfigOptionPoints{ Pointf(0, 0) }; + def.default_value = new ConfigOptionPoints{ Vec2d(0, 0) }; def.label = L("Origin"); def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle."); option = Option(def, "rect_origin"); @@ -115,14 +115,15 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt) // Called from the constructor. // Create a panel for a rectangular / circular / custom bed shape. -ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title){ +ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title) +{ auto panel = new wxPanel(m_shape_options_book); ConfigOptionsGroupShp optgroup; optgroup = std::make_shared(panel, _(L("Settings"))); optgroup->label_width = 100; - optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ + optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { update_shape(); }; @@ -149,21 +150,21 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points) // okay, it's a rectangle // find origin coordf_t x_min, x_max, y_min, y_max; - x_max = x_min = points->values[0].x; - y_max = y_min = points->values[0].y; + x_max = x_min = points->values[0](0); + y_max = y_min = points->values[0](1); for (auto pt : points->values) { - x_min = std::min(x_min, pt.x); - x_max = std::max(x_max, pt.x); - y_min = std::min(y_min, pt.y); - y_max = std::max(y_max, pt.y); + x_min = std::min(x_min, pt(0)); + x_max = std::max(x_max, pt(0)); + y_min = std::min(y_min, pt(1)); + y_max = std::max(y_max, pt(1)); } - auto origin = new ConfigOptionPoints{ Pointf(-x_min, -y_min) }; + auto origin = new ConfigOptionPoints{ Vec2d(-x_min, -y_min) }; m_shape_options_book->SetSelection(SHAPE_RECTANGULAR); auto optgroup = m_optgroups[SHAPE_RECTANGULAR]; - optgroup->set_value("rect_size", new ConfigOptionPoints{ Pointf(x_max - x_min, y_max - y_min) });//[x_max - x_min, y_max - y_min]); + optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(x_max - x_min, y_max - y_min) });//[x_max - x_min, y_max - y_min]); optgroup->set_value("rect_origin", origin); update_shape(); return; @@ -178,7 +179,7 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points) double avg_dist = 0; for (auto pt: polygon.points) { - double distance = center.distance_to(pt); + double distance = (pt - center).cast().norm(); vertex_distances.push_back(distance); avg_dist += distance; } @@ -195,7 +196,7 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points) // all vertices are equidistant to center m_shape_options_book->SetSelection(SHAPE_CIRCULAR); auto optgroup = m_optgroups[SHAPE_CIRCULAR]; - boost::any ret = wxNumberFormatter::ToString(unscale(avg_dist * 2), 0); + boost::any ret = wxNumberFormatter::ToString(unscale(avg_dist * 2), 0); optgroup->set_value("diameter", ret); update_shape(); return; @@ -206,8 +207,8 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points) // Invalid polygon.Revert to default bed dimensions. m_shape_options_book->SetSelection(SHAPE_RECTANGULAR); auto optgroup = m_optgroups[SHAPE_RECTANGULAR]; - optgroup->set_value("rect_size", new ConfigOptionPoints{ Pointf(200, 200) }); - optgroup->set_value("rect_origin", new ConfigOptionPoints{ Pointf(0, 0) }); + optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(200, 200) }); + optgroup->set_value("rect_origin", new ConfigOptionPoints{ Vec2d(0, 0) }); update_shape(); return; } @@ -230,19 +231,22 @@ void BedShapePanel::update_shape() { auto page_idx = m_shape_options_book->GetSelection(); if (page_idx == SHAPE_RECTANGULAR) { - Pointf rect_size, rect_origin; + Vec2d rect_size(Vec2d::Zero()); + Vec2d rect_origin(Vec2d::Zero()); try{ - rect_size = boost::any_cast(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_size")); } - catch (const std::exception &e){ - return;} - try{ - rect_origin = boost::any_cast(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_origin")); + rect_size = boost::any_cast(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_size")); } + catch (const std::exception & /* e */) { + return; + } + try { + rect_origin = boost::any_cast(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_origin")); + } + catch (const std::exception & /* e */) { + return; } - catch (const std::exception &e){ - return;} - auto x = rect_size.x; - auto y = rect_size.y; + auto x = rect_size(0); + auto y = rect_size(1); // empty strings or '-' or other things if (x == 0 || y == 0) return; double x0 = 0.0; @@ -250,34 +254,34 @@ void BedShapePanel::update_shape() double x1 = x; double y1 = y; - auto dx = rect_origin.x; - auto dy = rect_origin.y; + auto dx = rect_origin(0); + auto dy = rect_origin(1); x0 -= dx; x1 -= dx; y0 -= dy; y1 -= dy; - m_canvas->m_bed_shape = { Pointf(x0, y0), - Pointf(x1, y0), - Pointf(x1, y1), - Pointf(x0, y1)}; + m_canvas->m_bed_shape = { Vec2d(x0, y0), + Vec2d(x1, y0), + Vec2d(x1, y1), + Vec2d(x0, y1)}; } else if(page_idx == SHAPE_CIRCULAR) { double diameter; try{ diameter = boost::any_cast(m_optgroups[SHAPE_CIRCULAR]->get_value("diameter")); } - catch (const std::exception &e){ + catch (const std::exception & /* e */) { return; } if (diameter == 0.0) return ; auto r = diameter / 2; auto twopi = 2 * PI; auto edges = 60; - std::vector points; - for (size_t i = 1; i <= 60; ++i){ + std::vector points; + for (size_t i = 1; i <= 60; ++i) { auto angle = i * twopi / edges; - points.push_back(Pointf(r*cos(angle), r*sin(angle))); + points.push_back(Vec2d(r*cos(angle), r*sin(angle))); } m_canvas->m_bed_shape = points; } @@ -289,14 +293,8 @@ void BedShapePanel::update_shape() // Loads an stl file, projects it to the XY plane and calculates a polygon. void BedShapePanel::load_stl() { - t_file_wild_card vec_FILE_WILDCARDS = get_file_wild_card(); - std::vector file_types = { "known", "stl", "obj", "amf", "3mf", "prusa" }; - wxString MODEL_WILDCARD; - for (auto file_type: file_types) - MODEL_WILDCARD += vec_FILE_WILDCARDS.at(file_type) + "|"; - auto dialog = new wxFileDialog(this, _(L("Choose a file to import bed shape from (STL/OBJ/AMF/3MF/PRUSA):")), "", "", - MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + file_wildcards[FT_MODEL], wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog->ShowModal() != wxID_OK) { dialog->Destroy(); return; @@ -330,9 +328,9 @@ void BedShapePanel::load_stl() } auto polygon = expolygons[0].contour; - std::vector points; + std::vector points; for (auto pt : polygon.points) - points.push_back(Pointf::new_unscale(pt)); + points.push_back(unscale(pt)); m_canvas->m_bed_shape = points; update_preview(); } diff --git a/xs/src/slic3r/GUI/BedShapeDialog.hpp b/src/slic3r/GUI/BedShapeDialog.hpp similarity index 82% rename from xs/src/slic3r/GUI/BedShapeDialog.hpp rename to src/slic3r/GUI/BedShapeDialog.hpp index 5ff4880637..8ea595b0b6 100644 --- a/xs/src/slic3r/GUI/BedShapeDialog.hpp +++ b/src/slic3r/GUI/BedShapeDialog.hpp @@ -22,8 +22,8 @@ class BedShapePanel : public wxPanel std::vector m_optgroups; public: - BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY){} - ~BedShapePanel(){} + BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {} + ~BedShapePanel() {} void build_panel(ConfigOptionPoints* default_pt); @@ -34,7 +34,7 @@ public: void load_stl(); // Returns the resulting bed shape polygon. This value will be stored to the ini file. - std::vector GetValue() { return m_canvas->m_bed_shape; } + std::vector GetValue() { return m_canvas->m_bed_shape; } }; class BedShapeDialog : public wxDialog @@ -42,11 +42,11 @@ class BedShapeDialog : public wxDialog BedShapePanel* m_panel; public: BedShapeDialog(wxWindow* parent) : wxDialog(parent, wxID_ANY, _(L("Bed Shape")), - wxDefaultPosition, wxSize(350, 700), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER){} - ~BedShapeDialog(){ } + wxDefaultPosition, wxSize(350, 700), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {} + ~BedShapeDialog() {} void build_dialog(ConfigOptionPoints* default_pt); - std::vector GetValue() { return m_panel->GetValue(); } + std::vector GetValue() { return m_panel->GetValue(); } }; } // GUI diff --git a/xs/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp similarity index 96% rename from xs/src/slic3r/GUI/BitmapCache.cpp rename to src/slic3r/GUI/BitmapCache.cpp index 93853458e4..16baa1629e 100644 --- a/xs/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -97,7 +97,9 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg if (bmp->GetWidth() > 0) { if (bmp->GetDepth() == 32) { wxAlphaPixelData data(*const_cast(bmp)); - data.UseAlpha(); + //FIXME The following method is missing from wxWidgets 3.1.1. + // It looks like the wxWidgets 3.0.3 called the wrapped bitmap's UseAlpha(). + //data.UseAlpha(); if (data) { for (int r = 0; r < bmp->GetHeight(); ++ r) { wxAlphaPixelData::Iterator src(data); diff --git a/xs/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp similarity index 100% rename from xs/src/slic3r/GUI/BitmapCache.hpp rename to src/slic3r/GUI/BitmapCache.hpp diff --git a/xs/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp similarity index 99% rename from xs/src/slic3r/GUI/BonjourDialog.cpp rename to src/slic3r/GUI/BonjourDialog.cpp index 11cfea642d..9d303a85e1 100644 --- a/xs/src/slic3r/GUI/BonjourDialog.cpp +++ b/src/slic3r/GUI/BonjourDialog.cpp @@ -18,8 +18,9 @@ namespace Slic3r { -struct BonjourReplyEvent : public wxEvent +class BonjourReplyEvent : public wxEvent { +public: BonjourReply reply; BonjourReplyEvent(wxEventType eventType, int winid, BonjourReply &&reply) : diff --git a/xs/src/slic3r/GUI/BonjourDialog.hpp b/src/slic3r/GUI/BonjourDialog.hpp similarity index 100% rename from xs/src/slic3r/GUI/BonjourDialog.hpp rename to src/slic3r/GUI/BonjourDialog.hpp diff --git a/xs/src/slic3r/GUI/ButtonsDescription.cpp b/src/slic3r/GUI/ButtonsDescription.cpp similarity index 84% rename from xs/src/slic3r/GUI/ButtonsDescription.cpp rename to src/slic3r/GUI/ButtonsDescription.cpp index 5739fc90e3..b0e8d56ad3 100644 --- a/xs/src/slic3r/GUI/ButtonsDescription.cpp +++ b/src/slic3r/GUI/ButtonsDescription.cpp @@ -5,6 +5,7 @@ #include #include "GUI.hpp" +#include "GUI_App.hpp" namespace Slic3r { namespace GUI { @@ -36,15 +37,15 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, t_icon_descriptions* ic // Text color description auto sys_label = new wxStaticText(this, wxID_ANY, _(L("Value is the same as the system value"))); - sys_label->SetForegroundColour(get_label_clr_sys()); - auto sys_colour = new wxColourPickerCtrl(this, wxID_ANY, get_label_clr_sys()); + sys_label->SetForegroundColour(wxGetApp().get_label_clr_sys()); + auto sys_colour = new wxColourPickerCtrl(this, wxID_ANY, wxGetApp().get_label_clr_sys()); sys_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([sys_colour, sys_label](wxCommandEvent e) { sys_label->SetForegroundColour(sys_colour->GetColour()); sys_label->Refresh(); })); size_t t= 0; - while (t < 3){ + while (t < 3) { grid_sizer->Add(new wxStaticText(this, wxID_ANY, ""), -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); ++t; } @@ -53,8 +54,8 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, t_icon_descriptions* ic grid_sizer->Add(sys_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); auto mod_label = new wxStaticText(this, wxID_ANY, _(L("Value was changed and is not equal to the system value or the last saved preset"))); - mod_label->SetForegroundColour(get_label_clr_modified()); - auto mod_colour = new wxColourPickerCtrl(this, wxID_ANY, get_label_clr_modified()); + mod_label->SetForegroundColour(wxGetApp().get_label_clr_modified()); + auto mod_colour = new wxColourPickerCtrl(this, wxID_ANY, wxGetApp().get_label_clr_modified()); mod_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([mod_colour, mod_label](wxCommandEvent e) { mod_label->SetForegroundColour(mod_colour->GetColour()); @@ -70,8 +71,8 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, t_icon_descriptions* ic wxButton* btn = static_cast(FindWindowById(wxID_OK, this)); btn->Bind(wxEVT_BUTTON, [sys_colour, mod_colour, this](wxCommandEvent&) { - set_label_clr_sys(sys_colour->GetColour()); - set_label_clr_modified(mod_colour->GetColour()); + wxGetApp().set_label_clr_sys(sys_colour->GetColour()); + wxGetApp().set_label_clr_modified(mod_colour->GetColour()); EndModal(wxID_OK); }); diff --git a/xs/src/slic3r/GUI/ButtonsDescription.hpp b/src/slic3r/GUI/ButtonsDescription.hpp similarity index 94% rename from xs/src/slic3r/GUI/ButtonsDescription.hpp rename to src/slic3r/GUI/ButtonsDescription.hpp index 4858eaaea3..81baaf191f 100644 --- a/xs/src/slic3r/GUI/ButtonsDescription.hpp +++ b/src/slic3r/GUI/ButtonsDescription.hpp @@ -14,7 +14,7 @@ class ButtonsDescription : public wxDialog t_icon_descriptions* m_icon_descriptions; public: ButtonsDescription(wxWindow* parent, t_icon_descriptions* icon_descriptions); - ~ButtonsDescription(){} + ~ButtonsDescription() {} }; diff --git a/xs/src/slic3r/GUI/ConfigExceptions.hpp b/src/slic3r/GUI/ConfigExceptions.hpp similarity index 100% rename from xs/src/slic3r/GUI/ConfigExceptions.hpp rename to src/slic3r/GUI/ConfigExceptions.hpp diff --git a/xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/src/slic3r/GUI/ConfigSnapshotDialog.cpp similarity index 100% rename from xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp rename to src/slic3r/GUI/ConfigSnapshotDialog.cpp diff --git a/xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp b/src/slic3r/GUI/ConfigSnapshotDialog.hpp similarity index 100% rename from xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp rename to src/slic3r/GUI/ConfigSnapshotDialog.hpp diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp similarity index 98% rename from xs/src/slic3r/GUI/ConfigWizard.cpp rename to src/slic3r/GUI/ConfigWizard.cpp index e784d85256..ba87cd46a7 100644 --- a/xs/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -380,7 +380,7 @@ void PageVendors::on_variant_checked() PageFirmware::PageFirmware(ConfigWizard *parent) : ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware"))), - gcode_opt(print_config_def.options["gcode_flavor"]), + gcode_opt(*print_config_def.get("gcode_flavor")), gcode_picker(nullptr) { append_text(_(L("Choose the type of firmware used by your printer."))); @@ -440,13 +440,13 @@ PageDiameters::PageDiameters(ConfigWizard *parent) : { spin_nozzle->SetDigits(2); spin_nozzle->SetIncrement(0.1); - const auto &def_nozzle = print_config_def.options["nozzle_diameter"]; + const auto &def_nozzle = *print_config_def.get("nozzle_diameter"); auto *default_nozzle = dynamic_cast(def_nozzle.default_value); spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5); spin_filam->SetDigits(2); spin_filam->SetIncrement(0.25); - const auto &def_filam = print_config_def.options["filament_diameter"]; + const auto &def_filam = *print_config_def.get("filament_diameter"); auto *default_filam = dynamic_cast(def_filam.default_value); spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0); @@ -490,13 +490,13 @@ PageTemperatures::PageTemperatures(ConfigWizard *parent) : spin_bed(new wxSpinCtrlDouble(this, wxID_ANY)) { spin_extr->SetIncrement(5.0); - const auto &def_extr = print_config_def.options["temperature"]; + const auto &def_extr = *print_config_def.get("temperature"); spin_extr->SetRange(def_extr.min, def_extr.max); auto *default_extr = dynamic_cast(def_extr.default_value); spin_extr->SetValue(default_extr != nullptr && default_extr->size() > 0 ? default_extr->get_at(0) : 200); spin_bed->SetIncrement(5.0); - const auto &def_bed = print_config_def.options["bed_temperature"]; + const auto &def_bed = *print_config_def.get("bed_temperature"); spin_bed->SetRange(def_bed.min, def_bed.max); auto *default_bed = dynamic_cast(def_bed.default_value); spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0); @@ -831,8 +831,8 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) : topsizer->AddSpacer(INDEX_MARGIN); topsizer->Add(p->hscroll, 1, wxEXPAND); - p->btn_prev = new wxButton(this, wxID_NONE, _(L("< &Back"))); - p->btn_next = new wxButton(this, wxID_NONE, _(L("&Next >"))); + p->btn_prev = new wxButton(this, wxID_ANY, _(L("< &Back"))); + p->btn_next = new wxButton(this, wxID_ANY, _(L("&Next >"))); p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish"))); p->btn_cancel = new wxButton(this, wxID_CANCEL); p->btnsizer->AddStretchSpacer(); diff --git a/xs/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp similarity index 96% rename from xs/src/slic3r/GUI/ConfigWizard.hpp rename to src/slic3r/GUI/ConfigWizard.hpp index 73fce7cd21..6ac1bcbd84 100644 --- a/xs/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -39,7 +39,7 @@ private: struct priv; std::unique_ptr p; - friend class ConfigWizardPage; + friend struct ConfigWizardPage; }; diff --git a/xs/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp similarity index 100% rename from xs/src/slic3r/GUI/ConfigWizard_private.hpp rename to src/slic3r/GUI/ConfigWizard_private.hpp diff --git a/src/slic3r/GUI/Event.hpp b/src/slic3r/GUI/Event.hpp new file mode 100644 index 0000000000..429ef99b02 --- /dev/null +++ b/src/slic3r/GUI/Event.hpp @@ -0,0 +1,66 @@ +#ifndef slic3r_Events_hpp_ +#define slic3r_Events_hpp_ + +#include +#include + + +namespace Slic3r { + +namespace GUI { + + +struct SimpleEvent : public wxEvent +{ + SimpleEvent(wxEventType type, wxObject* origin = nullptr) : wxEvent(0, type) + { + m_propagationLevel = wxEVENT_PROPAGATE_MAX; + SetEventObject(origin); + } + + virtual wxEvent* Clone() const + { + return new SimpleEvent(GetEventType(), GetEventObject()); + } +}; + +template struct ArrayEvent : public wxEvent +{ + std::array data; + + ArrayEvent(wxEventType type, std::array data, wxObject* origin = nullptr) + : wxEvent(0, type), data(std::move(data)) + { + m_propagationLevel = wxEVENT_PROPAGATE_MAX; + SetEventObject(origin); + } + + virtual wxEvent* Clone() const + { + return new ArrayEvent(GetEventType(), data, GetEventObject()); + } +}; +template struct ArrayEvent : public wxEvent +{ + T data; + + ArrayEvent(wxEventType type, T data, wxObject* origin = nullptr) + : wxEvent(0, type), data(std::move(data)) + { + m_propagationLevel = wxEVENT_PROPAGATE_MAX; + SetEventObject(origin); + } + + virtual wxEvent* Clone() const + { + return new ArrayEvent(GetEventType(), data, GetEventObject()); + } +}; + +template using Event = ArrayEvent; + + +} +} + +#endif // slic3r_Events_hpp_ diff --git a/xs/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp similarity index 55% rename from xs/src/slic3r/GUI/Field.cpp rename to src/slic3r/GUI/Field.cpp index c7f1d48ffd..68d3948bbe 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -7,230 +7,244 @@ #include #include "PrintConfig.hpp" #include +#include "GUI_App.hpp" namespace Slic3r { namespace GUI { - wxString double_to_string(double const value) +wxString double_to_string(double const value, const int max_precision /*= 4*/) +{ + if (value - int(value) == 0) + return wxString::Format(_T("%i"), int(value)); + + int precision = max_precision; + for (size_t p = 1; p < max_precision; p++) { - if (value - int(value) == 0) - return wxString::Format(_T("%i"), int(value)); - else { - int precision = 4; - for (size_t p = 1; p < 4; p++) - { - double cur_val = pow(10, p)*value; - if (cur_val - int(cur_val) == 0) { - precision = p; - break; - } - } - return wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None); + double cur_val = pow(10, p)*value; + if (cur_val - int(cur_val) == 0) { + precision = p; + break; } } + return wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None); +} - void Field::PostInitialize(){ - auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - m_Undo_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - m_Undo_to_sys_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - if (wxMSW) { - m_Undo_btn->SetBackgroundColour(color); - m_Undo_to_sys_btn->SetBackgroundColour(color); +void Field::PostInitialize() +{ + auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + m_Undo_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); + m_Undo_to_sys_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); + if (wxMSW) { + m_Undo_btn->SetBackgroundColour(color); + m_Undo_to_sys_btn->SetBackgroundColour(color); + } + m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_initial_value(); })); + m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_sys_value(); })); + + //set default bitmap + wxBitmap bmp; + bmp.LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG); + set_undo_bitmap(&bmp); + set_undo_to_sys_bitmap(&bmp); + + switch (m_opt.type) + { + case coPercents: + case coFloats: + case coStrings: + case coBools: + case coInts: { + auto tag_pos = m_opt_id.find("#"); + if (tag_pos != std::string::npos) + m_opt_idx = stoi(m_opt_id.substr(tag_pos + 1, m_opt_id.size())); + break; + } + default: + break; + } + + BUILD(); +} + +void Field::on_kill_focus(wxEvent& 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(); + // call the registered function if it is available + if (m_on_kill_focus!=nullptr) + m_on_kill_focus(); +} + +void Field::on_change_field() +{ +// std::cerr << "calling Field::_on_change \n"; + if (m_on_change != nullptr && !m_disable_change_event) + m_on_change(m_opt_id, get_value()); +} + +void Field::on_back_to_initial_value() +{ + if (m_back_to_initial_value != nullptr && m_is_modified_value) + m_back_to_initial_value(m_opt_id); +} + +void Field::on_back_to_sys_value() +{ + if (m_back_to_sys_value != nullptr && m_is_nonsys_value) + m_back_to_sys_value(m_opt_id); +} + +wxString Field::get_tooltip_text(const wxString& default_string) +{ + wxString tooltip_text(""); + wxString tooltip = _(m_opt.tooltip); + if (tooltip.length() > 0) + tooltip_text = tooltip + "\n" + _(L("default value")) + "\t: " + + (boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") + default_string + + (boost::iends_with(m_opt_id, "_gcode") ? "" : "\n") + + _(L("parameter name")) + "\t: " + m_opt_id; + + return tooltip_text; +} + +bool Field::is_matched(const std::string& string, const std::string& pattern) +{ + std::regex regex_pattern(pattern, std::regex_constants::icase); // use ::icase to make the matching case insensitive like /i in perl + return std::regex_match(string, regex_pattern); +} + +void Field::get_value_by_opt_type(wxString& str) +{ + switch (m_opt.type) { + case coInt: + m_value = wxAtoi(str); + break; + case coPercent: + case coPercents: + case coFloats: + case coFloat:{ + if (m_opt.type == coPercent && str.Last() == '%') + str.RemoveLast(); + else if (str.Last() == '%') { + wxString label = m_Label->GetLabel(); + if (label.Last() == '\n') label.RemoveLast(); + while (label.Last() == ' ') label.RemoveLast(); + if (label.Last() == ':') label.RemoveLast(); + show_error(m_parent, wxString::Format(_(L("%s doesn't support percentage")), label)); + set_value(double_to_string(m_opt.min), true); + m_value = double(m_opt.min); + break; } - m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_initial_value(); })); - m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_sys_value(); })); - - //set default bitmap - wxBitmap bmp; - bmp.LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG); - set_undo_bitmap(&bmp); - set_undo_to_sys_bitmap(&bmp); - - switch (m_opt.type) + double val; + if(!str.ToCDouble(&val)) { - case coPercents: - case coFloats: - case coStrings: - case coBools: - case coInts: { - auto tag_pos = m_opt_id.find("#"); - if (tag_pos != std::string::npos) - m_opt_idx = stoi(m_opt_id.substr(tag_pos + 1, m_opt_id.size())); - break; + show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits"))); + set_value(double_to_string(val), true); } - default: - break; - } - - BUILD(); - } - - void Field::on_kill_focus(wxEvent& 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(); - // call the registered function if it is available - if (m_on_kill_focus!=nullptr) - m_on_kill_focus(); - } - void Field::on_change_field() - { -// std::cerr << "calling Field::_on_change \n"; - if (m_on_change != nullptr && !m_disable_change_event) - m_on_change(m_opt_id, get_value()); - } - - void Field::on_back_to_initial_value(){ - if (m_back_to_initial_value != nullptr && m_is_modified_value) - m_back_to_initial_value(m_opt_id); - } - - void Field::on_back_to_sys_value(){ - if (m_back_to_sys_value != nullptr && m_is_nonsys_value) - m_back_to_sys_value(m_opt_id); - } - - wxString Field::get_tooltip_text(const wxString& default_string) - { - wxString tooltip_text(""); - wxString tooltip = _(m_opt.tooltip); - if (tooltip.length() > 0) - tooltip_text = tooltip + "\n" + _(L("default value")) + "\t: " + - (boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") + default_string + - (boost::iends_with(m_opt_id, "_gcode") ? "" : "\n") + - _(L("parameter name")) + "\t: " + m_opt_id; - - return tooltip_text; - } - - bool Field::is_matched(const std::string& string, const std::string& pattern) - { - std::regex regex_pattern(pattern, std::regex_constants::icase); // use ::icase to make the matching case insensitive like /i in perl - return std::regex_match(string, regex_pattern); - } - - void Field::get_value_by_opt_type(wxString& str) - { - switch (m_opt.type){ - case coInt: - m_value = wxAtoi(str); - break; - case coPercent: - case coPercents: - case coFloats: - case coFloat:{ - if (m_opt.type == coPercent && str.Last() == '%') - str.RemoveLast(); - else if (str.Last() == '%') { - wxString label = m_Label->GetLabel(); - if (label.Last() == '\n') label.RemoveLast(); - while (label.Last() == ' ') label.RemoveLast(); - if (label.Last() == ':') label.RemoveLast(); - show_error(m_parent, wxString::Format(_(L("%s doesn't support percentage")), label)); - set_value(double_to_string(m_opt.min), true); - m_value = double(m_opt.min); - break; - } - double val; - str.ToCDouble(&val); - if (m_opt.min > val || val > m_opt.max) - { - show_error(m_parent, _(L("Input value is out of range"))); - if (m_opt.min > val) val = m_opt.min; - if (val > m_opt.max) val = m_opt.max; - set_value(double_to_string(val), true); - } - m_value = val; - break; } - case coString: - case coStrings: - case coFloatOrPercent: - m_value = str.ToStdString(); - break; - default: - break; - } - } - - void TextCtrl::BUILD() { - auto size = wxSize(wxDefaultSize); - if (m_opt.height >= 0) size.SetHeight(m_opt.height); - if (m_opt.width >= 0) size.SetWidth(m_opt.width); - - wxString text_value = wxString(""); - - switch (m_opt.type) { - case coFloatOrPercent: + if (m_opt.min > val || val > m_opt.max) { - text_value = double_to_string(m_opt.default_value->getFloat()); - if (static_cast(m_opt.default_value)->percent) - text_value += "%"; - break; + show_error(m_parent, _(L("Input value is out of range"))); + if (m_opt.min > val) val = m_opt.min; + if (val > m_opt.max) val = m_opt.max; + set_value(double_to_string(val), true); } - case coPercent: - { - text_value = wxString::Format(_T("%i"), int(m_opt.default_value->getFloat())); + m_value = val; + break; } + case coString: + case coStrings: + case coFloatOrPercent: + m_value = str.ToStdString(); + break; + default: + break; + } +} + +void TextCtrl::BUILD() { + auto size = wxSize(wxDefaultSize); + if (m_opt.height >= 0) size.SetHeight(m_opt.height); + if (m_opt.width >= 0) size.SetWidth(m_opt.width); + + wxString text_value = wxString(""); + + switch (m_opt.type) { + case coFloatOrPercent: + { + text_value = double_to_string(m_opt.default_value->getFloat()); + if (static_cast(m_opt.default_value)->percent) text_value += "%"; - break; - } - case coPercents: - case coFloats: - case coFloat: - { - double val = m_opt.type == coFloats ? - static_cast(m_opt.default_value)->get_at(m_opt_idx) : - m_opt.type == coFloat ? - m_opt.default_value->getFloat() : - static_cast(m_opt.default_value)->get_at(m_opt_idx); - text_value = double_to_string(val); - break; - } - case coString: - text_value = static_cast(m_opt.default_value)->value; - break; - case coStrings: - { - const ConfigOptionStrings *vec = static_cast(m_opt.default_value); - if (vec == nullptr || vec->empty()) break; //for the case of empty default value - text_value = vec->get_at(m_opt_idx); - break; - } - default: - break; - } + break; + } + case coPercent: + { + text_value = wxString::Format(_T("%i"), int(m_opt.default_value->getFloat())); + text_value += "%"; + break; + } + case coPercents: + case coFloats: + case coFloat: + { + double val = m_opt.type == coFloats ? + static_cast(m_opt.default_value)->get_at(m_opt_idx) : + m_opt.type == coFloat ? + m_opt.default_value->getFloat() : + static_cast(m_opt.default_value)->get_at(m_opt_idx); + text_value = double_to_string(val); + break; + } + case coString: + text_value = static_cast(m_opt.default_value)->value; + break; + case coStrings: + { + const ConfigOptionStrings *vec = static_cast(m_opt.default_value); + if (vec == nullptr || vec->empty()) break; //for the case of empty default value + text_value = vec->get_at(m_opt_idx); + break; + } + default: + break; + } - auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, (m_opt.multiline ? wxTE_MULTILINE : 0)); + const long style = m_opt.multiline ? wxTE_MULTILINE : 0 | m_process_enter ? wxTE_PROCESS_ENTER : 0; + auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style); - temp->SetToolTip(get_tooltip_text(text_value)); - - temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event) - { - //! to allow the default handling - event.Skip(); - //! eliminating the g-code pop up text description - bool flag = false; + temp->SetToolTip(get_tooltip_text(text_value)); + + temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event) + { + //! to allow the default handling + event.Skip(); + //! eliminating the g-code pop up text description + bool flag = false; #ifdef __WXGTK__ - // I have no idea why, but on GTK flag works in other way - flag = true; + // I have no idea why, but on GTK flag works in other way + flag = true; #endif // __WXGTK__ - temp->GetToolTip()->Enable(flag); - }), temp->GetId()); + temp->GetToolTip()->Enable(flag); + }), temp->GetId()); #if !defined(__WXGTK__) - temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e) - { - e.Skip();// on_kill_focus(e); - temp->GetToolTip()->Enable(true); - }), temp->GetId()); + temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e) + { + e.Skip();// on_kill_focus(e); + temp->GetToolTip()->Enable(true); + }), temp->GetId()); #endif // __WXGTK__ - temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt) - { + if (m_process_enter) { + temp->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent& evt) { on_change_field(); }), temp->GetId()); + } + else { + temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt) + { #ifdef __WXGTK__ - if (bChangedValueEvent) + if (bChangedValueEvent) #endif //__WXGTK__ - on_change_field(); - }), temp->GetId()); + on_change_field(); + }), temp->GetId()); #ifdef __WXGTK__ // to correct value updating on GTK we should: @@ -239,37 +253,38 @@ namespace Slic3r { namespace GUI { temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this); temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this); #endif //__WXGTK__ + } - // select all text using Ctrl+A - temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event) - { - if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL)) - temp->SetSelection(-1, -1); //select all - event.Skip(); - })); - - // recast as a wxWindow to fit the calling convention - window = dynamic_cast(temp); - } - - boost::any& TextCtrl::get_value() + // select all text using Ctrl+A + temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event) { - wxString ret_str = static_cast(window)->GetValue(); - get_value_by_opt_type(ret_str); + if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL)) + temp->SetSelection(-1, -1); //select all + event.Skip(); + })); - return m_value; - } + // recast as a wxWindow to fit the calling convention + window = dynamic_cast(temp); +} - void TextCtrl::enable() { dynamic_cast(window)->Enable(); dynamic_cast(window)->SetEditable(true); } - void TextCtrl::disable() { dynamic_cast(window)->Disable(); dynamic_cast(window)->SetEditable(false); } +boost::any& TextCtrl::get_value() +{ + wxString ret_str = static_cast(window)->GetValue(); + get_value_by_opt_type(ret_str); + + return m_value; +} + +void TextCtrl::enable() { dynamic_cast(window)->Enable(); dynamic_cast(window)->SetEditable(true); } +void TextCtrl::disable() { dynamic_cast(window)->Disable(); dynamic_cast(window)->SetEditable(false); } #ifdef __WXGTK__ - void TextCtrl::change_field_value(wxEvent& event) - { - if (bChangedValueEvent = event.GetEventType()==wxEVT_KEY_UP) - on_change_field(); - event.Skip(); - }; +void TextCtrl::change_field_value(wxEvent& event) +{ + if (bChangedValueEvent = event.GetEventType()==wxEVT_KEY_UP) + on_change_field(); + event.Skip(); +}; #endif //__WXGTK__ void CheckBox::BUILD() { @@ -279,7 +294,7 @@ void CheckBox::BUILD() { bool check_value = m_opt.type == coBool ? m_opt.default_value->getBool() : m_opt.type == coBools ? - static_cast(m_opt.default_value)->get_at(m_opt_idx) : + static_cast(m_opt.default_value)->get_at(m_opt_idx) : false; auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size); @@ -335,9 +350,7 @@ void SpinCtrl::BUILD() { break; } - const int min_val = m_opt_id == "standby_temperature_delta" ? - -500 : m_opt.min > 0 ? - m_opt.min : 0; + const int min_val = m_opt.min == INT_MIN ? 0: m_opt.min; const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647; auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, @@ -381,10 +394,10 @@ void Choice::BUILD() { // recast as a wxWindow to fit the calling convention window = dynamic_cast(temp); - if (m_opt.enum_labels.empty() && m_opt.enum_values.empty()){ + if (m_opt.enum_labels.empty() && m_opt.enum_values.empty()) { } else{ - for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels){ + for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels) { const wxString& str = _(el);//m_opt_id == "support" ? _(el) : el; temp->Append(str); } @@ -399,7 +412,7 @@ void Choice::BUILD() { void Choice::set_selection() { wxString text_value = wxString(""); - switch (m_opt.type){ + switch (m_opt.type) { case coFloat: case coPercent: { double val = m_opt.default_value->getFloat(); @@ -478,12 +491,12 @@ void Choice::set_value(const boost::any& value, bool change_event) { m_disable_change_event = !change_event; - switch (m_opt.type){ + switch (m_opt.type) { case coInt: case coFloat: case coPercent: case coString: - case coStrings:{ + case coStrings: { wxString text_value; if (m_opt.type == coInt) text_value = wxString::Format(_T("%i"), int(boost::any_cast(value))); @@ -501,11 +514,11 @@ void Choice::set_value(const boost::any& value, bool change_event) dynamic_cast(window)->SetSelection(idx); break; } - case coEnum:{ + case coEnum: { int val = boost::any_cast(value); if (m_opt_id.compare("external_fill_pattern") == 0) { - if (!m_opt.enum_values.empty()){ + if (!m_opt.enum_values.empty()) { std::string key; t_config_enum_values map_names = ConfigOptionEnum::get_enum_values(); for (auto it : map_names) { @@ -563,8 +576,11 @@ boost::any& Choice::get_value() // boost::any m_value; wxString ret_str = static_cast(window)->GetValue(); - if (m_opt_id == "support") - return m_value = boost::any(ret_str);//ret_str; + // options from right panel + std::vector right_panel_options{ "support", "scale_unit" }; + for (auto rp_option: right_panel_options) + if (m_opt_id == rp_option) + return m_value = boost::any(ret_str); if (m_opt.type != coEnum) /*m_value = */get_value_by_opt_type(ret_str); @@ -573,7 +589,7 @@ boost::any& Choice::get_value() int ret_enum = static_cast(window)->GetSelection(); if (m_opt_id.compare("external_fill_pattern") == 0) { - if (!m_opt.enum_values.empty()){ + if (!m_opt.enum_values.empty()) { std::string key = m_opt.enum_values[ret_enum]; t_config_enum_values map_names = ConfigOptionEnum::get_enum_values(); int value = map_names.at(key); @@ -604,18 +620,25 @@ void ColourPicker::BUILD() if (m_opt.height >= 0) size.SetHeight(m_opt.height); if (m_opt.width >= 0) size.SetWidth(m_opt.width); - wxString clr(static_cast(m_opt.default_value)->get_at(m_opt_idx)); + // Validate the color + wxString clr_str(static_cast(m_opt.default_value)->get_at(m_opt_idx)); + wxColour clr(clr_str); + if (! clr.IsOk()) { + clr = wxTransparentColour; + } + auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size); - + // // recast as a wxWindow to fit the calling convention window = dynamic_cast(temp); temp->Bind(wxEVT_COLOURPICKER_CHANGED, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); - temp->SetToolTip(get_tooltip_text(clr)); + temp->SetToolTip(get_tooltip_text(clr_str)); } -boost::any& ColourPicker::get_value(){ +boost::any& ColourPicker::get_value() +{ // boost::any m_value; auto colour = static_cast(window)->GetColour(); @@ -636,10 +659,10 @@ void PointCtrl::BUILD() // wxSize field_size(40, -1); - auto default_pt = static_cast(m_opt.default_value)->values.at(0); - double val = default_pt.x; + auto default_pt = static_cast(m_opt.default_value)->values.at(0); + double val = default_pt(0); wxString X = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None); - val = default_pt.y; + val = default_pt(1); wxString Y = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None); x_textctrl = new wxTextCtrl(m_parent, wxID_ANY, X, wxDefaultPosition, field_size); @@ -660,13 +683,13 @@ void PointCtrl::BUILD() y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y)); } -void PointCtrl::set_value(const Pointf& value, bool change_event) +void PointCtrl::set_value(const Vec2d& value, bool change_event) { m_disable_change_event = !change_event; - double val = value.x; + double val = value(0); x_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None)); - val = value.y; + val = value(1); y_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None)); m_disable_change_event = false; @@ -674,8 +697,8 @@ void PointCtrl::set_value(const Pointf& value, bool change_event) void PointCtrl::set_value(const boost::any& value, bool change_event) { - Pointf pt; - const Pointf *ptf = boost::any_cast(&value); + Vec2d pt(Vec2d::Zero()); + const Vec2d *ptf = boost::any_cast(&value); if (!ptf) { ConfigOptionPoints* pts = boost::any_cast(value); @@ -688,13 +711,10 @@ void PointCtrl::set_value(const boost::any& value, bool change_event) boost::any& PointCtrl::get_value() { - Pointf ret_point; - double val; - x_textctrl->GetValue().ToDouble(&val); - ret_point.x = val; - y_textctrl->GetValue().ToDouble(&val); - ret_point.y = val; - return m_value = ret_point; + double x, y; + x_textctrl->GetValue().ToDouble(&x); + y_textctrl->GetValue().ToDouble(&y); + return m_value = Vec2d(x, y); } void StaticText::BUILD() @@ -703,9 +723,9 @@ void StaticText::BUILD() if (m_opt.height >= 0) size.SetHeight(m_opt.height); if (m_opt.width >= 0) size.SetWidth(m_opt.width); - wxString legend(static_cast(m_opt.default_value)->value); + wxString legend(static_cast(m_opt.default_value)->value); auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size); - temp->SetFont(bold_font()); + temp->SetFont(wxGetApp().bold_font()); // // recast as a wxWindow to fit the calling convention window = dynamic_cast(temp); @@ -713,6 +733,69 @@ void StaticText::BUILD() temp->SetToolTip(get_tooltip_text(legend)); } +void SliderCtrl::BUILD() +{ + auto size = wxSize(wxDefaultSize); + if (m_opt.height >= 0) size.SetHeight(m_opt.height); + if (m_opt.width >= 0) size.SetWidth(m_opt.width); + + auto temp = new wxBoxSizer(wxHORIZONTAL); + + auto def_val = static_cast(m_opt.default_value)->value; + auto min = m_opt.min == INT_MIN ? 0 : m_opt.min; + auto max = m_opt.max == INT_MAX ? 100 : m_opt.max; + + m_slider = new wxSlider(m_parent, wxID_ANY, def_val * m_scale, + min * m_scale, max * m_scale, + wxDefaultPosition, size); + wxSize field_size(40, -1); + + m_textctrl = new wxTextCtrl(m_parent, wxID_ANY, wxString::Format("%d", m_slider->GetValue()/m_scale), + wxDefaultPosition, field_size); + + temp->Add(m_slider, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, 0); + temp->Add(m_textctrl, 0, wxALIGN_CENTER_VERTICAL, 0); + + m_slider->Bind(wxEVT_SLIDER, ([this](wxCommandEvent e) { + if (!m_disable_change_event) { + int val = boost::any_cast(get_value()); + m_textctrl->SetLabel(wxString::Format("%d", val)); + on_change_field(); + } + }), m_slider->GetId()); + + m_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { + std::string value = e.GetString().utf8_str().data(); + if (is_matched(value, "^-?\\d+(\\.\\d*)?$")) { + m_disable_change_event = true; + m_slider->SetValue(stoi(value)*m_scale); + m_disable_change_event = false; + on_change_field(); + } + }), m_textctrl->GetId()); + + m_sizer = dynamic_cast(temp); +} + +void SliderCtrl::set_value(const boost::any& value, bool change_event) +{ + m_disable_change_event = !change_event; + + m_slider->SetValue(boost::any_cast(value)*m_scale); + int val = boost::any_cast(get_value()); + m_textctrl->SetLabel(wxString::Format("%d", val)); + + m_disable_change_event = false; +} + +boost::any& SliderCtrl::get_value() +{ +// int ret_val; +// x_textctrl->GetValue().ToDouble(&val); + return m_value = int(m_slider->GetValue()/m_scale); +} + + } // GUI } // Slic3r diff --git a/xs/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp similarity index 87% rename from xs/src/slic3r/GUI/Field.hpp rename to src/slic3r/GUI/Field.hpp index 923f0fd7e2..29ae6f9ce0 100644 --- a/xs/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -34,10 +34,11 @@ using t_kill_focus = std::function; using t_change = std::function; using t_back_to_init = std::function; -wxString double_to_string(double const value); +wxString double_to_string(double const value, const int max_precision = 4); class MyButton : public wxButton { + bool hidden = false; // never show button if it's hidden ones public: MyButton() {} MyButton(wxWindow* parent, wxWindowID id, const wxString& label = wxEmptyString, @@ -52,6 +53,15 @@ public: // overridden from wxWindow base class virtual bool AcceptsFocusFromKeyboard() const { return false; } + + void set_as_hidden() { + Hide(); + hidden = true; + } + + virtual bool Show(bool show = true) override { + return wxButton::Show(hidden ? false : show); + } }; class Field { @@ -129,9 +139,10 @@ public: /// Factory method for generating new derived classes. template - static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) // interface for creating shared objects + static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id, const bool process_enter = false)// interface for creating shared objects { auto p = Slic3r::make_unique(parent, opt, id); + p->m_process_enter = process_enter; p->PostInitialize(); return std::move(p); //!p; } @@ -189,6 +200,10 @@ public: return false; } + void set_side_text_ptr(wxStaticText* side_text) { + m_side_text = side_text; + } + protected: MyButton* m_Undo_btn = nullptr; // Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one. @@ -203,8 +218,13 @@ protected: // Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one. const wxColour* m_label_color = nullptr; + wxStaticText* m_side_text = nullptr; + // current value boost::any m_value; + + //this variable shows a mode of a call of the on_change function + bool m_process_enter { false }; friend class OptionsGroup; }; @@ -214,7 +234,7 @@ protected: inline bool is_bad_field(const t_field& obj) { return obj->getSizer() == nullptr && obj->getWindow() == nullptr; } /// Covenience function to determine whether this field is a valid window field. -inline bool is_window_field(const t_field& obj) { return !is_bad_field(obj) && obj->getWindow() != nullptr; } +inline bool is_window_field(const t_field& obj) { return !is_bad_field(obj) && obj->getWindow() != nullptr && obj->getSizer() == nullptr; } /// Covenience function to determine whether this field is a valid sizer field. inline bool is_sizer_field(const t_field& obj) { return !is_bad_field(obj) && obj->getSizer() != nullptr; } @@ -373,7 +393,7 @@ public: void BUILD() override; - void set_value(const Pointf& value, bool change_event = false); + void set_value(const Vec2d& value, bool change_event = false); void set_value(const boost::any& value, bool change_event = false); boost::any& get_value() override; @@ -409,11 +429,44 @@ public: boost::any& get_value()override { return m_value; } - void enable() override { dynamic_cast(window)->Enable(); }; - void disable() override{ dynamic_cast(window)->Disable(); }; + void enable() override { dynamic_cast(window)->Enable(); }; + void disable() override{ dynamic_cast(window)->Disable(); }; wxWindow* getWindow() override { return window; } }; +class SliderCtrl : public Field { + using Field::Field; +public: + SliderCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} + SliderCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} + ~SliderCtrl() {} + + wxSizer* m_sizer{ nullptr }; + wxTextCtrl* m_textctrl{ nullptr }; + wxSlider* m_slider{ nullptr }; + + int m_scale = 10; + + void BUILD() override; + + void set_value(const int value, bool change_event = false); + void set_value(const boost::any& value, bool change_event = false); + boost::any& get_value() override; + + void enable() override { + m_slider->Enable(); + m_textctrl->Enable(); + m_textctrl->SetEditable(true); + } + void disable() override{ + m_slider->Disable(); + m_textctrl->Disable(); + m_textctrl->SetEditable(false); + } + wxSizer* getSizer() override { return m_sizer; } + wxWindow* getWindow() override { return dynamic_cast(m_slider); } +}; + } // GUI } // Slic3r diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/src/slic3r/GUI/FirmwareDialog.cpp similarity index 99% rename from xs/src/slic3r/GUI/FirmwareDialog.cpp rename to src/slic3r/GUI/FirmwareDialog.cpp index d5ac64d900..b836b8e65e 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/src/slic3r/GUI/FirmwareDialog.cpp @@ -35,6 +35,7 @@ #include #include #include +#include "GUI_App.hpp" namespace fs = boost::filesystem; @@ -213,7 +214,7 @@ void FirmwareDialog::priv::flashing_start(unsigned tasks) modal_response = wxID_NONE; txt_stdout->Clear(); set_txt_status(label_status_flashing); - txt_status->SetForegroundColour(GUI::get_label_clr_modified()); + txt_status->SetForegroundColour(GUI::wxGetApp().get_label_clr_modified()); port_picker->Disable(); btn_rescan->Disable(); hex_picker->Disable(); diff --git a/xs/src/slic3r/GUI/FirmwareDialog.hpp b/src/slic3r/GUI/FirmwareDialog.hpp similarity index 100% rename from xs/src/slic3r/GUI/FirmwareDialog.hpp rename to src/slic3r/GUI/FirmwareDialog.hpp diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp similarity index 55% rename from xs/src/slic3r/GUI/GLCanvas3D.cpp rename to src/slic3r/GUI/GLCanvas3D.cpp index ce1c55264e..b9e1f5195c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,25 +1,34 @@ #include "GLCanvas3D.hpp" -#include "../../admesh/stl.h" -#include "../../libslic3r/libslic3r.h" -#include "../../slic3r/GUI/3DScene.hpp" -#include "../../slic3r/GUI/GLShader.hpp" -#include "../../slic3r/GUI/GUI.hpp" -#include "../../slic3r/GUI/PresetBundle.hpp" -#include "../../slic3r/GUI/GLGizmo.hpp" -#include "../../libslic3r/ClipperUtils.hpp" -#include "../../libslic3r/PrintConfig.hpp" -#include "../../libslic3r/Print.hpp" -#include "../../libslic3r/GCode/PreviewData.hpp" +#include "admesh/stl.h" +#include "libslic3r/libslic3r.h" +#include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/GLShader.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/GUI/GLGizmo.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/GCode/PreviewData.hpp" +#include "libslic3r/Geometry.hpp" +#include "GUI_App.hpp" +#include "GUI_ObjectList.hpp" +#include "GUI_ObjectManipulation.hpp" #include #include -#include #include #include #include #include +#include + +// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. +#include "libslic3r/Print.hpp" +#include "libslic3r/SLAPrint.hpp" + +#include "wxExtensions.hpp" #include #include @@ -46,6 +55,8 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f }; static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f; static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; +static const float GIZMO_RESET_BUTTON_HEIGHT = 22.0f; +static const float GIZMO_RESET_BUTTON_WIDTH = 70.f; static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, @@ -72,8 +83,8 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool if (generate_tex_coords) m_tex_coords = std::vector(t_size, 0.0f); - float min_x = (float)unscale(triangles[0].points[0].x); - float min_y = (float)unscale(triangles[0].points[0].y); + float min_x = unscale(triangles[0].points[0](0)); + float min_y = unscale(triangles[0].points[0](1)); float max_x = min_x; float max_y = min_y; @@ -84,8 +95,8 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool for (unsigned int v = 0; v < 3; ++v) { const Point& p = t.points[v]; - float x = (float)unscale(p.x); - float y = (float)unscale(p.y); + float x = unscale(p(0)); + float y = unscale(p(1)); m_vertices[v_coord++] = x; m_vertices[v_coord++] = y; @@ -138,11 +149,11 @@ bool GeometryBuffer::set_from_lines(const Lines& lines, float z) unsigned int coord = 0; for (const Line& l : lines) { - m_vertices[coord++] = (float)unscale(l.a.x); - m_vertices[coord++] = (float)unscale(l.a.y); + m_vertices[coord++] = unscale(l.a(0)); + m_vertices[coord++] = unscale(l.a(1)); m_vertices[coord++] = z; - m_vertices[coord++] = (float)unscale(l.b.x); - m_vertices[coord++] = (float)unscale(l.b.y); + m_vertices[coord++] = unscale(l.b(0)); + m_vertices[coord++] = unscale(l.b(1)); m_vertices[coord++] = z; } @@ -312,15 +323,16 @@ bool GLCanvas3D::Bed::set_shape(const Pointfs& shape) if (m_shape == shape && m_type == new_type) // No change, no need to update the UI. return false; + m_shape = shape; m_type = new_type; _calc_bounding_box(); ExPolygon poly; - for (const Pointf& p : m_shape) + for (const Vec2d& p : m_shape) { - poly.contour.append(Point(scale_(p.x), scale_(p.y))); + poly.contour.append(Point(scale_(p(0)), scale_(p(1)))); } _calc_triangles(poly); @@ -374,9 +386,9 @@ void GLCanvas3D::Bed::render(float theta) const void GLCanvas3D::Bed::_calc_bounding_box() { m_bounding_box = BoundingBoxf3(); - for (const Pointf& p : m_shape) + for (const Vec2d& p : m_shape) { - m_bounding_box.merge(Pointf3(p.x, p.y, 0.0)); + m_bounding_box.merge(Vec3d(p(0), p(1), 0.0)); } } @@ -392,23 +404,23 @@ void GLCanvas3D::Bed::_calc_triangles(const ExPolygon& poly) void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) { Polylines axes_lines; - for (coord_t x = bed_bbox.min.x; x <= bed_bbox.max.x; x += scale_(10.0)) + for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0)) { Polyline line; - line.append(Point(x, bed_bbox.min.y)); - line.append(Point(x, bed_bbox.max.y)); + line.append(Point(x, bed_bbox.min(1))); + line.append(Point(x, bed_bbox.max(1))); axes_lines.push_back(line); } - for (coord_t y = bed_bbox.min.y; y <= bed_bbox.max.y; y += scale_(10.0)) + for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0)) { Polyline line; - line.append(Point(bed_bbox.min.x, y)); - line.append(Point(bed_bbox.max.x, y)); + line.append(Point(bed_bbox.min(0), y)); + line.append(Point(bed_bbox.max(0), y)); axes_lines.push_back(line); } // clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped - Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, SCALED_EPSILON))); + Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, (float)SCALED_EPSILON))); // append bed contours Lines contour_lines = to_lines(poly); @@ -422,7 +434,7 @@ GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const { EType type = Custom; - const PresetBundle* bundle = get_preset_bundle(); + auto bundle = wxGetApp().preset_bundle; if (bundle != nullptr) { const Preset* curr = &bundle->printers.get_selected_preset(); @@ -508,6 +520,7 @@ void GLCanvas3D::Bed::_render_prusa(float theta) const if (triangles_vcount > 0) { ::glEnable(GL_DEPTH_TEST); + ::glDepthMask(GL_FALSE); ::glEnable(GL_BLEND); ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -536,6 +549,7 @@ void GLCanvas3D::Bed::_render_prusa(float theta) const ::glDisable(GL_TEXTURE_2D); ::glDisable(GL_BLEND); + ::glDepthMask(GL_TRUE); } } @@ -592,7 +606,8 @@ bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2) } GLCanvas3D::Axes::Axes() - : length(0.0f) + : origin(Vec3d::Zero()) + , length(0.0f) { } @@ -607,12 +622,12 @@ void GLCanvas3D::Axes::render(bool depth_test) const ::glBegin(GL_LINES); // draw line for x axis ::glColor3f(1.0f, 0.0f, 0.0f); - ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z); - ::glVertex3f((GLfloat)origin.x + length, (GLfloat)origin.y, (GLfloat)origin.z); + ::glVertex3dv(origin.data()); + ::glVertex3f((GLfloat)origin(0) + length, (GLfloat)origin(1), (GLfloat)origin(2)); // draw line for y axis ::glColor3f(0.0f, 1.0f, 0.0f); - ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z); - ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y + length, (GLfloat)origin.z); + ::glVertex3dv(origin.data()); + ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1) + length, (GLfloat)origin(2)); ::glEnd(); // draw line for Z axis // (re-enable depth test so that axis is correctly shown when objects are behind it) @@ -621,75 +636,11 @@ void GLCanvas3D::Axes::render(bool depth_test) const ::glBegin(GL_LINES); ::glColor3f(0.0f, 0.0f, 1.0f); - ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z); - ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z + length); + ::glVertex3dv(origin.data()); + ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2) + length); ::glEnd(); } -GLCanvas3D::CuttingPlane::CuttingPlane() - : m_z(-1.0f) -{ -} - -bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons) -{ - m_z = z; - - // grow slices in order to display them better - ExPolygons expolygons = offset_ex(polygons, scale_(0.1)); - Lines lines = to_lines(expolygons); - return m_lines.set_from_lines(lines, m_z); -} - -void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) const -{ - _render_plane(bb); - _render_contour(); -} - -void GLCanvas3D::CuttingPlane::_render_plane(const BoundingBoxf3& bb) const -{ - if (m_z >= 0.0f) - { - ::glDisable(GL_CULL_FACE); - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - float margin = 20.0f; - float min_x = bb.min.x - margin; - float max_x = bb.max.x + margin; - float min_y = bb.min.y - margin; - float max_y = bb.max.y + margin; - - ::glBegin(GL_QUADS); - ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); - ::glVertex3f(min_x, min_y, m_z); - ::glVertex3f(max_x, min_y, m_z); - ::glVertex3f(max_x, max_y, m_z); - ::glVertex3f(min_x, max_y, m_z); - ::glEnd(); - - ::glEnable(GL_CULL_FACE); - ::glDisable(GL_BLEND); - } -} - -void GLCanvas3D::CuttingPlane::_render_contour() const -{ - ::glEnableClientState(GL_VERTEX_ARRAY); - - if (m_z >= 0.0f) - { - unsigned int lines_vcount = m_lines.get_vertices_count(); - - ::glLineWidth(2.0f); - ::glColor3f(0.0f, 0.0f, 0.0f); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_lines.get_vertices()); - ::glDrawArrays(GL_LINES, 0, (GLsizei)lines_vcount); - } - - ::glDisableClientState(GL_VERTEX_ARRAY); -} GLCanvas3D::Shader::Shader() : m_shader(nullptr) @@ -870,8 +821,8 @@ float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) { const Point& mouse_pos = canvas.get_local_mouse_position(); const Rect& rect = get_bar_rect_screen(canvas); - float x = (float)mouse_pos.x; - float y = (float)mouse_pos.y; + float x = (float)mouse_pos(0); + float y = (float)mouse_pos(1); float t = rect.get_top(); float b = rect.get_bottom(); @@ -980,7 +931,7 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const { - float max_z = print_object.model_object()->bounding_box().max.z; + float max_z = print_object.model_object()->bounding_box().max(2); m_shader.start_using(); @@ -1027,7 +978,7 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, // Get a maximum layer height value. // FIXME This is a duplicate code of Slicing.cpp. double layer_height_max = DBL_MAX; - const PrintConfig& print_config = print_object.print()->config; + const PrintConfig& print_config = print_object.print()->config(); const std::vector& nozzle_diameters = dynamic_cast(print_config.option("nozzle_diameter"))->values; const std::vector& layer_heights_min = dynamic_cast(print_config.option("min_layer_height"))->values; const std::vector& layer_heights_max = dynamic_cast(print_config.option("max_layer_height"))->values; @@ -1041,8 +992,8 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, // Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region. layer_height_max *= 1.12; - coordf_t max_z = unscale(print_object.size.z); - double layer_height = dynamic_cast(print_object.config.option("layer_height"))->value; + double max_z = unscale(print_object.size(2)); + double layer_height = dynamic_cast(print_object.config().option("layer_height"))->value; float l = bar_rect.get_left(); float w = bar_rect.get_right() - l; float b = bar_rect.get_bottom(); @@ -1063,7 +1014,7 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, const ModelObject* model_object = print_object.model_object(); if (model_object->layer_height_profile_valid) { - const std::vector& profile = model_object->layer_height_profile; + const std::vector& profile = model_object->layer_height_profile; ::glColor3f(0.0f, 0.0f, 1.0f); ::glBegin(GL_LINE_STRIP); @@ -1076,20 +1027,24 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, } const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); -const Pointf3 GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX); +const Vec3d GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX); GLCanvas3D::Mouse::Drag::Drag() : start_position_2D(Invalid_2D_Point) , start_position_3D(Invalid_3D_Point) - , move_with_shift(false) , move_volume_idx(-1) - , gizmo_volume_idx(-1) { } GLCanvas3D::Mouse::Mouse() : dragging(false) , position(DBL_MAX, DBL_MAX) +#if ENABLE_GIZMOS_ON_TOP + , scene_position(DBL_MAX, DBL_MAX, DBL_MAX) +#endif // ENABLE_GIZMOS_ON_TOP +#if ENABLE_GIZMOS_RESET + , ignore_up_event(false) +#endif // ENABLE_GIZMOS_RESET { } @@ -1113,6 +1068,1202 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const return (drag.start_position_3D != Drag::Invalid_3D_Point); } +#if ENABLE_MODELVOLUME_TRANSFORM +GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache() + : position(Vec3d::Zero()) + , rotation(Vec3d::Zero()) + , scaling_factor(Vec3d::Ones()) + , rotation_matrix(Transform3d::Identity()) + , scale_matrix(Transform3d::Identity()) +{ +} + +GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache(const Geometry::Transformation& transform) + : position(transform.get_offset()) + , rotation(transform.get_rotation()) + , scaling_factor(transform.get_scaling_factor()) +{ + rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation); + scale_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scaling_factor); +} + +GLCanvas3D::Selection::VolumeCache::VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform) + : m_volume(volume_transform) + , m_instance(instance_transform) +{ +} +#else +GLCanvas3D::Selection::VolumeCache::VolumeCache() + : m_position(Vec3d::Zero()) + , m_rotation(Vec3d::Zero()) + , m_scaling_factor(Vec3d::Ones()) +{ + m_rotation_matrix = Transform3d::Identity(); + m_scale_matrix = Transform3d::Identity(); +} + +GLCanvas3D::Selection::VolumeCache::VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor) + : m_position(position) + , m_rotation(rotation) + , m_scaling_factor(scaling_factor) +{ + m_rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), m_rotation); + m_scale_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), m_scaling_factor); +} +#endif // ENABLE_MODELVOLUME_TRANSFORM + +GLCanvas3D::Selection::Selection() + : m_volumes(nullptr) + , m_model(nullptr) + , m_mode(Instance) + , m_type(Empty) + , m_valid(false) + , m_bounding_box_dirty(true) +{ +} + +void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes) +{ + m_volumes = volumes; + _update_valid(); +} + +void GLCanvas3D::Selection::set_model(Model* model) +{ + m_model = model; + _update_valid(); +} + +void GLCanvas3D::Selection::add(unsigned int volume_idx, bool as_single_selection) +{ + if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) + return; + + const GLVolume* volume = (*m_volumes)[volume_idx]; + // wipe tower is already selected + if (is_wipe_tower() && volume->is_wipe_tower) + return; + + // resets the current list if needed + bool needs_reset = as_single_selection; + needs_reset |= volume->is_wipe_tower; + needs_reset |= is_wipe_tower() && !volume->is_wipe_tower; + needs_reset |= !is_modifier() && volume->is_modifier; + needs_reset |= is_modifier() && !volume->is_modifier; + + if (needs_reset) + clear(); + + if (volume->is_modifier) + m_mode = Volume; + + switch (m_mode) + { + case Volume: + { + if (volume->volume_idx() >= 0 && (is_empty() || (volume->instance_idx() == get_instance_idx()))) + _add_volume(volume_idx); + + break; + } + case Instance: + { + _add_instance(volume->object_idx(), volume->instance_idx()); + break; + } +#if !ENABLE_MODELVOLUME_TRANSFORM + case Object: + { + _add_object(volume->object_idx()); + break; + } +#endif // !ENABLE_MODELVOLUME_TRANSFORM + } + + _update_type(); + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::remove(unsigned int volume_idx) +{ + if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) + return; + + GLVolume* volume = (*m_volumes)[volume_idx]; + + switch (m_mode) + { + case Volume: + { + _remove_volume(volume_idx); + break; + } + case Instance: + { + _remove_instance(volume->object_idx(), volume->instance_idx()); + break; + } +#if !ENABLE_MODELVOLUME_TRANSFORM + case Object: + { + _remove_object(volume->object_idx()); + break; + } +#endif // !ENABLE_MODELVOLUME_TRANSFORM + } + + _update_type(); + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::add_object(unsigned int object_idx, bool as_single_selection) +{ + if (!m_valid) + return; + + // resets the current list if needed + if (as_single_selection) + clear(); + + m_mode = Instance; + + _add_object(object_idx); + + _update_type(); + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::remove_object(unsigned int object_idx) +{ + if (!m_valid) + return; + + _remove_object(object_idx); + + _update_type(); + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection) +{ + if (!m_valid) + return; + + // resets the current list if needed + if (as_single_selection) + clear(); + + m_mode = Instance; + + _add_instance(object_idx, instance_idx); + + _update_type(); + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::remove_instance(unsigned int object_idx, unsigned int instance_idx) +{ + if (!m_valid) + return; + + _remove_instance(object_idx, instance_idx); + + _update_type(); + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection) +{ + if (!m_valid) + return; + + // resets the current list if needed + if (as_single_selection) + clear(); + + m_mode = Volume; + + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) + { + if ((instance_idx != -1) && (v->instance_idx() == instance_idx)) + _add_volume(i); + } + } + + _update_type(); + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx) +{ + if (!m_valid) + return; + + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) + _remove_volume(i); + } + + _update_type(); + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::clear() +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + (*m_volumes)[i]->selected = false; + } + + m_list.clear(); + + _update_type(); + m_bounding_box_dirty = true; +} + +bool GLCanvas3D::Selection::is_single_full_instance() const +{ + if (m_type == SingleFullInstance) + return true; + + int object_idx = m_valid ? get_object_idx() : -1; + if ((0 <= object_idx) && (object_idx < (int)m_model->objects.size())) + return m_model->objects[object_idx]->volumes.size() == m_list.size(); + + return false; +} + +int GLCanvas3D::Selection::get_object_idx() const +{ + return (m_cache.content.size() == 1) ? m_cache.content.begin()->first : -1; +} + +int GLCanvas3D::Selection::get_instance_idx() const +{ + if (m_cache.content.size() == 1) + { + const InstanceIdxsList& idxs = m_cache.content.begin()->second; + if (idxs.size() == 1) + return *idxs.begin(); + } + + return -1; +} + +const GLCanvas3D::Selection::InstanceIdxsList& GLCanvas3D::Selection::get_instance_idxs() const +{ + assert(m_cache.content.size() == 1); + return m_cache.content.begin()->second; +} + +const GLVolume* GLCanvas3D::Selection::get_volume(unsigned int volume_idx) const +{ + return (m_valid && (volume_idx < (unsigned int)m_volumes->size())) ? (*m_volumes)[volume_idx] : nullptr; +} + +const BoundingBoxf3& GLCanvas3D::Selection::get_bounding_box() const +{ + if (m_bounding_box_dirty) + _calc_bounding_box(); + + return m_bounding_box; +} + +void GLCanvas3D::Selection::start_dragging() +{ + if (!m_valid) + return; + + _set_caches(); +} + +void GLCanvas3D::Selection::translate(const Vec3d& displacement) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { +#if ENABLE_MODELVOLUME_TRANSFORM + if (m_mode == Instance) + (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); + else if (m_mode == Volume) + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); +#else + (*m_volumes)[i]->set_offset(m_cache.volumes_data[i].get_position() + displacement); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + _synchronize_unselected_instances(); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + if (is_single_full_instance()) +#if ENABLE_MODELVOLUME_TRANSFORM + (*m_volumes)[i]->set_instance_rotation(rotation); +#else + (*m_volumes)[i]->set_rotation(rotation); +#endif // ENABLE_MODELVOLUME_TRANSFORM +#if ENABLE_MODELVOLUME_TRANSFORM + else if (is_single_volume() || is_single_modifier()) + (*m_volumes)[i]->set_volume_rotation(rotation); +#endif // ENABLE_MODELVOLUME_TRANSFORM + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); +#if ENABLE_MODELVOLUME_TRANSFORM + if (m_mode == Instance) + { + // extracts rotations from the composed transformation + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()); + if (!local) + (*m_volumes)[i]->set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); + + (*m_volumes)[i]->set_instance_rotation(new_rotation); + } + else if (m_mode == Volume) + { + // extracts rotations from the composed transformation + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + if (!local) + { + Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); + (*m_volumes)[i]->set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset); + } + (*m_volumes)[i]->set_volume_rotation(new_rotation); + } +#else + // extracts rotations from the composed transformation + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_rotation_matrix()); + (*m_volumes)[i]->set_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_position() - m_cache.dragging_center)); + (*m_volumes)[i]->set_rotation(new_rotation); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + } + +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + _synchronize_unselected_instances(); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::flattening_rotate(const Vec3d& normal) +{ + // We get the normal in untransformed coordinates. We must transform it using the instance matrix, find out + // how to rotate the instance so it faces downwards and do the rotation. All that for all selected instances. + // The function assumes that is_from_single_object() holds. + + if (!m_valid) + return; + + for (unsigned int i : m_list) + { +#if ENABLE_MODELVOLUME_TRANSFORM + Transform3d wst = m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_volume_scale_matrix(); + Vec3d scaling_factor = Vec3d(1./wst(0,0), 1./wst(1,1), 1./wst(2,2)); + + Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_volume_rotation_matrix()); + Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor) * normal; + transformed_normal.normalize(); + + Vec3d axis = transformed_normal(2) > 0.999f ? Vec3d(1., 0., 0.) : Vec3d(transformed_normal.cross(Vec3d(0., 0., -1.))); + axis.normalize(); + + Transform3d extra_rotation = Transform3d::Identity(); + extra_rotation.rotate(Eigen::AngleAxisd(acos(-transformed_normal(2)), axis)); + + Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_instance_rotation_matrix() ); + (*m_volumes)[i]->set_instance_rotation(new_rotation); +#else + Transform3d wst = m_cache.volumes_data[i].get_scale_matrix() * m_cache.volumes_data[i].get_scale_matrix(); + Vec3d scaling_factor = Vec3d(1. / wst(0, 0), 1. / wst(1, 1), 1. / wst(2, 2)); + + Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_rotation_matrix() * m_cache.volumes_data[i].get_rotation_matrix()); + Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor) * normal; + transformed_normal.normalize(); + + Vec3d axis = transformed_normal(2) > 0.999f ? Vec3d(1., 0., 0.) : Vec3d(transformed_normal.cross(Vec3d(0., 0., -1.))); + axis.normalize(); + + Transform3d extra_rotation = Transform3d::Identity(); + extra_rotation.rotate(Eigen::AngleAxisd(acos(-transformed_normal(2)), axis)); + + Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_rotation_matrix()); + (*m_volumes)[i]->set_rotation(new_rotation); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + _synchronize_unselected_instances(); +#endif // !DISABLE_INSTANCES_SYNCH + + m_bounding_box_dirty = true; +} + + +void GLCanvas3D::Selection::scale(const Vec3d& scale) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + if (is_single_full_instance()) +#if ENABLE_MODELVOLUME_TRANSFORM + (*m_volumes)[i]->set_instance_scaling_factor(scale); +#else + (*m_volumes)[i]->set_scaling_factor(scale); +#endif // ENABLE_MODELVOLUME_TRANSFORM +#if ENABLE_MODELVOLUME_TRANSFORM + else if (is_single_volume() || is_single_modifier()) + (*m_volumes)[i]->set_volume_scaling_factor(scale); +#endif // ENABLE_MODELVOLUME_TRANSFORM + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); +#if ENABLE_MODELVOLUME_TRANSFORM + if (m_mode == Instance) + { + Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); + // extracts scaling factors from the composed transformation + Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + (*m_volumes)[i]->set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); + (*m_volumes)[i]->set_instance_scaling_factor(new_scale); + } + else if (m_mode == Volume) + { + Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3); + // extracts scaling factors from the composed transformation + Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + (*m_volumes)[i]->set_volume_offset(m * m_cache.volumes_data[i].get_volume_position()); + (*m_volumes)[i]->set_volume_scaling_factor(new_scale); + } +#else + Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_scale_matrix()).matrix().block(0, 0, 3, 3); + // extracts scaling factors from the composed transformation + Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + (*m_volumes)[i]->set_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_position() - m_cache.dragging_center)); + (*m_volumes)[i]->set_scaling_factor(new_scale); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + } + +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + _synchronize_unselected_instances(); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + +#if ENABLE_ENSURE_ON_BED_WHILE_SCALING + _ensure_on_bed(); +#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::mirror(Axis axis) +{ + if (!m_valid) + return; + + bool single_full_instance = is_single_full_instance(); + + for (unsigned int i : m_list) + { + if (single_full_instance) +#if ENABLE_MODELVOLUME_TRANSFORM + (*m_volumes)[i]->set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis)); + else if (m_mode == Volume) + (*m_volumes)[i]->set_volume_mirror(axis, -(*m_volumes)[i]->get_volume_mirror(axis)); +#else + (*m_volumes)[i]->set_mirror(axis, -(*m_volumes)[i]->get_mirror(axis)); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + _synchronize_unselected_instances(); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::translate(unsigned int object_idx, const Vec3d& displacement) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() == object_idx) +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_instance_offset(v->get_instance_offset() + displacement); +#else + v->set_offset(v->get_offset() + displacement); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + + std::set done; // prevent processing volumes twice + done.insert(m_list.begin(), m_list.end()); + + for (unsigned int i : m_list) + { + if (done.size() == m_volumes->size()) + break; + + int object_idx = (*m_volumes)[i]->object_idx(); + if (object_idx >= 1000) + continue; + + // Process unselected volumes of the object. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (done.size() == m_volumes->size()) + break; + + if (done.find(j) != done.end()) + continue; + + GLVolume* v = (*m_volumes)[j]; + if (v->object_idx() != object_idx) + continue; + +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_instance_offset(v->get_instance_offset() + displacement); +#else + v->set_offset(v->get_offset() + displacement); +#endif // ENABLE_MODELVOLUME_TRANSFORM + done.insert(j); + } + } + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_instance_offset(v->get_instance_offset() + displacement); +#else + v->set_offset(v->get_offset() + displacement); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + + std::set done; // prevent processing volumes twice + done.insert(m_list.begin(), m_list.end()); + + for (unsigned int i : m_list) + { + if (done.size() == m_volumes->size()) + break; + + int object_idx = (*m_volumes)[i]->object_idx(); + if (object_idx >= 1000) + continue; + + // Process unselected volumes of the object. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (done.size() == m_volumes->size()) + break; + + if (done.find(j) != done.end()) + continue; + + GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) + continue; + +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_instance_offset(v->get_instance_offset() + displacement); +#else + v->set_offset(v->get_offset() + displacement); +#endif // ENABLE_MODELVOLUME_TRANSFORM + done.insert(j); + } + } + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::erase() +{ + if (!m_valid) + return; + + if (is_single_full_object()) + wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itObject, get_object_idx(), 0); + else if (is_multiple_full_object()) + { + std::vector items; + items.reserve(m_cache.content.size()); + for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) + { + items.emplace_back(ItemType::itObject, it->first, 0); + } + wxGetApp().obj_list()->delete_from_model_and_list(items); + } + else if (is_single_full_instance()) + wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itInstance, get_object_idx(), get_instance_idx()); + else if (is_multiple_full_instance()) + { + std::set> instances_idxs; + for (ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.begin(); obj_it != m_cache.content.end(); ++obj_it) + { + for (InstanceIdxsList::reverse_iterator inst_it = obj_it->second.rbegin(); inst_it != obj_it->second.rend(); ++inst_it) + { + instances_idxs.insert(std::make_pair(obj_it->first, *inst_it)); + } + } + + std::vector items; + items.reserve(instances_idxs.size()); + for (const std::pair& i : instances_idxs) + { + items.emplace_back(ItemType::itInstance, i.first, i.second); + } + + wxGetApp().obj_list()->delete_from_model_and_list(items); + } + else + { + std::set> volumes_idxs; + for (unsigned int i : m_list) + { + const GLVolume* v = (*m_volumes)[i]; + volumes_idxs.insert(std::make_pair(v->object_idx(), v->volume_idx())); + } + + std::vector items; + items.reserve(volumes_idxs.size()); + for (const std::pair& v : volumes_idxs) + { + items.emplace_back(ItemType::itVolume, v.first, v.second); + } + + wxGetApp().obj_list()->delete_from_model_and_list(items); + } +} + +void GLCanvas3D::Selection::render() const +{ + if (is_empty()) + return; + + // render cumulative bounding box of selected volumes + _render_selected_volumes(); + _render_synchronized_volumes(); +} + +void GLCanvas3D::Selection::_update_valid() +{ + m_valid = (m_volumes != nullptr) && (m_model != nullptr); +} + +void GLCanvas3D::Selection::_update_type() +{ + m_cache.content.clear(); + m_type = Mixed; + + for (unsigned int i : m_list) + { + const GLVolume* volume = (*m_volumes)[i]; + int obj_idx = volume->object_idx(); + int inst_idx = volume->instance_idx(); + ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.find(obj_idx); + if (obj_it == m_cache.content.end()) + obj_it = m_cache.content.insert(ObjectIdxsToInstanceIdxsMap::value_type(obj_idx, InstanceIdxsList())).first; + + obj_it->second.insert(inst_idx); + } + + bool requires_disable = false; + + if (!m_valid) + m_type = Invalid; + else + { + if (m_list.empty()) + m_type = Empty; + else if (m_list.size() == 1) + { + const GLVolume* first = (*m_volumes)[*m_list.begin()]; + if (first->is_wipe_tower) + m_type = WipeTower; + else if (first->is_modifier) + { + m_type = SingleModifier; + requires_disable = true; + } + else + { + const ModelObject* model_object = m_model->objects[first->object_idx()]; + unsigned int volumes_count = (unsigned int)model_object->volumes.size(); + unsigned int instances_count = (unsigned int)model_object->instances.size(); + if (volumes_count * instances_count == 1) + m_type = SingleFullObject; + else if (volumes_count == 1) // instances_count > 1 + m_type = SingleFullInstance; + else + { + m_type = SingleVolume; + requires_disable = true; + } + } + } + else + { + if (m_cache.content.size() == 1) // single object + { + const ModelObject* model_object = m_model->objects[m_cache.content.begin()->first]; + unsigned int volumes_count = (unsigned int)model_object->volumes.size(); + unsigned int instances_count = (unsigned int)model_object->instances.size(); + unsigned int selected_instances_count = (unsigned int)m_cache.content.begin()->second.size(); + if (volumes_count * instances_count == (unsigned int)m_list.size()) + m_type = SingleFullObject; + else if (selected_instances_count == 1) + { + if (volumes_count == (unsigned int)m_list.size()) + m_type = SingleFullInstance; + else + { + unsigned int modifiers_count = 0; + for (unsigned int i : m_list) + { + if ((*m_volumes)[i]->is_modifier) + ++modifiers_count; + } + + if (modifiers_count == 0) + { + m_type = MultipleVolume; + requires_disable = true; + } + else if (modifiers_count == (unsigned int)m_list.size()) + { + m_type = MultipleModifier; + requires_disable = true; + } + } + } + else if ((selected_instances_count > 1) && (selected_instances_count * volumes_count == (unsigned int)m_list.size())) + m_type = MultipleFullInstance; + } + else + { + int sels_cntr = 0; + for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) + { + const ModelObject* model_object = m_model->objects[it->first]; + unsigned int volumes_count = (unsigned int)model_object->volumes.size(); + unsigned int instances_count = (unsigned int)model_object->instances.size(); + sels_cntr += volumes_count * instances_count; + } + if (sels_cntr == (unsigned int)m_list.size()) + m_type = MultipleFullObject; + } + } + } + + int object_idx = get_object_idx(); + int instance_idx = get_instance_idx(); + for (GLVolume* v : *m_volumes) + { + v->disabled = requires_disable ? (v->object_idx() != object_idx) || (v->instance_idx() != instance_idx) : false; + } + + switch (m_type) + { + case Invalid: + { + std::cout << "selection type: Invalid" << std::endl; + break; + } + case Empty: + { + std::cout << "selection type: Empty" << std::endl; + break; + } + case WipeTower: + { + std::cout << "selection type: WipeTower" << std::endl; + break; + } + case SingleModifier: + { + std::cout << "selection type: SingleModifier" << std::endl; + break; + } + case MultipleModifier: + { + std::cout << "selection type: MultipleModifier" << std::endl; + break; + } + case SingleVolume: + { + std::cout << "selection type: SingleVolume" << std::endl; + break; + } + case MultipleVolume: + { + std::cout << "selection type: MultipleVolume" << std::endl; + break; + } + case SingleFullObject: + { + std::cout << "selection type: SingleFullObject" << std::endl; + break; + } + case MultipleFullObject: + { + std::cout << "selection type: MultipleFullObject" << std::endl; + break; + } + case SingleFullInstance: + { + std::cout << "selection type: SingleFullInstance" << std::endl; + break; + } + case MultipleFullInstance: + { + std::cout << "selection type: MultipleFullInstance" << std::endl; + break; + } + case Mixed: + { + std::cout << "selection type: Mixed" << std::endl; + break; + } + } +} + +void GLCanvas3D::Selection::_set_caches() +{ + m_cache.volumes_data.clear(); + for (unsigned int i : m_list) + { + const GLVolume* v = (*m_volumes)[i]; +#if ENABLE_MODELVOLUME_TRANSFORM + m_cache.volumes_data.emplace(i, VolumeCache(v->get_volume_transformation(), v->get_instance_transformation())); +#else + m_cache.volumes_data.emplace(i, VolumeCache(v->get_offset(), v->get_rotation(), v->get_scaling_factor())); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + m_cache.dragging_center = get_bounding_box().center(); +} + +void GLCanvas3D::Selection::_add_volume(unsigned int volume_idx) +{ + // check if the given idx is already selected + if (m_list.find(volume_idx) != m_list.end()) + return; + + m_list.insert(volume_idx); + (*m_volumes)[volume_idx]->selected = true; +} + +void GLCanvas3D::Selection::_add_instance(unsigned int object_idx, unsigned int instance_idx) +{ + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + _add_volume(i); + } +} + +void GLCanvas3D::Selection::_add_object(unsigned int object_idx) +{ + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() == object_idx) + _add_volume(i); + } +} + +void GLCanvas3D::Selection::_remove_volume(unsigned int volume_idx) +{ + IndicesList::iterator v_it = m_list.find(volume_idx); + if (v_it == m_list.end()) + return; + + m_list.erase(v_it); + + (*m_volumes)[volume_idx]->selected = false; +} + +void GLCanvas3D::Selection::_remove_instance(unsigned int object_idx, unsigned int instance_idx) +{ + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + _remove_volume(i); + } +} + +void GLCanvas3D::Selection::_remove_object(unsigned int object_idx) +{ + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() == object_idx) + _remove_volume(i); + } +} + +void GLCanvas3D::Selection::_calc_bounding_box() const +{ + m_bounding_box = BoundingBoxf3(); + if (m_valid) + { + for (unsigned int i : m_list) + { + m_bounding_box.merge((*m_volumes)[i]->transformed_convex_hull_bounding_box()); + } + } + m_bounding_box_dirty = false; +} + +void GLCanvas3D::Selection::_render_selected_volumes() const +{ + float color[3] = { 1.0f, 1.0f, 1.0f }; + _render_bounding_box(get_bounding_box(), color); +} + +void GLCanvas3D::Selection::_render_synchronized_volumes() const +{ + if (m_mode == Instance) + return; + + float color[3] = { 1.0f, 1.0f, 0.0f }; + + for (unsigned int i : m_list) + { + const GLVolume* volume = (*m_volumes)[i]; + int object_idx = volume->object_idx(); + int instance_idx = volume->instance_idx(); + int volume_idx = volume->volume_idx(); + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (i == j) + continue; + + const GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) + continue; + + _render_bounding_box(v->transformed_convex_hull_bounding_box(), color); + } + } +} + +void GLCanvas3D::Selection::_render_bounding_box(const BoundingBoxf3& box, float* color) const +{ + if (color == nullptr) + return; + + Vec3f b_min = box.min.cast(); + Vec3f b_max = box.max.cast(); + Vec3f size = 0.2f * box.size().cast(); + + ::glEnable(GL_DEPTH_TEST); + + ::glColor3fv(color); + ::glLineWidth(2.0f); + + ::glBegin(GL_LINES); + + ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_min(2)); + ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0), b_min(1) + size(1), b_min(2)); + ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0), b_min(1), b_min(2) + size(2)); + + ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0) - size(0), b_min(1), b_min(2)); + ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0), b_min(1) + size(1), b_min(2)); + ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0), b_min(1), b_min(2) + size(2)); + + ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0) - size(0), b_max(1), b_min(2)); + ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0), b_max(1) - size(1), b_min(2)); + ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0), b_max(1), b_min(2) + size(2)); + + ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_max(1), b_min(2)); + ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_min(2)); + ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0), b_max(1), b_min(2) + size(2)); + + ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_max(2)); + ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0), b_min(1) + size(1), b_max(2)); + ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0), b_min(1), b_max(2) - size(2)); + + ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0) - size(0), b_min(1), b_max(2)); + ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0), b_min(1) + size(1), b_max(2)); + ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0), b_min(1), b_max(2) - size(2)); + + ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0) - size(0), b_max(1), b_max(2)); + ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0), b_max(1) - size(1), b_max(2)); + ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0), b_max(1), b_max(2) - size(2)); + + ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0) + size(0), b_max(1), b_max(2)); + ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_max(2)); + ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1), b_max(2) - size(2)); + + ::glEnd(); +} + +void GLCanvas3D::Selection::_synchronize_unselected_instances() +{ + std::set done; // prevent processing volumes twice + done.insert(m_list.begin(), m_list.end()); + + for (unsigned int i : m_list) + { + if (done.size() == m_volumes->size()) + break; + + const GLVolume* volume = (*m_volumes)[i]; + int object_idx = volume->object_idx(); + if (object_idx >= 1000) + continue; + + int instance_idx = volume->instance_idx(); +#if ENABLE_MODELVOLUME_TRANSFORM + const Vec3d& rotation = volume->get_instance_rotation(); + const Vec3d& scaling_factor = volume->get_instance_scaling_factor(); + const Vec3d& mirror = volume->get_instance_mirror(); +#else + const Vec3d& rotation = volume->get_rotation(); + const Vec3d& scaling_factor = volume->get_scaling_factor(); + const Vec3d& mirror = volume->get_mirror(); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + // Process unselected instances. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (done.size() == m_volumes->size()) + break; + + if (done.find(j) != done.end()) + continue; + + GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx)) + continue; + +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_instance_rotation(Vec3d(rotation(0), rotation(1), v->get_instance_rotation()(2))); + v->set_instance_scaling_factor(scaling_factor); + v->set_instance_mirror(mirror); +#else + v->set_rotation(Vec3d(rotation(0), rotation(1), v->get_rotation()(2))); + v->set_scaling_factor(scaling_factor); + v->set_mirror(mirror); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + done.insert(j); + } + } +} + +void GLCanvas3D::Selection::_synchronize_unselected_volumes() +{ + for (unsigned int i : m_list) + { + const GLVolume* volume = (*m_volumes)[i]; + int object_idx = volume->object_idx(); + if (object_idx >= 1000) + continue; + + int volume_idx = volume->volume_idx(); +#if ENABLE_MODELVOLUME_TRANSFORM + const Vec3d& offset = volume->get_volume_offset(); + const Vec3d& rotation = volume->get_volume_rotation(); + const Vec3d& scaling_factor = volume->get_volume_scaling_factor(); + const Vec3d& mirror = volume->get_volume_mirror(); +#else + const Vec3d& offset = volume->get_offset(); + const Vec3d& rotation = volume->get_rotation(); + const Vec3d& scaling_factor = volume->get_scaling_factor(); + const Vec3d& mirror = volume->get_mirror(); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + // Process unselected volumes. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (j == i) + continue; + + GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) + continue; + +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_volume_offset(offset); + v->set_volume_rotation(rotation); + v->set_volume_scaling_factor(scaling_factor); + v->set_volume_mirror(mirror); +#else + v->set_offset(offset); + v->set_rotation(Vec3d(rotation)); + v->set_scaling_factor(scaling_factor); + v->set_mirror(mirror); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + } +} + +#if ENABLE_ENSURE_ON_BED_WHILE_SCALING +void GLCanvas3D::Selection::_ensure_on_bed() +{ + for (unsigned int i : m_list) + { + GLVolume* volume = (*m_volumes)[i]; + if (!volume->is_modifier) + { + double min_z = volume->transformed_convex_hull_bounding_box().min(2); + if (min_z != 0.0) + volume->set_instance_offset(Z, volume->get_instance_offset(Z) - min_z); + } + } +} +#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING + const float GLCanvas3D::Gizmos::OverlayTexturesScale = 0.75f; const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale; const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale; @@ -1120,7 +2271,6 @@ const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale; GLCanvas3D::Gizmos::Gizmos() : m_enabled(false) , m_current(Undefined) - , m_dragging(false) { } @@ -1129,9 +2279,18 @@ GLCanvas3D::Gizmos::~Gizmos() _reset(); } -bool GLCanvas3D::Gizmos::init() +bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) { - GLGizmoBase* gizmo = new GLGizmoScale; + GLGizmoBase* gizmo = new GLGizmoMove3D(parent); + if (gizmo == nullptr) + return false; + + if (!gizmo->init()) + return false; + + m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); + + gizmo = new GLGizmoScale3D(parent); if (gizmo == nullptr) return false; @@ -1140,7 +2299,7 @@ bool GLCanvas3D::Gizmos::init() m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); - gizmo = new GLGizmoRotate; + gizmo = new GLGizmoRotate3D(parent); if (gizmo == nullptr) { _reset(); @@ -1155,7 +2314,7 @@ bool GLCanvas3D::Gizmos::init() m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); - gizmo = new GLGizmoFlatten; + gizmo = new GLGizmoFlatten(parent); if (gizmo == nullptr) return false; @@ -1166,6 +2325,27 @@ bool GLCanvas3D::Gizmos::init() m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo)); + gizmo = new GLGizmoCut(parent); + if (gizmo == nullptr) + return false; + + if (!gizmo->init()) { + _reset(); + return false; + } + + m_gizmos.insert(GizmosMap::value_type(Cut, gizmo)); + + gizmo = new GLGizmoSlaSupports(parent); + if (gizmo == nullptr) + return false; + + if (!gizmo->init()) { + _reset(); + return false; + } + + m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo)); return true; } @@ -1180,33 +2360,39 @@ void GLCanvas3D::Gizmos::set_enabled(bool enable) m_enabled = enable; } -void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos) +std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection) { + std::string name = ""; + if (!m_enabled) - return; + return name; float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = _get_total_overlay_height(); float top_y = 0.5f * (cnv_h - height); - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - if (it->second == nullptr) + if ((it->second == nullptr) || !it->second->is_selectable()) continue; float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius - if (it->second->get_state() != GLGizmoBase::On) + if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) { - bool inside = length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size; + bool inside = (mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size; it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); + if (inside) + name = it->second->get_name(); } top_y += (tex_size + OverlayGapY); } + + return name; } -void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Pointf& mouse_pos) +void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection) { if (!m_enabled) return; @@ -1214,23 +2400,23 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Poi float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = _get_total_overlay_height(); float top_y = 0.5f * (cnv_h - height); - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - if (it->second == nullptr) + if ((it->second == nullptr) || !it->second->is_selectable()) continue; float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius - if (length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size) + if (it->second->is_activable(selection) && ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size)) { if ((it->second->get_state() == GLGizmoBase::On)) { - it->second->set_state(GLGizmoBase::Off); + it->second->set_state(GLGizmoBase::Hover); m_current = Undefined; } - else + else if ((it->second->get_state() == GLGizmoBase::Hover)) { it->second->set_state(GLGizmoBase::On); m_current = it->first; @@ -1241,6 +2427,23 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Poi top_y += (tex_size + OverlayGapY); } + + GizmosMap::iterator it = m_gizmos.find(m_current); + if ((it != m_gizmos.end()) && (it->second != nullptr) && (it->second->get_state() != GLGizmoBase::On)) + it->second->set_state(GLGizmoBase::On); +} + +void GLCanvas3D::Gizmos::update_on_off_state(const Selection& selection) +{ + GizmosMap::iterator it = m_gizmos.find(m_current); + if ((it != m_gizmos.end()) && (it->second != nullptr)) + { + if (!it->second->is_activable(selection)) + { + it->second->set_state(GLGizmoBase::Off); + m_current = Undefined; + } + } } void GLCanvas3D::Gizmos::reset_all_states() @@ -1272,7 +2475,22 @@ void GLCanvas3D::Gizmos::set_hover_id(int id) } } -bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const +void GLCanvas3D::Gizmos::enable_grabber(EType type, unsigned int id, bool enable) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(type); + if (it != m_gizmos.end()) + { + if (enable) + it->second->enable_grabber(id); + else + it->second->disable_grabber(id); + } +} + +bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const { if (!m_enabled) return false; @@ -1282,14 +2500,14 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const float top_y = 0.5f * (cnv_h - height); for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - if (it->second == nullptr) + if ((it->second == nullptr) || !it->second->is_selectable()) continue; float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius - if (length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size) + if ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size) return true; top_y += (tex_size + OverlayGapY); @@ -1307,25 +2525,27 @@ bool GLCanvas3D::Gizmos::grabber_contains_mouse() const return (curr != nullptr) ? (curr->get_hover_id() != -1) : false; } -void GLCanvas3D::Gizmos::update(const Pointf& mouse_pos) +void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray, bool shift_down, const Point* mouse_pos) { if (!m_enabled) return; GLGizmoBase* curr = _get_current(); if (curr != nullptr) - curr->update(mouse_pos); + curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos, shift_down)); } -void GLCanvas3D::Gizmos::refresh() +#if ENABLE_GIZMOS_RESET +void GLCanvas3D::Gizmos::process_double_click() { if (!m_enabled) return; GLGizmoBase* curr = _get_current(); if (curr != nullptr) - curr->refresh(); + curr->process_double_click(); } +#endif // ENABLE_GIZMOS_RESET GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const { @@ -1343,70 +2563,87 @@ bool GLCanvas3D::Gizmos::is_running() const bool GLCanvas3D::Gizmos::is_dragging() const { - return m_dragging; + if (!m_enabled) + return false; + + GLGizmoBase* curr = _get_current(); + return (curr != nullptr) ? curr->is_dragging() : false; } -void GLCanvas3D::Gizmos::start_dragging() +void GLCanvas3D::Gizmos::start_dragging(const GLCanvas3D::Selection& selection) { - m_dragging = true; + if (!m_enabled) + return; + GLGizmoBase* curr = _get_current(); if (curr != nullptr) - curr->start_dragging(); + curr->start_dragging(selection); } void GLCanvas3D::Gizmos::stop_dragging() { - m_dragging = false; + if (!m_enabled) + return; + GLGizmoBase* curr = _get_current(); if (curr != nullptr) curr->stop_dragging(); } -float GLCanvas3D::Gizmos::get_scale() const +Vec3d GLCanvas3D::Gizmos::get_displacement() const { if (!m_enabled) - return 1.0f; + return Vec3d::Zero(); - GizmosMap::const_iterator it = m_gizmos.find(Scale); - return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_scale() : 1.0f; + GizmosMap::const_iterator it = m_gizmos.find(Move); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_displacement() : Vec3d::Zero(); } -void GLCanvas3D::Gizmos::set_scale(float scale) +Vec3d GLCanvas3D::Gizmos::get_scale() const +{ + if (!m_enabled) + return Vec3d::Ones(); + + GizmosMap::const_iterator it = m_gizmos.find(Scale); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_scale() : Vec3d::Ones(); +} + +void GLCanvas3D::Gizmos::set_scale(const Vec3d& scale) { if (!m_enabled) return; GizmosMap::const_iterator it = m_gizmos.find(Scale); if (it != m_gizmos.end()) - reinterpret_cast(it->second)->set_scale(scale); + reinterpret_cast(it->second)->set_scale(scale); } -float GLCanvas3D::Gizmos::get_angle_z() const +Vec3d GLCanvas3D::Gizmos::get_rotation() const { if (!m_enabled) - return 0.0f; + return Vec3d::Zero(); GizmosMap::const_iterator it = m_gizmos.find(Rotate); - return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_angle_z() : 0.0f; + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_rotation() : Vec3d::Zero(); } -void GLCanvas3D::Gizmos::set_angle_z(float angle_z) +void GLCanvas3D::Gizmos::set_rotation(const Vec3d& rotation) { if (!m_enabled) return; GizmosMap::const_iterator it = m_gizmos.find(Rotate); if (it != m_gizmos.end()) - reinterpret_cast(it->second)->set_angle_z(angle_z); + reinterpret_cast(it->second)->set_rotation(rotation); } -Pointf3 GLCanvas3D::Gizmos::get_flattening_normal() const +Vec3d GLCanvas3D::Gizmos::get_flattening_normal() const { if (!m_enabled) - return Pointf3(0.f, 0.f, 0.f); + return Vec3d::Zero(); GizmosMap::const_iterator it = m_gizmos.find(Flatten); - return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Pointf3(0.f, 0.f, 0.f); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Vec3d::Zero(); } void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object) @@ -1419,39 +2656,74 @@ void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object) reinterpret_cast(it->second)->set_flattening_data(model_object); } -void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box, RenderOrder render_order) const +void GLCanvas3D::Gizmos::set_model_object_ptr(ModelObject* model_object) { if (!m_enabled) return; - ::glDisable(GL_DEPTH_TEST); - - if ((render_order == BeforeBed && dynamic_cast(_get_current())) - || (render_order == AfterBed && !dynamic_cast(_get_current()))) { - if (box.radius() > 0.0) - _render_current_gizmo(box); - } - - if (render_order == AfterBed) { - ::glPushMatrix(); - ::glLoadIdentity(); - - _render_overlay(canvas); - - ::glPopMatrix(); - } + GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); + if (it != m_gizmos.end()) + reinterpret_cast(it->second)->set_model_object_ptr(model_object); } -void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const +void GLCanvas3D::Gizmos::clicked_on_object(const Vec2d& mouse_position) { if (!m_enabled) return; - ::glDisable(GL_DEPTH_TEST); + GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); + if (it != m_gizmos.end()) + reinterpret_cast(it->second)->clicked_on_object(mouse_position); +} + +void GLCanvas3D::Gizmos::delete_current_grabber(bool delete_all) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); + if (it != m_gizmos.end()) + reinterpret_cast(it->second)->delete_current_grabber(delete_all); +} + +void GLCanvas3D::Gizmos::render_current_gizmo(const GLCanvas3D::Selection& selection) const +{ + if (!m_enabled) + return; + + _render_current_gizmo(selection); +} + +void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const GLCanvas3D::Selection& selection) const +{ + if (!m_enabled) + return; GLGizmoBase* curr = _get_current(); if (curr != nullptr) - curr->render_for_picking(box); + curr->render_for_picking(selection); +} + +void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas) const +{ + if (!m_enabled) + return; + + ::glDisable(GL_DEPTH_TEST); + + ::glPushMatrix(); + ::glLoadIdentity(); + + _render_overlay(canvas); + + ::glPopMatrix(); +} + +void GLCanvas3D::Gizmos::create_external_gizmo_widgets(wxWindow *parent) +{ + for (auto &entry : m_gizmos) { + entry.second->create_external_gizmo_widgets(parent); + } } void GLCanvas3D::Gizmos::_reset() @@ -1480,17 +2752,20 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const float scaled_gap_y = OverlayGapY * inv_zoom; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { + if ((it->second == nullptr) || !it->second->is_selectable()) + continue; + float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale * inv_zoom; GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y); top_y -= (tex_size + scaled_gap_y); } } -void GLCanvas3D::Gizmos::_render_current_gizmo(const BoundingBoxf3& box) const +void GLCanvas3D::Gizmos::_render_current_gizmo(const GLCanvas3D::Selection& selection) const { GLGizmoBase* curr = _get_current(); if (curr != nullptr) - curr->render(box); + curr->render(selection); } float GLCanvas3D::Gizmos::_get_total_overlay_height() const @@ -1499,17 +2774,30 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - height += (float)it->second->get_textures_size(); - if (std::distance(it, m_gizmos.end()) > 1) - height += OverlayGapY; + if (it->first == SlaSupports && wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) + continue; + height += (float)it->second->get_textures_size() + OverlayGapY; } - return height; + return height - OverlayGapY; +} + +GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const +{ + GizmosMap::const_iterator it = m_gizmos.find(m_current); + return (it != m_gizmos.end()) ? it->second : nullptr; } const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 9, 91, 134 }; const unsigned char GLCanvas3D::WarningTexture::Opacity = 255; +GLCanvas3D::WarningTexture::WarningTexture() + : GUI::GLTexture() + , m_original_width(0) + , m_original_height(0) +{ +} + bool GLCanvas3D::WarningTexture::generate(const std::string& msg) { reset(); @@ -1519,28 +2807,31 @@ bool GLCanvas3D::WarningTexture::generate(const std::string& msg) wxMemoryDC memDC; // select default font - memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + font.MakeLarger(); + font.MakeBold(); + memDC.SetFont(font); // calculates texture size wxCoord w, h; memDC.GetTextExtent(msg, &w, &h); - m_width = (int)w; - m_height = (int)h; + + int pow_of_two_size = next_highest_power_of_2(std::max(w, h)); + + m_original_width = (int)w; + m_original_height = (int)h; + m_width = pow_of_two_size; + m_height = pow_of_two_size; // generates bitmap wxBitmap bitmap(m_width, m_height); -#if defined(__APPLE__) || defined(_MSC_VER) - bitmap.UseAlpha(); -#endif - memDC.SelectObject(bitmap); memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2]))); memDC.Clear(); - memDC.SetTextForeground(*wxWHITE); - // draw message + memDC.SetTextForeground(*wxWHITE); memDC.DrawText(msg, 0, 0); memDC.SelectObject(wxNullBitmap); @@ -1577,10 +2868,51 @@ bool GLCanvas3D::WarningTexture::generate(const std::string& msg) return true; } +void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const +{ + if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0)) + { + ::glDisable(GL_DEPTH_TEST); + ::glPushMatrix(); + ::glLoadIdentity(); + + const Size& cnv_size = canvas.get_canvas_size(); + float zoom = canvas.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + float left = (-0.5f * (float)m_original_width) * inv_zoom; + float top = (-0.5f * (float)cnv_size.get_height() + (float)m_original_height + 2.0f) * inv_zoom; + float right = left + (float)m_original_width * inv_zoom; + float bottom = top - (float)m_original_height * inv_zoom; + + float uv_left = 0.0f; + float uv_top = 0.0f; + float uv_right = (float)m_original_width / (float)m_width; + float uv_bottom = (float)m_original_height / (float)m_height; + + GLTexture::Quad_UVs uvs; + uvs.left_top = { uv_left, uv_top }; + uvs.left_bottom = { uv_left, uv_bottom }; + uvs.right_bottom = { uv_right, uv_bottom }; + uvs.right_top = { uv_right, uv_top }; + + GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs); + + ::glPopMatrix(); + ::glEnable(GL_DEPTH_TEST); + } +} + const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 }; const unsigned char GLCanvas3D::LegendTexture::Background_Color[3] = { 9, 91, 134 }; const unsigned char GLCanvas3D::LegendTexture::Opacity = 255; +GLCanvas3D::LegendTexture::LegendTexture() + : GUI::GLTexture() + , m_original_width(0) + , m_original_height(0) +{ +} + bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector& tool_colors) { reset(); @@ -1613,25 +2945,25 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c max_text_height = std::max(max_text_height, (int)h); } - m_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width); - m_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square; + m_original_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width); + m_original_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square; if (items_count > 1) - m_height += (items_count - 1) * Px_Square_Contour; + m_original_height += (items_count - 1) * Px_Square_Contour; + + int pow_of_two_size = (int)next_highest_power_of_2(std::max(m_original_width, m_original_height)); + + m_width = pow_of_two_size; + m_height = pow_of_two_size; // generates bitmap wxBitmap bitmap(m_width, m_height); -#if defined(__APPLE__) || defined(_MSC_VER) - bitmap.UseAlpha(); -#endif - memDC.SelectObject(bitmap); memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2]))); memDC.Clear(); - memDC.SetTextForeground(*wxWHITE); - // draw title + memDC.SetTextForeground(*wxWHITE); int title_x = Px_Border; int title_y = Px_Border; memDC.DrawText(title, title_x, title_y); @@ -1725,18 +3057,60 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c return true; } -GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const +void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const { - GizmosMap::const_iterator it = m_gizmos.find(m_current); - return (it != m_gizmos.end()) ? it->second : nullptr; + if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0)) + { + ::glDisable(GL_DEPTH_TEST); + ::glPushMatrix(); + ::glLoadIdentity(); + + const Size& cnv_size = canvas.get_canvas_size(); + float zoom = canvas.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + float left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; + float top = (0.5f * (float)cnv_size.get_height()) * inv_zoom; + float right = left + (float)m_original_width * inv_zoom; + float bottom = top - (float)m_original_height * inv_zoom; + + float uv_left = 0.0f; + float uv_top = 0.0f; + float uv_right = (float)m_original_width / (float)m_width; + float uv_bottom = (float)m_original_height / (float)m_height; + + GLTexture::Quad_UVs uvs; + uvs.left_top = { uv_left, uv_top }; + uvs.left_bottom = { uv_left, uv_bottom }; + uvs.right_bottom = { uv_right, uv_bottom }; + uvs.right_top = { uv_right, uv_top }; + + GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs); + + ::glPopMatrix(); + ::glEnable(GL_DEPTH_TEST); + } } +wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); +wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); +wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) : m_canvas(canvas) , m_context(nullptr) - , m_timer(nullptr) + , m_toolbar(*this) , m_config(nullptr) , m_print(nullptr) + , m_sla_print(nullptr) , m_model(nullptr) , m_dirty(true) , m_initialized(false) @@ -1744,6 +3118,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_force_zoom_to_bed_enabled(false) , m_apply_zoom_to_volumes_filter(false) , m_hover_volume_id(-1) + , m_toolbar_action_running(false) , m_warning_texture_enabled(false) , m_legend_texture_enabled(false) , m_picking_enabled(false) @@ -1751,35 +3126,44 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_shader_enabled(false) , m_dynamic_background_enabled(false) , m_multisample_allowed(false) + , m_regenerate_volumes(true) , m_color_by("volume") - , m_select_by("object") - , m_drag_by("instance") , m_reload_delayed(false) + , m_external_gizmo_widgets_parent(nullptr) { if (m_canvas != nullptr) { +#if !ENABLE_USE_UNIQUE_GLCONTEXT m_context = new wxGLContext(m_canvas); - m_timer = new wxTimer(m_canvas); +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT + m_timer.SetOwner(m_canvas); } + + m_selection.set_volumes(&m_volumes.volumes); } GLCanvas3D::~GLCanvas3D() { reset_volumes(); - if (m_timer != nullptr) - { - delete m_timer; - m_timer = nullptr; - } - +#if !ENABLE_USE_UNIQUE_GLCONTEXT if (m_context != nullptr) { delete m_context; m_context = nullptr; } +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT +} - _deregister_callbacks(); +void GLCanvas3D::post_event(wxEvent &&event) +{ + event.SetEventObject(m_canvas); + wxPostEvent(m_canvas, event); +} + +void GLCanvas3D::viewport_changed() +{ + post_event(SimpleEvent(EVT_GLCANVAS_VIEWPORT_CHANGED)); } bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) @@ -1847,7 +3231,19 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (!m_volumes.empty()) m_volumes.finalize_geometry(m_use_VBOs); - if (m_gizmos.is_enabled() && !m_gizmos.init()) + if (m_gizmos.is_enabled()) { + if (! m_gizmos.init(*this)) { + std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl; + return false; + } + + if (m_external_gizmo_widgets_parent != nullptr) { + m_gizmos.create_external_gizmo_widgets(m_external_gizmo_widgets_parent); + m_canvas->GetParent()->Layout(); + } + } + + if (!_init_toolbar()) return false; m_initialized = true; @@ -1855,6 +3251,7 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) return true; } +#if !ENABLE_USE_UNIQUE_GLCONTEXT bool GLCanvas3D::set_current() { if ((m_canvas != nullptr) && (m_context != nullptr)) @@ -1862,6 +3259,7 @@ bool GLCanvas3D::set_current() return false; } +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT void GLCanvas3D::set_as_dirty() { @@ -1877,10 +3275,13 @@ void GLCanvas3D::reset_volumes() { if (!m_volumes.empty()) { +#if !ENABLE_USE_UNIQUE_GLCONTEXT // ensures this canvas is current if (!set_current()) return; +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT + m_selection.clear(); m_volumes.release_geometry(); m_volumes.clear(); m_dirty = true; @@ -1890,43 +3291,6 @@ void GLCanvas3D::reset_volumes() _reset_warning_texture(); } -void GLCanvas3D::deselect_volumes() -{ - for (GLVolume* vol : m_volumes.volumes) - { - if (vol != nullptr) - vol->selected = false; - } -} - -void GLCanvas3D::select_volume(unsigned int id) -{ - if (id < (unsigned int)m_volumes.volumes.size()) - { - GLVolume* vol = m_volumes.volumes[id]; - if (vol != nullptr) - vol->selected = true; - } -} - -void GLCanvas3D::update_volumes_selection(const std::vector& selections) -{ - if (m_model == nullptr) - return; - - for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) - { - if ((selections[obj_idx] == 1) && (obj_idx < (unsigned int)m_objects_volumes_idxs.size())) - { - const std::vector& volume_idxs = m_objects_volumes_idxs[obj_idx]; - for (int v : volume_idxs) - { - select_volume(v); - } - } - } -} - int GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const { ModelInstance::EPrintVolumeState state; @@ -1934,39 +3298,6 @@ int GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) co return (int)state; } -bool GLCanvas3D::move_volume_up(unsigned int id) -{ - if ((id > 0) && (id < (unsigned int)m_volumes.volumes.size())) - { - std::swap(m_volumes.volumes[id - 1], m_volumes.volumes[id]); - std::swap(m_volumes.volumes[id - 1]->composite_id, m_volumes.volumes[id]->composite_id); - std::swap(m_volumes.volumes[id - 1]->select_group_id, m_volumes.volumes[id]->select_group_id); - std::swap(m_volumes.volumes[id - 1]->drag_group_id, m_volumes.volumes[id]->drag_group_id); - return true; - } - - return false; -} - -bool GLCanvas3D::move_volume_down(unsigned int id) -{ - if ((id >= 0) && (id + 1 < (unsigned int)m_volumes.volumes.size())) - { - std::swap(m_volumes.volumes[id + 1], m_volumes.volumes[id]); - std::swap(m_volumes.volumes[id + 1]->composite_id, m_volumes.volumes[id]->composite_id); - std::swap(m_volumes.volumes[id + 1]->select_group_id, m_volumes.volumes[id]->select_group_id); - std::swap(m_volumes.volumes[id + 1]->drag_group_id, m_volumes.volumes[id]->drag_group_id); - return true; - } - - return false; -} - -void GLCanvas3D::set_objects_selections(const std::vector& selections) -{ - m_objects_selections = selections; -} - void GLCanvas3D::set_config(DynamicPrintConfig* config) { m_config = config; @@ -1977,9 +3308,15 @@ void GLCanvas3D::set_print(Print* print) m_print = print; } +void GLCanvas3D::set_SLA_print(SLAPrint* print) +{ + m_sla_print = print; +} + void GLCanvas3D::set_model(Model* model) { m_model = model; + m_selection.set_model(m_model); } void GLCanvas3D::set_bed_shape(const Pointfs& shape) @@ -1987,66 +3324,25 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) bool new_shape = m_bed.set_shape(shape); // Set the origin and size for painting of the coordinate system axes. - m_axes.origin = Pointf3(0.0, 0.0, (coordf_t)GROUND_Z); + m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size()); if (new_shape) - { - // forces the selection of the proper camera target - if (m_volumes.volumes.empty()) - zoom_to_bed(); - else - zoom_to_volumes(); - } + zoom_to_bed(); m_dirty = true; } -void GLCanvas3D::set_auto_bed_shape() -{ - // draw a default square bed around object center - const BoundingBoxf3& bbox = volumes_bounding_box(); - coordf_t max_size = bbox.max_size(); - const Pointf3& center = bbox.center(); - - Pointfs bed_shape; - bed_shape.reserve(4); - bed_shape.emplace_back(center.x - max_size, center.y - max_size); - bed_shape.emplace_back(center.x + max_size, center.y - max_size); - bed_shape.emplace_back(center.x + max_size, center.y + max_size); - bed_shape.emplace_back(center.x - max_size, center.y + max_size); - - set_bed_shape(bed_shape); - - // Set the origin for painting of the coordinate system axes. - m_axes.origin = Pointf3(center.x, center.y, (coordf_t)GROUND_Z); -} - void GLCanvas3D::set_axes_length(float length) { m_axes.length = length; } -void GLCanvas3D::set_cutting_plane(float z, const ExPolygons& polygons) -{ - m_cutting_plane.set(z, polygons); -} - void GLCanvas3D::set_color_by(const std::string& value) { m_color_by = value; } -void GLCanvas3D::set_select_by(const std::string& value) -{ - m_select_by = value; -} - -void GLCanvas3D::set_drag_by(const std::string& value) -{ - m_drag_by = value; -} - float GLCanvas3D::get_camera_zoom() const { return m_camera.zoom; @@ -2073,11 +3369,6 @@ bool GLCanvas3D::is_layers_editing_allowed() const return m_layers_editing.is_allowed(); } -bool GLCanvas3D::is_shader_enabled() const -{ - return m_shader_enabled; -} - bool GLCanvas3D::is_reload_delayed() const { return m_reload_delayed; @@ -2101,6 +3392,7 @@ void GLCanvas3D::enable_legend_texture(bool enable) void GLCanvas3D::enable_picking(bool enable) { m_picking_enabled = enable; + m_selection.set_mode(Selection::Instance); } void GLCanvas3D::enable_moving(bool enable) @@ -2113,6 +3405,11 @@ void GLCanvas3D::enable_gizmos(bool enable) m_gizmos.set_enabled(enable); } +void GLCanvas3D::enable_toolbar(bool enable) +{ + m_toolbar.set_enabled(enable); +} + void GLCanvas3D::enable_shader(bool enable) { m_shader_enabled = enable; @@ -2133,6 +3430,19 @@ void GLCanvas3D::allow_multisample(bool allow) m_multisample_allowed = allow; } +void GLCanvas3D::enable_toolbar_item(const std::string& name, bool enable) +{ + if (enable) + m_toolbar.enable_item(name); + else + m_toolbar.disable_item(name); +} + +bool GLCanvas3D::is_toolbar_item_pressed(const std::string& name) const +{ + return m_toolbar.is_item_pressed(name); +} + void GLCanvas3D::zoom_to_bed() { _zoom_to_bounding_box(m_bed.get_bounding_box()); @@ -2145,6 +3455,14 @@ void GLCanvas3D::zoom_to_volumes() m_apply_zoom_to_volumes_filter = false; } +#if ENABLE_MODIFIED_CAMERA_TARGET +void GLCanvas3D::zoom_to_selection() +{ + if (!m_selection.is_empty()) + _zoom_to_bounding_box(m_selection.get_bounding_box()); +} +#endif // ENABLE_MODIFIED_CAMERA_TARGET + void GLCanvas3D::select_view(const std::string& direction) { const float* dir_vec = nullptr; @@ -2164,12 +3482,12 @@ void GLCanvas3D::select_view(const std::string& direction) else if (direction == "rear") dir_vec = VIEW_REAR; - if ((dir_vec != nullptr) && !empty(volumes_bounding_box())) + if (dir_vec != nullptr) { m_camera.phi = dir_vec[0]; m_camera.set_theta(dir_vec[1]); - m_on_viewport_changed_callback.call(); + viewport_changed(); if (m_canvas != nullptr) m_canvas->Refresh(); @@ -2191,32 +3509,24 @@ void GLCanvas3D::update_volumes_colors_by_extruder() m_volumes.update_colors_by_extruder(m_config); } -void GLCanvas3D::update_gizmos_data() +// Returns a Rect object denoting size and position of the Reset button used by a gizmo. +// Returns in either screen or viewport coords. +Rect GLCanvas3D::get_gizmo_reset_rect(const GLCanvas3D& canvas, bool viewport) const { - if (!m_gizmos.is_enabled()) - return; + const Size& cnv_size = canvas.get_canvas_size(); + float w = (viewport ? -0.5f : 0.f) * (float)cnv_size.get_width(); + float h = (viewport ? 0.5f : 1.f) * (float)cnv_size.get_height(); + float zoom = canvas.get_camera_zoom(); + float inv_zoom = viewport ? ((zoom != 0.0f) ? 1.0f / zoom : 0.0f) : 1.f; + const float gap = 30.f; + return Rect((w + gap + 80.f) * inv_zoom, (viewport ? -1.f : 1.f) * (h - GIZMO_RESET_BUTTON_HEIGHT) * inv_zoom, + (w + gap + 80.f + GIZMO_RESET_BUTTON_WIDTH) * inv_zoom, (viewport ? -1.f : 1.f) * (h * inv_zoom)); +} - int id = _get_first_selected_object_id(); - if ((id != -1) && (m_model != nullptr)) - { - ModelObject* model_object = m_model->objects[id]; - if (model_object != nullptr) - { - ModelInstance* model_instance = model_object->instances[0]; - if (model_instance != nullptr) - { - m_gizmos.set_scale(model_instance->scaling_factor); - m_gizmos.set_angle_z(model_instance->rotation); - m_gizmos.set_flattening_data(model_object); - } - } - } - else - { - m_gizmos.set_scale(1.0f); - m_gizmos.set_angle_z(0.0f); - m_gizmos.set_flattening_data(nullptr); - } +bool GLCanvas3D::gizmo_reset_rect_contains(const GLCanvas3D& canvas, float x, float y) const +{ + const Rect& rect = get_gizmo_reset_rect(canvas, false); + return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); } void GLCanvas3D::render() @@ -2228,7 +3538,11 @@ void GLCanvas3D::render() return; // ensures this canvas is current and initialized +#if ENABLE_USE_UNIQUE_GLCONTEXT + if (!_set_current() || !_3DScene::init(m_canvas)) +#else if (!set_current() || !_3DScene::init(m_canvas)) +#endif // ENABLE_USE_UNIQUE_GLCONTEXT return; if (m_force_zoom_to_bed_enabled) @@ -2244,32 +3558,58 @@ void GLCanvas3D::render() float theta = m_camera.get_theta(); bool is_custom_bed = m_bed.is_custom(); + set_tooltip(""); + + // picking pass _picking_pass(); + + // draw scene + ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); _render_background(); - // untextured bed needs to be rendered before objects - if (is_custom_bed) + + if (is_custom_bed) // untextured bed needs to be rendered before objects { _render_bed(theta); // disable depth testing so that axes are not covered by ground _render_axes(false); } + _render_objects(); - _render_gizmo(Gizmos::RenderOrder::BeforeBed); - // textured bed needs to be rendered after objects - if (!is_custom_bed) + _render_selection(); + + if (!is_custom_bed) // textured bed needs to be rendered after objects { _render_axes(true); _render_bed(theta); } - _render_cutting_plane(); + +#if ENABLE_GIZMOS_ON_TOP + // we need to set the mouse's scene position here because the depth buffer + // could be invalidated by the following gizmo render methods + // this position is used later into on_mouse() to drag the objects + m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast()); +#endif // ENABLE_GIZMOS_ON_TOP + + _render_current_gizmo(); +#if ENABLE_SHOW_CAMERA_TARGET + _render_camera_target(); +#endif // ENABLE_SHOW_CAMERA_TARGET + + // draw overlays + _render_gizmos_overlay(); _render_warning_texture(); _render_legend_texture(); - _render_gizmo(Gizmos::RenderOrder::AfterBed); + _render_toolbar(); _render_layer_editing_overlay(); m_canvas->SwapBuffers(); } +void GLCanvas3D::delete_selected() +{ + m_selection.erase(); +} + std::vector GLCanvas3D::get_current_print_zs(bool active_only) const { return m_volumes.get_current_print_zs(active_only); @@ -2289,7 +3629,7 @@ std::vector GLCanvas3D::load_object(const ModelObject& model_object, int ob instance_idxs.push_back(i); } } - return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_select_by, m_drag_by, m_use_VBOs && m_initialized); + return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_use_VBOs && m_initialized); } std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) @@ -2304,16 +3644,37 @@ std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) return std::vector(); } +std::vector GLCanvas3D::load_support_meshes(const Model& model, int obj_idx) +{ + std::vector volumes = m_volumes.load_object_auxiliary(model.objects[obj_idx], m_sla_print->objects()[obj_idx], obj_idx, slaposSupportTree, m_use_VBOs && m_initialized); + append(volumes, m_volumes.load_object_auxiliary(model.objects[obj_idx], m_sla_print->objects()[obj_idx], obj_idx, slaposBasePool, m_use_VBOs && m_initialized)); + return volumes; +} + +void GLCanvas3D::mirror_selection(Axis axis) +{ + m_selection.mirror(axis); + _on_mirror(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +} + void GLCanvas3D::reload_scene(bool force) { if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) return; - - reset_volumes(); - +#if !ENABLE_USE_UNIQUE_GLCONTEXT // ensures this canvas is current if (!set_current()) return; +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT + + if (m_regenerate_volumes) + { + reset_volumes(); + + // to update the toolbar + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + } set_bed_shape(dynamic_cast(m_config->option("bed_shape"))->values); @@ -2325,49 +3686,50 @@ void GLCanvas3D::reload_scene(bool force) m_reload_delayed = false; - m_objects_volumes_idxs.clear(); - - for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) + PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + if (m_regenerate_volumes) { - m_objects_volumes_idxs.push_back(load_object(*m_model, obj_idx)); - } - - // 1st call to reset if no objects left - update_gizmos_data(); - update_volumes_selection(m_objects_selections); - // 2nd call to restore if something selected - if (!m_objects_selections.empty()) - update_gizmos_data(); - - if (m_config->has("nozzle_diameter")) - { - // Should the wipe tower be visualized ? - unsigned int extruders_count = (unsigned int)dynamic_cast(m_config->option("nozzle_diameter"))->values.size(); - - bool semm = dynamic_cast(m_config->option("single_extruder_multi_material"))->value; - bool wt = dynamic_cast(m_config->option("wipe_tower"))->value; - bool co = dynamic_cast(m_config->option("complete_objects"))->value; - - if ((extruders_count > 1) && semm && wt && !co) + for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) { - // Height of a print (Show at least a slab) - coordf_t height = std::max(m_model->bounding_box().max.z, 10.0); - - float x = dynamic_cast(m_config->option("wipe_tower_x"))->value; - float y = dynamic_cast(m_config->option("wipe_tower_y"))->value; - float w = dynamic_cast(m_config->option("wipe_tower_width"))->value; - float a = dynamic_cast(m_config->option("wipe_tower_rotation_angle"))->value; - - float depth = m_print->get_wipe_tower_depth(); - if (!m_print->state.is_done(psWipeTower)) - depth = (900.f/w) * (float)(extruders_count - 1) ; - - m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !m_print->state.is_done(psWipeTower), - m_print->config.nozzle_diameter.values[0] * 1.25f * 4.5f); + load_object(*m_model, obj_idx); + if (printer_technology == ptSLA) + load_support_meshes(*m_model, obj_idx); } } - update_volumes_colors_by_extruder(); + _update_gizmos_data(); + + if (m_regenerate_volumes) + { + if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) + { + // Should the wipe tower be visualized ? + unsigned int extruders_count = (unsigned int)dynamic_cast(m_config->option("nozzle_diameter"))->values.size(); + + bool semm = dynamic_cast(m_config->option("single_extruder_multi_material"))->value; + bool wt = dynamic_cast(m_config->option("wipe_tower"))->value; + bool co = dynamic_cast(m_config->option("complete_objects"))->value; + + if ((extruders_count > 1) && semm && wt && !co) + { + // Height of a print (Show at least a slab) + double height = std::max(m_model->bounding_box().max(2), 10.0); + + float x = dynamic_cast(m_config->option("wipe_tower_x"))->value; + float y = dynamic_cast(m_config->option("wipe_tower_y"))->value; + float w = dynamic_cast(m_config->option("wipe_tower_width"))->value; + float a = dynamic_cast(m_config->option("wipe_tower_rotation_angle"))->value; + + float depth = m_print->get_wipe_tower_depth(); + if (!m_print->is_step_done(psWipeTower)) + depth = (900.f/w) * (float)(extruders_count - 1) ; + m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !m_print->is_step_done(psWipeTower), + m_print->config().nozzle_diameter.values[0] * 1.25f * 4.5f); + } + } + + update_volumes_colors_by_extruder(); + } // checks for geometry outside the print volume to render it accordingly if (!m_volumes.empty()) @@ -2379,31 +3741,36 @@ void GLCanvas3D::reload_scene(bool force) { enable_warning_texture(true); _generate_warning_texture(L("Detected object outside print volume")); - m_on_enable_action_buttons_callback.call(state == ModelInstance::PVS_Fully_Outside); + post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, state == ModelInstance::PVS_Fully_Outside)); } else { enable_warning_texture(false); m_volumes.reset_outside_state(); _reset_warning_texture(); - m_on_enable_action_buttons_callback.call(!m_model->objects.empty()); + post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, !m_model->objects.empty())); } } else { enable_warning_texture(false); _reset_warning_texture(); - m_on_enable_action_buttons_callback.call(false); + post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); } + + // restore to default value + m_regenerate_volumes = true; } void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors) { if ((m_canvas != nullptr) && (m_print != nullptr)) { +#if !ENABLE_USE_UNIQUE_GLCONTEXT // ensures that this canvas is current if (!set_current()) return; +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT if (m_volumes.empty()) { @@ -2443,7 +3810,7 @@ void GLCanvas3D::load_preview(const std::vector& str_tool_colors) _load_print_toolpaths(); _load_wipe_tower_toolpaths(str_tool_colors); - for (const PrintObject* object : m_print->objects) + for (const PrintObject* object : m_print->objects()) { if (object != nullptr) _load_print_object_toolpaths(*object, str_tool_colors); @@ -2459,114 +3826,6 @@ void GLCanvas3D::load_preview(const std::vector& str_tool_colors) reset_legend_texture(); } -void GLCanvas3D::register_on_viewport_changed_callback(void* callback) -{ - if (callback != nullptr) - m_on_viewport_changed_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_double_click_callback(void* callback) -{ - if (callback != nullptr) - m_on_double_click_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_right_click_callback(void* callback) -{ - if (callback != nullptr) - m_on_right_click_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_select_object_callback(void* callback) -{ - if (callback != nullptr) - m_on_select_object_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_model_update_callback(void* callback) -{ - if (callback != nullptr) - m_on_model_update_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_remove_object_callback(void* callback) -{ - if (callback != nullptr) - m_on_remove_object_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_arrange_callback(void* callback) -{ - if (callback != nullptr) - m_on_arrange_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_rotate_object_left_callback(void* callback) -{ - if (callback != nullptr) - m_on_rotate_object_left_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_rotate_object_right_callback(void* callback) -{ - if (callback != nullptr) - m_on_rotate_object_right_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_scale_object_uniformly_callback(void* callback) -{ - if (callback != nullptr) - m_on_scale_object_uniformly_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_increase_objects_callback(void* callback) -{ - if (callback != nullptr) - m_on_increase_objects_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_decrease_objects_callback(void* callback) -{ - if (callback != nullptr) - m_on_decrease_objects_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_instance_moved_callback(void* callback) -{ - if (callback != nullptr) - m_on_instance_moved_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_wipe_tower_moved_callback(void* callback) -{ - if (callback != nullptr) - m_on_wipe_tower_moved_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_enable_action_buttons_callback(void* callback) -{ - if (callback != nullptr) - m_on_enable_action_buttons_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback) -{ - if (callback != nullptr) - m_on_gizmo_scale_uniformly_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback) -{ - if (callback != nullptr) - m_on_gizmo_rotate_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_update_geometry_info_callback(void* callback) -{ - if (callback != nullptr) - m_on_update_geometry_info_callback.register_callback(callback); -} - void GLCanvas3D::bind_event_handlers() { if (m_canvas != nullptr) @@ -2655,27 +3914,32 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) switch (keyCode) { // key + - case 43: { m_on_increase_objects_callback.call(); break; } + case 43: { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, +1)); break; } // key - - case 45: { m_on_decrease_objects_callback.call(); break; } + case 45: { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, -1)); break; } // key A/a case 65: - case 97: { m_on_arrange_callback.call(); break; } + case 97: { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; } // key B/b case 66: case 98: { zoom_to_bed(); break; } - // key L/l - case 76: - case 108: { m_on_rotate_object_left_callback.call(); break; } - // key R/r - case 82: - case 114: { m_on_rotate_object_right_callback.call(); break; } - // key S/s - case 83: - case 115: { m_on_scale_object_uniformly_callback.call(); break; } +#if ENABLE_MODIFIED_CAMERA_TARGET + // key Z/z + case 90: + case 122: + { + if (m_selection.is_empty()) + zoom_to_volumes(); + else + zoom_to_selection(); + + break; + } +#else // key Z/z case 90: case 122: { zoom_to_volumes(); break; } +#endif // ENABLE_MODIFIED_CAMERA_TARGET default: { evt.Skip(); @@ -2696,7 +3960,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) // Performs layers editing updates, if enabled if (is_layers_editing_enabled()) { - int object_idx_selected = _get_first_selected_object_id(); + int object_idx_selected = m_selection.get_object_idx(); if (object_idx_selected != -1) { // A volume is selected. Test, whether hovering over a layer thickness bar. @@ -2723,7 +3987,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) zoom = std::max(zoom, zoom_min * 0.8f); m_camera.zoom = zoom; - m_on_viewport_changed_callback.call(); + viewport_changed(); _refresh_if_shown_on_screen(); } @@ -2740,10 +4004,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { Point pos(evt.GetX(), evt.GetY()); - int selected_object_idx = _get_first_selected_object_id(); + int selected_object_idx = m_selection.get_object_idx(); int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; m_layers_editing.last_object_id = layer_editing_object_idx; bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); + int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position); if (evt.Entering()) { @@ -2754,62 +4019,114 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.set_start_position_2D_as_invalid(); #endif - } + } else if (evt.Leaving()) { // to remove hover on objects when the mouse goes out of this canvas - m_mouse.position = Pointf(-1.0, -1.0); + m_mouse.position = Vec2d(-1.0, -1.0); m_dirty = true; } - else if (evt.LeftDClick() && (m_hover_volume_id != -1)) - m_on_double_click_callback.call(); + else if (evt.LeftDClick() && (toolbar_contains_mouse != -1)) + { + m_toolbar_action_running = true; + m_toolbar.do_action((unsigned int)toolbar_contains_mouse); + } +#if ENABLE_GIZMOS_RESET + else if (evt.LeftDClick() && m_gizmos.grabber_contains_mouse()) + { + m_mouse.ignore_up_event = true; + m_gizmos.process_double_click(); + switch (m_gizmos.get_current_type()) + { + case Gizmos::Scale: + { + m_selection.scale(m_gizmos.get_scale()); + _on_scale(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); + m_dirty = true; + break; + } + case Gizmos::Rotate: + { + m_selection.rotate(m_gizmos.get_rotation(), false); + _on_rotate(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); + m_dirty = true; + break; + } + default: + { + break; + } + } + } +#endif // ENABLE_GIZMOS_RESET else if (evt.LeftDown() || evt.RightDown()) { // If user pressed left or right button we first check whether this happened // on a volume or not. - int volume_idx = m_hover_volume_id; m_layers_editing.state = LayersEditing::Unknown; - if ((layer_editing_object_idx != -1) && m_layers_editing.bar_rect_contains(*this, pos.x, pos.y)) + if ((layer_editing_object_idx != -1) && m_layers_editing.bar_rect_contains(*this, pos(0), pos(1))) { // A volume is selected and the mouse is inside the layer thickness bar. // Start editing the layer height. m_layers_editing.state = LayersEditing::Editing; _perform_layer_editing_action(&evt); } - else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos.x, pos.y)) + else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos(0), pos(1))) { if (evt.LeftDown()) { // A volume is selected and the mouse is inside the reset button. - m_print->get_object(layer_editing_object_idx)->reset_layer_height_profile(); + // The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself, + // therefore it is safe to call it while the background processing is running. + const_cast(m_print->get_object(layer_editing_object_idx))->reset_layer_height_profile(); // Index 2 means no editing, just wait for mouse up event. m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } } - else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse) + else if ((m_gizmos.get_current_type() == Gizmos::SlaSupports) && gizmo_reset_rect_contains(*this, pos(0), pos(1))) { - update_gizmos_data(); - m_gizmos.update_on_off_state(*this, m_mouse.position); + if (evt.LeftDown()) + { + m_gizmos.delete_current_grabber(true); +#if ENABLE_GIZMOS_RESET + m_mouse.ignore_up_event = true; +#endif // ENABLE_GIZMOS_RESET + m_dirty = true; + } + } + else if (!m_selection.is_empty() && gizmos_overlay_contains_mouse) + { + m_gizmos.update_on_off_state(*this, m_mouse.position, m_selection); + _update_gizmos_data(); m_dirty = true; } - else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse()) + else if (evt.LeftDown() && !m_selection.is_empty() && m_gizmos.grabber_contains_mouse()) { - update_gizmos_data(); - m_gizmos.start_dragging(); - m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx); - m_dirty = true; + _update_gizmos_data(); + m_selection.start_dragging(); + m_gizmos.start_dragging(m_selection); if (m_gizmos.get_current_type() == Gizmos::Flatten) { // Rotate the object so the normal points downward: - Pointf3 normal = m_gizmos.get_flattening_normal(); - if (normal.x != 0.f || normal.y != 0.f || normal.z != 0.f) { - Pointf3 axis = normal.z > 0.999f ? Pointf3(1, 0, 0) : cross(normal, Pointf3(0.f, 0.f, -1.f)); - float angle = -acos(-normal.z); - m_on_gizmo_rotate_callback.call(angle, axis.x, axis.y, axis.z); - } + m_selection.flattening_rotate(m_gizmos.get_flattening_normal()); + _on_flatten(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); } + + m_dirty = true; + } + else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse() && evt.RightDown()) { + if (m_gizmos.get_current_type() == Gizmos::SlaSupports) + m_gizmos.delete_current_grabber(); + } + else if (toolbar_contains_mouse != -1) + { + m_toolbar_action_running = true; + m_toolbar.do_action((unsigned int)toolbar_contains_mouse); } else { @@ -2817,59 +4134,82 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Don't deselect a volume if layer editing is enabled. We want the object to stay selected // during the scene manipulation. - if (m_picking_enabled && ((volume_idx != -1) || !is_layers_editing_enabled())) + if (m_picking_enabled && ((m_hover_volume_id != -1) || !is_layers_editing_enabled())) { - if (volume_idx != -1) + if (evt.LeftDown() && (m_hover_volume_id != -1)) { - deselect_volumes(); - select_volume(volume_idx); - int group_id = m_volumes.volumes[volume_idx]->select_group_id; - if (group_id != -1) + bool already_selected = m_selection.contains_volume(m_hover_volume_id); + bool shift_down = evt.ShiftDown(); + + if (already_selected && shift_down) + m_selection.remove(m_hover_volume_id); + else { - for (GLVolume* vol : m_volumes.volumes) - { - if ((vol != nullptr) && (vol->select_group_id == group_id)) - vol->selected = true; - } + bool add_as_single = !already_selected && !evt.ShiftDown(); + m_selection.add(m_hover_volume_id, add_as_single); } - update_gizmos_data(); - m_gizmos.refresh(); + m_gizmos.update_on_off_state(m_selection); + _update_gizmos_data(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); m_dirty = true; } } // propagate event through callback - if (m_picking_enabled && (volume_idx != -1)) - _on_select(volume_idx); - if (volume_idx != -1) + if (m_hover_volume_id != -1) { - if (evt.LeftDown() && m_moving_enabled) + if (evt.LeftDown() && m_moving_enabled && (m_mouse.drag.move_volume_idx == -1)) { +#if !ENABLE_GIZMOS_ON_TOP // The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate pos x, y, // an converts the screen space coordinate to unscaled object space. - Pointf3 pos3d = (volume_idx == -1) ? Pointf3(DBL_MAX, DBL_MAX) : _mouse_to_3d(pos); + Vec3d pos3d = _mouse_to_3d(pos); +#endif // !ENABLE_GIZMOS_ON_TOP // Only accept the initial position, if it is inside the volume bounding box. - BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box(); + BoundingBoxf3 volume_bbox = m_volumes.volumes[m_hover_volume_id]->transformed_bounding_box(); volume_bbox.offset(1.0); +#if ENABLE_GIZMOS_ON_TOP + if (volume_bbox.contains(m_mouse.scene_position)) +#else if (volume_bbox.contains(pos3d)) +#endif // ENABLE_GIZMOS_ON_TOP { // The dragging operation is initiated. - m_mouse.drag.move_with_shift = evt.ShiftDown(); - m_mouse.drag.move_volume_idx = volume_idx; + m_mouse.drag.move_volume_idx = m_hover_volume_id; + m_selection.start_dragging(); +#if ENABLE_GIZMOS_ON_TOP + m_mouse.drag.start_position_3D = m_mouse.scene_position; +#else m_mouse.drag.start_position_3D = pos3d; - // Remember the shift to to the object center.The object center will later be used - // to limit the object placement close to the bed. - m_mouse.drag.volume_center_offset = pos3d.vector_to(volume_bbox.center()); +#endif // ENABLE_GIZMOS_ON_TOP } } else if (evt.RightDown()) { - // if right clicking on volume, propagate event through callback - if (m_volumes.volumes[volume_idx]->hover) - m_on_right_click_callback.call(pos.x, pos.y); + // forces a frame render to ensure that m_hover_volume_id is updated even when the user right clicks while + // the context menu is already shown, ensuring it to disappear if the mouse is outside any volume + m_mouse.position = Vec2d((double)pos(0), (double)pos(1)); + render(); + if (m_hover_volume_id != -1) + { + // if right clicking on volume, propagate event through callback (shows context menu) + if (m_volumes.volumes[m_hover_volume_id]->hover && !m_volumes.volumes[m_hover_volume_id]->is_wipe_tower) + { + // forces the selection of the volume + m_selection.add(m_hover_volume_id); + m_gizmos.update_on_off_state(m_selection); + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + _update_gizmos_data(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); + // forces a frame render to update the view before the context menu is shown + render(); + post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, pos.cast())); + } + } } } } @@ -2881,116 +4221,62 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Get new position at the same Z of the initial click point. float z0 = 0.0f; float z1 = 1.0f; - Pointf3 cur_pos = Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D.z); - - // Clip the new position, so the object center remains close to the bed. - cur_pos.translate(m_mouse.drag.volume_center_offset); - Point cur_pos2(scale_(cur_pos.x), scale_(cur_pos.y)); - if (!m_bed.contains(cur_pos2)) - { - Point ip = m_bed.point_projection(cur_pos2); - cur_pos.x = unscale(ip.x); - cur_pos.y = unscale(ip.y); - } - cur_pos.translate(m_mouse.drag.volume_center_offset.negative()); + // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag + Vec3d cur_pos = m_selection.contains_volume(m_hover_volume_id) ? Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D(2)) : m_mouse.drag.start_position_3D; // Calculate the translation vector. - Vectorf3 vector = m_mouse.drag.start_position_3D.vector_to(cur_pos); - // Get the volume being dragged. - GLVolume* volume = m_volumes.volumes[m_mouse.drag.move_volume_idx]; - // Get all volumes belonging to the same group, if any. - std::vector volumes; - int group_id = m_mouse.drag.move_with_shift ? volume->select_group_id : volume->drag_group_id; - if (group_id == -1) - volumes.push_back(volume); - else - { - for (GLVolume* v : m_volumes.volumes) - { - if (v != nullptr) - { - if ((m_mouse.drag.move_with_shift && (v->select_group_id == group_id)) || (!m_mouse.drag.move_with_shift && (v->drag_group_id == group_id))) - volumes.push_back(v); - } - } - } - - // Apply new temporary volume origin and ignore Z. - for (GLVolume* v : volumes) - { - Pointf3 origin = v->get_origin(); - origin.translate(vector.x, vector.y, 0.0); - v->set_origin(origin); - } - - m_mouse.drag.start_position_3D = cur_pos; - m_gizmos.refresh(); + Vec3d displacement = cur_pos - m_mouse.drag.start_position_3D; + m_selection.translate(displacement); + wxGetApp().obj_manipul()->update_settings_value(m_selection); m_dirty = true; } else if (evt.Dragging() && m_gizmos.is_dragging()) { + if (!m_canvas->HasCapture()) + m_canvas->CaptureMouse(); + m_mouse.dragging = true; - - const Pointf3& cur_pos = _mouse_to_bed_3d(pos); - m_gizmos.update(Pointf(cur_pos.x, cur_pos.y)); - - std::vector volumes; - if (m_mouse.drag.gizmo_volume_idx != -1) - { - GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx]; - // Get all volumes belonging to the same group, if any. - if (volume->select_group_id == -1) - volumes.push_back(volume); - else - { - for (GLVolume* v : m_volumes.volumes) - { - if ((v != nullptr) && (v->select_group_id == volume->select_group_id)) - volumes.push_back(v); - } - } - } + m_gizmos.update(mouse_ray(pos), evt.ShiftDown(), &pos); switch (m_gizmos.get_current_type()) { + case Gizmos::Move: + { + // Apply new temporary offset + m_selection.translate(m_gizmos.get_displacement()); + wxGetApp().obj_manipul()->update_settings_value(m_selection); + break; + } case Gizmos::Scale: { - // Apply new temporary scale factor - float scale_factor = m_gizmos.get_scale(); - for (GLVolume* v : volumes) - { - v->set_scale_factor(scale_factor); - } + // Apply new temporary scale factors + m_selection.scale(m_gizmos.get_scale()); + wxGetApp().obj_manipul()->update_settings_value(m_selection); break; } case Gizmos::Rotate: { - // Apply new temporary angle_z - float angle_z = m_gizmos.get_angle_z(); - for (GLVolume* v : volumes) - { - v->set_angle_z(angle_z); - } + // Apply new temporary rotations + m_selection.rotate(m_gizmos.get_rotation(), evt.AltDown()); + wxGetApp().obj_manipul()->update_settings_value(m_selection); break; } default: break; } - if (!volumes.empty()) - { - BoundingBoxf3 bb; - for (const GLVolume* volume : volumes) - { - bb.merge(volume->transformed_bounding_box()); - } - const Pointf3& size = bb.size(); - m_on_update_geometry_info_callback.call(size.x, size.y, size.z, m_gizmos.get_scale()); - } - - if ((m_gizmos.get_current_type() != Gizmos::Rotate) && (volumes.size() > 1)) - m_gizmos.refresh(); +// if (!volumes.empty()) +// { +// BoundingBoxf3 bb; +// for (const GLVolume* volume : volumes) +// { +// bb.merge(volume->transformed_bounding_box()); +// } +// const Vec3d& size = bb.size(); +// const Vec3d& scale = m_gizmos.get_scale(); +// post_event(Vec3dsEvent<2>(EVT_GLCANVAS_UPDATE_GEOMETRY, {size, scale})); +// } m_dirty = true; } @@ -3008,15 +4294,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // if dragging over blank area with left button, rotate if (m_mouse.is_start_position_3D_defined()) { - const Pointf3& orig = m_mouse.drag.start_position_3D; - m_camera.phi += (((float)pos.x - (float)orig.x) * TRACKBALLSIZE); - m_camera.set_theta(m_camera.get_theta() - ((float)pos.y - (float)orig.y) * TRACKBALLSIZE); + const Vec3d& orig = m_mouse.drag.start_position_3D; + m_camera.phi += (((float)pos(0) - (float)orig(0)) * TRACKBALLSIZE); + m_camera.set_theta(m_camera.get_theta() - ((float)pos(1) - (float)orig(1)) * TRACKBALLSIZE); - m_on_viewport_changed_callback.call(); + viewport_changed(); m_dirty = true; } - m_mouse.drag.start_position_3D = Pointf3((coordf_t)pos.x, (coordf_t)pos.y, 0.0); + m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0); } else if (evt.MiddleIsDown() || evt.RightIsDown()) { @@ -3025,13 +4311,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { // get point in model space at Z = 0 float z = 0.0f; - const Pointf3& cur_pos = _mouse_to_3d(pos, &z); - Pointf3 orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z); - Pointf3 camera_target = m_camera.target; - camera_target.translate(orig.vector_to(cur_pos).negative()); - m_camera.target = camera_target; + const Vec3d& cur_pos = _mouse_to_3d(pos, &z); + Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z); + m_camera.target += orig - cur_pos; - m_on_viewport_changed_callback.call(); + viewport_changed(); m_dirty = true; } @@ -3047,71 +4331,82 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) _stop_timer(); if (layer_editing_object_idx != -1) - m_on_model_update_callback.call(); + post_event(SimpleEvent(EVT_GLCANVAS_MODEL_UPDATE)); } else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging) { - // get all volumes belonging to the same group, if any - std::vector volume_idxs; - int vol_id = m_mouse.drag.move_volume_idx; - int group_id = m_mouse.drag.move_with_shift ? m_volumes.volumes[vol_id]->select_group_id : m_volumes.volumes[vol_id]->drag_group_id; - if (group_id == -1) - volume_idxs.push_back(vol_id); - else - { - for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) - { - if ((m_mouse.drag.move_with_shift && (m_volumes.volumes[i]->select_group_id == group_id)) || (m_volumes.volumes[i]->drag_group_id == group_id)) - volume_idxs.push_back(i); - } - } - - _on_move(volume_idxs); - - // force re-selection of the wipe tower, if needed - if ((volume_idxs.size() == 1) && m_volumes.volumes[volume_idxs[0]]->is_wipe_tower) - select_volume(volume_idxs[0]); + m_regenerate_volumes = false; + _on_move(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); } - else if (!m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) + else if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_hover_volume_id != -1) + { + int id = m_selection.get_object_idx(); + + if ((id != -1) && (m_model != nullptr)) { + m_gizmos.clicked_on_object(Vec2d(pos(0), pos(1))); + } + } + else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) { // deselect and propagate event through callback - if (m_picking_enabled) +#if ENABLE_GIZMOS_RESET + if (!m_mouse.ignore_up_event && m_picking_enabled && !m_toolbar_action_running) +#else + if (m_picking_enabled && !m_toolbar_action_running) +#endif // ENABLE_GIZMOS_RESET { - deselect_volumes(); - _on_select(-1); - update_gizmos_data(); + m_selection.clear(); + m_selection.set_mode(Selection::Instance); + wxGetApp().obj_manipul()->update_settings_value(m_selection); + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + _update_gizmos_data(); } +#if ENABLE_GIZMOS_RESET + else if (m_mouse.ignore_up_event) + m_mouse.ignore_up_event = false; +#endif // ENABLE_GIZMOS_RESET } else if (evt.LeftUp() && m_gizmos.is_dragging()) { switch (m_gizmos.get_current_type()) { + case Gizmos::Move: + { + m_regenerate_volumes = false; + _on_move(); + break; + } case Gizmos::Scale: { - m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); + _on_scale(); break; } case Gizmos::Rotate: { - m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); + _on_rotate(); break; } default: break; } m_gizmos.stop_dragging(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); } m_mouse.drag.move_volume_idx = -1; - m_mouse.drag.gizmo_volume_idx = -1; m_mouse.set_start_position_3D_as_invalid(); m_mouse.set_start_position_2D_as_invalid(); m_mouse.dragging = false; + m_toolbar_action_running = false; m_dirty = true; + + if (m_canvas->HasCapture()) + m_canvas->ReleaseMouse(); } else if (evt.Moving()) { - m_mouse.position = Pointf((coordf_t)pos.x, (coordf_t)pos.y); + m_mouse.position = Vec2d((double)pos(0), (double)pos(1)); // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over. if (m_picking_enabled) m_dirty = true; @@ -3133,12 +4428,12 @@ void GLCanvas3D::on_key_down(wxKeyEvent& evt) { int key = evt.GetKeyCode(); if (key == WXK_DELETE) - m_on_remove_object_callback.call(); + post_event(SimpleEvent(EVT_GLCANVAS_REMOVE_OBJECT)); else { #ifdef __WXOSX__ if (key == WXK_BACK) - m_on_remove_object_callback.call(); + post_event(SimpleEvent(EVT_GLCANVAS_REMOVE_OBJECT)); #endif evt.Skip(); } @@ -3167,12 +4462,34 @@ Point GLCanvas3D::get_local_mouse_position() const void GLCanvas3D::reset_legend_texture() { +#if !ENABLE_USE_UNIQUE_GLCONTEXT if (!set_current()) return; +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT m_legend_texture.reset(); } +void GLCanvas3D::set_tooltip(const std::string& tooltip) const +{ + if (m_canvas != nullptr) + { + wxToolTip* t = m_canvas->GetToolTip(); + if (t != nullptr) + { + if (t->GetTip() != tooltip) + t->SetTip(tooltip); + } + else + m_canvas->SetToolTip(tooltip); + } +} + +void GLCanvas3D::set_external_gizmo_widgets_parent(wxWindow *parent) +{ + m_external_gizmo_widgets_parent = parent; +} + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; @@ -3184,13 +4501,135 @@ void GLCanvas3D::_force_zoom_to_bed() m_force_zoom_to_bed_enabled = false; } +bool GLCanvas3D::_init_toolbar() +{ + if (!m_toolbar.is_enabled()) + return true; + + if (!m_toolbar.init("toolbar.png", 36, 1, 1)) + { + // unable to init the toolbar texture, disable it + m_toolbar.set_enabled(false); + return true; + } + +// m_toolbar.set_layout_type(GLToolbar::Layout::Vertical); + m_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); + m_toolbar.set_separator_size(5); + m_toolbar.set_gap_size(2); + + GLToolbarItem::Data item; + + item.name = "add"; + item.tooltip = GUI::L_str("Add..."); + item.sprite_id = 0; + item.is_toggable = false; + item.action_event = EVT_GLTOOLBAR_ADD; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "delete"; + item.tooltip = GUI::L_str("Delete"); + item.sprite_id = 1; + item.is_toggable = false; + item.action_event = EVT_GLTOOLBAR_DELETE; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "deleteall"; + item.tooltip = GUI::L_str("Delete all"); + item.sprite_id = 2; + item.is_toggable = false; + item.action_event = EVT_GLTOOLBAR_DELETE_ALL; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "arrange"; + item.tooltip = GUI::L_str("Arrange"); + item.sprite_id = 3; + item.is_toggable = false; + item.action_event = EVT_GLTOOLBAR_ARRANGE; + if (!m_toolbar.add_item(item)) + return false; + + if (!m_toolbar.add_separator()) + return false; + + item.name = "more"; + item.tooltip = GUI::L_str("Add instance"); + item.sprite_id = 4; + item.is_toggable = false; + item.action_event = EVT_GLTOOLBAR_MORE; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "fewer"; + item.tooltip = GUI::L_str("Remove instance"); + item.sprite_id = 5; + item.is_toggable = false; + item.action_event = EVT_GLTOOLBAR_FEWER; + if (!m_toolbar.add_item(item)) + return false; + + if (!m_toolbar.add_separator()) + return false; + + item.name = "splitobjects"; + item.tooltip = GUI::L_str("Split to objects"); + item.sprite_id = 6; + item.is_toggable = false; + item.action_event = EVT_GLTOOLBAR_SPLIT_OBJECTS; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "splitvolumes"; + item.tooltip = GUI::L_str("Split to parts"); + item.sprite_id = 11; + item.is_toggable = false; + item.action_event = EVT_GLTOOLBAR_SPLIT_VOLUMES; + if (!m_toolbar.add_item(item)) + return false; + + if (!m_toolbar.add_separator()) + return false; + + item.name = "layersediting"; + item.tooltip = GUI::L_str("Layers editing"); + item.sprite_id = 9; + item.is_toggable = true; + item.action_event = EVT_GLTOOLBAR_LAYERSEDITING; + if (!m_toolbar.add_item(item)) + return false; + + if (!m_toolbar.add_separator()) + return false; + + enable_toolbar_item("add", true); + + return true; +} + +#if ENABLE_USE_UNIQUE_GLCONTEXT +bool GLCanvas3D::_set_current() +{ + if ((m_canvas != nullptr) && (m_context != nullptr)) + return m_canvas->SetCurrent(*m_context); + + return false; +} +#endif ENABLE_USE_UNIQUE_GLCONTEXT + void GLCanvas3D::_resize(unsigned int w, unsigned int h) { if ((m_canvas == nullptr) && (m_context == nullptr)) return; // ensures that this canvas is current +#if ENABLE_USE_UNIQUE_GLCONTEXT + _set_current(); +#else set_current(); +#endif // ENABLE_USE_UNIQUE_GLCONTEXT ::glViewport(0, 0, w, h); ::glMatrixMode(GL_PROJECTION); @@ -3259,51 +4698,6 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box() const return bb; } -BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const -{ - BoundingBoxf3 bb; - - std::vector selected_volumes; - for (const GLVolume* volume : m_volumes.volumes) - { - if ((volume != nullptr) && !volume->is_wipe_tower && volume->selected) - selected_volumes.push_back(volume); - } - - bool use_drag_group_id = selected_volumes.size() > 1; - if (use_drag_group_id) - { - int drag_group_id = selected_volumes[0]->drag_group_id; - for (const GLVolume* volume : selected_volumes) - { - if (drag_group_id != volume->drag_group_id) - { - use_drag_group_id = false; - break; - } - } - } - - if (use_drag_group_id) - { - for (const GLVolume* volume : selected_volumes) - { - bb.merge(volume->bounding_box); - } - - bb = bb.transformed(selected_volumes[0]->world_matrix()); - } - else - { - for (const GLVolume* volume : selected_volumes) - { - bb.merge(volume->transformed_bounding_box()); - } - } - - return bb; -} - void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { // Calculate the zoom factor needed to adjust viewport to bounding box. @@ -3314,7 +4708,7 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) // center view around bounding box center m_camera.target = bbox.center(); - m_on_viewport_changed_callback.call(); + viewport_changed(); _refresh_if_shown_on_screen(); } @@ -3337,41 +4731,41 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co ::glGetFloatv(GL_MODELVIEW_MATRIX, matrix); // camera axes - Pointf3 right((coordf_t)matrix[0], (coordf_t)matrix[4], (coordf_t)matrix[8]); - Pointf3 up((coordf_t)matrix[1], (coordf_t)matrix[5], (coordf_t)matrix[9]); - Pointf3 forward((coordf_t)matrix[2], (coordf_t)matrix[6], (coordf_t)matrix[10]); + Vec3d right((double)matrix[0], (double)matrix[4], (double)matrix[8]); + Vec3d up((double)matrix[1], (double)matrix[5], (double)matrix[9]); + Vec3d forward((double)matrix[2], (double)matrix[6], (double)matrix[10]); - Pointf3 bb_min = bbox.min; - Pointf3 bb_max = bbox.max; - Pointf3 bb_center = bbox.center(); + Vec3d bb_min = bbox.min; + Vec3d bb_max = bbox.max; + Vec3d bb_center = bbox.center(); // bbox vertices in world space - std::vector vertices; + std::vector vertices; vertices.reserve(8); vertices.push_back(bb_min); - vertices.emplace_back(bb_max.x, bb_min.y, bb_min.z); - vertices.emplace_back(bb_max.x, bb_max.y, bb_min.z); - vertices.emplace_back(bb_min.x, bb_max.y, bb_min.z); - vertices.emplace_back(bb_min.x, bb_min.y, bb_max.z); - vertices.emplace_back(bb_max.x, bb_min.y, bb_max.z); + vertices.emplace_back(bb_max(0), bb_min(1), bb_min(2)); + vertices.emplace_back(bb_max(0), bb_max(1), bb_min(2)); + vertices.emplace_back(bb_min(0), bb_max(1), bb_min(2)); + vertices.emplace_back(bb_min(0), bb_min(1), bb_max(2)); + vertices.emplace_back(bb_max(0), bb_min(1), bb_max(2)); vertices.push_back(bb_max); - vertices.emplace_back(bb_min.x, bb_max.y, bb_max.z); + vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2)); - coordf_t max_x = 0.0; - coordf_t max_y = 0.0; + double max_x = 0.0; + double max_y = 0.0; // margin factor to give some empty space around the bbox - coordf_t margin_factor = 1.25; + double margin_factor = 1.25; - for (const Pointf3 v : vertices) + for (const Vec3d v : vertices) { // project vertex on the plane perpendicular to camera forward axis - Pointf3 pos(v.x - bb_center.x, v.y - bb_center.y, v.z - bb_center.z); - Pointf3 proj_on_plane = pos - dot(pos, forward) * forward; + Vec3d pos(v(0) - bb_center(0), v(1) - bb_center(1), v(2) - bb_center(2)); + Vec3d proj_on_plane = pos - pos.dot(forward) * forward; // calculates vertex coordinate along camera xy axes - coordf_t x_on_plane = dot(proj_on_plane, right); - coordf_t y_on_plane = dot(proj_on_plane, up); + double x_on_plane = proj_on_plane.dot(right); + double y_on_plane = proj_on_plane.dot(up); max_x = std::max(max_x, margin_factor * std::abs(x_on_plane)); max_y = std::max(max_y, margin_factor * std::abs(y_on_plane)); @@ -3384,29 +4778,7 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co max_y *= 2.0; const Size& cnv_size = get_canvas_size(); - return (float)std::min((coordf_t)cnv_size.get_width() / max_x, (coordf_t)cnv_size.get_height() / max_y); -} - -void GLCanvas3D::_deregister_callbacks() -{ - m_on_viewport_changed_callback.deregister_callback(); - m_on_double_click_callback.deregister_callback(); - m_on_right_click_callback.deregister_callback(); - m_on_select_object_callback.deregister_callback(); - m_on_model_update_callback.deregister_callback(); - m_on_remove_object_callback.deregister_callback(); - m_on_arrange_callback.deregister_callback(); - m_on_rotate_object_left_callback.deregister_callback(); - m_on_rotate_object_right_callback.deregister_callback(); - m_on_scale_object_uniformly_callback.deregister_callback(); - m_on_increase_objects_callback.deregister_callback(); - m_on_decrease_objects_callback.deregister_callback(); - m_on_instance_moved_callback.deregister_callback(); - m_on_wipe_tower_moved_callback.deregister_callback(); - m_on_enable_action_buttons_callback.deregister_callback(); - m_on_gizmo_scale_uniformly_callback.deregister_callback(); - m_on_gizmo_rotate_callback.deregister_callback(); - m_on_update_geometry_info_callback.deregister_callback(); + return (float)std::min((double)cnv_size.get_width() / max_x, (double)cnv_size.get_height() / max_y); } void GLCanvas3D::_mark_volumes_for_layer_height() const @@ -3416,11 +4788,11 @@ void GLCanvas3D::_mark_volumes_for_layer_height() const for (GLVolume* vol : m_volumes.volumes) { - int object_id = int(vol->select_group_id / 1000000); + int object_id = vol->object_idx(); int shader_id = m_layers_editing.get_shader_program_id(); if (is_layers_editing_enabled() && (shader_id != -1) && vol->selected && - vol->has_layer_height_texture() && (object_id < (int)m_print->objects.size())) + vol->has_layer_height_texture() && (object_id < (int)m_print->objects().size())) { vol->set_layer_height_texture_data(m_layers_editing.get_z_texture_id(), shader_id, m_print->get_object(object_id), _get_layers_editing_cursor_z_relative(), m_layers_editing.band_width); @@ -3455,15 +4827,14 @@ void GLCanvas3D::_camera_tranform() const ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw - Pointf3 neg_target = m_camera.target.negative(); - ::glTranslatef((GLfloat)neg_target.x, (GLfloat)neg_target.y, (GLfloat)neg_target.z); + ::glTranslated(-m_camera.target(0), -m_camera.target(1), -m_camera.target(2)); } void GLCanvas3D::_picking_pass() const { - const Pointf& pos = m_mouse.position; + const Vec2d& pos = m_mouse.position; - if (m_picking_enabled && !m_mouse.dragging && (pos != Pointf(DBL_MAX, DBL_MAX))) + if (m_picking_enabled && !m_mouse.dragging && (pos != Vec2d(DBL_MAX, DBL_MAX))) { // Render the object for picking. // FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing. @@ -3478,39 +4849,25 @@ void GLCanvas3D::_picking_pass() const ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); _render_volumes(true); - m_gizmos.render_current_gizmo_for_picking_pass(_selected_volumes_bounding_box()); + m_gizmos.render_current_gizmo_for_picking_pass(m_selection); if (m_multisample_allowed) ::glEnable(GL_MULTISAMPLE); int volume_id = -1; - for (GLVolume* vol : m_volumes.volumes) - { - vol->hover = false; - } GLubyte color[4] = { 0, 0, 0, 0 }; const Size& cnv_size = get_canvas_size(); - bool inside = (0 <= pos.x) && (pos.x < cnv_size.get_width()) && (0 <= pos.y) && (pos.y < cnv_size.get_height()); + bool inside = (0 <= pos(0)) && (pos(0) < cnv_size.get_width()) && (0 <= pos(1)) && (pos(1) < cnv_size.get_height()); if (inside) { - ::glReadPixels(pos.x, cnv_size.get_height() - pos.y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); + ::glReadPixels(pos(0), cnv_size.get_height() - pos(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256; } if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) { m_hover_volume_id = volume_id; - m_volumes.volumes[volume_id]->hover = true; - int group_id = m_volumes.volumes[volume_id]->select_group_id; - if (group_id != -1) - { - for (GLVolume* vol : m_volumes.volumes) - { - if (vol->select_group_id == group_id) - vol->hover = true; - } - } m_gizmos.set_hover_id(-1); } else @@ -3519,18 +4876,24 @@ void GLCanvas3D::_picking_pass() const m_gizmos.set_hover_id(inside ? (254 - (int)color[2]) : -1); } + _update_volumes_hover_state(); + // updates gizmos overlay - if (_get_first_selected_object_id() != -1) - m_gizmos.update_hover_state(*this, pos); + if (!m_selection.is_empty()) + { + std::string name = m_gizmos.update_hover_state(*this, pos, m_selection); + if (!name.empty()) + set_tooltip(name); + } else m_gizmos.reset_all_states(); + + m_toolbar.update_hover_state(pos); } } void GLCanvas3D::_render_background() const { - ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - ::glPushMatrix(); ::glLoadIdentity(); ::glMatrixMode(GL_PROJECTION); @@ -3546,9 +4909,9 @@ void GLCanvas3D::_render_background() const ::glVertex2f(1.0f, -1.0f); if (m_dynamic_background_enabled && _is_any_volume_outside()) - ::glColor3f(ERROR_BG_COLOR[0], ERROR_BG_COLOR[1], ERROR_BG_COLOR[2]); + ::glColor3fv(ERROR_BG_COLOR); else - ::glColor3f(DEFAULT_BG_COLOR[0], DEFAULT_BG_COLOR[1], DEFAULT_BG_COLOR[2]); + ::glColor3fv(DEFAULT_BG_COLOR); ::glVertex2f(1.0f, 1.0f); ::glVertex2f(-1.0f, 1.0f); @@ -3577,6 +4940,7 @@ void GLCanvas3D::_render_objects() const return; ::glEnable(GL_LIGHTING); + ::glEnable(GL_DEPTH_TEST); if (!m_shader_enabled) _render_volumes(false); @@ -3589,7 +4953,7 @@ void GLCanvas3D::_render_objects() const if (m_config != nullptr) { const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(); - m_volumes.set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height")); + m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); m_volumes.check_outside_state(m_config, nullptr); } // do not cull backfaces to show broken geometry, if any @@ -3618,9 +4982,10 @@ void GLCanvas3D::_render_objects() const ::glDisable(GL_LIGHTING); } -void GLCanvas3D::_render_cutting_plane() const +void GLCanvas3D::_render_selection() const { - m_cutting_plane.render(volumes_bounding_box()); + if (!m_gizmos.is_running()) + m_selection.render(); } void GLCanvas3D::_render_warning_texture() const @@ -3628,32 +4993,7 @@ void GLCanvas3D::_render_warning_texture() const if (!m_warning_texture_enabled) return; - // If the warning texture has not been loaded into the GPU, do it now. - unsigned int tex_id = m_warning_texture.get_id(); - if (tex_id > 0) - { - int w = m_warning_texture.get_width(); - int h = m_warning_texture.get_height(); - if ((w > 0) && (h > 0)) - { - ::glDisable(GL_DEPTH_TEST); - ::glPushMatrix(); - ::glLoadIdentity(); - - const Size& cnv_size = get_canvas_size(); - float zoom = get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - float l = (-0.5f * (float)w) * inv_zoom; - float t = (-0.5f * (float)cnv_size.get_height() + (float)h) * inv_zoom; - float r = l + (float)w * inv_zoom; - float b = t - (float)h * inv_zoom; - - GLTexture::render_texture(tex_id, l, r, b, t); - - ::glPopMatrix(); - ::glEnable(GL_DEPTH_TEST); - } - } + m_warning_texture.render(*this); } void GLCanvas3D::_render_legend_texture() const @@ -3661,37 +5001,12 @@ void GLCanvas3D::_render_legend_texture() const if (!m_legend_texture_enabled) return; - // If the legend texture has not been loaded into the GPU, do it now. - unsigned int tex_id = m_legend_texture.get_id(); - if (tex_id > 0) - { - int w = m_legend_texture.get_width(); - int h = m_legend_texture.get_height(); - if ((w > 0) && (h > 0)) - { - ::glDisable(GL_DEPTH_TEST); - ::glPushMatrix(); - ::glLoadIdentity(); - - const Size& cnv_size = get_canvas_size(); - float zoom = get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - float l = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; - float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom; - float r = l + (float)w * inv_zoom; - float b = t - (float)h * inv_zoom; - - GLTexture::render_texture(tex_id, l, r, b, t); - - ::glPopMatrix(); - ::glEnable(GL_DEPTH_TEST); - } - } + m_legend_texture.render(*this); } void GLCanvas3D::_render_layer_editing_overlay() const { - if (m_print == nullptr) + if ((m_print == nullptr) || m_print->objects().empty()) return; GLVolume* volume = nullptr; @@ -3710,8 +5025,8 @@ void GLCanvas3D::_render_layer_editing_overlay() const // If the active object was not allocated at the Print, go away.This should only be a momentary case between an object addition / deletion // and an update by Platter::async_apply_config. - int object_idx = int(volume->select_group_id / 1000000); - if ((int)m_print->objects.size() < object_idx) + int object_idx = volume->object_idx(); + if ((int)m_print->objects().size() <= object_idx) return; const PrintObject* print_object = m_print->get_object(object_idx); @@ -3751,10 +5066,12 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const else { vol->set_render_color(); - ::glColor4f(vol->render_color[0], vol->render_color[1], vol->render_color[2], vol->render_color[3]); + ::glColor4fv(vol->render_color); } - vol->render(); + if (!fake_colors || !vol->disabled) + vol->render(); + ++volume_id; } @@ -3768,9 +5085,133 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const ::glDisable(GL_LIGHTING); } -void GLCanvas3D::_render_gizmo(Gizmos::RenderOrder render_order) const +void GLCanvas3D::_render_current_gizmo() const { - m_gizmos.render(*this, _selected_volumes_bounding_box(), render_order); + m_gizmos.render_current_gizmo(m_selection); +} + +void GLCanvas3D::_render_gizmos_overlay() const +{ + m_gizmos.render_overlay(*this); +} + +void GLCanvas3D::_render_toolbar() const +{ + _resize_toolbar(); + m_toolbar.render(); +} + +#if ENABLE_SHOW_CAMERA_TARGET +void GLCanvas3D::_render_camera_target() const +{ + double half_length = 5.0; + + ::glDisable(GL_DEPTH_TEST); + + ::glLineWidth(2.0f); + ::glBegin(GL_LINES); + // draw line for x axis + ::glColor3f(1.0f, 0.0f, 0.0f); + ::glVertex3d(m_camera.target(0) - half_length, m_camera.target(1), m_camera.target(2)); + ::glVertex3d(m_camera.target(0) + half_length, m_camera.target(1), m_camera.target(2)); + // draw line for y axis + ::glColor3f(0.0f, 1.0f, 0.0f); + ::glVertex3d(m_camera.target(0), m_camera.target(1) - half_length, m_camera.target(2)); + ::glVertex3d(m_camera.target(0), m_camera.target(1) + half_length, m_camera.target(2)); + ::glEnd(); + + ::glBegin(GL_LINES); + ::glColor3f(0.0f, 0.0f, 1.0f); + ::glVertex3d(m_camera.target(0), m_camera.target(1), m_camera.target(2) - half_length); + ::glVertex3d(m_camera.target(0), m_camera.target(1), m_camera.target(2) + half_length); + ::glEnd(); +} +#endif // ENABLE_SHOW_CAMERA_TARGET + +void GLCanvas3D::_update_volumes_hover_state() const +{ + for (GLVolume* v : m_volumes.volumes) + { + v->hover = false; + } + + if (m_hover_volume_id == -1) + return; + + GLVolume* volume = m_volumes.volumes[m_hover_volume_id]; + + switch (m_selection.get_mode()) + { + case Selection::Volume: + { + volume->hover = true; + break; + } + case Selection::Instance: + { + int object_idx = volume->object_idx(); + int instance_idx = volume->instance_idx(); + + for (GLVolume* v : m_volumes.volumes) + { + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + v->hover = true; + } + + break; + } + } +} + +void GLCanvas3D::_update_gizmos_data() +{ + if (!m_gizmos.is_enabled()) + return; + + bool enable_move_z = !m_selection.is_wipe_tower(); + m_gizmos.enable_grabber(Gizmos::Move, 2, enable_move_z); + bool enable_scale_xyz = m_selection.is_single_full_instance() || m_selection.is_single_volume() || m_selection.is_single_modifier(); + for (int i = 0; i < 6; ++i) + { + m_gizmos.enable_grabber(Gizmos::Scale, i, enable_scale_xyz); + } + + if (m_selection.is_single_full_instance()) + { +#if ENABLE_MODELVOLUME_TRANSFORM + // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first + const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; + m_gizmos.set_scale(volume->get_instance_scaling_factor()); + m_gizmos.set_rotation(volume->get_instance_rotation()); + ModelObject* model_object = m_model->objects[m_selection.get_object_idx()]; + m_gizmos.set_flattening_data(model_object); + m_gizmos.set_model_object_ptr(model_object); +#else + ModelObject* model_object = m_model->objects[m_selection.get_object_idx()]; + ModelInstance* model_instance = model_object->instances[m_selection.get_instance_idx()]; + m_gizmos.set_scale(model_instance->get_scaling_factor()); + m_gizmos.set_rotation(model_instance->get_rotation()); + m_gizmos.set_flattening_data(model_object); + m_gizmos.set_model_object_ptr(model_object); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } +#if ENABLE_MODELVOLUME_TRANSFORM + else if (m_selection.is_single_volume() || m_selection.is_single_modifier()) + { + const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; + m_gizmos.set_scale(volume->get_volume_scaling_factor()); + m_gizmos.set_rotation(volume->get_volume_rotation()); + m_gizmos.set_flattening_data(nullptr); + m_gizmos.set_model_object_ptr(nullptr); + } +#endif // ENABLE_MODELVOLUME_TRANSFORM + else + { + m_gizmos.set_scale(Vec3d::Ones()); + m_gizmos.set_rotation(Vec3d::Zero()); + m_gizmos.set_flattening_data(m_selection.is_from_single_object() ? m_model->objects[m_selection.get_object_idx()] : nullptr); + m_gizmos.set_model_object_ptr(nullptr); + } } float GLCanvas3D::_get_layers_editing_cursor_z_relative() const @@ -3787,7 +5228,7 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) if (m_print == nullptr) return; - PrintObject* selected_obj = m_print->get_object(object_idx_selected); + const PrintObject* selected_obj = m_print->get_object(object_idx_selected); if (selected_obj == nullptr) return; @@ -3796,20 +5237,21 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) { const Rect& rect = LayersEditing::get_bar_rect_screen(*this); float b = rect.get_bottom(); - m_layers_editing.last_z = unscale(selected_obj->size.z) * (b - evt->GetY() - 1.0f) / (b - rect.get_top()); + m_layers_editing.last_z = unscale(selected_obj->size(2)) * (b - evt->GetY() - 1.0f) / (b - rect.get_top()); m_layers_editing.last_action = evt->ShiftDown() ? (evt->RightIsDown() ? 3 : 2) : (evt->RightIsDown() ? 0 : 1); } // Mark the volume as modified, so Print will pick its layer height profile ? Where to mark it ? // Start a timer to refresh the print ? schedule_background_process() ? - // The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself. - selected_obj->adjust_layer_height_profile(m_layers_editing.last_z, m_layers_editing.strength, m_layers_editing.band_width, m_layers_editing.last_action); + // The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself, + // therefore it is safe to call it while the background processing is running. + const_cast(selected_obj)->adjust_layer_height_profile(m_layers_editing.last_z, m_layers_editing.strength, m_layers_editing.band_width, m_layers_editing.last_action); // searches the id of the first volume of the selected object int volume_idx = 0; for (int i = 0; i < object_idx_selected; ++i) { - PrintObject* obj = m_print->get_object(i); + const PrintObject* obj = m_print->get_object(i); if (obj != nullptr) { for (int j = 0; j < (int)obj->region_volumes.size(); ++j) @@ -3826,10 +5268,10 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) _start_timer(); } -Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) +Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) { if (m_canvas == nullptr) - return Pointf3(DBL_MAX, DBL_MAX, DBL_MAX); + return Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); _camera_tranform(); @@ -3840,110 +5282,81 @@ Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) GLdouble projection_matrix[16]; ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); - GLint y = viewport[3] - (GLint)mouse_pos.y; + GLint y = viewport[3] - (GLint)mouse_pos(1); GLfloat mouse_z; if (z == nullptr) - ::glReadPixels((GLint)mouse_pos.x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z); + ::glReadPixels((GLint)mouse_pos(0), y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z); else mouse_z = *z; GLdouble out_x, out_y, out_z; - ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)y, (GLdouble)mouse_z, modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); - return Pointf3((coordf_t)out_x, (coordf_t)out_y, (coordf_t)out_z); + ::gluUnProject((GLdouble)mouse_pos(0), (GLdouble)y, (GLdouble)mouse_z, modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); + return Vec3d((double)out_x, (double)out_y, (double)out_z); } -Pointf3 GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) +Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) +{ + return mouse_ray(mouse_pos).intersect_plane(0.0); +} + +Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos) { float z0 = 0.0f; float z1 = 1.0f; - return Linef3(_mouse_to_3d(mouse_pos, &z0), _mouse_to_3d(mouse_pos, &z1)).intersect_plane(0.0); + return Linef3(_mouse_to_3d(mouse_pos, &z0), _mouse_to_3d(mouse_pos, &z1)); } void GLCanvas3D::_start_timer() { - if (m_timer != nullptr) - m_timer->Start(100, wxTIMER_CONTINUOUS); + m_timer.Start(100, wxTIMER_CONTINUOUS); } void GLCanvas3D::_stop_timer() { - if (m_timer != nullptr) - m_timer->Stop(); -} - -int GLCanvas3D::_get_first_selected_object_id() const -{ - if (m_print != nullptr) - { - int objects_count = (int)m_print->objects.size(); - - for (const GLVolume* vol : m_volumes.volumes) - { - if ((vol != nullptr) && vol->selected) - { - int object_id = vol->select_group_id / 1000000; - // Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy. - if (object_id < 10000) - return (object_id >= objects_count) ? -1 : object_id; - } - } - } - return -1; -} - -int GLCanvas3D::_get_first_selected_volume_id(int object_id) const -{ - int volume_id = -1; - - for (const GLVolume* vol : m_volumes.volumes) - { - ++volume_id; - if ((vol != nullptr) && vol->selected && (object_id == vol->select_group_id / 1000000)) - return volume_id; - } - - return -1; + m_timer.Stop(); } void GLCanvas3D::_load_print_toolpaths() { +#if !ENABLE_USE_UNIQUE_GLCONTEXT // ensures this canvas is current if (!set_current()) return; +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT if (m_print == nullptr) return; - if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim)) + if (!m_print->is_step_done(psSkirt) || !m_print->is_step_done(psBrim)) return; - if (!m_print->has_skirt() && (m_print->config.brim_width.value == 0)) + if (!m_print->has_skirt() && (m_print->config().brim_width.value == 0)) return; const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish // number of skirt layers size_t total_layer_count = 0; - for (const PrintObject* print_object : m_print->objects) + for (const PrintObject* print_object : m_print->objects()) { total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); } - size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min(m_print->config.skirt_height.value, total_layer_count); - if ((skirt_height == 0) && (m_print->config.brim_width.value > 0)) + size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min(m_print->config().skirt_height.value, total_layer_count); + if ((skirt_height == 0) && (m_print->config().brim_width.value > 0)) skirt_height = 1; // get first skirt_height layers (maybe this should be moved to a PrintObject method?) - const PrintObject* object0 = m_print->objects.front(); + const PrintObject* object0 = m_print->objects().front(); std::vector print_zs; print_zs.reserve(skirt_height * 2); - for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++i) + for (size_t i = 0; i < std::min(skirt_height, object0->layers().size()); ++i) { - print_zs.push_back(float(object0->layers[i]->print_z)); + print_zs.push_back(float(object0->layers()[i]->print_z)); } //FIXME why there are support layers? - for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++i) + for (size_t i = 0; i < std::min(skirt_height, object0->support_layers().size()); ++i) { - print_zs.push_back(float(object0->support_layers[i]->print_z)); + print_zs.push_back(float(object0->support_layers()[i]->print_z)); } sort_remove_duplicates(print_zs); if (print_zs.size() > skirt_height) @@ -3956,9 +5369,9 @@ void GLCanvas3D::_load_print_toolpaths() volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size()); volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size()); if (i == 0) - _3DScene::extrusionentity_to_verts(m_print->brim, print_zs[i], Point(0, 0), volume); + _3DScene::extrusionentity_to_verts(m_print->brim(), print_zs[i], Point(0, 0), volume); - _3DScene::extrusionentity_to_verts(m_print->skirt, print_zs[i], Point(0, 0), volume); + _3DScene::extrusionentity_to_verts(m_print->skirt(), print_zs[i], Point(0, 0), volume); } volume.bounding_box = volume.indexed_vertex_array.bounding_box(); volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); @@ -3997,20 +5410,20 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } } ctxt; - ctxt.shifted_copies = &print_object._shifted_copies; + ctxt.shifted_copies = &print_object.copies(); // order layers by print_z - ctxt.layers.reserve(print_object.layers.size() + print_object.support_layers.size()); - for (const Layer *layer : print_object.layers) + ctxt.layers.reserve(print_object.layers().size() + print_object.support_layers().size()); + for (const Layer *layer : print_object.layers()) ctxt.layers.push_back(layer); - for (const Layer *layer : print_object.support_layers) + for (const Layer *layer : print_object.support_layers()) ctxt.layers.push_back(layer); std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); // Maximum size of an allocation block: 32MB / sizeof(float) - ctxt.has_perimeters = print_object.state.is_done(posPerimeters); - ctxt.has_infill = print_object.state.is_done(posInfill); - ctxt.has_support = print_object.state.is_done(posSupportMaterial); + ctxt.has_perimeters = print_object.is_step_done(posPerimeters); + ctxt.has_infill = print_object.is_step_done(posInfill); + ctxt.has_support = print_object.is_step_done(posSupportMaterial); ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; @@ -4030,7 +5443,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c tbb::parallel_for( tbb::blocked_range(0, ctxt.layers.size(), grain_size), [&ctxt, &new_volume](const tbb::blocked_range& range) { - std::vector vols; + GLVolumePtrs vols; if (ctxt.color_by_tool()) { for (size_t i = 0; i < ctxt.number_tools(); ++i) vols.emplace_back(new_volume(ctxt.color_tool(i))); @@ -4050,10 +5463,10 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } } for (const Point © : *ctxt.shifted_copies) { - for (const LayerRegion *layerm : layer->regions) { + for (const LayerRegion *layerm : layer->regions()) { if (ctxt.has_perimeters) _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, - *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]); + *vols[ctxt.volume_idx(layerm->region()->config().perimeter_extruder.value, 0)]); if (ctxt.has_infill) { for (const ExtrusionEntity *ee : layerm->fills.entities) { // fill represents infill extrusions of a single island. @@ -4062,8 +5475,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, *vols[ctxt.volume_idx( is_solid_infill(fill->entities.front()->role()) ? - layerm->region()->config.solid_infill_extruder : - layerm->region()->config.infill_extruder, + layerm->region()->config().solid_infill_extruder : + layerm->region()->config().infill_extruder, 1)]); } } @@ -4075,8 +5488,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, *vols[ctxt.volume_idx( (extrusion_entity->role() == erSupportMaterial) ? - support_layer->object()->config.support_material_extruder : - support_layer->object()->config.support_material_interface_extruder, + support_layer->object()->config().support_material_extruder : + support_layer->object()->config().support_material_interface_extruder, 2)]); } } @@ -4120,10 +5533,10 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_tool_colors) { - if ((m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty()) + if ((m_print == nullptr) || m_print->wipe_tower_data().tool_changes.empty()) return; - if (!m_print->state.is_done(psWipeTower)) + if (!m_print->is_step_done(psWipeTower)) return; std::vector tool_colors = _parse_colors(str_tool_colors); @@ -4151,9 +5564,10 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ } const std::vector& tool_change(size_t idx) { + const auto &tool_changes = print->wipe_tower_data().tool_changes; return priming.empty() ? - ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) : - ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]); + ((idx == tool_changes.size()) ? final : tool_changes[idx]) : + ((idx == 0) ? priming : (idx == tool_changes.size() + 1) ? final : tool_changes[idx - 1]); } std::vector priming; std::vector final; @@ -4161,18 +5575,18 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ ctxt.print = m_print; ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; - if (m_print->m_wipe_tower_priming && m_print->config.single_extruder_multi_material_priming) - ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get()); - if (m_print->m_wipe_tower_final_purge) - ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get()); + if (m_print->wipe_tower_data().priming && m_print->config().single_extruder_multi_material_priming) + ctxt.priming.emplace_back(*m_print->wipe_tower_data().priming.get()); + if (m_print->wipe_tower_data().final_purge) + ctxt.final.emplace_back(*m_print->wipe_tower_data().final_purge.get()); - ctxt.wipe_tower_angle = ctxt.print->config.wipe_tower_rotation_angle.value/180.f * M_PI; - ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config.wipe_tower_x.value, ctxt.print->config.wipe_tower_y.value); + ctxt.wipe_tower_angle = ctxt.print->config().wipe_tower_rotation_angle.value/180.f * PI; + ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value); BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start"; //FIXME Improve the heuristics for a grain size. - size_t n_items = m_print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); + size_t n_items = m_print->wipe_tower_data().tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); size_t grain_size = std::max(n_items / 128, size_t(1)); tbb::spin_mutex new_volume_mutex; auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* { @@ -4188,7 +5602,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ tbb::blocked_range(0, n_items, grain_size), [&ctxt, &new_volume](const tbb::blocked_range& range) { // Bounding box of this slab of a wipe tower. - std::vector vols; + GLVolumePtrs vols; if (ctxt.color_by_tool()) { for (size_t i = 0; i < ctxt.number_tools(); ++i) vols.emplace_back(new_volume(ctxt.color_tool(i))); @@ -4414,9 +5828,9 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat m_gcode_preview_volume_index.first_volumes.pop_back(); if (initial_volumes_count != m_volumes.volumes.size()) { - std::vector::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; - std::vector::iterator end = m_volumes.volumes.end(); - for (std::vector::iterator it = begin; it < end; ++it) + GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; + GLVolumePtrs::iterator end = m_volumes.volumes.end(); + for (GLVolumePtrs::iterator it = begin; it < end; ++it) { GLVolume* volume = *it; delete volume; @@ -4487,9 +5901,9 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, // an error occourred - restore to previous state and return if (initial_volumes_count != m_volumes.volumes.size()) { - std::vector::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; - std::vector::iterator end = m_volumes.volumes.end(); - for (std::vector::iterator it = begin; it < end; ++it) + GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; + GLVolumePtrs::iterator end = m_volumes.volumes.end(); + for (GLVolumePtrs::iterator it = begin; it < end; ++it) { GLVolume* volume = *it; delete volume; @@ -4567,7 +5981,7 @@ bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data) TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type)); if (type != types.end()) { - type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); + type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min(2))); type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size()); type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size()); @@ -4633,7 +6047,7 @@ bool GLCanvas3D::_travel_paths_by_feedrate(const GCodePreviewData& preview_data) FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)); if (feedrate != feedrates.end()) { - feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); + feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min(2))); feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size()); feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size()); @@ -4699,7 +6113,7 @@ bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, con ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)); if (tool != tools.end()) { - tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); + tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min(2))); tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size()); tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size()); @@ -4724,11 +6138,11 @@ void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data) m_volumes.volumes.emplace_back(volume); GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions); - std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); + std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position(2) < p2.position(2); }); for (const GCodePreviewData::Retraction::Position& position : copy) { - volume->print_zs.push_back(unscale(position.position.z)); + volume->print_zs.push_back(unscale(position.position(2))); volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); @@ -4755,11 +6169,11 @@ void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data) m_volumes.volumes.emplace_back(volume); GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions); - std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); + std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position(2) < p2.position(2); }); for (const GCodePreviewData::Retraction::Position& position : copy) { - volume->print_zs.push_back(unscale(position.position.z)); + volume->print_zs.push_back(unscale(position.position(2))); volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); @@ -4777,15 +6191,15 @@ void GLCanvas3D::_load_shells() size_t initial_volumes_count = m_volumes.volumes.size(); m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count); - if (m_print->objects.empty()) + if (m_print->objects().empty()) // nothing to render, return return; // adds objects' volumes unsigned int object_id = 0; - for (PrintObject* obj : m_print->objects) + for (const PrintObject* obj : m_print->objects()) { - ModelObject* model_obj = obj->model_object(); + const ModelObject* model_obj = obj->model_object(); std::vector instance_ids(model_obj->instances.size()); for (int i = 0; i < (int)model_obj->instances.size(); ++i) @@ -4793,21 +6207,23 @@ void GLCanvas3D::_load_shells() instance_ids[i] = i; } - m_volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs && m_initialized); + m_volumes.load_object(model_obj, object_id, instance_ids, "object", m_use_VBOs && m_initialized); ++object_id; } - // adds wipe tower's volume - coordf_t max_z = m_print->objects[0]->model_object()->get_model()->bounding_box().max.z; - const PrintConfig& config = m_print->config; - unsigned int extruders_count = config.nozzle_diameter.size(); - if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) { - float depth = m_print->get_wipe_tower_depth(); - if (!m_print->state.is_done(psWipeTower)) - depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1) ; - m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, - m_use_VBOs && m_initialized, !m_print->state.is_done(psWipeTower), m_print->config.nozzle_diameter.values[0] * 1.25f * 4.5f); + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF) { + // adds wipe tower's volume + double max_z = m_print->objects()[0]->model_object()->get_model()->bounding_box().max(2); + const PrintConfig& config = m_print->config(); + unsigned int extruders_count = config.nozzle_diameter.size(); + if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) { + float depth = m_print->get_wipe_tower_depth(); + if (!m_print->is_step_done(psWipeTower)) + depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1) ; + m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, + m_use_VBOs && m_initialized, !m_print->is_step_done(psWipeTower), m_print->config().nozzle_diameter.values[0] * 1.25f * 4.5f); + } } } @@ -4816,10 +6232,10 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size(); for (unsigned int i = 0; i < size; ++i) { - std::vector::iterator begin = m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i].id; - std::vector::iterator end = (i + 1 < size) ? m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i + 1].id : m_volumes.volumes.end(); + GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i].id; + GLVolumePtrs::iterator end = (i + 1 < size) ? m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i + 1].id : m_volumes.volumes.end(); - for (std::vector::iterator it = begin; it != end; ++it) + for (GLVolumePtrs::iterator it = begin; it != end; ++it) { GLVolume* volume = *it; @@ -4872,8 +6288,8 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe void GLCanvas3D::_update_toolpath_volumes_outside_state() { // tolerance to avoid false detection at bed edges - static const coordf_t tolerance_x = 0.05; - static const coordf_t tolerance_y = 0.05; + static const double tolerance_x = 0.05; + static const double tolerance_y = 0.05; BoundingBoxf3 print_volume; if (m_config != nullptr) @@ -4882,9 +6298,9 @@ void GLCanvas3D::_update_toolpath_volumes_outside_state() if (opt != nullptr) { BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); - print_volume = BoundingBoxf3(Pointf3(unscale(bed_box_2D.min.x) - tolerance_x, unscale(bed_box_2D.min.y) - tolerance_y, 0.0), Pointf3(unscale(bed_box_2D.max.x) + tolerance_x, unscale(bed_box_2D.max.y) + tolerance_y, m_config->opt_float("max_print_height"))); + print_volume = BoundingBoxf3(Vec3d(unscale(bed_box_2D.min(0)) - tolerance_x, unscale(bed_box_2D.min(1)) - tolerance_y, 0.0), Vec3d(unscale(bed_box_2D.max(0)) + tolerance_x, unscale(bed_box_2D.max(1)) + tolerance_y, m_config->opt_float("max_print_height"))); // Allow the objects to protrude below the print bed - print_volume.min.z = -1e10; + print_volume.min(2) = -1e10; } } @@ -4908,60 +6324,229 @@ void GLCanvas3D::_show_warning_texture_if_needed() } } -void GLCanvas3D::_on_move(const std::vector& volume_idxs) +void GLCanvas3D::_on_move() { if (m_model == nullptr) return; - std::set done; // prevent moving instances twice + std::set> done; // keeps track of modified instances bool object_moved = false; - Pointf3 wipe_tower_origin(0.0, 0.0, 0.0); - for (int volume_idx : volume_idxs) + Vec3d wipe_tower_origin = Vec3d::Zero(); + + Selection::EMode selection_mode = m_selection.get_mode(); + + for (const GLVolume* v : m_volumes.volumes) { - GLVolume* volume = m_volumes.volumes[volume_idx]; - int obj_idx = volume->object_idx(); - int instance_idx = volume->instance_idx(); + int object_idx = v->object_idx(); + int instance_idx = v->instance_idx(); + int volume_idx = v->volume_idx(); - // prevent moving instances twice - char done_id[64]; - ::sprintf(done_id, "%d_%d", obj_idx, instance_idx); - if (done.find(done_id) != done.end()) - continue; + std::pair done_id(object_idx, instance_idx); - done.insert(done_id); - - if (obj_idx < 1000) + if ((0 <= object_idx) && (object_idx < (int)m_model->objects.size())) { - // Move a regular object. - ModelObject* model_object = m_model->objects[obj_idx]; - const Pointf3& origin = volume->get_origin(); - model_object->instances[instance_idx]->offset = Pointf(origin.x, origin.y); - model_object->invalidate_bounding_box(); - object_moved = true; + done.insert(done_id); + + // Move instances/volumes + ModelObject* model_object = m_model->objects[object_idx]; + if (model_object != nullptr) + { +#if ENABLE_MODELVOLUME_TRANSFORM + if (selection_mode == Selection::Instance) + { + model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); + object_moved = true; + } + else if (selection_mode == Selection::Volume) + { + model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); + object_moved = true; + } + if (object_moved) +#else + model_object->instances[instance_idx]->set_offset(v->get_offset()); + object_moved = true; +#endif // ENABLE_MODELVOLUME_TRANSFORM + model_object->invalidate_bounding_box(); + } } - else if (obj_idx == 1000) + else if (object_idx == 1000) // Move a wipe tower proxy. - wipe_tower_origin = volume->get_origin(); +#if ENABLE_MODELVOLUME_TRANSFORM + wipe_tower_origin = v->get_volume_offset(); +#else + wipe_tower_origin = v->get_offset(); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + + // Fixes sinking/flying instances + for (const std::pair& i : done) + { + ModelObject* m = m_model->objects[i.first]; + Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); + m_selection.translate(i.first, i.second, shift); + m->translate_instance(i.second, shift); } if (object_moved) - m_on_instance_moved_callback.call(); + post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED)); - if (wipe_tower_origin != Pointf3(0.0, 0.0, 0.0)) - m_on_wipe_tower_moved_callback.call(wipe_tower_origin.x, wipe_tower_origin.y); + if (wipe_tower_origin != Vec3d::Zero()) + post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); } -void GLCanvas3D::_on_select(int volume_idx) +void GLCanvas3D::_on_rotate() { - int id = -1; - if ((volume_idx != -1) && (volume_idx < (int)m_volumes.volumes.size())) + if (m_model == nullptr) + return; + + std::set> done; // keeps track of modified instances + + Selection::EMode selection_mode = m_selection.get_mode(); + + for (const GLVolume* v : m_volumes.volumes) { - if (m_select_by == "volume") - id = m_volumes.volumes[volume_idx]->volume_idx(); - else if (m_select_by == "object") - id = m_volumes.volumes[volume_idx]->object_idx(); + int object_idx = v->object_idx(); + if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) + continue; + + int instance_idx = v->instance_idx(); + int volume_idx = v->volume_idx(); + + done.insert(std::pair(object_idx, instance_idx)); + + // Rotate instances/volumes. + ModelObject* model_object = m_model->objects[object_idx]; + if (model_object != nullptr) + { +#if ENABLE_MODELVOLUME_TRANSFORM + if (selection_mode == Selection::Instance) + { + model_object->instances[instance_idx]->set_rotation(v->get_instance_rotation()); + model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); + } + else if (selection_mode == Selection::Volume) + { + model_object->volumes[volume_idx]->set_rotation(v->get_volume_rotation()); + model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); + } +#else + model_object->instances[instance_idx]->set_rotation(v->get_rotation()); + model_object->instances[instance_idx]->set_offset(v->get_offset()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + model_object->invalidate_bounding_box(); + } } - m_on_select_object_callback.call(id); + + // Fixes sinking/flying instances + for (const std::pair& i : done) + { + ModelObject* m = m_model->objects[i.first]; + Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); + m_selection.translate(i.first, i.second, shift); + m->translate_instance(i.second, shift); + } + + post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + +void GLCanvas3D::_on_scale() +{ + if (m_model == nullptr) + return; + + std::set> done; // keeps track of modified instances + + Selection::EMode selection_mode = m_selection.get_mode(); + + for (const GLVolume* v : m_volumes.volumes) + { + int object_idx = v->object_idx(); + if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) + continue; + + int instance_idx = v->instance_idx(); + int volume_idx = v->volume_idx(); + + done.insert(std::pair(object_idx, instance_idx)); + + // Rotate instances/volumes + ModelObject* model_object = m_model->objects[object_idx]; + if (model_object != nullptr) + { +#if ENABLE_MODELVOLUME_TRANSFORM + if (selection_mode == Selection::Instance) + { + model_object->instances[instance_idx]->set_scaling_factor(v->get_instance_scaling_factor()); + model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); + } + else if (selection_mode == Selection::Volume) + { + model_object->volumes[volume_idx]->set_scaling_factor(v->get_volume_scaling_factor()); + model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); + } +#else + model_object->instances[instance_idx]->set_scaling_factor(v->get_scaling_factor()); + model_object->instances[instance_idx]->set_offset(v->get_offset()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + model_object->invalidate_bounding_box(); + } + } + + // Fixes sinking/flying instances + for (const std::pair& i : done) + { + ModelObject* m = m_model->objects[i.first]; + Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); + m_selection.translate(i.first, i.second, shift); + m->translate_instance(i.second, shift); + } + + post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + +void GLCanvas3D::_on_flatten() +{ + _on_rotate(); +} + +void GLCanvas3D::_on_mirror() +{ + if (m_model == nullptr) + return; + + std::set> done; // keeps track of modified instances + + Selection::EMode selection_mode = m_selection.get_mode(); + + for (const GLVolume* v : m_volumes.volumes) + { + int object_idx = v->object_idx(); + if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) + continue; + + int instance_idx = v->instance_idx(); + int volume_idx = v->volume_idx(); + + done.insert(std::pair(object_idx, instance_idx)); + + // Mirror instances/volumes + ModelObject* model_object = m_model->objects[object_idx]; + if (model_object != nullptr) + { +#if ENABLE_MODELVOLUME_TRANSFORM + if (selection_mode == Selection::Instance) + model_object->instances[instance_idx]->set_mirror(v->get_instance_mirror()); + else if (selection_mode == Selection::Volume) + model_object->volumes[volume_idx]->set_mirror(v->get_volume_mirror()); +#else + model_object->instances[instance_idx]->set_mirror(v->get_mirror()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + model_object->invalidate_bounding_box(); + } + } + + post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } std::vector GLCanvas3D::_parse_colors(const std::vector& colors) @@ -4991,24 +6576,30 @@ std::vector GLCanvas3D::_parse_colors(const std::vector& col void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors) { +#if !ENABLE_USE_UNIQUE_GLCONTEXT if (!set_current()) return; +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT m_legend_texture.generate(preview_data, tool_colors); } void GLCanvas3D::_generate_warning_texture(const std::string& msg) { +#if !ENABLE_USE_UNIQUE_GLCONTEXT if (!set_current()) return; +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT m_warning_texture.generate(msg); } void GLCanvas3D::_reset_warning_texture() { +#if !ENABLE_USE_UNIQUE_GLCONTEXT if (!set_current()) return; +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT m_warning_texture.reset(); } @@ -5024,5 +6615,36 @@ bool GLCanvas3D::_is_any_volume_outside() const return false; } +void GLCanvas3D::_resize_toolbar() const +{ + Size cnv_size = get_canvas_size(); + float zoom = get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + switch (m_toolbar.get_layout_type()) + { + default: + case GLToolbar::Layout::Horizontal: + { + // centers the toolbar on the top edge of the 3d scene + unsigned int toolbar_width = m_toolbar.get_width(); + float top = (0.5f * (float)cnv_size.get_height() - 2.0f) * inv_zoom; + float left = -0.5f * (float)toolbar_width * inv_zoom; + m_toolbar.set_position(top, left); + break; + } + case GLToolbar::Layout::Vertical: + { + // centers the toolbar on the right edge of the 3d scene + unsigned int toolbar_width = m_toolbar.get_width(); + unsigned int toolbar_height = m_toolbar.get_height(); + float top = 0.5f * (float)toolbar_height * inv_zoom; + float left = (0.5f * (float)cnv_size.get_width() - toolbar_width - 2.0f) * inv_zoom; + m_toolbar.set_position(top, left); + break; + } + } +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp similarity index 53% rename from xs/src/slic3r/GUI/GLCanvas3D.hpp rename to src/slic3r/GUI/GLCanvas3D.hpp index f09bd4b209..66747cf1e7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1,21 +1,29 @@ #ifndef slic3r_GLCanvas3D_hpp_ #define slic3r_GLCanvas3D_hpp_ -#include "../../slic3r/GUI/3DScene.hpp" -#include "../../slic3r/GUI/GLTexture.hpp" +#include -class wxTimer; +#include "3DScene.hpp" +#include "GLToolbar.hpp" +#include "Event.hpp" + +#include + +class wxWindow; class wxSizeEvent; class wxIdleEvent; class wxKeyEvent; class wxMouseEvent; class wxTimerEvent; class wxPaintEvent; +class wxGLCanvas; + namespace Slic3r { class GLShader; class ExPolygon; +class SLAPrint; namespace GUI { @@ -76,6 +84,26 @@ public: void set_bottom(float bottom); }; +wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); + +using Vec2dEvent = Event; +template using Vec2dsEvent = ArrayEvent; + +using Vec3dEvent = Event; +template using Vec3dsEvent = ArrayEvent; + +wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); // data: +1 => increase, -1 => decrease +wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); +wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); + class GLCanvas3D { struct GCodePreviewVolumeIndex @@ -105,7 +133,6 @@ class GLCanvas3D void reset() { first_volumes.clear(); } }; -public: struct Camera { enum EType : unsigned char @@ -120,7 +147,7 @@ public: float zoom; float phi; // float distance; - Pointf3 target; + Vec3d target; private: float m_theta; @@ -185,7 +212,7 @@ public: struct Axes { - Pointf3 origin; + Vec3d origin; float length; Axes(); @@ -193,23 +220,6 @@ public: void render(bool depth_test) const; }; - class CuttingPlane - { - float m_z; - GeometryBuffer m_lines; - - public: - CuttingPlane(); - - bool set(float z, const ExPolygons& polygons); - - void render(const BoundingBoxf3& bb) const; - - private: - void _render_plane(const BoundingBoxf3& bb) const; - void _render_contour() const; - }; - class Shader { GLShader* m_shader; @@ -299,23 +309,25 @@ public: struct Drag { static const Point Invalid_2D_Point; - static const Pointf3 Invalid_3D_Point; + static const Vec3d Invalid_3D_Point; Point start_position_2D; - Pointf3 start_position_3D; - Vectorf3 volume_center_offset; - - bool move_with_shift; + Vec3d start_position_3D; int move_volume_idx; - int gizmo_volume_idx; public: Drag(); }; bool dragging; - Pointf position; + Vec2d position; +#if ENABLE_GIZMOS_ON_TOP + Vec3d scene_position; +#endif // ENABLE_GIZMOS_ON_TOP Drag drag; +#if ENABLE_GIZMOS_RESET + bool ignore_up_event; +#endif // ENABLE_GIZMOS_RESET Mouse(); @@ -326,6 +338,219 @@ public: bool is_start_position_3D_defined() const; }; +public: + class Selection + { + public: + typedef std::set IndicesList; + + enum EMode : unsigned char + { +#if ENABLE_MODELVOLUME_TRANSFORM + Volume, + Instance +#else + Volume, + Instance, + Object +#endif // ENABLE_MODELVOLUME_TRANSFORM + }; + + enum EType : unsigned char + { + Invalid, + Empty, + WipeTower, + SingleModifier, + MultipleModifier, + SingleVolume, + MultipleVolume, + SingleFullObject, + MultipleFullObject, + SingleFullInstance, + MultipleFullInstance, + Mixed + }; + + private: + struct VolumeCache + { + private: +#if ENABLE_MODELVOLUME_TRANSFORM + struct TransformCache + { + Vec3d position; + Vec3d rotation; + Vec3d scaling_factor; + Transform3d rotation_matrix; + Transform3d scale_matrix; + + TransformCache(); + explicit TransformCache(const Geometry::Transformation& transform); + }; + + TransformCache m_volume; + TransformCache m_instance; +#else + Vec3d m_position; + Vec3d m_rotation; + Vec3d m_scaling_factor; + Transform3d m_rotation_matrix; + Transform3d m_scale_matrix; +#endif // ENABLE_MODELVOLUME_TRANSFORM + + public: +#if ENABLE_MODELVOLUME_TRANSFORM + VolumeCache() {} + VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform); +#else + VolumeCache(); + VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor); +#endif // ENABLE_MODELVOLUME_TRANSFORM + +#if ENABLE_MODELVOLUME_TRANSFORM + const Vec3d& get_volume_position() const { return m_volume.position; } + const Vec3d& get_volume_rotation() const { return m_volume.rotation; } + const Vec3d& get_volume_scaling_factor() const { return m_volume.scaling_factor; } + const Transform3d& get_volume_rotation_matrix() const { return m_volume.rotation_matrix; } + const Transform3d& get_volume_scale_matrix() const { return m_volume.scale_matrix; } + + const Vec3d& get_instance_position() const { return m_instance.position; } + const Vec3d& get_instance_rotation() const { return m_instance.rotation; } + const Vec3d& get_instance_scaling_factor() const { return m_instance.scaling_factor; } + const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; } + const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; } +#else + const Vec3d& get_position() const { return m_position; } + const Vec3d& get_rotation() const { return m_rotation; } + const Vec3d& get_scaling_factor() const { return m_scaling_factor; } + const Transform3d& get_rotation_matrix() const { return m_rotation_matrix; } + const Transform3d& get_scale_matrix() const { return m_scale_matrix; } +#endif // ENABLE_MODELVOLUME_TRANSFORM + }; + + typedef std::map VolumesCache; + typedef std::set InstanceIdxsList; + typedef std::map ObjectIdxsToInstanceIdxsMap; + + struct Cache + { + // Cache of GLVolume derived transformation matrices, valid during mouse dragging. + VolumesCache volumes_data; + // Center of the dragged selection, valid during mouse dragging. + Vec3d dragging_center; + // Map from indices of ModelObject instances in Model::objects + // to a set of indices of ModelVolume instances in ModelObject::instances + // Here the index means a position inside the respective std::vector, not ModelID. + ObjectIdxsToInstanceIdxsMap content; + }; + + // Volumes owned by GLCanvas3D. + GLVolumePtrs* m_volumes; + // Model, not owned. + Model* m_model; + + bool m_valid; + EMode m_mode; + EType m_type; + // set of indices to m_volumes + IndicesList m_list; + Cache m_cache; + mutable BoundingBoxf3 m_bounding_box; + mutable bool m_bounding_box_dirty; + + public: + Selection(); + + void set_volumes(GLVolumePtrs* volumes); + + Model* get_model() const { return m_model; } + void set_model(Model* model); + + EMode get_mode() const { return m_mode; } + void set_mode(EMode mode) { m_mode = mode; } + + void add(unsigned int volume_idx, bool as_single_selection = true); + void remove(unsigned int volume_idx); + + void add_object(unsigned int object_idx, bool as_single_selection = true); + void remove_object(unsigned int object_idx); + + void add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection = true); + void remove_instance(unsigned int object_idx, unsigned int instance_idx); + + void add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection = true); + void remove_volume(unsigned int object_idx, unsigned int volume_idx); + + void clear(); + + bool is_empty() const { return m_type == Empty; } + bool is_wipe_tower() const { return m_type == WipeTower; } + bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); } + bool is_single_modifier() const { return m_type == SingleModifier; } + bool is_single_full_instance() const; + bool is_multiple_full_instance() const { return m_type == MultipleFullInstance; } + bool is_single_full_object() const { return m_type == SingleFullObject; } + bool is_multiple_full_object() const { return m_type == MultipleFullObject; } + bool is_single_volume() const { return m_type == SingleVolume; } + bool is_multiple_volume() const { return m_type == MultipleVolume; } + bool is_mixed() const { return m_type == Mixed; } + bool is_from_single_instance() const { return get_instance_idx() != -1; } + bool is_from_single_object() const { return get_object_idx() != -1; } + + bool contains_volume(unsigned int volume_idx) const { return std::find(m_list.begin(), m_list.end(), volume_idx) != m_list.end(); } + + // Returns the the object id if the selection is from a single object, otherwise is -1 + int get_object_idx() const; + // Returns the instance id if the selection is from a single object and from a single instance, otherwise is -1 + int get_instance_idx() const; + // Returns the indices of selected instances. + // Can only be called if selection is from a single object. + const InstanceIdxsList& get_instance_idxs() const; + + const IndicesList& get_volume_idxs() const { return m_list; } + const GLVolume* get_volume(unsigned int volume_idx) const; + + unsigned int volumes_count() const { return (unsigned int)m_list.size(); } + const BoundingBoxf3& get_bounding_box() const; + + void start_dragging(); + + void translate(const Vec3d& displacement); + void rotate(const Vec3d& rotation, bool local); + void flattening_rotate(const Vec3d& normal); + void scale(const Vec3d& scale); + void mirror(Axis axis); + + void translate(unsigned int object_idx, const Vec3d& displacement); + void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement); + + void erase(); + + void render() const; + + private: + void _update_valid(); + void _update_type(); + void _set_caches(); + void _add_volume(unsigned int volume_idx); + void _add_instance(unsigned int object_idx, unsigned int instance_idx); + void _add_object(unsigned int object_idx); + void _remove_volume(unsigned int volume_idx); + void _remove_instance(unsigned int object_idx, unsigned int instance_idx); + void _remove_object(unsigned int object_idx); + void _calc_bounding_box() const; + void _render_selected_volumes() const; + void _render_synchronized_volumes() const; + void _render_bounding_box(const BoundingBoxf3& box, float* color) const; + void _synchronize_unselected_instances(); + void _synchronize_unselected_volumes(); +#if ENABLE_ENSURE_ON_BED_WHILE_SCALING + void _ensure_on_bed(); +#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING + }; + +private: class Gizmos { static const float OverlayTexturesScale; @@ -336,68 +561,81 @@ public: enum EType : unsigned char { Undefined, + Move, Scale, Rotate, Flatten, + Cut, + SlaSupports, Num_Types }; - enum RenderOrder : unsigned char { - BeforeBed, - AfterBed - }; private: bool m_enabled; typedef std::map GizmosMap; GizmosMap m_gizmos; EType m_current; - bool m_dragging; public: Gizmos(); ~Gizmos(); - bool init(); + bool init(GLCanvas3D& parent); bool is_enabled() const; void set_enabled(bool enable); - void update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos); - void update_on_off_state(const GLCanvas3D& canvas, const Pointf& mouse_pos); + std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); + void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); + void update_on_off_state(const Selection& selection); void reset_all_states(); void set_hover_id(int id); + void enable_grabber(EType type, unsigned int id, bool enable); - bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const; + bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; bool grabber_contains_mouse() const; - void update(const Pointf& mouse_pos); - void refresh(); - + void update(const Linef3& mouse_ray, bool shift_down, const Point* mouse_pos = nullptr); +#if ENABLE_GIZMOS_RESET + void process_double_click(); +#endif // ENABLE_GIZMOS_RESET + Rect get_reset_rect_viewport(const GLCanvas3D& canvas) const; EType get_current_type() const; bool is_running() const; bool is_dragging() const; - void start_dragging(); + void start_dragging(const Selection& selection); void stop_dragging(); - float get_scale() const; - void set_scale(float scale); + Vec3d get_displacement() const; - float get_angle_z() const; - void set_angle_z(float angle_z); + Vec3d get_scale() const; + void set_scale(const Vec3d& scale); + + Vec3d get_rotation() const; + void set_rotation(const Vec3d& rotation); + + Vec3d get_flattening_normal() const; void set_flattening_data(const ModelObject* model_object); - Pointf3 get_flattening_normal() const; + + void set_model_object_ptr(ModelObject* model_object); + void clicked_on_object(const Vec2d& mouse_position); + void delete_current_grabber(bool delete_all = false); - void render(const GLCanvas3D& canvas, const BoundingBoxf3& box, RenderOrder render_order) const; - void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const; + void render_current_gizmo(const Selection& selection) const; + void render_current_gizmo_for_picking_pass(const Selection& selection) const; + + void render_overlay(const GLCanvas3D& canvas) const; + + void create_external_gizmo_widgets(wxWindow *parent); private: void _reset(); void _render_overlay(const GLCanvas3D& canvas) const; - void _render_current_gizmo(const BoundingBoxf3& box) const; + void _render_current_gizmo(const Selection& selection) const; float _get_total_overlay_height() const; GLGizmoBase* _get_current() const; @@ -408,8 +646,15 @@ public: static const unsigned char Background_Color[3]; static const unsigned char Opacity; + int m_original_width; + int m_original_height; + public: + WarningTexture(); + bool generate(const std::string& msg); + + void render(const GLCanvas3D& canvas) const; }; class LegendTexture : public GUI::GLTexture @@ -423,28 +668,36 @@ public: static const unsigned char Background_Color[3]; static const unsigned char Opacity; + int m_original_width; + int m_original_height; + public: + LegendTexture(); + bool generate(const GCodePreviewData& preview_data, const std::vector& tool_colors); + + void render(const GLCanvas3D& canvas) const; }; -private: wxGLCanvas* m_canvas; wxGLContext* m_context; LegendTexture m_legend_texture; WarningTexture m_warning_texture; - wxTimer* m_timer; + wxTimer m_timer; Camera m_camera; Bed m_bed; Axes m_axes; - CuttingPlane m_cutting_plane; LayersEditing m_layers_editing; Shader m_shader; Mouse m_mouse; mutable Gizmos m_gizmos; + mutable GLToolbar m_toolbar; mutable GLVolumeCollection m_volumes; + Selection m_selection; DynamicPrintConfig* m_config; Print* m_print; + SLAPrint* m_sla_print; Model* m_model; bool m_dirty; @@ -453,6 +706,7 @@ private: bool m_force_zoom_to_bed_enabled; bool m_apply_zoom_to_volumes_filter; mutable int m_hover_volume_id; + bool m_toolbar_action_running; bool m_warning_texture_enabled; bool m_legend_texture_enabled; bool m_picking_enabled; @@ -460,76 +714,58 @@ private: bool m_shader_enabled; bool m_dynamic_background_enabled; bool m_multisample_allowed; + bool m_regenerate_volumes; std::string m_color_by; - std::string m_select_by; - std::string m_drag_by; bool m_reload_delayed; - std::vector> m_objects_volumes_idxs; - std::vector m_objects_selections; GCodePreviewVolumeIndex m_gcode_preview_volume_index; - PerlCallback m_on_viewport_changed_callback; - PerlCallback m_on_double_click_callback; - PerlCallback m_on_right_click_callback; - PerlCallback m_on_select_object_callback; - PerlCallback m_on_model_update_callback; - PerlCallback m_on_remove_object_callback; - PerlCallback m_on_arrange_callback; - PerlCallback m_on_rotate_object_left_callback; - PerlCallback m_on_rotate_object_right_callback; - PerlCallback m_on_scale_object_uniformly_callback; - PerlCallback m_on_increase_objects_callback; - PerlCallback m_on_decrease_objects_callback; - PerlCallback m_on_instance_moved_callback; - PerlCallback m_on_wipe_tower_moved_callback; - PerlCallback m_on_enable_action_buttons_callback; - PerlCallback m_on_gizmo_scale_uniformly_callback; - PerlCallback m_on_gizmo_rotate_callback; - PerlCallback m_on_update_geometry_info_callback; + wxWindow *m_external_gizmo_widgets_parent; + + void post_event(wxEvent &&event); + void viewport_changed(); public: GLCanvas3D(wxGLCanvas* canvas); ~GLCanvas3D(); +#if ENABLE_USE_UNIQUE_GLCONTEXT + void set_context(wxGLContext* context) { m_context = context; } +#endif // ENABLE_USE_UNIQUE_GLCONTEXT + + wxGLCanvas* get_wxglcanvas() { return m_canvas; } + bool init(bool useVBOs, bool use_legacy_opengl); +#if !ENABLE_USE_UNIQUE_GLCONTEXT bool set_current(); +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT void set_as_dirty(); unsigned int get_volumes_count() const; void reset_volumes(); - void deselect_volumes(); - void select_volume(unsigned int id); - void update_volumes_selection(const std::vector& selections); int check_volumes_outside_state(const DynamicPrintConfig* config) const; - bool move_volume_up(unsigned int id); - bool move_volume_down(unsigned int id); - - void set_objects_selections(const std::vector& selections); void set_config(DynamicPrintConfig* config); void set_print(Print* print); + void set_SLA_print(SLAPrint* print); void set_model(Model* model); + const Selection& get_selection() const { return m_selection; } + Selection& get_selection() { return m_selection; } + // Set the bed shape to a single closed 2D polygon(array of two element arrays), // triangulate the bed and store the triangles into m_bed.m_triangles, // fills the m_bed.m_grid_lines and sets m_bed.m_origin. // Sets m_bed.m_polygon to limit the object placement. void set_bed_shape(const Pointfs& shape); - // Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane to support the scene objects. - void set_auto_bed_shape(); void set_axes_length(float length); - void set_cutting_plane(float z, const ExPolygons& polygons); - void set_color_by(const std::string& value); - void set_select_by(const std::string& value); - void set_drag_by(const std::string& value); float get_camera_zoom() const; @@ -537,7 +773,6 @@ public: bool is_layers_editing_enabled() const; bool is_layers_editing_allowed() const; - bool is_shader_enabled() const; bool is_reload_delayed() const; @@ -547,51 +782,48 @@ public: void enable_picking(bool enable); void enable_moving(bool enable); void enable_gizmos(bool enable); + void enable_toolbar(bool enable); void enable_shader(bool enable); void enable_force_zoom_to_bed(bool enable); void enable_dynamic_background(bool enable); void allow_multisample(bool allow); + void enable_toolbar_item(const std::string& name, bool enable); + bool is_toolbar_item_pressed(const std::string& name) const; + void zoom_to_bed(); void zoom_to_volumes(); +#if ENABLE_MODIFIED_CAMERA_TARGET + void zoom_to_selection(); +#endif // ENABLE_MODIFIED_CAMERA_TARGET void select_view(const std::string& direction); void set_viewport_from_scene(const GLCanvas3D& other); void update_volumes_colors_by_extruder(); - void update_gizmos_data(); + + Rect get_gizmo_reset_rect(const GLCanvas3D& canvas, bool viewport) const; + bool gizmo_reset_rect_contains(const GLCanvas3D& canvas, float x, float y) const; void render(); + void delete_selected(); + std::vector get_current_print_zs(bool active_only) const; void set_toolpaths_range(double low, double high); std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs); std::vector load_object(const Model& model, int obj_idx); + // Load SLA support tree and SLA pad meshes into the scene, if available at the respective SLAPrintObject instances. + std::vector load_support_meshes(const Model& model, int obj_idx); + + void mirror_selection(Axis axis); + void reload_scene(bool force); void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); void load_preview(const std::vector& str_tool_colors); - void register_on_viewport_changed_callback(void* callback); - void register_on_double_click_callback(void* callback); - void register_on_right_click_callback(void* callback); - void register_on_select_object_callback(void* callback); - void register_on_model_update_callback(void* callback); - void register_on_remove_object_callback(void* callback); - void register_on_arrange_callback(void* callback); - void register_on_rotate_object_left_callback(void* callback); - void register_on_rotate_object_right_callback(void* callback); - void register_on_scale_object_uniformly_callback(void* callback); - void register_on_increase_objects_callback(void* callback); - void register_on_decrease_objects_callback(void* callback); - void register_on_instance_moved_callback(void* callback); - void register_on_wipe_tower_moved_callback(void* callback); - void register_on_enable_action_buttons_callback(void* callback); - void register_on_gizmo_scale_uniformly_callback(void* callback); - void register_on_gizmo_rotate_callback(void* callback); - void register_on_update_geometry_info_callback(void* callback); - void bind_event_handlers(); void unbind_event_handlers(); @@ -609,20 +841,26 @@ public: void reset_legend_texture(); + void set_tooltip(const std::string& tooltip) const; + + void set_external_gizmo_widgets_parent(wxWindow *parent); + private: bool _is_shown_on_screen() const; void _force_zoom_to_bed(); + bool _init_toolbar(); + +#if ENABLE_USE_UNIQUE_GLCONTEXT + bool _set_current(); +#endif // ENABLE_USE_UNIQUE_GLCONTEXT void _resize(unsigned int w, unsigned int h); BoundingBoxf3 _max_bounding_box() const; - BoundingBoxf3 _selected_volumes_bounding_box() const; void _zoom_to_bounding_box(const BoundingBoxf3& bbox); float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; - void _deregister_callbacks(); - void _mark_volumes_for_layer_height() const; void _refresh_if_shown_on_screen(); @@ -632,29 +870,37 @@ private: void _render_bed(float theta) const; void _render_axes(bool depth_test) const; void _render_objects() const; - void _render_cutting_plane() const; + void _render_selection() const; void _render_warning_texture() const; void _render_legend_texture() const; void _render_layer_editing_overlay() const; void _render_volumes(bool fake_colors) const; - void _render_gizmo(Gizmos::RenderOrder render_order) const; + void _render_current_gizmo() const; + void _render_gizmos_overlay() const; + void _render_toolbar() const; +#if ENABLE_SHOW_CAMERA_TARGET + void _render_camera_target() const; +#endif // ENABLE_SHOW_CAMERA_TARGET + + void _update_volumes_hover_state() const; + void _update_gizmos_data(); float _get_layers_editing_cursor_z_relative() const; void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); // Convert the screen space coordinate to an object space coordinate. // If the Z screen space coordinate is not provided, a depth buffer value is substituted. - Pointf3 _mouse_to_3d(const Point& mouse_pos, float* z = nullptr); + Vec3d _mouse_to_3d(const Point& mouse_pos, float* z = nullptr); // Convert the screen space coordinate to world coordinate on the bed. - Pointf3 _mouse_to_bed_3d(const Point& mouse_pos); + Vec3d _mouse_to_bed_3d(const Point& mouse_pos); + + // Returns the view ray line, in world coordinate, at the given mouse position. + Linef3 mouse_ray(const Point& mouse_pos); void _start_timer(); void _stop_timer(); - int _get_first_selected_object_id() const; - int _get_first_selected_volume_id(int object_id) const; - // Create 3D thick extrusion lines for a skirt and brim. // Adds a new Slic3r::GUI::3DScene::Volume to volumes. void _load_print_toolpaths(); @@ -682,10 +928,13 @@ private: void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data); void _update_toolpath_volumes_outside_state(); void _show_warning_texture_if_needed(); - - void _on_move(const std::vector& volume_idxs); - void _on_select(int volume_idx); - +public: + void _on_move(); + void _on_rotate(); + void _on_scale(); + void _on_flatten(); + void _on_mirror(); +private: // generates the legend texture in dependence of the current shown view type void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors); @@ -695,6 +944,8 @@ private: bool _is_any_volume_outside() const; + void _resize_toolbar() const; + static std::vector _parse_colors(const std::vector& colors); }; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp similarity index 67% rename from xs/src/slic3r/GUI/GLCanvas3DManager.cpp rename to src/slic3r/GUI/GLCanvas3DManager.cpp index 5249f6dc4f..abf7834605 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -110,14 +110,32 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten return out.str(); } +GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown; + GLCanvas3DManager::GLCanvas3DManager() +#if ENABLE_USE_UNIQUE_GLCONTEXT + : m_context(nullptr) + , m_current(nullptr) +#else : m_current(nullptr) +#endif // ENABLE_USE_UNIQUE_GLCONTEXT , m_gl_initialized(false) , m_use_legacy_opengl(false) , m_use_VBOs(false) { } +#if ENABLE_USE_UNIQUE_GLCONTEXT +GLCanvas3DManager::~GLCanvas3DManager() +{ + if (m_context != nullptr) + { + delete m_context; + m_context = nullptr; + } +} +#endif // ENABLE_USE_UNIQUE_GLCONTEXT + bool GLCanvas3DManager::add(wxGLCanvas* canvas) { if (canvas == nullptr) @@ -131,6 +149,18 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas) return false; canvas3D->bind_event_handlers(); + +#if ENABLE_USE_UNIQUE_GLCONTEXT + if (m_context == nullptr) + { + m_context = new wxGLContext(canvas); + if (m_context == nullptr) + return false; + } + + canvas3D->set_context(m_context); +#endif // ENABLE_USE_UNIQUE_GLCONTEXT + m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); return true; @@ -216,50 +246,16 @@ void GLCanvas3DManager::reset_volumes(wxGLCanvas* canvas) it->second->reset_volumes(); } -void GLCanvas3DManager::deselect_volumes(wxGLCanvas* canvas) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->deselect_volumes(); -} - -void GLCanvas3DManager::select_volume(wxGLCanvas* canvas, unsigned int id) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->select_volume(id); -} - -void GLCanvas3DManager::update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->update_volumes_selection(selections); -} - int GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const { CanvasesMap::const_iterator it = _get_canvas(canvas); return (it != m_canvases.end()) ? it->second->check_volumes_outside_state(config) : false; } -bool GLCanvas3DManager::move_volume_up(wxGLCanvas* canvas, unsigned int id) +GLCanvas3D* GLCanvas3DManager::get_canvas(wxGLCanvas* canvas) { CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->move_volume_up(id) : false; -} - -bool GLCanvas3DManager::move_volume_down(wxGLCanvas* canvas, unsigned int id) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->move_volume_down(id) : false; -} - -void GLCanvas3DManager::set_objects_selections(wxGLCanvas* canvas, const std::vector& selections) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_objects_selections(selections); + return (it != m_canvases.end()) ? it->second : nullptr; } void GLCanvas3DManager::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) @@ -276,6 +272,13 @@ void GLCanvas3DManager::set_print(wxGLCanvas* canvas, Print* print) it->second->set_print(print); } +void GLCanvas3DManager::set_SLA_print(wxGLCanvas* canvas, SLAPrint* print) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_SLA_print(print); +} + void GLCanvas3DManager::set_model(wxGLCanvas* canvas, Model* model) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -290,33 +293,6 @@ void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) it->second->set_bed_shape(shape); } -void GLCanvas3DManager::set_auto_bed_shape(wxGLCanvas* canvas) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_auto_bed_shape(); -} - -BoundingBoxf3 GLCanvas3DManager::get_volumes_bounding_box(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->volumes_bounding_box() : BoundingBoxf3(); -} - -void GLCanvas3DManager::set_axes_length(wxGLCanvas* canvas, float length) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_axes_length(length); -} - -void GLCanvas3DManager::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_cutting_plane(z, polygons); -} - void GLCanvas3DManager::set_color_by(wxGLCanvas* canvas, const std::string& value) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -324,20 +300,6 @@ void GLCanvas3DManager::set_color_by(wxGLCanvas* canvas, const std::string& valu it->second->set_color_by(value); } -void GLCanvas3DManager::set_select_by(wxGLCanvas* canvas, const std::string& value) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_select_by(value); -} - -void GLCanvas3DManager::set_drag_by(wxGLCanvas* canvas, const std::string& value) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_drag_by(value); -} - bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -350,12 +312,6 @@ bool GLCanvas3DManager::is_layers_editing_allowed(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_layers_editing_allowed() : false; } -bool GLCanvas3DManager::is_shader_enabled(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_shader_enabled() : false; -} - bool GLCanvas3DManager::is_reload_delayed(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -404,6 +360,13 @@ void GLCanvas3DManager::enable_gizmos(wxGLCanvas* canvas, bool enable) it->second->enable_gizmos(enable); } +void GLCanvas3DManager::enable_toolbar(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_toolbar(enable); +} + void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -432,6 +395,19 @@ void GLCanvas3DManager::allow_multisample(wxGLCanvas* canvas, bool allow) it->second->allow_multisample(allow); } +void GLCanvas3DManager::enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_toolbar_item(name, enable); +} + +bool GLCanvas3DManager::is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_toolbar_item_pressed(name) : false; +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -471,13 +447,6 @@ void GLCanvas3DManager::update_volumes_colors_by_extruder(wxGLCanvas* canvas) it->second->update_volumes_colors_by_extruder(); } -void GLCanvas3DManager::update_gizmos_data(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->update_gizmos_data(); -} - void GLCanvas3DManager::render(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -485,6 +454,13 @@ void GLCanvas3DManager::render(wxGLCanvas* canvas) const it->second->render(); } +void GLCanvas3DManager::delete_selected(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->delete_selected(); +} + std::vector GLCanvas3DManager::get_current_print_zs(wxGLCanvas* canvas, bool active_only) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -516,6 +492,13 @@ std::vector GLCanvas3DManager::load_object(wxGLCanvas* canvas, const Model* return (it != m_canvases.end()) ? it->second->load_object(*model, obj_idx) : std::vector(); } +void GLCanvas3DManager::mirror_selection(wxGLCanvas* canvas, Axis axis) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->mirror_selection(axis); +} + void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -540,139 +523,29 @@ void GLCanvas3DManager::load_preview(wxGLCanvas* canvas, const std::vectorsecond->load_preview(str_tool_colors); } -void GLCanvas3DManager::reset_legend_texture() +void GLCanvas3DManager::reset_legend_texture(wxGLCanvas* canvas) { - for (CanvasesMap::value_type& canvas : m_canvases) + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->reset_legend_texture(); +} + +wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent) +{ + int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0 }; + + if (s_multisample == MS_Unknown) { - if (canvas.second != nullptr) - canvas.second->reset_legend_texture(); + _detect_multisample(attribList); + // debug output + std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl; } -} -void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_viewport_changed_callback(callback); -} + if (! can_multisample()) { + attribList[4] = 0; + } -void GLCanvas3DManager::register_on_double_click_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_double_click_callback(callback); -} - -void GLCanvas3DManager::register_on_right_click_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_right_click_callback(callback); -} - -void GLCanvas3DManager::register_on_select_object_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_select_object_callback(callback); -} - -void GLCanvas3DManager::register_on_model_update_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_model_update_callback(callback); -} - -void GLCanvas3DManager::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_remove_object_callback(callback); -} - -void GLCanvas3DManager::register_on_arrange_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_arrange_callback(callback); -} - -void GLCanvas3DManager::register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_rotate_object_left_callback(callback); -} - -void GLCanvas3DManager::register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_rotate_object_right_callback(callback); -} - -void GLCanvas3DManager::register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_scale_object_uniformly_callback(callback); -} - -void GLCanvas3DManager::register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_increase_objects_callback(callback); -} - -void GLCanvas3DManager::register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_decrease_objects_callback(callback); -} - -void GLCanvas3DManager::register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_instance_moved_callback(callback); -} - -void GLCanvas3DManager::register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_wipe_tower_moved_callback(callback); -} - -void GLCanvas3DManager::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_enable_action_buttons_callback(callback); -} - -void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_gizmo_scale_uniformly_callback(callback); -} - -void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_gizmo_rotate_callback(callback); -} - -void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_update_geometry_info_callback(callback); + return new wxGLCanvas(parent, wxID_ANY, attribList); } GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) @@ -693,5 +566,18 @@ bool GLCanvas3DManager::_init(GLCanvas3D& canvas) return canvas.init(m_use_VBOs, m_use_legacy_opengl); } +void GLCanvas3DManager::_detect_multisample(int* attribList) +{ + int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER; + const AppConfig* app_config = GUI::get_app_config(); + bool enable_multisample = app_config != nullptr + && app_config->get("use_legacy_opengl") != "1" + && wxVersion >= 30003; + + s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? MS_Enabled : MS_Disabled; + // Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows + // s_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample"); +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp similarity index 62% rename from xs/src/slic3r/GUI/GLCanvas3DManager.hpp rename to src/slic3r/GUI/GLCanvas3DManager.hpp index 55c42fda69..097d3a118f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -6,6 +6,7 @@ #include #include +class wxWindow; class wxGLCanvas; class wxGLContext; @@ -13,13 +14,14 @@ namespace Slic3r { class DynamicPrintConfig; class Print; +class SLAPrint; class Model; class ExPolygon; typedef std::vector ExPolygons; class ModelObject; class PrintObject; class GCodePreviewData; - + namespace GUI { class GLCanvas3D; @@ -41,17 +43,31 @@ class GLCanvas3DManager std::string to_string(bool format_as_html, bool extensions) const; }; + enum EMultisampleState : unsigned char + { + MS_Unknown, + MS_Enabled, + MS_Disabled + }; + typedef std::map CanvasesMap; CanvasesMap m_canvases; +#if ENABLE_USE_UNIQUE_GLCONTEXT + wxGLContext* m_context; +#endif // ENABLE_USE_UNIQUE_GLCONTEXT wxGLCanvas* m_current; GLInfo m_gl_info; bool m_gl_initialized; bool m_use_legacy_opengl; bool m_use_VBOs; + static EMultisampleState s_multisample; public: GLCanvas3DManager(); +#if ENABLE_USE_UNIQUE_GLCONTEXT + ~GLCanvas3DManager(); +#endif // ENABLE_USE_UNIQUE_GLCONTEXT bool add(wxGLCanvas* canvas); bool remove(wxGLCanvas* canvas); @@ -72,35 +88,21 @@ public: unsigned int get_volumes_count(wxGLCanvas* canvas) const; void reset_volumes(wxGLCanvas* canvas); - void deselect_volumes(wxGLCanvas* canvas); - void select_volume(wxGLCanvas* canvas, unsigned int id); - void update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections); int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const; - bool move_volume_up(wxGLCanvas* canvas, unsigned int id); - bool move_volume_down(wxGLCanvas* canvas, unsigned int id); - void set_objects_selections(wxGLCanvas* canvas, const std::vector& selections); + GLCanvas3D* get_canvas(wxGLCanvas* canvas); void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); void set_print(wxGLCanvas* canvas, Print* print); + void set_SLA_print(wxGLCanvas* canvas, SLAPrint* print); void set_model(wxGLCanvas* canvas, Model* model); void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); - void set_auto_bed_shape(wxGLCanvas* canvas); - - BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); - - void set_axes_length(wxGLCanvas* canvas, float length); - - void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); void set_color_by(wxGLCanvas* canvas, const std::string& value); - void set_select_by(wxGLCanvas* canvas, const std::string& value); - void set_drag_by(wxGLCanvas* canvas, const std::string& value); bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_layers_editing_allowed(wxGLCanvas* canvas) const; - bool is_shader_enabled(wxGLCanvas* canvas) const; bool is_reload_delayed(wxGLCanvas* canvas) const; @@ -110,58 +112,50 @@ public: void enable_picking(wxGLCanvas* canvas, bool enable); void enable_moving(wxGLCanvas* canvas, bool enable); void enable_gizmos(wxGLCanvas* canvas, bool enable); + void enable_toolbar(wxGLCanvas* canvas, bool enable); void enable_shader(wxGLCanvas* canvas, bool enable); void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); void enable_dynamic_background(wxGLCanvas* canvas, bool enable); void allow_multisample(wxGLCanvas* canvas, bool allow); + void enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable); + bool is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name) const; + void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other); void update_volumes_colors_by_extruder(wxGLCanvas* canvas); - void update_gizmos_data(wxGLCanvas* canvas); void render(wxGLCanvas* canvas) const; + void delete_selected(wxGLCanvas* canvas); + std::vector get_current_print_zs(wxGLCanvas* canvas, bool active_only) const; void set_toolpaths_range(wxGLCanvas* canvas, double low, double high); std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); + void mirror_selection(wxGLCanvas* canvas, Axis axis); + void reload_scene(wxGLCanvas* canvas, bool force); void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); void load_preview(wxGLCanvas* canvas, const std::vector& str_tool_colors); - void reset_legend_texture(); + void reset_legend_texture(wxGLCanvas* canvas); - void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); - void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); - void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); - void register_on_select_object_callback(wxGLCanvas* canvas, void* callback); - void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); - void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback); - void register_on_arrange_callback(wxGLCanvas* canvas, void* callback); - void register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback); - void register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback); - void register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback); - void register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback); - void register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback); - void register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback); - void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); - void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); - void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); - void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); - void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); + static bool can_multisample() { return s_multisample == MS_Enabled; } + static wxGLCanvas* create_wxglcanvas(wxWindow *parent); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const; bool _init(GLCanvas3D& canvas); + static void _detect_multisample(int* attribList); }; } // namespace GUI diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp new file mode 100644 index 0000000000..3f7dc6cacf --- /dev/null +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -0,0 +1,2025 @@ +#include "libslic3r/libslic3r.h" +#include "GLGizmo.hpp" + +#include "GUI.hpp" +#include "GUI_App.hpp" + +#include "../../libslic3r/Utils.hpp" +#include "PresetBundle.hpp" + +#include +#include "libslic3r/Geometry.hpp" + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "GUI.hpp" +#include "GUI_Utils.hpp" +#include "GUI_App.hpp" + +// TODO: Display tooltips quicker on Linux + +static const float DEFAULT_BASE_COLOR[3] = { 0.625f, 0.625f, 0.625f }; +static const float DEFAULT_DRAG_COLOR[3] = { 1.0f, 1.0f, 1.0f }; +static const float DEFAULT_HIGHLIGHT_COLOR[3] = { 1.0f, 0.38f, 0.0f }; + +static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; + +namespace Slic3r { +namespace GUI { + +const float GLGizmoBase::Grabber::SizeFactor = 0.025f; +const float GLGizmoBase::Grabber::MinHalfSize = 1.5f; +const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; + +GLGizmoBase::Grabber::Grabber() + : center(Vec3d::Zero()) + , angles(Vec3d::Zero()) + , dragging(false) + , enabled(true) +{ + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; +} + +void GLGizmoBase::Grabber::render(bool hover, float size) const +{ + float render_color[3]; + if (hover) + { + render_color[0] = 1.0f - color[0]; + render_color[1] = 1.0f - color[1]; + render_color[2] = 1.0f - color[2]; + } + else + ::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float)); + + render(size, render_color, true); +} + +float GLGizmoBase::Grabber::get_half_size(float size) const +{ + return std::max(size * SizeFactor, MinHalfSize); +} + +float GLGizmoBase::Grabber::get_dragging_half_size(float size) const +{ + return std::max(size * SizeFactor * DraggingScaleFactor, MinHalfSize); +} + +void GLGizmoBase::Grabber::render(float size, const float* render_color, bool use_lighting) const +{ + float half_size = dragging ? get_dragging_half_size(size) : get_half_size(size); + + if (use_lighting) + ::glEnable(GL_LIGHTING); + + ::glColor3fv(render_color); + + ::glPushMatrix(); + ::glTranslated(center(0), center(1), center(2)); + + ::glRotated(Geometry::rad2deg(angles(2)), 0.0, 0.0, 1.0); + ::glRotated(Geometry::rad2deg(angles(1)), 0.0, 1.0, 0.0); + ::glRotated(Geometry::rad2deg(angles(0)), 1.0, 0.0, 0.0); + + // face min x + ::glPushMatrix(); + ::glTranslatef(-(GLfloat)half_size, 0.0f, 0.0f); + ::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); + render_face(half_size); + ::glPopMatrix(); + + // face max x + ::glPushMatrix(); + ::glTranslatef((GLfloat)half_size, 0.0f, 0.0f); + ::glRotatef(90.0f, 0.0f, 1.0f, 0.0f); + render_face(half_size); + ::glPopMatrix(); + + // face min y + ::glPushMatrix(); + ::glTranslatef(0.0f, -(GLfloat)half_size, 0.0f); + ::glRotatef(90.0f, 1.0f, 0.0f, 0.0f); + render_face(half_size); + ::glPopMatrix(); + + // face max y + ::glPushMatrix(); + ::glTranslatef(0.0f, (GLfloat)half_size, 0.0f); + ::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); + render_face(half_size); + ::glPopMatrix(); + + // face min z + ::glPushMatrix(); + ::glTranslatef(0.0f, 0.0f, -(GLfloat)half_size); + ::glRotatef(180.0f, 1.0f, 0.0f, 0.0f); + render_face(half_size); + ::glPopMatrix(); + + // face max z + ::glPushMatrix(); + ::glTranslatef(0.0f, 0.0f, (GLfloat)half_size); + render_face(half_size); + ::glPopMatrix(); + + ::glPopMatrix(); + + if (use_lighting) + ::glDisable(GL_LIGHTING); +} + +void GLGizmoBase::Grabber::render_face(float half_size) const +{ + ::glBegin(GL_TRIANGLES); + ::glNormal3f(0.0f, 0.0f, 1.0f); + ::glVertex3f(-(GLfloat)half_size, -(GLfloat)half_size, 0.0f); + ::glVertex3f((GLfloat)half_size, -(GLfloat)half_size, 0.0f); + ::glVertex3f((GLfloat)half_size, (GLfloat)half_size, 0.0f); + ::glVertex3f((GLfloat)half_size, (GLfloat)half_size, 0.0f); + ::glVertex3f(-(GLfloat)half_size, (GLfloat)half_size, 0.0f); + ::glVertex3f(-(GLfloat)half_size, -(GLfloat)half_size, 0.0f); + ::glEnd(); +} + +GLGizmoBase::GLGizmoBase(GLCanvas3D& parent) + : m_parent(parent) + , m_group_id(-1) + , m_state(Off) + , m_hover_id(-1) + , m_dragging(false) +{ + ::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 3 * sizeof(float)); + ::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 3 * sizeof(float)); + ::memcpy((void*)m_highlight_color, (const void*)DEFAULT_HIGHLIGHT_COLOR, 3 * sizeof(float)); +} + +void GLGizmoBase::set_hover_id(int id) +{ + if (m_grabbers.empty() || (id < (int)m_grabbers.size())) + { + m_hover_id = id; + on_set_hover_id(); + } +} + +void GLGizmoBase::set_highlight_color(const float* color) +{ + if (color != nullptr) + ::memcpy((void*)m_highlight_color, (const void*)color, 3 * sizeof(float)); +} + +void GLGizmoBase::enable_grabber(unsigned int id) +{ + if ((0 <= id) && (id < (unsigned int)m_grabbers.size())) + m_grabbers[id].enabled = true; + + on_enable_grabber(id); +} + +void GLGizmoBase::disable_grabber(unsigned int id) +{ + if ((0 <= id) && (id < (unsigned int)m_grabbers.size())) + m_grabbers[id].enabled = false; + + on_disable_grabber(id); +} + +void GLGizmoBase::start_dragging(const GLCanvas3D::Selection& selection) +{ + m_dragging = true; + + for (int i = 0; i < (int)m_grabbers.size(); ++i) + { + m_grabbers[i].dragging = (m_hover_id == i); + } + + on_start_dragging(selection); +} + +void GLGizmoBase::stop_dragging() +{ + m_dragging = false; + + for (int i = 0; i < (int)m_grabbers.size(); ++i) + { + m_grabbers[i].dragging = false; + } + + on_stop_dragging(); +} + +void GLGizmoBase::update(const UpdateData& data) +{ + if (m_hover_id != -1) + on_update(data); +} + +float GLGizmoBase::picking_color_component(unsigned int id) const +{ + int color = 254 - (int)id; + if (m_group_id > -1) + color -= m_group_id; + + return (float)color / 255.0f; +} + +void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const +{ + float size = (float)box.max_size(); + + for (int i = 0; i < (int)m_grabbers.size(); ++i) + { + if (m_grabbers[i].enabled) + m_grabbers[i].render((m_hover_id == i), size); + } +} + +void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const +{ + float size = (float)box.max_size(); + + for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) + { + if (m_grabbers[i].enabled) + { + m_grabbers[i].color[0] = 1.0f; + m_grabbers[i].color[1] = 1.0f; + m_grabbers[i].color[2] = picking_color_component(i); + m_grabbers[i].render_for_picking(size); + } + } +} + +void GLGizmoBase::create_external_gizmo_widgets(wxWindow *parent) {} + +void GLGizmoBase::set_tooltip(const std::string& tooltip) const +{ + m_parent.set_tooltip(tooltip); +} + +std::string GLGizmoBase::format(float value, unsigned int decimals) const +{ + return Slic3r::string_printf("%.*f", decimals, value); +} + +const float GLGizmoRotate::Offset = 5.0f; +const unsigned int GLGizmoRotate::CircleResolution = 64; +const unsigned int GLGizmoRotate::AngleResolution = 64; +const unsigned int GLGizmoRotate::ScaleStepsCount = 72; +const float GLGizmoRotate::ScaleStepRad = 2.0f * (float)PI / GLGizmoRotate::ScaleStepsCount; +const unsigned int GLGizmoRotate::ScaleLongEvery = 2; +const float GLGizmoRotate::ScaleLongTooth = 0.1f; // in percent of radius +const unsigned int GLGizmoRotate::SnapRegionsCount = 8; +const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius + +GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) + : GLGizmoBase(parent) + , m_axis(axis) + , m_angle(0.0) + , m_center(0.0, 0.0, 0.0) + , m_radius(0.0f) + , m_snap_coarse_in_radius(0.0f) + , m_snap_coarse_out_radius(0.0f) + , m_snap_fine_in_radius(0.0f) + , m_snap_fine_out_radius(0.0f) +{ +} + +void GLGizmoRotate::set_angle(double angle) +{ + if (std::abs(angle - 2.0 * (double)PI) < EPSILON) + angle = 0.0; + + m_angle = angle; +} + +bool GLGizmoRotate::on_init() +{ + m_grabbers.push_back(Grabber()); + return true; +} + +void GLGizmoRotate::on_start_dragging(const GLCanvas3D::Selection& selection) +{ + const BoundingBoxf3& box = selection.get_bounding_box(); + m_center = box.center(); + m_radius = Offset + box.radius(); + m_snap_coarse_in_radius = m_radius / 3.0f; + m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; + m_snap_fine_in_radius = m_radius; + m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; +} + +void GLGizmoRotate::on_update(const UpdateData& data) +{ + Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray)); + + Vec2d orig_dir = Vec2d::UnitX(); + Vec2d new_dir = mouse_pos.normalized(); + + double theta = ::acos(clamp(-1.0, 1.0, new_dir.dot(orig_dir))); + if (cross2(orig_dir, new_dir) < 0.0) + theta = 2.0 * (double)PI - theta; + + double len = mouse_pos.norm(); + + // snap to coarse snap region + if ((m_snap_coarse_in_radius <= len) && (len <= m_snap_coarse_out_radius)) + { + double step = 2.0 * (double)PI / (double)SnapRegionsCount; + theta = step * (double)std::round(theta / step); + } + else + { + // snap to fine snap region (scale) + if ((m_snap_fine_in_radius <= len) && (len <= m_snap_fine_out_radius)) + { + double step = 2.0 * (double)PI / (double)ScaleStepsCount; + theta = step * (double)std::round(theta / step); + } + } + + if (theta == 2.0 * (double)PI) + theta = 0.0; + + m_angle = theta; +} + +void GLGizmoRotate::on_render(const GLCanvas3D::Selection& selection) const +{ + if (!m_grabbers[0].enabled) + return; + + const BoundingBoxf3& box = selection.get_bounding_box(); + bool single_selection = selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume(); + + std::string axis; + switch (m_axis) + { + case X: { axis = "X: "; break; } + case Y: { axis = "Y: "; break; } + case Z: { axis = "Z: "; break; } + } + + if ((single_selection && (m_hover_id == 0)) || m_dragging) + set_tooltip(axis + format((float)Geometry::rad2deg(m_angle), 4) + "\u00B0"); + else + { + m_center = box.center(); + m_radius = Offset + box.radius(); + m_snap_coarse_in_radius = m_radius / 3.0f; + m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; + m_snap_fine_in_radius = m_radius; + m_snap_fine_out_radius = m_radius * (1.0f + ScaleLongTooth); + } + + ::glEnable(GL_DEPTH_TEST); + + ::glPushMatrix(); + transform_to_local(); + + ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); + ::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color); + + render_circle(); + + if (m_hover_id != -1) + { + render_scale(); + render_snap_radii(); + render_reference_radius(); + } + + ::glColor3fv(m_highlight_color); + + if (m_hover_id != -1) + render_angle(); + + render_grabber(box); + + ::glPopMatrix(); +} + +void GLGizmoRotate::on_render_for_picking(const GLCanvas3D::Selection& selection) const +{ + ::glDisable(GL_DEPTH_TEST); + + ::glPushMatrix(); + + transform_to_local(); + render_grabbers_for_picking(selection.get_bounding_box()); + + ::glPopMatrix(); +} + +void GLGizmoRotate::render_circle() const +{ + ::glBegin(GL_LINE_LOOP); + for (unsigned int i = 0; i < ScaleStepsCount; ++i) + { + float angle = (float)i * ScaleStepRad; + float x = ::cos(angle) * m_radius; + float y = ::sin(angle) * m_radius; + float z = 0.0f; + ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); + } + ::glEnd(); +} + +void GLGizmoRotate::render_scale() const +{ + float out_radius_long = m_snap_fine_out_radius; + float out_radius_short = m_radius * (1.0f + 0.5f * ScaleLongTooth); + + ::glBegin(GL_LINES); + for (unsigned int i = 0; i < ScaleStepsCount; ++i) + { + float angle = (float)i * ScaleStepRad; + float cosa = ::cos(angle); + float sina = ::sin(angle); + float in_x = cosa * m_radius; + float in_y = sina * m_radius; + float in_z = 0.0f; + float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; + float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; + float out_z = 0.0f; + ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); + ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); + } + ::glEnd(); +} + +void GLGizmoRotate::render_snap_radii() const +{ + float step = 2.0f * (float)PI / (float)SnapRegionsCount; + + float in_radius = m_radius / 3.0f; + float out_radius = 2.0f * in_radius; + + ::glBegin(GL_LINES); + for (unsigned int i = 0; i < SnapRegionsCount; ++i) + { + float angle = (float)i * step; + float cosa = ::cos(angle); + float sina = ::sin(angle); + float in_x = cosa * in_radius; + float in_y = sina * in_radius; + float in_z = 0.0f; + float out_x = cosa * out_radius; + float out_y = sina * out_radius; + float out_z = 0.0f; + ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); + ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); + } + ::glEnd(); +} + +void GLGizmoRotate::render_reference_radius() const +{ + ::glBegin(GL_LINES); + ::glVertex3f(0.0f, 0.0f, 0.0f); + ::glVertex3f((GLfloat)(m_radius * (1.0f + GrabberOffset)), 0.0f, 0.0f); + ::glEnd(); +} + +void GLGizmoRotate::render_angle() const +{ + float step_angle = (float)m_angle / AngleResolution; + float ex_radius = m_radius * (1.0f + GrabberOffset); + + ::glBegin(GL_LINE_STRIP); + for (unsigned int i = 0; i <= AngleResolution; ++i) + { + float angle = (float)i * step_angle; + float x = ::cos(angle) * ex_radius; + float y = ::sin(angle) * ex_radius; + float z = 0.0f; + ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); + } + ::glEnd(); +} + +void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const +{ + double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset) + 2.0 * (double)m_axis * (double)m_grabbers[0].get_half_size((float)box.max_size()); + m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); + m_grabbers[0].angles(2) = m_angle; + + ::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color); + + ::glBegin(GL_LINES); + ::glVertex3f(0.0f, 0.0f, 0.0f); + ::glVertex3dv(m_grabbers[0].center.data()); + ::glEnd(); + + ::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float)); + render_grabbers(box); +} + +void GLGizmoRotate::transform_to_local() const +{ + ::glTranslated(m_center(0), m_center(1), m_center(2)); + + switch (m_axis) + { + case X: + { + ::glRotatef(90.0f, 0.0f, 1.0f, 0.0f); + ::glRotatef(90.0f, 0.0f, 0.0f, 1.0f); + break; + } + case Y: + { + ::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); + ::glRotatef(180.0f, 0.0f, 0.0f, 1.0f); + break; + } + default: + case Z: + { + // no rotation + break; + } + } +} + +Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray) const +{ + double half_pi = 0.5 * (double)PI; + + Transform3d m = Transform3d::Identity(); + + switch (m_axis) + { + case X: + { + m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitZ())); + m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitY())); + break; + } + case Y: + { + m.rotate(Eigen::AngleAxisd(-(double)PI, Vec3d::UnitZ())); + m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitX())); + break; + } + default: + case Z: + { + // no rotation applied + break; + } + } + + m.translate(-m_center); + + return transform(mouse_ray, m).intersect_plane(0.0); +} + +GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent) + : GLGizmoBase(parent) +{ + m_gizmos.emplace_back(parent, GLGizmoRotate::X); + m_gizmos.emplace_back(parent, GLGizmoRotate::Y); + m_gizmos.emplace_back(parent, GLGizmoRotate::Z); + + for (unsigned int i = 0; i < 3; ++i) + { + m_gizmos[i].set_group_id(i); + } +} + +bool GLGizmoRotate3D::on_init() +{ + for (GLGizmoRotate& g : m_gizmos) + { + if (!g.init()) + return false; + } + + for (unsigned int i = 0; i < 3; ++i) + { + m_gizmos[i].set_highlight_color(AXES_COLOR[i]); + } + + std::string path = resources_dir() + "/icons/overlay/"; + + if (!m_textures[Off].load_from_file(path + "rotate_off.png", false)) + return false; + + if (!m_textures[Hover].load_from_file(path + "rotate_hover.png", false)) + return false; + + if (!m_textures[On].load_from_file(path + "rotate_on.png", false)) + return false; + + return true; +} + +std::string GLGizmoRotate3D::on_get_name() const +{ + return L("Rotate"); +} + +void GLGizmoRotate3D::on_start_dragging(const GLCanvas3D::Selection& selection) +{ + if ((0 <= m_hover_id) && (m_hover_id < 3)) + m_gizmos[m_hover_id].start_dragging(selection); +} + +void GLGizmoRotate3D::on_stop_dragging() +{ + if ((0 <= m_hover_id) && (m_hover_id < 3)) + m_gizmos[m_hover_id].stop_dragging(); +} + +void GLGizmoRotate3D::on_render(const GLCanvas3D::Selection& selection) const +{ +#if ENABLE_GIZMOS_ON_TOP + ::glClear(GL_DEPTH_BUFFER_BIT); +#endif // ENABLE_GIZMOS_ON_TOP + + if ((m_hover_id == -1) || (m_hover_id == 0)) + m_gizmos[X].render(selection); + + if ((m_hover_id == -1) || (m_hover_id == 1)) + m_gizmos[Y].render(selection); + + if ((m_hover_id == -1) || (m_hover_id == 2)) + m_gizmos[Z].render(selection); +} + +const float GLGizmoScale3D::Offset = 5.0f; + +GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent) + : GLGizmoBase(parent) + , m_scale(Vec3d::Ones()) + , m_snap_step(0.05) + , m_starting_scale(Vec3d::Ones()) +{ +} + +bool GLGizmoScale3D::on_init() +{ + std::string path = resources_dir() + "/icons/overlay/"; + + if (!m_textures[Off].load_from_file(path + "scale_off.png", false)) + return false; + + if (!m_textures[Hover].load_from_file(path + "scale_hover.png", false)) + return false; + + if (!m_textures[On].load_from_file(path + "scale_on.png", false)) + return false; + + for (int i = 0; i < 10; ++i) + { + m_grabbers.push_back(Grabber()); + } + + double half_pi = 0.5 * (double)PI; + + // x axis + m_grabbers[0].angles(1) = half_pi; + m_grabbers[1].angles(1) = half_pi; + + // y axis + m_grabbers[2].angles(0) = half_pi; + m_grabbers[3].angles(0) = half_pi; + + return true; +} + +std::string GLGizmoScale3D::on_get_name() const +{ + return L("Scale"); +} + +void GLGizmoScale3D::on_start_dragging(const GLCanvas3D::Selection& selection) +{ + if (m_hover_id != -1) + { + m_starting_drag_position = m_grabbers[m_hover_id].center; + m_starting_box = selection.get_bounding_box(); + } +} + +void GLGizmoScale3D::on_update(const UpdateData& data) +{ + if ((m_hover_id == 0) || (m_hover_id == 1)) + do_scale_x(data); + else if ((m_hover_id == 2) || (m_hover_id == 3)) + do_scale_y(data); + else if ((m_hover_id == 4) || (m_hover_id == 5)) + do_scale_z(data); + else if (m_hover_id >= 6) + do_scale_uniform(data); +} + +#if ENABLE_GIZMOS_RESET +void GLGizmoScale3D::on_process_double_click() +{ + if (m_hover_id >= 6) + m_scale = Vec3d::Ones(); +} +#endif // ENABLE_GIZMOS_RESET + +void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const +{ + bool single_instance = selection.is_single_full_instance(); + bool single_volume = selection.is_single_modifier() || selection.is_single_volume(); + bool single_selection = single_instance || single_volume; + + Vec3f scale = 100.0f * Vec3f::Ones(); +#if ENABLE_MODELVOLUME_TRANSFORM + if (single_instance) + scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast(); + else if (single_volume) + scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor().cast(); +#else + Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor().cast() : 100.0f * m_scale.cast(); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + if ((single_selection && ((m_hover_id == 0) || (m_hover_id == 1))) || m_grabbers[0].dragging || m_grabbers[1].dragging) + set_tooltip("X: " + format(scale(0), 4) + "%"); + else if ((single_selection && ((m_hover_id == 2) || (m_hover_id == 3))) || m_grabbers[2].dragging || m_grabbers[3].dragging) + set_tooltip("Y: " + format(scale(1), 4) + "%"); + else if ((single_selection && ((m_hover_id == 4) || (m_hover_id == 5))) || m_grabbers[4].dragging || m_grabbers[5].dragging) + set_tooltip("Z: " + format(scale(2), 4) + "%"); + else if ((single_selection && ((m_hover_id == 6) || (m_hover_id == 7) || (m_hover_id == 8) || (m_hover_id == 9))) + || m_grabbers[6].dragging || m_grabbers[7].dragging || m_grabbers[8].dragging || m_grabbers[9].dragging) + { + std::string tooltip = "X: " + format(scale(0), 4) + "%\n"; + tooltip += "Y: " + format(scale(1), 4) + "%\n"; + tooltip += "Z: " + format(scale(2), 4) + "%"; + set_tooltip(tooltip); + } + +#if ENABLE_GIZMOS_ON_TOP + ::glClear(GL_DEPTH_BUFFER_BIT); +#endif // ENABLE_GIZMOS_ON_TOP + ::glEnable(GL_DEPTH_TEST); + + BoundingBoxf3 box; + Transform3d transform = Transform3d::Identity(); + Vec3d angles = Vec3d::Zero(); + Transform3d offsets_transform = Transform3d::Identity(); + + if (single_instance) + { + // calculate bounding box in instance local reference system + const GLCanvas3D::Selection::IndicesList& idxs = selection.get_volume_idxs(); + for (unsigned int idx : idxs) + { + box.merge(selection.get_volume(idx)->bounding_box); + } + + // gets transform from first selected volume + const GLVolume* v = selection.get_volume(*idxs.begin()); +#if ENABLE_MODELVOLUME_TRANSFORM + transform = v->world_matrix(); + // gets angles from first selected volume + angles = v->get_instance_rotation(); + // consider rotation+mirror only components of the transform for offsets + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); +#else + transform = v->world_matrix().cast(); + // gets angles from first selected volume + angles = v->get_rotation(); + // consider rotation+mirror only components of the transform for offsets + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + else if (single_volume) + { + const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); + box = v->bounding_box; +#if ENABLE_MODELVOLUME_TRANSFORM + transform = v->world_matrix(); + angles = Geometry::extract_euler_angles(transform); + // consider rotation+mirror only components of the transform for offsets + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); +#else + transform = v->world_matrix().cast(); + angles = Geometry::extract_euler_angles(transform); + // consider rotation+mirror only components of the transform for offsets + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + else + box = selection.get_bounding_box(); + + m_box = box; + + const Vec3d& center = m_box.center(); + Vec3d offset_x = offsets_transform * Vec3d((double)Offset, 0.0, 0.0); + Vec3d offset_y = offsets_transform * Vec3d(0.0, (double)Offset, 0.0); + Vec3d offset_z = offsets_transform * Vec3d(0.0, 0.0, (double)Offset); + + // x axis + m_grabbers[0].center = transform * Vec3d(m_box.min(0), center(1), center(2)) - offset_x; + m_grabbers[1].center = transform * Vec3d(m_box.max(0), center(1), center(2)) + offset_x; + ::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float)); + ::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float)); + + // y axis + m_grabbers[2].center = transform * Vec3d(center(0), m_box.min(1), center(2)) - offset_y; + m_grabbers[3].center = transform * Vec3d(center(0), m_box.max(1), center(2)) + offset_y; + ::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float)); + ::memcpy((void*)m_grabbers[3].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float)); + + // z axis + m_grabbers[4].center = transform * Vec3d(center(0), center(1), m_box.min(2)) - offset_z; + m_grabbers[5].center = transform * Vec3d(center(0), center(1), m_box.max(2)) + offset_z; + ::memcpy((void*)m_grabbers[4].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float)); + ::memcpy((void*)m_grabbers[5].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float)); + + // uniform + m_grabbers[6].center = transform * Vec3d(m_box.min(0), m_box.min(1), center(2)) - offset_x - offset_y; + m_grabbers[7].center = transform * Vec3d(m_box.max(0), m_box.min(1), center(2)) + offset_x - offset_y; + m_grabbers[8].center = transform * Vec3d(m_box.max(0), m_box.max(1), center(2)) + offset_x + offset_y; + m_grabbers[9].center = transform * Vec3d(m_box.min(0), m_box.max(1), center(2)) - offset_x + offset_y; + for (int i = 6; i < 10; ++i) + { + ::memcpy((void*)m_grabbers[i].color, (const void*)m_highlight_color, 3 * sizeof(float)); + } + + // sets grabbers orientation + for (int i = 0; i < 10; ++i) + { + m_grabbers[i].angles = angles; + } + + ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); + + float box_max_size = (float)m_box.max_size(); + + if (m_hover_id == -1) + { + // draw connections + if (m_grabbers[0].enabled && m_grabbers[1].enabled) + { + ::glColor3fv(m_grabbers[0].color); + render_grabbers_connection(0, 1); + } + if (m_grabbers[2].enabled && m_grabbers[3].enabled) + { + ::glColor3fv(m_grabbers[2].color); + render_grabbers_connection(2, 3); + } + if (m_grabbers[4].enabled && m_grabbers[5].enabled) + { + ::glColor3fv(m_grabbers[4].color); + render_grabbers_connection(4, 5); + } + ::glColor3fv(m_base_color); + render_grabbers_connection(6, 7); + render_grabbers_connection(7, 8); + render_grabbers_connection(8, 9); + render_grabbers_connection(9, 6); + // draw grabbers + render_grabbers(m_box); + } + else if ((m_hover_id == 0) || (m_hover_id == 1)) + { + // draw connection + ::glColor3fv(m_grabbers[0].color); + render_grabbers_connection(0, 1); + // draw grabbers + m_grabbers[0].render(true, box_max_size); + m_grabbers[1].render(true, box_max_size); + } + else if ((m_hover_id == 2) || (m_hover_id == 3)) + { + // draw connection + ::glColor3fv(m_grabbers[2].color); + render_grabbers_connection(2, 3); + // draw grabbers + m_grabbers[2].render(true, box_max_size); + m_grabbers[3].render(true, box_max_size); + } + else if ((m_hover_id == 4) || (m_hover_id == 5)) + { + // draw connection + ::glColor3fv(m_grabbers[4].color); + render_grabbers_connection(4, 5); + // draw grabbers + m_grabbers[4].render(true, box_max_size); + m_grabbers[5].render(true, box_max_size); + } + else if (m_hover_id >= 6) + { + // draw connection + ::glColor3fv(m_drag_color); + render_grabbers_connection(6, 7); + render_grabbers_connection(7, 8); + render_grabbers_connection(8, 9); + render_grabbers_connection(9, 6); + // draw grabbers + for (int i = 6; i < 10; ++i) + { + m_grabbers[i].render(true, box_max_size); + } + } +} + +void GLGizmoScale3D::on_render_for_picking(const GLCanvas3D::Selection& selection) const +{ + ::glDisable(GL_DEPTH_TEST); + + render_grabbers_for_picking(selection.get_bounding_box()); +} + +void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const +{ + unsigned int grabbers_count = (unsigned int)m_grabbers.size(); + if ((id_1 < grabbers_count) && (id_2 < grabbers_count)) + { + ::glBegin(GL_LINES); + ::glVertex3dv(m_grabbers[id_1].center.data()); + ::glVertex3dv(m_grabbers[id_2].center.data()); + ::glEnd(); + } +} + +void GLGizmoScale3D::do_scale_x(const UpdateData& data) +{ + double ratio = calc_ratio(data); + if (ratio > 0.0) + m_scale(0) = m_starting_scale(0) * ratio; +} + +void GLGizmoScale3D::do_scale_y(const UpdateData& data) +{ + double ratio = calc_ratio(data); + if (ratio > 0.0) + m_scale(1) = m_starting_scale(1) * ratio; +} + +void GLGizmoScale3D::do_scale_z(const UpdateData& data) +{ + double ratio = calc_ratio(data); + if (ratio > 0.0) + m_scale(2) = m_starting_scale(2) * ratio; +} + +void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) +{ + double ratio = calc_ratio(data); + if (ratio > 0.0) + m_scale = m_starting_scale * ratio; +} + +double GLGizmoScale3D::calc_ratio(const UpdateData& data) const +{ + double ratio = 0.0; + + // vector from the center to the starting position + Vec3d starting_vec = m_starting_drag_position - m_starting_box.center(); + double len_starting_vec = starting_vec.norm(); + if (len_starting_vec != 0.0) + { + Vec3d mouse_dir = data.mouse_ray.unit_vector(); + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // in our case plane normal and ray direction are the same (orthogonal view) + // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal + Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + // vector from the starting position to the found intersection + Vec3d inters_vec = inters - m_starting_drag_position; + + // finds projection of the vector along the staring direction + double proj = inters_vec.dot(starting_vec.normalized()); + + ratio = (len_starting_vec + proj) / len_starting_vec; + } + + if (data.shift_down) + ratio = m_snap_step * (double)std::round(ratio / m_snap_step); + + return ratio; +} + +const double GLGizmoMove3D::Offset = 10.0; + +GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent) + : GLGizmoBase(parent) + , m_displacement(Vec3d::Zero()) + , m_snap_step(1.0) + , m_starting_drag_position(Vec3d::Zero()) + , m_starting_box_center(Vec3d::Zero()) + , m_starting_box_bottom_center(Vec3d::Zero()) +{ +} + +bool GLGizmoMove3D::on_init() +{ + std::string path = resources_dir() + "/icons/overlay/"; + + if (!m_textures[Off].load_from_file(path + "move_off.png", false)) + return false; + + if (!m_textures[Hover].load_from_file(path + "move_hover.png", false)) + return false; + + if (!m_textures[On].load_from_file(path + "move_on.png", false)) + return false; + + for (int i = 0; i < 3; ++i) + { + m_grabbers.push_back(Grabber()); + } + + return true; +} + +std::string GLGizmoMove3D::on_get_name() const +{ + return L("Move"); +} + +void GLGizmoMove3D::on_start_dragging(const GLCanvas3D::Selection& selection) +{ + if (m_hover_id != -1) + { + m_displacement = Vec3d::Zero(); + const BoundingBoxf3& box = selection.get_bounding_box(); + m_starting_drag_position = m_grabbers[m_hover_id].center; + m_starting_box_center = box.center(); + m_starting_box_bottom_center = box.center(); + m_starting_box_bottom_center(2) = box.min(2); + } +} + +void GLGizmoMove3D::on_stop_dragging() +{ + m_displacement = Vec3d::Zero(); +} + +void GLGizmoMove3D::on_update(const UpdateData& data) +{ + if (m_hover_id == 0) + m_displacement(0) = calc_projection(data); + else if (m_hover_id == 1) + m_displacement(1) = calc_projection(data); + else if (m_hover_id == 2) + m_displacement(2) = calc_projection(data); +} + +void GLGizmoMove3D::on_render(const GLCanvas3D::Selection& selection) const +{ + bool show_position = selection.is_single_full_instance(); + const Vec3d& position = selection.get_bounding_box().center(); + + if ((show_position && (m_hover_id == 0)) || m_grabbers[0].dragging) + set_tooltip("X: " + format(show_position ? position(0) : m_displacement(0), 2)); + else if ((show_position && (m_hover_id == 1)) || m_grabbers[1].dragging) + set_tooltip("Y: " + format(show_position ? position(1) : m_displacement(1), 2)); + else if ((show_position && (m_hover_id == 2)) || m_grabbers[2].dragging) + set_tooltip("Z: " + format(show_position ? position(2) : m_displacement(2), 2)); + +#if ENABLE_GIZMOS_ON_TOP + ::glClear(GL_DEPTH_BUFFER_BIT); +#endif // ENABLE_GIZMOS_ON_TOP + ::glEnable(GL_DEPTH_TEST); + + const BoundingBoxf3& box = selection.get_bounding_box(); + const Vec3d& center = box.center(); + + // x axis + m_grabbers[0].center = Vec3d(box.max(0) + Offset, center(1), center(2)); + ::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float)); + + // y axis + m_grabbers[1].center = Vec3d(center(0), box.max(1) + Offset, center(2)); + ::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float)); + + // z axis + m_grabbers[2].center = Vec3d(center(0), center(1), box.max(2) + Offset); + ::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float)); + + ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); + + if (m_hover_id == -1) + { + // draw axes + for (unsigned int i = 0; i < 3; ++i) + { + if (m_grabbers[i].enabled) + { + ::glColor3fv(AXES_COLOR[i]); + ::glBegin(GL_LINES); + ::glVertex3dv(center.data()); + ::glVertex3dv(m_grabbers[i].center.data()); + ::glEnd(); + } + } + + // draw grabbers + render_grabbers(box); + } + else + { + // draw axis + ::glColor3fv(AXES_COLOR[m_hover_id]); + ::glBegin(GL_LINES); + ::glVertex3dv(center.data()); + ::glVertex3dv(m_grabbers[m_hover_id].center.data()); + ::glEnd(); + + // draw grabber + m_grabbers[m_hover_id].render(true, box.max_size()); + } +} + +void GLGizmoMove3D::on_render_for_picking(const GLCanvas3D::Selection& selection) const +{ + ::glDisable(GL_DEPTH_TEST); + + render_grabbers_for_picking(selection.get_bounding_box()); +} + +double GLGizmoMove3D::calc_projection(const UpdateData& data) const +{ + double projection = 0.0; + + Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; + double len_starting_vec = starting_vec.norm(); + if (len_starting_vec != 0.0) + { + Vec3d mouse_dir = data.mouse_ray.unit_vector(); + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // in our case plane normal and ray direction are the same (orthogonal view) + // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal + Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + // vector from the starting position to the found intersection + Vec3d inters_vec = inters - m_starting_drag_position; + + // finds projection of the vector along the staring direction + projection = inters_vec.dot(starting_vec.normalized()); + } + + if (data.shift_down) + projection = m_snap_step * (double)std::round(projection / m_snap_step); + + return projection; +} + +GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent) + : GLGizmoBase(parent) + , m_normal(Vec3d::Zero()) + , m_starting_center(Vec3d::Zero()) +{ +} + +bool GLGizmoFlatten::on_init() +{ + std::string path = resources_dir() + "/icons/overlay/"; + + if (!m_textures[Off].load_from_file(path + "layflat_off.png", false)) + return false; + + if (!m_textures[Hover].load_from_file(path + "layflat_hover.png", false)) + return false; + + if (!m_textures[On].load_from_file(path + "layflat_on.png", false)) + return false; + + return true; +} + +std::string GLGizmoFlatten::on_get_name() const +{ + return L("Flatten"); +} + +void GLGizmoFlatten::on_start_dragging(const GLCanvas3D::Selection& selection) +{ + if (m_hover_id != -1) + { + m_normal = m_planes[m_hover_id].normal; + m_starting_center = selection.get_bounding_box().center(); + } +} + +void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const +{ + // The planes are rendered incorrectly when the object is being moved. We better won't render anything in that case. + // This indeed has a better solution (to be implemented when there is more time) + Vec3d dragged_offset(Vec3d::Zero()); + if (m_starting_center == Vec3d::Zero()) + m_starting_center = selection.get_bounding_box().center(); + dragged_offset = selection.get_bounding_box().center() - m_starting_center; + if (dragged_offset.norm() > 0.001) + return; + + ::glEnable(GL_BLEND); + ::glEnable(GL_DEPTH_TEST); + ::glDisable(GL_CULL_FACE); + + if (selection.is_from_single_object()) { + const std::set& instances_list = selection.get_instance_idxs(); + + if (!instances_list.empty() && m_model_object) { + for (const int instance_idx : instances_list) { + Transform3d m = m_model_object->instances[instance_idx]->get_matrix(); + for (int i=0; i<(int)m_planes.size(); ++i) { + if (i == m_hover_id) + ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f); + else + ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); + + m.pretranslate(dragged_offset); + ::glPushMatrix(); + ::glMultMatrixd(m.data()); + ::glBegin(GL_POLYGON); + for (const Vec3d& vertex : m_planes[i].vertices) + ::glVertex3dv(vertex.data()); + ::glEnd(); + ::glPopMatrix(); + } + } + } + } + + ::glEnable(GL_CULL_FACE); + ::glDisable(GL_BLEND); +} + +void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selection) const +{ + ::glEnable(GL_DEPTH_TEST); + ::glDisable(GL_CULL_FACE); + if (selection.is_from_single_object()) { + const std::set& instances_list = selection.get_instance_idxs(); + if (!instances_list.empty() && m_model_object) { + for (const int instance_idx : instances_list) { + for (int i=0; i<(int)m_planes.size(); ++i) { + ::glColor3f(1.0f, 1.0f, picking_color_component(i)); + ::glPushMatrix(); + ::glMultMatrixd(m_model_object->instances[instance_idx]->get_matrix().data()); + ::glBegin(GL_POLYGON); + for (const Vec3d& vertex : m_planes[i].vertices) + ::glVertex3dv(vertex.data()); + ::glEnd(); + ::glPopMatrix(); + } + } + } + } + + ::glEnable(GL_CULL_FACE); +} + +void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) +{ + m_starting_center = Vec3d::Zero(); + bool object_changed = m_model_object != model_object; + m_model_object = model_object; + + if (object_changed && is_plane_update_necessary()) + update_planes(); +} + +void GLGizmoFlatten::update_planes() +{ + TriangleMesh ch; + for (const ModelVolume* vol : m_model_object->volumes) +#if ENABLE_MODELVOLUME_TRANSFORM + { + TriangleMesh vol_ch = vol->get_convex_hull(); + vol_ch.transform(vol->get_matrix()); + ch.merge(vol_ch); + } +#else + ch.merge(vol->get_convex_hull()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + ch = ch.convex_hull_3d(); + + const Vec3d& bb_size = ch.bounding_box().size(); + double min_bb_face_area = std::min(bb_size(0) * bb_size(1), std::min(bb_size(0) * bb_size(2), bb_size(1) * bb_size(2))); + + m_planes.clear(); + + // Now we'll go through all the facets and append Points of facets sharing the same normal: + const int num_of_facets = ch.stl.stats.number_of_facets; + std::vector facet_queue(num_of_facets, 0); + std::vector facet_visited(num_of_facets, false); + int facet_queue_cnt = 0; + const stl_normal* normal_ptr = nullptr; + while (1) { + // Find next unvisited triangle: + int facet_idx = 0; + for (; facet_idx < num_of_facets; ++ facet_idx) + if (!facet_visited[facet_idx]) { + facet_queue[facet_queue_cnt ++] = facet_idx; + facet_visited[facet_idx] = true; + normal_ptr = &ch.stl.facet_start[facet_idx].normal; + m_planes.emplace_back(); + break; + } + if (facet_idx == num_of_facets) + break; // Everything was visited already + + while (facet_queue_cnt > 0) { + int facet_idx = facet_queue[-- facet_queue_cnt]; + const stl_normal& this_normal = ch.stl.facet_start[facet_idx].normal; + if (std::abs(this_normal(0) - (*normal_ptr)(0)) < 0.001 && std::abs(this_normal(1) - (*normal_ptr)(1)) < 0.001 && std::abs(this_normal(2) - (*normal_ptr)(2)) < 0.001) { + stl_vertex* first_vertex = ch.stl.facet_start[facet_idx].vertex; + for (int j=0; j<3; ++j) + m_planes.back().vertices.emplace_back((double)first_vertex[j](0), (double)first_vertex[j](1), (double)first_vertex[j](2)); + + facet_visited[facet_idx] = true; + for (int j = 0; j < 3; ++ j) { + int neighbor_idx = ch.stl.neighbors_start[facet_idx].neighbor[j]; + if (! facet_visited[neighbor_idx]) + facet_queue[facet_queue_cnt ++] = neighbor_idx; + } + } + } + m_planes.back().normal = Vec3d((double)(*normal_ptr)(0), (double)(*normal_ptr)(1), (double)(*normal_ptr)(2)); + + // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected anyway): + if (m_planes.back().vertices.size() == 3 && + ((m_planes.back().vertices[0] - m_planes.back().vertices[1]).norm() < 1.0 + || (m_planes.back().vertices[0] - m_planes.back().vertices[2]).norm() < 1.0 + || (m_planes.back().vertices[1] - m_planes.back().vertices[2]).norm() < 1.0)) + m_planes.pop_back(); + } + + const float minimal_area = 0.01f * (float)min_bb_face_area; + + // Now we'll go through all the polygons, transform the points into xy plane to process them: + for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) { + Pointf3s& polygon = m_planes[polygon_id].vertices; + const Vec3d& normal = m_planes[polygon_id].normal; + + // We are going to rotate about z and y to flatten the plane + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(normal, Vec3d::UnitZ()).toRotationMatrix(); + polygon = transform(polygon, m); + + polygon = Slic3r::Geometry::convex_hull(polygon); // To remove the inner points + + // We will calculate area of the polygons and discard ones that are too small + // The limit is more forgiving in case the normal is in the direction of the coordinate axes + float area_threshold = (std::abs(normal(0)) > 0.999f || std::abs(normal(1)) > 0.999f || std::abs(normal(2)) > 0.999f) ? minimal_area : 10.0f * minimal_area; + float& area = m_planes[polygon_id].area; + area = 0.f; + for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula + area += polygon[i](0)*polygon[i + 1 < polygon.size() ? i + 1 : 0](1) - polygon[i + 1 < polygon.size() ? i + 1 : 0](0)*polygon[i](1); + area = 0.5f * std::abs(area); + if (area < area_threshold) { + m_planes.erase(m_planes.begin()+(polygon_id--)); + continue; + } + + // We check the inner angles and discard polygons with angles smaller than the following threshold + const double angle_threshold = ::cos(10.0 * (double)PI / 180.0); + bool discard = false; + + for (unsigned int i = 0; i < polygon.size(); ++i) + { + const Vec3d& prec = polygon[(i == 0) ? polygon.size() - 1 : i - 1]; + const Vec3d& curr = polygon[i]; + const Vec3d& next = polygon[(i == polygon.size() - 1) ? 0 : i + 1]; + + if ((prec - curr).normalized().dot((next - curr).normalized()) > angle_threshold) + { + discard = true; + break; + } + } + + if (discard) + { + m_planes.erase(m_planes.begin() + (polygon_id--)); + continue; + } + + // We will shrink the polygon a little bit so it does not touch the object edges: + Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); + centroid /= (double)polygon.size(); + for (auto& vertex : polygon) + vertex = 0.9f*vertex + 0.1f*centroid; + + // Polygon is now simple and convex, we'll round the corners to make them look nicer. + // The algorithm takes a vertex, calculates middles of respective sides and moves the vertex + // towards their average (controlled by 'aggressivity'). This is repeated k times. + // In next iterations, the neighbours are not always taken at the middle (to increase the + // rounding effect at the corners, where we need it most). + const unsigned int k = 10; // number of iterations + const float aggressivity = 0.2f; // agressivity + const unsigned int N = polygon.size(); + std::vector> neighbours; + if (k != 0) { + Pointf3s points_out(2*k*N); // vector long enough to store the future vertices + for (unsigned int j=0; jvolumes) + m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box()); + const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); + m_source_data.mesh_first_point = Vec3d((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]); +} + +// Check if the bounding boxes of each volume's convex hull is the same as before +// and that scaling and rotation has not changed. In that case we don't have to recalculate it. +bool GLGizmoFlatten::is_plane_update_necessary() const +{ + if (m_state != On || !m_model_object || m_model_object->instances.empty()) + return false; + + if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size()) + return true; + + // now compare the bounding boxes: + for (unsigned int i=0; ivolumes.size(); ++i) + if (m_model_object->volumes[i]->get_convex_hull().bounding_box() != m_source_data.bounding_boxes[i]) + return true; + + const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); + Vec3d first_point((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]); + if (first_point != m_source_data.mesh_first_point) + return true; + + return false; +} + +Vec3d GLGizmoFlatten::get_flattening_normal() const +{ + Vec3d out = m_normal; + m_normal = Vec3d::Zero(); + m_starting_center = Vec3d::Zero(); + return out; +} + +GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent) + : GLGizmoBase(parent), m_starting_center(Vec3d::Zero()) +{ +} + +bool GLGizmoSlaSupports::on_init() +{ + std::string path = resources_dir() + "/icons/overlay/"; + + if (!m_textures[Off].load_from_file(path + "sla_support_points_off.png", false)) + return false; + + if (!m_textures[Hover].load_from_file(path + "sla_support_points_hover.png", false)) + return false; + + if (!m_textures[On].load_from_file(path + "sla_support_points_on.png", false)) + return false; + + return true; +} + +void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object) +{ + if (model_object != nullptr) + { + m_starting_center = Vec3d::Zero(); + m_model_object = model_object; + m_model_object_matrix = model_object->instances.front()->get_matrix(); + if (is_mesh_update_necessary()) + update_mesh(); + } +} + +void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const +{ + ::glEnable(GL_BLEND); + ::glEnable(GL_DEPTH_TEST); + + // the dragged_offset is a vector measuring where was the object moved + // with the gizmo being on. This is reset in set_flattening_data and + // does not work correctly when there are multiple copies. + + if (m_starting_center == Vec3d::Zero()) + m_starting_center = selection.get_bounding_box().center(); + Vec3d dragged_offset = selection.get_bounding_box().center() - m_starting_center; + + for (auto& g : m_grabbers) { + g.color[0] = 1.f; + g.color[1] = 0.f; + g.color[2] = 0.f; + } + + ::glPushMatrix(); + ::glTranslatef((GLfloat)dragged_offset(0), (GLfloat)dragged_offset(1), (GLfloat)dragged_offset(2)); + render_grabbers(); + ::glPopMatrix(); + + render_tooltip_texture(); + ::glDisable(GL_BLEND); +} + + +void GLGizmoSlaSupports::on_render_for_picking(const GLCanvas3D::Selection& selection) const +{ + ::glEnable(GL_DEPTH_TEST); + for (unsigned int i=0; iinstances.empty()) + return false; + + if ((m_model_object->instances.front()->get_matrix() * m_source_data.matrix.inverse() * Vec3d(1., 1., 1.) - Vec3d(1., 1., 1.)).norm() > 0.001) + return true; + + // following should detect direct mesh changes (can be removed after the mesh is made completely immutable): + /*const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); + Vec3d first_point((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]); + if (first_point != m_source_data.mesh_first_point) + return true;*/ + + return false; +} + +void GLGizmoSlaSupports::update_mesh() +{ + Eigen::MatrixXf& V = m_V; + Eigen::MatrixXi& F = m_F; + TriangleMesh mesh(m_model_object->mesh()); + const stl_file& stl = mesh.stl; + V.resize(3 * stl.stats.number_of_facets, 3); + F.resize(stl.stats.number_of_facets, 3); + for (unsigned int i=0; ivertex[0](0); V(3*i+0, 1) = facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2); + V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2); + V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2); + F(i, 0) = 3*i+0; + F(i, 1) = 3*i+1; + F(i, 2) = 3*i+2; + } + m_source_data.matrix = m_model_object->instances.front()->get_matrix(); + const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); + m_source_data.mesh_first_point = Vec3d((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]); + // we'll now reload Grabbers (selection might have changed): + m_grabbers.clear(); + for (const Vec3f& point : m_model_object->sla_support_points) { + m_grabbers.push_back(Grabber()); + m_grabbers.back().center = point.cast(); + } +} + +Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) +{ + // if the gizmo doesn't have the V, F structures for igl, calculate them first: + if (m_V.size() == 0 || is_mesh_update_necessary()) + update_mesh(); + + Eigen::Matrix viewport; + ::glGetIntegerv(GL_VIEWPORT, viewport.data()); + Eigen::Matrix modelview_matrix; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); + Eigen::Matrix projection_matrix; + ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix.data()); + + int fid = 0; + Eigen::Vector3f bc(0, 0, 0); + if (!igl::unproject_onto_mesh(Vec2f(mouse_pos(0), viewport(3)-mouse_pos(1)), modelview_matrix.cast(), projection_matrix.cast(), viewport.cast(), m_V, m_F, fid, bc)) + /*if (!igl::embree::unproject_onto_mesh(Vec2f(mouse_pos(0), viewport(3)-mouse_pos(1)), + m_F, + modelview_matrix.cast(), + projection_matrix.cast(), + viewport.cast(), + m_intersector, + fid, + bc))*/ + throw "unable to unproject_onto_mesh"; + + const Vec3f& a = m_V.row(m_F(fid, 0)); + const Vec3f& b = m_V.row(m_F(fid, 1)); + const Vec3f& c = m_V.row(m_F(fid, 2)); + Vec3f point = bc(0)*a + bc(1)*b + bc(2)*c; + return m_model_object->instances.front()->get_matrix().inverse().cast() * point; +} + +void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position) +{ + Vec3f new_pos; + try { + new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new grabber in that case + m_grabbers.push_back(Grabber()); + m_grabbers.back().center = new_pos.cast(); + m_model_object->sla_support_points.push_back(new_pos); + + // This should trigger the support generation + // wxGetApp().plater()->reslice(); + } + catch (...) {} +} + +void GLGizmoSlaSupports::delete_current_grabber(bool delete_all) +{ + if (delete_all) { + m_grabbers.clear(); + m_model_object->sla_support_points.clear(); + + // This should trigger the support generation + // wxGetApp().plater()->reslice(); + } + else + if (m_hover_id != -1) { + m_grabbers.erase(m_grabbers.begin() + m_hover_id); + m_model_object->sla_support_points.erase(m_model_object->sla_support_points.begin() + m_hover_id); + m_hover_id = -1; + + // This should trigger the support generation + // wxGetApp().plater()->reslice(); + } +} + +void GLGizmoSlaSupports::on_update(const UpdateData& data) +{ + if (m_hover_id != -1 && data.mouse_pos) { + Vec3f new_pos; + try { + new_pos = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1))); + m_grabbers[m_hover_id].center = new_pos.cast(); + m_model_object->sla_support_points[m_hover_id] = new_pos; + } + catch (...) {} + } +} + + +void GLGizmoSlaSupports::render_tooltip_texture() const { + if (m_tooltip_texture.get_id() == 0) + if (!m_tooltip_texture.load_from_file(resources_dir() + "/icons/variable_layer_height_tooltip.png", false)) + return; + if (m_reset_texture.get_id() == 0) + if (!m_reset_texture.load_from_file(resources_dir() + "/icons/variable_layer_height_reset.png", false)) + return; + + float zoom = m_parent.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + float gap = 30.0f * inv_zoom; + + const Size& cnv_size = m_parent.get_canvas_size(); + float l = gap - cnv_size.get_width()/2.f * inv_zoom; + float r = l + (float)m_tooltip_texture.get_width() * inv_zoom; + float b = gap - cnv_size.get_height()/2.f * inv_zoom; + float t = b + (float)m_tooltip_texture.get_height() * inv_zoom; + + Rect reset_rect = m_parent.get_gizmo_reset_rect(m_parent, true); + + ::glDisable(GL_DEPTH_TEST); + ::glPushMatrix(); + ::glLoadIdentity(); + GLTexture::render_texture(m_tooltip_texture.get_id(), l, r, b, t); + GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); + ::glPopMatrix(); + ::glEnable(GL_DEPTH_TEST); +} + +bool GLGizmoSlaSupports::on_is_activable(const GLCanvas3D::Selection& selection) const +{ + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA); +} + +bool GLGizmoSlaSupports::on_is_selectable() const +{ + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA); +} + +std::string GLGizmoSlaSupports::on_get_name() const +{ + return L("SLA Support Points"); +} + + + +// GLGizmoCut + +class GLGizmoCutPanel : public wxPanel +{ +public: + GLGizmoCutPanel(wxWindow *parent); + + void display(bool display); +private: + bool m_active; + wxCheckBox *m_cb_rotate; + wxButton *m_btn_cut; + wxButton *m_btn_cancel; +}; + +GLGizmoCutPanel::GLGizmoCutPanel(wxWindow *parent) + : wxPanel(parent) + , m_active(false) + , m_cb_rotate(new wxCheckBox(this, wxID_ANY, _(L("Rotate lower part upwards")))) + , m_btn_cut(new wxButton(this, wxID_OK, _(L("Perform cut")))) + , m_btn_cancel(new wxButton(this, wxID_CANCEL, _(L("Cancel")))) +{ + enum { MARGIN = 5 }; + + auto *sizer = new wxBoxSizer(wxHORIZONTAL); + + auto *label = new wxStaticText(this, wxID_ANY, _(L("Cut object:"))); + sizer->Add(label, 0, wxALL | wxALIGN_CENTER, MARGIN); + sizer->Add(m_cb_rotate, 0, wxALL | wxALIGN_CENTER, MARGIN); + sizer->AddStretchSpacer(); + sizer->Add(m_btn_cut, 0, wxALL | wxALIGN_CENTER, MARGIN); + sizer->Add(m_btn_cancel, 0, wxALL | wxALIGN_CENTER, MARGIN); + + SetSizer(sizer); +} + +void GLGizmoCutPanel::display(bool display) +{ + Show(display); + GetParent()->Layout(); +} + + +const double GLGizmoCut::Offset = 10.0; +const double GLGizmoCut::Margin = 20.0; +const std::array GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 }; + +GLGizmoCut::GLGizmoCut(GLCanvas3D& parent) + : GLGizmoBase(parent) + , m_cut_z(0.0) + , m_panel(nullptr) +{} + +void GLGizmoCut::create_external_gizmo_widgets(wxWindow *parent) +{ + wxASSERT(m_panel == nullptr); + + m_panel = new GLGizmoCutPanel(parent); + parent->GetSizer()->Add(m_panel, 0, wxEXPAND); + + parent->Layout(); + parent->Fit(); + auto prev_heigh = parent->GetMinSize().GetHeight(); + parent->SetMinSize(wxSize(-1, std::max(prev_heigh, m_panel->GetSize().GetHeight()))); + + m_panel->Hide(); + m_panel->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + perform_cut(); + }, wxID_OK); +} + +bool GLGizmoCut::on_init() +{ + // TODO: icon + + std::string path = resources_dir() + "/icons/overlay/"; + + if (!m_textures[Off].load_from_file(path + "cut_off.png", false)) { + return false; + } + + if (!m_textures[Hover].load_from_file(path + "cut_hover.png", false)) { + return false; + } + + if (!m_textures[On].load_from_file(path + "cut_on.png", false)) { + return false; + } + + m_grabbers.emplace_back(); + + return true; +} + +std::string GLGizmoCut::on_get_name() const +{ + return L("Cut"); +} + +void GLGizmoCut::on_set_state() +{ + // Reset m_cut_z on gizmo activation + if (get_state() == On) { + m_cut_z = 0.0; + } + + // Display or hide the extra panel + if (m_panel != nullptr) { + m_panel->display(get_state() == On); + } +} + +bool GLGizmoCut::on_is_activable(const GLCanvas3D::Selection& selection) const +{ + return selection.is_single_full_instance() && !selection.is_wipe_tower(); +} + +void GLGizmoCut::on_start_dragging(const GLCanvas3D::Selection& selection) +{ + if (m_hover_id == -1) { return; } + + const BoundingBoxf3& box = selection.get_bounding_box(); + m_start_z = m_cut_z; + m_max_z = box.size()(2) / 2.0; + m_drag_pos = m_grabbers[m_hover_id].center; + m_drag_center = box.center(); + m_drag_center(2) += m_cut_z; +} + +void GLGizmoCut::on_update(const UpdateData& data) +{ + if (m_hover_id != -1) { + // Clamp the plane to the object's bounding box + const double new_z = m_start_z + calc_projection(data.mouse_ray); + m_cut_z = std::max(-m_max_z, std::min(m_max_z, new_z)); + } +} + +void GLGizmoCut::on_render(const GLCanvas3D::Selection& selection) const +{ + if (m_grabbers[0].dragging) { + set_tooltip("Z: " + format(m_cut_z, 2)); + } + + const BoundingBoxf3& box = selection.get_bounding_box(); + Vec3d plane_center = box.center(); + plane_center(2) += m_cut_z; + + const float min_x = box.min(0) - Margin; + const float max_x = box.max(0) + Margin; + const float min_y = box.min(1) - Margin; + const float max_y = box.max(1) + Margin; + ::glEnable(GL_DEPTH_TEST); + ::glDisable(GL_CULL_FACE); + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Draw the cutting plane + ::glBegin(GL_QUADS); + ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); + ::glVertex3f(min_x, min_y, plane_center(2)); + ::glVertex3f(max_x, min_y, plane_center(2)); + ::glVertex3f(max_x, max_y, plane_center(2)); + ::glVertex3f(min_x, max_y, plane_center(2)); + ::glEnd(); + + ::glEnable(GL_CULL_FACE); + ::glDisable(GL_BLEND); + + // TODO: draw cut part contour? + + // Draw the grabber and the connecting line + m_grabbers[0].center = plane_center; + m_grabbers[0].center(2) = plane_center(2) + Offset; + + ::glDisable(GL_DEPTH_TEST); + ::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f); + ::glColor3f(1.0, 1.0, 0.0); + ::glBegin(GL_LINES); + ::glVertex3dv(plane_center.data()); + ::glVertex3dv(m_grabbers[0].center.data()); + ::glEnd(); + + std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_grabbers[0].color); + m_grabbers[0].render(m_hover_id == 0, box.max_size()); +} + +void GLGizmoCut::on_render_for_picking(const GLCanvas3D::Selection& selection) const +{ + ::glDisable(GL_DEPTH_TEST); + + render_grabbers_for_picking(selection.get_bounding_box()); +} + +void GLGizmoCut::perform_cut() +{ + const auto &selection = m_parent.get_selection(); + + const auto instance_idx = selection.get_instance_idx(); + const auto object_idx = selection.get_object_idx(); + + wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection"); + + wxGetApp().plater()->cut(object_idx, instance_idx, m_cut_z); +} + +double GLGizmoCut::calc_projection(const Linef3& mouse_ray) const +{ + double projection = 0.0; + + const Vec3d starting_vec = m_drag_pos - m_drag_center; + const double len_starting_vec = starting_vec.norm(); + if (len_starting_vec != 0.0) + { + Vec3d mouse_dir = mouse_ray.unit_vector(); + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // in our case plane normal and ray direction are the same (orthogonal view) + // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal + Vec3d inters = mouse_ray.a + (m_drag_pos - mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + // vector from the starting position to the found intersection + Vec3d inters_vec = inters - m_drag_pos; + + // finds projection of the vector along the staring direction + projection = inters_vec.dot(starting_vec.normalized()); + } + return projection; +} + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp new file mode 100644 index 0000000000..feebc00e38 --- /dev/null +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -0,0 +1,505 @@ +#ifndef slic3r_GLGizmo_hpp_ +#define slic3r_GLGizmo_hpp_ + +#include "../../slic3r/GUI/GLTexture.hpp" +#include "../../slic3r/GUI/GLCanvas3D.hpp" +#include "../../libslic3r/Point.hpp" +#include "../../libslic3r/BoundingBox.hpp" + +#include +#include +#include + + +class wxWindow; + + +namespace Slic3r { + +class BoundingBoxf3; +class Linef3; +class ModelObject; + +namespace GUI { + +class GLCanvas3D; + +class GLGizmoBase +{ +protected: + struct Grabber + { + static const float SizeFactor; + static const float MinHalfSize; + static const float DraggingScaleFactor; + + Vec3d center; + Vec3d angles; + float color[3]; + bool enabled; + bool dragging; + + Grabber(); + + void render(bool hover, float size) const; + void render_for_picking(float size) const { render(size, color, false); } + + float get_half_size(float size) const; + float get_dragging_half_size(float size) const; + + private: + void render(float size, const float* render_color, bool use_lighting) const; + void render_face(float half_size) const; + }; + +public: + enum EState + { + Off, + Hover, + On, + Num_States + }; + + struct UpdateData + { + const Linef3 mouse_ray; + const Point* mouse_pos; + bool shift_down; + + UpdateData(const Linef3& mouse_ray, const Point* mouse_pos = nullptr, bool shift_down = false) + : mouse_ray(mouse_ray), mouse_pos(mouse_pos), shift_down(shift_down) + {} + }; + +protected: + GLCanvas3D& m_parent; + + int m_group_id; + EState m_state; + // textures are assumed to be square and all with the same size in pixels, no internal check is done + GLTexture m_textures[Num_States]; + int m_hover_id; + bool m_dragging; + float m_base_color[3]; + float m_drag_color[3]; + float m_highlight_color[3]; + mutable std::vector m_grabbers; + +public: + explicit GLGizmoBase(GLCanvas3D& parent); + virtual ~GLGizmoBase() {} + + bool init() { return on_init(); } + + std::string get_name() const { return on_get_name(); } + + int get_group_id() const { return m_group_id; } + void set_group_id(int id) { m_group_id = id; } + + EState get_state() const { return m_state; } + void set_state(EState state) { m_state = state; on_set_state(); } + + bool is_activable(const GLCanvas3D::Selection& selection) const { return on_is_activable(selection); } + bool is_selectable() const { return on_is_selectable(); } + + unsigned int get_texture_id() const { return m_textures[m_state].get_id(); } + int get_textures_size() const { return m_textures[Off].get_width(); } + + int get_hover_id() const { return m_hover_id; } + void set_hover_id(int id); + + void set_highlight_color(const float* color); + + void enable_grabber(unsigned int id); + void disable_grabber(unsigned int id); + + void start_dragging(const GLCanvas3D::Selection& selection); + void stop_dragging(); + bool is_dragging() const { return m_dragging; } + + void update(const UpdateData& data); + +#if ENABLE_GIZMOS_RESET + void process_double_click() { on_process_double_click(); } +#endif // ENABLE_GIZMOS_RESET + + void render(const GLCanvas3D::Selection& selection) const { on_render(selection); } + void render_for_picking(const GLCanvas3D::Selection& selection) const { on_render_for_picking(selection); } + + virtual void create_external_gizmo_widgets(wxWindow *parent); + +protected: + virtual bool on_init() = 0; + virtual std::string on_get_name() const = 0; + virtual void on_set_state() {} + virtual void on_set_hover_id() {} + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return true; } + virtual bool on_is_selectable() const { return true; } + virtual void on_enable_grabber(unsigned int id) {} + virtual void on_disable_grabber(unsigned int id) {} + virtual void on_start_dragging(const GLCanvas3D::Selection& selection) {} + virtual void on_stop_dragging() {} + virtual void on_update(const UpdateData& data) = 0; +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click() {} +#endif // ENABLE_GIZMOS_RESET + virtual void on_render(const GLCanvas3D::Selection& selection) const = 0; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const = 0; + + float picking_color_component(unsigned int id) const; + void render_grabbers(const BoundingBoxf3& box) const; + void render_grabbers_for_picking(const BoundingBoxf3& box) const; + + void set_tooltip(const std::string& tooltip) const; + std::string format(float value, unsigned int decimals) const; +}; + +class GLGizmoRotate : public GLGizmoBase +{ + static const float Offset; + static const unsigned int CircleResolution; + static const unsigned int AngleResolution; + static const unsigned int ScaleStepsCount; + static const float ScaleStepRad; + static const unsigned int ScaleLongEvery; + static const float ScaleLongTooth; + static const unsigned int SnapRegionsCount; + static const float GrabberOffset; + +public: + enum Axis : unsigned char + { + X, + Y, + Z + }; + +private: + Axis m_axis; + double m_angle; + + mutable Vec3d m_center; + mutable float m_radius; + + mutable float m_snap_coarse_in_radius; + mutable float m_snap_coarse_out_radius; + mutable float m_snap_fine_in_radius; + mutable float m_snap_fine_out_radius; + +public: + GLGizmoRotate(GLCanvas3D& parent, Axis axis); + + double get_angle() const { return m_angle; } + void set_angle(double angle); + +protected: + virtual bool on_init(); + virtual std::string on_get_name() const { return ""; } + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); + virtual void on_update(const UpdateData& data); +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click() { m_angle = 0.0; } +#endif // ENABLE_GIZMOS_RESET + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; + +private: + void render_circle() const; + void render_scale() const; + void render_snap_radii() const; + void render_reference_radius() const; + void render_angle() const; + void render_grabber(const BoundingBoxf3& box) const; + + void transform_to_local() const; + // returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate + Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray) const; +}; + +class GLGizmoRotate3D : public GLGizmoBase +{ + std::vector m_gizmos; + +public: + explicit GLGizmoRotate3D(GLCanvas3D& parent); + + Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } + void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } + +protected: + virtual bool on_init(); + virtual std::string on_get_name() const; + virtual void on_set_state() + { + for (GLGizmoRotate& g : m_gizmos) + { + g.set_state(m_state); + } + } + virtual void on_set_hover_id() + { + for (unsigned int i = 0; i < 3; ++i) + { + m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); + } + } + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return !selection.is_wipe_tower(); } + virtual void on_enable_grabber(unsigned int id) + { + if ((0 <= id) && (id < 3)) + m_gizmos[id].enable_grabber(0); + } + virtual void on_disable_grabber(unsigned int id) + { + if ((0 <= id) && (id < 3)) + m_gizmos[id].disable_grabber(0); + } + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); + virtual void on_stop_dragging(); + virtual void on_update(const UpdateData& data) + { + for (GLGizmoRotate& g : m_gizmos) + { + g.update(data); + } + } +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click() + { + if (m_hover_id != -1) + m_gizmos[m_hover_id].process_double_click(); + } +#endif // ENABLE_GIZMOS_RESET + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const + { + for (const GLGizmoRotate& g : m_gizmos) + { + g.render_for_picking(selection); + } + } +}; + +class GLGizmoScale3D : public GLGizmoBase +{ + static const float Offset; + + mutable BoundingBoxf3 m_box; + + Vec3d m_scale; + + double m_snap_step; + + Vec3d m_starting_scale; + Vec3d m_starting_drag_position; + BoundingBoxf3 m_starting_box; + +public: + explicit GLGizmoScale3D(GLCanvas3D& parent); + + double get_snap_step(double step) const { return m_snap_step; } + void set_snap_step(double step) { m_snap_step = step; } + + const Vec3d& get_scale() const { return m_scale; } + void set_scale(const Vec3d& scale) { m_starting_scale = scale; m_scale = scale; } + +protected: + virtual bool on_init(); + virtual std::string on_get_name() const; + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return !selection.is_wipe_tower(); } + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); + virtual void on_update(const UpdateData& data); +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click(); +#endif // ENABLE_GIZMOS_RESET + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; + +private: + void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const; + + void do_scale_x(const UpdateData& data); + void do_scale_y(const UpdateData& data); + void do_scale_z(const UpdateData& data); + void do_scale_uniform(const UpdateData& data); + + double calc_ratio(const UpdateData& data) const; +}; + +class GLGizmoMove3D : public GLGizmoBase +{ + static const double Offset; + + Vec3d m_displacement; + + double m_snap_step; + + Vec3d m_starting_drag_position; + Vec3d m_starting_box_center; + Vec3d m_starting_box_bottom_center; + +public: + explicit GLGizmoMove3D(GLCanvas3D& parent); + + double get_snap_step(double step) const { return m_snap_step; } + void set_snap_step(double step) { m_snap_step = step; } + + const Vec3d& get_displacement() const { return m_displacement; } + +protected: + virtual bool on_init(); + virtual std::string on_get_name() const; + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); + virtual void on_stop_dragging(); + virtual void on_update(const UpdateData& data); + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; + +private: + double calc_projection(const UpdateData& data) const; +}; + +class GLGizmoFlatten : public GLGizmoBase +{ +// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. + +private: + mutable Vec3d m_normal; + + struct PlaneData { + std::vector vertices; + Vec3d normal; + float area; + }; + struct SourceDataSummary { + std::vector bounding_boxes; // bounding boxes of convex hulls of individual volumes + Vec3d mesh_first_point; + }; + + // This holds information to decide whether recalculation is necessary: + SourceDataSummary m_source_data; + + std::vector m_planes; + mutable Vec3d m_starting_center; + const ModelObject* m_model_object = nullptr; + std::vector instances_matrices; + + void update_planes(); + bool is_plane_update_necessary() const; + +public: + explicit GLGizmoFlatten(GLCanvas3D& parent); + + void set_flattening_data(const ModelObject* model_object); + Vec3d get_flattening_normal() const; + +protected: + virtual bool on_init(); + virtual std::string on_get_name() const; + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return (selection.is_from_single_object() && !selection.is_wipe_tower() && !selection.is_modifier()); } + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); + virtual void on_update(const UpdateData& data) {} + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; + virtual void on_set_state() + { + if (m_state == On && is_plane_update_necessary()) + update_planes(); + } +}; + +class GLGizmoSlaSupports : public GLGizmoBase +{ +private: + ModelObject* m_model_object = nullptr; + Transform3d m_model_object_matrix; + Vec3f unproject_on_mesh(const Vec2d& mouse_pos); + + Eigen::MatrixXf m_V; // vertices + Eigen::MatrixXi m_F; // facets indices + struct SourceDataSummary { + BoundingBoxf3 bounding_box; + Transform3d matrix; + Vec3d mesh_first_point; + }; + + // This holds information to decide whether recalculation is necessary: + SourceDataSummary m_source_data; + + mutable Vec3d m_starting_center; + +public: + explicit GLGizmoSlaSupports(GLCanvas3D& parent); + void set_model_object_ptr(ModelObject* model_object); + void clicked_on_object(const Vec2d& mouse_position); + void delete_current_grabber(bool delete_all); + +private: + bool on_init(); + void on_update(const UpdateData& data); + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; + + void render_grabbers(bool picking = false) const; + void render_tooltip_texture() const; + bool is_mesh_update_necessary() const; + void update_mesh(); + + mutable GLTexture m_tooltip_texture; + mutable GLTexture m_reset_texture; + +protected: + void on_set_state() override { + if (m_state == On && is_mesh_update_necessary()) { + update_mesh(); + } + } + + virtual std::string on_get_name() const; + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const; + virtual bool on_is_selectable() const; +}; + + +class GLGizmoCutPanel; + +class GLGizmoCut : public GLGizmoBase +{ + static const double Offset; + static const double Margin; + static const std::array GrabberColor; + + double m_cut_z; + double m_start_z; + double m_max_z; + Vec3d m_drag_pos; + Vec3d m_drag_center; + GLGizmoCutPanel *m_panel; + +public: + explicit GLGizmoCut(GLCanvas3D& parent); + + virtual void create_external_gizmo_widgets(wxWindow *parent); + +protected: + virtual bool on_init(); + virtual std::string on_get_name() const; + virtual void on_set_state(); + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const; + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); + virtual void on_update(const UpdateData& data); + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; + +private: + void perform_cut(); + double calc_projection(const Linef3& mouse_ray) const; +}; + + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmo_hpp_ + diff --git a/xs/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp similarity index 99% rename from xs/src/slic3r/GUI/GLShader.cpp rename to src/slic3r/GUI/GLShader.cpp index e2995f7c3e..623e02a057 100644 --- a/xs/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -135,7 +135,7 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char* return false; vs.seekg(0, vs.end); - int file_length = vs.tellg(); + int file_length = (int)vs.tellg(); vs.seekg(0, vs.beg); std::string vertex_shader(file_length, '\0'); vs.read(const_cast(vertex_shader.data()), file_length); @@ -149,7 +149,7 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char* return false; fs.seekg(0, fs.end); - file_length = fs.tellg(); + file_length = (int)fs.tellg(); fs.seekg(0, fs.beg); std::string fragment_shader(file_length, '\0'); fs.read(const_cast(fragment_shader.data()), file_length); diff --git a/xs/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp similarity index 100% rename from xs/src/slic3r/GUI/GLShader.hpp rename to src/slic3r/GUI/GLShader.hpp diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp similarity index 87% rename from xs/src/slic3r/GUI/GLTexture.cpp rename to src/slic3r/GUI/GLTexture.cpp index 18c9f5dea0..235e3d93b6 100644 --- a/xs/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -12,6 +12,8 @@ namespace Slic3r { namespace GUI { +GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } }; + GLTexture::GLTexture() : m_id(0) , m_width(0) @@ -128,6 +130,11 @@ const std::string& GLTexture::get_source() const } void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) +{ + render_sub_texture(tex_id, left, right, bottom, top, FullTextureUVs); +} + +void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const GLTexture::Quad_UVs& uvs) { ::glEnable(GL_BLEND); ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -138,10 +145,10 @@ void GLTexture::render_texture(unsigned int tex_id, float left, float right, flo ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); ::glBegin(GL_QUADS); - ::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(left, bottom); - ::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(right, bottom); - ::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(right, top); - ::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(left, top); + ::glTexCoord2f(uvs.left_bottom.u, uvs.left_bottom.v); ::glVertex2f(left, bottom); + ::glTexCoord2f(uvs.right_bottom.u, uvs.right_bottom.v); ::glVertex2f(right, bottom); + ::glTexCoord2f(uvs.right_top.u, uvs.right_top.v); ::glVertex2f(right, top); + ::glTexCoord2f(uvs.left_top.u, uvs.left_top.v); ::glVertex2f(left, top); ::glEnd(); ::glBindTexture(GL_TEXTURE_2D, 0); diff --git a/xs/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp similarity index 66% rename from xs/src/slic3r/GUI/GLTexture.hpp rename to src/slic3r/GUI/GLTexture.hpp index 3113fcab20..e027bd152f 100644 --- a/xs/src/slic3r/GUI/GLTexture.hpp +++ b/src/slic3r/GUI/GLTexture.hpp @@ -10,6 +10,23 @@ namespace GUI { class GLTexture { + public: + struct UV + { + float u; + float v; + }; + + struct Quad_UVs + { + UV left_bottom; + UV right_bottom; + UV right_top; + UV left_top; + }; + + static Quad_UVs FullTextureUVs; + protected: unsigned int m_id; int m_width; @@ -30,6 +47,7 @@ namespace GUI { const std::string& get_source() const; static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top); + static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs); protected: unsigned int _generate_mipmaps(wxImage& image); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp new file mode 100644 index 0000000000..0e99f88ff8 --- /dev/null +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -0,0 +1,743 @@ +#include "../../libslic3r/Point.hpp" +#include "GLToolbar.hpp" + +#include "../../libslic3r/libslic3r.h" +#include "../../slic3r/GUI/GLCanvas3D.hpp" + +#include + +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace GUI { + + +wxDEFINE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_VOLUMES, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent); + + +GLToolbarItem::Data::Data() + : name("") + , tooltip("") + , sprite_id(-1) + , is_toggable(false) +{ +} + +GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Data& data) + : m_type(type) + , m_state(Disabled) + , m_data(data) +{ +} + +GLToolbarItem::EState GLToolbarItem::get_state() const +{ + return m_state; +} + +void GLToolbarItem::set_state(GLToolbarItem::EState state) +{ + m_state = state; +} + +const std::string& GLToolbarItem::get_name() const +{ + return m_data.name; +} + +const std::string& GLToolbarItem::get_tooltip() const +{ + return m_data.tooltip; +} + +void GLToolbarItem::do_action(wxEvtHandler *target) +{ + wxPostEvent(target, SimpleEvent(m_data.action_event)); +} + +bool GLToolbarItem::is_enabled() const +{ + return m_state != Disabled; +} + +bool GLToolbarItem::is_hovered() const +{ + return (m_state == Hover) || (m_state == HoverPressed); +} + +bool GLToolbarItem::is_pressed() const +{ + return (m_state == Pressed) || (m_state == HoverPressed); +} + +bool GLToolbarItem::is_toggable() const +{ + return m_data.is_toggable; +} + +bool GLToolbarItem::is_separator() const +{ + return m_type == Separator; +} + +void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const +{ + GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(texture_size, border_size, icon_size, gap_size)); +} + +GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const +{ + GLTexture::Quad_UVs uvs; + + float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f; + + float scaled_icon_size = (float)icon_size * inv_texture_size; + float scaled_border_size = (float)border_size * inv_texture_size; + float scaled_gap_size = (float)gap_size * inv_texture_size; + float stride = scaled_icon_size + scaled_gap_size; + + float left = scaled_border_size + (float)m_state * stride; + float right = left + scaled_icon_size; + float top = scaled_border_size + (float)m_data.sprite_id * stride; + float bottom = top + scaled_icon_size; + + uvs.left_top = { left, top }; + uvs.left_bottom = { left, bottom }; + uvs.right_bottom = { right, bottom }; + uvs.right_top = { right, top }; + + return uvs; +} + +GLToolbar::ItemsIconsTexture::ItemsIconsTexture() + : items_icon_size(0) + , items_icon_border_size(0) + , items_icon_gap_size(0) +{ +} + +GLToolbar::Layout::Layout() + : type(Horizontal) + , top(0.0f) + , left(0.0f) + , separator_size(0.0f) + , gap_size(0.0f) +{ +} + +GLToolbar::GLToolbar(GLCanvas3D& parent) + : m_parent(parent) + , m_enabled(false) +{ +} + +GLToolbar::~GLToolbar() +{ + for (GLToolbarItem* item : m_items) + { + delete item; + } +} + +bool GLToolbar::init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size) +{ + std::string path = resources_dir() + "/icons/"; + bool res = !icons_texture_filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture_filename, false); + if (res) + { + m_icons_texture.items_icon_size = items_icon_size; + m_icons_texture.items_icon_border_size = items_icon_border_size; + m_icons_texture.items_icon_gap_size = items_icon_gap_size; + } + + return res; +} + +GLToolbar::Layout::Type GLToolbar::get_layout_type() const +{ + return m_layout.type; +} + +void GLToolbar::set_layout_type(GLToolbar::Layout::Type type) +{ + m_layout.type = type; +} + +void GLToolbar::set_position(float top, float left) +{ + m_layout.top = top; + m_layout.left = left; +} + +void GLToolbar::set_separator_size(float size) +{ + m_layout.separator_size = size; +} + +void GLToolbar::set_gap_size(float size) +{ + m_layout.gap_size = size; +} + +bool GLToolbar::is_enabled() const +{ + return m_enabled; +} + +void GLToolbar::set_enabled(bool enable) +{ + m_enabled = true; +} + +bool GLToolbar::add_item(const GLToolbarItem::Data& data) +{ + GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Action, data); + if (item == nullptr) + return false; + + m_items.push_back(item); + return true; +} + +bool GLToolbar::add_separator() +{ + GLToolbarItem::Data data; + GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Separator, data); + if (item == nullptr) + return false; + + m_items.push_back(item); + return true; +} + +float GLToolbar::get_width() const +{ + switch (m_layout.type) + { + default: + case Layout::Horizontal: + { + return get_width_horizontal(); + } + case Layout::Vertical: + { + return get_width_vertical(); + } + } +} + +float GLToolbar::get_height() const +{ + switch (m_layout.type) + { + default: + case Layout::Horizontal: + { + return get_height_horizontal(); + } + case Layout::Vertical: + { + return get_height_vertical(); + } + } +} + +void GLToolbar::enable_item(const std::string& name) +{ + for (GLToolbarItem* item : m_items) + { + if ((item->get_name() == name) && (item->get_state() == GLToolbarItem::Disabled)) + { + item->set_state(GLToolbarItem::Normal); + return; + } + } +} + +void GLToolbar::disable_item(const std::string& name) +{ + for (GLToolbarItem* item : m_items) + { + if (item->get_name() == name) + { + item->set_state(GLToolbarItem::Disabled); + return; + } + } +} + +bool GLToolbar::is_item_pressed(const std::string& name) const +{ + for (GLToolbarItem* item : m_items) + { + if (item->get_name() == name) + return item->is_pressed(); + } + + return false; +} + +void GLToolbar::update_hover_state(const Vec2d& mouse_pos) +{ + if (!m_enabled) + return; + + switch (m_layout.type) + { + default: + case Layout::Horizontal: + { + update_hover_state_horizontal(mouse_pos); + break; + } + case Layout::Vertical: + { + update_hover_state_vertical(mouse_pos); + break; + } + } +} + +int GLToolbar::contains_mouse(const Vec2d& mouse_pos) const +{ + if (!m_enabled) + return -1; + + switch (m_layout.type) + { + default: + case Layout::Horizontal: + { + return contains_mouse_horizontal(mouse_pos); + } + case Layout::Vertical: + { + return contains_mouse_vertical(mouse_pos); + } + } +} + +void GLToolbar::do_action(unsigned int item_id) +{ + if (item_id < (unsigned int)m_items.size()) + { + GLToolbarItem* item = m_items[item_id]; + if ((item != nullptr) && !item->is_separator() && item->is_hovered()) + { + if (item->is_toggable()) + { + GLToolbarItem::EState state = item->get_state(); + if (state == GLToolbarItem::Hover) + item->set_state(GLToolbarItem::HoverPressed); + else if (state == GLToolbarItem::HoverPressed) + item->set_state(GLToolbarItem::Hover); + + m_parent.render(); + item->do_action(m_parent.get_wxglcanvas()); + } + else + { + item->set_state(GLToolbarItem::HoverPressed); + m_parent.render(); + item->do_action(m_parent.get_wxglcanvas()); + if (item->get_state() != GLToolbarItem::Disabled) + { + // the item may get disabled during the action, if not, set it back to hover state + item->set_state(GLToolbarItem::Hover); + m_parent.render(); + } + } + } + } +} + +void GLToolbar::render() const +{ + if (!m_enabled || m_items.empty()) + return; + + ::glDisable(GL_DEPTH_TEST); + + ::glPushMatrix(); + ::glLoadIdentity(); + + switch (m_layout.type) + { + default: + case Layout::Horizontal: + { + render_horizontal(); + break; + } + case Layout::Vertical: + { + render_vertical(); + break; + } + } + + ::glPopMatrix(); +} + +float GLToolbar::get_width_horizontal() const +{ + return get_main_size(); +} + +float GLToolbar::get_width_vertical() const +{ + return m_icons_texture.items_icon_size; +} + +float GLToolbar::get_height_horizontal() const +{ + return m_icons_texture.items_icon_size; +} + +float GLToolbar::get_height_vertical() const +{ + return get_main_size(); +} + +float GLToolbar::get_main_size() const +{ + float size = 0.0f; + for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + { + if (m_items[i]->is_separator()) + size += m_layout.separator_size; + else + size += (float)m_icons_texture.items_icon_size; + } + + if (m_items.size() > 1) + size += ((float)m_items.size() - 1.0f) * m_layout.gap_size; + + return size; +} + +void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) +{ + float zoom = m_parent.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + Size cnv_size = m_parent.get_canvas_size(); + Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); + + float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_separator_size = m_layout.separator_size * inv_zoom; + float scaled_gap_size = m_layout.gap_size * inv_zoom; + + float separator_stride = scaled_separator_size + scaled_gap_size; + float icon_stride = scaled_icons_size + scaled_gap_size; + + float left = m_layout.left; + float top = m_layout.top; + + std::string tooltip = ""; + + for (GLToolbarItem* item : m_items) + { + if (item->is_separator()) + left += separator_stride; + else + { + float right = left + scaled_icons_size; + float bottom = top - scaled_icons_size; + + GLToolbarItem::EState state = item->get_state(); + bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); + + switch (state) + { + case GLToolbarItem::Normal: + { + if (inside) + item->set_state(GLToolbarItem::Hover); + + break; + } + case GLToolbarItem::Hover: + { + if (inside) + tooltip = item->get_tooltip(); + else + item->set_state(GLToolbarItem::Normal); + + break; + } + case GLToolbarItem::Pressed: + { + if (inside) + item->set_state(GLToolbarItem::HoverPressed); + + break; + } + case GLToolbarItem::HoverPressed: + { + if (inside) + tooltip = item->get_tooltip(); + else + item->set_state(GLToolbarItem::Pressed); + + break; + } + default: + case GLToolbarItem::Disabled: + { + break; + } + } + + left += icon_stride; + } + } + + if (!tooltip.empty()) + m_parent.set_tooltip(tooltip); +} + +void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) +{ + float zoom = m_parent.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + Size cnv_size = m_parent.get_canvas_size(); + Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); + + float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_separator_size = m_layout.separator_size * inv_zoom; + float scaled_gap_size = m_layout.gap_size * inv_zoom; + + float separator_stride = scaled_separator_size + scaled_gap_size; + float icon_stride = scaled_icons_size + scaled_gap_size; + + float left = m_layout.left; + float top = m_layout.top; + + std::string tooltip = ""; + + for (GLToolbarItem* item : m_items) + { + if (item->is_separator()) + top -= separator_stride; + else + { + float right = left + scaled_icons_size; + float bottom = top - scaled_icons_size; + + GLToolbarItem::EState state = item->get_state(); + bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); + + switch (state) + { + case GLToolbarItem::Normal: + { + if (inside) + item->set_state(GLToolbarItem::Hover); + + break; + } + case GLToolbarItem::Hover: + { + if (inside) + tooltip = item->get_tooltip(); + else + item->set_state(GLToolbarItem::Normal); + + break; + } + case GLToolbarItem::Pressed: + { + if (inside) + item->set_state(GLToolbarItem::HoverPressed); + + break; + } + case GLToolbarItem::HoverPressed: + { + if (inside) + tooltip = item->get_tooltip(); + else + item->set_state(GLToolbarItem::Pressed); + + break; + } + default: + case GLToolbarItem::Disabled: + { + break; + } + } + + top -= icon_stride; + } + } + + m_parent.set_tooltip(tooltip); +} + +int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos) const +{ + float zoom = m_parent.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + Size cnv_size = m_parent.get_canvas_size(); + Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); + + float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_separator_size = m_layout.separator_size * inv_zoom; + float scaled_gap_size = m_layout.gap_size * inv_zoom; + + float separator_stride = scaled_separator_size + scaled_gap_size; + float icon_stride = scaled_icons_size + scaled_gap_size; + + float left = m_layout.left; + float top = m_layout.top; + + int id = -1; + + for (GLToolbarItem* item : m_items) + { + ++id; + + if (item->is_separator()) + left += separator_stride; + else + { + float right = left + scaled_icons_size; + float bottom = top - scaled_icons_size; + + if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + return id; + + left += icon_stride; + } + } + + return -1; +} + +int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos) const +{ + float zoom = m_parent.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + Size cnv_size = m_parent.get_canvas_size(); + Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); + + float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_separator_size = m_layout.separator_size * inv_zoom; + float scaled_gap_size = m_layout.gap_size * inv_zoom; + + float separator_stride = scaled_separator_size + scaled_gap_size; + float icon_stride = scaled_icons_size + scaled_gap_size; + + float left = m_layout.left; + float top = m_layout.top; + + int id = -1; + + for (GLToolbarItem* item : m_items) + { + ++id; + + if (item->is_separator()) + top -= separator_stride; + else + { + float right = left + scaled_icons_size; + float bottom = top - scaled_icons_size; + + if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + return id; + + top -= icon_stride; + } + } + + return -1; +} + +void GLToolbar::render_horizontal() const +{ + unsigned int tex_id = m_icons_texture.texture.get_id(); + int tex_size = m_icons_texture.texture.get_width(); + + if ((tex_id == 0) || (tex_size <= 0)) + return; + + float zoom = m_parent.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_separator_size = m_layout.separator_size * inv_zoom; + float scaled_gap_size = m_layout.gap_size * inv_zoom; + + float separator_stride = scaled_separator_size + scaled_gap_size; + float icon_stride = scaled_icons_size + scaled_gap_size; + + float left = m_layout.left; + float top = m_layout.top; + + // renders icons + for (const GLToolbarItem* item : m_items) + { + if (item->is_separator()) + left += separator_stride; + else + { + item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); + left += icon_stride; + } + } +} + +void GLToolbar::render_vertical() const +{ + unsigned int tex_id = m_icons_texture.texture.get_id(); + int tex_size = m_icons_texture.texture.get_width(); + + if ((tex_id == 0) || (tex_size <= 0)) + return; + + float zoom = m_parent.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_separator_size = m_layout.separator_size * inv_zoom; + float scaled_gap_size = m_layout.gap_size * inv_zoom; + + float separator_stride = scaled_separator_size + scaled_gap_size; + float icon_stride = scaled_icons_size + scaled_gap_size; + + float left = m_layout.left; + float top = m_layout.top; + + // renders icons + for (const GLToolbarItem* item : m_items) + { + if (item->is_separator()) + top -= separator_stride; + else + { + item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); + top -= icon_stride; + } + } +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp new file mode 100644 index 0000000000..dde62c6452 --- /dev/null +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -0,0 +1,190 @@ +#ifndef slic3r_GLToolbar_hpp_ +#define slic3r_GLToolbar_hpp_ + +#include +#include +#include + +#include "GLTexture.hpp" +#include "Event.hpp" + + +class wxEvtHandler; + +namespace Slic3r { +namespace GUI { + +class GLCanvas3D; + +wxDECLARE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_VOLUMES, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent); + +class GLToolbarItem +{ +public: + enum EType : unsigned char + { + Action, + Separator, + Num_Types + }; + + enum EState : unsigned char + { + Normal, + Pressed, + Disabled, + Hover, + HoverPressed, + Num_States + }; + + struct Data + { + std::string name; + std::string tooltip; + unsigned int sprite_id; + bool is_toggable; + wxEventType action_event; + + Data(); + }; + +private: + EType m_type; + EState m_state; + Data m_data; + +public: + GLToolbarItem(EType type, const Data& data); + + EState get_state() const; + void set_state(EState state); + + const std::string& get_name() const; + const std::string& get_tooltip() const; + + void do_action(wxEvtHandler *target); + + bool is_enabled() const; + bool is_hovered() const; + bool is_pressed() const; + + bool is_toggable() const; + bool is_separator() const; + + void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const; + +private: + GLTexture::Quad_UVs get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const; +}; + +class GLToolbar +{ +public: + // items icon textures are assumed to be square and all with the same size in pixels, no internal check is done + // icons are layed-out into the texture starting from the top-left corner in the same order as enum GLToolbarItem::EState + // from left to right + struct ItemsIconsTexture + { + GLTexture texture; + // size of the square icons, in pixels + unsigned int items_icon_size; + // distance from the border, in pixels + unsigned int items_icon_border_size; + // distance between two adjacent icons (to avoid filtering artifacts), in pixels + unsigned int items_icon_gap_size; + + ItemsIconsTexture(); + }; + + struct Layout + { + enum Type : unsigned char + { + Horizontal, + Vertical, + Num_Types + }; + + Type type; + float top; + float left; + float separator_size; + float gap_size; + + Layout(); + }; + +private: + typedef std::vector ItemsList; + + GLCanvas3D& m_parent; + bool m_enabled; + ItemsIconsTexture m_icons_texture; + Layout m_layout; + + ItemsList m_items; + +public: + explicit GLToolbar(GLCanvas3D& parent); + ~GLToolbar(); + + bool init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size); + + Layout::Type get_layout_type() const; + void set_layout_type(Layout::Type type); + + void set_position(float top, float left); + void set_separator_size(float size); + void set_gap_size(float size); + + bool is_enabled() const; + void set_enabled(bool enable); + + bool add_item(const GLToolbarItem::Data& data); + bool add_separator(); + + float get_width() const; + float get_height() const; + + void enable_item(const std::string& name); + void disable_item(const std::string& name); + + bool is_item_pressed(const std::string& name) const; + + void update_hover_state(const Vec2d& mouse_pos); + + // returns the id of the item under the given mouse position or -1 if none + int contains_mouse(const Vec2d& mouse_pos) const; + + void do_action(unsigned int item_id); + + void render() const; + +private: + float get_width_horizontal() const; + float get_width_vertical() const; + float get_height_horizontal() const; + float get_height_vertical() const; + float get_main_size() const; + void update_hover_state_horizontal(const Vec2d& mouse_pos); + void update_hover_state_vertical(const Vec2d& mouse_pos); + int contains_mouse_horizontal(const Vec2d& mouse_pos) const; + int contains_mouse_vertical(const Vec2d& mouse_pos) const; + + void render_horizontal() const; + void render_vertical() const; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLToolbar_hpp_ diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp new file mode 100644 index 0000000000..5828e9a225 --- /dev/null +++ b/src/slic3r/GUI/GUI.cpp @@ -0,0 +1,455 @@ +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "WipeTowerDialog.hpp" + +#include + +#include +#include + +#if __APPLE__ +#import +#elif _WIN32 +#include +// Undefine min/max macros incompatible with the standard library +// For example, std::numeric_limits::max() +// produces some weird errors +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif +#include "boost/nowide/convert.hpp" +#endif + +#include + +#include "wxExtensions.hpp" +#include "GUI_Preview.hpp" +#include "GUI_PreviewIface.hpp" +#include "AboutDialog.hpp" +#include "AppConfig.hpp" +#include "ConfigWizard.hpp" +#include "PresetBundle.hpp" +#include "UpdateDialogs.hpp" + +#include "../../libslic3r/Utils.hpp" +#include "../../libslic3r/Print.hpp" + +namespace Slic3r { namespace GUI { + +#if __APPLE__ +IOPMAssertionID assertionID; +#endif + +void disable_screensaver() +{ + #if __APPLE__ + CFStringRef reasonForActivity = CFSTR("Slic3r"); + IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, + kIOPMAssertionLevelOn, reasonForActivity, &assertionID); + // ignore result: success == kIOReturnSuccess + #elif _WIN32 + SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS); + #endif +} + +void enable_screensaver() +{ + #if __APPLE__ + IOReturn success = IOPMAssertionRelease(assertionID); + #elif _WIN32 + SetThreadExecutionState(ES_CONTINUOUS); + #endif +} + +bool debugged() +{ + #ifdef _WIN32 + return IsDebuggerPresent() == TRUE; + #else + return false; + #endif /* _WIN32 */ +} + +void break_to_debugger() +{ + #ifdef _WIN32 + if (IsDebuggerPresent()) + DebugBreak(); + #endif /* _WIN32 */ +} + +PreviewIface* g_preview = nullptr; + +bool config_wizard_startup(bool app_config_exists) +{ + if (!app_config_exists || wxGetApp().preset_bundle->printers.size() <= 1) { + config_wizard(ConfigWizard::RR_DATA_EMPTY); + return true; + } else if (get_app_config()->legacy_datadir()) { + // Looks like user has legacy pre-vendorbundle data directory, + // explain what this is and run the wizard + + MsgDataLegacy dlg; + dlg.ShowModal(); + + config_wizard(ConfigWizard::RR_DATA_LEGACY); + return true; + } + return false; +} + +void config_wizard(int reason) +{ + // Exit wizard if there are unsaved changes and the user cancels the action. + if (! wxGetApp().check_unsaved_changes()) + return; + + try { + ConfigWizard wizard(nullptr, static_cast(reason)); + wizard.run(wxGetApp().preset_bundle, wxGetApp().preset_updater); + } + catch (const std::exception &e) { + show_error(nullptr, e.what()); + } + + // Load the currently selected preset into the GUI, update the preset selection box. + wxGetApp().load_current_presets(); +} + +PreviewIface* create_preview_iface(wxNotebook* parent, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) +{ + if (g_preview == nullptr) + { + Preview* panel = new Preview(parent, config, print, gcode_preview_data); + g_preview = new PreviewIface(panel); + } + + return g_preview; +} + +// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element) +void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) +{ + try{ + switch (config.def()->get(opt_key)->type) { + case coFloatOrPercent:{ + std::string str = boost::any_cast(value); + bool percent = false; + if (str.back() == '%') { + str.pop_back(); + percent = true; + } + double val = stod(str); + config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(val, percent)); + break;} + case coPercent: + config.set_key_value(opt_key, new ConfigOptionPercent(boost::any_cast(value))); + break; + case coFloat:{ + double& val = config.opt_float(opt_key); + val = boost::any_cast(value); + break; + } + case coPercents:{ + ConfigOptionPercents* vec_new = new ConfigOptionPercents{ boost::any_cast(value) }; + config.option(opt_key)->set_at(vec_new, opt_index, opt_index); + break; + } + case coFloats:{ + ConfigOptionFloats* vec_new = new ConfigOptionFloats{ boost::any_cast(value) }; + config.option(opt_key)->set_at(vec_new, opt_index, opt_index); + break; + } + case coString: + config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast(value))); + break; + case coStrings:{ + if (opt_key.compare("compatible_printers") == 0) { + config.option(opt_key)->values = + boost::any_cast>(value); + } + else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0) { + std::string str = boost::any_cast(value); + if (str.back() == ';') str.pop_back(); + // 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. + std::vector values; + boost::split(values, str, boost::is_any_of(";")); + if (values.size() == 1 && values[0] == "") + break; + config.option(opt_key)->values = values; + } + else{ + ConfigOptionStrings* vec_new = new ConfigOptionStrings{ boost::any_cast(value) }; + config.option(opt_key)->set_at(vec_new, opt_index, 0); + } + } + break; + case coBool: + config.set_key_value(opt_key, new ConfigOptionBool(boost::any_cast(value))); + break; + case coBools:{ + ConfigOptionBools* vec_new = new ConfigOptionBools{ boost::any_cast(value) != 0 }; + config.option(opt_key)->set_at(vec_new, opt_index, 0); + break;} + case coInt: + config.set_key_value(opt_key, new ConfigOptionInt(boost::any_cast(value))); + break; + case coInts:{ + ConfigOptionInts* vec_new = new ConfigOptionInts{ boost::any_cast(value) }; + config.option(opt_key)->set_at(vec_new, opt_index, 0); + } + break; + case coEnum:{ + if (opt_key.compare("external_fill_pattern") == 0 || + opt_key.compare("fill_pattern") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("gcode_flavor") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("support_material_pattern") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("seam_position") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("host_type") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + } + break; + case coPoints:{ + if (opt_key.compare("bed_shape") == 0) { + config.option(opt_key)->values = boost::any_cast>(value); + break; + } + ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast(value) }; + config.option(opt_key)->set_at(vec_new, opt_index, 0); + } + break; + case coNone: + break; + default: + break; + } + } + catch (const std::exception & /* e */) + { + int i = 0;//no reason, just experiment + } +} + +void show_error(wxWindow* parent, const wxString& message) +{ + ErrorDialog msg(parent, message); + msg.ShowModal(); +} + +void show_error_id(int id, const std::string& message) +{ + auto *parent = id != 0 ? wxWindow::FindWindowById(id) : nullptr; + show_error(parent, wxString::FromUTF8(message.data())); +} + +void show_info(wxWindow* parent, const wxString& message, const wxString& title) +{ + wxMessageDialog msg_wingow(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION); + msg_wingow.ShowModal(); +} + +void warning_catcher(wxWindow* parent, const wxString& message) +{ + if (message == "GLUquadricObjPtr | " + _(L("Attempt to free unreferenced scalar")) ) + return; + wxMessageDialog msg(parent, message, _(L("Warning")), wxOK | wxICON_WARNING); + msg.ShowModal(); +} + +void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value) +{ + if (comboCtrl == nullptr) + return; + + wxCheckListBoxComboPopup* popup = new wxCheckListBoxComboPopup; + if (popup != nullptr) + { + // FIXME If the following line is removed, the combo box popup list will not react to mouse clicks. + // On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10. + comboCtrl->UseAltPopupWindow(); + + comboCtrl->EnablePopupAnimation(false); + comboCtrl->SetPopupControl(popup); + popup->SetStringValue(from_u8(text)); + popup->Bind(wxEVT_CHECKLISTBOX, [popup](wxCommandEvent& evt) { popup->OnCheckListBox(evt); }); + popup->Bind(wxEVT_LISTBOX, [popup](wxCommandEvent& evt) { popup->OnListBoxSelection(evt); }); + popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); }); + popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); }); + + std::vector items_str; + boost::split(items_str, items, boost::is_any_of("|"), boost::token_compress_off); + + for (const std::string& item : items_str) + { + popup->Append(from_u8(item)); + } + + for (unsigned int i = 0; i < popup->GetCount(); ++i) + { + popup->Check(i, initial_value); + } + } +} + +int combochecklist_get_flags(wxComboCtrl* comboCtrl) +{ + int flags = 0; + + wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup); + if (popup != nullptr) + { + for (unsigned int i = 0; i < popup->GetCount(); ++i) + { + if (popup->IsChecked(i)) + flags |= 1 << i; + } + } + + return flags; +} + +AppConfig* get_app_config() +{ + return wxGetApp().app_config; +} + +wxString L_str(const std::string &str) +{ + //! Explicitly specify that the source string is already in UTF-8 encoding + return wxGetTranslation(wxString(str.c_str(), wxConvUTF8)); +} + +wxString from_u8(const std::string &str) +{ + return wxString::FromUTF8(str.c_str()); +} + +std::string into_u8(const wxString &str) +{ + auto buffer_utf8 = str.utf8_str(); + return std::string(buffer_utf8.data()); +} + +bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height) +{ + const auto idx = wxDisplay::GetFromWindow(window); + if (idx == wxNOT_FOUND) { + return false; + } + + wxDisplay display(idx); + const auto disp_size = display.GetClientArea(); + width = disp_size.GetWidth(); + height = disp_size.GetHeight(); + + return true; +} + +void save_window_size(wxTopLevelWindow *window, const std::string &name) +{ + const wxSize size = window->GetSize(); + const wxPoint pos = window->GetPosition(); + const auto maximized = window->IsMaximized() ? "1" : "0"; + + get_app_config()->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str()); + get_app_config()->set((boost::format("window_%1%_maximized") % name).str(), maximized); +} + +void restore_window_size(wxTopLevelWindow *window, const std::string &name) +{ + // XXX: This still doesn't behave nicely in some situations (mostly on Linux). + // The problem is that it's hard to obtain window position with respect to screen geometry reliably + // from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which + // it's actually visible. I suspect this has something to do with window initialization (maybe we + // restore window geometry too early), but haven't yet found a workaround. + + const auto display_idx = wxDisplay::GetFromWindow(window); + if (display_idx == wxNOT_FOUND) { return; } + + const auto display = wxDisplay(display_idx).GetClientArea(); + std::vector pair; + + try { + const auto key_size = (boost::format("window_%1%_size") % name).str(); + if (get_app_config()->has(key_size)) { + if (unescape_strings_cstyle(get_app_config()->get(key_size), pair) && pair.size() == 2) { + auto width = boost::lexical_cast(pair[0]); + auto height = boost::lexical_cast(pair[1]); + + window->SetSize(width, height); + } + } + } catch(const boost::bad_lexical_cast &) {} + + // Maximizing should be the last thing to do. + // This ensure the size and position are sane when the user un-maximizes the window. + const auto key_maximized = (boost::format("window_%1%_maximized") % name).str(); + if (get_app_config()->get(key_maximized) == "1") { + window->Maximize(true); + } +} + +void about() +{ + AboutDialog dlg; + dlg.ShowModal(); + dlg.Destroy(); +} + +void desktop_open_datadir_folder() +{ + // Execute command to open a file explorer, platform dependent. + // FIXME: The const_casts aren't needed in wxWidgets 3.1, remove them when we upgrade. + + const auto path = data_dir(); +#ifdef _WIN32 + const auto widepath = wxString::FromUTF8(path.data()); + const wchar_t *argv[] = { L"explorer", widepath.GetData(), nullptr }; + ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr); +#elif __APPLE__ + const char *argv[] = { "open", path.data(), nullptr }; + ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr); +#else + const char *argv[] = { "xdg-open", path.data(), nullptr }; + + // Check if we're running in an AppImage container, if so, we need to remove AppImage's env vars, + // because they may mess up the environment expected by the file manager. + // Mostly this is about LD_LIBRARY_PATH, but we remove a few more too for good measure. + if (wxGetEnv("APPIMAGE", nullptr)) { + // We're running from AppImage + wxEnvVariableHashMap env_vars; + wxGetEnvMap(&env_vars); + + env_vars.erase("APPIMAGE"); + env_vars.erase("APPDIR"); + env_vars.erase("LD_LIBRARY_PATH"); + env_vars.erase("LD_PRELOAD"); + env_vars.erase("UNION_PRELOAD"); + + wxExecuteEnv exec_env; + exec_env.env = std::move(env_vars); + + wxString owd; + if (wxGetEnv("OWD", &owd)) { + // This is the original work directory from which the AppImage image was run, + // set it as CWD for the child process: + exec_env.cwd = std::move(owd); + } + + ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr, &exec_env); + } else { + // Looks like we're NOT running from AppImage, we'll make no changes to the environment. + ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr, nullptr); + } +#endif +} + +} } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp similarity index 51% rename from xs/src/slic3r/GUI/GUI.hpp rename to src/slic3r/GUI/GUI.hpp index 68dbdfe848..cbc20c3c1a 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -1,40 +1,28 @@ #ifndef slic3r_GUI_hpp_ #define slic3r_GUI_hpp_ -#include -#include #include "Config.hpp" -#include "../../libslic3r/Utils.hpp" +#include "callback.hpp" #include -#include -class wxApp; class wxWindow; -class wxFrame; -class wxFont; class wxMenuBar; class wxNotebook; class wxComboCtrl; -class wxString; -class wxArrayString; -class wxArrayLong; -class wxColour; -class wxBoxSizer; -class wxFlexGridSizer; -class wxButton; class wxFileDialog; class wxTopLevelWindow; namespace Slic3r { -class PresetBundle; -class PresetCollection; class AppConfig; -class PresetUpdater; class DynamicPrintConfig; -class TabIface; -class _3DScene; +class PreviewIface; +class Print; +class GCodePreviewData; +class AppControllerBase; + +using AppControllerPtr = std::shared_ptr; #define _(s) Slic3r::GUI::I18N::translate((s)) @@ -61,60 +49,19 @@ namespace GUI { namespace I18N { namespace GUI { -class Tab; -class ConfigOptionsGroup; -// Map from an file_type name to full file wildcard name. -typedef std::map t_file_wild_card; -inline t_file_wild_card& get_file_wild_card() { - static t_file_wild_card FILE_WILDCARDS; - if (FILE_WILDCARDS.empty()){ - FILE_WILDCARDS["known"] = "Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA"; - FILE_WILDCARDS["stl"] = "STL files (*.stl)|*.stl;*.STL"; - FILE_WILDCARDS["obj"] = "OBJ files (*.obj)|*.obj;*.OBJ"; - FILE_WILDCARDS["amf"] = "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML"; - FILE_WILDCARDS["3mf"] = "3MF files (*.3mf)|*.3mf;*.3MF;"; - FILE_WILDCARDS["prusa"] = "Prusa Control files (*.prusa)|*.prusa;*.PRUSA"; - FILE_WILDCARDS["ini"] = "INI files *.ini|*.ini;*.INI"; - FILE_WILDCARDS["gcode"] = "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC"; - FILE_WILDCARDS["svg"] = "SVG files *.svg|*.svg;*.SVG"; - } - return FILE_WILDCARDS; -} - void disable_screensaver(); void enable_screensaver(); bool debugged(); void break_to_debugger(); -// Passing the wxWidgets GUI classes instantiated by the Perl part to C++. -void set_wxapp(wxApp *app); -void set_main_frame(wxFrame *main_frame); -void set_tab_panel(wxNotebook *tab_panel); -void set_app_config(AppConfig *app_config); -void set_preset_bundle(PresetBundle *preset_bundle); -void set_preset_updater(PresetUpdater *updater); -void set_3DScene(_3DScene *scene); +AppConfig* get_app_config(); -AppConfig* get_app_config(); -wxApp* get_app(); -PresetBundle* get_preset_bundle(); - -const wxColour& get_label_clr_modified(); -const wxColour& get_label_clr_sys(); -const wxColour& get_label_clr_default(); -unsigned get_colour_approx_luma(const wxColour &colour); -void set_label_clr_modified(const wxColour& clr); -void set_label_clr_sys(const wxColour& clr); - -const wxFont& small_font(); -const wxFont& bold_font(); +AppControllerPtr get_appctl(); +void set_cli_appctl(); +void set_gui_appctl(); extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change); -// This is called when closing the application, when loading a config file or when starting the config wizard -// to notify the user whether he is aware that some preset changes will be lost. -extern bool check_unsaved_changes(); - // Checks if configuration wizard needs to run, calls config_wizard if so. // Returns whether the Wizard ran. extern bool config_wizard_startup(bool app_config_exists); @@ -123,39 +70,16 @@ extern bool config_wizard_startup(bool app_config_exists); // The run_reason argument is actually ConfigWizard::RunReason, but int is used here because of Perl. extern void config_wizard(int run_reason); -// Create "Preferences" dialog after selecting menu "Preferences" in Perl part -extern void open_preferences_dialog(int event_preferences); +PreviewIface* create_preview_iface(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data); -// Create a new preset tab (print, filament and printer), -void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed); -TabIface* get_preset_tab_iface(char *name); - -// add it at the end of the tab panel. -void add_created_tab(Tab* panel); // Change option value in config void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); -// Update UI / Tabs to reflect changes in the currently loaded presets -void load_current_presets(); - void show_error(wxWindow* parent, const wxString& message); void show_error_id(int id, const std::string& message); // For Perl void show_info(wxWindow* parent, const wxString& message, const wxString& title); void warning_catcher(wxWindow* parent, const wxString& message); -// load language saved at application config -bool load_language(); -// save language at application config -void save_language(); -// get list of installed languages -void get_installed_languages(wxArrayString & names, wxArrayLong & identifiers); -// select language from the list of installed languages -bool select_language(wxArrayString & names, wxArrayLong & identifiers); - -std::vector& get_tabs_list(); -bool checked_tab(Tab* tab); -void delete_tab_from_list(Tab* tab); - // Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items. // Items are all initialized to the given value. // Items must be separated by '|', for example "Item1|Item2|Item3", and so on. @@ -169,18 +93,11 @@ int combochecklist_get_flags(wxComboCtrl* comboCtrl); wxString L_str(const std::string &str); // Return wxString from std::string in UTF8 wxString from_u8(const std::string &str); - - -void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer); +// Return std::string in UTF8 from wxString +std::string into_u8(const wxString &str); // Callback to trigger a configuration update timer on the Plater. static PerlCallback g_on_request_update_callback; - -ConfigOptionsGroup* get_optgroup(); -wxButton* get_wiping_dialog_button(); - -void add_export_option(wxFileDialog* dlg, const std::string& format); -int get_export_option(wxFileDialog* dlg); // Returns the dimensions of the screen on which the main frame is displayed bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp new file mode 100644 index 0000000000..33fdb87793 --- /dev/null +++ b/src/slic3r/GUI/GUI_App.cpp @@ -0,0 +1,739 @@ +#include "GUI_App.hpp" +#include "GUI_ObjectList.hpp" +#include "GUI_ObjectManipulation.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Utils.hpp" +#include "GUI.hpp" +#include "GUI_Utils.hpp" +#include "AppConfig.hpp" +#include "PresetBundle.hpp" +#include "3DScene.hpp" +#include "Model.hpp" + +#include "../Utils/PresetUpdater.hpp" +#include "ConfigWizard_private.hpp" +#include "slic3r/Config/Snapshot.hpp" +#include "ConfigSnapshotDialog.hpp" +#include "FirmwareDialog.hpp" +#include "Preferences.hpp" +#include "Tab.hpp" +#include +#include +#include "SysInfoDialog.hpp" + +namespace Slic3r { +namespace GUI { + + +const wxString file_wildcards[FT_SIZE] = { + /* FT_STL */ "STL files (*.stl)|*.stl;*.STL", + /* FT_OBJ */ "OBJ files (*.obj)|*.obj;*.OBJ", + /* FT_AMF */ "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML", + /* FT_3MF */ "3MF files (*.3mf)|*.3mf;*.3MF;", + /* FT_PRUSA */ "Prusa Control files (*.prusa)|*.prusa;*.PRUSA", + /* FT_GCODE */ "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC", + /* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA", + + /* FT_INI */ "INI files *.ini|*.ini;*.INI", + /* FT_SVG */ "SVG files *.svg|*.svg;*.SVG", + /* FT_PNGZIP */"Zipped PNG files *.zip|*.zip;*.ZIP", // This is lame, but that's what we use for SLA +}; + + +static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } + +IMPLEMENT_APP(GUI_App) +bool GUI_App::OnInit() +{ + SetAppName("Slic3rPE-alpha"); + SetAppDisplayName("Slic3r Prusa Edition"); + +// Slic3r::debugf "wxWidgets version %s, Wx version %s\n", wxVERSION_STRING, wxVERSION; + + // Set the Slic3r data directory at the Slic3r XS module. + // Unix: ~/ .Slic3r + // Windows : "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" + // Mac : "~/Library/Application Support/Slic3r" + if (data_dir().empty()) + set_data_dir(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data()); + + app_config = new AppConfig(); + preset_bundle = new PresetBundle(); + + // just checking for existence of Slic3r::data_dir is not enough : it may be an empty directory + // supplied as argument to --datadir; in that case we should still run the wizard + // eval{ + preset_bundle->setup_directories(); + // }; + // if ($@) { + // warn $@ . "\n"; + // fatal_error(undef, $@); + // } + app_conf_exists = app_config->exists(); + // load settings + if (app_conf_exists) app_config->load(); + app_config->set("version", SLIC3R_VERSION); + app_config->save(); + + preset_updater = new PresetUpdater(); + + load_language(); + + // Suppress the '- default -' presets. + preset_bundle->set_default_suppressed(app_config->get("no_defaults") == "1"); + try { + preset_bundle->load_presets(*app_config); + } catch (const std::exception & /* ex */) { + // warn $@ . "\n"; + // show_error(undef, $@); + } + + // Let the libslic3r know the callback, which will translate messages on demand. + Slic3r::I18N::set_translate_callback(libslic3r_translate_callback); + // initialize label colors and fonts + init_label_colours(); + init_fonts(); + + // application frame + std::cerr << "Creating main frame..." << std::endl; + // wxImage::FindHandlerType(wxBITMAP_TYPE_PNG) || + wxImage::AddHandler(new wxPNGHandler()); + mainframe = new MainFrame(no_plater, false); + sidebar().obj_list()->init_objects(); // propagate model objects to object list + update_mode(); + SetTopWindow(mainframe); + + CallAfter([this]() { + // temporary workaround for the correct behavior of the Scrolled sidebar panel + auto& panel = sidebar(); + if (panel.obj_list()->GetMinHeight() > 200) { + wxWindowUpdateLocker noUpdates_sidebar(&panel); + panel.obj_list()->SetMinSize(wxSize(-1, 200)); + panel.Layout(); + } + }); + + // This makes CallAfter() work + Bind(wxEVT_IDLE, [this](wxIdleEvent& event) + { + std::function cur_cb{ nullptr }; + // try to get the mutex. If we can't, just skip this idle event and get the next one. + if (!callback_register.try_lock()) return; + // pop callback + if (m_cb.size() != 0) { + cur_cb = m_cb.top(); + m_cb.pop(); + } + // unlock mutex + this->callback_register.unlock(); + + try { // call the function if it's not nullptr; + if (cur_cb != nullptr) cur_cb(); + } + catch (std::exception& e) { + std::cerr << "Exception thrown: " << e.what() << std::endl; + } + + if (app_config->dirty()) + app_config->save(); + }); + + // On OS X the UI tends to freeze in weird ways if modal dialogs(config wizard, update notifications, ...) + // are shown before or in the same event callback with the main frame creation. + // Therefore we schedule them for later using CallAfter. + CallAfter([this]() { + // eval{ + if (!preset_updater->config_update()) + mainframe->Close(); + // }; + // if ($@) { + // show_error(undef, $@); + // mainframe->Close(); + // } + }); + + CallAfter([this]() { + if (!config_wizard_startup(app_conf_exists)) { + // Only notify if there was not wizard so as not to bother too much ... + preset_updater->slic3r_update_notify(); + } + preset_updater->sync(preset_bundle); + }); + + + mainframe->Show(true); + return true; +} + +unsigned GUI_App::get_colour_approx_luma(const wxColour &colour) +{ + double r = colour.Red(); + double g = colour.Green(); + double b = colour.Blue(); + + return std::round(std::sqrt( + r * r * .241 + + g * g * .691 + + b * b * .068 + )); +} + +void GUI_App::init_label_colours() +{ + auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + if (luma >= 128) { + m_color_label_modified = wxColour(252, 77, 1); + m_color_label_sys = wxColour(26, 132, 57); + } + else { + m_color_label_modified = wxColour(253, 111, 40); + m_color_label_sys = wxColour(115, 220, 103); + } + m_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); +} + +void GUI_App::update_label_colours_from_appconfig() +{ + if (app_config->has("label_clr_sys")) { + auto str = app_config->get("label_clr_sys"); + if (str != "") + m_color_label_sys = wxColour(str); + } + + if (app_config->has("label_clr_modified")) { + auto str = app_config->get("label_clr_modified"); + if (str != "") + m_color_label_modified = wxColour(str); + } +} + +void GUI_App::init_fonts() +{ + m_small_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + m_bold_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); +#ifdef __WXMAC__ + m_small_font.SetPointSize(11); + m_bold_font.SetPointSize(13); +#endif /*__WXMAC__*/ +} + +void GUI_App::set_label_clr_modified(const wxColour& clr) { + m_color_label_modified = clr; + auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue()); + std::string str = clr_str.ToStdString(); + app_config->set("label_clr_modified", str); + app_config->save(); +} + +void GUI_App::set_label_clr_sys(const wxColour& clr) { + m_color_label_sys = clr; + auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue()); + std::string str = clr_str.ToStdString(); + app_config->set("label_clr_sys", str); + app_config->save(); +} + +void GUI_App::recreate_GUI() +{ + std::cerr << "recreate_GUI" << std::endl; + + auto topwindow = GetTopWindow(); + mainframe = new MainFrame(no_plater,false); + sidebar().obj_list()->init_objects(); // propagate model objects to object list + update_mode(); + + if (topwindow) { + SetTopWindow(mainframe); + topwindow->Destroy(); + } + + // On OSX the UI was not initialized correctly if the wizard was called + // before the UI was up and running. + CallAfter([]() { + // Run the config wizard, don't offer the "reset user profile" checkbox. + config_wizard_startup(true); + }); +} + +void GUI_App::system_info() +{ + SysInfoDialog dlg; + dlg.ShowModal(); + dlg.Destroy(); +} + +// static method accepting a wxWindow object as first parameter +bool GUI_App::catch_error(std::function cb, + // wxMessageDialog* message_dialog, + const std::string& err /*= ""*/) +{ + if (!err.empty()) { + if (cb) + cb(); + // if (message_dialog) + // message_dialog->(err, "Error", wxOK | wxICON_ERROR); + show_error(/*this*/nullptr, err); + return true; + } + return false; +} + +// static method accepting a wxWindow object as first parameter +void fatal_error(wxWindow* parent) +{ + show_error(parent, ""); + // exit 1; // #ys_FIXME +} + +// Called after the Preferences dialog is closed and the program settings are saved. +// Update the UI based on the current preferences. +void GUI_App::update_ui_from_settings() +{ + mainframe->update_ui_from_settings(); +} + +#if ENABLE_NEW_MENU_LAYOUT +void GUI_App::load_project(wxWindow *parent, wxString& input_file) +{ + input_file.Clear(); + wxFileDialog dialog(parent ? parent : GetTopWindow(), + _(L("Choose one file (3MF):")), + app_config->get_last_dir(), "", + file_wildcards[FT_3MF], wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + if (dialog.ShowModal() == wxID_OK) + input_file = dialog.GetPath(); +} + +void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) +#else +void GUI_App::open_model(wxWindow *parent, wxArrayString& input_files) +#endif // ENABLE_NEW_MENU_LAYOUT +{ + input_files.Clear(); + wxFileDialog dialog(parent ? parent : GetTopWindow(), + _(L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):")), + app_config->get_last_dir(), "", + file_wildcards[FT_MODEL], wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); + + if (dialog.ShowModal() == wxID_OK) + dialog.GetPaths(input_files); +} + +void GUI_App::CallAfter(std::function cb) +{ + // set mutex + callback_register.lock(); + // push function onto stack + m_cb.emplace(cb); + // unset mutex + callback_register.unlock(); +} + +void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name) +{ + if (name.empty()) { return; } + const auto config_key = (boost::format("window_%1%") % name).str(); + + WindowMetrics metrics = WindowMetrics::from_window(window); + app_config->set(config_key, metrics.serialize()); + app_config->save(); +} + +void GUI_App::window_pos_restore(wxTopLevelWindow* window, const std::string &name) +{ + if (name.empty()) { return; } + const auto config_key = (boost::format("window_%1%") % name).str(); + + if (! app_config->has(config_key)) { return; } + + auto metrics = WindowMetrics::deserialize(app_config->get(config_key)); + if (! metrics) { return; } + + window->SetSize(metrics->get_rect()); + window->Maximize(metrics->get_maximized()); +} + +void GUI_App::window_pos_sanitize(wxTopLevelWindow* window) +{ + const auto display_idx = wxDisplay::GetFromWindow(window); + if (display_idx == wxNOT_FOUND) { return; } + + const auto display = wxDisplay(display_idx).GetClientArea(); + + auto metrics = WindowMetrics::from_window(window); + + metrics.sanitize_for_display(display); + if (window->GetScreenRect() != metrics.get_rect()) { + window->SetSize(metrics.get_rect()); + } +} + +// select language from the list of installed languages +bool GUI_App::select_language( wxArrayString & names, + wxArrayLong & identifiers) +{ + wxCHECK_MSG(names.Count() == identifiers.Count(), false, + _(L("Array of language names and identifiers should have the same size."))); + int init_selection = 0; + long current_language = m_wxLocale ? m_wxLocale->GetLanguage() : wxLANGUAGE_UNKNOWN; + for (auto lang : identifiers) { + if (lang == current_language) + break; + ++init_selection; + } + if (init_selection == identifiers.size()) + init_selection = 0; + long index = wxGetSingleChoiceIndex(_(L("Select the language")), _(L("Language")), + names, init_selection); + if (index != -1) + { + m_wxLocale = new wxLocale; + m_wxLocale->Init(identifiers[index]); + m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); + m_wxLocale->AddCatalog(GetAppName()); + wxSetlocale(LC_NUMERIC, "C"); + Preset::update_suffix_modified(); + return true; + } + return false; +} + +// load language saved at application config +bool GUI_App::load_language() +{ + wxString language = wxEmptyString; + if (app_config->has("translation_language")) + language = app_config->get("translation_language"); + + if (language.IsEmpty()) + return false; + wxArrayString names; + wxArrayLong identifiers; + get_installed_languages(names, identifiers); + for (size_t i = 0; i < identifiers.Count(); i++) + { + if (wxLocale::GetLanguageCanonicalName(identifiers[i]) == language) + { + m_wxLocale = new wxLocale; + m_wxLocale->Init(identifiers[i]); + m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); + m_wxLocale->AddCatalog(GetAppName()); + wxSetlocale(LC_NUMERIC, "C"); + Preset::update_suffix_modified(); + return true; + } + } + return false; +} + +// save language at application config +void GUI_App::save_language() +{ + wxString language = wxEmptyString; + if (m_wxLocale) + language = m_wxLocale->GetCanonicalName(); + + app_config->set("translation_language", language.ToStdString()); + app_config->save(); +} + +// get list of installed languages +void GUI_App::get_installed_languages(wxArrayString & names, wxArrayLong & identifiers) +{ + names.Clear(); + identifiers.Clear(); + + wxDir dir(localization_dir()); + wxString filename; + const wxLanguageInfo * langinfo; + wxString name = wxLocale::GetLanguageName(wxLANGUAGE_DEFAULT); + if (!name.IsEmpty()) + { + names.Add(_(L("Default"))); + identifiers.Add(wxLANGUAGE_DEFAULT); + } + for (bool cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS); + cont; cont = dir.GetNext(&filename)) + { + langinfo = wxLocale::FindLanguageInfo(filename); + if (langinfo != NULL) + { + auto full_file_name = dir.GetName() + wxFileName::GetPathSeparator() + + filename + wxFileName::GetPathSeparator() + + GetAppName() + wxT(".mo"); + if (wxFileExists(full_file_name)) + { + names.Add(langinfo->Description); + identifiers.Add(langinfo->Language); + } + } + } +} + +Tab* GUI_App::get_tab(Preset::Type type) +{ + for (Tab* tab: tabs_list) + if (tab->type() == type) + return tab; + return nullptr; +} + +ConfigMenuIDs GUI_App::get_view_mode() +{ + if (!app_config->has("view_mode")) + return ConfigMenuModeSimple; + + const auto mode = app_config->get("view_mode"); + return mode == "expert" ? ConfigMenuModeExpert : + mode == "simple" ? ConfigMenuModeSimple : ConfigMenuModeAdvanced; +} + +// Update view mode according to selected menu +void GUI_App::update_mode() +{ + wxWindowUpdateLocker noUpdates(&sidebar()); + + ConfigMenuIDs mode = wxGetApp().get_view_mode(); + + obj_list()->get_sizer()->Show(mode == ConfigMenuModeExpert); + sidebar().set_mode_value(mode); + sidebar().show_buttons(mode == ConfigMenuModeExpert); + obj_list()->update_selections(); + + sidebar().Layout(); + + ConfigOptionMode opt_mode = mode == ConfigMenuModeSimple ? comSimple : + mode == ConfigMenuModeExpert ? comExpert : comAdvanced; + for (auto tab : tabs_list) + tab->update_visibility(opt_mode); +} + +void GUI_App::add_config_menu(wxMenuBar *menu) +{ + auto local_menu = new wxMenu(); + wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt); + + const auto config_wizard_name = _(ConfigWizard::name().wx_str()); + const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), config_wizard_name); + // Cmd+, is standard on OS X - what about other operating systems? + local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip); + local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots")) + dots, _(L("Inspect / activate configuration snapshots"))); + local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot"))); + // local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates"))); + local_menu->AppendSeparator(); + local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences")) + dots + "\tCtrl+,", _(L("Application preferences"))); + local_menu->AppendSeparator(); + auto mode_menu = new wxMenu(); + mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("&Simple")), _(L("Simple View Mode"))); + mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _(L("&Advanced")), _(L("Advanced View Mode"))); + mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _(L("&Expert")), _(L("Expert View Mode"))); + mode_menu->Check(config_id_base + get_view_mode(), true); + local_menu->AppendSubMenu(mode_menu, _(L("&Mode")), _(L("Slic3r View Mode"))); + local_menu->AppendSeparator(); + local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language"))); + local_menu->AppendSeparator(); + local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _(L("Flash printer firmware")), _(L("Upload a firmware image into an Arduino based printer"))); + // TODO: for when we're able to flash dictionaries + // local_menu->Append(config_id_base + FirmwareMenuDict, _(L("Flash language file")), _(L("Upload a language dictionary file into a Prusa printer"))); + + local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) { + switch (event.GetId() - config_id_base) { + case ConfigMenuWizard: + config_wizard(ConfigWizard::RR_USER); + break; + case ConfigMenuTakeSnapshot: + // Take a configuration snapshot. + if (check_unsaved_changes()) { + wxTextEntryDialog dlg(nullptr, _(L("Taking configuration snapshot")), _(L("Snapshot name"))); + if (dlg.ShowModal() == wxID_OK) + app_config->set("on_snapshot", + Slic3r::GUI::Config::SnapshotDB::singleton().take_snapshot( + *app_config, Slic3r::GUI::Config::Snapshot::SNAPSHOT_USER, dlg.GetValue().ToUTF8().data()).id); + } + break; + case ConfigMenuSnapshots: + if (check_unsaved_changes()) { + std::string on_snapshot; + if (Config::SnapshotDB::singleton().is_on_snapshot(*app_config)) + on_snapshot = app_config->get("on_snapshot"); + ConfigSnapshotDialog dlg(Slic3r::GUI::Config::SnapshotDB::singleton(), on_snapshot); + dlg.ShowModal(); + if (!dlg.snapshot_to_activate().empty()) { + if (!Config::SnapshotDB::singleton().is_on_snapshot(*app_config)) + Config::SnapshotDB::singleton().take_snapshot(*app_config, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK); + app_config->set("on_snapshot", + Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *app_config).id); + preset_bundle->load_presets(*app_config); + // Load the currently selected preset into the GUI, update the preset selection box. + load_current_presets(); + } + } + break; + case ConfigMenuPreferences: + { + PreferencesDialog dlg(mainframe); + dlg.ShowModal(); + break; + } + case ConfigMenuLanguage: + { + wxArrayString names; + wxArrayLong identifiers; + get_installed_languages(names, identifiers); + if (select_language(names, identifiers)) { + save_language(); + show_info(mainframe->m_tabpanel, _(L("Application will be restarted")), _(L("Attention!"))); + _3DScene::remove_all_canvases();// remove all canvas before recreate GUI + recreate_GUI(); + } + break; + } + case ConfigMenuFlashFirmware: + FirmwareDialog::run(mainframe); + break; + default: + break; + } + }); + mode_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent& event) { + int id_mode = event.GetId() - config_id_base; + std::string mode = id_mode == ConfigMenuModeExpert ? "expert" : + id_mode == ConfigMenuModeSimple ? "simple" : "advanced"; + app_config->set("view_mode", mode); + app_config->save(); + update_mode(); + }); + menu->Append(local_menu, _(L("&Configuration"))); +} + +// This is called when closing the application, when loading a config file or when starting the config wizard +// to notify the user whether he is aware that some preset changes will be lost. +bool GUI_App::check_unsaved_changes() +{ + std::string dirty; + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + for (Tab *tab : tabs_list) + if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) + if (dirty.empty()) + dirty = tab->name(); + else + dirty += std::string(", ") + tab->name(); + if (dirty.empty()) + // No changes, the application may close or reload presets. + return true; + // Ask the user. + auto dialog = new wxMessageDialog(mainframe, + _(L("You have unsaved changes ")) + dirty + _(L(". Discard changes and continue anyway?")), + _(L("Unsaved Presets")), + wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT); + return dialog->ShowModal() == wxID_YES; +} + +bool GUI_App::checked_tab(Tab* tab) +{ + bool ret = true; + if (find(tabs_list.begin(), tabs_list.end(), tab) == tabs_list.end()) + ret = false; + return ret; +} + +void GUI_App::delete_tab_from_list(Tab* tab) +{ + std::vector::iterator itr = find(tabs_list.begin(), tabs_list.end(), tab); + if (itr != tabs_list.end()) + tabs_list.erase(itr); +} + +// Update UI / Tabs to reflect changes in the currently loaded presets +void GUI_App::load_current_presets() +{ + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + for (Tab *tab : tabs_list) + if (tab->supports_printer_technology(printer_technology)) { + if (tab->name() == "printer") + static_cast(tab)->update_pages(); + tab->load_current_preset(); + } +} + +Sidebar& GUI_App::sidebar() +{ + return plater_->sidebar(); +} + +ObjectManipulation* GUI_App::obj_manipul() +{ + return sidebar().obj_manipul(); +} + +ObjectSettings* GUI_App::obj_settings() +{ + return sidebar().obj_settings(); +} + +ObjectList* GUI_App::obj_list() +{ + return sidebar().obj_list(); +} + +Plater* GUI_App::plater() +{ + return plater_; +} + +wxGLCanvas* GUI_App::canvas3D() +{ + return plater_->canvas3D(); +} + +ModelObjectPtrs* GUI_App::model_objects() +{ + return &plater_->model().objects; +} + +wxNotebook* GUI_App::tab_panel() const +{ + return mainframe->m_tabpanel; +} + +// static method accepting a wxWindow object as first parameter +// void warning_catcher{ +// my($self, $message_dialog) = @_; +// return sub{ +// my $message = shift; +// return if $message = ~/ GLUquadricObjPtr | Attempt to free unreferenced scalar / ; +// my @params = ($message, 'Warning', wxOK | wxICON_WARNING); +// $message_dialog +// ? $message_dialog->(@params) +// : Wx::MessageDialog->new($self, @params)->ShowModal; +// }; +// } + +// Do we need this function??? +// void GUI_App::notify(message) { +// auto frame = GetTopWindow(); +// // try harder to attract user attention on OS X +// if (!frame->IsActive()) +// frame->RequestUserAttention(defined(__WXOSX__/*&Wx::wxMAC */)? wxUSER_ATTENTION_ERROR : wxUSER_ATTENTION_INFO); +// +// // There used to be notifier using a Growl application for OSX, but Growl is dead. +// // The notifier also supported the Linux X D - bus notifications, but that support was broken. +// //TODO use wxNotificationMessage ? +// } + + +} // GUI +} //Slic3r diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp new file mode 100644 index 0000000000..dcd9b7ad37 --- /dev/null +++ b/src/slic3r/GUI/GUI_App.hpp @@ -0,0 +1,161 @@ +#ifndef slic3r_GUI_App_hpp_ +#define slic3r_GUI_App_hpp_ + +#include +#include "PrintConfig.hpp" +#include "MainFrame.hpp" + +#include +#include +#include + +#include +#include + +class wxMenuItem; +class wxMenuBar; +class wxTopLevelWindow; +class wxNotebook; + +namespace Slic3r { +class AppConfig; +class PresetBundle; +class PresetUpdater; +class ModelObject; + +namespace GUI +{ + +enum FileType +{ + FT_STL, + FT_OBJ, + FT_AMF, + FT_3MF, + FT_PRUSA, + FT_GCODE, + FT_MODEL, + + FT_INI, + FT_SVG, + FT_PNGZIP, + + FT_SIZE, +}; + +extern const wxString file_wildcards[FT_SIZE]; + +enum ConfigMenuIDs { + ConfigMenuWizard, + ConfigMenuSnapshots, + ConfigMenuTakeSnapshot, + ConfigMenuUpdate, + ConfigMenuPreferences, + ConfigMenuModeSimple, + ConfigMenuModeAdvanced, + ConfigMenuModeExpert, + ConfigMenuLanguage, + ConfigMenuFlashFirmware, + ConfigMenuCnt, +}; + +class Tab; + +static wxString dots("…", wxConvUTF8); + +class GUI_App : public wxApp +{ + bool no_plater{ false }; + bool app_conf_exists{ false }; + + // Lock to guard the callback stack + std::mutex callback_register; + // callbacks registered to run during idle event. + std::stack> m_cb{}; + + wxColour m_color_label_modified; + wxColour m_color_label_sys; + wxColour m_color_label_default; + + wxFont m_small_font; + wxFont m_bold_font; + + wxLocale* m_wxLocale{ nullptr }; + +public: + bool OnInit() override; + GUI_App() : wxApp() {} + + unsigned get_colour_approx_luma(const wxColour &colour); + void init_label_colours(); + void update_label_colours_from_appconfig(); + void init_fonts(); + void set_label_clr_modified(const wxColour& clr); + void set_label_clr_sys(const wxColour& clr); + + const wxColour& get_label_clr_modified(){ return m_color_label_modified; } + const wxColour& get_label_clr_sys() { return m_color_label_sys; } + const wxColour& get_label_clr_default() { return m_color_label_default; } + + const wxFont& small_font() { return m_small_font; } + const wxFont& bold_font() { return m_bold_font; } + + void recreate_GUI(); + void system_info(); +#if ENABLE_NEW_MENU_LAYOUT + void load_project(wxWindow *parent, wxString& input_file); + void import_model(wxWindow *parent, wxArrayString& input_files); +#else + void open_model(wxWindow *parent, wxArrayString& input_files); +#endif // ENABLE_NEW_MENU_LAYOUT + static bool catch_error(std::function cb, +// wxMessageDialog* message_dialog, + const std::string& err); +// void notify(/*message*/); + void update_ui_from_settings(); + void CallAfter(std::function cb); + + void window_pos_save(wxTopLevelWindow* window, const std::string &name); + void window_pos_restore(wxTopLevelWindow* window, const std::string &name); + void window_pos_sanitize(wxTopLevelWindow* window); + + bool select_language(wxArrayString & names, wxArrayLong & identifiers); + bool load_language(); + void save_language(); + void get_installed_languages(wxArrayString & names, wxArrayLong & identifiers); + + Tab* get_tab(Preset::Type type); + ConfigMenuIDs get_view_mode(); + void update_mode(); + + void add_config_menu(wxMenuBar *menu); + bool check_unsaved_changes(); + bool checked_tab(Tab* tab); + void delete_tab_from_list(Tab* tab); + void load_current_presets(); + + Sidebar& sidebar(); + ObjectManipulation* obj_manipul(); + ObjectSettings* obj_settings(); + ObjectList* obj_list(); + Plater* plater(); + wxGLCanvas* canvas3D(); + std::vector *model_objects(); + + AppConfig* app_config{ nullptr }; + PresetBundle* preset_bundle{ nullptr }; + PresetUpdater* preset_updater{ nullptr }; + MainFrame* mainframe{ nullptr }; + Plater* plater_{ nullptr }; + + wxNotebook* tab_panel() const ; + + std::vector tabs_list; + +}; +DECLARE_APP(GUI_App) + +} // GUI +} //Slic3r + +#endif // slic3r_GUI_App_hpp_ diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp new file mode 100644 index 0000000000..06f1907d8b --- /dev/null +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -0,0 +1,1505 @@ +#include "GUI_ObjectList.hpp" +#include "GUI_ObjectManipulation.hpp" +#include "GUI_App.hpp" + +#include "OptionsGroup.hpp" +#include "PresetBundle.hpp" +#include "Tab.hpp" +#include "wxExtensions.hpp" +#include "Model.hpp" +#include "LambdaObjectDialog.hpp" +#include "GLCanvas3D.hpp" + +#include +#include "slic3r/Utils/FixModelByWin10.hpp" + +namespace Slic3r +{ +namespace GUI +{ + +wxDEFINE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent); + +ObjectList::ObjectList(wxWindow* parent) : + wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE) +{ + // Fill CATEGORY_ICON + { + CATEGORY_ICON[L("Layers and Perimeters")] = wxBitmap(from_u8(var("layers.png")), wxBITMAP_TYPE_PNG); + CATEGORY_ICON[L("Infill")] = wxBitmap(from_u8(var("infill.png")), wxBITMAP_TYPE_PNG); + CATEGORY_ICON[L("Support material")] = wxBitmap(from_u8(var("building.png")), wxBITMAP_TYPE_PNG); + CATEGORY_ICON[L("Speed")] = wxBitmap(from_u8(var("time.png")), wxBITMAP_TYPE_PNG); + CATEGORY_ICON[L("Extruders")] = wxBitmap(from_u8(var("funnel.png")), wxBITMAP_TYPE_PNG); + CATEGORY_ICON[L("Extrusion Width")] = wxBitmap(from_u8(var("funnel.png")), wxBITMAP_TYPE_PNG); +// CATEGORY_ICON[L("Skirt and brim")] = wxBitmap(from_u8(var("box.png")), wxBITMAP_TYPE_PNG); +// CATEGORY_ICON[L("Speed > Acceleration")] = wxBitmap(from_u8(var("time.png")), wxBITMAP_TYPE_PNG); + CATEGORY_ICON[L("Advanced")] = wxBitmap(from_u8(var("wand.png")), wxBITMAP_TYPE_PNG); + } + + // create control + create_objects_ctrl(); + + init_icons(); + + // describe control behavior + Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) { + selection_changed(); +#ifndef __WXMSW__ + set_tooltip_for_item(get_mouse_position_in_control()); +#endif //__WXMSW__ + }); + + Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, [this](wxDataViewEvent& event) { + context_menu(); + }); + + Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX + +#ifdef __WXMSW__ + // Extruder value changed + Bind(wxEVT_CHOICE, [this](wxCommandEvent& event) { update_extruder_in_config(event.GetString()); }); + + GetMainWindow()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) { + set_tooltip_for_item(/*event.GetPosition()*/get_mouse_position_in_control()); + event.Skip(); + }); +#else + // equivalent to wxEVT_CHOICE on __WXMSW__ + Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, [this](wxDataViewEvent& e) { item_value_change(e); }); +#endif //__WXMSW__ + + Bind(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, [this](wxDataViewEvent& e) {on_begin_drag(e); }); + Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, [this](wxDataViewEvent& e) {on_drop_possible(e); }); + Bind(wxEVT_DATAVIEW_ITEM_DROP, [this](wxDataViewEvent& e) {on_drop(e); }); + + Bind(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED,[this](wxCommandEvent& e) {last_volume_is_deleted(e.GetInt()); }); +} + +ObjectList::~ObjectList() +{ + if (m_default_config) + delete m_default_config; +} + +void ObjectList::create_objects_ctrl() +{ + // temporary workaround for the correct behavior of the Scrolled sidebar panel: + // 1. set a height of the list to some big value + // 2. change it to the normal min value (200) after first whole App updating/layouting + SetMinSize(wxSize(-1, 1500)); // #ys_FIXME + + m_sizer = new wxBoxSizer(wxVERTICAL); + m_sizer->Add(this, 1, wxGROW | wxLEFT, 20); + + m_objects_model = new PrusaObjectDataViewModel; + AssociateModel(m_objects_model); + m_objects_model->SetAssociatedControl(this); +#if wxUSE_DRAG_AND_DROP && wxUSE_UNICODE + EnableDragSource(wxDF_UNICODETEXT); + EnableDropTarget(wxDF_UNICODETEXT); +#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE + + // column 0(Icon+Text) of the view control: + // And Icon can be consisting of several bitmaps + AppendColumn(new wxDataViewColumn(_(L("Name")), new PrusaBitmapTextRenderer(), + 0, 200, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE)); + + // column 1 of the view control: + AppendColumn(create_objects_list_extruder_column(4)); + + // column 2 of the view control: + AppendBitmapColumn(" ", 2, wxDATAVIEW_CELL_INERT, 25, + wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); +} + +void ObjectList::set_tooltip_for_item(const wxPoint& pt) +{ + wxDataViewItem item; + wxDataViewColumn* col; + HitTest(pt, item, col); + if (!item) return; + + if (col->GetTitle() == " " && GetSelectedItemsCount()<2) + GetMainWindow()->SetToolTip(_(L("Right button click the icon to change the object settings"))); + else if (col->GetTitle() == _("Name") && + m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.GetRefData()) { + int obj_idx = m_objects_model->GetIdByItem(item); + auto& stats = (*m_objects)[obj_idx]->volumes[0]->mesh.stl.stats; + int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + + stats.facets_added + stats.facets_reversed + stats.backwards_edges; + + wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors):\n")), errors); + + std::map error_msg; + error_msg[L("degenerate facets")] = stats.degenerate_facets; + error_msg[L("edges fixed")] = stats.edges_fixed; + error_msg[L("facets removed")] = stats.facets_removed; + error_msg[L("facets added")] = stats.facets_added; + error_msg[L("facets reversed")] = stats.facets_reversed; + error_msg[L("backwards edges")] = stats.backwards_edges; + + for (auto error : error_msg) + { + if (error.second > 0) + tooltip += wxString::Format(_("\t%d %s\n"), error.second, error.first); + } +// OR +// tooltip += wxString::Format(_(L("%d degenerate facets, %d edges fixed, %d facets removed, " +// "%d facets added, %d facets reversed, %d backwards edges")), +// stats.degenerate_facets, stats.edges_fixed, stats.facets_removed, +// stats.facets_added, stats.facets_reversed, stats.backwards_edges); + + if (is_windows10()) + tooltip += _(L("Right button click the icon to fix STL through Netfabb")); + + GetMainWindow()->SetToolTip(tooltip); + } + else + GetMainWindow()->SetToolTip(""); // hide tooltip +} + +wxPoint ObjectList::get_mouse_position_in_control() +{ + const wxPoint& pt = wxGetMousePosition(); +// wxWindow* win = GetMainWindow(); +// wxPoint screen_pos = win->GetScreenPosition(); + return wxPoint(pt.x - /*win->*/GetScreenPosition().x, pt.y - /*win->*/GetScreenPosition().y); +} + +int ObjectList::get_selected_obj_idx() const +{ + if (GetSelectedItemsCount() == 1) + return m_objects_model->GetIdByItem(m_objects_model->GetTopParent(GetSelection())); + + return -1; +} + +wxDataViewColumn* ObjectList::create_objects_list_extruder_column(int extruders_count) +{ + wxArrayString choices; + choices.Add("default"); + for (int i = 1; i <= extruders_count; ++i) + choices.Add(wxString::Format("%d", i)); + wxDataViewChoiceRenderer *c = + new wxDataViewChoiceRenderer(choices, wxDATAVIEW_CELL_EDITABLE, wxALIGN_CENTER_HORIZONTAL); + wxDataViewColumn* column = new wxDataViewColumn(_(L("Extruder")), c, 1, 80, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); + return column; +} + +void ObjectList::update_objects_list_extruder_column(int extruders_count) +{ + if (!this) return; // #ys_FIXME + if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) + extruders_count = 1; + + // delete old 2nd column + DeleteColumn(GetColumn(1)); + // insert new created 3rd column + InsertColumn(1, create_objects_list_extruder_column(extruders_count)); + // set show/hide for this column + set_extruder_column_hidden(extruders_count <= 1); +} + +void ObjectList::set_extruder_column_hidden(bool hide) +{ + GetColumn(1)->SetHidden(hide); +} + +void ObjectList::update_extruder_in_config(const wxString& selection) +{ + if (!m_config || selection.empty()) + return; + + int extruder = selection.size() > 1 ? 0 : atoi(selection.c_str()); + m_config->set_key_value("extruder", new ConfigOptionInt(extruder)); + + // update scene + wxGetApp().plater()->update(); +} + +void ObjectList::init_icons() +{ + m_bmp_modifiermesh = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG); + m_bmp_solidmesh = wxBitmap(from_u8(var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG); + + m_bmp_support_enforcer = wxBitmap(from_u8(var("support_enforcer_.png")), wxBITMAP_TYPE_PNG); + m_bmp_support_blocker = wxBitmap(from_u8(var("support_blocker_.png")), wxBITMAP_TYPE_PNG); + + m_bmp_vector.reserve(4); // bitmaps for different types of parts + m_bmp_vector.push_back(&m_bmp_solidmesh); // Add part + m_bmp_vector.push_back(&m_bmp_modifiermesh); // Add modifier + m_bmp_vector.push_back(&m_bmp_support_enforcer); // Add support enforcer + m_bmp_vector.push_back(&m_bmp_support_blocker); // Add support blocker + m_objects_model->SetVolumeBitmaps(m_bmp_vector); + + // init icon for manifold warning + m_bmp_manifold_warning = wxBitmap(from_u8(var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG); + + // init bitmap for "Split to sub-objects" context menu + m_bmp_split = wxBitmap(from_u8(var("split.png")), wxBITMAP_TYPE_PNG); + + // init bitmap for "Add Settings" context menu + m_bmp_cog = wxBitmap(from_u8(var("cog.png")), wxBITMAP_TYPE_PNG); +} + + +void ObjectList::selection_changed() +{ + if (m_prevent_list_events) return; + + fix_multiselection_conflicts(); + + // update object selection on Plater + update_selections_on_canvas(); + + // to update the toolbar and info sizer + if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) { + auto event = SimpleEvent(EVT_OBJ_LIST_OBJECT_SELECT); + event.SetEventObject(this); + wxPostEvent(this, event); + } + + part_selection_changed(); + +#ifdef __WXOSX__ + update_extruder_in_config(m_selected_extruder); +#endif //__WXOSX__ +} + +void ObjectList::context_menu() +{ + wxDataViewItem item; + wxDataViewColumn* col; + const wxPoint pt = get_mouse_position_in_control(); + HitTest(pt, item, col); + if (!item) +#ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX + // after Yosemite OS X version, HitTest return undefined item + item = GetSelection(); + if (item) + show_context_menu(); + else + printf("undefined item\n"); + return; +#else + return; +#endif // __WXOSX__ + const wxString title = col->GetTitle(); + + if (title == " ") + show_context_menu(); + + else if (title == _("Name") && pt.x >15 && + m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.GetRefData()) + { + if (is_windows10()) { + const auto obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); + wxGetApp().plater()->fix_through_netfabb(obj_idx); + } + } +#ifndef __WXMSW__ + GetMainWindow()->SetToolTip(""); // hide tooltip +#endif //__WXMSW__ +} + +void ObjectList::show_context_menu() +{ + const auto item = GetSelection(); + if (item) + { + if (!(m_objects_model->GetItemType(item) & (itObject | itVolume))) + return; + const auto menu = m_objects_model->GetParent(item) == wxDataViewItem(0) ? + create_add_part_popupmenu() : + create_part_settings_popupmenu(); + wxGetApp().tab_panel()->GetPage(0)->PopupMenu(menu); + } +} + + +void ObjectList::key_event(wxKeyEvent& event) +{ + if (event.GetKeyCode() == WXK_TAB) + Navigate(event.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward); + else if (event.GetKeyCode() == WXK_DELETE +#ifdef __WXOSX__ + || event.GetKeyCode() == WXK_BACK +#endif //__WXOSX__ + ) { + printf("WXK_BACK\n"); + remove(); + } + else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL)) + select_item_all_children(); + else + event.Skip(); +} + +void ObjectList::item_value_change(wxDataViewEvent& event) +{ + if (event.GetColumn() == 1) + { + wxVariant variant; + m_objects_model->GetValue(variant, event.GetItem(), 1); +#ifdef __WXOSX__ + m_selected_extruder = variant.GetString(); +#else // --> for Linux + update_extruder_in_config(variant.GetString()); +#endif //__WXOSX__ + } +} + +struct draging_item_data +{ + int obj_idx; + int vol_idx; +}; + +void ObjectList::on_begin_drag(wxDataViewEvent &event) +{ + wxDataViewItem item(event.GetItem()); + + // only allow drags for item, not containers + if (multiple_selection() || + m_objects_model->GetParent(item) == wxDataViewItem(0) || + m_objects_model->GetItemType(item) != itVolume ) { + event.Veto(); + return; + } + + /* Under MSW or OSX, DnD moves an item to the place of another selected item + * But under GTK, DnD moves an item between another two items. + * And as a result - call EVT_CHANGE_SELECTION to unselect all items. + * To prevent such behavior use g_prevent_list_events + **/ + m_prevent_list_events = true;//it's needed for GTK + + wxTextDataObject *obj = new wxTextDataObject; + obj->SetText(wxString::Format("%d", m_objects_model->GetVolumeIdByItem(item))); + event.SetDataObject(obj); + event.SetDragFlags(/*wxDrag_AllowMove*/wxDrag_DefaultMove); // allows both copy and move; +} + +void ObjectList::on_drop_possible(wxDataViewEvent &event) +{ + wxDataViewItem item(event.GetItem()); + + // only allow drags for item or background, not containers + if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || + event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->GetItemType(item) != itVolume) + event.Veto(); +} + +void ObjectList::on_drop(wxDataViewEvent &event) +{ + wxDataViewItem item(event.GetItem()); + + // only allow drops for item, not containers + if (m_selected_object_id < 0 || + item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || + event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->GetItemType(item) != itVolume) { + event.Veto(); + return; + } + + wxTextDataObject obj; + obj.SetData(wxDF_UNICODETEXT, event.GetDataSize(), event.GetDataBuffer()); + + int from_volume_id = std::stoi(obj.GetText().ToStdString()); + int to_volume_id = m_objects_model->GetVolumeIdByItem(item); + +#ifdef __WXGTK__ + /* Under GTK, DnD moves an item between another two items. + * And event.GetItem() return item, which is under "insertion line" + * So, if we move item down we should to decrease the to_volume_id value + **/ + if (to_volume_id > from_volume_id) to_volume_id--; +#endif // __WXGTK__ + + auto& volumes = (*m_objects)[m_selected_object_id]->volumes; + auto delta = to_volume_id < from_volume_id ? -1 : 1; + int cnt = 0; + for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++) + std::swap(volumes[id], volumes[id + delta]); + + select_item(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id, + m_objects_model->GetParent(item))); + + m_parts_changed = true; + parts_changed(m_selected_object_id); + +// m_prevent_list_events = false; +} + + +// Context Menu + +std::vector get_options(const bool is_part) +{ + PrintRegionConfig reg_config; + auto options = reg_config.keys(); + if (!is_part) { + PrintObjectConfig obj_config; + std::vector obj_options = obj_config.keys(); + options.insert(options.end(), obj_options.begin(), obj_options.end()); + } + return options; +} + +// category -> vector ( option ; label ) +typedef std::map< std::string, std::vector< std::pair > > settings_menu_hierarchy; +void get_options_menu(settings_menu_hierarchy& settings_menu, bool is_part) +{ + auto options = get_options(is_part); + + auto extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 : + wxGetApp().preset_bundle->printers.get_edited_preset().config.option("nozzle_diameter")->values.size(); + + DynamicPrintConfig config; + for (auto& option : options) + { + auto const opt = config.def()->get(option); + auto category = opt->category; + if (category.empty() || + (category == "Extruders" && extruders_cnt == 1)) continue; + + std::pair option_label(option, opt->label); + std::vector< std::pair > new_category; + auto& cat_opt_label = settings_menu.find(category) == settings_menu.end() ? new_category : settings_menu.at(category); + cat_opt_label.push_back(option_label); + if (cat_opt_label.size() == 1) + settings_menu[category] = cat_opt_label; + } +} + +void ObjectList::get_settings_choice(wxMenu *menu, int id, bool is_part) +{ + const auto category_name = menu->GetLabel(id); + + wxArrayString names; + wxArrayInt selections; + + settings_menu_hierarchy settings_menu; + get_options_menu(settings_menu, is_part); + std::vector< std::pair > *settings_list = nullptr; + + auto opt_keys = m_config->keys(); + + for (auto& cat : settings_menu) + { + if (_(cat.first) == category_name) { + int sel = 0; + for (auto& pair : cat.second) { + names.Add(_(pair.second)); + if (find(opt_keys.begin(), opt_keys.end(), pair.first) != opt_keys.end()) + selections.Add(sel); + sel++; + } + settings_list = &cat.second; + break; + } + } + + if (!settings_list) + return; + + if (wxGetSelectedChoices(selections, _(L("Select showing settings")), category_name, names) == -1) + return; + + std::vector selected_options; + for (auto sel : selections) + selected_options.push_back((*settings_list)[sel].first); + + for (auto& setting : (*settings_list)) + { + auto& opt_key = setting.first; + if (find(opt_keys.begin(), opt_keys.end(), opt_key) != opt_keys.end() && + find(selected_options.begin(), selected_options.end(), opt_key) == selected_options.end()) + m_config->erase(opt_key); + + if (find(opt_keys.begin(), opt_keys.end(), opt_key) == opt_keys.end() && + find(selected_options.begin(), selected_options.end(), opt_key) != selected_options.end()) + m_config->set_key_value(opt_key, m_default_config->option(opt_key)->clone()); + } + + + // Add settings item for object + const auto item = GetSelection(); + if (item) { + const auto settings_item = m_objects_model->GetSettingsItem(item); + select_item(settings_item ? settings_item : + m_objects_model->AddSettingsChild(item)); +#ifndef __WXOSX__ +// part_selection_changed(); +#endif //no __WXOSX__ + } + else { + auto panel = wxGetApp().sidebar().scrolled_panel(); + panel->Freeze(); + wxGetApp().obj_settings()->UpdateAndShow(true);//obj_manipul()->update_settings_list(); + panel->Thaw(); + } +} + +void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id, const int type) { + auto sub_menu = new wxMenu; + + const wxString menu_load = _(L("Load")) +" "+ dots; + sub_menu->Append(new wxMenuItem(sub_menu, id++, menu_load)); + sub_menu->AppendSeparator(); + + std::vector menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }; + for (auto& item : menu_items) + sub_menu->Append(new wxMenuItem(sub_menu, id++, _(item))); + +#ifndef __WXMSW__ + sub_menu->Bind(wxEVT_MENU, [sub_menu, type, menu_load, this](wxEvent &event) { + auto selection = sub_menu->GetLabel(event.GetId()); + if (selection == menu_load) + load_subobject(type); + else + load_generic_subobject(selection.ToStdString(), type); + }); +#endif //no __WXMSW__ + + menu->SetSubMenu(sub_menu); +} + +wxMenuItem* ObjectList::menu_item_split(wxMenu* menu, int id) { + auto menu_item = new wxMenuItem(menu, id, _(L("Split to parts"))); + menu_item->SetBitmap(m_bmp_split); + return menu_item; +} + +wxMenuItem* ObjectList::menu_item_settings(wxMenu* menu, int id, const bool is_part) { + auto menu_item = new wxMenuItem(menu, id, _(L("Add settings"))); + menu_item->SetBitmap(m_bmp_cog); + + auto sub_menu = create_add_settings_popupmenu(is_part); + menu_item->SetSubMenu(sub_menu); + return menu_item; +} + +wxMenu* ObjectList::create_add_part_popupmenu() +{ + wxMenu *menu = new wxMenu; + // Note: id accords to type of the sub-object, so sequence of the menu items is important + std::vector menu_object_types_items = {L("Add part"), // ~ModelVolume::MODEL_PART + L("Add modifier"), // ~ModelVolume::PARAMETER_MODIFIER + L("Add support enforcer"), // ~ModelVolume::SUPPORT_ENFORCER + L("Add support bloker") }; // ~ModelVolume::SUPPORT_BLOCKER + + const int obj_types_count = menu_object_types_items.size(); + const int generics_count = 5; // "Load ...", "Box", "Cylinder", "Sphere", "Slab" + + wxWindowID config_id_base = NewControlId(generics_count*obj_types_count + 2); + + // Add first 4 menu items + for (int type = 0; type < obj_types_count; type++) { + auto& item = menu_object_types_items[type]; + auto menu_item = new wxMenuItem(menu, config_id_base + type, _(item)); + menu_item->SetBitmap(*m_bmp_vector[type]); + menu_item_add_generic(menu_item, config_id_base + type*generics_count, type); + menu->Append(menu_item); + } + + // Split object to parts + menu->AppendSeparator(); + auto menu_item = menu_item_split(menu, config_id_base + obj_types_count * generics_count); + menu->Append(menu_item); + menu_item->Enable(is_splittable_object(false)); + + // Settings + menu->AppendSeparator(); + // Append settings popupmenu + menu->Append(menu_item_settings(menu, config_id_base + obj_types_count * generics_count+1, false)); + + menu->Bind(wxEVT_MENU, [config_id_base, menu, obj_types_count, generics_count, this](wxEvent &event) { + auto selection = event.GetId() - config_id_base; + + if ( selection == 0 * generics_count || // ~ModelVolume::MODEL_PART + selection == 1 * generics_count || // ~ModelVolume::PARAMETER_MODIFIER + selection == 2 * generics_count || // ~ModelVolume::SUPPORT_ENFORCER + selection == 3 * generics_count ) // ~ModelVolume::SUPPORT_BLOCKER + load_subobject(int(selection / generics_count)); + else if ( selection == obj_types_count * generics_count) + split(false); +#ifdef __WXMSW__ + else if ( selection > obj_types_count * generics_count) // "Add Settings" is selected + get_settings_choice(menu, event.GetId(), false); + else // Some generic model is selected + load_generic_subobject(menu->GetLabel(event.GetId()).ToStdString(), int(selection / generics_count)); +#endif // __WXMSW__ + }); + + return menu; +} + +wxMenu* ObjectList::create_part_settings_popupmenu() +{ + wxMenu *menu = new wxMenu; + wxWindowID config_id_base = NewControlId(3); + + auto menu_item = menu_item_split(menu, config_id_base); + menu->Append(menu_item); + menu_item->Enable(is_splittable_object(true)); + + // Append change part type + menu->AppendSeparator(); + menu->Append(new wxMenuItem(menu, config_id_base + 1, _(L("Change type")))); + + // Append settings popupmenu + menu->AppendSeparator(); + menu_item = menu_item_settings(menu, config_id_base + 2, true); + menu->Append(menu_item); + menu_item->Enable(get_selected_model_volume()->type() <= ModelVolume::PARAMETER_MODIFIER); + + menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) { + switch (event.GetId() - config_id_base) { + case 0: + split(true); + break; + case 1: + change_part_type(); + break; + default: + get_settings_choice(menu, event.GetId(), true); + break; + } + }); + + return menu; +} + +wxMenu* ObjectList::create_add_settings_popupmenu(bool is_part) +{ + wxMenu *menu = new wxMenu; + + settings_menu_hierarchy settings_menu; + get_options_menu(settings_menu, is_part); + + for (auto cat : settings_menu) + { + auto menu_item = new wxMenuItem(menu, wxID_ANY, _(cat.first)); + menu_item->SetBitmap(CATEGORY_ICON.find(cat.first) == CATEGORY_ICON.end() ? + wxNullBitmap : CATEGORY_ICON.at(cat.first)); + menu->Append(menu_item); + } +#ifndef __WXMSW__ + menu->Bind(wxEVT_MENU, [this, menu, is_part](wxEvent &event) { + get_settings_choice(menu, event.GetId(), is_part); + }); +#endif //no __WXMSW__ + return menu; +} + +void ObjectList::load_subobject(int type) +{ + auto item = GetSelection(); + if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0)) + return; + int obj_idx = m_objects_model->GetIdByItem(item); + + if (obj_idx < 0) return; + wxArrayString part_names; + load_part((*m_objects)[obj_idx], part_names, type); + + parts_changed(obj_idx); + + for (int i = 0; i < part_names.size(); ++i) { + const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i), type); + + if (i == part_names.size() - 1) + select_item(sel_item); + } + +#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME +// selection_changed(); +#endif //no __WXOSX__//__WXMSW__ + +} + +void ObjectList::load_part( ModelObject* model_object, + wxArrayString& part_names, + int type) +{ + wxWindow* parent = wxGetApp().tab_panel()->GetPage(0); + + m_parts_changed = false; + wxArrayString input_files; +#if ENABLE_NEW_MENU_LAYOUT + wxGetApp().import_model(parent, input_files); +#else + wxGetApp().open_model(parent, input_files); +#endif // ENABLE_NEW_MENU_LAYOUT + for (int i = 0; i < input_files.size(); ++i) { + std::string input_file = input_files.Item(i).ToStdString(); + + Model model; + try { + model = Model::read_from_file(input_file); + } + catch (std::exception &e) { + auto msg = _(L("Error! ")) + input_file + " : " + e.what() + "."; + show_error(parent, msg); + exit(1); + } + + for (auto object : model.objects) { + Vec3d delta = Vec3d::Zero(); + if (model_object->origin_translation != Vec3d::Zero()) + { + object->center_around_origin(); +#if !ENABLE_MODELVOLUME_TRANSFORM + object->ensure_on_bed(); +#endif // !ENABLE_MODELVOLUME_TRANSFORM + delta = model_object->origin_translation - object->origin_translation; + } + for (auto volume : object->volumes) { +#if ENABLE_MODELVOLUME_TRANSFORM + volume->center_geometry(); + volume->translate(delta); +#endif // ENABLE_MODELVOLUME_TRANSFORM + auto new_volume = model_object->add_volume(*volume); + new_volume->set_type(static_cast(type)); + new_volume->name = boost::filesystem::path(input_file).filename().string(); + + part_names.Add(new_volume->name); + +#if !ENABLE_MODELVOLUME_TRANSFORM + if (delta != Vec3d::Zero()) + new_volume->translate(delta); +#endif // !ENABLE_MODELVOLUME_TRANSFORM + + // set a default extruder value, since user can't add it manually + new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); + + m_parts_changed = true; + } + } + } + +} + +void ObjectList::load_generic_subobject(const std::string& type_name, const int type) +{ + const auto obj_idx = get_selected_obj_idx(); + if (obj_idx < 0) return; + + const std::string name = "lambda-" + type_name; + TriangleMesh mesh; + + auto& bed_shape = wxGetApp().preset_bundle->printers.get_edited_preset().config.option("bed_shape")->values; + const auto& sz = BoundingBoxf(bed_shape).size(); + const auto side = 0.1 * std::max(sz(0), sz(1)); + + if (type_name == _("Box")) { + mesh = make_cube(side, side, side); + // box sets the base coordinate at 0, 0, move to center of plate + mesh.translate(-side * 0.5, -side * 0.5, 0); + } + else if (type_name == _("Cylinder")) + mesh = make_cylinder(0.5*side, side); + else if (type_name == _("Sphere")) + mesh = make_sphere(side, PI/18); + else if (type_name == _("Slab")) { + const auto& size = (*m_objects)[obj_idx]->bounding_box().size(); + mesh = make_cube(size(0)*1.5, size(1)*1.5, size(2)*0.5); + // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z + mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, 0); + } + mesh.repair(); + + auto new_volume = (*m_objects)[obj_idx]->add_volume(mesh); + new_volume->set_type(static_cast(type)); + +#if ENABLE_MODELVOLUME_TRANSFORM + new_volume->set_offset((*m_objects)[obj_idx]->origin_translation + Vec3d(0.0, 0.0, -mesh.stl.stats.min(2))); + new_volume->center_geometry(); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + new_volume->name = name; + // set a default extruder value, since user can't add it manually + new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); + + m_parts_changed = true; + parts_changed(obj_idx); + + select_item(m_objects_model->AddVolumeChild(GetSelection(), name, type)); +#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME + selection_changed(); +#endif //no __WXOSX__ //__WXMSW__ +} + +void ObjectList::del_object(const int obj_idx) +{ + wxGetApp().plater()->delete_object_from_model(obj_idx); +} + +// Delete subobject +void ObjectList::del_subobject_item(wxDataViewItem& item) +{ + if (!item) return; + + int obj_idx, idx; + ItemType type; + + m_objects_model->GetItemInfo(item, type, obj_idx, idx); + if (type == itUndef) + return; + + if (type == itSettings) + del_settings_from_config(); + else if (type == itInstanceRoot && obj_idx != -1) + del_instances_from_object(obj_idx); + else if (idx == -1) + return; + else if (!del_subobject_from_object(obj_idx, idx, type)) + return; + + m_objects_model->Delete(item); +} + +void ObjectList::del_settings_from_config() +{ + auto opt_keys = m_config->keys(); + if (opt_keys.size() == 1 && opt_keys[0] == "extruder") + return; + int extruder = -1; + if (m_config->has("extruder")) + extruder = m_config->option("extruder")->value; + + m_config->clear(); + + if (extruder >= 0) + m_config->set_key_value("extruder", new ConfigOptionInt(extruder)); +} + +void ObjectList::del_instances_from_object(const int obj_idx) +{ + auto& instances = (*m_objects)[obj_idx]->instances; + if (instances.size() <= 1) + return; + + while ( instances.size()> 1) + instances.pop_back(); + + (*m_objects)[obj_idx]->invalidate_bounding_box(); // ? #ys_FIXME + + m_parts_changed = true; + parts_changed(obj_idx); +} + +bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type) +{ + if (type == itVolume) { + const auto volume = (*m_objects)[obj_idx]->volumes[idx]; + + // if user is deleting the last solid part, throw error + int solid_cnt = 0; + for (auto vol : (*m_objects)[obj_idx]->volumes) + if (vol->is_model_part()) + ++solid_cnt; + if (volume->is_model_part() && solid_cnt == 1) { + Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from object."))); + return false; + } + + (*m_objects)[obj_idx]->delete_volume(idx); + } + else if (type == itInstance) { + if ((*m_objects)[obj_idx]->instances.size() == 1) { + Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last intance from object."))); + return false; + } + (*m_objects)[obj_idx]->delete_instance(idx); + } + else + return false; + + m_parts_changed = true; + parts_changed(obj_idx); + + return true; +} + +void ObjectList::split(const bool split_part) +{ + const auto item = GetSelection(); + const int obj_idx = get_selected_obj_idx(); + if (!item || obj_idx < 0) + return; + + ModelVolume* volume; + if (!get_volume_by_item(split_part, item, volume)) return; + DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config; + const ConfigOption *nozzle_dmtrs_opt = config.option("nozzle_diameter", false); + const auto nozzle_dmrs_cnt = (nozzle_dmtrs_opt == nullptr) ? size_t(1) : dynamic_cast(nozzle_dmtrs_opt)->values.size(); + if (volume->split(nozzle_dmrs_cnt) == 1) { + wxMessageBox(_(L("The selected object couldn't be split because it contains only one part."))); + return; + } + + auto model_object = (*m_objects)[obj_idx]; + + auto parent = m_objects_model->GetTopParent(item); + if (parent) + m_objects_model->DeleteVolumeChildren(parent); + else + parent = item; + + for (auto id = 0; id < model_object->volumes.size(); id++) { + const auto vol_item = m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name, + model_object->volumes[id]->is_modifier() ? + ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART, + model_object->volumes[id]->config.has("extruder") ? + model_object->volumes[id]->config.option("extruder")->value : 0, + false); + // add settings to the part, if it has those + auto opt_keys = model_object->volumes[id]->config.keys(); + if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) { + select_item(m_objects_model->AddSettingsChild(vol_item)); + Collapse(vol_item); + } + } + + if (parent == item) + Expand(parent); + + m_parts_changed = true; + parts_changed(obj_idx); +} + +bool ObjectList::get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume) +{ + auto obj_idx = get_selected_obj_idx(); + if (!item || obj_idx < 0) + return false; + const auto volume_id = m_objects_model->GetVolumeIdByItem(item); + + // object is selected + if (volume_id < 0) { + if ( split_part || (*m_objects)[obj_idx]->volumes.size() > 1 ) + return false; + volume = (*m_objects)[obj_idx]->volumes[0]; + } + // volume is selected + else + volume = (*m_objects)[obj_idx]->volumes[volume_id]; + + return true; +} + +bool ObjectList::is_splittable_object(const bool split_part) +{ + const wxDataViewItem item = GetSelection(); + if (!item) return false; + + ModelVolume* volume; + if (!get_volume_by_item(split_part, item, volume) || !volume) + return false; + + TriangleMeshPtrs meshptrs = volume->mesh.split(); + bool splittable = meshptrs.size() > 1; + for (TriangleMesh* m : meshptrs) { delete m; } + + return splittable; +} + +void ObjectList::part_settings_changed() +{ + m_part_settings_changed = true; + wxGetApp().plater()->changed_object(get_selected_obj_idx()); + m_part_settings_changed = false; +} + +void ObjectList::parts_changed(int obj_idx) +{ + wxGetApp().plater()->changed_object(obj_idx); + m_parts_changed = false; +} + +void ObjectList::part_selection_changed() +{ + int obj_idx = -1; + m_config = nullptr; + wxString og_name = wxEmptyString; + + bool update_and_show_manipulations = false; + bool update_and_show_settings = false; + + if (multiple_selection()) { + og_name = _(L("Group manipulation")); + update_and_show_manipulations = true; + } + else + { + const auto item = GetSelection(); + if (item) + { + bool is_part = false; + if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { + obj_idx = m_objects_model->GetIdByItem(item); + og_name = _(L("Object manipulation")); + m_config = &(*m_objects)[obj_idx]->config; + update_and_show_manipulations = true; + } + else { + auto parent = m_objects_model->GetParent(item); + // Take ID of the parent object to "inform" perl-side which object have to be selected on the scene + obj_idx = m_objects_model->GetIdByItem(parent); + if (m_objects_model->GetItemType(item) == itSettings) { + if (m_objects_model->GetParent(parent) == wxDataViewItem(0)) { + og_name = _(L("Object Settings to modify")); + m_config = &(*m_objects)[obj_idx]->config; + } + else { + og_name = _(L("Part Settings to modify")); + is_part = true; + auto main_parent = m_objects_model->GetParent(parent); + obj_idx = m_objects_model->GetIdByItem(main_parent); + const auto volume_id = m_objects_model->GetVolumeIdByItem(parent); + m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; + } + update_and_show_settings = true; + } + else if (m_objects_model->GetItemType(item) == itVolume) { + og_name = _(L("Part manipulation")); + is_part = true; + const auto volume_id = m_objects_model->GetVolumeIdByItem(item); + m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; + update_and_show_manipulations = true; + } + else if (m_objects_model->GetItemType(item) == itInstance) { + og_name = _(L("Instance manipulation")); + update_and_show_manipulations = true; + } + } + + if (m_default_config) delete m_default_config; + m_default_config = DynamicPrintConfig::new_from_defaults_keys(get_options(is_part)); + } + } + + m_selected_object_id = obj_idx; + + if (update_and_show_manipulations) { + wxGetApp().obj_manipul()->get_og()->set_name(" " + og_name + " "); + wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(GetSelection())); + } + + if (update_and_show_settings) + wxGetApp().obj_settings()->get_og()->set_name(" " + og_name + " "); + + Sidebar& panel = wxGetApp().sidebar(); + panel.Freeze(); + + wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations); + wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings); + wxGetApp().sidebar().show_info_sizer(); + + panel.Layout(); + panel.Thaw(); +} + +void ObjectList::add_object_to_list(size_t obj_idx) +{ + auto model_object = (*m_objects)[obj_idx]; + wxString item_name = model_object->name; + auto item = m_objects_model->Add(item_name); + + // Add error icon if detected auto-repaire + auto stats = model_object->volumes[0]->mesh.stl.stats; + int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + + stats.facets_added + stats.facets_reversed + stats.backwards_edges; + if (errors > 0) { + wxVariant variant; + variant << PrusaDataViewBitmapText(item_name, m_bmp_manifold_warning); + m_objects_model->SetValue(variant, item, 0); + } + + // add volumes to the object + if (model_object->volumes.size() > 1) { + for (auto id = 0; id < model_object->volumes.size(); id++) + m_objects_model->AddVolumeChild(item, + model_object->volumes[id]->name, + ModelVolume::MODEL_PART, + !model_object->volumes[id]->config.has("extruder") ? 0 : + model_object->volumes[id]->config.option("extruder")->value, + false); + Expand(item); + } + + // add instances to the object, if it has those + if (model_object->instances.size()>1) + increase_object_instances(obj_idx, model_object->instances.size()); + + // add settings to the object, if it has those + auto opt_keys = model_object->config.keys(); + if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) { + select_item(m_objects_model->AddSettingsChild(item)); + Collapse(item); + } + +#ifndef __WXOSX__ + selection_changed(); +#endif //__WXMSW__ +} + +void ObjectList::delete_object_from_list() +{ + auto item = GetSelection(); + if (!item) + return; + if (m_objects_model->GetParent(item) == wxDataViewItem(0)) + select_item(m_objects_model->Delete(item)); + else + select_item(m_objects_model->Delete(m_objects_model->GetParent(item))); +} + +void ObjectList::delete_object_from_list(const size_t obj_idx) +{ + select_item(m_objects_model->Delete(m_objects_model->GetItemById(obj_idx))); +} + +void ObjectList::delete_volume_from_list(const size_t obj_idx, const size_t vol_idx) +{ + select_item(m_objects_model->Delete(m_objects_model->GetItemByVolumeId(obj_idx, vol_idx))); +} + +void ObjectList::delete_instance_from_list(const size_t obj_idx, const size_t inst_idx) +{ + select_item(m_objects_model->Delete(m_objects_model->GetItemByInstanceId(obj_idx, inst_idx))); +} + +void ObjectList::delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx) +{ + if ( !(type&(itObject|itVolume|itInstance)) ) + return; + + if (type&itObject) { + del_object(obj_idx); + delete_object_from_list(obj_idx); + } + else { + del_subobject_from_object(obj_idx, sub_obj_idx, type); + + type == itVolume ? delete_volume_from_list(obj_idx, sub_obj_idx) : + delete_instance_from_list(obj_idx, sub_obj_idx); + } +} + +void ObjectList::delete_from_model_and_list(const std::vector& items_for_delete) +{ + if (items_for_delete.empty()) + return; + + for (std::vector::const_reverse_iterator item = items_for_delete.rbegin(); item != items_for_delete.rend(); ++item) + { + if (!(item->type&(itObject | itVolume | itInstance))) + continue; + if (item->type&itObject) { + del_object(item->obj_idx); + m_objects_model->Delete(m_objects_model->GetItemById(item->obj_idx)); + } + else { + del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type); + if (item->type&itVolume) + m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx)); + else + m_objects_model->Delete(m_objects_model->GetItemByInstanceId(item->obj_idx, item->sub_obj_idx)); + } + } + part_selection_changed(); +} + +void ObjectList::delete_all_objects_from_list() +{ + m_objects_model->DeleteAll(); + part_selection_changed(); +} + +void ObjectList::increase_object_instances(const size_t obj_idx, const size_t num) +{ + select_item(m_objects_model->AddInstanceChild(m_objects_model->GetItemById(obj_idx), num)); +} + +void ObjectList::decrease_object_instances(const size_t obj_idx, const size_t num) +{ + select_item(m_objects_model->DeleteLastInstance(m_objects_model->GetItemById(obj_idx), num)); +} + +void ObjectList::unselect_objects() +{ + if (!GetSelection()) + return; + + m_prevent_list_events = true; + UnselectAll(); + part_selection_changed(); + m_prevent_list_events = false; +} + +void ObjectList::select_current_object(int idx) +{ + m_prevent_list_events = true; + UnselectAll(); + if (idx >= 0) + Select(m_objects_model->GetItemById(idx)); + part_selection_changed(); + m_prevent_list_events = false; +} + +void ObjectList::select_current_volume(int idx, int vol_idx) +{ + if (vol_idx < 0) { + select_current_object(idx); + return; + } + m_prevent_list_events = true; + UnselectAll(); + if (idx >= 0) + Select(m_objects_model->GetItemByVolumeId(idx, vol_idx)); + part_selection_changed(); + m_prevent_list_events = false; +} + +void ObjectList::remove() +{ + if (GetSelectedItemsCount() == 0) + return; + + wxDataViewItemArray sels; + GetSelections(sels); + + for (auto& item : sels) + { + if (m_objects_model->GetParent(item) == wxDataViewItem(0)) + wxGetApp().plater()->remove(m_objects_model->GetIdByItem(item)); + else + del_subobject_item(item); + } +} + +void ObjectList::init_objects() +{ + m_objects = wxGetApp().model_objects(); +} + +bool ObjectList::multiple_selection() const +{ + return GetSelectedItemsCount() > 1; +} + +void ObjectList::update_selections() +{ + auto& selection = _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection(); + wxDataViewItemArray sels; + + if (selection.is_single_full_object()) + { + sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); + } + else if (selection.is_single_volume() || selection.is_modifier() || + selection.is_multiple_volume() || selection.is_multiple_full_object()) { + for (auto idx : selection.get_volume_idxs()) { + const auto gl_vol = selection.get_volume(idx); + if (selection.is_multiple_full_object()) + sels.Add(m_objects_model->GetItemById(gl_vol->object_idx())); + else + sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); + } + } + else if (selection.is_single_full_instance() || selection.is_multiple_full_instance()) { + for (auto idx : selection.get_instance_idxs()) { + sels.Add(m_objects_model->GetItemByInstanceId(selection.get_object_idx(), idx)); + } + } + + select_items(sels); +} + +void ObjectList::update_selections_on_canvas() +{ + auto& selection = _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection(); + + const int sel_cnt = GetSelectedItemsCount(); + if (sel_cnt == 0) { + selection.clear(); + _3DScene::render(wxGetApp().canvas3D()); + return; + } + + auto add_to_selection = [this](const wxDataViewItem& item, GLCanvas3D::Selection& selection, bool as_single_selection) + { + if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { + selection.add_object(m_objects_model->GetIdByItem(item), as_single_selection); + return; + } + + if (m_objects_model->GetItemType(item) == itVolume) { + const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item)); + const int vol_idx = m_objects_model->GetVolumeIdByItem(item); + selection.add_volume(obj_idx, vol_idx, 0, as_single_selection); + } + else if (m_objects_model->GetItemType(item) == itInstance) { + const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); + const int inst_idx = m_objects_model->GetInstanceIdByItem(item); + selection.add_instance(obj_idx, inst_idx, as_single_selection); + } + }; + + if (sel_cnt == 1) { + wxDataViewItem item = GetSelection(); + if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot)) + add_to_selection(m_objects_model->GetParent(item), selection, true); + else + add_to_selection(item, selection, true); + + _3DScene::render(wxGetApp().canvas3D()); + return; + } + + wxDataViewItemArray sels; + GetSelections(sels); + + selection.clear(); + for (auto item: sels) + add_to_selection(item, selection, false); + + _3DScene::render(wxGetApp().canvas3D()); +} + +void ObjectList::select_item(const wxDataViewItem& item) +{ + m_prevent_list_events = true; + + UnselectAll(); + Select(item); + part_selection_changed(); + + m_prevent_list_events = false; +} + +void ObjectList::select_items(const wxDataViewItemArray& sels) +{ + m_prevent_list_events = true; + + UnselectAll(); + SetSelections(sels); + part_selection_changed(); + + m_prevent_list_events = false; +} + +void ObjectList::select_all() +{ + SelectAll(); + selection_changed(); +} + +void ObjectList::select_item_all_children() +{ + wxDataViewItemArray sels; + + // There is no selection before OR some object is selected => select all objects + if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) { + for (int i = 0; i < m_objects->size(); i++) + sels.Add(m_objects_model->GetItemById(i)); + } + else { + const auto item = GetSelection(); + // Some volume(instance) is selected => select all volumes(instances) inside the current object + if (m_objects_model->GetItemType(item) & (itVolume | itInstance)) { + m_objects_model->GetChildren(m_objects_model->GetParent(item), sels); + } + } + + SetSelections(sels); + selection_changed(); +} + +void ObjectList::fix_multiselection_conflicts() +{ + if (GetSelectedItemsCount() <= 1) + return; + + m_prevent_list_events = true; + + wxDataViewItemArray sels; + GetSelections(sels); + + for (auto item : sels) { + if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot)) + Unselect(item); + else if (m_objects_model->GetParent(item) != wxDataViewItem(0)) + Unselect(m_objects_model->GetParent(item)); + } + + m_prevent_list_events = false; +} + +ModelVolume* ObjectList::get_selected_model_volume() +{ + auto item = GetSelection(); + if (!item || m_objects_model->GetItemType(item) != itVolume) + return nullptr; + + const auto vol_idx = m_objects_model->GetVolumeIdByItem(item); + const auto obj_idx = get_selected_obj_idx(); + if (vol_idx < 0 || obj_idx < 0) + return nullptr; + + return (*m_objects)[obj_idx]->volumes[vol_idx]; +} + +void ObjectList::change_part_type() +{ + ModelVolume* volume = get_selected_model_volume(); + if (!volume) + return; + const auto type = volume->type(); + + const wxString names[] = { "Part", "Modifier", "Support Enforcer", "Support Blocker" }; + + auto new_type = wxGetSingleChoiceIndex("Type: ", _(L("Select type of part")), wxArrayString(4, names), type); + + if (new_type == type || new_type < 0) + return; + + const auto item = GetSelection(); + volume->set_type(static_cast(new_type)); + m_objects_model->SetVolumeType(item, new_type); + + m_parts_changed = true; + parts_changed(get_selected_obj_idx()); + + // Update settings showing, if we have it + //(we show additional settings for Part and Modifier and hide it for Support Blocker/Enforcer) + const auto settings_item = m_objects_model->GetSettingsItem(item); + if (settings_item && + new_type == ModelVolume::SUPPORT_ENFORCER || new_type == ModelVolume::SUPPORT_BLOCKER) { + m_objects_model->Delete(settings_item); + } + else if (!settings_item && + new_type == ModelVolume::MODEL_PART || new_type == ModelVolume::PARAMETER_MODIFIER) { + select_item(m_objects_model->AddSettingsChild(item)); + } +} + +void ObjectList::last_volume_is_deleted(const int obj_idx) +{ + + if (obj_idx < 0 || (*m_objects).empty() || (*m_objects)[obj_idx]->volumes.empty()) + return; + auto volume = (*m_objects)[obj_idx]->volumes[0]; + + // clear volume's config values + volume->config.clear(); + + // set a default extruder value, since user can't add it manually + volume->config.set_key_value("extruder", new ConfigOptionInt(0)); +} + +} //namespace GUI +} //namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp new file mode 100644 index 0000000000..31ae5f5eda --- /dev/null +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -0,0 +1,174 @@ +#ifndef slic3r_GUI_ObjectList_hpp_ +#define slic3r_GUI_ObjectList_hpp_ + +#include +#include +#include +#include + +#include "Event.hpp" +#include "wxExtensions.hpp" + +class wxBoxSizer; +class PrusaObjectDataViewModel; + +namespace Slic3r { +class ConfigOptionsGroup; +class DynamicPrintConfig; +class ModelObject; +class ModelVolume; + +namespace GUI { + +wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent); + +struct ItemForDelete +{ + ItemType type; + int obj_idx; + int sub_obj_idx; + + ItemForDelete(ItemType type, int obj_idx, int sub_obj_idx) + : type(type), obj_idx(obj_idx), sub_obj_idx(sub_obj_idx) + {} +}; + +class ObjectList : public wxDataViewCtrl +{ + wxBoxSizer *m_sizer {nullptr}; + + DynamicPrintConfig *m_default_config {nullptr}; + + wxBitmap m_bmp_modifiermesh; + wxBitmap m_bmp_solidmesh; + wxBitmap m_bmp_support_enforcer; + wxBitmap m_bmp_support_blocker; + wxBitmap m_bmp_manifold_warning; + wxBitmap m_bmp_cog; + wxBitmap m_bmp_split; + + std::vector m_bmp_vector; + + int m_selected_object_id = -1; + bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select() + // happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler + // calls this method again and again and again +#ifdef __WXOSX__ + wxString m_selected_extruder = ""; +#endif //__WXOSX__ + bool m_parts_changed = false; + bool m_part_settings_changed = false; + +public: + ObjectList(wxWindow* parent); + ~ObjectList(); + + + std::map CATEGORY_ICON; + + PrusaObjectDataViewModel *m_objects_model{ nullptr }; + DynamicPrintConfig *m_config {nullptr}; + + std::vector *m_objects{ nullptr }; + + + void create_objects_ctrl(); + wxDataViewColumn* create_objects_list_extruder_column(int extruders_count); + void update_objects_list_extruder_column(int extruders_count); + // show/hide "Extruder" column for Objects List + void set_extruder_column_hidden(bool hide); + // update extruder in current config + void update_extruder_in_config(const wxString& selection); + + void init_icons(); + + void set_tooltip_for_item(const wxPoint& pt); + + void selection_changed(); + void context_menu(); + void show_context_menu(); + void key_event(wxKeyEvent& event); + void item_value_change(wxDataViewEvent& event); + + void on_begin_drag(wxDataViewEvent &event); + void on_drop_possible(wxDataViewEvent &event); + void on_drop(wxDataViewEvent &event); + + void get_settings_choice(wxMenu *menu, int id, bool is_part); + void menu_item_add_generic(wxMenuItem* &menu, int id, const int type); + wxMenuItem* menu_item_split(wxMenu* menu, int id); + wxMenuItem* menu_item_settings(wxMenu* menu, int id, const bool is_part); + wxMenu* create_add_part_popupmenu(); + wxMenu* create_part_settings_popupmenu(); + wxMenu* create_add_settings_popupmenu(bool is_part); + + void load_subobject(int type); + void load_part(ModelObject* model_object, wxArrayString& part_names, int type); + void load_generic_subobject(const std::string& type_name, const int type); + void del_object(const int obj_idx); + void del_subobject_item(wxDataViewItem& item); + void del_settings_from_config(); + void del_instances_from_object(const int obj_idx); + bool del_subobject_from_object(const int obj_idx, const int idx, const int type); + void split(const bool split_part); + bool get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume); + bool is_splittable_object(const bool split_part); + + wxPoint get_mouse_position_in_control(); + wxBoxSizer* get_sizer() {return m_sizer;} + int get_selected_obj_idx() const; + bool is_parts_changed() const { return m_parts_changed; } + bool is_part_settings_changed() const { return m_part_settings_changed; } + void part_settings_changed(); + + void parts_changed(int obj_idx); + void part_selection_changed(); + + // Add object to the list + void add_object_to_list(size_t obj_idx); + // Delete object from the list + void delete_object_from_list(); + void delete_object_from_list(const size_t obj_idx); + void delete_volume_from_list(const size_t obj_idx, const size_t vol_idx); + void delete_instance_from_list(const size_t obj_idx, const size_t inst_idx); + void delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx); + void delete_from_model_and_list(const std::vector& items_for_delete); + // Delete all objects from the list + void delete_all_objects_from_list(); + // Increase instances count + void increase_object_instances(const size_t obj_idx, const size_t num); + // Decrease instances count + void decrease_object_instances(const size_t obj_idx, const size_t num); + + // #ys_FIXME_to_delete + // Unselect all objects in the list on c++ side + void unselect_objects(); + // Select current object in the list on c++ side + void select_current_object(int idx); + // Select current volume in the list on c++ side + void select_current_volume(int idx, int vol_idx); + + // Remove objects/sub-object from the list + void remove(); + + void init_objects(); + bool multiple_selection() const ; + void update_selections(); + void update_selections_on_canvas(); + void select_item(const wxDataViewItem& item); + void select_items(const wxDataViewItemArray& sels); + void select_all(); + void select_item_all_children(); + // correct current selections to avoid of the possible conflicts + void fix_multiselection_conflicts(); + + ModelVolume* get_selected_model_volume(); + void change_part_type(); + + void last_volume_is_deleted(const int obj_idx); +}; + + +}} + +#endif //slic3r_GUI_ObjectList_hpp_ diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp new file mode 100644 index 0000000000..6418b2e8ba --- /dev/null +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -0,0 +1,449 @@ +#include "GUI_ObjectManipulation.hpp" +#include "GUI_ObjectList.hpp" + +#include "OptionsGroup.hpp" +#include "wxExtensions.hpp" +#include "PresetBundle.hpp" +#include "Model.hpp" +#include "Geometry.hpp" + +#include + +namespace Slic3r +{ +namespace GUI +{ + +ObjectManipulation::ObjectManipulation(wxWindow* parent) : + OG_Settings(parent, true) +{ + m_og->set_name(_(L("Object Manipulation"))); + m_og->label_width = 100; + m_og->set_grid_vgap(5); + m_og->set_process_enter(); // We need to update new values only after press ENTER + + m_og->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + std::vector axes{ "_x", "_y", "_z" }; + + if (opt_key == "scale_unit") { + const wxString& selection = boost::any_cast(value); + for (auto axis : axes) { + std::string key = "scale" + axis; + m_og->set_side_text(key, selection); + } + + m_is_percent_scale = selection == _("%"); + update_scale_values(); + return; + } + + std::string param; + std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); + + size_t i = 0; + Vec3d new_value; + for (auto axis : axes) + new_value(i++) = boost::any_cast(m_og->get_value(param+axis)); + + if (param == "position") + change_position_value(new_value); + else if (param == "rotation") + change_rotation_value(new_value); + else if (param == "scale") + change_scale_value(new_value); + }; + + ConfigOptionDef def; + + // Objects(sub-objects) name + def.label = L("Name"); + // def.type = coString; + def.gui_type = "legend"; + def.tooltip = L("Object name"); + def.full_width = true; + def.default_value = new ConfigOptionString{ " " }; + m_og->append_single_option_line(Option(def, "object_name")); + + // Legend for object modification + auto line = Line{ "", "" }; + def.label = ""; + def.type = coString; + def.width = 50; + + std::vector axes{ "x", "y", "z" }; + for (const auto axis : axes) { + const auto label = boost::algorithm::to_upper_copy(axis); + def.default_value = new ConfigOptionString{ " " + label }; + Option option = Option(def, axis + "_axis_legend"); + line.append_option(option); + } + m_og->append_line(line); + + + auto add_og_to_object_settings = [](const std::string& option_name, const std::string& sidetext) + { + Line line = { _(option_name), "" }; + if (option_name == "Scale") { + line.near_label_widget = [](wxWindow* parent) { + auto btn = new PrusaLockButton(parent, wxID_ANY); + btn->Bind(wxEVT_BUTTON, [btn](wxCommandEvent &event) { + event.Skip(); + wxTheApp->CallAfter([btn]() { + wxGetApp().obj_manipul()->set_uniform_scaling(btn->IsLocked()); + }); + }); + return btn; + }; + } + + ConfigOptionDef def; + def.type = coFloat; + def.default_value = new ConfigOptionFloat(0.0); + def.width = 50; + + if (option_name == "Rotation") + def.min = -360; + + const std::string lower_name = boost::algorithm::to_lower_copy(option_name); + + std::vector axes{ "x", "y", "z" }; + for (auto axis : axes) { + if (axis == "z" && option_name != "Scale") + def.sidetext = sidetext; + Option option = Option(def, lower_name + "_" + axis); + option.opt.full_width = true; + line.append_option(option); + } + + if (option_name == "Scale") + { + def.width = 45; + def.type = coStrings; + def.gui_type = "select_open"; + def.enum_labels.push_back(L("%")); + def.enum_labels.push_back(L("mm")); + def.default_value = new ConfigOptionStrings{ "mm" }; + + const Option option = Option(def, lower_name + "_unit"); + line.append_option(option); + } + + return line; + }; + + + // Settings table + m_og->append_line(add_og_to_object_settings(L("Position"), L("mm"))); + m_og->append_line(add_og_to_object_settings(L("Rotation"), "°")); + m_og->append_line(add_og_to_object_settings(L("Scale"), "mm")); + + /* Unused parameter at this time + def.label = L("Place on bed"); + def.type = coBool; + def.tooltip = L("Automatic placing of models on printing bed in Y axis"); + def.gui_type = ""; + def.sidetext = ""; + def.default_value = new ConfigOptionBool{ false }; + m_og->append_single_option_line(Option(def, "place_on_bed")); + */ +} + +void ObjectManipulation::Show(const bool show) +{ + if (show == IsShown()) + return; + + m_og->Show(show); + + if (show && wxGetApp().get_view_mode() != ConfigMenuModeSimple) { + m_og->get_grid_sizer()->Show(size_t(0), false); + m_og->get_grid_sizer()->Show(size_t(1), false); + } +} + +bool ObjectManipulation::IsShown() +{ + return m_og->get_grid_sizer()->IsShown(2); +} + +void ObjectManipulation::UpdateAndShow(const bool show) +{ + if (show) + update_settings_value(_3DScene::get_canvas(wxGetApp().canvas3D())->get_selection()); + + OG_Settings::UpdateAndShow(show); +} + +int ObjectManipulation::ol_selection() +{ + return wxGetApp().obj_list()->get_selected_obj_idx(); +} + +void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) +{ +#if ENABLE_MODELVOLUME_TRANSFORM + if (selection.is_single_full_instance() || selection.is_single_full_object()) +#else + if (selection.is_single_full_object()) + { + auto obj_idx = selection.get_object_idx(); + if (obj_idx >=0 && !wxGetApp().model_objects()->empty() && (*wxGetApp().model_objects())[obj_idx]->instances.size() == 1) + { + // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + update_position_value(volume->get_offset()); + update_rotation_value(volume->get_rotation()); + update_scale_value(volume->get_scaling_factor()); + m_og->enable(); + } + else + reset_settings_value(); + } + else if (selection.is_single_full_instance()) +#endif // ENABLE_MODELVOLUME_TRANSFORM + { + // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if ENABLE_MODELVOLUME_TRANSFORM + update_position_value(volume->get_instance_offset()); + update_rotation_value(volume->get_instance_rotation()); + update_scale_value(volume->get_instance_scaling_factor()); +#else + update_position_value(volume->get_offset()); + update_rotation_value(volume->get_rotation()); + update_scale_value(volume->get_scaling_factor()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + m_og->enable(); + } + else if (selection.is_wipe_tower()) + { + // the selection contains a single volume + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if ENABLE_MODELVOLUME_TRANSFORM + update_position_value(volume->get_volume_offset()); + update_rotation_value(volume->get_volume_rotation()); + update_scale_value(volume->get_volume_scaling_factor()); +#else + update_position_value(volume->get_offset()); + update_rotation_value(volume->get_rotation()); + update_scale_value(volume->get_scaling_factor()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + m_og->enable(); + } + else if (selection.is_modifier()) + { + // the selection contains a single volume + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if ENABLE_MODELVOLUME_TRANSFORM + update_position_value(volume->get_volume_offset()); + update_rotation_value(volume->get_volume_rotation()); + update_scale_value(volume->get_volume_scaling_factor()); +#else + update_position_value(volume->get_offset()); + update_rotation_value(volume->get_rotation()); + update_scale_value(volume->get_scaling_factor()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + m_og->enable(); + } + else + reset_settings_value(); + + m_og->get_field("scale_unit")->disable();// temporary decision +} + +void ObjectManipulation::reset_settings_value() +{ + reset_position_value(); + reset_rotation_value(); + reset_scale_value(); + m_og->disable(); +} + +wxString def_0 {"0"}; +wxString def_100 {"100"}; + +void ObjectManipulation::reset_position_value() +{ + m_og->set_value("position_x", def_0); + m_og->set_value("position_y", def_0); + m_og->set_value("position_z", def_0); + + cache_position = { 0., 0., 0. }; +} + +void ObjectManipulation::reset_rotation_value() +{ + m_og->set_value("rotation_x", def_0); + m_og->set_value("rotation_y", def_0); + m_og->set_value("rotation_z", def_0); +} + +void ObjectManipulation::reset_scale_value() +{ + m_is_percent_scale = true; + m_og->set_value("scale_unit", _("%")); + m_og->set_value("scale_x", def_100); + m_og->set_value("scale_y", def_100); + m_og->set_value("scale_z", def_100); +} + +void ObjectManipulation::update_values() +{ + int selection = ol_selection(); + if (selection < 0 || wxGetApp().mainframe->m_plater->model().objects.size() <= selection) { + m_og->set_value("position_x", def_0); + m_og->set_value("position_y", def_0); + m_og->set_value("position_z", def_0); + m_og->set_value("scale_x" , def_0); + m_og->set_value("scale_y" , def_0); + m_og->set_value("scale_z" , def_0); + m_og->set_value("rotation_x", def_0); + m_og->set_value("rotation_y", def_0); + m_og->set_value("rotation_z", def_0); + m_og->disable(); + return; + } + m_is_percent_scale = boost::any_cast(m_og->get_value("scale_unit")) == _("%"); + + update_position_values(); + update_scale_values(); + update_rotation_values(); + m_og->enable(); +} + +void ObjectManipulation::update_scale_values() +{ + int selection = ol_selection(); + ModelObjectPtrs& objects = wxGetApp().mainframe->m_plater->model().objects; + + auto instance = objects[selection]->instances.front(); + auto size = objects[selection]->instance_bounding_box(0).size(); + + if (m_is_percent_scale) { + m_og->set_value("scale_x", double_to_string(instance->get_scaling_factor(X) * 100, 2)); + m_og->set_value("scale_y", double_to_string(instance->get_scaling_factor(Y) * 100, 2)); + m_og->set_value("scale_z", double_to_string(instance->get_scaling_factor(Z) * 100, 2)); + } + else { + m_og->set_value("scale_x", double_to_string(size(0), 2)); + m_og->set_value("scale_y", double_to_string(size(1), 2)); + m_og->set_value("scale_z", double_to_string(size(2), 2)); + } +} + +void ObjectManipulation::update_position_values() +{ + auto instance = wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front(); + + m_og->set_value("position_x", double_to_string(instance->get_offset(X), 2)); + m_og->set_value("position_y", double_to_string(instance->get_offset(Y), 2)); + m_og->set_value("position_z", double_to_string(instance->get_offset(Z), 2)); +} + +void ObjectManipulation::update_position_value(const Vec3d& position) +{ + m_og->set_value("position_x", double_to_string(position(0), 2)); + m_og->set_value("position_y", double_to_string(position(1), 2)); + m_og->set_value("position_z", double_to_string(position(2), 2)); + + cache_position = position; +} + +void ObjectManipulation::update_scale_value(const Vec3d& scaling_factor) +{ + // this is temporary + // to be able to update the values as size + // we need to store somewhere the original size + // or have it passed as parameter + if (!m_is_percent_scale) { + m_is_percent_scale = true; + m_og->set_value("scale_unit", _("%")); + } + + auto scale = scaling_factor * 100.0; + m_og->set_value("scale_x", double_to_string(scale(0), 2)); + m_og->set_value("scale_y", double_to_string(scale(1), 2)); + m_og->set_value("scale_z", double_to_string(scale(2), 2)); +} + +void ObjectManipulation::update_rotation_values() +{ + update_rotation_value(wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front()->get_rotation()); +} + +void ObjectManipulation::update_rotation_value(double angle, Axis axis) +{ + std::string axis_str; + switch (axis) { + case X: { + axis_str = "rotation_x"; + break; } + case Y: { + axis_str = "rotation_y"; + break; } + case Z: { + axis_str = "rotation_z"; + break; } + } + + m_og->set_value(axis_str, round_nearest(int(Geometry::rad2deg(angle)), 0)); +} + +void ObjectManipulation::update_rotation_value(const Vec3d& rotation) +{ + m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(rotation(0)), 0), 2)); + m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(rotation(1)), 0), 2)); + m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(rotation(2)), 0), 2)); +} + + +void ObjectManipulation::change_position_value(const Vec3d& position) +{ + Vec3d displacement(position - cache_position); + + auto canvas = _3DScene::get_canvas(wxGetApp().canvas3D()); + canvas->get_selection().start_dragging(); + canvas->get_selection().translate(displacement); + canvas->_on_move(); + + cache_position = position; +} + +void ObjectManipulation::change_rotation_value(const Vec3d& rotation) +{ + Vec3d rad_rotation; + for (size_t i = 0; i < 3; ++i) + rad_rotation(i) = Geometry::deg2rad(rotation(i)); + auto canvas = _3DScene::get_canvas(wxGetApp().canvas3D()); + canvas->get_selection().start_dragging(); + canvas->get_selection().rotate(rad_rotation, false); + canvas->_on_rotate(); +} + +void ObjectManipulation::change_scale_value(const Vec3d& scale) +{ + Vec3d scaling_factor; + if (m_is_percent_scale) + scaling_factor = scale*0.01; + else { + int selection = ol_selection(); + ModelObjectPtrs& objects = *wxGetApp().model_objects(); + + auto size = objects[selection]->instance_bounding_box(0).size(); + for (size_t i = 0; i < 3; ++i) + scaling_factor(i) = scale(i) / size(i); + } + + auto canvas = _3DScene::get_canvas(wxGetApp().canvas3D()); + canvas->get_selection().start_dragging(); + canvas->get_selection().scale(scaling_factor); + canvas->_on_scale(); +} + +void ObjectManipulation::print_cashe_value(const std::string& label, const Vec3d& v) +{ + std::cout << label << " => " << " X:" << v(0) << " Y:" << v(1) << " Z:" << v(2) << std::endl; +} + +} //namespace GUI +} //namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp new file mode 100644 index 0000000000..6ab76a83b0 --- /dev/null +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -0,0 +1,66 @@ +#ifndef slic3r_GUI_ObjectManipulation_hpp_ +#define slic3r_GUI_ObjectManipulation_hpp_ + +#include + +#include "GUI_ObjectSettings.hpp" +#include "GLCanvas3D.hpp" + + +namespace Slic3r { +namespace GUI { + + +class ObjectManipulation : public OG_Settings +{ + bool m_is_percent_scale = false; // true -> percentage scale unit + // false -> uniform scale unit + bool m_is_uniform_scale = false; // It indicates if scale is uniform + + Vec3d cache_position { 0., 0., 0. }; + +public: + ObjectManipulation(wxWindow* parent); + ~ObjectManipulation() {} + + void Show(const bool show) override; + bool IsShown() override; + void UpdateAndShow(const bool show) override; + + int ol_selection(); + + void update_settings_value(const GLCanvas3D::Selection& selection); + void reset_settings_value(); + void reset_position_value(); + void reset_rotation_value(); + void reset_scale_value(); + + void update_values(); + // update position values displacements or "gizmos" + void update_position_values(); + void update_position_value(const Vec3d& position); + // update scale values after scale unit changing or "gizmos" + void update_scale_values(); + void update_scale_value(const Vec3d& scaling_factor); + // update rotation values object selection changing + void update_rotation_values(); + // update rotation value after "gizmos" + void update_rotation_value(double angle, Axis axis); + void update_rotation_value(const Vec3d& rotation); + + void set_uniform_scaling(const bool uniform_scale) { m_is_uniform_scale = uniform_scale; } + + + // change values + void change_position_value(const Vec3d& position); + void change_rotation_value(const Vec3d& rotation); + void change_scale_value(const Vec3d& scale); + + +private: + void print_cashe_value(const std::string& label, const Vec3d& value); +}; + +}} + +#endif // slic3r_GUI_ObjectManipulation_hpp_ diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp new file mode 100644 index 0000000000..ec463dfb58 --- /dev/null +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -0,0 +1,159 @@ +#include "GUI_ObjectSettings.hpp" +#include "GUI_ObjectList.hpp" + +#include "OptionsGroup.hpp" +#include "wxExtensions.hpp" +#include "PresetBundle.hpp" +#include "Model.hpp" + +#include + +namespace Slic3r +{ +namespace GUI +{ + +OG_Settings::OG_Settings(wxWindow* parent, const bool staticbox) : + m_parent(parent) +{ + wxString title = staticbox ? " " : ""; // temporary workaround - #ys_FIXME + m_og = std::make_shared(parent, title); +} + +bool OG_Settings::IsShown() +{ + return m_og->sizer->IsEmpty() ? false : m_og->sizer->IsShown(size_t(0)); +} + +void OG_Settings::Show(const bool show) +{ + m_og->Show(show); +} + +void OG_Settings::Hide() +{ + Show(false); +} + +void OG_Settings::UpdateAndShow(const bool show) +{ + Show(show); +// m_parent->Layout(); +} + +wxSizer* OG_Settings::get_sizer() +{ + return m_og->sizer; +} + + + +ObjectSettings::ObjectSettings(wxWindow* parent) : + OG_Settings(parent, true) +{ + m_og->set_name(_(L("Additional Settings"))); + + m_settings_list_sizer = new wxBoxSizer(wxVERTICAL); + m_og->sizer->Add(m_settings_list_sizer, 1, wxEXPAND | wxLEFT, 5); +} + +void ObjectSettings::update_settings_list() +{ + m_settings_list_sizer->Clear(true); + + auto objects_ctrl = wxGetApp().obj_list(); + auto objects_model = wxGetApp().obj_list()->m_objects_model; + auto config = wxGetApp().obj_list()->m_config; + + const auto item = objects_ctrl->GetSelection(); + if (item && !objects_ctrl->multiple_selection() && + config && objects_model->IsSettingsItem(item)) + { + auto extra_column = [config, this](wxWindow* parent, const Line& line) + { + auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line + + auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG), + wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); +#ifdef __WXMSW__ + btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#endif // __WXMSW__ + btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) { + config->erase(opt_key); + wxTheApp->CallAfter([this]() { + update_settings_list(); + m_parent->Layout(); + }); + }); + return btn; + }; + + std::map> cat_options; + auto opt_keys = config->keys(); + m_og_settings.resize(0); + std::vector categories; + if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return; + { + auto extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 : + wxGetApp().preset_bundle->printers.get_edited_preset().config.option("nozzle_diameter")->values.size(); + + for (auto& opt_key : opt_keys) { + auto category = config->def()->get(opt_key)->category; + if (category.empty() || + (category == "Extruders" && extruders_cnt == 1)) continue; + + std::vector< std::string > new_category; + + auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category); + cat_opt.push_back(opt_key); + if (cat_opt.size() == 1) + cat_options[category] = cat_opt; + } + + for (auto& cat : cat_options) { + if (cat.second.size() == 1 && cat.second[0] == "extruder") + continue; + + auto optgroup = std::make_shared(m_parent, cat.first, config, false, extra_column); + optgroup->label_width = 150; + optgroup->sidetext_width = 70; + + optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) { + wxGetApp().obj_list()->part_settings_changed(); }; + + for (auto& opt : cat.second) + { + if (opt == "extruder") + continue; + Option option = optgroup->get_option(opt); + option.opt.width = 70; + optgroup->append_single_option_line(option); + } + optgroup->reload_config(); + m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0); + m_og_settings.push_back(optgroup); + + categories.push_back(cat.first); + } + } + + if (m_og_settings.empty()) { + objects_ctrl->select_item(objects_model->Delete(item)); + } + else { + if (!categories.empty()) + objects_model->UpdateSettingsDigest(item, categories); + } + } +} + +void ObjectSettings::UpdateAndShow(const bool show) +{ + if (show) + update_settings_list(); + + OG_Settings::UpdateAndShow(show); +} + +} //namespace GUI +} //namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp new file mode 100644 index 0000000000..7b58d4c4eb --- /dev/null +++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp @@ -0,0 +1,49 @@ +#ifndef slic3r_GUI_ObjectSettings_hpp_ +#define slic3r_GUI_ObjectSettings_hpp_ + +#include +#include + +class wxBoxSizer; + +namespace Slic3r { +namespace GUI { +class ConfigOptionsGroup; + +class OG_Settings +{ +protected: + std::shared_ptr m_og; + wxWindow* m_parent; +public: + OG_Settings(wxWindow* parent, const bool staticbox); + ~OG_Settings() {} + + virtual bool IsShown(); + virtual void Show(const bool show); + virtual void Hide(); + virtual void UpdateAndShow(const bool show); + + wxSizer* get_sizer(); + ConfigOptionsGroup* get_og() { return m_og.get(); } +}; + + +class ObjectSettings : public OG_Settings +{ + // sizer for extra Object/Part's settings + wxBoxSizer* m_settings_list_sizer{ nullptr }; + // option groups for settings + std::vector > m_og_settings; + +public: + ObjectSettings(wxWindow* parent); + ~ObjectSettings() {} + + void update_settings_list(); + void UpdateAndShow(const bool show) override; +}; + +}} + +#endif // slic3r_GUI_ObjectSettings_hpp_ diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp new file mode 100644 index 0000000000..b7f85d1ad8 --- /dev/null +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -0,0 +1,598 @@ +#include "../../libslic3r/libslic3r.h" +#include "GUI_Preview.hpp" +#include "GUI_App.hpp" +#include "GUI.hpp" +#include "AppConfig.hpp" +#include "3DScene.hpp" +#include "GLCanvas3DManager.hpp" +#include "../../libslic3r/GCode/PreviewData.hpp" +#include "PresetBundle.hpp" +#include "wxExtensions.hpp" + +#include +#include +#include +#include +#include +#include +#include + +// this include must follow the wxWidgets ones or it won't compile on Windows -> see http://trac.wxwidgets.org/ticket/2421 +#include "../../libslic3r/Print.hpp" + +namespace Slic3r { +namespace GUI { + + +Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data, std::function schedule_background_process_func) + : m_canvas(nullptr) + , m_double_slider_sizer(nullptr) + , m_label_view_type(nullptr) + , m_choice_view_type(nullptr) + , m_label_show_features(nullptr) + , m_combochecklist_features(nullptr) + , m_checkbox_travel(nullptr) + , m_checkbox_retractions(nullptr) + , m_checkbox_unretractions(nullptr) + , m_checkbox_shells(nullptr) + , m_config(config) + , m_print(print) + , m_gcode_preview_data(gcode_preview_data) + , m_number_extruders(1) + , m_preferred_color_mode("feature") + , m_loaded(false) + , m_enabled(false) + , m_force_sliders_full_range(false) + , m_schedule_background_process(schedule_background_process_func) +{ + if (init(notebook, config, print, gcode_preview_data)) + { + notebook->AddPage(this, _(L("Preview"))); + show_hide_ui_elements("none"); + load_print(); + } +} + +bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) +{ + if ((notebook == nullptr) || (config == nullptr) || (print == nullptr) || (gcode_preview_data == nullptr)) + return false; + + // creates this panel add append it to the given notebook as a new page + if (!Create(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize)) + return false; + + m_canvas = GLCanvas3DManager::create_wxglcanvas(this); + + _3DScene::add_canvas(m_canvas); + _3DScene::allow_multisample(m_canvas, GLCanvas3DManager::can_multisample()); + _3DScene::enable_shader(m_canvas, true); + _3DScene::set_config(m_canvas, m_config); + _3DScene::set_print(m_canvas, m_print); + _3DScene::enable_legend_texture(m_canvas, true); + _3DScene::enable_dynamic_background(m_canvas, true); + + m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL); + create_double_slider(); + + m_label_view_type = new wxStaticText(this, wxID_ANY, _(L("View"))); + + m_choice_view_type = new wxChoice(this, wxID_ANY); + m_choice_view_type->Append(_(L("Feature type"))); + m_choice_view_type->Append(_(L("Height"))); + m_choice_view_type->Append(_(L("Width"))); + m_choice_view_type->Append(_(L("Speed"))); + m_choice_view_type->Append(_(L("Volumetric flow rate"))); + m_choice_view_type->Append(_(L("Tool"))); + m_choice_view_type->SetSelection(0); + + m_label_show_features = new wxStaticText(this, wxID_ANY, _(L("Show"))); + + m_combochecklist_features = new wxComboCtrl(); + m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxSize(200, -1), wxCB_READONLY); + std::string feature_text = GUI::into_u8(_(L("Feature types"))); + std::string feature_items = GUI::into_u8( + _(L("Perimeter")) + "|" + + _(L("External perimeter")) + "|" + + _(L("Overhang perimeter")) + "|" + + _(L("Internal infill")) + "|" + + _(L("Solid infill")) + "|" + + _(L("Top solid infill")) + "|" + + _(L("Bridge infill")) + "|" + + _(L("Gap fill")) + "|" + + _(L("Skirt")) + "|" + + _(L("Support material")) + "|" + + _(L("Support material interface")) + "|" + + _(L("Wipe tower")) + "|" + + _(L("Custom")) + ); + Slic3r::GUI::create_combochecklist(m_combochecklist_features, feature_text, feature_items, true); + + m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); + m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L("Retractions"))); + m_checkbox_unretractions = new wxCheckBox(this, wxID_ANY, _(L("Unretractions"))); + m_checkbox_shells = new wxCheckBox(this, wxID_ANY, _(L("Shells"))); + + wxBoxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL); + top_sizer->Add(m_canvas, 1, wxALL | wxEXPAND, 0); + top_sizer->Add(m_double_slider_sizer, 0, wxEXPAND, 0); + + wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL); + bottom_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->Add(m_choice_view_type, 0, wxEXPAND | wxALL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_label_show_features, 0, wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->Add(m_combochecklist_features, 0, wxEXPAND | wxALL, 5); + bottom_sizer->AddSpacer(20); + bottom_sizer->Add(m_checkbox_travel, 0, wxEXPAND | wxALL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_checkbox_retractions, 0, wxEXPAND | wxALL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_checkbox_unretractions, 0, wxEXPAND | wxALL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_checkbox_shells, 0, wxEXPAND | wxALL, 5); + + wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(top_sizer, 1, wxALL | wxEXPAND, 0); + main_sizer->Add(bottom_sizer, 0, wxALL | wxEXPAND, 0); + + SetSizer(main_sizer); + SetMinSize(GetSize()); + GetSizer()->SetSizeHints(this); + + bind_event_handlers(); + + // sets colors for gcode preview extrusion roles + std::vector extrusion_roles_colors = { + "Perimeter", "FFFF66", + "External perimeter", "FFA500", + "Overhang perimeter", "0000FF", + "Internal infill", "B1302A", + "Solid infill", "D732D7", + "Top solid infill", "FF1A1A", + "Bridge infill", "9999FF", + "Gap fill", "FFFFFF", + "Skirt", "845321", + "Support material", "00FF00", + "Support material interface", "008000", + "Wipe tower", "B3E3AB", + "Custom", "28CC94" + }; + m_gcode_preview_data->set_extrusion_paths_colors(extrusion_roles_colors); + + return true; +} + +Preview::~Preview() +{ + unbind_event_handlers(); + + if (m_canvas != nullptr) + { + _3DScene::remove_canvas(m_canvas); + delete m_canvas; + } +} + +void Preview::set_number_extruders(unsigned int number_extruders) +{ + if (m_number_extruders != number_extruders) + { + m_number_extruders = number_extruders; + int type = 0; // color by a feature type + if (number_extruders > 1) + { + int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); + int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type + m_choice_view_type->SetSelection(type); + if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) + m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; + + m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature"; + } + } +} + +void Preview::reset_gcode_preview_data() +{ + m_gcode_preview_data->reset(); + if (m_canvas != nullptr) + _3DScene::reset_legend_texture(m_canvas); +} + +void Preview::set_canvas_as_dirty() +{ + if (m_canvas != nullptr) + _3DScene::set_as_dirty(m_canvas); +} + +void Preview::set_enabled(bool enabled) +{ + m_enabled = enabled; +} + +void Preview::set_bed_shape(const Pointfs& shape) +{ + if (m_canvas != nullptr) + _3DScene::set_bed_shape(m_canvas, shape); +} + +void Preview::select_view(const std::string& direction) +{ + if (m_canvas != nullptr) + _3DScene::select_view(m_canvas, direction); +} + +void Preview::set_viewport_from_scene(wxGLCanvas* canvas) +{ + if ((m_canvas != nullptr) && (canvas != nullptr)) + _3DScene::set_viewport_from_scene(m_canvas, canvas); +} + +void Preview::set_viewport_into_scene(wxGLCanvas* canvas) +{ + if ((m_canvas != nullptr) && (canvas != nullptr)) + _3DScene::set_viewport_from_scene(canvas, m_canvas); +} + +void Preview::set_drop_target(wxDropTarget* target) +{ + if (target != nullptr) + SetDropTarget(target); +} + +void Preview::load_print() +{ + if (m_loaded) + return; + + // 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) + unsigned int n_layers = 0; + if (m_print->is_step_done(posSlice)) + { + std::set zs; + for (const PrintObject* print_object : m_print->objects()) + { + const LayerPtrs& layers = print_object->layers(); + const SupportLayerPtrs& support_layers = print_object->support_layers(); + for (const Layer* layer : layers) + { + zs.insert(layer->print_z); + } + for (const SupportLayer* layer : support_layers) + { + zs.insert(layer->print_z); + } + } + + n_layers = (unsigned int)zs.size(); + } + + if (n_layers == 0) + { + reset_sliders(); + if (m_canvas != nullptr) + { + _3DScene::reset_legend_texture(m_canvas); + m_canvas->Refresh(); + } + return; + } + + if (m_preferred_color_mode == "tool_or_feature") + { + // It is left to Slic3r to decide whether the print shall be colored by the tool or by the feature. + // Color by feature if it is a single extruder print. + unsigned int number_extruders = (unsigned int)m_print->extruders().size(); + int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); + int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type + m_choice_view_type->SetSelection(type); + if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) + m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; + // If the->SetSelection changed the following line, revert it to "decide yourself". + m_preferred_color_mode = "tool_or_feature"; + } + + // Collect colors per extruder. + std::vector colors; + if (!m_gcode_preview_data->empty() || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool)) + { + const ConfigOptionStrings* extruders_opt = dynamic_cast(m_config->option("extruder_colour")); + const ConfigOptionStrings* filamemts_opt = dynamic_cast(m_config->option("filament_colour")); + unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size()); + + unsigned char rgb[3]; + for (unsigned int i = 0; i < colors_count; ++i) + { + std::string color = m_config->opt_string("extruder_colour", i); + if (!PresetBundle::parse_color(color, rgb)) + { + color = m_config->opt_string("filament_colour", i); + if (!PresetBundle::parse_color(color, rgb)) + color = "#FFFFFF"; + } + + colors.push_back(color); + } + } + + if (IsShown() && (m_canvas != nullptr)) + { + // used to set the sliders to the extremes of the current zs range + m_force_sliders_full_range = false; + + if (m_gcode_preview_data->empty()) + { + // load skirt and brim + _3DScene::load_preview(m_canvas, colors); + show_hide_ui_elements("simple"); + } + else + { + m_force_sliders_full_range = (_3DScene::get_volumes_count(m_canvas) == 0); + _3DScene::load_gcode_preview(m_canvas, m_gcode_preview_data, colors); + show_hide_ui_elements("full"); + + // recalculates zs and update sliders accordingly + n_layers = (unsigned int)_3DScene::get_current_print_zs(m_canvas, true).size(); + if (n_layers == 0) + { + // all layers filtered out + reset_sliders(); + m_canvas->Refresh(); + } + } + + if (n_layers > 0) + update_sliders(); + + m_loaded = true; + } +} + +void Preview::reload_print(bool force) +{ + _3DScene::reset_volumes(m_canvas); + m_loaded = false; + + if (!IsShown() && !force) + return; + + load_print(); +} + +void Preview::refresh_print() +{ + m_loaded = false; + + if (!IsShown()) + return; + + load_print(); +} + +void Preview::bind_event_handlers() +{ + this->Bind(wxEVT_SIZE, &Preview::on_size, this); + m_choice_view_type->Bind(wxEVT_CHOICE, &Preview::on_choice_view_type, this); + m_combochecklist_features->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this); + m_checkbox_travel->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); + m_checkbox_retractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); + m_checkbox_unretractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this); + m_checkbox_shells->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this); +} + +void Preview::unbind_event_handlers() +{ + this->Unbind(wxEVT_SIZE, &Preview::on_size, this); + m_choice_view_type->Unbind(wxEVT_CHOICE, &Preview::on_choice_view_type, this); + m_combochecklist_features->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this); + m_checkbox_travel->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); + m_checkbox_retractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); + m_checkbox_unretractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this); + m_checkbox_shells->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this); +} + +void Preview::show_hide_ui_elements(const std::string& what) +{ + bool enable = (what == "full"); + m_label_show_features->Enable(enable); + m_combochecklist_features->Enable(enable); + m_checkbox_travel->Enable(enable); + m_checkbox_retractions->Enable(enable); + m_checkbox_unretractions->Enable(enable); + m_checkbox_shells->Enable(enable); + + enable = (what != "none"); + m_label_view_type->Enable(enable); + m_choice_view_type->Enable(enable); +} + +void Preview::reset_sliders() +{ + m_enabled = false; + reset_double_slider(); + m_double_slider_sizer->Hide((size_t)0); +} + +void Preview::update_sliders() +{ + m_enabled = true; + update_double_slider(m_force_sliders_full_range); + m_double_slider_sizer->Show((size_t)0); + Layout(); +} + +void Preview::on_size(wxSizeEvent& evt) +{ + evt.Skip(); + Refresh(); +} + +void Preview::on_choice_view_type(wxCommandEvent& evt) +{ + m_preferred_color_mode = (m_choice_view_type->GetStringSelection() == L("Tool")) ? "tool" : "feature"; + int selection = m_choice_view_type->GetCurrentSelection(); + if ((0 <= selection) && (selection < (int)GCodePreviewData::Extrusion::Num_View_Types)) + m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)selection; + + reload_print(); +} + +void Preview::on_combochecklist_features(wxCommandEvent& evt) +{ + int flags = Slic3r::GUI::combochecklist_get_flags(m_combochecklist_features); + m_gcode_preview_data->extrusion.role_flags = (unsigned int)flags; + refresh_print(); +} + +void Preview::on_checkbox_travel(wxCommandEvent& evt) +{ + m_gcode_preview_data->travel.is_visible = m_checkbox_travel->IsChecked(); + refresh_print(); +} + +void Preview::on_checkbox_retractions(wxCommandEvent& evt) +{ + m_gcode_preview_data->retraction.is_visible = m_checkbox_retractions->IsChecked(); + refresh_print(); +} + +void Preview::on_checkbox_unretractions(wxCommandEvent& evt) +{ + m_gcode_preview_data->unretraction.is_visible = m_checkbox_unretractions->IsChecked(); + refresh_print(); +} + +void Preview::on_checkbox_shells(wxCommandEvent& evt) +{ + m_gcode_preview_data->shell.is_visible = m_checkbox_shells->IsChecked(); + refresh_print(); +} + +void Preview::create_double_slider() +{ + m_slider = new PrusaDoubleSlider(this, wxID_ANY, 0, 0, 0, 100); + m_double_slider_sizer->Add(m_slider, 0, wxEXPAND, 0); + + // sizer, m_canvas + m_canvas->Bind(wxEVT_KEY_DOWN, &Preview::update_double_slider_from_canvas, this); + + m_slider->Bind(wxEVT_SCROLL_CHANGED, [this](wxEvent& event) { + _3DScene::set_toolpaths_range(m_canvas, m_slider->GetLowerValueD() - 1e-6, m_slider->GetHigherValueD() + 1e-6); + if (IsShown()) + m_canvas->Refresh(); + }); + + Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { + auto& config = wxGetApp().preset_bundle->project_config; + ((config.option("colorprint_heights"))->values) = (m_slider->GetTicksValues()); + m_schedule_background_process(); + }); +} + +void Preview::update_double_slider(bool force_sliders_full_range) +{ + std::vector> values; + std::vector layers_z = _3DScene::get_current_print_zs(m_canvas, true); + fill_slider_values(values, layers_z); + + const double z_low = m_slider->GetLowerValueD(); + const double z_high = m_slider->GetHigherValueD(); + m_slider->SetMaxValue(layers_z.size() - 1); + m_slider->SetSliderValues(values); + + const auto& config = wxGetApp().preset_bundle->project_config; + const std::vector &ticks_from_config = (config.option("colorprint_heights"))->values; + + m_slider->SetTicksValues(ticks_from_config); + + set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high); +} + +void Preview::fill_slider_values(std::vector> &values, + const std::vector &layers_z) +{ + std::vector layers_all_z = _3DScene::get_current_print_zs(m_canvas, false); + if (layers_all_z.size() == layers_z.size()) + for (int i = 0; i < layers_z.size(); i++) + values.push_back(std::pair(i + 1, layers_z[i])); + else if (layers_all_z.size() > layers_z.size()) { + int cur_id = 0; + for (int i = 0; i < layers_z.size(); i++) + for (int j = cur_id; j < layers_all_z.size(); j++) + if (layers_z[i] - 1e-6 < layers_all_z[j] && layers_all_z[j] < layers_z[i] + 1e-6) { + values.push_back(std::pair(j + 1, layers_z[i])); + cur_id = j; + break; + } + } + + // All ticks that would end up outside the slider range should be erased. + // TODO: this should be placed into more appropriate part of code, + // this function is e.g. not called when the last object is deleted + auto& config = wxGetApp().preset_bundle->project_config; + std::vector &ticks_from_config = (config.option("colorprint_heights"))->values; + unsigned int old_size = ticks_from_config.size(); + ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(), + [values](double val) { return values.back().second < val; }), + ticks_from_config.end()); + if (ticks_from_config.size() != old_size) + m_schedule_background_process(); +} + +void Preview::set_double_slider_thumbs(const bool force_sliders_full_range, + const std::vector &layers_z, + const double z_low, + const double z_high) +{ + // Force slider full range only when slider is created. + // Support selected diapason on the all next steps + if (/*force_sliders_full_range*/z_high == 0.0) { + m_slider->SetLowerValue(0); + m_slider->SetHigherValue(layers_z.size() - 1); + return; + } + + for (int i = layers_z.size() - 1; i >= 0; i--) + if (z_low >= layers_z[i]) { + m_slider->SetLowerValue(i); + break; + } + for (int i = layers_z.size() - 1; i >= 0; i--) + if (z_high >= layers_z[i]) { + m_slider->SetHigherValue(i); + break; + } +} + +void Preview::reset_double_slider() +{ + m_slider->SetHigherValue(0); + m_slider->SetLowerValue(0); +} + +void Preview::update_double_slider_from_canvas(wxKeyEvent& event) +{ + if (event.HasModifiers()) { + event.Skip(); + return; + } + + const auto key = event.GetKeyCode(); + + if (key == 'U' || key == 'D') { + const int new_pos = key == 'U' ? m_slider->GetHigherValue() + 1 : m_slider->GetHigherValue() - 1; + m_slider->SetHigherValue(new_pos); + if (event.ShiftDown()) m_slider->SetLowerValue(m_slider->GetHigherValue()); + } + else if (key == 'S') + m_slider->ChangeOneLayerLock(); + else + event.Skip(); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp new file mode 100644 index 0000000000..bafcba1bac --- /dev/null +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -0,0 +1,111 @@ +#ifndef slic3r_GUI_Preview_hpp_ +#define slic3r_GUI_Preview_hpp_ + +#include +#include "../../libslic3r/Point.hpp" + +#include + +class wxNotebook; +class wxGLCanvas; +class wxBoxSizer; +class wxStaticText; +class wxChoice; +class wxComboCtrl; +class wxCheckBox; +class PrusaDoubleSlider; + +namespace Slic3r { + +class DynamicPrintConfig; +class Print; +class GCodePreviewData; + +namespace GUI { + +class Preview : public wxPanel +{ + wxGLCanvas* m_canvas; + wxBoxSizer* m_double_slider_sizer; + wxStaticText* m_label_view_type; + wxChoice* m_choice_view_type; + wxStaticText* m_label_show_features; + wxComboCtrl* m_combochecklist_features; + wxCheckBox* m_checkbox_travel; + wxCheckBox* m_checkbox_retractions; + wxCheckBox* m_checkbox_unretractions; + wxCheckBox* m_checkbox_shells; + + DynamicPrintConfig* m_config; + Print* m_print; + GCodePreviewData* m_gcode_preview_data; + + // Calling this function object forces Plater::schedule_background_process. + std::function m_schedule_background_process; + + unsigned int m_number_extruders; + std::string m_preferred_color_mode; + + bool m_loaded; + bool m_enabled; + bool m_force_sliders_full_range; + + PrusaDoubleSlider* m_slider {nullptr}; + +public: + Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = [](){}); + virtual ~Preview(); + + wxGLCanvas* get_wxglcanvas() { return m_canvas; } + + void set_number_extruders(unsigned int number_extruders); + void reset_gcode_preview_data(); + void set_canvas_as_dirty(); + void set_enabled(bool enabled); + void set_bed_shape(const Pointfs& shape); + void select_view(const std::string& direction); + void set_viewport_from_scene(wxGLCanvas* canvas); + void set_viewport_into_scene(wxGLCanvas* canvas); + void set_drop_target(wxDropTarget* target); + + void load_print(); + void reload_print(bool force = false); + void refresh_print(); + +private: + bool init(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data); + + void bind_event_handlers(); + void unbind_event_handlers(); + + void show_hide_ui_elements(const std::string& what); + + void reset_sliders(); + void update_sliders(); + + void on_size(wxSizeEvent& evt); + void on_choice_view_type(wxCommandEvent& evt); + void on_combochecklist_features(wxCommandEvent& evt); + void on_checkbox_travel(wxCommandEvent& evt); + void on_checkbox_retractions(wxCommandEvent& evt); + void on_checkbox_unretractions(wxCommandEvent& evt); + void on_checkbox_shells(wxCommandEvent& evt); + + // Create/Update/Reset double slider on 3dPreview + void create_double_slider(); + void update_double_slider(bool force_sliders_full_range); + void fill_slider_values(std::vector> &values, + const std::vector &layers_z); + void set_double_slider_thumbs( const bool force_sliders_full_range, + const std::vector &layers_z, + const double z_low, + const double z_high); + void reset_double_slider(); + // update DoubleSlider after keyDown in canvas + void update_double_slider_from_canvas(wxKeyEvent& event); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GUI_Preview_hpp_ diff --git a/src/slic3r/GUI/GUI_PreviewIface.cpp b/src/slic3r/GUI/GUI_PreviewIface.cpp new file mode 100644 index 0000000000..cbec512059 --- /dev/null +++ b/src/slic3r/GUI/GUI_PreviewIface.cpp @@ -0,0 +1,57 @@ +#include "../../libslic3r/libslic3r.h" +#include "GUI_PreviewIface.hpp" +#include "GUI_Preview.hpp" + +namespace Slic3r { + +void PreviewIface::set_number_extruders(unsigned int number_extruders) +{ + m_preview->set_number_extruders(number_extruders); +} + +void PreviewIface::reset_gcode_preview_data() +{ + m_preview->reset_gcode_preview_data(); +} + +void PreviewIface::reload_print(bool force) +{ + m_preview->reload_print(force); +} + +void PreviewIface::set_canvas_as_dirty() +{ + m_preview->set_canvas_as_dirty(); +} + +void PreviewIface::set_enabled(bool enabled) +{ + m_preview->set_enabled(enabled); +} + +void PreviewIface::set_bed_shape(const Pointfs& shape) +{ + m_preview->set_bed_shape(shape); +} + +void PreviewIface::select_view(const std::string& direction) +{ + m_preview->select_view(direction); +} + +void PreviewIface::set_viewport_from_scene(wxGLCanvas* canvas) +{ + m_preview->set_viewport_from_scene(canvas); +} + +void PreviewIface::set_viewport_into_scene(wxGLCanvas* canvas) +{ + m_preview->set_viewport_into_scene(canvas); +} + +void PreviewIface::set_drop_target(wxDropTarget* target) +{ + m_preview->set_drop_target(target); +} + +} // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_PreviewIface.hpp b/src/slic3r/GUI/GUI_PreviewIface.hpp new file mode 100644 index 0000000000..df5fccaaf5 --- /dev/null +++ b/src/slic3r/GUI/GUI_PreviewIface.hpp @@ -0,0 +1,36 @@ +#ifndef slic3r_GUI_PreviewIface_hpp_ +#define slic3r_GUI_PreviewIface_hpp_ + +#include "../../libslic3r/Point.hpp" + +class wxGLCanvas; +class wxDropTarget; + +namespace Slic3r { + +namespace GUI { +class Preview; +} // namespace GUI + +class PreviewIface +{ + GUI::Preview* m_preview; + +public: + explicit PreviewIface(GUI::Preview* preview) : m_preview(preview) {} + + void set_number_extruders(unsigned int number_extruders); + void reset_gcode_preview_data(); + void reload_print(bool force = false); + void set_canvas_as_dirty(); + void set_enabled(bool enabled); + void set_bed_shape(const Pointfs& shape); + void select_view(const std::string& direction); + void set_viewport_from_scene(wxGLCanvas* canvas); + void set_viewport_into_scene(wxGLCanvas* canvas); + void set_drop_target(wxDropTarget* target); +}; + +} // namespace Slic3r + +#endif // slic3r_GUI_PreviewIface_hpp_ diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp new file mode 100644 index 0000000000..43b3c38f71 --- /dev/null +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -0,0 +1,139 @@ +#include "GUI_Utils.hpp" + +#include +#include +#include + +#include +#include +#include + +#include "libslic3r/Config.hpp" + + +namespace Slic3r { +namespace GUI { + + +wxTopLevelWindow* find_toplevel_parent(wxWindow *window) +{ + for (; window != nullptr; window = window->GetParent()) { + if (window->IsTopLevel()) { + return dynamic_cast(window); + } + } + + return nullptr; +} + + +CheckboxFileDialog::ExtraPanel::ExtraPanel(wxWindow *parent) + : wxPanel(parent, wxID_ANY) +{ + // WARN: wxMSW does some extra shenanigans to calc the extra control size. + // It first calls the create function with a dummy empty wxDialog parent and saves its size. + // Afterwards, the create function is called again with the real parent. + // Additionally there's no way to pass any extra data to the create function (no closure), + // which is why we have to this stuff here. Grrr! + auto *dlg = dynamic_cast(parent); + const wxString checkbox_label(dlg != nullptr ? dlg->checkbox_label : wxString("String long enough to contain dlg->checkbox_label")); + + auto* sizer = new wxBoxSizer(wxHORIZONTAL); + cbox = new wxCheckBox(this, wxID_ANY, checkbox_label); + cbox->SetValue(true); + sizer->AddSpacer(5); + sizer->Add(this->cbox, 0, wxEXPAND | wxALL, 5); + SetSizer(sizer); + sizer->SetSizeHints(this); +} + +wxWindow* CheckboxFileDialog::ExtraPanel::ctor(wxWindow *parent) { + return new ExtraPanel(parent); +} + +CheckboxFileDialog::CheckboxFileDialog(wxWindow *parent, + const wxString &checkbox_label, + bool checkbox_value, + const wxString &message, + const wxString &default_dir, + const wxString &default_file, + const wxString &wildcard, + long style, + const wxPoint &pos, + const wxSize &size, + const wxString &name +) + : wxFileDialog(parent, message, default_dir, default_file, wildcard, style, pos, size, name) + , checkbox_label(checkbox_label) +{ + if (checkbox_label.IsEmpty()) { + return; + } + + SetExtraControlCreator(ExtraPanel::ctor); +} + +bool CheckboxFileDialog::get_checkbox_value() const +{ + auto *extra_panel = dynamic_cast(GetExtraControl()); + return extra_panel != nullptr ? extra_panel->cbox->GetValue() : false; +} + + +WindowMetrics WindowMetrics::from_window(wxTopLevelWindow *window) +{ + WindowMetrics res; + res.rect = window->GetScreenRect(); + res.maximized = window->IsMaximized(); + return res; +} + +boost::optional WindowMetrics::deserialize(const std::string &str) +{ + std::vector metrics_str; + metrics_str.reserve(5); + + if (!unescape_strings_cstyle(str, metrics_str) || metrics_str.size() != 5) { + return boost::none; + } + + int metrics[5]; + try { + for (size_t i = 0; i < 5; i++) { + metrics[i] = boost::lexical_cast(metrics_str[i]); + } + } catch(const boost::bad_lexical_cast &) { + return boost::none; + } + + if ((metrics[4] & ~1) != 0) { // Checks if the maximized flag is 1 or 0 + metrics[4] = 0; + } + + WindowMetrics res; + res.rect = wxRect(metrics[0], metrics[1], metrics[2], metrics[3]); + res.maximized = metrics[4] != 0; + + return res; +} + +void WindowMetrics::sanitize_for_display(const wxRect &screen_rect) +{ + rect = rect.Intersect(screen_rect); +} + +std::string WindowMetrics::serialize() +{ + return (boost::format("%1%; %2%; %3%; %4%; %5%") + % rect.GetX() + % rect.GetY() + % rect.GetWidth() + % rect.GetHeight() + % static_cast(maximized) + ).str(); +} + + + +} +} diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp new file mode 100644 index 0000000000..9cee986b0c --- /dev/null +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -0,0 +1,117 @@ +#ifndef slic3r_GUI_Utils_hpp_ +#define slic3r_GUI_Utils_hpp_ + +#include +#include + +#include + +#include +#include +#include +#include + +class wxCheckBox; +class wxTopLevelWindow; +class wxRect; + + +namespace Slic3r { +namespace GUI { + + +wxTopLevelWindow* find_toplevel_parent(wxWindow *window); + + +class EventGuard +{ +public: + EventGuard() {} + EventGuard(const EventGuard&) = delete; + EventGuard(EventGuard &&other) : unbinder(std::move(other.unbinder)) {} + + ~EventGuard() { + if (unbinder) { + unbinder(false); + } + } + + template void bind(wxEvtHandler *emitter, const EvTag &type, Fun fun) + { + // This is a way to type-erase both the event type as well as the handler: + + unbinder = std::move([=](bool bind) { + if (bind) { + emitter->Bind(type, fun); + } else { + emitter->Unbind(type, fun); + } + }); + + unbinder(true); + } + + EventGuard& operator=(const EventGuard&) = delete; + EventGuard& operator=(EventGuard &&other) + { + unbinder.swap(other.unbinder); + return *this; + } +private: + std::function unbinder; +}; + + +class CheckboxFileDialog : public wxFileDialog +{ +public: + CheckboxFileDialog(wxWindow *parent, + const wxString &checkbox_label, + bool checkbox_value, + const wxString &message = wxFileSelectorPromptStr, + const wxString &default_dir = wxEmptyString, + const wxString &default_file = wxEmptyString, + const wxString &wildcard = wxFileSelectorDefaultWildcardStr, + long style = wxFD_DEFAULT_STYLE, + const wxPoint &pos = wxDefaultPosition, + const wxSize &size = wxDefaultSize, + const wxString &name = wxFileDialogNameStr + ); + + bool get_checkbox_value() const; + +private: + struct ExtraPanel : public wxPanel + { + wxCheckBox *cbox; + + ExtraPanel(wxWindow *parent); + static wxWindow* ctor(wxWindow *parent); + }; + + wxString checkbox_label; +}; + + +class WindowMetrics +{ +private: + wxRect rect; + bool maximized; + + WindowMetrics() : maximized(false) {} +public: + static WindowMetrics from_window(wxTopLevelWindow *window); + static boost::optional deserialize(const std::string &str); + + wxRect get_rect() const { return rect; } + bool get_maximized() const { return maximized; } + + void sanitize_for_display(const wxRect &screen_rect); + std::string serialize(); +}; + + +}} + +#endif diff --git a/src/slic3r/GUI/LambdaObjectDialog.cpp b/src/slic3r/GUI/LambdaObjectDialog.cpp new file mode 100644 index 0000000000..9c89a8c047 --- /dev/null +++ b/src/slic3r/GUI/LambdaObjectDialog.cpp @@ -0,0 +1,199 @@ +#include "LambdaObjectDialog.hpp" + +#include +#include +#include "OptionsGroup.hpp" + +namespace Slic3r +{ +namespace GUI +{ + +LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent, + const wxString type_name): + m_type_name(type_name) +{ + Create(parent, wxID_ANY, _(L("Lambda Object")), + parent->GetScreenPosition(), wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + + // instead of double dim[3] = { 1.0, 1.0, 1.0 }; + object_parameters.dim[0] = 1.0; + object_parameters.dim[1] = 1.0; + object_parameters.dim[2] = 1.0; + + sizer = new wxBoxSizer(wxVERTICAL); + + // modificator options + if (m_type_name == wxEmptyString) { + m_modificator_options_book = new wxChoicebook( this, wxID_ANY, wxDefaultPosition, + wxDefaultSize, wxCHB_TOP); + sizer->Add(m_modificator_options_book, 1, wxEXPAND | wxALL, 10); + } + else { + m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); + sizer->Add(m_panel, 1, wxEXPAND | wxALL, 10); + } + + ConfigOptionDef def; + def.width = 70; + auto optgroup = init_modificator_options_page(_(L("Box"))); + if (optgroup) { + optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + int opt_id = opt_key == "l" ? 0 : + opt_key == "w" ? 1 : + opt_key == "h" ? 2 : -1; + if (opt_id < 0) return; + object_parameters.dim[opt_id] = boost::any_cast(value); + }; + + def.type = coFloat; + def.default_value = new ConfigOptionFloat{ 1.0 }; + def.label = L("Length"); + Option option(def, "l"); + optgroup->append_single_option_line(option); + + def.label = L("Width"); + option = Option(def, "w"); + optgroup->append_single_option_line(option); + + def.label = L("Height"); + option = Option(def, "h"); + optgroup->append_single_option_line(option); + } + + optgroup = init_modificator_options_page(_(L("Cylinder"))); + if (optgroup) { + optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + int val = boost::any_cast(value); + if (opt_key == "cyl_r") + object_parameters.cyl_r = val; + else if (opt_key == "cyl_h") + object_parameters.cyl_h = val; + else return; + }; + + def.type = coInt; + def.default_value = new ConfigOptionInt{ 1 }; + def.label = L("Radius"); + auto option = Option(def, "cyl_r"); + optgroup->append_single_option_line(option); + + def.label = L("Height"); + option = Option(def, "cyl_h"); + optgroup->append_single_option_line(option); + } + + optgroup = init_modificator_options_page(_(L("Sphere"))); + if (optgroup) { + optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + if (opt_key == "sph_rho") + object_parameters.sph_rho = boost::any_cast(value); + else return; + }; + + def.type = coFloat; + def.default_value = new ConfigOptionFloat{ 1.0 }; + def.label = L("Rho"); + auto option = Option(def, "sph_rho"); + optgroup->append_single_option_line(option); + } + + optgroup = init_modificator_options_page(_(L("Slab"))); + if (optgroup) { + optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + double val = boost::any_cast(value); + if (opt_key == "slab_z") + object_parameters.slab_z = val; + else if (opt_key == "slab_h") + object_parameters.slab_h = val; + else return; + }; + + def.type = coFloat; + def.default_value = new ConfigOptionFloat{ 1.0 }; + def.label = L("Height"); + auto option = Option(def, "slab_h"); + optgroup->append_single_option_line(option); + + def.label = L("Initial Z"); + option = Option(def, "slab_z"); + optgroup->append_single_option_line(option); + } + + Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e) + { + auto page_idx = m_modificator_options_book->GetSelection(); + if (page_idx < 0) return; + switch (page_idx) + { + case 0: + object_parameters.type = LambdaTypeBox; + break; + case 1: + object_parameters.type = LambdaTypeCylinder; + break; + case 2: + object_parameters.type = LambdaTypeSphere; + break; + case 3: + object_parameters.type = LambdaTypeSlab; + break; + default: + break; + } + })); + + const auto button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL); + + wxButton* btn_OK = static_cast(FindWindowById(wxID_OK, this)); + btn_OK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + // validate user input + if (!CanClose())return; + EndModal(wxID_OK); + Destroy(); + }); + + wxButton* btn_CANCEL = static_cast(FindWindowById(wxID_CANCEL, this)); + btn_CANCEL->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + // validate user input + if (!CanClose())return; + EndModal(wxID_CANCEL); + Destroy(); + }); + + sizer->Add(button_sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); + + SetSizer(sizer); + sizer->Fit(this); + sizer->SetSizeHints(this); +} + +// Called from the constructor. +// Create a panel for a rectangular / circular / custom bed shape. +ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wxString& title) +{ + if (!m_type_name.IsEmpty() && m_type_name != title) + return nullptr; + + auto panel = m_type_name.IsEmpty() ? new wxPanel(m_modificator_options_book) : m_panel; + + ConfigOptionsGroupShp optgroup; + optgroup = std::make_shared(panel, _(L("Add")) + " " +title + " " +dots); + optgroup->label_width = 100; + + m_optgroups.push_back(optgroup); + + if (m_type_name.IsEmpty()) { + panel->SetSizerAndFit(optgroup->sizer); + m_modificator_options_book->AddPage(panel, title); + } + else + panel->SetSizer(optgroup->sizer); + + return optgroup; +} + + +} //namespace GUI +} //namespace Slic3r diff --git a/src/slic3r/GUI/LambdaObjectDialog.hpp b/src/slic3r/GUI/LambdaObjectDialog.hpp new file mode 100644 index 0000000000..6cc99c8a74 --- /dev/null +++ b/src/slic3r/GUI/LambdaObjectDialog.hpp @@ -0,0 +1,58 @@ +#ifndef slic3r_LambdaObjectDialog_hpp_ +#define slic3r_LambdaObjectDialog_hpp_ + +#include "GUI.hpp" + +#include +#include +#include + +class wxPanel; + +namespace Slic3r +{ +namespace GUI +{ +enum LambdaTypeIDs{ + LambdaTypeBox, + LambdaTypeCylinder, + LambdaTypeSphere, + LambdaTypeSlab +}; + +struct OBJECT_PARAMETERS +{ + LambdaTypeIDs type = LambdaTypeBox; + double dim[3];// = { 1.0, 1.0, 1.0 }; + int cyl_r = 1; + int cyl_h = 1; + double sph_rho = 1.0; + double slab_h = 1.0; + double slab_z = 0.0; +}; +class ConfigOptionsGroup; +using ConfigOptionsGroupShp = std::shared_ptr; +class LambdaObjectDialog : public wxDialog +{ + wxChoicebook* m_modificator_options_book = nullptr; + std::vector m_optgroups; + wxString m_type_name; + wxPanel* m_panel = nullptr; +public: + LambdaObjectDialog(wxWindow* parent, + const wxString type_name = wxEmptyString); + ~LambdaObjectDialog() {} + + bool CanClose() { return true; } // ??? + OBJECT_PARAMETERS& ObjectParameters() { return object_parameters; } + + ConfigOptionsGroupShp init_modificator_options_page(const wxString& title); + + // Note whether the window was already closed, so a pending update is not executed. + bool m_already_closed = false; + OBJECT_PARAMETERS object_parameters; + wxBoxSizer* sizer = nullptr; +}; +} //namespace GUI +} //namespace Slic3r +#endif //slic3r_LambdaObjectDialog_hpp_ diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp new file mode 100644 index 0000000000..94bb0bd5eb --- /dev/null +++ b/src/slic3r/GUI/MainFrame.cpp @@ -0,0 +1,812 @@ +#include "MainFrame.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Tab.hpp" +#include "PresetBundle.hpp" +#include "ProgressStatusBar.hpp" +#include "3DScene.hpp" +#include "Print.hpp" +#include "Polygon.hpp" +#include "AppConfig.hpp" +#include "wxExtensions.hpp" + +#include +#include "GUI_App.hpp" + +namespace Slic3r { +namespace GUI { + +MainFrame::MainFrame(const bool no_plater, const bool loaded) : +wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE), + m_no_plater(no_plater), + m_loaded(loaded) +{ + // Load the icon either from the exe, or from the ico file. +#if _WIN32 + { + TCHAR szExeFileName[MAX_PATH]; + GetModuleFileName(nullptr, szExeFileName, MAX_PATH); + SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); + } +#else + SetIcon(wxIcon(Slic3r::var("Slic3r_128px.png"), wxBITMAP_TYPE_PNG)); +#endif // _WIN32 + + // initialize tabpanel and menubar + init_tabpanel(); + init_menubar(); + + // set default tooltip timer in msec + // SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values + // (SetAutoPop is not available on GTK.) + wxToolTip::SetAutoPop(32767); + + // initialize status bar + m_statusbar = new ProgressStatusBar(this); + m_statusbar->embed(this); + m_statusbar->set_status_text(_(L("Version ")) + + SLIC3R_VERSION + + _(L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"))); + + m_loaded = true; + + // initialize layout + auto sizer = new wxBoxSizer(wxVERTICAL); + if (m_tabpanel) + sizer->Add(m_tabpanel, 1, wxEXPAND); + sizer->SetSizeHints(this); + SetSizer(sizer); + Fit(); + SetMinSize(wxSize(760, 490)); + SetSize(GetMinSize()); + Layout(); + + // declare events + Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) { + if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) { + event.Veto(); + return; + } + // save window size + wxGetApp().window_pos_save(this, "mainframe"); + // 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. + wxGetApp().app_config->save(); +// if (m_plater) +// m_plater->print = undef; + _3DScene::remove_all_canvases(); +// Slic3r::GUI::deregister_on_request_update_callback(); + // propagate event + event.Skip(); + }); + + // NB: Restoring the window position is done in a two-phase manner here, + // first the saved position is restored as-is and validation is done after the window is shown + // and initial round of events is complete, because on some platforms that is the only way + // to get an accurate window position & size. + wxGetApp().window_pos_restore(this, "mainframe"); + Bind(wxEVT_SHOW, [this](wxShowEvent&) { + CallAfter([this]() { + wxGetApp().window_pos_sanitize(this); + }); + }); + + update_ui_from_settings(); +} + +void MainFrame::init_tabpanel() +{ + m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL); + + m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) { + auto panel = m_tabpanel->GetCurrentPage(); + + if (panel == nullptr) + return; + + auto& tabs_list = wxGetApp().tabs_list; + if (find(tabs_list.begin(), tabs_list.end(), panel) != tabs_list.end()) { + // On GTK, the wxEVT_NOTEBOOK_PAGE_CHANGED event is triggered + // before the MainFrame is fully set up. + static_cast(panel)->OnActivate(); + } + }); + + if (!m_no_plater) { + m_plater = new Slic3r::GUI::Plater(m_tabpanel, this); + wxGetApp().plater_ = m_plater; + m_tabpanel->AddPage(m_plater, _(L("Plater"))); + } + + // The following event is emited by Tab implementation on config value change. + Bind(EVT_TAB_VALUE_CHANGED, &MainFrame::on_value_changed, this); + + // The following event is emited by Tab on preset selection, + // or when the preset's "modified" status changes. + Bind(EVT_TAB_PRESETS_CHANGED, &MainFrame::on_presets_changed, this); + + create_preset_tabs(); + + if (m_plater) { + // load initial config + auto full_config = wxGetApp().preset_bundle->full_config(); + m_plater->on_config_change(full_config); + + // Show a correct number of filament fields. + // nozzle_diameter is undefined when SLA printer is selected + if (full_config.has("nozzle_diameter")) { + m_plater->on_extruders_change(full_config.option("nozzle_diameter")->values.size()); + } + } +} + +void MainFrame::create_preset_tabs() +{ + wxGetApp().update_label_colours_from_appconfig(); + add_created_tab(new TabPrint(m_tabpanel)); + add_created_tab(new TabFilament(m_tabpanel)); + add_created_tab(new TabSLAPrint(m_tabpanel)); + add_created_tab(new TabSLAMaterial(m_tabpanel)); + add_created_tab(new TabPrinter(m_tabpanel)); +} + +void MainFrame::add_created_tab(Tab* panel) +{ + panel->create_preset_tab(); + + const auto printer_tech = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + + if (panel->supports_printer_technology(printer_tech)) + m_tabpanel->AddPage(panel, panel->title()); +} + +#if ENABLE_NEW_MENU_LAYOUT +bool MainFrame::can_save() const +{ + return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false; +} + +bool MainFrame::can_export_model() const +{ + return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false; +} + +bool MainFrame::can_export_gcode() const +{ + if (m_plater == nullptr) + return false; + + if (m_plater->model().objects.empty()) + return false; + + if (m_plater->is_export_gcode_scheduled()) + return false; + + // TODO:: add other filters + + return true; +} + +bool MainFrame::can_change_view() const +{ + int page_id = m_tabpanel->GetSelection(); + return (page_id != wxNOT_FOUND) ? m_tabpanel->GetPageText((size_t)page_id).Lower() == "plater" : false; +} +#endif // ENABLE_NEW_MENU_LAYOUT + +void MainFrame::init_menubar() +{ + // File menu + wxMenu* fileMenu = new wxMenu; + { +#if ENABLE_NEW_MENU_LAYOUT + wxMenuItem* item_open = append_menu_item(fileMenu, wxID_ANY, _(L("Open…\tCtrl+O")), _(L("Open a project file")), + [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "brick_add.png"); + wxMenuItem* item_save = append_menu_item(fileMenu, wxID_ANY, _(L("Save\tCtrl+S")), _(L("Save current project file")), + [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(m_plater->get_project_filename().wx_str()); }, "disk.png"); + wxMenuItem* item_save_as = append_menu_item(fileMenu, wxID_ANY, _(L("Save as…\tCtrl+Alt+S")), _(L("Save current project file as")), + [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "disk.png"); + + fileMenu->AppendSeparator(); + + wxMenu* import_menu = new wxMenu(); + wxMenuItem* item_import_model = append_menu_item(import_menu, wxID_ANY, _(L("Import STL/OBJ/AMF/3MF…\tCtrl+I")), _(L("Load a model")), + [this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "brick_add.png"); + import_menu->AppendSeparator(); + append_menu_item(import_menu, wxID_ANY, _(L("Import Config…\tCtrl+L")), _(L("Load exported configuration file")), + [this](wxCommandEvent&) { load_config_file(); }, "plugin_add.png"); + append_menu_item(import_menu, wxID_ANY, _(L("Import Config from project…\tCtrl+Alt+L")), _(L("Load configuration from project file")), + [this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, "plugin_add.png"); + import_menu->AppendSeparator(); + append_menu_item(import_menu, wxID_ANY, _(L("Import Config Bundle…")), _(L("Load presets from a bundle")), + [this](wxCommandEvent&) { load_configbundle(); }, "lorry_add.png"); + append_submenu(fileMenu, import_menu, wxID_ANY, _(L("Import")), _(L(""))); + + wxMenu* export_menu = new wxMenu(); + wxMenuItem* item_export_gcode = append_menu_item(export_menu, wxID_ANY, _(L("Export G-code…")), _(L("Export current plate as G-code")), + [this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, "cog_go.png"); + export_menu->AppendSeparator(); + wxMenuItem* item_export_stl = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL…")), _(L("Export current plate as STL")), + [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "brick_go.png"); + wxMenuItem* item_export_amf = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as AMF…")), _(L("Export current plate as AMF")), + [this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "brick_go.png"); + export_menu->AppendSeparator(); + append_menu_item(export_menu, wxID_ANY, _(L("Export Config…\tCtrl+E")), _(L("Export current configuration to file")), + [this](wxCommandEvent&) { export_config(); }, "plugin_go.png"); + append_menu_item(export_menu, wxID_ANY, _(L("Export Config Bundle…")), _(L("Export all presets to file")), + [this](wxCommandEvent&) { export_configbundle(); }, "lorry_go.png"); + append_submenu(fileMenu, export_menu, wxID_ANY, _(L("Export")), _(L(""))); + + fileMenu->AppendSeparator(); +#else + append_menu_item(fileMenu, wxID_ANY, _(L("Open STL/OBJ/AMF/3MF…\tCtrl+O")), _(L("Open a model")), + [this](wxCommandEvent&) { if (m_plater) m_plater->add(); }, "brick_add.png"); + append_menu_item(fileMenu, wxID_ANY, _(L("&Load Config…\tCtrl+L")), _(L("Load exported configuration file")), + [this](wxCommandEvent&) { load_config_file(); }, "plugin_add.png"); + append_menu_item(fileMenu, wxID_ANY, _(L("&Export Config…\tCtrl+E")), _(L("Export current configuration to file")), + [this](wxCommandEvent&) { export_config(); }, "plugin_go.png"); + append_menu_item(fileMenu, wxID_ANY, _(L("&Load Config Bundle…")), _(L("Load presets from a bundle")), + [this](wxCommandEvent&) { load_configbundle(); }, "lorry_add.png"); + append_menu_item(fileMenu, wxID_ANY, _(L("&Export Config Bundle…")), _(L("Export all presets to file")), + [this](wxCommandEvent&) { export_configbundle(); }, "lorry_go.png"); + fileMenu->AppendSeparator(); +#endif // ENABLE_NEW_MENU_LAYOUT + + m_menu_item_repeat = nullptr; + append_menu_item(fileMenu, wxID_ANY, _(L("Q&uick Slice…\tCtrl+U")), _(L("Slice a file into a G-code")), + [this](wxCommandEvent&) { + wxTheApp->CallAfter([this]() { + quick_slice(); + m_menu_item_repeat->Enable(is_last_input_file()); + }); }, "cog_go.png"); + append_menu_item(fileMenu, wxID_ANY, _(L("Quick Slice and Save &As…\tCtrl+Alt+U")), _(L("Slice a file into a G-code, save as")), + [this](wxCommandEvent&) { + wxTheApp->CallAfter([this]() { + quick_slice(qsSaveAs); + m_menu_item_repeat->Enable(is_last_input_file()); + }); }, "cog_go.png"); + m_menu_item_repeat = append_menu_item(fileMenu, wxID_ANY, _(L("&Repeat Last Quick Slice\tCtrl+Shift+U")), _(L("Repeat last quick slice")), + [this](wxCommandEvent&) { + wxTheApp->CallAfter([this]() { + quick_slice(qsReslice); + }); }, "cog_go.png"); + m_menu_item_repeat->Enable(false); + fileMenu->AppendSeparator(); + append_menu_item(fileMenu, wxID_ANY, _(L("Slice to SV&G…\tCtrl+G")), _(L("Slice file to a multi-layer SVG")), + [this](wxCommandEvent&) { quick_slice(qsSaveAs | qsExportSVG); }, "shape_handles.png"); + m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(&Re)Slice Now\tCtrl+S")), _(L("Start new slicing process")), + [this](wxCommandEvent&) { reslice_now(); }, "shape_handles.png"); + fileMenu->AppendSeparator(); + append_menu_item(fileMenu, wxID_ANY, _(L("Repair STL file…")), _(L("Automatically repair an STL file")), + [this](wxCommandEvent&) { repair_stl(); }, "wrench.png"); + fileMenu->AppendSeparator(); + append_menu_item(fileMenu, wxID_EXIT, _(L("&Quit")), _(L("Quit Slic3r")), + [this](wxCommandEvent&) { Close(false); } ); + +#if ENABLE_NEW_MENU_LAYOUT + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater != nullptr); }, item_open->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_save()); }, item_save->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_save()); }, item_save_as->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater != nullptr); }, item_import_model->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_gcode()); }, item_export_gcode->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_model()); }, item_export_stl->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_model()); }, item_export_amf->GetId()); +#endif // ENABLE_NEW_MENU_LAYOUT + } + +#if !ENABLE_NEW_MENU_LAYOUT + // Plater menu + if (m_plater) { + m_plater_menu = new wxMenu(); + append_menu_item(m_plater_menu, wxID_ANY, _(L("Export G-code...")), _(L("Export current plate as G-code")), + [this](wxCommandEvent&) { m_plater->export_gcode(); }, "cog_go.png"); + append_menu_item(m_plater_menu, wxID_ANY, _(L("Export plate as STL...")), _(L("Export current plate as STL")), + [this](wxCommandEvent&) { m_plater->export_stl(); }, "brick_go.png"); + append_menu_item(m_plater_menu, wxID_ANY, _(L("Export plate as AMF...")), _(L("Export current plate as AMF")), + [this](wxCommandEvent&) { m_plater->export_amf(); }, "brick_go.png"); + append_menu_item(m_plater_menu, wxID_ANY, _(L("Export plate as 3MF...")), _(L("Export current plate as 3MF")), + [this](wxCommandEvent&) { m_plater->export_3mf(); }, "brick_go.png"); + } +#endif // !ENABLE_NEW_MENU_LAYOUT + + // Window menu + auto windowMenu = new wxMenu(); + { + size_t tab_offset = 0; + if (m_plater) { + append_menu_item(windowMenu, wxID_ANY, L("Select &Plater Tab\tCtrl+1"), L("Show the plater"), + [this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png"); + tab_offset += 1; + } + if (tab_offset > 0) { + windowMenu->AppendSeparator(); + } + append_menu_item(windowMenu, wxID_ANY, L("Select P&rint Settings Tab\tCtrl+2"), L("Show the print settings"), + [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png"); + append_menu_item(windowMenu, wxID_ANY, L("Select &Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), + [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png"); + append_menu_item(windowMenu, wxID_ANY, L("Select Print&er Settings Tab\tCtrl+4"), L("Show the printer settings"), + [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png"); + } + + // View menu +#if ENABLE_NEW_MENU_LAYOUT + wxMenu* viewMenu = nullptr; + if (m_plater) { + viewMenu = new wxMenu(); + // \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators, + // as the simple numeric accelerators spoil all numeric data entry. + // The camera control accelerators are captured by GLCanvas3D::on_char(). + wxMenuItem* item_iso = append_menu_item(viewMenu, wxID_ANY, _(L("Iso")) + "\t\xA0" + "0", _(L("Iso View")), [this](wxCommandEvent&) { select_view("iso"); }); + viewMenu->AppendSeparator(); + wxMenuItem* item_top = append_menu_item(viewMenu, wxID_ANY, _(L("Top")) + "\t\xA0" + "1", _(L("Top View")), [this](wxCommandEvent&) { select_view("top"); }); + wxMenuItem* item_bottom = append_menu_item(viewMenu, wxID_ANY, _(L("Bottom")) + "\t\xA0" + "2", _(L("Bottom View")), [this](wxCommandEvent&) { select_view("bottom"); }); + wxMenuItem* item_front = append_menu_item(viewMenu, wxID_ANY, _(L("Front")) + "\t\xA0" + "3", _(L("Front View")), [this](wxCommandEvent&) { select_view("front"); }); + wxMenuItem* item_rear = append_menu_item(viewMenu, wxID_ANY, _(L("Rear")) + "\t\xA0" + "4", _(L("Rear View")), [this](wxCommandEvent&) { select_view("rear"); }); + wxMenuItem* item_left = append_menu_item(viewMenu, wxID_ANY, _(L("Left")) + "\t\xA0" + "5", _(L("Left View")), [this](wxCommandEvent&) { select_view("left"); }); + wxMenuItem* item_right = append_menu_item(viewMenu, wxID_ANY, _(L("Right")) + "\t\xA0" + "6", _(L("Right View")), [this](wxCommandEvent&) { select_view("right"); }); + + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_iso->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_top->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_bottom->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_front->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_rear->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_left->GetId()); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_right->GetId()); + } +#else + if (m_plater) { + m_viewMenu = new wxMenu(); + // \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators, + // as the simple numeric accelerators spoil all numeric data entry. + // The camera control accelerators are captured by GLCanvas3D::on_char(). + append_menu_item(m_viewMenu, wxID_ANY, _(L("Iso")) + "\t\xA0" + "0", _(L("Iso View")), [this](wxCommandEvent&) { select_view("iso"); }); + m_viewMenu->AppendSeparator(); + append_menu_item(m_viewMenu, wxID_ANY, _(L("Top")) + "\t\xA0" + "1", _(L("Top View")), [this](wxCommandEvent&) { select_view("top"); }); + append_menu_item(m_viewMenu, wxID_ANY, _(L("Bottom")) + "\t\xA0" + "2", _(L("Bottom View")), [this](wxCommandEvent&) { select_view("bottom"); }); + append_menu_item(m_viewMenu, wxID_ANY, _(L("Front")) + "\t\xA0" + "3", _(L("Front View")), [this](wxCommandEvent&) { select_view("front"); }); + append_menu_item(m_viewMenu, wxID_ANY, _(L("Rear")) + "\t\xA0" + "4", _(L("Rear View")), [this](wxCommandEvent&) { select_view("rear"); }); + append_menu_item(m_viewMenu, wxID_ANY, _(L("Left")) + "\t\xA0" + "5", _(L("Left View")), [this](wxCommandEvent&) { select_view("left"); }); + append_menu_item(m_viewMenu, wxID_ANY, _(L("Right")) + "\t\xA0" + "6", _(L("Right View")), [this](wxCommandEvent&) { select_view("right"); }); + } +#endif // ENABLE_NEW_MENU_LAYOUT + + // Help menu + auto helpMenu = new wxMenu(); + { + append_menu_item(helpMenu, wxID_ANY, _(L("Prusa 3D Drivers")), _(L("Open the Prusa3D drivers download page in your browser")), + [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://www.prusa3d.com/drivers/"); }); + append_menu_item(helpMenu, wxID_ANY, _(L("Prusa Edition Releases")), _(L("Open the Prusa Edition releases page in your browser")), + [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/releases"); }); +//# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{ +//# wxTheApp->check_version(1); +//# }); +//# $versioncheck->Enable(wxTheApp->have_version_check); + append_menu_item(helpMenu, wxID_ANY, _(L("Slic3r &Website")), _(L("Open the Slic3r website in your browser")), + [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://slic3r.org/"); }); + append_menu_item(helpMenu, wxID_ANY, _(L("Slic3r &Manual")), _(L("Open the Slic3r manual in your browser")), + [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://manual.slic3r.org/"); }); + helpMenu->AppendSeparator(); + append_menu_item(helpMenu, wxID_ANY, _(L("System Info")), _(L("Show system information")), + [this](wxCommandEvent&) { wxGetApp().system_info(); }); + append_menu_item(helpMenu, wxID_ANY, _(L("Show &Configuration Folder")), _(L("Show user configuration folder (datadir)")), + [this](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); }); + append_menu_item(helpMenu, wxID_ANY, _(L("Report an Issue")), _(L("Report an issue on the Slic3r Prusa Edition")), + [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/issues/new"); }); + append_menu_item(helpMenu, wxID_ANY, _(L("&About Slic3r")), _(L("Show about dialog")), + [this](wxCommandEvent&) { Slic3r::GUI::about(); }); + } + + // menubar + // assign menubar to frame after appending items, otherwise special items + // will not be handled correctly + { + auto menubar = new wxMenuBar(); + menubar->Append(fileMenu, L("&File")); +#if !ENABLE_NEW_MENU_LAYOUT + if (m_plater_menu) menubar->Append(m_plater_menu, L("&Plater")); +#endif // !ENABLE_NEW_MENU_LAYOUT + menubar->Append(windowMenu, L("&Window")); +#if ENABLE_NEW_MENU_LAYOUT + if (viewMenu) menubar->Append(viewMenu, L("&View")); +#else + if (m_viewMenu) menubar->Append(m_viewMenu, L("&View")); +#endif // !ENABLE_NEW_MENU_LAYOUT + // Add additional menus from C++ + wxGetApp().add_config_menu(menubar); + menubar->Append(helpMenu, L("&Help")); + SetMenuBar(menubar); + } +} + +// To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG". +void MainFrame::quick_slice(const int qs) +{ +// my $progress_dialog; + wxString input_file; +// eval +// { + // validate configuration + auto config = wxGetApp().preset_bundle->full_config(); + config.validate(); + + // select input file + if (!(qs & qsReslice)) { + auto dlg = new wxFileDialog(this, _(L("Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):")), + wxGetApp().app_config->get_last_dir(), "", + file_wildcards[FT_MODEL], wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dlg->ShowModal() != wxID_OK) { + dlg->Destroy(); + return; + } + input_file = dlg->GetPath(); + dlg->Destroy(); + if (!(qs & qsExportSVG)) + m_qs_last_input_file = input_file; + } + else { + if (m_qs_last_input_file.IsEmpty()) { + auto dlg = new wxMessageDialog(this, _(L("No previously sliced file.")), + _(L("Error")), wxICON_ERROR | wxOK); + dlg->ShowModal(); + return; + } + if (std::ifstream(m_qs_last_input_file.char_str())) { + auto dlg = new wxMessageDialog(this, _(L("Previously sliced file ("))+m_qs_last_input_file+_(L(") not found.")), + _(L("File Not Found")), wxICON_ERROR | wxOK); + dlg->ShowModal(); + return; + } + input_file = m_qs_last_input_file; + } + auto input_file_basename = get_base_name(input_file); + wxGetApp().app_config->update_skein_dir(get_dir_name(input_file)); + + auto bed_shape = Slic3r::Polygon::new_scale(config.option("bed_shape")->values); +// auto print_center = Slic3r::Pointf->new_unscale(bed_shape.bounding_box().center()); +// +// auto sprint = new Slic3r::Print::Simple( +// print_center = > print_center, +// status_cb = > [](int percent, const wxString& msg) { +// m_progress_dialog->Update(percent, msg+"…"); +// }); + + // keep model around + auto model = Slic3r::Model::read_from_file(input_file.ToStdString()); + +// sprint->apply_config(config); +// sprint->set_model(model); + + // Copy the names of active presets into the placeholder parser. +// wxGetApp().preset_bundle->export_selections(sprint->placeholder_parser); + + // select output file + wxString output_file; + if (qs & qsReslice) { + if (!m_qs_last_output_file.IsEmpty()) + output_file = m_qs_last_output_file; + } + else if (qs & qsSaveAs) { + // The following line may die if the output_filename_format template substitution fails. +// output_file = sprint->output_filepath; +// if (export_svg) +// output_file = ~s / \.[gG][cC][oO][dD][eE]$ / .svg /; + auto dlg = new wxFileDialog(this, _(L("Save ")) + (qs & qsExportSVG ? _(L("SVG")) : _(L("G-code"))) + _(L(" file as:")), + wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), get_base_name(input_file), + qs & qsExportSVG ? file_wildcards[FT_SVG] : file_wildcards[FT_GCODE], + wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (dlg->ShowModal() != wxID_OK) { + dlg->Destroy(); + return; + } + output_file = dlg->GetPath(); + dlg->Destroy(); + if (!(qs & qsExportSVG)) + m_qs_last_output_file = output_file; + wxGetApp().app_config->update_last_output_dir(get_dir_name(output_file)); + } + else if (qs & qsExportPNG) { +// output_file = sprint->output_filepath; +// output_file = ~s / \.[gG][cC][oO][dD][eE]$ / .zip / ; + auto dlg = new wxFileDialog(this, _(L("Save zip file as:")), + wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), + get_base_name(output_file), "*.zip", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (dlg->ShowModal() != wxID_OK) { + dlg->Destroy(); + return; + } + output_file = dlg->GetPath(); + dlg->Destroy(); + } + + // show processbar dialog + m_progress_dialog = new wxProgressDialog(_(L("Slicing…")), _(L("Processing ")) + input_file_basename + "…", + 100, this, 4); + m_progress_dialog->Pulse(); + { +// my @warnings = (); +// local $SIG{ __WARN__ } = sub{ push @warnings, $_[0] }; + +// sprint->output_file(output_file); +// if (export_svg) { +// sprint->export_svg(); +// } +// else if(export_png) { +// sprint->export_png(); +// } +// else { +// sprint->export_gcode(); +// } +// sprint->status_cb(undef); +// Slic3r::GUI::warning_catcher($self)->($_) for @warnings; + } + m_progress_dialog->Destroy(); + m_progress_dialog = nullptr; + + auto message = input_file_basename + _(L(" was successfully sliced.")); +// wxTheApp->notify(message); + wxMessageDialog(this, message, _(L("Slicing Done!")), wxOK | wxICON_INFORMATION).ShowModal(); +// }; +// Slic3r::GUI::catch_error(this, []() { if (m_progress_dialog) m_progress_dialog->Destroy(); }); +} + +void MainFrame::reslice_now() +{ + if (m_plater) + m_plater->reslice(); +} + +void MainFrame::repair_stl() +{ + wxString input_file; + { + auto dlg = new wxFileDialog(this, _(L("Select the STL file to repair:")), + wxGetApp().app_config->get_last_dir(), "", + file_wildcards[FT_STL], wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dlg->ShowModal() != wxID_OK) { + dlg->Destroy(); + return; + } + input_file = dlg->GetPath(); + dlg->Destroy(); + } + + auto output_file = input_file; + { +// output_file = ~s / \.[sS][tT][lL]$ / _fixed.obj / ; + auto dlg = new wxFileDialog( this, L("Save OBJ file (less prone to coordinate errors than STL) as:"), + get_dir_name(output_file), get_base_name(output_file), + file_wildcards[FT_OBJ], wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (dlg->ShowModal() != wxID_OK) { + dlg->Destroy(); + return /*undef*/; + } + output_file = dlg->GetPath(); + dlg->Destroy(); + } + + auto tmesh = new Slic3r::TriangleMesh(); + tmesh->ReadSTLFile(input_file.char_str()); + tmesh->repair(); + tmesh->WriteOBJFile(output_file.char_str()); + Slic3r::GUI::show_info(this, L("Your file was repaired."), L("Repair")); +} + +void MainFrame::export_config() +{ + // Generate a cummulative configuration for the selected print, filaments and printer. + auto config = wxGetApp().preset_bundle->full_config(); + // Validate the cummulative configuration. + auto valid = config.validate(); + if (!valid.empty()) { +// Slic3r::GUI::catch_error(this); + return; + } + // Ask user for the file name for the config file. + auto dlg = new wxFileDialog(this, _(L("Save configuration as:")), + !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), + !m_last_config.IsEmpty() ? get_base_name(m_last_config) : "config.ini", + file_wildcards[FT_INI], wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + wxString file; + if (dlg->ShowModal() == wxID_OK) + file = dlg->GetPath(); + dlg->Destroy(); + if (!file.IsEmpty()) { + wxGetApp().app_config->update_config_dir(get_dir_name(file)); + m_last_config = file; + config.save(file.ToStdString()); + } +} + +// Load a config file containing a Print, Filament & Printer preset. +void MainFrame::load_config_file(wxString file/* = wxEmptyString*/) +{ + if (file.IsEmpty()) { + if (!wxGetApp().check_unsaved_changes()) + return; + auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")), + !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), + "config.ini", "INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g", wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dlg->ShowModal() != wxID_OK) + return; + file = dlg->GetPath(); + dlg->Destroy(); + } + try { + wxGetApp().preset_bundle->load_config_file(file.ToStdString()); + } catch (std::exception & /* ex */) { + // Dont proceed further if the config file cannot be loaded. + // if (Slic3r::GUI::catch_error(this)) + // return; + } + wxGetApp().load_current_presets(); + wxGetApp().app_config->update_config_dir(get_dir_name(file)); + m_last_config = file; +} + +void MainFrame::export_configbundle() +{ + if (!wxGetApp().check_unsaved_changes()) + return; + // validate current configuration in case it's dirty + auto valid = wxGetApp().preset_bundle->full_config().validate(); + if (!valid.empty()) { +// Slic3r::GUI::catch_error(this); + return; + } + // Ask user for a file name. + auto dlg = new wxFileDialog(this, _(L("Save presets bundle as:")), + !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), + "Slic3r_config_bundle.ini", + file_wildcards[FT_INI], wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + wxString file; + if (dlg->ShowModal() == wxID_OK) + file = dlg->GetPath(); + dlg->Destroy(); + if (!file.IsEmpty()) { + // Export the config bundle. + wxGetApp().app_config->update_config_dir(get_dir_name(file)); + try { + wxGetApp().preset_bundle->export_configbundle(file.ToStdString()); + } catch (std::exception & /* ex */) { +// Slic3r::GUI::catch_error(this); + } + } +} + +// Loading a config bundle with an external file name used to be used +// to auto - install a config bundle on a fresh user account, +// but that behavior was not documented and likely buggy. +void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/) +{ + if (!wxGetApp().check_unsaved_changes()) + return; + if (file.IsEmpty()) { + auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")), + !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), + "config.ini", file_wildcards[FT_INI], wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dlg->ShowModal() != wxID_OK) + return; + file = dlg->GetPath(); + dlg->Destroy(); + } + + wxGetApp().app_config->update_config_dir(get_dir_name(file)); + + auto presets_imported = 0; + try { + presets_imported = wxGetApp().preset_bundle->load_configbundle(file.ToStdString()); + } catch (std::exception & /* ex */) { +// Slic3r::GUI::catch_error(this) and return; + } + + // Load the currently selected preset into the GUI, update the preset selection box. + wxGetApp().load_current_presets(); + + const auto message = wxString::Format(_(L("%d presets successfully imported.")), presets_imported); + Slic3r::GUI::show_info(this, message, "Info"); +} + +// Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset. +// Also update the platter with the new presets. +void MainFrame::load_config(const DynamicPrintConfig& config) +{ + for (auto tab : wxGetApp().tabs_list) + tab->load_config(config); + if (m_plater) + m_plater->on_config_change(config); +} + +void MainFrame::select_tab(size_t tab) const +{ + m_tabpanel->SetSelection(tab); +} + +// Set a camera direction, zoom to all objects. +void MainFrame::select_view(const std::string& direction) +{ + if (m_plater) + m_plater->select_view(direction); +} + +void MainFrame::on_presets_changed(SimpleEvent &event) +{ + auto *tab = dynamic_cast(event.GetEventObject()); + wxASSERT(tab != nullptr); + if (tab == nullptr) { + return; + } + + // Update preset combo boxes(Print settings, Filament, Material, Printer) from their respective tabs. + auto presets = tab->get_presets(); + if (m_plater != nullptr && presets != nullptr) { + + // FIXME: The preset type really should be a property of Tab instead + Slic3r::Preset::Type preset_type = tab->type(); + if (preset_type == Slic3r::Preset::TYPE_INVALID) { + wxASSERT(false); + return; + } + + m_plater->on_config_change(*tab->get_config()); + m_plater->sidebar().update_presets(preset_type); + } +} + +void MainFrame::on_value_changed(wxCommandEvent& event) +{ + auto *tab = dynamic_cast(event.GetEventObject()); + wxASSERT(tab != nullptr); + if (tab == nullptr) + return; + + auto opt_key = event.GetString(); + if (m_plater) { + m_plater->on_config_change(*tab->get_config()); // propagate config change events to the plater + if (opt_key == "extruders_count") { + auto value = event.GetInt(); + m_plater->on_extruders_change(value); + } + } + // Don't save while loading for the first time. + if (m_loaded) { + AppConfig &cfg = *wxGetApp().app_config; + if (cfg.get("autosave") == "1") + cfg.save(); + } +} + +// Called after the Preferences dialog is closed and the program settings are saved. +// Update the UI based on the current preferences. +void MainFrame::update_ui_from_settings() +{ + m_menu_item_reslice_now->Enable(wxGetApp().app_config->get("background_processing") == "1"); +// if (m_plater) m_plater->update_ui_from_settings(); + + for (auto tab: wxGetApp().tabs_list) + tab->update_ui_from_settings(); +} + + +std::string MainFrame::get_base_name(const wxString full_name) const +{ + return boost::filesystem::path(full_name).filename().string(); +} + +std::string MainFrame::get_dir_name(const wxString full_name) const +{ + return boost::filesystem::path(full_name).parent_path().string(); +} + + +} // GUI +} // Slic3r diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp new file mode 100644 index 0000000000..8cac3e8e23 --- /dev/null +++ b/src/slic3r/GUI/MainFrame.hpp @@ -0,0 +1,112 @@ +#ifndef slic3r_MainFrame_hpp_ +#define slic3r_MainFrame_hpp_ + +#include "PrintConfig.hpp" + +#include + +#include +#include + +#include "Plater.hpp" +#include "Event.hpp" + +class wxMenuBar; +class wxNotebook; +class wxPanel; +class wxMenu; +class wxProgressDialog; + +namespace Slic3r { + +class ProgressStatusBar; + +// #define _(s) Slic3r::GUI::I18N::translate((s)) + +namespace GUI +{ +class Tab; + +enum QuickSlice +{ + qsUndef = 0, + qsReslice = 1, + qsSaveAs = 2, + qsExportSVG = 4, + qsExportPNG = 8 +}; + +struct PresetTab { + std::string name; + Tab* panel; + PrinterTechnology technology; +}; + +class MainFrame : public wxFrame +{ + bool m_no_plater; + bool m_loaded; + int m_lang_ch_event; + int m_preferences_event; + + wxString m_qs_last_input_file = wxEmptyString; + wxString m_qs_last_output_file = wxEmptyString; + wxString m_last_config = wxEmptyString; + + wxMenuItem* m_menu_item_repeat { nullptr }; + wxMenuItem* m_menu_item_reslice_now { nullptr }; +#if !ENABLE_NEW_MENU_LAYOUT + wxMenu* m_plater_menu{ nullptr }; + wxMenu* m_viewMenu{ nullptr }; +#endif // !ENABLE_NEW_MENU_LAYOUT + + std::string get_base_name(const wxString full_name) const ; + std::string get_dir_name(const wxString full_name) const ; + + void on_presets_changed(SimpleEvent&); + void on_value_changed(wxCommandEvent&); + +#if ENABLE_NEW_MENU_LAYOUT + bool can_save() const; + bool can_export_model() const; + bool can_export_gcode() const; + bool can_change_view() const; +#endif // ENABLE_NEW_MENU_LAYOUT + +public: + MainFrame() {} + MainFrame(const bool no_plater, const bool loaded); + ~MainFrame() {} + + Plater* plater() { return m_plater; } + + void init_tabpanel(); + void create_preset_tabs(); + void add_created_tab(Tab* panel); + void init_menubar(); + + void update_ui_from_settings(); + bool is_loaded() const { return m_loaded; } + bool is_last_input_file() const { return !m_qs_last_input_file.IsEmpty(); } + + void quick_slice(const int qs = qsUndef); + void reslice_now(); + void repair_stl(); + void export_config(); + void load_config_file(wxString file = wxEmptyString); + void export_configbundle(); + void load_configbundle(wxString file = wxEmptyString); + void load_config(const DynamicPrintConfig& config); + void select_tab(size_t tab) const; + void select_view(const std::string& direction); + + Plater* m_plater { nullptr }; + wxNotebook* m_tabpanel { nullptr }; + wxProgressDialog* m_progress_dialog { nullptr }; + ProgressStatusBar* m_statusbar { nullptr }; +}; + +} // GUI +} //Slic3r + +#endif // slic3r_MainFrame_hpp_ diff --git a/xs/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp similarity index 100% rename from xs/src/slic3r/GUI/MsgDialog.cpp rename to src/slic3r/GUI/MsgDialog.cpp diff --git a/xs/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp similarity index 100% rename from xs/src/slic3r/GUI/MsgDialog.hpp rename to src/slic3r/GUI/MsgDialog.hpp diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp similarity index 73% rename from xs/src/slic3r/GUI/OptionsGroup.cpp rename to src/slic3r/GUI/OptionsGroup.cpp index a2d6559a9a..4d70d529e4 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -22,17 +22,18 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co // is the normal type. if (opt.gui_type.compare("select") == 0) { } else if (opt.gui_type.compare("select_open") == 0) { - m_fields.emplace(id, STDMOVE(Choice::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(Choice::Create(parent(), opt, id))); } else if (opt.gui_type.compare("color") == 0) { - m_fields.emplace(id, STDMOVE(ColourPicker::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(ColourPicker::Create(parent(), opt, id))); } else if (opt.gui_type.compare("f_enum_open") == 0 || opt.gui_type.compare("i_enum_open") == 0 || opt.gui_type.compare("i_enum_closed") == 0) { - m_fields.emplace(id, STDMOVE(Choice::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(Choice::Create(parent(), opt, id))); } else if (opt.gui_type.compare("slider") == 0) { + m_fields.emplace(id, std::move(SliderCtrl::Create(parent(), opt, id))); } else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl } else if (opt.gui_type.compare("legend") == 0) { // StaticText - m_fields.emplace(id, STDMOVE(StaticText::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(StaticText::Create(parent(), opt, id))); } else { switch (opt.type) { case coFloatOrPercent: @@ -42,21 +43,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co case coPercents: case coString: case coStrings: - m_fields.emplace(id, STDMOVE(TextCtrl::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(TextCtrl::Create(parent(), opt, id, process_enter))); break; case coBool: case coBools: - m_fields.emplace(id, STDMOVE(CheckBox::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(CheckBox::Create(parent(), opt, id))); break; case coInt: case coInts: - m_fields.emplace(id, STDMOVE(SpinCtrl::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(SpinCtrl::Create(parent(), opt, id))); break; case coEnum: - m_fields.emplace(id, STDMOVE(Choice::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(Choice::Create(parent(), opt, id))); break; case coPoints: - m_fields.emplace(id, STDMOVE(PointCtrl::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(PointCtrl::Create(parent(), opt, id))); break; case coNone: break; default: @@ -65,43 +66,49 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co } // Grab a reference to fields for convenience const t_field& field = m_fields[id]; - field->m_on_change = [this](std::string opt_id, boost::any value){ + field->m_on_change = [this](std::string opt_id, boost::any value) { //! This function will be called from Field. //! Call OptionGroup._on_change(...) - if (!this->m_disabled) + if (!m_disabled) this->on_change_OG(opt_id, value); }; - field->m_on_kill_focus = [this](){ + field->m_on_kill_focus = [this]() { //! This function will be called from Field. - if (!this->m_disabled) + if (!m_disabled) this->on_kill_focus(); }; field->m_parent = parent(); //! Label to change background color, when option is modified field->m_Label = label; - field->m_back_to_initial_value = [this](std::string opt_id){ - if (!this->m_disabled) + field->m_back_to_initial_value = [this](std::string opt_id) { + if (!m_disabled) this->back_to_initial_value(opt_id); }; - field->m_back_to_sys_value = [this](std::string opt_id){ + field->m_back_to_sys_value = [this](std::string opt_id) { if (!this->m_disabled) this->back_to_sys_value(opt_id); }; - if (!m_show_modified_btns) { - field->m_Undo_btn->Hide(); - field->m_Undo_to_sys_btn->Hide(); - } -// if (nonsys_btn_icon != nullptr) -// field->set_nonsys_btn_icon(*nonsys_btn_icon); // assign function objects for callbacks, etc. return field; } +void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field) +{ + if (!m_show_modified_btns) { + field->m_Undo_btn->set_as_hidden(); + field->m_Undo_to_sys_btn->set_as_hidden(); + return; + } + + sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); + sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL); +} + void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* = nullptr*/) { -//! if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)){ - if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width){ +//! if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)) { + if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width) { if (line.sizer != nullptr) { sizer->Add(line.sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 15); return; @@ -116,6 +123,10 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* for (auto opt : option_set) m_options.emplace(opt.opt_id, opt); + // add mode value for current line to m_options_mode + if (!option_set.empty()) + m_options_mode.push_back(option_set[0].opt.mode); + // if we have a single option with no label, no sidetext just add it directly to sizer if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && @@ -133,8 +144,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* const auto& field = build_field(option); auto btn_sizer = new wxBoxSizer(wxHORIZONTAL); - btn_sizer->Add(field->m_Undo_to_sys_btn); - btn_sizer->Add(field->m_Undo_btn); + add_undo_buttuns_to_sizer(btn_sizer, field); tmp_sizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 0); if (is_window_field(field)) tmp_sizer->Add(field->getWindow(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5); @@ -149,6 +159,10 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* m_panel->Layout(); #endif /* __WXGTK__ */ + // if we have an extra column, build it + if (extra_column) + grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3); + // Build a label if we have it wxStaticText* label=nullptr; if (label_width != 0) { @@ -163,7 +177,16 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* wxDefaultPosition, wxSize(label_width, -1), label_style); label->SetFont(label_font); label->Wrap(label_width); // avoid a Linux/GTK bug - grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5); + if (!line.near_label_widget) + grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5); + else { + // If we're here, we have some widget near the label + // so we need a horizontal sizer to arrange these things + auto sizer = new wxBoxSizer(wxHORIZONTAL); + grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1); + sizer->Add(line.near_label_widget(parent()), 0, wxRIGHT, 7); + sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5); + } if (line.label_tooltip.compare("") != 0) label->SetToolTip(line.label_tooltip); } @@ -177,28 +200,28 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* return; } - // if we have a single option with no sidetext just add it directly to the grid sizer + // 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 auto sizer = new wxBoxSizer(wxHORIZONTAL); - grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM|wxTOP|wxLEFT), staticbox ? 0 : 1); + grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1); + // If we have a single option with no sidetext just add it directly to the grid sizer if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { const auto& option = option_set.front(); const auto& field = build_field(option, label); - sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); - sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL); + add_undo_buttuns_to_sizer(sizer, field); if (is_window_field(field)) - sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, (option.opt.full_width ? wxEXPAND : 0) | - wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, (wxOSX||!staticbox) ? 0 : 2); + sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, //(option.opt.full_width ? wxEXPAND : 0) | + wxBOTTOM | wxTOP | (option.opt.full_width ? wxEXPAND : wxALIGN_CENTER_VERTICAL), (wxOSX || !staticbox) ? 0 : 2); if (is_sizer_field(field)) - sizer->Add(field->getSizer(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0); + sizer->Add(field->getSizer(), 1, /*(*/option.opt.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 - for (auto opt : option_set) { + for (auto opt : option_set) { ConfigOptionDef option = opt.opt; + wxSizer* sizer_tmp = sizer; // add label if any if (option.label != "") { wxString str_label = _(option.label); @@ -208,33 +231,34 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* // L_str(option.label); label = new wxStaticText(parent(), wxID_ANY, str_label + ":", wxDefaultPosition, wxDefaultSize); label->SetFont(label_font); - sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL, 0); + sizer_tmp->Add(label, 0, /*wxALIGN_RIGHT |*/ wxALIGN_CENTER_VERTICAL, 0); } // add field const Option& opt_ref = opt; auto& field = build_field(opt_ref, label); - sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); - sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL, 0); + add_undo_buttuns_to_sizer(sizer_tmp, field); is_sizer_field(field) ? - sizer->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) : - sizer->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0); + sizer_tmp->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) : + sizer_tmp->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0); // add sidetext if any if (option.sidetext != "") { - auto sidetext = new wxStaticText(parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, wxDefaultSize); + auto sidetext = new wxStaticText( parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, + wxSize(sidetext_width, -1)/*wxDefaultSize*/, wxALIGN_LEFT); sidetext->SetFont(sidetext_font); - sizer->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); + sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); + field->set_side_text_ptr(sidetext); } // add side widget if any if (opt.side_widget != nullptr) { - sizer->Add(opt.side_widget(parent())/*!.target()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification + sizer_tmp->Add(opt.side_widget(parent())/*!.target()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification } if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back()) { - sizer->AddSpacer(6); + sizer_tmp->AddSpacer(6); } } // add extra sizers if any @@ -259,7 +283,7 @@ void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost:: Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index /*= -1*/) { if (!m_config->has(opt_key)) { - std::cerr << "No " << opt_key << " in ConfigOptionsGroup config."; + std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.\n"; } std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index); @@ -289,7 +313,7 @@ void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const b // get value //! auto field_value = get_value(opt_id); if (option.gui_flags.compare("serialized")==0) { - if (opt_index != -1){ + if (opt_index != -1) { // die "Can't set serialized option indexed value" ; } change_opt_value(*m_config, opt_key, value); @@ -331,7 +355,7 @@ void ConfigOptionsGroup::back_to_sys_value(const std::string& opt_key) void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key) { boost::any value; - if (opt_key == "extruders_count"){ + if (opt_key == "extruders_count") { auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); value = int(nozzle_diameter->values.size()); } @@ -352,7 +376,7 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, on_change_OG(opt_key, get_value(opt_key)); } -void ConfigOptionsGroup::reload_config(){ +void ConfigOptionsGroup::reload_config() { for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) { auto opt_id = it->first; std::string opt_key = m_opt_map.at(opt_id).first; @@ -363,7 +387,50 @@ void ConfigOptionsGroup::reload_config(){ } -boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_index, bool deserialize){ +void ConfigOptionsGroup::Hide() +{ + Show(false); +} + +void ConfigOptionsGroup::Show(const bool show) +{ + sizer->ShowItems(show); +#ifdef __WXGTK__ + m_panel->Show(show); + m_grid_sizer->Show(show); +#endif /* __WXGTK__ */ +} + +bool ConfigOptionsGroup::update_visibility(ConfigOptionMode mode) { + if (m_options_mode.empty()) + return true; + if (m_grid_sizer->GetEffectiveRowsCount() != m_options_mode.size() && + m_options_mode.size() == 1) + return m_options_mode[0] <= mode; + + Show(true); + + int coef = 0; + int hidden_row_cnt = 0; + const int cols = m_grid_sizer->GetCols(); + for (auto opt_mode : m_options_mode) { + const bool show = opt_mode <= mode; + if (!show) { + hidden_row_cnt++; + for (int i = 0; i < cols; ++i) + m_grid_sizer->Show(coef + i, show); + } + coef+= cols; + } + + if (hidden_row_cnt == m_options_mode.size()) { + sizer->ShowItems(false); + return false; + } + return true; +} + +boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_index, bool deserialize) { if (deserialize) { // Want to edit a vector value(currently only multi - strings) in a single edit box. @@ -387,7 +454,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config boost::any ret; wxString text_value = wxString(""); const ConfigOptionDef* opt = config.def()->get(opt_key); - switch (opt->type){ + switch (opt->type) { case coFloatOrPercent:{ const auto &value = *config.option(opt_key); if (value.percent) @@ -420,13 +487,13 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config ret = static_cast(config.opt_string(opt_key)); break; case coStrings: - if (opt_key.compare("compatible_printers") == 0){ + if (opt_key.compare("compatible_printers") == 0) { ret = config.option(opt_key)->values; break; } if (config.option(opt_key)->values.empty()) ret = text_value; - else if (opt->gui_flags.compare("serialized") == 0){ + else if (opt->gui_flags.compare("serialized") == 0) { std::vector values = config.option(opt_key)->values; if (!values.empty() && values[0].compare("") != 0) for (auto el : values) @@ -450,19 +517,19 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config break; case coEnum:{ if (opt_key.compare("external_fill_pattern") == 0 || - opt_key.compare("fill_pattern") == 0 ){ + opt_key.compare("fill_pattern") == 0 ) { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("gcode_flavor") == 0 ){ + else if (opt_key.compare("gcode_flavor") == 0 ) { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("support_material_pattern") == 0){ + else if (opt_key.compare("support_material_pattern") == 0) { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("seam_position") == 0){ + else if (opt_key.compare("seam_position") == 0) { ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("host_type") == 0){ + else if (opt_key.compare("host_type") == 0) { ret = static_cast(config.option>(opt_key)->value); } } @@ -480,13 +547,14 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config return ret; } -Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int opt_index){ +Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int opt_index) +{ Field* field = get_field(opt_key); if (field != nullptr) return field; std::string opt_id = ""; for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) { - if (opt_key == m_opt_map.at(it->first).first && opt_index == m_opt_map.at(it->first).second){ + if (opt_key == m_opt_map.at(it->first).first && opt_index == m_opt_map.at(it->first).second) { opt_id = it->first; break; } diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp similarity index 74% rename from xs/src/slic3r/GUI/OptionsGroup.hpp rename to src/slic3r/GUI/OptionsGroup.hpp index 422e1afd9e..c86ad09753 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -14,6 +14,7 @@ #include "libslic3r/libslic3r.h" #include "Field.hpp" +#include "GUI_App.hpp" // Translate the ifdef #ifdef __WXOSX__ @@ -28,7 +29,6 @@ namespace Slic3r { namespace GUI { /// Widget type describes a function object that returns a wxWindow (our widget) and accepts a wxWidget (parent window). using widget_t = std::function;//!std::function; -using column_t = std::function; //auto default_label_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); //GetSystemColour //auto modified_label_clr = *new wxColour(254, 189, 101); @@ -53,6 +53,7 @@ public: size_t full_width {0}; wxSizer* sizer {nullptr}; widget_t widget {nullptr}; + std::function near_label_widget{ nullptr }; void append_option(const Option& option) { m_options.push_back(option); @@ -71,10 +72,13 @@ private: std::vector m_extra_widgets;//! {std::vector()}; }; +using column_t = std::function;//std::function; + using t_optionfield_map = std::map; using t_opt_map = std::map< std::string, std::pair >; class OptionsGroup { + wxStaticBox* stb; public: const bool staticbox {true}; const wxString title {wxString("")}; @@ -88,8 +92,9 @@ public: wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; + int sidetext_width{ -1 }; -// std::function nonsys_btn_icon{ nullptr }; + bool process_enter { false }; /// Returns a copy of the pointer of the parent wxWindow. /// Accessor function is because users are not allowed to change the parent @@ -101,6 +106,11 @@ public: return m_parent; #endif /* __WXGTK__ */ } +#ifdef __WXGTK__ + wxWindow* get_parent() const { + return m_parent; + } +#endif /* __WXGTK__ */ void append_line(const Line& line, wxStaticText** colored_Label = nullptr); Line create_single_option_line(const Option& option) const; @@ -124,24 +134,47 @@ public: return out; } - inline void enable() { for (auto& field : m_fields) field.second->enable(); } - inline void disable() { for (auto& field : m_fields) field.second->disable(); } - - void set_show_modified_btns_val(bool show) { - m_show_modified_btns = show; + bool set_side_text(const t_config_option_key& opt_key, const wxString& side_text) { + if (m_fields.find(opt_key) == m_fields.end()) return false; + auto st = m_fields.at(opt_key)->m_side_text; + if (!st) return false; + st->SetLabel(side_text); + return true; } - OptionsGroup(wxWindow* _parent, const wxString& title, bool is_tab_opt=false) : - m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt), staticbox(title!="") { - auto stb = new wxStaticBox(_parent, wxID_ANY, title); - stb->SetFont(bold_font()); - sizer = (staticbox ? new wxStaticBoxSizer(stb/*new wxStaticBox(_parent, wxID_ANY, title)*/, wxVERTICAL) : new wxBoxSizer(wxVERTICAL)); + void set_name(const wxString& new_name) { + stb->SetLabel(new_name); + } + + inline void enable() { for (auto& field : m_fields) field.second->enable(); } + inline void disable() { for (auto& field : m_fields) field.second->disable(); } + void set_grid_vgap(int gap) { m_grid_sizer->SetVGap(gap); } + + void set_show_modified_btns_val(bool show) { + m_show_modified_btns = show; + } + + // The controls inside this option group will generate the event wxEVT_TEXT_ENTER + void set_process_enter() { + process_enter = true; + } + + OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false, + column_t extra_clmn = nullptr) : + m_parent(_parent), title(title), + m_show_modified_btns(is_tab_opt), + staticbox(title!=""), extra_column(extra_clmn) { + if (staticbox) { + stb = new wxStaticBox(_parent, wxID_ANY, title); + stb->SetFont(wxGetApp().bold_font()); + } + sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL)); auto num_columns = 1U; if (label_width != 0) num_columns++; if (extra_column != nullptr) num_columns++; - m_grid_sizer = new wxFlexGridSizer(0, num_columns, 0,0); - static_cast(m_grid_sizer)->SetFlexibleDirection(wxHORIZONTAL); - static_cast(m_grid_sizer)->AddGrowableCol(label_width != 0); + m_grid_sizer = new wxFlexGridSizer(0, num_columns, 1,0); + static_cast(m_grid_sizer)->SetFlexibleDirection(wxBOTH/*wxHORIZONTAL*/); + static_cast(m_grid_sizer)->AddGrowableCol(label_width == 0 ? 0 : !extra_column ? 1 : 2 ); #ifdef __WXGTK__ m_panel = new wxPanel( _parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); sizer->Fit(m_panel); @@ -151,9 +184,12 @@ public: #endif /* __WXGTK__ */ } + wxGridSizer* get_grid_sizer() { return m_grid_sizer; } + protected: std::map m_options; wxWindow* m_parent {nullptr}; + std::vector m_options_mode; /// Field list, contains unique_ptrs of the derived type. /// using types that need to know what it is beyond the public interface @@ -177,23 +213,26 @@ protected: const t_field& build_field(const t_config_option_key& id, const ConfigOptionDef& opt, wxStaticText* label = nullptr); const t_field& build_field(const t_config_option_key& id, wxStaticText* label = nullptr); const t_field& build_field(const Option& opt, wxStaticText* label = nullptr); + void add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field); - virtual void on_kill_focus (){}; + virtual void on_kill_focus () {}; virtual void on_change_OG(const t_config_option_key& opt_id, const boost::any& value); - virtual void back_to_initial_value(const std::string& opt_key){} - virtual void back_to_sys_value(const std::string& opt_key){} + virtual void back_to_initial_value(const std::string& opt_key) {} + virtual void back_to_sys_value(const std::string& opt_key) {} }; class ConfigOptionsGroup: public OptionsGroup { public: - ConfigOptionsGroup(wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, bool is_tab_opt = false) : - OptionsGroup(parent, title, is_tab_opt), m_config(_config) {} + ConfigOptionsGroup( wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, + bool is_tab_opt = false, column_t extra_clmn = nullptr) : + OptionsGroup(parent, title, is_tab_opt, extra_clmn), m_config(_config) {} /// reference to libslic3r config, non-owning pointer (?). DynamicPrintConfig* m_config {nullptr}; bool m_full_labels {0}; t_opt_map m_opt_map; + void set_config(DynamicPrintConfig* config) { m_config = config; } Option get_option(const std::string& opt_key, int opt_index = -1); Line create_single_option_line(const std::string& title, int idx = -1) /*const*/{ Option option = get_option(title, idx); @@ -214,6 +253,10 @@ public: void back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key); void on_kill_focus() override{ reload_config();} void reload_config(); + // return value shows visibility : false => all options are hidden + void Hide(); + void Show(const bool show); + bool update_visibility(ConfigOptionMode mode); boost::any config_value(const std::string& opt_key, int opt_index, bool deserialize); // return option value from config boost::any get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index = -1); @@ -224,8 +267,8 @@ public: class ogStaticText :public wxStaticText{ public: ogStaticText() {} - ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize){} - ~ogStaticText(){} + ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize) {} + ~ogStaticText() {} void SetText(const wxString& value, bool wrap = true); }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp new file mode 100644 index 0000000000..3b44f0c832 --- /dev/null +++ b/src/slic3r/GUI/Plater.cpp @@ -0,0 +1,2685 @@ +#include "Plater.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libslic3r/libslic3r.h" +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/ModelArrange.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/GCode/PreviewData.hpp" +#include "libslic3r/Utils.hpp" +#include "libslic3r/Polygon.hpp" +#include "libslic3r/Format/STL.hpp" +#include "libslic3r/Format/AMF.hpp" +#include "libslic3r/Format/3mf.hpp" +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "GUI_ObjectList.hpp" +#include "GUI_ObjectManipulation.hpp" +#include "GUI_Utils.hpp" +#include "wxExtensions.hpp" +#include "MainFrame.hpp" +#include "3DScene.hpp" +#include "GLCanvas3D.hpp" +#include "GLToolbar.hpp" +#include "GUI_Preview.hpp" +#include "Tab.hpp" +#include "PresetBundle.hpp" +#include "BackgroundSlicingProcess.hpp" +#include "ProgressStatusBar.hpp" +#include "slic3r/Utils/ASCIIFolding.hpp" +#include "../Utils/FixModelByWin10.hpp" + +#include // Needs to be last because reasons :-/ +#include "WipeTowerDialog.hpp" + +using boost::optional; +namespace fs = boost::filesystem; +using Slic3r::_3DScene; +using Slic3r::Preset; + + +namespace Slic3r { +namespace GUI { + + +wxDEFINE_EVENT(EVT_PROGRESS_BAR, wxCommandEvent); +wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent); +wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, wxCommandEvent); + +// Sidebar widgets + +// struct InfoBox : public wxStaticBox +// { +// InfoBox(wxWindow *parent, const wxString &label) : +// wxStaticBox(parent, wxID_ANY, label) +// { +// SetFont(GUI::small_font().Bold()); +// } +// }; + +class ObjectInfo : public wxStaticBoxSizer +{ +public: + ObjectInfo(wxWindow *parent); + + wxStaticBitmap *manifold_warning_icon; + wxStaticText *info_size; + wxStaticText *info_volume; + wxStaticText *info_facets; + wxStaticText *info_materials; + wxStaticText *info_manifold; + bool showing_manifold_warning_icon; + void show_sizer(bool show); +}; + +ObjectInfo::ObjectInfo(wxWindow *parent) : + wxStaticBoxSizer(new wxStaticBox(parent, wxID_ANY, _(L("Info"))), wxVERTICAL) +{ + GetStaticBox()->SetFont(wxGetApp().bold_font()); + + auto *grid_sizer = new wxFlexGridSizer(4, 5, 5); + grid_sizer->SetFlexibleDirection(wxHORIZONTAL); + grid_sizer->AddGrowableCol(1, 1); + grid_sizer->AddGrowableCol(3, 1); + + auto init_info_label = [parent, grid_sizer](wxStaticText **info_label, wxString text_label) { + auto *text = new wxStaticText(parent, wxID_ANY, text_label); + text->SetFont(wxGetApp().small_font()); + *info_label = new wxStaticText(parent, wxID_ANY, ""); + (*info_label)->SetFont(wxGetApp().small_font()); + grid_sizer->Add(text, 0); + grid_sizer->Add(*info_label, 0); + }; + + init_info_label(&info_size, _(L("Size:"))); + init_info_label(&info_volume, _(L("Volume:"))); + init_info_label(&info_facets, _(L("Facets:"))); + init_info_label(&info_materials, _(L("Materials:"))); + Add(grid_sizer, 0, wxEXPAND); + + auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _(L("Manifold:"))); + info_manifold_text->SetFont(wxGetApp().small_font()); + info_manifold = new wxStaticText(parent, wxID_ANY, ""); + info_manifold->SetFont(wxGetApp().small_font()); + wxBitmap bitmap(GUI::from_u8(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG); + manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, bitmap); + auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL); + sizer_manifold->Add(info_manifold_text, 0); + sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2); + sizer_manifold->Add(info_manifold, 0, wxLEFT, 2); + Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4); +} + +void ObjectInfo::show_sizer(bool show) +{ + Show(show); + if (show) + manifold_warning_icon->Show(showing_manifold_warning_icon && show); +} + +enum SlisedInfoIdx +{ + siFilament_m, + siFilament_mm3, + siFilament_g, + siCost, + siEstimatedTime, + siWTNumbetOfToolchanges, + + siCount +}; + +class SlicedInfo : public wxStaticBoxSizer +{ +public: + SlicedInfo(wxWindow *parent); + void SetTextAndShow(SlisedInfoIdx idx, const wxString& text, const wxString& new_label=""); + +private: + std::vector> info_vec; +}; + +SlicedInfo::SlicedInfo(wxWindow *parent) : + wxStaticBoxSizer(new wxStaticBox(parent, wxID_ANY, _(L("Sliced Info"))), wxVERTICAL) +{ + GetStaticBox()->SetFont(wxGetApp().bold_font()); + + auto *grid_sizer = new wxFlexGridSizer(2, 5, 15); + grid_sizer->SetFlexibleDirection(wxVERTICAL); + + info_vec.reserve(siCount); + + auto init_info_label = [this, parent, grid_sizer](wxString text_label) { + auto *text = new wxStaticText(parent, wxID_ANY, text_label); + text->SetFont(wxGetApp().small_font()); + auto info_label = new wxStaticText(parent, wxID_ANY, "N/A"); + info_label->SetFont(wxGetApp().small_font()); + grid_sizer->Add(text, 0); + grid_sizer->Add(info_label, 0); + info_vec.push_back(std::pair(text, info_label)); + }; + + init_info_label(_(L("Used Filament (m)"))); + init_info_label(_(L("Used Filament (mm³)"))); + init_info_label(_(L("Used Filament (g)"))); + init_info_label(_(L("Cost"))); + init_info_label(_(L("Estimated printing time"))); + init_info_label(_(L("Number of tool changes"))); + + Add(grid_sizer, 0, wxEXPAND); + this->Show(false); +} + +void SlicedInfo::SetTextAndShow(SlisedInfoIdx idx, const wxString& text, const wxString& new_label/*=""*/) +{ + const bool show = text != "N/A"; + if (show) + info_vec[idx].second->SetLabelText(text); + if (!new_label.IsEmpty()) + info_vec[idx].first->SetLabelText(new_label); + info_vec[idx].first->Show(show); + info_vec[idx].second->Show(show); +} + +PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) : + wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY), + preset_type(preset_type), + last_selected(wxNOT_FOUND) +{ + Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) { + auto selected_item = this->GetSelection(); + + auto marker = reinterpret_cast(this->GetClientData(selected_item)); + if (marker == LABEL_ITEM_MARKER) { + this->SetSelection(this->last_selected); + evt.StopPropagation(); + } else if (this->last_selected != selected_item) { + this->last_selected = selected_item; + evt.SetInt(this->preset_type); + evt.Skip(); + } else { + evt.StopPropagation(); + } + }); + + if (preset_type == Slic3r::Preset::TYPE_FILAMENT) + { + Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) { + if (extruder_idx < 0 || event.GetLogicalPosition(wxClientDC(this)).x > 24) { + // Let the combo box process the mouse click. + event.Skip(); + return; + } + + // Swallow the mouse click and open the color picker. + auto data = new wxColourData(); + data->SetChooseFull(1); + auto dialog = new wxColourDialog(wxGetApp().mainframe, data); + if (dialog->ShowModal() == wxID_OK) { + DynamicPrintConfig cfg = *wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config(); + + //FIXME this is too expensive to call full_config to get just the extruder color! + auto colors = static_cast(wxGetApp().preset_bundle->full_config().option("extruder_colour")->clone()); + colors->values[extruder_idx] = dialog->GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX); + + cfg.set_key_value("extruder_colour", colors); + + wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg); + wxGetApp().preset_bundle->update_platter_filament_ui(extruder_idx, this); + wxGetApp().plater()->on_config_change(cfg); + } + dialog->Destroy(); + }); + } +} + +PresetComboBox::~PresetComboBox() {} + + +void PresetComboBox::set_label_marker(int item) +{ + this->SetClientData(item, (void*)LABEL_ITEM_MARKER); +} + +// Frequently changed parameters + +class FreqChangedParams : public OG_Settings +{ + double m_brim_width = 0.0; + wxButton* m_wiping_dialog_button{ nullptr }; +public: + FreqChangedParams(wxWindow* parent, const int label_width); + ~FreqChangedParams() {} + + wxButton* get_wiping_dialog_button() { return m_wiping_dialog_button; } + void Show(const bool show); +}; + +FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : + OG_Settings(parent, false) +{ + DynamicPrintConfig* config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; + + m_og->set_config(config); + m_og->label_width = label_width; + + m_og->m_on_change = [config, this](t_config_option_key opt_key, boost::any value) { + TabPrint* tab_print = nullptr; + for (size_t i = 0; i < wxGetApp().tab_panel()->GetPageCount(); ++i) { + Tab *tab = dynamic_cast(wxGetApp().tab_panel()->GetPage(i)); + if (!tab) + continue; + if (tab->name() == "print") { + tab_print = static_cast(tab); + break; + } + } + if (tab_print == nullptr) + return; + + if (opt_key == "fill_density") { + value = m_og->get_config_value(*config, opt_key); + tab_print->set_value(opt_key, value); + tab_print->update(); + } + else{ + DynamicPrintConfig new_conf = *config; + if (opt_key == "brim") { + double new_val; + double brim_width = config->opt_float("brim_width"); + if (boost::any_cast(value) == true) + { + new_val = m_brim_width == 0.0 ? 10 : + m_brim_width < 0.0 ? m_brim_width * (-1) : + m_brim_width; + } + else{ + m_brim_width = brim_width * (-1); + new_val = 0; + } + new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val)); + } + else{ //(opt_key == "support") + const wxString& selection = boost::any_cast(value); + + auto support_material = selection == _("None") ? false : true; + new_conf.set_key_value("support_material", new ConfigOptionBool(support_material)); + + if (selection == _("Everywhere")) + new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(false)); + else if (selection == _("Support on build plate only")) + new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(true)); + } + tab_print->load_config(new_conf); + } + + tab_print->update_dirty(); + }; + + Option option = m_og->get_option("fill_density"); + option.opt.sidetext = ""; + option.opt.full_width = true; + m_og->append_single_option_line(option); + + ConfigOptionDef def; + + def.label = L("Support"); + def.type = coStrings; + def.gui_type = "select_open"; + def.tooltip = L("Select what kind of support do you need"); + def.enum_labels.push_back(L("None")); + def.enum_labels.push_back(L("Support on build plate only")); + def.enum_labels.push_back(L("Everywhere")); + std::string selection = !config->opt_bool("support_material") ? + "None" : + config->opt_bool("support_material_buildplate_only") ? + "Support on build plate only" : + "Everywhere"; + def.default_value = new ConfigOptionStrings{ selection }; + option = Option(def, "support"); + option.opt.full_width = true; + m_og->append_single_option_line(option); + + m_brim_width = config->opt_float("brim_width"); + def.label = L("Brim"); + def.type = coBool; + def.tooltip = L("This flag enables the brim that will be printed around each object on the first layer."); + def.gui_type = ""; + def.default_value = new ConfigOptionBool{ m_brim_width > 0.0 ? true : false }; + option = Option(def, "brim"); + m_og->append_single_option_line(option); + + + Line line = { "", "" }; + line.widget = [config, this](wxWindow* parent) { + m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(m_wiping_dialog_button); + m_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e) + { + auto &config = wxGetApp().preset_bundle->project_config; + const std::vector &init_matrix = (config.option("wiping_volumes_matrix"))->values; + const std::vector &init_extruders = (config.option("wiping_volumes_extruders"))->values; + + WipingDialog dlg(parent, cast(init_matrix), cast(init_extruders)); + + if (dlg.ShowModal() == wxID_OK) { + std::vector matrix = dlg.get_matrix(); + std::vector extruders = dlg.get_extruders(); + (config.option("wiping_volumes_matrix"))->values = std::vector(matrix.begin(), matrix.end()); + (config.option("wiping_volumes_extruders"))->values = std::vector(extruders.begin(), extruders.end()); + g_on_request_update_callback.call(); + } + })); + return sizer; + }; + m_og->append_line(line); +} + + +void FreqChangedParams::Show(const bool show) +{ + bool is_wdb_shown = m_wiping_dialog_button->IsShown(); + m_og->Show(show); + + // correct showing of the FreqChangedParams sizer when m_wiping_dialog_button is hidden + if (show && !is_wdb_shown) + m_wiping_dialog_button->Hide(); +} + +// Sidebar / private + +struct Sidebar::priv +{ + Plater *plater; + + wxScrolledWindow *scrolled; + + wxFlexGridSizer *sizer_presets; + PresetComboBox *combo_print; + std::vector combos_filament; + wxBoxSizer *sizer_filaments; + PresetComboBox *combo_sla_print; + PresetComboBox *combo_sla_material; + PresetComboBox *combo_printer; + + wxBoxSizer *sizer_params; + FreqChangedParams *frequently_changed_parameters; + ObjectList *object_list; + ObjectManipulation *object_manipulation; + ObjectSettings *object_settings; + ObjectInfo *object_info; + SlicedInfo *sliced_info; + + wxButton *btn_export_gcode; + wxButton *btn_reslice; + // wxButton *btn_print; // XXX: remove + wxButton *btn_send_gcode; + + priv(Plater *plater) : plater(plater) {} + + void show_preset_comboboxes(); +}; + +void Sidebar::priv::show_preset_comboboxes() +{ + const bool showSLA = plater->printer_technology() == ptSLA; + + wxWindowUpdateLocker noUpdates_scrolled(scrolled->GetParent()); + + for (size_t i = 0; i < 4; ++i) + sizer_presets->Show(i, !showSLA); + + for (size_t i = 4; i < 8; ++i) { + if (sizer_presets->IsShown(i) != showSLA) + sizer_presets->Show(i, showSLA); + } + + frequently_changed_parameters->Show(!showSLA); + + scrolled->GetParent()->Layout(); + scrolled->Refresh(); +} + + +// Sidebar / public + +Sidebar::Sidebar(Plater *parent) + : wxPanel(parent), p(new priv(parent)) +{ + p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(400, -1)); + p->scrolled->SetScrollbars(0, 1, 1, 1); + + // Sizer in the scrolled area + auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL); + p->scrolled->SetSizer(scrolled_sizer); + + // The preset chooser + p->sizer_presets = new wxFlexGridSizer(5, 2, 1, 2); + p->sizer_presets->AddGrowableCol(1, 1); + p->sizer_presets->SetFlexibleDirection(wxBOTH); + p->sizer_filaments = new wxBoxSizer(wxVERTICAL); + + auto init_combo = [this](PresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) { + auto *text = new wxStaticText(p->scrolled, wxID_ANY, label); + text->SetFont(wxGetApp().small_font()); + *combo = new PresetComboBox(p->scrolled, preset_type); + + auto *sizer_presets = this->p->sizer_presets; + auto *sizer_filaments = this->p->sizer_filaments; + sizer_presets->Add(text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4); + if (! filament) { + sizer_presets->Add(*combo, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM, 1); + } else { + sizer_filaments->Add(*combo, 1, wxEXPAND | wxBOTTOM, 1); + (*combo)->set_extruder_idx(0); + sizer_presets->Add(sizer_filaments, 1, wxEXPAND); + } + }; + + p->combos_filament.push_back(nullptr); + init_combo(&p->combo_print, _(L("Print settings")), Preset::TYPE_PRINT, false); + init_combo(&p->combos_filament[0], _(L("Filament")), Preset::TYPE_FILAMENT, true); + init_combo(&p->combo_sla_print, _(L("SLA print")), Preset::TYPE_SLA_PRINT, false); + init_combo(&p->combo_sla_material, _(L("SLA material")), Preset::TYPE_SLA_MATERIAL, false); + init_combo(&p->combo_printer, _(L("Printer")), Preset::TYPE_PRINTER, false); + + // calculate width of the preset labels + p->sizer_presets->Layout(); + const wxArrayInt& ar = p->sizer_presets->GetColWidths(); + int label_width = ar.IsEmpty() ? 100 : ar.front()-4; + + p->sizer_params = new wxBoxSizer(wxVERTICAL); + + // Frequently changed parameters + p->frequently_changed_parameters = new FreqChangedParams(p->scrolled, label_width); + p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxBOTTOM | wxLEFT, 2); + + // Object List + p->object_list = new ObjectList(p->scrolled); + p->sizer_params->Add(p->object_list->get_sizer(), 1, wxEXPAND | wxTOP, 20); + + // Object Manipulations + p->object_manipulation = new ObjectManipulation(p->scrolled); + p->object_manipulation->Hide(); + p->sizer_params->Add(p->object_manipulation->get_sizer(), 0, wxEXPAND | wxLEFT | wxTOP, 20); + + // Frequently Object Settings + p->object_settings = new ObjectSettings(p->scrolled); + p->object_settings->Hide(); + p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxLEFT | wxTOP, 20); + + // Buttons in the scrolled area + wxBitmap arrow_up(GUI::from_u8(Slic3r::var("brick_go.png")), wxBITMAP_TYPE_PNG); + p->btn_send_gcode = new wxButton(p->scrolled, wxID_ANY, _(L("Send to printer"))); + p->btn_send_gcode->SetBitmap(arrow_up); + p->btn_send_gcode->Hide(); + auto *btns_sizer_scrolled = new wxBoxSizer(wxHORIZONTAL); + btns_sizer_scrolled->Add(p->btn_send_gcode); + + // Info boxes + p->object_info = new ObjectInfo(p->scrolled); + p->sliced_info = new SlicedInfo(p->scrolled); + + // Sizer in the scrolled area + scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, 2); + scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND); + scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | wxLEFT, 20); + scrolled_sizer->Add(btns_sizer_scrolled, 0, wxEXPAND, 0); + scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | wxLEFT, 20); + + // Buttons underneath the scrolled area + p->btn_export_gcode = new wxButton(this, wxID_ANY, _(L("Export G-code…"))); + p->btn_export_gcode->SetFont(wxGetApp().bold_font()); + p->btn_reslice = new wxButton(this, wxID_ANY, _(L("Slice now"))); + p->btn_reslice->SetFont(wxGetApp().bold_font()); + + auto *btns_sizer = new wxBoxSizer(wxVERTICAL); + btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, 5); + btns_sizer->Add(p->btn_export_gcode, 0, wxEXPAND | wxTOP, 5); + + auto *sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(p->scrolled, 1, wxEXPAND | wxTOP, 5); + sizer->Add(btns_sizer, 0, wxEXPAND | wxLEFT, 20); + SetSizer(sizer); + + // Events + p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(); }); + p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->reslice(); }); + p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); +} + +Sidebar::~Sidebar() {} + +void Sidebar::init_filament_combo(PresetComboBox **combo, const int extr_idx) { + *combo = new PresetComboBox(p->scrolled, Slic3r::Preset::TYPE_FILAMENT); +// # copy icons from first choice +// $choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets; + + (*combo)->set_extruder_idx(extr_idx); + + auto /***/sizer_filaments = this->p->sizer_filaments; + sizer_filaments->Add(*combo, 1, wxEXPAND | wxBOTTOM, 1); +} + +void Sidebar::remove_unused_filament_combos(const int current_extruder_count) +{ + if (current_extruder_count >= p->combos_filament.size()) + return; + auto sizer_filaments = this->p->sizer_filaments; + while (p->combos_filament.size() > current_extruder_count) { + const int last = p->combos_filament.size() - 1; + sizer_filaments->Remove(last); + (*p->combos_filament[last]).Destroy(); + p->combos_filament.pop_back(); + } +} + +void Sidebar::update_presets(Preset::Type preset_type) +{ + PresetBundle &preset_bundle = *wxGetApp().preset_bundle; + + switch (preset_type) { + case Preset::TYPE_FILAMENT: + if (p->combos_filament.size() == 1) { + // Single filament printer, synchronize the filament presets. + const std::string &name = preset_bundle.filaments.get_selected_preset().name; + preset_bundle.set_filament_preset(0, name); + } + + for (size_t i = 0; i < p->combos_filament.size(); i++) { + preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]); + } + + break; + + case Preset::TYPE_PRINT: + preset_bundle.prints.update_platter_ui(p->combo_print); + break; + + case Preset::TYPE_SLA_PRINT: + preset_bundle.sla_prints.update_platter_ui(p->combo_sla_print); + break; + + case Preset::TYPE_SLA_MATERIAL: + preset_bundle.sla_materials.update_platter_ui(p->combo_sla_material); + break; + + case Preset::TYPE_PRINTER: + { + // Update the print choosers to only contain the compatible presets, update the dirty flags. + if (p->plater->printer_technology() == ptFFF) + preset_bundle.prints.update_platter_ui(p->combo_print); + else { + preset_bundle.sla_prints.update_platter_ui(p->combo_sla_print); + preset_bundle.sla_materials.update_platter_ui(p->combo_sla_material); + } + // Update the printer choosers, update the dirty flags. + preset_bundle.printers.update_platter_ui(p->combo_printer); + // Update the filament choosers to only contain the compatible presets, update the color preview, + // update the dirty flags. + if (p->plater->printer_technology() == ptFFF) { + for (size_t i = 0; i < p->combos_filament.size(); ++ i) + preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]); + } + p->show_preset_comboboxes(); + break; + } + + default: break; + } + + // Synchronize config.ini with the current selections. + wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); +} + +ObjectManipulation* Sidebar::obj_manipul() +{ + return p->object_manipulation; +} + +ObjectList* Sidebar::obj_list() +{ + return p->object_list; +} + +ObjectSettings* Sidebar::obj_settings() +{ + return p->object_settings; +} + +wxScrolledWindow* Sidebar::scrolled_panel() +{ + return p->scrolled; +} + +ConfigOptionsGroup* Sidebar::og_freq_chng_params() +{ + return p->frequently_changed_parameters->get_og(); +} + +wxButton* Sidebar::get_wiping_dialog_button() +{ + return p->frequently_changed_parameters->get_wiping_dialog_button(); +} + +void Sidebar::update_objects_list_extruder_column(int extruders_count) +{ + p->object_list->update_objects_list_extruder_column(extruders_count); +} + +void Sidebar::show_info_sizer() +{ + if (!p->plater->is_single_full_object_selection() || + m_mode < ConfigMenuModeExpert || + p->plater->model().objects.empty()) { + p->object_info->Show(false); + return; + } + + int obj_idx = p->plater->get_selected_object_idx(); + + const ModelObject* model_object = p->plater->model().objects[obj_idx]; + // hack to avoid crash when deleting the last object on the bed + if (model_object->volumes.empty()) + { + p->object_info->Show(false); + return; + } + + const ModelInstance* model_instance = !model_object->instances.empty() ? model_object->instances.front() : nullptr; + + auto size = model_object->bounding_box().size(); + p->object_info->info_size->SetLabel(wxString::Format("%.2f x %.2f x %.2f",size(0), size(1), size(2))); + p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast(model_object->materials_count()))); + + auto& stats = model_object->volumes.front()->mesh.stl.stats; + auto sf = model_instance->get_scaling_factor(); + p->object_info->info_volume->SetLabel(wxString::Format("%.2f", stats.volume * sf(0) * sf(1) * sf(2))); + p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast(model_object->facets_count()), stats.number_of_parts)); + + int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + + stats.facets_added + stats.facets_reversed + stats.backwards_edges; + if (errors > 0) { + wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors)")), errors); + p->object_info->info_manifold->SetLabel(tooltip); + + tooltip += wxString::Format(_(L(":\n%d degenerate facets, %d edges fixed, %d facets removed, " + "%d facets added, %d facets reversed, %d backwards edges")), + stats.degenerate_facets, stats.edges_fixed, stats.facets_removed, + stats.facets_added, stats.facets_reversed, stats.backwards_edges); + + p->object_info->showing_manifold_warning_icon = true; + p->object_info->info_manifold->SetToolTip(tooltip); + p->object_info->manifold_warning_icon->SetToolTip(tooltip); + } + else { + p->object_info->info_manifold->SetLabel(L("Yes")); + p->object_info->showing_manifold_warning_icon = false; + p->object_info->info_manifold->SetToolTip(""); + p->object_info->manifold_warning_icon->SetToolTip(""); + } + + p->object_info->show_sizer(true); +} + +void Sidebar::show_sliced_info_sizer(const bool show) +{ + wxWindowUpdateLocker freeze_guard(this); + + p->sliced_info->Show(show); + if (show) { + const PrintStatistics& ps = p->plater->print().print_statistics(); + const bool is_wipe_tower = ps.total_wipe_tower_filament > 0; + + wxString new_label = _(L("Used Filament (m)")); + if (is_wipe_tower) + new_label += wxString::Format(" :\n - %s\n - %s", _(L("objects")), _(L("wipe tower"))); + + wxString info_text = is_wipe_tower ? + wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / 1000, + (ps.total_used_filament - ps.total_wipe_tower_filament) / 1000, + ps.total_wipe_tower_filament / 1000) : + wxString::Format("%.2f", ps.total_used_filament / 1000); + p->sliced_info->SetTextAndShow(siFilament_m, info_text, new_label); + + p->sliced_info->SetTextAndShow(siFilament_mm3, wxString::Format("%.2f", ps.total_extruded_volume)); + p->sliced_info->SetTextAndShow(siFilament_g, wxString::Format("%.2f", ps.total_weight)); + + + new_label = _(L("Cost")); + if (is_wipe_tower) + new_label += wxString::Format(" :\n - %s\n - %s", _(L("objects")), _(L("wipe tower"))); + + info_text = is_wipe_tower ? + wxString::Format("%.2f \n%.2f \n%.2f", ps.total_cost, + (ps.total_cost - ps.total_wipe_tower_cost), + ps.total_wipe_tower_cost) : + wxString::Format("%.2f", ps.total_cost); + p->sliced_info->SetTextAndShow(siCost, info_text, new_label); + + if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") + p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A"); + else { + new_label = "Estimated printing time :"; + info_text = ""; + if (ps.estimated_normal_print_time != "N/A") { + new_label += wxString::Format("\n - %s", _(L("normal mode"))); + info_text += wxString::Format("\n%s", ps.estimated_normal_print_time); + } + if (ps.estimated_silent_print_time != "N/A") { + new_label += wxString::Format("\n - %s", _(L("silent mode"))); + info_text += wxString::Format("\n%s", ps.estimated_silent_print_time); + } + p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); + } + + // if there is a wipe tower, insert number of toolchanges info into the array: + p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", p->plater->print().wipe_tower_data().number_of_toolchanges) : "N/A"); + } + + Layout(); + p->scrolled->Refresh(); +} + +void Sidebar::show_buttons(const bool show) +{ + p->btn_reslice->Show(show); + for (size_t i = 0; i < wxGetApp().tab_panel()->GetPageCount(); ++i) { + TabPrinter *tab = dynamic_cast(wxGetApp().tab_panel()->GetPage(i)); + if (!tab) + continue; + if (p->plater->printer_technology() == ptFFF) { + p->btn_send_gcode->Show(show && !tab->m_config->opt_string("print_host").empty()); + } + break; + } +} + +void Sidebar::enable_buttons(bool enable) +{ + p->btn_reslice->Enable(enable); + p->btn_export_gcode->Enable(enable); + p->btn_send_gcode->Enable(enable); +} + +void Sidebar::show_button(ButtonAction but_action, bool show) +{ + switch (but_action) + { + case baReslice: + p->btn_reslice->Show(show); + break; + case baExportGcode: + p->btn_export_gcode->Show(show); + break; + case baSendGcode: + p->btn_send_gcode->Show(show); + break; + default: + break; + } +} + +bool Sidebar::is_multifilament() +{ + return p->combos_filament.size() > 1; +} + + +std::vector& Sidebar::combos_filament() +{ + return p->combos_filament; +} + +// Plater::DropTarget + +class PlaterDropTarget : public wxFileDropTarget +{ +public: + PlaterDropTarget(Plater *plater) : plater(plater) {} + + virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames); + +private: + Plater *plater; + + static const std::regex pattern_drop; +}; + +const std::regex PlaterDropTarget::pattern_drop(".*[.](stl|obj|amf|3mf|prusa)", std::regex::icase); + +bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames) +{ + std::vector paths; + + for (const auto &filename : filenames) { + fs::path path(filename); + + if (std::regex_match(path.string(), pattern_drop)) { + paths.push_back(std::move(path)); + } else { + return false; + } + } + + plater->load_files(paths); + return true; +} + +// Plater / private +struct Plater::priv +{ + // PIMPL back pointer ("Q-Pointer") + Plater *q; + MainFrame *main_frame; + + // Object popup menu + wxMenu object_menu; + + // Data + Slic3r::DynamicPrintConfig *config; + Slic3r::Print print; + Slic3r::SLAPrint sla_print; + Slic3r::Model model; + PrinterTechnology printer_technology = ptFFF; + Slic3r::GCodePreviewData gcode_preview_data; + + // GUI elements + wxNotebook *notebook; + Sidebar *sidebar; + wxPanel *panel3d; + wxGLCanvas *canvas3D; // TODO: Use GLCanvas3D when we can + Preview *preview; + +#if ENABLE_NEW_MENU_LAYOUT + wxString project_filename; +#endif // ENABLE_NEW_MENU_LAYOUT + + BackgroundSlicingProcess background_process; + std::atomic arranging; + + wxTimer background_process_timer; + + static const std::regex pattern_bundle; + static const std::regex pattern_3mf; + static const std::regex pattern_zip_amf; + + priv(Plater *q, MainFrame *main_frame); + + void update(bool force_autocenter = false); + void select_view(const std::string& direction); + void update_ui_from_settings(); + ProgressStatusBar* statusbar(); + std::string get_config(const std::string &key) const; + BoundingBoxf bed_shape_bb() const; + BoundingBox scaled_bed_shape_bb() const; +#if ENABLE_NEW_MENU_LAYOUT + std::vector load_files(const std::vector& input_files, bool load_model, bool load_config); +#else + std::vector load_files(const std::vector &input_files); +#endif // ENABLE_NEW_MENU_LAYOUT + std::vector load_model_objects(const ModelObjectPtrs &model_objects); + std::unique_ptr get_export_file(GUI::FileType file_type); + + const GLCanvas3D::Selection& get_selection() const; + GLCanvas3D::Selection& get_selection(); + int get_selected_object_idx() const; + void selection_changed(); + void object_list_changed(); + + void remove(size_t obj_idx); + void delete_object_from_model(size_t obj_idx); + void reset(); + void mirror(Axis axis); + void arrange(); + void split_object(); + void split_volume(); + void schedule_background_process(); + // Update background processing thread from the current config and Model. + enum UpdateBackgroundProcessReturnState { + // update_background_process() reports, that the Print / SLAPrint was updated in a way, + // that the background process was invalidated and it needs to be re-run. + UPDATE_BACKGROUND_PROCESS_RESTART = 1, + // update_background_process() reports, that the Print / SLAPrint was updated in a way, + // that a scene needs to be refreshed (you should call _3DScene::reload_scene(canvas3D, false)) + UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE = 2, + // update_background_process() reports, that the Print / SLAPrint is invalid, and the error message + // was sent to the status line. + UPDATE_BACKGROUND_PROCESS_INVALID = 4, + }; + // returns bit mask of UpdateBackgroundProcessReturnState + unsigned int update_background_process(); + void async_apply_config(); + void reload_from_disk(); + void export_object_stl(); + void fix_through_netfabb(const int obj_idx); + + void on_notebook_changed(wxBookCtrlEvent&); + void on_select_preset(wxCommandEvent&); + void on_progress_event(wxCommandEvent&); + void on_update_print_preview(wxCommandEvent&); + void on_process_completed(wxCommandEvent&); + void on_layer_editing_toggled(bool enable); + + void on_schedule_background_process(SimpleEvent&); + + void on_action_add(SimpleEvent&); + void on_action_split_objects(SimpleEvent&); + void on_action_split_volumes(SimpleEvent&); + void on_action_layersediting(SimpleEvent&); + + void on_object_select(SimpleEvent&); + void on_viewport_changed(SimpleEvent&); + void on_right_click(Vec2dEvent&); + void on_model_update(SimpleEvent&); + void on_wipetower_moved(Vec3dEvent&); + void on_enable_action_buttons(Event&); + void on_update_geometry(Vec3dsEvent<2>&); + +private: + bool init_object_menu(); + + bool can_delete_object() const; + bool can_increase_instances() const; + bool can_decrease_instances() const; + bool can_split_to_objects() const; + bool can_split_to_volumes() const; + bool layers_height_allowed() const; + bool can_delete_all() const; + bool can_arrange() const; + bool can_mirror() const; +}; + +const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase); +const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase); +const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::icase); +Plater::priv::priv(Plater *q, MainFrame *main_frame) + : q(q) + , main_frame(main_frame) + , config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({ + "bed_shape", "complete_objects", "extruder_clearance_radius", "skirts", "skirt_distance", + "brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host", + "printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material", + "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", + "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology" + })) + , notebook(new wxNotebook(q, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM)) + , sidebar(new Sidebar(q)) + , panel3d(new wxPanel(notebook, wxID_ANY)) + , canvas3D(GLCanvas3DManager::create_wxglcanvas(panel3d)) +#if ENABLE_NEW_MENU_LAYOUT + , project_filename(wxEmptyString) +#endif // ENABLE_NEW_MENU_LAYOUT +{ + arranging.store(false); + background_process.set_fff_print(&print); + background_process.set_sla_print(&sla_print); + background_process.set_gcode_preview_data(&gcode_preview_data); + background_process.set_sliced_event(EVT_SLICING_COMPLETED); + background_process.set_finished_event(EVT_PROCESS_COMPLETED); + // Default printer technology for default config. + background_process.select_technology(this->printer_technology); + // Register progress callback from the Print class to the Platter. + + auto statuscb = [this](int percent, const std::string &message) { + wxCommandEvent event(EVT_PROGRESS_BAR); + event.SetInt(percent); + event.SetString(message); + wxQueueEvent(this->q, event.Clone()); + }; + print.set_status_callback(statuscb); + sla_print.set_status_callback(statuscb); + this->q->Bind(EVT_PROGRESS_BAR, &priv::on_progress_event, this); + + _3DScene::add_canvas(canvas3D); + _3DScene::allow_multisample(canvas3D, GLCanvas3DManager::can_multisample()); + + auto *panel3dsizer = new wxBoxSizer(wxVERTICAL); + panel3dsizer->Add(canvas3D, 1, wxEXPAND); + auto *panel_gizmo_widgets = new wxPanel(panel3d, wxID_ANY); + panel_gizmo_widgets->SetSizer(new wxBoxSizer(wxVERTICAL)); + panel3dsizer->Add(panel_gizmo_widgets, 0, wxEXPAND); + + panel3d->SetSizer(panel3dsizer); + notebook->AddPage(panel3d, _(L("3D"))); + preview = new GUI::Preview(notebook, config, &print, &gcode_preview_data, [this](){ schedule_background_process(); }); + + _3DScene::get_canvas(canvas3D)->set_external_gizmo_widgets_parent(panel_gizmo_widgets); + + // XXX: If have OpenGL + _3DScene::enable_picking(canvas3D, true); + _3DScene::enable_moving(canvas3D, true); + // XXX: more config from 3D.pm + _3DScene::set_model(canvas3D, &model); + _3DScene::set_print(canvas3D, &print); + _3DScene::set_SLA_print(canvas3D, &sla_print); + _3DScene::set_config(canvas3D, config); + _3DScene::enable_gizmos(canvas3D, true); + _3DScene::enable_toolbar(canvas3D, true); + _3DScene::enable_shader(canvas3D, true); + _3DScene::enable_force_zoom_to_bed(canvas3D, true); + + this->background_process_timer.SetOwner(this->q, 0); + this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->async_apply_config(); }); + + auto *bed_shape = config->opt("bed_shape"); + _3DScene::set_bed_shape(canvas3D, bed_shape->values); + _3DScene::zoom_to_bed(canvas3D); + preview->set_bed_shape(bed_shape->values); + + update(); + + auto *hsizer = new wxBoxSizer(wxHORIZONTAL); + hsizer->Add(notebook, 1, wxEXPAND | wxTOP, 1); + hsizer->Add(sidebar, 0, wxEXPAND | wxLEFT | wxRIGHT, 0); + q->SetSizer(hsizer); + + init_object_menu(); + + // Events: + + // Notebook page change event + notebook->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &priv::on_notebook_changed, this); + + // Preset change event + sidebar->Bind(wxEVT_COMBOBOX, &priv::on_select_preset, this); + + sidebar->Bind(EVT_OBJ_LIST_OBJECT_SELECT, [this](wxEvent&) { priv::selection_changed(); }); + + // 3DScene events: + canvas3D->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, &priv::on_schedule_background_process, this); + canvas3D->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this); + canvas3D->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); + canvas3D->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); + canvas3D->Bind(EVT_GLCANVAS_MODEL_UPDATE, &priv::on_model_update, this); + canvas3D->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); + canvas3D->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); + canvas3D->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); }); + canvas3D->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); + canvas3D->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); + canvas3D->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, &priv::on_enable_action_buttons, this); + canvas3D->Bind(EVT_GLCANVAS_UPDATE_GEOMETRY, &priv::on_update_geometry, this); + // 3DScene/Toolbar: + canvas3D->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); + canvas3D->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); } ); + canvas3D->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { reset(); }); + canvas3D->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); }); + canvas3D->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); }); + canvas3D->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); }); + canvas3D->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this); + canvas3D->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this); + canvas3D->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); + + // Preview events: + preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); + + q->Bind(EVT_SLICING_COMPLETED, &priv::on_update_print_preview, this); + q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this); + + // Drop target: + q->SetDropTarget(new PlaterDropTarget(q)); // if my understanding is right, wxWindow takes the owenership + + update_ui_from_settings(); + q->Layout(); +} + +void Plater::priv::update(bool force_autocenter) +{ + wxWindowUpdateLocker freeze_guard(q); + if (get_config("autocenter") == "1" || force_autocenter) { + // auto *bed_shape_opt = config->opt("bed_shape"); + // const auto bed_shape = Slic3r::Polygon::new_scale(bed_shape_opt->values); + // const BoundingBox bed_shape_bb = bed_shape.bounding_box(); + const Vec2d& bed_center = bed_shape_bb().center(); + model.center_instances_around_point(bed_center); + } + + if (this->printer_technology == ptSLA) { + // Update the SLAPrint from the current Model, so that the reload_scene() + // pulls the correct data. + this->update_background_process(); + } + _3DScene::reload_scene(canvas3D, false); + preview->reset_gcode_preview_data(); + preview->reload_print(); + + this->schedule_background_process(); +} + +void Plater::priv::select_view(const std::string& direction) +{ + int page_id = notebook->GetSelection(); + if (page_id != wxNOT_FOUND) + { + const wxString& page_text = notebook->GetPageText(page_id); + if (page_text == _(L("3D"))) + _3DScene::select_view(canvas3D, direction); + else if (page_text == _(L("Preview"))) + preview->select_view(direction); + } +} + +void Plater::priv::update_ui_from_settings() +{ + // TODO: (?) + // my ($self) = @_; + // if (defined($self->{btn_reslice}) && $self->{buttons_sizer}->IsShown($self->{btn_reslice}) != (! wxTheApp->{app_config}->get("background_processing"))) { + // $self->{buttons_sizer}->Show($self->{btn_reslice}, ! wxTheApp->{app_config}->get("background_processing")); + // $self->{buttons_sizer}->Layout; + // } +} + +ProgressStatusBar* Plater::priv::statusbar() +{ + return main_frame->m_statusbar; +} + +std::string Plater::priv::get_config(const std::string &key) const +{ + return wxGetApp().app_config->get(key); +} + +BoundingBoxf Plater::priv::bed_shape_bb() const +{ + BoundingBox bb = scaled_bed_shape_bb(); + return BoundingBoxf(unscale(bb.min), unscale(bb.max)); +} + +BoundingBox Plater::priv::scaled_bed_shape_bb() const +{ + const auto *bed_shape_opt = config->opt("bed_shape"); + const auto bed_shape = Slic3r::Polygon::new_scale(bed_shape_opt->values); + return bed_shape.bounding_box(); +} + +#if ENABLE_NEW_MENU_LAYOUT +std::vector Plater::priv::load_files(const std::vector& input_files, bool load_model, bool load_config) +#else +std::vector Plater::priv::load_files(const std::vector &input_files) +#endif // ENABLE_NEW_MENU_LAYOUT +{ + if (input_files.empty()) { return std::vector(); } + + auto *nozzle_dmrs = config->opt("nozzle_diameter"); + + bool one_by_one = input_files.size() == 1 || nozzle_dmrs->values.size() <= 1; + if (! one_by_one) { + for (const auto &path : input_files) { + if (std::regex_match(path.string(), pattern_bundle)) { + one_by_one = true; + break; + } + } + } + + const auto loading = _(L("Loading…")); + wxProgressDialog dlg(loading, loading); + dlg.Pulse(); + +#if ENABLE_NEW_MENU_LAYOUT + auto *new_model = (!load_model || one_by_one) ? nullptr : new Slic3r::Model(); +#else + auto *new_model = one_by_one ? nullptr : new Slic3r::Model(); +#endif // ENABLE_NEW_MENU_LAYOUT + std::vector obj_idxs; + + for (size_t i = 0; i < input_files.size(); i++) { + const auto &path = input_files[i]; + const auto filename = path.filename(); + const auto dlg_info = wxString::Format(_(L("Processing input file %s\n")), filename.string()); + dlg.Update(100 * i / input_files.size(), dlg_info); + + const bool type_3mf = std::regex_match(path.string(), pattern_3mf); + const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf); + + Slic3r::Model model; + try { + if (type_3mf || type_zip_amf) { + DynamicPrintConfig config; + { + DynamicPrintConfig config_loaded; + model = Slic3r::Model::read_from_archive(path.string(), &config_loaded, false); +#if ENABLE_NEW_MENU_LAYOUT + if (load_config && !config_loaded.empty()) { +#else + if (!config_loaded.empty()) { +#endif // ENABLE_NEW_MENU_LAYOUT + // Based on the printer technology field found in the loaded config, select the base for the config, + PrinterTechnology printer_technology = Preset::printer_technology(config_loaded); + config.apply(printer_technology == ptFFF ? + static_cast(FullPrintConfig::defaults()) : + static_cast(SLAFullPrintConfig::defaults())); + // and place the loaded config over the base. + config += std::move(config_loaded); + } + } +#if ENABLE_NEW_MENU_LAYOUT + if (load_config) + { +#endif // ENABLE_NEW_MENU_LAYOUT + if (!config.empty()) { + Preset::normalize(config); + wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config)); + wxGetApp().load_current_presets(); + } + wxGetApp().app_config->update_config_dir(path.parent_path().string()); +#if ENABLE_NEW_MENU_LAYOUT + } +#endif // ENABLE_NEW_MENU_LAYOUT + } + else { + model = Slic3r::Model::read_from_file(path.string(), nullptr, false); + for (auto obj : model.objects) + if (obj->name.empty()) + obj->name = fs::path(obj->input_file).filename().string(); + } + } + catch (const std::runtime_error &e) { + GUI::show_error(q, e.what()); + continue; + } + +#if ENABLE_NEW_MENU_LAYOUT + if (load_model) + { +#endif // ENABLE_NEW_MENU_LAYOUT + // The model should now be initialized + + if (model.looks_like_multipart_object()) { + wxMessageDialog dlg(q, _(L( + "This file contains several objects positioned at multiple heights. " + "Instead of considering them as multiple objects, should I consider\n" + "this file as a single object having multiple parts?\n" + )), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); + if (dlg.ShowModal() == wxID_YES) { + model.convert_multipart_object(nozzle_dmrs->values.size()); + } + } + + if (type_3mf) { + for (ModelObject* model_object : model.objects) { + model_object->center_around_origin(); + model_object->ensure_on_bed(); + } + } + + if (one_by_one) { + auto loaded_idxs = load_model_objects(model.objects); + obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); + } else { + // This must be an .stl or .obj file, which may contain a maximum of one volume. + for (const ModelObject* model_object : model.objects) { + new_model->add_object(*model_object); + } + } +#if ENABLE_NEW_MENU_LAYOUT + } +#endif // ENABLE_NEW_MENU_LAYOUT + } + + if (new_model != nullptr) { + wxMessageDialog dlg(q, _(L( + "Multiple objects were loaded for a multi-material printer.\n" + "Instead of considering them as multiple objects, should I consider\n" + "these files to represent a single object having multiple parts?\n" + )), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); + if (dlg.ShowModal() == wxID_YES) { + new_model->convert_multipart_object(nozzle_dmrs->values.size()); + } + + auto loaded_idxs = load_model_objects(new_model->objects); + obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); + } + +#if ENABLE_NEW_MENU_LAYOUT + if (load_model) + { +#endif // ENABLE_NEW_MENU_LAYOUT + wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().string()); + // XXX: Plater.pm had @loaded_files, but didn't seem to fill them with the filenames... + statusbar()->set_status_text(_(L("Loaded"))); +#if ENABLE_NEW_MENU_LAYOUT + } +#endif // ENABLE_NEW_MENU_LAYOUT + return obj_idxs; +} + +std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) +{ + const BoundingBoxf bed_shape = bed_shape_bb(); +#if !ENABLE_MODELVOLUME_TRANSFORM + const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast(), 0.0); +#endif // !ENABLE_MODELVOLUME_TRANSFORM + const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0); + + bool need_arrange = false; + bool scaled_down = false; + std::vector obj_idxs; + unsigned int obj_count = model.objects.size(); + + for (ModelObject *model_object : model_objects) { + auto *object = model.add_object(*model_object); + std::string object_name = object->name.empty() ? fs::path(object->input_file).filename().string() : object->name; + obj_idxs.push_back(obj_count++); + + if (model_object->instances.empty()) { + // if object has no defined position(s) we need to rearrange everything after loading + need_arrange = true; + + // add a default instance and center object around origin + object->center_around_origin(); // also aligns object to Z = 0 + ModelInstance* instance = object->add_instance(); +#if ENABLE_MODELVOLUME_TRANSFORM + instance->set_offset(Slic3r::to_3d(bed_shape.center().cast(), -object->origin_translation(2))); +#else + instance->set_offset(bed_center); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + + const Vec3d size = object->bounding_box().size(); + const Vec3d ratio = size.cwiseQuotient(bed_size); + const double max_ratio = std::max(ratio(0), ratio(1)); + if (max_ratio > 10000) { + // the size of the object is too big -> this could lead to overflow when moving to clipper coordinates, + // so scale down the mesh + // const Vec3d inverse = ratio.cwiseInverse(); + // object->scale(inverse); + object->scale(ratio.cwiseInverse()); + scaled_down = true; + } else if (max_ratio > 5) { + const Vec3d inverse = ratio.cwiseInverse(); + for (ModelInstance *instance : model_object->instances) { + instance->set_scaling_factor(inverse); + } + } + + object->ensure_on_bed(); + + // print.auto_assign_extruders(object); + // print.add_model_object(object); + } + + if (scaled_down) { + GUI::show_info(q, + _(L("Your object appears to be too large, so it was automatically scaled down to fit your print bed.")), + _(L("Object too large?"))); + } + + for (const size_t idx : obj_idxs) { + wxGetApp().obj_list()->add_object_to_list(idx); + } + + update(); +#if !ENABLE_MODIFIED_CAMERA_TARGET + _3DScene::zoom_to_volumes(canvas3D); +#endif // !ENABLE_MODIFIED_CAMERA_TARGET + object_list_changed(); + + this->schedule_background_process(); + + return obj_idxs; +} + +std::unique_ptr Plater::priv::get_export_file(GUI::FileType file_type) +{ + wxString wildcard; + switch (file_type) { + case FT_STL: + case FT_AMF: + case FT_3MF: + case FT_GCODE: + wildcard = file_wildcards[file_type]; + break; +// FT_GCODE + default: + wildcard = file_wildcards[FT_MODEL]; + break; + } + + fs::path output_file(print.output_filepath(std::string())); + + switch (file_type) { + case FT_STL: output_file.replace_extension("stl"); break; + case FT_AMF: output_file.replace_extension("zip.amf"); break; // XXX: Problem on OS X with double extension? + case FT_3MF: output_file.replace_extension("3mf"); break; + default: break; + } + + wxGetApp().preset_bundle->export_selections(print.placeholder_parser()); + + auto dlg = Slic3r::make_unique(q, + ((file_type == FT_AMF) || (file_type == FT_3MF)) ? _(L("Export print config")) : "", + true, + _(L("Save file as:")), + output_file.parent_path().string(), + output_file.filename().string(), + wildcard, + wxFD_SAVE | wxFD_OVERWRITE_PROMPT + ); + + if (dlg->ShowModal() != wxID_OK) { + return nullptr; + } + + fs::path path(dlg->GetPath()); + wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); + + return dlg; +} + +const GLCanvas3D::Selection& Plater::priv::get_selection() const +{ + return _3DScene::get_canvas(canvas3D)->get_selection(); +} + +GLCanvas3D::Selection& Plater::priv::get_selection() +{ + return _3DScene::get_canvas(canvas3D)->get_selection(); +} + +int Plater::priv::get_selected_object_idx() const +{ + int idx = get_selection().get_object_idx(); + return ((0 <= idx) && (idx < 1000)) ? idx : -1; +} + +void Plater::priv::selection_changed() +{ + _3DScene::enable_toolbar_item(canvas3D, "delete", can_delete_object()); + _3DScene::enable_toolbar_item(canvas3D, "more", can_increase_instances()); + _3DScene::enable_toolbar_item(canvas3D, "fewer", can_decrease_instances()); + _3DScene::enable_toolbar_item(canvas3D, "splitobjects", can_split_to_objects()); + _3DScene::enable_toolbar_item(canvas3D, "splitvolumes", can_split_to_volumes()); + _3DScene::enable_toolbar_item(canvas3D, "layersediting", layers_height_allowed()); + // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) + _3DScene::render(canvas3D); +} + +void Plater::priv::object_list_changed() +{ + // Enable/disable buttons depending on whether there are any objects on the platter. + _3DScene::enable_toolbar_item(canvas3D, "deleteall", can_delete_all()); + _3DScene::enable_toolbar_item(canvas3D, "arrange", can_arrange()); + + const bool export_in_progress = this->background_process.is_export_scheduled(); // || ! send_gcode_file.empty()); + // XXX: is this right? + const bool model_fits = _3DScene::check_volumes_outside_state(canvas3D, config) == ModelInstance::PVS_Inside; + + sidebar->enable_buttons(!model.objects.empty() && !export_in_progress && model_fits); +} + +void Plater::priv::remove(size_t obj_idx) +{ + // Prevent toolpaths preview from rendering while we modify the Print object + preview->set_enabled(false); + + if (_3DScene::is_layers_editing_enabled(canvas3D)) + _3DScene::enable_layers_editing(canvas3D, false); + + model.delete_object(obj_idx); +// print.delete_object(obj_idx); + // Delete object from Sidebar list + sidebar->obj_list()->delete_object_from_list(obj_idx); + + object_list_changed(); + update(); +} + + +void Plater::priv::delete_object_from_model(size_t obj_idx) +{ + model.delete_object(obj_idx); + object_list_changed(); + update(); +} + +void Plater::priv::reset() +{ +#if ENABLE_NEW_MENU_LAYOUT + project_filename.Clear(); +#endif // ENABLE_NEW_MENU_LAYOUT + + // Prevent toolpaths preview from rendering while we modify the Print object + preview->set_enabled(false); + + if (_3DScene::is_layers_editing_enabled(canvas3D)) + _3DScene::enable_layers_editing(canvas3D, false); + + // Stop and reset the Print content. + this->background_process.reset(); + model.clear_objects(); + + // Delete all objects from list on c++ side + sidebar->obj_list()->delete_all_objects_from_list(); + object_list_changed(); + update(); +} + +void Plater::priv::mirror(Axis axis) +{ + _3DScene::mirror_selection(canvas3D, axis); +} + +void Plater::priv::arrange() +{ + // don't do anything if currently arranging. Then this is a re-entrance + if(arranging.load()) return; + + // Guard the arrange process + arranging.store(true); + + _3DScene::enable_toolbar_item(canvas3D, "arrange", can_arrange()); + + this->background_process.stop(); + unsigned count = 0; + for(auto obj : model.objects) count += obj->instances.size(); + + auto prev_range = statusbar()->get_range(); + statusbar()->set_range(count); + + auto statusfn = [this, count] (unsigned st, const std::string& msg) { + /* // In case we would run the arrange asynchronously + wxCommandEvent event(EVT_PROGRESS_BAR); + event.SetInt(st); + event.SetString(msg); + wxQueueEvent(this->q, event.Clone()); */ + statusbar()->set_progress(count - st); + statusbar()->set_status_text(msg); + + // ok, this is dangerous, but we are protected by the atomic flag + // 'arranging'. This call is needed for the cancel button to work. + wxYieldIfNeeded(); + }; + + statusbar()->set_cancel_callback([this, statusfn](){ + arranging.store(false); + statusfn(0, L("Arranging canceled")); + }); + + static const std::string arrangestr = L("Arranging"); + + // FIXME: I don't know how to obtain the minimum distance, it depends + // on printer technology. I guess the following should work but it crashes. + double dist = 6; //PrintConfig::min_object_distance(config); + + auto min_obj_distance = static_cast(dist/SCALING_FACTOR); + + const auto *bed_shape_opt = config->opt("bed_shape"); + + assert(bed_shape_opt); + auto& bedpoints = bed_shape_opt->values; + Polyline bed; bed.points.reserve(bedpoints.size()); + for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); + + statusfn(0, arrangestr); + + try { + arr::BedShapeHint hint; + + // TODO: from Sasha from GUI or + hint.type = arr::BedShapeType::WHO_KNOWS; + + arr::arrange(model, + min_obj_distance, + bed, + hint, + false, // create many piles not just one pile + [statusfn](unsigned st) { statusfn(st, arrangestr); }, + [this] () { return !arranging.load(); }); + } catch(std::exception& /*e*/) { + GUI::show_error(this->q, L("Could not arrange model objects! " + "Some geometries may be invalid.")); + } + + statusfn(0, L("Arranging done.")); + statusbar()->set_range(prev_range); + statusbar()->set_cancel_callback(); // remove cancel button + arranging.store(false); + + this->schedule_background_process(); + + // ignore arrange failures on purpose: user has visual feedback and we + // don't need to warn him when parts don't fit in print bed + + _3DScene::enable_toolbar_item(canvas3D, "arrange", can_arrange()); + update(); +} + +void Plater::priv::split_object() +{ + int obj_idx = get_selected_object_idx(); + if (obj_idx == -1) + return; + + // we clone model object because split_object() adds the split volumes + // into the same model object, thus causing duplicates when we call load_model_objects() + Model new_model = model; + ModelObject* current_model_object = new_model.objects[obj_idx]; + + if (current_model_object->volumes.size() > 1) + { + Slic3r::GUI::warning_catcher(q, _(L("The selected object can't be split because it contains more than one volume/material."))); + return; + } + + ModelObjectPtrs new_objects; + current_model_object->split(&new_objects); + if (new_objects.size() == 1) + Slic3r::GUI::warning_catcher(q, _(L("The selected object couldn't be split because it contains only one part."))); + else + { + unsigned int counter = 1; + for (ModelObject* m : new_objects) + m->name = current_model_object->name + "_" + std::to_string(counter++); + + remove(obj_idx); + + // load all model objects at once, otherwise the plate would be rearranged after each one + // causing original positions not to be kept + std::vector idxs = load_model_objects(new_objects); + + // select newly added objects + for (size_t idx : idxs) + { + get_selection().add_object((unsigned int)idx, false); + } + } +} + +void Plater::priv::split_volume() +{ + wxGetApp().obj_list()->split(false); +} + +void Plater::priv::schedule_background_process() +{ + // Trigger the timer event after 0.5s + this->background_process_timer.Start(500, wxTIMER_ONE_SHOT); +} + +// Update background processing thread from the current config and Model. +// Returns a bitmask of UpdateBackgroundProcessReturnState. +unsigned int Plater::priv::update_background_process() +{ + // bitmap of enum UpdateBackgroundProcessReturnState + unsigned int return_state = 0; + + // If the async_apply_config() was not called by the timer, kill the timer, so the async_apply_config() + // will not be called again in vain. + this->background_process_timer.Stop(); + + DynamicPrintConfig config = wxGetApp().preset_bundle->full_config(); + BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.opt("bed_shape")->values)); + BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(config.opt_float("max_print_height")))); + // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. + print_volume.min(2) = -1e10; + this->q->model().update_print_volume_state(print_volume); + + // Apply new config to the possibly running background task. + Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), std::move(config)); + + // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. + if (Slic3r::_3DScene::is_layers_editing_enabled(this->canvas3D)) + this->canvas3D->Refresh(); + + if (invalidated == Print::APPLY_STATUS_INVALIDATED) { + // Some previously calculated data on the Print was invalidated. + // Hide the slicing results, as the current slicing status is no more valid. + this->sidebar->show_sliced_info_sizer(false); + // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. + // Otherwise they will be just refreshed. + this->gcode_preview_data.reset(); + switch (this->printer_technology) { + case ptFFF: + if (this->preview != nullptr) + // If the preview is not visible, the following line just invalidates the preview, + // but the G-code paths are calculated first once the preview is made visible. + this->preview->reload_print(); + // We also need to reload 3D scene because of the wipe tower preview box + if (this->config->opt_bool("wipe_tower")) { + // std::vector selections = this->collect_selections(); + // Slic3r::_3DScene::set_objects_selections(this->canvas3D, selections); + return_state |= UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE; + } + break; + case ptSLA: + //FIXME as of now the Print::APPLY_STATUS_INVALIDATED is not reliable, and + // currently the scene refresh is expensive and loses selection. + //return_state |= UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE; + break; + } + } + + if (! this->background_process.empty()) { + std::string err = this->background_process.validate(); + if (err.empty()) { + if (invalidated != Print::APPLY_STATUS_UNCHANGED) + return_state |= UPDATE_BACKGROUND_PROCESS_RESTART; + } else { + // The print is not valid. + GUI::show_error(this->q, _(err)); + return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; + } + } + return return_state; +} + +void Plater::priv::async_apply_config() +{ + // bitmask of UpdateBackgroundProcessReturnState + unsigned int state = this->update_background_process(); + if (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) + _3DScene::reload_scene(canvas3D, false); + if ((state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 && this->get_config("background_processing") == "1") { + // The print is valid and it can be started. + if (this->background_process.start()) + this->statusbar()->set_cancel_callback([this]() { + this->statusbar()->set_status_text(L("Cancelling")); + this->background_process.stop(); + }); + } +} + +void Plater::priv::reload_from_disk() +{ + // TODO +} + +void Plater::priv::export_object_stl() +{ + // TODO +} + +void Plater::priv::fix_through_netfabb(const int obj_idx) +{ + if (obj_idx < 0) + return; + + const auto model_object = model.objects[obj_idx]; + Model model_fixed;// = new Model(); + fix_model_by_win10_sdk_gui(*model_object, print, model_fixed); + + auto new_obj_idxs = load_model_objects(model_fixed.objects); + if (new_obj_idxs.empty()) + return; + + for(auto new_obj_idx : new_obj_idxs) { + auto o = model.objects[new_obj_idx]; + o->clear_instances(); + for (auto instance: model_object->instances) + o->add_instance(*instance); + // o->invalidate_bounding_box(); + + if (o->volumes.size() == model_object->volumes.size()) { + for (int i = 0; i < o->volumes.size(); i++) { + o->volumes[i]->config.apply(model_object->volumes[i]->config); + } + } + // FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid, + } + + remove(obj_idx); +} + +void Plater::priv::on_notebook_changed(wxBookCtrlEvent&) +{ + const auto current_id = notebook->GetCurrentPage()->GetId(); + if (current_id == panel3d->GetId()) { + if (_3DScene::is_reload_delayed(canvas3D)) { + // Delayed loading of the 3D scene. + if (this->printer_technology == ptSLA) { + // Update the SLAPrint from the current Model, so that the reload_scene() + // pulls the correct data. + if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART) + this->schedule_background_process(); + } + _3DScene::reload_scene(canvas3D, true); + } + // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) + _3DScene::set_as_dirty(canvas3D); + } else if (current_id == preview->GetId()) { + preview->reload_print(); + preview->set_canvas_as_dirty(); + } +} + +void Plater::priv::on_select_preset(wxCommandEvent &evt) +{ + auto preset_type = static_cast(evt.GetInt()); + auto *combo = static_cast(evt.GetEventObject()); + + auto idx = combo->get_extruder_idx(); + + //! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, + //! but the OSX version derived from wxOwnerDrawnCombo. + //! So, to get selected string we do + //! combo->GetString(combo->GetSelection()) + //! instead of + //! combo->GetStringSelection().ToStdString()); + + std::string selected_string = combo->GetString(combo->GetSelection()).ToUTF8().data(); + + if (preset_type == Preset::TYPE_FILAMENT) { + wxGetApp().preset_bundle->set_filament_preset(idx, selected_string); + } + + // TODO: ? + if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) { + // Only update the platter UI for the 2nd and other filaments. + wxGetApp().preset_bundle->update_platter_filament_ui(idx, combo); + } + else { + for (Tab* tab : wxGetApp().tabs_list) { + if (tab->type() == preset_type) { + tab->select_preset(selected_string); + break; + } + } + } + + // update plater with new config + wxGetApp().plater()->on_config_change(wxGetApp().preset_bundle->full_config()); +} + +void Plater::priv::on_progress_event(wxCommandEvent &evt) +{ + this->statusbar()->set_progress(evt.GetInt()); + this->statusbar()->set_status_text(evt.GetString() + wxString::FromUTF8("…")); +} + +void Plater::priv::on_update_print_preview(wxCommandEvent &) +{ + if (this->preview != nullptr) + this->preview->reload_print(); + // in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: +// auto selections = collect_selections(); +// _3DScene::set_objects_selections(canvas3D, selections); +// if (canvas3D) +// _3DScene::reload_scene(canvas3D, true); +} + +void Plater::priv::on_process_completed(wxCommandEvent &evt) +{ + // Stop the background task, wait until the thread goes into the "Idle" state. + // At this point of time the thread should be either finished or canceled, + // so the following call just confirms, that the produced data were consumed. + this->background_process.stop(); + this->statusbar()->reset_cancel_callback(); + this->statusbar()->stop_busy(); + + bool canceled = evt.GetInt() < 0; + bool success = evt.GetInt() > 0; + // Reset the "export G-code path" name, so that the automatic background processing will be enabled again. + this->background_process.reset_export(); + if (! success) { + wxString message = evt.GetString(); + if (message.IsEmpty()) + message = _(L("Export failed")); + this->statusbar()->set_status_text(message); + } + if (canceled) + this->statusbar()->set_status_text(L("Cancelled")); + + this->sidebar->show_sliced_info_sizer(success); + + // this updates buttons status + //$self->object_list_changed; + + // refresh preview + switch (this->printer_technology) { + case ptFFF: + if (this->preview != nullptr) + this->preview->reload_print(); + break; + case ptSLA: + // Update the SLAPrint from the current Model, so that the reload_scene() + // pulls the correct data. + + // FIXME: SLAPrint::apply is not ready for this. At this stage it would + // invalidate the previous result and the supports would not be available + // for rendering. +// if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART) +// this->schedule_background_process(); + + _3DScene::reload_scene(canvas3D, true); + break; + } +} + +void Plater::priv::on_layer_editing_toggled(bool enable) +{ + _3DScene::enable_layers_editing(canvas3D, enable); + if (enable && !_3DScene::is_layers_editing_enabled(canvas3D)) { + // Initialization of the OpenGL shaders failed. Disable the tool. + _3DScene::enable_toolbar_item(canvas3D, "layersediting", false); + } + canvas3D->Refresh(); + canvas3D->Update(); +} + +void Plater::priv::on_schedule_background_process(SimpleEvent&) +{ + schedule_background_process(); +} + +void Plater::priv::on_action_add(SimpleEvent&) +{ + if (q != nullptr) +#if ENABLE_NEW_MENU_LAYOUT + q->add_model(); +#else + q->add(); +#endif // ENABLE_NEW_MENU_LAYOUT +} + +void Plater::priv::on_action_split_objects(SimpleEvent&) +{ + split_object(); +} + +void Plater::priv::on_action_split_volumes(SimpleEvent&) +{ + split_volume(); +} + +void Plater::priv::on_action_layersediting(SimpleEvent&) +{ + bool enable = !_3DScene::is_layers_editing_enabled(canvas3D); + _3DScene::enable_layers_editing(canvas3D, enable); + if (enable && !_3DScene::is_layers_editing_enabled(canvas3D)) + _3DScene::enable_toolbar_item(canvas3D, "layersediting", false); +} + +void Plater::priv::on_object_select(SimpleEvent& evt) +{ + selection_changed(); + wxGetApp().obj_list()->update_selections(); +} + +void Plater::priv::on_viewport_changed(SimpleEvent& evt) +{ + wxObject* o = evt.GetEventObject(); + if (o == preview->get_wxglcanvas()) + preview->set_viewport_into_scene(canvas3D); + else if (o == canvas3D) + preview->set_viewport_from_scene(canvas3D); +} + +void Plater::priv::on_right_click(Vec2dEvent& evt) +{ + int obj_idx = get_selected_object_idx(); + if (obj_idx == -1) + return; + + if (q != nullptr) + q->PopupMenu(&object_menu, (int)evt.data.x(), (int)evt.data.y()); +} + +void Plater::priv::on_model_update(SimpleEvent&) +{ + // TODO +} + +void Plater::priv::on_wipetower_moved(Vec3dEvent &evt) +{ + DynamicPrintConfig cfg; + cfg.opt("wipe_tower_x", true)->value = evt.data(0); + cfg.opt("wipe_tower_y", true)->value = evt.data(1); + wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg); +} + +void Plater::priv::on_enable_action_buttons(Event&) +{ + // TODO +} + +void Plater::priv::on_update_geometry(Vec3dsEvent<2>&) +{ + // TODO +} + +bool Plater::priv::init_object_menu() +{ + wxMenuItem* item_delete = append_menu_item(&object_menu, wxID_ANY, _(L("Delete\tDel")), _(L("Remove the selected object")), + [this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png"); + wxMenuItem* item_increase = append_menu_item(&object_menu, wxID_ANY, _(L("Increase copies\t+")), _(L("Place one more copy of the selected object")), + [this](wxCommandEvent&) { q->increase_instances(); }, "add.png"); + wxMenuItem* item_decrease = append_menu_item(&object_menu, wxID_ANY, _(L("Decrease copies\t-")), _(L("Remove one copy of the selected object")), + [this](wxCommandEvent&) { q->decrease_instances(); }, "delete.png"); + wxMenuItem* item_set_number_of_copies = append_menu_item(&object_menu, wxID_ANY, _(L("Set number of copies…")), _(L("Change the number of copies of the selected object")), + [this](wxCommandEvent&) { q->set_number_of_copies(); }, "textfield.png"); + + object_menu.AppendSeparator(); + + wxMenu* mirror_menu = new wxMenu(); + if (mirror_menu == nullptr) + return false; + + append_menu_item(mirror_menu, wxID_ANY, _(L("Along X axis")), _(L("Mirror the selected object along the X axis")), + [this](wxCommandEvent&) { mirror(X); }, "bullet_red.png", &object_menu); + append_menu_item(mirror_menu, wxID_ANY, _(L("Along Y axis")), _(L("Mirror the selected object along the Y axis")), + [this](wxCommandEvent&) { mirror(Y); }, "bullet_green.png", &object_menu); + append_menu_item(mirror_menu, wxID_ANY, _(L("Along Z axis")), _(L("Mirror the selected object along the Z axis")), + [this](wxCommandEvent&) { mirror(Z); }, "bullet_blue.png", &object_menu); + + wxMenuItem* item_mirror = append_submenu(&object_menu, mirror_menu, wxID_ANY, _(L("Mirror")), _(L("Mirror the selected object"))); + + wxMenu* split_menu = new wxMenu(); + if (split_menu == nullptr) + return false; + + wxMenuItem* item_split_objects = append_menu_item(split_menu, wxID_ANY, _(L("To objects")), _(L("Split the selected object into individual objects")), + [this](wxCommandEvent&) { split_object(); }, "shape_ungroup_o.png", &object_menu); + wxMenuItem* item_split_volumes = append_menu_item(split_menu, wxID_ANY, _(L("To parts")), _(L("Split the selected object into individual sub-parts")), + [this](wxCommandEvent&) { split_volume(); }, "shape_ungroup_p.png", &object_menu); + + wxMenuItem* item_split = append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object")), "shape_ungroup.png"); + + // ui updates needs to be binded to the parent panel + if (q != nullptr) + { + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_mirror()); }, item_mirror->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete_object()); }, item_delete->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_increase->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_decrease_instances()); }, item_decrease->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_set_number_of_copies->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects() || can_split_to_volumes()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split_objects->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split_volumes->GetId()); + } + + return true; +} + +bool Plater::priv::can_delete_object() const +{ + int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); +} + +bool Plater::priv::can_increase_instances() const +{ + int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); +} + +bool Plater::priv::can_decrease_instances() const +{ + int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); +} + +bool Plater::priv::can_split_to_objects() const +{ + int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && !model.objects[obj_idx]->is_multiparts(); +} + +bool Plater::priv::can_split_to_volumes() const +{ + int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && !model.objects[obj_idx]->is_multiparts(); +} + +bool Plater::priv::layers_height_allowed() const +{ + int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && _3DScene::is_layers_editing_allowed(canvas3D); +} + +bool Plater::priv::can_delete_all() const +{ + return !model.objects.empty(); +} + +bool Plater::priv::can_arrange() const +{ + return !model.objects.empty() && !arranging.load(); +} + +bool Plater::priv::can_mirror() const +{ + return get_selection().is_from_single_instance(); +} + +// Plater / Public + +Plater::Plater(wxWindow *parent, MainFrame *main_frame) + : wxPanel(parent), p(new priv(this, main_frame)) +{ + // Initialization performed in the private c-tor +} + +Plater::~Plater() +{ + _3DScene::remove_canvas(p->canvas3D); +} + +Sidebar& Plater::sidebar() { return *p->sidebar; } +Model& Plater::model() { return p->model; } +Print& Plater::print() { return p->print; } + +#if ENABLE_NEW_MENU_LAYOUT +void Plater::load_project() +{ + wxString input_file; + wxGetApp().load_project(this, input_file); + + if (input_file.empty()) + return; + + p->reset(); + p->project_filename = input_file; + + std::vector input_paths; + input_paths.push_back(input_file.wx_str()); + load_files(input_paths); +} + +void Plater::add_model() +#else +void Plater::add() +#endif // ENABLE_NEW_MENU_LAYOUT +{ + wxArrayString input_files; +#if ENABLE_NEW_MENU_LAYOUT + wxGetApp().import_model(this, input_files); +#else + wxGetApp().open_model(this, input_files); +#endif // ENABLE_NEW_MENU_LAYOUT +#if ENABLE_NEW_MENU_LAYOUT + if (input_files.empty()) + return; +#endif // ENABLE_NEW_MENU_LAYOUT + + std::vector input_paths; + for (const auto &file : input_files) { + input_paths.push_back(file.wx_str()); + } +#if ENABLE_NEW_MENU_LAYOUT + load_files(input_paths, true, false); +#else + load_files(input_paths); +#endif // ENABLE_NEW_MENU_LAYOUT +} + +#if ENABLE_NEW_MENU_LAYOUT +void Plater::extract_config_from_project() +{ + wxString input_file; + wxGetApp().load_project(this, input_file); + + if (input_file.empty()) + return; + + std::vector input_paths; + input_paths.push_back(input_file.wx_str()); + load_files(input_paths, false, true); +} +#endif // ENABLE_NEW_MENU_LAYOUT + +#if ENABLE_NEW_MENU_LAYOUT +void Plater::load_files(const std::vector& input_files, bool load_model, bool load_config) { p->load_files(input_files, load_model, load_config); } +#else +void Plater::load_files(const std::vector &input_files) { p->load_files(input_files); } +#endif // ENABLE_NEW_MENU_LAYOUT + +void Plater::update(bool force_autocenter) { p->update(force_autocenter); } + +void Plater::select_view(const std::string& direction) { p->select_view(direction); } + +void Plater::remove(size_t obj_idx) { p->remove(obj_idx); } +void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_model(obj_idx); } + +void Plater::remove_selected() +{ + _3DScene::delete_selected(canvas3D()); +} + +void Plater::increase_instances(size_t num) +{ + int obj_idx = p->get_selected_object_idx(); + if (obj_idx == -1) + return; + + ModelObject* model_object = p->model.objects[obj_idx]; + ModelInstance* model_instance = model_object->instances.back(); + + bool was_one_instance = model_object->instances.size()==1; + + float offset = 10.0; + for (size_t i = 0; i < num; i++, offset += 10.0) { + Vec3d offset_vec = model_instance->get_offset() + Vec3d(offset, offset, 0.0); + model_object->add_instance(offset_vec, model_instance->get_scaling_factor(), model_instance->get_rotation()); +// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec)); + } + + sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num); + + if (p->get_config("autocenter") == "1") { + p->arrange(); + } else { + p->update(); + } + + p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1); + + p->selection_changed(); + + this->p->schedule_background_process(); +} + +void Plater::decrease_instances(size_t num) +{ + int obj_idx = p->get_selected_object_idx(); + if (obj_idx == -1) + return; + + ModelObject* model_object = p->model.objects[obj_idx]; + if (model_object->instances.size() > num) { + for (size_t i = 0; i < num; ++ i) + model_object->delete_last_instance(); + sidebar().obj_list()->decrease_object_instances(obj_idx, num); + } + else { + remove(obj_idx); + } + + p->update(); + + if (!model_object->instances.empty()) + p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1); + + p->selection_changed(); + this->p->schedule_background_process(); +} + +void Plater::set_number_of_copies(/*size_t num*/) +{ + int obj_idx = p->get_selected_object_idx(); + if (obj_idx == -1) + return; + + ModelObject* model_object = p->model.objects[obj_idx]; + + const auto num = wxGetNumberFromUser( " ", _("Enter the number of copies:"), + _("Copies of the selected object"), model_object->instances.size(), 0, 1000, this ); + if (num < 0) + return; + + int diff = (int)num - (int)model_object->instances.size(); + if (diff > 0) + increase_instances(diff); + else if (diff < 0) + decrease_instances(-diff); +} + +void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z) +{ + wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); + auto *object = p->model.objects[obj_idx]; + + wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); + + const auto new_objects = object->cut(instance_idx, z); + + remove(obj_idx); + p->load_model_objects(new_objects); + + p->arrange(); +} + +void Plater::export_gcode(fs::path output_path) +{ + if (p->model.objects.empty()) + return; + + if (this->p->background_process.is_export_scheduled()) { + GUI::show_error(this, _(L("Another export job is currently running."))); + return; + } + + std::string err = wxGetApp().preset_bundle->full_config().validate(); + if (err.empty()) + err = p->background_process.validate(); + if (! err.empty()) { + // The config is not valid + GUI::show_error(this, _(err)); + return; + } + + std::string final_path; + if(printer_technology() == ptFFF) { // TODO: custom button for SLA export + + // Copy the names of active presets into the placeholder parser. + wxGetApp().preset_bundle->export_selections(p->print.placeholder_parser()); + + // select output file + if (output_path.empty()) { + // XXX: take output path from CLI opts? Ancient Slic3r versions used to do that... + + // If possible, remove accents from accented latin characters. + // This function is useful for generating file names to be processed by legacy firmwares. + auto default_output_file = fs::path(Slic3r::fold_utf8_to_ascii( + p->print.output_filepath(output_path.string()) + // FIXME: ^ errors to handle? + )); + auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string()); + + wxFileDialog dlg(this, _(L("Save G-code file as:")), + start_dir, + default_output_file.filename().string(), + GUI::file_wildcards[FT_GCODE], + wxFD_SAVE | wxFD_OVERWRITE_PROMPT + ); + + if (dlg.ShowModal() == wxID_OK) { + fs::path path(dlg.GetPath()); + wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); + output_path = path; + } + } + + final_path = p->print.output_filepath(output_path.string()); + } else { + wxFileDialog dlg(this, _(L("Save Zip file as:")), + wxGetApp().app_config->get_last_output_dir(""), + "out.zip", + GUI::file_wildcards[FT_PNGZIP], + wxFD_SAVE | wxFD_OVERWRITE_PROMPT + ); + + if (dlg.ShowModal() == wxID_OK) { + fs::path path(dlg.GetPath()); + wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); + output_path = path; + } + + final_path = output_path.string(); + } + + if (! output_path.empty()) { + this->p->background_process.schedule_export(final_path); + this->p->background_process.start(); + } +} + +void Plater::export_stl() +{ + if (p->model.objects.empty()) { return; } + + auto dialog = p->get_export_file(FT_STL); + if (! dialog) { return; } + + // Store a binary STL + wxString path = dialog->GetPath(); + auto path_cstr = path.c_str(); + auto mesh = p->model.mesh(); + Slic3r::store_stl(path_cstr, &mesh, true); + p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path)); +} + +void Plater::export_amf() +{ + if (p->model.objects.empty()) { return; } + + auto dialog = p->get_export_file(FT_AMF); + if (! dialog) { return; } + + wxString path = dialog->GetPath(); + auto path_cstr = path.c_str(); + + DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config(); + if (Slic3r::store_amf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { + // Success + p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); + } else { + // Failure + p->statusbar()->set_status_text(wxString::Format(_(L("Error exporting AMF file %s")), path)); + } +} + +#if ENABLE_NEW_MENU_LAYOUT +void Plater::export_3mf(const boost::filesystem::path& output_path) +#else +void Plater::export_3mf() +#endif // ENABLE_NEW_MENU_LAYOUT +{ + if (p->model.objects.empty()) { return; } + +#if ENABLE_NEW_MENU_LAYOUT + wxString path; + bool export_config = true; + if (output_path.empty()) + { +#endif // ENABLE_NEW_MENU_LAYOUT + auto dialog = p->get_export_file(FT_3MF); + if (!dialog) { return; } +#if ENABLE_NEW_MENU_LAYOUT + path = dialog->GetPath(); + export_config = dialog->get_checkbox_value(); + } + else + path = output_path.string(); + + if (!path.Lower().EndsWith(".3mf")) + return; +#else + wxString path = dialog->GetPath(); + auto path_cstr = path.c_str(); +#endif // ENABLE_NEW_MENU_LAYOUT + + DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config(); +#if ENABLE_NEW_MENU_LAYOUT + if (Slic3r::store_3mf(path.c_str(), &p->model, export_config ? &cfg : nullptr)) { +#else + if (Slic3r::store_3mf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { +#endif // ENABLE_NEW_MENU_LAYOUT + // Success + p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); + } else { + // Failure + p->statusbar()->set_status_text(wxString::Format(_(L("Error exporting 3MF file %s")), path)); + } +} + +void Plater::reslice() +{ + //FIXME Don't reslice if export of G-code or sending to OctoPrint is running. + // bitmask of UpdateBackgroundProcessReturnState + unsigned int state = this->p->update_background_process(); + if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) + _3DScene::reload_scene(this->p->canvas3D, false); + if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && !this->p->background_process.running()) { + // The print is valid and it can be started. + // Copy the names of active presets into the placeholder parser. + //FIXME how to generate a file name for the SLA printers? + wxGetApp().preset_bundle->export_selections(this->print().placeholder_parser()); + if (this->p->background_process.start()) + this->p->statusbar()->set_cancel_callback([this]() { + this->p->statusbar()->set_status_text(L("Cancelling")); + this->p->background_process.stop(); + }); + } +} + +void Plater::send_gcode() +{ +// p->send_gcode_file = export_gcode(); +} + +void Plater::on_extruders_change(int num_extruders) +{ + auto& choices = sidebar().combos_filament(); + + wxWindowUpdateLocker noUpdates_scrolled_panel(&sidebar()/*.scrolled_panel()*/); +// sidebar().scrolled_panel()->Freeze(); + + int i = choices.size(); + while ( i < num_extruders ) + { + PresetComboBox* choice/*{ nullptr }*/; + sidebar().init_filament_combo(&choice, i); + choices.push_back(choice); + + // initialize selection + wxGetApp().preset_bundle->update_platter_filament_ui(i, choice); + ++i; + } + + // remove unused choices if any + sidebar().remove_unused_filament_combos(num_extruders); + + sidebar().Layout(); + sidebar().scrolled_panel()->Refresh(); +} + +void Plater::on_config_change(const DynamicPrintConfig &config) +{ + bool update_scheduled = false; + for (auto opt_key : p->config->diff(config)) { + p->config->set_key_value(opt_key, config.option(opt_key)->clone()); + if (opt_key == "printer_technology") { + p->printer_technology = config.opt_enum(opt_key); + p->background_process.select_technology(this->printer_technology()); + } + else if (opt_key == "bed_shape") { + if (p->canvas3D) _3DScene::set_bed_shape(p->canvas3D, p->config->option(opt_key)->values); + if (p->preview) p->preview->set_bed_shape(p->config->option(opt_key)->values); + update_scheduled = true; + } + else if(opt_key == "wipe_tower" /*|| opt_key == "filament_minimal_purge_on_wipe_tower"*/ || // ? #ys_FIXME + opt_key == "single_extruder_multi_material") { + update_scheduled = true; + } +// else if(opt_key == "serial_port") { +// sidebar()->p->btn_print->Show(config.get("serial_port")); // ???: btn_print is removed +// Layout(); +// } + else if (opt_key == "print_host") { + sidebar().show_button(baReslice, !p->config->option(opt_key)->value.empty()); + Layout(); + } + else if(opt_key == "variable_layer_height") { + if (p->config->opt_bool("variable_layer_height") != true) { + _3DScene::enable_toolbar_item(p->canvas3D, "layersediting", false); + _3DScene::enable_layers_editing(p->canvas3D, 0); + p->canvas3D->Refresh(); + p->canvas3D->Update(); + } + else if (_3DScene::is_layers_editing_allowed(p->canvas3D)) { + _3DScene::enable_toolbar_item(p->canvas3D, "layersediting", true); + } + } + else if(opt_key == "extruder_colour") { + update_scheduled = true; + p->preview->set_number_extruders(p->config->option(opt_key)->values.size()); + } else if(opt_key == "max_print_height") { + update_scheduled = true; + } else if(opt_key == "printer_model") { + // update to force bed selection(for texturing) + if (p->canvas3D) _3DScene::set_bed_shape(p->canvas3D, p->config->option("bed_shape")->values); + if (p->preview) p->preview->set_bed_shape(p->config->option("bed_shape")->values); + update_scheduled = true; + } + } + + if (update_scheduled) + update(); + + if (p->main_frame->is_loaded()) + this->p->schedule_background_process(); +} + +#if ENABLE_NEW_MENU_LAYOUT +const wxString& Plater::get_project_filename() const +{ + return p->project_filename; +} + +bool Plater::is_export_gcode_scheduled() const +{ + return p->background_process.is_export_scheduled(); +} +#endif // ENABLE_NEW_MENU_LAYOUT + +int Plater::get_selected_object_idx() +{ + return p->get_selected_object_idx(); +} + +bool Plater::is_single_full_object_selection() const +{ + return p->get_selection().is_single_full_object(); +} + +wxGLCanvas* Plater::canvas3D() +{ + return p->canvas3D; +} + +PrinterTechnology Plater::printer_technology() const +{ + return p->printer_technology; +} + +void Plater::changed_object(int obj_idx) +{ + if (obj_idx < 0) + return; + auto list = wxGetApp().obj_list(); + wxASSERT(list != nullptr); + if (list == nullptr) + return; + + if (list->is_parts_changed()) { + // recenter and re - align to Z = 0 + auto model_object = p->model.objects[obj_idx]; +#if !ENABLE_MODELVOLUME_TRANSFORM + model_object->center_around_origin(); +#endif // !ENABLE_MODELVOLUME_TRANSFORM + model_object->ensure_on_bed(); + _3DScene::reload_scene(p->canvas3D, false); + } + + // update print + if (list->is_parts_changed() || list->is_part_settings_changed()) { + this->p->schedule_background_process(); +#if !ENABLE_MODIFIED_CAMERA_TARGET + _3DScene::zoom_to_volumes(p->canvas3D); +#endif // !ENABLE_MODIFIED_CAMERA_TARGET + } + else { + this->p->schedule_background_process(); + } + +} + +void Plater::fix_through_netfabb(const int obj_idx) { p->fix_through_netfabb(obj_idx); } + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp new file mode 100644 index 0000000000..d6716c620a --- /dev/null +++ b/src/slic3r/GUI/Plater.hpp @@ -0,0 +1,175 @@ +#ifndef slic3r_Plater_hpp_ +#define slic3r_Plater_hpp_ + +#include +#include +#include + +#include +#include + +#include "Preset.hpp" + +class wxButton; +class wxBoxSizer; +class wxGLCanvas; +class wxScrolledWindow; + +namespace Slic3r { + +class Model; +class Print; +class SLAPrint; + +namespace GUI { + +class MainFrame; +class ConfigOptionsGroup; +class ObjectManipulation; +class ObjectSettings; +class ObjectList; + +using t_optgroups = std::vector >; + +class Plater; + +class PresetComboBox : public wxBitmapComboBox +{ +public: + PresetComboBox(wxWindow *parent, Preset::Type preset_type); + ~PresetComboBox(); + + void set_label_marker(int item); + void set_extruder_idx(const int extr_idx) { extruder_idx = extr_idx; } + int get_extruder_idx() const { return extruder_idx; } + +private: + typedef std::size_t Marker; + enum { LABEL_ITEM_MARKER = 0x4d }; + + Preset::Type preset_type; + int last_selected; + int extruder_idx = -1; +}; + +enum ButtonAction +{ + baUndef, + baReslice, + baExportGcode, + baSendGcode +}; + +class Sidebar : public wxPanel +{ + /*ConfigMenuIDs*/int m_mode; +public: + Sidebar(Plater *parent); + Sidebar(Sidebar &&) = delete; + Sidebar(const Sidebar &) = delete; + Sidebar &operator=(Sidebar &&) = delete; + Sidebar &operator=(const Sidebar &) = delete; + ~Sidebar(); + + void init_filament_combo(PresetComboBox **combo, const int extr_idx); + void remove_unused_filament_combos(const int current_extruder_count); + void update_presets(Slic3r::Preset::Type preset_type); + + ObjectManipulation* obj_manipul(); + ObjectList* obj_list(); + ObjectSettings* obj_settings(); + wxScrolledWindow* scrolled_panel(); + + ConfigOptionsGroup* og_freq_chng_params(); + wxButton* get_wiping_dialog_button(); + void update_objects_list_extruder_column(int extruders_count); + void show_info_sizer(); + void show_sliced_info_sizer(const bool show); + void show_buttons(const bool show); + void show_button(ButtonAction but_action, bool show); + void enable_buttons(bool enable); + bool is_multifilament(); + void set_mode_value(const /*ConfigMenuIDs*/int mode) { m_mode = mode; } + + std::vector& combos_filament(); +private: + struct priv; + std::unique_ptr p; +}; + +class Plater: public wxPanel +{ +public: + Plater(wxWindow *parent, MainFrame *main_frame); + Plater(Plater &&) = delete; + Plater(const Plater &) = delete; + Plater &operator=(Plater &&) = delete; + Plater &operator=(const Plater &) = delete; + ~Plater(); + + Sidebar& sidebar(); + Model& model(); + Print& print(); + +#if ENABLE_NEW_MENU_LAYOUT + void load_project(); + void add_model(); + void extract_config_from_project(); +#else + void add(); +#endif // ENABLE_NEW_MENU_LAYOUT + +#if ENABLE_NEW_MENU_LAYOUT + void load_files(const std::vector& input_files, bool load_model = true, bool load_config = true); +#else + void load_files(const std::vector &input_files); +#endif // ENABLE_NEW_MENU_LAYOUT + + void update(bool force_autocenter = false); + void select_view(const std::string& direction); + + void remove(size_t obj_idx); + void delete_object_from_model(size_t obj_idx); + void remove_selected(); + void increase_instances(size_t num = 1); + void decrease_instances(size_t num = 1); + void set_number_of_copies(/*size_t num*/); + + void cut(size_t obj_idx, size_t instance_idx, coordf_t z); + + // Note: empty path means "use the default" + void export_gcode(boost::filesystem::path output_path = boost::filesystem::path()); + void export_stl(); + void export_amf(); +#if ENABLE_NEW_MENU_LAYOUT + void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); +#else + void export_3mf(); +#endif // ENABLE_NEW_MENU_LAYOUT + void reslice(); + void changed_object(int obj_idx); + void fix_through_netfabb(const int obj_idx); + void send_gcode(); + + void on_extruders_change(int extruders_count); + void on_config_change(const DynamicPrintConfig &config); + +#if ENABLE_NEW_MENU_LAYOUT + const wxString& get_project_filename() const; + bool is_export_gcode_scheduled() const; +#endif // ENABLE_NEW_MENU_LAYOUT + + int get_selected_object_idx(); + bool is_single_full_object_selection() const; + wxGLCanvas* canvas3D(); + + PrinterTechnology printer_technology() const; +private: + struct priv; + std::unique_ptr p; +}; + + +}} + +#endif diff --git a/xs/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp similarity index 87% rename from xs/src/slic3r/GUI/Preferences.cpp rename to src/slic3r/GUI/Preferences.cpp index 2500afa136..2037572da3 100644 --- a/xs/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -5,11 +5,10 @@ namespace Slic3r { namespace GUI { -PreferencesDialog::PreferencesDialog(wxWindow* parent, int event_preferences) : - wxDialog(parent, wxID_ANY, _(L("Preferences")), wxDefaultPosition, wxDefaultSize), - m_event_preferences(event_preferences) { - build(); - } +PreferencesDialog::PreferencesDialog(wxWindow* parent) : + wxDialog(parent, wxID_ANY, _(L("Preferences")), wxDefaultPosition, wxDefaultSize) { + build(); +} void PreferencesDialog::build() { @@ -71,14 +70,6 @@ void PreferencesDialog::build() option = Option (def, "preset_update"); m_optgroup->append_single_option_line(option); - def.label = L("Disable USB/serial connection"); - def.type = coBool; - def.tooltip = L("Disable communication with the printer over a serial / USB cable. " - "This simplifies the user interface in case the printer is never attached to the computer."); - def.default_value = new ConfigOptionBool{ app_config->get("no_controller")[0] == '1' }; // 1; - option = Option (def,"no_controller"); - m_optgroup->append_single_option_line(option); - def.label = L("Suppress \" - default - \" presets"); def.type = coBool; def.tooltip = L("Suppress \" - default - \" presets in the Print / Filament / Printer " @@ -118,8 +109,7 @@ void PreferencesDialog::build() void PreferencesDialog::accept() { - if (m_values.find("no_controller") != m_values.end()|| - m_values.find("no_defaults") != m_values.end()|| + if (m_values.find("no_defaults") != m_values.end()|| m_values.find("use_legacy_opengl")!= m_values.end()) { warning_catcher(this, _(L("You need to restart Slic3r to make the changes effective."))); } @@ -133,10 +123,7 @@ void PreferencesDialog::accept() Close(); // needed on Linux // Nothify the UI to update itself from the ini file. - if (m_event_preferences > 0) { - wxCommandEvent event(m_event_preferences); - get_app()->ProcessEvent(event); - } + wxGetApp().update_ui_from_settings(); } } // GUI diff --git a/xs/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp similarity index 79% rename from xs/src/slic3r/GUI/Preferences.hpp rename to src/slic3r/GUI/Preferences.hpp index d01d78b70b..363daebbc7 100644 --- a/xs/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -15,10 +15,9 @@ class PreferencesDialog : public wxDialog { std::map m_values; std::shared_ptr m_optgroup; - int m_event_preferences; public: - PreferencesDialog(wxWindow* parent, int event_preferences); - ~PreferencesDialog(){ } + PreferencesDialog(wxWindow* parent); + ~PreferencesDialog() {} void build(); void accept(); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp similarity index 82% rename from xs/src/slic3r/GUI/Preset.cpp rename to src/slic3r/GUI/Preset.cpp index 8af10d293a..1599af0c4d 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -28,6 +28,7 @@ #include "../../libslic3r/libslic3r.h" #include "../../libslic3r/Utils.hpp" #include "../../libslic3r/PlaceholderParser.hpp" +#include "Plater.hpp" using boost::property_tree::ptree; @@ -120,6 +121,11 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem VendorProfile::PrinterModel model; model.id = section.first.substr(printer_model_key.size()); model.name = section.second.get("name", model.id); + auto technology_field = section.second.get("technology", "FFF"); + if (! ConfigOptionEnum::from_string(technology_field, model.technology)) { + BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Invalid printer technology field: `%2%`") % id % technology_field; + model.technology = ptFFF; + } section.second.get("variants", ""); const auto variants_field = section.second.get("variants", ""); std::vector variants; @@ -164,10 +170,12 @@ void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extr { const auto &defaults = FullPrintConfig::defaults(); for (const std::string &key : Preset::nozzle_options()) { + if (key == "default_filament_profile") + continue; auto *opt = config.option(key, false); assert(opt != nullptr); assert(opt->is_vector()); - if (opt != nullptr && opt->is_vector() && key != "default_filament_profile") + if (opt != nullptr && opt->is_vector()) static_cast(opt)->resize(num_extruders, defaults.option(key)); } } @@ -177,7 +185,7 @@ void Preset::normalize(DynamicPrintConfig &config) { auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); if (nozzle_diameter != nullptr) - // Loaded the Printer settings. Verify, that all extruder dependent values have enough values. + // Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values. set_num_extruders(config, (unsigned int)nozzle_diameter->values.size()); if (config.option("filament_diameter") != nullptr) { // This config contains single or multiple filament presets. @@ -188,42 +196,35 @@ void Preset::normalize(DynamicPrintConfig &config) if (key == "compatible_printers") continue; auto *opt = config.option(key, false); - assert(opt != nullptr); - assert(opt->is_vector()); + /*assert(opt != nullptr); + assert(opt->is_vector());*/ if (opt != nullptr && opt->is_vector()) static_cast(opt)->resize(n, defaults.option(key)); } // The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately. for (const std::string &key : { "filament_settings_id" }) { auto *opt = config.option(key, false); - assert(opt != nullptr); - assert(opt->type() == coStrings); + assert(opt == nullptr || opt->type() == coStrings); if (opt != nullptr && opt->type() == coStrings) static_cast(opt)->values.resize(n, std::string()); } } } -// Load a config file, return a C++ class Slic3r::DynamicPrintConfig with $keys initialized from the config file. -// In case of a "default" config item, return the default values. -DynamicPrintConfig& Preset::load(const std::vector &keys) +std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config) { - // Set the configuration from the defaults. - Slic3r::FullPrintConfig defaults; - this->config.apply_only(defaults, keys.empty() ? defaults.keys() : keys); - if (! this->is_default) { - // Load the preset file, apply preset values on top of defaults. - try { - this->config.load_from_ini(this->file); - Preset::normalize(this->config); - } catch (const std::ifstream::failure &err) { - throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + this->file + "\n\tReason: " + err.what()); - } catch (const std::runtime_error &err) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + this->file + "\n\tReason: " + err.what()); + std::string incorrect_keys; + for (const std::string &key : config.keys()) + if (! default_config.has(key)) { + if (incorrect_keys.empty()) + incorrect_keys = key; + else { + incorrect_keys += ", "; + incorrect_keys += key; + } + config.erase(key); } - } - this->loaded = true; - return this->config; + return incorrect_keys; } void Preset::save() @@ -260,8 +261,9 @@ bool Preset::is_compatible_with_printer(const Preset &active_printer) const { DynamicPrintConfig config; config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name)); - config.set_key_value("num_extruders", new ConfigOptionInt( - (int)static_cast(active_printer.config.option("nozzle_diameter"))->values.size())); + const ConfigOption *opt = active_printer.config.option("nozzle_diameter"); + if (opt) + config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast(opt)->values.size())); return this->is_compatible_with_printer(active_printer, &config); } @@ -328,8 +330,10 @@ const std::vector& Preset::printer_options() static std::vector s_opts; if (s_opts.empty()) { s_opts = { - "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", "host_type", - "print_host", "printhost_apikey", "printhost_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", + "printer_technology", + "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", + "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", + "host_type", "print_host", "printhost_apikey", "printhost_cafile", "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction", "cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits", @@ -359,7 +363,68 @@ const std::vector& Preset::nozzle_options() return s_opts; } -PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys) : +const std::vector& Preset::sla_printer_options() +{ + static std::vector s_opts; + if (s_opts.empty()) { + s_opts = { + "printer_technology", + "bed_shape", "max_print_height", + "display_width", "display_height", "display_pixels_x", "display_pixels_y", + "printer_correction", + "printer_notes", + "inherits" + }; + } + return s_opts; +} + +const std::vector& Preset::sla_material_options() +{ + static std::vector s_opts; + if (s_opts.empty()) { + s_opts = { + "layer_height", "initial_layer_height", + "exposure_time", "initial_exposure_time", + "material_correction_printing", "material_correction_curing", + "material_notes", + "default_sla_material_profile", + "compatible_printers", + "compatible_printers_condition", "inherits" + }; + } + return s_opts; +} + +const std::vector& Preset::sla_print_options() +{ + static std::vector s_opts; + if (s_opts.empty()) { + s_opts = { + "layer_height", + "support_head_front_radius", + "support_head_penetration", + "support_head_back_radius", + "support_head_width", + "support_pillar_radius", + "support_base_radius", + "support_base_height", + "support_critical_angle", + "support_max_bridge_length", + "pad_wall_thickness", + "pad_wall_height", + "pad_max_merge_distance", + "pad_edge_radius", + "default_sla_print_profile", + "compatible_printers", + "compatible_printers_condition", + "inherits" + }; + } + return s_opts; +} + +PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) : m_type(type), m_edited_preset(type, "", false), m_idx_selected(0), @@ -367,8 +432,7 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vectoradd_default_preset(keys, defaults, default_name); m_edited_preset.config.apply(m_presets.front().config); } @@ -382,7 +446,7 @@ PresetCollection::~PresetCollection() void PresetCollection::reset(bool delete_files) { - if (m_presets.size() > 1) { + if (m_presets.size() > m_num_default_presets) { if (delete_files) { // Erase the preset files. for (Preset &preset : m_presets) @@ -390,18 +454,26 @@ void PresetCollection::reset(bool delete_files) boost::nowide::remove(preset.file.c_str()); } // Don't use m_presets.resize() here as it requires a default constructor for Preset. - m_presets.erase(m_presets.begin() + 1, m_presets.end()); + m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end()); this->select_preset(0); } } +void PresetCollection::add_default_preset(const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name) +{ + // Insert just the default preset. + m_presets.emplace_back(Preset(this->type(), preset_name, true)); + m_presets.back().config.apply_only(defaults, keys.empty() ? defaults.keys() : keys); + m_presets.back().loaded = true; + ++ m_num_default_presets; +} + // Load all presets found in dir_path. // Throws an exception on error. void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir) { boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred(); m_dir_path = dir.string(); - t_config_option_keys keys = this->default_preset().config.keys(); std::string errors_cummulative; for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) { @@ -417,21 +489,33 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri try { Preset preset(m_type, name, false); preset.file = dir_entry.path().string(); - DynamicPrintConfig &config = preset.load(keys); - // Report configuration fields, which are misplaced into a wrong group. - std::string incorrect_keys; - if (config.remove_keys_not_in(this->default_preset().config, incorrect_keys) > 0) - BOOST_LOG_TRIVIAL(error) << "Error in \"" << dir_entry.path().string() << "\": The preset contains the following incorrect keys: " << - incorrect_keys << ", which were ignored"; - // Normalize once again to set the length of the filament specific vectors to 1. - Preset::normalize(config); + // Load the preset file, apply preset values on top of defaults. + try { + DynamicPrintConfig config; + config.load_from_ini(preset.file); + // Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field. + const Preset &default_preset = this->default_preset_for(config); + preset.config = default_preset.config; + preset.config.apply(std::move(config)); + Preset::normalize(preset.config); + // Report configuration fields, which are misplaced into a wrong group. + std::string incorrect_keys = Preset::remove_invalid_keys(config, default_preset.config); + if (! incorrect_keys.empty()) + BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" << + preset.file << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed"; + preset.loaded = true; + } catch (const std::ifstream::failure &err) { + throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what()); + } catch (const std::runtime_error &err) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what()); + } m_presets.emplace_back(preset); } catch (const std::runtime_error &err) { errors_cummulative += err.what(); errors_cummulative += "\n"; } } - std::sort(m_presets.begin() + 1, m_presets.end()); + std::sort(m_presets.begin() + m_num_default_presets, m_presets.end()); this->select_preset(first_visible_idx()); if (! errors_cummulative.empty()) throw std::runtime_error(errors_cummulative); @@ -452,8 +536,8 @@ static bool profile_print_params_same(const DynamicPrintConfig &cfg1, const Dyna // Following keys are used by the UI, not by the slicing core, therefore they are not important // when comparing profiles for equality. Ignore them. for (const char *key : { "compatible_printers", "compatible_printers_condition", "inherits", - "print_settings_id", "filament_settings_id", "printer_settings_id", - "printer_model", "printer_variant", "default_print_profile", "default_filament_profile" }) + "print_settings_id", "filament_settings_id", "sla_material_settings_id", "printer_settings_id", + "printer_model", "printer_variant", "default_print_profile", "default_filament_profile", "default_sla_material_profile" }) diff.erase(std::remove(diff.begin(), diff.end(), key), diff.end()); // Preset with the same name as stored inside the config exists. return diff.empty(); @@ -479,7 +563,8 @@ Preset& PresetCollection::load_external_preset( cfg.apply_only(config, cfg.keys(), true); // Is there a preset already loaded with the name stored inside the config? std::deque::iterator it = this->find_preset_internal(original_name); - if (it != m_presets.end() && it->name == original_name && profile_print_params_same(it->config, cfg)) { + bool found = it != m_presets.end() && it->name == original_name; + if (found && profile_print_params_same(it->config, cfg)) { // The preset exists and it matches the values stored inside config. if (select) this->select_preset(it - m_presets.begin()); @@ -487,7 +572,7 @@ Preset& PresetCollection::load_external_preset( } // Update the "inherits" field. std::string &inherits = Preset::inherits(cfg); - if (it != m_presets.end() && inherits.empty()) { + if (found && inherits.empty()) { // There is a profile with the same name already loaded. Should we update the "inherits" field? if (it->vendor == nullptr) inherits = it->inherits(); @@ -649,11 +734,11 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl // Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible. size_t PresetCollection::first_visible_idx() const { - size_t idx = m_default_suppressed ? 1 : 0; + size_t idx = m_default_suppressed ? m_num_default_presets : 0; for (; idx < this->m_presets.size(); ++ idx) if (m_presets[idx].is_visible) break; - if (idx == this->m_presets.size()) + if (idx == m_presets.size()) idx = 0; return idx; } @@ -662,7 +747,7 @@ void PresetCollection::set_default_suppressed(bool default_suppressed) { if (m_default_suppressed != default_suppressed) { m_default_suppressed = default_suppressed; - m_presets.front().is_visible = ! default_suppressed || (m_presets.size() > 1 && m_idx_selected > 0); + m_presets.front().is_visible = ! default_suppressed || (m_presets.size() > m_num_default_presets && m_idx_selected > 0); } } @@ -670,15 +755,16 @@ size_t PresetCollection::update_compatible_with_printer_internal(const Preset &a { DynamicPrintConfig config; config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name)); - config.set_key_value("num_extruders", new ConfigOptionInt( - (int)static_cast(active_printer.config.option("nozzle_diameter"))->values.size())); - for (size_t idx_preset = 1; idx_preset < m_presets.size(); ++ idx_preset) { + const ConfigOption *opt = active_printer.config.option("nozzle_diameter"); + if (opt) + config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast(opt)->values.size())); + for (size_t idx_preset = m_num_default_presets; idx_preset < m_presets.size(); ++ idx_preset) { bool selected = idx_preset == m_idx_selected; Preset &preset_selected = m_presets[idx_preset]; Preset &preset_edited = selected ? m_edited_preset : preset_selected; if (! preset_edited.update_compatible_with_printer(active_printer, &config) && selected && unselect_if_incompatible) - m_idx_selected = (size_t)-1; + m_idx_selected = -1; if (selected) preset_selected.is_compatible = preset_edited.is_compatible; } @@ -695,7 +781,7 @@ size_t PresetCollection::update_compatible_with_printer_internal(const Preset &a // Update the wxChoice UI component from this list of presets. // Hide the -void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) +void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui) { if (ui == nullptr) return; @@ -712,8 +798,8 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) std::map nonsys_presets; wxString selected = ""; if (!this->m_presets.front().is_visible) - ui->Append("------- " +_(L("System presets")) + " -------", wxNullBitmap); - for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) { + ui->set_label_marker(ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap)); + for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) { const Preset &preset = this->m_presets[i]; if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected)) continue; @@ -739,7 +825,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) bmp = m_bitmap_cache->insert(bitmap_key, bmps); } - if (preset.is_default || preset.is_system){ + if (preset.is_default || preset.is_system) { ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); if (i == m_idx_selected) @@ -751,12 +837,12 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) if (i == m_idx_selected) selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); } - if (preset.is_default) - ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap); + if (i + 1 == m_num_default_presets) + ui->set_label_marker(ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap)); } if (!nonsys_presets.empty()) { - ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap); + ui->set_label_marker(ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap)); for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { ui->Append(it->first, *it->second); if (it->first == selected) @@ -781,7 +867,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati wxString selected = ""; if (!this->m_presets.front().is_visible) ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap); - for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) { + for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) { const Preset &preset = this->m_presets[i]; if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected)) continue; @@ -799,7 +885,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati bmp = m_bitmap_cache->insert(bitmap_key, bmps); } - if (preset.is_default || preset.is_system){ + if (preset.is_default || preset.is_system) { ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); if (i == m_idx_selected) @@ -811,7 +897,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati if (i == m_idx_selected) selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); } - if (preset.is_default) + if (i + 1 == m_num_default_presets) ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap); } if (!nonsys_presets.empty()) @@ -844,7 +930,8 @@ bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui) std::string old_label = ui->GetString(ui_id).utf8_str().data(); std::string preset_name = Preset::remove_suffix_modified(old_label); const Preset *preset = this->find_preset(preset_name, false); - assert(preset != nullptr); +// The old_label could be the "----- system presets ------" or the "------- user presets --------" separator. +// assert(preset != nullptr); if (preset != nullptr) { std::string new_label = preset->is_dirty ? preset->name + g_suffix_modified : preset->name; if (old_label != new_label) @@ -859,11 +946,11 @@ bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui) return was_dirty != is_dirty; } -std::vector PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type /*= false*/) +std::vector PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare /*= false*/) { std::vector changed; if (edited != nullptr && reference != nullptr) { - changed = is_printer_type ? + changed = deep_compare ? reference->config.deep_diff(edited->config) : reference->config.diff(edited->config); // The "compatible_printers" option key is handled differently from the others: @@ -903,7 +990,7 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b idx = it - m_presets.begin(); else { // Find the first visible preset. - for (size_t i = m_default_suppressed ? 1 : 0; i < m_presets.size(); ++ i) + for (size_t i = m_default_suppressed ? m_num_default_presets : 0; i < m_presets.size(); ++ i) if (m_presets[i].is_visible) { idx = i; break; @@ -945,7 +1032,7 @@ std::vector PresetCollection::merge_presets(PresetCollection &&othe if (preset.is_default || preset.is_external) continue; Preset key(m_type, preset.name); - auto it = std::lower_bound(m_presets.begin() + 1, m_presets.end(), key); + auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key); if (it == m_presets.end() || it->name != preset.name) { if (preset.vendor != nullptr) { // Re-assign a pointer to the vendor structure in the new PresetBundle. @@ -977,4 +1064,10 @@ std::string PresetCollection::path_from_name(const std::string &new_name) const return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string(); } +const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const +{ + const ConfigOptionEnumGeneric *opt_printer_technology = config.opt("printer_technology"); + return this->default_preset((opt_printer_technology == nullptr || opt_printer_technology->value == ptFFF) ? 0 : 1); +} + } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp similarity index 83% rename from xs/src/slic3r/GUI/Preset.hpp rename to src/slic3r/GUI/Preset.hpp index 0d00cae485..fc459d8dd1 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -22,6 +22,7 @@ class PresetBundle; namespace GUI { class BitmapCache; + class PresetComboBox; } enum ConfigFileType @@ -52,6 +53,7 @@ public: PrinterModel() {} std::string id; std::string name; + PrinterTechnology technology; std::vector variants; PrinterVariant* variant(const std::string &name) { for (auto &v : this->variants) @@ -82,7 +84,9 @@ public: { TYPE_INVALID, TYPE_PRINT, + TYPE_SLA_PRINT, TYPE_FILAMENT, + TYPE_SLA_MATERIAL, TYPE_PRINTER, }; @@ -122,10 +126,6 @@ public: // Configuration data, loaded from a file, or set from the defaults. DynamicPrintConfig config; - // Load this profile for the following keys only. - // Throws std::runtime_error in case the file cannot be read. - DynamicPrintConfig& load(const std::vector &keys); - void save(); // Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty. @@ -149,6 +149,10 @@ public: std::string& compatible_printers_condition() { return Preset::compatible_printers_condition(this->config); } const std::string& compatible_printers_condition() const { return Preset::compatible_printers_condition(const_cast(this)->config); } + static PrinterTechnology& printer_technology(DynamicPrintConfig &cfg) { return cfg.option>("printer_technology", true)->value; } + PrinterTechnology& printer_technology() { return Preset::printer_technology(this->config); } + const PrinterTechnology& printer_technology() const { return Preset::printer_technology(const_cast(this)->config); } + // Mark this preset as compatible if it is compatible with active_printer. bool update_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config); @@ -167,12 +171,19 @@ public: static const std::vector& printer_options(); // Nozzle options of the printer options. static const std::vector& nozzle_options(); - static void update_suffix_modified(); + + static const std::vector& sla_printer_options(); + static const std::vector& sla_material_options(); + static const std::vector& sla_print_options(); + + static void update_suffix_modified(); + static void normalize(DynamicPrintConfig &config); + // Report configuration fields, which are misplaced into a wrong group, remove them from the config. + static std::string remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config); protected: friend class PresetCollection; friend class PresetBundle; - static void normalize(DynamicPrintConfig &config); // Resize the extruder specific vectors () static void set_num_extruders(DynamicPrintConfig &config, unsigned int n); static const std::string& suffix_modified(); @@ -184,15 +195,17 @@ class PresetCollection { public: // Initialize the PresetCollection with the "- default -" preset. - PresetCollection(Preset::Type type, const std::vector &keys); + PresetCollection(Preset::Type type, const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name = "- default -"); ~PresetCollection(); typedef std::deque::iterator Iterator; typedef std::deque::const_iterator ConstIterator; - Iterator begin() { return m_presets.begin() + 1; } - ConstIterator begin() const { return m_presets.begin() + 1; } - Iterator end() { return m_presets.end(); } - ConstIterator end() const { return m_presets.end(); } + Iterator begin() { return m_presets.begin() + m_num_default_presets; } + ConstIterator begin() const { return m_presets.cbegin() + m_num_default_presets; } + ConstIterator cbegin() const { return m_presets.cbegin() + m_num_default_presets; } + Iterator end() { return m_presets.end(); } + ConstIterator end() const { return m_presets.cend(); } + ConstIterator cend() const { return m_presets.cend(); } void reset(bool delete_files); @@ -200,6 +213,9 @@ public: std::string name() const; const std::deque& operator()() const { return m_presets; } + // Add default preset at the start of the collection, increment the m_default_preset counter. + void add_default_preset(const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name); + // Load ini files of the particular type from the provided directory path. void load_presets(const std::string &dir_path, const std::string &subdir); @@ -247,6 +263,8 @@ public: Preset& get_selected_preset() { return m_presets[m_idx_selected]; } const Preset& get_selected_preset() const { return m_presets[m_idx_selected]; } int get_selected_idx() const { return m_idx_selected; } + // Returns the name of the selected preset, or an empty string if no preset is selected. + std::string get_selected_preset_name() const { return (m_idx_selected == -1) ? std::string() : this->get_selected_preset().name; } // For the current edited preset, return the parent preset if there is one. // If there is no parent preset, nullptr is returned. // The parent preset may be a system preset or a user preset, which will be @@ -264,8 +282,9 @@ public: static const std::string& get_suffix_modified(); // Return a preset possibly with modifications. - Preset& default_preset() { return m_presets.front(); } - const Preset& default_preset() const { return m_presets.front(); } + Preset& default_preset(size_t idx = 0) { assert(idx < m_num_default_presets); return m_presets[idx]; } + const Preset& default_preset(size_t idx = 0) const { assert(idx < m_num_default_presets); return m_presets[idx]; } + virtual const Preset& default_preset_for(const DynamicPrintConfig & /* config */) const { return this->default_preset(); } // Return a preset by an index. If the preset is active, a temporary copy is returned. Preset& preset(size_t idx) { return (int(idx) == m_idx_selected) ? m_edited_preset : m_presets[idx]; } const Preset& preset(size_t idx) const { return const_cast(this)->preset(idx); } @@ -283,7 +302,7 @@ public: template size_t first_compatible_idx(PreferedCondition prefered_condition) const { - size_t i = m_default_suppressed ? 1 : 0; + size_t i = m_default_suppressed ? m_num_default_presets : 0; size_t n = this->m_presets.size(); size_t i_compatible = n; for (; i < n; ++ i) @@ -309,7 +328,8 @@ public: const Preset& first_compatible() const { return this->preset(this->first_compatible_idx()); } // Return number of presets including the "- default -" preset. - size_t size() const { return this->m_presets.size(); } + size_t size() const { return m_presets.size(); } + bool has_defaults_only() const { return m_presets.size() <= m_num_default_presets; } // For Print / Filament presets, disable those, which are not compatible with the printer. template @@ -327,11 +347,11 @@ public: // Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ. bool current_is_dirty() const { return ! this->current_dirty_options().empty(); } // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. - std::vector current_dirty_options(const bool is_printer_type = false) const - { return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), is_printer_type); } + std::vector current_dirty_options(const bool deep_compare = false) const + { return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), deep_compare); } // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. - std::vector current_different_from_parent_options(const bool is_printer_type = false) const - { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), is_printer_type); } + std::vector current_different_from_parent_options(const bool deep_compare = false) const + { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); } // Update the choice UI from the list of presets. // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. @@ -340,7 +360,7 @@ public: // Update the choice UI from the list of presets. // Only the compatible presets are shown. // If an incompatible preset is selected, it is shown as well. - void update_platter_ui(wxBitmapComboBox *ui); + void update_platter_ui(GUI::PresetComboBox *ui); // Update a dirty floag of the current preset, update the labels of the UI component accordingly. // Return true if the dirty flag changed. @@ -374,8 +394,16 @@ private: std::deque::iterator find_preset_internal(const std::string &name) { Preset key(m_type, name); - auto it = std::lower_bound(m_presets.begin() + 1, m_presets.end(), key); - return ((it == m_presets.end() || it->name != name) && m_presets.front().name == name) ? m_presets.begin() : it; + auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key); + if (it == m_presets.end() || it->name != name) { + // Preset has not been not found in the sorted list of non-default presets. Try the defaults. + for (size_t i = 0; i < m_num_default_presets; ++ i) + if (m_presets[i].name == name) { + it = m_presets.begin() + i; + break; + } + } + return it; } std::deque::const_iterator find_preset_internal(const std::string &name) const { return const_cast(this)->find_preset_internal(name); } @@ -395,7 +423,8 @@ private: // Selected preset. int m_idx_selected; // Is the "- default -" preset suppressed? - bool m_default_suppressed = true; + bool m_default_suppressed = true; + size_t m_num_default_presets = 0; // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items of a Platter. // These bitmaps are not owned by PresetCollection, but by a PresetBundle. const wxBitmap *m_bitmap_compatible = nullptr; @@ -415,6 +444,16 @@ private: friend class PresetBundle; }; +// Printer supports the FFF and SLA technologies, with different set of configuration values, +// therefore this PresetCollection needs to handle two defaults. +class PrinterPresetCollection : public PresetCollection +{ +public: + PrinterPresetCollection(Preset::Type type, const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name = "- default -") : + PresetCollection(type, keys, defaults, default_name) {} + const Preset& default_preset_for(const DynamicPrintConfig &config) const override; +}; + } // namespace Slic3r #endif /* slic3r_Preset_hpp_ */ diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp similarity index 68% rename from xs/src/slic3r/GUI/PresetBundle.cpp rename to src/slic3r/GUI/PresetBundle.cpp index e7ae4ebd9e..55729d7e4e 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -3,6 +3,7 @@ #include "PresetBundle.hpp" #include "BitmapCache.hpp" +#include "Plater.hpp" #include #include @@ -35,14 +36,17 @@ namespace Slic3r { static std::vector s_project_options { + "colorprint_heights", "wiping_volumes_extruders", "wiping_volumes_matrix" }; PresetBundle::PresetBundle() : - prints(Preset::TYPE_PRINT, Preset::print_options()), - filaments(Preset::TYPE_FILAMENT, Preset::filament_options()), - printers(Preset::TYPE_PRINTER, Preset::printer_options()), + prints(Preset::TYPE_PRINT, Preset::print_options(), static_cast(FullPrintConfig::defaults())), + filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast(FullPrintConfig::defaults())), + sla_materials(Preset::TYPE_SLA_MATERIAL, Preset::sla_material_options(), static_cast(SLAFullPrintConfig::defaults())), + sla_prints(Preset::TYPE_SLA_PRINT, Preset::sla_print_options(), static_cast(SLAFullPrintConfig::defaults())), + printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast(FullPrintConfig::defaults()), "- default FFF -"), m_bitmapCompatible(new wxBitmap), m_bitmapIncompatible(new wxBitmap), m_bitmapLock(new wxBitmap), @@ -69,24 +73,49 @@ PresetBundle::PresetBundle() : this->filaments.default_preset().compatible_printers_condition(); this->filaments.default_preset().inherits(); - this->printers.default_preset().config.optptr("printer_settings_id", true); - this->printers.default_preset().config.optptr("printer_vendor", true); - this->printers.default_preset().config.optptr("printer_model", true); - this->printers.default_preset().config.optptr("printer_variant", true); - this->printers.default_preset().config.optptr("default_print_profile", true); - this->printers.default_preset().config.option("default_filament_profile", true)->values = { "" }; - this->printers.default_preset().inherits(); + this->sla_materials.default_preset().config.optptr("sla_material_settings_id", true); + this->sla_materials.default_preset().compatible_printers_condition(); + this->sla_materials.default_preset().inherits(); + + this->sla_prints.default_preset().config.optptr("sla_print_settings_id", true); + this->sla_prints.default_preset().compatible_printers_condition(); + this->sla_prints.default_preset().inherits(); + + this->printers.add_default_preset(Preset::sla_printer_options(), static_cast(SLAFullPrintConfig::defaults()), "- default SLA -"); + this->printers.preset(1).printer_technology() = ptSLA; + for (size_t i = 0; i < 2; ++ i) { + // The following ugly switch is to avoid printers.preset(0) to return the edited instance, as the 0th default is the current one. + Preset &preset = this->printers.default_preset(i); + preset.config.optptr("printer_settings_id", true); + preset.config.optptr("printer_vendor", true); + preset.config.optptr("printer_model", true); + preset.config.optptr("printer_variant", true); + if (i == 0) { + preset.config.optptr("default_print_profile", true); + preset.config.option("default_filament_profile", true)->values = { "" }; + } + else { + preset.config.optptr("default_sla_material_profile", true); + preset.config.optptr("default_sla_print_profile", true); + } + // default_sla_material_profile + preset.inherits(); + } // Load the default preset bitmaps. - this->prints .load_bitmap_default("cog.png"); - this->filaments.load_bitmap_default("spool.png"); - this->printers .load_bitmap_default("printer_empty.png"); + this->prints .load_bitmap_default("cog.png"); + this->sla_prints .load_bitmap_default("package_green.png"); + this->filaments .load_bitmap_default("spool.png"); + this->sla_materials.load_bitmap_default("package_green.png"); + this->printers .load_bitmap_default("printer_empty.png"); this->load_compatible_bitmaps(); // Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above. - this->prints .select_preset(0); - this->filaments.select_preset(0); - this->printers .select_preset(0); + this->prints .select_preset(0); + this->sla_prints .select_preset(0); + this->filaments .select_preset(0); + this->sla_materials.select_preset(0); + this->printers .select_preset(0); this->project_config.apply_only(FullPrintConfig::defaults(), s_project_options); } @@ -113,13 +142,17 @@ void PresetBundle::reset(bool delete_files) { // Clear the existing presets, delete their respective files. this->vendors.clear(); - this->prints .reset(delete_files); - this->filaments.reset(delete_files); - this->printers .reset(delete_files); + this->prints .reset(delete_files); + this->sla_prints .reset(delete_files); + this->filaments .reset(delete_files); + this->sla_materials.reset(delete_files); + this->printers .reset(delete_files); this->filament_presets.clear(); - this->filament_presets.emplace_back(this->filaments.get_selected_preset().name); + this->filament_presets.emplace_back(this->filaments.get_selected_preset_name()); this->obsolete_presets.prints.clear(); + this->obsolete_presets.sla_prints.clear(); this->obsolete_presets.filaments.clear(); + this->obsolete_presets.sla_materials.clear(); this->obsolete_presets.printers.clear(); } @@ -135,11 +168,15 @@ void PresetBundle::setup_directories() data_dir / "presets", data_dir / "presets" / "print", data_dir / "presets" / "filament", + data_dir / "presets" / "sla_print", + data_dir / "presets" / "sla_material", data_dir / "presets" / "printer" #else // Store the print/filament/printer presets at the same location as the upstream Slic3r. data_dir / "print", data_dir / "filament", + data_dir / "sla_print", + data_dir / "sla_material", data_dir / "printer" #endif }; @@ -170,11 +207,21 @@ void PresetBundle::load_presets(const AppConfig &config) } catch (const std::runtime_error &err) { errors_cummulative += err.what(); } + try { + this->sla_prints.load_presets(dir_user_presets, "sla_print"); + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + } try { this->filaments.load_presets(dir_user_presets, "filament"); } catch (const std::runtime_error &err) { errors_cummulative += err.what(); } + try { + this->sla_materials.load_presets(dir_user_presets, "sla_material"); + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + } try { this->printers.load_presets(dir_user_presets, "printer"); } catch (const std::runtime_error &err) { @@ -238,13 +285,19 @@ std::string PresetBundle::load_system_presets() std::vector PresetBundle::merge_presets(PresetBundle &&other) { this->vendors.insert(other.vendors.begin(), other.vendors.end()); - std::vector duplicate_prints = this->prints .merge_presets(std::move(other.prints), this->vendors); - std::vector duplicate_filaments = this->filaments.merge_presets(std::move(other.filaments), this->vendors); - std::vector duplicate_printers = this->printers .merge_presets(std::move(other.printers), this->vendors); - append(this->obsolete_presets.prints, std::move(other.obsolete_presets.prints)); - append(this->obsolete_presets.filaments, std::move(other.obsolete_presets.filaments)); - append(this->obsolete_presets.printers, std::move(other.obsolete_presets.printers)); + std::vector duplicate_prints = this->prints .merge_presets(std::move(other.prints), this->vendors); + std::vector duplicate_sla_prints = this->sla_prints .merge_presets(std::move(other.sla_prints), this->vendors); + std::vector duplicate_filaments = this->filaments .merge_presets(std::move(other.filaments), this->vendors); + std::vector duplicate_sla_materials = this->sla_materials.merge_presets(std::move(other.sla_materials), this->vendors); + std::vector duplicate_printers = this->printers .merge_presets(std::move(other.printers), this->vendors); + append(this->obsolete_presets.prints, std::move(other.obsolete_presets.prints)); + append(this->obsolete_presets.sla_prints, std::move(other.obsolete_presets.sla_prints)); + append(this->obsolete_presets.filaments, std::move(other.obsolete_presets.filaments)); + append(this->obsolete_presets.sla_materials, std::move(other.obsolete_presets.sla_materials)); + append(this->obsolete_presets.printers, std::move(other.obsolete_presets.printers)); + append(duplicate_prints, std::move(duplicate_sla_prints)); append(duplicate_prints, std::move(duplicate_filaments)); + append(duplicate_prints, std::move(duplicate_sla_materials)); append(duplicate_prints, std::move(duplicate_printers)); return duplicate_prints; } @@ -275,31 +328,45 @@ void PresetBundle::load_selections(const AppConfig &config) this->load_installed_printers(config); // Parse the initial print / filament / printer profile names. - std::string initial_print_profile_name = remove_ini_suffix(config.get("presets", "print")); - std::string initial_filament_profile_name = remove_ini_suffix(config.get("presets", "filament")); - std::string initial_printer_profile_name = remove_ini_suffix(config.get("presets", "printer")); + std::string initial_print_profile_name = remove_ini_suffix(config.get("presets", "print")); + std::string initial_sla_print_profile_name = remove_ini_suffix(config.get("presets", "sla_print")); + std::string initial_filament_profile_name = remove_ini_suffix(config.get("presets", "filament")); + std::string initial_sla_material_profile_name = remove_ini_suffix(config.get("presets", "sla_material")); + std::string initial_printer_profile_name = remove_ini_suffix(config.get("presets", "printer")); // Activate print / filament / printer profiles from the config. // If the printer profile enumerated by the config are not visible, select an alternate preset. // Do not select alternate profiles for the print / filament profiles as those presets // will be selected by the following call of this->update_compatible_with_printer(true). - prints.select_preset_by_name_strict(initial_print_profile_name); - filaments.select_preset_by_name_strict(initial_filament_profile_name); printers.select_preset_by_name(initial_printer_profile_name, true); - - // Load the names of the other filament profiles selected for a multi-material printer. - auto *nozzle_diameter = dynamic_cast(printers.get_selected_preset().config.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); - this->filament_presets = { initial_filament_profile_name }; - for (unsigned int i = 1; i < (unsigned int)num_extruders; ++ i) { - char name[64]; - sprintf(name, "filament_%d", i); - if (! config.has("presets", name)) - break; - this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name))); + PrinterTechnology printer_technology = printers.get_selected_preset().printer_technology(); + if (printer_technology == ptFFF) { + prints.select_preset_by_name_strict(initial_print_profile_name); + filaments.select_preset_by_name_strict(initial_filament_profile_name); + sla_prints.select_preset_by_name(initial_sla_material_profile_name, true); + sla_materials.select_preset_by_name(initial_sla_material_profile_name, true); + } else { + prints.select_preset_by_name(initial_print_profile_name, true); + filaments.select_preset_by_name(initial_filament_profile_name, true); + sla_prints.select_preset_by_name_strict(initial_sla_material_profile_name); + sla_materials.select_preset_by_name_strict(initial_sla_material_profile_name); + } + + if (printers.get_selected_preset().printer_technology() == ptFFF) { + // Load the names of the other filament profiles selected for a multi-material printer. + auto *nozzle_diameter = dynamic_cast(printers.get_selected_preset().config.option("nozzle_diameter")); + size_t num_extruders = nozzle_diameter->values.size(); + this->filament_presets = { initial_filament_profile_name }; + for (unsigned int i = 1; i < (unsigned int)num_extruders; ++i) { + char name[64]; + sprintf(name, "filament_%d", i); + if (!config.has("presets", name)) + break; + this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name))); + } + // Do not define the missing filaments, so that the update_compatible_with_printer() will use the preferred filaments. + this->filament_presets.resize(num_extruders, ""); } - // Do not define the missing filaments, so that the update_compatible_with_printer() will use the preferred filaments. - this->filament_presets.resize(num_extruders, ""); // Update visibility of presets based on their compatibility with the active printer. // Always try to select a compatible print and filament preset to the current printer preset, @@ -312,26 +379,36 @@ void PresetBundle::load_selections(const AppConfig &config) // Export selections (current print, current filaments, current printer) into config.ini void PresetBundle::export_selections(AppConfig &config) { - assert(filament_presets.size() >= 1); - assert(filament_presets.size() > 1 || filaments.get_selected_preset().name == filament_presets.front()); + assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() >= 1); + assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front()); config.clear_section("presets"); - config.set("presets", "print", prints.get_selected_preset().name); - config.set("presets", "filament", filament_presets.front()); + config.set("presets", "print", prints.get_selected_preset_name()); + config.set("presets", "filament", filament_presets.front()); for (int i = 1; i < filament_presets.size(); ++i) { char name[64]; sprintf(name, "filament_%d", i); config.set("presets", name, filament_presets[i]); } - config.set("presets", "printer", printers.get_selected_preset().name); + config.set("presets", "sla_print", sla_prints.get_selected_preset_name()); + config.set("presets", "sla_material", sla_materials.get_selected_preset_name()); + config.set("presets", "printer", printers.get_selected_preset_name()); } void PresetBundle::export_selections(PlaceholderParser &pp) { assert(filament_presets.size() >= 1); - assert(filament_presets.size() > 1 || filaments.get_selected_preset().name == filament_presets.front()); - pp.set("print_preset", prints.get_selected_preset().name); - pp.set("filament_preset", filament_presets); - pp.set("printer_preset", printers.get_selected_preset().name); + assert(filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front()); + switch (printers.get_edited_preset().printer_technology()) { + case ptFFF: + pp.set("print_preset", prints.get_selected_preset().name); + pp.set("filament_preset", filament_presets); + break; + case ptSLA: + pp.set("sla_print_preset", sla_prints.get_selected_preset().name); + pp.set("sla_material_preset", sla_materials.get_selected_preset().name); + break; + } + pp.set("printer_preset", printers.get_selected_preset().name); } bool PresetBundle::load_compatible_bitmaps() @@ -349,32 +426,47 @@ bool PresetBundle::load_compatible_bitmaps() bool loaded_lock_open = m_bitmapLockOpen->LoadFile( wxString::FromUTF8(Slic3r::var(path_bitmap_lock_open).c_str()), wxBITMAP_TYPE_PNG); if (loaded_compatible) { - prints .set_bitmap_compatible(m_bitmapCompatible); - filaments.set_bitmap_compatible(m_bitmapCompatible); + prints .set_bitmap_compatible(m_bitmapCompatible); + filaments .set_bitmap_compatible(m_bitmapCompatible); + sla_prints .set_bitmap_compatible(m_bitmapCompatible); + sla_materials.set_bitmap_compatible(m_bitmapCompatible); // printers .set_bitmap_compatible(m_bitmapCompatible); } if (loaded_incompatible) { - prints .set_bitmap_incompatible(m_bitmapIncompatible); - filaments.set_bitmap_incompatible(m_bitmapIncompatible); -// printers .set_bitmap_incompatible(m_bitmapIncompatible); + prints .set_bitmap_incompatible(m_bitmapIncompatible); + filaments .set_bitmap_incompatible(m_bitmapIncompatible); + sla_prints .set_bitmap_incompatible(m_bitmapIncompatible); + sla_materials.set_bitmap_incompatible(m_bitmapIncompatible); +// printers .set_bitmap_incompatible(m_bitmapIncompatible); } if (loaded_lock) { - prints .set_bitmap_lock(m_bitmapLock); - filaments.set_bitmap_lock(m_bitmapLock); - printers .set_bitmap_lock(m_bitmapLock); + prints .set_bitmap_lock(m_bitmapLock); + filaments .set_bitmap_lock(m_bitmapLock); + sla_prints .set_bitmap_lock(m_bitmapLock); + sla_materials.set_bitmap_lock(m_bitmapLock); + printers .set_bitmap_lock(m_bitmapLock); } if (loaded_lock_open) { - prints .set_bitmap_lock_open(m_bitmapLock); - filaments.set_bitmap_lock_open(m_bitmapLock); - printers .set_bitmap_lock_open(m_bitmapLock); + prints .set_bitmap_lock_open(m_bitmapLock); + filaments .set_bitmap_lock_open(m_bitmapLock); + sla_prints .set_bitmap_lock_open(m_bitmapLock); + sla_materials.set_bitmap_lock_open(m_bitmapLock); + printers .set_bitmap_lock_open(m_bitmapLock); } return loaded_compatible && loaded_incompatible && loaded_lock && loaded_lock_open; } DynamicPrintConfig PresetBundle::full_config() const +{ + return (this->printers.get_edited_preset().printer_technology() == ptFFF) ? + this->full_fff_config() : + this->full_sla_config(); +} + +DynamicPrintConfig PresetBundle::full_fff_config() const { DynamicPrintConfig out; - out.apply(FullPrintConfig()); + out.apply(FullPrintConfig::defaults()); out.apply(this->prints.get_edited_preset().config); // Add the default filament preset to have the "filament_preset_id" defined. out.apply(this->filaments.default_preset().config); @@ -463,9 +555,59 @@ DynamicPrintConfig PresetBundle::full_config() const }; add_if_some_non_empty(std::move(compatible_printers_condition), "compatible_printers_condition_cummulative"); add_if_some_non_empty(std::move(inherits), "inherits_cummulative"); + + out.option("printer_technology", true)->value = ptFFF; return out; } +DynamicPrintConfig PresetBundle::full_sla_config() const +{ + DynamicPrintConfig out; + out.apply(SLAFullPrintConfig::defaults()); + out.apply(this->sla_prints.get_edited_preset().config); + out.apply(this->sla_materials.get_edited_preset().config); + out.apply(this->printers.get_edited_preset().config); + // There are no project configuration values as of now, the project_config is reserved for FFF printers. +// out.apply(this->project_config); + + // Collect the "compatible_printers_condition" and "inherits" values over all presets (sla_prints, sla_materials, printers) into a single vector. + std::vector compatible_printers_condition; + std::vector inherits; + compatible_printers_condition.emplace_back(this->/*prints*/sla_prints.get_edited_preset().compatible_printers_condition()); + inherits .emplace_back(this->/*prints*/sla_prints.get_edited_preset().inherits()); + compatible_printers_condition.emplace_back(this->/*prints*/sla_materials.get_edited_preset().compatible_printers_condition()); + inherits .emplace_back(this->/*prints*/sla_materials.get_edited_preset().inherits()); + inherits .emplace_back(this->printers.get_edited_preset().inherits()); + + // These two value types clash between the print and filament profiles. They should be renamed. + out.erase("compatible_printers"); + out.erase("compatible_printers_condition"); + out.erase("inherits"); + + out.option("sla_print_settings_id", true)->value = this->sla_prints.get_selected_preset().name; + out.option("sla_material_settings_id", true)->value = this->sla_materials.get_selected_preset().name; + out.option("printer_settings_id", true)->value = this->printers.get_selected_preset().name; + + // Serialize the collected "compatible_printers_condition" and "inherits" fields. + // There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored. + // The vector will not be stored if all fields are empty strings. + auto add_if_some_non_empty = [&out](std::vector &&values, const std::string &key) { + bool nonempty = false; + for (const std::string &v : values) + if (! v.empty()) { + nonempty = true; + break; + } + if (nonempty) + out.set_key_value(key, new ConfigOptionStrings(std::move(values))); + }; + add_if_some_non_empty(std::move(compatible_printers_condition), "compatible_printers_condition_cummulative"); + add_if_some_non_empty(std::move(inherits), "inherits_cummulative"); + + out.option("printer_technology", true)->value = ptSLA; + return out; +} + // Load an external config file containing the print, filament and printer presets. // Instead of a config file, a G-code may be loaded containing the full set of parameters. // In the future the configuration will likely be read from an AMF file as well. @@ -515,21 +657,11 @@ void PresetBundle::load_config_file(const std::string &path) } } -void PresetBundle::load_config_string(const char* str, const char* source_filename) -{ - if (str != nullptr) - { - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); - config.load_from_gcode_string(str); - Preset::normalize(config); - load_config_file_config((source_filename == nullptr) ? "" : source_filename, true, std::move(config)); - } -} - // Load a config file from a boost property_tree. This is a private method called from load_config_file. void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config) { + PrinterTechnology printer_technology = Preset::printer_technology(config); + // The "compatible_printers" field should not have been exported into a config.ini or a G-code anyway, // but some of the alpha versions of Slic3r did. { @@ -541,8 +673,10 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool } } - size_t num_extruders = std::min(config.option("nozzle_diameter" )->values.size(), - config.option("filament_diameter")->values.size()); + size_t num_extruders = (printer_technology == ptFFF) ? + std::min(config.option("nozzle_diameter" )->values.size(), + config.option("filament_diameter")->values.size()) : + 0; // Make a copy of the "compatible_printers_condition_cummulative" and "inherits_cummulative" vectors, which // accumulate values over all presets (print, filaments, printers). // These values will be distributed into their particular presets when loading. @@ -553,7 +687,8 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool compatible_printers_condition_values.resize(num_extruders + 2, std::string()); inherits_values.resize(num_extruders + 2, std::string()); // The "default_filament_profile" will be later extracted into the printer profile. - config.option("default_filament_profile", true)->values.resize(num_extruders, std::string()); + if (printer_technology == ptFFF) + config.option("default_filament_profile", true)->values.resize(num_extruders, std::string()); // 1) Create a name from the file name. // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles. @@ -561,82 +696,86 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // 2) If the loading succeeded, split and load the config into print / filament / printer settings. // First load the print and printer presets. + + // #ys_FIXME_SLA_PRINT for (size_t i_group = 0; i_group < 2; ++ i_group) { - PresetCollection &presets = (i_group == 0) ? this->prints : this->printers; + PresetCollection &presets = (i_group == 0) ? ((printer_technology == ptFFF) ? this->prints : this->sla_materials) : this->printers; // Split the "compatible_printers_condition" and "inherits" values one by one from a single vector to the print & printer profiles. size_t idx = (i_group == 0) ? 0 : num_extruders + 1; inherits = inherits_values[idx]; compatible_printers_condition = compatible_printers_condition_values[idx]; if (is_external) presets.load_external_preset(name_or_path, name, - config.opt_string((i_group == 0) ? "print_settings_id" : "printer_settings_id", true), + config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_settings_id") : "printer_settings_id", true), config); else presets.load_preset(presets.path_from_name(name), name, config).save(); } - // 3) Now load the filaments. If there are multiple filament presets, split them and load them. - auto old_filament_profile_names = config.option("filament_settings_id", true); - old_filament_profile_names->values.resize(num_extruders, std::string()); + if (Preset::printer_technology(config) == ptFFF) { + // 3) Now load the filaments. If there are multiple filament presets, split them and load them. + auto old_filament_profile_names = config.option("filament_settings_id", true); + old_filament_profile_names->values.resize(num_extruders, std::string()); - if (num_extruders <= 1) { - // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets. - inherits = inherits_values[1]; - compatible_printers_condition = compatible_printers_condition_values[1]; - if (is_external) - this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config); - else - this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save(); - this->filament_presets.clear(); - this->filament_presets.emplace_back(name); - } else { - // Split the filament presets, load each of them separately. - std::vector configs(num_extruders, this->filaments.default_preset().config); - // loop through options and scatter them into configs. - for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { - const ConfigOption *other_opt = config.option(key); - if (other_opt == nullptr) - continue; - if (other_opt->is_scalar()) { - for (size_t i = 0; i < configs.size(); ++ i) - configs[i].option(key, false)->set(other_opt); - } else if (key != "compatible_printers") { - for (size_t i = 0; i < configs.size(); ++ i) - static_cast(configs[i].option(key, false))->set_at(other_opt, 0, i); - } - } - // Load the configs into this->filaments and make them active. - this->filament_presets.clear(); - for (size_t i = 0; i < configs.size(); ++ i) { - DynamicPrintConfig &cfg = configs[i]; + if (num_extruders <= 1) { // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets. - cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1]; - cfg.opt_string("inherits", true) = inherits_values[i + 1]; - // Load all filament presets, but only select the first one in the preset dialog. - Preset *loaded = nullptr; + inherits = inherits_values[1]; + compatible_printers_condition = compatible_printers_condition_values[1]; if (is_external) - loaded = &this->filaments.load_external_preset(name_or_path, name, - (i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "", - std::move(cfg), i == 0); - else { - // Used by the config wizard when creating a custom setup. - // Therefore this block should only be called for a single extruder. - char suffix[64]; - if (i == 0) - suffix[0] = 0; - else - sprintf(suffix, "%d", i); - std::string new_name = name + suffix; - loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name), - new_name, std::move(cfg), i == 0); - loaded->save(); + this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config); + else + this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save(); + this->filament_presets.clear(); + this->filament_presets.emplace_back(name); + } else { + // Split the filament presets, load each of them separately. + std::vector configs(num_extruders, this->filaments.default_preset().config); + // loop through options and scatter them into configs. + for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { + const ConfigOption *other_opt = config.option(key); + if (other_opt == nullptr) + continue; + if (other_opt->is_scalar()) { + for (size_t i = 0; i < configs.size(); ++ i) + configs[i].option(key, false)->set(other_opt); + } else if (key != "compatible_printers") { + for (size_t i = 0; i < configs.size(); ++ i) + static_cast(configs[i].option(key, false))->set_at(other_opt, 0, i); + } + } + // Load the configs into this->filaments and make them active. + this->filament_presets.clear(); + for (size_t i = 0; i < configs.size(); ++ i) { + DynamicPrintConfig &cfg = configs[i]; + // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets. + cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1]; + cfg.opt_string("inherits", true) = inherits_values[i + 1]; + // Load all filament presets, but only select the first one in the preset dialog. + Preset *loaded = nullptr; + if (is_external) + loaded = &this->filaments.load_external_preset(name_or_path, name, + (i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "", + std::move(cfg), i == 0); + else { + // Used by the config wizard when creating a custom setup. + // Therefore this block should only be called for a single extruder. + char suffix[64]; + if (i == 0) + suffix[0] = 0; + else + sprintf(suffix, "%d", i); + std::string new_name = name + suffix; + loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name), + new_name, std::move(cfg), i == 0); + loaded->save(); + } + this->filament_presets.emplace_back(loaded->name); } - this->filament_presets.emplace_back(loaded->name); } - } - // 4) Load the project config values (the per extruder wipe matrix etc). - this->project_config.apply_only(config, s_project_options); + // 4) Load the project config values (the per extruder wipe matrix etc). + this->project_config.apply_only(config, s_project_options); + } this->update_compatible_with_printer(false); } @@ -691,9 +830,11 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const collection_dst.load_preset(path, preset_name_dst, std::move(preset_src->config), activate).is_external = true; return preset_name_dst; }; - load_one(this->prints, tmp_bundle.prints, tmp_bundle.prints .get_selected_preset().name, true); - load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments.get_selected_preset().name, true); - load_one(this->printers, tmp_bundle.printers, tmp_bundle.printers .get_selected_preset().name, true); + load_one(this->prints, tmp_bundle.prints, tmp_bundle.prints .get_selected_preset().name, true); + load_one(this->sla_prints, tmp_bundle.sla_prints, tmp_bundle.sla_prints .get_selected_preset().name, true); + load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments .get_selected_preset().name, true); + load_one(this->sla_materials, tmp_bundle.sla_materials, tmp_bundle.sla_materials.get_selected_preset().name, true); + load_one(this->printers, tmp_bundle.printers, tmp_bundle.printers .get_selected_preset().name, true); this->update_multi_material_filament_presets(); for (size_t i = 1; i < std::min(tmp_bundle.filament_presets.size(), this->filament_presets.size()); ++ i) this->filament_presets[i] = load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filament_presets[i], false); @@ -817,6 +958,8 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree) { flatten_configbundle_hierarchy(tree, "print"); flatten_configbundle_hierarchy(tree, "filament"); + flatten_configbundle_hierarchy(tree, "sla_print"); + flatten_configbundle_hierarchy(tree, "sla_material"); flatten_configbundle_hierarchy(tree, "printer"); } @@ -853,9 +996,13 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla // Parse the obsolete preset names, to be deleted when upgrading from the old configuration structure. std::vector loaded_prints; std::vector loaded_filaments; + std::vector loaded_sla_prints; + std::vector loaded_sla_materials; std::vector loaded_printers; std::string active_print; std::vector active_filaments; + std::string active_sla_print; + std::string active_sla_material; std::string active_printer; size_t presets_loaded = 0; for (const auto §ion : tree) { @@ -870,6 +1017,14 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla presets = &this->filaments; loaded = &loaded_filaments; preset_name = section.first.substr(9); + } else if (boost::starts_with(section.first, "sla_print:")) { + presets = &this->sla_prints; + loaded = &loaded_sla_prints; + preset_name = section.first.substr(10); + } else if (boost::starts_with(section.first, "sla_material:")) { + presets = &this->sla_materials; + loaded = &loaded_sla_materials; + preset_name = section.first.substr(13); } else if (boost::starts_with(section.first, "printer:")) { presets = &this->printers; loaded = &loaded_printers; @@ -886,6 +1041,10 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla active_filaments.resize(idx + 1, std::string()); active_filaments[idx] = kvp.second.data(); } + } else if (kvp.first == "sla_print") { + active_sla_print = kvp.second.data(); + } else if (kvp.first == "sla_material") { + active_sla_material = kvp.second.data(); } else if (kvp.first == "printer") { active_printer = kvp.second.data(); } @@ -899,6 +1058,10 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla dst = &this->obsolete_presets.prints; else if (kvp.first == "filament") dst = &this->obsolete_presets.filaments; + else if (kvp.first == "sla_print") + dst = &this->obsolete_presets.sla_prints; + else if (kvp.first == "sla_material") + dst = &this->obsolete_presets.sla_materials; else if (kvp.first == "printer") dst = &this->obsolete_presets.printers; if (dst) @@ -915,16 +1078,28 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla continue; if (presets != nullptr) { // Load the print, filament or printer preset. - const DynamicPrintConfig &default_config = presets->default_preset().config; - DynamicPrintConfig config(default_config); - for (auto &kvp : section.second) - config.set_deserialize(kvp.first, kvp.second.data()); - // Report configuration fields, which are misplaced into a wrong group. - std::string incorrect_keys; - if (config.remove_keys_not_in(default_config, incorrect_keys) > 0) - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << - section.first << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed"; + const DynamicPrintConfig *default_config = nullptr; + DynamicPrintConfig config; + if (presets == &this->printers) { + // Select the default config based on the printer_technology field extracted from kvp. + DynamicPrintConfig config_src; + for (auto &kvp : section.second) + config_src.set_deserialize(kvp.first, kvp.second.data()); + default_config = &presets->default_preset_for(config_src).config; + config = *default_config; + config.apply(config_src); + } else { + default_config = &presets->default_preset().config; + config = *default_config; + for (auto &kvp : section.second) + config.set_deserialize(kvp.first, kvp.second.data()); + } Preset::normalize(config); + // Report configuration fields, which are misplaced into a wrong group. + std::string incorrect_keys = Preset::remove_invalid_keys(config, *default_config); + if (! incorrect_keys.empty()) + BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << + section.first << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed"; if ((flags & LOAD_CFGBNDLE_SYSTEM) && presets == &printers) { // Filter out printer presets, which are not mentioned in the vendor profile. // These presets are considered not installed. @@ -987,6 +1162,10 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla if ((flags & LOAD_CFGBNDLE_SYSTEM) == 0) { if (! active_print.empty()) prints.select_preset_by_name(active_print, true); + if (! active_sla_print.empty()) + sla_materials.select_preset_by_name(active_sla_print, true); + if (! active_sla_material.empty()) + sla_materials.select_preset_by_name(active_sla_material, true); if (! active_printer.empty()) printers.select_preset_by_name(active_printer, true); // Activate the first filament preset. @@ -1003,6 +1182,9 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla void PresetBundle::update_multi_material_filament_presets() { + if (printers.get_edited_preset().printer_technology() != ptFFF) + return; + // Verify and select the filament presets. auto *nozzle_diameter = static_cast(printers.get_edited_preset().config.option("nozzle_diameter")); size_t num_extruders = nozzle_diameter->values.size(); @@ -1043,35 +1225,58 @@ void PresetBundle::update_multi_material_filament_presets() void PresetBundle::update_compatible_with_printer(bool select_other_if_incompatible) { const Preset &printer_preset = this->printers.get_edited_preset(); - const std::string &prefered_print_profile = printer_preset.config.opt_string("default_print_profile"); - const std::vector &prefered_filament_profiles = printer_preset.config.option("default_filament_profile")->values; - prefered_print_profile.empty() ? - this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : - this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible, - [&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; }); - prefered_filament_profiles.empty() ? - this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : - this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible, - [&prefered_filament_profiles](const std::string& profile_name) - { return std::find(prefered_filament_profiles.begin(), prefered_filament_profiles.end(), profile_name) != prefered_filament_profiles.end(); }); - if (select_other_if_incompatible) { - // Verify validity of the current filament presets. - this->filament_presets.front() = this->filaments.get_edited_preset().name; - for (size_t idx = 1; idx < this->filament_presets.size(); ++ idx) { - std::string &filament_name = this->filament_presets[idx]; - Preset *preset = this->filaments.find_preset(filament_name, false); - if (preset == nullptr || ! preset->is_compatible) { - // Pick a compatible profile. If there are prefered_filament_profiles, use them. - if (prefered_filament_profiles.empty()) - filament_name = this->filaments.first_compatible().name; - else { - const std::string &preferred = (idx < prefered_filament_profiles.size()) ? - prefered_filament_profiles[idx] : prefered_filament_profiles.front(); - filament_name = this->filaments.first_compatible( - [&preferred](const std::string& profile_name){ return profile_name == preferred; }).name; + + switch (printers.get_edited_preset().printer_technology()) { + case ptFFF: + { + const std::string &prefered_print_profile = printer_preset.config.opt_string("default_print_profile"); + const std::vector &prefered_filament_profiles = printer_preset.config.option("default_filament_profile")->values; + prefered_print_profile.empty() ? + this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : + this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible, + [&prefered_print_profile](const std::string& profile_name) { return profile_name == prefered_print_profile; }); + prefered_filament_profiles.empty() ? + this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : + this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible, + [&prefered_filament_profiles](const std::string& profile_name) + { return std::find(prefered_filament_profiles.begin(), prefered_filament_profiles.end(), profile_name) != prefered_filament_profiles.end(); }); + if (select_other_if_incompatible) { + // Verify validity of the current filament presets. + this->filament_presets.front() = this->filaments.get_edited_preset().name; + for (size_t idx = 1; idx < this->filament_presets.size(); ++ idx) { + std::string &filament_name = this->filament_presets[idx]; + Preset *preset = this->filaments.find_preset(filament_name, false); + if (preset == nullptr || ! preset->is_compatible) { + // Pick a compatible profile. If there are prefered_filament_profiles, use them. + if (prefered_filament_profiles.empty()) + filament_name = this->filaments.first_compatible().name; + else { + const std::string &preferred = (idx < prefered_filament_profiles.size()) ? + prefered_filament_profiles[idx] : prefered_filament_profiles.front(); + filament_name = this->filaments.first_compatible( + [&preferred](const std::string& profile_name) { return profile_name == preferred; }).name; + } } } } + break; + } + case ptSLA: + { + const std::string &prefered_sla_material_profile = printer_preset.config.opt_string("default_sla_material_profile"); + prefered_sla_material_profile.empty() ? + this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : + this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible, + [&prefered_sla_material_profile](const std::string& profile_name){ return profile_name == prefered_sla_material_profile; }); + + const std::string &prefered_sla_print_profile = printer_preset.config.opt_string("default_sla_print_profile"); + prefered_sla_print_profile.empty() ? + this->sla_prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : + this->sla_prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible, + [&prefered_sla_print_profile](const std::string& profile_name){ return profile_name == prefered_sla_print_profile; }); + + break; + } } } @@ -1084,6 +1289,8 @@ void PresetBundle::export_configbundle(const std::string &path) //, const Dynami c << "# " << Slic3r::header_slic3r_generated() << std::endl; // Export the print, filament and printer profiles. + + // #ys_FIXME_SLA_PRINT for (size_t i_group = 0; i_group < 3; ++ i_group) { const PresetCollection &presets = (i_group == 0) ? this->prints : (i_group == 1) ? this->filaments : this->printers; for (const Preset &preset : presets()) { @@ -1099,6 +1306,8 @@ void PresetBundle::export_configbundle(const std::string &path) //, const Dynami // Export the names of the active presets. c << std::endl << "[presets]" << std::endl; c << "print = " << this->prints.get_selected_preset().name << std::endl; + c << "sla_print = " << this->sla_prints.get_selected_preset().name << std::endl; + c << "sla_material = " << this->sla_materials.get_selected_preset().name << std::endl; c << "printer = " << this->printers.get_selected_preset().name << std::endl; for (size_t i = 0; i < this->filament_presets.size(); ++ i) { char suffix[64]; @@ -1156,9 +1365,9 @@ bool PresetBundle::parse_color(const std::string &scolor, unsigned char *rgb_out return true; } -void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui) +void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui) { - if (ui == nullptr) + if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA) return; unsigned char rgb[3]; @@ -1179,7 +1388,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma std::map nonsys_presets; wxString selected_str = ""; if (!this->filaments().front().is_visible) - ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap); + ui->set_label_marker(ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap)); for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) { const Preset &preset = this->filaments.preset(i); bool selected = this->filament_presets[idx_extruder] == preset.name; @@ -1218,7 +1427,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma bitmap = m_bitmapCache->insert(bitmap_key, bmps); } - if (preset.is_default || preset.is_system){ + if (preset.is_default || preset.is_system) { ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap); if (selected) @@ -1232,12 +1441,12 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma selected_str = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); } if (preset.is_default) - ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap); + ui->set_label_marker(ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap)); } if (!nonsys_presets.empty()) { - ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap); + ui->set_label_marker(ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap)); for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { ui->Append(it->first, *it->second); if (it->first == selected_str) @@ -1253,6 +1462,8 @@ void PresetBundle::set_default_suppressed(bool default_suppressed) { prints.set_default_suppressed(default_suppressed); filaments.set_default_suppressed(default_suppressed); + sla_prints.set_default_suppressed(default_suppressed); + sla_materials.set_default_suppressed(default_suppressed); printers.set_default_suppressed(default_suppressed); } diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp similarity index 90% rename from xs/src/slic3r/GUI/PresetBundle.hpp rename to src/slic3r/GUI/PresetBundle.hpp index a5c5682f94..06ae950940 100644 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -39,8 +39,10 @@ public: void export_selections(PlaceholderParser &pp); PresetCollection prints; + PresetCollection sla_prints; PresetCollection filaments; - PresetCollection printers; + PresetCollection sla_materials; + PrinterPresetCollection printers; // Filament preset names for a multi-extruder or multi-material print. // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size() std::vector filament_presets; @@ -56,13 +58,15 @@ public: struct ObsoletePresets { std::vector prints; + std::vector sla_prints; std::vector filaments; + std::vector sla_materials; std::vector printers; }; ObsoletePresets obsolete_presets; bool has_defauls_only() const - { return prints.size() <= 1 && filaments.size() <= 1 && printers.size() <= 1; } + { return prints.has_defaults_only() && filaments.has_defaults_only() && printers.has_defaults_only(); } DynamicPrintConfig full_config() const; @@ -71,17 +75,17 @@ public: void load_config(const std::string &name, DynamicPrintConfig config) { this->load_config_file_config(name, false, std::move(config)); } + // Load configuration that comes from a model file containing configuration, such as 3MF et al. + // This method is called by the Plater. + void load_config_model(const std::string &name, DynamicPrintConfig config) + { this->load_config_file_config(name, true, std::move(config)); } + // Load an external config file containing the print, filament and printer presets. // Instead of a config file, a G-code may be loaded containing the full set of parameters. // In the future the configuration will likely be read from an AMF file as well. // If the file is loaded successfully, its print / filament / printer profiles will be activated. void load_config_file(const std::string &path); - // Load an external config source containing the print, filament and printer presets. - // The given string must contain the full set of parameters (same as those exported to gcode). - // If the string is parsed successfully, its print / filament / printer profiles will be activated. - void load_config_string(const char* str, const char* source_filename = nullptr); - // Load a config bundle file, into presets and store the loaded presets into separate files // of the local configuration directory. // Load settings into the provided settings instance. @@ -103,7 +107,7 @@ public: void export_configbundle(const std::string &path); // , const DynamicPrintConfig &settings); // Update a filament selection combo box on the platter for an idx_extruder. - void update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); + void update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui); // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); @@ -146,6 +150,9 @@ private: void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); bool load_compatible_bitmaps(); + DynamicPrintConfig full_fff_config() const; + DynamicPrintConfig full_sla_config() const; + // Indicator, that the preset is compatible with the selected printer. wxBitmap *m_bitmapCompatible; // Indicator, that the preset is NOT compatible with the selected printer. diff --git a/xs/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp similarity index 100% rename from xs/src/slic3r/GUI/PresetHints.cpp rename to src/slic3r/GUI/PresetHints.cpp diff --git a/xs/src/slic3r/GUI/PresetHints.hpp b/src/slic3r/GUI/PresetHints.hpp similarity index 100% rename from xs/src/slic3r/GUI/PresetHints.hpp rename to src/slic3r/GUI/PresetHints.hpp diff --git a/xs/src/slic3r/ProgressIndicator.hpp b/src/slic3r/GUI/ProgressIndicator.hpp similarity index 70% rename from xs/src/slic3r/ProgressIndicator.hpp rename to src/slic3r/GUI/ProgressIndicator.hpp index 1b064077e8..280ba63ac8 100644 --- a/xs/src/slic3r/ProgressIndicator.hpp +++ b/src/slic3r/GUI/ProgressIndicator.hpp @@ -14,31 +14,31 @@ public: using CancelFn = std::function; // Cancel function signature. private: - float state_ = .0f, max_ = 1.f, step_; - CancelFn cancelfunc_ = [](){}; + float m_state = .0f, m_max = 1.f, m_step; + CancelFn m_cancelfunc = [](){}; public: inline virtual ~ProgressIndicator() {} /// Get the maximum of the progress range. - float max() const { return max_; } + float max() const { return m_max; } /// Get the current progress state - float state() const { return state_; } + float state() const { return m_state; } /// Set the maximum of the progress range - virtual void max(float maxval) { max_ = maxval; } + virtual void max(float maxval) { m_max = maxval; } /// Set the current state of the progress. - virtual void state(float val) { state_ = val; } + virtual void state(float val) { m_state = val; } /** * @brief Number of states int the progress. Can be used instead of giving a * maximum value. */ virtual void states(unsigned statenum) { - step_ = max_ / statenum; + m_step = m_max / statenum; } /// Message shown on the next status update. @@ -51,7 +51,13 @@ public: virtual void message_fmt(const std::string& fmt, ...); /// Set up a cancel callback for the operation if feasible. - virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; } + virtual void on_cancel(CancelFn func = CancelFn()) { m_cancelfunc = func; } + + /** + * Explicitly shut down the progress indicator and call the associated + * callback. + */ + virtual void cancel() { m_cancelfunc(); } /// Convenience function to call message and status update in one function. void update(float st, const std::string& msg) { diff --git a/src/slic3r/GUI/ProgressStatusBar.cpp b/src/slic3r/GUI/ProgressStatusBar.cpp new file mode 100644 index 0000000000..44a7b06c51 --- /dev/null +++ b/src/slic3r/GUI/ProgressStatusBar.cpp @@ -0,0 +1,162 @@ +#include "ProgressStatusBar.hpp" + +#include +#include +#include +#include +#include +#include "GUI_App.hpp" + +#include + +namespace Slic3r { + +ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id): + self(new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe, + id == -1? wxID_ANY : id)), + m_timer(new wxTimer(self)), + m_prog (new wxGauge(self, + wxGA_HORIZONTAL, + 100, + wxDefaultPosition, + wxDefaultSize)), + m_cancelbutton(new wxButton(self, + -1, + "Cancel", + wxDefaultPosition, + wxDefaultSize)) +{ + m_prog->Hide(); + m_cancelbutton->Hide(); + + self->SetFieldsCount(3); + int w[] = {-1, 150, 155}; + self->SetStatusWidths(3, w); + + self->Bind(wxEVT_TIMER, [this](const wxTimerEvent&) { + if (m_prog->IsShown()) m_timer->Stop(); + if(is_busy()) m_prog->Pulse(); + }); + + self->Bind(wxEVT_SIZE, [this](wxSizeEvent& event){ + wxRect rect; + self->GetFieldRect(1, rect); + auto offset = 0; + m_cancelbutton->Move(rect.GetX() + offset, rect.GetY() + offset); + m_cancelbutton->SetSize(rect.GetWidth() - offset, rect.GetHeight()); + + self->GetFieldRect(2, rect); + m_prog->Move(rect.GetX() + offset, rect.GetY() + offset); + m_prog->SetSize(rect.GetWidth() - offset, rect.GetHeight()); + + event.Skip(); + }); + + m_cancelbutton->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { + if (m_cancel_cb) + m_cancel_cb(); + m_cancelbutton->Hide(); + }); +} + +ProgressStatusBar::~ProgressStatusBar() { + if(m_timer->IsRunning()) m_timer->Stop(); +} + +int ProgressStatusBar::get_progress() const +{ + return m_prog->GetValue(); +} + +void ProgressStatusBar::set_progress(int val) +{ + if(!m_prog->IsShown()) show_progress(true); + + if(val == m_prog->GetRange()) { + m_prog->SetValue(0); + show_progress(false); + } else { + m_prog->SetValue(val); + } +} + +int ProgressStatusBar::get_range() const +{ + return m_prog->GetRange(); +} + +void ProgressStatusBar::set_range(int val) +{ + if(val != m_prog->GetRange()) { + m_prog->SetRange(val); + } +} + +void ProgressStatusBar::show_progress(bool show) +{ + m_prog->Show(show); + m_prog->Pulse(); +} + +void ProgressStatusBar::start_busy(int rate) +{ + m_busy = true; + show_progress(true); + if (!m_timer->IsRunning()) { + m_timer->Start(rate); + } +} + +void ProgressStatusBar::stop_busy() +{ + m_timer->Stop(); + show_progress(false); + m_prog->SetValue(0); + m_busy = false; +} + +void ProgressStatusBar::set_cancel_callback(ProgressStatusBar::CancelFn ccb) { + m_cancel_cb = ccb; + if(ccb) m_cancelbutton->Show(); + else m_cancelbutton->Hide(); +} + +void ProgressStatusBar::run(int rate) +{ + if(!m_timer->IsRunning()) { + m_timer->Start(rate); + } +} + +void ProgressStatusBar::embed(wxFrame *frame) +{ + wxFrame* mf = frame ? frame : GUI::wxGetApp().mainframe; + mf->SetStatusBar(self); +} + +void ProgressStatusBar::set_status_text(const wxString& txt) +{ + self->SetStatusText(txt); +} + +void ProgressStatusBar::set_status_text(const std::string& txt) +{ + this->set_status_text(txt.c_str()); +} + +void ProgressStatusBar::set_status_text(const char *txt) +{ + this->set_status_text(wxString::FromUTF8(txt)); +} + +void ProgressStatusBar::show_cancel_button() +{ + m_cancelbutton->Show(); +} + +void ProgressStatusBar::hide_cancel_button() +{ + m_cancelbutton->Hide(); +} + +} diff --git a/src/slic3r/GUI/ProgressStatusBar.hpp b/src/slic3r/GUI/ProgressStatusBar.hpp new file mode 100644 index 0000000000..f33a70ed30 --- /dev/null +++ b/src/slic3r/GUI/ProgressStatusBar.hpp @@ -0,0 +1,68 @@ +#ifndef PROGRESSSTATUSBAR_HPP +#define PROGRESSSTATUSBAR_HPP + +#include +#include + +class wxTimer; +class wxGauge; +class wxButton; +class wxTimerEvent; +class wxStatusBar; +class wxWindow; +class wxFrame; +class wxString; + +namespace Slic3r { + +/** + * @brief The ProgressStatusBar class is the widgets occupying the lower area + * of the Slicer main window. It consists of a message area to the left and a + * progress indication area to the right with an optional cancel button. + */ +class ProgressStatusBar +{ + wxStatusBar *self; // we cheat! It should be the base class but: perl! + wxTimer *m_timer; + wxGauge *m_prog; + wxButton *m_cancelbutton; +public: + + /// Cancel callback function type + using CancelFn = std::function; + + ProgressStatusBar(wxWindow *parent = nullptr, int id = -1); + ~ProgressStatusBar(); + + int get_progress() const; + void set_progress(int); + int get_range() const; + void set_range(int = 100); + void show_progress(bool); + void start_busy(int = 100); + void stop_busy(); + inline bool is_busy() const { return m_busy; } + void set_cancel_callback(CancelFn = CancelFn()); + inline void reset_cancel_callback() { set_cancel_callback(); } + void run(int rate); + void embed(wxFrame *frame = nullptr); + void set_status_text(const wxString& txt); + void set_status_text(const std::string& txt); + void set_status_text(const char *txt); + + // Temporary methods to satisfy Perl side + void show_cancel_button(); + void hide_cancel_button(); + +private: + bool m_busy = false; + CancelFn m_cancel_cb; +}; + +namespace GUI { + using Slic3r::ProgressStatusBar; +} + +} + +#endif // PROGRESSSTATUSBAR_HPP diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/src/slic3r/GUI/RammingChart.cpp similarity index 98% rename from xs/src/slic3r/GUI/RammingChart.cpp rename to src/slic3r/GUI/RammingChart.cpp index 2603a5eab8..8fadfa867e 100644 --- a/xs/src/slic3r/GUI/RammingChart.cpp +++ b/src/slic3r/GUI/RammingChart.cpp @@ -52,7 +52,7 @@ void Chart::draw() { // draw x-axis: float last_mark = -10000; - for (float math_x=int(visible_area.m_x*10)/10 ; math_x < (visible_area.m_x+visible_area.m_width) ; math_x+=0.1) { + for (float math_x=int(visible_area.m_x*10)/10 ; math_x < (visible_area.m_x+visible_area.m_width) ; math_x+=0.1f) { int x = math_to_screen(wxPoint2DDouble(math_x,visible_area.m_y)).x; int y = m_rect.GetBottom(); if (x-last_mark < 50) continue; @@ -261,7 +261,7 @@ std::vector Chart::get_ramming_speed(float sampling) const { std::vector> Chart::get_buttons() const { std::vector> buttons_out; for (const auto& button : m_buttons) - buttons_out.push_back(std::make_pair(button.get_pos().m_x,button.get_pos().m_y)); + buttons_out.push_back(std::make_pair(float(button.get_pos().m_x),float(button.get_pos().m_y))); return buttons_out; } diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/src/slic3r/GUI/RammingChart.hpp similarity index 100% rename from xs/src/slic3r/GUI/RammingChart.hpp rename to src/slic3r/GUI/RammingChart.hpp diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp new file mode 100644 index 0000000000..007b6e6e40 --- /dev/null +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -0,0 +1,148 @@ +#include "SysInfoDialog.hpp" +// #include "AboutDialog.hpp" + +#include +#include + +// #include "../../libslic3r/Utils.hpp" +#include "3DScene.hpp" +#include "GUI.hpp" + +namespace Slic3r { +namespace GUI { + +std::string get_main_info(bool format_as_html) +{ + std::stringstream out; + + std::string b_start = format_as_html ? "" : ""; + std::string b_end = format_as_html ? "" : ""; + std::string line_end = format_as_html ? "
" : "\n"; + + if (!format_as_html) + out << b_start << SLIC3R_FORK_NAME << b_end << line_end; + out << b_start << "Version: " << b_end << SLIC3R_VERSION << line_end; + out << b_start << "Build: " << b_end << SLIC3R_BUILD << line_end; + out << line_end; + out << b_start << "Operating System: " << b_end << wxPlatformInfo::Get().GetOperatingSystemFamilyName() << line_end; + out << b_start << "System Architecture: " << b_end << wxPlatformInfo::Get().GetArchName() << line_end; + out << b_start << +#if defined _WIN32 + "Windows Version: " +#else + // Hopefully some kind of unix / linux. + "System Version: " +#endif + << b_end << wxPlatformInfo::Get().GetOperatingSystemDescription() << line_end; + + return out.str(); +} + +SysInfoDialog::SysInfoDialog() + : wxDialog(NULL, wxID_ANY, _(L("Slic3r Prusa Edition - System Information")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) +{ + wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + SetBackgroundColour(bgr_clr); + wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL); + hsizer->SetMinSize(wxSize(600, -1)); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(hsizer, 0, wxEXPAND | wxALL, 10); + + // logo + wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_128px.png")), wxBITMAP_TYPE_PNG); + auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(logo_bmp)); + hsizer->Add(logo, 0, wxEXPAND | wxTOP | wxBOTTOM, 15); + + wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); + hsizer->Add(vsizer, 1, wxEXPAND|wxLEFT, 20); + + // title + { + wxStaticText* title = new wxStaticText(this, wxID_ANY, SLIC3R_FORK_NAME, wxDefaultPosition, wxDefaultSize); + wxFont title_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + title_font.SetWeight(wxFONTWEIGHT_BOLD); + title_font.SetFamily(wxFONTFAMILY_ROMAN); + title_font.SetPointSize(14); + title->SetFont(title_font); + vsizer->Add(title, 0, wxALIGN_LEFT | wxTOP, 10); + } + + // main_info_text + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); + auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); + + const int fs = font.GetPointSize() - 1; + int size[] = { static_cast(fs*1.5), static_cast(fs*1.4), static_cast(fs*1.3), fs, fs, fs, fs }; + + wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER); + { + html->SetFonts(font.GetFaceName(), font.GetFaceName(), size); + html->SetBorders(2); + const auto text = wxString::Format( + "" + "" + "" + "%s" + "" + "" + "", bgr_clr_str, text_clr_str, text_clr_str, + get_main_info(true)); + html->SetPage(text); + vsizer->Add(html, 1, wxEXPAND); + } + + + // opengl_info + wxHtmlWindow* opengl_info_html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); + { + opengl_info_html->SetMinSize(wxSize(-1, 200)); + opengl_info_html->SetFonts(font.GetFaceName(), font.GetFaceName(), size); + opengl_info_html->SetBorders(10); + const auto text = wxString::Format( + "" + "" + "" + "%s" + "" + "" + "", bgr_clr_str, text_clr_str, text_clr_str, + _3DScene::get_gl_info(true, true)); + opengl_info_html->SetPage(text); + main_sizer->Add(opengl_info_html, 1, wxEXPAND | wxBOTTOM, 15); + } + + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK); + auto btn_copy_to_clipboard = new wxButton(this, wxID_ANY, "Copy to Clipboard", wxDefaultPosition, wxDefaultSize); + buttons->Insert(0, btn_copy_to_clipboard, 0, wxLEFT, 5); + btn_copy_to_clipboard->Bind(wxEVT_BUTTON, &SysInfoDialog::onCopyToClipboard, this); + + this->SetEscapeId(wxID_CLOSE); + this->Bind(wxEVT_BUTTON, &SysInfoDialog::onCloseDialog, this, wxID_OK); + main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3); + + this->Bind(wxEVT_LEFT_DOWN, &SysInfoDialog::onCloseDialog, this); + logo->Bind(wxEVT_LEFT_DOWN, &SysInfoDialog::onCloseDialog, this); + + SetSizer(main_sizer); + main_sizer->SetSizeHints(this); +} + +void SysInfoDialog::onCopyToClipboard(wxEvent &) +{ + wxTheClipboard->Open(); + const auto text = get_main_info(false)+"\n"+_3DScene::get_gl_info(false, true); + wxTheClipboard->SetData(new wxTextDataObject(text)); + wxTheClipboard->Close(); +} + +void SysInfoDialog::onCloseDialog(wxEvent &) +{ + this->EndModal(wxID_CLOSE); + this->Close(); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/SysInfoDialog.hpp b/src/slic3r/GUI/SysInfoDialog.hpp new file mode 100644 index 0000000000..ee1b85ce60 --- /dev/null +++ b/src/slic3r/GUI/SysInfoDialog.hpp @@ -0,0 +1,24 @@ +#ifndef slic3r_GUI_SysInfoDialog_hpp_ +#define slic3r_GUI_SysInfoDialog_hpp_ + +#include +#include + +namespace Slic3r { +namespace GUI { + +class SysInfoDialog : public wxDialog +{ + wxString text_info {wxEmptyString}; +public: + SysInfoDialog(); + +private: + void onCopyToClipboard(wxEvent &); + void onCloseDialog(wxEvent &); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/xs/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp similarity index 78% rename from xs/src/slic3r/GUI/Tab.cpp rename to src/slic3r/GUI/Tab.cpp index 9d265cfc4d..4c9e56bbd9 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -27,17 +27,30 @@ #include "wxExtensions.hpp" #include -#include +#include "GUI_App.hpp" namespace Slic3r { namespace GUI { -static wxString dots("…", wxConvUTF8); + +wxDEFINE_EVENT(EVT_TAB_VALUE_CHANGED, wxCommandEvent); +wxDEFINE_EVENT(EVT_TAB_PRESETS_CHANGED, SimpleEvent); + + +void Tab::set_type() +{ + if (m_name == "print") { m_type = Slic3r::Preset::TYPE_PRINT; } + else if (m_name == "sla_print") { m_type = Slic3r::Preset::TYPE_SLA_PRINT; } + else if (m_name == "filament") { m_type = Slic3r::Preset::TYPE_FILAMENT; } + else if (m_name == "sla_material") { m_type = Slic3r::Preset::TYPE_SLA_MATERIAL; } + else if (m_name == "printer") { m_type = Slic3r::Preset::TYPE_PRINTER; } + else { m_type = Slic3r::Preset::TYPE_INVALID; } +} // sub new -void Tab::create_preset_tab(PresetBundle *preset_bundle) +void Tab::create_preset_tab() { - m_preset_bundle = preset_bundle; + m_preset_bundle = wxGetApp().preset_bundle; // Vertical sizer to hold the choice menu and the rest of the page. #ifdef __WXOSX__ @@ -98,7 +111,7 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) "or click this button."))); // Determine the theme color of OS (dark or light) - auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + auto luma = wxGetApp().get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field. m_bmp_value_lock .LoadFile(from_u8(var("sys_lock.png")), wxBITMAP_TYPE_PNG); m_bmp_value_unlock .LoadFile(from_u8(var(luma >= 128 ? "sys_unlock.png" : "sys_unlock_grey.png")), wxBITMAP_TYPE_PNG); @@ -112,27 +125,27 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) set_tooltips_text(); m_undo_btn->SetBitmap(m_bmp_white_bullet); - m_undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(); })); + m_undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_roll_back_value(); })); m_undo_to_sys_btn->SetBitmap(m_bmp_white_bullet); - m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(true); })); + m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_roll_back_value(true); })); m_question_btn->SetBitmap(m_bmp_question); m_question_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { auto dlg = new ButtonsDescription(this, &m_icon_descriptions); - if (dlg->ShowModal() == wxID_OK){ + if (dlg->ShowModal() == wxID_OK) { // Colors for ui "decoration" - for (Tab *tab : get_tabs_list()){ - tab->m_sys_label_clr = get_label_clr_sys(); - tab->m_modified_label_clr = get_label_clr_modified(); + for (Tab *tab : wxGetApp().tabs_list) { + tab->m_sys_label_clr = wxGetApp().get_label_clr_sys(); + tab->m_modified_label_clr = wxGetApp().get_label_clr_modified(); tab->update_labels_colour(); } } })); // Colors for ui "decoration" - m_sys_label_clr = get_label_clr_sys(); - m_modified_label_clr = get_label_clr_modified(); - m_default_text_clr = get_label_clr_default(); + m_sys_label_clr = wxGetApp().get_label_clr_sys(); + m_modified_label_clr = wxGetApp().get_label_clr_modified(); + m_default_text_clr = wxGetApp().get_label_clr_default(); m_hsizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(m_hsizer, 0, wxBOTTOM, 3); @@ -148,7 +161,6 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL); m_hsizer->AddSpacer(32); m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL); -// m_hsizer->Add(m_cc_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); //Horizontal sizer to hold the tree and the selected page. m_hsizer = new wxBoxSizer(wxHORIZONTAL); @@ -173,7 +185,7 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, &Tab::OnTreeSelChange, this); m_treectrl->Bind(wxEVT_KEY_DOWN, &Tab::OnKeyDown, this); - m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e){ + m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { //! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, //! but the OSX version derived from wxOwnerDrawnCombo, instead of: //! select_preset(m_presets_choice->GetStringSelection().ToStdString()); @@ -181,11 +193,11 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) int selected_item = m_presets_choice->GetSelection(); if (m_selected_preset_item == selected_item && !m_presets->current_is_dirty()) return; - if (selected_item >= 0){ + if (selected_item >= 0) { std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data(); if (selected_string.find("-------") == 0 /*selected_string == "------- System presets -------" || - selected_string == "------- User presets -------"*/){ + selected_string == "------- User presets -------"*/) { m_presets_choice->SetSelection(m_selected_preset_item); return; } @@ -194,16 +206,18 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) } })); - m_btn_save_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e){ save_preset(); })); - m_btn_delete_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e){ delete_preset(); })); - m_btn_hide_incompatible_presets->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e){ + m_btn_save_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { save_preset(); })); + m_btn_delete_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { delete_preset(); })); + m_btn_hide_incompatible_presets->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { toggle_show_hide_incompatible(); })); // Initialize the DynamicPrintConfig by default keys/values. build(); rebuild_page_tree(); - update(); +// update(); + // Load the currently selected preset into the GUI, update the preset selection box. + load_current_preset(); } void Tab::load_initial_data() @@ -214,7 +228,7 @@ void Tab::load_initial_data() m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns; } -PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages/* = false*/) +Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages /*= false*/) { // Index of icon in an icon list $self->{icons}. auto icon_idx = 0; @@ -222,9 +236,9 @@ PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bo icon_idx = (m_icon_index.find(icon) == m_icon_index.end()) ? -1 : m_icon_index.at(icon); if (icon_idx == -1) { // Add a new icon to the icon list. - const auto img_icon = new wxIcon(from_u8(Slic3r::var(icon)), wxBITMAP_TYPE_PNG); - m_icons->Add(*img_icon); - icon_idx = ++m_icon_count; + wxIcon img_icon(from_u8(Slic3r::var(icon)), wxBITMAP_TYPE_PNG); + m_icons->Add(img_icon); + icon_idx = ++m_icon_count; m_icon_index[icon] = icon_idx; } } @@ -238,7 +252,8 @@ PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bo page->SetScrollbars(1, 1, 1, 1); page->Hide(); m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5); - if (!is_extruder_pages) + + if (!is_extruder_pages) m_pages.push_back(page); page->set_config(m_config); @@ -266,7 +281,7 @@ void Tab::update_labels_colour() const wxColour *color = &m_sys_label_clr; // value isn't equal to system value - if ((opt.second & osSystemValue) == 0){ + if ((opt.second & osSystemValue) == 0) { // value is equal to last saved if ((opt.second & osInitValue) != 0) color = &m_default_text_clr; @@ -289,7 +304,7 @@ void Tab::update_labels_colour() Thaw(); auto cur_item = m_treectrl->GetFirstVisibleItem(); - while (cur_item){ + while (cur_item) { auto title = m_treectrl->GetItemText(cur_item); for (auto page : m_pages) { @@ -313,10 +328,10 @@ void Tab::update_changed_ui() if (m_postpone_update_ui) return; - const bool is_printer_type = (name() == "printer"); - auto dirty_options = m_presets->current_dirty_options(is_printer_type); - auto nonsys_options = m_presets->current_different_from_parent_options(is_printer_type); - if (is_printer_type){ + const bool deep_compare = (m_name == "printer" || m_name == "sla_material"); + auto dirty_options = m_presets->current_dirty_options(deep_compare); + auto nonsys_options = m_presets->current_different_from_parent_options(deep_compare); + if (name() == "printer") { TabPrinter* tab = static_cast(this); if (tab->m_initial_extruders_count != tab->m_extruders_count) dirty_options.emplace_back("extruders_count"); @@ -345,7 +360,7 @@ void Tab::update_changed_ui() const wxString *tt = &m_tt_value_revert; // value isn't equal to system value - if ((opt.second & osSystemValue) == 0){ + if ((opt.second & osSystemValue) == 0) { is_nonsys_value = true; sys_icon = m_bmp_non_system; sys_tt = m_tt_non_system; @@ -397,7 +412,7 @@ void Tab::init_options_list() } template -void add_correct_opts_to_options_list(const std::string &opt_key, std::map& map, TabPrinter *tab, const int& value) +void add_correct_opts_to_options_list(const std::string &opt_key, std::map& map, Tab *tab, const int& value) { T *opt_cur = static_cast(tab->m_config->option(opt_key)); for (int i = 0; i < opt_cur->values.size(); i++) @@ -411,7 +426,7 @@ void TabPrinter::init_options_list() for (const auto opt_key : m_config->keys()) { - if (opt_key == "bed_shape"){ + if (opt_key == "bed_shape") { m_options_list.emplace(opt_key, m_opt_status_value); continue; } @@ -429,6 +444,30 @@ void TabPrinter::init_options_list() m_options_list.emplace("extruders_count", m_opt_status_value); } +void TabSLAMaterial::init_options_list() +{ + if (!m_options_list.empty()) + m_options_list.clear(); + + for (const auto opt_key : m_config->keys()) + { + if (opt_key == "compatible_printers") { + m_options_list.emplace(opt_key, m_opt_status_value); + continue; + } + switch (m_config->option(opt_key)->type()) + { + case coInts: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + case coBools: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + case coFloats: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + case coStrings: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + case coPercents:add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + case coPoints: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + default: m_options_list.emplace(opt_key, m_opt_status_value); break; + } + } +} + void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page) { auto opt = m_options_list.find(opt_key); @@ -439,22 +478,24 @@ void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool void Tab::update_changed_tree_ui() { auto cur_item = m_treectrl->GetFirstVisibleItem(); + if (!cur_item || !m_treectrl->IsVisible(cur_item)) + return; auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); - while (cur_item){ + while (cur_item) { auto title = m_treectrl->GetItemText(cur_item); for (auto page : m_pages) { - if (page->title() != title) + if (page->title() != title) continue; bool sys_page = true; bool modified_page = false; - if (title == _("General")){ + if (title == _("General")) { std::initializer_list optional_keys{ "extruders_count", "bed_shape" }; for (auto &opt_key : optional_keys) { get_sys_and_mod_flags(opt_key, sys_page, modified_page); } } - if (title == _("Dependencies")){ + if (title == _("Dependencies")) { if (name() != "printer") get_sys_and_mod_flags("compatible_printers", sys_page, modified_page); else { @@ -482,14 +523,14 @@ void Tab::update_changed_tree_ui() page->m_is_nonsys_values = !sys_page; page->m_is_modified_values = modified_page; - if (selection == title){ + if (selection == title) { m_is_nonsys_values = page->m_is_nonsys_values; m_is_modified_values = page->m_is_modified_values; } break; } - auto next_item = m_treectrl->GetNextVisible(cur_item); - cur_item = next_item; + auto next_item = m_treectrl->GetNextVisible(cur_item); + cur_item = next_item; } update_undo_buttons(); } @@ -520,20 +561,20 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); for (auto page : m_pages) if (page->title() == selection) { - for (auto group : page->m_optgroups){ - if (group->title == _("Capabilities")){ + for (auto group : page->m_optgroups) { + if (group->title == _("Capabilities")) { if ((m_options_list["extruders_count"] & os) == 0) to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count"); } - if (group->title == _("Size and coordinates")){ - if ((m_options_list["bed_shape"] & os) == 0){ + if (group->title == _("Size and coordinates")) { + if ((m_options_list["bed_shape"] & os) == 0) { to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape"); load_key_value("bed_shape", true/*some value*/, true); } } - if (group->title == _("Profile dependencies") && name() != "printer"){ - if ((m_options_list["compatible_printers"] & os) == 0){ + if (group->title == _("Profile dependencies") && name() != "printer") { + if ((m_options_list["compatible_printers"] & os) == 0) { to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers"); load_key_value("compatible_printers", true/*some value*/, true); @@ -557,18 +598,16 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) // Update the combo box label of the selected preset based on its "dirty" state, // comparing the selected preset config with $self->{config}. -void Tab::update_dirty(){ +void Tab::update_dirty() +{ m_presets->update_dirty_ui(m_presets_choice); on_presets_changed(); update_changed_ui(); -// update_dirty_presets(m_cc_presets_choice); } void Tab::update_tab_ui() { m_selected_preset_item = m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets); -// update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets); -// update_presetsctrl(m_presetctrl, m_show_incompatible_presets); } // Load a provied DynamicConfig into the tab, modifying the active preset. @@ -589,17 +628,37 @@ void Tab::load_config(const DynamicPrintConfig& config) } // Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. -void Tab::reload_config(){ +void Tab::reload_config() +{ Freeze(); for (auto page : m_pages) page->reload_config(); Thaw(); } +void Tab::update_visibility(ConfigOptionMode mode) +{ + Freeze(); + + for (auto page : m_pages) + page->update_visibility(mode); + update_page_tree_visibility(); + + m_hsizer->Layout(); + Refresh(); + + Thaw(); + + // to update tree items color + wxTheApp->CallAfter([this]() { + update_changed_tree_ui(); + }); +} + Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const { Field* field = nullptr; - for (auto page : m_pages){ + for (auto page : m_pages) { field = page->get_field(opt_key, opt_index); if (field != nullptr) return field; @@ -610,7 +669,7 @@ Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/ // Set a key/value pair on this page. Return true if the value has been modified. // Currently used for distributing extruders_count over preset pages of Slic3r::GUI::Tab::Printer // after a preset is loaded. -bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value){ +bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value) { bool changed = false; for(auto page: m_pages) { if (page->set_value(opt_key, value)) @@ -636,25 +695,25 @@ void Tab::load_key_value(const std::string& opt_key, const boost::any& value, bo update(); } -extern wxFrame *g_wxMainFrame; - void Tab::on_value_change(const std::string& opt_key, const boost::any& value) { - if (m_event_value_change > 0) { - wxCommandEvent event(m_event_value_change); - std::string str_out = opt_key + " " + m_name; - event.SetString(str_out); - if (opt_key == "extruders_count") - { - int val = boost::any_cast(value); - event.SetInt(val); - } - g_wxMainFrame->ProcessWindowEvent(event); + wxCommandEvent event(EVT_TAB_VALUE_CHANGED); + event.SetEventObject(this); + event.SetString(opt_key); + if (opt_key == "extruders_count") + { + int val = boost::any_cast(value); + event.SetInt(val); } + + wxPostEvent(this, event); + + + auto og_freq_chng_params = wxGetApp().sidebar().og_freq_chng_params(); if (opt_key == "fill_density") { - boost::any val = get_optgroup()->get_config_value(*m_config, opt_key); - get_optgroup()->set_value(opt_key, val); + boost::any val = og_freq_chng_params->get_config_value(*m_config, opt_key); + og_freq_chng_params->set_value(opt_key, val); } if (opt_key == "support_material" || opt_key == "support_material_buildplate_only") { @@ -663,12 +722,12 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) m_config->opt_bool("support_material_buildplate_only") ? _("Support on build plate only") : _("Everywhere"); - get_optgroup()->set_value("support", new_selection); + og_freq_chng_params->set_value("support", new_selection); } if (opt_key == "brim_width") { bool val = m_config->opt_float("brim_width") > 0.0 ? true : false; - get_optgroup()->set_value("brim", val); + og_freq_chng_params->set_value("brim", val); } if (opt_key == "wipe_tower" || opt_key == "single_extruder_multi_material" || opt_key == "extruders_count" ) @@ -677,18 +736,19 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) update(); } - // Show/hide the 'purging volumes' button void Tab::update_wiping_button_visibility() { + if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) + return; // ys_FIXME bool wipe_tower_enabled = dynamic_cast( (m_preset_bundle->prints.get_edited_preset().config ).option("wipe_tower"))->value; bool multiple_extruders = dynamic_cast((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1; bool single_extruder_mm = dynamic_cast( (m_preset_bundle->printers.get_edited_preset().config).option("single_extruder_multi_material"))->value; - if (wipe_tower_enabled && multiple_extruders && single_extruder_mm) - get_wiping_dialog_button()->Show(); - else get_wiping_dialog_button()->Hide(); - - (get_wiping_dialog_button()->GetParent())->Layout(); + auto wiping_dialog_button = wxGetApp().sidebar().get_wiping_dialog_button(); + if (wiping_dialog_button) { + wiping_dialog_button->Show(wipe_tower_enabled && multiple_extruders && single_extruder_mm); + wiping_dialog_button->GetParent()->Layout(); + } } @@ -699,11 +759,19 @@ void Tab::update_wiping_button_visibility() { // to uddate number of "filament" selection boxes when the number of extruders change. void Tab::on_presets_changed() { - if (m_event_presets_changed > 0) { - wxCommandEvent event(m_event_presets_changed); - event.SetString(m_name); - g_wxMainFrame->ProcessWindowEvent(event); - } + if (m_type == Slic3r::Preset::TYPE_PRINTER && !m_dependent_tabs.empty()) { + // Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. + for (auto t: m_dependent_tabs) + { + // If the printer tells us that the print or filament/sla_material preset has been switched or invalidated, + // refresh the print or filament/sla_material tab page. + wxGetApp().get_tab(t)->load_current_preset(); + } + } + + wxCommandEvent event(EVT_TAB_PRESETS_CHANGED); + event.SetEventObject(this); + wxPostEvent(this, event); update_preset_description_line(); } @@ -729,22 +797,41 @@ void Tab::update_preset_description_line() description_line += "\n\n" + _(L("Additional information:")) + "\n"; description_line += "\t" + _(L("vendor")) + ": " + (name()=="printer" ? "\n\t\t" : "") + parent->vendor->name + ", ver: " + parent->vendor->config_version.to_string(); - if (name() == "printer"){ - const std::string &printer_model = preset.config.opt_string("printer_model"); - const std::string &default_print_profile = preset.config.opt_string("default_print_profile"); - const std::vector &default_filament_profiles = preset.config.option("default_filament_profile")->values; - if (!printer_model.empty()) + if (name() == "printer") { + const std::string &printer_model = preset.config.opt_string("printer_model"); + if (! printer_model.empty()) description_line += "\n\n\t" + _(L("printer model")) + ": \n\t\t" + printer_model; - if (!default_print_profile.empty()) - description_line += "\n\n\t" + _(L("default print profile")) + ": \n\t\t" + default_print_profile; - if (!default_filament_profiles.empty()) + switch (preset.printer_technology()) { + case ptFFF: { - description_line += "\n\n\t" + _(L("default filament profile")) + ": \n\t\t"; - for (auto& profile : default_filament_profiles){ - if (&profile != &*default_filament_profiles.begin()) - description_line += ", "; - description_line += profile; + //FIXME add prefered_sla_material_profile for SLA + const std::string &default_print_profile = preset.config.opt_string("default_print_profile"); + const std::vector &default_filament_profiles = preset.config.option("default_filament_profile")->values; + if (!default_print_profile.empty()) + description_line += "\n\n\t" + _(L("default print profile")) + ": \n\t\t" + default_print_profile; + if (!default_filament_profiles.empty()) + { + description_line += "\n\n\t" + _(L("default filament profile")) + ": \n\t\t"; + for (auto& profile : default_filament_profiles) { + if (&profile != &*default_filament_profiles.begin()) + description_line += ", "; + description_line += profile; + } } + break; + } + case ptSLA: + { + //FIXME add prefered_sla_material_profile for SLA + const std::string &default_sla_material_profile = preset.config.opt_string("default_sla_material_profile"); + if (!default_sla_material_profile.empty()) + description_line += "\n\n\t" + _(L("default SLA material profile")) + ": \n\t\t" + default_sla_material_profile; + + const std::string &default_sla_print_profile = preset.config.opt_string("default_sla_print_profile"); + if (!default_sla_print_profile.empty()) + description_line += "\n\n\t" + _(L("default SLA print profile")) + ": \n\t\t" + default_sla_print_profile; + break; + } } } } @@ -754,18 +841,20 @@ void Tab::update_preset_description_line() void Tab::update_frequently_changed_parameters() { - boost::any value = get_optgroup()->get_config_value(*m_config, "fill_density"); - get_optgroup()->set_value("fill_density", value); + auto og_freq_chng_params = wxGetApp().sidebar().og_freq_chng_params(); + if (!og_freq_chng_params) return; + boost::any value = og_freq_chng_params->get_config_value(*m_config, "fill_density"); + og_freq_chng_params->set_value("fill_density", value); wxString new_selection = !m_config->opt_bool("support_material") ? _("None") : m_config->opt_bool("support_material_buildplate_only") ? _("Support on build plate only") : _("Everywhere"); - get_optgroup()->set_value("support", new_selection); + og_freq_chng_params->set_value("support", new_selection); bool val = m_config->opt_float("brim_width") > 0.0 ? true : false; - get_optgroup()->set_value("brim", val); + og_freq_chng_params->set_value("brim", val); update_wiping_button_visibility(); } @@ -982,8 +1071,8 @@ void TabPrint::build() page = add_options_page(_(L("Dependencies")), "wrench.png"); optgroup = page->new_optgroup(_(L("Profile dependencies"))); - line = { _(L("Compatible printers")), "" }; - line.widget = [this](wxWindow* parent){ + line = optgroup->create_single_option_line("compatible_printers");//{ _(L("Compatible printers")), "" }; + line.widget = [this](wxWindow* parent) { return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn); }; optgroup->append_line(line, &m_colored_Label); @@ -1001,13 +1090,17 @@ void TabPrint::build() } // Reload current config (aka presets->edited_preset->config) into the UI fields. -void TabPrint::reload_config(){ +void TabPrint::reload_config() +{ reload_compatible_printers_widget(); Tab::reload_config(); } void TabPrint::update() { + if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) + return; // ys_FIXME + Freeze(); double fill_density = m_config->option("fill_density")->value; @@ -1113,7 +1206,7 @@ void TabPrint::update() break; } } - if (!str_fill_pattern.empty()){ + if (!str_fill_pattern.empty()) { auto external_fill_pattern = m_config->def()->get("external_fill_pattern")->enum_values; bool correct_100p_fill = false; for (auto fill : external_fill_pattern) @@ -1123,7 +1216,7 @@ void TabPrint::update() } // get fill_pattern name from enum_labels for using this one at dialog_msg str_fill_pattern = m_config->def()->get("fill_pattern")->enum_labels[fill_pattern]; - if (!correct_100p_fill){ + if (!correct_100p_fill) { wxString msg_text = _(L("The ")) + str_fill_pattern + _(L(" infill pattern is not supposed to work at 100% density.\n" "\nShall I switch to rectilinear fill pattern?")); auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Infill")), wxICON_WARNING | wxYES | wxNO); @@ -1305,8 +1398,8 @@ void TabFilament::build() optgroup->append_single_option_line("filament_cooling_final_speed"); optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower"); - line = { _(L("Ramming")), "" }; - line.widget = [this](wxWindow* parent){ + line = optgroup->create_single_option_line("filament_ramming_parameters");// { _(L("Ramming")), "" }; + line.widget = [this](wxWindow* parent) { auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(ramming_dialog_btn); @@ -1345,8 +1438,8 @@ void TabFilament::build() page = add_options_page(_(L("Dependencies")), "wrench.png"); optgroup = page->new_optgroup(_(L("Profile dependencies"))); - line = { _(L("Compatible printers")), "" }; - line.widget = [this](wxWindow* parent){ + line = optgroup->create_single_option_line("compatible_printers");//{ _(L("Compatible printers")), "" }; + line.widget = [this](wxWindow* parent) { return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn); }; optgroup->append_line(line, &m_colored_Label); @@ -1364,13 +1457,17 @@ void TabFilament::build() } // Reload current config (aka presets->edited_preset->config) into the UI fields. -void TabFilament::reload_config(){ +void TabFilament::reload_config() +{ reload_compatible_printers_widget(); Tab::reload_config(); } void TabFilament::update() { + if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) + return; // ys_FIXME + Freeze(); wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset())); m_cooling_description_line->SetText(text); @@ -1416,6 +1513,15 @@ void TabPrinter::build() m_presets = &m_preset_bundle->printers; load_initial_data(); + m_printer_technology = m_presets->get_selected_preset().printer_technology(); + + m_presets->get_selected_preset().printer_technology() == ptSLA ? build_sla() : build_fff(); +} + +void TabPrinter::build_fff() +{ + if (!m_pages.empty()) + m_pages.resize(0); // to avoid redundant memory allocation / deallocation during extruders count changing m_pages.reserve(30); @@ -1428,10 +1534,10 @@ void TabPrinter::build() auto page = add_options_page(_(L("General")), "printer_empty.png"); auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); - Line line{ _(L("Bed shape")), "" }; - line.widget = [this](wxWindow* parent){ + Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" }; + line.widget = [this](wxWindow* parent) { auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - // btn->SetFont(Slic3r::GUI::small_font); + btn->SetFont(wxGetApp().small_font()); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); auto sizer = new wxBoxSizer(wxHORIZONTAL); @@ -1441,7 +1547,7 @@ void TabPrinter::build() { auto dlg = new BedShapeDialog(this); dlg->build_dialog(m_config->option("bed_shape")); - if (dlg->ShowModal() == wxID_OK){ + if (dlg->ShowModal() == wxID_OK) { load_key_value("bed_shape", dlg->GetValue()); update_changed_ui(); } @@ -1460,13 +1566,14 @@ void TabPrinter::build() def.label = L("Extruders"); def.tooltip = L("Number of extruders of the printer."); def.min = 1; + def.mode = comExpert; Option option(def, "extruders_count"); optgroup->append_single_option_line(option); optgroup->append_single_option_line("single_extruder_multi_material"); - optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){ + optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { size_t extruders_count = boost::any_cast(optgroup->get_value("extruders_count")); - wxTheApp->CallAfter([this, opt_key, value, extruders_count](){ + wxTheApp->CallAfter([this, opt_key, value, extruders_count]() { if (opt_key.compare("extruders_count")==0 || opt_key.compare("single_extruder_multi_material")==0) { extruders_count_changed(extruders_count); update_dirty(); @@ -1481,12 +1588,13 @@ void TabPrinter::build() }; +#if 0 if (!m_no_controller) { optgroup = page->new_optgroup(_(L("USB/Serial connection"))); line = {_(L("Serial port")), ""}; Option serial_port = optgroup->get_option("serial_port"); - serial_port.side_widget = ([this](wxWindow* parent){ + serial_port.side_widget = ([this](wxWindow* parent) { auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(Slic3r::var("arrow_rotate_clockwise.png")), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); btn->SetToolTip(_(L("Rescan serial ports"))); @@ -1496,15 +1604,15 @@ void TabPrinter::build() btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {update_serial_ports(); }); return sizer; }); - auto serial_test = [this](wxWindow* parent){ + auto serial_test = [this](wxWindow* parent) { auto btn = m_serial_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); -// btn->SetFont($Slic3r::GUI::small_font); + btn->SetFont(Slic3r::GUI::small_font()); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); - btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e){ + btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e) { auto sender = Slic3r::make_unique(); auto res = sender->connect( m_config->opt_string("serial_port"), @@ -1525,6 +1633,7 @@ void TabPrinter::build() line.append_widget(serial_test); optgroup->append_line(line); } +#endif optgroup = page->new_optgroup(_(L("Printer Host upload"))); @@ -1588,7 +1697,7 @@ void TabPrinter::build() auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); - btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e){ + btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) { static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*")); wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (openFileDialog.ShowModal() != wxID_CANCEL) { @@ -1622,8 +1731,8 @@ void TabPrinter::build() optgroup->append_single_option_line("silent_mode"); optgroup->append_single_option_line("remaining_times"); - optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){ - wxTheApp->CallAfter([this, opt_key, value](){ + optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { + wxTheApp->CallAfter([this, opt_key, value]() { if (opt_key.compare("silent_mode") == 0) { bool val = boost::any_cast(value); if (m_use_silent_mode != val) { @@ -1698,23 +1807,98 @@ void TabPrinter::build() build_extruder_pages(); +#if 0 if (!m_no_controller) update_serial_ports(); +#endif } -void TabPrinter::update_serial_ports(){ +void TabPrinter::build_sla() +{ + if (!m_pages.empty()) + m_pages.resize(0); + auto page = add_options_page(_(L("General")), "printer_empty.png"); + auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); + + Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" }; + line.widget = [this](wxWindow* parent) { + auto btn = new wxButton(parent, wxID_ANY, _(L(" Set ")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); + // btn->SetFont(Slic3r::GUI::small_font); + btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn); + + btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) + { + auto dlg = new BedShapeDialog(this); + dlg->build_dialog(m_config->option("bed_shape")); + if (dlg->ShowModal() == wxID_OK) { + load_key_value("bed_shape", dlg->GetValue()); + update_changed_ui(); + } + })); + + return sizer; + }; + optgroup->append_line(line, &m_colored_Label); + optgroup->append_single_option_line("max_print_height"); + + optgroup = page->new_optgroup(_(L("Display"))); + optgroup->append_single_option_line("display_width"); + optgroup->append_single_option_line("display_height"); + + auto option = optgroup->get_option("display_pixels_x"); + line = { _(option.opt.full_label), "" }; + line.append_option(option); + line.append_option(optgroup->get_option("display_pixels_y")); + optgroup->append_line(line); + + optgroup = page->new_optgroup(_(L("Corrections"))); + line = Line{ m_config->def()->get("printer_correction")->full_label, "" }; + std::vector axes{ "X", "Y", "Z" }; + int id = 0; + for (auto& axis : axes) { + auto opt = optgroup->get_option("printer_correction", id); + opt.opt.label = axis; + line.append_option(opt); + ++id; + } + optgroup->append_line(line); + + page = add_options_page(_(L("Notes")), "note.png"); + optgroup = page->new_optgroup(_(L("Notes")), 0); + option = optgroup->get_option("printer_notes"); + option.opt.full_width = true; + option.opt.height = 250; + optgroup->append_single_option_line(option); + + page = add_options_page(_(L("Dependencies")), "wrench.png"); + optgroup = page->new_optgroup(_(L("Profile dependencies"))); + line = Line{ "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + return description_line_widget(parent, &m_parent_preset_description_line); + }; + optgroup->append_line(line); +} + +void TabPrinter::update_serial_ports() +{ Field *field = get_field("serial_port"); Choice *choice = static_cast(field); choice->set_values(Utils::scan_serial_ports()); } -void TabPrinter::extruders_count_changed(size_t extruders_count){ +void TabPrinter::extruders_count_changed(size_t extruders_count) +{ m_extruders_count = extruders_count; m_preset_bundle->printers.get_edited_preset().set_num_extruders(extruders_count); m_preset_bundle->update_multi_material_filament_presets(); build_extruder_pages(); reload_config(); on_value_change("extruders_count", extruders_count); + wxGetApp().sidebar().update_objects_list_extruder_column(extruders_count); } void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key) @@ -1798,7 +1982,7 @@ void TabPrinter::build_extruder_pages() break; } - if (existed_page < n_before_extruders && is_marlin_flavor){ + if (existed_page < n_before_extruders && is_marlin_flavor) { auto page = build_kinematics_page(); m_pages.insert(m_pages.begin() + n_before_extruders, page); } @@ -1831,7 +2015,7 @@ void TabPrinter::build_extruder_pages() } - for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx){ + for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx) { //# build page char buf[MIN_BUF_LENGTH_FOR_L]; sprintf(buf, _CHB(L("Extruder %d")), extruder_idx + 1); @@ -1893,7 +2077,38 @@ void TabPrinter::on_preset_loaded() extruders_count_changed(extruders_count); } -void TabPrinter::update(){ +void TabPrinter::update_pages() +{ + // update m_pages ONLY if printer technology is changed + const PrinterTechnology new_printer_technology = m_presets->get_edited_preset().printer_technology(); + if (new_printer_technology == m_printer_technology) + return; + + // hide all old pages + for (auto& el : m_pages) + el.get()->Hide(); + + // set m_pages to m_pages_(technology before changing) + m_printer_technology == ptFFF ? m_pages.swap(m_pages_fff) : m_pages.swap(m_pages_sla); + + // build Tab according to the technology, if it's not exist jet OR + // set m_pages_(technology after changing) to m_pages + // m_printer_technology will be set by Tab::load_current_preset() + if (new_printer_technology == ptFFF) + m_pages_fff.empty() ? build_fff() : m_pages.swap(m_pages_fff); + else + m_pages_sla.empty() ? build_sla() : m_pages.swap(m_pages_sla); + + rebuild_page_tree(true); +} + +void TabPrinter::update() +{ + m_presets->get_edited_preset().printer_technology() == ptFFF ? update_fff() : update_sla(); +} + +void TabPrinter::update_fff() +{ Freeze(); bool en; @@ -1996,20 +2211,26 @@ void TabPrinter::update(){ Thaw(); } +void TabPrinter::update_sla() +{ ; } + // Initialize the UI from the current preset void Tab::load_current_preset() { auto preset = m_presets->get_edited_preset(); (preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); - update(); - // For the printer profile, generate the extruder pages. - on_preset_loaded(); - // Reload preset pages with the new configuration values. - reload_config(); + + update(); + // For the printer profile, generate the extruder pages. + if (preset.printer_technology() == ptFFF) + on_preset_loaded(); + // Reload preset pages with the new configuration values. + reload_config(); + m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet; m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns; - m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns; + m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns; m_undo_to_sys_btn->Enable(!preset.is_default); @@ -2019,19 +2240,42 @@ void Tab::load_current_preset() // (not sure this is true anymore now that update_dirty is idempotent) wxTheApp->CallAfter([this]{ // checking out if this Tab exists till this moment - if (!checked_tab(this)) + if (!wxGetApp().checked_tab(this)) return; update_tab_ui(); - on_presets_changed(); - if (name() == "print") - update_frequently_changed_parameters(); - if (m_name == "printer"){ - static_cast(this)->m_initial_extruders_count = static_cast(this)->m_extruders_count; - const Preset* parent_preset = m_presets->get_selected_preset_parent(); - static_cast(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 : - static_cast(parent_preset->config.option("nozzle_diameter"))->values.size(); + // update show/hide tabs + if (m_name == "printer") { + PrinterTechnology& printer_technology = m_presets->get_edited_preset().printer_technology(); + if (printer_technology != static_cast(this)->m_printer_technology) + { + for (auto tab : wxGetApp().tabs_list) { + if (tab->type() == Preset::TYPE_PRINTER) // Printer tab is shown every time + continue; + if (tab->supports_printer_technology(printer_technology)) + wxGetApp().tab_panel()->InsertPage(wxGetApp().tab_panel()->FindPage(this), tab, tab->title()); + else { + int page_id = wxGetApp().tab_panel()->FindPage(tab); + wxGetApp().tab_panel()->GetPage(page_id)->Show(false); + wxGetApp().tab_panel()->RemovePage(page_id); + } + } + static_cast(this)->m_printer_technology = printer_technology; + } + on_presets_changed(); + if (printer_technology == ptFFF) { + static_cast(this)->m_initial_extruders_count = static_cast(this)->m_extruders_count; + const Preset* parent_preset = m_presets->get_selected_preset_parent(); + static_cast(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 : + static_cast(parent_preset->config.option("nozzle_diameter"))->values.size(); + } } + else { + on_presets_changed(); + if (m_name == "print") + update_frequently_changed_parameters(); + } + m_opt_status_value = (m_presets->get_selected_preset_parent() ? osSystemValue : 0) | osInitValue; init_options_list(); update_changed_ui(); @@ -2039,12 +2283,13 @@ void Tab::load_current_preset() } //Regerenerate content of the page tree. -void Tab::rebuild_page_tree() +void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/) { Freeze(); // get label of the currently selected item - auto selected = m_treectrl->GetItemText(m_treectrl->GetSelection()); - auto rootItem = m_treectrl->GetRootItem(); + const auto sel_item = m_treectrl->GetSelection(); + const auto selected = sel_item ? m_treectrl->GetItemText(sel_item) : ""; + const auto rootItem = m_treectrl->GetRootItem(); auto have_selection = 0; m_treectrl->DeleteChildren(rootItem); @@ -2054,9 +2299,9 @@ void Tab::rebuild_page_tree() m_treectrl->SetItemTextColour(itemId, p->get_item_colour()); if (p->title() == selected) { if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange - m_disable_tree_sel_changed_event = 1; + m_disable_tree_sel_changed_event = !tree_sel_change_event; m_treectrl->SelectItem(itemId); - m_disable_tree_sel_changed_event = 0; + m_disable_tree_sel_changed_event = false; have_selection = 1; } } @@ -2068,51 +2313,87 @@ void Tab::rebuild_page_tree() Thaw(); } +void Tab::update_page_tree_visibility() +{ + const auto sel_item = m_treectrl->GetSelection(); + const auto selected = sel_item ? m_treectrl->GetItemText(sel_item) : ""; + const auto rootItem = m_treectrl->GetRootItem(); + + auto have_selection = 0; + m_treectrl->DeleteChildren(rootItem); + for (auto p : m_pages) + { + if (!p->get_show()) + continue; + auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID()); + m_treectrl->SetItemTextColour(itemId, p->get_item_colour()); + if (p->title() == selected) { + m_treectrl->SelectItem(itemId); + have_selection = 1; + } + } + + if (!have_selection) { + // this is triggered on first load, so we don't disable the sel change event + m_treectrl->SelectItem(m_treectrl->GetFirstVisibleItem());//! (treectrl->GetFirstChild(rootItem)); + } + +} + // Called by the UI combo box when the user switches profiles. // Select a preset by a name.If !defined(name), then the default preset is selected. // If the current profile is modified, user is asked to save the changes. -void Tab::select_preset(const std::string& preset_name /*= ""*/) +void Tab::select_preset(std::string preset_name) { - std::string name = preset_name; - auto force = false; - auto presets = m_presets; // If no name is provided, select the "-- default --" preset. - if (name.empty()) - name= presets->default_preset().name; - auto current_dirty = presets->current_is_dirty(); - auto canceled = false; - auto printer_tab = presets->name().compare("printer")==0; - m_reload_dependent_tabs = {}; - if (!force && current_dirty && !may_discard_current_dirty_preset()) { + if (preset_name.empty()) + preset_name = m_presets->default_preset().name; + auto current_dirty = m_presets->current_is_dirty(); + auto printer_tab = m_presets->name() == "printer"; + auto canceled = false; +// m_reload_dependent_tabs = {}; + m_dependent_tabs = {}; + if (current_dirty && !may_discard_current_dirty_preset()) { canceled = true; - } else if(printer_tab) { + } else if (printer_tab) { // Before switching the printer to a new one, verify, whether the currently active print and filament // are compatible with the new printer. // If they are not compatible and the current print or filament are dirty, let user decide // whether to discard the changes or keep the current printer selection. - auto new_printer_preset = presets->find_preset(name, true); - auto print_presets = &m_preset_bundle->prints; - bool print_preset_dirty = print_presets->current_is_dirty(); - bool print_preset_compatible = print_presets->get_edited_preset().is_compatible_with_printer(*new_printer_preset); - canceled = !force && print_preset_dirty && !print_preset_compatible && - !may_discard_current_dirty_preset(print_presets, name); - auto filament_presets = &m_preset_bundle->filaments; - bool filament_preset_dirty = filament_presets->current_is_dirty(); - bool filament_preset_compatible = filament_presets->get_edited_preset().is_compatible_with_printer(*new_printer_preset); - if (!canceled && !force) { - canceled = filament_preset_dirty && !filament_preset_compatible && - !may_discard_current_dirty_preset(filament_presets, name); + // + // With the introduction of the SLA printer types, we need to support switching between + // the FFF and SLA printers. + const Preset &new_printer_preset = *m_presets->find_preset(preset_name, true); + PrinterTechnology old_printer_technology = m_presets->get_edited_preset().printer_technology(); + PrinterTechnology new_printer_technology = new_printer_preset.printer_technology(); + struct PresetUpdate { + Preset::Type tab_type; + PresetCollection *presets; + PrinterTechnology technology; + bool old_preset_dirty; + bool new_preset_compatible; + }; + std::vector updates = { + { Preset::Type::TYPE_PRINT, &m_preset_bundle->prints, ptFFF }, + { Preset::Type::TYPE_SLA_PRINT, &m_preset_bundle->sla_prints, ptSLA }, + { Preset::Type::TYPE_FILAMENT, &m_preset_bundle->filaments, ptFFF }, + { Preset::Type::TYPE_SLA_MATERIAL,&m_preset_bundle->sla_materials, ptSLA } + }; + for (PresetUpdate &pu : updates) { + pu.old_preset_dirty = (old_printer_technology == pu.technology) && pu.presets->current_is_dirty(); + pu.new_preset_compatible = (new_printer_technology == pu.technology) && pu.presets->get_edited_preset().is_compatible_with_printer(new_printer_preset); + if (! canceled) + canceled = pu.old_preset_dirty && ! pu.new_preset_compatible && ! may_discard_current_dirty_preset(pu.presets, preset_name); } - if (!canceled) { - if (!print_preset_compatible) { + if (! canceled) { + for (PresetUpdate &pu : updates) { // The preset will be switched to a different, compatible preset, or the '-- default --'. - m_reload_dependent_tabs.push_back("print"); - if (print_preset_dirty) print_presets->discard_current_changes(); - } - if (!filament_preset_compatible) { - // The preset will be switched to a different, compatible preset, or the '-- default --'. - m_reload_dependent_tabs.push_back("filament"); - if (filament_preset_dirty) filament_presets->discard_current_changes(); + if (pu.technology == new_printer_technology) { +// m_reload_dependent_tabs.emplace_back(pu.name); + m_dependent_tabs.emplace_back(pu.tab_type); + } + if (pu.old_preset_dirty) + pu.presets->discard_current_changes(); } } } @@ -2121,19 +2402,20 @@ void Tab::select_preset(const std::string& preset_name /*= ""*/) // Trigger the on_presets_changed event so that we also restore the previous value in the plater selector, // if this action was initiated from the platter. on_presets_changed(); - } - else { - if (current_dirty) presets->discard_current_changes() ; - presets->select_preset_by_name(name, force); + } else { + if (current_dirty) + m_presets->discard_current_changes() ; + m_presets->select_preset_by_name(preset_name, false); // Mark the print & filament enabled if they are compatible with the currently selected preset. // The following method should not discard changes of current print or filament presets on change of a printer profile, // if they are compatible with the current printer. if (current_dirty || printer_tab) m_preset_bundle->update_compatible_with_printer(true); // Initialize the UI from the current preset. + if (printer_tab) + static_cast(this)->update_pages(); load_current_preset(); } - } // If the current preset is dirty, the user is asked whether the changes may be discarded. @@ -2186,9 +2468,13 @@ void Tab::OnTreeSelChange(wxTreeEvent& event) wxWindowUpdateLocker noUpdates(this); #endif + if (m_pages.empty()) + return; + Page* page = nullptr; - auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); - for (auto p : m_pages) + const auto sel_item = m_treectrl->GetSelection(); + const auto selection = sel_item ? m_treectrl->GetItemText(sel_item) : ""; + for (auto p : m_pages) if (p->title() == selection) { page = p.get(); @@ -2255,7 +2541,7 @@ void Tab::save_preset(std::string name /*= ""*/) if (dlg->ShowModal() != wxID_OK) return; name = dlg->get_name(); - if (name == ""){ + if (name == "") { show_error(this, _(L("The supplied name is empty. It can't be saved."))); return; } @@ -2301,7 +2587,7 @@ void Tab::delete_preset() // Delete the file and select some other reasonable preset. // The 'external' presets will only be removed from the preset list, their files will not be deleted. try{ m_presets->delete_current_preset(); } - catch (const std::exception &e) + catch (const std::exception & /* e */) { return; } @@ -2329,8 +2615,9 @@ void Tab::update_ui_from_settings() { // Show the 'show / hide presets' button only for the print and filament tabs, and only if enabled // in application preferences. - m_show_btn_incompatible_presets = get_app_config()->get("show_incompatible_presets")[0] == '1' ? true : false; + m_show_btn_incompatible_presets = wxGetApp().app_config->get("show_incompatible_presets")[0] == '1' ? true : false; bool show = m_show_btn_incompatible_presets && m_presets->name().compare("printer") != 0; + Layout(); show ? m_btn_hide_incompatible_presets->Show() : m_btn_hide_incompatible_presets->Hide(); // If the 'show / hide presets' button is hidden, hide the incompatible presets. if (show) { @@ -2412,173 +2699,6 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox return sizer; } -void Tab::update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible) -{ - if (ui == nullptr) - return; - ui->Freeze(); - ui->DeleteAllItems(); - auto presets = m_presets->get_presets(); - auto idx_selected = m_presets->get_idx_selected(); - auto suffix_modified = m_presets->get_suffix_modified(); - int icon_compatible = 0; - int icon_incompatible = 1; - int cnt_items = 0; - - auto root_sys = ui->AppendContainer(wxDataViewItem(0), _(L("System presets"))); - auto root_def = ui->AppendContainer(wxDataViewItem(0), _(L("Default presets"))); - - auto show_def = get_app_config()->get("no_defaults")[0] != '1'; - - for (size_t i = presets.front().is_visible ? 0 : 1; i < presets.size(); ++i) { - const Preset &preset = presets[i]; - if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected)) - continue; - - auto preset_name = wxString::FromUTF8((preset.name + (preset.is_dirty ? suffix_modified : "")).c_str()); - - wxDataViewItem item; - if (preset.is_system) - item = ui->AppendItem(root_sys, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else if (show_def && preset.is_default) - item = ui->AppendItem(root_def, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else - { - auto parent = m_presets->get_preset_parent(preset); - if (parent == nullptr) - item = ui->AppendItem(root_def, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else - { - auto parent_name = parent->name; - - wxDataViewTreeStoreContainerNode *node = ui->GetStore()->FindContainerNode(root_sys); - if (node) - { - wxDataViewTreeStoreNodeList::iterator iter; - for (iter = node->GetChildren().begin(); iter != node->GetChildren().end(); iter++) - { - wxDataViewTreeStoreNode* child = *iter; - auto child_item = child->GetItem(); - auto item_text = ui->GetItemText(child_item); - if (item_text == parent_name) - { - auto added_child = ui->AppendItem(child->GetItem(), preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - if (!added_child){ - ui->DeleteItem(child->GetItem()); - auto new_parent = ui->AppendContainer(root_sys, parent_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - ui->AppendItem(new_parent, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - } - break; - } - } - } - } - } - - cnt_items++; - if (i == idx_selected){ - ui->Select(item); - m_cc_presets_choice->SetText(preset_name); - } - } - if (ui->GetStore()->GetChildCount(root_def) == 0) - ui->DeleteItem(root_def); - - ui->Thaw(); -} - -void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible) -{ - if (ui == nullptr) - return; - ui->Freeze(); - ui->Clear(); - auto presets = m_presets->get_presets(); - auto idx_selected = m_presets->get_idx_selected(); - auto suffix_modified = m_presets->get_suffix_modified(); - int icon_compatible = 0; - int icon_incompatible = 1; - int cnt_items = 0; - - wxDataViewTreeCtrlComboPopup* popup = wxDynamicCast(m_cc_presets_choice->GetPopupControl(), wxDataViewTreeCtrlComboPopup); - if (popup != nullptr) - { - popup->DeleteAllItems(); - - auto root_sys = popup->AppendContainer(wxDataViewItem(0), _(L("System presets"))); - auto root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets"))); - - auto show_def = get_app_config()->get("no_defaults")[0] != '1'; - - for (size_t i = presets.front().is_visible ? 0 : 1; i < presets.size(); ++i) { - const Preset &preset = presets[i]; - if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected)) - continue; - - auto preset_name = wxString::FromUTF8((preset.name + (preset.is_dirty ? suffix_modified : "")).c_str()); - - wxDataViewItem item; - if (preset.is_system) - item = popup->AppendItem(root_sys, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else if (show_def && preset.is_default) - item = popup->AppendItem(root_def, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else - { - auto parent = m_presets->get_preset_parent(preset); - if (parent == nullptr) - item = popup->AppendItem(root_def, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else - { - auto parent_name = parent->name; - - wxDataViewTreeStoreContainerNode *node = popup->GetStore()->FindContainerNode(root_sys); - if (node) - { - wxDataViewTreeStoreNodeList::iterator iter; - for (iter = node->GetChildren().begin(); iter != node->GetChildren().end(); iter++) - { - wxDataViewTreeStoreNode* child = *iter; - auto child_item = child->GetItem(); - auto item_text = popup->GetItemText(child_item); - if (item_text == parent_name) - { - auto added_child = popup->AppendItem(child->GetItem(), preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - if (!added_child){ - popup->DeleteItem(child->GetItem()); - auto new_parent = popup->AppendContainer(root_sys, parent_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - popup->AppendItem(new_parent, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - } - break; - } - } - } - } - } - - cnt_items++; - if (i == idx_selected){ - popup->Select(item); - m_cc_presets_choice->SetText(preset_name); - } - } - if (popup->GetStore()->GetChildCount(root_def) == 0) - popup->DeleteItem(root_def); - } - ui->Thaw(); -} - void Tab::fill_icon_descriptions() { m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_lock, L("LOCKED LOCK;" @@ -2654,10 +2774,19 @@ void Page::reload_config() group->reload_config(); } +void Page::update_visibility(ConfigOptionMode mode) +{ + bool ret_val = false; + for (auto group : m_optgroups) + ret_val = group->update_visibility(mode) || ret_val; + + m_show = ret_val; +} + Field* Page::get_field(const t_config_option_key& opt_key, int opt_index /*= -1*/) const { Field* field = nullptr; - for (auto opt : m_optgroups){ + for (auto opt : m_optgroups) { field = opt->get_fieldc(opt_key, opt_index); if (field != nullptr) return field; @@ -2665,7 +2794,7 @@ Field* Page::get_field(const t_config_option_key& opt_key, int opt_index /*= -1* return field; } -bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value){ +bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value) { bool changed = false; for(auto optgroup: m_optgroups) { if (optgroup->set_value(opt_key, value)) @@ -2677,8 +2806,22 @@ bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value // package Slic3r::GUI::Tab::Page; ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_label_width /*= -1*/) { + auto extra_column = [](wxWindow* parent, const Line& line) + { + std::string bmp_name; + if (line.get_options().size() == 0) + bmp_name = "error.png"; + else { + auto mode = line.get_options()[0].opt.mode; //we assume that we have one option per line + bmp_name = mode == comExpert ? "mode_expert_.png" : + mode == comAdvanced ? "mode_middle_.png" : "mode_simple_.png"; + } + auto bmp = new wxStaticBitmap(parent, wxID_ANY, wxBitmap(from_u8(var(bmp_name)), wxBITMAP_TYPE_PNG)); + return bmp; + }; + //! config_ have to be "right" - ConfigOptionsGroupShp optgroup = std::make_shared(this, title, m_config, true); + ConfigOptionsGroupShp optgroup = std::make_shared(this, title, m_config, true, extra_column); if (noncommon_label_width >= 0) optgroup->label_width = noncommon_label_width; @@ -2687,7 +2830,7 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la #else auto tab = GetParent(); #endif - optgroup->m_on_change = [this, tab](t_config_option_key opt_key, boost::any value){ + optgroup->m_on_change = [this, tab](t_config_option_key opt_key, boost::any value) { //! This function will be called from OptionGroup. //! Using of CallAfter is redundant. //! And in some cases it causes update() function to be recalled again @@ -2697,17 +2840,17 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la //! }); }; - optgroup->m_get_initial_config = [this, tab](){ + optgroup->m_get_initial_config = [this, tab]() { DynamicPrintConfig config = static_cast(tab)->m_presets->get_selected_preset().config; return config; }; - optgroup->m_get_sys_config = [this, tab](){ + optgroup->m_get_sys_config = [this, tab]() { DynamicPrintConfig config = static_cast(tab)->m_presets->get_selected_preset_parent()->config; return config; }; - optgroup->have_sys_config = [this, tab](){ + optgroup->have_sys_config = [this, tab]() { return static_cast(tab)->m_presets->get_selected_preset_parent() != nullptr; }; @@ -2748,8 +2891,8 @@ void SavePresetWindow::accept() bool is_unusable_symbol = false; bool is_unusable_postfix = false; const std::string unusable_postfix = PresetCollection::get_suffix_modified();//"(modified)"; - for (size_t i = 0; i < std::strlen(unusable_symbols); i++){ - if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos){ + for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { + if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) { is_unusable_symbol = true; break; } @@ -2761,7 +2904,7 @@ void SavePresetWindow::accept() show_error(this,_(L("The supplied name is not valid;")) + "\n" + _(L("the following characters are not allowed:")) + " <>:/\\|?*\""); } - else if (is_unusable_postfix){ + else if (is_unusable_postfix) { show_error(this,_(L("The supplied name is not valid;")) + "\n" + _(L("the following postfix are not allowed:")) + "\n\t" + //unusable_postfix); wxString::FromUTF8(unusable_postfix.c_str())); @@ -2775,5 +2918,129 @@ void SavePresetWindow::accept() } } +void TabSLAMaterial::build() +{ + m_presets = &m_preset_bundle->sla_materials; + load_initial_data(); + + auto page = add_options_page(_(L("Material")), "package_green.png"); + + auto optgroup = page->new_optgroup(_(L("Layers"))); +// optgroup->append_single_option_line("layer_height"); + optgroup->append_single_option_line("initial_layer_height"); + + optgroup = page->new_optgroup(_(L("Exposure"))); + optgroup->append_single_option_line("exposure_time"); + optgroup->append_single_option_line("initial_exposure_time"); + + optgroup = page->new_optgroup(_(L("Corrections"))); + optgroup->label_width = 190; + std::vector corrections = { "material_correction_printing", "material_correction_curing" }; + std::vector axes{ "X", "Y", "Z" }; + for (auto& opt_key : corrections) { + auto line = Line{ m_config->def()->get(opt_key)->full_label, "" }; + int id = 0; + for (auto& axis : axes) { + auto opt = optgroup->get_option(opt_key, id); + opt.opt.label = axis; + opt.opt.width = 60; + line.append_option(opt); + ++id; + } + optgroup->append_line(line); + } + + page = add_options_page(_(L("Notes")), "note.png"); + optgroup = page->new_optgroup(_(L("Notes")), 0); + optgroup->label_width = 0; + Option option = optgroup->get_option("material_notes"); + option.opt.full_width = true; + option.opt.height = 250; + optgroup->append_single_option_line(option); + + page = add_options_page(_(L("Dependencies")), "wrench.png"); + optgroup = page->new_optgroup(_(L("Profile dependencies"))); + Line line = optgroup->create_single_option_line("compatible_printers");//Line { _(L("Compatible printers")), "" }; + line.widget = [this](wxWindow* parent) { + return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn); + }; + optgroup->append_line(line, &m_colored_Label); + + option = optgroup->get_option("compatible_printers_condition"); + option.opt.full_width = true; + optgroup->append_single_option_line(option); + + line = Line{ "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + return description_line_widget(parent, &m_parent_preset_description_line); + }; + optgroup->append_line(line); +} + +void TabSLAMaterial::update() +{ + if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF) + return; // #ys_FIXME +} + +void TabSLAPrint::build() +{ + m_presets = &m_preset_bundle->sla_prints; + load_initial_data(); + + auto page = add_options_page(_(L("Layers and perimeters")), "package_green.png"); + + auto optgroup = page->new_optgroup(_(L("Layers"))); + optgroup->append_single_option_line("layer_height"); + + page = add_options_page(_(L("Supports")), "building.png"); + optgroup = page->new_optgroup(_(L("Support head"))); + optgroup->append_single_option_line("support_head_front_radius"); + optgroup->append_single_option_line("support_head_back_radius"); + optgroup->append_single_option_line("support_head_penetration"); + optgroup->append_single_option_line("support_head_width"); + + optgroup = page->new_optgroup(_(L("Support pillar"))); + optgroup->append_single_option_line("support_pillar_radius"); + optgroup->append_single_option_line("support_base_radius"); + optgroup->append_single_option_line("support_base_height"); + + optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions"))); + optgroup->append_single_option_line("support_critical_angle"); + optgroup->append_single_option_line("support_max_bridge_length"); + + optgroup = page->new_optgroup(_(L("Pad"))); + optgroup->append_single_option_line("pad_wall_thickness"); + optgroup->append_single_option_line("pad_wall_height"); + optgroup->append_single_option_line("pad_max_merge_distance"); + optgroup->append_single_option_line("pad_edge_radius"); + + page = add_options_page(_(L("Dependencies")), "wrench.png"); + optgroup = page->new_optgroup(_(L("Profile dependencies"))); + Line line = optgroup->create_single_option_line("compatible_printers");//Line { _(L("Compatible printers")), "" }; + line.widget = [this](wxWindow* parent) { + return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn); + }; + optgroup->append_line(line, &m_colored_Label); + + Option option = optgroup->get_option("compatible_printers_condition"); + option.opt.full_width = true; + optgroup->append_single_option_line(option); + + line = Line{ "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + return description_line_widget(parent, &m_parent_preset_description_line); + }; + optgroup->append_line(line); +} + +void TabSLAPrint::update() +{ + if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF) + return; // #ys_FIXME +} + } // GUI } // Slic3r diff --git a/xs/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp similarity index 77% rename from xs/src/slic3r/GUI/Tab.hpp rename to src/slic3r/GUI/Tab.hpp index 230fe659e5..9b897b6f7a 100644 --- a/xs/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -23,16 +23,14 @@ #include #include #include -#include -#include #include #include #include #include "BedShapeDialog.hpp" +#include "Event.hpp" -//!enum { ID_TAB_TREE = wxID_HIGHEST + 1 }; namespace Slic3r { namespace GUI { @@ -49,6 +47,7 @@ class Page : public wxScrolledWindow wxString m_title; size_t m_iconID; wxBoxSizer* m_vsizer; + bool m_show = true; public: Page(wxWindow* parent, const wxString title, const int iconID) : m_parent(parent), @@ -57,10 +56,10 @@ public: { Create(m_parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); m_vsizer = new wxBoxSizer(wxVERTICAL); - m_item_color = &get_label_clr_default(); + m_item_color = &wxGetApp().get_label_clr_default(); SetSizer(m_vsizer); } - ~Page(){} + ~Page() {} bool m_is_modified_values{ false }; bool m_is_nonsys_values{ true }; @@ -75,6 +74,7 @@ public: size_t iconID() const { return m_iconID; } void set_config(DynamicPrintConfig* config_in) { m_config = config_in; } void reload_config(); + void update_visibility(ConfigOptionMode mode); Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const; bool set_value(const t_config_option_key& opt_key, const boost::any& value); ConfigOptionsGroupShp new_optgroup(const wxString& title, int noncommon_label_width = -1); @@ -90,13 +90,17 @@ public: const wxColour get_item_colour() { return *m_item_color; } + bool get_show() const { return m_show; } protected: // Color of TreeCtrlItem. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one. const wxColour* m_item_color; }; -// Slic3r::GUI::Tab; + +wxDECLARE_EVENT(EVT_TAB_VALUE_CHANGED, wxCommandEvent); +wxDECLARE_EVENT(EVT_TAB_PRESETS_CHANGED, SimpleEvent); + using PageShp = std::shared_ptr; class Tab: public wxPanel @@ -107,6 +111,7 @@ class Tab: public wxPanel int m_size_move = -1; #endif // __WXOSX__ protected: + Preset::Type m_type; std::string m_name; const wxString m_title; wxBitmapComboBox* m_presets_choice; @@ -122,8 +127,6 @@ protected: wxButton* m_undo_btn; wxButton* m_undo_to_sys_btn; wxButton* m_question_btn; - wxComboCtrl* m_cc_presets_choice; - wxDataViewTreeCtrl* m_presetctrl; wxImageList* m_preset_icons; // Cached bitmaps. @@ -170,25 +173,23 @@ protected: std::vector m_pages; bool m_disable_tree_sel_changed_event; bool m_show_incompatible_presets; - bool m_no_controller; std::vector m_reload_dependent_tabs = {}; + std::vector m_dependent_tabs = {}; enum OptStatus { osSystemValue = 1, osInitValue = 2 }; std::map m_options_list; int m_opt_status_value = 0; t_icon_descriptions m_icon_descriptions = {}; - // The two following two event IDs are generated at Plater.pm by calling Wx::NewEventType. - wxEventType m_event_value_change = 0; - wxEventType m_event_presets_changed = 0; - bool m_is_modified_values{ false }; bool m_is_nonsys_values{ true }; bool m_postpone_update_ui {false}; size_t m_selected_preset_item{ 0 }; + void set_type(); + public: PresetBundle* m_preset_bundle; bool m_show_btn_incompatible_presets = false; @@ -199,31 +200,30 @@ public: public: Tab() {} - Tab(wxNotebook* parent, const wxString& title, const char* name, bool no_controller) : - m_parent(parent), m_title(title), m_name(name), m_no_controller(no_controller) { + Tab(wxNotebook* parent, const wxString& title, const char* name) : + m_parent(parent), m_title(title), m_name(name) { Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL, name); - get_tabs_list().push_back(this); + set_type(); + wxGetApp().tabs_list.push_back(this); } - ~Tab(){ - delete_tab_from_list(this); + ~Tab() { + wxGetApp().delete_tab_from_list(this); } wxWindow* parent() const { return m_parent; } wxString title() const { return m_title; } std::string name() const { return m_name; } + Preset::Type type() const { return m_type; } + virtual bool supports_printer_technology(const PrinterTechnology tech) = 0; - // Set the events to the callbacks posted to the main frame window (currently implemented in Perl). - void set_event_value_change(wxEventType evt) { m_event_value_change = evt; } - void set_event_presets_changed(wxEventType evt) { m_event_presets_changed = evt; } - - void create_preset_tab(PresetBundle *preset_bundle); + void create_preset_tab(); void load_current_preset(); - void rebuild_page_tree(); - void select_preset(const std::string& preset_name = ""); + void rebuild_page_tree(bool tree_sel_change_event = false); + void update_page_tree_visibility(); + void select_preset(std::string preset_name = ""); bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = ""); wxSizer* compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn); - void update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible); void load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value = false); void reload_compatible_printers_widget(); @@ -246,7 +246,7 @@ public: PageShp add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages = false); virtual void OnActivate(); - virtual void on_preset_loaded(){} + virtual void on_preset_loaded() {} virtual void build() = 0; virtual void update() = 0; virtual void init_options_list(); @@ -255,6 +255,7 @@ public: void update_tab_ui(); void load_config(const DynamicPrintConfig& config); virtual void reload_config(); + void update_visibility(ConfigOptionMode mode); Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const; bool set_value(const t_config_option_key& opt_key, const boost::any& value); wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText); @@ -267,12 +268,11 @@ public: void on_value_change(const std::string& opt_key, const boost::any& value); + void update_wiping_button_visibility(); protected: void on_presets_changed(); void update_preset_description_line(); void update_frequently_changed_parameters(); - void update_wiping_button_visibility(); - void update_tab_presets(wxComboCtrl* ui, bool show_incompatible); void fill_icon_descriptions(); void set_tooltips_text(); }; @@ -282,9 +282,9 @@ class TabPrint : public Tab { public: TabPrint() {} - TabPrint(wxNotebook* parent, bool no_controller) : - Tab(parent, _(L("Print Settings")), "print", no_controller) {} - ~TabPrint(){} + TabPrint(wxNotebook* parent) : + Tab(parent, _(L("Print Settings")), "print") {} + ~TabPrint() {} ogStaticText* m_recommended_thin_wall_thickness_description_line; bool m_support_material_overhangs_queried = false; @@ -293,6 +293,7 @@ public: void reload_config() override; void update() override; void OnActivate() override; + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } }; //Slic3r::GUI::Tab::Filament; @@ -302,14 +303,15 @@ class TabFilament : public Tab ogStaticText* m_cooling_description_line; public: TabFilament() {} - TabFilament(wxNotebook* parent, bool no_controller) : - Tab(parent, _(L("Filament Settings")), "filament", no_controller) {} - ~TabFilament(){} + TabFilament(wxNotebook* parent) : + Tab(parent, _(L("Filament Settings")), "filament") {} + ~TabFilament() {} void build() override; void reload_config() override; void update() override; void OnActivate() override; + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } }; //Slic3r::GUI::Tab::Printer; @@ -319,35 +321,73 @@ class TabPrinter : public Tab bool m_use_silent_mode = false; void append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key); bool m_rebuild_kinematics_page = false; + + std::vector m_pages_fff; + std::vector m_pages_sla; public: - wxButton* m_serial_test_btn; - wxButton* m_print_host_test_btn; - wxButton* m_printhost_browse_btn; + wxButton* m_serial_test_btn = nullptr; + wxButton* m_print_host_test_btn = nullptr; + wxButton* m_printhost_browse_btn = nullptr; size_t m_extruders_count; size_t m_extruders_count_old = 0; size_t m_initial_extruders_count; size_t m_sys_extruders_count; + PrinterTechnology m_printer_technology = ptFFF; + TabPrinter() {} - TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {} - ~TabPrinter(){} + TabPrinter(wxNotebook* parent) : Tab(parent, _(L("Printer Settings")), "printer") {} + ~TabPrinter() {} void build() override; - void update() override; + void build_fff(); + void build_sla(); + void update() override; + void update_fff(); + void update_sla(); + void update_pages(); // update m_pages according to printer technology void update_serial_ports(); void extruders_count_changed(size_t extruders_count); PageShp build_kinematics_page(); void build_extruder_pages(); void on_preset_loaded() override; void init_options_list() override; + bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; } +}; + +class TabSLAMaterial : public Tab +{ +public: + TabSLAMaterial() {} + TabSLAMaterial(wxNotebook* parent) : + Tab(parent, _(L("SLA Material Settings")), "sla_material") {} + ~TabSLAMaterial() {} + + void build() override; + void update() override; + void init_options_list() override; + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; } +}; + +class TabSLAPrint : public Tab +{ +public: + TabSLAPrint() {} + TabSLAPrint(wxNotebook* parent) : + Tab(parent, _(L("SLA Print Settings")), "sla_print") {} + ~TabSLAPrint() {} + void build() override; + void update() override; +// void init_options_list() override; + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; } }; class SavePresetWindow :public wxDialog { public: - SavePresetWindow(wxWindow* parent) :wxDialog(parent, wxID_ANY, _(L("Save preset"))){} - ~SavePresetWindow(){} + SavePresetWindow(wxWindow* parent) :wxDialog(parent, wxID_ANY, _(L("Save preset"))) {} + ~SavePresetWindow() {} std::string m_chosen_name; wxComboBox* m_combo; diff --git a/xs/src/slic3r/GUI/TabIface.cpp b/src/slic3r/GUI/TabIface.cpp similarity index 100% rename from xs/src/slic3r/GUI/TabIface.cpp rename to src/slic3r/GUI/TabIface.cpp diff --git a/xs/src/slic3r/GUI/TabIface.hpp b/src/slic3r/GUI/TabIface.hpp similarity index 100% rename from xs/src/slic3r/GUI/TabIface.hpp rename to src/slic3r/GUI/TabIface.hpp diff --git a/xs/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp similarity index 100% rename from xs/src/slic3r/GUI/UpdateDialogs.cpp rename to src/slic3r/GUI/UpdateDialogs.cpp diff --git a/xs/src/slic3r/GUI/UpdateDialogs.hpp b/src/slic3r/GUI/UpdateDialogs.hpp similarity index 100% rename from xs/src/slic3r/GUI/UpdateDialogs.hpp rename to src/slic3r/GUI/UpdateDialogs.hpp diff --git a/xs/src/slic3r/GUI/Widget.hpp b/src/slic3r/GUI/Widget.hpp similarity index 100% rename from xs/src/slic3r/GUI/Widget.hpp rename to src/slic3r/GUI/Widget.hpp diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp similarity index 99% rename from xs/src/slic3r/GUI/WipeTowerDialog.cpp rename to src/slic3r/GUI/WipeTowerDialog.cpp index eef4017c1f..b31e59cc0e 100644 --- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -108,7 +108,7 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters) m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} ); - Refresh(this); + Refresh(true); // erase background } void RammingPanel::line_parameters_changed() { diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/src/slic3r/GUI/WipeTowerDialog.hpp similarity index 100% rename from xs/src/slic3r/GUI/WipeTowerDialog.hpp rename to src/slic3r/GUI/WipeTowerDialog.hpp diff --git a/src/slic3r/GUI/callback.hpp b/src/slic3r/GUI/callback.hpp new file mode 100644 index 0000000000..53f750e16c --- /dev/null +++ b/src/slic3r/GUI/callback.hpp @@ -0,0 +1,32 @@ +// I AM A PHONY PLACEHOLDER FOR THE PERL CALLBACK. +// GET RID OF ME! + +#ifndef slic3r_GUI_PerlCallback_phony_hpp_ +#define slic3r_GUI_PerlCallback_phony_hpp_ + +#include + +namespace Slic3r { + +class PerlCallback { +public: + // PerlCallback(void *) {} + PerlCallback() {} + void register_callback(void *) {} + void deregister_callback() {} + void call() const {} + void call(int) const {} + void call(int, int) const {} + void call(const std::vector&) const {} + void call(double) const {} + void call(double, double) const {} + void call(double, double, double) const {} + void call(double, double, double, double) const {} + void call(double, double, double, double, double) const {} + void call(double, double, double, double, double, double) const {} + void call(bool b) const {} +}; + +} // namespace Slic3r + +#endif /* slic3r_GUI_PerlCallback_phony_hpp_ */ diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp new file mode 100644 index 0000000000..5d0b44d471 --- /dev/null +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -0,0 +1,2043 @@ +#include "wxExtensions.hpp" + +#include "../../libslic3r/Utils.hpp" +#include "BitmapCache.hpp" + +#include +#include +#include +#include +#include "GUI_App.hpp" +#include "GUI_ObjectList.hpp" +#include "Model.hpp" + +wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); +wxDEFINE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); + +wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, + std::function cb, const std::string& icon, wxEvtHandler* event_handler) +{ + if (id == wxID_ANY) + id = wxNewId(); + + wxMenuItem* item = menu->Append(id, string, description); + if (!icon.empty()) + item->SetBitmap(wxBitmap(Slic3r::var(icon), wxBITMAP_TYPE_PNG)); + + if (event_handler != nullptr) + event_handler->Bind(wxEVT_MENU, cb, id); + else + menu->Bind(wxEVT_MENU, cb, id); + + return item; +} + +wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description, const std::string& icon) +{ + if (id == wxID_ANY) + id = wxNewId(); + + wxMenuItem* item = new wxMenuItem(menu, id, string, description); + if (!icon.empty()) + item->SetBitmap(wxBitmap(Slic3r::var(icon), wxBITMAP_TYPE_PNG)); + + item->SetSubMenu(sub_menu); + menu->Append(item); + + return item; +} + +const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200; +const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200; +const unsigned int wxCheckListBoxComboPopup::DefaultItemHeight = 18; + +bool wxCheckListBoxComboPopup::Create(wxWindow* parent) +{ + return wxCheckListBox::Create(parent, wxID_HIGHEST + 1, wxPoint(0, 0)); +} + +wxWindow* wxCheckListBoxComboPopup::GetControl() +{ + return this; +} + +void wxCheckListBoxComboPopup::SetStringValue(const wxString& value) +{ + m_text = value; +} + +wxString wxCheckListBoxComboPopup::GetStringValue() const +{ + return m_text; +} + +wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight) +{ + // matches owner wxComboCtrl's width + // and sets height dinamically in dependence of contained items count + + wxComboCtrl* cmb = GetComboCtrl(); + if (cmb != nullptr) + { + wxSize size = GetComboCtrl()->GetSize(); + + unsigned int count = GetCount(); + if (count > 0) + size.SetHeight(count * DefaultItemHeight); + else + size.SetHeight(DefaultHeight); + + return size; + } + else + return wxSize(DefaultWidth, DefaultHeight); +} + +void wxCheckListBoxComboPopup::OnKeyEvent(wxKeyEvent& evt) +{ + // filters out all the keys which are not working properly + switch (evt.GetKeyCode()) + { + case WXK_LEFT: + case WXK_UP: + case WXK_RIGHT: + case WXK_DOWN: + case WXK_PAGEUP: + case WXK_PAGEDOWN: + case WXK_END: + case WXK_HOME: + case WXK_NUMPAD_LEFT: + case WXK_NUMPAD_UP: + case WXK_NUMPAD_RIGHT: + case WXK_NUMPAD_DOWN: + case WXK_NUMPAD_PAGEUP: + case WXK_NUMPAD_PAGEDOWN: + case WXK_NUMPAD_END: + case WXK_NUMPAD_HOME: + { + break; + } + default: + { + evt.Skip(); + break; + } + } +} + +void wxCheckListBoxComboPopup::OnCheckListBox(wxCommandEvent& evt) +{ + // forwards the checklistbox event to the owner wxComboCtrl + + if (m_check_box_events_status == OnCheckListBoxFunction::FreeToProceed ) + { + wxComboCtrl* cmb = GetComboCtrl(); + if (cmb != nullptr) { + wxCommandEvent event(wxEVT_CHECKLISTBOX, cmb->GetId()); + event.SetEventObject(cmb); + cmb->ProcessWindowEvent(event); + } + } + + evt.Skip(); + + #ifndef _WIN32 // events are sent differently on OSX+Linux vs Win (more description in header file) + if ( m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed ) + // this happens if the event was resent by OnListBoxSelection - next call to OnListBoxSelection is due to user clicking the text, so the function should + // explicitly change the state on the checkbox + m_check_box_events_status = OnCheckListBoxFunction::WasRefusedLastTime; + else + // if the user clicked the checkbox square, this event was sent before OnListBoxSelection was called, so we don't want it to resend it + m_check_box_events_status = OnCheckListBoxFunction::RefuseToProceed; + #endif +} + +void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt) +{ + // transforms list box item selection event into checklistbox item toggle event + + int selId = GetSelection(); + if (selId != wxNOT_FOUND) + { + #ifndef _WIN32 + if (m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed) + #endif + Check((unsigned int)selId, !IsChecked((unsigned int)selId)); + + m_check_box_events_status = OnCheckListBoxFunction::FreeToProceed; // so the checkbox reacts to square-click the next time + + SetSelection(wxNOT_FOUND); + wxCommandEvent event(wxEVT_CHECKLISTBOX, GetId()); + event.SetInt(selId); + event.SetEventObject(this); + ProcessEvent(event); + } +} + + +// *** wxDataViewTreeCtrlComboPopup *** + +const unsigned int wxDataViewTreeCtrlComboPopup::DefaultWidth = 270; +const unsigned int wxDataViewTreeCtrlComboPopup::DefaultHeight = 200; +const unsigned int wxDataViewTreeCtrlComboPopup::DefaultItemHeight = 22; + +bool wxDataViewTreeCtrlComboPopup::Create(wxWindow* parent) +{ + return wxDataViewTreeCtrl::Create(parent, wxID_ANY/*HIGHEST + 1*/, wxPoint(0, 0), wxDefaultSize/*wxSize(270, -1)*/, wxDV_NO_HEADER); +} +/* +wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight) +{ + // matches owner wxComboCtrl's width + // and sets height dinamically in dependence of contained items count + wxComboCtrl* cmb = GetComboCtrl(); + if (cmb != nullptr) + { + wxSize size = GetComboCtrl()->GetSize(); + if (m_cnt_open_items > 0) + size.SetHeight(m_cnt_open_items * DefaultItemHeight); + else + size.SetHeight(DefaultHeight); + + return size; + } + else + return wxSize(DefaultWidth, DefaultHeight); +} +*/ +void wxDataViewTreeCtrlComboPopup::OnKeyEvent(wxKeyEvent& evt) +{ + // filters out all the keys which are not working properly + if (evt.GetKeyCode() == WXK_UP) + { + return; + } + else if (evt.GetKeyCode() == WXK_DOWN) + { + return; + } + else + { + evt.Skip(); + return; + } +} + +void wxDataViewTreeCtrlComboPopup::OnDataViewTreeCtrlSelection(wxCommandEvent& evt) +{ + wxComboCtrl* cmb = GetComboCtrl(); + auto selected = GetItemText(GetSelection()); + cmb->SetText(selected); +} + +// ---------------------------------------------------------------------------- +// *** PrusaCollapsiblePane *** +// ---------------------------------------------------------------------------- +void PrusaCollapsiblePane::OnStateChange(const wxSize& sz) +{ +#ifdef __WXOSX__ + wxCollapsiblePane::OnStateChange(sz); +#else + SetSize(sz); + + if (this->HasFlag(wxCP_NO_TLW_RESIZE)) + { + // the user asked to explicitly handle the resizing itself... + return; + } + + auto top = GetParent(); //right_panel + if (!top) + return; + + wxSizer *sizer = top->GetSizer(); + if (!sizer) + return; + + const wxSize newBestSize = sizer->ComputeFittingClientSize(top); + top->SetMinClientSize(newBestSize); + + wxWindowUpdateLocker noUpdates_p(top->GetParent()); + // we shouldn't attempt to resize a maximized window, whatever happens + // if (!top->IsMaximized()) + // top->SetClientSize(newBestSize); + top->GetParent()->Layout(); + top->Refresh(); +#endif //__WXOSX__ +} + +// ---------------------------------------------------------------------------- +// *** PrusaCollapsiblePaneMSW *** used only #ifdef __WXMSW__ +// ---------------------------------------------------------------------------- +#ifdef __WXMSW__ +bool PrusaCollapsiblePaneMSW::Create(wxWindow *parent, wxWindowID id, const wxString& label, + const wxPoint& pos, const wxSize& size, long style, const wxValidator& val, const wxString& name) +{ + if (!wxControl::Create(parent, id, pos, size, style, val, name)) + return false; + m_pStaticLine = NULL; + m_strLabel = label; + + // sizer containing the expand button and possibly a static line + m_sz = new wxBoxSizer(wxHORIZONTAL); + + m_bmp_close.LoadFile(Slic3r::GUI::from_u8(Slic3r::var("disclosure_triangle_close.png")), wxBITMAP_TYPE_PNG); + m_bmp_open.LoadFile(Slic3r::GUI::from_u8(Slic3r::var("disclosure_triangle_open.png")), wxBITMAP_TYPE_PNG); + + m_pDisclosureTriangleButton = new wxButton(this, wxID_ANY, m_strLabel, wxPoint(0, 0), + wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); + UpdateBtnBmp(); + m_pDisclosureTriangleButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) + { + if (event.GetEventObject() != m_pDisclosureTriangleButton) + { + event.Skip(); + return; + } + + Collapse(!IsCollapsed()); + + // this change was generated by the user - send the event + wxCollapsiblePaneEvent ev(this, GetId(), IsCollapsed()); + GetEventHandler()->ProcessEvent(ev); + }); + + m_sz->Add(m_pDisclosureTriangleButton, 0, wxLEFT | wxTOP | wxBOTTOM, GetBorder()); + + // do not set sz as our sizers since we handle the pane window without using sizers + m_pPane = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxTAB_TRAVERSAL | wxNO_BORDER, wxT("wxCollapsiblePanePane")); + + wxColour& clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + m_pDisclosureTriangleButton->SetBackgroundColour(clr); + this->SetBackgroundColour(clr); + m_pPane->SetBackgroundColour(clr); + + // start as collapsed: + m_pPane->Hide(); + + return true; +} + +void PrusaCollapsiblePaneMSW::UpdateBtnBmp() +{ + if (IsCollapsed()) + m_pDisclosureTriangleButton->SetBitmap(m_bmp_close); + else{ + m_pDisclosureTriangleButton->SetBitmap(m_bmp_open); + // To updating button bitmap it's needed to lost focus on this button, so + // we set focus to mainframe + //GetParent()->GetParent()->GetParent()->SetFocus(); + //or to pane + GetPane()->SetFocus(); + } + Layout(); +} + +void PrusaCollapsiblePaneMSW::SetLabel(const wxString &label) +{ + m_strLabel = label; + m_pDisclosureTriangleButton->SetLabel(m_strLabel); + Layout(); +} + +bool PrusaCollapsiblePaneMSW::Layout() +{ + if (!m_pDisclosureTriangleButton || !m_pPane || !m_sz) + return false; // we need to complete the creation first! + + wxSize oursz(GetSize()); + + // move & resize the button and the static line + m_sz->SetDimension(0, 0, oursz.GetWidth(), m_sz->GetMinSize().GetHeight()); + m_sz->Layout(); + + if (IsExpanded()) + { + // move & resize the container window + int yoffset = m_sz->GetSize().GetHeight() + GetBorder(); + m_pPane->SetSize(0, yoffset, + oursz.x, oursz.y - yoffset); + + // this is very important to make the pane window layout show correctly + m_pPane->Layout(); + } + + return true; +} + +void PrusaCollapsiblePaneMSW::Collapse(bool collapse) +{ + // optimization + if (IsCollapsed() == collapse) + return; + + InvalidateBestSize(); + + // update our state + m_pPane->Show(!collapse); + + // update button bitmap + UpdateBtnBmp(); + + OnStateChange(GetBestSize()); +} +#endif //__WXMSW__ + +// ***************************************************************************** +// ---------------------------------------------------------------------------- +// PrusaObjectDataViewModelNode +// ---------------------------------------------------------------------------- + +void PrusaObjectDataViewModelNode::set_object_action_icon() { + m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("add_object.png")), wxBITMAP_TYPE_PNG); +} +void PrusaObjectDataViewModelNode::set_part_action_icon() { + m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG); +} + +Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr; +bool PrusaObjectDataViewModelNode::update_settings_digest(const std::vector& categories) +{ + if (m_type != itSettings || m_opt_categories == categories) + return false; + + m_opt_categories = categories; + m_name = wxEmptyString; + m_bmp = m_empty_bmp; + + std::map& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON; + + for (auto& cat : m_opt_categories) + m_name += cat + "; "; + + wxBitmap *bmp = m_bitmap_cache->find(m_name.ToStdString()); + if (bmp == nullptr) { + std::vector bmps; + for (auto& cat : m_opt_categories) + bmps.emplace_back(categories_icon.find(cat) == categories_icon.end() ? + wxNullBitmap : categories_icon.at(cat)); + bmp = m_bitmap_cache->insert(m_name.ToStdString(), bmps); + } + + m_bmp = *bmp; + + return true; +} + +// ***************************************************************************** +// ---------------------------------------------------------------------------- +// PrusaObjectDataViewModel +// ---------------------------------------------------------------------------- + +PrusaObjectDataViewModel::PrusaObjectDataViewModel() +{ + m_bitmap_cache = new Slic3r::GUI::BitmapCache; +} + +PrusaObjectDataViewModel::~PrusaObjectDataViewModel() +{ + for (auto object : m_objects) + delete object; + delete m_bitmap_cache; + m_bitmap_cache = nullptr; +} + +wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name) +{ + auto root = new PrusaObjectDataViewModelNode(name); + m_objects.push_back(root); + // notify control + wxDataViewItem child((void*)root); + wxDataViewItem parent((void*)NULL); + ItemAdded(parent, child); + return child; +} + +wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item, + const wxString &name, + const int volume_type, + const int extruder/* = 0*/, + const bool create_frst_child/* = true*/) +{ + PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); + if (!root) return wxDataViewItem(0); + + wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); + + // because of istance_root is a last item of the object + int insert_position = root->GetChildCount() - 1; + if (insert_position < 0 || root->GetNthChild(insert_position)->m_type != itInstanceRoot) + insert_position = -1; + + if (create_frst_child && root->m_volumes_cnt == 0) + { + const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, *m_volume_bmps[0], extruder_str, 0); + insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); + // notify control + const wxDataViewItem child((void*)node); + ItemAdded(parent_item, child); + + root->m_volumes_cnt++; + if (insert_position > 0) insert_position++; + } + + const auto node = new PrusaObjectDataViewModelNode(root, name, *m_volume_bmps[volume_type], extruder_str, root->m_volumes_cnt); + insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); + // notify control + const wxDataViewItem child((void*)node); + ItemAdded(parent_item, child); + root->m_volumes_cnt++; + + return child; +} + +wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item) +{ + PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); + if (!root) return wxDataViewItem(0); + + const auto node = new PrusaObjectDataViewModelNode(root, itSettings); + root->Insert(node, 0); + // notify control + const wxDataViewItem child((void*)node); + ItemAdded(parent_item, child); + return child; +} + +int get_istances_root_idx(PrusaObjectDataViewModelNode *parent_node) +{ + // because of istance_root is a last item of the object + const int inst_root_idx = parent_node->GetChildCount()-1; + + if (inst_root_idx < 0 || parent_node->GetNthChild(inst_root_idx)->m_type == itInstanceRoot) + return inst_root_idx; + + return -1; +} + +wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num) +{ + PrusaObjectDataViewModelNode *parent_node = (PrusaObjectDataViewModelNode*)parent_item.GetID(); + if (!parent_node) return wxDataViewItem(0); + + // Check and create/get instances root node + const int inst_root_id = get_istances_root_idx(parent_node); + + PrusaObjectDataViewModelNode *inst_root_node = inst_root_id < 0 ? + new PrusaObjectDataViewModelNode(parent_node, itInstanceRoot) : + parent_node->GetNthChild(inst_root_id); + const wxDataViewItem inst_root_item((void*)inst_root_node); + + if (inst_root_id < 0) { + parent_node->Append(inst_root_node); + // notify control + ItemAdded(parent_item, inst_root_item); +// if (num == 1) num++; + } + + // Add instance nodes + PrusaObjectDataViewModelNode *instance_node = nullptr; + size_t counter = 0; + while (counter < num) { + instance_node = new PrusaObjectDataViewModelNode(inst_root_node, itInstance); + inst_root_node->Append(instance_node); + // notify control + const wxDataViewItem instance_item((void*)instance_node); + ItemAdded(inst_root_item, instance_item); + ++counter; + } + + return wxDataViewItem((void*)instance_node); +} + +wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) +{ + auto ret_item = wxDataViewItem(0); + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (!node) // happens if item.IsOk()==false + return ret_item; + + auto node_parent = node->GetParent(); + wxDataViewItem parent(node_parent); + + // first remove the node from the parent's array of children; + // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ + // thus removing the node from it doesn't result in freeing it + if (node_parent) { + if (node->m_type == itInstanceRoot) + { + for (int i = node->GetChildCount() - 1; i > 0; i--) + Delete(wxDataViewItem(node->GetNthChild(i))); + return parent; + } + + auto id = node_parent->GetChildren().Index(node); + auto idx = node->GetIdx(); + + + if (node->m_type == itVolume) { + node_parent->m_volumes_cnt--; + DeleteSettings(item); + } + node_parent->GetChildren().Remove(node); + + if (id > 0) { + if(id == node_parent->GetChildCount()) id--; + ret_item = wxDataViewItem(node_parent->GetChildren().Item(id)); + } + + //update idx value for remaining child-nodes + auto children = node_parent->GetChildren(); + for (size_t i = 0; i < node_parent->GetChildCount() && idx>=0; i++) + { + auto cur_idx = children[i]->GetIdx(); + if (cur_idx > idx) + children[i]->SetIdx(cur_idx-1); + } + + // if there is last instance item, delete both of it and instance root item + if (node_parent->GetChildCount() == 1 && node_parent->GetNthChild(0)->m_type == itInstance) + { + delete node; + ItemDeleted(parent, item); + + PrusaObjectDataViewModelNode *last_instance_node = node_parent->GetNthChild(0); + node_parent->GetChildren().Remove(last_instance_node); + delete last_instance_node; + ItemDeleted(parent, wxDataViewItem(last_instance_node)); + + PrusaObjectDataViewModelNode *obj_node = node_parent->GetParent(); + obj_node->GetChildren().Remove(node_parent); + delete node_parent; + ret_item = wxDataViewItem(obj_node); + +#ifndef __WXGTK__ + if (obj_node->GetChildCount() == 0) + obj_node->m_container = false; +#endif //__WXGTK__ + ItemDeleted(ret_item, wxDataViewItem(node_parent)); + return ret_item; + } + + // if there is last volume item after deleting, delete this last volume too + if (node_parent->GetChildCount() <= 3) + { + int vol_cnt = 0; + int vol_idx = 0; + for (int i = 0; i < node_parent->GetChildCount(); ++i) { + if (node_parent->GetNthChild(i)->GetType() == itVolume) { + vol_idx = i; + vol_cnt++; + } + if (vol_cnt > 1) + break; + } + + if (vol_cnt == 1) { + delete node; + ItemDeleted(parent, item); + + PrusaObjectDataViewModelNode *last_child_node = node_parent->GetNthChild(vol_idx); + DeleteSettings(wxDataViewItem(last_child_node)); + node_parent->GetChildren().Remove(last_child_node); + node_parent->m_volumes_cnt = 0; + delete last_child_node; + +#ifndef __WXGTK__ + if (node_parent->GetChildCount() == 0) + node_parent->m_container = false; +#endif //__WXGTK__ + ItemDeleted(parent, wxDataViewItem(last_child_node)); + + wxCommandEvent event(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED); + auto it = find(m_objects.begin(), m_objects.end(), node_parent); + event.SetInt(it == m_objects.end() ? -1 : it - m_objects.begin()); + wxPostEvent(m_ctrl, event); + + ret_item = parent; + + return ret_item; + } + } + } + else + { + auto it = find(m_objects.begin(), m_objects.end(), node); + auto id = it - m_objects.begin(); + if (it != m_objects.end()) + { + // Delete all sub-items + int i = m_objects[id]->GetChildCount() - 1; + while (i >= 0) { + Delete(wxDataViewItem(m_objects[id]->GetNthChild(i))); + i = m_objects[id]->GetChildCount() - 1; + } + m_objects.erase(it); + } + if (id > 0) { + if(id == m_objects.size()) id--; + ret_item = wxDataViewItem(m_objects[id]); + } + } + // free the node + delete node; + + // set m_containet to FALSE if parent has no child + if (node_parent) { +#ifndef __WXGTK__ + if (node_parent->GetChildCount() == 0) + node_parent->m_container = false; +#endif //__WXGTK__ + ret_item = parent; + } + + // notify control + ItemDeleted(parent, item); + return ret_item; +} + +wxDataViewItem PrusaObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &parent_item, size_t num) +{ + auto ret_item = wxDataViewItem(0); + PrusaObjectDataViewModelNode *parent_node = (PrusaObjectDataViewModelNode*)parent_item.GetID(); + if (!parent_node) return ret_item; + + const int inst_root_id = get_istances_root_idx(parent_node); + if (inst_root_id < 0) return ret_item; + + wxDataViewItemArray items; + PrusaObjectDataViewModelNode *inst_root_node = parent_node->GetNthChild(inst_root_id); + const wxDataViewItem inst_root_item((void*)inst_root_node); + + const int inst_cnt = inst_root_node->GetChildCount(); + const bool delete_inst_root_item = inst_cnt - num < 2 ? true : false; + + int stop = delete_inst_root_item ? 0 : inst_cnt - num; + for (int i = inst_cnt - 1; i >= stop;--i) { + PrusaObjectDataViewModelNode *last_instance_node = inst_root_node->GetNthChild(i); + inst_root_node->GetChildren().Remove(last_instance_node); + delete last_instance_node; + ItemDeleted(inst_root_item, wxDataViewItem(last_instance_node)); + } + + if (delete_inst_root_item) { + ret_item = parent_item; + parent_node->GetChildren().Remove(inst_root_node); + ItemDeleted(parent_item, inst_root_item); +#ifndef __WXGTK__ + if (parent_node->GetChildCount() == 0) + parent_node->m_container = false; +#endif //__WXGTK__ + } + + return ret_item; +} + +void PrusaObjectDataViewModel::DeleteAll() +{ + while (!m_objects.empty()) + { + auto object = m_objects.back(); +// object->RemoveAllChildren(); + Delete(wxDataViewItem(object)); + } +} + +void PrusaObjectDataViewModel::DeleteChildren(wxDataViewItem& parent) +{ + PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent.GetID(); + if (!root) // happens if item.IsOk()==false + return; + + // first remove the node from the parent's array of children; + // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ + // thus removing the node from it doesn't result in freeing it + auto& children = root->GetChildren(); + for (int id = root->GetChildCount() - 1; id >= 0; --id) + { + auto node = children[id]; + auto item = wxDataViewItem(node); + children.RemoveAt(id); + + if (node->m_type == itVolume) + root->m_volumes_cnt--; + + // free the node + delete node; + + // notify control + ItemDeleted(parent, item); + } + + // set m_containet to FALSE if parent has no child +#ifndef __WXGTK__ + root->m_container = false; +#endif //__WXGTK__ +} + +void PrusaObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent) +{ + PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent.GetID(); + if (!root) // happens if item.IsOk()==false + return; + + // first remove the node from the parent's array of children; + // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ + // thus removing the node from it doesn't result in freeing it + auto& children = root->GetChildren(); + for (int id = root->GetChildCount() - 1; id >= 0; --id) + { + auto node = children[id]; + if (node->m_type != itVolume) + continue; + + auto item = wxDataViewItem(node); + DeleteSettings(item); + children.RemoveAt(id); + + // free the node + delete node; + + // notify control + ItemDeleted(parent, item); + } + root->m_volumes_cnt = 0; + + // set m_containet to FALSE if parent has no child +#ifndef __WXGTK__ + root->m_container = false; +#endif //__WXGTK__ +} + +void PrusaObjectDataViewModel::DeleteSettings(const wxDataViewItem& parent) +{ + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent.GetID(); + if (!node) return; + + // if volume has a "settings"item, than delete it before volume deleting + if (node->GetChildCount() > 0 && node->GetNthChild(0)->GetType() == itSettings) { + auto settings_node = node->GetNthChild(0); + auto settings_item = wxDataViewItem(settings_node); + node->GetChildren().RemoveAt(0); + delete settings_node; + ItemDeleted(parent, settings_item); + } +} + +wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx) +{ + if (obj_idx >= m_objects.size()) + { + printf("Error! Out of objects range.\n"); + return wxDataViewItem(0); + } + return wxDataViewItem(m_objects[obj_idx]); +} + + +wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx) +{ + if (obj_idx >= m_objects.size() || obj_idx < 0) { + printf("Error! Out of objects range.\n"); + return wxDataViewItem(0); + } + + auto parent = m_objects[obj_idx]; + if (parent->GetChildCount() == 0 || + (parent->GetChildCount() == 1 && parent->GetNthChild(0)->GetType() & itSettings )) { + if (volume_idx == 0) + return GetItemById(obj_idx); + + printf("Error! Object has no one volume.\n"); + return wxDataViewItem(0); + } + + for (size_t i = 0; i < parent->GetChildCount(); i++) + if (parent->GetNthChild(i)->m_idx == volume_idx && parent->GetNthChild(i)->GetType() & itVolume) + return wxDataViewItem(parent->GetNthChild(i)); + + return wxDataViewItem(0); +} + +wxDataViewItem PrusaObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx) +{ + if (obj_idx >= m_objects.size() || obj_idx < 0) { + printf("Error! Out of objects range.\n"); + return wxDataViewItem(0); + } + + auto instances_item = GetInstanceRootItem(wxDataViewItem(m_objects[obj_idx])); + if (!instances_item) + return wxDataViewItem(0); + + auto parent = (PrusaObjectDataViewModelNode*)instances_item.GetID();; + for (size_t i = 0; i < parent->GetChildCount(); i++) + if (parent->GetNthChild(i)->m_idx == inst_idx) + return wxDataViewItem(parent->GetNthChild(i)); + + return wxDataViewItem(0); +} + +int PrusaObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) +{ + wxASSERT(item.IsOk()); + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + auto it = find(m_objects.begin(), m_objects.end(), node); + if (it == m_objects.end()) + return -1; + + return it - m_objects.begin(); +} + +int PrusaObjectDataViewModel::GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const +{ + wxASSERT(item.IsOk()); + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (!node || node->m_type != type) + return -1; + return node->GetIdx(); +} + +int PrusaObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) const +{ + return GetIdByItemAndType(item, itVolume); +} + +int PrusaObjectDataViewModel::GetInstanceIdByItem(const wxDataViewItem& item) const +{ + return GetIdByItemAndType(item, itInstance); +} + +void PrusaObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx) +{ + wxASSERT(item.IsOk()); + type = itUndef; + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (!node || node->GetIdx() <-1 || node->GetIdx() ==-1 && !(node->GetType() & (itObject | itSettings | itInstanceRoot))) + return; + + idx = node->GetIdx(); + type = node->GetType(); + + PrusaObjectDataViewModelNode *parent_node = node->GetParent(); + if (!parent_node) return; + if (type == itInstance) + parent_node = node->GetParent()->GetParent(); + if (!parent_node || parent_node->m_type != itObject) { type = itUndef; return; } + + auto it = find(m_objects.begin(), m_objects.end(), parent_node); + if (it != m_objects.end()) + obj_idx = it - m_objects.begin(); + else + type = itUndef; +} + +wxString PrusaObjectDataViewModel::GetName(const wxDataViewItem &item) const +{ + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (!node) // happens if item.IsOk()==false + return wxEmptyString; + + return node->m_name; +} + +wxBitmap& PrusaObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const +{ + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + return node->m_bmp; +} + +void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const +{ + wxASSERT(item.IsOk()); + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + switch (col) + { + case 0: + variant << PrusaDataViewBitmapText(node->m_name, node->m_bmp); + break; + case 1: + variant = node->m_extruder; + break; + case 2: + variant << node->m_action_icon; + break; + default: + ; + } +} + +bool PrusaObjectDataViewModel::SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned int col) +{ + wxASSERT(item.IsOk()); + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + return node->SetValue(variant, col); +} + +bool PrusaObjectDataViewModel::SetValue(const wxVariant &variant, const int item_idx, unsigned int col) +{ + if (item_idx < 0 || item_idx >= m_objects.size()) + return false; + + return m_objects[item_idx]->SetValue(variant, col); +} +/* +wxDataViewItem PrusaObjectDataViewModel::MoveChildUp(const wxDataViewItem &item) +{ + auto ret_item = wxDataViewItem(0); + wxASSERT(item.IsOk()); + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (!node) // happens if item.IsOk()==false + return ret_item; + + auto node_parent = node->GetParent(); + if (!node_parent) // If isn't part, but object + return ret_item; + + auto volume_id = node->GetVolumeId(); + if (0 < volume_id && volume_id < node_parent->GetChildCount()) { + node_parent->SwapChildrens(volume_id - 1, volume_id); + ret_item = wxDataViewItem(node_parent->GetNthChild(volume_id - 1)); + ItemChanged(item); + ItemChanged(ret_item); + } + else + ret_item = wxDataViewItem(node_parent->GetNthChild(0)); + return ret_item; +} + +wxDataViewItem PrusaObjectDataViewModel::MoveChildDown(const wxDataViewItem &item) +{ + auto ret_item = wxDataViewItem(0); + wxASSERT(item.IsOk()); + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (!node) // happens if item.IsOk()==false + return ret_item; + + auto node_parent = node->GetParent(); + if (!node_parent) // If isn't part, but object + return ret_item; + + auto volume_id = node->GetVolumeId(); + if (0 <= volume_id && volume_id+1 < node_parent->GetChildCount()) { + node_parent->SwapChildrens(volume_id + 1, volume_id); + ret_item = wxDataViewItem(node_parent->GetNthChild(volume_id + 1)); + ItemChanged(item); + ItemChanged(ret_item); + } + else + ret_item = wxDataViewItem(node_parent->GetNthChild(node_parent->GetChildCount()-1)); + return ret_item; +} +*/ +wxDataViewItem PrusaObjectDataViewModel::ReorganizeChildren(int current_volume_id, int new_volume_id, const wxDataViewItem &parent) +{ + auto ret_item = wxDataViewItem(0); + if (current_volume_id == new_volume_id) + return ret_item; + wxASSERT(parent.IsOk()); + PrusaObjectDataViewModelNode *node_parent = (PrusaObjectDataViewModelNode*)parent.GetID(); + if (!node_parent) // happens if item.IsOk()==false + return ret_item; + + const size_t shift = node_parent->GetChildren().Item(0)->m_type == itSettings ? 1 : 0; + + PrusaObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift); + node_parent->GetChildren().Remove(deleted_node); + ItemDeleted(parent, wxDataViewItem(deleted_node)); + node_parent->Insert(deleted_node, new_volume_id+shift); + ItemAdded(parent, wxDataViewItem(deleted_node)); + const auto settings_item = GetSettingsItem(wxDataViewItem(deleted_node)); + if (settings_item) + ItemAdded(wxDataViewItem(deleted_node), settings_item); + + //update volume_id value for child-nodes + auto children = node_parent->GetChildren(); + int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id; + int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id; + for (int id = id_frst; id <= id_last; ++id) + children[id+shift]->SetIdx(id); + + return wxDataViewItem(node_parent->GetNthChild(new_volume_id+shift)); +} + +bool PrusaObjectDataViewModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const +{ + wxASSERT(item.IsOk()); + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + + // disable extruder selection for the "Settings" item + return !(col == 2 && node->m_extruder.IsEmpty()); +} + +wxDataViewItem PrusaObjectDataViewModel::GetParent(const wxDataViewItem &item) const +{ + // the invisible root node has no parent + if (!item.IsOk()) + return wxDataViewItem(0); + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + + // objects nodes has no parent too + if (node->m_type == itObject) + return wxDataViewItem(0); + + return wxDataViewItem((void*)node->GetParent()); +} + +wxDataViewItem PrusaObjectDataViewModel::GetTopParent(const wxDataViewItem &item) const +{ + // the invisible root node has no parent + if (!item.IsOk()) + return wxDataViewItem(0); + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (node->m_type == itObject) + return item; + + PrusaObjectDataViewModelNode *parent_node = node->GetParent(); + while (parent_node->m_type != itObject) + { + node = parent_node; + parent_node = node->GetParent(); + } + + return wxDataViewItem((void*)parent_node); +} + +bool PrusaObjectDataViewModel::IsContainer(const wxDataViewItem &item) const +{ + // the invisible root node can have children + if (!item.IsOk()) + return true; + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + return node->IsContainer(); +} + +unsigned int PrusaObjectDataViewModel::GetChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const +{ + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent.GetID(); + if (!node) + { + for (auto object : m_objects) + array.Add(wxDataViewItem((void*)object)); + return m_objects.size(); + } + + if (node->GetChildCount() == 0) + { + return 0; + } + + unsigned int count = node->GetChildren().GetCount(); + for (unsigned int pos = 0; pos < count; pos++) + { + PrusaObjectDataViewModelNode *child = node->GetChildren().Item(pos); + array.Add(wxDataViewItem((void*)child)); + } + + return count; +} + +ItemType PrusaObjectDataViewModel::GetItemType(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return itUndef; + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + return node->m_type; +} + +wxDataViewItem PrusaObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const +{ + if (!parent_item.IsOk()) + return wxDataViewItem(0); + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent_item.GetID(); + if (node->GetChildCount() == 0) + return wxDataViewItem(0); + + for (int i = 0; i < node->GetChildCount(); i++) { + if (node->GetNthChild(i)->m_type == type) + return wxDataViewItem((void*)node->GetNthChild(i)); + } + + return wxDataViewItem(0); +} + +wxDataViewItem PrusaObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const +{ + return GetItemByType(item, itSettings); +} + +wxDataViewItem PrusaObjectDataViewModel::GetInstanceRootItem(const wxDataViewItem &item) const +{ + return GetItemByType(item, itInstanceRoot); +} + +bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return false; + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + return node->m_type == itSettings; +} + + + +void PrusaObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item, + const std::vector& categories) +{ + if (!item.IsOk()) return; + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (!node->update_settings_digest(categories)) + return; + ItemChanged(item); +} + +void PrusaObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const int type) +{ + if (!item.IsOk() || GetItemType(item) != itVolume) + return; + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + node->SetBitmap(*m_volume_bmps[type]); + ItemChanged(item); +} + +//----------------------------------------------------------------------------- +// PrusaDataViewBitmapText +//----------------------------------------------------------------------------- + +wxIMPLEMENT_DYNAMIC_CLASS(PrusaDataViewBitmapText, wxObject) + +IMPLEMENT_VARIANT_OBJECT(PrusaDataViewBitmapText) + +// --------------------------------------------------------- +// PrusaIconTextRenderer +// --------------------------------------------------------- + +bool PrusaBitmapTextRenderer::SetValue(const wxVariant &value) +{ + m_value << value; + return true; +} + +bool PrusaBitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const +{ + return false; +} + +bool PrusaBitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) +{ + int xoffset = 0; + + const wxBitmap& icon = m_value.GetBitmap(); + if (icon.IsOk()) + { + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); + xoffset = icon.GetWidth() + 4; + } + + RenderText(m_value.GetText(), xoffset, rect, dc, state); + + return true; +} + +wxSize PrusaBitmapTextRenderer::GetSize() const +{ + if (!m_value.GetText().empty()) + { + wxSize size = GetTextExtent(m_value.GetText()); + + if (m_value.GetBitmap().IsOk()) + size.x += m_value.GetBitmap().GetWidth() + 4; + return size; + } + return wxSize(80, 20); +} + + +// ---------------------------------------------------------------------------- +// PrusaDoubleSlider +// ---------------------------------------------------------------------------- +PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent, + wxWindowID id, + int lowerValue, + int higherValue, + int minValue, + int maxValue, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& val, + const wxString& name) : + wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), + m_lower_value(lowerValue), m_higher_value (higherValue), + m_min_value(minValue), m_max_value(maxValue), + m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL) +{ +#ifndef __WXOSX__ // SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX + SetDoubleBuffered(true); +#endif //__WXOSX__ + + m_bmp_thumb_higher = wxBitmap(style == wxSL_HORIZONTAL ? Slic3r::GUI::from_u8(Slic3r::var("right_half_circle.png")) : + Slic3r::GUI::from_u8(Slic3r::var("up_half_circle.png")), wxBITMAP_TYPE_PNG); + m_bmp_thumb_lower = wxBitmap(style == wxSL_HORIZONTAL ? Slic3r::GUI::from_u8(Slic3r::var("left_half_circle.png")) : + Slic3r::GUI::from_u8(Slic3r::var("down_half_circle.png")), wxBITMAP_TYPE_PNG); + m_thumb_size = m_bmp_thumb_lower.GetSize(); + + m_bmp_add_tick_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_add_on.png")), wxBITMAP_TYPE_PNG); + m_bmp_add_tick_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_add_off.png")), wxBITMAP_TYPE_PNG); + m_bmp_del_tick_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG); + m_bmp_del_tick_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_delete_off.png")), wxBITMAP_TYPE_PNG); + m_tick_icon_dim = m_bmp_add_tick_on.GetSize().x; + + m_bmp_one_layer_lock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG); + m_bmp_one_layer_lock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_off.png")), wxBITMAP_TYPE_PNG); + m_bmp_one_layer_unlock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_on.png")), wxBITMAP_TYPE_PNG); + m_bmp_one_layer_unlock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_off.png")), wxBITMAP_TYPE_PNG); + m_lock_icon_dim = m_bmp_one_layer_lock_on.GetSize().x; + + m_selection = ssUndef; + + // slider events + Bind(wxEVT_PAINT, &PrusaDoubleSlider::OnPaint, this); + Bind(wxEVT_LEFT_DOWN, &PrusaDoubleSlider::OnLeftDown, this); + Bind(wxEVT_MOTION, &PrusaDoubleSlider::OnMotion, this); + Bind(wxEVT_LEFT_UP, &PrusaDoubleSlider::OnLeftUp, this); + Bind(wxEVT_MOUSEWHEEL, &PrusaDoubleSlider::OnWheel, this); + Bind(wxEVT_ENTER_WINDOW,&PrusaDoubleSlider::OnEnterWin, this); + Bind(wxEVT_LEAVE_WINDOW,&PrusaDoubleSlider::OnLeaveWin, this); + Bind(wxEVT_KEY_DOWN, &PrusaDoubleSlider::OnKeyDown, this); + Bind(wxEVT_KEY_UP, &PrusaDoubleSlider::OnKeyUp, this); + Bind(wxEVT_RIGHT_DOWN, &PrusaDoubleSlider::OnRightDown,this); + Bind(wxEVT_RIGHT_UP, &PrusaDoubleSlider::OnRightUp, this); + + // control's view variables + SLIDER_MARGIN = 4 + (style == wxSL_HORIZONTAL ? m_bmp_thumb_higher.GetWidth() : m_bmp_thumb_higher.GetHeight()); + + DARK_ORANGE_PEN = wxPen(wxColour(253, 84, 2)); + ORANGE_PEN = wxPen(wxColour(253, 126, 66)); + LIGHT_ORANGE_PEN = wxPen(wxColour(254, 177, 139)); + + DARK_GREY_PEN = wxPen(wxColour(128, 128, 128)); + GREY_PEN = wxPen(wxColour(164, 164, 164)); + LIGHT_GREY_PEN = wxPen(wxColour(204, 204, 204)); + + line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN }; + segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN }; +} + +int PrusaDoubleSlider::GetActiveValue() const +{ + return m_selection == ssLower ? + m_lower_value : m_selection == ssHigher ? + m_higher_value : -1; +} + +wxSize PrusaDoubleSlider::DoGetBestSize() const +{ + const wxSize size = wxControl::DoGetBestSize(); + if (size.x > 1 && size.y > 1) + return size; + const int new_size = is_horizontal() ? 80 : 120; + return wxSize(new_size, new_size); +} + +void PrusaDoubleSlider::SetLowerValue(const int lower_val) +{ + m_selection = ssLower; + m_lower_value = lower_val; + correct_lower_value(); + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void PrusaDoubleSlider::SetHigherValue(const int higher_val) +{ + m_selection = ssHigher; + m_higher_value = higher_val; + correct_higher_value(); + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void PrusaDoubleSlider::SetMaxValue(const int max_value) +{ + m_max_value = max_value; + Refresh(); + Update(); +} + +void PrusaDoubleSlider::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) +{ + int width; + int height; + get_size(&width, &height); + + wxCoord line_beg_x = is_horizontal() ? SLIDER_MARGIN : width*0.5 - 1; + wxCoord line_beg_y = is_horizontal() ? height*0.5 - 1 : SLIDER_MARGIN; + wxCoord line_end_x = is_horizontal() ? width - SLIDER_MARGIN + 1 : width*0.5 - 1; + wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1; + + wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1; + wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos-1; + wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1; + wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1; + + for (int id = 0; id < line_pens.size(); id++) + { + dc.SetPen(*line_pens[id]); + dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y); + dc.SetPen(*segm_pens[id]); + dc.DrawLine(segm_beg_x, segm_beg_y, segm_end_x, segm_end_y); + if (is_horizontal()) + line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1; + else + line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1; + } +} + +double PrusaDoubleSlider::get_scroll_step() +{ + const wxSize sz = get_size(); + const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y; + return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value); +} + +// get position on the slider line from entered value +wxCoord PrusaDoubleSlider::get_position_from_value(const int value) +{ + const double step = get_scroll_step(); + const int val = is_horizontal() ? value : m_max_value - value; + return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); +} + +wxSize PrusaDoubleSlider::get_size() +{ + int w, h; + get_size(&w, &h); + return wxSize(w, h); +} + +void PrusaDoubleSlider::get_size(int *w, int *h) +{ + GetSize(w, h); + is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; +} + +double PrusaDoubleSlider::get_double_value(const SelectedSlider& selection) const +{ + if (m_values.empty()) + return 0.0; + return m_values[selection == ssLower ? m_lower_value : m_higher_value].second; +} + +std::vector PrusaDoubleSlider::GetTicksValues() const +{ + std::vector values; + + if (!m_values.empty()) + for (auto tick : m_ticks) + values.push_back(m_values[tick].second); + + return values; +} + +void PrusaDoubleSlider::SetTicksValues(const std::vector& heights) +{ + if (m_values.empty()) + return; + + m_ticks.clear(); + unsigned int i = 0; + for (auto h : heights) { + while (i < m_values.size() && m_values[i].second - 1e-6 < h) + ++i; + if (i == m_values.size()) + return; + m_ticks.insert(i); + } + +} + +void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) +{ + const double step = get_scroll_step(); + if (is_horizontal()) { + lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5); + higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5); + } + else { + lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5); + higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5); + } +} + +void PrusaDoubleSlider::draw_focus_rect() +{ + if (!m_is_focused) + return; + const wxSize sz = GetSize(); + wxPaintDC dc(this); + const wxPen pen = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT); + dc.SetPen(pen); + dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT)); + dc.DrawRectangle(1, 1, sz.x - 2, sz.y - 2); +} + +void PrusaDoubleSlider::render() +{ + SetBackgroundColour(GetParent()->GetBackgroundColour()); + draw_focus_rect(); + + wxPaintDC dc(this); + wxFont font = dc.GetFont(); + const wxFont smaller_font = font.Smaller(); + dc.SetFont(smaller_font); + + const wxCoord lower_pos = get_position_from_value(m_lower_value); + const wxCoord higher_pos = get_position_from_value(m_higher_value); + + // draw line + draw_scroll_line(dc, lower_pos, higher_pos); + +// //lower slider: +// draw_thumb(dc, lower_pos, ssLower); +// //higher slider: +// draw_thumb(dc, higher_pos, ssHigher); + + // draw both sliders + draw_thumbs(dc, lower_pos, higher_pos); + + //draw color print ticks + draw_ticks(dc); + + //draw color print ticks + draw_one_layer_icon(dc); +} + +void PrusaDoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off : &m_bmp_add_tick_on; + if (m_ticks.find(tick) != m_ticks.end()) + icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off : &m_bmp_del_tick_on; + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; + if (m_selection == ssLower) + is_horizontal() ? y_draw = pt_end.y + 3 : x_draw = pt_beg.x - m_tick_icon_dim-2; + else + is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3; + + dc.DrawBitmap(*icon, x_draw, y_draw); + + //update rect of the tick action icon + m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); +} + +void PrusaDoubleSlider::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const SelectedSlider selection) +{ + if (m_selection == selection) { + //draw info line + dc.SetPen(DARK_ORANGE_PEN); + const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y - 1); + const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y - 1); + dc.DrawLine(pt_beg, pt_end); + + //draw action icon + draw_action_icon(dc, pt_beg, pt_end); + } +} + +wxString PrusaDoubleSlider::get_label(const SelectedSlider& selection) const +{ + const int value = selection == ssLower ? m_lower_value : m_higher_value; + + if (m_label_koef == 1.0 && m_values.empty()) + return wxString::Format("%d", value); + if (value >= m_values.size()) + return "ErrVal"; + + const wxString str = m_values.empty() ? + wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : + wxNumberFormatter::ToString(m_values[value].second, 2, wxNumberFormatter::Style_None); + return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : m_values[value].first); +} + +void PrusaDoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const +{ + if ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection || !selection) + return; + wxCoord text_width, text_height; + const wxString label = get_label(selection); + dc.GetMultiLineTextExtent(label, &text_width, &text_height); + wxPoint text_pos; + if (selection ==ssLower) + text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) : + wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1); + else + text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) : + wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1); + dc.DrawText(label, text_pos); +} + +void PrusaDoubleSlider::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) +{ + wxCoord x_draw, y_draw; + if (selection == ssLower) { + if (is_horizontal()) { + x_draw = pos.x - m_thumb_size.x; + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + else { + x_draw = pos.x - int(0.5*m_thumb_size.x); + y_draw = pos.y; + } + } + else{ + if (is_horizontal()) { + x_draw = pos.x; + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + else { + x_draw = pos.x - int(0.5*m_thumb_size.x); + y_draw = pos.y - m_thumb_size.y; + } + } + dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower : m_bmp_thumb_higher, x_draw, y_draw); + + // Update thumb rect + update_thumb_rect(x_draw, y_draw, selection); +} + +void PrusaDoubleSlider::draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection) +{ + //calculate thumb position on slider line + int width, height; + get_size(&width, &height); + const wxPoint pos = is_horizontal() ? wxPoint(pos_coord, height*0.5) : wxPoint(0.5*width, pos_coord); + + // Draw thumb + draw_thumb_item(dc, pos, selection); + + // Draw info_line + draw_info_line_with_icon(dc, pos, selection); + + // Draw thumb text + draw_thumb_text(dc, pos, selection); +} + +void PrusaDoubleSlider::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos) +{ + //calculate thumb position on slider line + int width, height; + get_size(&width, &height); + const wxPoint pos_l = is_horizontal() ? wxPoint(lower_pos, height*0.5) : wxPoint(0.5*width, lower_pos); + const wxPoint pos_h = is_horizontal() ? wxPoint(higher_pos, height*0.5) : wxPoint(0.5*width, higher_pos); + + // Draw lower thumb + draw_thumb_item(dc, pos_l, ssLower); + // Draw lower info_line + draw_info_line_with_icon(dc, pos_l, ssLower); + + // Draw higher thumb + draw_thumb_item(dc, pos_h, ssHigher); + // Draw higher info_line + draw_info_line_with_icon(dc, pos_h, ssHigher); + // Draw higher thumb text + draw_thumb_text(dc, pos_h, ssHigher); + + // Draw lower thumb text + draw_thumb_text(dc, pos_l, ssLower); +} + +void PrusaDoubleSlider::draw_ticks(wxDC& dc) +{ + dc.SetPen(DARK_GREY_PEN); + int height, width; + get_size(&width, &height); + const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; + for (auto tick : m_ticks) + { + const wxCoord pos = get_position_from_value(tick); + + is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) : + dc.DrawLine(mid - 14, pos - 1, mid - 9, pos - 1); + is_horizontal() ? dc.DrawLine(pos, mid+14, pos, mid+9) : + dc.DrawLine(mid + 14, pos - 1, mid + 9, pos - 1); + } +} + +void PrusaDoubleSlider::draw_one_layer_icon(wxDC& dc) +{ + wxBitmap* icon = m_is_one_layer ? + m_is_one_layer_icon_focesed ? &m_bmp_one_layer_lock_off : &m_bmp_one_layer_lock_on : + m_is_one_layer_icon_focesed ? &m_bmp_one_layer_unlock_off : &m_bmp_one_layer_unlock_on; + + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; + is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; + + dc.DrawBitmap(*icon, x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); +} + +void PrusaDoubleSlider::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection) +{ + const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, m_thumb_size.y); + if (selection == ssLower) + m_rect_lower_thumb = rect; + else + m_rect_higher_thumb = rect; +} + +int PrusaDoubleSlider::get_value_from_position(const wxCoord x, const wxCoord y) +{ + const int height = get_size().y; + const double step = get_scroll_step(); + + if (is_horizontal()) + return int(double(x - SLIDER_MARGIN) / step + 0.5); + else + return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5); +} + +void PrusaDoubleSlider::detect_selected_slider(const wxPoint& pt, const bool is_mouse_wheel /*= false*/) +{ + if (is_mouse_wheel) + { + if (is_horizontal()) { + m_selection = pt.x <= m_rect_lower_thumb.GetRight() ? ssLower : + pt.x >= m_rect_higher_thumb.GetLeft() ? ssHigher : ssUndef; + } + else { + m_selection = pt.y >= m_rect_lower_thumb.GetTop() ? ssLower : + pt.y <= m_rect_higher_thumb.GetBottom() ? ssHigher : ssUndef; + } + return; + } + + m_selection = is_point_in_rect(pt, m_rect_lower_thumb) ? ssLower : + is_point_in_rect(pt, m_rect_higher_thumb) ? ssHigher : ssUndef; +} + +bool PrusaDoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect) +{ + if (rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() && + rect.GetTop() <= pt.y && pt.y <= rect.GetBottom()) + return true; + return false; +} + +void PrusaDoubleSlider::ChangeOneLayerLock() +{ + m_is_one_layer = !m_is_one_layer; + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void PrusaDoubleSlider::OnLeftDown(wxMouseEvent& event) +{ + this->CaptureMouse(); + wxClientDC dc(this); + wxPoint pos = event.GetLogicalPosition(dc); + if (is_point_in_rect(pos, m_rect_tick_action)) { + action_tick(taOnIcon); + return; + } + + m_is_left_down = true; + if (is_point_in_rect(pos, m_rect_one_layer_icon)) { + m_is_one_layer = !m_is_one_layer; + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + } + else + detect_selected_slider(pos); + + Refresh(); + Update(); + event.Skip(); +} + +void PrusaDoubleSlider::correct_lower_value() +{ + if (m_lower_value < m_min_value) + m_lower_value = m_min_value; + else if (m_lower_value > m_max_value) + m_lower_value = m_max_value; + + if (m_lower_value >= m_higher_value && m_lower_value <= m_max_value || m_is_one_layer) + m_higher_value = m_lower_value; +} + +void PrusaDoubleSlider::correct_higher_value() +{ + if (m_higher_value > m_max_value) + m_higher_value = m_max_value; + else if (m_higher_value < m_min_value) + m_higher_value = m_min_value; + + if (m_higher_value <= m_lower_value && m_higher_value >= m_min_value || m_is_one_layer) + m_lower_value = m_higher_value; +} + +void PrusaDoubleSlider::OnMotion(wxMouseEvent& event) +{ + const wxClientDC dc(this); + const wxPoint pos = event.GetLogicalPosition(dc); + m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon); + if (!m_is_left_down && !m_is_one_layer) { + m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action); + } + else if (m_is_left_down || m_is_right_down) { + if (m_selection == ssLower) { + m_lower_value = get_value_from_position(pos.x, pos.y); + correct_lower_value(); + } + else if (m_selection == ssHigher) { + m_higher_value = get_value_from_position(pos.x, pos.y); + correct_higher_value(); + } + } + Refresh(); + Update(); + event.Skip(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void PrusaDoubleSlider::OnLeftUp(wxMouseEvent& event) +{ + this->ReleaseMouse(); + m_is_left_down = false; + Refresh(); + Update(); + event.Skip(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void PrusaDoubleSlider::enter_window(wxMouseEvent& event, const bool enter) +{ + m_is_focused = enter; + Refresh(); + Update(); + event.Skip(); +} + +// "condition" have to be true for: +// - value increase (if wxSL_VERTICAL) +// - value decrease (if wxSL_HORIZONTAL) +void PrusaDoubleSlider::move_current_thumb(const bool condition) +{ + m_is_one_layer = wxGetKeyState(WXK_CONTROL); + int delta = condition ? -1 : 1; + if (is_horizontal()) + delta *= -1; + + if (m_selection == ssLower) { + m_lower_value -= delta; + correct_lower_value(); + } + else if (m_selection == ssHigher) { + m_higher_value -= delta; + correct_higher_value(); + } + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void PrusaDoubleSlider::action_tick(const TicksAction action) +{ + if (m_selection == ssUndef) + return; + + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + + if (action == taOnIcon && !m_ticks.insert(tick).second) + m_ticks.erase(tick); + else { + const auto it = m_ticks.find(tick); + if (it == m_ticks.end() && action == taAdd) + m_ticks.insert(tick); + else if (it != m_ticks.end() && action == taDel) + m_ticks.erase(tick); + else { + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + return; + } + } + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + Refresh(); + Update(); +} + +void PrusaDoubleSlider::OnWheel(wxMouseEvent& event) +{ + wxClientDC dc(this); + wxPoint pos = event.GetLogicalPosition(dc); + detect_selected_slider(pos, true); + + if (m_selection == ssUndef) + return; + + move_current_thumb(event.GetWheelRotation() > 0); +} + +void PrusaDoubleSlider::OnKeyDown(wxKeyEvent &event) +{ + const int key = event.GetKeyCode(); + if (key == '+' || key == WXK_NUMPAD_ADD) + action_tick(taAdd); + else if (key == '-' || key == 390 || key == WXK_DELETE || key == WXK_BACK) + action_tick(taDel); + else if (is_horizontal()) + { + if (key == WXK_LEFT || key == WXK_RIGHT) + move_current_thumb(key == WXK_LEFT); + else if (key == WXK_UP || key == WXK_DOWN) { + m_selection = key == WXK_UP ? ssHigher : ssLower; + Refresh(); + } + } + else { + if (key == WXK_LEFT || key == WXK_RIGHT) { + m_selection = key == WXK_LEFT ? ssHigher : ssLower; + Refresh(); + } + else if (key == WXK_UP || key == WXK_DOWN) + move_current_thumb(key == WXK_UP); + } +} + +void PrusaDoubleSlider::OnKeyUp(wxKeyEvent &event) +{ + if (event.GetKeyCode() == WXK_CONTROL) + m_is_one_layer = false; + Refresh(); + Update(); + event.Skip(); +} + +void PrusaDoubleSlider::OnRightDown(wxMouseEvent& event) +{ + this->CaptureMouse(); + const wxClientDC dc(this); + detect_selected_slider(event.GetLogicalPosition(dc)); + if (!m_selection) + return; + + if (m_selection == ssLower) + m_higher_value = m_lower_value; + else + m_lower_value = m_higher_value; + + m_is_right_down = m_is_one_layer = true; + + Refresh(); + Update(); + event.Skip(); +} + +void PrusaDoubleSlider::OnRightUp(wxMouseEvent& event) +{ + this->ReleaseMouse(); + m_is_right_down = m_is_one_layer = false; + + Refresh(); + Update(); + event.Skip(); +} + + +// ---------------------------------------------------------------------------- +// PrusaLockButton +// ---------------------------------------------------------------------------- + +PrusaLockButton::PrusaLockButton( wxWindow *parent, + wxWindowID id, + const wxPoint& pos /*= wxDefaultPosition*/, + const wxSize& size /*= wxDefaultSize*/): + wxButton(parent, id, wxEmptyString, pos, size, wxBU_EXACTFIT | wxNO_BORDER) +{ + m_bmp_lock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG); + m_bmp_lock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_off.png")), wxBITMAP_TYPE_PNG); + m_bmp_unlock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_on.png")), wxBITMAP_TYPE_PNG); + m_bmp_unlock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_off.png")), wxBITMAP_TYPE_PNG); + m_lock_icon_dim = m_bmp_lock_on.GetSize().x; + +#ifdef __WXMSW__ + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#endif // __WXMSW__ + SetBitmap(m_bmp_unlock_on); + + //button events + Bind(wxEVT_BUTTON, &PrusaLockButton::OnButton, this); + Bind(wxEVT_ENTER_WINDOW, &PrusaLockButton::OnEnterBtn, this); + Bind(wxEVT_LEAVE_WINDOW, &PrusaLockButton::OnLeaveBtn, this); +} + +void PrusaLockButton::OnButton(wxCommandEvent& event) +{ + m_is_pushed = !m_is_pushed; + enter_button(true); + + event.Skip(); +} + +void PrusaLockButton::enter_button(const bool enter) +{ + wxBitmap* icon = m_is_pushed ? + enter ? &m_bmp_lock_off : &m_bmp_lock_on : + enter ? &m_bmp_unlock_off : &m_bmp_unlock_on; + SetBitmap(*icon); + + Refresh(); + Update(); +} + +// ************************************** EXPERIMENTS *************************************** + +// ***************************************************************************** + + + diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp new file mode 100644 index 0000000000..d9f996b27c --- /dev/null +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -0,0 +1,823 @@ +#ifndef slic3r_GUI_wxExtensions_hpp_ +#define slic3r_GUI_wxExtensions_hpp_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, + std::function cb, const std::string& icon = "", wxEvtHandler* event_handler = nullptr); + +wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description, const std::string& icon = ""); + +class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup +{ + static const unsigned int DefaultWidth; + static const unsigned int DefaultHeight; + static const unsigned int DefaultItemHeight; + + wxString m_text; + + // Events sent on mouseclick are quite complex. Function OnListBoxSelection is supposed to pass the event to the checkbox, which works fine on + // Win. On OSX and Linux the events are generated differently - clicking on the checkbox square generates the event twice (and the square + // therefore seems not to respond). + // This enum is meant to save current state of affairs, i.e., if the event forwarding is ok to do or not. It is only used on Linux + // and OSX by some #ifdefs. It also stores information whether OnListBoxSelection is supposed to change the checkbox status, + // or if it changed status on its own already (which happens when the square is clicked). More comments in OnCheckListBox(...) + // There indeed is a better solution, maybe making a custom event used for the event passing to distinguish the original and passed message + // and blocking one of them on OSX and Linux. Feel free to refactor, but carefully test on all platforms. + enum class OnCheckListBoxFunction{ + FreeToProceed, + RefuseToProceed, + WasRefusedLastTime + } m_check_box_events_status = OnCheckListBoxFunction::FreeToProceed; + + +public: + virtual bool Create(wxWindow* parent); + virtual wxWindow* GetControl(); + virtual void SetStringValue(const wxString& value); + virtual wxString GetStringValue() const; + virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight); + + virtual void OnKeyEvent(wxKeyEvent& evt); + + void OnCheckListBox(wxCommandEvent& evt); + void OnListBoxSelection(wxCommandEvent& evt); +}; + + +// *** wxDataViewTreeCtrlComboBox *** + +class wxDataViewTreeCtrlComboPopup: public wxDataViewTreeCtrl, public wxComboPopup +{ + static const unsigned int DefaultWidth; + static const unsigned int DefaultHeight; + static const unsigned int DefaultItemHeight; + + wxString m_text; + int m_cnt_open_items{0}; + +public: + virtual bool Create(wxWindow* parent); + virtual wxWindow* GetControl() { return this; } + virtual void SetStringValue(const wxString& value) { m_text = value; } + virtual wxString GetStringValue() const { return m_text; } +// virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight); + + virtual void OnKeyEvent(wxKeyEvent& evt); + void OnDataViewTreeCtrlSelection(wxCommandEvent& evt); + void SetItemsCnt(int cnt) { m_cnt_open_items = cnt; } +}; + + + +// *** PrusaCollapsiblePane *** +// ---------------------------------------------------------------------------- +class PrusaCollapsiblePane : public wxCollapsiblePane +{ +public: + PrusaCollapsiblePane() {} + PrusaCollapsiblePane(wxWindow *parent, + wxWindowID winid, + const wxString& label, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxCP_DEFAULT_STYLE, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxCollapsiblePaneNameStr) + { + Create(parent, winid, label, pos, size, style, val, name); + } + ~PrusaCollapsiblePane() {} + + void OnStateChange(const wxSize& sz); //override/hide of OnStateChange from wxCollapsiblePane + virtual bool Show(bool show = true) override { + wxCollapsiblePane::Show(show); + OnStateChange(GetBestSize()); + return true; + } +}; + + +// *** PrusaCollapsiblePaneMSW *** used only #ifdef __WXMSW__ +// ---------------------------------------------------------------------------- +#ifdef __WXMSW__ +class PrusaCollapsiblePaneMSW : public PrusaCollapsiblePane//wxCollapsiblePane +{ + wxButton* m_pDisclosureTriangleButton = nullptr; + wxBitmap m_bmp_close; + wxBitmap m_bmp_open; + wxString m_strLabel; +public: + PrusaCollapsiblePaneMSW() {} + PrusaCollapsiblePaneMSW( wxWindow *parent, + wxWindowID winid, + const wxString& label, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxCP_DEFAULT_STYLE, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxCollapsiblePaneNameStr) + { + Create(parent, winid, label, pos, size, style, val, name); + } + + ~PrusaCollapsiblePaneMSW() {} + + bool Create(wxWindow *parent, + wxWindowID id, + const wxString& label, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& val, + const wxString& name); + + void UpdateBtnBmp(); + void SetLabel(const wxString &label) override; + bool Layout() override; + void Collapse(bool collapse) override; +}; +#endif //__WXMSW__ + +// ***************************************************************************** + +// ---------------------------------------------------------------------------- +// PrusaDataViewBitmapText: helper class used by PrusaBitmapTextRenderer +// ---------------------------------------------------------------------------- + +class PrusaDataViewBitmapText : public wxObject +{ +public: + PrusaDataViewBitmapText(const wxString &text = wxEmptyString, + const wxBitmap& bmp = wxNullBitmap) : + m_text(text), m_bmp(bmp) + { } + + PrusaDataViewBitmapText(const PrusaDataViewBitmapText &other) + : wxObject(), + m_text(other.m_text), + m_bmp(other.m_bmp) + { } + + void SetText(const wxString &text) { m_text = text; } + wxString GetText() const { return m_text; } + void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; } + const wxBitmap &GetBitmap() const { return m_bmp; } + + bool IsSameAs(const PrusaDataViewBitmapText& other) const { + return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp); + } + + bool operator==(const PrusaDataViewBitmapText& other) const { + return IsSameAs(other); + } + + bool operator!=(const PrusaDataViewBitmapText& other) const { + return !IsSameAs(other); + } + +private: + wxString m_text; + wxBitmap m_bmp; + + wxDECLARE_DYNAMIC_CLASS(PrusaDataViewBitmapText); +}; +DECLARE_VARIANT_OBJECT(PrusaDataViewBitmapText) + + +// ---------------------------------------------------------------------------- +// PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel +// ---------------------------------------------------------------------------- + +enum ItemType{ + itUndef = 0, + itObject = 1, + itVolume = 2, + itInstanceRoot = 4, + itInstance = 8, + itSettings = 16 +}; + +class PrusaObjectDataViewModelNode; +WX_DEFINE_ARRAY_PTR(PrusaObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray); + +class PrusaObjectDataViewModelNode +{ + PrusaObjectDataViewModelNode* m_parent; + MyObjectTreeModelNodePtrArray m_children; + wxBitmap m_empty_bmp; + size_t m_volumes_cnt = 0; + std::vector< std::string > m_opt_categories; +public: + PrusaObjectDataViewModelNode(const wxString &name) { + m_parent = NULL; + m_name = name; + m_type = itObject; +#ifdef __WXGTK__ + // it's necessary on GTK because of control have to know if this item will be container + // in another case you couldn't to add subitem for this item + // it will be produce "segmentation fault" + m_container = true; +#endif //__WXGTK__ + set_object_action_icon(); + } + + PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent, + const wxString& sub_obj_name, + const wxBitmap& bmp, + const wxString& extruder, + const int idx = -1 ) { + m_parent = parent; + m_name = sub_obj_name; + m_bmp = bmp; + m_type = itVolume; + m_idx = idx; + m_extruder = extruder; +#ifdef __WXGTK__ + // it's necessary on GTK because of control have to know if this item will be container + // in another case you couldn't to add subitem for this item + // it will be produce "segmentation fault" + m_container = true; +#endif //__WXGTK__ + set_part_action_icon(); + } + + PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent, const ItemType type) : + m_parent(parent), + m_type(type), + m_extruder(wxEmptyString) + { + if (type == itSettings) { + m_name = "Settings to modified"; + } + else if (type == itInstanceRoot) { + m_name = "Instances"; +#ifdef __WXGTK__ + m_container = true; +#endif //__WXGTK__ + } + else if (type == itInstance) { + m_idx = parent->GetChildCount(); + m_name = wxString::Format("Instance_%d", m_idx+1); + } + } + + ~PrusaObjectDataViewModelNode() + { + // free all our children nodes + size_t count = m_children.GetCount(); + for (size_t i = 0; i < count; i++) + { + PrusaObjectDataViewModelNode *child = m_children[i]; + delete child; + } + } + + wxString m_name; + wxBitmap& m_bmp = m_empty_bmp; + ItemType m_type; + int m_idx = -1; + bool m_container = false; + wxString m_extruder = "default"; + wxBitmap m_action_icon; + + bool IsContainer() const + { + return m_container; + } + + PrusaObjectDataViewModelNode* GetParent() + { + return m_parent; + } + MyObjectTreeModelNodePtrArray& GetChildren() + { + return m_children; + } + PrusaObjectDataViewModelNode* GetNthChild(unsigned int n) + { + return m_children.Item(n); + } + void Insert(PrusaObjectDataViewModelNode* child, unsigned int n) + { + if (!m_container) + m_container = true; + m_children.Insert(child, n); + } + void Append(PrusaObjectDataViewModelNode* child) + { + if (!m_container) + m_container = true; + m_children.Add(child); + } + void RemoveAllChildren() + { + if (GetChildCount() == 0) + return; + for (size_t id = GetChildCount() - 1; id >= 0; --id) + { + if (m_children.Item(id)->GetChildCount() > 0) + m_children[id]->RemoveAllChildren(); + auto node = m_children[id]; + m_children.RemoveAt(id); + delete node; + } + } + + size_t GetChildCount() const + { + return m_children.GetCount(); + } + + bool SetValue(const wxVariant &variant, unsigned int col) + { + switch (col) + { + case 0:{ + PrusaDataViewBitmapText data; + data << variant; + m_bmp = data.GetBitmap(); + m_name = data.GetText(); + return true;} + case 1: + m_extruder = variant.GetString(); + return true; + case 2: + m_action_icon << variant; + return true; + default: + printf("MyObjectTreeModel::SetValue: wrong column"); + } + return false; + } + + void SetBitmap(const wxBitmap &icon) + { + m_bmp = icon; + } + + ItemType GetType() const { + return m_type; + } + + void SetIdx(const int& idx) { + m_idx = idx; + // update name if this node is instance + if (m_type == itInstance) + m_name = wxString::Format("Instance_%d", m_idx + 1); + } + + int GetIdx() const { + return m_idx; + } + + // use this function only for childrens + void AssignAllVal(PrusaObjectDataViewModelNode& from_node) + { + // ! Don't overwrite other values because of equality of this values for all children -- + m_name = from_node.m_name; + m_bmp = from_node.m_bmp; + m_idx = from_node.m_idx; + m_extruder = from_node.m_extruder; + m_type = from_node.m_type; + } + + bool SwapChildrens(int frst_id, int scnd_id) { + if (GetChildCount() < 2 || + frst_id < 0 || frst_id >= GetChildCount() || + scnd_id < 0 || scnd_id >= GetChildCount()) + return false; + + PrusaObjectDataViewModelNode new_scnd = *GetNthChild(frst_id); + PrusaObjectDataViewModelNode new_frst = *GetNthChild(scnd_id); + + new_scnd.m_idx = m_children.Item(scnd_id)->m_idx; + new_frst.m_idx = m_children.Item(frst_id)->m_idx; + + m_children.Item(frst_id)->AssignAllVal(new_frst); + m_children.Item(scnd_id)->AssignAllVal(new_scnd); + return true; + } + + // Set action icons for node + void set_object_action_icon(); + void set_part_action_icon(); + bool update_settings_digest(const std::vector& categories); +private: + friend class PrusaObjectDataViewModel; +}; + +// ---------------------------------------------------------------------------- +// PrusaObjectDataViewModel +// ---------------------------------------------------------------------------- + +// custom message the model sends to associated control to notify a last volume deleted from the object: +wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); + +class PrusaObjectDataViewModel :public wxDataViewModel +{ + std::vector m_objects; + std::vector m_volume_bmps; + + wxDataViewCtrl* m_ctrl{ nullptr }; + +public: + PrusaObjectDataViewModel(); + ~PrusaObjectDataViewModel(); + + wxDataViewItem Add(const wxString &name); + wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item, + const wxString &name, + const int volume_type, + const int extruder = 0, + const bool create_frst_child = true); + wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); + wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num); + wxDataViewItem Delete(const wxDataViewItem &item); + wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num); + void DeleteAll(); + void DeleteChildren(wxDataViewItem& parent); + void DeleteVolumeChildren(wxDataViewItem& parent); + void DeleteSettings(const wxDataViewItem& parent); + wxDataViewItem GetItemById(int obj_idx); + wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); + wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx); + int GetIdByItem(const wxDataViewItem& item); + int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; + int GetVolumeIdByItem(const wxDataViewItem& item) const; + int GetInstanceIdByItem(const wxDataViewItem& item) const; + void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx); + bool IsEmpty() { return m_objects.empty(); } + + // helper method for wxLog + + wxString GetName(const wxDataViewItem &item) const; + wxBitmap& GetBitmap(const wxDataViewItem &item) const; + + // helper methods to change the model + + virtual unsigned int GetColumnCount() const override { return 3;} + virtual wxString GetColumnType(unsigned int col) const override{ return wxT("string"); } + + virtual void GetValue(wxVariant &variant, + const wxDataViewItem &item, unsigned int col) const override; + virtual bool SetValue(const wxVariant &variant, + const wxDataViewItem &item, unsigned int col) override; + bool SetValue(const wxVariant &variant, const int item_idx, unsigned int col); + +// wxDataViewItem MoveChildUp(const wxDataViewItem &item); +// wxDataViewItem MoveChildDown(const wxDataViewItem &item); + // For parent move child from cur_volume_id place to new_volume_id + // Remaining items will moved up/down accordingly + wxDataViewItem ReorganizeChildren(int cur_volume_id, + int new_volume_id, + const wxDataViewItem &parent); + + virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override; + + virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override; + // get object item + wxDataViewItem GetTopParent(const wxDataViewItem &item) const; + virtual bool IsContainer(const wxDataViewItem &item) const override; + virtual unsigned int GetChildren(const wxDataViewItem &parent, + wxDataViewItemArray &array) const override; + + // Is the container just a header or an item with all columns + // In our case it is an item with all columns + virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } + + ItemType GetItemType(const wxDataViewItem &item) const ; + wxDataViewItem GetItemByType(const wxDataViewItem &parent_item, ItemType type) const; + wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const; + wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const; + bool IsSettingsItem(const wxDataViewItem &item) const; + void UpdateSettingsDigest(const wxDataViewItem &item, const std::vector& categories); + + void SetVolumeBitmaps(const std::vector& volume_bmps) { m_volume_bmps = volume_bmps; } + void SetVolumeType(const wxDataViewItem &item, const int type); + + void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } +}; + +// ---------------------------------------------------------------------------- +// PrusaBitmapTextRenderer +// ---------------------------------------------------------------------------- + +class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer +{ +public: + PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, + int align = wxDVR_DEFAULT_ALIGNMENT): + wxDataViewCustomRenderer(wxT("PrusaDataViewBitmapText"), mode, align) {} + + bool SetValue(const wxVariant &value); + bool GetValue(wxVariant &value) const; + + virtual bool Render(wxRect cell, wxDC *dc, int state); + virtual wxSize GetSize() const; + + virtual bool HasEditorCtrl() const { return false; } + +private: + PrusaDataViewBitmapText m_value; +}; + + +// ---------------------------------------------------------------------------- +// MyCustomRenderer +// ---------------------------------------------------------------------------- + +class MyCustomRenderer : public wxDataViewCustomRenderer +{ +public: + // This renderer can be either activatable or editable, for demonstration + // purposes. In real programs, you should select whether the user should be + // able to activate or edit the cell and it doesn't make sense to switch + // between the two -- but this is just an example, so it doesn't stop us. + explicit MyCustomRenderer(wxDataViewCellMode mode) + : wxDataViewCustomRenderer("string", mode, wxALIGN_CENTER) + { } + + virtual bool Render(wxRect rect, wxDC *dc, int state) override/*wxOVERRIDE*/ + { + dc->SetBrush(*wxLIGHT_GREY_BRUSH); + dc->SetPen(*wxTRANSPARENT_PEN); + + rect.Deflate(2); + dc->DrawRoundedRectangle(rect, 5); + + RenderText(m_value, + 0, // no offset + wxRect(dc->GetTextExtent(m_value)).CentreIn(rect), + dc, + state); + return true; + } + + virtual bool ActivateCell(const wxRect& WXUNUSED(cell), + wxDataViewModel *WXUNUSED(model), + const wxDataViewItem &WXUNUSED(item), + unsigned int WXUNUSED(col), + const wxMouseEvent *mouseEvent) override/*wxOVERRIDE*/ + { + wxString position; + if (mouseEvent) + position = wxString::Format("via mouse at %d, %d", mouseEvent->m_x, mouseEvent->m_y); + else + position = "from keyboard"; +// wxLogMessage("MyCustomRenderer ActivateCell() %s", position); + return false; + } + + virtual wxSize GetSize() const override/*wxOVERRIDE*/ + { + return wxSize(60, 20); + } + + virtual bool SetValue(const wxVariant &value) override/*wxOVERRIDE*/ + { + m_value = value.GetString(); + return true; + } + + virtual bool GetValue(wxVariant &WXUNUSED(value)) const override/*wxOVERRIDE*/{ return true; } + + virtual bool HasEditorCtrl() const override/*wxOVERRIDE*/{ return true; } + + virtual wxWindow* + CreateEditorCtrl(wxWindow* parent, + wxRect labelRect, + const wxVariant& value) override/*wxOVERRIDE*/ + { + wxTextCtrl* text = new wxTextCtrl(parent, wxID_ANY, value, + labelRect.GetPosition(), + labelRect.GetSize(), + wxTE_PROCESS_ENTER); + text->SetInsertionPointEnd(); + + return text; + } + + virtual bool + GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override/*wxOVERRIDE*/ + { + wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl); + if (!text) + return false; + + value = text->GetValue(); + + return true; + } + +private: + wxString m_value; +}; + + +// ---------------------------------------------------------------------------- +// PrusaDoubleSlider +// ---------------------------------------------------------------------------- + +// custom message the slider sends to its parent to notify a tick-change: +wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); + +enum SelectedSlider { + ssUndef, + ssLower, + ssHigher +}; +enum TicksAction{ + taOnIcon, + taAdd, + taDel +}; + +class PrusaDoubleSlider : public wxControl +{ +public: + PrusaDoubleSlider( + wxWindow *parent, + wxWindowID id, + int lowerValue, + int higherValue, + int minValue, + int maxValue, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxSL_VERTICAL, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxEmptyString); + ~PrusaDoubleSlider() {} + + int GetLowerValue() const { + return m_lower_value; + } + int GetHigherValue() const { + return m_higher_value; + } + int GetActiveValue() const; + double GetLowerValueD() const { return get_double_value(ssLower); } + double GetHigherValueD() const { return get_double_value(ssHigher); } + wxSize DoGetBestSize() const override; + void SetLowerValue(const int lower_val); + void SetHigherValue(const int higher_val); + void SetMaxValue(const int max_value); + void SetKoefForLabels(const double koef) { + m_label_koef = koef; + } + void SetSliderValues(const std::vector>& values) { + m_values = values; + } + void ChangeOneLayerLock(); + std::vector GetTicksValues() const; + void SetTicksValues(const std::vector& heights); + + void OnPaint(wxPaintEvent& ) { render();} + void OnLeftDown(wxMouseEvent& event); + void OnMotion(wxMouseEvent& event); + void OnLeftUp(wxMouseEvent& event); + void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); } + void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); } + void OnWheel(wxMouseEvent& event); + void OnKeyDown(wxKeyEvent &event); + void OnKeyUp(wxKeyEvent &event); + void OnRightDown(wxMouseEvent& event); + void OnRightUp(wxMouseEvent& event); + +protected: + + void render(); + void draw_focus_rect(); + void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end); + void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos); + void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection); + void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos); + void draw_ticks(wxDC& dc); + void draw_one_layer_icon(wxDC& dc); + void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); + void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); + void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; + + void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection); + void detect_selected_slider(const wxPoint& pt, const bool is_mouse_wheel = false); + void correct_lower_value(); + void correct_higher_value(); + void move_current_thumb(const bool condition); + void action_tick(const TicksAction action); + void enter_window(wxMouseEvent& event, const bool enter); + + bool is_point_in_rect(const wxPoint& pt, const wxRect& rect); + bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } + + double get_scroll_step(); + wxString get_label(const SelectedSlider& selection) const; + void get_lower_and_higher_position(int& lower_pos, int& higher_pos); + int get_value_from_position(const wxCoord x, const wxCoord y); + wxCoord get_position_from_value(const int value); + wxSize get_size(); + void get_size(int *w, int *h); + double get_double_value(const SelectedSlider& selection) const; + +private: + int m_min_value; + int m_max_value; + int m_lower_value; + int m_higher_value; + wxBitmap m_bmp_thumb_higher; + wxBitmap m_bmp_thumb_lower; + wxBitmap m_bmp_add_tick_on; + wxBitmap m_bmp_add_tick_off; + wxBitmap m_bmp_del_tick_on; + wxBitmap m_bmp_del_tick_off; + wxBitmap m_bmp_one_layer_lock_on; + wxBitmap m_bmp_one_layer_lock_off; + wxBitmap m_bmp_one_layer_unlock_on; + wxBitmap m_bmp_one_layer_unlock_off; + SelectedSlider m_selection; + bool m_is_left_down = false; + bool m_is_right_down = false; + bool m_is_one_layer = false; + bool m_is_focused = false; + bool m_is_action_icon_focesed = false; + bool m_is_one_layer_icon_focesed = false; + + wxRect m_rect_lower_thumb; + wxRect m_rect_higher_thumb; + wxRect m_rect_tick_action; + wxRect m_rect_one_layer_icon; + wxSize m_thumb_size; + int m_tick_icon_dim; + int m_lock_icon_dim; + long m_style; + float m_label_koef = 1.0; + +// control's view variables + wxCoord SLIDER_MARGIN; // margin around slider + + wxPen DARK_ORANGE_PEN; + wxPen ORANGE_PEN; + wxPen LIGHT_ORANGE_PEN; + + wxPen DARK_GREY_PEN; + wxPen GREY_PEN; + wxPen LIGHT_GREY_PEN; + + std::vector line_pens; + std::vector segm_pens; + std::set m_ticks; + std::vector> m_values; +}; + + +// ---------------------------------------------------------------------------- +// PrusaLockButton +// ---------------------------------------------------------------------------- + +class PrusaLockButton : public wxButton +{ +public: + PrusaLockButton( + wxWindow *parent, + wxWindowID id, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize); + ~PrusaLockButton() {} + + void OnButton(wxCommandEvent& event); + void OnEnterBtn(wxMouseEvent& event) { enter_button(true); event.Skip(); } + void OnLeaveBtn(wxMouseEvent& event) { enter_button(false); event.Skip(); } + + bool IsLocked() const { return m_is_pushed; } + +protected: + void enter_button(const bool enter); + +private: + bool m_is_pushed = false; + + wxBitmap m_bmp_lock_on; + wxBitmap m_bmp_lock_off; + wxBitmap m_bmp_unlock_on; + wxBitmap m_bmp_unlock_off; + + int m_lock_icon_dim; +}; + + +// ******************************* EXPERIMENTS ********************************************** +// ****************************************************************************************** + + +#endif // slic3r_GUI_wxExtensions_hpp_ diff --git a/xs/src/slic3r/GUI/wxinit.h b/src/slic3r/GUI/wxinit.h similarity index 100% rename from xs/src/slic3r/GUI/wxinit.h rename to src/slic3r/GUI/wxinit.h diff --git a/xs/src/slic3r/Utils/ASCIIFolding.cpp b/src/slic3r/Utils/ASCIIFolding.cpp similarity index 100% rename from xs/src/slic3r/Utils/ASCIIFolding.cpp rename to src/slic3r/Utils/ASCIIFolding.cpp diff --git a/xs/src/slic3r/Utils/ASCIIFolding.hpp b/src/slic3r/Utils/ASCIIFolding.hpp similarity index 100% rename from xs/src/slic3r/Utils/ASCIIFolding.hpp rename to src/slic3r/Utils/ASCIIFolding.hpp diff --git a/xs/src/slic3r/Utils/Bonjour.cpp b/src/slic3r/Utils/Bonjour.cpp similarity index 99% rename from xs/src/slic3r/Utils/Bonjour.cpp rename to src/slic3r/Utils/Bonjour.cpp index 09d9b58738..bfd9d48282 100644 --- a/xs/src/slic3r/Utils/Bonjour.cpp +++ b/src/slic3r/Utils/Bonjour.cpp @@ -677,7 +677,7 @@ void Bonjour::priv::lookup_perform() socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler); } } - } catch (std::exception& e) { + } catch (std::exception& /* e */) { } } diff --git a/xs/src/slic3r/Utils/Bonjour.hpp b/src/slic3r/Utils/Bonjour.hpp similarity index 100% rename from xs/src/slic3r/Utils/Bonjour.hpp rename to src/slic3r/Utils/Bonjour.hpp diff --git a/xs/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp similarity index 100% rename from xs/src/slic3r/Utils/Duet.cpp rename to src/slic3r/Utils/Duet.cpp diff --git a/xs/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp similarity index 100% rename from xs/src/slic3r/Utils/Duet.hpp rename to src/slic3r/Utils/Duet.hpp diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp similarity index 96% rename from xs/src/slic3r/Utils/FixModelByWin10.cpp rename to src/slic3r/Utils/FixModelByWin10.cpp index 556035a5b1..cec0802bdb 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -4,6 +4,16 @@ # define NOMINMAX #endif +// Windows Runtime +#include +// for ComPtr +#include + +// from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ +#include +#include +#include + #include "FixModelByWin10.hpp" #include @@ -18,14 +28,6 @@ #include #include -#include -// for ComPtr -#include -// from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ -#include -#include -#include - #include "libslic3r/Model.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/Format/3mf.hpp" @@ -347,8 +349,9 @@ void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &pr boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); path_src += ".3mf"; Model model; + DynamicPrintConfig config; model.add_object(model_object); - if (! Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast(&print), false)) { + if (! Slic3r::store_3mf(path_src.string().c_str(), &model, &config/*const_cast(&print), false*/)) { boost::filesystem::remove(path_src); throw std::runtime_error(L("Export of a temporary 3mf file failed")); } @@ -359,16 +362,17 @@ void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &pr fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string(), on_progress, [&canceled]() { if (canceled) throw RepairCanceledException(); }); boost::filesystem::remove(path_src); - PresetBundle bundle; + // PresetBundle bundle; on_progress(L("Loading the repaired model"), 80); - bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result); - boost::filesystem::remove(path_dst); + bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &config/*bundle*/, &result); + result.objects[0]->name = boost::filesystem::path(model_object.name).filename().stem().string() + "_fixed"; + boost::filesystem::remove(path_dst); if (! loaded) throw std::runtime_error(L("Import of the repaired 3mf file failed")); success = true; finished = true; on_progress(L("Model repair finished"), 100); - } catch (RepairCanceledException &ex) { + } catch (RepairCanceledException & /* ex */) { canceled = true; finished = true; on_progress(L("Model repair canceled"), 100); diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/src/slic3r/Utils/FixModelByWin10.hpp similarity index 100% rename from xs/src/slic3r/Utils/FixModelByWin10.hpp rename to src/slic3r/Utils/FixModelByWin10.hpp diff --git a/xs/src/slic3r/Utils/HexFile.cpp b/src/slic3r/Utils/HexFile.cpp similarity index 98% rename from xs/src/slic3r/Utils/HexFile.cpp rename to src/slic3r/Utils/HexFile.cpp index 282c647bdc..9e08033259 100644 --- a/xs/src/slic3r/Utils/HexFile.cpp +++ b/src/slic3r/Utils/HexFile.cpp @@ -76,7 +76,7 @@ HexFile::HexFile(fs::path path) : pt::ptree ptree; try { pt::read_ini(header_ini, ptree); - } catch (std::exception &e) { + } catch (std::exception & /* e */) { return; } diff --git a/xs/src/slic3r/Utils/HexFile.hpp b/src/slic3r/Utils/HexFile.hpp similarity index 100% rename from xs/src/slic3r/Utils/HexFile.hpp rename to src/slic3r/Utils/HexFile.hpp diff --git a/xs/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp similarity index 100% rename from xs/src/slic3r/Utils/Http.cpp rename to src/slic3r/Utils/Http.cpp diff --git a/xs/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp similarity index 100% rename from xs/src/slic3r/Utils/Http.hpp rename to src/slic3r/Utils/Http.hpp diff --git a/xs/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp similarity index 100% rename from xs/src/slic3r/Utils/OctoPrint.cpp rename to src/slic3r/Utils/OctoPrint.cpp diff --git a/xs/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp similarity index 100% rename from xs/src/slic3r/Utils/OctoPrint.hpp rename to src/slic3r/Utils/OctoPrint.hpp diff --git a/xs/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp similarity index 94% rename from xs/src/slic3r/Utils/PresetUpdater.cpp rename to src/slic3r/Utils/PresetUpdater.cpp index 6e23ab4219..47eece8abd 100644 --- a/xs/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -24,6 +24,7 @@ #include "slic3r/Utils/Http.hpp" #include "slic3r/Config/Version.hpp" #include "slic3r/Config/Snapshot.hpp" +#include "slic3r/GUI/GUI_App.hpp" namespace fs = boost::filesystem; using Slic3r::GUI::Config::Index; @@ -90,7 +91,6 @@ struct Updates struct PresetUpdater::priv { - int version_online_event; std::vector index_db; bool enabled_version_check; @@ -105,7 +105,7 @@ struct PresetUpdater::priv bool cancel; std::thread thread; - priv(int version_online_event); + priv(); void set_download_prefs(AppConfig *app_config); bool get_file(const std::string &url, const fs::path &target_path) const; @@ -120,15 +120,14 @@ struct PresetUpdater::priv static void copy_file(const fs::path &from, const fs::path &to); }; -PresetUpdater::priv::priv(int version_online_event) : - version_online_event(version_online_event), +PresetUpdater::priv::priv() : had_config_update(false), cache_path(fs::path(Slic3r::data_dir()) / "cache"), rsrc_path(fs::path(resources_dir()) / "profiles"), vendor_path(fs::path(Slic3r::data_dir()) / "vendor"), cancel(false) { - set_download_prefs(GUI::get_app_config()); + set_download_prefs(GUI::wxGetApp().app_config); check_install_indices(); index_db = std::move(Index::load_db()); } @@ -209,9 +208,11 @@ void PresetUpdater::priv::sync_version() const .on_complete([&](std::string body, unsigned /* http_status */) { boost::trim(body); BOOST_LOG_TRIVIAL(info) << boost::format("Got Slic3rPE online version: `%1%`. Sending to GUI thread...") % body; - wxCommandEvent* evt = new wxCommandEvent(version_online_event); - evt->SetString(body); - GUI::get_app()->QueueEvent(evt); +// wxCommandEvent* evt = new wxCommandEvent(version_online_event); +// evt->SetString(body); +// GUI::get_app()->QueueEvent(evt); + GUI::wxGetApp().app_config->set("version_online", body); + GUI::wxGetApp().app_config->save(); }) .perform_sync(); } @@ -395,7 +396,7 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons if (updates.incompats.size() > 0) { if (snapshot) { BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; - SnapshotDB::singleton().take_snapshot(*GUI::get_app_config(), Snapshot::SNAPSHOT_DOWNGRADE); + SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_DOWNGRADE); } BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size(); @@ -408,7 +409,7 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons else if (updates.updates.size() > 0) { if (snapshot) { BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; - SnapshotDB::singleton().take_snapshot(*GUI::get_app_config(), Snapshot::SNAPSHOT_UPGRADE); + SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE); } BOOST_LOG_TRIVIAL(info) << boost::format("Performing %1% updates") % updates.updates.size(); @@ -447,6 +448,8 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons for (const auto &name : bundle.obsolete_presets.prints) { obsolete_remover("print", name); } for (const auto &name : bundle.obsolete_presets.filaments) { obsolete_remover("filament", name); } + for (const auto &name : bundle.obsolete_presets.sla_prints) { obsolete_remover("sla_print", name); } + for (const auto &name : bundle.obsolete_presets.sla_materials/*filaments*/) { obsolete_remover("sla_material", name); } for (const auto &name : bundle.obsolete_presets.printers) { obsolete_remover("printer", name); } } } @@ -465,8 +468,8 @@ void PresetUpdater::priv::copy_file(const fs::path &source, const fs::path &targ } -PresetUpdater::PresetUpdater(int version_online_event) : - p(new priv(version_online_event)) +PresetUpdater::PresetUpdater() : + p(new priv()) {} @@ -484,7 +487,7 @@ PresetUpdater::~PresetUpdater() void PresetUpdater::sync(PresetBundle *preset_bundle) { - p->set_download_prefs(GUI::get_app_config()); + p->set_download_prefs(GUI::wxGetApp().app_config); if (!p->enabled_version_check && !p->enabled_config_update) { return; } // Copy the whole vendors data for use in the background thread @@ -508,7 +511,7 @@ void PresetUpdater::slic3r_update_notify() return; } - auto* app_config = GUI::get_app_config(); + auto* app_config = GUI::wxGetApp().app_config; const auto ver_slic3r = Semver::parse(SLIC3R_VERSION); const auto ver_online_str = app_config->get("version_online"); const auto ver_online = Semver::parse(ver_online_str); @@ -568,10 +571,10 @@ bool PresetUpdater::config_update() const BOOST_LOG_TRIVIAL(info) << "User wants to re-configure..."; p->perform_updates(std::move(updates)); GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT); - if (! wizard.run(GUI::get_preset_bundle(), this)) { + if (! wizard.run(GUI::wxGetApp().preset_bundle, this)) { return false; } - GUI::load_current_presets(); + GUI::wxGetApp().load_current_presets(); } else { BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye..."; return false; @@ -600,9 +603,9 @@ bool PresetUpdater::config_update() const p->perform_updates(std::move(updates)); // Reload global configuration - auto *app_config = GUI::get_app_config(); - GUI::get_preset_bundle()->load_presets(*app_config); - GUI::load_current_presets(); + auto *app_config = GUI::wxGetApp().app_config; + GUI::wxGetApp().preset_bundle->load_presets(*app_config); + GUI::wxGetApp().load_current_presets(); } else { BOOST_LOG_TRIVIAL(info) << "User refused the update"; } diff --git a/xs/src/slic3r/Utils/PresetUpdater.hpp b/src/slic3r/Utils/PresetUpdater.hpp similarity index 96% rename from xs/src/slic3r/Utils/PresetUpdater.hpp rename to src/slic3r/Utils/PresetUpdater.hpp index 6a53cca816..451e8b2cf5 100644 --- a/xs/src/slic3r/Utils/PresetUpdater.hpp +++ b/src/slic3r/Utils/PresetUpdater.hpp @@ -13,7 +13,7 @@ class PresetBundle; class PresetUpdater { public: - PresetUpdater(int version_online_event); + PresetUpdater(); PresetUpdater(PresetUpdater &&) = delete; PresetUpdater(const PresetUpdater &) = delete; PresetUpdater &operator=(PresetUpdater &&) = delete; diff --git a/xs/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp similarity index 100% rename from xs/src/slic3r/Utils/PrintHost.cpp rename to src/slic3r/Utils/PrintHost.cpp diff --git a/xs/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp similarity index 100% rename from xs/src/slic3r/Utils/PrintHost.hpp rename to src/slic3r/Utils/PrintHost.hpp diff --git a/xs/src/slic3r/Utils/PrintHostSendDialog.cpp b/src/slic3r/Utils/PrintHostSendDialog.cpp similarity index 100% rename from xs/src/slic3r/Utils/PrintHostSendDialog.cpp rename to src/slic3r/Utils/PrintHostSendDialog.cpp diff --git a/xs/src/slic3r/Utils/PrintHostSendDialog.hpp b/src/slic3r/Utils/PrintHostSendDialog.hpp similarity index 100% rename from xs/src/slic3r/Utils/PrintHostSendDialog.hpp rename to src/slic3r/Utils/PrintHostSendDialog.hpp diff --git a/xs/src/slic3r/Utils/Semver.hpp b/src/slic3r/Utils/Semver.hpp similarity index 98% rename from xs/src/slic3r/Utils/Semver.hpp rename to src/slic3r/Utils/Semver.hpp index 736f9b891a..2fb4e3f4bf 100644 --- a/xs/src/slic3r/Utils/Semver.hpp +++ b/src/slic3r/Utils/Semver.hpp @@ -111,8 +111,8 @@ public: bool operator>(const Semver &b) const { return ::semver_compare(ver, b.ver) == 1; } // We're using '&' instead of the '~' operator here as '~' is unary-only: // Satisfies patch if Major and minor are equal. - bool operator&(const Semver &b) const { return ::semver_satisfies_patch(ver, b.ver); } - bool operator^(const Semver &b) const { return ::semver_satisfies_caret(ver, b.ver); } + bool operator&(const Semver &b) const { return ::semver_satisfies_patch(ver, b.ver) != 0; } + bool operator^(const Semver &b) const { return ::semver_satisfies_caret(ver, b.ver) != 0; } bool in_range(const Semver &low, const Semver &high) const { return low <= *this && *this <= high; } // Conversion diff --git a/xs/src/slic3r/Utils/Serial.cpp b/src/slic3r/Utils/Serial.cpp similarity index 100% rename from xs/src/slic3r/Utils/Serial.cpp rename to src/slic3r/Utils/Serial.cpp diff --git a/xs/src/slic3r/Utils/Serial.hpp b/src/slic3r/Utils/Serial.hpp similarity index 100% rename from xs/src/slic3r/Utils/Serial.hpp rename to src/slic3r/Utils/Serial.hpp diff --git a/xs/src/slic3r/Utils/Time.cpp b/src/slic3r/Utils/Time.cpp similarity index 100% rename from xs/src/slic3r/Utils/Time.cpp rename to src/slic3r/Utils/Time.cpp diff --git a/xs/src/slic3r/Utils/Time.hpp b/src/slic3r/Utils/Time.hpp similarity index 100% rename from xs/src/slic3r/Utils/Time.hpp rename to src/slic3r/Utils/Time.hpp diff --git a/src/slic3r/pchheader.cpp b/src/slic3r/pchheader.cpp new file mode 100644 index 0000000000..9ab59c53d5 --- /dev/null +++ b/src/slic3r/pchheader.cpp @@ -0,0 +1 @@ +#include "pchheader.hpp" diff --git a/src/slic3r/pchheader.hpp b/src/slic3r/pchheader.hpp new file mode 100644 index 0000000000..3b321d9604 --- /dev/null +++ b/src/slic3r/pchheader.hpp @@ -0,0 +1,185 @@ +#ifdef WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef _MSC_VER + // avoid some "macro redefinition" warnings + #include +#endif /* _MSC_VER */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include +// #include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libslic3r/Config.hpp" +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/Point.hpp" +#include "libslic3r/MultiPoint.hpp" +#include "libslic3r/Polygon.hpp" +#include "libslic3r/Polyline.hpp" +#include "libslic3r/BoundingBox.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/libslic3r.h" diff --git a/src/slic3r_app_msvc.cpp b/src/slic3r_app_msvc.cpp new file mode 100644 index 0000000000..9823a325d3 --- /dev/null +++ b/src/slic3r_app_msvc.cpp @@ -0,0 +1,253 @@ +// Why? +#define _WIN32_WINNT 0x0502 +// The standard Windows includes. +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include +#include +// Let the NVIDIA and AMD know we want to use their graphics card +// on a dual graphics card system. +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; + +#include +#include +#include + +#include +#include + +#include +#include + +class OpenGLVersionCheck +{ +public: + std::string version; + std::string glsl_version; + std::string vendor; + std::string renderer; + + HINSTANCE hOpenGL = nullptr; + bool success = false; + + bool load_opengl_dll() + { + MSG msg = {0}; + WNDCLASS wc = {0}; + wc.lpfnWndProc = OpenGLVersionCheck::supports_opengl2_wndproc; + wc.hInstance = (HINSTANCE)GetModuleHandle(nullptr); + wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); + wc.lpszClassName = L"slic3r_opengl_version_check"; + wc.style = CS_OWNDC; + if (RegisterClass(&wc)) { + HWND hwnd = CreateWindowW(wc.lpszClassName, L"slic3r_opengl_version_check", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, 0, 0, wc.hInstance, (LPVOID)this); + if (hwnd) { + this->message_pump_exit = false; + while (GetMessage(&msg, NULL, 0, 0 ) > 0 && ! this->message_pump_exit) + DispatchMessage(&msg); + } + } + return this->success; + } + + void unload_opengl_dll() + { + if (this->hOpenGL) { + FreeLibrary(this->hOpenGL); + this->hOpenGL = nullptr; + } + } + + bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const + { + std::vector tokens; + boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on); + if (tokens.empty()) + return false; + + std::vector numbers; + boost::split(numbers, tokens[0], boost::is_any_of("."), boost::token_compress_on); + + unsigned int gl_major = 0; + unsigned int gl_minor = 0; + if (numbers.size() > 0) + gl_major = ::atoi(numbers[0].c_str()); + if (numbers.size() > 1) + gl_minor = ::atoi(numbers[1].c_str()); + if (gl_major < major) + return false; + else if (gl_major > major) + return true; + else + return gl_minor >= minor; + } + +protected: + bool message_pump_exit = false; + + void check(HWND hWnd) + { + hOpenGL = LoadLibraryExW(L"opengl32.dll", nullptr, 0); + if (hOpenGL == nullptr) { + printf("Failed loading the system opengl32.dll\n"); + return; + } + + typedef HGLRC (WINAPI *Func_wglCreateContext)(HDC); + typedef BOOL (WINAPI *Func_wglMakeCurrent )(HDC, HGLRC); + typedef BOOL (WINAPI *Func_wglDeleteContext)(HGLRC); + typedef GLubyte* (WINAPI *Func_glGetString )(GLenum); + + Func_wglCreateContext wglCreateContext = (Func_wglCreateContext)GetProcAddress(hOpenGL, "wglCreateContext"); + Func_wglMakeCurrent wglMakeCurrent = (Func_wglMakeCurrent) GetProcAddress(hOpenGL, "wglMakeCurrent"); + Func_wglDeleteContext wglDeleteContext = (Func_wglDeleteContext)GetProcAddress(hOpenGL, "wglDeleteContext"); + Func_glGetString glGetString = (Func_glGetString) GetProcAddress(hOpenGL, "glGetString"); + + if (wglCreateContext == nullptr || wglMakeCurrent == nullptr || wglDeleteContext == nullptr || glGetString == nullptr) { + printf("Failed loading the system opengl32.dll: The library is invalid.\n"); + return; + } + + PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. + 32, // Color depth of the framebuffer. + 0, 0, 0, 0, 0, 0, + 0, + 0, + 0, + 0, 0, 0, 0, + 24, // Number of bits for the depthbuffer + 8, // Number of bits for the stencilbuffer + 0, // Number of Aux buffers in the framebuffer. + PFD_MAIN_PLANE, + 0, + 0, 0, 0 + }; + + HDC ourWindowHandleToDeviceContext = ::GetDC(hWnd); + // Gdi32.dll + int letWindowsChooseThisPixelFormat = ::ChoosePixelFormat(ourWindowHandleToDeviceContext, &pfd); + // Gdi32.dll + SetPixelFormat(ourWindowHandleToDeviceContext,letWindowsChooseThisPixelFormat, &pfd); + // Opengl32.dll + HGLRC glcontext = wglCreateContext(ourWindowHandleToDeviceContext); + wglMakeCurrent(ourWindowHandleToDeviceContext, glcontext); + // Opengl32.dll + const char *data = (const char*)glGetString(GL_VERSION); + if (data != nullptr) + this->version = data; + data = (const char*)glGetString(0x8B8C); // GL_SHADING_LANGUAGE_VERSION + if (data != nullptr) + this->glsl_version = data; + data = (const char*)glGetString(GL_VENDOR); + if (data != nullptr) + this->vendor = data; + data = (const char*)glGetString(GL_RENDERER); + if (data != nullptr) + this->renderer = data; + // Opengl32.dll + wglDeleteContext(glcontext); + this->success = true; + } + + static LRESULT CALLBACK supports_opengl2_wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) + { + switch(message) + { + case WM_CREATE: + { + CREATESTRUCT *pCreate = reinterpret_cast(lParam); + OpenGLVersionCheck *ogl_data = reinterpret_cast(pCreate->lpCreateParams); + ogl_data->check(hWnd); + DestroyWindow(hWnd); + ogl_data->message_pump_exit = true; + return 0; + } + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + } +}; + +extern "C" { + typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv); + Slic3rMainFunc slic3r_main = nullptr; +} + +#ifdef SLIC3R_WRAPPER_NOCONSOLE +int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpCmdLine, int nCmdShow) +{ + int argc; + wchar_t **argv = CommandLineToArgvW(lpCmdLine, &argc); +#else +int wmain(int argc, wchar_t **argv) +{ +#endif + + OpenGLVersionCheck opengl_version_check; + bool load_mesa = ! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0); + + wchar_t path_to_exe[MAX_PATH + 1] = { 0 }; + ::GetModuleFileNameW(nullptr, path_to_exe, MAX_PATH); + wchar_t drive[_MAX_DRIVE]; + wchar_t dir[_MAX_DIR]; + wchar_t fname[_MAX_FNAME]; + wchar_t ext[_MAX_EXT]; + _wsplitpath(path_to_exe, drive, dir, fname, ext); + _wmakepath(path_to_exe, drive, dir, nullptr, nullptr); + +// https://wiki.qt.io/Cross_compiling_Mesa_for_Windows +// http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/ + if (load_mesa) { + opengl_version_check.unload_opengl_dll(); + wchar_t path_to_mesa[MAX_PATH + 1] = { 0 }; + wcscpy(path_to_mesa, path_to_exe); + wcscat(path_to_mesa, L"mesa\\opengl32.dll"); + printf("Loading MESA OpenGL library: %S\n", path_to_mesa); + HINSTANCE hInstance_OpenGL = LoadLibraryExW(path_to_mesa, nullptr, 0); + if (hInstance_OpenGL == nullptr) { + printf("MESA OpenGL library was not loaded\n"); + } + } + + wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 }; + wcscpy(path_to_slic3r, path_to_exe); + wcscat(path_to_slic3r, L"slic3r.dll"); +// printf("Loading Slic3r library: %S\n", path_to_slic3r); + HINSTANCE hInstance_Slic3r = LoadLibraryExW(path_to_slic3r, nullptr, 0); + if (hInstance_Slic3r == nullptr) { + printf("slic3r.dll was not loaded\n"); + return -1; + } + + // resolve function address here + slic3r_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r, +#ifdef _WIN64 + // there is just a single calling conversion, therefore no mangling of the function name. + "slic3r_main" +#else // stdcall calling convention declaration + "_slic3r_main@8" +#endif + ); + if (slic3r_main == nullptr) { + printf("could not locate the function slic3r_main in slic3r.dll\n"); + return -1; + } + + std::vector argv_extended; + argv_extended.emplace_back(argv[0]); +#ifdef SLIC3R_WRAPPER_GUI + std::wstring cmd_gui = L"--gui"; + argv_extended.emplace_back(const_cast(cmd_gui.data())); +#endif + for (int i = 1; i < argc; ++ i) + argv_extended.emplace_back(argv[i]); + argv_extended.emplace_back(nullptr); + return slic3r_main(argc, argv_extended.data()); +} diff --git a/t/svg.t b/t/svg.t deleted file mode 100644 index 47d24ceb87..0000000000 --- a/t/svg.t +++ /dev/null @@ -1,37 +0,0 @@ -use Test::More tests => 2; -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Slic3r; -use Slic3r::Test; -use IO::Scalar; - -{ - my $print = Slic3r::Test::init_print('20mm_cube'); - eval { - my $fh = IO::Scalar->new(\my $gcode); - $print->print->export_svg(output_fh => $fh, quiet => 1); - $fh->close; - }; - die $@ if $@; - ok !$@, 'successful SVG export'; -} - -{ - my $print = Slic3r::Test::init_print('two_hollow_squares'); - eval { - my $fh = IO::Scalar->new(\my $gcode); - $print->print->export_svg(output_fh => $fh, quiet => 1); - $fh->close; - }; - die $@ if $@; - ok !$@, 'successful SVG export of object with two islands'; -} - -__END__ diff --git a/t/threads.t b/t/threads.t deleted file mode 100644 index 7fede33283..0000000000 --- a/t/threads.t +++ /dev/null @@ -1,35 +0,0 @@ -use Test::More tests => 2; -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use List::Util qw(first); -use Slic3r; -use Slic3r::Test; - -{ - my $print = Slic3r::Test::init_print('20mm_cube'); - { - my $thread = threads->create(sub { Slic3r::thread_cleanup(); return 1; }); - ok $thread->join, "print survives thread spawning"; - } -} - -{ - my $thread = threads->create(sub { - { - my $print = Slic3r::Test::init_print('20mm_cube'); - Slic3r::Test::gcode($print); - } - Slic3r::thread_cleanup(); - return 1; - }); - ok $thread->join, "process print in a separate thread"; -} - -__END__ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000000..11bdc4b3d9 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +# TODO Add individual tests as executables in separate directories + +# add_subirectory() \ No newline at end of file diff --git a/utils/amf-to-stl.pl b/utils/amf-to-stl.pl deleted file mode 100755 index 802fd9a536..0000000000 --- a/utils/amf-to-stl.pl +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/perl -# This script converts an AMF file to STL - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use File::Basename qw(basename); -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -$|++; - -# Convert all parameters from the local code page to utf8 on Windows. -@ARGV = map Slic3r::decode_path($_), @ARGV if $^O eq 'MSWin32'; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'ascii' => \$opt{ascii}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -{ - my $model = Slic3r::Model->load_amf($ARGV[0]); - my $output_file = $ARGV[0]; - $output_file =~ s/\.[aA][mM][fF](?:\.[xX][mM][lL])?$/\.stl/; - - printf "Writing to %s\n", basename($output_file); - $model->store_stl($output_file, binary => !$opt{ascii}); -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: amf-to-stl.pl [ OPTIONS ] file.amf - - --help Output this usage screen and exit - --ascii Generate ASCII STL files (default: binary) - -EOF - exit ($exit_code || 0); -} - -__END__ diff --git a/utils/dump-stl.pl b/utils/dump-stl.pl deleted file mode 100644 index eee3c73dc1..0000000000 --- a/utils/dump-stl.pl +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/perl -# This script dumps a STL file into Perl syntax for writing tests -# or dumps a test model into a STL file - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Slic3r; -use Slic3r::Test; -use File::Basename qw(basename); -$|++; - -$ARGV[0] or usage(1); - -if (-e $ARGV[0]) { - my $model = Slic3r::Model->load_stl($ARGV[0], basename($ARGV[0])); - $model->objects->[0]->add_instance(offset => Slic3r::Pointf->new(0,0)); - my $mesh = $model->mesh; - $mesh->repair; - printf "VERTICES = %s\n", join ',', map "[$_->[0],$_->[1],$_->[2]]", @{$mesh->vertices}; - printf "FACETS = %s\n", join ',', map "[$_->[0],$_->[1],$_->[2]]", @{$mesh->facets}; - exit 0; -} elsif ((my $model = Slic3r::Test::model($ARGV[0]))) { - $ARGV[1] or die "Missing writeable destination as second argument\n"; - $model->store_stl($ARGV[1], 1); - printf "Model $ARGV[0] written to $ARGV[1]\n"; - exit 0; -} else { - die "No such model exists\n"; -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: dump-stl.pl file.stl - dump-stl.pl modelname file.stl -EOF - exit ($exit_code || 0); -} - -__END__ diff --git a/utils/modifier_helpers/layer_generator.jscad b/utils/modifier_helpers/layer_generator.jscad deleted file mode 100644 index fc193a53f9..0000000000 --- a/utils/modifier_helpers/layer_generator.jscad +++ /dev/null @@ -1,19 +0,0 @@ -// title: Layer_generator -// written by: Joseph Lenox -// Used for generating cubes oriented about the center -// for making simple modifier meshes. - -var width = 100; -var layer_height = 0.3; -var z = 30; -function main() { - - return cube(size=[width,width,layer_height], center=true).translate([0,0,z]); -} -function getParameterDefinitions() { - return [ - { name: 'width', type: 'float', initial: 100, caption: "Width of the cube:" }, - { name: 'layer_height', type: 'float', initial: 0.3, caption: "Layer height used:" }, - { name: 'z', type: 'float', initial: 0, caption: "Z:" } - ]; -} diff --git a/utils/modifier_helpers/solid_layers.scad b/utils/modifier_helpers/solid_layers.scad deleted file mode 100644 index 3782949495..0000000000 --- a/utils/modifier_helpers/solid_layers.scad +++ /dev/null @@ -1,24 +0,0 @@ -// Used to generate a modifier mesh to do something every few layers. -// Load into OpenSCAD, tweak the variables below, export as STL and load as -// a modifier mesh. Then change settings for the modifier mesh. - -// Written by Joseph Lenox; in public domain. - -layer_height = 0.3; // set to layer height in slic3r for "best" results. -number_of_solid_layers = 2; -N = 4; // N > number_of_solid_layers or else the whole thing will be solid -model_height = 300.0; -model_width = 300.0; // these two should be at least as big as the model -model_depth = 300.0; // but bigger isn't a problem -initial_offset=0; // don't generate below this - -position_on_bed=[0,0,0]; // in case you need to move it around - -// don't touch below unless you know what you are doing. -simple_layers = round(model_height/layer_height); -translate(position_on_bed) - for (i = [initial_offset:N:simple_layers]) { - translate([0,0,i*layer_height]) - translate([0,0,(layer_height*number_of_solid_layers)/2]) - cube([model_width,model_depth,layer_height*number_of_solid_layers], center=true); - } diff --git a/utils/pdf-slices.pl b/utils/pdf-slices.pl deleted file mode 100755 index ca61da08e9..0000000000 --- a/utils/pdf-slices.pl +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/perl -# This script exports model slices to a PDF file as solid fills, one per page - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use PDF::API2; -use Slic3r; -use Slic3r::Geometry qw(scale unscale X Y); - -use constant mm => 25.4 / 72; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'output|o=s' => \$opt{output_file}, - 'layer-height|h=f' => \$opt{layer_height}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -{ - # prepare config - my $config = Slic3r::Config->new; - $config->set('layer_height', $opt{layer_height}) if $opt{layer_height}; - - # read model - my $model = Slic3r::Model->read_from_file(my $input_file = $ARGV[0]); - - # init print object - my $sprint = Slic3r::Print::Simple->new( - print_center => [0,0], - ); - $sprint->apply_config($config); - $sprint->set_model($model); - my $print = $sprint->_print; - - # compute sizes - my $bb = $print->bounding_box; - my $size = $bb->size; - my $mediabox = [ map unscale($_)/mm, @{$size} ]; - - # init PDF - my $pdf = PDF::API2->new(); - my $color = $pdf->colorspace_separation('RDG_GLOSS', 'darkblue'); - - # slice and build output geometry - $_->slice for @{$print->objects}; - foreach my $object (@{ $print->objects }) { - my $shift = $object->_shifted_copies->[0]; - $shift->translate(map $_/2, @$size); - - foreach my $layer (@{ $object->layers }) { - my $page = $pdf->page(); - $page->mediabox(@$mediabox); - my $content = $page->gfx; - $content->fillcolor($color, 1); - - foreach my $expolygon (@{$layer->slices}) { - $expolygon = $expolygon->clone; - $expolygon->translate(@$shift); - $content->poly(map { unscale($_->x)/mm, unscale($_->y)/mm } @{$expolygon->contour}); #) - $content->close; - foreach my $hole (@{$expolygon->holes}) { - $content->poly(map { unscale($_->x)/mm, unscale($_->y)/mm } @$hole); #) - $content->close; - } - $content->fill; # non-zero by default - } - } - } - - # write output file - my $output_file = $opt{output_file}; - if (!defined $output_file) { - $output_file = $input_file; - $output_file =~ s/\.(?:[sS][tT][lL])$/.pdf/; - } - $pdf->saveas($output_file); - printf "PDF file written to %s\n", $output_file; -} - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: pdf-slices.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --output, -o Write to the specified file - --layer-height, -h Use the specified layer height - -EOF - exit ($exit_code || 0); -} - -__END__ diff --git a/utils/post-processing/decimate.pl b/utils/post-processing/decimate.pl deleted file mode 100755 index 9e2938c5f2..0000000000 --- a/utils/post-processing/decimate.pl +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/perl -i~ - -use strict; -use warnings; - -my %lastpos = (X => 10000, Y => 10000, Z => 10000, E => 10000, F => 10000); -my %pos = (X => 0, Y => 0, Z => 0, E => 0, F => 0); - -my $mindist = 0.33; - -my $mindistz = 0.005; - -my $mindistsq = $mindist * $mindist; - -sub dist { - my $sq = 0; - for (qw/X Y Z E/) { - $sq += ($pos{$_} - $lastpos{$_}) ** 2; - } - return $sq; -} - -while (<>) { - if (m#\bG[01]\b#) { - while (m#([XYZEF])(\d+(\.\d+)?)#gi) { - $pos{uc $1} = $2; - } - if ( - ( - /X/ && - /Y/ && - (dist() >= $mindistsq) - ) || - (abs($pos{Z} - $lastpos{Z}) > $mindistz) || - (!/X/ || !/Y/) - ) { - print; - %lastpos = %pos; - } - elsif (($pos{F} - $lastpos{F}) != 0) { - printf "G1 F%s\n", $pos{F}; - $lastpos{F} = $pos{F}; - } - } - else { - if (m#\bG92\b#) { - while (m#([XYZEF])(\d+(\.\d+)?)#gi) { - $lastpos{uc $1} = $2; - } - } - print; - } -} diff --git a/utils/post-processing/fan_kickstart.py b/utils/post-processing/fan_kickstart.py deleted file mode 100644 index 9ee1bc0a4a..0000000000 --- a/utils/post-processing/fan_kickstart.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/python -import sys -import re - -sea = re.compile("M106 S[1-9]+[0-9]*") -rep = re.compile("M106 S255\n\g<0>") -out = open(sys.argv[1]+"_fixed", 'w') - with open(sys.argv[1]) as f: - for r in f: - if re.search(sea, r) is not None: - out.write(re.sub(sea,"M106 S255\n\g<0>",r)) - else: - out.write(r) diff --git a/utils/post-processing/filament-weight.pl b/utils/post-processing/filament-weight.pl deleted file mode 100755 index 5ed8364612..0000000000 --- a/utils/post-processing/filament-weight.pl +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/perl -i -# -# Post-processing script for adding weight and cost of required -# filament to G-code output. - -use strict; -use warnings; - -# example densities, adjust according to filament specifications -use constant PLA_P => 1.25; # g/cm3 -use constant ABS_P => 1.05; # g/cm3 - -# example costs, adjust according to filament prices -use constant PLA_PRICE => 0.05; # EUR/g -use constant ABS_PRICE => 0.02; # EUR/g -use constant CURRENCY => "EUR"; - -while (<>) { - if (/^(;\s+filament\s+used\s+=\s.*\((\d+(?:\.\d+)?)cm3)\)/) { - my $pla_weight = $2 * PLA_P; - my $abs_weight = $2 * ABS_P; - - my $pla_costs = $pla_weight * PLA_PRICE; - my $abs_costs = $abs_weight * ABS_PRICE; - - printf "%s or %.2fg PLA/%.2fg ABS)\n", $1, $pla_weight, $abs_weight; - printf "; costs = %s %.2f (PLA), %s %.2f (ABS)\n", CURRENCY, $pla_costs, CURRENCY, $abs_costs; - } else { - print; - } -} diff --git a/utils/post-processing/flowrate.pl b/utils/post-processing/flowrate.pl deleted file mode 100755 index f29d2312d4..0000000000 --- a/utils/post-processing/flowrate.pl +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/perl -i - -# -# Post-processing script for calculating flow rate for each move - -use strict; -use warnings; - -use constant PI => 3.141592653589793238; -my @filament_diameter = split /,/, $ENV{SLIC3R_FILAMENT_DIAMETER}; - -my $E = 0; -my $T = 0; -my ($X, $Y, $F); -while (<>) { - if (/^G1.*? F([0-9.]+)/) { - $F = $1; - } - if (/^G1 X([0-9.]+) Y([0-9.]+).*? E([0-9.]+)/) { - my ($x, $y, $e) = ($1, $2, $3); - my $e_length = $e - $E; - if ($e_length > 0 && defined $X && defined $Y) { - my $dist = sqrt( (($x-$X)**2) + (($y-$Y)**2) ); - if ($dist > 0) { - my $mm_per_mm = $e_length / $dist; # dE/dXY - my $mm3_per_mm = ($filament_diameter[$T] ** 2) * PI/4 * $mm_per_mm; - my $vol_speed = $F/60 * $mm3_per_mm; - my $comment = sprintf ' ; dXY = %.3fmm ; dE = %.5fmm ; dE/XY = %.5fmm/mm; volspeed = %.5fmm\x{00B3}/sec', - $dist, $e_length, $mm_per_mm, $vol_speed; - s/(\R+)/$comment$1/; - } - } - $E = $e; - $X = $x; - $Y = $y; - } - if (/^G1 X([0-9.]+) Y([0-9.]+)/) { - $X = $1; - $Y = $2; - } - if (/^G1.*? E([0-9.]+)/) { - $E = $1; - } - if (/^G92 E0/) { - $E = 0; - } - if (/^T(\d+)/) { - $T = $1; - } - print; -} - -__END__ diff --git a/utils/post-processing/prowl-notification.pl b/utils/post-processing/prowl-notification.pl deleted file mode 100755 index 5a5e1ca539..0000000000 --- a/utils/post-processing/prowl-notification.pl +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/perl -# -# Example post-processing script for sending a Prowl notification upon -# completion. See http://www.prowlapp.com/ for more info. - -use strict; -use warnings; - -use File::Basename qw(basename); -use WebService::Prowl; - -# set your Prowl API key here -my $apikey = ''; - -my $file = basename $ARGV[0]; -my $prowl = WebService::Prowl->new(apikey => $apikey); -my %options = (application => 'Slic3r', - event =>'Slicing Done!', - description => "$file was successfully generated"); -printf STDERR "Error sending Prowl notification: %s\n", $prowl->error - unless $prowl->add(%options); diff --git a/utils/post-processing/z-every-line.pl b/utils/post-processing/z-every-line.pl deleted file mode 100755 index aaf57e172a..0000000000 --- a/utils/post-processing/z-every-line.pl +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/perl -i - -use strict; -use warnings; - -my $z = 0; - -# read stdin and any/all files passed as parameters one line at a time -while (<>) { - # if we find a Z word, save it - $z = $1 if /Z\s*(\d+(\.\d+)?)/; - - # if we don't have Z, but we do have X and Y - if (!/Z/ && /X/ && /Y/ && $z > 0) { - # chop off the end of the line (incl. comments), saving chopped section in $1 - s/\s*([\r\n\;\(].*)/" Z$z $1"/es; - # print start of line, insert our Z value then re-add the chopped end of line - # print "$_ Z$z $1"; - } - #else { - # nothing interesting, print line as-is - print or die $!; - #} -} diff --git a/utils/send-gcode.pl b/utils/send-gcode.pl deleted file mode 100644 index 0b803baa6d..0000000000 --- a/utils/send-gcode.pl +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env perl - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Slic3r; - -die "Usage: send-gcode.pl SERIALPORT BAUDRATE GCODE_FILE\n" - if @ARGV != 3; - -my $serial = Slic3r::GCode::Sender->new($ARGV[0], $ARGV[1]); -1 until $serial->is_connected; -print "Connected to printer\n"; - -{ - local $/ = "\n"; - Slic3r::open(\my $fh, '<', $ARGV[2]) - or die "Unable to open $ARGV[2]: $!\n"; - binmode $fh, ':utf8'; - while (<$fh>) { - $serial->send($_); - } - close $fh; -} - -while ((my $queue_size = $serial->queue_size) > 0) { - printf "Queue size: %d\n", $queue_size; -} -$serial->disconnect; - -__END__ diff --git a/utils/split_stl.pl b/utils/split_stl.pl deleted file mode 100755 index 56217de4bd..0000000000 --- a/utils/split_stl.pl +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/perl -# This script splits a STL plate into individual files - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use File::Basename qw(basename); -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -$|++; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'ascii' => \$opt{ascii}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -{ - my $model = Slic3r::Model->load_stl($ARGV[0], basename($ARGV[0])); - my $basename = $ARGV[0]; - $basename =~ s/\.[sS][tT][lL]$//; - - my $part_count = 0; - my $mesh = $model->objects->[0]->volumes->[0]->mesh; - foreach my $new_mesh (@{$mesh->split}) { - $new_mesh->repair; - - my $new_model = Slic3r::Model->new; - $new_model - ->add_object() - ->add_volume(mesh => $new_mesh); - - $new_model->add_default_instances; - - my $output_file = sprintf '%s_%02d.stl', $basename, ++$part_count; - printf "Writing to %s\n", basename($output_file); - $new_model->store_stl($output_file, !$opt{ascii}); - } -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: split_stl.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --ascii Generate ASCII STL files (default: binary) - -EOF - exit ($exit_code || 0); -} - -__END__ diff --git a/utils/stl-to-amf.pl b/utils/stl-to-amf.pl deleted file mode 100755 index bb88b21617..0000000000 --- a/utils/stl-to-amf.pl +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/perl -# This script converts a STL file to AMF - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use File::Basename qw(basename); -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -$|++; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'distinct-materials' => \$opt{distinct_materials}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -{ - my @models = map Slic3r::Model->load_stl($_, basename($_)), @ARGV; - my $output_file = $ARGV[0]; - $output_file =~ s/\.[sS][tT][lL]$/.amf.xml/; - - my $new_model = Slic3r::Model->new; - - if ($opt{distinct_materials} && @models > 1) { - my $new_object = $new_model->add_object; - for my $m (0 .. $#models) { - my $model = $models[$m]; - $new_model->set_material($m, { Name => basename($ARGV[$m]) }); - $new_object->add_volume( - material_id => $m, - facets => $model->objects->[0]->volumes->[0]->facets, - vertices => $model->objects->[0]->vertices, - ); - } - } else { - foreach my $model (@models) { - $new_model->add_object( - vertices => $model->objects->[0]->vertices, - )->add_volume( - facets => $model->objects->[0]->volumes->[0]->facets, - ); - } - } - - printf "Writing to %s\n", basename($output_file); - $new_model->store_amf($output_file); -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: amf-to-stl.pl [ OPTIONS ] file.stl [ file2.stl [ file3.stl ] ] - - --help Output this usage screen and exit - --distinct-materials Assign each STL file to a different material - -EOF - exit ($exit_code || 0); -} - -__END__ diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl deleted file mode 100644 index 03153ab3f5..0000000000 --- a/utils/view-mesh.pl +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/perl -# This script displays 3D preview of a mesh - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -use Slic3r::GUI; -use Slic3r::GUI::3DScene; -$|++; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'cut=f' => \$opt{cut}, - 'enable-moving' => \$opt{enable_moving}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -{ - my $model = Slic3r::Model->read_from_file($ARGV[0]); - $_->center_around_origin for @{$model->objects}; # and align to Z = 0 - - my $app = Slic3r::ViewMesh->new; - $app->{canvas}->enable_picking(1); - $app->{canvas}->enable_moving($opt{enable_moving}); - $app->{canvas}->load_object($model, 0); - $app->{canvas}->set_auto_bed_shape; - $app->{canvas}->zoom_to_volumes; - $app->{canvas}->SetCuttingPlane($opt{cut}) if defined $opt{cut}; - $app->MainLoop; -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: view-mesh.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --cut Z Display the cutting plane at the given Z - -EOF - exit ($exit_code || 0); -} - -package Slic3r::ViewMesh; -use Wx qw(:sizer); -use base qw(Wx::App); - -sub OnInit { - my $self = shift; - - my $frame = Wx::Frame->new(undef, -1, 'Mesh Viewer', [-1, -1], [500, 400]); - my $panel = Wx::Panel->new($frame, -1); - - $self->{canvas} = Slic3r::GUI::3DScene->new($panel); - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($self->{canvas}, 1, wxEXPAND, 0); - $panel->SetSizer($sizer); - $sizer->SetSizeHints($panel); - - $frame->Show(1); -} - -__END__ diff --git a/utils/view-toolpaths.pl b/utils/view-toolpaths.pl deleted file mode 100755 index e064885ca3..0000000000 --- a/utils/view-toolpaths.pl +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/perl -# This script displays 3D preview of a mesh - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -use Slic3r::GUI; -use Slic3r::GUI::3DScene; -$|++; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'load=s' => \$opt{load}, - '3D' => \$opt{d3}, - 'duplicate=i' => \$opt{duplicate}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -{ - # load model - my $model = Slic3r::Model->read_from_file($ARGV[0]); - - # load config - my $config = Slic3r::Config::new_from_defaults; - if ($opt{load}) { - $config->apply(Slic3r::Config::load($opt{load})); - } - - # init print - my $sprint = Slic3r::Print::Simple->new; - $sprint->duplicate($opt{duplicate} // 1); - $sprint->apply_config($config); - $sprint->set_model($model); - $sprint->process; - - # visualize toolpaths - $Slic3r::ViewToolpaths::print = $sprint->_print; - $Slic3r::ViewToolpaths::d3 = $opt{d3}; - my $app = Slic3r::ViewToolpaths->new; - $app->MainLoop; -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: view-toolpaths.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --load CONFIG Loads the supplied config file - -EOF - exit ($exit_code || 0); -} - - -package Slic3r::ViewToolpaths; -use Wx qw(:sizer); -use base qw(Wx::App Class::Accessor); - -our $print; -our $d3; - -sub OnInit { - my $self = shift; - - my $frame = Wx::Frame->new(undef, -1, 'Toolpaths', [-1, -1], [500, 500]); - my $panel = Wx::Panel->new($frame, -1); - - my $canvas; - if ($d3) { - $canvas = Slic3r::GUI::3DScene->new($panel); - $canvas->set_bed_shape($print->config->bed_shape); - $canvas->load_print_toolpaths($print); - - foreach my $object (@{$print->objects}) { - #$canvas->load_print_object_slices($object); - $canvas->load_print_object_toolpaths($object); - #$canvas->load_object($object->model_object); - } - $canvas->zoom_to_volumes; - } else { - $canvas = Slic3r::GUI::Plater::2DToolpaths->new($panel, $print); - } - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($canvas, 1, wxEXPAND, 0); - $panel->SetSizer($sizer); - - $frame->Show(1); -} - -__END__ diff --git a/utils/wireframe.pl b/utils/wireframe.pl deleted file mode 100644 index 5399f31aca..0000000000 --- a/utils/wireframe.pl +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/perl -# This script exports experimental G-code for wireframe printing -# (inspired by the brilliant WirePrint concept) - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale X Y PI); - -my %opt = ( - step_height => 5, - nozzle_angle => 30, - nozzle_width => 10, - first_layer_height => 0.3, -); -{ - my %options = ( - 'help' => sub { usage() }, - 'output|o=s' => \$opt{output_file}, - 'step-height|h=f' => \$opt{step_height}, - 'nozzle-angle|a=f' => \$opt{nozzle_angle}, - 'nozzle-width|w=f' => \$opt{nozzle_width}, - 'first-layer-height=f' => \$opt{first_layer_height}, - ); - GetOptions(%options) or usage(1); - $opt{output_file} or usage(1); - $ARGV[0] or usage(1); -} - -{ - # load model - my $model = Slic3r::Model->read_from_file($ARGV[0]); - $model->center_instances_around_point(Slic3r::Pointf->new(100,100)); - my $mesh = $model->mesh; - $mesh->translate(0, 0, -$mesh->bounding_box->z_min); - - # get slices - my @z = (); - my $z_max = $mesh->bounding_box->z_max; - for (my $z = $opt{first_layer_height}; $z <= $z_max; $z += $opt{step_height}) { - push @z, $z; - } - my @slices = @{$mesh->slice(\@z)}; - - my $flow = Slic3r::Flow->new( - width => 0.35, - height => 0.35, - nozzle_diameter => 0.35, - bridge => 1, - ); - - my $config = Slic3r::Config::Print->new; - $config->set('gcode_comments', 1); - - open my $fh, '>', $opt{output_file}; - my $gcodegen = Slic3r::GCode->new( - enable_loop_clipping => 0, # better bonding - ); - $gcodegen->apply_print_config($config); - $gcodegen->set_extruders([0]); - print $fh $gcodegen->set_extruder(0); - print $fh $gcodegen->writer->preamble; - - my $e = $gcodegen->writer->extruder->e_per_mm3 * $flow->mm3_per_mm; - - foreach my $layer_id (0..$#z) { - my $z = $z[$layer_id]; - - foreach my $island (@{$slices[$layer_id]}) { - foreach my $polygon (@$island) { - if ($layer_id > 0) { - # find the lower polygon that we want to connect to this one - my $lower = $slices[$layer_id-1]->[0]->contour; # 't was easy, wasn't it? - my $lower_z = $z[$layer_id-1]; - - { - my @points = (); - - # keep all points with strong angles - { - my @pp = @$polygon; - foreach my $i (0..$#pp) { - push @points, $pp[$i-1] if abs($pp[$i-1]->ccw_angle($pp[$i-2], $pp[$i]) - PI) > PI/3; - } - } - - $polygon = Slic3r::Polygon->new(@points); - } - #$polygon = Slic3r::Polygon->new(@{$polygon->split_at_first_point->equally_spaced_points(scale $opt{nozzle_width})}); - - # find vertical lines - my @vertical = (); - foreach my $point (@{$polygon}) { - push @vertical, Slic3r::Line->new($point->projection_onto_polygon($lower), $point); - } - - next if !@vertical; - - my @points = (); - foreach my $line (@vertical) { - push @points, Slic3r::Pointf3->new( - unscale($line->a->x), - unscale($line->a->y), #)) - $lower_z, - ); - push @points, Slic3r::Pointf3->new( - unscale($line->b->x), - unscale($line->b->y), #)) - $z, - ); - } - - # reappend first point as destination of the last diagonal segment - push @points, Slic3r::Pointf3->new( - unscale($vertical[0]->a->x), - unscale($vertical[0]->a->y), #)) - $lower_z, - ); - - # move to the position of the first vertical line - print $fh $gcodegen->writer->travel_to_xyz(shift @points); - - # extrude segments - foreach my $point (@points) { - print $fh $gcodegen->writer->extrude_to_xyz($point, $e * $gcodegen->writer->get_position->distance_to($point)); - } - } - } - - print $fh $gcodegen->writer->travel_to_z($z); - foreach my $polygon (@$island) { - #my $polyline = $polygon->split_at_vertex(Slic3r::Point->new_scale(@{$gcodegen->writer->get_position}[0,1])); - my $polyline = $polygon->split_at_first_point; - print $fh $gcodegen->writer->travel_to_xy(Slic3r::Pointf->new_unscale(@{ $polyline->first_point }), "move to first contour point"); - - foreach my $line (@{$polyline->lines}) { - my $point = Slic3r::Pointf->new_unscale(@{ $line->b }); - print $fh $gcodegen->writer->extrude_to_xy($point, $e * unscale($line->length)); - } - } - } - } - - close $fh; -} - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: wireframe.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --output, -o Write to the specified file - --step-height, -h Use the specified step height - --nozzle-angle, -a Max nozzle angle - --nozzle-width, -w External nozzle diameter - -EOF - exit ($exit_code || 0); -} - -__END__ diff --git a/utils/zsh/README.markdown b/utils/zsh/README.markdown deleted file mode 100644 index e84b943104..0000000000 --- a/utils/zsh/README.markdown +++ /dev/null @@ -1,21 +0,0 @@ -# ZSH Completions for Slic3r - -To enable zsh(1) completions for Slic3r, add the following to your -``~/.zshrc`` file, replacing ``/path/to/Slic3r/`` with the actual path -to your Slic3r directory: - - typeset -U fpath - - if [[ -d /path/to/Slic3r/utils/zsh/functions ]]; then - fpath=(/path/to/Slic3r/utils/zsh/functions $fpath) - fi - - autoload -Uz compinit - compinit - zstyle ':completion:*' verbose true - zstyle ':completion:*:descriptions' format '%B%d%b' - zstyle ':completion:*:messages' format '%d' - zstyle ':completion:*:warnings' format 'No matches for %d' - zstyle ':completion:*' group-name '%d' - -See the zshcompsys(1) man page for further details. diff --git a/utils/zsh/functions/_slic3r b/utils/zsh/functions/_slic3r deleted file mode 100644 index cea887cc61..0000000000 --- a/utils/zsh/functions/_slic3r +++ /dev/null @@ -1,118 +0,0 @@ -#compdef -P slic3r(|.pl|.exe) -# -# Slic3r completions configuration for zsh(1). - -# Currently undocumented options: -# --debug, --gui, --ignore-nonexistent-config -# --acceleration, --perimeter-acceleration, --infill-acceleration - -_arguments -S \ - '(- *)--help[output usage screen and exit]' \ - '(- *)--version[output the version of Slic3r and exit]' \ - '--save[save configuration to file]:config output file:_files -g "*.(#i)ini(-.)"' \ - '*--load[load configuration from file]:config input file:_files -g "*.(#i)ini(-.)"' \ - '(--output -o)'{--output,-o}'[specify output file]:output file:_files -g "*.(#i)(gcode|svg)(-.)"' \ - '(--threads -j)'{--threads,-j}'[specify number of threads to use]:number of threads' \ - \ - '--output-filename-format[specify output filename format]:output filename format' \ - '*--post-process[specify post-processing script]:post-processing script file:_files' \ - '--export-svg[export SVG containing slices instead of G-code]' \ - '(--merge -m)'{--merge,-m}'[merge multiple input files into a single print]' \ - \ - '*--nozzle-diameter[specify nozzle diameter]:nozzle diameter in mm' \ - '--print-center[specify print center coordinates]:print center coordinates in mm,mm' \ - '--z-offset[specify Z-axis offset]:Z-axis offset in mm' \ - '--gcode-flavor[specify the type of G-code to generate]:G-code flavor:(reprap teacup repetier makerware sailfish mach3 machinekit smoothie no-extrusion)' \ - '(--use-relative-e-distances --no-use-relative-e-distances)'--{no-,}use-relative-e-distances'[disable/enable relative E values]' \ - '--extrusion-axis[specify letter associated with the extrusion axis]:extrusion axis letter' \ - '(--gcode-arcs --no-gcode-arcs)'--{no-,}gcode-arcs'[disable/enable G2/G3 commands for native arcs]' \ - '(--gcode-comments --no-gcode-comments)'--{no-,}gcode-comments'[disable/enable verbose G-code comments]' \ - \ - '*--filament-diameter[specify raw filament diameter]:raw filament diameter in mm' \ - '*--extrusion-multiplier[specify multiplier for amount of plastic extruded]:extrusion multiplier' \ - '*--temperature[specify extrusion temperature]:extrusion temperature in Celsius' \ - '*--first-layer-temperature[specify extrusion temperature for the first layer]:first layer extrusion temperature in Celsius' \ - '--bed-temperature[specify heated bed temperature]:heated bed temperature in Celsius' \ - '--first-layer-bed-temperature[specify heated bed temperature for the first layer]:first layer heated bed temperature in Celsius' \ - \ - '--perimeter-extruder[specify extruder to use for printing perimeters]:extruder number' \ - '--infill-extruder[specify extruder to use for printing infill]:extruder number' \ - '--support-material-extruder[specify extruder to use for printing support material]:extruder number' \ - \ - '--travel-speed[specify speed of non-print moves]:speed of non-print moves in mm/s' \ - '--perimeter-speed[specify speed of print moves for perimeters]:speed of print moves for perimeters in mm/s' \ - '--external-perimeter-speed[specify speed of print moves for external perimeters]:speed of print moves for external perimeters in mm/s or % of --perimeter-speed' \ - '--small-perimeter-speed[specify speed of print moves for small perimeters]:speed of print moves for small perimeters in mm/s or % of --perimeter-speed' \ - '--infill-speed[specify speed of infill print moves]:speed of infill print moves in mm/s' \ - '--solid-infill-speed[specify speed of solid surface print moves]:speed of solid surface print moves in mm/s or % of --infill-speed' \ - '--top-solid-infill-speed[specify speed of top surface print moves]:speed of top surface print moves in mm/s or % of --solid-infill-speed' \ - '--bridge-speed[specify speed of bridge print moves]:speed of bridge print moves in mm/s' \ - '--first-layer-speed[specify speed of bottom layer print moves]:speed of bottom layer print moves in mm/s or % of normal speeds' \ - \ - '--layer-height[specify layer height]:layer height in mm' \ - '--first-layer-height[specify layer height for bottom layer]:layer height for bottom layer in mm or % of --layer-height' \ - '--infill-every-layers[specify infill for every N layers]:N layers' \ - \ - '--perimeters[specify number of perimeters]:number of perimeters' \ - '--solid-layers[specify number of solid layers to do for top/bottom surfaces]:number of layers for top/bottom surfaces' \ - '--fill-density[specify infill density]:infill density in percent' \ - '--fill-angle[specify infill angle]:infill angle in degrees' \ - '--fill-pattern[specify pattern used for infill]:infill pattern:(rectilinear line concentric honeycomb hilbertcurve archimedeanchords octagramspiral)' \ - '--solid-fill-pattern[specify pattern used for solid layers]:solid fill pattern:(rectilinear concentric hilbertcurve archimedeanchords octagramspiral)' \ - '--start-gcode[load initial G-code from file]:start G-code file:_files -g "*.(#i)(gcode)(-.)"' \ - '--end-gcode[load final G-code from file]:end G-code file:_files -g "*.(#i)(gcode)(-.)"' \ - '--layer-gcode[load layer-change G-code from file]:layer-change G-code file:_files -g "*.(#i)(gcode)(-.)"' \ - '(--support-material --no-support-material)'--{no-,}support-material'[disable/enable generation of support material for overhangs]' \ - '--support-material-threshold[specify support material threshold]:maximum slope angle for generating support material' \ - '--support-material-pattern[specify pattern used for support material]:support material pattern:(rectilinear honeycomb)' \ - '--support-material-spacing[specify spacing between support material lines]:spacing between support material lines in mm' \ - '--support-material-angle[specify support material angle]:support material angle in degrees' \ - '(--randomize-start --no-randomize-start)'--{no-,}randomize-start'[disable/enable randomization of starting point across layers]' \ - '(--extra-perimeters --no-extra-perimeters)'--{no-,}extra-perimeters'[disable/enable generation of extra perimeters when needed]' \ - \ - '--retract-length[specify filament retraction length when pausing extrusion]:filament retraction length in mm' \ - '--retract-speed[specify filament retraction speed]:filament retraction speed in mm/s' \ - '--retract-restart-extra[specify filament length to extrude for compensating retraction]: filament lenght in mm' \ - '--retract-before-travel[specify minimum travel length for activating retraction]:minimum travel length for activating retraction in mm' \ - '--retract-lift[specify Z-axis lift for use when retracting]:Z-axis lift in mm' \ - \ - '(--cooling --no-cooling)'--{no-,}cooling'[disable/enable fan and cooling control]' \ - '--min-fan-speed[specify minimum fan speed]:minimum fan speed in percent' \ - '--max-fan-speed[specify maximum fan speed]:maximum fan speed in percent' \ - '--bridge-fan-speed[specify fan speed to use for bridging]:bridging fan speed in percent' \ - '--fan-below-layer-time[specify maximum layer print time before activating fan]:maximum layer print time in seconds' \ - '--slowdown-below-layer-time[specify maximum layer print time before slowing down printing]:maximum layer print time in seconds' \ - '--min-print-speed[specify minimum print speed]:minimum print speed in mm/s' \ - '--disable-fan-first-layers[specify number of bottom layers to print before activating fan]:number of bottom layers' \ - '(--fan-always-on --no-fan-always-on)'--{no-,}fan-always-on'[disable/enable deactivation of fan]' \ - \ - '--skirts[specify number of skirts]:number of skirts' \ - '--skirt-distance[specify distance between innermost skirt and object]:distance between innermost skirt and object in mm' \ - '--skirt-height[specify number of skirt layers]:number of skirt layers' \ - '--brim-width[specify brim width]:width of brim in mm' \ - \ - '--scale[specify object scaling factor]:object scaling factor in percent' \ - '--rotate[specify object rotation angle]:object rotation angle in degrees' \ - '(--duplicate-grid)--duplicate[specify number of duplicates for auto-arrange]:number of duplicates for auto-arrange' \ - '(--duplicate-grid)--bed-size[specify bed size for auto-arrange]:bed size for auto-arrange in mm,mm' \ - '(--duplicate --bed-size)--duplicate-grid[specify number of duplicates for grid arrangement]:number of duplicates for grid arrangement as x,y' \ - '--duplicate-distance[specify distance between duplicates]:distance between duplicates in mm' \ - \ - '(--complete-objects --no-complete-objects)'--{no-,}complete-objects'[disable/enable completion of each object before starting a new one]' \ - '--extruder-clearance-radius[specify radius above which extruder will not collide with anything]:radius in mm' \ - '--extruder-clearance-height[specify maximum vertical extruder depth]:maximum vertical extruder depth in mm' \ - \ - '--notes[specify notes to be added as comments to the output file]:notes' \ - \ - '--extrusion-width[specify extrusion width]:extrusion width in mm or % of --layer-height' \ - '--first-layer-extrusion-width[specify extrusion width for first layer]:first layer extrusion width in mm or % og --layer-height' \ - '--perimeters-extrusion-width[specify extrusion width for perimeters]:perimeter extrusion width in mm or % of --layer-height' \ - '--infill-extrusion-width[specify extrusion width for infill]:infill extrusion width in mm or % of --layer-height' \ - '--support-material-extrusion-width[specify extrusion width for support material]:support material extrusion width in mm or % of --layer-height' \ - '--bridge-flow-ratio[specify multiplier for extrusion when bridging]:bridge extrusion multiplier' \ - \ - '*:input file:_files -g "*.(#i)(stl|obj|amf|xml|prusa)(-.)"' - -# Local Variables: *** -# mode:sh *** -# End: *** diff --git a/version.inc b/version.inc new file mode 100644 index 0000000000..95e2741dd2 --- /dev/null +++ b/version.inc @@ -0,0 +1,9 @@ +# Included by CMakeLists, edited by the build script +# (the version numbers are generated by the build script from the git current label) + +set(SLIC3R_FORK_NAME "Slic3r Prusa Edition") +set(SLIC3R_VERSION "1.42.0-alpha") +set(SLIC3R_BUILD "${SLIC3R_VERSION}+UNKNOWN") +set(SLIC3R_BUILD_ID "${SLIC3R_BUILD_ID}") +set(SLIC3R_RC_VERSION "1,42,0,0") +set(SLIC3R_RC_VERSION_DOTS "1.42.0.0") diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index d19aad70a3..27228dacff 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -1,371 +1,29 @@ -# Enable C++11 language standard. -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) +project(XS) -# Add our own cmake module path. -list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/) - -if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - # Workaround for an old CMake, which does not understand CMAKE_CXX_STANDARD. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall" ) - find_package(PkgConfig REQUIRED) +# 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() -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. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals" ) -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) -#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_ITERATOR_DEBUG_LEVEL) - 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_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE -DWXINTL_NO_GETTEXT_MACRO) - -add_library(libslic3r STATIC - ${LIBDIR}/libslic3r/BoundingBox.cpp - ${LIBDIR}/libslic3r/BoundingBox.hpp - ${LIBDIR}/libslic3r/BridgeDetector.cpp - ${LIBDIR}/libslic3r/BridgeDetector.hpp - ${LIBDIR}/libslic3r/ClipperUtils.cpp - ${LIBDIR}/libslic3r/ClipperUtils.hpp - ${LIBDIR}/libslic3r/Config.cpp - ${LIBDIR}/libslic3r/Config.hpp - ${LIBDIR}/libslic3r/EdgeGrid.cpp - ${LIBDIR}/libslic3r/EdgeGrid.hpp - ${LIBDIR}/libslic3r/ExPolygon.cpp - ${LIBDIR}/libslic3r/ExPolygon.hpp - ${LIBDIR}/libslic3r/ExPolygonCollection.cpp - ${LIBDIR}/libslic3r/ExPolygonCollection.hpp - ${LIBDIR}/libslic3r/Extruder.cpp - ${LIBDIR}/libslic3r/Extruder.hpp - ${LIBDIR}/libslic3r/ExtrusionEntity.cpp - ${LIBDIR}/libslic3r/ExtrusionEntity.hpp - ${LIBDIR}/libslic3r/ExtrusionEntityCollection.cpp - ${LIBDIR}/libslic3r/ExtrusionEntityCollection.hpp - ${LIBDIR}/libslic3r/ExtrusionSimulator.cpp - ${LIBDIR}/libslic3r/ExtrusionSimulator.hpp - ${LIBDIR}/libslic3r/FileParserError.hpp - ${LIBDIR}/libslic3r/Fill/Fill.cpp - ${LIBDIR}/libslic3r/Fill/Fill.hpp - ${LIBDIR}/libslic3r/Fill/Fill3DHoneycomb.cpp - ${LIBDIR}/libslic3r/Fill/Fill3DHoneycomb.hpp - ${LIBDIR}/libslic3r/Fill/FillBase.cpp - ${LIBDIR}/libslic3r/Fill/FillBase.hpp - ${LIBDIR}/libslic3r/Fill/FillConcentric.cpp - ${LIBDIR}/libslic3r/Fill/FillConcentric.hpp - ${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp - ${LIBDIR}/libslic3r/Fill/FillHoneycomb.hpp - ${LIBDIR}/libslic3r/Fill/FillGyroid.cpp - ${LIBDIR}/libslic3r/Fill/FillGyroid.hpp - ${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp - ${LIBDIR}/libslic3r/Fill/FillPlanePath.hpp - ${LIBDIR}/libslic3r/Fill/FillRectilinear.cpp - ${LIBDIR}/libslic3r/Fill/FillRectilinear.hpp - ${LIBDIR}/libslic3r/Fill/FillRectilinear2.cpp - ${LIBDIR}/libslic3r/Fill/FillRectilinear2.hpp - ${LIBDIR}/libslic3r/Fill/FillRectilinear3.cpp - ${LIBDIR}/libslic3r/Fill/FillRectilinear3.hpp - ${LIBDIR}/libslic3r/Flow.cpp - ${LIBDIR}/libslic3r/Flow.hpp - ${LIBDIR}/libslic3r/Format/3mf.cpp - ${LIBDIR}/libslic3r/Format/3mf.hpp - ${LIBDIR}/libslic3r/Format/AMF.cpp - ${LIBDIR}/libslic3r/Format/AMF.hpp - ${LIBDIR}/libslic3r/Format/OBJ.cpp - ${LIBDIR}/libslic3r/Format/OBJ.hpp - ${LIBDIR}/libslic3r/Format/objparser.cpp - ${LIBDIR}/libslic3r/Format/objparser.hpp - ${LIBDIR}/libslic3r/Format/PRUS.cpp - ${LIBDIR}/libslic3r/Format/PRUS.hpp - ${LIBDIR}/libslic3r/Format/STL.cpp - ${LIBDIR}/libslic3r/Format/STL.hpp - ${LIBDIR}/libslic3r/GCode/Analyzer.cpp - ${LIBDIR}/libslic3r/GCode/Analyzer.hpp - ${LIBDIR}/libslic3r/GCode/CoolingBuffer.cpp - ${LIBDIR}/libslic3r/GCode/CoolingBuffer.hpp - ${LIBDIR}/libslic3r/GCode/PressureEqualizer.cpp - ${LIBDIR}/libslic3r/GCode/PressureEqualizer.hpp - ${LIBDIR}/libslic3r/GCode/PreviewData.cpp - ${LIBDIR}/libslic3r/GCode/PreviewData.hpp - ${LIBDIR}/libslic3r/GCode/PrintExtents.cpp - ${LIBDIR}/libslic3r/GCode/PrintExtents.hpp - ${LIBDIR}/libslic3r/GCode/SpiralVase.cpp - ${LIBDIR}/libslic3r/GCode/SpiralVase.hpp - ${LIBDIR}/libslic3r/GCode/ToolOrdering.cpp - ${LIBDIR}/libslic3r/GCode/ToolOrdering.hpp - ${LIBDIR}/libslic3r/GCode/WipeTower.hpp - ${LIBDIR}/libslic3r/GCode/WipeTowerPrusaMM.cpp - ${LIBDIR}/libslic3r/GCode/WipeTowerPrusaMM.hpp - ${LIBDIR}/libslic3r/GCode.cpp - ${LIBDIR}/libslic3r/GCode.hpp - ${LIBDIR}/libslic3r/GCodeReader.cpp - ${LIBDIR}/libslic3r/GCodeReader.hpp - ${LIBDIR}/libslic3r/GCodeSender.cpp - ${LIBDIR}/libslic3r/GCodeSender.hpp - ${LIBDIR}/libslic3r/GCodeTimeEstimator.cpp - ${LIBDIR}/libslic3r/GCodeTimeEstimator.hpp - ${LIBDIR}/libslic3r/GCodeWriter.cpp - ${LIBDIR}/libslic3r/GCodeWriter.hpp - ${LIBDIR}/libslic3r/Geometry.cpp - ${LIBDIR}/libslic3r/Geometry.hpp - ${LIBDIR}/libslic3r/Int128.hpp -# ${LIBDIR}/libslic3r/KdTree.hpp - ${LIBDIR}/libslic3r/Layer.cpp - ${LIBDIR}/libslic3r/Layer.hpp - ${LIBDIR}/libslic3r/LayerRegion.cpp - ${LIBDIR}/libslic3r/libslic3r.h - ${LIBDIR}/libslic3r/Line.cpp - ${LIBDIR}/libslic3r/Line.hpp - ${LIBDIR}/libslic3r/Model.cpp - ${LIBDIR}/libslic3r/Model.hpp - ${LIBDIR}/libslic3r/ModelArrange.hpp - ${LIBDIR}/libslic3r/MotionPlanner.cpp - ${LIBDIR}/libslic3r/MotionPlanner.hpp - ${LIBDIR}/libslic3r/MultiPoint.cpp - ${LIBDIR}/libslic3r/MultiPoint.hpp - ${LIBDIR}/libslic3r/MutablePriorityQueue.hpp - ${LIBDIR}/libslic3r/PerimeterGenerator.cpp - ${LIBDIR}/libslic3r/PerimeterGenerator.hpp - ${LIBDIR}/libslic3r/PlaceholderParser.cpp - ${LIBDIR}/libslic3r/PlaceholderParser.hpp - ${LIBDIR}/libslic3r/Point.cpp - ${LIBDIR}/libslic3r/Point.hpp - ${LIBDIR}/libslic3r/Polygon.cpp - ${LIBDIR}/libslic3r/Polygon.hpp - ${LIBDIR}/libslic3r/Polyline.cpp - ${LIBDIR}/libslic3r/Polyline.hpp - ${LIBDIR}/libslic3r/PolylineCollection.cpp - ${LIBDIR}/libslic3r/PolylineCollection.hpp - ${LIBDIR}/libslic3r/Print.cpp - ${LIBDIR}/libslic3r/Print.hpp - ${LIBDIR}/libslic3r/PrintConfig.cpp - ${LIBDIR}/libslic3r/PrintConfig.hpp - ${LIBDIR}/libslic3r/PrintObject.cpp - ${LIBDIR}/libslic3r/PrintRegion.cpp - ${LIBDIR}/libslic3r/Slicing.cpp - ${LIBDIR}/libslic3r/Slicing.hpp - ${LIBDIR}/libslic3r/SlicingAdaptive.cpp - ${LIBDIR}/libslic3r/SlicingAdaptive.hpp - ${LIBDIR}/libslic3r/SupportMaterial.cpp - ${LIBDIR}/libslic3r/SupportMaterial.hpp - ${LIBDIR}/libslic3r/Surface.cpp - ${LIBDIR}/libslic3r/Surface.hpp - ${LIBDIR}/libslic3r/SurfaceCollection.cpp - ${LIBDIR}/libslic3r/SurfaceCollection.hpp - ${LIBDIR}/libslic3r/SVG.cpp - ${LIBDIR}/libslic3r/SVG.hpp - ${LIBDIR}/libslic3r/TriangleMesh.cpp - ${LIBDIR}/libslic3r/TriangleMesh.hpp -# ${LIBDIR}/libslic3r/utils.cpp - ${LIBDIR}/libslic3r/Utils.hpp -) - -add_library(libslic3r_gui STATIC - ${LIBDIR}/slic3r/GUI/AboutDialog.cpp - ${LIBDIR}/slic3r/GUI/AboutDialog.hpp - ${LIBDIR}/slic3r/GUI/AppConfig.cpp - ${LIBDIR}/slic3r/GUI/AppConfig.hpp - ${LIBDIR}/slic3r/GUI/BitmapCache.cpp - ${LIBDIR}/slic3r/GUI/BitmapCache.hpp - ${LIBDIR}/slic3r/GUI/ConfigSnapshotDialog.cpp - ${LIBDIR}/slic3r/GUI/ConfigSnapshotDialog.hpp - ${LIBDIR}/slic3r/GUI/3DScene.cpp - ${LIBDIR}/slic3r/GUI/3DScene.hpp - ${LIBDIR}/slic3r/GUI/GLShader.cpp - ${LIBDIR}/slic3r/GUI/GLShader.hpp - ${LIBDIR}/slic3r/GUI/GLCanvas3D.hpp - ${LIBDIR}/slic3r/GUI/GLCanvas3D.cpp - ${LIBDIR}/slic3r/GUI/GLCanvas3DManager.hpp - ${LIBDIR}/slic3r/GUI/GLCanvas3DManager.cpp - ${LIBDIR}/slic3r/GUI/GLGizmo.hpp - ${LIBDIR}/slic3r/GUI/GLGizmo.cpp - ${LIBDIR}/slic3r/GUI/GLTexture.hpp - ${LIBDIR}/slic3r/GUI/GLTexture.cpp - ${LIBDIR}/slic3r/GUI/Preferences.cpp - ${LIBDIR}/slic3r/GUI/Preferences.hpp - ${LIBDIR}/slic3r/GUI/Preset.cpp - ${LIBDIR}/slic3r/GUI/Preset.hpp - ${LIBDIR}/slic3r/GUI/PresetBundle.cpp - ${LIBDIR}/slic3r/GUI/PresetBundle.hpp - ${LIBDIR}/slic3r/GUI/PresetHints.cpp - ${LIBDIR}/slic3r/GUI/PresetHints.hpp - ${LIBDIR}/slic3r/GUI/GUI.cpp - ${LIBDIR}/slic3r/GUI/GUI.hpp - ${LIBDIR}/slic3r/GUI/Tab.cpp - ${LIBDIR}/slic3r/GUI/Tab.hpp - ${LIBDIR}/slic3r/GUI/TabIface.cpp - ${LIBDIR}/slic3r/GUI/TabIface.hpp - ${LIBDIR}/slic3r/GUI/Field.cpp - ${LIBDIR}/slic3r/GUI/Field.hpp - ${LIBDIR}/slic3r/GUI/OptionsGroup.cpp - ${LIBDIR}/slic3r/GUI/OptionsGroup.hpp - ${LIBDIR}/slic3r/GUI/BedShapeDialog.cpp - ${LIBDIR}/slic3r/GUI/BedShapeDialog.hpp - ${LIBDIR}/slic3r/GUI/2DBed.cpp - ${LIBDIR}/slic3r/GUI/2DBed.hpp - ${LIBDIR}/slic3r/GUI/wxExtensions.cpp - ${LIBDIR}/slic3r/GUI/wxExtensions.hpp - ${LIBDIR}/slic3r/GUI/WipeTowerDialog.cpp - ${LIBDIR}/slic3r/GUI/WipeTowerDialog.hpp - ${LIBDIR}/slic3r/GUI/RammingChart.cpp - ${LIBDIR}/slic3r/GUI/RammingChart.hpp - ${LIBDIR}/slic3r/GUI/BonjourDialog.cpp - ${LIBDIR}/slic3r/GUI/BonjourDialog.hpp - ${LIBDIR}/slic3r/GUI/ButtonsDescription.cpp - ${LIBDIR}/slic3r/GUI/ButtonsDescription.hpp - ${LIBDIR}/slic3r/Config/Snapshot.cpp - ${LIBDIR}/slic3r/Config/Snapshot.hpp - ${LIBDIR}/slic3r/Config/Version.cpp - ${LIBDIR}/slic3r/Config/Version.hpp - ${LIBDIR}/slic3r/Utils/ASCIIFolding.cpp - ${LIBDIR}/slic3r/Utils/ASCIIFolding.hpp - ${LIBDIR}/slic3r/Utils/Serial.cpp - ${LIBDIR}/slic3r/Utils/Serial.hpp - ${LIBDIR}/slic3r/GUI/ConfigWizard.cpp - ${LIBDIR}/slic3r/GUI/ConfigWizard.hpp - ${LIBDIR}/slic3r/GUI/MsgDialog.cpp - ${LIBDIR}/slic3r/GUI/MsgDialog.hpp - ${LIBDIR}/slic3r/GUI/UpdateDialogs.cpp - ${LIBDIR}/slic3r/GUI/UpdateDialogs.hpp - ${LIBDIR}/slic3r/GUI/FirmwareDialog.cpp - ${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp - ${LIBDIR}/slic3r/Utils/Http.cpp - ${LIBDIR}/slic3r/Utils/Http.hpp - ${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp - ${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp - ${LIBDIR}/slic3r/Utils/PrintHostSendDialog.cpp - ${LIBDIR}/slic3r/Utils/PrintHostSendDialog.hpp - ${LIBDIR}/slic3r/Utils/OctoPrint.cpp - ${LIBDIR}/slic3r/Utils/OctoPrint.hpp - ${LIBDIR}/slic3r/Utils/Duet.cpp - ${LIBDIR}/slic3r/Utils/Duet.hpp - ${LIBDIR}/slic3r/Utils/PrintHost.cpp - ${LIBDIR}/slic3r/Utils/PrintHost.hpp - ${LIBDIR}/slic3r/Utils/Bonjour.cpp - ${LIBDIR}/slic3r/Utils/Bonjour.hpp - ${LIBDIR}/slic3r/Utils/PresetUpdater.cpp - ${LIBDIR}/slic3r/Utils/PresetUpdater.hpp - ${LIBDIR}/slic3r/Utils/Time.cpp - ${LIBDIR}/slic3r/Utils/Time.hpp - ${LIBDIR}/slic3r/Utils/HexFile.cpp - ${LIBDIR}/slic3r/Utils/HexFile.hpp - ${LIBDIR}/slic3r/ProgressIndicator.hpp - ${LIBDIR}/slic3r/AppController.hpp - ${LIBDIR}/slic3r/AppController.cpp - ${LIBDIR}/slic3r/AppControllerWx.cpp -) - -add_library(admesh STATIC - ${LIBDIR}/admesh/connect.cpp - ${LIBDIR}/admesh/normals.cpp - ${LIBDIR}/admesh/shared.cpp - ${LIBDIR}/admesh/stl.h - ${LIBDIR}/admesh/stl_io.cpp - ${LIBDIR}/admesh/stlinit.cpp - ${LIBDIR}/admesh/util.cpp -) - -add_library(miniz STATIC - ${LIBDIR}/miniz/miniz.h - ${LIBDIR}/miniz/miniz_common.h - ${LIBDIR}/miniz/miniz_tdef.h - ${LIBDIR}/miniz/miniz_tinfl.h - ${LIBDIR}/miniz/miniz_zip.h - ${LIBDIR}/miniz/miniz.cpp - ${LIBDIR}/miniz/miniz_tdef.cpp - ${LIBDIR}/miniz/miniz_tinfl.cpp - ${LIBDIR}/miniz/miniz_zip.cpp -) - -add_library(clipper STATIC - ${LIBDIR}/clipper.cpp - ${LIBDIR}/clipper.hpp -) - -add_library(polypartition STATIC - ${LIBDIR}/polypartition.cpp - ${LIBDIR}/polypartition.h -) - -add_library(poly2tri STATIC - ${LIBDIR}/poly2tri/common/shapes.cc - ${LIBDIR}/poly2tri/common/shapes.h - ${LIBDIR}/poly2tri/common/utils.h - ${LIBDIR}/poly2tri/poly2tri.h - ${LIBDIR}/poly2tri/sweep/advancing_front.cc - ${LIBDIR}/poly2tri/sweep/advancing_front.h - ${LIBDIR}/poly2tri/sweep/cdt.cc - ${LIBDIR}/poly2tri/sweep/cdt.h - ${LIBDIR}/poly2tri/sweep/sweep.cc - ${LIBDIR}/poly2tri/sweep/sweep.h - ${LIBDIR}/poly2tri/sweep/sweep_context.cc - ${LIBDIR}/poly2tri/sweep/sweep_context.h -) - -add_library(nowide STATIC - ${LIBDIR}/boost/nowide/args.hpp - ${LIBDIR}/boost/nowide/cenv.hpp - ${LIBDIR}/boost/nowide/config.hpp - ${LIBDIR}/boost/nowide/convert.hpp - ${LIBDIR}/boost/nowide/cstdio.hpp - ${LIBDIR}/boost/nowide/cstdlib.hpp - ${LIBDIR}/boost/nowide/filebuf.hpp - ${LIBDIR}/boost/nowide/fstream.hpp - ${LIBDIR}/boost/nowide/integration/filesystem.hpp - ${LIBDIR}/boost/nowide/iostream.cpp - ${LIBDIR}/boost/nowide/iostream.hpp - ${LIBDIR}/boost/nowide/stackstring.hpp - ${LIBDIR}/boost/nowide/system.hpp - ${LIBDIR}/boost/nowide/utf8_codecvt.hpp - ${LIBDIR}/boost/nowide/windows.hpp -) - -add_library(Shiny STATIC - ${LIBDIR}/Shiny/Shiny.h - ${LIBDIR}/Shiny/ShinyConfig.h - ${LIBDIR}/Shiny/ShinyData.h - ${LIBDIR}/Shiny/ShinyMacros.h - ${LIBDIR}/Shiny/ShinyManager.c - ${LIBDIR}/Shiny/ShinyManager.h - ${LIBDIR}/Shiny/ShinyNode.c - ${LIBDIR}/Shiny/ShinyNode.h - ${LIBDIR}/Shiny/ShinyNodePool.c - ${LIBDIR}/Shiny/ShinyNodePool.h - ${LIBDIR}/Shiny/ShinyNodeState.c - ${LIBDIR}/Shiny/ShinyNodeState.h - ${LIBDIR}/Shiny/ShinyOutput.c - ${LIBDIR}/Shiny/ShinyOutput.h - ${LIBDIR}/Shiny/ShinyPrereqs.h - ${LIBDIR}/Shiny/ShinyTools.c - ${LIBDIR}/Shiny/ShinyTools.h - ${LIBDIR}/Shiny/ShinyVersion.h - ${LIBDIR}/Shiny/ShinyZone.c - ${LIBDIR}/Shiny/ShinyZone.h -) - -add_library(semver STATIC - ${LIBDIR}/semver/semver.h - ${LIBDIR}/semver/semver.c -) - +# Perl specific stuff +find_package(PerlLibs REQUIRED) +set(PerlEmbed_DEBUG 1) +find_package(PerlEmbed REQUIRED) # Generate the Slic3r Perl module (XS) typemap file. set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap) @@ -397,11 +55,6 @@ set(XS_XSP_FILES ${XSP_DIR}/GCode.xsp ${XSP_DIR}/GCodeSender.xsp ${XSP_DIR}/Geometry.xsp - ${XSP_DIR}/GUI.xsp - ${XSP_DIR}/GUI_AppConfig.xsp - ${XSP_DIR}/GUI_3DScene.xsp - ${XSP_DIR}/GUI_Preset.xsp - ${XSP_DIR}/GUI_Tab.xsp ${XSP_DIR}/Layer.xsp ${XSP_DIR}/Line.xsp ${XSP_DIR}/Model.xsp @@ -416,9 +69,6 @@ set(XS_XSP_FILES ${XSP_DIR}/Surface.xsp ${XSP_DIR}/SurfaceCollection.xsp ${XSP_DIR}/TriangleMesh.xsp - ${XSP_DIR}/Utils_PrintHost.xsp - ${XSP_DIR}/Utils_PresetUpdater.xsp - ${XSP_DIR}/AppController.xsp ${XSP_DIR}/XS.xsp ) foreach (file ${XS_XSP_FILES}) @@ -449,48 +99,35 @@ else() endif() add_library(XS ${XS_SHARED_LIBRARY_TYPE} ${XS_MAIN_CPP} - ${LIBDIR}/libslic3r/utils.cpp - ${LIBDIR}/slic3r/GUI/wxPerlIface.cpp - ${LIBDIR}/perlglue.cpp - ${LIBDIR}/ppport.h - ${LIBDIR}/xsinit.h - ${CMAKE_CURRENT_LIST_DIR}/xsp/my.map +# ${LIBDIR}/libslic3r/utils.cpp +# ${LIBDIR}/slic3r/GUI/wxPerlIface.cpp + src/perlglue.cpp +# src/callback.cpp +# src/callback.hpp + src/ppport.h + src/xsinit.h + xsp/my.map # mytype.map is empty. Is it required by Build.PL or the Perl xspp module? - ${CMAKE_CURRENT_LIST_DIR}/xsp/mytype.map + xsp/mytype.map # Used by Perl xsubpp to generate XS.cpp - ${CMAKE_CURRENT_LIST_DIR}/xsp/typemap.xspt + xsp/typemap.xspt ) if(APPLE) set_target_properties(XS PROPERTIES BUNDLE TRUE) # Ignore undefined symbols of the perl interpreter, they will be found in the caller image. target_link_libraries(XS "-undefined dynamic_lookup") endif() -target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver avrdude qhull) -if(SLIC3R_PROFILE) - target_link_libraries(XS Shiny) -endif() +target_link_libraries(XS libslic3r) -# Add the OpenGL and GLU libraries. -if (SLIC3R_GUI) - if (MSVC) - target_link_libraries(XS user32.lib Setupapi.lib OpenGL32.Lib GlU32.Lib) - elseif (MINGW) - target_link_libraries(XS -lopengl32) - elseif (APPLE) - target_link_libraries(XS "-framework OpenGL") - else () - target_link_libraries(XS -lGL -lGLU) - endif () -endif () - -target_include_directories(XS PRIVATE src src/libslic3r) # Local include directories +target_include_directories(XS PRIVATE src) target_compile_definitions(XS PRIVATE -DSLIC3RXS) set_target_properties(XS PROPERTIES PREFIX "") # Prevent cmake from generating libXS.so instead of XS.so +target_link_libraries(XS ${Boost_LIBRARIES}) + if (APPLE) -# add_compile_options(-stdlib=libc++) -# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE) - target_link_libraries(XS "-framework IOKit" "-framework CoreFoundation" -lc++) + # -liconv: boost links to libiconv by default + target_link_libraries(XS "-liconv -framework IOKit" "-framework CoreFoundation" -lc++) elseif (MSVC) target_link_libraries(XS ) else () @@ -510,193 +147,34 @@ if (MSVC AND SLIC3R_MSVC_PDB AND "${CMAKE_BUILD_TYPE}" STREQUAL "Release") ) endif() -## Configuration flags -if (SLIC3R_GUI) - message("Slic3r will be built with GUI support") - target_compile_definitions(XS PRIVATE -DSLIC3R_GUI) -endif () - -if (SLIC3R_PROFILE) - message("Slic3r will be built with a Shiny invasive profiler") - add_definitions(-DSLIC3R_PROFILE) -endif () - if (CMAKE_BUILD_TYPE MATCHES DEBUG) target_compile_definitions(XS PRIVATE -DSLIC3R_DEBUG -DDEBUG -D_DEBUG) else () target_compile_definitions(XS PRIVATE -DNDEBUG) endif () -# Perl specific stuff -find_package(PerlLibs REQUIRED) -set(PerlEmbed_DEBUG 1) -find_package(PerlEmbed REQUIRED) target_include_directories(XS PRIVATE ${PERL_INCLUDE_PATH}) target_compile_options(XS PRIVATE ${PerlEmbed_CCFLAGS}) -# If the Perl is compiled with optimization off, disable optimization over the whole project. -if (WIN32 AND ";${PerlEmbed_CCFLAGS};" MATCHES ";[-/]Od;") - 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 /DNDEBUG /DWIN32") - set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32") - set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") - set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32") -endif() -# The following line will add -fPIC on Linux to make the XS.so rellocable. -add_definitions(${PerlEmbed_CCCDLFLAGS}) + if (WIN32) target_link_libraries(XS ${PERL_LIBRARY}) endif() -add_subdirectory(src/avrdude) - -add_subdirectory(src/qhull) -include_directories(${LIBDIR}/qhull/src) -message(STATUS ${LIBDIR}/qhull/src) - -## REQUIRED packages - -# 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) -find_package(Boost REQUIRED COMPONENTS system filesystem thread log locale regex) -if(Boost_FOUND) - include_directories(${Boost_INCLUDE_DIRS}) - target_link_libraries(XS ${Boost_LIBRARIES}) - 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) +target_link_libraries(XS ${Boost_LIBRARIES}) target_link_libraries(XS ${TBB_LIBRARIES}) - -# Find and configure wxWidgets -if (SLIC3R_PRUSACONTROL) - set(wxWidgets_UseAlienWx 1) - if (wxWidgets_UseAlienWx) - set(AlienWx_DEBUG 1) - find_package(AlienWx REQUIRED COMPONENTS base core adv html gl) - include_directories(${AlienWx_INCLUDE_DIRS}) - #add_compile_options(${AlienWx_CXX_FLAGS}) - add_definitions(${AlienWx_DEFINITIONS}) - set(wxWidgets_LIBRARIES ${AlienWx_LIBRARIES}) - # On Linux / gtk, we need to have a direct access to gtk+ for some workarounds. - if (AlienWx_GUI_TOOLKIT STREQUAL "gtk2") - pkg_check_modules(GTK2 gtk+-2.0) - include_directories(${GTK2_INCLUDE_DIRS}) - endif() - if (AlienWx_GUI_TOOLKIT STREQUAL "gtk3") - pkg_check_modules(GTK3 gtk+-3.0) - include_directories(${GTK3_INCLUDE_DIRS}) - endif() - else () - find_package(wxWidgets REQUIRED COMPONENTS base core adv html gl) - include(${wxWidgets_USE_FILE}) - endif () - add_definitions(-DSLIC3R_GUI -DSLIC3R_PRUS) - target_link_libraries(XS ${wxWidgets_LIBRARIES}) -endif() - -find_package(CURL REQUIRED) -include_directories(${CURL_INCLUDE_DIRS}) -target_link_libraries(XS ${CURL_LIBRARIES}) - -if (SLIC3R_STATIC) - if (NOT APPLE) - # libcurl is always linked dynamically to the system libcurl on OSX. - # On other systems, libcurl is linked statically if SLIC3R_STATIC is set. - add_definitions(-DCURL_STATICLIB) - endif() - if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - # As of now, our build system produces a statically linked libcurl, - # which links the OpenSSL library dynamically. - find_package(OpenSSL REQUIRED) - message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}") - message("OpenSSL libraries: ${OPENSSL_LIBRARIES}") - include_directories(${OPENSSL_INCLUDE_DIR}) - target_link_libraries(XS ${OPENSSL_LIBRARIES}) - endif() -endif() - -## OPTIONAL packages - -# 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}) +# target_link_libraries(XS ${wxWidgets_LIBRARIES}) target_link_libraries(XS ${EXPAT_LIBRARIES}) +# target_link_libraries(XS ${GLEW_LIBRARIES}) -# 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}) -target_link_libraries(XS ${GLEW_LIBRARIES}) # Install the XS.pm and XS.{so,dll,bundle} into the local-lib directory. -set(PERL_LOCAL_LIB_DIR "${PROJECT_SOURCE_DIR}/local-lib/lib/perl5/${PerlEmbed_ARCHNAME}") +set(PERL_LOCAL_LIB_DIR "../../local-lib/lib/perl5/${PerlEmbed_ARCHNAME}") add_custom_command( TARGET XS POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "${PERL_LOCAL_LIB_DIR}/auto/Slic3r/XS/" COMMAND ${CMAKE_COMMAND} -E copy "$" "${PERL_LOCAL_LIB_DIR}/auto/Slic3r/XS/" COMMAND ${CMAKE_COMMAND} -E make_directory "${PERL_LOCAL_LIB_DIR}/Slic3r/" - COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/xs/lib/Slic3r/XS.pm" "${PERL_LOCAL_LIB_DIR}/Slic3r/" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/lib/Slic3r/XS.pm" "${PERL_LOCAL_LIB_DIR}/Slic3r/" COMMENT "Installing XS.pm and XS.{so,dll,bundle} into the local-lib directory ..." ) if(APPLE) @@ -707,18 +185,9 @@ if(APPLE) ) endif() -# Create a slic3r executable -add_executable(slic3r ${PROJECT_SOURCE_DIR}/src/slic3r.cpp) -target_include_directories(XS PRIVATE src src/libslic3r) -target_link_libraries(slic3r libslic3r libslic3r_gui admesh miniz ${Boost_LIBRARIES} clipper ${EXPAT_LIBRARIES} ${GLEW_LIBRARIES} polypartition poly2tri ${TBB_LIBRARIES} ${wxWidgets_LIBRARIES}) if(SLIC3R_PROFILE) target_link_libraries(Shiny) endif() -if (APPLE) - target_link_libraries(slic3r "-framework IOKit" "-framework CoreFoundation" -lc++) -elseif (NOT MSVC) - target_link_libraries(slic3r -lstdc++) -endif () if (MSVC) # Here we associate some additional properties with the MSVC project to enable compilation and debugging out of the box. @@ -726,35 +195,21 @@ if (MSVC) string(REPLACE "/" "\\" PROPS_PERL_BIN_PATH "${PROPS_PERL_BIN_PATH}") string(REPLACE "/" "\\" PROPS_PERL_EXECUTABLE "${PERL_EXECUTABLE}") string(REPLACE "/" "\\" PROPS_CMAKE_SOURCE_DIR "${CMAKE_SOURCE_DIR}") - configure_file("${PROJECT_SOURCE_DIR}/cmake/msvc/xs.wperl.props.in" "${CMAKE_BINARY_DIR}/xs.wperl.props" NEWLINE_STYLE CRLF) + configure_file("../cmake/msvc/xs.wperl.props.in" "${CMAKE_BINARY_DIR}/xs.wperl.props" NEWLINE_STYLE CRLF) set_target_properties(XS PROPERTIES VS_USER_PROPS "${CMAKE_BINARY_DIR}/xs.wperl.props") endif() -# l10n -set(L10N_DIR "${PROJECT_SOURCE_DIR}/resources/localization") -add_custom_target(pot - 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" -) - -# ############################################################################## -# Adding libnest2d project for bin packing... -# ############################################################################## - -set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") - -add_subdirectory(${LIBDIR}/libnest2d) -target_compile_definitions(libslic3r PUBLIC -DUSE_TBB) -target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES}) -target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES}) - -message(STATUS "Libnest2D Libraries: ${LIBNEST2D_LIBRARIES}") -target_link_libraries(libslic3r ${LIBNEST2D_LIBRARIES}) -# ############################################################################## - # Installation install(TARGETS XS DESTINATION ${PERL_VENDORARCH}/auto/Slic3r/XS) install(FILES lib/Slic3r/XS.pm DESTINATION ${PERL_VENDORLIB}/Slic3r) + +# Unit / integration tests +enable_testing() +get_filename_component(PERL_BIN_PATH "${PERL_EXECUTABLE}" DIRECTORY) +if (MSVC) + set(PERL_PROVE "${PERL_BIN_PATH}/prove.bat") +else () + set(PERL_PROVE "${PERL_BIN_PATH}/prove") +endif () +add_test (NAME xs COMMAND "${PERL_EXECUTABLE}" ${PERL_PROVE} -I ${PROJECT_SOURCE_DIR}/../local-lib/lib/perl5 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) +add_test (NAME integration COMMAND "${PERL_EXECUTABLE}" ${PERL_PROVE} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/..) diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index a4847fb45e..3073d068ee 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -33,16 +33,6 @@ use overload '@{}' => sub { $_[0]->arrayref }, 'fallback' => 1; -package Slic3r::Point3; -use overload - '@{}' => sub { [ $_[0]->x, $_[0]->y, $_[0]->z ] }, #, - 'fallback' => 1; - -sub pp { - my ($self) = @_; - return [ @$self ]; -} - package Slic3r::Pointf; use overload '@{}' => sub { $_[0]->arrayref }, @@ -241,23 +231,16 @@ sub new { ); } -package Slic3r::GUI::_3DScene::GLShader; -sub CLONE_SKIP { 1 } - package Slic3r::GUI::_3DScene::GLVolume::Collection; use overload '@{}' => sub { $_[0]->arrayref }, 'fallback' => 1; -sub CLONE_SKIP { 1 } - package Slic3r::GUI::PresetCollection; use overload '@{}' => sub { $_[0]->arrayref }, 'fallback' => 1; -sub CLONE_SKIP { 1 } - package main; for my $class (qw( Slic3r::BridgeDetector diff --git a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s01.html~ b/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s01.html~ deleted file mode 100644 index 20b5c4514d..0000000000 --- a/xs/src/avrdude/atmel-docs/EDBG/protocoldocs/ch03s01s01.html~ +++ /dev/null @@ -1,215 +0,0 @@ - - - - -QUERY - - Atmel EDBG-based Tools Protocols \ No newline at end of file diff --git a/xs/src/callback.cpp b/xs/src/callback.cpp new file mode 100644 index 0000000000..3d2a8e903f --- /dev/null +++ b/xs/src/callback.cpp @@ -0,0 +1,175 @@ +#include "callback.hpp" + +#include + +void PerlCallback::register_callback(void *sv) +{ + if (! SvROK((SV*)sv) || SvTYPE(SvRV((SV*)sv)) != SVt_PVCV) + croak("Not a Callback %_ for PerlFunction", (SV*)sv); + if (m_callback) + SvSetSV((SV*)m_callback, (SV*)sv); + else + m_callback = newSVsv((SV*)sv); +} + +void PerlCallback::deregister_callback() +{ + if (m_callback) { + sv_2mortal((SV*)m_callback); + m_callback = nullptr; + } +} + +void PerlCallback::call() const +{ + if (! m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(int i) const +{ + if (! m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSViv(i))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(int i, int j) const +{ + if (! m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSViv(i))); + XPUSHs(sv_2mortal(newSViv(j))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(const std::vector& ints) const +{ + if (! m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + for (int i : ints) + { + XPUSHs(sv_2mortal(newSViv(i))); + } + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(double a) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(double a, double b) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + XPUSHs(sv_2mortal(newSVnv(b))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(double a, double b, double c) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + XPUSHs(sv_2mortal(newSVnv(b))); + XPUSHs(sv_2mortal(newSVnv(c))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(double a, double b, double c, double d) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + XPUSHs(sv_2mortal(newSVnv(b))); + XPUSHs(sv_2mortal(newSVnv(c))); + XPUSHs(sv_2mortal(newSVnv(d))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(double a, double b, double c, double d, double e, double f) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + XPUSHs(sv_2mortal(newSVnv(b))); + XPUSHs(sv_2mortal(newSVnv(c))); + XPUSHs(sv_2mortal(newSVnv(d))); + XPUSHs(sv_2mortal(newSVnv(e))); + XPUSHs(sv_2mortal(newSVnv(f))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(bool b) const +{ + call(b ? 1 : 0); +} \ No newline at end of file diff --git a/xs/src/callback.hpp b/xs/src/callback.hpp new file mode 100644 index 0000000000..ee7a5eadf9 --- /dev/null +++ b/xs/src/callback.hpp @@ -0,0 +1,33 @@ +#ifndef slic3r_PerlCallback_hpp_ +#define slic3r_PerlCallback_hpp_ + +#include + +#include "libslic3r.h" + +namespace Slic3r { + +class PerlCallback { +public: + PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); } + PerlCallback() : m_callback(nullptr) {} + ~PerlCallback() { this->deregister_callback(); } + void register_callback(void *sv); + void deregister_callback(); + void call() const; + void call(int i) const; + void call(int i, int j) const; + void call(const std::vector& ints) const; + void call(double a) const; + void call(double a, double b) const; + void call(double a, double b, double c) const; + void call(double a, double b, double c, double d) const; + void call(double a, double b, double c, double d, double e, double f) const; + void call(bool b) const; +private: + void *m_callback; +}; + +} // namespace Slic3r + +#endif /* slic3r_PerlCallback_hpp_ */ diff --git a/xs/src/libnest2d/CMakeLists.txt b/xs/src/libnest2d/CMakeLists.txt deleted file mode 100644 index f813550123..0000000000 --- a/xs/src/libnest2d/CMakeLists.txt +++ /dev/null @@ -1,135 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -project(Libnest2D) - -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - # Update if necessary - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long ") -endif() - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED) - -# Add our own cmake module path. -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/) - -option(LIBNEST2D_UNITTESTS "If enabled, googletest framework will be downloaded - and the provided unit tests will be included in the build." OFF) - -option(LIBNEST2D_BUILD_EXAMPLES "If enabled, examples will be built." OFF) - -set(LIBNEST2D_GEOMETRIES_BACKEND "clipper" CACHE STRING - "Build libnest2d with geometry classes implemented by the chosen backend.") - -set(LIBNEST2D_OPTIMIZER_BACKEND "nlopt" CACHE STRING - "Build libnest2d with optimization features implemented by the chosen backend.") - -set(LIBNEST2D_SRCFILES - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/libnest2d.hpp # Templates only - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d.h # Exports ready made types using template arguments - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/geometry_traits.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/metaloop.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/rotfinder.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/placer_boilerplate.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/geometry_traits_nfp.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/selections/selection_boilerplate.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/selections/filler.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/selections/firstfit.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/selections/djd_heuristic.hpp - ) - -set(LIBNEST2D_LIBRARIES "") - -set(LIBNEST2D_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}) - -if(LIBNEST2D_GEOMETRIES_BACKEND STREQUAL "clipper") - - # Clipper backend is not enough on its own, it still needs some functions - # from Boost geometry - if(NOT Boost_INCLUDE_DIRS_FOUND) - find_package(Boost 1.58 REQUIRED) - # TODO automatic download of boost geometry headers - endif() - - add_subdirectory(libnest2d/clipper_backend) - - include_directories(BEFORE ${CLIPPER_INCLUDE_DIRS}) - include_directories(${Boost_INCLUDE_DIRS}) - - list(APPEND LIBNEST2D_SRCFILES ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/clipper_backend/clipper_backend.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/boost_alg.hpp) - list(APPEND LIBNEST2D_LIBRARIES ${CLIPPER_LIBRARIES}) - list(APPEND LIBNEST2D_HEADERS ${CLIPPER_INCLUDE_DIRS} - ${Boost_INCLUDE_DIRS_FOUND}) -endif() - -if(LIBNEST2D_OPTIMIZER_BACKEND STREQUAL "nlopt") - find_package(NLopt 1.4) - if(NOT NLopt_FOUND) - message(STATUS "NLopt not found so downloading " - "and automatic build is performed...") - include(DownloadNLopt) - endif() - find_package(Threads REQUIRED) - - list(APPEND LIBNEST2D_SRCFILES ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/simplex.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/subplex.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/genetic.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/nlopt_boilerplate.hpp) - list(APPEND LIBNEST2D_LIBRARIES ${NLopt_LIBS}) - list(APPEND LIBNEST2D_HEADERS ${NLopt_INCLUDE_DIR}) -endif() - -if(LIBNEST2D_UNITTESTS) - enable_testing() - add_subdirectory(tests) -endif() - -if(LIBNEST2D_BUILD_EXAMPLES) - - add_executable(example examples/main.cpp -# tools/libnfpglue.hpp -# tools/libnfpglue.cpp - tools/nfp_svgnest.hpp - tools/nfp_svgnest_glue.hpp - tools/svgtools.hpp - tests/printer_parts.cpp - tests/printer_parts.h - ${LIBNEST2D_SRCFILES} - ) - set(TBB_STATIC ON) - find_package(TBB QUIET) - if(TBB_FOUND) - message(STATUS "Parallelization with Intel TBB") - target_include_directories(example PUBLIC ${TBB_INCLUDE_DIRS}) - target_compile_definitions(example PUBLIC ${TBB_DEFINITIONS} -DUSE_TBB) - if(MSVC) - # Suppress implicit linking of the TBB libraries by the Visual Studio compiler. - target_compile_definitions(example PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE) - endif() - # The Intel TBB library will use the std::exception_ptr feature of C++11. - target_compile_definitions(example PUBLIC -DTBB_USE_CAPTURED_EXCEPTION=1) - - target_link_libraries(example ${TBB_LIBRARIES}) - else() - find_package(OpenMP QUIET) - if(OpenMP_CXX_FOUND) - message(STATUS "Parallelization with OpenMP") - target_include_directories(example PUBLIC OpenMP::OpenMP_CXX) - target_link_libraries(example OpenMP::OpenMP_CXX) - endif() - endif() - - target_link_libraries(example ${LIBNEST2D_LIBRARIES}) - target_include_directories(example PUBLIC ${LIBNEST2D_HEADERS}) -endif() - -get_directory_property(hasParent PARENT_DIRECTORY) -if(hasParent) - set(LIBNEST2D_INCLUDES ${LIBNEST2D_HEADERS} PARENT_SCOPE) - set(LIBNEST2D_LIBRARIES ${LIBNEST2D_LIBRARIES} PARENT_SCOPE) -endif() diff --git a/xs/src/libnest2d/README.md b/xs/src/libnest2d/README.md deleted file mode 100644 index 61a7ac7d05..0000000000 --- a/xs/src/libnest2d/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Introduction - -Libnest2D is a library and framework for the 2D bin packaging problem. -Inspired from the [SVGNest](svgnest.com) Javascript library the project is -built from scratch in C++11. The library is written with a policy that it should -be usable out of the box with a very simple interface but has to be customizable -to the very core as well. The algorithms are defined in a header only fashion -with templated geometry types. These geometries can have custom or already -existing implementation to avoid copying or having unnecessary dependencies. - -A default backend is provided if the user of the library just wants to use it -out of the box without additional integration. This backend is reasonably -fast and robust, being built on top of boost geometry and the -[polyclipping](http://www.angusj.com/delphi/clipper.php) library. Usage of -this default backend implies the dependency on these packages but its header -only as well. - -This software is currently under construction and lacks a throughout -documentation and some essential algorithms as well. At this stage it works well -for rectangles and convex closed polygons without considering holes and -concavities. - -Holes and non-convex polygons will be usable in the near future as well. The -no fit polygon based placer module combined with the first fit selection -strategy is now used in the [Slic3r](https://github.com/prusa3d/Slic3r) -application's arrangement feature. It uses local optimization techniques to find -the best placement of each new item based on some features of the arrangement. - -In the near future I would like to use machine learning to evaluate the -placements and (or) the order if items in which they are placed and see what -results can be obtained. This is a different approach than that of SVGnest which -uses genetic algorithms to find better and better selection orders. Maybe the -two approaches can be combined as well. - -# References -- [SVGNest](https://github.com/Jack000/SVGnest) -- [An effective heuristic for the two-dimensional irregular -bin packing problem](http://www.cs.stir.ac.uk/~goc/papers/EffectiveHueristic2DAOR2013.pdf) -- [Complete and robust no-fit polygon generation for the irregular stock cutting problem](https://www.sciencedirect.com/science/article/abs/pii/S0377221706001639) -- [Applying Meta-Heuristic Algorithms to the Nesting -Problem Utilising the No Fit Polygon](http://www.graham-kendall.com/papers/k2001.pdf) -- [A comprehensive and robust procedure for obtaining the nofit polygon -using Minkowski sums](https://www.sciencedirect.com/science/article/pii/S0305054806000669) \ No newline at end of file diff --git a/xs/src/libnest2d/cmake_modules/FindTBB.cmake b/xs/src/libnest2d/cmake_modules/FindTBB.cmake deleted file mode 100644 index 8b498d3ab1..0000000000 --- a/xs/src/libnest2d/cmake_modules/FindTBB.cmake +++ /dev/null @@ -1,322 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2015 Justus Calvin -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to 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. - -# -# FindTBB -# ------- -# -# Find TBB include directories and libraries. -# -# Usage: -# -# find_package(TBB [major[.minor]] [EXACT] -# [QUIET] [REQUIRED] -# [[COMPONENTS] [components...]] -# [OPTIONAL_COMPONENTS components...]) -# -# where the allowed components are tbbmalloc and tbb_preview. Users may modify -# the behavior of this module with the following variables: -# -# * TBB_ROOT_DIR - The base directory the of TBB installation. -# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files. -# * TBB_LIBRARY - The directory that contains the TBB library files. -# * TBB__LIBRARY - The path of the TBB the corresponding TBB library. -# These libraries, if specified, override the -# corresponding library search results, where -# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug, -# tbb_preview, or tbb_preview_debug. -# * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will -# be used instead of the release version. -# * TBB_STATIC - Static linking of libraries with a _static suffix. -# For example, on Windows a tbb_static.lib will be searched for -# instead of tbb.lib. -# -# Users may modify the behavior of this module with the following environment -# variables: -# -# * TBB_INSTALL_DIR -# * TBBROOT -# * LIBRARY_PATH -# -# This module will set the following variables: -# -# * TBB_FOUND - Set to false, or undefined, if we haven’t found, or -# don’t want to use TBB. -# * TBB__FOUND - If False, optional part of TBB sytem is -# not available. -# * TBB_VERSION - The full version string -# * TBB_VERSION_MAJOR - The major version -# * TBB_VERSION_MINOR - The minor version -# * TBB_INTERFACE_VERSION - The interface version number defined in -# tbb/tbb_stddef.h. -# * TBB__LIBRARY_RELEASE - The path of the TBB release version of -# , where may be tbb, tbb_debug, -# tbbmalloc, tbbmalloc_debug, tbb_preview, or -# tbb_preview_debug. -# * TBB__LIBRARY_DEGUG - The path of the TBB release version of -# , where may be tbb, tbb_debug, -# tbbmalloc, tbbmalloc_debug, tbb_preview, or -# tbb_preview_debug. -# -# The following varibles should be used to build and link with TBB: -# -# * TBB_INCLUDE_DIRS - The include directory for TBB. -# * TBB_LIBRARIES - The libraries to link against to use TBB. -# * TBB_LIBRARIES_RELEASE - The release libraries to link against to use TBB. -# * TBB_LIBRARIES_DEBUG - The debug libraries to link against to use TBB. -# * TBB_DEFINITIONS - Definitions to use when compiling code that uses -# TBB. -# * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that -# uses TBB. -# * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that -# uses TBB. -# -# This module will also create the "tbb" target that may be used when building -# executables and libraries. - -include(FindPackageHandleStandardArgs) - -if(NOT TBB_FOUND) - - ################################## - # Check the build type - ################################## - - if(NOT DEFINED TBB_USE_DEBUG_BUILD) - if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)") - set(TBB_BUILD_TYPE DEBUG) - else() - set(TBB_BUILD_TYPE RELEASE) - endif() - elseif(TBB_USE_DEBUG_BUILD) - set(TBB_BUILD_TYPE DEBUG) - else() - set(TBB_BUILD_TYPE RELEASE) - endif() - - ################################## - # Set the TBB search directories - ################################## - - # Define search paths based on user input and environment variables - set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT}) - - # Define the search directories based on the current platform - if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB" - "C:/Program Files (x86)/Intel/TBB") - - # Set the target architecture - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(TBB_ARCHITECTURE "intel64") - else() - set(TBB_ARCHITECTURE "ia32") - endif() - - # Set the TBB search library path search suffix based on the version of VC - if(WINDOWS_STORE) - set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui") - elseif(MSVC14) - set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14") - elseif(MSVC12) - set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12") - elseif(MSVC11) - set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11") - elseif(MSVC10) - set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10") - endif() - - # Add the library path search suffix for the VC independent version of TBB - list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt") - - elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - # OS X - set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") - - # TODO: Check to see which C++ library is being used by the compiler. - if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0) - # The default C++ library on OS X 10.9 and later is libc++ - set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib") - else() - set(TBB_LIB_PATH_SUFFIX "lib") - endif() - elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - # Linux - set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") - - # TODO: Check compiler version to see the suffix should be /gcc4.1 or - # /gcc4.1. For now, assume that the compiler is more recent than - # gcc 4.4.x or later. - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") - set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4") - endif() - endif() - - ################################## - # Find the TBB include dir - ################################## - - find_path(TBB_INCLUDE_DIRS tbb/tbb.h - HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR} - PATHS ${TBB_DEFAULT_SEARCH_DIR} - PATH_SUFFIXES include) - - ################################## - # Set version strings - ################################## - - if(TBB_INCLUDE_DIRS) - file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file) - string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" - TBB_VERSION_MAJOR "${_tbb_version_file}") - string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" - TBB_VERSION_MINOR "${_tbb_version_file}") - string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" - TBB_INTERFACE_VERSION "${_tbb_version_file}") - set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}") - endif() - - ################################## - # Find TBB components - ################################## - - if(TBB_VERSION VERSION_LESS 4.3) - set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb) - else() - set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb) - endif() - - if(TBB_STATIC) - set(TBB_STATIC_SUFFIX "_static") - endif() - - # Find each component - foreach(_comp ${TBB_SEARCH_COMPOMPONENTS}) - if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") - - # Search for the libraries - find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX} - HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} - PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH - PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) - - find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug - HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} - PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH - PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) - - if(TBB_${_comp}_LIBRARY_DEBUG) - list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}") - endif() - if(TBB_${_comp}_LIBRARY_RELEASE) - list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}") - endif() - if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY) - set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}") - endif() - - if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}") - set(TBB_${_comp}_FOUND TRUE) - else() - set(TBB_${_comp}_FOUND FALSE) - endif() - - # Mark internal variables as advanced - mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) - mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) - mark_as_advanced(TBB_${_comp}_LIBRARY) - - endif() - endforeach() - - unset(TBB_STATIC_SUFFIX) - - ################################## - # Set compile flags and libraries - ################################## - - set(TBB_DEFINITIONS_RELEASE "") - set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1") - - if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) - set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}") - set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") - elseif(TBB_LIBRARIES_RELEASE) - set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}") - set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}") - elseif(TBB_LIBRARIES_DEBUG) - set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}") - set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}") - endif() - - find_package_handle_standard_args(TBB - REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES - HANDLE_COMPONENTS - VERSION_VAR TBB_VERSION) - - ################################## - # Create targets - ################################## - - if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) - add_library(tbb SHARED IMPORTED) - set_target_properties(tbb PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} - IMPORTED_LOCATION ${TBB_LIBRARIES}) - if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG) - set_target_properties(tbb PROPERTIES - INTERFACE_COMPILE_DEFINITIONS "$<$,$>:TBB_USE_DEBUG=1>" - IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG} - IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG} - IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE} - IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE} - ) - elseif(TBB_LIBRARIES_RELEASE) - set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE}) - else() - set_target_properties(tbb PROPERTIES - INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}" - IMPORTED_LOCATION ${TBB_LIBRARIES_DEBUG} - ) - endif() - endif() - - mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES) - - unset(TBB_ARCHITECTURE) - unset(TBB_BUILD_TYPE) - unset(TBB_LIB_PATH_SUFFIX) - unset(TBB_DEFAULT_SEARCH_DIR) - - if(TBB_DEBUG) - message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}") - message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}") - message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}") - message(STATUS " TBB_DEFINITIONS_DEBUG = ${TBB_DEFINITIONS_DEBUG}") - message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}") - message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}") - message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}") - endif() - -endif() diff --git a/xs/src/libnest2d/examples/main.cpp b/xs/src/libnest2d/examples/main.cpp deleted file mode 100644 index ebc3fb15c5..0000000000 --- a/xs/src/libnest2d/examples/main.cpp +++ /dev/null @@ -1,218 +0,0 @@ -#include -#include -#include -//#define DEBUG_EXPORT_NFP - -#include - -#include "tests/printer_parts.h" -#include "tools/benchmark.h" -#include "tools/svgtools.hpp" -#include "libnest2d/rotfinder.hpp" - -//#include "tools/libnfpglue.hpp" -//#include "tools/nfp_svgnest_glue.hpp" - - -using namespace libnest2d; -using ItemGroup = std::vector>; - -std::vector& _parts(std::vector& ret, const TestData& data) -{ - if(ret.empty()) { - ret.reserve(data.size()); - for(auto& inp : data) - ret.emplace_back(inp); - } - - return ret; -} - -std::vector& prusaParts() { - static std::vector ret; - return _parts(ret, PRINTER_PART_POLYGONS); -} - -std::vector& stegoParts() { - static std::vector ret; - return _parts(ret, STEGOSAUR_POLYGONS); -} - -std::vector& prusaExParts() { - static std::vector ret; - if(ret.empty()) { - ret.reserve(PRINTER_PART_POLYGONS_EX.size()); - for(auto& p : PRINTER_PART_POLYGONS_EX) { - ret.emplace_back(p.Contour, p.Holes); - } - } - return ret; -} - -void arrangeRectangles() { - using namespace libnest2d; - - const int SCALE = 1000000; - - std::vector rects(202, { - {-9945219, -3065619}, - {-9781479, -2031780}, - {-9510560, -1020730}, - {-9135450, -43529}, - {-2099999, 14110899}, - {2099999, 14110899}, - {9135450, -43529}, - {9510560, -1020730}, - {9781479, -2031780}, - {9945219, -3065619}, - {10000000, -4110899}, - {9945219, -5156179}, - {9781479, -6190019}, - {9510560, -7201069}, - {9135450, -8178270}, - {8660249, -9110899}, - {8090169, -9988750}, - {7431449, -10802209}, - {6691309, -11542349}, - {5877850, -12201069}, - {5000000, -12771149}, - {4067369, -13246350}, - {3090169, -13621459}, - {2079119, -13892379}, - {1045279, -14056119}, - {0, -14110899}, - {-1045279, -14056119}, - {-2079119, -13892379}, - {-3090169, -13621459}, - {-4067369, -13246350}, - {-5000000, -12771149}, - {-5877850, -12201069}, - {-6691309, -11542349}, - {-7431449, -10802209}, - {-8090169, -9988750}, - {-8660249, -9110899}, - {-9135450, -8178270}, - {-9510560, -7201069}, - {-9781479, -6190019}, - {-9945219, -5156179}, - {-10000000, -4110899}, - {-9945219, -3065619}, - }); - - std::vector input; - input.insert(input.end(), prusaParts().begin(), prusaParts().end()); -// input.insert(input.end(), prusaExParts().begin(), prusaExParts().end()); -// input.insert(input.end(), stegoParts().begin(), stegoParts().end()); -// input.insert(input.end(), rects.begin(), rects.end()); - - Box bin(250*SCALE, 210*SCALE); -// PolygonImpl bin = { -// { -// {25*SCALE, 0}, -// {0, 25*SCALE}, -// {0, 225*SCALE}, -// {25*SCALE, 250*SCALE}, -// {225*SCALE, 250*SCALE}, -// {250*SCALE, 225*SCALE}, -// {250*SCALE, 25*SCALE}, -// {225*SCALE, 0}, -// {25*SCALE, 0} -// }, -// {} -// }; - -// Circle bin({0, 0}, 125*SCALE); - - auto min_obj_distance = static_cast(6*SCALE); - - using Placer = placers::_NofitPolyPlacer; - using Packer = Nester; - - Packer arrange(bin, min_obj_distance); - - Packer::PlacementConfig pconf; - pconf.alignment = Placer::Config::Alignment::CENTER; - pconf.starting_point = Placer::Config::Alignment::CENTER; - pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/}; - pconf.accuracy = 0.65f; - pconf.parallel = true; - - Packer::SelectionConfig sconf; -// sconf.allow_parallel = false; -// sconf.force_parallel = false; -// sconf.try_triplets = true; -// sconf.try_reverse_order = true; -// sconf.waste_increment = 0.01; - - arrange.configure(pconf, sconf); - - arrange.progressIndicator([&](unsigned r){ - std::cout << "Remaining items: " << r << std::endl; - }); - -// findMinimumBoundingBoxRotations(input.begin(), input.end()); - - Benchmark bench; - - bench.start(); - Packer::ResultType result; - - try { - result = arrange.execute(input.begin(), input.end()); - } catch(GeometryException& ge) { - std::cerr << "Geometry error: " << ge.what() << std::endl; - return ; - } catch(std::exception& e) { - std::cerr << "Exception: " << e.what() << std::endl; - return ; - } - - bench.stop(); - - std::vector eff; - eff.reserve(result.size()); - - auto bin_area = sl::area(bin); - for(auto& r : result) { - double a = 0; - std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); }); - eff.emplace_back(a/bin_area); - }; - - std::cout << bench.getElapsedSec() << " bin count: " << result.size() - << std::endl; - - std::cout << "Bin efficiency: ("; - for(double e : eff) std::cout << e*100.0 << "% "; - std::cout << ") Average: " - << std::accumulate(eff.begin(), eff.end(), 0.0)*100.0/result.size() - << " %" << std::endl; - - std::cout << "Bin usage: ("; - size_t total = 0; - for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); } - std::cout << ") Total: " << total << std::endl; - -// for(auto& it : input) { -// auto ret = sl::isValid(it.transformedShape()); -// std::cout << ret.second << std::endl; -// } - - if(total != input.size()) std::cout << "ERROR " << "could not pack " - << input.size() - total << " elements!" - << std::endl; - - using SVGWriter = svg::SVGWriter; - - SVGWriter::Config conf; - conf.mm_in_coord_units = SCALE; - SVGWriter svgw(conf); - svgw.setSize(Box(250*SCALE, 210*SCALE)); - svgw.writePackGroup(result); - svgw.save("out"); -} - -int main(void /*int argc, char **argv*/) { - arrangeRectangles(); - return EXIT_SUCCESS; -} diff --git a/xs/src/libnest2d/libnest2d.h b/xs/src/libnest2d/libnest2d.h deleted file mode 100644 index bfd88f4f5d..0000000000 --- a/xs/src/libnest2d/libnest2d.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef LIBNEST2D_H -#define LIBNEST2D_H - -// The type of backend should be set conditionally by the cmake configuriation -// for now we set it statically to clipper backend -#include - -// We include the stock optimizers for local and global optimization -#include // Local subplex for NfpPlacer -#include // Genetic for min. bounding box - -#include -#include -#include -#include -#include -#include - -namespace libnest2d { - -using Point = PointImpl; -using Coord = TCoord; -using Box = _Box; -using Segment = _Segment; -using Circle = _Circle; - -using Item = _Item; -using Rectangle = _Rectangle; - -using PackGroup = _PackGroup; -using IndexedPackGroup = _IndexedPackGroup; - -using FillerSelection = selections::_FillerSelection; -using FirstFitSelection = selections::_FirstFitSelection; -using DJDHeuristic = selections::_DJDHeuristic; - -using NfpPlacer = placers::_NofitPolyPlacer; -using BottomLeftPlacer = placers::_BottomLeftPlacer; - -} - -#endif // LIBNEST2D_H diff --git a/xs/src/libnest2d/libnest2d/geometry_traits.hpp b/xs/src/libnest2d/libnest2d/geometry_traits.hpp deleted file mode 100644 index d162577315..0000000000 --- a/xs/src/libnest2d/libnest2d/geometry_traits.hpp +++ /dev/null @@ -1,825 +0,0 @@ -#ifndef GEOMETRY_TRAITS_HPP -#define GEOMETRY_TRAITS_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.hpp" - -namespace libnest2d { - -/// Getting the coordinate data type for a geometry class. -template struct CoordType { using Type = long; }; - -/// TCoord as shorthand for typename `CoordType::Type`. -template -using TCoord = typename CoordType>::Type; - -/// Getting the type of point structure used by a shape. -template struct PointType { using Type = typename Sh::PointType; }; - -/// TPoint as shorthand for `typename PointType::Type`. -template -using TPoint = typename PointType>::Type; - -/** - * \brief A point pair base class for other point pairs (segment, box, ...). - * \tparam RawPoint The actual point type to use. - */ -template -struct PointPair { - RawPoint p1; - RawPoint p2; -}; - -struct PolygonTag {}; -struct MultiPolygonTag {}; -struct BoxTag {}; -struct CircleTag {}; - -template struct ShapeTag { using Type = typename Shape::Tag; }; -template using Tag = typename ShapeTag::Type; - -template struct MultiShape { using Type = std::vector; }; -template using TMultiShape = typename MultiShape::Type; - -/** - * \brief An abstraction of a box; - */ -template -class _Box: PointPair { - using PointPair::p1; - using PointPair::p2; -public: - - using Tag = BoxTag; - using PointType = RawPoint; - - inline _Box() = default; - inline _Box(const RawPoint& p, const RawPoint& pp): - PointPair({p, pp}) {} - - inline _Box(TCoord width, TCoord height): - _Box(RawPoint{0, 0}, RawPoint{width, height}) {} - - inline const RawPoint& minCorner() const BP2D_NOEXCEPT { return p1; } - inline const RawPoint& maxCorner() const BP2D_NOEXCEPT { return p2; } - - inline RawPoint& minCorner() BP2D_NOEXCEPT { return p1; } - inline RawPoint& maxCorner() BP2D_NOEXCEPT { return p2; } - - inline TCoord width() const BP2D_NOEXCEPT; - inline TCoord height() const BP2D_NOEXCEPT; - - inline RawPoint center() const BP2D_NOEXCEPT; - - inline double area() const BP2D_NOEXCEPT { - return double(width()*height()); - } -}; - -template -class _Circle { - RawPoint center_; - double radius_ = 0; -public: - - using Tag = CircleTag; - using PointType = RawPoint; - - _Circle() = default; - - _Circle(const RawPoint& center, double r): center_(center), radius_(r) {} - - inline const RawPoint& center() const BP2D_NOEXCEPT { return center_; } - inline const void center(const RawPoint& c) { center_ = c; } - - inline double radius() const BP2D_NOEXCEPT { return radius_; } - inline void radius(double r) { radius_ = r; } - - inline double area() const BP2D_NOEXCEPT { - return 2.0*Pi*radius_*radius_; - } -}; - -/** - * \brief An abstraction of a directed line segment with two points. - */ -template -class _Segment: PointPair { - using PointPair::p1; - using PointPair::p2; - mutable Radians angletox_ = std::nan(""); -public: - - using PointType = RawPoint; - - inline _Segment() = default; - - inline _Segment(const RawPoint& p, const RawPoint& pp): - PointPair({p, pp}) {} - - /** - * @brief Get the first point. - * @return Returns the starting point. - */ - inline const RawPoint& first() const BP2D_NOEXCEPT { return p1; } - - /** - * @brief The end point. - * @return Returns the end point of the segment. - */ - inline const RawPoint& second() const BP2D_NOEXCEPT { return p2; } - - inline void first(const RawPoint& p) BP2D_NOEXCEPT - { - angletox_ = std::nan(""); p1 = p; - } - - inline void second(const RawPoint& p) BP2D_NOEXCEPT { - angletox_ = std::nan(""); p2 = p; - } - - /// Returns the angle measured to the X (horizontal) axis. - inline Radians angleToXaxis() const; - - /// The length of the segment in the measure of the coordinate system. - inline double length(); -}; - -// This struct serves almost as a namespace. The only difference is that is can -// used in friend declarations. -namespace pointlike { - - template - inline TCoord x(const RawPoint& p) - { - return p.x(); - } - - template - inline TCoord y(const RawPoint& p) - { - return p.y(); - } - - template - inline TCoord& x(RawPoint& p) - { - return p.x(); - } - - template - inline TCoord& y(RawPoint& p) - { - return p.y(); - } - - template - inline double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/) - { - static_assert(always_false::value, - "PointLike::distance(point, point) unimplemented!"); - return 0; - } - - template - inline double distance(const RawPoint& /*p1*/, - const _Segment& /*s*/) - { - static_assert(always_false::value, - "PointLike::distance(point, segment) unimplemented!"); - return 0; - } - - template - inline std::pair, bool> horizontalDistance( - const RawPoint& p, const _Segment& s) - { - using Unit = TCoord; - auto x = pointlike::x(p), y = pointlike::y(p); - auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first()); - auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second()); - - TCoord ret; - - if( (y < y1 && y < y2) || (y > y1 && y > y2) ) - return {0, false}; - if ((y == y1 && y == y2) && (x > x1 && x > x2)) - ret = std::min( x-x1, x -x2); - else if( (y == y1 && y == y2) && (x < x1 && x < x2)) - ret = -std::min(x1 - x, x2 - x); - else if(std::abs(y - y1) <= std::numeric_limits::epsilon() && - std::abs(y - y2) <= std::numeric_limits::epsilon()) - ret = 0; - else - ret = x - x1 + (x1 - x2)*(y1 - y)/(y1 - y2); - - return {ret, true}; - } - - template - inline std::pair, bool> verticalDistance( - const RawPoint& p, const _Segment& s) - { - using Unit = TCoord; - auto x = pointlike::x(p), y = pointlike::y(p); - auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first()); - auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second()); - - TCoord ret; - - if( (x < x1 && x < x2) || (x > x1 && x > x2) ) - return {0, false}; - if ((x == x1 && x == x2) && (y > y1 && y > y2)) - ret = std::min( y-y1, y -y2); - else if( (x == x1 && x == x2) && (y < y1 && y < y2)) - ret = -std::min(y1 - y, y2 - y); - else if(std::abs(x - x1) <= std::numeric_limits::epsilon() && - std::abs(x - x2) <= std::numeric_limits::epsilon()) - ret = 0; - else - ret = y - y1 + (y1 - y2)*(x1 - x)/(x1 - x2); - - return {ret, true}; - } -} - -template -TCoord _Box::width() const BP2D_NOEXCEPT -{ - return pointlike::x(maxCorner()) - pointlike::x(minCorner()); -} - -template -TCoord _Box::height() const BP2D_NOEXCEPT -{ - return pointlike::y(maxCorner()) - pointlike::y(minCorner()); -} - -template -TCoord getX(const RawPoint& p) { return pointlike::x(p); } - -template -TCoord getY(const RawPoint& p) { return pointlike::y(p); } - -template -void setX(RawPoint& p, const TCoord& val) -{ - pointlike::x(p) = val; -} - -template -void setY(RawPoint& p, const TCoord& val) -{ - pointlike::y(p) = val; -} - -template -inline Radians _Segment::angleToXaxis() const -{ - if(std::isnan(static_cast(angletox_))) { - TCoord dx = getX(second()) - getX(first()); - TCoord dy = getY(second()) - getY(first()); - - double a = std::atan2(dy, dx); - auto s = std::signbit(a); - - if(s) a += Pi_2; - angletox_ = a; - } - return angletox_; -} - -template -inline double _Segment::length() -{ - return pointlike::distance(first(), second()); -} - -template -inline RawPoint _Box::center() const BP2D_NOEXCEPT { - auto& minc = minCorner(); - auto& maxc = maxCorner(); - - using Coord = TCoord; - - RawPoint ret = { // No rounding here, we dont know if these are int coords - static_cast( (getX(minc) + getX(maxc))/2.0 ), - static_cast( (getY(minc) + getY(maxc))/2.0 ) - }; - - return ret; -} - -template -struct HolesContainer { - using Type = std::vector; -}; - -template -using THolesContainer = typename HolesContainer>::Type; - -template -struct CountourType { - using Type = RawShape; -}; - -template -using TContour = typename CountourType>::Type; - -enum class Orientation { - CLOCKWISE, - COUNTER_CLOCKWISE -}; - -template -struct OrientationType { - - // Default Polygon orientation that the library expects - static const Orientation Value = Orientation::CLOCKWISE; -}; - -enum class Formats { - WKT, - SVG -}; - -// This struct serves as a namespace. The only difference is that it can be -// used in friend declarations and can be aliased at class scope. -namespace shapelike { - - template - using Shapes = TMultiShape; - - template - inline RawShape create(const TContour& contour, - const THolesContainer& holes) - { - return RawShape(contour, holes); - } - - template - inline RawShape create(TContour&& contour, - THolesContainer&& holes) - { - return RawShape(contour, holes); - } - - template - inline RawShape create(const TContour& contour) - { - return create(contour, {}); - } - - template - inline RawShape create(TContour&& contour) - { - return create(contour, {}); - } - - template - inline THolesContainer& holes(RawShape& /*sh*/) - { - static THolesContainer empty; - return empty; - } - - template - inline const THolesContainer& holes(const RawShape& /*sh*/) - { - static THolesContainer empty; - return empty; - } - - template - inline TContour& getHole(RawShape& sh, unsigned long idx) - { - return holes(sh)[idx]; - } - - template - inline const TContour& getHole(const RawShape& sh, - unsigned long idx) - { - return holes(sh)[idx]; - } - - template - inline size_t holeCount(const RawShape& sh) - { - return holes(sh).size(); - } - - template - inline TContour& getContour(RawShape& sh) - { - return sh; - } - - template - inline const TContour& getContour(const RawShape& sh) - { - return sh; - } - - // Optional, does nothing by default - template - inline void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {} - - template - inline void addVertex(RawShape& sh, Args...args) - { - return getContour(sh).emplace_back(std::forward(args)...); - } - - template - inline typename TContour::iterator begin(RawShape& sh) - { - return getContour(sh).begin(); - } - - template - inline typename TContour::iterator end(RawShape& sh) - { - return getContour(sh).end(); - } - - template - inline typename TContour::const_iterator - cbegin(const RawShape& sh) - { - return getContour(sh).cbegin(); - } - - template - inline typename TContour::const_iterator cend(const RawShape& sh) - { - return getContour(sh).cend(); - } - - template - inline std::string toString(const RawShape& /*sh*/) - { - return ""; - } - - template - inline std::string serialize(const RawShape& /*sh*/, double /*scale*/=1) - { - static_assert(always_false::value, - "ShapeLike::serialize() unimplemented!"); - return ""; - } - - template - inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/) - { - static_assert(always_false::value, - "ShapeLike::unserialize() unimplemented!"); - } - - template - inline double area(const RawShape& /*sh*/, const PolygonTag&) - { - static_assert(always_false::value, - "ShapeLike::area() unimplemented!"); - return 0; - } - - template - inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/) - { - static_assert(always_false::value, - "ShapeLike::intersects() unimplemented!"); - return false; - } - - template - inline bool isInside(const TPoint& /*point*/, - const RawShape& /*shape*/) - { - static_assert(always_false::value, - "ShapeLike::isInside(point, shape) unimplemented!"); - return false; - } - - template - inline bool isInside(const RawShape& /*shape*/, - const RawShape& /*shape*/) - { - static_assert(always_false::value, - "ShapeLike::isInside(shape, shape) unimplemented!"); - return false; - } - - template - inline bool touches( const RawShape& /*shape*/, - const RawShape& /*shape*/) - { - static_assert(always_false::value, - "ShapeLike::touches(shape, shape) unimplemented!"); - return false; - } - - template - inline bool touches( const TPoint& /*point*/, - const RawShape& /*shape*/) - { - static_assert(always_false::value, - "ShapeLike::touches(point, shape) unimplemented!"); - return false; - } - - template - inline _Box> boundingBox(const RawShape& /*sh*/, - const PolygonTag&) - { - static_assert(always_false::value, - "ShapeLike::boundingBox(shape) unimplemented!"); - } - - template - inline _Box> - boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&) - { - static_assert(always_false::value, - "ShapeLike::boundingBox(shapes) unimplemented!"); - } - - template - inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&) - { - static_assert(always_false::value, - "ShapeLike::convexHull(shape) unimplemented!"); - return RawShape(); - } - - template - inline typename RawShapes::value_type - convexHull(const RawShapes& /*sh*/, const MultiPolygonTag&) - { - static_assert(always_false::value, - "ShapeLike::convexHull(shapes) unimplemented!"); - return typename RawShapes::value_type(); - } - - template - inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/) - { - static_assert(always_false::value, - "ShapeLike::rotate() unimplemented!"); - } - - template - inline void translate(RawShape& /*sh*/, const RawPoint& /*offs*/) - { - static_assert(always_false::value, - "ShapeLike::translate() unimplemented!"); - } - - template - inline void offset(RawShape& /*sh*/, TCoord> /*distance*/) - { - dout() << "The current geometry backend does not support offsetting!\n"; - } - - template - inline std::pair isValid(const RawShape& /*sh*/) - { - return {false, "ShapeLike::isValid() unimplemented!"}; - } - - template - inline bool isConvex(const TContour& sh) - { - using Vertex = TPoint; - auto first = sh.begin(); - auto middle = std::next(first); - auto last = std::next(middle); - using CVrRef = const Vertex&; - - auto zcrossproduct = [](CVrRef k, CVrRef k1, CVrRef k2) { - auto dx1 = getX(k1) - getX(k); - auto dy1 = getY(k1) - getY(k); - auto dx2 = getX(k2) - getX(k1); - auto dy2 = getY(k2) - getY(k1); - return dx1*dy2 - dy1*dx2; - }; - - auto firstprod = zcrossproduct( *(std::prev(std::prev(sh.end()))), - *first, - *middle ); - - bool ret = true; - bool frsign = firstprod > 0; - while(last != sh.end()) { - auto &k = *first, &k1 = *middle, &k2 = *last; - auto zc = zcrossproduct(k, k1, k2); - ret &= frsign == (zc > 0); - ++first; ++middle; ++last; - } - - return ret; - } - - // ************************************************************************* - // No need to implement these - // ************************************************************************* - - template - inline Box boundingBox(const Box& box, const BoxTag& ) - { - return box; - } - - template - inline _Box boundingBox( - const Circle& circ, const CircleTag&) - { - using Point = typename Circle::PointType; - using Coord = TCoord; - Point pmin = { - static_cast(getX(circ.center()) - circ.radius()), - static_cast(getY(circ.center()) - circ.radius()) }; - - Point pmax = { - static_cast(getX(circ.center()) + circ.radius()), - static_cast(getY(circ.center()) + circ.radius()) }; - - return {pmin, pmax}; - } - - template // Dispatch function - inline _Box> boundingBox(const S& sh) - { - return boundingBox(sh, Tag() ); - } - - template - inline double area(const Box& box, const BoxTag& ) - { - return box.area(); - } - - template - inline double area(const Circle& circ, const CircleTag& ) - { - return circ.area(); - } - - template // Dispatching function - inline double area(const RawShape& sh) - { - return area(sh, Tag()); - } - - template - inline double area(const Shapes& shapes) - { - return std::accumulate(shapes.begin(), shapes.end(), 0.0, - [](double a, const RawShape& b) { - return a += area(b); - }); - } - - template - inline auto convexHull(const RawShape& sh) - -> decltype(convexHull(sh, Tag())) // TODO: C++14 could deduce - { - return convexHull(sh, Tag()); - } - - template - inline bool isInside(const TPoint& point, - const _Circle>& circ) - { - return pointlike::distance(point, circ.center()) < circ.radius(); - } - - template - inline bool isInside(const TPoint& point, - const _Box>& box) - { - auto px = getX(point); - auto py = getY(point); - auto minx = getX(box.minCorner()); - auto miny = getY(box.minCorner()); - auto maxx = getX(box.maxCorner()); - auto maxy = getY(box.maxCorner()); - - return px > minx && px < maxx && py > miny && py < maxy; - } - - template - inline bool isInside(const RawShape& sh, - const _Circle>& circ) - { - return std::all_of(cbegin(sh), cend(sh), - [&circ](const TPoint& p){ - return isInside(p, circ); - }); - } - - template - inline bool isInside(const _Box>& box, - const _Circle>& circ) - { - return isInside(box.minCorner(), circ) && - isInside(box.maxCorner(), circ); - } - - template - inline bool isInside(const _Box>& ibb, - const _Box>& box) - { - auto iminX = getX(ibb.minCorner()); - auto imaxX = getX(ibb.maxCorner()); - auto iminY = getY(ibb.minCorner()); - auto imaxY = getY(ibb.maxCorner()); - - auto minX = getX(box.minCorner()); - auto maxX = getX(box.maxCorner()); - auto minY = getY(box.minCorner()); - auto maxY = getY(box.maxCorner()); - - return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY; - } - - template // Potential O(1) implementation may exist - inline TPoint& vertex(RawShape& sh, unsigned long idx) - { - return *(begin(sh) + idx); - } - - template // Potential O(1) implementation may exist - inline const TPoint& vertex(const RawShape& sh, - unsigned long idx) - { - return *(cbegin(sh) + idx); - } - - template - inline size_t contourVertexCount(const RawShape& sh) - { - return cend(sh) - cbegin(sh); - } - - template - inline void foreachContourVertex(RawShape& sh, Fn fn) { - for(auto it = begin(sh); it != end(sh); ++it) fn(*it); - } - - template - inline void foreachHoleVertex(RawShape& sh, Fn fn) { - for(int i = 0; i < holeCount(sh); ++i) { - auto& h = getHole(sh, i); - for(auto it = begin(h); it != end(h); ++it) fn(*it); - } - } - - template - inline void foreachContourVertex(const RawShape& sh, Fn fn) { - for(auto it = cbegin(sh); it != cend(sh); ++it) fn(*it); - } - - template - inline void foreachHoleVertex(const RawShape& sh, Fn fn) { - for(int i = 0; i < holeCount(sh); ++i) { - auto& h = getHole(sh, i); - for(auto it = cbegin(h); it != cend(h); ++it) fn(*it); - } - } - - template - inline void foreachVertex(RawShape& sh, Fn fn) { - foreachContourVertex(sh, fn); - foreachHoleVertex(sh, fn); - } - - template - inline void foreachVertex(const RawShape& sh, Fn fn) { - foreachContourVertex(sh, fn); - foreachHoleVertex(sh, fn); - } -} - -#define DECLARE_MAIN_TYPES(T) \ - using Polygon = T; \ - using Point = TPoint; \ - using Coord = TCoord; \ - using Contour = TContour; \ - using Box = _Box; \ - using Circle = _Circle; \ - using Segment = _Segment; \ - using Polygons = TMultiShape - -} - -#endif // GEOMETRY_TRAITS_HPP diff --git a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp deleted file mode 100644 index 2982454cdc..0000000000 --- a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp +++ /dev/null @@ -1,558 +0,0 @@ -#ifndef GEOMETRIES_NOFITPOLYGON_HPP -#define GEOMETRIES_NOFITPOLYGON_HPP - -#include "geometry_traits.hpp" -#include -#include -#include -#include - -namespace libnest2d { - -namespace __nfp { -// Do not specialize this... -template -inline bool _vsort(const TPoint& v1, const TPoint& v2) -{ - using Coord = TCoord>; - Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2); - auto diff = y1 - y2; - if(std::abs(diff) <= std::numeric_limits::epsilon()) - return x1 < x2; - - return diff < 0; -} -} - -/// A collection of static methods for handling the no fit polygon creation. -namespace nfp { - -//namespace sl = shapelike; -//namespace pl = pointlike; - -/// The complexity level of a polygon that an NFP implementation can handle. -enum class NfpLevel: unsigned { - CONVEX_ONLY, - ONE_CONVEX, - BOTH_CONCAVE, - ONE_CONVEX_WITH_HOLES, - BOTH_CONCAVE_WITH_HOLES -}; - -template -using NfpResult = std::pair>; - -template struct MaxNfpLevel { - static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY; -}; - - -// Shorthand for a pile of polygons -template -using Shapes = TMultiShape; - -/** - * Merge a bunch of polygons with the specified additional polygon. - * - * \tparam RawShape the Polygon data type. - * \param shc The pile of polygons that will be unified with sh. - * \param sh A single polygon to unify with shc. - * - * \return A set of polygons that is the union of the input polygons. Note that - * mostly it will be a set containing only one big polygon but if the input - * polygons are disjuct than the resulting set will contain more polygons. - */ -template -inline RawShapes merge(const RawShapes& /*shc*/) -{ - static_assert(always_false::value, - "Nfp::merge(shapes, shape) unimplemented!"); -} - -/** - * Merge a bunch of polygons with the specified additional polygon. - * - * \tparam RawShape the Polygon data type. - * \param shc The pile of polygons that will be unified with sh. - * \param sh A single polygon to unify with shc. - * - * \return A set of polygons that is the union of the input polygons. Note that - * mostly it will be a set containing only one big polygon but if the input - * polygons are disjuct than the resulting set will contain more polygons. - */ -template -inline TMultiShape merge(const TMultiShape& shc, - const RawShape& sh) -{ - auto m = nfp::merge(shc); - m.push_back(sh); - return nfp::merge(m); -} - -/** - * Get the vertex of the polygon that is at the lowest values (bottom) in the Y - * axis and if there are more than one vertices on the same Y coordinate than - * the result will be the leftmost (with the highest X coordinate). - */ -template -inline TPoint leftmostDownVertex(const RawShape& sh) -{ - - // find min x and min y vertex - auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh), - __nfp::_vsort); - - return it == shapelike::cend(sh) ? TPoint() : *it;; -} - -/** - * Get the vertex of the polygon that is at the highest values (top) in the Y - * axis and if there are more than one vertices on the same Y coordinate than - * the result will be the rightmost (with the lowest X coordinate). - */ -template -TPoint rightmostUpVertex(const RawShape& sh) -{ - - // find max x and max y vertex - auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh), - __nfp::_vsort); - - return it == shapelike::cend(sh) ? TPoint() : *it; -} - -/** - * A method to get a vertex from a polygon that always maintains a relative - * position to the coordinate system: It is always the rightmost top vertex. - * - * This way it does not matter in what order the vertices are stored, the - * reference will be always the same for the same polygon. - */ -template -inline TPoint referenceVertex(const RawShape& sh) -{ - return rightmostUpVertex(sh); -} - -/** - * The "trivial" Cuninghame-Green implementation of NFP for convex polygons. - * - * You can use this even if you provide implementations for the more complex - * cases (Through specializing the the NfpImpl struct). Currently, no other - * cases are covered in the library. - * - * Complexity should be no more than linear in the number of edges of the input - * polygons. - * - * \tparam RawShape the Polygon data type. - * \param sh The stationary polygon - * \param cother The orbiting polygon - * \return Returns a pair of the NFP and its reference vertex of the two input - * polygons which have to be strictly convex. The resulting NFP is proven to be - * convex as well in this case. - * - */ -template -inline NfpResult nfpConvexOnly(const RawShape& sh, - const RawShape& other) -{ - using Vertex = TPoint; using Edge = _Segment; - namespace sl = shapelike; - - RawShape rsh; // Final nfp placeholder - Vertex top_nfp; - std::vector edgelist; - - auto cap = sl::contourVertexCount(sh) + sl::contourVertexCount(other); - - // Reserve the needed memory - edgelist.reserve(cap); - sl::reserve(rsh, static_cast(cap)); - - { // place all edges from sh into edgelist - auto first = sl::cbegin(sh); - auto next = std::next(first); - - while(next != sl::cend(sh)) { - edgelist.emplace_back(*(first), *(next)); - ++first; ++next; - } - } - - { // place all edges from other into edgelist - auto first = sl::cbegin(other); - auto next = std::next(first); - - while(next != sl::cend(other)) { - edgelist.emplace_back(*(next), *(first)); - ++first; ++next; - } - } - - // Sort the edges by angle to X axis. - std::sort(edgelist.begin(), edgelist.end(), - [](const Edge& e1, const Edge& e2) - { - return e1.angleToXaxis() > e2.angleToXaxis(); - }); - - // Add the two vertices from the first edge into the final polygon. - sl::addVertex(rsh, edgelist.front().first()); - sl::addVertex(rsh, edgelist.front().second()); - - // Sorting function for the nfp reference vertex search - auto& cmp = __nfp::_vsort; - - // the reference (rightmost top) vertex so far - top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp ); - - auto tmp = std::next(sl::begin(rsh)); - - // Construct final nfp by placing each edge to the end of the previous - for(auto eit = std::next(edgelist.begin()); - eit != edgelist.end(); - ++eit) - { - auto d = *tmp - eit->first(); - Vertex p = eit->second() + d; - - sl::addVertex(rsh, p); - - // Set the new reference vertex - if(cmp(top_nfp, p)) top_nfp = p; - - tmp = std::next(tmp); - } - - return {rsh, top_nfp}; -} - -template -NfpResult nfpSimpleSimple(const RawShape& cstationary, - const RawShape& cother) -{ - - // Algorithms are from the original algorithm proposed in paper: - // https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf - - // ///////////////////////////////////////////////////////////////////////// - // Algorithm 1: Obtaining the minkowski sum - // ///////////////////////////////////////////////////////////////////////// - - // I guess this is not a full minkowski sum of the two input polygons by - // definition. This yields a subset that is compatible with the next 2 - // algorithms. - - using Result = NfpResult; - using Vertex = TPoint; - using Coord = TCoord; - using Edge = _Segment; - namespace sl = shapelike; - using std::signbit; - using std::sort; - using std::vector; - using std::ref; - using std::reference_wrapper; - - // TODO The original algorithms expects the stationary polygon in - // counter clockwise and the orbiter in clockwise order. - // So for preventing any further complication, I will make the input - // the way it should be, than make my way around the orientations. - - // Reverse the stationary contour to counter clockwise - auto stcont = sl::getContour(cstationary); - std::reverse(stcont.begin(), stcont.end()); - RawShape stationary; - sl::getContour(stationary) = stcont; - - // Reverse the orbiter contour to counter clockwise - auto orbcont = sl::getContour(cother); - - std::reverse(orbcont.begin(), orbcont.end()); - - // Copy the orbiter (contour only), we will have to work on it - RawShape orbiter; - sl::getContour(orbiter) = orbcont; - - // Step 1: Make the orbiter reverse oriented - for(auto &v : sl::getContour(orbiter)) v = -v; - - // An egde with additional data for marking it - struct MarkedEdge { - Edge e; Radians turn_angle = 0; bool is_turning_point = false; - MarkedEdge() = default; - MarkedEdge(const Edge& ed, Radians ta, bool tp): - e(ed), turn_angle(ta), is_turning_point(tp) {} - }; - - // Container for marked edges - using EdgeList = vector; - - EdgeList A, B; - - // This is how an edge list is created from the polygons - auto fillEdgeList = [](EdgeList& L, const RawShape& poly, int dir) { - L.reserve(sl::contourVertexCount(poly)); - - auto it = sl::cbegin(poly); - auto nextit = std::next(it); - - double turn_angle = 0; - bool is_turn_point = false; - - while(nextit != sl::cend(poly)) { - L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point); - it++; nextit++; - } - - auto getTurnAngle = [](const Edge& e1, const Edge& e2) { - auto phi = e1.angleToXaxis(); - auto phi_prev = e2.angleToXaxis(); - auto TwoPi = 2.0*Pi; - if(phi > Pi) phi -= TwoPi; - if(phi_prev > Pi) phi_prev -= TwoPi; - auto turn_angle = phi-phi_prev; - if(turn_angle > Pi) turn_angle -= TwoPi; - return phi-phi_prev; - }; - - if(dir > 0) { - auto eit = L.begin(); - auto enext = std::next(eit); - - eit->turn_angle = getTurnAngle(L.front().e, L.back().e); - - while(enext != L.end()) { - enext->turn_angle = getTurnAngle( enext->e, eit->e); - enext->is_turning_point = - signbit(enext->turn_angle) != signbit(eit->turn_angle); - ++eit; ++enext; - } - - L.front().is_turning_point = signbit(L.front().turn_angle) != - signbit(L.back().turn_angle); - } else { - std::cout << L.size() << std::endl; - - auto eit = L.rbegin(); - auto enext = std::next(eit); - - eit->turn_angle = getTurnAngle(L.back().e, L.front().e); - - while(enext != L.rend()) { - enext->turn_angle = getTurnAngle(enext->e, eit->e); - enext->is_turning_point = - signbit(enext->turn_angle) != signbit(eit->turn_angle); - std::cout << enext->is_turning_point << " " << enext->turn_angle << std::endl; - - ++eit; ++enext; - } - - L.back().is_turning_point = signbit(L.back().turn_angle) != - signbit(L.front().turn_angle); - } - }; - - // Step 2: Fill the edgelists - fillEdgeList(A, stationary, 1); - fillEdgeList(B, orbiter, -1); - - // A reference to a marked edge that also knows its container - struct MarkedEdgeRef { - reference_wrapper eref; - reference_wrapper> container; - Coord dir = 1; // Direction modifier - - inline Radians angleX() const { return eref.get().e.angleToXaxis(); } - inline const Edge& edge() const { return eref.get().e; } - inline Edge& edge() { return eref.get().e; } - inline bool isTurningPoint() const { - return eref.get().is_turning_point; - } - inline bool isFrom(const vector& cont ) { - return &(container.get()) == &cont; - } - inline bool eq(const MarkedEdgeRef& mr) { - return &(eref.get()) == &(mr.eref.get()); - } - - MarkedEdgeRef(reference_wrapper er, - reference_wrapper> ec): - eref(er), container(ec), dir(1) {} - - MarkedEdgeRef(reference_wrapper er, - reference_wrapper> ec, - Coord d): - eref(er), container(ec), dir(d) {} - }; - - using EdgeRefList = vector; - - // Comparing two marked edges - auto sortfn = [](const MarkedEdgeRef& e1, const MarkedEdgeRef& e2) { - return e1.angleX() < e2.angleX(); - }; - - EdgeRefList Aref, Bref; // We create containers for the references - Aref.reserve(A.size()); Bref.reserve(B.size()); - - // Fill reference container for the stationary polygon - std::for_each(A.begin(), A.end(), [&Aref](MarkedEdge& me) { - Aref.emplace_back( ref(me), ref(Aref) ); - }); - - // Fill reference container for the orbiting polygon - std::for_each(B.begin(), B.end(), [&Bref](MarkedEdge& me) { - Bref.emplace_back( ref(me), ref(Bref) ); - }); - - struct EdgeGroup { typename EdgeRefList::const_iterator first, last; }; - - auto mink = [sortfn] // the Mink(Q, R, direction) sub-procedure - (const EdgeGroup& Q, const EdgeGroup& R, bool positive) - { - - // Step 1 "merge sort_list(Q) and sort_list(R) to form merge_list(Q,R)" - // Sort the containers of edge references and merge them. - // Q could be sorted only once and be reused here but we would still - // need to merge it with sorted(R). - - EdgeRefList merged; - EdgeRefList S, seq; - merged.reserve((Q.last - Q.first) + (R.last - R.first)); - - merged.insert(merged.end(), Q.first, Q.last); - merged.insert(merged.end(), R.first, R.last); - sort(merged.begin(), merged.end(), sortfn); - - // Step 2 "set i = 1, k = 1, direction = 1, s1 = q1" - // we dont use i, instead, q is an iterator into Q. k would be an index - // into the merged sequence but we use "it" as an iterator for that - - // here we obtain references for the containers for later comparisons - const auto& Rcont = R.first->container.get(); - const auto& Qcont = Q.first->container.get(); - - // Set the intial direction - Coord dir = positive? 1 : -1; - - // roughly i = 1 (so q = Q.first) and s1 = q1 so S[0] = q; - auto q = Q.first; - S.push_back(*q++); - - // Roughly step 3 - while(q != Q.last) { - auto it = merged.begin(); - while(it != merged.end() && !(it->eq(*(Q.first))) ) { - if(it->isFrom(Rcont)) { - auto s = *it; - s.dir = dir; - S.push_back(s); - } - if(it->eq(*q)) { - S.push_back(*q); - if(it->isTurningPoint()) dir = -dir; - if(q != Q.first) it += dir; - } - else it += dir; - } - ++q; // "Set i = i + 1" - } - - // Step 4: - - // "Let starting edge r1 be in position si in sequence" - // whaaat? I guess this means the following: - S[0] = *R.first; - auto it = S.begin(); - - // "Set j = 1, next = 2, direction = 1, seq1 = si" - // we dont use j, seq is expanded dynamically. - dir = 1; auto next = std::next(R.first); - - // Step 5: - // "If all si edges have been allocated to seqj" should mean that - // we loop until seq has equal size with S - while(seq.size() < S.size()) { - ++it; if(it == S.end()) it = S.begin(); - - if(it->isFrom(Qcont)) { - seq.push_back(*it); // "If si is from Q, j = j + 1, seqj = si" - - // "If si is a turning point in Q, - // direction = - direction, next = next + direction" - if(it->isTurningPoint()) { dir = -dir; next += dir; } - } - - if(it->eq(*next) && dir == next->dir) { // "If si = direction.rnext" - // "j = j + 1, seqj = si, next = next + direction" - seq.push_back(*it); next += dir; - } - } - - return seq; - }; - - EdgeGroup R{ Bref.begin(), Bref.begin() }, Q{ Aref.begin(), Aref.end() }; - auto it = Bref.begin(); - bool orientation = true; - EdgeRefList seqlist; - seqlist.reserve(3*(Aref.size() + Bref.size())); - - while(it != Bref.end()) // This is step 3 and step 4 in one loop - if(it->isTurningPoint()) { - R = {R.last, it++}; - auto seq = mink(Q, R, orientation); - - // TODO step 6 (should be 5 shouldn't it?): linking edges from A - // I don't get this step - - seqlist.insert(seqlist.end(), seq.begin(), seq.end()); - orientation = !orientation; - } else ++it; - - if(seqlist.empty()) seqlist = mink(Q, {Bref.begin(), Bref.end()}, true); - - // ///////////////////////////////////////////////////////////////////////// - // Algorithm 2: breaking Minkowski sums into track line trips - // ///////////////////////////////////////////////////////////////////////// - - - // ///////////////////////////////////////////////////////////////////////// - // Algorithm 3: finding the boundary of the NFP from track line trips - // ///////////////////////////////////////////////////////////////////////// - - - - return Result(stationary, Vertex()); -} - -// Specializable NFP implementation class. Specialize it if you have a faster -// or better NFP implementation -template -struct NfpImpl { - NfpResult operator()(const RawShape& sh, const RawShape& other) - { - static_assert(nfptype == NfpLevel::CONVEX_ONLY, - "Nfp::noFitPolygon() unimplemented!"); - - // Libnest2D has a default implementation for convex polygons and will - // use it if feasible. - return nfpConvexOnly(sh, other); - } -}; - -/// Helper function to get the NFP -template -inline NfpResult noFitPolygon(const RawShape& sh, - const RawShape& other) -{ - NfpImpl nfps; - return nfps(sh, other); -} - -} - -} - -#endif // GEOMETRIES_NOFITPOLYGON_HPP diff --git a/xs/src/libnest2d/tools/benchmark.h b/xs/src/libnest2d/tools/benchmark.h deleted file mode 100644 index 19870b37b1..0000000000 --- a/xs/src/libnest2d/tools/benchmark.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) Tamás Mészáros - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#ifndef INCLUDE_BENCHMARK_H_ -#define INCLUDE_BENCHMARK_H_ - -#include -#include - -/** - * A class for doing benchmarks. - */ -class Benchmark { - typedef std::chrono::high_resolution_clock Clock; - typedef Clock::duration Duration; - typedef Clock::time_point TimePoint; - - TimePoint t1, t2; - Duration d; - - inline double to_sec(Duration d) { - return d.count() * double(Duration::period::num) / Duration::period::den; - } - -public: - - /** - * Measure time from the moment of this call. - */ - void start() { t1 = Clock::now(); } - - /** - * Measure time to the moment of this call. - */ - void stop() { t2 = Clock::now(); } - - /** - * Get the time elapsed between a start() end a stop() call. - * @return Returns the elapsed time in seconds. - */ - double getElapsedSec() { d = t2 - t1; return to_sec(d); } -}; - - -#endif /* INCLUDE_BENCHMARK_H_ */ diff --git a/xs/src/libnest2d/tools/libnfpglue.cpp b/xs/src/libnest2d/tools/libnfpglue.cpp deleted file mode 100644 index 31733acf97..0000000000 --- a/xs/src/libnest2d/tools/libnfpglue.cpp +++ /dev/null @@ -1,157 +0,0 @@ -//#ifndef NDEBUG -//#define NFP_DEBUG -//#endif - -#include "libnfpglue.hpp" -#include "tools/libnfporb/libnfporb.hpp" - -namespace libnest2d { - -namespace { -inline bool vsort(const libnfporb::point_t& v1, const libnfporb::point_t& v2) -{ - using Coord = libnfporb::coord_t; - Coord x1 = v1.x_, x2 = v2.x_, y1 = v1.y_, y2 = v2.y_; - auto diff = y1 - y2; -#ifdef LIBNFP_USE_RATIONAL - long double diffv = diff.convert_to(); -#else - long double diffv = diff.val(); -#endif - if(std::abs(diffv) <= - std::numeric_limits::epsilon()) - return x1 < x2; - - return diff < 0; -} - -TCoord getX(const libnfporb::point_t& p) { -#ifdef LIBNFP_USE_RATIONAL - return p.x_.convert_to>(); -#else - return static_cast>(std::round(p.x_.val())); -#endif -} - -TCoord getY(const libnfporb::point_t& p) { -#ifdef LIBNFP_USE_RATIONAL - return p.y_.convert_to>(); -#else - return static_cast>(std::round(p.y_.val())); -#endif -} - -libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) { -#ifdef LIBNFP_USE_RATIONAL - auto px = p.x_.convert_to(); - auto py = p.y_.convert_to(); -#else - long double px = p.x_.val(); - long double py = p.y_.val(); -#endif - return {px*factor, py*factor}; -} - -} - -NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) -{ - namespace sl = shapelike; - - NfpR ret; - - try { - libnfporb::polygon_t pstat, porb; - - boost::geometry::convert(sh, pstat); - boost::geometry::convert(cother, porb); - - long double factor = 0.0000001;//libnfporb::NFP_EPSILON; - long double refactor = 1.0/factor; - - for(auto& v : pstat.outer()) v = scale(v, factor); -// std::string message; -// boost::geometry::is_valid(pstat, message); -// std::cout << message << std::endl; - for(auto& h : pstat.inners()) for(auto& v : h) v = scale(v, factor); - - for(auto& v : porb.outer()) v = scale(v, factor); -// message; -// boost::geometry::is_valid(porb, message); -// std::cout << message << std::endl; - for(auto& h : porb.inners()) for(auto& v : h) v = scale(v, factor); - - - // this can throw - auto nfp = libnfporb::generateNFP(pstat, porb, true); - - auto &ct = sl::getContour(ret.first); - ct.reserve(nfp.front().size()+1); - for(auto v : nfp.front()) { - v = scale(v, refactor); - ct.emplace_back(getX(v), getY(v)); - } - ct.push_back(ct.front()); - std::reverse(ct.begin(), ct.end()); - - auto &rholes = sl::holes(ret.first); - for(size_t hidx = 1; hidx < nfp.size(); ++hidx) { - if(nfp[hidx].size() >= 3) { - rholes.emplace_back(); - auto& h = rholes.back(); - h.reserve(nfp[hidx].size()+1); - - for(auto& v : nfp[hidx]) { - v = scale(v, refactor); - h.emplace_back(getX(v), getY(v)); - } - h.push_back(h.front()); - std::reverse(h.begin(), h.end()); - } - } - - ret.second = nfp::referenceVertex(ret.first); - - } catch(std::exception& e) { - std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl; -// auto ch_stat = ShapeLike::convexHull(sh); -// auto ch_orb = ShapeLike::convexHull(cother); - ret = nfp::nfpConvexOnly(sh, cother); - } - - return ret; -} - -NfpR nfp::NfpImpl::operator()( - const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) -{ - return _nfp(sh, cother);//nfpConvexOnly(sh, cother); -} - -NfpR nfp::NfpImpl::operator()( - const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) -{ - return _nfp(sh, cother); -} - -NfpR nfp::NfpImpl::operator()( - const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) -{ - return _nfp(sh, cother); -} - -//PolygonImpl -//Nfp::NfpImpl::operator()( -// const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) -//{ -// return _nfp(sh, cother); -//} - -//PolygonImpl -//Nfp::NfpImpl::operator()( -// const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) -//{ -// return _nfp(sh, cother); -//} - -} diff --git a/xs/src/libnest2d/tools/libnfpglue.hpp b/xs/src/libnest2d/tools/libnfpglue.hpp deleted file mode 100644 index 1ff033cb92..0000000000 --- a/xs/src/libnest2d/tools/libnfpglue.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef LIBNFPGLUE_HPP -#define LIBNFPGLUE_HPP - -#include - -namespace libnest2d { - -using NfpR = nfp::NfpResult; - -NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother); - -template<> -struct nfp::NfpImpl { - NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); -}; - -template<> -struct nfp::NfpImpl { - NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); -}; - -template<> -struct nfp::NfpImpl { - NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); -}; - -//template<> -//struct Nfp::NfpImpl { -// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother); -//}; - -//template<> -//struct Nfp::NfpImpl { -// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother); -//}; - -template<> struct nfp::MaxNfpLevel { - static const BP2D_CONSTEXPR NfpLevel value = -// NfpLevel::CONVEX_ONLY; - NfpLevel::BOTH_CONCAVE; -}; - -} - - -#endif // LIBNFPGLUE_HPP diff --git a/xs/src/libnest2d/tools/libnfporb/LICENSE b/xs/src/libnest2d/tools/libnfporb/LICENSE deleted file mode 100644 index 94a9ed024d..0000000000 --- a/xs/src/libnest2d/tools/libnfporb/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/xs/src/libnest2d/tools/libnfporb/ORIGIN b/xs/src/libnest2d/tools/libnfporb/ORIGIN deleted file mode 100644 index 788bfd9af2..0000000000 --- a/xs/src/libnest2d/tools/libnfporb/ORIGIN +++ /dev/null @@ -1,2 +0,0 @@ -https://github.com/kallaballa/libnfp.git -commit hash a5cf9f6a76ddab95567fccf629d4d099b60237d7 \ No newline at end of file diff --git a/xs/src/libnest2d/tools/libnfporb/README.md b/xs/src/libnest2d/tools/libnfporb/README.md deleted file mode 100644 index 9698972be4..0000000000 --- a/xs/src/libnest2d/tools/libnfporb/README.md +++ /dev/null @@ -1,89 +0,0 @@ -[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html) -##### If you give me a real good reason i might be willing to give you permission to use it under a different license for a specific application. Real good reasons include the following (non-exhausive): the greater good, educational purpose and money :) - -# libnfporb -Implementation of a robust no-fit polygon generation in a C++ library using an orbiting approach. - -__Please note:__ The paper this implementation is based it on has several bad assumptions that required me to "improvise". That means the code doesn't reflect the paper anymore and is running way slower than expected. At the moment I'm working on implementing a new approach based on this paper (using minkowski sums): https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf - -## Description - -The no-fit polygon optimization makes it possible to check for overlap (or non-overlapping touch) of two polygons with only 1 point in polygon check (by providing the set of non-overlapping placements). -This library implements the orbiting approach to generate the no-fit polygon: Given two polygons A and B, A is the stationary one and B the orbiting one, B is slid as tightly as possibly around the edges of polygon A. During the orbiting a chosen reference point is tracked. By tracking the movement of the reference point a third polygon can be generated: the no-fit polygon. - -Once the no-fit polygon has been generated it can be used to test for overlap by only checking if the reference point is inside the NFP (overlap) outside the NFP (no overlap) or exactly on the edge of the NFP (touch). - -### Examples: - -The polygons: - -![Start of NFP](/images/start.png?raw=true) - -Orbiting: - -![State 1](/images/next0.png?raw=true) -![State 2](/images/next1.png?raw=true) -![State 3](/images/next2.png?raw=true) -![State 4](/images/next3.png?raw=true) - -![State 5](/images/next4.png?raw=true) -![State 6](/images/next5.png?raw=true) -![State 7](/images/next6.png?raw=true) -![State 8](/images/next7.png?raw=true) - -![State 9](/images/next8.png?raw=true) - -The resulting NFP is red: - -![nfp](/images/nfp.png?raw=true) - -Polygons can have concavities, holes, interlocks or might fit perfectly: - -![concavities](/images/concavities.png?raw=true) -![hole](/images/hole.png?raw=true) -![interlock](/images/interlock.png?raw=true) -![jigsaw](/images/jigsaw.png?raw=true) - -## The Approach -The approch of this library is highly inspired by the scientific paper [Complete and robust no-fit polygon generation -for the irregular stock cutting problem](https://pdfs.semanticscholar.org/e698/0dd78306ba7d5bb349d20c6d8f2e0aa61062.pdf) and by [Svgnest](http://svgnest.com) - -Note that is wasn't completely possible to implement it as suggested in the paper because it had several shortcomings that prevent complete NFP generation on some of my test cases. Especially the termination criteria (reference point returns to first point of NFP) proved to be wrong (see: test-case rect). Also tracking of used edges can't be performed as suggested in the paper since there might be situations where no edge of A is traversed (see: test-case doublecon). - -By default the library is using floating point as coordinate type but by defining the flag "LIBNFP_USE_RATIONAL" the library can be instructed to use infinite precision. - -## Build -The library has two dependencies: [Boost Geometry](http://www.boost.org/doc/libs/1_65_1/libs/geometry/doc/html/index.html) and [libgmp](https://gmplib.org). You need to install those first before building. Note that building is only required for the examples. The library itself is header-only. - - git clone https://github.com/kallaballa/libnfp.git - cd libnfp - make - sudo make install - -## Code Example - -```c++ -//uncomment next line to use infinite precision (slow) -//#define LIBNFP_USE_RATIONAL -#include "../src/libnfp.hpp" - -int main(int argc, char** argv) { - using namespace libnfp; - polygon_t pA; - polygon_t pB; - //read polygons from wkt files - read_wkt_polygon(argv[1], pA); - read_wkt_polygon(argv[2], pB); - - //generate NFP of polygon A and polygon B and check the polygons for validity. - //When the third parameters is false validity check is skipped for a little performance increase - nfp_t nfp = generateNFP(pA, pB, true); - - //write a svg containing pA, pB and NFP - write_svg("nfp.svg",{pA,pB},nfp); - return 0; -} -``` -Run the example program: - - examples/nfp data/crossing/A.wkt data/crossing/B.wkt diff --git a/xs/src/libnest2d/tools/libnfporb/libnfporb.hpp b/xs/src/libnest2d/tools/libnfporb/libnfporb.hpp deleted file mode 100644 index 8cb34567eb..0000000000 --- a/xs/src/libnest2d/tools/libnfporb/libnfporb.hpp +++ /dev/null @@ -1,1547 +0,0 @@ -#ifndef NFP_HPP_ -#define NFP_HPP_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER <= 1800 || __cplusplus < 201103L - #define LIBNFP_NOEXCEPT - #define LIBNFP_CONSTEXPR -#elif __cplusplus >= 201103L - #define LIBNFP_NOEXCEPT noexcept - #define LIBNFP_CONSTEXPR constexpr -#endif - -#ifdef LIBNFP_USE_RATIONAL -#include -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef LIBNFP_USE_RATIONAL -namespace bm = boost::multiprecision; -#endif -namespace bg = boost::geometry; -namespace trans = boost::geometry::strategy::transform; - - -namespace libnfporb { -#ifdef NFP_DEBUG -#define DEBUG_VAL(x) std::cerr << x << std::endl; -#define DEBUG_MSG(title, value) std::cerr << title << ":" << value << std::endl; -#else -#define DEBUG_VAL(x) -#define DEBUG_MSG(title, value) -#endif - -using std::string; - -static LIBNFP_CONSTEXPR long double NFP_EPSILON=0.00000001; - -class LongDouble { -private: - long double val_; -public: - LongDouble() : val_(0) { - } - - LongDouble(const long double& val) : val_(val) { - } - - void setVal(const long double& v) { - val_ = v; - } - - long double val() const { - return val_; - } - - LongDouble operator/(const LongDouble& other) const { - return this->val_ / other.val_; - } - - LongDouble operator*(const LongDouble& other) const { - return this->val_ * other.val_; - } - - LongDouble operator-(const LongDouble& other) const { - return this->val_ - other.val_; - } - - LongDouble operator-() const { - return this->val_ * -1; - } - - LongDouble operator+(const LongDouble& other) const { - return this->val_ + other.val_; - } - - void operator/=(const LongDouble& other) { - this->val_ = this->val_ / other.val_; - } - - void operator*=(const LongDouble& other) { - this->val_ = this->val_ * other.val_; - } - - void operator-=(const LongDouble& other) { - this->val_ = this->val_ - other.val_; - } - - void operator+=(const LongDouble& other) { - this->val_ = this->val_ + other.val_; - } - - bool operator==(const int& other) const { - return this->operator ==(static_cast(other)); - } - - bool operator==(const LongDouble& other) const { - return this->operator ==(other.val()); - } - - bool operator==(const long double& other) const { - return this->val() == other; - } - - bool operator!=(const int& other) const { - return !this->operator ==(other); - } - - bool operator!=(const LongDouble& other) const { - return !this->operator ==(other); - } - - bool operator!=(const long double& other) const { - return !this->operator ==(other); - } - - bool operator<(const int& other) const { - return this->operator <(static_cast(other)); - } - - bool operator<(const LongDouble& other) const { - return this->operator <(other.val()); - } - - bool operator<(const long double& other) const { - return this->val() < other; - } - - bool operator>(const int& other) const { - return this->operator >(static_cast(other)); - } - - bool operator>(const LongDouble& other) const { - return this->operator >(other.val()); - } - - bool operator>(const long double& other) const { - return this->val() > other; - } - - bool operator>=(const int& other) const { - return this->operator >=(static_cast(other)); - } - - bool operator>=(const LongDouble& other) const { - return this->operator >=(other.val()); - } - - bool operator>=(const long double& other) const { - return this->val() >= other; - } - - bool operator<=(const int& other) const { - return this->operator <=(static_cast(other)); - } - - bool operator<=(const LongDouble& other) const { - return this->operator <=(other.val()); - } - - bool operator<=(const long double& other) const { - return this->val() <= other; - } -}; -} - - -namespace std { -template<> - struct numeric_limits - { - static const LIBNFP_CONSTEXPR bool is_specialized = true; - - static const LIBNFP_CONSTEXPR long double - min() LIBNFP_NOEXCEPT { return std::numeric_limits::min(); } - - static LIBNFP_CONSTEXPR long double - max() LIBNFP_NOEXCEPT { return std::numeric_limits::max(); } - -#if __cplusplus >= 201103L - static LIBNFP_CONSTEXPR long double - lowest() LIBNFP_NOEXCEPT { return -std::numeric_limits::lowest(); } -#endif - - static const LIBNFP_CONSTEXPR int digits = std::numeric_limits::digits; - static const LIBNFP_CONSTEXPR int digits10 = std::numeric_limits::digits10; -#if __cplusplus >= 201103L - static const LIBNFP_CONSTEXPR int max_digits10 - = std::numeric_limits::max_digits10; -#endif - static const LIBNFP_CONSTEXPR bool is_signed = true; - static const LIBNFP_CONSTEXPR bool is_integer = false; - static const LIBNFP_CONSTEXPR bool is_exact = false; - static const LIBNFP_CONSTEXPR int radix = std::numeric_limits::radix; - - static const LIBNFP_CONSTEXPR long double - epsilon() LIBNFP_NOEXCEPT { return libnfporb::NFP_EPSILON; } - - static const LIBNFP_CONSTEXPR long double - round_error() LIBNFP_NOEXCEPT { return 0.5L; } - - static const LIBNFP_CONSTEXPR int min_exponent = std::numeric_limits::min_exponent; - static const LIBNFP_CONSTEXPR int min_exponent10 = std::numeric_limits::min_exponent10; - static const LIBNFP_CONSTEXPR int max_exponent = std::numeric_limits::max_exponent; - static const LIBNFP_CONSTEXPR int max_exponent10 = std::numeric_limits::max_exponent10; - - - static const LIBNFP_CONSTEXPR bool has_infinity = std::numeric_limits::has_infinity; - static const LIBNFP_CONSTEXPR bool has_quiet_NaN = std::numeric_limits::has_quiet_NaN; - static const LIBNFP_CONSTEXPR bool has_signaling_NaN = has_quiet_NaN; - static const LIBNFP_CONSTEXPR float_denorm_style has_denorm - = std::numeric_limits::has_denorm; - static const LIBNFP_CONSTEXPR bool has_denorm_loss - = std::numeric_limits::has_denorm_loss; - - - static const LIBNFP_CONSTEXPR long double - infinity() LIBNFP_NOEXCEPT { return std::numeric_limits::infinity(); } - - static const LIBNFP_CONSTEXPR long double - quiet_NaN() LIBNFP_NOEXCEPT { return std::numeric_limits::quiet_NaN(); } - - static const LIBNFP_CONSTEXPR long double - signaling_NaN() LIBNFP_NOEXCEPT { return std::numeric_limits::signaling_NaN(); } - - - static const LIBNFP_CONSTEXPR long double - denorm_min() LIBNFP_NOEXCEPT { return std::numeric_limits::denorm_min(); } - - static const LIBNFP_CONSTEXPR bool is_iec559 - = has_infinity && has_quiet_NaN && has_denorm == denorm_present; - - static const LIBNFP_CONSTEXPR bool is_bounded = true; - static const LIBNFP_CONSTEXPR bool is_modulo = false; - - static const LIBNFP_CONSTEXPR bool traps = std::numeric_limits::traps; - static const LIBNFP_CONSTEXPR bool tinyness_before = - std::numeric_limits::tinyness_before; - static const LIBNFP_CONSTEXPR float_round_style round_style = - round_to_nearest; - }; -} - -namespace boost { -namespace numeric { - template<> - struct raw_converter> - { - typedef boost::numeric::conversion_traits::result_type result_type ; - typedef boost::numeric::conversion_traits::argument_type argument_type ; - - static result_type low_level_convert ( argument_type s ) { return s.val() ; } - } ; -} -} - -namespace libnfporb { - -#ifndef LIBNFP_USE_RATIONAL -typedef LongDouble coord_t; -#else -typedef bm::number rational_t; -typedef rational_t coord_t; -#endif - -bool equals(const LongDouble& lhs, const LongDouble& rhs); -#ifdef LIBNFP_USE_RATIONAL -bool equals(const rational_t& lhs, const rational_t& rhs); -#endif -bool equals(const long double& lhs, const long double& rhs); - -const coord_t MAX_COORD = 999999999999999999.0; -const coord_t MIN_COORD = std::numeric_limits::min(); - -class point_t { -public: - point_t() : x_(0), y_(0) { - } - point_t(coord_t x, coord_t y) : x_(x), y_(y) { - } - bool marked_ = false; - coord_t x_; - coord_t y_; - - point_t operator-(const point_t& other) const { - point_t result = *this; - bg::subtract_point(result, other); - return result; - } - - point_t operator+(const point_t& other) const { - point_t result = *this; - bg::add_point(result, other); - return result; - } - - bool operator==(const point_t& other) const { - return bg::equals(this, other); - } - - bool operator!=(const point_t& other) const { - return !this->operator ==(other); - } - - bool operator<(const point_t& other) const { - return boost::geometry::math::smaller(this->x_, other.x_) || (equals(this->x_, other.x_) && boost::geometry::math::smaller(this->y_, other.y_)); - } -}; - - - - -inline long double toLongDouble(const LongDouble& c) { - return c.val(); -} - -#ifdef LIBNFP_USE_RATIONAL -inline long double toLongDouble(const rational_t& c) { - return bm::numerator(c).convert_to() / bm::denominator(c).convert_to(); -} -#endif - -std::ostream& operator<<(std::ostream& os, const coord_t& p) { - os << toLongDouble(p); - return os; -} - -std::istream& operator>>(std::istream& is, LongDouble& c) { - long double val; - is >> val; - c.setVal(val); - return is; -} - -std::ostream& operator<<(std::ostream& os, const point_t& p) { - os << "{" << toLongDouble(p.x_) << "," << toLongDouble(p.y_) << "}"; - return os; -} -const point_t INVALID_POINT = {MAX_COORD, MAX_COORD}; - -typedef bg::model::segment segment_t; -} - -#ifdef LIBNFP_USE_RATIONAL -inline long double acos(const libnfporb::rational_t& r) { - return acos(libnfporb::toLongDouble(r)); -} -#endif - -inline long double acos(const libnfporb::LongDouble& ld) { - return acos(libnfporb::toLongDouble(ld)); -} - -#ifdef LIBNFP_USE_RATIONAL -inline long double sqrt(const libnfporb::rational_t& r) { - return sqrt(libnfporb::toLongDouble(r)); -} -#endif - -inline long double sqrt(const libnfporb::LongDouble& ld) { - return sqrt(libnfporb::toLongDouble(ld)); -} - -BOOST_GEOMETRY_REGISTER_POINT_2D(libnfporb::point_t, libnfporb::coord_t, cs::cartesian, x_, y_) - - -namespace boost { -namespace geometry { -namespace math { -namespace detail { - -template <> -struct square_root -{ - typedef libnfporb::LongDouble return_type; - - static inline libnfporb::LongDouble apply(libnfporb::LongDouble const& a) - { - return std::sqrt(a.val()); - } -}; - -#ifdef LIBNFP_USE_RATIONAL -template <> -struct square_root -{ - typedef libnfporb::rational_t return_type; - - static inline libnfporb::rational_t apply(libnfporb::rational_t const& a) - { - return std::sqrt(libnfporb::toLongDouble(a)); - } -}; -#endif - -template<> -struct abs - { - static libnfporb::LongDouble apply(libnfporb::LongDouble const& value) - { - libnfporb::LongDouble const zero = libnfporb::LongDouble(); - return value.val() < zero.val() ? -value.val() : value.val(); - } - }; - -template <> -struct equals -{ - template - static inline bool apply(libnfporb::LongDouble const& lhs, libnfporb::LongDouble const& rhs, Policy const& policy) - { - if(lhs.val() == rhs.val()) - return true; - - return bg::math::detail::abs::apply(lhs.val() - rhs.val()) <= policy.apply(lhs.val(), rhs.val()) * libnfporb::NFP_EPSILON; - } -}; - -template <> -struct smaller -{ - static inline bool apply(libnfporb::LongDouble const& lhs, libnfporb::LongDouble const& rhs) - { - if(lhs.val() == rhs.val() || bg::math::detail::abs::apply(lhs.val() - rhs.val()) <= libnfporb::NFP_EPSILON * std::max(lhs.val(), rhs.val())) - return false; - - return lhs < rhs; - } -}; -} -} -} -} - -namespace libnfporb { -inline bool smaller(const LongDouble& lhs, const LongDouble& rhs) { - return boost::geometry::math::detail::smaller::apply(lhs, rhs); -} - -inline bool larger(const LongDouble& lhs, const LongDouble& rhs) { - return smaller(rhs, lhs); -} - -bool equals(const LongDouble& lhs, const LongDouble& rhs) { - if(lhs.val() == rhs.val()) - return true; - - return bg::math::detail::abs::apply(lhs.val() - rhs.val()) <= libnfporb::NFP_EPSILON * std::max(lhs.val(), rhs.val()); -} - -#ifdef LIBNFP_USE_RATIONAL -inline bool smaller(const rational_t& lhs, const rational_t& rhs) { - return lhs < rhs; -} - -inline bool larger(const rational_t& lhs, const rational_t& rhs) { - return smaller(rhs, lhs); -} - -bool equals(const rational_t& lhs, const rational_t& rhs) { - return lhs == rhs; -} -#endif - -inline bool smaller(const long double& lhs, const long double& rhs) { - return lhs < rhs; -} - -inline bool larger(const long double& lhs, const long double& rhs) { - return smaller(rhs, lhs); -} - - -bool equals(const long double& lhs, const long double& rhs) { - return lhs == rhs; -} - -typedef bg::model::polygon polygon_t; -typedef std::vector nfp_t; -typedef bg::model::linestring linestring_t; - -typedef polygon_t::ring_type::size_type psize_t; - -typedef bg::model::d2::point_xy pointf_t; -typedef bg::model::segment segmentf_t; -typedef bg::model::polygon polygonf_t; - -polygonf_t::ring_type convert(const polygon_t::ring_type& r) { - polygonf_t::ring_type rf; - for(const auto& pt : r) { - rf.push_back(pointf_t(toLongDouble(pt.x_), toLongDouble(pt.y_))); - } - return rf; -} - -polygonf_t convert(polygon_t p) { - polygonf_t pf; - pf.outer() = convert(p.outer()); - - for(const auto& r : p.inners()) { - pf.inners().push_back(convert(r)); - } - - return pf; -} - -polygon_t nfpRingsToNfpPoly(const nfp_t& nfp) { - polygon_t nfppoly; - for (const auto& pt : nfp.front()) { - nfppoly.outer().push_back(pt); - } - - for (size_t i = 1; i < nfp.size(); ++i) { - nfppoly.inners().push_back({}); - for (const auto& pt : nfp[i]) { - nfppoly.inners().back().push_back(pt); - } - } - - return nfppoly; -} - -void write_svg(std::string const& filename,const std::vector& segments) { - std::ofstream svg(filename.c_str()); - - boost::geometry::svg_mapper mapper(svg, 100, 100, "width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\""); - for(const auto& seg : segments) { - segmentf_t segf({toLongDouble(seg.first.x_), toLongDouble(seg.first.y_)}, {toLongDouble(seg.second.x_), toLongDouble(seg.second.y_)}); - mapper.add(segf); - mapper.map(segf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2"); - } -} - -void write_svg(std::string const& filename, const polygon_t& p, const polygon_t::ring_type& ring) { - std::ofstream svg(filename.c_str()); - - boost::geometry::svg_mapper mapper(svg, 100, 100, "width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\""); - auto pf = convert(p); - auto rf = convert(ring); - - mapper.add(pf); - mapper.map(pf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2"); - mapper.add(rf); - mapper.map(rf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2"); -} - -void write_svg(std::string const& filename, std::vector const& polygons) { - std::ofstream svg(filename.c_str()); - - boost::geometry::svg_mapper mapper(svg, 100, 100, "width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\""); - for (auto p : polygons) { - auto pf = convert(p); - mapper.add(pf); - mapper.map(pf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2"); - } -} - -void write_svg(std::string const& filename, std::vector const& polygons, const nfp_t& nfp) { - polygon_t nfppoly; - for (const auto& pt : nfp.front()) { - nfppoly.outer().push_back(pt); - } - - for (size_t i = 1; i < nfp.size(); ++i) { - nfppoly.inners().push_back({}); - for (const auto& pt : nfp[i]) { - nfppoly.inners().back().push_back(pt); - } - } - std::ofstream svg(filename.c_str()); - - boost::geometry::svg_mapper mapper(svg, 100, 100, "width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\""); - for (auto p : polygons) { - auto pf = convert(p); - mapper.add(pf); - mapper.map(pf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2"); - } - bg::correct(nfppoly); - auto nfpf = convert(nfppoly); - mapper.add(nfpf); - mapper.map(nfpf, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2"); - - for(auto& r: nfpf.inners()) { - if(r.size() == 1) { - mapper.add(r.front()); - mapper.map(r.front(), "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2"); - } else if(r.size() == 2) { - segmentf_t seg(r.front(), *(r.begin()+1)); - mapper.add(seg); - mapper.map(seg, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2"); - } - } -} - -std::ostream& operator<<(std::ostream& os, const segment_t& seg) { - os << "{" << seg.first << "," << seg.second << "}"; - return os; -} - -bool operator<(const segment_t& lhs, const segment_t& rhs) { - return lhs.first < rhs.first || ((lhs.first == rhs.first) && (lhs.second < rhs.second)); -} - -bool operator==(const segment_t& lhs, const segment_t& rhs) { - return (lhs.first == rhs.first && lhs.second == rhs.second) || (lhs.first == rhs.second && lhs.second == rhs.first); -} - -bool operator!=(const segment_t& lhs, const segment_t& rhs) { - return !operator==(lhs,rhs); -} - -enum Alignment { - LEFT, - RIGHT, - ON -}; - -point_t normalize(const point_t& pt) { - point_t norm = pt; - coord_t len = bg::length(segment_t{{0,0},pt}); - - if(len == 0.0L) - return {0,0}; - - norm.x_ /= len; - norm.y_ /= len; - - return norm; -} - -Alignment get_alignment(const segment_t& seg, const point_t& pt){ - coord_t res = ((seg.second.x_ - seg.first.x_)*(pt.y_ - seg.first.y_) - - (seg.second.y_ - seg.first.y_)*(pt.x_ - seg.first.x_)); - - if(equals(res, 0)) { - return ON; - } else if(larger(res,0)) { - return LEFT; - } else { - return RIGHT; - } -} - -long double get_inner_angle(const point_t& joint, const point_t& end1, const point_t& end2) { - coord_t dx21 = end1.x_-joint.x_; - coord_t dx31 = end2.x_-joint.x_; - coord_t dy21 = end1.y_-joint.y_; - coord_t dy31 = end2.y_-joint.y_; - coord_t m12 = sqrt((dx21*dx21 + dy21*dy21)); - coord_t m13 = sqrt((dx31*dx31 + dy31*dy31)); - if(m12 == 0.0L || m13 == 0.0L) - return 0; - return acos( (dx21*dx31 + dy21*dy31) / (m12 * m13) ); -} - -struct TouchingPoint { - enum Type { - VERTEX, - A_ON_B, - B_ON_A - }; - Type type_; - psize_t A_; - psize_t B_; -}; - -struct TranslationVector { - point_t vector_; - segment_t edge_; - bool fromA_; - string name_; - - bool operator<(const TranslationVector& other) const { - return this->vector_ < other.vector_ || ((this->vector_ == other.vector_) && (this->edge_ < other.edge_)); - } -}; - -std::ostream& operator<<(std::ostream& os, const TranslationVector& tv) { - os << "{" << tv.edge_ << " -> " << tv.vector_ << "} = " << tv.name_; - return os; -} - - -void read_wkt_polygon(const string& filename, polygon_t& p) { - std::ifstream t(filename); - - std::string str; - t.seekg(0, std::ios::end); - str.reserve(t.tellg()); - t.seekg(0, std::ios::beg); - - str.assign((std::istreambuf_iterator(t)), - std::istreambuf_iterator()); - - str.pop_back(); - bg::read_wkt(str, p); - bg::correct(p); -} - -std::vector find_minimum_y(const polygon_t& p) { - std::vector result; - coord_t min = MAX_COORD; - auto& po = p.outer(); - for(psize_t i = 0; i < p.outer().size() - 1; ++i) { - if(smaller(po[i].y_, min)) { - result.clear(); - min = po[i].y_; - result.push_back(i); - } else if (equals(po[i].y_, min)) { - result.push_back(i); - } - } - return result; -} - -std::vector find_maximum_y(const polygon_t& p) { - std::vector result; - coord_t max = MIN_COORD; - auto& po = p.outer(); - for(psize_t i = 0; i < p.outer().size() - 1; ++i) { - if(larger(po[i].y_, max)) { - result.clear(); - max = po[i].y_; - result.push_back(i); - } else if (equals(po[i].y_, max)) { - result.push_back(i); - } - } - return result; -} - -psize_t find_point(const polygon_t::ring_type& ring, const point_t& pt) { - for(psize_t i = 0; i < ring.size(); ++i) { - if(ring[i] == pt) - return i; - } - return std::numeric_limits::max(); -} - -std::vector findTouchingPoints(const polygon_t::ring_type& ringA, const polygon_t::ring_type& ringB) { - std::vector touchers; - for(psize_t i = 0; i < ringA.size() - 1; i++) { - psize_t nextI = i+1; - for(psize_t j = 0; j < ringB.size() - 1; j++) { - psize_t nextJ = j+1; - if(ringA[i] == ringB[j]) { - touchers.push_back({TouchingPoint::VERTEX, i, j}); - } else if (ringA[nextI] != ringB[j] && bg::intersects(segment_t(ringA[i],ringA[nextI]), ringB[j])) { - touchers.push_back({TouchingPoint::B_ON_A, nextI, j}); - } else if (ringB[nextJ] != ringA[i] && bg::intersects(segment_t(ringB[j],ringB[nextJ]), ringA[i])) { - touchers.push_back({TouchingPoint::A_ON_B, i, nextJ}); - } - } - } - return touchers; -} - -//TODO deduplicate code -TranslationVector trimVector(const polygon_t::ring_type& rA, const polygon_t::ring_type& rB, const TranslationVector& tv) { - coord_t shortest = bg::length(tv.edge_); - TranslationVector trimmed = tv; - for(const auto& ptA : rA) { - point_t translated; - //for polygon A we invert the translation - trans::translate_transformer translate(-tv.vector_.x_, -tv.vector_.y_); - boost::geometry::transform(ptA, translated, translate); - linestring_t projection; - segment_t segproj(ptA, translated); - projection.push_back(ptA); - projection.push_back(translated); - std::vector intersections; - bg::intersection(rB, projection, intersections); - if(bg::touches(projection, rB) && intersections.size() < 2) { - continue; - } - - //find shortest intersection - coord_t len; - segment_t segi; - for(const auto& pti : intersections) { - segi = segment_t(ptA,pti); - len = bg::length(segi); - if(smaller(len, shortest)) { - trimmed.vector_ = ptA - pti; - trimmed.edge_ = segi; - shortest = len; - } - } - } - - for(const auto& ptB : rB) { - point_t translated; - - trans::translate_transformer translate(tv.vector_.x_, tv.vector_.y_); - boost::geometry::transform(ptB, translated, translate); - linestring_t projection; - segment_t segproj(ptB, translated); - projection.push_back(ptB); - projection.push_back(translated); - std::vector intersections; - bg::intersection(rA, projection, intersections); - if(bg::touches(projection, rA) && intersections.size() < 2) { - continue; - } - - //find shortest intersection - coord_t len; - segment_t segi; - for(const auto& pti : intersections) { - - segi = segment_t(ptB,pti); - len = bg::length(segi); - if(smaller(len, shortest)) { - trimmed.vector_ = pti - ptB; - trimmed.edge_ = segi; - shortest = len; - } - } - } - return trimmed; -} - -std::vector findFeasibleTranslationVectors(polygon_t::ring_type& ringA, polygon_t::ring_type& ringB, const std::vector& touchers) { - //use a set to automatically filter duplicate vectors - std::vector potentialVectors; - std::vector> touchEdges; - - for (psize_t i = 0; i < touchers.size(); i++) { - point_t& vertexA = ringA[touchers[i].A_]; - vertexA.marked_ = true; - - // adjacent A vertices - auto prevAindex = static_cast(touchers[i].A_ - 1); - auto nextAindex = static_cast(touchers[i].A_ + 1); - - prevAindex = (prevAindex < 0) ? static_cast(ringA.size() - 2) : prevAindex; // loop - nextAindex = (static_cast(nextAindex) >= ringA.size()) ? 1 : nextAindex; // loop - - point_t& prevA = ringA[prevAindex]; - point_t& nextA = ringA[nextAindex]; - - // adjacent B vertices - point_t& vertexB = ringB[touchers[i].B_]; - - auto prevBindex = static_cast(touchers[i].B_ - 1); - auto nextBindex = static_cast(touchers[i].B_ + 1); - - prevBindex = (prevBindex < 0) ? static_cast(ringB.size() - 2) : prevBindex; // loop - nextBindex = (static_cast(nextBindex) >= ringB.size()) ? 1 : nextBindex; // loop - - point_t& prevB = ringB[prevBindex]; - point_t& nextB = ringB[nextBindex]; - - if (touchers[i].type_ == TouchingPoint::VERTEX) { - segment_t a1 = { vertexA, nextA }; - segment_t a2 = { vertexA, prevA }; - segment_t b1 = { vertexB, nextB }; - segment_t b2 = { vertexB, prevB }; - - //swap the segment elements so that always the first point is the touching point - //also make the second segment always a segment of ringB - touchEdges.push_back({a1, b1}); - touchEdges.push_back({a1, b2}); - touchEdges.push_back({a2, b1}); - touchEdges.push_back({a2, b2}); -#ifdef NFP_DEBUG - write_svg("touchersV" + std::to_string(i) + ".svg", {a1,a2,b1,b2}); -#endif - - //TODO test parallel edges for floating point stability - Alignment al; - //a1 and b1 meet at start vertex - al = get_alignment(a1, b1.second); - if(al == LEFT) { - potentialVectors.push_back({b1.first - b1.second, b1, false, "vertex1"}); - } else if(al == RIGHT) { - potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex2"}); - } else { - potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex3"}); - } - - //a1 and b2 meet at start and end - al = get_alignment(a1, b2.second); - if(al == LEFT) { - //no feasible translation - } else if(al == RIGHT) { - potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex4"}); - } else { - potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex5"}); - } - - //a2 and b1 meet at end and start - al = get_alignment(a2, b1.second); - if(al == LEFT) { - //no feasible translation - } else if(al == RIGHT) { - potentialVectors.push_back({b1.first - b1.second, b1, false, "vertex6"}); - } else { - potentialVectors.push_back({b1.first - b1.second, b1, false, "vertex7"}); - } - } else if (touchers[i].type_ == TouchingPoint::B_ON_A) { - segment_t a1 = {vertexB, vertexA}; - segment_t a2 = {vertexB, prevA}; - segment_t b1 = {vertexB, prevB}; - segment_t b2 = {vertexB, nextB}; - - touchEdges.push_back({a1, b1}); - touchEdges.push_back({a1, b2}); - touchEdges.push_back({a2, b1}); - touchEdges.push_back({a2, b2}); -#ifdef NFP_DEBUG - write_svg("touchersB" + std::to_string(i) + ".svg", {a1,a2,b1,b2}); -#endif - potentialVectors.push_back({vertexA - vertexB, {vertexB, vertexA}, true, "bona"}); - } else if (touchers[i].type_ == TouchingPoint::A_ON_B) { - //TODO testme - segment_t a1 = {vertexA, prevA}; - segment_t a2 = {vertexA, nextA}; - segment_t b1 = {vertexA, vertexB}; - segment_t b2 = {vertexA, prevB}; -#ifdef NFP_DEBUG - write_svg("touchersA" + std::to_string(i) + ".svg", {a1,a2,b1,b2}); -#endif - touchEdges.push_back({a1, b1}); - touchEdges.push_back({a2, b1}); - touchEdges.push_back({a1, b2}); - touchEdges.push_back({a2, b2}); - potentialVectors.push_back({vertexA - vertexB, {vertexA, vertexB}, false, "aonb"}); - } - } - - //discard immediately intersecting translations - std::vector vectors; - for(const auto& v : potentialVectors) { - bool discarded = false; - for(const auto& sp : touchEdges) { - point_t normEdge = normalize(v.edge_.second - v.edge_.first); - point_t normFirst = normalize(sp.first.second - sp.first.first); - point_t normSecond = normalize(sp.second.second - sp.second.first); - - Alignment a1 = get_alignment({{0,0},normEdge}, normFirst); - Alignment a2 = get_alignment({{0,0},normEdge}, normSecond); - - if(a1 == a2 && a1 != ON) { - long double df = get_inner_angle({0,0},normEdge, normFirst); - long double ds = get_inner_angle({0,0},normEdge, normSecond); - - point_t normIn = normalize(v.edge_.second - v.edge_.first); - if (equals(df, ds)) { - TranslationVector trimmed = trimVector(ringA,ringB, v); - polygon_t::ring_type translated; - trans::translate_transformer translate(trimmed.vector_.x_, trimmed.vector_.y_); - boost::geometry::transform(ringB, translated, translate); - if (!(bg::intersects(translated, ringA) && !bg::overlaps(translated, ringA) && !bg::covered_by(translated, ringA) && !bg::covered_by(ringA, translated))) { - discarded = true; - break; - } - } else { - - if (normIn == normalize(v.vector_)) { - if (larger(ds, df)) { - discarded = true; - break; - } - } else { - if (smaller(ds, df)) { - discarded = true; - break; - } - } - } - } - } - if(!discarded) - vectors.push_back(v); - } - return vectors; -} - -bool find(const std::vector& h, const TranslationVector& tv) { - for(const auto& htv : h) { - if(htv.vector_ == tv.vector_) - return true; - } - return false; -} - -TranslationVector getLongest(const std::vector& tvs) { - coord_t len; - coord_t maxLen = MIN_COORD; - TranslationVector longest; - longest.vector_ = INVALID_POINT; - - for(auto& tv : tvs) { - len = bg::length(segment_t{{0,0},tv.vector_}); - if(larger(len, maxLen)) { - maxLen = len; - longest = tv; - } - } - return longest; -} - -TranslationVector selectNextTranslationVector(const polygon_t& pA, const polygon_t::ring_type& rA, const polygon_t::ring_type& rB, const std::vector& tvs, const std::vector& history) { - if(!history.empty()) { - TranslationVector last = history.back(); - std::vector historyCopy = history; - if(historyCopy.size() >= 2) { - historyCopy.erase(historyCopy.end() - 1); - historyCopy.erase(historyCopy.end() - 1); - if(historyCopy.size() > 4) { - historyCopy.erase(historyCopy.begin(), historyCopy.end() - 4); - } - - } else { - historyCopy.clear(); - } - DEBUG_MSG("last", last); - - psize_t laterI = std::numeric_limits::max(); - point_t previous = rA[0]; - point_t next; - - if(last.fromA_) { - for (psize_t i = 1; i < rA.size() + 1; ++i) { - if (i >= rA.size()) - next = rA[i % rA.size()]; - else - next = rA[i]; - - segment_t candidate( previous, next ); - if(candidate == last.edge_) { - laterI = i; - break; - } - previous = next; - } - - if (laterI == std::numeric_limits::max()) { - point_t later; - if (last.vector_ == (last.edge_.second - last.edge_.first)) { - later = last.edge_.second; - } else { - later = last.edge_.first; - } - - laterI = find_point(rA, later); - } - } else { - point_t later; - if (last.vector_ == (last.edge_.second - last.edge_.first)) { - later = last.edge_.second; - } else { - later = last.edge_.first; - } - - laterI = find_point(rA, later); - } - - if (laterI == std::numeric_limits::max()) { - throw std::runtime_error( - "Internal error: Can't find later point of last edge"); - } - - std::vector viableEdges; - previous = rA[laterI]; - for(psize_t i = laterI + 1; i < rA.size() + laterI + 1; ++i) { - if(i >= rA.size()) - next = rA[i % rA.size()]; - else - next = rA[i]; - - viableEdges.push_back({previous, next}); - previous = next; - } - -// auto rng = std::default_random_engine {}; -// std::shuffle(std::begin(viableEdges), std::end(viableEdges), rng); - - //search with consulting the history to prevent oscillation - std::vector viableTrans; - for(const auto& ve: viableEdges) { - for(const auto& tv : tvs) { - if((tv.fromA_ && (normalize(tv.vector_) == normalize(ve.second - ve.first))) && (tv.edge_ != last.edge_ || tv.vector_.x_ != -last.vector_.x_ || tv.vector_.y_ != -last.vector_.y_) && !find(historyCopy, tv)) { - viableTrans.push_back(tv); - } - } - for (const auto& tv : tvs) { - if (!tv.fromA_) { - point_t later; - if (tv.vector_ == (tv.edge_.second - tv.edge_.first) && (tv.edge_ != last.edge_ || tv.vector_.x_ != -last.vector_.x_ || tv.vector_.y_ != -last.vector_.y_) && !find(historyCopy, tv)) { - later = tv.edge_.second; - } else if (tv.vector_ == (tv.edge_.first - tv.edge_.second)) { - later = tv.edge_.first; - } else - continue; - - if (later == ve.first || later == ve.second) { - viableTrans.push_back(tv); - } - } - } - } - - if(!viableTrans.empty()) - return getLongest(viableTrans); - - //search again without the history - for(const auto& ve: viableEdges) { - for(const auto& tv : tvs) { - if((tv.fromA_ && (normalize(tv.vector_) == normalize(ve.second - ve.first))) && (tv.edge_ != last.edge_ || tv.vector_.x_ != -last.vector_.x_ || tv.vector_.y_ != -last.vector_.y_)) { - viableTrans.push_back(tv); - } - } - for (const auto& tv : tvs) { - if (!tv.fromA_) { - point_t later; - if (tv.vector_ == (tv.edge_.second - tv.edge_.first) && (tv.edge_ != last.edge_ || tv.vector_.x_ != -last.vector_.x_ || tv.vector_.y_ != -last.vector_.y_)) { - later = tv.edge_.second; - } else if (tv.vector_ == (tv.edge_.first - tv.edge_.second)) { - later = tv.edge_.first; - } else - continue; - - if (later == ve.first || later == ve.second) { - viableTrans.push_back(tv); - } - } - } - } - if(!viableTrans.empty()) - return getLongest(viableTrans); - - /* - //search again without the history and without checking last edge - for(const auto& ve: viableEdges) { - for(const auto& tv : tvs) { - if((tv.fromA_ && (normalize(tv.vector_) == normalize(ve.second - ve.first)))) { - return tv; - } - } - for (const auto& tv : tvs) { - if (!tv.fromA_) { - point_t later; - if (tv.vector_ == (tv.edge_.second - tv.edge_.first)) { - later = tv.edge_.second; - } else if (tv.vector_ == (tv.edge_.first - tv.edge_.second)) { - later = tv.edge_.first; - } else - continue; - - if (later == ve.first || later == ve.second) { - return tv; - } - } - } - }*/ - - if(tvs.size() == 1) - return *tvs.begin(); - - TranslationVector tv; - tv.vector_ = INVALID_POINT; - return tv; - } else { - return getLongest(tvs); - } -} - -bool inNfp(const point_t& pt, const nfp_t& nfp) { - for(const auto& r : nfp) { - if(bg::touches(pt, r)) - return true; - } - - return false; -} - -enum SearchStartResult { - FIT, - FOUND, - NOT_FOUND -}; - -SearchStartResult searchStartTranslation(polygon_t::ring_type& rA, const polygon_t::ring_type& rB, const nfp_t& nfp,const bool& inside, point_t& result) { - for(psize_t i = 0; i < rA.size() - 1; i++) { - psize_t index; - if (i >= rA.size()) - index = i % rA.size() + 1; - else - index = i; - - auto& ptA = rA[index]; - - if(ptA.marked_) - continue; - - ptA.marked_ = true; - - for(const auto& ptB: rB) { - point_t testTranslation = ptA - ptB; - polygon_t::ring_type translated; - boost::geometry::transform(rB, translated, trans::translate_transformer(testTranslation.x_, testTranslation.y_)); - - //check if the translated rB is identical to rA - bool identical = false; - for(const auto& ptT: translated) { - identical = false; - for(const auto& ptA: rA) { - if(ptT == ptA) { - identical = true; - break; - } - } - if(!identical) - break; - } - - if(identical) { - result = testTranslation; - return FIT; - } - - bool bInside = false; - for(const auto& ptT: translated) { - if(bg::within(ptT, rA)) { - bInside = true; - break; - } else if(!bg::touches(ptT, rA)) { - bInside = false; - break; - } - } - - if(((bInside && inside) || (!bInside && !inside)) && (!bg::overlaps(translated, rA) && !bg::covered_by(translated, rA) && !bg::covered_by(rA, translated)) && !inNfp(translated.front(), nfp)){ - result = testTranslation; - return FOUND; - } - - point_t nextPtA = rA[index + 1]; - TranslationVector slideVector; - slideVector.vector_ = nextPtA - ptA; - slideVector.edge_ = {ptA, nextPtA}; - slideVector.fromA_ = true; - TranslationVector trimmed = trimVector(rA, translated, slideVector); - polygon_t::ring_type translated2; - trans::translate_transformer trans(trimmed.vector_.x_, trimmed.vector_.y_); - boost::geometry::transform(translated, translated2, trans); - - //check if the translated rB is identical to rA - identical = false; - for(const auto& ptT: translated) { - identical = false; - for(const auto& ptA: rA) { - if(ptT == ptA) { - identical = true; - break; - } - } - if(!identical) - break; - } - - if(identical) { - result = trimmed.vector_ + testTranslation; - return FIT; - } - - bInside = false; - for(const auto& ptT: translated2) { - if(bg::within(ptT, rA)) { - bInside = true; - break; - } else if(!bg::touches(ptT, rA)) { - bInside = false; - break; - } - } - - if(((bInside && inside) || (!bInside && !inside)) && (!bg::overlaps(translated2, rA) && !bg::covered_by(translated2, rA) && !bg::covered_by(rA, translated2)) && !inNfp(translated2.front(), nfp)){ - result = trimmed.vector_ + testTranslation; - return FOUND; - } - } - } - return NOT_FOUND; -} - -enum SlideResult { - LOOP, - NO_LOOP, - NO_TRANSLATION -}; - -SlideResult slide(polygon_t& pA, polygon_t::ring_type& rA, polygon_t::ring_type& rB, nfp_t& nfp, const point_t& transB, bool inside) { - polygon_t::ring_type rifsB; - boost::geometry::transform(rB, rifsB, trans::translate_transformer(transB.x_, transB.y_)); - rB = std::move(rifsB); - -#ifdef NFP_DEBUG - write_svg("ifs.svg", pA, rB); -#endif - - bool startAvailable = true; - psize_t cnt = 0; - point_t referenceStart = rB.front(); - std::vector history; - - //generate the nfp for the ring - while(startAvailable) { - DEBUG_VAL(cnt); - //use first point of rB as reference - nfp.back().push_back(rB.front()); - if(cnt == 15) - std::cerr << ""; - - std::vector touchers = findTouchingPoints(rA, rB); - -#ifdef NFP_DEBUG - DEBUG_MSG("touchers", touchers.size()); - for(auto t : touchers) { - DEBUG_VAL(t.type_); - } -#endif - if(touchers.empty()) { - throw std::runtime_error("Internal error: No touching points found"); - } - std::vector transVectors = findFeasibleTranslationVectors(rA, rB, touchers); - -#ifdef NFP_DEBUG - DEBUG_MSG("collected vectors", transVectors.size()); - for(auto pt : transVectors) { - DEBUG_VAL(pt); - } -#endif - - if(transVectors.empty()) { - return NO_LOOP; - } - - TranslationVector next = selectNextTranslationVector(pA, rA, rB, transVectors, history); - - if(next.vector_ == INVALID_POINT) - return NO_TRANSLATION; - - DEBUG_MSG("next", next); - - TranslationVector trimmed = trimVector(rA, rB, next); - DEBUG_MSG("trimmed", trimmed); - - history.push_back(next); - - polygon_t::ring_type nextRB; - boost::geometry::transform(rB, nextRB, trans::translate_transformer(trimmed.vector_.x_, trimmed.vector_.y_)); - rB = std::move(nextRB); - -#ifdef NFP_DEBUG - write_svg("next" + std::to_string(cnt) + ".svg", pA,rB); -#endif - - ++cnt; - if(referenceStart == rB.front() || (inside && bg::touches(rB.front(), nfp.front()))) { - startAvailable = false; - } - } - return LOOP; -} - -void removeCoLinear(polygon_t::ring_type& r) { - assert(r.size() > 2); - psize_t nextI; - psize_t prevI = 0; - segment_t segment(r[r.size() - 2], r[0]); - polygon_t::ring_type newR; - - for (psize_t i = 1; i < r.size() + 1; ++i) { - if (i >= r.size()) - nextI = i % r.size() + 1; - else - nextI = i; - - if (get_alignment(segment, r[nextI]) != ON) { - newR.push_back(r[prevI]); - } - segment = {segment.second, r[nextI]}; - prevI = nextI; - } - - r = newR; -} - -void removeCoLinear(polygon_t& p) { - removeCoLinear(p.outer()); - for (auto& r : p.inners()) - removeCoLinear(r); -} - -nfp_t generateNFP(polygon_t& pA, polygon_t& pB, const bool checkValidity = true) { - removeCoLinear(pA); - removeCoLinear(pB); - - if(checkValidity) { - std::string reason; - if(!bg::is_valid(pA, reason)) - throw std::runtime_error("Polygon A is invalid: " + reason); - - if(!bg::is_valid(pB, reason)) - throw std::runtime_error("Polygon B is invalid: " + reason); - } - - nfp_t nfp; - -#ifdef NFP_DEBUG - write_svg("start.svg", {pA, pB}); -#endif - - DEBUG_VAL(bg::wkt(pA)) - DEBUG_VAL(bg::wkt(pB)); - - //prevent double vertex connections at start because we might come back the same way we go which would end the nfp prematurely - std::vector ptyaminI = find_minimum_y(pA); - std::vector ptybmaxI = find_maximum_y(pB); - - point_t pAstart; - point_t pBstart; - - if(ptyaminI.size() > 1 || ptybmaxI.size() > 1) { - //find right-most of A and left-most of B to prevent double connection at start - coord_t maxX = MIN_COORD; - psize_t iRightMost = 0; - for(psize_t& ia : ptyaminI) { - const point_t& candidateA = pA.outer()[ia]; - if(larger(candidateA.x_, maxX)) { - maxX = candidateA.x_; - iRightMost = ia; - } - } - - coord_t minX = MAX_COORD; - psize_t iLeftMost = 0; - for(psize_t& ib : ptybmaxI) { - const point_t& candidateB = pB.outer()[ib]; - if(smaller(candidateB.x_, minX)) { - minX = candidateB.x_; - iLeftMost = ib; - } - } - pAstart = pA.outer()[iRightMost]; - pBstart = pB.outer()[iLeftMost]; - } else { - pAstart = pA.outer()[ptyaminI.front()]; - pBstart = pB.outer()[ptybmaxI.front()]; - } - - nfp.push_back({}); - point_t transB = {pAstart - pBstart}; - - if(slide(pA, pA.outer(), pB.outer(), nfp, transB, false) != LOOP) { - throw std::runtime_error("Unable to complete outer nfp loop"); - } - - DEBUG_VAL("##### outer #####"); - point_t startTrans; - while(true) { - SearchStartResult res = searchStartTranslation(pA.outer(), pB.outer(), nfp, false, startTrans); - if(res == FOUND) { - nfp.push_back({}); - DEBUG_VAL("##### interlock start #####") - polygon_t::ring_type rifsB; - boost::geometry::transform(pB.outer(), rifsB, trans::translate_transformer(startTrans.x_, startTrans.y_)); - if(inNfp(rifsB.front(), nfp)) { - continue; - } - SlideResult sres = slide(pA, pA.outer(), pB.outer(), nfp, startTrans, true); - if(sres != LOOP) { - if(sres == NO_TRANSLATION) { - //no initial slide found -> jiggsaw - if(!inNfp(pB.outer().front(),nfp)) { - nfp.push_back({}); - nfp.back().push_back(pB.outer().front()); - } - } - } - DEBUG_VAL("##### interlock end #####"); - } else if(res == FIT) { - point_t reference = pB.outer().front(); - point_t translated; - trans::translate_transformer translate(startTrans.x_, startTrans.y_); - boost::geometry::transform(reference, translated, translate); - if(!inNfp(translated,nfp)) { - nfp.push_back({}); - nfp.back().push_back(translated); - } - break; - } else - break; - } - - - for(auto& rA : pA.inners()) { - while(true) { - SearchStartResult res = searchStartTranslation(rA, pB.outer(), nfp, true, startTrans); - if(res == FOUND) { - nfp.push_back({}); - DEBUG_VAL("##### hole start #####"); - slide(pA, rA, pB.outer(), nfp, startTrans, true); - DEBUG_VAL("##### hole end #####"); - } else if(res == FIT) { - point_t reference = pB.outer().front(); - point_t translated; - trans::translate_transformer translate(startTrans.x_, startTrans.y_); - boost::geometry::transform(reference, translated, translate); - if(!inNfp(translated,nfp)) { - nfp.push_back({}); - nfp.back().push_back(translated); - } - break; - } else - break; - } - } - -#ifdef NFP_DEBUG - write_svg("nfp.svg", {pA,pB}, nfp); -#endif - - return nfp; -} -} -#endif diff --git a/xs/src/libnest2d/tools/nfp_svgnest.hpp b/xs/src/libnest2d/tools/nfp_svgnest.hpp deleted file mode 100644 index ac5700c101..0000000000 --- a/xs/src/libnest2d/tools/nfp_svgnest.hpp +++ /dev/null @@ -1,1018 +0,0 @@ -#ifndef NFP_SVGNEST_HPP -#define NFP_SVGNEST_HPP - -#include -#include - -#include - -namespace libnest2d { - -namespace __svgnest { - -using std::sqrt; -using std::min; -using std::max; -using std::abs; -using std::isnan; - -//template struct _Scale { -// static const BP2D_CONSTEXPR long long Value = 1000000; -//}; - -template struct _alg { - using Contour = TContour; - using Point = TPoint; - using iCoord = TCoord; - using Coord = double; - using Shapes = nfp::Shapes; - - static const Coord TOL; - -#define dNAN std::nan("") - - struct Vector { - Coord x = 0.0, y = 0.0; - bool marked = false; - Vector() = default; - Vector(Coord X, Coord Y): x(X), y(Y) {} - Vector(const Point& p): x(Coord(getX(p))), y(Coord(getY(p))) {} - operator Point() const { return {iCoord(x), iCoord(y)}; } - Vector& operator=(const Point& p) { - x = getX(p), y = getY(p); return *this; - } - bool operator!=(const Vector& v) const { - return v.x != x || v.y != y; - } - Vector(std::initializer_list il): - x(*il.begin()), y(*std::next(il.begin())) {} - }; - - static inline Coord x(const Point& p) { return Coord(getX(p)); } - static inline Coord y(const Point& p) { return Coord(getY(p)); } - - static inline Coord x(const Vector& p) { return p.x; } - static inline Coord y(const Vector& p) { return p.y; } - - class Cntr { - std::vector v_; - public: - Cntr(const Contour& c) { - v_.reserve(c.size()); - std::transform(c.begin(), c.end(), std::back_inserter(v_), - [](const Point& p) { - return Vector(double(x(p)) / 1e6, double(y(p)) / 1e6); - }); - std::reverse(v_.begin(), v_.end()); - v_.pop_back(); - } - Cntr() = default; - - Coord offsetx = 0; - Coord offsety = 0; - size_t size() const { return v_.size(); } - bool empty() const { return v_.empty(); } - typename std::vector::const_iterator cbegin() const { return v_.cbegin(); } - typename std::vector::const_iterator cend() const { return v_.cend(); } - typename std::vector::iterator begin() { return v_.begin(); } - typename std::vector::iterator end() { return v_.end(); } - Vector& operator[](size_t idx) { return v_[idx]; } - const Vector& operator[](size_t idx) const { return v_[idx]; } - template - void emplace_back(Args&&...args) { - v_.emplace_back(std::forward(args)...); - } - template - void push(Args&&...args) { - v_.emplace_back(std::forward(args)...); - } - void clear() { v_.clear(); } - - operator Contour() const { - Contour cnt; - cnt.reserve(v_.size() + 1); - std::transform(v_.begin(), v_.end(), std::back_inserter(cnt), - [](const Vector& vertex) { - return Point(iCoord(vertex.x) * 1000000, iCoord(vertex.y) * 1000000); - }); - if(!cnt.empty()) cnt.emplace_back(cnt.front()); - S sh = shapelike::create(cnt); - -// std::reverse(cnt.begin(), cnt.end()); - return shapelike::getContour(sh); - } - }; - - inline static bool _almostEqual(Coord a, Coord b, - Coord tolerance = TOL) - { - return std::abs(a - b) < tolerance; - } - - // returns true if p lies on the line segment defined by AB, - // but not at any endpoints may need work! - static bool _onSegment(const Vector& A, const Vector& B, const Vector& p) { - - // vertical line - if(_almostEqual(A.x, B.x) && _almostEqual(p.x, A.x)) { - if(!_almostEqual(p.y, B.y) && !_almostEqual(p.y, A.y) && - p.y < max(B.y, A.y) && p.y > min(B.y, A.y)){ - return true; - } - else{ - return false; - } - } - - // horizontal line - if(_almostEqual(A.y, B.y) && _almostEqual(p.y, A.y)){ - if(!_almostEqual(p.x, B.x) && !_almostEqual(p.x, A.x) && - p.x < max(B.x, A.x) && p.x > min(B.x, A.x)){ - return true; - } - else{ - return false; - } - } - - //range check - if((p.x < A.x && p.x < B.x) || (p.x > A.x && p.x > B.x) || - (p.y < A.y && p.y < B.y) || (p.y > A.y && p.y > B.y)) - return false; - - // exclude end points - if((_almostEqual(p.x, A.x) && _almostEqual(p.y, A.y)) || - (_almostEqual(p.x, B.x) && _almostEqual(p.y, B.y))) - return false; - - - double cross = (p.y - A.y) * (B.x - A.x) - (p.x - A.x) * (B.y - A.y); - - if(abs(cross) > TOL) return false; - - double dot = (p.x - A.x) * (B.x - A.x) + (p.y - A.y)*(B.y - A.y); - - if(dot < 0 || _almostEqual(dot, 0)) return false; - - double len2 = (B.x - A.x)*(B.x - A.x) + (B.y - A.y)*(B.y - A.y); - - if(dot > len2 || _almostEqual(dot, len2)) return false; - - return true; - } - - // return true if point is in the polygon, false if outside, and null if exactly on a point or edge - static int pointInPolygon(const Vector& point, const Cntr& polygon) { - if(polygon.size() < 3){ - return 0; - } - - bool inside = false; - Coord offsetx = polygon.offsetx; - Coord offsety = polygon.offsety; - - for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j=i++) { - auto xi = polygon[i].x + offsetx; - auto yi = polygon[i].y + offsety; - auto xj = polygon[j].x + offsetx; - auto yj = polygon[j].y + offsety; - - if(_almostEqual(xi, point.x) && _almostEqual(yi, point.y)){ - return 0; // no result - } - - if(_onSegment({xi, yi}, {xj, yj}, point)){ - return 0; // exactly on the segment - } - - if(_almostEqual(xi, xj) && _almostEqual(yi, yj)){ // ignore very small lines - continue; - } - - bool intersect = ((yi > point.y) != (yj > point.y)) && - (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi); - if (intersect) inside = !inside; - } - - return inside? 1 : -1; - } - - static bool intersect(const Cntr& A, const Cntr& B){ - Contour a = A, b = B; - return shapelike::intersects(shapelike::create(a), shapelike::create(b)); - } - - static Vector _normalizeVector(const Vector& v) { - if(_almostEqual(v.x*v.x + v.y*v.y, Coord(1))){ - return Point(v); // given vector was already a unit vector - } - auto len = sqrt(v.x*v.x + v.y*v.y); - auto inverse = 1/len; - - return { Coord(v.x*inverse), Coord(v.y*inverse) }; - } - - static double pointDistance( const Vector& p, - const Vector& s1, - const Vector& s2, - Vector normal, - bool infinite = false) - { - normal = _normalizeVector(normal); - - Vector dir = { - normal.y, - -normal.x - }; - - auto pdot = p.x*dir.x + p.y*dir.y; - auto s1dot = s1.x*dir.x + s1.y*dir.y; - auto s2dot = s2.x*dir.x + s2.y*dir.y; - - auto pdotnorm = p.x*normal.x + p.y*normal.y; - auto s1dotnorm = s1.x*normal.x + s1.y*normal.y; - auto s2dotnorm = s2.x*normal.x + s2.y*normal.y; - - if(!infinite){ - if (((pdots1dot || _almostEqual(pdot, s1dot)) && - (pdot>s2dot || _almostEqual(pdot, s2dot)))) - { - // dot doesn't collide with segment, - // or lies directly on the vertex - return dNAN; - } - if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) && - (pdotnorm>s1dotnorm && pdotnorm>s2dotnorm)) - { - return min(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm); - } - if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) && - (pdotnorm EFmax){ - return dNAN; - } - - double overlap = 0; - - if((ABmax > EFmax && ABmin < EFmin) || (EFmax > ABmax && EFmin < ABmin)) - { - overlap = 1; - } - else{ - auto minMax = min(ABmax, EFmax); - auto maxMin = max(ABmin, EFmin); - - auto maxMax = max(ABmax, EFmax); - auto minMin = min(ABmin, EFmin); - - overlap = (minMax-maxMin)/(maxMax-minMin); - } - - auto crossABE = (E.y - A.y) * (B.x - A.x) - (E.x - A.x) * (B.y - A.y); - auto crossABF = (F.y - A.y) * (B.x - A.x) - (F.x - A.x) * (B.y - A.y); - - // lines are colinear - if(_almostEqual(crossABE,0) && _almostEqual(crossABF,0)){ - - Vector ABnorm = {B.y-A.y, A.x-B.x}; - Vector EFnorm = {F.y-E.y, E.x-F.x}; - - auto ABnormlength = sqrt(ABnorm.x*ABnorm.x + ABnorm.y*ABnorm.y); - ABnorm.x /= ABnormlength; - ABnorm.y /= ABnormlength; - - auto EFnormlength = sqrt(EFnorm.x*EFnorm.x + EFnorm.y*EFnorm.y); - EFnorm.x /= EFnormlength; - EFnorm.y /= EFnormlength; - - // segment normals must point in opposite directions - if(abs(ABnorm.y * EFnorm.x - ABnorm.x * EFnorm.y) < TOL && - ABnorm.y * EFnorm.y + ABnorm.x * EFnorm.x < 0){ - // normal of AB segment must point in same direction as - // given direction vector - auto normdot = ABnorm.y * direction.y + ABnorm.x * direction.x; - // the segments merely slide along eachother - if(_almostEqual(normdot,0, TOL)){ - return dNAN; - } - if(normdot < 0){ - return 0.0; - } - } - return dNAN; - } - - std::vector distances; distances.reserve(10); - - // coincident points - if(_almostEqual(dotA, dotE)){ - distances.emplace_back(crossA-crossE); - } - else if(_almostEqual(dotA, dotF)){ - distances.emplace_back(crossA-crossF); - } - else if(dotA > EFmin && dotA < EFmax){ - auto d = pointDistance(A,E,F,reverse); - if(!isnan(d) && _almostEqual(d, 0)) - { // A currently touches EF, but AB is moving away from EF - auto dB = pointDistance(B,E,F,reverse,true); - if(dB < 0 || _almostEqual(dB*overlap,0)){ - d = dNAN; - } - } - if(!isnan(d)){ - distances.emplace_back(d); - } - } - - if(_almostEqual(dotB, dotE)){ - distances.emplace_back(crossB-crossE); - } - else if(_almostEqual(dotB, dotF)){ - distances.emplace_back(crossB-crossF); - } - else if(dotB > EFmin && dotB < EFmax){ - auto d = pointDistance(B,E,F,reverse); - - if(!isnan(d) && _almostEqual(d, 0)) - { // crossA>crossB A currently touches EF, but AB is moving away from EF - double dA = pointDistance(A,E,F,reverse,true); - if(dA < 0 || _almostEqual(dA*overlap,0)){ - d = dNAN; - } - } - if(!isnan(d)){ - distances.emplace_back(d); - } - } - - if(dotE > ABmin && dotE < ABmax){ - auto d = pointDistance(E,A,B,direction); - if(!isnan(d) && _almostEqual(d, 0)) - { // crossF ABmin && dotF < ABmax){ - auto d = pointDistance(F,A,B,direction); - if(!isnan(d) && _almostEqual(d, 0)) - { // && crossE 0 || _almostEqual(d, 0)){ - distance = d; - } - } - } - } - return distance; - } - - static double polygonProjectionDistance(const Cntr& AA, - const Cntr& BB, - Vector direction) - { - Cntr A = AA; - Cntr B = BB; - - auto Boffsetx = B.offsetx; - auto Boffsety = B.offsety; - auto Aoffsetx = A.offsetx; - auto Aoffsety = A.offsety; - - // close the loop for polygons - if(A[0] != A[A.size()-1]){ - A.push(A[0]); - } - - if(B[0] != B[B.size()-1]){ - B.push(B[0]); - } - - auto& edgeA = A; - auto& edgeB = B; - - double distance = dNAN, d; -// Vector p, s1, s2; - - for(size_t i = 0; i < edgeB.size(); i++) { - // the shortest/most negative projection of B onto A - double minprojection = dNAN; - Vector minp; - for(size_t j = 0; j < edgeA.size() - 1; j++){ - Vector p = {x(edgeB[i]) + Boffsetx, y(edgeB[i]) + Boffsety }; - Vector s1 = {x(edgeA[j]) + Aoffsetx, y(edgeA[j]) + Aoffsety }; - Vector s2 = {x(edgeA[j+1]) + Aoffsetx, y(edgeA[j+1]) + Aoffsety }; - - if(abs((s2.y-s1.y) * direction.x - - (s2.x-s1.x) * direction.y) < TOL) continue; - - // project point, ignore edge boundaries - d = pointDistance(p, s1, s2, direction); - - if(!isnan(d) && (isnan(minprojection) || d < minprojection)) { - minprojection = d; - minp = p; - } - } - - if(!isnan(minprojection) && (isnan(distance) || - minprojection > distance)){ - distance = minprojection; - } - } - - return distance; - } - - static std::pair searchStartPoint( - const Cntr& AA, const Cntr& BB, bool inside, const std::vector& NFP = {}) - { - // clone arrays - auto A = AA; - auto B = BB; - -// // close the loop for polygons -// if(A[0] != A[A.size()-1]){ -// A.push(A[0]); -// } - -// if(B[0] != B[B.size()-1]){ -// B.push(B[0]); -// } - - // returns true if point already exists in the given nfp - auto inNfp = [](const Vector& p, const std::vector& nfp){ - if(nfp.empty()){ - return false; - } - - for(size_t i=0; i < nfp.size(); i++){ - for(size_t j = 0; j< nfp[i].size(); j++){ - if(_almostEqual(p.x, nfp[i][j].x) && - _almostEqual(p.y, nfp[i][j].y)){ - return true; - } - } - } - - return false; - }; - - for(size_t i = 0; i < A.size() - 1; i++){ - if(!A[i].marked) { - A[i].marked = true; - for(size_t j = 0; j < B.size(); j++){ - B.offsetx = A[i].x - B[j].x; - B.offsety = A[i].y - B[j].y; - - int Binside = 0; - for(size_t k = 0; k < B.size(); k++){ - int inpoly = pointInPolygon({B[k].x + B.offsetx, B[k].y + B.offsety}, A); - if(inpoly != 0){ - Binside = inpoly; - break; - } - } - - if(Binside == 0){ // A and B are the same - return {false, {}}; - } - - auto startPoint = std::make_pair(true, Vector(B.offsetx, B.offsety)); - if(((Binside && inside) || (!Binside && !inside)) && - !intersect(A,B) && !inNfp(startPoint.second, NFP)){ - return startPoint; - } - - // slide B along vector - auto vx = A[i+1].x - A[i].x; - auto vy = A[i+1].y - A[i].y; - - double d1 = polygonProjectionDistance(A,B,{vx, vy}); - double d2 = polygonProjectionDistance(B,A,{-vx, -vy}); - - double d = dNAN; - - // todo: clean this up - if(isnan(d1) && isnan(d2)){ - // nothin - } - else if(isnan(d1)){ - d = d2; - } - else if(isnan(d2)){ - d = d1; - } - else{ - d = min(d1,d2); - } - - // only slide until no longer negative - // todo: clean this up - if(!isnan(d) && !_almostEqual(d,0) && d > 0){ - - } - else{ - continue; - } - - auto vd2 = vx*vx + vy*vy; - - if(d*d < vd2 && !_almostEqual(d*d, vd2)){ - auto vd = sqrt(vx*vx + vy*vy); - vx *= d/vd; - vy *= d/vd; - } - - B.offsetx += vx; - B.offsety += vy; - - for(size_t k = 0; k < B.size(); k++){ - int inpoly = pointInPolygon({B[k].x + B.offsetx, B[k].y + B.offsety}, A); - if(inpoly != 0){ - Binside = inpoly; - break; - } - } - startPoint = std::make_pair(true, Vector{B.offsetx, B.offsety}); - if(((Binside && inside) || (!Binside && !inside)) && - !intersect(A,B) && !inNfp(startPoint.second, NFP)){ - return startPoint; - } - } - } - } - - return {false, Vector(0, 0)}; - } - - static std::vector noFitPolygon(Cntr A, - Cntr B, - bool inside, - bool searchEdges) - { - if(A.size() < 3 || B.size() < 3) { - throw GeometryException(GeomErr::NFP); - return {}; - } - - A.offsetx = 0; - A.offsety = 0; - - long i = 0, j = 0; - - auto minA = y(A[0]); - long minAindex = 0; - - auto maxB = y(B[0]); - long maxBindex = 0; - - for(i = 1; i < A.size(); i++){ - A[i].marked = false; - if(y(A[i]) < minA){ - minA = y(A[i]); - minAindex = i; - } - } - - for(i = 1; i < B.size(); i++){ - B[i].marked = false; - if(y(B[i]) > maxB){ - maxB = y(B[i]); - maxBindex = i; - } - } - - std::pair startpoint; - - if(!inside){ - // shift B such that the bottom-most point of B is at the top-most - // point of A. This guarantees an initial placement with no - // intersections - startpoint = { true, - { x(A[minAindex]) - x(B[maxBindex]), - y(A[minAindex]) - y(B[maxBindex]) } - }; - } - else { - // no reliable heuristic for inside - startpoint = searchStartPoint(A, B, true); - } - - std::vector NFPlist; - - struct Touch { - int type; - long A; - long B; - Touch(int t, long a, long b): type(t), A(a), B(b) {} - }; - - while(startpoint.first) { - - B.offsetx = startpoint.second.x; - B.offsety = startpoint.second.y; - - // maintain a list of touching points/edges - std::vector touching; - - struct V { - Coord x, y; - Vector *start, *end; - operator bool() { - return start != nullptr && end != nullptr; - } - operator Vector() const { return {x, y}; } - } prevvector = {0, 0, nullptr, nullptr}; - - Cntr NFP; - NFP.emplace_back(x(B[0]) + B.offsetx, y(B[0]) + B.offsety); - - auto referencex = x(B[0]) + B.offsetx; - auto referencey = y(B[0]) + B.offsety; - auto startx = referencex; - auto starty = referencey; - unsigned counter = 0; - - // sanity check, prevent infinite loop - while(counter < 10*(A.size() + B.size())){ - touching.clear(); - - // find touching vertices/edges - for(i = 0; i < A.size(); i++){ - long nexti = (i == A.size() - 1) ? 0 : i + 1; - for(j = 0; j < B.size(); j++){ - - long nextj = (j == B.size() - 1) ? 0 : j + 1; - - if( _almostEqual(A[i].x, B[j].x+B.offsetx) && - _almostEqual(A[i].y, B[j].y+B.offsety)) - { - touching.emplace_back(0, i, j); - } - else if( _onSegment( - A[i], A[nexti], - { B[j].x+B.offsetx, B[j].y + B.offsety}) ) - { - touching.emplace_back(1, nexti, j); - } - else if( _onSegment( - {B[j].x+B.offsetx, B[j].y + B.offsety}, - {B[nextj].x+B.offsetx, B[nextj].y + B.offsety}, - A[i]) ) - { - touching.emplace_back(2, i, nextj); - } - } - } - - // generate translation vectors from touching vertices/edges - std::vector vectors; - for(i=0; i < touching.size(); i++){ - auto& vertexA = A[touching[i].A]; - vertexA.marked = true; - - // adjacent A vertices - auto prevAindex = touching[i].A - 1; - auto nextAindex = touching[i].A + 1; - - prevAindex = (prevAindex < 0) ? A.size() - 1 : prevAindex; // loop - nextAindex = (nextAindex >= A.size()) ? 0 : nextAindex; // loop - - auto& prevA = A[prevAindex]; - auto& nextA = A[nextAindex]; - - // adjacent B vertices - auto& vertexB = B[touching[i].B]; - - auto prevBindex = touching[i].B-1; - auto nextBindex = touching[i].B+1; - - prevBindex = (prevBindex < 0) ? B.size() - 1 : prevBindex; // loop - nextBindex = (nextBindex >= B.size()) ? 0 : nextBindex; // loop - - auto& prevB = B[prevBindex]; - auto& nextB = B[nextBindex]; - - if(touching[i].type == 0){ - - V vA1 = { - prevA.x - vertexA.x, - prevA.y - vertexA.y, - &vertexA, - &prevA - }; - - V vA2 = { - nextA.x - vertexA.x, - nextA.y - vertexA.y, - &vertexA, - &nextA - }; - - // B vectors need to be inverted - V vB1 = { - vertexB.x - prevB.x, - vertexB.y - prevB.y, - &prevB, - &vertexB - }; - - V vB2 = { - vertexB.x - nextB.x, - vertexB.y - nextB.y, - &nextB, - &vertexB - }; - - vectors.emplace_back(vA1); - vectors.emplace_back(vA2); - vectors.emplace_back(vB1); - vectors.emplace_back(vB2); - } - else if(touching[i].type == 1){ - vectors.emplace_back(V{ - vertexA.x-(vertexB.x+B.offsetx), - vertexA.y-(vertexB.y+B.offsety), - &prevA, - &vertexA - }); - - vectors.emplace_back(V{ - prevA.x-(vertexB.x+B.offsetx), - prevA.y-(vertexB.y+B.offsety), - &vertexA, - &prevA - }); - } - else if(touching[i].type == 2){ - vectors.emplace_back(V{ - vertexA.x-(vertexB.x+B.offsetx), - vertexA.y-(vertexB.y+B.offsety), - &prevB, - &vertexB - }); - - vectors.emplace_back(V{ - vertexA.x-(prevB.x+B.offsetx), - vertexA.y-(prevB.y+B.offsety), - &vertexB, - &prevB - }); - } - } - - // TODO: there should be a faster way to reject vectors that - // will cause immediate intersection. For now just check them all - - V translate = {0, 0, nullptr, nullptr}; - double maxd = 0; - - for(i = 0; i < vectors.size(); i++) { - if(vectors[i].x == 0 && vectors[i].y == 0){ - continue; - } - - // if this vector points us back to where we came from, ignore it. - // ie cross product = 0, dot product < 0 - if(prevvector && vectors[i].y * prevvector.y + vectors[i].x * prevvector.x < 0){ - - // compare magnitude with unit vectors - double vectorlength = sqrt(vectors[i].x*vectors[i].x+vectors[i].y*vectors[i].y); - Vector unitv = {Coord(vectors[i].x/vectorlength), - Coord(vectors[i].y/vectorlength)}; - - double prevlength = sqrt(prevvector.x*prevvector.x+prevvector.y*prevvector.y); - Vector prevunit = { prevvector.x/prevlength, prevvector.y/prevlength}; - - // we need to scale down to unit vectors to normalize vector length. Could also just do a tan here - if(abs(unitv.y * prevunit.x - unitv.x * prevunit.y) < 0.0001){ - continue; - } - } - - V vi = vectors[i]; - double d = polygonSlideDistance(A, B, vi, true); - double vecd2 = vectors[i].x*vectors[i].x + vectors[i].y*vectors[i].y; - - if(isnan(d) || d*d > vecd2){ - double vecd = sqrt(vectors[i].x*vectors[i].x + vectors[i].y*vectors[i].y); - d = vecd; - } - - if(!isnan(d) && d > maxd){ - maxd = d; - translate = vectors[i]; - } - } - - if(!translate || _almostEqual(maxd, 0)) - { - // didn't close the loop, something went wrong here - NFP.clear(); - break; - } - - translate.start->marked = true; - translate.end->marked = true; - - prevvector = translate; - - // trim - double vlength2 = translate.x*translate.x + translate.y*translate.y; - if(maxd*maxd < vlength2 && !_almostEqual(maxd*maxd, vlength2)){ - double scale = sqrt((maxd*maxd)/vlength2); - translate.x *= scale; - translate.y *= scale; - } - - referencex += translate.x; - referencey += translate.y; - - if(_almostEqual(referencex, startx) && - _almostEqual(referencey, starty)) { - // we've made a full loop - break; - } - - // if A and B start on a touching horizontal line, - // the end point may not be the start point - bool looped = false; - if(NFP.size() > 0) { - for(i = 0; i < NFP.size() - 1; i++) { - if(_almostEqual(referencex, NFP[i].x) && - _almostEqual(referencey, NFP[i].y)){ - looped = true; - } - } - } - - if(looped){ - // we've made a full loop - break; - } - - NFP.emplace_back(referencex, referencey); - - B.offsetx += translate.x; - B.offsety += translate.y; - - counter++; - } - - if(NFP.size() > 0){ - NFPlist.emplace_back(NFP); - } - - if(!searchEdges){ - // only get outer NFP or first inner NFP - break; - } - - startpoint = - searchStartPoint(A, B, inside, NFPlist); - - } - - return NFPlist; - } -}; - -template const double _alg::TOL = std::pow(10, -9); - -} -} - -#endif // NFP_SVGNEST_HPP diff --git a/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp b/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp deleted file mode 100644 index ea1fb4d076..0000000000 --- a/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef NFP_SVGNEST_GLUE_HPP -#define NFP_SVGNEST_GLUE_HPP - -#include "nfp_svgnest.hpp" - -#include - -namespace libnest2d { - -namespace __svgnest { - -//template<> struct _Tol { -// static const BP2D_CONSTEXPR TCoord Value = 1000000; -//}; - -} - -namespace nfp { - -using NfpR = NfpResult; - -template<> struct NfpImpl { - NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) { -// return nfpConvexOnly(sh, cother); - namespace sl = shapelike; - using alg = __svgnest::_alg; - - auto nfp_p = alg::noFitPolygon(sl::getContour(sh), - sl::getContour(cother), false, false); - - PolygonImpl nfp_cntr; - if(!nfp_p.empty()) nfp_cntr.Contour = nfp_p.front(); - return {nfp_cntr, referenceVertex(nfp_cntr)}; - } -}; - -template<> struct NfpImpl { - NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) { -// return nfpConvexOnly(sh, cother); - namespace sl = shapelike; - using alg = __svgnest::_alg; - - std::cout << "Itt vagyok" << std::endl; - auto nfp_p = alg::noFitPolygon(sl::getContour(sh), - sl::getContour(cother), false, false); - - PolygonImpl nfp_cntr; - nfp_cntr.Contour = nfp_p.front(); - return {nfp_cntr, referenceVertex(nfp_cntr)}; - } -}; - -template<> -struct NfpImpl { - NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) { - namespace sl = shapelike; - using alg = __svgnest::_alg; - - auto nfp_p = alg::noFitPolygon(sl::getContour(sh), - sl::getContour(cother), true, false); - - PolygonImpl nfp_cntr; - nfp_cntr.Contour = nfp_p.front(); - return {nfp_cntr, referenceVertex(nfp_cntr)}; - } -}; - -template<> struct MaxNfpLevel { -// static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::BOTH_CONCAVE; - static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY; -}; - -}} - -#endif // NFP_SVGNEST_GLUE_HPP diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp deleted file mode 100644 index 4355cd61b1..0000000000 --- a/xs/src/libslic3r/BoundingBox.cpp +++ /dev/null @@ -1,293 +0,0 @@ -#include "BoundingBox.hpp" -#include -#include - -#include - -namespace Slic3r { - -template BoundingBoxBase::BoundingBoxBase(const std::vector &points); -template BoundingBoxBase::BoundingBoxBase(const std::vector &points); - -template BoundingBox3Base::BoundingBox3Base(const std::vector &points); - -BoundingBox::BoundingBox(const Lines &lines) -{ - Points points; - points.reserve(lines.size()); - for (const Line &line : lines) { - points.emplace_back(line.a); - points.emplace_back(line.b); - } - *this = BoundingBox(points); -} - -void -BoundingBox::polygon(Polygon* polygon) const -{ - polygon->points.clear(); - polygon->points.resize(4); - polygon->points[0].x = this->min.x; - polygon->points[0].y = this->min.y; - polygon->points[1].x = this->max.x; - polygon->points[1].y = this->min.y; - polygon->points[2].x = this->max.x; - polygon->points[2].y = this->max.y; - polygon->points[3].x = this->min.x; - polygon->points[3].y = this->max.y; -} - -Polygon -BoundingBox::polygon() const -{ - Polygon p; - this->polygon(&p); - return p; -} - -BoundingBox BoundingBox::rotated(double angle) const -{ - BoundingBox out; - out.merge(this->min.rotated(angle)); - out.merge(this->max.rotated(angle)); - out.merge(Point(this->min.x, this->max.y).rotated(angle)); - out.merge(Point(this->max.x, this->min.y).rotated(angle)); - return out; -} - -BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const -{ - BoundingBox out; - out.merge(this->min.rotated(angle, center)); - out.merge(this->max.rotated(angle, center)); - out.merge(Point(this->min.x, this->max.y).rotated(angle, center)); - out.merge(Point(this->max.x, this->min.y).rotated(angle, center)); - return out; -} - -template void -BoundingBoxBase::scale(double factor) -{ - this->min.scale(factor); - this->max.scale(factor); -} -template void BoundingBoxBase::scale(double factor); -template void BoundingBoxBase::scale(double factor); -template void BoundingBoxBase::scale(double factor); - -template void -BoundingBoxBase::merge(const PointClass &point) -{ - if (this->defined) { - this->min.x = std::min(point.x, this->min.x); - this->min.y = std::min(point.y, this->min.y); - this->max.x = std::max(point.x, this->max.x); - this->max.y = std::max(point.y, this->max.y); - } else { - this->min = this->max = point; - this->defined = true; - } -} -template void BoundingBoxBase::merge(const Point &point); -template void BoundingBoxBase::merge(const Pointf &point); - -template void -BoundingBoxBase::merge(const std::vector &points) -{ - this->merge(BoundingBoxBase(points)); -} -template void BoundingBoxBase::merge(const Points &points); -template void BoundingBoxBase::merge(const Pointfs &points); - -template void -BoundingBoxBase::merge(const BoundingBoxBase &bb) -{ - assert(bb.defined || bb.min.x >= bb.max.x || bb.min.y >= bb.max.y); - if (bb.defined) { - if (this->defined) { - this->min.x = std::min(bb.min.x, this->min.x); - this->min.y = std::min(bb.min.y, this->min.y); - this->max.x = std::max(bb.max.x, this->max.x); - this->max.y = std::max(bb.max.y, this->max.y); - } else { - this->min = bb.min; - this->max = bb.max; - this->defined = true; - } - } -} -template void BoundingBoxBase::merge(const BoundingBoxBase &bb); -template void BoundingBoxBase::merge(const BoundingBoxBase &bb); - -template void -BoundingBox3Base::merge(const PointClass &point) -{ - if (this->defined) { - this->min.z = std::min(point.z, this->min.z); - this->max.z = std::max(point.z, this->max.z); - } - BoundingBoxBase::merge(point); -} -template void BoundingBox3Base::merge(const Pointf3 &point); - -template void -BoundingBox3Base::merge(const std::vector &points) -{ - this->merge(BoundingBox3Base(points)); -} -template void BoundingBox3Base::merge(const Pointf3s &points); - -template void -BoundingBox3Base::merge(const BoundingBox3Base &bb) -{ - assert(bb.defined || bb.min.x >= bb.max.x || bb.min.y >= bb.max.y || bb.min.z >= bb.max.z); - if (bb.defined) { - if (this->defined) { - this->min.z = std::min(bb.min.z, this->min.z); - this->max.z = std::max(bb.max.z, this->max.z); - } - BoundingBoxBase::merge(bb); - } -} -template void BoundingBox3Base::merge(const BoundingBox3Base &bb); - -template PointClass -BoundingBoxBase::size() const -{ - return PointClass(this->max.x - this->min.x, this->max.y - this->min.y); -} -template Point BoundingBoxBase::size() const; -template Pointf BoundingBoxBase::size() const; - -template PointClass -BoundingBox3Base::size() const -{ - return PointClass(this->max.x - this->min.x, this->max.y - this->min.y, this->max.z - this->min.z); -} -template Pointf3 BoundingBox3Base::size() const; - -template double BoundingBoxBase::radius() const -{ - assert(this->defined); - double x = this->max.x - this->min.x; - double y = this->max.y - this->min.y; - return 0.5 * sqrt(x*x+y*y); -} -template double BoundingBoxBase::radius() const; -template double BoundingBoxBase::radius() const; - -template double BoundingBox3Base::radius() const -{ - double x = this->max.x - this->min.x; - double y = this->max.y - this->min.y; - double z = this->max.z - this->min.z; - return 0.5 * sqrt(x*x+y*y+z*z); -} -template double BoundingBox3Base::radius() const; - -template void -BoundingBoxBase::offset(coordf_t delta) -{ - this->min.translate(-delta, -delta); - this->max.translate(delta, delta); -} -template void BoundingBoxBase::offset(coordf_t delta); -template void BoundingBoxBase::offset(coordf_t delta); - -template void -BoundingBox3Base::offset(coordf_t delta) -{ - this->min.translate(-delta, -delta, -delta); - this->max.translate(delta, delta, delta); -} -template void BoundingBox3Base::offset(coordf_t delta); - -template PointClass -BoundingBoxBase::center() const -{ - return PointClass( - (this->max.x + this->min.x)/2, - (this->max.y + this->min.y)/2 - ); -} -template Point BoundingBoxBase::center() const; -template Pointf BoundingBoxBase::center() const; - -template PointClass -BoundingBox3Base::center() const -{ - return PointClass( - (this->max.x + this->min.x)/2, - (this->max.y + this->min.y)/2, - (this->max.z + this->min.z)/2 - ); -} -template Pointf3 BoundingBox3Base::center() const; - -template coordf_t -BoundingBox3Base::max_size() const -{ - PointClass s = size(); - return std::max(s.x, std::max(s.y, s.z)); -} -template coordf_t BoundingBox3Base::max_size() const; - -// Align a coordinate to a grid. The coordinate may be negative, -// the aligned value will never be bigger than the original one. -static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) { - // Current C++ standard defines the result of integer division to be rounded to zero, - // for both positive and negative numbers. Here we want to round down for negative - // numbers as well. - coord_t aligned = (coord < 0) ? - ((coord - spacing + 1) / spacing) * spacing : - (coord / spacing) * spacing; - assert(aligned <= coord); - return aligned; -} - -void BoundingBox::align_to_grid(const coord_t cell_size) -{ - if (this->defined) { - min.x = _align_to_grid(min.x, cell_size); - min.y = _align_to_grid(min.y, cell_size); - } -} - -BoundingBoxf3 BoundingBoxf3::transformed(const std::vector& matrix) const -{ - Eigen::Matrix vertices; - - vertices(0, 0) = (float)min.x; vertices(1, 0) = (float)min.y; vertices(2, 0) = (float)min.z; - vertices(0, 1) = (float)max.x; vertices(1, 1) = (float)min.y; vertices(2, 1) = (float)min.z; - vertices(0, 2) = (float)max.x; vertices(1, 2) = (float)max.y; vertices(2, 2) = (float)min.z; - vertices(0, 3) = (float)min.x; vertices(1, 3) = (float)max.y; vertices(2, 3) = (float)min.z; - vertices(0, 4) = (float)min.x; vertices(1, 4) = (float)min.y; vertices(2, 4) = (float)max.z; - vertices(0, 5) = (float)max.x; vertices(1, 5) = (float)min.y; vertices(2, 5) = (float)max.z; - vertices(0, 6) = (float)max.x; vertices(1, 6) = (float)max.y; vertices(2, 6) = (float)max.z; - vertices(0, 7) = (float)min.x; vertices(1, 7) = (float)max.y; vertices(2, 7) = (float)max.z; - - Eigen::Transform m; - ::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float)); - Eigen::Matrix transf_vertices = m * vertices.colwise().homogeneous(); - - float min_x = transf_vertices(0, 0); - float max_x = transf_vertices(0, 0); - float min_y = transf_vertices(1, 0); - float max_y = transf_vertices(1, 0); - float min_z = transf_vertices(2, 0); - float max_z = transf_vertices(2, 0); - - for (int i = 1; i < 8; ++i) - { - min_x = std::min(min_x, transf_vertices(0, i)); - max_x = std::max(max_x, transf_vertices(0, i)); - min_y = std::min(min_y, transf_vertices(1, i)); - max_y = std::max(max_y, transf_vertices(1, i)); - min_z = std::min(min_z, transf_vertices(2, i)); - max_z = std::max(max_z, transf_vertices(2, i)); - } - - return BoundingBoxf3(Pointf3((coordf_t)min_x, (coordf_t)min_y, (coordf_t)min_z), Pointf3((coordf_t)max_x, (coordf_t)max_y, (coordf_t)max_z)); -} - -} diff --git a/xs/src/libslic3r/Format/AMF.hpp b/xs/src/libslic3r/Format/AMF.hpp deleted file mode 100644 index 4779e9a51c..0000000000 --- a/xs/src/libslic3r/Format/AMF.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef slic3r_Format_AMF_hpp_ -#define slic3r_Format_AMF_hpp_ - -namespace Slic3r { - -class Model; -class Print; -class PresetBundle; - -// Load the content of an amf file into the given model and preset bundle. -extern bool load_amf(const char *path, PresetBundle* bundle, Model *model); - -// Save the given model and the config data contained in the given Print into an amf file. -// The model could be modified during the export process if meshes are not repaired or have no shared vertices -extern bool store_amf(const char *path, Model *model, Print* print, bool export_print_config); - -}; // namespace Slic3r - -#endif /* slic3r_Format_AMF_hpp_ */ \ No newline at end of file diff --git a/xs/src/libslic3r/Format/PRUS.cpp b/xs/src/libslic3r/Format/PRUS.cpp deleted file mode 100644 index 1809eaead3..0000000000 --- a/xs/src/libslic3r/Format/PRUS.cpp +++ /dev/null @@ -1,387 +0,0 @@ -#ifdef SLIC3R_PRUS - -#include - -#include - -#include -#include -#include - -#include - -#include "../libslic3r.h" -#include "../Model.hpp" - -#include "PRUS.hpp" - -#if 0 -// Enable debugging and assert in this file. -#define DEBUG -#define _DEBUG -#undef NDEBUG -#endif - -#include - -namespace Slic3r -{ - -struct StlHeader -{ - char comment[80]; - uint32_t nTriangles; -}; - -static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct"); - -// Buffered line reader for the wxInputStream. -class LineReader -{ -public: - LineReader(wxInputStream &input_stream, const char *initial_data, int initial_len) : - m_input_stream(input_stream), - m_pos(0), - m_len(initial_len) - { - assert(initial_len >= 0 && initial_len < m_bufsize); - memcpy(m_buffer, initial_data, initial_len); - } - - const char* next_line() { - for (;;) { - // Skip empty lines. - while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n')) - ++ m_pos; - if (m_pos == m_len) { - // Empty buffer, fill it from the input stream. - m_pos = 0; - m_input_stream.Read(m_buffer, m_bufsize - 1); - m_len = m_input_stream.LastRead(); - assert(m_len >= 0 && m_len < m_bufsize); - if (m_len == 0) - // End of file. - return nullptr; - // Skip empty lines etc. - continue; - } - // The buffer is nonempty and it does not start with end of lines. Find the first end of line. - int end = m_pos + 1; - while (end < m_len && m_buffer[end] != '\r' && m_buffer[end] != '\n') - ++ end; - if (end == m_len && ! m_input_stream.Eof() && m_len < m_bufsize) { - // Move the buffer content to the buffer start and fill the rest of the buffer. - assert(m_pos > 0); - memmove(m_buffer, m_buffer + m_pos, m_len - m_pos); - m_len -= m_pos; - assert(m_len >= 0 && m_len < m_bufsize); - m_pos = 0; - m_input_stream.Read(m_buffer + m_len, m_bufsize - 1 - m_len); - int new_data = m_input_stream.LastRead(); - if (new_data > 0) { - m_len += new_data; - assert(m_len >= 0 && m_len < m_bufsize); - continue; - } - } - char *ptr_out = m_buffer + m_pos; - m_pos = end + 1; - m_buffer[end] = 0; - if (m_pos >= m_len) { - m_pos = 0; - m_len = 0; - } - return ptr_out; - } - } - - int next_line_scanf(const char *format, ...) - { - const char *line = next_line(); - if (line == nullptr) - return -1; - int result; - va_list arglist; - va_start(arglist, format); - result = vsscanf(line, format, arglist); - va_end(arglist); - return result; - } - -private: - wxInputStream &m_input_stream; - static const int m_bufsize = 4096; - char m_buffer[m_bufsize]; - int m_pos = 0; - int m_len = 0; -}; - -// Load a PrusaControl project file into a provided model. -bool load_prus(const char *path, Model *model) -{ - // To receive the content of the zipped 'scene.xml' file. - std::vector scene_xml_data; - wxFFileInputStream in( -#ifdef WIN32 - // On Windows, convert to a 16bit unicode string. - boost::nowide::widen(path).c_str() -#else - path -#endif - ); - wxZipInputStream zip(in); - std::unique_ptr entry; - size_t num_models = 0; - std::map group_to_model_object; - while (entry.reset(zip.GetNextEntry()), entry.get() != NULL) { - wxString name = entry->GetName(); - if (name == "scene.xml") { - if (! scene_xml_data.empty()) { - // scene.xml has been found more than once in the archive. - return false; - } - size_t size_last = 0; - size_t size_incr = 4096; - scene_xml_data.resize(size_incr); - while (! zip.Read(scene_xml_data.data() + size_last, size_incr).Eof()) { - size_last += zip.LastRead(); - if (scene_xml_data.size() < size_last + size_incr) - scene_xml_data.resize(size_last + size_incr); - } - size_last += zip.LastRead(); - if (scene_xml_data.size() == size_last) - scene_xml_data.resize(size_last + 1); - else if (scene_xml_data.size() > size_last + 1) - scene_xml_data.erase(scene_xml_data.begin() + size_last + 1, scene_xml_data.end()); - scene_xml_data[size_last] = 0; - } - else if (name.EndsWith(".stl") || name.EndsWith(".STL")) { - // Find the model entry in the XML data. - const wxScopedCharBuffer name_utf8 = name.ToUTF8(); - char model_name_tag[1024]; - sprintf(model_name_tag, "", name_utf8.data()); - const char *model_xml = strstr(scene_xml_data.data(), model_name_tag); - const char *zero_tag = ""; - const char *zero_xml = strstr(scene_xml_data.data(), zero_tag); - float trafo[3][4] = { 0 }; - double instance_rotation = 0.; - double instance_scaling_factor = 1.f; - Pointf instance_offset(0., 0.); - bool trafo_set = false; - unsigned int group_id = (unsigned int)-1; - unsigned int extruder_id = (unsigned int)-1; - ModelObject *model_object = nullptr; - if (model_xml != nullptr) { - model_xml += strlen(model_name_tag); - const char *position_tag = ""; - const char *position_xml = strstr(model_xml, position_tag); - const char *rotation_tag = ""; - const char *rotation_xml = strstr(model_xml, rotation_tag); - const char *scale_tag = ""; - const char *scale_xml = strstr(model_xml, scale_tag); - float position[3], rotation[3], scale[3], zero[3]; - if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr && zero_xml != nullptr && - sscanf(position_xml+strlen(position_tag), - "[%f, %f, %f]", position, position+1, position+2) == 3 && - sscanf(rotation_xml+strlen(rotation_tag), - "[%f, %f, %f]", rotation, rotation+1, rotation+2) == 3 && - sscanf(scale_xml+strlen(scale_tag), - "[%f, %f, %f]", scale, scale+1, scale+2) == 3 && - sscanf(zero_xml+strlen(zero_tag), - "[%f, %f, %f]", zero, zero+1, zero+2) == 3) { - if (scale[0] == scale[1] && scale[1] == scale[2]) { - instance_scaling_factor = scale[0]; - scale[0] = scale[1] = scale[2] = 1.; - } - if (rotation[0] == 0. && rotation[1] == 0.) { - instance_rotation = - rotation[2]; - rotation[2] = 0.; - } - Eigen::Matrix3f mat_rot, mat_scale, mat_trafo; - mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) * - Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) * - Eigen::AngleAxisf(-rotation[0], Eigen::Vector3f::UnitX()); - mat_scale = Eigen::Scaling(scale[0], scale[1], scale[2]); - mat_trafo = mat_rot * mat_scale; - for (size_t r = 0; r < 3; ++ r) { - for (size_t c = 0; c < 3; ++ c) - trafo[r][c] += mat_trafo(r, c); - } - instance_offset.x = position[0] - zero[0]; - instance_offset.y = position[1] - zero[1]; - trafo[2][3] = position[2] / instance_scaling_factor; - trafo_set = true; - } - const char *group_tag = ""; - const char *group_xml = strstr(model_xml, group_tag); - const char *extruder_tag = ""; - const char *extruder_xml = strstr(model_xml, extruder_tag); - if (group_xml != nullptr) { - int group = atoi(group_xml + strlen(group_tag)); - if (group > 0) { - group_id = group; - auto it = group_to_model_object.find(group_id); - if (it != group_to_model_object.end()) - model_object = it->second; - } - } - if (extruder_xml != nullptr) { - int e = atoi(extruder_xml + strlen(extruder_tag)); - if (e > 0) - extruder_id = e; - } - } - if (trafo_set) { - // Extract the STL. - StlHeader header; - TriangleMesh mesh; - bool mesh_valid = false; - bool stl_ascii = false; - if (!zip.Read((void*)&header, sizeof(StlHeader)).Eof()) { - if (strncmp(header.comment, "solid ", 6) == 0) - stl_ascii = true; - else { - // Header has been extracted. Now read the faces. - stl_file &stl = mesh.stl; - stl.error = 0; - stl.stats.type = inmemory; - stl.stats.number_of_facets = header.nTriangles; - stl.stats.original_num_facets = header.nTriangles; - stl_allocate(&stl); - if (header.nTriangles > 0 && zip.ReadAll((void*)stl.facet_start, 50 * header.nTriangles)) { - if (sizeof(stl_facet) > SIZEOF_STL_FACET) { - // The stl.facet_start is not packed tightly. Unpack the array of stl_facets. - unsigned char *data = (unsigned char*)stl.facet_start; - for (size_t i = header.nTriangles - 1; i > 0; -- i) - memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET); - } - // All the faces have been read. - stl_get_size(&stl); - mesh.repair(); - // Transform the model. - stl_transform(&stl, &trafo[0][0]); - if (std::abs(stl.stats.min.z) < EPSILON) - stl.stats.min.z = 0.; - // Add a mesh to a model. - if (mesh.facets_count() > 0) - mesh_valid = true; - } - } - } else - stl_ascii = true; - if (stl_ascii) { - // Try to parse ASCII STL. - char normal_buf[3][32]; - stl_facet facet; - std::vector facets; - LineReader line_reader(zip, (char*)&header, zip.LastRead()); - std::string solid_name; - facet.extra[0] = facet.extra[1] = 0; - for (;;) { - const char *line = line_reader.next_line(); - if (line == nullptr) - // End of file. - break; - if (strncmp(line, "solid", 5) == 0) { - // Opening the "solid" block. - if (! solid_name.empty()) { - // Error, solid block is already open. - facets.clear(); - break; - } - solid_name = line + 5; - if (solid_name.empty()) - solid_name = "unknown"; - continue; - } - if (strncmp(line, "endsolid", 8) == 0) { - // Closing the "solid" block. - if (solid_name.empty()) { - // Error, no solid block is open. - facets.clear(); - break; - } - solid_name.clear(); - continue; - } - // Line has to start with the word solid. - int res_normal = sscanf(line, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]); - assert(res_normal == 3); - int res_outer_loop = line_reader.next_line_scanf(" outer loop"); - assert(res_outer_loop == 0); - int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z); - assert(res_vertex1 == 3); - int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z); - assert(res_vertex2 == 3); - int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z); - assert(res_vertex3 == 3); - int res_endloop = line_reader.next_line_scanf(" endloop"); - assert(res_endloop == 0); - int res_endfacet = line_reader.next_line_scanf(" endfacet"); - if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) { - // perror("Something is syntactically very wrong with this ASCII STL!"); - facets.clear(); - break; - } - // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. - if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 || - sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 || - sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) { - // Normal was mangled. Maybe denormals or "not a number" were stored? - // Just reset the normal and silently ignore it. - memset(&facet.normal, 0, sizeof(facet.normal)); - } - facets.emplace_back(facet); - } - if (! facets.empty() && solid_name.empty()) { - stl_file &stl = mesh.stl; - stl.stats.type = inmemory; - stl.stats.number_of_facets = facets.size(); - stl.stats.original_num_facets = facets.size(); - stl_allocate(&stl); - memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50); - stl_get_size(&stl); - mesh.repair(); - // Transform the model. - stl_transform(&stl, &trafo[0][0]); - // Add a mesh to a model. - if (mesh.facets_count() > 0) - mesh_valid = true; - } - } - - if (mesh_valid) { - // Add this mesh to the model. - ModelVolume *volume = nullptr; - if (model_object == nullptr) { - // This is a first mesh of a group. Create a new object & volume. - model_object = model->add_object(name_utf8.data(), path, std::move(mesh)); - volume = model_object->volumes.front(); - ModelInstance *instance = model_object->add_instance(); - instance->rotation = instance_rotation; - instance->scaling_factor = instance_scaling_factor; - instance->offset = instance_offset; - ++ num_models; - if (group_id != (size_t)-1) - group_to_model_object[group_id] = model_object; - } else { - // This is not the 1st mesh of a group. Add it to the ModelObject. - volume = model_object->add_volume(std::move(mesh)); - volume->name = name_utf8.data(); - } - // Set the extruder to the volume. - if (extruder_id != (unsigned int)-1) { - char str_extruder[64]; - sprintf(str_extruder, "%ud", extruder_id); - volume->config.set_deserialize("extruder", str_extruder); - } - } - } - } - } - return num_models > 0; -} - -}; // namespace Slic3r - -#endif /* SLIC3R_PRUS */ diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp deleted file mode 100644 index 6205831285..0000000000 --- a/xs/src/libslic3r/Layer.hpp +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef slic3r_Layer_hpp_ -#define slic3r_Layer_hpp_ - -#include "libslic3r.h" -#include "Flow.hpp" -#include "SurfaceCollection.hpp" -#include "ExtrusionEntityCollection.hpp" -#include "ExPolygonCollection.hpp" -#include "PolylineCollection.hpp" - - -namespace Slic3r { - -class Layer; -class PrintRegion; -class PrintObject; - -// TODO: make stuff private -class LayerRegion -{ - friend class Layer; - -public: - Layer* layer() { return this->_layer; } - const Layer* layer() const { return this->_layer; } - PrintRegion* region() { return this->_region; } - const PrintRegion* region() const { return this->_region; } - - // Collection of surfaces generated by slicing the original geometry, divided by type top/bottom/internal. - SurfaceCollection slices; - - // Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature") - // and for re-starting of infills. - ExPolygons fill_expolygons; - // collection of surfaces for infill generation - SurfaceCollection fill_surfaces; - // Collection of extrusion paths/loops filling gaps. - // These fills are generated by the perimeter generator. - // They are not printed on their own, but they are copied to this->fills during infill generation. - ExtrusionEntityCollection thin_fills; - - // Collection of expolygons representing the bridged areas (thus not needing support material). - //FIXME Not used as of now. - Polygons bridged; - - // collection of polylines representing the unsupported bridge edges - PolylineCollection unsupported_bridge_edges; - - // Ordered collection of extrusion paths/loops to build all perimeters. - // This collection contains only ExtrusionEntityCollection objects. - ExtrusionEntityCollection perimeters; - // Ordered collection of extrusion paths to fill surfaces. - // This collection contains only ExtrusionEntityCollection objects. - ExtrusionEntityCollection fills; - - Flow flow(FlowRole role, bool bridge = false, double width = -1) const; - void slices_to_fill_surfaces_clipped(); - void prepare_fill_surfaces(); - void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); - void process_external_surfaces(const Layer* lower_layer); - double infill_area_threshold() const; - - void export_region_slices_to_svg(const char *path) const; - void export_region_fill_surfaces_to_svg(const char *path) const; - // Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export. - void export_region_slices_to_svg_debug(const char *name) const; - void export_region_fill_surfaces_to_svg_debug(const char *name) const; - - // Is there any valid extrusion assigned to this LayerRegion? - bool has_extrusions() const { return ! this->perimeters.entities.empty() || ! this->fills.entities.empty(); } - -private: - Layer *_layer; - PrintRegion *_region; - - LayerRegion(Layer *layer, PrintRegion *region) : _layer(layer), _region(region) {} - ~LayerRegion() {} -}; - - -typedef std::vector LayerRegionPtrs; - -class Layer { - friend class PrintObject; - -public: - size_t id() const { return this->_id; } - void set_id(size_t id) { this->_id = id; } - PrintObject* object() { return this->_object; } - const PrintObject* object() const { return this->_object; } - - Layer *upper_layer; - Layer *lower_layer; - LayerRegionPtrs regions; - bool slicing_errors; - coordf_t slice_z; // Z used for slicing in unscaled coordinates - coordf_t print_z; // Z used for printing in unscaled coordinates - coordf_t height; // layer height in unscaled coordinates - - // collection of expolygons generated by slicing the original geometry; - // also known as 'islands' (all regions and surface types are merged here) - // The slices are chained by the shortest traverse distance and this traversal - // order will be recovered by the G-code generator. - ExPolygonCollection slices; - - size_t region_count() const { return this->regions.size(); } - const LayerRegion* get_region(int idx) const { return this->regions.at(idx); } - LayerRegion* get_region(int idx) { return this->regions.at(idx); } - LayerRegion* add_region(PrintRegion* print_region); - - void make_slices(); - void merge_slices(); - template bool any_internal_region_slice_contains(const T &item) const { - for (const LayerRegion *layerm : this->regions) if (layerm->slices.any_internal_contains(item)) return true; - return false; - } - template bool any_bottom_region_slice_contains(const T &item) const { - for (const LayerRegion *layerm : this->regions) if (layerm->slices.any_bottom_contains(item)) return true; - return false; - } - void make_perimeters(); - void make_fills(); - - void export_region_slices_to_svg(const char *path) const; - void export_region_fill_surfaces_to_svg(const char *path) const; - // Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export. - void export_region_slices_to_svg_debug(const char *name) const; - void export_region_fill_surfaces_to_svg_debug(const char *name) const; - - // Is there any valid extrusion assigned to this LayerRegion? - virtual bool has_extrusions() const { for (auto layerm : this->regions) if (layerm->has_extrusions()) return true; return false; } - -protected: - size_t _id; // sequential number of layer, 0-based - PrintObject *_object; - - Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : - upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false), - slice_z(slice_z), print_z(print_z), height(height), - _id(id), _object(object) {} - virtual ~Layer(); -}; - -class SupportLayer : public Layer { - friend class PrintObject; - -public: - // Polygons covered by the supports: base, interface and contact areas. - ExPolygonCollection support_islands; - // Extrusion paths for the support base and for the support interface and contacts. - ExtrusionEntityCollection support_fills; - - // Is there any valid extrusion assigned to this LayerRegion? - virtual bool has_extrusions() const { return ! support_fills.empty(); } - -//protected: - // The constructor has been made public to be able to insert additional support layers for the skirt or a wipe tower - // between the raft and the object first layer. - SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : - Layer(id, object, height, print_z, slice_z) {} - virtual ~SupportLayer() {} -}; - -} - -#endif diff --git a/xs/src/libslic3r/Line.cpp b/xs/src/libslic3r/Line.cpp deleted file mode 100644 index e9d5d7742c..0000000000 --- a/xs/src/libslic3r/Line.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#include "Geometry.hpp" -#include "Line.hpp" -#include "Polyline.hpp" -#include -#include -#include - -namespace Slic3r { - -std::string -Line::wkt() const -{ - std::ostringstream ss; - ss << "LINESTRING(" << this->a.x << " " << this->a.y << "," - << this->b.x << " " << this->b.y << ")"; - return ss.str(); -} - -Line::operator Lines() const -{ - Lines lines; - lines.push_back(*this); - return lines; -} - -Line::operator Polyline() const -{ - Polyline pl; - pl.points.push_back(this->a); - pl.points.push_back(this->b); - return pl; -} - -void -Line::scale(double factor) -{ - this->a.scale(factor); - this->b.scale(factor); -} - -void -Line::translate(double x, double y) -{ - this->a.translate(x, y); - this->b.translate(x, y); -} - -void -Line::rotate(double angle, const Point ¢er) -{ - this->a.rotate(angle, center); - this->b.rotate(angle, center); -} - -void -Line::reverse() -{ - std::swap(this->a, this->b); -} - -double -Line::length() const -{ - return this->a.distance_to(this->b); -} - -Point -Line::midpoint() const -{ - return Point((this->a.x + this->b.x) / 2.0, (this->a.y + this->b.y) / 2.0); -} - -void -Line::point_at(double distance, Point* point) const -{ - double len = this->length(); - *point = this->a; - if (this->a.x != this->b.x) - point->x = this->a.x + (this->b.x - this->a.x) * distance / len; - if (this->a.y != this->b.y) - point->y = this->a.y + (this->b.y - this->a.y) * distance / len; -} - -Point -Line::point_at(double distance) const -{ - Point p; - this->point_at(distance, &p); - return p; -} - -bool -Line::intersection_infinite(const Line &other, Point* point) const -{ - Vector x = this->a.vector_to(other.a); - Vector d1 = this->vector(); - Vector d2 = other.vector(); - - double cross = d1.x * d2.y - d1.y * d2.x; - if (std::fabs(cross) < EPSILON) - return false; - - double t1 = (x.x * d2.y - x.y * d2.x)/cross; - point->x = this->a.x + d1.x * t1; - point->y = this->a.y + d1.y * t1; - return true; -} - -bool -Line::coincides_with(const Line &line) const -{ - return this->a.coincides_with(line.a) && this->b.coincides_with(line.b); -} - -double -Line::distance_to(const Point &point) const -{ - return point.distance_to(*this); -} - -double -Line::atan2_() const -{ - return atan2(this->b.y - this->a.y, this->b.x - this->a.x); -} - -double -Line::orientation() const -{ - double angle = this->atan2_(); - if (angle < 0) angle = 2*PI + angle; - return angle; -} - -double -Line::direction() const -{ - double atan2 = this->atan2_(); - return (fabs(atan2 - PI) < EPSILON) ? 0 - : (atan2 < 0) ? (atan2 + PI) - : atan2; -} - -bool -Line::parallel_to(double angle) const { - return Slic3r::Geometry::directions_parallel(this->direction(), angle); -} - -bool -Line::parallel_to(const Line &line) const { - return this->parallel_to(line.direction()); -} - -Vector -Line::vector() const -{ - return Vector(this->b.x - this->a.x, this->b.y - this->a.y); -} - -Vector -Line::normal() const -{ - return Vector((this->b.y - this->a.y), -(this->b.x - this->a.x)); -} - -void -Line::extend_end(double distance) -{ - // relocate last point by extending the segment by the specified length - Line line = *this; - line.reverse(); - this->b = line.point_at(-distance); -} - -void -Line::extend_start(double distance) -{ - // relocate first point by extending the first segment by the specified length - this->a = this->point_at(-distance); -} - -bool -Line::intersection(const Line& line, Point* intersection) const -{ - double denom = ((double)(line.b.y - line.a.y)*(this->b.x - this->a.x)) - - ((double)(line.b.x - line.a.x)*(this->b.y - this->a.y)); - - double nume_a = ((double)(line.b.x - line.a.x)*(this->a.y - line.a.y)) - - ((double)(line.b.y - line.a.y)*(this->a.x - line.a.x)); - - double nume_b = ((double)(this->b.x - this->a.x)*(this->a.y - line.a.y)) - - ((double)(this->b.y - this->a.y)*(this->a.x - line.a.x)); - - if (fabs(denom) < EPSILON) { - if (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON) { - return false; // coincident - } - return false; // parallel - } - - double ua = nume_a / denom; - double ub = nume_b / denom; - - if (ua >= 0 && ua <= 1.0f && ub >= 0 && ub <= 1.0f) - { - // Get the intersection point. - intersection->x = this->a.x + ua*(this->b.x - this->a.x); - intersection->y = this->a.y + ua*(this->b.y - this->a.y); - return true; - } - - return false; // not intersecting -} - -double -Line::ccw(const Point& point) const -{ - return point.ccw(*this); -} - -double Line3::length() const -{ - return a.distance_to(b); -} - -Vector3 Line3::vector() const -{ - return Vector3(b.x - a.x, b.y - a.y, b.z - a.z); -} - -Pointf3 -Linef3::intersect_plane(double z) const -{ - return Pointf3( - this->a.x + (this->b.x - this->a.x) * (z - this->a.z) / (this->b.z - this->a.z), - this->a.y + (this->b.y - this->a.y) * (z - this->a.z) / (this->b.z - this->a.z), - z - ); -} - -void -Linef3::scale(double factor) -{ - this->a.scale(factor); - this->b.scale(factor); -} - -} diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp deleted file mode 100644 index 4826017ab5..0000000000 --- a/xs/src/libslic3r/Line.hpp +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef slic3r_Line_hpp_ -#define slic3r_Line_hpp_ - -#include "libslic3r.h" -#include "Point.hpp" - -namespace Slic3r { - -class Line; -class Line3; -class Linef3; -class Polyline; -class ThickLine; -typedef std::vector Lines; -typedef std::vector Lines3; -typedef std::vector ThickLines; - -class Line -{ -public: - Point a; - Point b; - Line() {}; - explicit Line(Point _a, Point _b): a(_a), b(_b) {}; - std::string wkt() const; - operator Lines() const; - operator Polyline() const; - void scale(double factor); - void translate(double x, double y); - void rotate(double angle, const Point ¢er); - void reverse(); - double length() const; - Point midpoint() const; - void point_at(double distance, Point* point) const; - Point point_at(double distance) const; - bool intersection_infinite(const Line &other, Point* point) const; - bool coincides_with(const Line &line) const; - double distance_to(const Point &point) const; - bool parallel_to(double angle) const; - bool parallel_to(const Line &line) const; - double atan2_() const; - double orientation() const; - double direction() const; - Vector vector() const; - Vector normal() const; - void extend_end(double distance); - void extend_start(double distance); - bool intersection(const Line& line, Point* intersection) const; - double ccw(const Point& point) const; -}; - -class ThickLine : public Line -{ - public: - coordf_t a_width, b_width; - - ThickLine() : a_width(0), b_width(0) {}; - ThickLine(Point _a, Point _b) : Line(_a, _b), a_width(0), b_width(0) {}; -}; - -class Line3 -{ -public: - Point3 a; - Point3 b; - - Line3() {} - Line3(const Point3& _a, const Point3& _b) : a(_a), b(_b) {} - - double length() const; - Vector3 vector() const; -}; - -class Linef -{ - public: - Pointf a; - Pointf b; - Linef() {}; - explicit Linef(Pointf _a, Pointf _b): a(_a), b(_b) {}; -}; - -class Linef3 -{ - public: - Pointf3 a; - Pointf3 b; - Linef3() {}; - explicit Linef3(Pointf3 _a, Pointf3 _b): a(_a), b(_b) {}; - Pointf3 intersect_plane(double z) const; - void scale(double factor); -}; - -} // namespace Slic3r - -// start Boost -#include -namespace boost { namespace polygon { - template <> - struct geometry_concept { typedef segment_concept type; }; - - template <> - struct segment_traits { - typedef coord_t coordinate_type; - typedef Slic3r::Point point_type; - - static inline point_type get(const Slic3r::Line& line, direction_1d dir) { - return dir.to_int() ? line.b : line.a; - } - }; -} } -// end Boost - -#endif diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp deleted file mode 100644 index 9fe855aa17..0000000000 --- a/xs/src/libslic3r/Model.cpp +++ /dev/null @@ -1,1116 +0,0 @@ -#include "Model.hpp" -#include "Geometry.hpp" - -#include "Format/AMF.hpp" -#include "Format/OBJ.hpp" -#include "Format/PRUS.hpp" -#include "Format/STL.hpp" -#include "Format/3mf.hpp" - -#include - -#include -#include -#include -#include - -#include "SVG.hpp" -#include - -static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - -namespace Slic3r { - - unsigned int Model::s_auto_extruder_id = 1; - -Model::Model(const Model &other) -{ - // copy materials - for (const auto &m : other.materials) - this->add_material(m.first, *m.second); - // copy objects - this->objects.reserve(other.objects.size()); - for (const ModelObject *o : other.objects) - this->add_object(*o, true); -} - -Model& Model::operator=(Model other) -{ - this->swap(other); - return *this; -} - -void Model::swap(Model &other) -{ - std::swap(this->materials, other.materials); - std::swap(this->objects, other.objects); -} - -Model Model::read_from_file(const std::string &input_file, bool add_default_instances) -{ - Model model; - - bool result = false; - if (boost::algorithm::iends_with(input_file, ".stl")) - result = load_stl(input_file.c_str(), &model); - else if (boost::algorithm::iends_with(input_file, ".obj")) - result = load_obj(input_file.c_str(), &model); - else if (!boost::algorithm::iends_with(input_file, ".zip.amf") && (boost::algorithm::iends_with(input_file, ".amf") || - boost::algorithm::iends_with(input_file, ".amf.xml"))) - result = load_amf(input_file.c_str(), nullptr, &model); -#ifdef SLIC3R_PRUS - else if (boost::algorithm::iends_with(input_file, ".prusa")) - result = load_prus(input_file.c_str(), &model); -#endif /* SLIC3R_PRUS */ - else - throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension."); - - if (! result) - throw std::runtime_error("Loading of a model file failed."); - - if (model.objects.empty()) - throw std::runtime_error("The supplied file couldn't be read because it's empty"); - - for (ModelObject *o : model.objects) - o->input_file = input_file; - - if (add_default_instances) - model.add_default_instances(); - - return model; -} - -Model Model::read_from_archive(const std::string &input_file, PresetBundle* bundle, bool add_default_instances) -{ - Model model; - - bool result = false; - if (boost::algorithm::iends_with(input_file, ".3mf")) - result = load_3mf(input_file.c_str(), bundle, &model); - else if (boost::algorithm::iends_with(input_file, ".zip.amf")) - result = load_amf(input_file.c_str(), bundle, &model); - else - throw std::runtime_error("Unknown file format. Input file must have .3mf or .zip.amf extension."); - - if (!result) - throw std::runtime_error("Loading of a model file failed."); - - if (model.objects.empty()) - throw std::runtime_error("The supplied file couldn't be read because it's empty"); - - for (ModelObject *o : model.objects) - { - if (boost::algorithm::iends_with(input_file, ".zip.amf")) - { - // we remove the .zip part of the extension to avoid it be added to filenames when exporting - o->input_file = boost::ireplace_last_copy(input_file, ".zip.", "."); - } - else - o->input_file = input_file; - } - - if (add_default_instances) - model.add_default_instances(); - - return model; -} - -ModelObject* Model::add_object() -{ - this->objects.emplace_back(new ModelObject(this)); - return this->objects.back(); -} - -ModelObject* Model::add_object(const char *name, const char *path, const TriangleMesh &mesh) -{ - ModelObject* new_object = new ModelObject(this); - this->objects.push_back(new_object); - new_object->name = name; - new_object->input_file = path; - ModelVolume *new_volume = new_object->add_volume(mesh); - new_volume->name = name; - new_object->invalidate_bounding_box(); - return new_object; -} - -ModelObject* Model::add_object(const char *name, const char *path, TriangleMesh &&mesh) -{ - ModelObject* new_object = new ModelObject(this); - this->objects.push_back(new_object); - new_object->name = name; - new_object->input_file = path; - ModelVolume *new_volume = new_object->add_volume(std::move(mesh)); - new_volume->name = name; - new_object->invalidate_bounding_box(); - return new_object; -} - -ModelObject* Model::add_object(const ModelObject &other, bool copy_volumes) -{ - ModelObject* new_object = new ModelObject(this, other, copy_volumes); - this->objects.push_back(new_object); - return new_object; -} - -void Model::delete_object(size_t idx) -{ - ModelObjectPtrs::iterator i = this->objects.begin() + idx; - delete *i; - this->objects.erase(i); -} - -void Model::delete_object(ModelObject* object) -{ - if (object == nullptr) - return; - - for (ModelObjectPtrs::iterator it = objects.begin(); it != objects.end(); ++it) - { - ModelObject* obj = *it; - if (obj == object) - { - delete obj; - objects.erase(it); - return; - } - } -} - -void Model::clear_objects() -{ - for (ModelObject *o : this->objects) - delete o; - this->objects.clear(); -} - -void Model::delete_material(t_model_material_id material_id) -{ - ModelMaterialMap::iterator i = this->materials.find(material_id); - if (i != this->materials.end()) { - delete i->second; - this->materials.erase(i); - } -} - -void Model::clear_materials() -{ - for (auto &m : this->materials) - delete m.second; - this->materials.clear(); -} - -ModelMaterial* Model::add_material(t_model_material_id material_id) -{ - ModelMaterial* material = this->get_material(material_id); - if (material == nullptr) - material = this->materials[material_id] = new ModelMaterial(this); - return material; -} - -ModelMaterial* Model::add_material(t_model_material_id material_id, const ModelMaterial &other) -{ - // delete existing material if any - ModelMaterial* material = this->get_material(material_id); - delete material; - // set new material - material = new ModelMaterial(this, other); - this->materials[material_id] = material; - return material; -} - -// makes sure all objects have at least one instance -bool Model::add_default_instances() -{ - // apply a default position to all objects not having one - for (ModelObject *o : this->objects) - if (o->instances.empty()) - o->add_instance(); - return true; -} - -// this returns the bounding box of the *transformed* instances -BoundingBoxf3 Model::bounding_box() const -{ - BoundingBoxf3 bb; - for (ModelObject *o : this->objects) - bb.merge(o->bounding_box()); - return bb; -} - -void Model::center_instances_around_point(const Pointf &point) -{ -// BoundingBoxf3 bb = this->bounding_box(); - BoundingBoxf3 bb; - for (ModelObject *o : this->objects) - for (size_t i = 0; i < o->instances.size(); ++ i) - bb.merge(o->instance_bounding_box(i, false)); - - Sizef3 size = bb.size(); - coordf_t shift_x = -bb.min.x + point.x - size.x/2; - coordf_t shift_y = -bb.min.y + point.y - size.y/2; - for (ModelObject *o : this->objects) { - for (ModelInstance *i : o->instances) - i->offset.translate(shift_x, shift_y); - o->invalidate_bounding_box(); - } -} - -// flattens everything to a single mesh -TriangleMesh Model::mesh() const -{ - TriangleMesh mesh; - for (const ModelObject *o : this->objects) - mesh.merge(o->mesh()); - return mesh; -} - -static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out) -{ - if (sizes.empty()) - // return if the list is empty or the following call to BoundingBoxf constructor will lead to a crash - return true; - - // we supply unscaled data to arrange() - bool result = Slic3r::Geometry::arrange( - sizes.size(), // number of parts - BoundingBoxf(sizes).max, // width and height of a single cell - dist, // distance between cells - bb, // bounding box of the area to fill - out // output positions - ); - - if (!result && bb != nullptr) { - // Try to arrange again ignoring bb - result = Slic3r::Geometry::arrange( - sizes.size(), // number of parts - BoundingBoxf(sizes).max, // width and height of a single cell - dist, // distance between cells - nullptr, // bounding box of the area to fill - out // output positions - ); - } - - return result; -} - -/* arrange objects preserving their instance count - but altering their instance positions */ -bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) -{ - // get the (transformed) size of each instance so that we take - // into account their different transformations when packing - Pointfs instance_sizes; - Pointfs instance_centers; - for (const ModelObject *o : this->objects) - for (size_t i = 0; i < o->instances.size(); ++ i) { - // an accurate snug bounding box around the transformed mesh. - BoundingBoxf3 bbox(o->instance_bounding_box(i, true)); - instance_sizes.push_back(bbox.size()); - instance_centers.push_back(bbox.center()); - } - - Pointfs positions; - if (! _arrange(instance_sizes, dist, bb, positions)) - return false; - - size_t idx = 0; - for (ModelObject *o : this->objects) { - for (ModelInstance *i : o->instances) { - i->offset = positions[idx] - instance_centers[idx]; - ++ idx; - } - o->invalidate_bounding_box(); - } - - return true; -} - -// Duplicate the entire model preserving instance relative positions. -void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) -{ - Pointfs model_sizes(copies_num-1, this->bounding_box().size()); - Pointfs positions; - if (! _arrange(model_sizes, dist, bb, positions)) - CONFESS("Cannot duplicate part as the resulting objects would not fit on the print bed.\n"); - - // note that this will leave the object count unaltered - - for (ModelObject *o : this->objects) { - // make a copy of the pointers in order to avoid recursion when appending their copies - ModelInstancePtrs instances = o->instances; - for (const ModelInstance *i : instances) { - for (const Pointf &pos : positions) { - ModelInstance *instance = o->add_instance(*i); - instance->offset.translate(pos); - } - } - o->invalidate_bounding_box(); - } -} - -/* this will append more instances to each object - and then automatically rearrange everything */ -void Model::duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) -{ - for (ModelObject *o : this->objects) { - // make a copy of the pointers in order to avoid recursion when appending their copies - ModelInstancePtrs instances = o->instances; - for (const ModelInstance *i : instances) - for (size_t k = 2; k <= copies_num; ++ k) - o->add_instance(*i); - } - - this->arrange_objects(dist, bb); -} - -void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist) -{ - if (this->objects.size() > 1) throw "Grid duplication is not supported with multiple objects"; - if (this->objects.empty()) throw "No objects!"; - - ModelObject* object = this->objects.front(); - object->clear_instances(); - - Sizef3 size = object->bounding_box().size(); - - for (size_t x_copy = 1; x_copy <= x; ++x_copy) { - for (size_t y_copy = 1; y_copy <= y; ++y_copy) { - ModelInstance* instance = object->add_instance(); - instance->offset.x = (size.x + dist) * (x_copy-1); - instance->offset.y = (size.y + dist) * (y_copy-1); - } - } -} - -bool Model::looks_like_multipart_object() const -{ - if (this->objects.size() <= 1) - return false; - double zmin = std::numeric_limits::max(); - for (const ModelObject *obj : this->objects) { - if (obj->volumes.size() > 1 || obj->config.keys().size() > 1) - return false; - for (const ModelVolume *vol : obj->volumes) { - double zmin_this = vol->mesh.bounding_box().min.z; - if (zmin == std::numeric_limits::max()) - zmin = zmin_this; - else if (std::abs(zmin - zmin_this) > EPSILON) - // The volumes don't share zmin. - return true; - } - } - return false; -} - -void Model::convert_multipart_object(unsigned int max_extruders) -{ - if (this->objects.empty()) - return; - - ModelObject* object = new ModelObject(this); - object->input_file = this->objects.front()->input_file; - - reset_auto_extruder_id(); - - for (const ModelObject* o : this->objects) - for (const ModelVolume* v : o->volumes) - { - ModelVolume* new_v = object->add_volume(*v); - if (new_v != nullptr) - { - new_v->name = o->name; - new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string(max_extruders)); - } - } - - for (const ModelInstance* i : this->objects.front()->instances) - object->add_instance(*i); - - this->clear_objects(); - this->objects.push_back(object); -} - -void Model::adjust_min_z() -{ - if (objects.empty()) - return; - - if (bounding_box().min.z < 0.0) - { - for (ModelObject* obj : objects) - { - if (obj != nullptr) - { - coordf_t obj_min_z = obj->bounding_box().min.z; - if (obj_min_z < 0.0) - obj->translate(0.0, 0.0, -obj_min_z); - } - } - } -} - -unsigned int Model::get_auto_extruder_id(unsigned int max_extruders) -{ - unsigned int id = s_auto_extruder_id; - if (id > max_extruders) { - // The current counter is invalid, likely due to switching the printer profiles - // to a profile with a lower number of extruders. - reset_auto_extruder_id(); - id = s_auto_extruder_id; - } else if (++s_auto_extruder_id > max_extruders) { - reset_auto_extruder_id(); - } - return id; -} - -std::string Model::get_auto_extruder_id_as_string(unsigned int max_extruders) -{ - char str_extruder[64]; - sprintf(str_extruder, "%ud", get_auto_extruder_id(max_extruders)); - return str_extruder; -} - -void Model::reset_auto_extruder_id() -{ - s_auto_extruder_id = 1; -} - -ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) : - name(other.name), - input_file(other.input_file), - instances(), - volumes(), - config(other.config), - layer_height_ranges(other.layer_height_ranges), - layer_height_profile(other.layer_height_profile), - layer_height_profile_valid(other.layer_height_profile_valid), - origin_translation(other.origin_translation), - m_bounding_box(other.m_bounding_box), - m_bounding_box_valid(other.m_bounding_box_valid), - m_model(model) -{ - if (copy_volumes) { - this->volumes.reserve(other.volumes.size()); - for (ModelVolumePtrs::const_iterator i = other.volumes.begin(); i != other.volumes.end(); ++i) - this->add_volume(**i); - } - - this->instances.reserve(other.instances.size()); - for (ModelInstancePtrs::const_iterator i = other.instances.begin(); i != other.instances.end(); ++i) - this->add_instance(**i); -} - -ModelObject& ModelObject::operator=(ModelObject other) -{ - this->swap(other); - return *this; -} - -void ModelObject::swap(ModelObject &other) -{ - std::swap(this->input_file, other.input_file); - std::swap(this->instances, other.instances); - std::swap(this->volumes, other.volumes); - std::swap(this->config, other.config); - std::swap(this->layer_height_ranges, other.layer_height_ranges); - std::swap(this->layer_height_profile, other.layer_height_profile); - std::swap(this->layer_height_profile_valid, other.layer_height_profile_valid); - std::swap(this->origin_translation, other.origin_translation); - std::swap(m_bounding_box, other.m_bounding_box); - std::swap(m_bounding_box_valid, other.m_bounding_box_valid); -} - -ModelObject::~ModelObject() -{ - this->clear_volumes(); - this->clear_instances(); -} - -ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh) -{ - ModelVolume* v = new ModelVolume(this, mesh); - this->volumes.push_back(v); - this->invalidate_bounding_box(); - return v; -} - -ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh) -{ - ModelVolume* v = new ModelVolume(this, std::move(mesh)); - this->volumes.push_back(v); - this->invalidate_bounding_box(); - return v; -} - -ModelVolume* ModelObject::add_volume(const ModelVolume &other) -{ - ModelVolume* v = new ModelVolume(this, other); - this->volumes.push_back(v); - this->invalidate_bounding_box(); - return v; -} - -void ModelObject::delete_volume(size_t idx) -{ - ModelVolumePtrs::iterator i = this->volumes.begin() + idx; - delete *i; - this->volumes.erase(i); - this->invalidate_bounding_box(); -} - -void ModelObject::clear_volumes() -{ - for (ModelVolume *v : this->volumes) - delete v; - this->volumes.clear(); - this->invalidate_bounding_box(); -} - -ModelInstance* ModelObject::add_instance() -{ - ModelInstance* i = new ModelInstance(this); - this->instances.push_back(i); - this->invalidate_bounding_box(); - return i; -} - -ModelInstance* ModelObject::add_instance(const ModelInstance &other) -{ - ModelInstance* i = new ModelInstance(this, other); - this->instances.push_back(i); - this->invalidate_bounding_box(); - return i; -} - -void ModelObject::delete_instance(size_t idx) -{ - ModelInstancePtrs::iterator i = this->instances.begin() + idx; - delete *i; - this->instances.erase(i); - this->invalidate_bounding_box(); -} - -void ModelObject::delete_last_instance() -{ - this->delete_instance(this->instances.size() - 1); -} - -void ModelObject::clear_instances() -{ - for (ModelInstance *i : this->instances) - delete i; - this->instances.clear(); - this->invalidate_bounding_box(); -} - -// Returns the bounding box of the transformed instances. -// This bounding box is approximate and not snug. -const BoundingBoxf3& ModelObject::bounding_box() const -{ - if (! m_bounding_box_valid) { - BoundingBoxf3 raw_bbox; - for (const ModelVolume *v : this->volumes) - if (v->is_model_part()) - // mesh.bounding_box() returns a cached value. - raw_bbox.merge(v->mesh.bounding_box()); - BoundingBoxf3 bb; - for (const ModelInstance *i : this->instances) - bb.merge(i->transform_bounding_box(raw_bbox)); - m_bounding_box = bb; - m_bounding_box_valid = true; - } - return m_bounding_box; -} - -// A mesh containing all transformed instances of this object. -TriangleMesh ModelObject::mesh() const -{ - TriangleMesh mesh; - TriangleMesh raw_mesh = this->raw_mesh(); - for (const ModelInstance *i : this->instances) { - TriangleMesh m = raw_mesh; - i->transform_mesh(&m); - mesh.merge(m); - } - return mesh; -} - -// Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes. -// Currently used by ModelObject::mesh(), to calculate the 2D envelope for 2D platter -// and to display the object statistics at ModelObject::print_info(). -TriangleMesh ModelObject::raw_mesh() const -{ - TriangleMesh mesh; - for (const ModelVolume *v : this->volumes) - if (v->is_model_part()) - mesh.merge(v->mesh); - return mesh; -} - -// A transformed snug bounding box around the non-modifier object volumes, without the translation applied. -// This bounding box is only used for the actual slicing. -BoundingBoxf3 ModelObject::raw_bounding_box() const -{ - BoundingBoxf3 bb; - for (const ModelVolume *v : this->volumes) - if (v->is_model_part()) { - if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances"); - bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true)); - } - return bb; -} - -// This returns an accurate snug bounding box of the transformed object instance, without the translation applied. -BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_translate) const -{ - BoundingBoxf3 bb; - for (ModelVolume *v : this->volumes) - if (v->is_model_part()) - bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate)); - return bb; -} - -void ModelObject::center_around_origin() -{ - // calculate the displacements needed to - // center this object around the origin - BoundingBoxf3 bb; - for (ModelVolume *v : this->volumes) - if (v->is_model_part()) - bb.merge(v->mesh.bounding_box()); - - // first align to origin on XYZ - Vectorf3 vector(-bb.min.x, -bb.min.y, -bb.min.z); - - // then center it on XY - Sizef3 size = bb.size(); - vector.x -= size.x/2; - vector.y -= size.y/2; - - this->translate(vector); - this->origin_translation.translate(vector); - - if (!this->instances.empty()) { - for (ModelInstance *i : this->instances) { - // apply rotation and scaling to vector as well before translating instance, - // in order to leave final position unaltered - Vectorf3 v = vector.negative(); - v.rotate(i->rotation); - v.scale(i->scaling_factor); - i->offset.translate(v.x, v.y); - } - this->invalidate_bounding_box(); - } -} - -void ModelObject::translate(coordf_t x, coordf_t y, coordf_t z) -{ - for (ModelVolume *v : this->volumes) - { - v->mesh.translate(float(x), float(y), float(z)); - v->m_convex_hull.translate(float(x), float(y), float(z)); - } - - if (m_bounding_box_valid) - m_bounding_box.translate(x, y, z); -} - -void ModelObject::scale(const Pointf3 &versor) -{ - for (ModelVolume *v : this->volumes) - { - v->mesh.scale(versor); - v->m_convex_hull.scale(versor); - } - // reset origin translation since it doesn't make sense anymore - this->origin_translation = Pointf3(0,0,0); - this->invalidate_bounding_box(); -} - -void ModelObject::rotate(float angle, const Pointf3& axis) -{ - for (ModelVolume *v : this->volumes) - { - v->mesh.rotate(angle, axis); - v->m_convex_hull.rotate(angle, axis); - } - - center_around_origin(); - - this->origin_translation = Pointf3(0, 0, 0); - this->invalidate_bounding_box(); -} - -void ModelObject::transform(const float* matrix3x4) -{ - if (matrix3x4 == nullptr) - return; - - for (ModelVolume* v : volumes) - { - v->mesh.transform(matrix3x4); - v->m_convex_hull.transform(matrix3x4); - } - - origin_translation = Pointf3(0.0, 0.0, 0.0); - invalidate_bounding_box(); -} - -void ModelObject::mirror(const Axis &axis) -{ - for (ModelVolume *v : this->volumes) - { - v->mesh.mirror(axis); - v->m_convex_hull.mirror(axis); - } - - this->origin_translation = Pointf3(0, 0, 0); - this->invalidate_bounding_box(); -} - -size_t ModelObject::materials_count() const -{ - std::set material_ids; - for (const ModelVolume *v : this->volumes) - material_ids.insert(v->material_id()); - return material_ids.size(); -} - -size_t ModelObject::facets_count() const -{ - size_t num = 0; - for (const ModelVolume *v : this->volumes) - if (v->is_model_part()) - num += v->mesh.stl.stats.number_of_facets; - return num; -} - -bool ModelObject::needed_repair() const -{ - for (const ModelVolume *v : this->volumes) - if (v->is_model_part() && v->mesh.needed_repair()) - return true; - return false; -} - -void ModelObject::cut(coordf_t z, Model* model) const -{ - // clone this one to duplicate instances, materials etc. - ModelObject* upper = model->add_object(*this); - ModelObject* lower = model->add_object(*this); - upper->clear_volumes(); - lower->clear_volumes(); - upper->input_file = ""; - lower->input_file = ""; - - for (ModelVolume *volume : this->volumes) { - if (! volume->is_model_part()) { - // don't cut modifiers - upper->add_volume(*volume); - lower->add_volume(*volume); - } else { - TriangleMesh upper_mesh, lower_mesh; - TriangleMeshSlicer tms(&volume->mesh); - tms.cut(z, &upper_mesh, &lower_mesh); - - upper_mesh.repair(); - lower_mesh.repair(); - upper_mesh.reset_repair_stats(); - lower_mesh.reset_repair_stats(); - - if (upper_mesh.facets_count() > 0) { - ModelVolume* vol = upper->add_volume(upper_mesh); - vol->name = volume->name; - vol->config = volume->config; - vol->set_material(volume->material_id(), *volume->material()); - } - if (lower_mesh.facets_count() > 0) { - ModelVolume* vol = lower->add_volume(lower_mesh); - vol->name = volume->name; - vol->config = volume->config; - vol->set_material(volume->material_id(), *volume->material()); - } - } - } -} - -void ModelObject::split(ModelObjectPtrs* new_objects) -{ - if (this->volumes.size() > 1) { - // We can't split meshes if there's more than one volume, because - // we can't group the resulting meshes by object afterwards - new_objects->push_back(this); - return; - } - - ModelVolume* volume = this->volumes.front(); - TriangleMeshPtrs meshptrs = volume->mesh.split(); - for (TriangleMesh *mesh : meshptrs) { - // Snap the mesh to Z=0. - float z0 = FLT_MAX; - - mesh->repair(); - - ModelObject* new_object = m_model->add_object(*this, false); - new_object->input_file = ""; - ModelVolume* new_volume = new_object->add_volume(*mesh); - new_volume->name = volume->name; - new_volume->config = volume->config; - new_volume->set_type(volume->type()); - new_volume->material_id(volume->material_id()); - - new_objects->push_back(new_object); - delete mesh; - } - - return; -} - -void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) -{ - for (const ModelVolume* vol : this->volumes) - { - if (vol->is_model_part()) - { - for (ModelInstance* inst : this->instances) - { - std::vector world_mat(UNIT_MATRIX, std::end(UNIT_MATRIX)); - Eigen::Transform m = Eigen::Transform::Identity(); - m.translate(Eigen::Vector3f((float)inst->offset.x, (float)inst->offset.y, 0.0f)); - m.rotate(Eigen::AngleAxisf(inst->rotation, Eigen::Vector3f::UnitZ())); - m.scale(inst->scaling_factor); - ::memcpy((void*)world_mat.data(), (const void*)m.data(), 16 * sizeof(float)); - - BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(world_mat); - - if (print_volume.contains(bb)) - inst->print_volume_state = ModelInstance::PVS_Inside; - else if (print_volume.intersects(bb)) - inst->print_volume_state = ModelInstance::PVS_Partly_Outside; - else - inst->print_volume_state = ModelInstance::PVS_Fully_Outside; - } - } - } -} - -void ModelObject::print_info() const -{ - using namespace std; - cout << fixed; - boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl; - - TriangleMesh mesh = this->raw_mesh(); - mesh.check_topology(); - BoundingBoxf3 bb = mesh.bounding_box(); - Sizef3 size = bb.size(); - cout << "size_x = " << size.x << endl; - cout << "size_y = " << size.y << endl; - cout << "size_z = " << size.z << endl; - cout << "min_x = " << bb.min.x << endl; - cout << "min_y = " << bb.min.y << endl; - cout << "min_z = " << bb.min.z << endl; - cout << "max_x = " << bb.max.x << endl; - cout << "max_y = " << bb.max.y << endl; - cout << "max_z = " << bb.max.z << endl; - cout << "number_of_facets = " << mesh.stl.stats.number_of_facets << endl; - cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl; - - mesh.repair(); // this calculates number_of_parts - if (mesh.needed_repair()) { - mesh.repair(); - if (mesh.stl.stats.degenerate_facets > 0) - cout << "degenerate_facets = " << mesh.stl.stats.degenerate_facets << endl; - if (mesh.stl.stats.edges_fixed > 0) - cout << "edges_fixed = " << mesh.stl.stats.edges_fixed << endl; - if (mesh.stl.stats.facets_removed > 0) - cout << "facets_removed = " << mesh.stl.stats.facets_removed << endl; - if (mesh.stl.stats.facets_added > 0) - cout << "facets_added = " << mesh.stl.stats.facets_added << endl; - if (mesh.stl.stats.facets_reversed > 0) - cout << "facets_reversed = " << mesh.stl.stats.facets_reversed << endl; - if (mesh.stl.stats.backwards_edges > 0) - cout << "backwards_edges = " << mesh.stl.stats.backwards_edges << endl; - } - cout << "number_of_parts = " << mesh.stl.stats.number_of_parts << endl; - cout << "volume = " << mesh.volume() << endl; -} - -void ModelVolume::material_id(t_model_material_id material_id) -{ - this->_material_id = material_id; - - // ensure this->_material_id references an existing material - (void)this->object->get_model()->add_material(material_id); -} - -ModelMaterial* ModelVolume::material() const -{ - return this->object->get_model()->get_material(this->_material_id); -} - -void ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material) -{ - this->_material_id = material_id; - (void)this->object->get_model()->add_material(material_id, material); -} - -ModelMaterial* ModelVolume::assign_unique_material() -{ - Model* model = this->get_object()->get_model(); - - // as material-id "0" is reserved by the AMF spec we start from 1 - this->_material_id = 1 + model->materials.size(); // watchout for implicit cast - return model->add_material(this->_material_id); -} - -void ModelVolume::calculate_convex_hull() -{ - m_convex_hull = mesh.convex_hull_3d(); -} - -const TriangleMesh& ModelVolume::get_convex_hull() const -{ - return m_convex_hull; -} - -TriangleMesh& ModelVolume::get_convex_hull() -{ - return m_convex_hull; -} - -ModelVolume::Type ModelVolume::type_from_string(const std::string &s) -{ - // Legacy support - if (s == "0") - return MODEL_PART; - if (s == "1") - return PARAMETER_MODIFIER; - // New type (supporting the support enforcers & blockers) - if (s == "ModelPart") - return MODEL_PART; - if (s == "ParameterModifier") - return PARAMETER_MODIFIER; - if (s == "SupportEnforcer") - return SUPPORT_ENFORCER; - if (s == "SupportBlocker") - return SUPPORT_BLOCKER; -} - -std::string ModelVolume::type_to_string(const Type t) -{ - switch (t) { - case MODEL_PART: return "ModelPart"; - case PARAMETER_MODIFIER: return "ParameterModifier"; - case SUPPORT_ENFORCER: return "SupportEnforcer"; - case SUPPORT_BLOCKER: return "SupportBlocker"; - default: - assert(false); - return "ModelPart"; - } -} - -// Split this volume, append the result to the object owning this volume. -// Return the number of volumes created from this one. -// This is useful to assign different materials to different volumes of an object. -size_t ModelVolume::split(unsigned int max_extruders) -{ - TriangleMeshPtrs meshptrs = this->mesh.split(); - if (meshptrs.size() <= 1) { - delete meshptrs.front(); - return 1; - } - - size_t idx = 0; - size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin(); - std::string name = this->name; - - Model::reset_auto_extruder_id(); - - for (TriangleMesh *mesh : meshptrs) { - mesh->repair(); - if (idx == 0) - this->mesh = std::move(*mesh); - else - this->object->volumes.insert(this->object->volumes.begin() + (++ ivolume), new ModelVolume(object, *this, std::move(*mesh))); - char str_idx[64]; - sprintf(str_idx, "_%d", idx + 1); - this->object->volumes[ivolume]->name = name + str_idx; - this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders)); - delete mesh; - ++ idx; - } - - return idx; -} - -void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const -{ - mesh->rotate_z(this->rotation); // rotate around mesh origin - mesh->scale(this->scaling_factor); // scale around mesh origin - if (!dont_translate) - mesh->translate(this->offset.x, this->offset.y, 0); -} - -BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const -{ - // Rotate around mesh origin. - double c = cos(this->rotation); - double s = sin(this->rotation); - BoundingBoxf3 bbox; - for (int i = 0; i < mesh->stl.stats.number_of_facets; ++ i) { - const stl_facet &facet = mesh->stl.facet_start[i]; - for (int j = 0; j < 3; ++ j) { - stl_vertex v = facet.vertex[j]; - double xold = v.x; - double yold = v.y; - v.x = float(c * xold - s * yold); - v.y = float(s * xold + c * yold); - bbox.merge(Pointf3(v.x, v.y, v.z)); - } - } - if (! empty(bbox)) { - // Scale the bounding box uniformly. - if (std::abs(this->scaling_factor - 1.) > EPSILON) { - bbox.min.x *= float(this->scaling_factor); - bbox.min.y *= float(this->scaling_factor); - bbox.min.z *= float(this->scaling_factor); - bbox.max.x *= float(this->scaling_factor); - bbox.max.y *= float(this->scaling_factor); - bbox.max.z *= float(this->scaling_factor); - } - // Translate the bounding box. - if (! dont_translate) { - bbox.min.x += float(this->offset.x); - bbox.min.y += float(this->offset.y); - bbox.max.x += float(this->offset.x); - bbox.max.y += float(this->offset.y); - } - } - return bbox; -} - -BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const -{ - Eigen::Transform matrix = Eigen::Transform::Identity(); - if (!dont_translate) - matrix.translate(Eigen::Vector3f((float)offset.x, (float)offset.y, 0.0f)); - - matrix.rotate(Eigen::AngleAxisf(rotation, Eigen::Vector3f::UnitZ())); - matrix.scale(scaling_factor); - - std::vector m(16, 0.0f); - ::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float)); - return bbox.transformed(m); -} - -void ModelInstance::transform_polygon(Polygon* polygon) const -{ - polygon->rotate(this->rotation); // rotate around polygon origin - polygon->scale(this->scaling_factor); // scale around polygon origin -} - -} diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp deleted file mode 100644 index 353dc5cccc..0000000000 --- a/xs/src/libslic3r/Model.hpp +++ /dev/null @@ -1,345 +0,0 @@ -#ifndef slic3r_Model_hpp_ -#define slic3r_Model_hpp_ - -#include "libslic3r.h" -#include "PrintConfig.hpp" -#include "Layer.hpp" -#include "Point.hpp" -#include "TriangleMesh.hpp" -#include "Slicing.hpp" -#include -#include -#include -#include - -namespace Slic3r { - -class Model; -class ModelInstance; -class ModelMaterial; -class ModelObject; -class ModelVolume; -class PresetBundle; - -typedef std::string t_model_material_id; -typedef std::string t_model_material_attribute; -typedef std::map t_model_material_attributes; - -typedef std::map ModelMaterialMap; -typedef std::vector ModelObjectPtrs; -typedef std::vector ModelVolumePtrs; -typedef std::vector ModelInstancePtrs; - -// Material, which may be shared across multiple ModelObjects of a single Model. -class ModelMaterial -{ - friend class Model; -public: - // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose. - t_model_material_attributes attributes; - // Dynamic configuration storage for the object specific configuration values, overriding the global configuration. - DynamicPrintConfig config; - - Model* get_model() const { return m_model; } - void apply(const t_model_material_attributes &attributes) - { this->attributes.insert(attributes.begin(), attributes.end()); } - -private: - // Parent, owning this material. - Model *m_model; - - ModelMaterial(Model *model) : m_model(model) {} - ModelMaterial(Model *model, const ModelMaterial &other) : attributes(other.attributes), config(other.config), m_model(model) {} -}; - -// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), -// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. -// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, -// different rotation and different uniform scaling. -class ModelObject -{ - friend class Model; -public: - std::string name; - std::string input_file; - // Instances of this ModelObject. Each instance defines a shift on the print bed, rotation around the Z axis and a uniform scaling. - // Instances are owned by this ModelObject. - ModelInstancePtrs instances; - // Printable and modifier volumes, each with its material ID and a set of override parameters. - // ModelVolumes are owned by this ModelObject. - ModelVolumePtrs volumes; - // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings. - DynamicPrintConfig config; - // Variation of a layer thickness for spans of Z coordinates. - t_layer_height_ranges layer_height_ranges; - // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. - // The pairs of are packed into a 1D array to simplify handling by the Perl XS. - std::vector layer_height_profile; - // layer_height_profile is initialized when the layer editing mode is entered. - // Only if the user really modified the layer height, layer_height_profile_valid is set - // and used subsequently by the PrintObject. - bool layer_height_profile_valid; - - /* This vector accumulates the total translation applied to the object by the - center_around_origin() method. Callers might want to apply the same translation - to new volumes before adding them to this object in order to preserve alignment - when user expects that. */ - Pointf3 origin_translation; - - Model* get_model() const { return m_model; }; - - ModelVolume* add_volume(const TriangleMesh &mesh); - ModelVolume* add_volume(TriangleMesh &&mesh); - ModelVolume* add_volume(const ModelVolume &volume); - void delete_volume(size_t idx); - void clear_volumes(); - - ModelInstance* add_instance(); - ModelInstance* add_instance(const ModelInstance &instance); - void delete_instance(size_t idx); - void delete_last_instance(); - void clear_instances(); - - // Returns the bounding box of the transformed instances. - // This bounding box is approximate and not snug. - // This bounding box is being cached. - const BoundingBoxf3& bounding_box() const; - void invalidate_bounding_box() { m_bounding_box_valid = false; } - - // A mesh containing all transformed instances of this object. - TriangleMesh mesh() const; - // Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes. - // Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D platter. - TriangleMesh raw_mesh() const; - // A transformed snug bounding box around the non-modifier object volumes, without the translation applied. - // This bounding box is only used for the actual slicing. - BoundingBoxf3 raw_bounding_box() const; - // A snug bounding box around the transformed non-modifier object volumes. - BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const; - void center_around_origin(); - void translate(const Vectorf3 &vector) { this->translate(vector.x, vector.y, vector.z); } - void translate(coordf_t x, coordf_t y, coordf_t z); - void scale(const Pointf3 &versor); - void rotate(float angle, const Pointf3& axis); - void transform(const float* matrix3x4); - void mirror(const Axis &axis); - size_t materials_count() const; - size_t facets_count() const; - bool needed_repair() const; - void cut(coordf_t z, Model* model) const; - void split(ModelObjectPtrs* new_objects); - - void check_instances_print_volume_state(const BoundingBoxf3& print_volume); - - // Print object statistics to console. - void print_info() const; - -private: - ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), m_bounding_box_valid(false) {} - ModelObject(Model *model, const ModelObject &other, bool copy_volumes = true); - ModelObject& operator= (ModelObject other); - void swap(ModelObject &other); - ~ModelObject(); - - // Parent object, owning this ModelObject. - Model *m_model; - // Bounding box, cached. - - mutable BoundingBoxf3 m_bounding_box; - mutable bool m_bounding_box_valid; -}; - -// An object STL, or a modifier volume, over which a different set of parameters shall be applied. -// ModelVolume instances are owned by a ModelObject. -class ModelVolume -{ - friend class ModelObject; - - // The convex hull of this model's mesh. - TriangleMesh m_convex_hull; - -public: - std::string name; - // The triangular model. - TriangleMesh mesh; - // Configuration parameters specific to an object model geometry or a modifier volume, - // overriding the global Slic3r settings and the ModelObject settings. - DynamicPrintConfig config; - - enum Type { - MODEL_TYPE_INVALID = -1, - MODEL_PART = 0, - PARAMETER_MODIFIER, - SUPPORT_ENFORCER, - SUPPORT_BLOCKER, - }; - - // A parent object owning this modifier volume. - ModelObject* get_object() const { return this->object; }; - Type type() const { return m_type; } - void set_type(const Type t) { m_type = t; } - bool is_model_part() const { return m_type == MODEL_PART; } - bool is_modifier() const { return m_type == PARAMETER_MODIFIER; } - bool is_support_enforcer() const { return m_type == SUPPORT_ENFORCER; } - bool is_support_blocker() const { return m_type == SUPPORT_BLOCKER; } - t_model_material_id material_id() const { return this->_material_id; } - void material_id(t_model_material_id material_id); - ModelMaterial* material() const; - void set_material(t_model_material_id material_id, const ModelMaterial &material); - // Split this volume, append the result to the object owning this volume. - // Return the number of volumes created from this one. - // This is useful to assign different materials to different volumes of an object. - size_t split(unsigned int max_extruders); - - ModelMaterial* assign_unique_material(); - - void calculate_convex_hull(); - const TriangleMesh& get_convex_hull() const; - TriangleMesh& get_convex_hull(); - - // Helpers for loading / storing into AMF / 3MF files. - static Type type_from_string(const std::string &s); - static std::string type_to_string(const Type t); - -private: - // Parent object owning this ModelVolume. - ModelObject* object; - // Is it an object to be printed, or a modifier volume? - Type m_type; - t_model_material_id _material_id; - - ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object) - { - if (mesh.stl.stats.number_of_facets > 1) - calculate_convex_hull(); - } - ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {} - ModelVolume(ModelObject *object, const ModelVolume &other) : - name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object) - { - this->material_id(other.material_id()); - } - ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : - name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object) - { - this->material_id(other.material_id()); - if (mesh.stl.stats.number_of_facets > 1) - calculate_convex_hull(); - } -}; - -// A single instance of a ModelObject. -// Knows the affine transformation of an object. -class ModelInstance -{ -public: - enum EPrintVolumeState : unsigned char - { - PVS_Inside, - PVS_Partly_Outside, - PVS_Fully_Outside, - Num_BedStates - }; - - friend class ModelObject; - - double rotation; // Rotation around the Z axis, in radians around mesh center point - double scaling_factor; - Pointf offset; // in unscaled coordinates - - // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state()) - EPrintVolumeState print_volume_state; - - ModelObject* get_object() const { return this->object; } - - // To be called on an external mesh - void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; - // Calculate a bounding box of a transformed mesh. To be called on an external mesh. - BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate = false) const; - // Transform an external bounding box. - BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const; - // To be called on an external polygon. It does not translate the polygon, only rotates and scales. - void transform_polygon(Polygon* polygon) const; - - bool is_printable() const { return print_volume_state == PVS_Inside; } - -private: - // Parent object, owning this instance. - ModelObject* object; - - ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), object(object), print_volume_state(PVS_Inside) {} - ModelInstance(ModelObject *object, const ModelInstance &other) : - rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {} -}; - - -// The print bed content. -// Description of a triangular model with multiple materials, multiple instances with various affine transformations -// and with multiple modifier meshes. -// A model groups multiple objects, each object having possibly multiple instances, -// all objects may share mutliple materials. -class Model -{ - static unsigned int s_auto_extruder_id; - -public: - // Materials are owned by a model and referenced by objects through t_model_material_id. - // Single material may be shared by multiple models. - ModelMaterialMap materials; - // Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation). - ModelObjectPtrs objects; - - Model() {} - Model(const Model &other); - Model& operator= (Model other); - void swap(Model &other); - ~Model() { this->clear_objects(); this->clear_materials(); } - - static Model read_from_file(const std::string &input_file, bool add_default_instances = true); - static Model read_from_archive(const std::string &input_file, PresetBundle* bundle, bool add_default_instances = true); - - ModelObject* add_object(); - ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh); - ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh); - ModelObject* add_object(const ModelObject &other, bool copy_volumes = true); - void delete_object(size_t idx); - void delete_object(ModelObject* object); - void clear_objects(); - - ModelMaterial* add_material(t_model_material_id material_id); - ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other); - ModelMaterial* get_material(t_model_material_id material_id) { - ModelMaterialMap::iterator i = this->materials.find(material_id); - return (i == this->materials.end()) ? nullptr : i->second; - } - - void delete_material(t_model_material_id material_id); - void clear_materials(); - bool add_default_instances(); - // Returns approximate axis aligned bounding box of this model - BoundingBoxf3 bounding_box() const; - void center_instances_around_point(const Pointf &point); - void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); } - TriangleMesh mesh() const; - bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL); - // Croaks if the duplicated objects do not fit the print bed. - void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); - void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); - void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); - - bool looks_like_multipart_object() const; - void convert_multipart_object(unsigned int max_extruders); - - // Ensures that the min z of the model is not negative - void adjust_min_z(); - - void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); } - - static unsigned int get_auto_extruder_id(unsigned int max_extruders); - static std::string get_auto_extruder_id_as_string(unsigned int max_extruders); - static void reset_auto_extruder_id(); -}; - -} - -#endif diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp deleted file mode 100644 index 349b00bb67..0000000000 --- a/xs/src/libslic3r/Point.cpp +++ /dev/null @@ -1,401 +0,0 @@ -#include "Point.hpp" -#include "Line.hpp" -#include "MultiPoint.hpp" -#include "Int128.hpp" -#include -#include - -namespace Slic3r { - -Point::Point(double x, double y) -{ - this->x = lrint(x); - this->y = lrint(y); -} - -std::string -Point::wkt() const -{ - std::ostringstream ss; - ss << "POINT(" << this->x << " " << this->y << ")"; - return ss.str(); -} - -std::string -Point::dump_perl() const -{ - std::ostringstream ss; - ss << "[" << this->x << "," << this->y << "]"; - return ss.str(); -} - -void -Point::scale(double factor) -{ - this->x *= factor; - this->y *= factor; -} - -void -Point::translate(double x, double y) -{ - this->x += x; - this->y += y; -} - -void -Point::translate(const Vector &vector) -{ - this->translate(vector.x, vector.y); -} - -void -Point::rotate(double angle) -{ - double cur_x = (double)this->x; - double cur_y = (double)this->y; - double s = sin(angle); - double c = cos(angle); - this->x = (coord_t)round(c * cur_x - s * cur_y); - this->y = (coord_t)round(c * cur_y + s * cur_x); -} - -void -Point::rotate(double angle, const Point ¢er) -{ - double cur_x = (double)this->x; - double cur_y = (double)this->y; - double s = sin(angle); - double c = cos(angle); - double dx = cur_x - (double)center.x; - double dy = cur_y - (double)center.y; - this->x = (coord_t)round( (double)center.x + c * dx - s * dy ); - this->y = (coord_t)round( (double)center.y + c * dy + s * dx ); -} - -bool -Point::coincides_with_epsilon(const Point &point) const -{ - return std::abs(this->x - point.x) < SCALED_EPSILON && std::abs(this->y - point.y) < SCALED_EPSILON; -} - -int -Point::nearest_point_index(const Points &points) const -{ - PointConstPtrs p; - p.reserve(points.size()); - for (Points::const_iterator it = points.begin(); it != points.end(); ++it) - p.push_back(&*it); - return this->nearest_point_index(p); -} - -int Point::nearest_point_index(const PointConstPtrs &points) const -{ - int idx = -1; - double distance = -1; // double because long is limited to 2147483647 on some platforms and it's not enough - - for (PointConstPtrs::const_iterator it = points.begin(); it != points.end(); ++it) { - /* If the X distance of the candidate is > than the total distance of the - best previous candidate, we know we don't want it */ - double d = sqr(this->x - (*it)->x); - if (distance != -1 && d > distance) continue; - - /* If the Y distance of the candidate is > than the total distance of the - best previous candidate, we know we don't want it */ - d += sqr(this->y - (*it)->y); - if (distance != -1 && d > distance) continue; - - idx = it - points.begin(); - distance = d; - - if (distance < EPSILON) break; - } - - return idx; -} - -int -Point::nearest_point_index(const PointPtrs &points) const -{ - PointConstPtrs p; - p.reserve(points.size()); - for (PointPtrs::const_iterator it = points.begin(); it != points.end(); ++it) - p.push_back(*it); - return this->nearest_point_index(p); -} - -bool -Point::nearest_point(const Points &points, Point* point) const -{ - int idx = this->nearest_point_index(points); - if (idx == -1) return false; - *point = points.at(idx); - return true; -} - -/* distance to the closest point of line */ -double -Point::distance_to(const Line &line) const -{ - const double dx = line.b.x - line.a.x; - const double dy = line.b.y - line.a.y; - - const double l2 = dx*dx + dy*dy; // avoid a sqrt - if (l2 == 0.0) return this->distance_to(line.a); // line.a == line.b case - - // Consider the line extending the segment, parameterized as line.a + t (line.b - line.a). - // We find projection of this point onto the line. - // It falls where t = [(this-line.a) . (line.b-line.a)] / |line.b-line.a|^2 - const double t = ((this->x - line.a.x) * dx + (this->y - line.a.y) * dy) / l2; - if (t < 0.0) return this->distance_to(line.a); // beyond the 'a' end of the segment - else if (t > 1.0) return this->distance_to(line.b); // beyond the 'b' end of the segment - Point projection( - line.a.x + t * dx, - line.a.y + t * dy - ); - return this->distance_to(projection); -} - -double -Point::perp_distance_to(const Line &line) const -{ - if (line.a.coincides_with(line.b)) return this->distance_to(line.a); - - double n = (double)(line.b.x - line.a.x) * (double)(line.a.y - this->y) - - (double)(line.a.x - this->x) * (double)(line.b.y - line.a.y); - - return std::abs(n) / line.length(); -} - -/* Three points are a counter-clockwise turn if ccw > 0, clockwise if - * ccw < 0, and collinear if ccw = 0 because ccw is a determinant that - * gives the signed area of the triangle formed by p1, p2 and this point. - * In other words it is the 2D cross product of p1-p2 and p1-this, i.e. - * z-component of their 3D cross product. - * We return double because it must be big enough to hold 2*max(|coordinate|)^2 - */ -double -Point::ccw(const Point &p1, const Point &p2) const -{ - return (double)(p2.x - p1.x)*(double)(this->y - p1.y) - (double)(p2.y - p1.y)*(double)(this->x - p1.x); -} - -double -Point::ccw(const Line &line) const -{ - return this->ccw(line.a, line.b); -} - -// returns the CCW angle between this-p1 and this-p2 -// i.e. this assumes a CCW rotation from p1 to p2 around this -double -Point::ccw_angle(const Point &p1, const Point &p2) const -{ - double angle = atan2(p1.x - this->x, p1.y - this->y) - - atan2(p2.x - this->x, p2.y - this->y); - - // we only want to return only positive angles - return angle <= 0 ? angle + 2*PI : angle; -} - -Point -Point::projection_onto(const MultiPoint &poly) const -{ - Point running_projection = poly.first_point(); - double running_min = this->distance_to(running_projection); - - Lines lines = poly.lines(); - for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { - Point point_temp = this->projection_onto(*line); - if (this->distance_to(point_temp) < running_min) { - running_projection = point_temp; - running_min = this->distance_to(running_projection); - } - } - return running_projection; -} - -Point -Point::projection_onto(const Line &line) const -{ - if (line.a.coincides_with(line.b)) return line.a; - - /* - (Ported from VisiLibity by Karl J. Obermeyer) - The projection of point_temp onto the line determined by - line_segment_temp can be represented as an affine combination - expressed in the form projection of - Point = theta*line_segment_temp.first + (1.0-theta)*line_segment_temp.second. - If theta is outside the interval [0,1], then one of the Line_Segment's endpoints - must be closest to calling Point. - */ - double lx = (double)(line.b.x - line.a.x); - double ly = (double)(line.b.y - line.a.y); - double theta = ( (double)(line.b.x - this->x)*lx + (double)(line.b.y- this->y)*ly ) - / ( sqr(lx) + sqr(ly) ); - - if (0.0 <= theta && theta <= 1.0) - return theta * line.a + (1.0-theta) * line.b; - - // Else pick closest endpoint. - if (this->distance_to(line.a) < this->distance_to(line.b)) { - return line.a; - } else { - return line.b; - } -} - -Point -Point::negative() const -{ - return Point(-this->x, -this->y); -} - -Vector -Point::vector_to(const Point &point) const -{ - return Vector(point.x - this->x, point.y - this->y); -} - -std::ostream& -operator<<(std::ostream &stm, const Pointf &pointf) -{ - return stm << pointf.x << "," << pointf.y; -} - -double -Pointf::ccw(const Pointf &p1, const Pointf &p2) const -{ - return (double)(p2.x - p1.x)*(double)(this->y - p1.y) - (double)(p2.y - p1.y)*(double)(this->x - p1.x); -} - -std::string -Pointf::wkt() const -{ - std::ostringstream ss; - ss << "POINT(" << this->x << " " << this->y << ")"; - return ss.str(); -} - -std::string -Pointf::dump_perl() const -{ - std::ostringstream ss; - ss << "[" << this->x << "," << this->y << "]"; - return ss.str(); -} - -void -Pointf::scale(double factor) -{ - this->x *= factor; - this->y *= factor; -} - -void -Pointf::translate(double x, double y) -{ - this->x += x; - this->y += y; -} - -void -Pointf::translate(const Vectorf &vector) -{ - this->translate(vector.x, vector.y); -} - -void -Pointf::rotate(double angle) -{ - double cur_x = this->x; - double cur_y = this->y; - double s = sin(angle); - double c = cos(angle); - this->x = c * cur_x - s * cur_y; - this->y = c * cur_y + s * cur_x; -} - -void -Pointf::rotate(double angle, const Pointf ¢er) -{ - double cur_x = this->x; - double cur_y = this->y; - double s = sin(angle); - double c = cos(angle); - double dx = cur_x - center.x; - double dy = cur_y - center.y; - this->x = center.x + c * dx - s * dy; - this->y = center.y + c * dy + s * dx; -} - -Pointf -Pointf::negative() const -{ - return Pointf(-this->x, -this->y); -} - -Vectorf -Pointf::vector_to(const Pointf &point) const -{ - return Vectorf(point.x - this->x, point.y - this->y); -} - -void -Pointf3::scale(double factor) -{ - Pointf::scale(factor); - this->z *= factor; -} - -void -Pointf3::translate(const Vectorf3 &vector) -{ - this->translate(vector.x, vector.y, vector.z); -} - -void -Pointf3::translate(double x, double y, double z) -{ - Pointf::translate(x, y); - this->z += z; -} - -double -Pointf3::distance_to(const Pointf3 &point) const -{ - double dx = ((double)point.x - this->x); - double dy = ((double)point.y - this->y); - double dz = ((double)point.z - this->z); - return sqrt(dx*dx + dy*dy + dz*dz); -} - -Pointf3 -Pointf3::negative() const -{ - return Pointf3(-this->x, -this->y, -this->z); -} - -Vectorf3 -Pointf3::vector_to(const Pointf3 &point) const -{ - return Vectorf3(point.x - this->x, point.y - this->y, point.z - this->z); -} - -namespace int128 { - -int orient(const Point &p1, const Point &p2, const Point &p3) -{ - Slic3r::Vector v1(p2 - p1); - Slic3r::Vector v2(p3 - p1); - return Int128::sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y); -} - -int cross(const Point &v1, const Point &v2) -{ - return Int128::sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y); -} - -} - -} diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp deleted file mode 100644 index a8c732fd7e..0000000000 --- a/xs/src/libslic3r/Point.hpp +++ /dev/null @@ -1,357 +0,0 @@ -#ifndef slic3r_Point_hpp_ -#define slic3r_Point_hpp_ - -#include "libslic3r.h" -#include -#include -#include -#include -#include - -namespace Slic3r { - -class Line; -class Linef; -class MultiPoint; -class Point; -class Point3; -class Pointf; -class Pointf3; -typedef Point Vector; -typedef Point3 Vector3; -typedef Pointf Vectorf; -typedef Pointf3 Vectorf3; -typedef std::vector Points; -typedef std::vector PointPtrs; -typedef std::vector PointConstPtrs; -typedef std::vector Points3; -typedef std::vector Pointfs; -typedef std::vector Pointf3s; - -class Point -{ -public: - typedef coord_t coord_type; - coord_t x; - coord_t y; - Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {}; - Point(int64_t _x, int64_t _y): x(coord_t(_x)), y(coord_t(_y)) {}; // for Clipper - Point(double x, double y); - static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); } - - bool operator==(const Point& rhs) const { return this->x == rhs.x && this->y == rhs.y; } - bool operator!=(const Point& rhs) const { return ! (*this == rhs); } - bool operator<(const Point& rhs) const { return this->x < rhs.x || (this->x == rhs.x && this->y < rhs.y); } - - Point& operator+=(const Point& rhs) { this->x += rhs.x; this->y += rhs.y; return *this; } - Point& operator-=(const Point& rhs) { this->x -= rhs.x; this->y -= rhs.y; return *this; } - Point& operator*=(const coord_t& rhs) { this->x *= rhs; this->y *= rhs; return *this; } - - std::string wkt() const; - std::string dump_perl() const; - void scale(double factor); - void translate(double x, double y); - void translate(const Vector &vector); - void rotate(double angle); - void rotate(double angle, const Point ¢er); - Point rotated(double angle) const { Point res(*this); res.rotate(angle); return res; } - Point rotated(double angle, const Point ¢er) const { Point res(*this); res.rotate(angle, center); return res; } - bool coincides_with(const Point &point) const { return this->x == point.x && this->y == point.y; } - bool coincides_with_epsilon(const Point &point) const; - int nearest_point_index(const Points &points) const; - int nearest_point_index(const PointConstPtrs &points) const; - int nearest_point_index(const PointPtrs &points) const; - bool nearest_point(const Points &points, Point* point) const; - double distance_to(const Point &point) const { return sqrt(distance_to_sq(point)); } - double distance_to_sq(const Point &point) const { double dx = double(point.x - this->x); double dy = double(point.y - this->y); return dx*dx + dy*dy; } - double distance_to(const Line &line) const; - double perp_distance_to(const Line &line) const; - double ccw(const Point &p1, const Point &p2) const; - double ccw(const Line &line) const; - double ccw_angle(const Point &p1, const Point &p2) const; - Point projection_onto(const MultiPoint &poly) const; - Point projection_onto(const Line &line) const; - Point negative() const; - Vector vector_to(const Point &point) const; -}; - -inline Point operator+(const Point& point1, const Point& point2) { return Point(point1.x + point2.x, point1.y + point2.y); } -inline Point operator-(const Point& point1, const Point& point2) { return Point(point1.x - point2.x, point1.y - point2.y); } -inline Point operator*(double scalar, const Point& point2) { return Point(scalar * point2.x, scalar * point2.y); } -inline int64_t cross(const Point &v1, const Point &v2) { return int64_t(v1.x) * int64_t(v2.y) - int64_t(v1.y) * int64_t(v2.x); } -inline int64_t dot(const Point &v1, const Point &v2) { return int64_t(v1.x) * int64_t(v2.x) + int64_t(v1.y) * int64_t(v2.y); } - -namespace int128 { - -// Exact orientation predicate, -// returns +1: CCW, 0: collinear, -1: CW. -int orient(const Point &p1, const Point &p2, const Point &p3); - -// Exact orientation predicate, -// returns +1: CCW, 0: collinear, -1: CW. -int cross(const Point &v1, const Slic3r::Point &v2); -} - -// To be used by std::unordered_map, std::unordered_multimap and friends. -struct PointHash { - size_t operator()(const Point &pt) const { - return std::hash()(pt.x) ^ std::hash()(pt.y); - } -}; - -// A generic class to search for a closest Point in a given radius. -// It uses std::unordered_multimap to implement an efficient 2D spatial hashing. -// The PointAccessor has to return const Point*. -// If a nullptr is returned, it is ignored by the query. -template class ClosestPointInRadiusLookup -{ -public: - ClosestPointInRadiusLookup(coord_t search_radius, PointAccessor point_accessor = PointAccessor()) : - m_search_radius(search_radius), m_point_accessor(point_accessor), m_grid_log2(0) - { - // Resolution of a grid, twice the search radius + some epsilon. - coord_t gridres = 2 * m_search_radius + 4; - m_grid_resolution = gridres; - assert(m_grid_resolution > 0); - assert(m_grid_resolution < (coord_t(1) << 30)); - // Compute m_grid_log2 = log2(m_grid_resolution) - if (m_grid_resolution > 32767) { - m_grid_resolution >>= 16; - m_grid_log2 += 16; - } - if (m_grid_resolution > 127) { - m_grid_resolution >>= 8; - m_grid_log2 += 8; - } - if (m_grid_resolution > 7) { - m_grid_resolution >>= 4; - m_grid_log2 += 4; - } - if (m_grid_resolution > 1) { - m_grid_resolution >>= 2; - m_grid_log2 += 2; - } - if (m_grid_resolution > 0) - ++ m_grid_log2; - m_grid_resolution = 1 << m_grid_log2; - assert(m_grid_resolution >= gridres); - assert(gridres > m_grid_resolution / 2); - } - - void insert(const ValueType &value) { - const Point *pt = m_point_accessor(value); - if (pt != nullptr) - m_map.emplace(std::make_pair(Point(pt->x>>m_grid_log2, pt->y>>m_grid_log2), value)); - } - - void insert(ValueType &&value) { - const Point *pt = m_point_accessor(value); - if (pt != nullptr) - m_map.emplace(std::make_pair(Point(pt->x>>m_grid_log2, pt->y>>m_grid_log2), std::move(value))); - } - - // Erase a data point equal to value. (ValueType has to declare the operator==). - // Returns true if the data point equal to value was found and removed. - bool erase(const ValueType &value) { - const Point *pt = m_point_accessor(value); - if (pt != nullptr) { - // Range of fragment starts around grid_corner, close to pt. - auto range = m_map.equal_range(Point(pt->x>>m_grid_log2, pt->y>>m_grid_log2)); - // Remove the first item. - for (auto it = range.first; it != range.second; ++ it) { - if (it->second == value) { - m_map.erase(it); - return true; - } - } - } - return false; - } - - // Return a pair of - std::pair find(const Point &pt) { - // Iterate over 4 closest grid cells around pt, - // find the closest start point inside these cells to pt. - const ValueType *value_min = nullptr; - double dist_min = std::numeric_limits::max(); - // Round pt to a closest grid_cell corner. - Point grid_corner((pt.x+(m_grid_resolution>>1))>>m_grid_log2, (pt.y+(m_grid_resolution>>1))>>m_grid_log2); - // For four neighbors of grid_corner: - for (coord_t neighbor_y = -1; neighbor_y < 1; ++ neighbor_y) { - for (coord_t neighbor_x = -1; neighbor_x < 1; ++ neighbor_x) { - // Range of fragment starts around grid_corner, close to pt. - auto range = m_map.equal_range(Point(grid_corner.x + neighbor_x, grid_corner.y + neighbor_y)); - // Find the map entry closest to pt. - for (auto it = range.first; it != range.second; ++it) { - const ValueType &value = it->second; - const Point *pt2 = m_point_accessor(value); - if (pt2 != nullptr) { - const double d2 = pt.distance_to_sq(*pt2); - if (d2 < dist_min) { - dist_min = d2; - value_min = &value; - } - } - } - } - } - return (value_min != nullptr && dist_min < coordf_t(m_search_radius) * coordf_t(m_search_radius)) ? - std::make_pair(value_min, dist_min) : - std::make_pair(nullptr, std::numeric_limits::max()); - } - -private: - typedef typename std::unordered_multimap map_type; - PointAccessor m_point_accessor; - map_type m_map; - coord_t m_search_radius; - coord_t m_grid_resolution; - coord_t m_grid_log2; -}; - -class Point3 : public Point -{ -public: - coord_t z; - explicit Point3(coord_t _x = 0, coord_t _y = 0, coord_t _z = 0): Point(_x, _y), z(_z) {}; - static Point3 new_scale(coordf_t x, coordf_t y, coordf_t z) { return Point3(coord_t(scale_(x)), coord_t(scale_(y)), coord_t(scale_(z))); } - bool operator==(const Point3 &rhs) const { return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; } - bool operator!=(const Point3 &rhs) const { return ! (*this == rhs); } - bool coincides_with(const Point3& rhs) const { return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; } -private: - // Hide the following inherited methods: - bool operator==(const Point &rhs) const; - bool operator!=(const Point &rhs) const; -}; - -std::ostream& operator<<(std::ostream &stm, const Pointf &pointf); - -class Pointf -{ -public: - typedef coordf_t coord_type; - coordf_t x; - coordf_t y; - explicit Pointf(coordf_t _x = 0, coordf_t _y = 0): x(_x), y(_y) {}; - static Pointf new_unscale(coord_t x, coord_t y) { - return Pointf(unscale(x), unscale(y)); - }; - static Pointf new_unscale(const Point &p) { - return Pointf(unscale(p.x), unscale(p.y)); - }; - double ccw(const Pointf &p1, const Pointf &p2) const; - std::string wkt() const; - std::string dump_perl() const; - void scale(double factor); - void translate(double x, double y); - void translate(const Vectorf &vector); - void rotate(double angle); - void rotate(double angle, const Pointf ¢er); - Pointf negative() const; - Vectorf vector_to(const Pointf &point) const; - - Pointf& operator+=(const Pointf& rhs) { this->x += rhs.x; this->y += rhs.y; return *this; } - Pointf& operator-=(const Pointf& rhs) { this->x -= rhs.x; this->y -= rhs.y; return *this; } - Pointf& operator*=(const coordf_t& rhs) { this->x *= rhs; this->y *= rhs; return *this; } - - bool operator==(const Pointf &rhs) const { return this->x == rhs.x && this->y == rhs.y; } - bool operator!=(const Pointf &rhs) const { return ! (*this == rhs); } - bool operator< (const Pointf& rhs) const { return this->x < rhs.x || (this->x == rhs.x && this->y < rhs.y); } -}; - -inline Pointf operator+(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x + point2.x, point1.y + point2.y); } -inline Pointf operator-(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x - point2.x, point1.y - point2.y); } -inline Pointf operator*(double scalar, const Pointf& point2) { return Pointf(scalar * point2.x, scalar * point2.y); } -inline Pointf operator*(const Pointf& point2, double scalar) { return Pointf(scalar * point2.x, scalar * point2.y); } -inline coordf_t cross(const Pointf &v1, const Pointf &v2) { return v1.x * v2.y - v1.y * v2.x; } -inline coordf_t dot(const Pointf &v1, const Pointf &v2) { return v1.x * v2.x + v1.y * v2.y; } -inline coordf_t dot(const Pointf &v) { return v.x * v.x + v.y * v.y; } -inline double length(const Vectorf &v) { return sqrt(dot(v)); } -inline double l2(const Vectorf &v) { return dot(v); } -inline Vectorf normalize(const Vectorf& v) -{ - coordf_t len = ::sqrt(sqr(v.x) + sqr(v.y)); - return (len != 0.0) ? 1.0 / len * v : Vectorf(0.0, 0.0); -} - -class Pointf3 : public Pointf -{ -public: - coordf_t z; - explicit Pointf3(coordf_t _x = 0, coordf_t _y = 0, coordf_t _z = 0): Pointf(_x, _y), z(_z) {}; - static Pointf3 new_unscale(coord_t x, coord_t y, coord_t z) { - return Pointf3(unscale(x), unscale(y), unscale(z)); - }; - static Pointf3 new_unscale(const Point3& p) { return Pointf3(unscale(p.x), unscale(p.y), unscale(p.z)); } - void scale(double factor); - void translate(const Vectorf3 &vector); - void translate(double x, double y, double z); - double distance_to(const Pointf3 &point) const; - Pointf3 negative() const; - Vectorf3 vector_to(const Pointf3 &point) const; - - bool operator==(const Pointf3 &rhs) const { return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; } - bool operator!=(const Pointf3 &rhs) const { return ! (*this == rhs); } - -private: - // Hide the following inherited methods: - bool operator==(const Pointf &rhs) const; - bool operator!=(const Pointf &rhs) const; -}; - -inline Pointf3 operator+(const Pointf3& p1, const Pointf3& p2) { return Pointf3(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z); } -inline Pointf3 operator-(const Pointf3& p1, const Pointf3& p2) { return Pointf3(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z); } -inline Pointf3 operator-(const Pointf3& p) { return Pointf3(-p.x, -p.y, -p.z); } -inline Pointf3 operator*(double scalar, const Pointf3& p) { return Pointf3(scalar * p.x, scalar * p.y, scalar * p.z); } -inline Pointf3 operator*(const Pointf3& p, double scalar) { return Pointf3(scalar * p.x, scalar * p.y, scalar * p.z); } -inline Pointf3 cross(const Pointf3& v1, const Pointf3& v2) { return Pointf3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); } -inline coordf_t dot(const Pointf3& v1, const Pointf3& v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } -inline Pointf3 normalize(const Pointf3& v) -{ - coordf_t len = ::sqrt(sqr(v.x) + sqr(v.y) + sqr(v.z)); - return (len != 0.0) ? 1.0 / len * v : Pointf3(0.0, 0.0, 0.0); -} - -template inline TO convert_to(const Point &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y)); } -template inline TO convert_to(const Pointf &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y)); } -template inline TO convert_to(const Point3 &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y), typename TO::coord_type(src.z)); } -template inline TO convert_to(const Pointf3 &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y), typename TO::coord_type(src.z)); } - -} // namespace Slic3r - -// start Boost -#include -#include -namespace boost { namespace polygon { - template <> - struct geometry_concept { typedef point_concept type; }; - - template <> - struct point_traits { - typedef coord_t coordinate_type; - - static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) { - return (orient == HORIZONTAL) ? point.x : point.y; - } - }; - - template <> - struct point_mutable_traits { - typedef coord_t coordinate_type; - static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) { - if (orient == HORIZONTAL) - point.x = value; - else - point.y = value; - } - static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) { - Slic3r::Point retval; - retval.x = x_value; - retval.y = y_value; - return retval; - } - }; -} } -// end Boost - -#endif diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp deleted file mode 100644 index c2fac891d2..0000000000 --- a/xs/src/libslic3r/Print.cpp +++ /dev/null @@ -1,1250 +0,0 @@ -#include "Print.hpp" -#include "BoundingBox.hpp" -#include "ClipperUtils.hpp" -#include "Extruder.hpp" -#include "Flow.hpp" -#include "Geometry.hpp" -#include "I18N.hpp" -#include "SupportMaterial.hpp" -#include "GCode/WipeTowerPrusaMM.hpp" -#include -#include -#include -#include - -//! macro used to mark string used at localization, -//! return same string -#define L(s) Slic3r::I18N::translate(s) - -namespace Slic3r { - -template class PrintState; -template class PrintState; - -void Print::clear_objects() -{ - for (int i = int(this->objects.size())-1; i >= 0; --i) - this->delete_object(i); - for (PrintRegion *region : this->regions) - delete region; - this->regions.clear(); -} - -void Print::delete_object(size_t idx) -{ - // destroy object and remove it from our container - delete this->objects[idx]; - this->objects.erase(this->objects.begin() + idx); - this->invalidate_all_steps(); - // TODO: purge unused regions -} - -void Print::reload_object(size_t /* idx */) -{ - /* TODO: this method should check whether the per-object config and per-material configs - have changed in such a way that regions need to be rearranged or we can just apply - the diff and invalidate something. Same logic as apply_config() - For now we just re-add all objects since we haven't implemented this incremental logic yet. - This should also check whether object volumes (parts) have changed. */ - - // collect all current model objects - ModelObjectPtrs model_objects; - model_objects.reserve(this->objects.size()); - for (PrintObject *object : this->objects) - model_objects.push_back(object->model_object()); - // remove our print objects - this->clear_objects(); - // re-add model objects - for (ModelObject *mo : model_objects) - this->add_model_object(mo); -} - -// Reloads the model instances into the print class. -// The slicing shall not be running as the modified model instances at the print -// are used for the brim & skirt calculation. -// Returns true if the brim or skirt have been invalidated. -bool Print::reload_model_instances() -{ - bool invalidated = false; - for (PrintObject *object : this->objects) - invalidated |= object->reload_model_instances(); - return invalidated; -} - -PrintObjectPtrs Print::get_printable_objects() const -{ - PrintObjectPtrs printable_objects(this->objects); - printable_objects.erase(std::remove_if(printable_objects.begin(), printable_objects.end(), [](PrintObject* o) { return !o->is_printable(); }), printable_objects.end()); - return printable_objects; -} - -PrintRegion* Print::add_region() -{ - regions.push_back(new PrintRegion(this)); - return regions.back(); -} - -// Called by Print::apply_config(). -// This method only accepts PrintConfig option keys. -bool Print::invalidate_state_by_config_options(const std::vector &opt_keys) -{ - if (opt_keys.empty()) - return false; - - // Cache the plenty of parameters, which influence the G-code generator only, - // or they are only notes not influencing the generated G-code. - static std::unordered_set steps_ignore = { - "avoid_crossing_perimeters", - "bed_shape", - "bed_temperature", - "before_layer_gcode", - "between_objects_gcode", - "bridge_acceleration", - "bridge_fan_speed", - "cooling", - "default_acceleration", - "deretract_speed", - "disable_fan_first_layers", - "duplicate_distance", - "end_gcode", - "end_filament_gcode", - "extrusion_axis", - "extruder_clearance_height", - "extruder_clearance_radius", - "extruder_colour", - "extruder_offset", - "extrusion_multiplier", - "fan_always_on", - "fan_below_layer_time", - "filament_colour", - "filament_diameter", - "filament_density", - "filament_notes", - "filament_cost", - "filament_max_volumetric_speed", - "first_layer_acceleration", - "first_layer_bed_temperature", - "first_layer_speed", - "gcode_comments", - "gcode_flavor", - "infill_acceleration", - "layer_gcode", - "min_fan_speed", - "max_fan_speed", - "max_print_height", - "min_print_speed", - "max_print_speed", - "max_volumetric_speed", - "max_volumetric_extrusion_rate_slope_positive", - "max_volumetric_extrusion_rate_slope_negative", - "notes", - "only_retract_when_crossing_perimeters", - "output_filename_format", - "perimeter_acceleration", - "post_process", - "printer_notes", - "retract_before_travel", - "retract_before_wipe", - "retract_layer_change", - "retract_length", - "retract_length_toolchange", - "retract_lift", - "retract_lift_above", - "retract_lift_below", - "retract_restart_extra", - "retract_restart_extra_toolchange", - "retract_speed", - "single_extruder_multi_material_priming", - "slowdown_below_layer_time", - "standby_temperature_delta", - "start_gcode", - "start_filament_gcode", - "toolchange_gcode", - "threads", - "travel_speed", - "use_firmware_retraction", - "use_relative_e_distances", - "use_volumetric_e", - "variable_layer_height", - "wipe", - "wipe_tower_x", - "wipe_tower_y", - "wipe_tower_rotation_angle" - }; - - std::vector steps; - std::vector osteps; - bool invalidated = false; - - for (const t_config_option_key &opt_key : opt_keys) { - if (steps_ignore.find(opt_key) != steps_ignore.end()) { - // These options only affect G-code export or they are just notes without influence on the generated G-code, - // so there is nothing to invalidate. - } else if ( - opt_key == "skirts" - || opt_key == "skirt_height" - || opt_key == "skirt_distance" - || opt_key == "min_skirt_length" - || opt_key == "ooze_prevention") { - steps.emplace_back(psSkirt); - } else if (opt_key == "brim_width") { - steps.emplace_back(psBrim); - steps.emplace_back(psSkirt); - } else if ( - opt_key == "nozzle_diameter" - || opt_key == "resolution") { - osteps.emplace_back(posSlice); - } else if ( - opt_key == "complete_objects" - || opt_key == "filament_type" - || opt_key == "filament_soluble" - || opt_key == "first_layer_temperature" - || opt_key == "filament_loading_speed" - || opt_key == "filament_loading_speed_start" - || opt_key == "filament_unloading_speed" - || opt_key == "filament_unloading_speed_start" - || opt_key == "filament_toolchange_delay" - || opt_key == "filament_cooling_moves" - || opt_key == "filament_minimal_purge_on_wipe_tower" - || opt_key == "filament_cooling_initial_speed" - || opt_key == "filament_cooling_final_speed" - || opt_key == "filament_ramming_parameters" - || opt_key == "gcode_flavor" - || opt_key == "infill_first" - || opt_key == "single_extruder_multi_material" - || opt_key == "spiral_vase" - || opt_key == "temperature" - || opt_key == "wipe_tower" - || opt_key == "wipe_tower_width" - || opt_key == "wipe_tower_bridging" - || opt_key == "wiping_volumes_matrix" - || opt_key == "parking_pos_retraction" - || opt_key == "cooling_tube_retraction" - || opt_key == "cooling_tube_length" - || opt_key == "extra_loading_move" - || opt_key == "z_offset") { - steps.emplace_back(psWipeTower); - } else if ( - opt_key == "first_layer_extrusion_width" - || opt_key == "min_layer_height" - || opt_key == "max_layer_height") { - osteps.emplace_back(posPerimeters); - osteps.emplace_back(posInfill); - osteps.emplace_back(posSupportMaterial); - steps.emplace_back(psSkirt); - steps.emplace_back(psBrim); - } else { - // for legacy, if we can't handle this option let's invalidate all steps - //FIXME invalidate all steps of all objects as well? - invalidated |= this->invalidate_all_steps(); - // Continue with the other opt_keys to possibly invalidate any object specific steps. - } - } - - sort_remove_duplicates(steps); - for (PrintStep step : steps) - invalidated |= this->invalidate_step(step); - sort_remove_duplicates(osteps); - for (PrintObjectStep ostep : osteps) - for (PrintObject *object : this->objects) - invalidated |= object->invalidate_step(ostep); - return invalidated; -} - -bool Print::invalidate_step(PrintStep step) -{ - bool invalidated = this->state.invalidate(step); - // Propagate to dependent steps. - //FIXME Why should skirt invalidate brim? Shouldn't it be vice versa? - if (step == psSkirt) - invalidated |= this->state.invalidate(psBrim); - return invalidated; -} - -// returns true if an object step is done on all objects -// and there's at least one object -bool Print::step_done(PrintObjectStep step) const -{ - if (this->objects.empty()) - return false; - for (const PrintObject *object : this->objects) - if (!object->state.is_done(step)) - return false; - return true; -} - -// returns 0-based indices of used extruders -std::vector Print::object_extruders() const -{ - std::vector extruders; - - for (PrintRegion* region : this->regions) { - // these checks reflect the same logic used in the GUI for enabling/disabling - // extruder selection fields - if (region->config.perimeters.value > 0 || this->config.brim_width.value > 0) - extruders.push_back(region->config.perimeter_extruder - 1); - if (region->config.fill_density.value > 0) - extruders.push_back(region->config.infill_extruder - 1); - if (region->config.top_solid_layers.value > 0 || region->config.bottom_solid_layers.value > 0) - extruders.push_back(region->config.solid_infill_extruder - 1); - } - - sort_remove_duplicates(extruders); - return extruders; -} - -// returns 0-based indices of used extruders -std::vector Print::support_material_extruders() const -{ - std::vector extruders; - bool support_uses_current_extruder = false; - - for (PrintObject *object : this->objects) { - if (object->has_support_material()) { - if (object->config.support_material_extruder == 0) - support_uses_current_extruder = true; - else - extruders.push_back(object->config.support_material_extruder - 1); - if (object->config.support_material_interface_extruder == 0) - support_uses_current_extruder = true; - else - extruders.push_back(object->config.support_material_interface_extruder - 1); - } - } - - if (support_uses_current_extruder) - // Add all object extruders to the support extruders as it is not know which one will be used to print supports. - append(extruders, this->object_extruders()); - - sort_remove_duplicates(extruders); - return extruders; -} - -// returns 0-based indices of used extruders -std::vector Print::extruders() const -{ - std::vector extruders = this->object_extruders(); - append(extruders, this->support_material_extruders()); - sort_remove_duplicates(extruders); - return extruders; -} - -void Print::_simplify_slices(double distance) -{ - for (PrintObject *object : this->objects) { - for (Layer *layer : object->layers) { - layer->slices.simplify(distance); - for (LayerRegion *layerm : layer->regions) - layerm->slices.simplify(distance); - } - } -} - -double Print::max_allowed_layer_height() const -{ - double nozzle_diameter_max = 0.; - for (unsigned int extruder_id : this->extruders()) - nozzle_diameter_max = std::max(nozzle_diameter_max, this->config.nozzle_diameter.get_at(extruder_id)); - return nozzle_diameter_max; -} - -// Caller is responsible for supplying models whose objects don't collide -// and have explicit instance positions. -void Print::add_model_object(ModelObject* model_object, int idx) -{ - // Initialize a new print object and store it at the given position. - PrintObject *object = new PrintObject(this, model_object, model_object->raw_bounding_box()); - if (idx != -1) { - delete this->objects[idx]; - this->objects[idx] = object; - } else - this->objects.emplace_back(object); - // Invalidate all print steps. - this->invalidate_all_steps(); - - size_t volume_id = 0; - for (const ModelVolume *volume : model_object->volumes) { - if (! volume->is_model_part() && ! volume->is_modifier()) - continue; - // Get the config applied to this volume. - PrintRegionConfig config = this->_region_config_from_model_volume(*volume); - // Find an existing print region with the same config. - size_t region_id = size_t(-1); - for (size_t i = 0; i < this->regions.size(); ++ i) - if (config.equals(this->regions[i]->config)) { - region_id = i; - break; - } - // If no region exists with the same config, create a new one. - if (region_id == size_t(-1)) { - region_id = this->regions.size(); - this->add_region()->config.apply(config); - } - // Assign volume to a region. - object->add_region_volume(region_id, volume_id); - ++ volume_id; - } - - // Apply config to print object. - object->config.apply(this->default_object_config); - normalize_and_apply_config(object->config, model_object->config); - - // update placeholders - { - // get the first input file name - std::string input_file; - std::vector v_scale; - for (const PrintObject *object : this->objects) { - const ModelObject &mobj = *object->model_object(); - v_scale.push_back(boost::lexical_cast(mobj.instances[0]->scaling_factor*100) + "%"); - if (input_file.empty()) - input_file = mobj.input_file; - } - - PlaceholderParser &pp = this->placeholder_parser; - pp.set("scale", v_scale); - if (! input_file.empty()) { - // get basename with and without suffix - const std::string input_basename = boost::filesystem::path(input_file).filename().string(); - pp.set("input_filename", input_basename); - const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of(".")); - pp.set("input_filename_base", input_basename_base); - } - } -} - -bool Print::apply_config(DynamicPrintConfig config) -{ - // we get a copy of the config object so we can modify it safely - config.normalize(); - - // apply variables to placeholder parser - this->placeholder_parser.apply_config(config); - - // handle changes to print config - t_config_option_keys print_diff = this->config.diff(config); - this->config.apply_only(config, print_diff, true); - bool invalidated = this->invalidate_state_by_config_options(print_diff); - - // handle changes to object config defaults - this->default_object_config.apply(config, true); - for (PrintObject *object : this->objects) { - // we don't assume that config contains a full ObjectConfig, - // so we base it on the current print-wise default - PrintObjectConfig new_config = this->default_object_config; - // we override the new config with object-specific options - normalize_and_apply_config(new_config, object->model_object()->config); - // Force a refresh of a variable layer height profile at the PrintObject if it is not valid. - if (! object->layer_height_profile_valid) { - // The layer_height_profile is not valid for some reason (updated by the user or invalidated due to some option change). - // Invalidate the slicing step, which in turn invalidates everything. - object->invalidate_step(posSlice); - // Trigger recalculation. - invalidated = true; - } - // check whether the new config is different from the current one - t_config_option_keys diff = object->config.diff(new_config); - object->config.apply_only(new_config, diff, true); - invalidated |= object->invalidate_state_by_config_options(diff); - } - - // handle changes to regions config defaults - this->default_region_config.apply(config, true); - - // All regions now have distinct settings. - // Check whether applying the new region config defaults we'd get different regions. - bool rearrange_regions = false; - { - // Collect the already visited region configs into other_region_configs, - // so one may check for duplicates. - std::vector other_region_configs; - for (size_t region_id = 0; region_id < this->regions.size(); ++ region_id) { - PrintRegion ®ion = *this->regions[region_id]; - PrintRegionConfig this_region_config; - bool this_region_config_set = false; - for (PrintObject *object : this->objects) { - if (region_id < object->region_volumes.size()) { - for (int volume_id : object->region_volumes[region_id]) { - const ModelVolume &volume = *object->model_object()->volumes[volume_id]; - if (this_region_config_set) { - // If the new config for this volume differs from the other - // volume configs currently associated to this region, it means - // the region subdivision does not make sense anymore. - if (! this_region_config.equals(this->_region_config_from_model_volume(volume))) { - rearrange_regions = true; - goto exit_for_rearrange_regions; - } - } else { - this_region_config = this->_region_config_from_model_volume(volume); - this_region_config_set = true; - } - for (const PrintRegionConfig &cfg : other_region_configs) { - // If the new config for this volume equals any of the other - // volume configs that are not currently associated to this - // region, it means the region subdivision does not make - // sense anymore. - if (cfg.equals(this_region_config)) { - rearrange_regions = true; - goto exit_for_rearrange_regions; - } - } - } - } - } - if (this_region_config_set) { - t_config_option_keys diff = region.config.diff(this_region_config); - if (! diff.empty()) { - region.config.apply_only(this_region_config, diff); - for (PrintObject *object : this->objects) - if (region_id < object->region_volumes.size() && ! object->region_volumes[region_id].empty()) - invalidated |= object->invalidate_state_by_config_options(diff); - } - other_region_configs.emplace_back(std::move(this_region_config)); - } - } - } - -exit_for_rearrange_regions: - - if (rearrange_regions) { - // The current subdivision of regions does not make sense anymore. - // We need to remove all objects and re-add them. - ModelObjectPtrs model_objects; - model_objects.reserve(this->objects.size()); - for (PrintObject *object : this->objects) - model_objects.push_back(object->model_object()); - this->clear_objects(); - for (ModelObject *mo : model_objects) - this->add_model_object(mo); - invalidated = true; - } - - // Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads. - for (PrintObject *object : this->objects) - if (! object->layer_height_profile_valid) - object->update_layer_height_profile(); - - return invalidated; -} - -bool Print::has_infinite_skirt() const -{ - return (this->config.skirt_height == -1 && this->config.skirts > 0) - || (this->config.ooze_prevention && this->extruders().size() > 1); -} - -bool Print::has_skirt() const -{ - return (this->config.skirt_height > 0 && this->config.skirts > 0) - || this->has_infinite_skirt(); -} - -std::string Print::validate() const -{ - BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.bed_shape.values)); - BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height)); - // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. - print_volume.min.z = -1e10; - unsigned int printable_count = 0; - for (PrintObject *po : this->objects) { - po->model_object()->check_instances_print_volume_state(print_volume); - po->reload_model_instances(); - if (po->is_printable()) - ++printable_count; - } - - if (printable_count == 0) - return L("All objects are outside of the print volume."); - - if (this->config.complete_objects) { - // Check horizontal clearance. - { - Polygons convex_hulls_other; - for (PrintObject *object : this->objects) { - // Get convex hull of all meshes assigned to this print object. - Polygon convex_hull; - { - Polygons mesh_convex_hulls; - for (const std::vector &volumes : object->region_volumes) - for (int volume_id : volumes) - mesh_convex_hulls.emplace_back(object->model_object()->volumes[volume_id]->mesh.convex_hull()); - // make a single convex hull for all of them - convex_hull = Slic3r::Geometry::convex_hull(mesh_convex_hulls); - } - // Apply the same transformations we apply to the actual meshes when slicing them. - object->model_object()->instances.front()->transform_polygon(&convex_hull); - // Grow convex hull with the clearance margin. - convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front(); - // Now we check that no instance of convex_hull intersects any of the previously checked object instances. - for (const Point © : object->_shifted_copies) { - Polygon p = convex_hull; - p.translate(copy); - if (! intersection(convex_hulls_other, p).empty()) - return L("Some objects are too close; your extruder will collide with them."); - polygons_append(convex_hulls_other, p); - } - } - } - // Check vertical clearance. - { - std::vector object_height; - for (const PrintObject *object : this->objects) - object_height.insert(object_height.end(), object->copies().size(), object->size.z); - std::sort(object_height.begin(), object_height.end()); - // Ignore the tallest *copy* (this is why we repeat height for all of them): - // it will be printed as last one so its height doesn't matter. - object_height.pop_back(); - if (! object_height.empty() && object_height.back() > scale_(this->config.extruder_clearance_height.value)) - return L("Some objects are too tall and cannot be printed without extruder collisions."); - } - } // end if (this->config.complete_objects) - - if (this->config.spiral_vase) { - size_t total_copies_count = 0; - for (const PrintObject *object : this->objects) - total_copies_count += object->copies().size(); - // #4043 - if (total_copies_count > 1 && ! this->config.complete_objects.value) - return L("The Spiral Vase option can only be used when printing a single object."); - if (this->regions.size() > 1) - return L("The Spiral Vase option can only be used when printing single material objects."); - } - - if (this->config.single_extruder_multi_material) { - for (size_t i=1; iconfig.nozzle_diameter.values.size(); ++i) - if (this->config.nozzle_diameter.values[i] != this->config.nozzle_diameter.values[i-1]) - return L("All extruders must have the same diameter for single extruder multimaterial printer."); - } - - if (this->has_wipe_tower() && ! this->objects.empty()) { - if (this->config.gcode_flavor != gcfRepRap && this->config.gcode_flavor != gcfMarlin) - return L("The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors."); - if (! this->config.use_relative_e_distances) - return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."); - SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters(); - - const PrintObject* tallest_object = this->objects.front(); // let's find the tallest object - for (const auto* object : objects) - if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) ) - tallest_object = object; - - for (PrintObject *object : this->objects) { - SlicingParameters slicing_params = object->slicing_parameters(); - if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON || - std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON) - return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths"); - if (slicing_params.raft_layers() != slicing_params0.raft_layers()) - return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers"); - if (object->config.support_material_contact_distance != this->objects.front()->config.support_material_contact_distance) - return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance"); - if (! equal_layering(slicing_params, slicing_params0)) - return L("The Wipe Tower is only supported for multiple objects if they are sliced equally."); - bool was_layer_height_profile_valid = object->layer_height_profile_valid; - object->update_layer_height_profile(); - object->layer_height_profile_valid = was_layer_height_profile_valid; - - if ( this->config.variable_layer_height ) { // comparing layer height profiles - bool failed = false; - if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) { - int i = 0; - while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) { - if (std::abs(tallest_object->layer_height_profile[i] - object->layer_height_profile[i])) { - failed = true; - break; - } - ++i; - if (i == object->layer_height_profile.size()-2) // this element contains this objects max z - if (tallest_object->layer_height_profile[i] > object->layer_height_profile[i]) // the difference does not matter in this case - ++i; - } - } - else - failed = true; - - if (failed) - return L("The Wipe tower is only supported if all objects have the same layer height profile"); - } - } - } - - { - // find the smallest nozzle diameter - std::vector extruders = this->extruders(); - if (extruders.empty()) - return L("The supplied settings will cause an empty print."); - - std::vector nozzle_diameters; - for (unsigned int extruder_id : extruders) - nozzle_diameters.push_back(this->config.nozzle_diameter.get_at(extruder_id)); - double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end()); - - unsigned int total_extruders_count = this->config.nozzle_diameter.size(); - for (const auto& extruder_idx : extruders) - if ( extruder_idx >= total_extruders_count ) - return L("One or more object were assigned an extruder that the printer does not have."); - - for (PrintObject *object : this->objects) { - if ((object->config.support_material_extruder == -1 || object->config.support_material_interface_extruder == -1) && - (object->config.raft_layers > 0 || object->config.support_material.value)) { - // The object has some form of support and either support_material_extruder or support_material_interface_extruder - // will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles - // are of the same diameter. - if (nozzle_diameters.size() > 1) - return L("Printing with multiple extruders of differing nozzle diameters. " - "If support is to be printed with the current extruder (support_material_extruder == 0 or support_material_interface_extruder == 0), " - "all nozzles have to be of the same diameter."); - } - - // validate first_layer_height - double first_layer_height = object->config.get_abs_value(L("first_layer_height")); - double first_layer_min_nozzle_diameter; - if (object->config.raft_layers > 0) { - // if we have raft layers, only support material extruder is used on first layer - size_t first_layer_extruder = object->config.raft_layers == 1 - ? object->config.support_material_interface_extruder-1 - : object->config.support_material_extruder-1; - first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ? - min_nozzle_diameter : - this->config.nozzle_diameter.get_at(first_layer_extruder); - } else { - // if we don't have raft layers, any nozzle diameter is potentially used in first layer - first_layer_min_nozzle_diameter = min_nozzle_diameter; - } - if (first_layer_height > first_layer_min_nozzle_diameter) - return L("First layer height can't be greater than nozzle diameter"); - - // validate layer_height - if (object->config.layer_height.value > min_nozzle_diameter) - return L("Layer height can't be greater than nozzle diameter"); - } - } - - return std::string(); -} - -// the bounding box of objects placed in copies position -// (without taking skirt/brim/support material into account) -BoundingBox Print::bounding_box() const -{ - BoundingBox bb; - for (const PrintObject *object : this->objects) - for (Point copy : object->_shifted_copies) { - bb.merge(copy); - copy.translate(object->size); - bb.merge(copy); - } - return bb; -} - -// the total bounding box of extrusions, including skirt/brim/support material -// this methods needs to be called even when no steps were processed, so it should -// only use configuration values -BoundingBox Print::total_bounding_box() const -{ - // get objects bounding box - BoundingBox bb = this->bounding_box(); - - // we need to offset the objects bounding box by at least half the perimeters extrusion width - Flow perimeter_flow = this->objects.front()->get_layer(0)->get_region(0)->flow(frPerimeter); - double extra = perimeter_flow.width/2; - - // consider support material - if (this->has_support_material()) { - extra = std::max(extra, SUPPORT_MATERIAL_MARGIN); - } - - // consider brim and skirt - if (this->config.brim_width.value > 0) { - Flow brim_flow = this->brim_flow(); - extra = std::max(extra, this->config.brim_width.value + brim_flow.width/2); - } - if (this->has_skirt()) { - int skirts = this->config.skirts.value; - if (skirts == 0 && this->has_infinite_skirt()) skirts = 1; - Flow skirt_flow = this->skirt_flow(); - extra = std::max( - extra, - this->config.brim_width.value - + this->config.skirt_distance.value - + skirts * skirt_flow.spacing() - + skirt_flow.width/2 - ); - } - - if (extra > 0) - bb.offset(scale_(extra)); - - return bb; -} - -double Print::skirt_first_layer_height() const -{ - if (this->objects.empty()) CONFESS("skirt_first_layer_height() can't be called without PrintObjects"); - return this->objects.front()->config.get_abs_value("first_layer_height"); -} - -Flow Print::brim_flow() const -{ - ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width; - if (width.value == 0) - width = this->regions.front()->config.perimeter_extrusion_width; - if (width.value == 0) - width = this->objects.front()->config.extrusion_width; - - /* We currently use a random region's perimeter extruder. - While this works for most cases, we should probably consider all of the perimeter - extruders and take the one with, say, the smallest index. - The same logic should be applied to the code that selects the extruder during G-code - generation as well. */ - return Flow::new_from_config_width( - frPerimeter, - width, - this->config.nozzle_diameter.get_at(this->regions.front()->config.perimeter_extruder-1), - this->skirt_first_layer_height(), - 0 - ); -} - -Flow Print::skirt_flow() const -{ - ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width; - if (width.value == 0) - width = this->regions.front()->config.perimeter_extrusion_width; - if (width.value == 0) - width = this->objects.front()->config.extrusion_width; - - /* We currently use a random object's support material extruder. - While this works for most cases, we should probably consider all of the support material - extruders and take the one with, say, the smallest index; - The same logic should be applied to the code that selects the extruder during G-code - generation as well. */ - return Flow::new_from_config_width( - frPerimeter, - width, - this->config.nozzle_diameter.get_at(this->objects.front()->config.support_material_extruder-1), - this->skirt_first_layer_height(), - 0 - ); -} - -PrintRegionConfig Print::_region_config_from_model_volume(const ModelVolume &volume) -{ - PrintRegionConfig config = this->default_region_config; - normalize_and_apply_config(config, volume.get_object()->config); - normalize_and_apply_config(config, volume.config); - if (! volume.material_id().empty()) - normalize_and_apply_config(config, volume.material()->config); - return config; -} - -bool Print::has_support_material() const -{ - for (const PrintObject *object : this->objects) - if (object->has_support_material()) - return true; - return false; -} - -/* This method assigns extruders to the volumes having a material - but not having extruders set in the volume config. */ -void Print::auto_assign_extruders(ModelObject* model_object) const -{ - // only assign extruders if object has more than one volume - if (model_object->volumes.size() < 2) - return; - -// size_t extruders = this->config.nozzle_diameter.values.size(); - for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { - ModelVolume *volume = model_object->volumes[volume_id]; - //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned. - if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder")) - volume->config.opt("extruder", true)->value = int(volume_id + 1); - } -} - -void Print::_make_skirt() -{ - // First off we need to decide how tall the skirt must be. - // The skirt_height option from config is expressed in layers, but our - // object might have different layer heights, so we need to find the print_z - // of the highest layer involved. - // Note that unless has_infinite_skirt() == true - // the actual skirt might not reach this $skirt_height_z value since the print - // order of objects on each layer is not guaranteed and will not generally - // include the thickest object first. It is just guaranteed that a skirt is - // prepended to the first 'n' layers (with 'n' = skirt_height). - // $skirt_height_z in this case is the highest possible skirt height for safety. - coordf_t skirt_height_z = 0.; - PrintObjectPtrs printable_objects = get_printable_objects(); - for (const PrintObject *object : printable_objects) { - size_t skirt_layers = this->has_infinite_skirt() ? - object->layer_count() : - std::min(size_t(this->config.skirt_height.value), object->layer_count()); - skirt_height_z = std::max(skirt_height_z, object->layers[skirt_layers-1]->print_z); - } - - // Collect points from all layers contained in skirt height. - Points points; - for (const PrintObject *object : printable_objects) { - Points object_points; - // Get object layers up to skirt_height_z. - for (const Layer *layer : object->layers) { - if (layer->print_z > skirt_height_z) - break; - for (const ExPolygon &expoly : layer->slices.expolygons) - // Collect the outer contour points only, ignore holes for the calculation of the convex hull. - append(object_points, expoly.contour.points); - } - // Get support layers up to skirt_height_z. - for (const SupportLayer *layer : object->support_layers) { - if (layer->print_z > skirt_height_z) - break; - for (const ExtrusionEntity *extrusion_entity : layer->support_fills.entities) - append(object_points, extrusion_entity->as_polyline().points); - } - // Repeat points for each object copy. - for (const Point &shift : object->_shifted_copies) { - Points copy_points = object_points; - for (Point &pt : copy_points) - pt.translate(shift); - append(points, copy_points); - } - } - - if (points.size() < 3) - // At least three points required for a convex hull. - return; - - Polygon convex_hull = Slic3r::Geometry::convex_hull(points); - - // Skirt may be printed on several layers, having distinct layer heights, - // but loops must be aligned so can't vary width/spacing - // TODO: use each extruder's own flow - double first_layer_height = this->skirt_first_layer_height(); - Flow flow = this->skirt_flow(); - float spacing = flow.spacing(); - double mm3_per_mm = flow.mm3_per_mm(); - - std::vector extruders; - std::vector extruders_e_per_mm; - { - auto set_extruders = this->extruders(); - extruders.reserve(set_extruders.size()); - extruders_e_per_mm.reserve(set_extruders.size()); - for (auto &extruder_id : set_extruders) { - extruders.push_back(extruder_id); - extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &this->config).e_per_mm(mm3_per_mm)); - } - } - - // Number of skirt loops per skirt layer. - int n_skirts = this->config.skirts.value; - if (this->has_infinite_skirt() && n_skirts == 0) - n_skirts = 1; - - // Initial offset of the brim inner edge from the object (possible with a support & raft). - // The skirt will touch the brim if the brim is extruded. - Flow brim_flow = this->brim_flow(); - double actual_brim_width = brim_flow.spacing() * floor(this->config.brim_width.value / brim_flow.spacing()); - coord_t distance = scale_(std::max(this->config.skirt_distance.value, actual_brim_width) - spacing/2.); - // Draw outlines from outside to inside. - // Loop while we have less skirts than required or any extruder hasn't reached the min length if any. - std::vector extruded_length(extruders.size(), 0.); - for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) { - // Offset the skirt outside. - distance += coord_t(scale_(spacing)); - // Generate the skirt centerline. - Polygon loop; - { - Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1)); - Geometry::simplify_polygons(loops, scale_(0.05), &loops); - loop = loops.front(); - } - // Extrude the skirt loop. - ExtrusionLoop eloop(elrSkirt); - eloop.paths.emplace_back(ExtrusionPath( - ExtrusionPath( - erSkirt, - mm3_per_mm, // this will be overridden at G-code export time - flow.width, - first_layer_height // this will be overridden at G-code export time - ))); - eloop.paths.back().polyline = loop.split_at_first_point(); - this->skirt.append(eloop); - if (this->config.min_skirt_length.value > 0) { - // The skirt length is limited. Sum the total amount of filament length extruded, in mm. - extruded_length[extruder_idx] += unscale(loop.length()) * extruders_e_per_mm[extruder_idx]; - if (extruded_length[extruder_idx] < this->config.min_skirt_length.value) { - // Not extruded enough yet with the current extruder. Add another loop. - if (i == 1) - ++ i; - } else { - assert(extruded_length[extruder_idx] >= this->config.min_skirt_length.value); - // Enough extruded with the current extruder. Extrude with the next one, - // until the prescribed number of skirt loops is extruded. - if (extruder_idx + 1 < extruders.size()) - ++ extruder_idx; - } - } else { - // The skirt lenght is not limited, extrude the skirt with the 1st extruder only. - } - } - // Brims were generated inside out, reverse to print the outmost contour first. - this->skirt.reverse(); -} - -void Print::_make_brim() -{ - // Brim is only printed on first layer and uses perimeter extruder. - Flow flow = this->brim_flow(); - Polygons islands; - PrintObjectPtrs printable_objects = get_printable_objects(); - for (PrintObject *object : printable_objects) { - Polygons object_islands; - for (ExPolygon &expoly : object->layers.front()->slices.expolygons) - object_islands.push_back(expoly.contour); - if (! object->support_layers.empty()) - object->support_layers.front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); - islands.reserve(islands.size() + object_islands.size() * object->_shifted_copies.size()); - for (const Point &pt : object->_shifted_copies) - for (Polygon &poly : object_islands) { - islands.push_back(poly); - islands.back().translate(pt); - } - } - Polygons loops; - size_t num_loops = size_t(floor(this->config.brim_width.value / flow.spacing())); - for (size_t i = 0; i < num_loops; ++ i) { - islands = offset(islands, float(flow.scaled_spacing()), jtSquare); - for (Polygon &poly : islands) { - // poly.simplify(SCALED_RESOLUTION); - poly.points.push_back(poly.points.front()); - Points p = MultiPoint::_douglas_peucker(poly.points, SCALED_RESOLUTION); - p.pop_back(); - poly.points = std::move(p); - } - polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing()))); - } - - loops = union_pt_chained(loops, false); - std::reverse(loops.begin(), loops.end()); - extrusion_entities_append_loops(this->brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())); -} - -// Wipe tower support. -bool Print::has_wipe_tower() const -{ - return - this->config.single_extruder_multi_material.value && - ! this->config.spiral_vase.value && - this->config.wipe_tower.value && - this->config.nozzle_diameter.values.size() > 1; -} - -void Print::_clear_wipe_tower() -{ - m_tool_ordering.clear(); - m_wipe_tower_priming.reset(nullptr); - m_wipe_tower_tool_changes.clear(); - m_wipe_tower_final_purge.reset(nullptr); -} - -void Print::_make_wipe_tower() -{ - this->_clear_wipe_tower(); - if (! this->has_wipe_tower()) - return; - - m_wipe_tower_depth = 0.f; - - // Get wiping matrix to get number of extruders and convert vector to vector: - std::vector wiping_matrix((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end()); - // Extract purging volumes for each extruder pair: - std::vector> wipe_volumes; - const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON); - for (unsigned int i = 0; i(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders)); - - // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print. - m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true); - if (! m_tool_ordering.has_wipe_tower()) - // Don't generate any wipe tower. - return; - - // Check whether there are any layers in m_tool_ordering, which are marked with has_wipe_tower, - // they print neither object, nor support. These layers are above the raft and below the object, and they - // shall be added to the support layers to be printed. - // see https://github.com/prusa3d/Slic3r/issues/607 - { - size_t idx_begin = size_t(-1); - size_t idx_end = m_tool_ordering.layer_tools().size(); - // Find the first wipe tower layer, which does not have a counterpart in an object or a support layer. - for (size_t i = 0; i < idx_end; ++ i) { - const LayerTools < = m_tool_ordering.layer_tools()[i]; - if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) { - idx_begin = i; - break; - } - } - if (idx_begin != size_t(-1)) { - // Find the position in this->objects.first()->support_layers to insert these new support layers. - double wipe_tower_new_layer_print_z_first = m_tool_ordering.layer_tools()[idx_begin].print_z; - SupportLayerPtrs::iterator it_layer = this->objects.front()->support_layers.begin(); - SupportLayerPtrs::iterator it_end = this->objects.front()->support_layers.end(); - for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer); - // Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer. - for (size_t i = idx_begin; i < idx_end; ++ i) { - LayerTools < = const_cast(m_tool_ordering.layer_tools()[i]); - if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support)) - break; - lt.has_support = true; - // Insert the new support layer. - double height = lt.print_z - m_tool_ordering.layer_tools()[i-1].print_z; - //FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway. - auto *new_layer = new SupportLayer(size_t(-1), this->objects.front(), - height, lt.print_z, lt.print_z - 0.5 * height); - it_layer = this->objects.front()->support_layers.insert(it_layer, new_layer); - ++ it_layer; - } - } - } - - // Initialize the wipe tower. - WipeTowerPrusaMM wipe_tower( - float(this->config.wipe_tower_x.value), float(this->config.wipe_tower_y.value), - float(this->config.wipe_tower_width.value), - float(this->config.wipe_tower_rotation_angle.value), float(this->config.cooling_tube_retraction.value), - float(this->config.cooling_tube_length.value), float(this->config.parking_pos_retraction.value), - float(this->config.extra_loading_move.value), float(this->config.wipe_tower_bridging), wipe_volumes, - m_tool_ordering.first_extruder()); - - //wipe_tower.set_retract(); - //wipe_tower.set_zhop(); - - // Set the extruder & material properties at the wipe tower object. - for (size_t i = 0; i < number_of_extruders; ++ i) - wipe_tower.set_extruder( - i, - WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()), - this->config.temperature.get_at(i), - this->config.first_layer_temperature.get_at(i), - this->config.filament_loading_speed.get_at(i), - this->config.filament_loading_speed_start.get_at(i), - this->config.filament_unloading_speed.get_at(i), - this->config.filament_unloading_speed_start.get_at(i), - this->config.filament_toolchange_delay.get_at(i), - this->config.filament_cooling_moves.get_at(i), - this->config.filament_cooling_initial_speed.get_at(i), - this->config.filament_cooling_final_speed.get_at(i), - this->config.filament_ramming_parameters.get_at(i), - this->config.nozzle_diameter.get_at(i)); - - m_wipe_tower_priming = Slic3r::make_unique( - wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), false)); - - // Lets go through the wipe tower layers and determine pairs of extruder changes for each - // to pass to wipe_tower (so that it can use it for planning the layout of the tower) - { - unsigned int current_extruder_id = m_tool_ordering.all_extruders().back(); - for (auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers - if (!layer_tools.has_wipe_tower) continue; - bool first_layer = &layer_tools == &m_tool_ordering.front(); - wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false); - for (const auto extruder_id : layer_tools.extruders) { - if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) { - float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange - // Not all of that can be used for infill purging: - volume_to_wipe -= config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id); - - // try to assign some infills/objects for the wiping: - volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe); - - // add back the minimal amount toforce on the wipe tower: - volume_to_wipe += config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id); - - // request a toolchange at the wipe tower with at least volume_to_wipe purging amount - wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, - first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe); - current_extruder_id = extruder_id; - } - } - layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this); - if (&layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0) - break; - } - } - - // Generate the wipe tower layers. - m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size()); - wipe_tower.generate(m_wipe_tower_tool_changes); - m_wipe_tower_depth = wipe_tower.get_depth(); - - // Unload the current filament over the purge tower. - coordf_t layer_height = this->objects.front()->config.layer_height.value; - if (m_tool_ordering.back().wipe_tower_partitions > 0) { - // The wipe tower goes up to the last layer of the print. - if (wipe_tower.layer_finished()) { - // The wipe tower is printed to the top of the print and it has no space left for the final extruder purge. - // Lift Z to the next layer. - wipe_tower.set_layer(float(m_tool_ordering.back().print_z + layer_height), float(layer_height), 0, false, true); - } else { - // There is yet enough space at this layer of the wipe tower for the final purge. - } - } else { - // The wipe tower does not reach the last print layer, perform the pruge at the last print layer. - assert(m_tool_ordering.back().wipe_tower_partitions == 0); - wipe_tower.set_layer(float(m_tool_ordering.back().print_z), float(layer_height), 0, false, true); - } - m_wipe_tower_final_purge = Slic3r::make_unique( - wipe_tower.tool_change((unsigned int)-1, false)); - - m_wipe_tower_used_filament = wipe_tower.get_used_filament(); - m_wipe_tower_number_of_toolchanges = wipe_tower.get_number_of_toolchanges(); -} - -std::string Print::output_filename() -{ - this->placeholder_parser.update_timestamp(); - try { - return this->placeholder_parser.process(this->config.output_filename_format.value, 0); - } catch (std::runtime_error &err) { - throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what()); - } -} - -std::string Print::output_filepath(const std::string &path) -{ - // if we were supplied no path, generate an automatic one based on our first object's input file - if (path.empty()) { - // get the first input file name - std::string input_file; - for (const PrintObject *object : this->objects) { - input_file = object->model_object()->input_file; - if (! input_file.empty()) - break; - } - return (boost::filesystem::path(input_file).parent_path() / this->output_filename()).make_preferred().string(); - } - - // if we were supplied a directory, use it and append our automatically generated filename - boost::filesystem::path p(path); - if (boost::filesystem::is_directory(p)) - return (p / this->output_filename()).make_preferred().string(); - - // if we were supplied a file which is not a directory, use it - return path; -} - -void Print::set_status(int percent, const std::string &message) -{ - printf("Print::status %d => %s\n", percent, message.c_str()); -} - -// Returns extruder this eec should be printed with, according to PrintRegion config -int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion) -{ - return is_infill(fill.role()) ? std::max(0, (is_solid_infill(fill.entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) : - std::max(region.config.perimeter_extruder.value - 1, 0); -} - -} diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp deleted file mode 100644 index 3ef720db3a..0000000000 --- a/xs/src/libslic3r/Print.hpp +++ /dev/null @@ -1,356 +0,0 @@ -#ifndef slic3r_Print_hpp_ -#define slic3r_Print_hpp_ - -#include "libslic3r.h" -#include -#include -#include -#include "BoundingBox.hpp" -#include "Flow.hpp" -#include "PrintConfig.hpp" -#include "Point.hpp" -#include "Layer.hpp" -#include "Model.hpp" -#include "PlaceholderParser.hpp" -#include "Slicing.hpp" -#include "GCode/ToolOrdering.hpp" -#include "GCode/WipeTower.hpp" - -#include "tbb/atomic.h" - -namespace Slic3r { - -class Print; -class PrintObject; -class ModelObject; - - -// Print step IDs for keeping track of the print state. -enum PrintStep { - psSkirt, psBrim, psWipeTower, psCount, -}; -enum PrintObjectStep { - posSlice, posPerimeters, posPrepareInfill, - posInfill, posSupportMaterial, posCount, -}; - -// To be instantiated over PrintStep or PrintObjectStep enums. -template -class PrintState -{ -public: - PrintState() { memset(state, 0, sizeof(state)); } - - enum State { - INVALID, - STARTED, - DONE, - }; - State state[COUNT]; - - bool is_started(StepType step) const { return this->state[step] == STARTED; } - bool is_done(StepType step) const { return this->state[step] == DONE; } - void set_started(StepType step) { this->state[step] = STARTED; } - void set_done(StepType step) { this->state[step] = DONE; } - bool invalidate(StepType step) { - bool invalidated = this->state[step] != INVALID; - this->state[step] = INVALID; - return invalidated; - } - bool invalidate_all() { - bool invalidated = false; - for (size_t i = 0; i < COUNT; ++ i) - if (this->state[i] != INVALID) { - invalidated = true; - break; - } - memset(state, 0, sizeof(state)); - return invalidated; - } -}; - -// A PrintRegion object represents a group of volumes to print -// sharing the same config (including the same assigned extruder(s)) -class PrintRegion -{ - friend class Print; - -public: - PrintRegionConfig config; - - Print* print() { return this->_print; } - Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; - // Average diameter of nozzles participating on extruding this region. - coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; - // Average diameter of nozzles participating on extruding this region. - coordf_t bridging_height_avg(const PrintConfig &print_config) const; - -private: - Print* _print; - - PrintRegion(Print* print) : _print(print) {} - ~PrintRegion() {} -}; - - -typedef std::vector LayerPtrs; -typedef std::vector SupportLayerPtrs; -class BoundingBoxf3; // TODO: for temporary constructor parameter - -class PrintObject -{ - friend class Print; - -public: - // vector of (vectors of volume ids), indexed by region_id - std::vector> region_volumes; - PrintObjectConfig config; - t_layer_height_ranges layer_height_ranges; - - // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. - // The pairs of are packed into a 1D array to simplify handling by the Perl XS. - // layer_height_profile must not be set by the background thread. - std::vector layer_height_profile; - // There is a layer_height_profile at both PrintObject and ModelObject. The layer_height_profile at the ModelObject - // is used for interactive editing and for loading / storing into a project file (AMF file as of today). - // This flag indicates that the layer_height_profile at the UI has been updated, therefore the backend needs to get it. - // This flag is necessary as we cannot safely clear the layer_height_profile if the background calculation is running. - bool layer_height_profile_valid; - - // this is set to true when LayerRegion->slices is split in top/internal/bottom - // so that next call to make_perimeters() performs a union() before computing loops - bool typed_slices; - - Point3 size; // XYZ in scaled coordinates - - // scaled coordinates to add to copies (to compensate for the alignment - // operated when creating the object but still preserving a coherent API - // for external callers) - Point _copies_shift; - - // Slic3r::Point objects in scaled G-code coordinates in our coordinates - Points _shifted_copies; - - LayerPtrs layers; - SupportLayerPtrs support_layers; - PrintState state; - - Print* print() { return this->_print; } - const Print* print() const { return this->_print; } - ModelObject* model_object() { return this->_model_object; } - const ModelObject* model_object() const { return this->_model_object; } - - const Points& copies() const { return this->_copies; } - bool add_copy(const Pointf &point); - bool delete_last_copy(); - bool delete_all_copies() { return this->set_copies(Points()); } - bool set_copies(const Points &points); - bool reload_model_instances(); - // since the object is aligned to origin, bounding box coincides with size - BoundingBox bounding_box() const { return BoundingBox(Point(0,0), this->size); } - - // adds region_id, too, if necessary - void add_region_volume(unsigned int region_id, int volume_id) { - if (region_id >= region_volumes.size()) - region_volumes.resize(region_id + 1); - region_volumes[region_id].push_back(volume_id); - } - // This is the *total* layer count (including support layers) - // this value is not supposed to be compared with Layer::id - // since they have different semantics. - size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } - size_t layer_count() const { return this->layers.size(); } - void clear_layers(); - Layer* get_layer(int idx) { return this->layers.at(idx); } - const Layer* get_layer(int idx) const { return this->layers.at(idx); } - - // print_z: top of the layer; slice_z: center of the layer. - Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); - - size_t support_layer_count() const { return this->support_layers.size(); } - void clear_support_layers(); - SupportLayer* get_support_layer(int idx) { return this->support_layers.at(idx); } - SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z); - void delete_support_layer(int idx); - - // methods for handling state - bool invalidate_state_by_config_options(const std::vector &opt_keys); - bool invalidate_step(PrintObjectStep step); - bool invalidate_all_steps() { return this->state.invalidate_all(); } - - // To be used over the layer_height_profile of both the PrintObject and ModelObject - // to initialize the height profile with the height ranges. - bool update_layer_height_profile(std::vector &layer_height_profile) const; - - // Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile. - // The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces. - bool update_layer_height_profile(); - - void reset_layer_height_profile(); - - void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action); - - // Collect the slicing parameters, to be used by variable layer thickness algorithm, - // by the interactive layer height editor and by the printing process itself. - // The slicing parameters are dependent on various configuration values - // (layer height, first layer height, raft settings, print nozzle diameter etc). - SlicingParameters slicing_parameters() const; - - void _slice(); - std::string _fix_slicing_errors(); - void _simplify_slices(double distance); - void _prepare_infill(); - bool has_support_material() const; - void detect_surfaces_type(); - void process_external_surfaces(); - void discover_vertical_shells(); - void bridge_over_infill(); - void _make_perimeters(); - void _infill(); - void clip_fill_surfaces(); - void discover_horizontal_shells(); - void combine_infill(); - void _generate_support_material(); - - bool is_printable() const { return !this->_shifted_copies.empty(); } - - // Helpers to slice support enforcer / blocker meshes by the support generator. - std::vector slice_support_enforcers() const; - std::vector slice_support_blockers() const; - -private: - Print* _print; - ModelObject* _model_object; - Points _copies; // Slic3r::Point objects in scaled G-code coordinates - - // TODO: call model_object->get_bounding_box() instead of accepting - // parameter - PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); - ~PrintObject() {} - - std::vector _slice_region(size_t region_id, const std::vector &z, bool modifier); - std::vector _slice_volumes(const std::vector &z, const std::vector &volumes) const; -}; - -typedef std::vector PrintObjectPtrs; -typedef std::vector PrintRegionPtrs; - -// The complete print tray with possibly multiple objects. -class Print -{ -public: - PrintConfig config; - PrintObjectConfig default_object_config; - PrintRegionConfig default_region_config; - PrintObjectPtrs objects; - PrintRegionPtrs regions; - PlaceholderParser placeholder_parser; - // TODO: status_cb - std::string estimated_normal_print_time; - std::string estimated_silent_print_time; - double total_used_filament, total_extruded_volume, total_cost, total_weight, total_wipe_tower_cost, total_wipe_tower_filament; - std::map filament_stats; - PrintState state; - - // ordered collections of extrusion paths to build skirt loops and brim - ExtrusionEntityCollection skirt, brim; - - Print() : total_used_filament(0), total_extruded_volume(0) { restart(); } - ~Print() { clear_objects(); } - - // methods for handling objects - void clear_objects(); - PrintObject* get_object(size_t idx) { return objects.at(idx); } - const PrintObject* get_object(size_t idx) const { return objects.at(idx); } - - void delete_object(size_t idx); - void reload_object(size_t idx); - bool reload_model_instances(); - - PrintObjectPtrs get_printable_objects() const; - - // methods for handling regions - PrintRegion* get_region(size_t idx) { return regions.at(idx); } - const PrintRegion* get_region(size_t idx) const { return regions.at(idx); } - PrintRegion* add_region(); - - // methods for handling state - bool invalidate_step(PrintStep step); - bool invalidate_all_steps() { return this->state.invalidate_all(); } - bool step_done(PrintObjectStep step) const; - - void add_model_object(ModelObject* model_object, int idx = -1); - bool apply_config(DynamicPrintConfig config); - float get_wipe_tower_depth() const { return m_wipe_tower_depth; } - bool has_infinite_skirt() const; - bool has_skirt() const; - // Returns an empty string if valid, otherwise returns an error message. - std::string validate() const; - BoundingBox bounding_box() const; - BoundingBox total_bounding_box() const; - double skirt_first_layer_height() const; - Flow brim_flow() const; - Flow skirt_flow() const; - - std::vector object_extruders() const; - std::vector support_material_extruders() const; - std::vector extruders() const; - void _simplify_slices(double distance); - double max_allowed_layer_height() const; - bool has_support_material() const; - void auto_assign_extruders(ModelObject* model_object) const; - - // Returns extruder this eec should be printed with, according to PrintRegion config: - static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion); - - void _make_skirt(); - void _make_brim(); - - // Wipe tower support. - bool has_wipe_tower() const; - void _clear_wipe_tower(); - void _make_wipe_tower(); - // Tool ordering of a non-sequential print has to be known to calculate the wipe tower. - // Cache it here, so it does not need to be recalculated during the G-code generation. - ToolOrdering m_tool_ordering; - // Cache of tool changes per print layer. - std::unique_ptr m_wipe_tower_priming; - std::vector> m_wipe_tower_tool_changes; - std::unique_ptr m_wipe_tower_final_purge; - std::vector m_wipe_tower_used_filament; - int m_wipe_tower_number_of_toolchanges = -1; - - std::string output_filename(); - std::string output_filepath(const std::string &path); - - // Calls a registered callback to update the status. - void set_status(int percent, const std::string &message); - // Cancel the running computation. Stop execution of all the background threads. - void cancel() { m_canceled = true; } - // Cancel the running computation. Stop execution of all the background threads. - void restart() { m_canceled = false; } - // Has the calculation been canceled? - bool canceled() { return m_canceled; } - - -private: - bool invalidate_state_by_config_options(const std::vector &opt_keys); - PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume); - - // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box: - float m_wipe_tower_depth = 0.f; - - // Has the calculation been canceled? - tbb::atomic m_canceled; -}; - - -#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator) -#define FOREACH_REGION(print, region) FOREACH_BASE(PrintRegionPtrs, (print)->regions, region) -#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->objects, object) -#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->layers, layer) -#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->regions, layerm) - -} - -#endif diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp deleted file mode 100644 index 5bb1fffb3c..0000000000 --- a/xs/src/libslic3r/PrintRegion.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "Print.hpp" - -namespace Slic3r { - -Flow -PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const -{ - ConfigOptionFloatOrPercent config_width; - if (width != -1) { - // use the supplied custom width, if any - config_width.value = width; - config_width.percent = false; - } else { - // otherwise, get extrusion width from configuration - // (might be an absolute value, or a percent value, or zero for auto) - if (first_layer && this->_print->config.first_layer_extrusion_width.value > 0) { - config_width = this->_print->config.first_layer_extrusion_width; - } else if (role == frExternalPerimeter) { - config_width = this->config.external_perimeter_extrusion_width; - } else if (role == frPerimeter) { - config_width = this->config.perimeter_extrusion_width; - } else if (role == frInfill) { - config_width = this->config.infill_extrusion_width; - } else if (role == frSolidInfill) { - config_width = this->config.solid_infill_extrusion_width; - } else if (role == frTopSolidInfill) { - config_width = this->config.top_infill_extrusion_width; - } else { - CONFESS("Unknown role"); - } - } - if (config_width.value == 0) { - config_width = object.config.extrusion_width; - } - - // get the configured nozzle_diameter for the extruder associated - // to the flow role requested - size_t extruder = 0; // 1-based - if (role == frPerimeter || role == frExternalPerimeter) { - extruder = this->config.perimeter_extruder; - } else if (role == frInfill) { - extruder = this->config.infill_extruder; - } else if (role == frSolidInfill || role == frTopSolidInfill) { - extruder = this->config.solid_infill_extruder; - } else { - CONFESS("Unknown role $role"); - } - double nozzle_diameter = this->_print->config.nozzle_diameter.get_at(extruder-1); - - return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)this->config.bridge_flow_ratio : 0.0); -} - -coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const -{ - return (print_config.nozzle_diameter.get_at(this->config.perimeter_extruder.value - 1) + - print_config.nozzle_diameter.get_at(this->config.infill_extruder.value - 1) + - print_config.nozzle_diameter.get_at(this->config.solid_infill_extruder.value - 1)) / 3.; -} - -coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const -{ - return this->nozzle_dmr_avg(print_config) * sqrt(this->config.bridge_flow_ratio.value); -} - -} diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index d6bd0e94c3..72184be34b 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -41,9 +41,8 @@ REGISTER_CLASS(BoundingBoxf, "Geometry::BoundingBoxf"); REGISTER_CLASS(BoundingBoxf3, "Geometry::BoundingBoxf3"); REGISTER_CLASS(BridgeDetector, "BridgeDetector"); REGISTER_CLASS(Point, "Point"); -REGISTER_CLASS(Point3, "Point3"); -REGISTER_CLASS(Pointf, "Pointf"); -REGISTER_CLASS(Pointf3, "Pointf3"); +__REGISTER_CLASS(Vec2d, "Pointf"); +__REGISTER_CLASS(Vec3d, "Pointf3"); REGISTER_CLASS(DynamicPrintConfig, "Config"); REGISTER_CLASS(StaticPrintConfig, "Config::Static"); REGISTER_CLASS(PrintObjectConfig, "Config::PrintObject"); @@ -56,6 +55,7 @@ REGISTER_CLASS(SurfaceCollection, "Surface::Collection"); REGISTER_CLASS(PrintObjectSupportMaterial, "Print::SupportMaterial2"); REGISTER_CLASS(TriangleMesh, "TriangleMesh"); REGISTER_CLASS(AppConfig, "GUI::AppConfig"); +REGISTER_CLASS(BackgroundSlicingProcess, "GUI::BackgroundSlicingProcess"); REGISTER_CLASS(GLShader, "GUI::_3DScene::GLShader"); REGISTER_CLASS(GLVolume, "GUI::_3DScene::GLVolume"); REGISTER_CLASS(GLVolumeCollection, "GUI::_3DScene::GLVolume::Collection"); @@ -63,8 +63,9 @@ REGISTER_CLASS(Preset, "GUI::Preset"); REGISTER_CLASS(PresetCollection, "GUI::PresetCollection"); REGISTER_CLASS(PresetBundle, "GUI::PresetBundle"); REGISTER_CLASS(TabIface, "GUI::Tab"); +REGISTER_CLASS(PreviewIface, "GUI::Preview"); +REGISTER_CLASS(ProgressStatusBar, "GUI::ProgressStatusBar"); REGISTER_CLASS(PresetUpdater, "PresetUpdater"); -REGISTER_CLASS(AppController, "AppController"); REGISTER_CLASS(PrintController, "PrintController"); REGISTER_CLASS(PrintHost, "PrintHost"); @@ -133,7 +134,7 @@ SV* ConfigOption_to_SV(const ConfigOption &opt, const ConfigOptionDef &def) auto optv = static_cast(&opt); AV* av = newAV(); av_fill(av, optv->values.size()-1); - for (const Pointf &v : optv->values) + for (const Vec2d &v : optv->values) av_store(av, &v - optv->values.data(), perl_to_SV_clone_ref(v)); return newRV_noinc((SV*)av); } @@ -263,14 +264,14 @@ bool ConfigBase__set(ConfigBase* THIS, const t_config_option_key &opt_key, SV* v return from_SV_check(value, &static_cast(opt)->value); case coPoints: { - std::vector &values = static_cast(opt)->values; + std::vector &values = static_cast(opt)->values; AV* av = (AV*)SvRV(value); const size_t len = av_len(av)+1; values.clear(); values.reserve(len); for (size_t i = 0; i < len; i++) { SV** elem = av_fetch(av, i, 0); - Pointf point; + Vec2d point(Vec2d::Zero()); if (elem == NULL || !from_SV_check(*elem, &point)) return false; values.emplace_back(point); } @@ -485,8 +486,8 @@ SV* to_SV_pureperl(const Point* THIS) { AV* av = newAV(); av_fill(av, 1); - av_store(av, 0, newSViv(THIS->x)); - av_store(av, 1, newSViv(THIS->y)); + av_store(av, 0, newSViv((*THIS)(0))); + av_store(av, 1, newSViv((*THIS)(1))); return newRV_noinc((SV*)av); } @@ -495,8 +496,7 @@ void from_SV(SV* point_sv, Point* point) AV* point_av = (AV*)SvRV(point_sv); // get a double from Perl and round it, otherwise // it would get truncated - point->x = lrint(SvNV(*av_fetch(point_av, 0, 0))); - point->y = lrint(SvNV(*av_fetch(point_av, 1, 0))); + (*point) = Point(SvNV(*av_fetch(point_av, 0, 0)), SvNV(*av_fetch(point_av, 1, 0))); } void from_SV_check(SV* point_sv, Point* point) @@ -510,33 +510,32 @@ void from_SV_check(SV* point_sv, Point* point) } } -SV* to_SV_pureperl(const Pointf* point) +SV* to_SV_pureperl(const Vec2d* point) { AV* av = newAV(); av_fill(av, 1); - av_store(av, 0, newSVnv(point->x)); - av_store(av, 1, newSVnv(point->y)); + av_store(av, 0, newSVnv((*point)(0))); + av_store(av, 1, newSVnv((*point)(1))); return newRV_noinc((SV*)av); } -bool from_SV(SV* point_sv, Pointf* point) +bool from_SV(SV* point_sv, Vec2d* point) { AV* point_av = (AV*)SvRV(point_sv); SV* sv_x = *av_fetch(point_av, 0, 0); SV* sv_y = *av_fetch(point_av, 1, 0); if (!looks_like_number(sv_x) || !looks_like_number(sv_y)) return false; - point->x = SvNV(sv_x); - point->y = SvNV(sv_y); + *point = Vec2d(SvNV(sv_x), SvNV(sv_y)); return true; } -bool from_SV_check(SV* point_sv, Pointf* point) +bool from_SV_check(SV* point_sv, Vec2d* point) { if (sv_isobject(point_sv) && (SvTYPE(SvRV(point_sv)) == SVt_PVMG)) { if (!sv_isa(point_sv, perl_class_name(point)) && !sv_isa(point_sv, perl_class_name_ref(point))) CONFESS("Not a valid %s object (got %s)", perl_class_name(point), HvNAME(SvSTASH(SvRV(point_sv)))); - *point = *(Pointf*)SvIV((SV*)SvRV( point_sv )); + *point = *(Vec2d*)SvIV((SV*)SvRV( point_sv )); return true; } else { return from_SV(point_sv, point); diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp deleted file mode 100644 index 7bd044b25d..0000000000 --- a/xs/src/slic3r/AppController.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "AppController.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace Slic3r { - -class AppControllerBoilerplate::PriData { -public: - std::mutex m; - std::thread::id ui_thread; - - inline explicit PriData(std::thread::id uit): ui_thread(uit) {} -}; - -AppControllerBoilerplate::AppControllerBoilerplate() - :pri_data_(new PriData(std::this_thread::get_id())) {} - -AppControllerBoilerplate::~AppControllerBoilerplate() { - pri_data_.reset(); -} - -bool AppControllerBoilerplate::is_main_thread() const -{ - return pri_data_->ui_thread == std::this_thread::get_id(); -} - -namespace GUI { -PresetBundle* get_preset_bundle(); -} - -AppControllerBoilerplate::ProgresIndicatorPtr -AppControllerBoilerplate::global_progress_indicator() { - ProgresIndicatorPtr ret; - - pri_data_->m.lock(); - ret = global_progressind_; - pri_data_->m.unlock(); - - return ret; -} - -void AppControllerBoilerplate::global_progress_indicator( - AppControllerBoilerplate::ProgresIndicatorPtr gpri) -{ - pri_data_->m.lock(); - global_progressind_ = gpri; - pri_data_->m.unlock(); -} - -void ProgressIndicator::message_fmt( - const std::string &fmtstr, ...) { - std::stringstream ss; - va_list args; - va_start(args, fmtstr); - - auto fmt = fmtstr.begin(); - - while (*fmt != '\0') { - if (*fmt == 'd') { - int i = va_arg(args, int); - ss << i << '\n'; - } else if (*fmt == 'c') { - // note automatic conversion to integral type - int c = va_arg(args, int); - ss << static_cast(c) << '\n'; - } else if (*fmt == 'f') { - double d = va_arg(args, double); - ss << d << '\n'; - } - ++fmt; - } - - va_end(args); - message(ss.str()); -} - -const PrintConfig &PrintController::config() const -{ - return print_->config; -} - -void AppController::arrange_model() -{ - auto ftr = std::async( - supports_asynch()? std::launch::async : std::launch::deferred, - [this]() - { - using Coord = libnest2d::TCoord; - - unsigned count = 0; - for(auto obj : model_->objects) count += obj->instances.size(); - - auto pind = global_progress_indicator(); - - float pmax = 1.0; - - if(pind) { - pmax = pind->max(); - - // Set the range of the progress to the object count - pind->max(count); - - } - - auto dist = print_ctl()->config().min_object_distance(); - - // Create the arranger config - auto min_obj_distance = static_cast(dist/SCALING_FACTOR); - - auto& bedpoints = print_ctl()->config().bed_shape.values; - Polyline bed; bed.points.reserve(bedpoints.size()); - for(auto& v : bedpoints) - bed.append(Point::new_scale(v.x, v.y)); - - if(pind) pind->update(0, L("Arranging objects...")); - - try { - arr::BedShapeHint hint; - // TODO: from Sasha from GUI - hint.type = arr::BedShapeType::WHO_KNOWS; - - arr::arrange(*model_, - min_obj_distance, - bed, - hint, - false, // create many piles not just one pile - [pind, count](unsigned rem) { - if(pind) - pind->update(count - rem, L("Arranging objects...")); - }); - } catch(std::exception& e) { - std::cerr << e.what() << std::endl; - report_issue(IssueType::ERR, - L("Could not arrange model objects! " - "Some geometries may be invalid."), - L("Exception occurred")); - } - - // Restore previous max value - if(pind) { - pind->max(pmax); - pind->update(0, L("Arranging done.")); - } - }); - - while( ftr.wait_for(std::chrono::milliseconds(10)) - != std::future_status::ready) { - process_events(); - } -} - -} diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp deleted file mode 100644 index 3ef47ffdc5..0000000000 --- a/xs/src/slic3r/AppController.hpp +++ /dev/null @@ -1,244 +0,0 @@ -#ifndef APPCONTROLLER_HPP -#define APPCONTROLLER_HPP - -#include -#include -#include -#include -#include - -#include "ProgressIndicator.hpp" - -namespace Slic3r { - -class Model; -class Print; -class PrintObject; -class PrintConfig; -class ProgressStatusBar; -class DynamicPrintConfig; - -/** - * @brief A boilerplate class for creating application logic. It should provide - * features as issue reporting and progress indication, etc... - * - * The lower lever UI independent classes can be manipulated with a subclass - * of this controller class. We can also catch any exceptions that lower level - * methods could throw and display appropriate errors and warnings. - * - * Note that the outer and the inner interface of this class is free from any - * UI toolkit dependencies. We can implement it with any UI framework or make it - * a cli client. - */ -class AppControllerBoilerplate { -public: - - /// A Progress indicator object smart pointer - using ProgresIndicatorPtr = std::shared_ptr; - -private: - class PriData; // Some structure to store progress indication data - - // Pimpl data for thread safe progress indication features - std::unique_ptr pri_data_; - -public: - - AppControllerBoilerplate(); - ~AppControllerBoilerplate(); - - using Path = std::string; - using PathList = std::vector; - - /// Common runtime issue types - enum class IssueType { - INFO, - WARN, - WARN_Q, // Warning with a question to continue - ERR, - FATAL - }; - - /** - * @brief Query some paths from the user. - * - * It should display a file chooser dialog in case of a UI application. - * @param title Title of a possible query dialog. - * @param extensions Recognized file extensions. - * @return Returns a list of paths choosed by the user. - */ - PathList query_destination_paths( - const std::string& title, - const std::string& extensions) const; - - /** - * @brief Same as query_destination_paths but works for directories only. - */ - PathList query_destination_dirs( - const std::string& title) const; - - /** - * @brief Same as query_destination_paths but returns only one path. - */ - Path query_destination_path( - const std::string& title, - const std::string& extensions, - const std::string& hint = "") const; - - /** - * @brief Report an issue to the user be it fatal or recoverable. - * - * In a UI app this should display some message dialog. - * - * @param issuetype The type of the runtime issue. - * @param description A somewhat longer description of the issue. - * @param brief A very brief description. Can be used for message dialog - * title. - */ - bool report_issue(IssueType issuetype, - const std::string& description, - const std::string& brief); - - bool report_issue(IssueType issuetype, - const std::string& description); - - /** - * @brief Return the global progress indicator for the current controller. - * Can be empty as well. - * - * Only one thread should use the global indicator at a time. - */ - ProgresIndicatorPtr global_progress_indicator(); - - void global_progress_indicator(ProgresIndicatorPtr gpri); - - /** - * @brief A predicate telling the caller whether it is the thread that - * created the AppConroller object itself. This probably means that the - * execution is in the UI thread. Otherwise it returns false meaning that - * some worker thread called this function. - * @return Return true for the same caller thread that created this - * object and false for every other. - */ - bool is_main_thread() const; - - /** - * @brief The frontend supports asynch execution. - * - * A Graphic UI will support this, a CLI may not. This can be used in - * subclass methods to decide whether to start threads for block free UI. - * - * Note that even a progress indicator's update called regularly can solve - * the blocking UI problem in some cases even when an event loop is present. - * This is how wxWidgets gauge work but creating a separate thread will make - * the UI even more fluent. - * - * @return true if a job or method can be executed asynchronously, false - * otherwise. - */ - bool supports_asynch() const; - - void process_events(); - -protected: - - /** - * @brief Create a new progress indicator and return a smart pointer to it. - * @param statenum The number of states for the given procedure. - * @param title The title of the procedure. - * @param firstmsg The message for the first subtask to be displayed. - * @return Smart pointer to the created object. - */ - ProgresIndicatorPtr create_progress_indicator( - unsigned statenum, - const std::string& title, - const std::string& firstmsg) const; - - ProgresIndicatorPtr create_progress_indicator( - unsigned statenum, - const std::string& title) const; - - // This is a global progress indicator placeholder. In the Slic3r UI it can - // contain the progress indicator on the statusbar. - ProgresIndicatorPtr global_progressind_; -}; - -/** - * @brief Implementation of the printing logic. - */ -class PrintController: public AppControllerBoilerplate { - Print *print_ = nullptr; -public: - - // Must be public for perl to use it - explicit inline PrintController(Print *print): print_(print) {} - - PrintController(const PrintController&) = delete; - PrintController(PrintController&&) = delete; - - using Ptr = std::unique_ptr; - - inline static Ptr create(Print *print) { - return PrintController::Ptr( new PrintController(print) ); - } - - const PrintConfig& config() const; -}; - -/** - * @brief Top level controller. - */ -class AppController: public AppControllerBoilerplate { - Model *model_ = nullptr; - PrintController::Ptr printctl; -public: - - /** - * @brief Get the print controller object. - * - * @return Return a raw pointer instead of a smart one for perl to be able - * to use this function and access the print controller. - */ - PrintController * print_ctl() { return printctl.get(); } - - /** - * @brief Set a model object. - * - * @param model A raw pointer to the model object. This can be used from - * perl. - */ - void set_model(Model *model) { model_ = model; } - - /** - * @brief Set the print object from perl. - * - * This will create a print controller that will then be accessible from - * perl. - * @param print A print object which can be a perl-ish extension as well. - */ - void set_print(Print *print) { - printctl = PrintController::create(print); - } - - /** - * @brief Set up a global progress indicator. - * - * In perl we have a progress indicating status bar on the bottom of the - * window which is defined and created in perl. We can pass the ID-s of the - * gauge and the statusbar id and make a wrapper implementation of the - * ProgressIndicator interface so we can use this GUI widget from C++. - * - * This function should be called from perl. - * - * @param gauge_id The ID of the gague widget of the status bar. - * @param statusbar_id The ID of the status bar. - */ - void set_global_progress_indicator(unsigned gauge_id, - unsigned statusbar_id); - - void arrange_model(); -}; - -} - -#endif // APPCONTROLLER_HPP diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp deleted file mode 100644 index 36a465919f..0000000000 --- a/xs/src/slic3r/AppControllerWx.cpp +++ /dev/null @@ -1,307 +0,0 @@ -#include "AppController.hpp" - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -// This source file implements the UI dependent methods of the AppControllers. -// It will be clear what is needed to be reimplemented in case of a UI framework -// change or a CLI client creation. In this particular case we use wxWidgets to -// implement everything. - -namespace Slic3r { - -bool AppControllerBoilerplate::supports_asynch() const -{ - return true; -} - -void AppControllerBoilerplate::process_events() -{ - wxSafeYield(); -} - -AppControllerBoilerplate::PathList -AppControllerBoilerplate::query_destination_paths( - const std::string &title, - const std::string &extensions) const -{ - - wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) ); - dlg.SetWildcard(extensions); - - dlg.ShowModal(); - - wxArrayString paths; - dlg.GetPaths(paths); - - PathList ret(paths.size(), ""); - for(auto& p : paths) ret.push_back(p.ToStdString()); - - return ret; -} - -AppControllerBoilerplate::Path -AppControllerBoilerplate::query_destination_path( - const std::string &title, - const std::string &extensions, - const std::string& hint) const -{ - wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) ); - dlg.SetWildcard(extensions); - - dlg.SetFilename(hint); - - Path ret; - - if(dlg.ShowModal() == wxID_OK) { - ret = Path(dlg.GetPath()); - } - - return ret; -} - -bool AppControllerBoilerplate::report_issue(IssueType issuetype, - const std::string &description, - const std::string &brief) -{ - auto icon = wxICON_INFORMATION; - auto style = wxOK|wxCENTRE; - switch(issuetype) { - case IssueType::INFO: break; - case IssueType::WARN: icon = wxICON_WARNING; break; - case IssueType::WARN_Q: icon = wxICON_WARNING; style |= wxCANCEL; break; - case IssueType::ERR: - case IssueType::FATAL: icon = wxICON_ERROR; - } - - auto ret = wxMessageBox(_(description), _(brief), icon | style); - return ret != wxCANCEL; -} - -bool AppControllerBoilerplate::report_issue( - AppControllerBoilerplate::IssueType issuetype, - const std::string &description) -{ - return report_issue(issuetype, description, std::string()); -} - -wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent); - -namespace { - -/* - * A simple thread safe progress dialog implementation that can be used from - * the main thread as well. - */ -class GuiProgressIndicator: - public ProgressIndicator, public wxEvtHandler { - - wxProgressDialog gauge_; - using Base = ProgressIndicator; - wxString message_; - int range_; wxString title_; - bool is_asynch_ = false; - - const int id_ = wxWindow::NewControlId(); - - // status update handler - void _state( wxCommandEvent& evt) { - unsigned st = evt.GetInt(); - message_ = evt.GetString(); - _state(st); - } - - // Status update implementation - void _state( unsigned st) { - if(!gauge_.IsShown()) gauge_.ShowModal(); - Base::state(st); - gauge_.Update(static_cast(st), message_); - } - -public: - - /// Setting whether it will be used from the UI thread or some worker thread - inline void asynch(bool is) { is_asynch_ = is; } - - /// Get the mode of parallel operation. - inline bool asynch() const { return is_asynch_; } - - inline GuiProgressIndicator(int range, const wxString& title, - const wxString& firstmsg) : - gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(), - wxPD_APP_MODAL | wxPD_AUTO_HIDE), - message_(firstmsg), - range_(range), title_(title) - { - Base::max(static_cast(range)); - Base::states(static_cast(range)); - - Bind(PROGRESS_STATUS_UPDATE_EVENT, - &GuiProgressIndicator::_state, - this, id_); - } - - virtual void state(float val) override { - state(static_cast(val)); - } - - void state(unsigned st) { - // send status update event - if(is_asynch_) { - auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_); - evt->SetInt(st); - evt->SetString(message_); - wxQueueEvent(this, evt); - } else _state(st); - } - - virtual void message(const std::string & msg) override { - message_ = _(msg); - } - - virtual void messageFmt(const std::string& fmt, ...) { - va_list arglist; - va_start(arglist, fmt); - message_ = wxString::Format(_(fmt), arglist); - va_end(arglist); - } - - virtual void title(const std::string & title) override { - title_ = _(title); - } -}; -} - -AppControllerBoilerplate::ProgresIndicatorPtr -AppControllerBoilerplate::create_progress_indicator( - unsigned statenum, - const std::string& title, - const std::string& firstmsg) const -{ - auto pri = - std::make_shared(statenum, title, firstmsg); - - // We set up the mode of operation depending of the creator thread's - // identity - pri->asynch(!is_main_thread()); - - return pri; -} - -AppControllerBoilerplate::ProgresIndicatorPtr -AppControllerBoilerplate::create_progress_indicator( - unsigned statenum, const std::string &title) const -{ - return create_progress_indicator(statenum, title, std::string()); -} - -namespace { - -// A wrapper progress indicator class around the statusbar created in perl. -class Wrapper: public ProgressIndicator, public wxEvtHandler { - wxGauge *gauge_; - wxStatusBar *stbar_; - using Base = ProgressIndicator; - wxString message_; - AppControllerBoilerplate& ctl_; - - void showProgress(bool show = true) { - gauge_->Show(show); - } - - void _state(unsigned st) { - if( st <= ProgressIndicator::max() ) { - Base::state(st); - - if(!gauge_->IsShown()) showProgress(true); - - stbar_->SetStatusText(message_); - if(static_cast(st) == gauge_->GetRange()) { - gauge_->SetValue(0); - showProgress(false); - } else { - gauge_->SetValue(static_cast(st)); - } - } - } - - // status update handler - void _state( wxCommandEvent& evt) { - unsigned st = evt.GetInt(); _state(st); - } - - const int id_ = wxWindow::NewControlId(); - -public: - - inline Wrapper(wxGauge *gauge, wxStatusBar *stbar, - AppControllerBoilerplate& ctl): - gauge_(gauge), stbar_(stbar), ctl_(ctl) - { - Base::max(static_cast(gauge->GetRange())); - Base::states(static_cast(gauge->GetRange())); - - Bind(PROGRESS_STATUS_UPDATE_EVENT, - &Wrapper::_state, - this, id_); - } - - virtual void state(float val) override { - state(unsigned(val)); - } - - virtual void max(float val) override { - if(val > 1.0) { - gauge_->SetRange(static_cast(val)); - ProgressIndicator::max(val); - } - } - - void state(unsigned st) { - if(!ctl_.is_main_thread()) { - auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_); - evt->SetInt(st); - wxQueueEvent(this, evt); - } else { - _state(st); - } - } - - virtual void message(const std::string & msg) override { - message_ = _(msg); - } - - virtual void message_fmt(const std::string& fmt, ...) override { - va_list arglist; - va_start(arglist, fmt); - message_ = wxString::Format(_(fmt), arglist); - va_end(arglist); - } - - virtual void title(const std::string & /*title*/) override {} - -}; -} - -void AppController::set_global_progress_indicator( - unsigned gid, - unsigned sid) -{ - wxGauge* gauge = dynamic_cast(wxWindow::FindWindowById(gid)); - wxStatusBar* sb = dynamic_cast(wxWindow::FindWindowById(sid)); - - if(gauge && sb) { - global_progressind_ = std::make_shared(gauge, sb, *this); - } -} -} diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp deleted file mode 100644 index bbd8f44eb5..0000000000 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ /dev/null @@ -1,830 +0,0 @@ -#include "GLGizmo.hpp" - -#include "../../libslic3r/Utils.hpp" -#include "../../libslic3r/BoundingBox.hpp" -#include "../../libslic3r/Model.hpp" -#include "../../libslic3r/Geometry.hpp" - -#include - -#include -#include - -namespace Slic3r { -namespace GUI { - -const float GLGizmoBase::Grabber::HalfSize = 2.0f; -const float GLGizmoBase::Grabber::HoverOffset = 0.5f; -const float GLGizmoBase::BaseColor[3] = { 1.0f, 1.0f, 1.0f }; -const float GLGizmoBase::HighlightColor[3] = { 1.0f, 0.38f, 0.0f }; - -GLGizmoBase::Grabber::Grabber() - : center(Pointf(0.0, 0.0)) - , angle_z(0.0f) -{ - color[0] = 1.0f; - color[1] = 1.0f; - color[2] = 1.0f; -} - -void GLGizmoBase::Grabber::render(bool hover) const -{ - float min_x = -HalfSize; - float max_x = +HalfSize; - float min_y = -HalfSize; - float max_y = +HalfSize; - - ::glColor3f((GLfloat)color[0], (GLfloat)color[1], (GLfloat)color[2]); - - float angle_z_in_deg = angle_z * 180.0f / (float)PI; - ::glPushMatrix(); - ::glTranslatef((GLfloat)center.x, (GLfloat)center.y, 0.0f); - ::glRotatef((GLfloat)angle_z_in_deg, 0.0f, 0.0f, 1.0f); - - ::glDisable(GL_CULL_FACE); - ::glBegin(GL_TRIANGLES); - ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); - ::glEnd(); - ::glEnable(GL_CULL_FACE); - - if (hover) - { - min_x -= HoverOffset; - max_x += HoverOffset; - min_y -= HoverOffset; - max_y += HoverOffset; - - ::glBegin(GL_LINE_LOOP); - ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); - ::glEnd(); - } - - ::glPopMatrix(); -} - -GLGizmoBase::GLGizmoBase() - : m_state(Off) - , m_hover_id(-1) -{ -} - -GLGizmoBase::~GLGizmoBase() -{ -} - -bool GLGizmoBase::init() -{ - return on_init(); -} - -GLGizmoBase::EState GLGizmoBase::get_state() const -{ - return m_state; -} - -void GLGizmoBase::set_state(GLGizmoBase::EState state) -{ - m_state = state; - on_set_state(); -} - -unsigned int GLGizmoBase::get_texture_id() const -{ - return m_textures[m_state].get_id(); -} - -int GLGizmoBase::get_textures_size() const -{ - return m_textures[Off].get_width(); -} - -int GLGizmoBase::get_hover_id() const -{ - return m_hover_id; -} - -void GLGizmoBase::set_hover_id(int id) -{ - //if (id < (int)m_grabbers.size()) - m_hover_id = id; -} - -void GLGizmoBase::start_dragging() -{ - on_start_dragging(); -} - -void GLGizmoBase::stop_dragging() -{ - on_stop_dragging(); -} - -void GLGizmoBase::update(const Pointf& mouse_pos) -{ - if (m_hover_id != -1) - on_update(mouse_pos); -} - -void GLGizmoBase::refresh() -{ - on_refresh(); -} - -void GLGizmoBase::render(const BoundingBoxf3& box) const -{ - on_render(box); -} - -void GLGizmoBase::render_for_picking(const BoundingBoxf3& box) const -{ - on_render_for_picking(box); -} - -void GLGizmoBase::on_set_state() -{ - // do nothing -} - -void GLGizmoBase::on_start_dragging() -{ - // do nothing -} - -void GLGizmoBase::on_stop_dragging() -{ - // do nothing -} - -void GLGizmoBase::on_refresh() -{ - // do nothing -} - -void GLGizmoBase::render_grabbers() const -{ - for (int i = 0; i < (int)m_grabbers.size(); ++i) - { - m_grabbers[i].render(m_hover_id == i); - } -} - -const float GLGizmoRotate::Offset = 5.0f; -const unsigned int GLGizmoRotate::CircleResolution = 64; -const unsigned int GLGizmoRotate::AngleResolution = 64; -const unsigned int GLGizmoRotate::ScaleStepsCount = 60; -const float GLGizmoRotate::ScaleStepRad = 2.0f * (float)PI / GLGizmoRotate::ScaleStepsCount; -const unsigned int GLGizmoRotate::ScaleLongEvery = 5; -const float GLGizmoRotate::ScaleLongTooth = 2.0f; -const float GLGizmoRotate::ScaleShortTooth = 1.0f; -const unsigned int GLGizmoRotate::SnapRegionsCount = 8; -const float GLGizmoRotate::GrabberOffset = 5.0f; - -GLGizmoRotate::GLGizmoRotate() - : GLGizmoBase() - , m_angle_z(0.0f) - , m_center(Pointf(0.0, 0.0)) - , m_radius(0.0f) - , m_keep_initial_values(false) -{ -} - -float GLGizmoRotate::get_angle_z() const -{ - return m_angle_z; -} - -void GLGizmoRotate::set_angle_z(float angle_z) -{ - if (std::abs(angle_z - 2.0f * PI) < EPSILON) - angle_z = 0.0f; - - m_angle_z = angle_z; -} - -bool GLGizmoRotate::on_init() -{ - std::string path = resources_dir() + "/icons/overlay/"; - - std::string filename = path + "rotate_off.png"; - if (!m_textures[Off].load_from_file(filename, false)) - return false; - - filename = path + "rotate_hover.png"; - if (!m_textures[Hover].load_from_file(filename, false)) - return false; - - filename = path + "rotate_on.png"; - if (!m_textures[On].load_from_file(filename, false)) - return false; - - m_grabbers.push_back(Grabber()); - - return true; -} - -void GLGizmoRotate::on_set_state() -{ - m_keep_initial_values = (m_state == On) ? false : true; -} - -void GLGizmoRotate::on_update(const Pointf& mouse_pos) -{ - Vectorf orig_dir(1.0, 0.0); - Vectorf new_dir = normalize(mouse_pos - m_center); - coordf_t theta = ::acos(clamp(-1.0, 1.0, dot(new_dir, orig_dir))); - if (cross(orig_dir, new_dir) < 0.0) - theta = 2.0 * (coordf_t)PI - theta; - - // snap - if (length(m_center.vector_to(mouse_pos)) < 2.0 * (double)m_radius / 3.0) - { - coordf_t step = 2.0 * (coordf_t)PI / (coordf_t)SnapRegionsCount; - theta = step * (coordf_t)std::round(theta / step); - } - - if (theta == 2.0 * (coordf_t)PI) - theta = 0.0; - - m_angle_z = (float)theta; -} - -void GLGizmoRotate::on_refresh() -{ - m_keep_initial_values = false; -} - -void GLGizmoRotate::on_render(const BoundingBoxf3& box) const -{ - ::glDisable(GL_DEPTH_TEST); - - if (!m_keep_initial_values) - { - const Pointf3& size = box.size(); - m_center = box.center(); - m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y)); - m_keep_initial_values = true; - } - - ::glLineWidth(2.0f); - ::glColor3fv(BaseColor); - - _render_circle(); - _render_scale(); - _render_snap_radii(); - _render_reference_radius(); - - ::glColor3fv(HighlightColor); - _render_angle_z(); - _render_grabber(); -} - -void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const -{ - ::glDisable(GL_DEPTH_TEST); - - m_grabbers[0].color[0] = 1.0f; - m_grabbers[0].color[1] = 1.0f; - m_grabbers[0].color[2] = 254.0f / 255.0f; - render_grabbers(); -} - -void GLGizmoRotate::_render_circle() const -{ - ::glBegin(GL_LINE_LOOP); - for (unsigned int i = 0; i < ScaleStepsCount; ++i) - { - float angle = (float)i * ScaleStepRad; - float x = m_center.x + ::cos(angle) * m_radius; - float y = m_center.y + ::sin(angle) * m_radius; - ::glVertex3f((GLfloat)x, (GLfloat)y, 0.0f); - } - ::glEnd(); -} - -void GLGizmoRotate::_render_scale() const -{ - float out_radius_long = m_radius + ScaleLongTooth; - float out_radius_short = m_radius + ScaleShortTooth; - - ::glBegin(GL_LINES); - for (unsigned int i = 0; i < ScaleStepsCount; ++i) - { - float angle = (float)i * ScaleStepRad; - float cosa = ::cos(angle); - float sina = ::sin(angle); - float in_x = m_center.x + cosa * m_radius; - float in_y = m_center.y + sina * m_radius; - float out_x = (i % ScaleLongEvery == 0) ? m_center.x + cosa * out_radius_long : m_center.x + cosa * out_radius_short; - float out_y = (i % ScaleLongEvery == 0) ? m_center.y + sina * out_radius_long : m_center.y + sina * out_radius_short; - ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, 0.0f); - ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, 0.0f); - } - ::glEnd(); -} - -void GLGizmoRotate::_render_snap_radii() const -{ - float step = 2.0f * (float)PI / (float)SnapRegionsCount; - - float in_radius = m_radius / 3.0f; - float out_radius = 2.0f * in_radius; - - ::glBegin(GL_LINES); - for (unsigned int i = 0; i < SnapRegionsCount; ++i) - { - float angle = (float)i * step; - float cosa = ::cos(angle); - float sina = ::sin(angle); - float in_x = m_center.x + cosa * in_radius; - float in_y = m_center.y + sina * in_radius; - float out_x = m_center.x + cosa * out_radius; - float out_y = m_center.y + sina * out_radius; - ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, 0.0f); - ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, 0.0f); - } - ::glEnd(); -} - -void GLGizmoRotate::_render_reference_radius() const -{ - ::glBegin(GL_LINES); - ::glVertex3f((GLfloat)m_center.x, (GLfloat)m_center.y, 0.0f); - ::glVertex3f((GLfloat)m_center.x + m_radius + GrabberOffset, (GLfloat)m_center.y, 0.0f); - ::glEnd(); -} - -void GLGizmoRotate::_render_angle_z() const -{ - float step_angle = m_angle_z / AngleResolution; - float ex_radius = m_radius + GrabberOffset; - - ::glBegin(GL_LINE_STRIP); - for (unsigned int i = 0; i <= AngleResolution; ++i) - { - float angle = (float)i * step_angle; - float x = m_center.x + ::cos(angle) * ex_radius; - float y = m_center.y + ::sin(angle) * ex_radius; - ::glVertex3f((GLfloat)x, (GLfloat)y, 0.0f); - } - ::glEnd(); -} - -void GLGizmoRotate::_render_grabber() const -{ - float grabber_radius = m_radius + GrabberOffset; - m_grabbers[0].center.x = m_center.x + ::cos(m_angle_z) * grabber_radius; - m_grabbers[0].center.y = m_center.y + ::sin(m_angle_z) * grabber_radius; - m_grabbers[0].angle_z = m_angle_z; - - ::glColor3fv(BaseColor); - ::glBegin(GL_LINES); - ::glVertex3f((GLfloat)m_center.x, (GLfloat)m_center.y, 0.0f); - ::glVertex3f((GLfloat)m_grabbers[0].center.x, (GLfloat)m_grabbers[0].center.y, 0.0f); - ::glEnd(); - - ::memcpy((void*)m_grabbers[0].color, (const void*)HighlightColor, 3 * sizeof(float)); - render_grabbers(); -} - -const float GLGizmoScale::Offset = 5.0f; - -GLGizmoScale::GLGizmoScale() - : GLGizmoBase() - , m_scale(1.0f) - , m_starting_scale(1.0f) -{ -} - -float GLGizmoScale::get_scale() const -{ - return m_scale; -} - -void GLGizmoScale::set_scale(float scale) -{ - m_starting_scale = scale; -} - -bool GLGizmoScale::on_init() -{ - std::string path = resources_dir() + "/icons/overlay/"; - - std::string filename = path + "scale_off.png"; - if (!m_textures[Off].load_from_file(filename, false)) - return false; - - filename = path + "scale_hover.png"; - if (!m_textures[Hover].load_from_file(filename, false)) - return false; - - filename = path + "scale_on.png"; - if (!m_textures[On].load_from_file(filename, false)) - return false; - - for (unsigned int i = 0; i < 4; ++i) - { - m_grabbers.push_back(Grabber()); - } - - return true; -} - -void GLGizmoScale::on_start_dragging() -{ - if (m_hover_id != -1) - m_starting_drag_position = m_grabbers[m_hover_id].center; -} - -void GLGizmoScale::on_update(const Pointf& mouse_pos) -{ - Pointf center(0.5 * (m_grabbers[1].center.x + m_grabbers[0].center.x), 0.5 * (m_grabbers[3].center.y + m_grabbers[0].center.y)); - - coordf_t orig_len = length(m_starting_drag_position - center); - coordf_t new_len = length(mouse_pos - center); - coordf_t ratio = (orig_len != 0.0) ? new_len / orig_len : 1.0; - - m_scale = m_starting_scale * (float)ratio; -} - -void GLGizmoScale::on_render(const BoundingBoxf3& box) const -{ - ::glDisable(GL_DEPTH_TEST); - - coordf_t min_x = box.min.x - (coordf_t)Offset; - coordf_t max_x = box.max.x + (coordf_t)Offset; - coordf_t min_y = box.min.y - (coordf_t)Offset; - coordf_t max_y = box.max.y + (coordf_t)Offset; - - m_grabbers[0].center.x = min_x; - m_grabbers[0].center.y = min_y; - m_grabbers[1].center.x = max_x; - m_grabbers[1].center.y = min_y; - m_grabbers[2].center.x = max_x; - m_grabbers[2].center.y = max_y; - m_grabbers[3].center.x = min_x; - m_grabbers[3].center.y = max_y; - - ::glLineWidth(2.0f); - ::glColor3fv(BaseColor); - // draw outline - ::glBegin(GL_LINE_LOOP); - for (unsigned int i = 0; i < 4; ++i) - { - ::glVertex3f((GLfloat)m_grabbers[i].center.x, (GLfloat)m_grabbers[i].center.y, 0.0f); - } - ::glEnd(); - - // draw grabbers - for (unsigned int i = 0; i < 4; ++i) - { - ::memcpy((void*)m_grabbers[i].color, (const void*)HighlightColor, 3 * sizeof(float)); - } - render_grabbers(); -} - -void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const -{ - static const GLfloat INV_255 = 1.0f / 255.0f; - - ::glDisable(GL_DEPTH_TEST); - - for (unsigned int i = 0; i < 4; ++i) - { - m_grabbers[i].color[0] = 1.0f; - m_grabbers[i].color[1] = 1.0f; - m_grabbers[i].color[2] = (254.0f - (float)i) * INV_255; - } - render_grabbers(); -} - - -GLGizmoFlatten::GLGizmoFlatten() - : GLGizmoBase(), - m_normal(Pointf3(0.f, 0.f, 0.f)) -{} - - -bool GLGizmoFlatten::on_init() -{ - std::string path = resources_dir() + "/icons/overlay/"; - - std::string filename = path + "layflat_off.png"; - if (!m_textures[Off].load_from_file(filename, false)) - return false; - - filename = path + "layflat_hover.png"; - if (!m_textures[Hover].load_from_file(filename, false)) - return false; - - filename = path + "layflat_on.png"; - if (!m_textures[On].load_from_file(filename, false)) - return false; - - return true; -} - -void GLGizmoFlatten::on_start_dragging() -{ - if (m_hover_id != -1) - m_normal = m_planes[m_hover_id].normal; -} - -void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const -{ - // the dragged_offset is a vector measuring where was the object moved - // with the gizmo being on. This is reset in set_flattening_data and - // does not work correctly when there are multiple copies. - if (!m_center) // this is the first bounding box that we see - m_center.reset(new Pointf3(box.center().x, box.center().y)); - Pointf3 dragged_offset = box.center() - *m_center; - - bool blending_was_enabled = ::glIsEnabled(GL_BLEND); - bool depth_test_was_enabled = ::glIsEnabled(GL_DEPTH_TEST); - ::glEnable(GL_BLEND); - ::glEnable(GL_DEPTH_TEST); - - for (int i=0; i<(int)m_planes.size(); ++i) { - if (i == m_hover_id) - ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f); - else - ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); - - for (Pointf offset : m_instances_positions) { - offset += dragged_offset; - ::glBegin(GL_POLYGON); - for (const auto& vertex : m_planes[i].vertices) - ::glVertex3f((GLfloat)vertex.x + offset.x, (GLfloat)vertex.y + offset.y, (GLfloat)vertex.z); - ::glEnd(); - } - } - - if (!blending_was_enabled) - ::glDisable(GL_BLEND); - if (!depth_test_was_enabled) - ::glDisable(GL_DEPTH_TEST); -} - -void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const -{ - static const GLfloat INV_255 = 1.0f / 255.0f; - - ::glDisable(GL_DEPTH_TEST); - - for (unsigned int i = 0; i < m_planes.size(); ++i) - { - ::glColor3f(1.f, 1.f, (254.0f - (float)i) * INV_255); - for (const Pointf& offset : m_instances_positions) { - ::glBegin(GL_POLYGON); - for (const auto& vertex : m_planes[i].vertices) - ::glVertex3f((GLfloat)vertex.x + offset.x, (GLfloat)vertex.y + offset.y, (GLfloat)vertex.z); - ::glEnd(); - } - } -} - - -// TODO - remove and use Eigen instead -static Pointf3 super_rotation(Pointf3 axis, float angle, const Pointf3& point) -{ - axis = normalize(axis); - const float& x = axis.x; - const float& y = axis.y; - const float& z = axis.z; - float s = sin(angle); - float c = cos(angle); - float D = 1-c; - float matrix[3][3] = { { c + x*x*D, x*y*D-z*s, x*z*D+y*s }, - { y*x*D+z*s, c+y*y*D, y*z*D-x*s }, - { z*x*D-y*s, z*y*D+x*s, c+z*z*D } }; - float in[3] = { (float)point.x, (float)point.y, (float)point.z }; - float out[3] = { 0, 0, 0 }; - - for (unsigned char i=0; i<3; ++i) - for (unsigned char j=0; j<3; ++j) - out[i] += matrix[i][j] * in[j]; - - return Pointf3(out[0], out[1], out[2]); -} - - -void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) -{ - m_center.release(); // object is not being dragged (this would not be called otherwise) - we must forget about the bounding box position... - m_model_object = model_object; - - // ...and save the updated positions of the object instances: - if (m_model_object && !m_model_object->instances.empty()) { - m_instances_positions.clear(); - for (const auto* instance : m_model_object->instances) - m_instances_positions.emplace_back(instance->offset); - } - - if (is_plane_update_necessary()) - update_planes(); -} - -void GLGizmoFlatten::update_planes() -{ - TriangleMesh ch; - for (const ModelVolume* vol : m_model_object->volumes) - ch.merge(vol->get_convex_hull()); - ch = ch.convex_hull_3d(); - ch.scale(m_model_object->instances.front()->scaling_factor); - ch.rotate_z(m_model_object->instances.front()->rotation); - - m_planes.clear(); - - // Now we'll go through all the facets and append Points of facets sharing the same normal: - const int num_of_facets = ch.stl.stats.number_of_facets; - std::vector facet_queue(num_of_facets, 0); - std::vector facet_visited(num_of_facets, false); - int facet_queue_cnt = 0; - const stl_normal* normal_ptr = nullptr; - while (1) { - // Find next unvisited triangle: - int facet_idx = 0; - for (; facet_idx < num_of_facets; ++ facet_idx) - if (!facet_visited[facet_idx]) { - facet_queue[facet_queue_cnt ++] = facet_idx; - facet_visited[facet_idx] = true; - normal_ptr = &ch.stl.facet_start[facet_idx].normal; - m_planes.emplace_back(); - break; - } - if (facet_idx == num_of_facets) - break; // Everything was visited already - - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; - const stl_normal* this_normal_ptr = &ch.stl.facet_start[facet_idx].normal; - //if (this_normal_ptr->x == normal_ptr->x && this_normal_ptr->y == normal_ptr->y && this_normal_ptr->z == normal_ptr->z) { - if (std::abs(this_normal_ptr->x-normal_ptr->x) < 0.001 && std::abs(this_normal_ptr->y-normal_ptr->y) < 0.001 && std::abs(this_normal_ptr->z-normal_ptr->z) < 0.001) { - stl_vertex* first_vertex = ch.stl.facet_start[facet_idx].vertex; - for (int j=0; j<3; ++j) - m_planes.back().vertices.emplace_back(first_vertex[j].x, first_vertex[j].y, first_vertex[j].z); - - facet_visited[facet_idx] = true; - for (int j = 0; j < 3; ++ j) { - int neighbor_idx = ch.stl.neighbors_start[facet_idx].neighbor[j]; - if (! facet_visited[neighbor_idx]) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } - } - } - m_planes.back().normal = Pointf3(normal_ptr->x, normal_ptr->y, normal_ptr->z); - - // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected anyway): - if (m_planes.back().vertices.size() == 3 && - ( m_planes.back().vertices[0].distance_to(m_planes.back().vertices[1]) < 1.f - || m_planes.back().vertices[0].distance_to(m_planes.back().vertices[2]) < 1.f)) - m_planes.pop_back(); - } - - // Now we'll go through all the polygons, transform the points into xy plane to process them: - for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) { - Pointf3s& polygon = m_planes[polygon_id].vertices; - const Pointf3& normal = m_planes[polygon_id].normal; - - // We are going to rotate about z and y to flatten the plane - float angle_z = 0.f; - float angle_y = 0.f; - if (std::abs(normal.y) > 0.001) - angle_z = -atan2(normal.y, normal.x); // angle to rotate so that normal ends up in xz-plane - if (std::abs(normal.x*cos(angle_z)-normal.y*sin(angle_z)) > 0.001) - angle_y = - atan2(normal.x*cos(angle_z)-normal.y*sin(angle_z), normal.z); // angle to rotate to make normal point upwards - else { - // In case it already was in z-direction, we must ensure it is not the wrong way: - angle_y = normal.z > 0.f ? 0 : -M_PI; - } - - // Rotate all points to the xy plane: - for (auto& vertex : polygon) { - vertex = super_rotation(Pointf3(0,0,1), angle_z, vertex); - vertex = super_rotation(Pointf3(0,1,0), angle_y, vertex); - } - polygon = Slic3r::Geometry::convex_hull(polygon); // To remove the inner points - - // We will calculate area of the polygon and discard ones that are too small - // The limit is more forgiving in case the normal is in the direction of the coordinate axes - const float minimal_area = (std::abs(normal.x) > 0.999f || std::abs(normal.y) > 0.999f || std::abs(normal.z) > 0.999f) ? 1.f : 20.f; - float& area = m_planes[polygon_id].area; - area = 0.f; - for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula - area += polygon[i].x*polygon[i+1 < polygon.size() ? i+1 : 0 ].y - polygon[i+1 < polygon.size() ? i+1 : 0].x*polygon[i].y; - area = std::abs(area/2.f); - if (area < minimal_area) { - m_planes.erase(m_planes.begin()+(polygon_id--)); - continue; - } - - // We will shrink the polygon a little bit so it does not touch the object edges: - Pointf3 centroid = std::accumulate(polygon.begin(), polygon.end(), Pointf3(0.f, 0.f, 0.f)); - centroid.scale(1.f/polygon.size()); - for (auto& vertex : polygon) - vertex = 0.9f*vertex + 0.1f*centroid; - - // Polygon is now simple and convex, we'll round the corners to make them look nicer. - // The algorithm takes a vertex, calculates middles of respective sides and moves the vertex - // towards their average (controlled by 'aggressivity'). This is repeated k times. - // In next iterations, the neighbours are not always taken at the middle (to increase the - // rounding effect at the corners, where we need it most). - const unsigned int k = 10; // number of iterations - const float aggressivity = 0.2f; // agressivity - const unsigned int N = polygon.size(); - std::vector> neighbours; - if (k != 0) { - Pointf3s points_out(2*k*N); // vector long enough to store the future vertices - for (unsigned int j=0; jvolumes) - m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box()); - m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor; - m_source_data.rotation = m_model_object->instances.front()->rotation; - const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); - m_source_data.mesh_first_point = Pointf3(first_vertex[0], first_vertex[1], first_vertex[2]); -} - -// Check if the bounding boxes of each volume's convex hull is the same as before -// and that scaling and rotation has not changed. In that case we don't have to recalculate it. -bool GLGizmoFlatten::is_plane_update_necessary() const -{ - if (m_state != On || !m_model_object || m_model_object->instances.empty()) - return false; - - if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size() - || m_model_object->instances.front()->scaling_factor != m_source_data.scaling_factor - || m_model_object->instances.front()->rotation != m_source_data.rotation) - return true; - - // now compare the bounding boxes: - for (unsigned int i=0; ivolumes.size(); ++i) - if (m_model_object->volumes[i]->get_convex_hull().bounding_box() != m_source_data.bounding_boxes[i]) - return true; - - const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); - Pointf3 first_point(first_vertex[0], first_vertex[1], first_vertex[2]); - if (first_point != m_source_data.mesh_first_point) - return true; - - return false; -} - -Pointf3 GLGizmoFlatten::get_flattening_normal() const { - Pointf3 normal = m_normal; - normal.rotate(-m_model_object->instances.front()->rotation); - m_normal = Pointf3(0.f, 0.f, 0.f); - return normal; -} - - - -} // namespace GUI -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp deleted file mode 100644 index aad31349c1..0000000000 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ /dev/null @@ -1,207 +0,0 @@ -#ifndef slic3r_GLGizmo_hpp_ -#define slic3r_GLGizmo_hpp_ - -#include "../../slic3r/GUI/GLTexture.hpp" -#include "../../libslic3r/Point.hpp" - -#include - -namespace Slic3r { - -class BoundingBoxf3; -class Pointf3; -class ModelObject; - -namespace GUI { - -class GLGizmoBase -{ -protected: - static const float BaseColor[3]; - static const float HighlightColor[3]; - - struct Grabber - { - static const float HalfSize; - static const float HoverOffset; - - Pointf center; - float angle_z; - float color[3]; - - Grabber(); - void render(bool hover) const; - }; - -public: - enum EState - { - Off, - Hover, - On, - Num_States - }; - -protected: - EState m_state; - // textures are assumed to be square and all with the same size in pixels, no internal check is done - GLTexture m_textures[Num_States]; - int m_hover_id; - mutable std::vector m_grabbers; - -public: - GLGizmoBase(); - virtual ~GLGizmoBase(); - - bool init(); - - EState get_state() const; - void set_state(EState state); - - unsigned int get_texture_id() const; - int get_textures_size() const; - - int get_hover_id() const; - void set_hover_id(int id); - - void start_dragging(); - void stop_dragging(); - void update(const Pointf& mouse_pos); - void refresh(); - - void render(const BoundingBoxf3& box) const; - void render_for_picking(const BoundingBoxf3& box) const; - -protected: - virtual bool on_init() = 0; - virtual void on_set_state(); - virtual void on_start_dragging(); - virtual void on_stop_dragging(); - virtual void on_update(const Pointf& mouse_pos) = 0; - virtual void on_refresh(); - virtual void on_render(const BoundingBoxf3& box) const = 0; - virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; - - void render_grabbers() const; -}; - -class GLGizmoRotate : public GLGizmoBase -{ - static const float Offset; - static const unsigned int CircleResolution; - static const unsigned int AngleResolution; - static const unsigned int ScaleStepsCount; - static const float ScaleStepRad; - static const unsigned int ScaleLongEvery; - static const float ScaleLongTooth; - static const float ScaleShortTooth; - static const unsigned int SnapRegionsCount; - static const float GrabberOffset; - - float m_angle_z; - - mutable Pointf m_center; - mutable float m_radius; - mutable bool m_keep_initial_values; - -public: - GLGizmoRotate(); - - float get_angle_z() const; - void set_angle_z(float angle_z); - -protected: - virtual bool on_init(); - virtual void on_set_state(); - virtual void on_update(const Pointf& mouse_pos); - virtual void on_refresh(); - virtual void on_render(const BoundingBoxf3& box) const; - virtual void on_render_for_picking(const BoundingBoxf3& box) const; - -private: - void _render_circle() const; - void _render_scale() const; - void _render_snap_radii() const; - void _render_reference_radius() const; - void _render_angle_z() const; - void _render_grabber() const; -}; - -class GLGizmoScale : public GLGizmoBase -{ - static const float Offset; - - float m_scale; - float m_starting_scale; - - Pointf m_starting_drag_position; - -public: - GLGizmoScale(); - - float get_scale() const; - void set_scale(float scale); - -protected: - virtual bool on_init(); - virtual void on_start_dragging(); - virtual void on_update(const Pointf& mouse_pos); - virtual void on_render(const BoundingBoxf3& box) const; - virtual void on_render_for_picking(const BoundingBoxf3& box) const; -}; - - -class GLGizmoFlatten : public GLGizmoBase -{ -// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. - -private: - mutable Pointf3 m_normal; - - struct PlaneData { - std::vector vertices; - Pointf3 normal; - float area; - }; - struct SourceDataSummary { - std::vector bounding_boxes; // bounding boxes of convex hulls of individual volumes - float scaling_factor; - float rotation; - Pointf3 mesh_first_point; - }; - - // This holds information to decide whether recalculation is necessary: - SourceDataSummary m_source_data; - - std::vector m_planes; - std::vector m_instances_positions; - mutable std::unique_ptr m_center = nullptr; - const ModelObject* m_model_object = nullptr; - void update_planes(); - bool is_plane_update_necessary() const; - -public: - GLGizmoFlatten(); - - void set_flattening_data(const ModelObject* model_object); - Pointf3 get_flattening_normal() const; - -protected: - bool on_init() override; - void on_start_dragging() override; - void on_update(const Pointf& mouse_pos) override {}; - void on_render(const BoundingBoxf3& box) const override; - void on_render_for_picking(const BoundingBoxf3& box) const override; - void on_set_state() override { - if (m_state == On && is_plane_update_necessary()) - update_planes(); - } -}; - - - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GLGizmo_hpp_ - diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp deleted file mode 100644 index 9d49ff4960..0000000000 --- a/xs/src/slic3r/GUI/GUI.cpp +++ /dev/null @@ -1,1093 +0,0 @@ -#include "GUI.hpp" -#include "WipeTowerDialog.hpp" - -#include -#include - -#include -#include -#include -#include - -#if __APPLE__ -#import -#elif _WIN32 -#include -// Undefine min/max macros incompatible with the standard library -// For example, std::numeric_limits::max() -// produces some weird errors -#ifdef min -#undef min -#endif -#ifdef max -#undef max -#endif -#include "boost/nowide/convert.hpp" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "wxExtensions.hpp" - -#include "Tab.hpp" -#include "TabIface.hpp" -#include "AboutDialog.hpp" -#include "AppConfig.hpp" -#include "ConfigSnapshotDialog.hpp" -#include "Utils.hpp" -#include "MsgDialog.hpp" -#include "ConfigWizard.hpp" -#include "Preferences.hpp" -#include "PresetBundle.hpp" -#include "UpdateDialogs.hpp" -#include "FirmwareDialog.hpp" - -#include "../Utils/PresetUpdater.hpp" -#include "../Config/Snapshot.hpp" - -#include "3DScene.hpp" -#include "libslic3r/I18N.hpp" - -namespace Slic3r { namespace GUI { - -#if __APPLE__ -IOPMAssertionID assertionID; -#endif - -void disable_screensaver() -{ - #if __APPLE__ - CFStringRef reasonForActivity = CFSTR("Slic3r"); - IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, - kIOPMAssertionLevelOn, reasonForActivity, &assertionID); - // ignore result: success == kIOReturnSuccess - #elif _WIN32 - SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS); - #endif -} - -void enable_screensaver() -{ - #if __APPLE__ - IOReturn success = IOPMAssertionRelease(assertionID); - #elif _WIN32 - SetThreadExecutionState(ES_CONTINUOUS); - #endif -} - -bool debugged() -{ - #ifdef _WIN32 - return IsDebuggerPresent(); - #else - return false; - #endif /* _WIN32 */ -} - -void break_to_debugger() -{ - #ifdef _WIN32 - if (IsDebuggerPresent()) - DebugBreak(); - #endif /* _WIN32 */ -} - -// Passing the wxWidgets GUI classes instantiated by the Perl part to C++. -wxApp *g_wxApp = nullptr; -wxFrame *g_wxMainFrame = nullptr; -wxNotebook *g_wxTabPanel = nullptr; -AppConfig *g_AppConfig = nullptr; -PresetBundle *g_PresetBundle= nullptr; -PresetUpdater *g_PresetUpdater = nullptr; -_3DScene *g_3DScene = nullptr; -wxColour g_color_label_modified; -wxColour g_color_label_sys; -wxColour g_color_label_default; - -std::vector g_tabs_list; - -wxLocale* g_wxLocale; - -wxFont g_small_font; -wxFont g_bold_font; - -std::shared_ptr m_optgroup; -double m_brim_width = 0.0; -wxButton* g_wiping_dialog_button = nullptr; - -static void init_label_colours() -{ - auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - if (luma >= 128) { - g_color_label_modified = wxColour(252, 77, 1); - g_color_label_sys = wxColour(26, 132, 57); - } else { - g_color_label_modified = wxColour(253, 111, 40); - g_color_label_sys = wxColour(115, 220, 103); - } - g_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); -} - -void update_label_colours_from_appconfig() -{ - if (g_AppConfig->has("label_clr_sys")){ - auto str = g_AppConfig->get("label_clr_sys"); - if (str != "") - g_color_label_sys = wxColour(str); - } - - if (g_AppConfig->has("label_clr_modified")){ - auto str = g_AppConfig->get("label_clr_modified"); - if (str != "") - g_color_label_modified = wxColour(str); - } -} - -static void init_fonts() -{ - g_small_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - g_bold_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); -#ifdef __WXMAC__ - g_small_font.SetPointSize(11); - g_bold_font.SetPointSize(13); -#endif /*__WXMAC__*/ -} - -static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } - -void set_wxapp(wxApp *app) -{ - g_wxApp = app; - // Let the libslic3r know the callback, which will translate messages on demand. - Slic3r::I18N::set_translate_callback(libslic3r_translate_callback); - init_label_colours(); - init_fonts(); -} - -void set_main_frame(wxFrame *main_frame) -{ - g_wxMainFrame = main_frame; -} - -void set_tab_panel(wxNotebook *tab_panel) -{ - g_wxTabPanel = tab_panel; -} - -void set_app_config(AppConfig *app_config) -{ - g_AppConfig = app_config; -} - -void set_preset_bundle(PresetBundle *preset_bundle) -{ - g_PresetBundle = preset_bundle; -} - -void set_preset_updater(PresetUpdater *updater) -{ - g_PresetUpdater = updater; -} - -void set_3DScene(_3DScene *scene) -{ - g_3DScene = scene; -} - -std::vector& get_tabs_list() -{ - return g_tabs_list; -} - -bool checked_tab(Tab* tab) -{ - bool ret = true; - if (find(g_tabs_list.begin(), g_tabs_list.end(), tab) == g_tabs_list.end()) - ret = false; - return ret; -} - -void delete_tab_from_list(Tab* tab) -{ - std::vector::iterator itr = find(g_tabs_list.begin(), g_tabs_list.end(), tab); - if (itr != g_tabs_list.end()) - g_tabs_list.erase(itr); -} - -bool select_language(wxArrayString & names, - wxArrayLong & identifiers) -{ - wxCHECK_MSG(names.Count() == identifiers.Count(), false, - _(L("Array of language names and identifiers should have the same size."))); - int init_selection = 0; - long current_language = g_wxLocale ? g_wxLocale->GetLanguage() : wxLANGUAGE_UNKNOWN; - for (auto lang : identifiers){ - if (lang == current_language) - break; - else - ++init_selection; - } - if (init_selection == identifiers.size()) - init_selection = 0; - long index = wxGetSingleChoiceIndex(_(L("Select the language")), _(L("Language")), - names, init_selection); - if (index != -1) - { - g_wxLocale = new wxLocale; - g_wxLocale->Init(identifiers[index]); - g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir())); - g_wxLocale->AddCatalog(g_wxApp->GetAppName()); - wxSetlocale(LC_NUMERIC, "C"); - Preset::update_suffix_modified(); - return true; - } - return false; -} - -bool load_language() -{ - wxString language = wxEmptyString; - if (g_AppConfig->has("translation_language")) - language = g_AppConfig->get("translation_language"); - - if (language.IsEmpty()) - return false; - wxArrayString names; - wxArrayLong identifiers; - get_installed_languages(names, identifiers); - for (size_t i = 0; i < identifiers.Count(); i++) - { - if (wxLocale::GetLanguageCanonicalName(identifiers[i]) == language) - { - g_wxLocale = new wxLocale; - g_wxLocale->Init(identifiers[i]); - g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir())); - g_wxLocale->AddCatalog(g_wxApp->GetAppName()); - wxSetlocale(LC_NUMERIC, "C"); - Preset::update_suffix_modified(); - return true; - } - } - return false; -} - -void save_language() -{ - wxString language = wxEmptyString; - if (g_wxLocale) - language = g_wxLocale->GetCanonicalName(); - - g_AppConfig->set("translation_language", language.ToStdString()); - g_AppConfig->save(); -} - -void get_installed_languages(wxArrayString & names, - wxArrayLong & identifiers) -{ - names.Clear(); - identifiers.Clear(); - - wxDir dir(wxPathOnly(localization_dir())); - wxString filename; - const wxLanguageInfo * langinfo; - wxString name = wxLocale::GetLanguageName(wxLANGUAGE_DEFAULT); - if (!name.IsEmpty()) - { - names.Add(_(L("Default"))); - identifiers.Add(wxLANGUAGE_DEFAULT); - } - for (bool cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS); - cont; cont = dir.GetNext(&filename)) - { - langinfo = wxLocale::FindLanguageInfo(filename); - if (langinfo != NULL) - { - auto full_file_name = dir.GetName() + wxFileName::GetPathSeparator() + - filename + wxFileName::GetPathSeparator() + - g_wxApp->GetAppName() + wxT(".mo"); - if (wxFileExists(full_file_name)) - { - names.Add(langinfo->Description); - identifiers.Add(langinfo->Language); - } - } - } -} - -enum ConfigMenuIDs { - ConfigMenuWizard, - ConfigMenuSnapshots, - ConfigMenuTakeSnapshot, - ConfigMenuUpdate, - ConfigMenuPreferences, - ConfigMenuLanguage, - ConfigMenuFlashFirmware, - ConfigMenuCnt, -}; - -static wxString dots("…", wxConvUTF8); - -void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change) -{ - auto local_menu = new wxMenu(); - wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt); - - const auto config_wizard_name = _(ConfigWizard::name().wx_str()); - const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), config_wizard_name); - // Cmd+, is standard on OS X - what about other operating systems? - local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip); - local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots"))+dots, _(L("Inspect / activate configuration snapshots"))); - local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot"))); -// local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates"))); - local_menu->AppendSeparator(); - local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences"))+dots+"\tCtrl+,", _(L("Application preferences"))); - local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language"))); - local_menu->AppendSeparator(); - local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _(L("Flash printer firmware")), _(L("Upload a firmware image into an Arduino based printer"))); - // TODO: for when we're able to flash dictionaries - // local_menu->Append(config_id_base + FirmwareMenuDict, _(L("Flash language file")), _(L("Upload a language dictionary file into a Prusa printer"))); - - local_menu->Bind(wxEVT_MENU, [config_id_base, event_language_change, event_preferences_changed](wxEvent &event){ - switch (event.GetId() - config_id_base) { - case ConfigMenuWizard: - config_wizard(ConfigWizard::RR_USER); - break; - case ConfigMenuTakeSnapshot: - // Take a configuration snapshot. - if (check_unsaved_changes()) { - wxTextEntryDialog dlg(nullptr, _(L("Taking configuration snapshot")), _(L("Snapshot name"))); - if (dlg.ShowModal() == wxID_OK) - g_AppConfig->set("on_snapshot", - Slic3r::GUI::Config::SnapshotDB::singleton().take_snapshot( - *g_AppConfig, Slic3r::GUI::Config::Snapshot::SNAPSHOT_USER, dlg.GetValue().ToUTF8().data()).id); - } - break; - case ConfigMenuSnapshots: - if (check_unsaved_changes()) { - std::string on_snapshot; - if (Config::SnapshotDB::singleton().is_on_snapshot(*g_AppConfig)) - on_snapshot = g_AppConfig->get("on_snapshot"); - ConfigSnapshotDialog dlg(Slic3r::GUI::Config::SnapshotDB::singleton(), on_snapshot); - dlg.ShowModal(); - if (! dlg.snapshot_to_activate().empty()) { - if (! Config::SnapshotDB::singleton().is_on_snapshot(*g_AppConfig)) - Config::SnapshotDB::singleton().take_snapshot(*g_AppConfig, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK); - g_AppConfig->set("on_snapshot", - Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *g_AppConfig).id); - g_PresetBundle->load_presets(*g_AppConfig); - // Load the currently selected preset into the GUI, update the preset selection box. - load_current_presets(); - } - } - break; - case ConfigMenuPreferences: - { - PreferencesDialog dlg(g_wxMainFrame, event_preferences_changed); - dlg.ShowModal(); - break; - } - case ConfigMenuLanguage: - { - wxArrayString names; - wxArrayLong identifiers; - get_installed_languages(names, identifiers); - if (select_language(names, identifiers)) { - save_language(); - show_info(g_wxTabPanel, _(L("Application will be restarted")), _(L("Attention!"))); - if (event_language_change > 0) { - _3DScene::remove_all_canvases();// remove all canvas before recreate GUI - wxCommandEvent event(event_language_change); - g_wxApp->ProcessEvent(event); - } - } - break; - } - case ConfigMenuFlashFirmware: - FirmwareDialog::run(g_wxMainFrame); - break; - default: - break; - } - }); - menu->Append(local_menu, _(L("&Configuration"))); -} - -void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change) -{ - add_config_menu(menu, event_preferences_changed, event_language_change); -} - -// This is called when closing the application, when loading a config file or when starting the config wizard -// to notify the user whether he is aware that some preset changes will be lost. -bool check_unsaved_changes() -{ - std::string dirty; - for (Tab *tab : g_tabs_list) - if (tab->current_preset_is_dirty()) - if (dirty.empty()) - dirty = tab->name(); - else - dirty += std::string(", ") + tab->name(); - if (dirty.empty()) - // No changes, the application may close or reload presets. - return true; - // Ask the user. - auto dialog = new wxMessageDialog(g_wxMainFrame, - _(L("You have unsaved changes ")) + dirty + _(L(". Discard changes and continue anyway?")), - _(L("Unsaved Presets")), - wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT); - return dialog->ShowModal() == wxID_YES; -} - -bool config_wizard_startup(bool app_config_exists) -{ - if (! app_config_exists || g_PresetBundle->printers.size() <= 1) { - config_wizard(ConfigWizard::RR_DATA_EMPTY); - return true; - } else if (g_AppConfig->legacy_datadir()) { - // Looks like user has legacy pre-vendorbundle data directory, - // explain what this is and run the wizard - - MsgDataLegacy dlg; - dlg.ShowModal(); - - config_wizard(ConfigWizard::RR_DATA_LEGACY); - return true; - } - return false; -} - -void config_wizard(int reason) -{ - // Exit wizard if there are unsaved changes and the user cancels the action. - if (! check_unsaved_changes()) - return; - - try { - ConfigWizard wizard(nullptr, static_cast(reason)); - wizard.run(g_PresetBundle, g_PresetUpdater); - } - catch (const std::exception &e) { - show_error(nullptr, e.what()); - } - - // Load the currently selected preset into the GUI, update the preset selection box. - load_current_presets(); -} - -void open_preferences_dialog(int event_preferences) -{ - auto dlg = new PreferencesDialog(g_wxMainFrame, event_preferences); - dlg->ShowModal(); -} - -void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed) -{ - update_label_colours_from_appconfig(); - add_created_tab(new TabPrint (g_wxTabPanel, no_controller)); - add_created_tab(new TabFilament (g_wxTabPanel, no_controller)); - add_created_tab(new TabPrinter (g_wxTabPanel, no_controller)); - for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) { - Tab *tab = dynamic_cast(g_wxTabPanel->GetPage(i)); - if (! tab) - continue; - tab->set_event_value_change(wxEventType(event_value_change)); - tab->set_event_presets_changed(wxEventType(event_presets_changed)); - } -} - -TabIface* get_preset_tab_iface(char *name) -{ - for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) { - Tab *tab = dynamic_cast(g_wxTabPanel->GetPage(i)); - if (! tab) - continue; - if (tab->name() == name) { - return new TabIface(tab); - } - } - return new TabIface(nullptr); -} - -// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element) -void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) -{ - try{ - switch (config.def()->get(opt_key)->type){ - case coFloatOrPercent:{ - std::string str = boost::any_cast(value); - bool percent = false; - if (str.back() == '%'){ - str.pop_back(); - percent = true; - } - double val = stod(str); - config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(val, percent)); - break;} - case coPercent: - config.set_key_value(opt_key, new ConfigOptionPercent(boost::any_cast(value))); - break; - case coFloat:{ - double& val = config.opt_float(opt_key); - val = boost::any_cast(value); - break; - } - case coPercents:{ - ConfigOptionPercents* vec_new = new ConfigOptionPercents{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, opt_index); - break; - } - case coFloats:{ - ConfigOptionFloats* vec_new = new ConfigOptionFloats{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, opt_index); - break; - } - case coString: - config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast(value))); - break; - case coStrings:{ - if (opt_key.compare("compatible_printers") == 0) { - config.option(opt_key)->values = - boost::any_cast>(value); - } - else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0){ - std::string str = boost::any_cast(value); - if (str.back() == ';') str.pop_back(); - // 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. - std::vector values; - boost::split(values, str, boost::is_any_of(";")); - if (values.size() == 1 && values[0] == "") - break; - config.option(opt_key)->values = values; - } - else{ - ConfigOptionStrings* vec_new = new ConfigOptionStrings{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - } - } - break; - case coBool: - config.set_key_value(opt_key, new ConfigOptionBool(boost::any_cast(value))); - break; - case coBools:{ - ConfigOptionBools* vec_new = new ConfigOptionBools{ (bool)boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - break;} - case coInt: - config.set_key_value(opt_key, new ConfigOptionInt(boost::any_cast(value))); - break; - case coInts:{ - ConfigOptionInts* vec_new = new ConfigOptionInts{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - } - break; - case coEnum:{ - if (opt_key.compare("external_fill_pattern") == 0 || - opt_key.compare("fill_pattern") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); - else if (opt_key.compare("gcode_flavor") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); - else if (opt_key.compare("support_material_pattern") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); - else if (opt_key.compare("seam_position") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); - else if (opt_key.compare("host_type") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); - } - break; - case coPoints:{ - if (opt_key.compare("bed_shape") == 0){ - config.option(opt_key)->values = boost::any_cast>(value); - break; - } - ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - } - break; - case coNone: - break; - default: - break; - } - } - catch (const std::exception &e) - { - int i = 0;//no reason, just experiment - } -} - -void add_created_tab(Tab* panel) -{ - panel->create_preset_tab(g_PresetBundle); - - // Load the currently selected preset into the GUI, update the preset selection box. - panel->load_current_preset(); - g_wxTabPanel->AddPage(panel, panel->title()); -} - -void load_current_presets() -{ - for (Tab *tab : g_tabs_list) { - tab->load_current_preset(); - } -} - -void show_error(wxWindow* parent, const wxString& message) { - ErrorDialog msg(parent, message); - msg.ShowModal(); -} - -void show_error_id(int id, const std::string& message) { - auto *parent = id != 0 ? wxWindow::FindWindowById(id) : nullptr; - show_error(parent, wxString::FromUTF8(message.data())); -} - -void show_info(wxWindow* parent, const wxString& message, const wxString& title){ - wxMessageDialog msg_wingow(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION); - msg_wingow.ShowModal(); -} - -void warning_catcher(wxWindow* parent, const wxString& message){ - if (message == "GLUquadricObjPtr | " + _(L("Attempt to free unreferenced scalar")) ) - return; - wxMessageDialog msg(parent, message, _(L("Warning")), wxOK | wxICON_WARNING); - msg.ShowModal(); -} - -wxApp* get_app(){ - return g_wxApp; -} - -PresetBundle* get_preset_bundle() -{ - return g_PresetBundle; -} - -const wxColour& get_label_clr_modified() { - return g_color_label_modified; -} - -const wxColour& get_label_clr_sys() { - return g_color_label_sys; -} - -void set_label_clr_modified(const wxColour& clr) { - g_color_label_modified = clr; - auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue()); - std::string str = clr_str.ToStdString(); - g_AppConfig->set("label_clr_modified", str); - g_AppConfig->save(); -} - -void set_label_clr_sys(const wxColour& clr) { - g_color_label_sys = clr; - auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue()); - std::string str = clr_str.ToStdString(); - g_AppConfig->set("label_clr_sys", str); - g_AppConfig->save(); -} - -const wxFont& small_font(){ - return g_small_font; -} - -const wxFont& bold_font(){ - return g_bold_font; -} - -const wxColour& get_label_clr_default() { - return g_color_label_default; -} - -unsigned get_colour_approx_luma(const wxColour &colour) -{ - double r = colour.Red(); - double g = colour.Green(); - double b = colour.Blue(); - - return std::round(std::sqrt( - r * r * .241 + - g * g * .691 + - b * b * .068 - )); -} - -void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value) -{ - if (comboCtrl == nullptr) - return; - - wxCheckListBoxComboPopup* popup = new wxCheckListBoxComboPopup; - if (popup != nullptr) - { - // FIXME If the following line is removed, the combo box popup list will not react to mouse clicks. - // On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10. - comboCtrl->UseAltPopupWindow(); - - comboCtrl->EnablePopupAnimation(false); - comboCtrl->SetPopupControl(popup); - popup->SetStringValue(from_u8(text)); - popup->Bind(wxEVT_CHECKLISTBOX, [popup](wxCommandEvent& evt) { popup->OnCheckListBox(evt); }); - popup->Bind(wxEVT_LISTBOX, [popup](wxCommandEvent& evt) { popup->OnListBoxSelection(evt); }); - popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); }); - popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); }); - - std::vector items_str; - boost::split(items_str, items, boost::is_any_of("|"), boost::token_compress_off); - - for (const std::string& item : items_str) - { - popup->Append(from_u8(item)); - } - - for (unsigned int i = 0; i < popup->GetCount(); ++i) - { - popup->Check(i, initial_value); - } - } -} - -int combochecklist_get_flags(wxComboCtrl* comboCtrl) -{ - int flags = 0; - - wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup); - if (popup != nullptr) - { - for (unsigned int i = 0; i < popup->GetCount(); ++i) - { - if (popup->IsChecked(i)) - flags |= 1 << i; - } - } - - return flags; -} - -AppConfig* get_app_config() -{ - return g_AppConfig; -} - -wxString L_str(const std::string &str) -{ - //! Explicitly specify that the source string is already in UTF-8 encoding - return wxGetTranslation(wxString(str.c_str(), wxConvUTF8)); -} - -wxString from_u8(const std::string &str) -{ - return wxString::FromUTF8(str.c_str()); -} - - -void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer) -{ - DynamicPrintConfig* config = &g_PresetBundle->prints.get_edited_preset().config; - m_optgroup = std::make_shared(parent, "", config); - const wxArrayInt& ar = preset_sizer->GetColWidths(); - m_optgroup->label_width = ar.IsEmpty() ? 100 : ar.front()-4; // doesn't work - m_optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){ - TabPrint* tab_print = nullptr; - for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) { - Tab *tab = dynamic_cast(g_wxTabPanel->GetPage(i)); - if (!tab) - continue; - if (tab->name() == "print"){ - tab_print = static_cast(tab); - break; - } - } - if (tab_print == nullptr) - return; - - if (opt_key == "fill_density"){ - value = m_optgroup->get_config_value(*config, opt_key); - tab_print->set_value(opt_key, value); - tab_print->update(); - } - else{ - DynamicPrintConfig new_conf = *config; - if (opt_key == "brim"){ - double new_val; - double brim_width = config->opt_float("brim_width"); - if (boost::any_cast(value) == true) - { - new_val = m_brim_width == 0.0 ? 5 : - m_brim_width < 0.0 ? m_brim_width * (-1) : - m_brim_width; - } - else{ - m_brim_width = brim_width * (-1); - new_val = 0; - } - new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val)); - } - else{ //(opt_key == "support") - const wxString& selection = boost::any_cast(value); - - auto support_material = selection == _("None") ? false : true; - new_conf.set_key_value("support_material", new ConfigOptionBool(support_material)); - - if (selection == _("Everywhere")) - new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(false)); - else if (selection == _("Support on build plate only")) - new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(true)); - } - tab_print->load_config(new_conf); - } - - tab_print->update_dirty(); - }; - - Option option = m_optgroup->get_option("fill_density"); - option.opt.sidetext = ""; - option.opt.full_width = true; - m_optgroup->append_single_option_line(option); - - ConfigOptionDef def; - - def.label = L("Support"); - def.type = coStrings; - def.gui_type = "select_open"; - def.tooltip = L("Select what kind of support do you need"); - def.enum_labels.push_back(L("None")); - def.enum_labels.push_back(L("Support on build plate only")); - def.enum_labels.push_back(L("Everywhere")); - std::string selection = !config->opt_bool("support_material") ? - "None" : - config->opt_bool("support_material_buildplate_only") ? - "Support on build plate only" : - "Everywhere"; - def.default_value = new ConfigOptionStrings { selection }; - option = Option(def, "support"); - option.opt.full_width = true; - m_optgroup->append_single_option_line(option); - - m_brim_width = config->opt_float("brim_width"); - def.label = L("Brim"); - def.type = coBool; - def.tooltip = L("This flag enables the brim that will be printed around each object on the first layer."); - def.gui_type = ""; - def.default_value = new ConfigOptionBool{ m_brim_width > 0.0 ? true : false }; - option = Option(def, "brim"); - m_optgroup->append_single_option_line(option); - - - Line line = { "", "" }; - line.widget = [config](wxWindow* parent){ - g_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(g_wiping_dialog_button); - g_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e) - { - auto &config = g_PresetBundle->project_config; - std::vector init_matrix = (config.option("wiping_volumes_matrix"))->values; - std::vector init_extruders = (config.option("wiping_volumes_extruders"))->values; - - WipingDialog dlg(parent,std::vector(init_matrix.begin(),init_matrix.end()),std::vector(init_extruders.begin(),init_extruders.end())); - - if (dlg.ShowModal() == wxID_OK) { - std::vector matrix = dlg.get_matrix(); - std::vector extruders = dlg.get_extruders(); - (config.option("wiping_volumes_matrix"))->values = std::vector(matrix.begin(),matrix.end()); - (config.option("wiping_volumes_extruders"))->values = std::vector(extruders.begin(),extruders.end()); - g_on_request_update_callback.call(); - } - })); - return sizer; - }; - m_optgroup->append_line(line); - - - - sizer->Add(m_optgroup->sizer, 1, wxEXPAND | wxBOTTOM, 2); -} - -ConfigOptionsGroup* get_optgroup() -{ - return m_optgroup.get(); -} - -wxButton* get_wiping_dialog_button() -{ - return g_wiping_dialog_button; -} - -wxWindow* export_option_creator(wxWindow* parent) -{ - wxPanel* panel = new wxPanel(parent, -1); - wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); - wxCheckBox* cbox = new wxCheckBox(panel, wxID_HIGHEST + 1, L("Export print config")); - cbox->SetValue(true); - sizer->AddSpacer(5); - sizer->Add(cbox, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); - panel->SetSizer(sizer); - sizer->SetSizeHints(panel); - return panel; -} - -void add_export_option(wxFileDialog* dlg, const std::string& format) -{ - if ((dlg != nullptr) && (format == "AMF") || (format == "3MF")) - { - if (dlg->SupportsExtraControl()) - dlg->SetExtraControlCreator(export_option_creator); - } -} - -int get_export_option(wxFileDialog* dlg) -{ - if (dlg != nullptr) - { - wxWindow* wnd = dlg->GetExtraControl(); - if (wnd != nullptr) - { - wxPanel* panel = dynamic_cast(wnd); - if (panel != nullptr) - { - wxWindow* child = panel->FindWindow(wxID_HIGHEST + 1); - if (child != nullptr) - { - wxCheckBox* cbox = dynamic_cast(child); - if (cbox != nullptr) - return cbox->IsChecked() ? 1 : 0; - } - } - } - } - - return 0; - -} - -bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height) -{ - const auto idx = wxDisplay::GetFromWindow(window); - if (idx == wxNOT_FOUND) { - return false; - } - - wxDisplay display(idx); - const auto disp_size = display.GetClientArea(); - width = disp_size.GetWidth(); - height = disp_size.GetHeight(); - - return true; -} - -void save_window_size(wxTopLevelWindow *window, const std::string &name) -{ - const wxSize size = window->GetSize(); - const wxPoint pos = window->GetPosition(); - const auto maximized = window->IsMaximized() ? "1" : "0"; - - g_AppConfig->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str()); - g_AppConfig->set((boost::format("window_%1%_maximized") % name).str(), maximized); -} - -void restore_window_size(wxTopLevelWindow *window, const std::string &name) -{ - // XXX: This still doesn't behave nicely in some situations (mostly on Linux). - // The problem is that it's hard to obtain window position with respect to screen geometry reliably - // from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which - // it's actually visible. I suspect this has something to do with window initialization (maybe we - // restore window geometry too early), but haven't yet found a workaround. - - const auto display_idx = wxDisplay::GetFromWindow(window); - if (display_idx == wxNOT_FOUND) { return; } - - const auto display = wxDisplay(display_idx).GetClientArea(); - std::vector pair; - - try { - const auto key_size = (boost::format("window_%1%_size") % name).str(); - if (g_AppConfig->has(key_size)) { - if (unescape_strings_cstyle(g_AppConfig->get(key_size), pair) && pair.size() == 2) { - auto width = boost::lexical_cast(pair[0]); - auto height = boost::lexical_cast(pair[1]); - - window->SetSize(width, height); - } - } - } catch(const boost::bad_lexical_cast &) {} - - // Maximizing should be the last thing to do. - // This ensure the size and position are sane when the user un-maximizes the window. - const auto key_maximized = (boost::format("window_%1%_maximized") % name).str(); - if (g_AppConfig->get(key_maximized) == "1") { - window->Maximize(true); - } -} - - -void about() -{ - AboutDialog dlg; - dlg.ShowModal(); - dlg.Destroy(); -} - -void desktop_open_datadir_folder() -{ - // Execute command to open a file explorer, platform dependent. - // FIXME: The const_casts aren't needed in wxWidgets 3.1, remove them when we upgrade. - - const auto path = data_dir(); -#ifdef _WIN32 - const auto widepath = wxString::FromUTF8(path.data()); - const wchar_t *argv[] = { L"explorer", widepath.GetData(), nullptr }; - ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr); -#elif __APPLE__ - const char *argv[] = { "open", path.data(), nullptr }; - ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr); -#else - const char *argv[] = { "xdg-open", path.data(), nullptr }; - - // Check if we're running in an AppImage container, if so, we need to remove AppImage's env vars, - // because they may mess up the environment expected by the file manager. - // Mostly this is about LD_LIBRARY_PATH, but we remove a few more too for good measure. - if (wxGetEnv("APPIMAGE", nullptr)) { - // We're running from AppImage - wxEnvVariableHashMap env_vars; - wxGetEnvMap(&env_vars); - - env_vars.erase("APPIMAGE"); - env_vars.erase("APPDIR"); - env_vars.erase("LD_LIBRARY_PATH"); - env_vars.erase("LD_PRELOAD"); - env_vars.erase("UNION_PRELOAD"); - - wxExecuteEnv exec_env; - exec_env.env = std::move(env_vars); - - wxString owd; - if (wxGetEnv("OWD", &owd)) { - // This is the original work directory from which the AppImage image was run, - // set it as CWD for the child process: - exec_env.cwd = std::move(owd); - } - - ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr, &exec_env); - } else { - // Looks like we're NOT running from AppImage, we'll make no changes to the environment. - ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr, nullptr); - } -#endif -} - -} } diff --git a/xs/src/slic3r/GUI/wxExtensions.cpp b/xs/src/slic3r/GUI/wxExtensions.cpp deleted file mode 100644 index 5949efb377..0000000000 --- a/xs/src/slic3r/GUI/wxExtensions.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include "wxExtensions.hpp" - -const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200; -const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200; -const unsigned int wxCheckListBoxComboPopup::DefaultItemHeight = 18; - -bool wxCheckListBoxComboPopup::Create(wxWindow* parent) -{ - return wxCheckListBox::Create(parent, wxID_HIGHEST + 1, wxPoint(0, 0)); -} - -wxWindow* wxCheckListBoxComboPopup::GetControl() -{ - return this; -} - -void wxCheckListBoxComboPopup::SetStringValue(const wxString& value) -{ - m_text = value; -} - -wxString wxCheckListBoxComboPopup::GetStringValue() const -{ - return m_text; -} - -wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight) -{ - // matches owner wxComboCtrl's width - // and sets height dinamically in dependence of contained items count - - wxComboCtrl* cmb = GetComboCtrl(); - if (cmb != nullptr) - { - wxSize size = GetComboCtrl()->GetSize(); - - unsigned int count = GetCount(); - if (count > 0) - size.SetHeight(count * DefaultItemHeight); - else - size.SetHeight(DefaultHeight); - - return size; - } - else - return wxSize(DefaultWidth, DefaultHeight); -} - -void wxCheckListBoxComboPopup::OnKeyEvent(wxKeyEvent& evt) -{ - // filters out all the keys which are not working properly - switch (evt.GetKeyCode()) - { - case WXK_LEFT: - case WXK_UP: - case WXK_RIGHT: - case WXK_DOWN: - case WXK_PAGEUP: - case WXK_PAGEDOWN: - case WXK_END: - case WXK_HOME: - case WXK_NUMPAD_LEFT: - case WXK_NUMPAD_UP: - case WXK_NUMPAD_RIGHT: - case WXK_NUMPAD_DOWN: - case WXK_NUMPAD_PAGEUP: - case WXK_NUMPAD_PAGEDOWN: - case WXK_NUMPAD_END: - case WXK_NUMPAD_HOME: - { - break; - } - default: - { - evt.Skip(); - break; - } - } -} - -void wxCheckListBoxComboPopup::OnCheckListBox(wxCommandEvent& evt) -{ - // forwards the checklistbox event to the owner wxComboCtrl - - if (m_check_box_events_status == OnCheckListBoxFunction::FreeToProceed ) - { - wxComboCtrl* cmb = GetComboCtrl(); - if (cmb != nullptr) { - wxCommandEvent event(wxEVT_CHECKLISTBOX, cmb->GetId()); - event.SetEventObject(cmb); - cmb->ProcessWindowEvent(event); - } - } - - evt.Skip(); - - #ifndef _WIN32 // events are sent differently on OSX+Linux vs Win (more description in header file) - if ( m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed ) - // this happens if the event was resent by OnListBoxSelection - next call to OnListBoxSelection is due to user clicking the text, so the function should - // explicitly change the state on the checkbox - m_check_box_events_status = OnCheckListBoxFunction::WasRefusedLastTime; - else - // if the user clicked the checkbox square, this event was sent before OnListBoxSelection was called, so we don't want it to resend it - m_check_box_events_status = OnCheckListBoxFunction::RefuseToProceed; - #endif -} - -void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt) -{ - // transforms list box item selection event into checklistbox item toggle event - - int selId = GetSelection(); - if (selId != wxNOT_FOUND) - { - #ifndef _WIN32 - if (m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed) - #endif - Check((unsigned int)selId, !IsChecked((unsigned int)selId)); - - m_check_box_events_status = OnCheckListBoxFunction::FreeToProceed; // so the checkbox reacts to square-click the next time - - SetSelection(wxNOT_FOUND); - wxCommandEvent event(wxEVT_CHECKLISTBOX, GetId()); - event.SetInt(selId); - event.SetEventObject(this); - ProcessEvent(event); - } -} - - -// *** wxDataViewTreeCtrlComboPopup *** - -const unsigned int wxDataViewTreeCtrlComboPopup::DefaultWidth = 270; -const unsigned int wxDataViewTreeCtrlComboPopup::DefaultHeight = 200; -const unsigned int wxDataViewTreeCtrlComboPopup::DefaultItemHeight = 22; - -bool wxDataViewTreeCtrlComboPopup::Create(wxWindow* parent) -{ - return wxDataViewTreeCtrl::Create(parent, wxID_ANY/*HIGHEST + 1*/, wxPoint(0, 0), wxDefaultSize/*wxSize(270, -1)*/, wxDV_NO_HEADER); -} -/* -wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight) -{ - // matches owner wxComboCtrl's width - // and sets height dinamically in dependence of contained items count - wxComboCtrl* cmb = GetComboCtrl(); - if (cmb != nullptr) - { - wxSize size = GetComboCtrl()->GetSize(); - if (m_cnt_open_items > 0) - size.SetHeight(m_cnt_open_items * DefaultItemHeight); - else - size.SetHeight(DefaultHeight); - - return size; - } - else - return wxSize(DefaultWidth, DefaultHeight); -} -*/ -void wxDataViewTreeCtrlComboPopup::OnKeyEvent(wxKeyEvent& evt) -{ - // filters out all the keys which are not working properly - if (evt.GetKeyCode() == WXK_UP) - { - return; - } - else if (evt.GetKeyCode() == WXK_DOWN) - { - return; - } - else - { - evt.Skip(); - return; - } -} - -void wxDataViewTreeCtrlComboPopup::OnDataViewTreeCtrlSelection(wxCommandEvent& evt) -{ - wxComboCtrl* cmb = GetComboCtrl(); - auto selected = GetItemText(GetSelection()); - cmb->SetText(selected); -} diff --git a/xs/src/slic3r/GUI/wxExtensions.hpp b/xs/src/slic3r/GUI/wxExtensions.hpp deleted file mode 100644 index 3667c7905b..0000000000 --- a/xs/src/slic3r/GUI/wxExtensions.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef slic3r_GUI_wxExtensions_hpp_ -#define slic3r_GUI_wxExtensions_hpp_ - -#include -#include -#include - -class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup -{ - static const unsigned int DefaultWidth; - static const unsigned int DefaultHeight; - static const unsigned int DefaultItemHeight; - - wxString m_text; - - // Events sent on mouseclick are quite complex. Function OnListBoxSelection is supposed to pass the event to the checkbox, which works fine on - // Win. On OSX and Linux the events are generated differently - clicking on the checkbox square generates the event twice (and the square - // therefore seems not to respond). - // This enum is meant to save current state of affairs, i.e., if the event forwarding is ok to do or not. It is only used on Linux - // and OSX by some #ifdefs. It also stores information whether OnListBoxSelection is supposed to change the checkbox status, - // or if it changed status on its own already (which happens when the square is clicked). More comments in OnCheckListBox(...) - // There indeed is a better solution, maybe making a custom event used for the event passing to distinguish the original and passed message - // and blocking one of them on OSX and Linux. Feel free to refactor, but carefully test on all platforms. - enum class OnCheckListBoxFunction{ - FreeToProceed, - RefuseToProceed, - WasRefusedLastTime - } m_check_box_events_status = OnCheckListBoxFunction::FreeToProceed; - - -public: - virtual bool Create(wxWindow* parent); - virtual wxWindow* GetControl(); - virtual void SetStringValue(const wxString& value); - virtual wxString GetStringValue() const; - virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight); - - virtual void OnKeyEvent(wxKeyEvent& evt); - - void OnCheckListBox(wxCommandEvent& evt); - void OnListBoxSelection(wxCommandEvent& evt); -}; - - -// *** wxDataViewTreeCtrlComboBox *** - -class wxDataViewTreeCtrlComboPopup: public wxDataViewTreeCtrl, public wxComboPopup -{ - static const unsigned int DefaultWidth; - static const unsigned int DefaultHeight; - static const unsigned int DefaultItemHeight; - - wxString m_text; - int m_cnt_open_items{0}; - -public: - virtual bool Create(wxWindow* parent); - virtual wxWindow* GetControl() { return this; } - virtual void SetStringValue(const wxString& value) { m_text = value; } - virtual wxString GetStringValue() const { return m_text; } -// virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight); - - virtual void OnKeyEvent(wxKeyEvent& evt); - void OnDataViewTreeCtrlSelection(wxCommandEvent& evt); - void SetItemsCnt(int cnt) { m_cnt_open_items = cnt; } -}; - -#endif // slic3r_GUI_wxExtensions_hpp_ diff --git a/xs/src/slic3r/GUI/wxPerlIface.cpp b/xs/src/slic3r/GUI/wxPerlIface.cpp deleted file mode 100644 index 216ca4b3b8..0000000000 --- a/xs/src/slic3r/GUI/wxPerlIface.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// Derived from the following: - -///////////////////////////////////////////////////////////////////////////// -// Name: cpp/helpers.cpp -// Purpose: implementation for helpers.h -// Author: Mattia Barbon -// Modified by: -// Created: 29/10/2000 -// RCS-ID: $Id: helpers.cpp 3397 2012-09-30 02:26:07Z mdootson $ -// Copyright: (c) 2000-2011 Mattia Barbon -// Licence: This program is free software; you can redistribute it and/or -// modify it under the same terms as Perl itself -///////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -extern "C" { -#endif -#include "EXTERN.h" -#include "perl.h" -#include "XSUB.h" -#include "ppport.h" -#undef do_open -#undef do_close -#ifdef __cplusplus -} -#endif - -//#include - -// ---------------------------------------------------------------------------- -// Utility functions for working with MAGIC -// ---------------------------------------------------------------------------- - -struct my_magic -{ - my_magic() : object( NULL ), deleteable( true ) { } - - void* object; - bool deleteable; -}; - -//STATIC MGVTBL my_vtbl = { 0, 0, 0, 0, 0, 0, 0, 0 }; - -my_magic* wxPli_get_magic( pTHX_ SV* rv ) -{ - // check for reference - if( !SvROK( rv ) ) - return NULL; - SV* ref = SvRV( rv ); - - // if it isn't a SvPVMG, then it can't have MAGIC - // so it is deleteable - if( !ref || SvTYPE( ref ) < SVt_PVMG ) - return NULL; - - // search for '~' / PERL_MAGIC_ext magic, and check the value -// MAGIC* magic = mg_findext( ref, PERL_MAGIC_ext, &my_vtbl ); - MAGIC* magic = mg_find( ref, '~' ); - if( !magic ) - return NULL; - - return (my_magic*)magic->mg_ptr; -} - -// gets 'this' pointer from a blessed scalar/hash reference -void* wxPli_sv_2_object( pTHX_ SV* scalar, const char* classname ) -{ - // is it correct to use undef as 'NULL'? - if( !SvOK( scalar ) ) - { - return NULL; - } - - if( !SvROK( scalar ) ) - croak( "variable is not an object: it must have type %s", classname ); - - if( !classname || sv_derived_from( scalar, (char*) classname ) ) - { - SV* ref = SvRV( scalar ); - - my_magic* mg = wxPli_get_magic( aTHX_ scalar ); - - // rationale: if this is an hash-ish object, it always - // has both mg and mg->object; if however this is a - // scalar-ish object that has been marked/unmarked deletable - // it has mg, but not mg->object - if( !mg || !mg->object ) - return INT2PTR( void*, SvOK( ref ) ? SvIV( ref ) : 0 ); - - return mg->object; - } - else - { - croak( "variable is not of type %s", classname ); - return NULL; // dummy, for compiler - } -} diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 9365f19791..e36376bd1a 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -68,7 +68,16 @@ extern "C" { #undef fputc #undef fwrite #undef fclose + + // Breaks compilation with Eigen matrices embedded into Slic3r::Point. + #undef malloc + #undef realloc + #undef free + #undef select #endif /* _MSC_VER */ +#undef Zero +#undef Packet +#undef _ } #endif @@ -80,7 +89,6 @@ extern "C" { #include #include #include -#include namespace Slic3r { @@ -188,9 +196,9 @@ void from_SV_check(SV* poly_sv, Polyline* THIS); SV* to_SV_pureperl(const Point* THIS); void from_SV(SV* point_sv, Point* point); void from_SV_check(SV* point_sv, Point* point); -SV* to_SV_pureperl(const Pointf* point); -bool from_SV(SV* point_sv, Pointf* point); -bool from_SV_check(SV* point_sv, Pointf* point); +SV* to_SV_pureperl(const Vec2d* point); +bool from_SV(SV* point_sv, Vec2d* point); +bool from_SV_check(SV* point_sv, Vec2d* point); void from_SV_check(SV* surface_sv, Surface* THIS); SV* to_SV(TriangleMesh* THIS); @@ -200,6 +208,41 @@ SV* to_SV(TriangleMesh* THIS); // Return a pointer to the associated wxWidgets object instance given by classname. extern void* wxPli_sv_2_object( pTHX_ SV* scalar, const char* classname ); +inline void confess_at(const char *file, int line, const char *func, const char *pat, ...) +{ + #ifdef SLIC3RXS + va_list args; + SV *error_sv = newSVpvf("Error in function %s at %s:%d: ", func, + file, line); + + va_start(args, pat); + sv_vcatpvf(error_sv, pat, &args); + va_end(args); + + sv_catpvn(error_sv, "\n\t", 2); + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs( sv_2mortal(error_sv) ); + PUTBACK; + call_pv("Carp::confess", G_DISCARD); + FREETMPS; + LEAVE; + #endif +} + +#ifndef CONFESS +/* Implementation of CONFESS("foo"): */ +#ifdef _MSC_VER + #define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) +#else + #define CONFESS(...) confess_at(__FILE__, __LINE__, __func__, __VA_ARGS__) +#endif +/* End implementation of CONFESS("foo"): */ +#endif /* CONFESS */ + using namespace Slic3r; #endif diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t index 4013a1f830..fd57cf8051 100644 --- a/xs/t/01_trianglemesh.t +++ b/xs/t/01_trianglemesh.t @@ -79,9 +79,7 @@ my $cube = { my $m = Slic3r::TriangleMesh->new; $m->ReadFromPerl($cube->{vertices}, $cube->{facets}); $m->repair; - # The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be - # open intervals at the bottom end, closed at the top end. - my @z = (0.0001,2,4,8,6,8,10,12,14,16,18,20); + my @z = (0,2,4,8,6,8,10,12,14,16,18,20); my $result = $m->slice(\@z); my $SCALING_FACTOR = 0.000001; for my $i (0..$#z) { @@ -107,9 +105,7 @@ my $cube = { # this second test also checks that performing a second slice on a mesh after # a transformation works properly (shared_vertices is correctly invalidated); # at Z = -10 we have a bottom horizontal surface - # (The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be - # open intervals at the bottom end, closed at the top end, so the Z = -10 is shifted a bit up to get a valid slice). - my $slices = $m->slice([ -5, -10+0.00001 ]); + my $slices = $m->slice([ -5, -10 ]); is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a bottom tangent plane includes its area'; } } diff --git a/xs/t/20_print.t b/xs/t/20_print.t index e535cdd8cb..68ec1e7195 100644 --- a/xs/t/20_print.t +++ b/xs/t/20_print.t @@ -4,14 +4,12 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 5; +use Test::More tests => 3; { my $print = Slic3r::Print->new; isa_ok $print, 'Slic3r::Print'; isa_ok $print->config, 'Slic3r::Config::Static::Ref'; - isa_ok $print->default_object_config, 'Slic3r::Config::Static::Ref'; - isa_ok $print->default_region_config, 'Slic3r::Config::Static::Ref'; isa_ok $print->placeholder_parser, 'Slic3r::GCode::PlaceholderParser::Ref'; } diff --git a/xs/xsp/AppController.xsp b/xs/xsp/AppController.xsp deleted file mode 100644 index a578fe0b17..0000000000 --- a/xs/xsp/AppController.xsp +++ /dev/null @@ -1,24 +0,0 @@ -%module{Slic3r::XS}; - -%{ -#include -#include "slic3r/AppController.hpp" -#include "libslic3r/Model.hpp" -#include "libslic3r/Print.hpp" -%} - -%name{Slic3r::PrintController} class PrintController { - PrintController(Print *print); -}; - -%name{Slic3r::AppController} class AppController { - - AppController(); - - PrintController *print_ctl(); - void set_model(Model *model); - void set_print(Print *print); - void set_global_progress_indicator(unsigned gauge_id, unsigned statusbar_id); - - void arrange_model(); -}; \ No newline at end of file diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp index ebeb178221..a34cad0bc8 100644 --- a/xs/xsp/BoundingBox.xsp +++ b/xs/xsp/BoundingBox.xsp @@ -25,15 +25,15 @@ double radius(); Clone min_point() %code{% RETVAL = THIS->min; %}; Clone max_point() %code{% RETVAL = THIS->max; %}; - int x_min() %code{% RETVAL = THIS->min.x; %}; - int x_max() %code{% RETVAL = THIS->max.x; %}; - int y_min() %code{% RETVAL = THIS->min.y; %}; - int y_max() %code{% RETVAL = THIS->max.y; %}; - void set_x_min(double val) %code{% THIS->min.x = val; %}; - void set_x_max(double val) %code{% THIS->max.x = val; %}; - void set_y_min(double val) %code{% THIS->min.y = val; %}; - void set_y_max(double val) %code{% THIS->max.y = val; %}; - std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %}; + int x_min() %code{% RETVAL = THIS->min(0); %}; + int x_max() %code{% RETVAL = THIS->max(0); %}; + int y_min() %code{% RETVAL = THIS->min(1); %}; + int y_max() %code{% RETVAL = THIS->max(1); %}; + void set_x_min(double val) %code{% THIS->min(0) = val; %}; + void set_x_max(double val) %code{% THIS->max(0) = val; %}; + void set_y_min(double val) %code{% THIS->min(1) = val; %}; + void set_y_max(double val) %code{% THIS->max(1) = val; %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min(0), THIS->min(1), THIS->max(0), THIS->max(1)); RETVAL = buf; %}; bool defined() %code{% RETVAL = THIS->defined; %}; %{ @@ -56,24 +56,24 @@ new_from_points(CLASS, points) Clone clone() %code{% RETVAL = THIS; %}; void merge(BoundingBoxf* bb) %code{% THIS->merge(*bb); %}; - void merge_point(Pointf* point) %code{% THIS->merge(*point); %}; + void merge_point(Vec2d* point) %code{% THIS->merge(*point); %}; void scale(double factor); void translate(double x, double y); - Clone size(); - Clone center(); + Clone size(); + Clone center(); double radius(); bool empty() %code{% RETVAL = empty(*THIS); %}; - Clone min_point() %code{% RETVAL = THIS->min; %}; - Clone max_point() %code{% RETVAL = THIS->max; %}; - double x_min() %code{% RETVAL = THIS->min.x; %}; - double x_max() %code{% RETVAL = THIS->max.x; %}; - double y_min() %code{% RETVAL = THIS->min.y; %}; - double y_max() %code{% RETVAL = THIS->max.y; %}; - void set_x_min(double val) %code{% THIS->min.x = val; %}; - void set_x_max(double val) %code{% THIS->max.x = val; %}; - void set_y_min(double val) %code{% THIS->min.y = val; %}; - void set_y_max(double val) %code{% THIS->max.y = val; %}; - std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf;%lf,%lf", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %}; + Clone min_point() %code{% RETVAL = THIS->min; %}; + Clone max_point() %code{% RETVAL = THIS->max; %}; + double x_min() %code{% RETVAL = THIS->min(0); %}; + double x_max() %code{% RETVAL = THIS->max(0); %}; + double y_min() %code{% RETVAL = THIS->min(1); %}; + double y_max() %code{% RETVAL = THIS->max(1); %}; + void set_x_min(double val) %code{% THIS->min(0) = val; %}; + void set_x_max(double val) %code{% THIS->max(0) = val; %}; + void set_y_min(double val) %code{% THIS->min(1) = val; %}; + void set_y_max(double val) %code{% THIS->max(1) = val; %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf;%lf,%lf", THIS->min(0), THIS->min(1), THIS->max(0), THIS->max(1)); RETVAL = buf; %}; bool defined() %code{% RETVAL = THIS->defined; %}; %{ @@ -96,23 +96,23 @@ new_from_points(CLASS, points) Clone clone() %code{% RETVAL = THIS; %}; void merge(BoundingBoxf3* bb) %code{% THIS->merge(*bb); %}; - void merge_point(Pointf3* point) %code{% THIS->merge(*point); %}; + void merge_point(Vec3d* point) %code{% THIS->merge(*point); %}; void scale(double factor); void translate(double x, double y, double z); void offset(double delta); - bool contains_point(Pointf3* point) %code{% RETVAL = THIS->contains(*point); %}; - Clone size(); - Clone center(); + bool contains_point(Vec3d* point) %code{% RETVAL = THIS->contains(*point); %}; + Clone size(); + Clone center(); double radius(); bool empty() %code{% RETVAL = empty(*THIS); %}; - Clone min_point() %code{% RETVAL = THIS->min; %}; - Clone max_point() %code{% RETVAL = THIS->max; %}; - double x_min() %code{% RETVAL = THIS->min.x; %}; - double x_max() %code{% RETVAL = THIS->max.x; %}; - double y_min() %code{% RETVAL = THIS->min.y; %}; - double y_max() %code{% RETVAL = THIS->max.y; %}; - double z_min() %code{% RETVAL = THIS->min.z; %}; - double z_max() %code{% RETVAL = THIS->max.z; %}; - std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf;%lf,%lf,%lf", THIS->min.x, THIS->min.y, THIS->min.z, THIS->max.x, THIS->max.y, THIS->max.z); RETVAL = buf; %}; + Clone min_point() %code{% RETVAL = THIS->min; %}; + Clone max_point() %code{% RETVAL = THIS->max; %}; + double x_min() %code{% RETVAL = THIS->min(0); %}; + double x_max() %code{% RETVAL = THIS->max(0); %}; + double y_min() %code{% RETVAL = THIS->min(1); %}; + double y_max() %code{% RETVAL = THIS->max(1); %}; + double z_min() %code{% RETVAL = THIS->min(2); %}; + double z_max() %code{% RETVAL = THIS->max(2); %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf;%lf,%lf,%lf", THIS->min(0), THIS->min(1), THIS->min(2), THIS->max(0), THIS->max(1), THIS->max(2)); RETVAL = buf; %}; bool defined() %code{% RETVAL = THIS->defined; %}; }; diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index b8ad84ba46..d5d2958397 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -137,7 +137,7 @@ PROTOTYPES: DISABLE SV* print_config_def() CODE: - t_optiondef_map &def = Slic3r::print_config_def.options; + t_optiondef_map &def = *const_cast(&Slic3r::print_config_def.options); HV* options_hv = newHV(); for (t_optiondef_map::iterator oit = def.begin(); oit != def.end(); ++oit) { diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index c1856ccf7b..9e04edd4c6 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -35,9 +35,9 @@ } %}; - Ref origin() + Ref origin() %code{% RETVAL = &(THIS->origin()); %}; - void set_origin(Pointf* pointf) + void set_origin(Vec2d* pointf) %code{% THIS->set_origin(*pointf); %}; Ref last_pos() %code{% RETVAL = &(THIS->last_pos()); %}; diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 8d2efb8588..a1e3e4670d 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -35,17 +35,29 @@ bool is_windows10() void set_wxapp(SV *ui) %code%{ Slic3r::GUI::set_wxapp((wxApp*)wxPli_sv_2_object(aTHX_ ui, "Wx::App")); %}; +void set_gui_appctl() + %code%{ Slic3r::GUI::set_gui_appctl(); %}; + +void set_cli_appctl() + %code%{ Slic3r::GUI::set_cli_appctl(); %}; + +void set_progress_status_bar(ProgressStatusBar *prs) + %code%{ Slic3r::GUI::set_progress_status_bar(prs); %}; + void set_main_frame(SV *ui) %code%{ Slic3r::GUI::set_main_frame((wxFrame*)wxPli_sv_2_object(aTHX_ ui, "Wx::Frame")); %}; void set_tab_panel(SV *ui) %code%{ Slic3r::GUI::set_tab_panel((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook")); %}; +void set_plater(SV *ui) + %code%{ Slic3r::GUI::set_plater((wxPanel*)wxPli_sv_2_object(aTHX_ ui, "Wx::Panel")); %}; + void add_menus(SV *ui, int event_preferences_changed, int event_language_change) %code%{ Slic3r::GUI::add_menus((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_preferences_changed, event_language_change); %}; -void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed) - %code%{ Slic3r::GUI::create_preset_tabs(no_controller, event_value_change, event_presets_changed); %}; +void create_preset_tabs(int event_value_change, int event_presets_changed) + %code%{ Slic3r::GUI::create_preset_tabs(event_value_change, event_presets_changed); %}; void show_error_id(int id, std::string msg) %code%{ Slic3r::GUI::show_error_id(id, msg); %}; @@ -53,6 +65,9 @@ void show_error_id(int id, std::string msg) TabIface* get_preset_tab(char *name) %code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %}; +PreviewIface* create_preview_iface(SV *ui, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) + %code%{ RETVAL=Slic3r::GUI::create_preview_iface((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook"), config, print, gcode_preview_data); %}; + bool load_language() %code%{ RETVAL=Slic3r::GUI::load_language(); %}; @@ -87,6 +102,73 @@ void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_siz (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"), (wxFlexGridSizer*)wxPli_sv_2_object(aTHX_ ui_p_sizer, "Wx::FlexGridSizer")); %}; +void set_print_callback_event(Print *print, int id) + %code%{ Slic3r::GUI::set_print_callback_event(print, id); %}; + +void set_model_events_from_perl(Model *model, + int event_object_selection_changed, + int event_object_settings_changed, + int event_remove_object, + int event_update_scene) + %code%{ Slic3r::GUI::set_model_events_from_perl(*model, + event_object_selection_changed, + event_object_settings_changed, + event_remove_object, + event_update_scene); %}; + +void set_objects_from_perl( SV *ui_parent, + SV *frequently_changed_parameters_sizer, + SV *info_sizer, + SV *btn_export_gcode, + SV *btn_reslice, + SV *btn_print, + SV *btn_send_gcode, + SV *manifold_warning_icon) + %code%{ Slic3r::GUI::set_objects_from_perl( + (wxWindow *)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"), + (wxBoxSizer *)wxPli_sv_2_object(aTHX_ frequently_changed_parameters_sizer, "Wx::BoxSizer"), + (wxBoxSizer *)wxPli_sv_2_object(aTHX_ info_sizer, "Wx::BoxSizer"), + (wxButton *)wxPli_sv_2_object(aTHX_ btn_export_gcode, "Wx::Button"), + (wxButton *)wxPli_sv_2_object(aTHX_ btn_reslice, "Wx::Button"), + (wxButton *)wxPli_sv_2_object(aTHX_ btn_print, "Wx::Button"), + (wxButton *)wxPli_sv_2_object(aTHX_ btn_send_gcode, "Wx::Button"), + (wxStaticBitmap *)wxPli_sv_2_object(aTHX_ manifold_warning_icon, "Wx::StaticBitmap")); %}; + +void set_show_print_info(bool show) + %code%{ Slic3r::GUI::set_show_print_info(show); %}; + +void set_show_manifold_warning_icon(bool show) + %code%{ Slic3r::GUI::set_show_manifold_warning_icon(show); %}; + +void update_mode() + %code%{ Slic3r::GUI::update_mode(); %}; + +void add_object_to_list(const char *name, SV *object_model) + %code%{ Slic3r::GUI::add_object_to_list( + name, + (ModelObject *)wxPli_sv_2_object(aTHX_ object_model, "Slic3r::Model::Object") ); %}; + +void delete_object_from_list() + %code%{ Slic3r::GUI::delete_object_from_list(); %}; + +void delete_all_objects_from_list() + %code%{ Slic3r::GUI::delete_all_objects_from_list(); %}; + +void set_object_count(int idx, int count) + %code%{ Slic3r::GUI::set_object_count(idx, count); %}; + +void unselect_objects() + %code%{ Slic3r::GUI::unselect_objects(); %}; + +void select_current_object(int idx) + %code%{ Slic3r::GUI::select_current_object(idx); %}; + +void select_current_volume(int idx, int vol_idx) + %code%{ Slic3r::GUI::select_current_volume(idx, vol_idx); %}; + +void remove_obj() + %code%{ Slic3r::GUI::remove(); %}; + std::string fold_utf8_to_ascii(const char *src) %code%{ RETVAL = Slic3r::fold_utf8_to_ascii(src); %}; @@ -102,18 +184,28 @@ void desktop_open_datadir_folder() void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst) %code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %}; -void set_3DScene(SV *scene) - %code%{ Slic3r::GUI::set_3DScene((_3DScene *)wxPli_sv_2_object(aTHX_ scene, "Slic3r::Model::3DScene") ); %}; - void register_on_request_update_callback(SV* callback) %code%{ Slic3r::GUI::g_on_request_update_callback.register_callback(callback); %}; void deregister_on_request_update_callback() %code%{ Slic3r::GUI::g_on_request_update_callback.deregister_callback(); %}; +void create_double_slider(SV *ui_parent, SV *ui_sizer, SV *ui_canvas) + %code%{ Slic3r::GUI::create_double_slider( (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"), + (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"), + (wxGLCanvas*)wxPli_sv_2_object(aTHX_ ui_canvas, "Wx::GLCanvas")); %}; + +void update_double_slider(bool force_sliders_full_range) + %code%{ Slic3r::GUI::update_double_slider(force_sliders_full_range); %}; + +void reset_double_slider() + %code%{ Slic3r::GUI::reset_double_slider(); %}; + +void enable_action_buttons(bool enable) + %code%{ Slic3r::GUI::enable_action_buttons(enable); %}; + void save_window_size(SV *window, std::string name) %code%{ Slic3r::GUI::save_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %}; void restore_window_size(SV *window, std::string name) %code%{ Slic3r::GUI::restore_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %}; - diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 1a0fc9b9f9..dc252d8bd7 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -55,14 +55,10 @@ int object_idx() const; int volume_idx() const; int instance_idx() const; - Clone origin() const - %code%{ RETVAL = THIS->get_origin(); %}; + Clone origin() const + %code%{ RETVAL = THIS->get_offset(); %}; void translate(double x, double y, double z) - %code%{ - Pointf3 o = THIS->get_origin(); - o.translate(x, y, z); - THIS->set_origin(o); - %}; + %code%{ THIS->set_offset(THIS->get_offset() + Vec3d(x, y, z)); %}; Clone bounding_box() const %code%{ RETVAL = THIS->bounding_box; %}; @@ -341,6 +337,14 @@ set_drag_by(canvas, value) CODE: _3DScene::set_drag_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), value); +std::string +get_select_by(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_select_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + bool is_layers_editing_enabled(canvas) SV *canvas; @@ -422,6 +426,13 @@ enable_shader(canvas, enable) CODE: _3DScene::enable_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); +void +enable_toolbar(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_toolbar((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void enable_force_zoom_to_bed(canvas, enable) SV *canvas; @@ -443,6 +454,23 @@ allow_multisample(canvas, allow) CODE: _3DScene::allow_multisample((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), allow); +void +enable_toolbar_item(canvas, item, enable) + SV *canvas; + std::string item; + bool enable; + CODE: + _3DScene::enable_toolbar_item((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), item, enable); + +bool +is_toolbar_item_pressed(canvas, item) + SV *canvas; + std::string item; + CODE: + RETVAL = _3DScene::is_toolbar_item_pressed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), item); + OUTPUT: + RETVAL + void zoom_to_bed(canvas) SV *canvas; @@ -616,6 +644,13 @@ register_on_gizmo_scale_uniformly_callback(canvas, callback) CODE: _3DScene::register_on_gizmo_scale_uniformly_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_gizmo_scale_3D_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_scale_3D_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + void register_on_gizmo_rotate_callback(canvas, callback) SV *canvas; @@ -623,13 +658,118 @@ register_on_gizmo_rotate_callback(canvas, callback) CODE: _3DScene::register_on_gizmo_rotate_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_gizmo_rotate_3D_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_rotate_3D_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_gizmo_flatten_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_flatten_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_gizmo_flatten_3D_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_flatten_3D_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + void register_on_update_geometry_info_callback(canvas, callback) SV *canvas; SV *callback; CODE: _3DScene::register_on_update_geometry_info_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); - + +void +register_on_update_geometry_3D_info_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_update_geometry_3D_info_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_add_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_add_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_delete_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_delete_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_deleteall_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_deleteall_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_arrange_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_arrange_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_more_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_more_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_fewer_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_fewer_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_split_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_split_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_cut_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_cut_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_settings_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_settings_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_layersediting_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_layersediting_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_action_selectbyparts_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_selectbyparts_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + void reset_legend_texture() CODE: @@ -646,6 +786,24 @@ load_model_object(canvas, model_object, obj_idx, instance_idxs) OUTPUT: RETVAL +int +get_first_volume_id(canvas, obj_idx) + SV *canvas; + int obj_idx; + CODE: + RETVAL = _3DScene::get_first_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), obj_idx); + OUTPUT: + RETVAL + +int +get_in_object_volume_id(canvas, scene_vol_idx) + SV *canvas; + int scene_vol_idx; + CODE: + RETVAL = _3DScene::get_in_object_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), scene_vol_idx); + OUTPUT: + RETVAL + std::vector load_model(canvas, model, obj_idx) SV *canvas; diff --git a/xs/xsp/GUI_BackgroundSlicingProcess.xsp b/xs/xsp/GUI_BackgroundSlicingProcess.xsp new file mode 100644 index 0000000000..8452b8c314 --- /dev/null +++ b/xs/xsp/GUI_BackgroundSlicingProcess.xsp @@ -0,0 +1,25 @@ + +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/GUI/BackgroundSlicingProcess.hpp" +%} + +%name{Slic3r::GUI::BackgroundSlicingProcess} class BackgroundSlicingProcess { + BackgroundSlicingProcess(); + ~BackgroundSlicingProcess(); + + void set_print(Print *print); + void set_gcode_preview_data(GCodePreviewData *gpd); + void set_sliced_event(int event_id); + void set_finished_event(int event_id); + + void set_output_path(const char *path); + bool start(); + bool stop(); + bool apply_config(DynamicPrintConfig *config) + %code%{ RETVAL = THIS->apply_config(*config); %}; + + bool running(); +}; diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index 2c63db10c4..99d23a1421 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -32,7 +32,6 @@ %name{Slic3r::GUI::PresetCollection} class PresetCollection { Ref preset(size_t idx) %code%{ RETVAL = &THIS->preset(idx); %}; - Ref default_preset() %code%{ RETVAL = &THIS->default_preset(); %}; size_t size() const; size_t num_visible() const; std::string name() const; @@ -133,6 +132,7 @@ PresetCollection::arrayref() Ref print() %code%{ RETVAL = &THIS->prints; %}; Ref filament() %code%{ RETVAL = &THIS->filaments; %}; + Ref sla_material() %code%{ RETVAL = &THIS->sla_materials; %}; Ref printer() %code%{ RETVAL = &THIS->printers; %}; Ref project_config() %code%{ RETVAL = &THIS->project_config; %}; diff --git a/xs/xsp/GUI_Preview.xsp b/xs/xsp/GUI_Preview.xsp new file mode 100644 index 0000000000..da50c0d21c --- /dev/null +++ b/xs/xsp/GUI_Preview.xsp @@ -0,0 +1,28 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/GUI/GUI_PreviewIface.hpp" +%} + +%name{Slic3r::GUI::Preview} class PreviewIface { + + void register_on_viewport_changed_callback(SV* callback) + %code%{ THIS->register_on_viewport_changed_callback((void*)callback); %}; + + void set_number_extruders(unsigned int number_extruders); + void reset_gcode_preview_data(); + void reload_print(bool force = false); + void set_canvas_as_dirty(); + void set_enabled(bool enabled); + void set_bed_shape(Pointfs shape); + void select_view(std::string direction); + void set_viewport_from_scene(SV *ui) + %code%{ THIS->set_viewport_from_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ ui, "Wx::GLCanvas")); %}; + + void set_viewport_into_scene(SV *ui) + %code%{ THIS->set_viewport_into_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ ui, "Wx::GLCanvas")); %}; + + void SetDropTarget(SV *ui) + %code%{ THIS->set_drop_target((wxDropTarget*)wxPli_sv_2_object(aTHX_ ui, "Wx::DropTarget")); %}; +}; \ No newline at end of file diff --git a/xs/xsp/Geometry.xsp b/xs/xsp/Geometry.xsp index b23bbeffa9..b7e92ba695 100644 --- a/xs/xsp/Geometry.xsp +++ b/xs/xsp/Geometry.xsp @@ -8,7 +8,7 @@ %package{Slic3r::Geometry}; -Pointfs arrange(size_t total_parts, Pointf* part, coordf_t dist, BoundingBoxf* bb = NULL) +Pointfs arrange(size_t total_parts, Vec2d* part, coordf_t dist, BoundingBoxf* bb = NULL) %code{% Pointfs points; if (! Slic3r::Geometry::arrange(total_parts, *part, dist, bb, points)) diff --git a/xs/xsp/Line.xsp b/xs/xsp/Line.xsp index 92429e57a8..777dc41fa7 100644 --- a/xs/xsp/Line.xsp +++ b/xs/xsp/Line.xsp @@ -29,7 +29,6 @@ bool parallel_to_line(Line* line) %code{% RETVAL = THIS->parallel_to(*line); %}; Clone midpoint(); - Clone point_at(double distance); Clone intersection_infinite(Line* other) %code{% Point p; @@ -37,8 +36,8 @@ if (!res) CONFESS("Intersection failed"); RETVAL = p; %}; - Clone as_polyline() - %code{% RETVAL = Polyline(*THIS); %}; + Polyline* as_polyline() + %code{% RETVAL = new Polyline(THIS->a, THIS->b); %}; Clone normal(); Clone vector(); double ccw(Point* point) @@ -70,7 +69,7 @@ Line::coincides_with(line_sv) CODE: Line line; from_SV_check(line_sv, &line); - RETVAL = THIS->coincides_with(line); + RETVAL = (*THIS) == line; OUTPUT: RETVAL @@ -79,15 +78,15 @@ Line::coincides_with(line_sv) %name{Slic3r::Linef3} class Linef3 { - Linef3(Pointf3* a, Pointf3* b) + Linef3(Vec3d* a, Vec3d* b) %code{% RETVAL = new Linef3(*a, *b); %}; ~Linef3(); Clone clone() %code{% RETVAL = THIS; %}; - Ref a() + Ref a() %code{% RETVAL = &THIS->a; %}; - Ref b() + Ref b() %code{% RETVAL = &THIS->b; %}; - Clone intersect_plane(double z); + Clone intersect_plane(double z); void scale(double factor); }; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 0b59b3126b..6091d00ad4 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -21,27 +21,18 @@ %name{read_from_file} Model(std::string input_file, bool add_default_instances = true) %code%{ try { - RETVAL = new Model(Model::read_from_file(input_file, add_default_instances)); + RETVAL = new Model(Model::read_from_file(input_file, nullptr, add_default_instances)); } catch (std::exception& e) { croak("Error while opening %s: %s\n", input_file.c_str(), e.what()); } %}; - %name{read_from_archive} Model(std::string input_file, PresetBundle* bundle, bool add_default_instances = true) - %code%{ - try { - RETVAL = new Model(Model::read_from_archive(input_file, bundle, add_default_instances)); - } catch (std::exception& e) { - croak("Error while opening %s: %s\n", input_file.c_str(), e.what()); - } - %}; - Clone clone() %code%{ RETVAL = THIS; %}; %name{_add_object} Ref add_object(); Ref _add_object_clone(ModelObject* other, bool copy_volumes = true) - %code%{ RETVAL = THIS->add_object(*other, copy_volumes); %}; + %code%{ auto ptr = THIS->add_object(*other); if (! copy_volumes) ptr->clear_volumes(); RETVAL = ptr; %}; void delete_object(size_t idx); void clear_objects(); size_t objects_count() @@ -81,7 +72,7 @@ bool add_default_instances(); Clone bounding_box(); - void center_instances_around_point(Pointf* point) + void center_instances_around_point(Vec2d* point) %code%{ THIS->center_instances_around_point(*point); %}; void translate(double x, double y, double z); Clone mesh(); @@ -101,10 +92,6 @@ bool store_stl(char *path, bool binary) %code%{ TriangleMesh mesh = THIS->mesh(); RETVAL = Slic3r::store_stl(path, &mesh, binary); %}; - bool store_amf(char *path, Print* print, bool export_print_config) - %code%{ RETVAL = Slic3r::store_amf(path, THIS, print, export_print_config); %}; - bool store_3mf(char *path, Print* print, bool export_print_config) - %code%{ RETVAL = Slic3r::store_3mf(path, THIS, print, export_print_config); %}; %{ @@ -122,67 +109,7 @@ load_stl(CLASS, path, object_name) OUTPUT: RETVAL -Model* -load_obj(CLASS, path, object_name) - char* CLASS; - char* path; - char* object_name; - CODE: - RETVAL = new Model(); - if (! load_obj(path, RETVAL, object_name)) { - delete RETVAL; - RETVAL = NULL; - } - OUTPUT: - RETVAL - -Model* -load_amf(CLASS, bundle, path) - char* CLASS; - PresetBundle* bundle; - char* path; - CODE: - RETVAL = new Model(); - if (! load_amf(path, bundle, RETVAL)) { - delete RETVAL; - RETVAL = NULL; - } - OUTPUT: - RETVAL - -Model* -load_3mf(CLASS, bundle, path) - char* CLASS; - PresetBundle* bundle; - char* path; - CODE: - RETVAL = new Model(); - if (! load_3mf(path, bundle, RETVAL)) { - delete RETVAL; - RETVAL = NULL; - } - OUTPUT: - RETVAL - -Model* -load_prus(CLASS, path) - char* CLASS; - char* path; - CODE: -#ifdef SLIC3R_PRUS - RETVAL = new Model(); - if (! load_prus(path, RETVAL)) { - delete RETVAL; - RETVAL = NULL; - } -#else - RETVAL = nullptr; -#endif - OUTPUT: - RETVAL - %} - }; %name{Slic3r::Model::Material} class ModelMaterial { @@ -289,9 +216,9 @@ ModelMaterial::attributes() void set_layer_height_profile(std::vector profile) %code%{ THIS->layer_height_profile = profile; THIS->layer_height_profile_valid = true; %}; - Ref origin_translation() + Ref origin_translation() %code%{ RETVAL = &THIS->origin_translation; %}; - void set_origin_translation(Pointf3* point) + void set_origin_translation(Vec3d* point) %code%{ THIS->origin_translation = *point; %}; bool needed_repair() const; @@ -299,17 +226,11 @@ ModelMaterial::attributes() int facets_count(); void center_around_origin(); void translate(double x, double y, double z); - void scale_xyz(Pointf3* versor) + void scale_xyz(Vec3d* versor) %code{% THIS->scale(*versor); %}; - void rotate(float angle, Pointf3* axis) + void rotate(float angle, Vec3d* axis) %code{% THIS->rotate(angle, *axis); %}; void mirror(Axis axis); - - Model* cut(double z) - %code%{ - RETVAL = new Model(); - THIS->cut(z, RETVAL); - %}; ModelObjectPtrs* split_object() %code%{ @@ -331,15 +252,13 @@ ModelMaterial::attributes() %code%{ THIS->name = value; %}; t_model_material_id material_id(); void set_material_id(t_model_material_id material_id) - %code%{ THIS->material_id(material_id); %}; + %code%{ THIS->set_material_id(material_id); %}; Ref material(); Ref config() %code%{ RETVAL = &THIS->config; %}; Ref mesh() %code%{ RETVAL = &THIS->mesh; %}; - Ref convex_hull() - %code%{ RETVAL = &THIS->get_convex_hull(); %}; bool modifier() %code%{ RETVAL = THIS->is_modifier(); %}; @@ -357,8 +276,6 @@ ModelMaterial::attributes() %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %}; size_t split(unsigned int max_extruders); - - ModelMaterial* assign_unique_material(); }; @@ -366,20 +283,33 @@ ModelMaterial::attributes() Ref object() %code%{ RETVAL = THIS->get_object(); %}; - double rotation() - %code%{ RETVAL = THIS->rotation; %}; - double scaling_factor() - %code%{ RETVAL = THIS->scaling_factor; %}; - Ref offset() - %code%{ RETVAL = &THIS->offset; %}; - + Vec3d* rotation() + %code%{ RETVAL = new Vec3d(THIS->get_rotation(X), THIS->get_rotation(Y), THIS->get_rotation(Z)); %}; + + Vec3d* scaling_factor() + %code%{ RETVAL = new Vec3d(THIS->get_scaling_factor(X), THIS->get_scaling_factor(Y), THIS->get_scaling_factor(Z)); %}; + + Vec2d* offset() + %code%{ RETVAL = new Vec2d(THIS->get_offset(X), THIS->get_offset(Y)); %}; + void set_rotation(double val) - %code%{ THIS->rotation = val; THIS->get_object()->invalidate_bounding_box(); %}; + %code%{ THIS->set_rotation(Z, val); THIS->get_object()->invalidate_bounding_box(); %}; + + void set_rotations(Vec3d *rotation) + %code%{ THIS->set_rotation(*rotation); THIS->get_object()->invalidate_bounding_box(); %}; + void set_scaling_factor(double val) - %code%{ THIS->scaling_factor = val; THIS->get_object()->invalidate_bounding_box(); %}; - void set_offset(Pointf *offset) - %code%{ THIS->offset = *offset; %}; - + %code%{ THIS->set_scaling_factor(X, val); THIS->set_scaling_factor(Y, val); THIS->set_scaling_factor(Z, val); THIS->get_object()->invalidate_bounding_box(); %}; + + void set_scaling_factors(Vec3d *scale) + %code%{ THIS->set_scaling_factor(*scale); THIS->get_object()->invalidate_bounding_box(); %}; + + void set_offset(Vec2d *offset) + %code%{ + THIS->set_offset(X, (*offset)(0)); + THIS->set_offset(Y, (*offset)(1)); + %}; + void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; void transform_polygon(Polygon* polygon) const; }; diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp index b7aded6a0c..beefc62494 100644 --- a/xs/xsp/Point.xsp +++ b/xs/xsp/Point.xsp @@ -3,6 +3,7 @@ %{ #include #include "libslic3r/Point.hpp" +#include "libslic3r/Line.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/Polyline.hpp" %} @@ -12,44 +13,44 @@ ~Point(); Clone clone() %code{% RETVAL=THIS; %}; - void scale(double factor); - void translate(double x, double y); + void scale(double factor) + %code{% *THIS *= factor; %}; + void translate(double x, double y) + %code{% *THIS += Point(x, y); %}; SV* arrayref() %code{% RETVAL = to_SV_pureperl(THIS); %}; SV* pp() %code{% RETVAL = to_SV_pureperl(THIS); %}; int x() - %code{% RETVAL = THIS->x; %}; + %code{% RETVAL = (*THIS)(0); %}; int y() - %code{% RETVAL = THIS->y; %}; + %code{% RETVAL = (*THIS)(1); %}; void set_x(int val) - %code{% THIS->x = val; %}; + %code{% (*THIS)(0) = val; %}; void set_y(int val) - %code{% THIS->y = val; %}; + %code{% (*THIS)(1) = val; %}; int nearest_point_index(Points points); Clone nearest_point(Points points) %code{% Point p; THIS->nearest_point(points, &p); RETVAL = p; %}; double distance_to(Point* point) - %code{% RETVAL = THIS->distance_to(*point); %}; + %code{% RETVAL = (*point - *THIS).cast().norm(); %}; double distance_to_line(Line* line) - %code{% RETVAL = THIS->distance_to(*line); %}; + %code{% RETVAL = line->distance_to(*THIS); %}; double perp_distance_to_line(Line* line) - %code{% RETVAL = THIS->perp_distance_to(*line); %}; + %code{% RETVAL = line->perp_distance_to(*THIS); %}; double ccw(Point* p1, Point* p2) %code{% RETVAL = THIS->ccw(*p1, *p2); %}; double ccw_angle(Point* p1, Point* p2) %code{% RETVAL = THIS->ccw_angle(*p1, *p2); %}; - Clone projection_onto_polygon(Polygon* polygon) + Point* projection_onto_polygon(Polygon* polygon) %code{% RETVAL = new Point(THIS->projection_onto(*polygon)); %}; - Clone projection_onto_polyline(Polyline* polyline) + Point* projection_onto_polyline(Polyline* polyline) %code{% RETVAL = new Point(THIS->projection_onto(*polyline)); %}; - Clone projection_onto_line(Line* line) + Point* projection_onto_line(Line* line) %code{% RETVAL = new Point(THIS->projection_onto(*line)); %}; - Clone negative() - %code{% RETVAL = new Point(THIS->negative()); %}; - bool coincides_with_epsilon(Point* point) - %code{% RETVAL = THIS->coincides_with_epsilon(*point); %}; - std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld", THIS->x, THIS->y); RETVAL = buf; %}; + Point* negative() + %code{% RETVAL = new Point(- *THIS); %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld", (*THIS)(0), (*THIS)(1)); RETVAL = buf; %}; %{ @@ -68,7 +69,7 @@ Point::coincides_with(point_sv) CODE: Point point; from_SV_check(point_sv, &point); - RETVAL = THIS->coincides_with(point); + RETVAL = (*THIS) == point; OUTPUT: RETVAL @@ -76,72 +77,62 @@ Point::coincides_with(point_sv) }; -%name{Slic3r::Point3} class Point3 { - Point3(int _x = 0, int _y = 0, int _z = 0); - ~Point3(); - Clone clone() - %code{% RETVAL = THIS; %}; - int x() - %code{% RETVAL = THIS->x; %}; - int y() - %code{% RETVAL = THIS->y; %}; - int z() - %code{% RETVAL = THIS->z; %}; - std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld,%ld", THIS->x, THIS->y, THIS->z); RETVAL = buf; %}; -}; - -%name{Slic3r::Pointf} class Pointf { - Pointf(double _x = 0, double _y = 0); - ~Pointf(); - Clone clone() +%name{Slic3r::Pointf} class Vec2d { + Vec2d(double _x = 0, double _y = 0); + ~Vec2d(); + Clone clone() %code{% RETVAL = THIS; %}; SV* arrayref() %code{% RETVAL = to_SV_pureperl(THIS); %}; SV* pp() %code{% RETVAL = to_SV_pureperl(THIS); %}; double x() - %code{% RETVAL = THIS->x; %}; + %code{% RETVAL = (*THIS)(0); %}; double y() - %code{% RETVAL = THIS->y; %}; + %code{% RETVAL = (*THIS)(1); %}; void set_x(double val) - %code{% THIS->x = val; %}; + %code{% (*THIS)(0) = val; %}; void set_y(double val) - %code{% THIS->y = val; %}; - void translate(double x, double y); - void scale(double factor); - void rotate(double angle, Pointf* center) - %code{% THIS->rotate(angle, *center); %}; - Clone negative() - %code{% RETVAL = THIS->negative(); %}; - Clone vector_to(Pointf* point) - %code{% RETVAL = THIS->vector_to(*point); %}; - std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf", THIS->x, THIS->y); RETVAL = buf; %}; + %code{% (*THIS)(1) = val; %}; + void translate(double x, double y) + %code{% *THIS += Vec2d(x, y); %}; + void scale(double factor) + %code{% *THIS *= factor; %}; + void rotate(double angle, Vec2d* center) + %code{% *THIS = Eigen::Translation2d(*center) * Eigen::Rotation2Dd(angle) * Eigen::Translation2d(- *center) * Eigen::Vector2d((*THIS)(0), (*THIS)(1)); %}; + Vec2d* negative() + %code{% RETVAL = new Vec2d(- *THIS); %}; + Vec2d* vector_to(Vec2d* point) + %code{% RETVAL = new Vec2d(*point - *THIS); %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf", (*THIS)(0), (*THIS)(1)); RETVAL = buf; %}; }; -%name{Slic3r::Pointf3} class Pointf3 { - Pointf3(double _x = 0, double _y = 0, double _z = 0); - ~Pointf3(); - Clone clone() +%name{Slic3r::Pointf3} class Vec3d { + Vec3d(double _x = 0, double _y = 0, double _z = 0); + ~Vec3d(); + Clone clone() %code{% RETVAL = THIS; %}; double x() - %code{% RETVAL = THIS->x; %}; + %code{% RETVAL = (*THIS)(0); %}; double y() - %code{% RETVAL = THIS->y; %}; + %code{% RETVAL = (*THIS)(1); %}; double z() - %code{% RETVAL = THIS->z; %}; + %code{% RETVAL = (*THIS)(2); %}; void set_x(double val) - %code{% THIS->x = val; %}; + %code{% (*THIS)(0) = val; %}; void set_y(double val) - %code{% THIS->y = val; %}; + %code{% (*THIS)(1) = val; %}; void set_z(double val) - %code{% THIS->z = val; %}; - void translate(double x, double y, double z); - void scale(double factor); - double distance_to(Pointf3* point) - %code{% RETVAL = THIS->distance_to(*point); %}; - Clone negative() - %code{% RETVAL = THIS->negative(); %}; - Clone vector_to(Pointf3* point) - %code{% RETVAL = THIS->vector_to(*point); %}; - std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf", THIS->x, THIS->y, THIS->z); RETVAL = buf; %}; + %code{% (*THIS)(2) = val; %}; + void translate(double x, double y, double z) + %code{% *THIS += Vec3d(x, y, z); %}; + void scale(double factor) + %code{% *THIS *= factor; %}; + double distance_to(Vec3d* point) + %code{% RETVAL = (*point - *THIS).norm(); %}; + Vec3d* negative() + %code{% RETVAL = new Vec3d(- *THIS); %}; + Vec3d* vector_to(Vec3d* point) + %code{% RETVAL = new Vec3d(*point - *THIS); %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf", (*THIS)(0), (*THIS)(1), (*THIS)(2)); RETVAL = buf; %}; }; diff --git a/xs/xsp/Polygon.xsp b/xs/xsp/Polygon.xsp index f5db9f5150..a944254777 100644 --- a/xs/xsp/Polygon.xsp +++ b/xs/xsp/Polygon.xsp @@ -39,7 +39,6 @@ %code{% THIS->triangulate_convex(&RETVAL); %}; Clone centroid(); Clone bounding_box(); - std::string wkt(); Points concave_points(double angle); Points convex_points(double angle); Clone point_projection(Point* point) diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp index 60d7c6aca7..0dbd0e5728 100644 --- a/xs/xsp/Polyline.xsp +++ b/xs/xsp/Polyline.xsp @@ -38,7 +38,6 @@ bool is_straight(); Clone bounding_box(); void remove_duplicate_points(); - std::string wkt(); %{ Polyline* diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 1dee8a4c4c..f6edb5d64c 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -27,168 +27,106 @@ _constant() %} - %name{Slic3r::Print::Region} class PrintRegion { // owned by Print, no constructor/destructor Ref config() - %code%{ RETVAL = &THIS->config; %}; + %code%{ RETVAL = &THIS->config(); %}; Ref print(); Clone flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, PrintObject* object) %code%{ RETVAL = THIS->flow(role, layer_height, bridge, first_layer, width, *object); %}; }; - %name{Slic3r::Print::Object} class PrintObject { // owned by Print, no constructor/destructor - void add_region_volume(int region_id, int volume_id); - std::vector get_region_volumes(int region_id) - %code%{ - if (0 <= region_id && region_id < THIS->region_volumes.size()) - RETVAL = THIS->region_volumes[region_id]; - %}; int region_count() - %code%{ RETVAL = THIS->print()->regions.size(); %}; + %code%{ RETVAL = THIS->print()->regions().size(); %}; - int region_volumes_count() - %code%{ RETVAL = THIS->region_volumes.size(); %}; Ref print(); Ref model_object(); Ref config() - %code%{ RETVAL = &THIS->config; %}; + %code%{ RETVAL = &THIS->config(); %}; Points copies(); t_layer_height_ranges layer_height_ranges() %code%{ RETVAL = THIS->layer_height_ranges; %}; std::vector layer_height_profile() %code%{ RETVAL = THIS->layer_height_profile; %}; - Ref size() - %code%{ RETVAL = &THIS->size; %}; Clone bounding_box(); Points _shifted_copies() - %code%{ RETVAL = THIS->_shifted_copies; %}; - void set_shifted_copies(Points value) - %code%{ THIS->_shifted_copies = value; %}; + %code%{ RETVAL = THIS->copies(); %}; - bool add_copy(Pointf* point) - %code%{ RETVAL = THIS->add_copy(*point); %}; - bool delete_last_copy(); - bool delete_all_copies(); - bool set_copies(Points copies); - bool reload_model_instances(); void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges) %code%{ THIS->layer_height_ranges = layer_height_ranges; %}; - void set_layer_height_profile(std::vector profile) - %code%{ THIS->layer_height_profile = profile; %}; - size_t total_layer_count(); size_t layer_count(); - void clear_layers(); Ref get_layer(int idx); - Ref add_layer(int id, coordf_t height, coordf_t print_z, - coordf_t slice_z); size_t support_layer_count(); - void clear_support_layers(); Ref get_support_layer(int idx); bool step_done(PrintObjectStep step) - %code%{ RETVAL = THIS->state.is_done(step); %}; - void set_step_done(PrintObjectStep step) - %code%{ THIS->state.set_done(step); %}; - void set_step_started(PrintObjectStep step) - %code%{ THIS->state.set_started(step); %}; + %code%{ RETVAL = THIS->is_step_done(step); %}; - void _slice(); - std::string _fix_slicing_errors(); - void _simplify_slices(double distance); - void _prepare_infill(); - void detect_surfaces_type(); - void process_external_surfaces(); - void _make_perimeters(); - void _infill(); - void _generate_support_material(); - - std::vector get_layer_height_min_max() - %code%{ - SlicingParameters slicing_params = THIS->slicing_parameters(); - RETVAL.push_back(slicing_params.min_layer_height); - RETVAL.push_back(slicing_params.max_layer_height); - RETVAL.push_back(slicing_params.first_print_layer_height); - RETVAL.push_back(slicing_params.first_object_layer_height); - RETVAL.push_back(slicing_params.layer_height); - %}; - - void reset_layer_height_profile(); - - int ptr() - %code%{ RETVAL = (int)(intptr_t)THIS; %}; + void slice(); }; - %name{Slic3r::Print} class Print { Print(); ~Print(); Ref config() - %code%{ RETVAL = static_cast(&THIS->config); %}; - Ref default_object_config() - %code%{ RETVAL = &THIS->default_object_config; %}; - Ref default_region_config() - %code%{ RETVAL = &THIS->default_region_config; %}; + %code%{ RETVAL = const_cast(static_cast(&THIS->config())); %}; Ref placeholder_parser() - %code%{ RETVAL = &THIS->placeholder_parser; %}; - // TODO: status_cb + %code%{ RETVAL = &THIS->placeholder_parser(); %}; Ref skirt() - %code%{ RETVAL = &THIS->skirt; %}; + %code%{ RETVAL = const_cast(&THIS->skirt()); %}; Ref brim() - %code%{ RETVAL = &THIS->brim; %}; + %code%{ RETVAL = const_cast(&THIS->brim()); %}; std::string estimated_normal_print_time() - %code%{ RETVAL = THIS->estimated_normal_print_time; %}; + %code%{ RETVAL = THIS->print_statistics().estimated_normal_print_time; %}; std::string estimated_silent_print_time() - %code%{ RETVAL = THIS->estimated_silent_print_time; %}; - + %code%{ RETVAL = THIS->print_statistics().estimated_silent_print_time; %}; + double total_used_filament() + %code%{ RETVAL = THIS->print_statistics().total_used_filament; %}; + double total_extruded_volume() + %code%{ RETVAL = THIS->print_statistics().total_extruded_volume; %}; + double total_weight() + %code%{ RETVAL = THIS->print_statistics().total_weight; %}; + double total_cost() + %code%{ RETVAL = THIS->print_statistics().total_cost; %}; + double total_wipe_tower_cost() + %code%{ RETVAL = THIS->print_statistics().total_wipe_tower_cost; %}; + double total_wipe_tower_filament() + %code%{ RETVAL = THIS->print_statistics().total_wipe_tower_filament; %}; + int wipe_tower_number_of_toolchanges() + %code%{ RETVAL = THIS->wipe_tower_data().number_of_toolchanges; %}; PrintObjectPtrs* objects() - %code%{ RETVAL = &THIS->objects; %}; - void clear_objects(); - Ref get_object(int idx); - void delete_object(int idx); + %code%{ RETVAL = const_cast(&THIS->objects()); %}; + Ref get_object(int idx) + %code%{ RETVAL = THIS->objects()[idx]; %}; void reload_object(int idx); - bool reload_model_instances(); size_t object_count() - %code%{ RETVAL = THIS->objects.size(); %}; + %code%{ RETVAL = THIS->objects().size(); %}; PrintRegionPtrs* regions() - %code%{ RETVAL = &THIS->regions; %}; - Ref get_region(int idx); - Ref add_region(); + %code%{ RETVAL = const_cast(&THIS->regions()); %}; + Ref get_region(int idx) + %code%{ RETVAL = THIS->regions()[idx]; %}; size_t region_count() - %code%{ RETVAL = THIS->regions.size(); %}; + %code%{ RETVAL = THIS->regions().size(); %}; bool step_done(PrintStep step) - %code%{ RETVAL = THIS->state.is_done(step); %}; + %code%{ RETVAL = THIS->is_step_done(step); %}; bool object_step_done(PrintObjectStep step) - %code%{ RETVAL = THIS->step_done(step); %}; - void set_step_done(PrintStep step) - %code%{ THIS->state.set_done(step); %}; - void set_step_started(PrintStep step) - %code%{ THIS->state.set_started(step); %}; + %code%{ RETVAL = THIS->is_step_done(step); %}; - void clear_filament_stats() - %code%{ - THIS->filament_stats.clear(); - %}; - void set_filament_stats(int extruder_id, float length) - %code%{ - THIS->filament_stats.insert(std::pair(extruder_id, 0)); - THIS->filament_stats[extruder_id] += length; - %}; SV* filament_stats() %code%{ HV* hv = newHV(); - for (std::map::const_iterator it = THIS->filament_stats.begin(); it != THIS->filament_stats.end(); ++it) { + for (std::map::const_iterator it = THIS->print_statistics().filament_stats.begin(); it != THIS->print_statistics().filament_stats.end(); ++it) { // stringify extruder_id std::ostringstream ss; ss << it->first; @@ -198,7 +136,6 @@ _constant() RETVAL = newRV_noinc((SV*)hv); } %}; - void _simplify_slices(double distance); double max_allowed_layer_height() const; bool has_support_material() const; void auto_assign_extruders(ModelObject* model_object); @@ -210,12 +147,11 @@ _constant() croak("%s\n", e.what()); } %}; - + void add_model_object(ModelObject* model_object, int idx = -1); bool apply_config(DynamicPrintConfig* config) %code%{ RETVAL = THIS->apply_config(*config); %}; bool has_infinite_skirt(); - bool has_skirt(); std::vector extruders() const; int validate() %code%{ std::string err = THIS->validate(); @@ -225,88 +161,34 @@ _constant() %}; Clone bounding_box(); Clone total_bounding_box(); - double skirt_first_layer_height(); - Clone brim_flow(); - Clone skirt_flow(); + Clone size() %code%{ RETVAL = THIS->bounding_box().size(); %}; - void _make_skirt(); - void _make_brim(); + void set_callback_event(int evt) %code%{ + %}; + void set_status_silent(); + void set_status(int percent, const char *message); - bool has_wipe_tower(); - void _clear_wipe_tower(); - void _make_wipe_tower(); + void process() %code%{ + try { + THIS->process(); + } catch (std::exception& e) { + croak(e.what()); + } + %}; -%{ + void export_gcode(char *path_template) %code%{ + try { + THIS->export_gcode(path_template, nullptr); + } catch (std::exception& e) { + croak(e.what()); + } + %}; -double -Print::total_used_filament(...) - CODE: - if (items > 1) { - THIS->total_used_filament = (double)SvNV(ST(1)); - } - RETVAL = THIS->total_used_filament; - OUTPUT: - RETVAL - -double -Print::total_extruded_volume(...) - CODE: - if (items > 1) { - THIS->total_extruded_volume = (double)SvNV(ST(1)); - } - RETVAL = THIS->total_extruded_volume; - OUTPUT: - RETVAL - - -double -Print::total_weight(...) - CODE: - if (items > 1) { - THIS->total_weight = (double)SvNV(ST(1)); - } - RETVAL = THIS->total_weight; - OUTPUT: - RETVAL - -double -Print::total_cost(...) - CODE: - if (items > 1) { - THIS->total_cost = (double)SvNV(ST(1)); - } - RETVAL = THIS->total_cost; - OUTPUT: - RETVAL - -double -Print::total_wipe_tower_cost(...) - CODE: - if (items > 1) { - THIS->total_wipe_tower_cost = (double)SvNV(ST(1)); - } - RETVAL = THIS->total_wipe_tower_cost; - OUTPUT: - RETVAL - -double -Print::total_wipe_tower_filament(...) - CODE: - if (items > 1) { - THIS->total_wipe_tower_filament = (double)SvNV(ST(1)); - } - RETVAL = THIS->total_wipe_tower_filament; - OUTPUT: - RETVAL - -int -Print::m_wipe_tower_number_of_toolchanges(...) - CODE: - if (items > 1) { - THIS->m_wipe_tower_number_of_toolchanges = (double)SvNV(ST(1)); - } - RETVAL = THIS->m_wipe_tower_number_of_toolchanges; - OUTPUT: - RETVAL -%} + void export_png(char *path) %code%{ + try { + THIS->export_png(path); + } catch (std::exception& e) { + croak(e.what()); + } + %}; }; diff --git a/xs/xsp/ProgressStatusBar.xsp b/xs/xsp/ProgressStatusBar.xsp new file mode 100644 index 0000000000..703a53b679 --- /dev/null +++ b/xs/xsp/ProgressStatusBar.xsp @@ -0,0 +1,48 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/GUI/ProgressStatusBar.hpp" +#include "slic3r/GUI/GUI.hpp" +%} + +%name{Slic3r::GUI::ProgressStatusBar} class ProgressStatusBar { + ProgressStatusBar(); + ~ProgressStatusBar(); + + int GetProgress() const + %code%{ RETVAL=THIS->get_progress(); %}; + + void SetProgress(int val) + %code%{ THIS->set_progress(val); %}; + + void SetRange(int val = 100) + %code%{ THIS->set_range(val); %}; + + void ShowProgress(bool show) + %code%{ THIS->show_progress(show); %}; + + void StartBusy(int val = 100) + %code%{ THIS->start_busy(val); %}; + + void StopBusy() + %code%{ THIS->stop_busy(); %}; + + bool IsBusy() const + %code%{ RETVAL=THIS->is_busy(); %}; + + void Run(int rate) + %code%{ THIS->run(rate); %}; + + void Embed() + %code%{ THIS->embed(); %}; + + void SetStatusText(const char *txt) + %code%{ THIS->set_status_text(_(txt)); %}; + + void SetCancelCallback(SV* callback) + %code%{ THIS->m_perl_cancel_callback.register_callback(callback); THIS->show_cancel_button();%}; + void ResetCancelCallback() + %code%{ THIS->m_perl_cancel_callback.deregister_callback(); THIS->hide_cancel_button(); %}; + +}; \ No newline at end of file diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index d4578303b1..95f2f7d52f 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -16,7 +16,7 @@ void repair(); void WriteOBJFile(char* output_file); void scale(float factor); - void scale_xyz(Pointf3* versor) + void scale_xyz(Vec3d* versor) %code{% THIS->scale(*versor); %}; void translate(float x, float y, float z); void rotate_x(float angle); @@ -33,7 +33,7 @@ ExPolygons horizontal_projection(); Clone convex_hull(); Clone bounding_box(); - Clone center() + Clone center() %code{% RETVAL = THIS->bounding_box().center(); %}; int facets_count(); void reset_repair_stats(); @@ -60,14 +60,14 @@ TriangleMesh::ReadFromPerl(vertices, facets) for (int i = 0; i < stl.stats.number_of_facets; i++) { AV* facet_av = (AV*)SvRV(*av_fetch(facets_av, i, 0)); stl_facet facet; - facet.normal.x = 0; - facet.normal.y = 0; - facet.normal.z = 0; + facet.normal(0) = 0; + facet.normal(1) = 0; + facet.normal(2) = 0; for (unsigned int v = 0; v <= 2; v++) { AV* vertex_av = (AV*)SvRV(*av_fetch(vertices_av, SvIV(*av_fetch(facet_av, v, 0)), 0)); - facet.vertex[v].x = SvNV(*av_fetch(vertex_av, 0, 0)); - facet.vertex[v].y = SvNV(*av_fetch(vertex_av, 1, 0)); - facet.vertex[v].z = SvNV(*av_fetch(vertex_av, 2, 0)); + facet.vertex[v](0) = SvNV(*av_fetch(vertex_av, 0, 0)); + facet.vertex[v](1) = SvNV(*av_fetch(vertex_av, 1, 0)); + facet.vertex[v](2) = SvNV(*av_fetch(vertex_av, 2, 0)); } facet.extra[0] = 0; facet.extra[1] = 0; @@ -110,9 +110,9 @@ TriangleMesh::vertices() AV* vertex = newAV(); av_store(vertices, i, newRV_noinc((SV*)vertex)); av_extend(vertex, 2); - av_store(vertex, 0, newSVnv(THIS->stl.v_shared[i].x)); - av_store(vertex, 1, newSVnv(THIS->stl.v_shared[i].y)); - av_store(vertex, 2, newSVnv(THIS->stl.v_shared[i].z)); + av_store(vertex, 0, newSVnv(THIS->stl.v_shared[i](0))); + av_store(vertex, 1, newSVnv(THIS->stl.v_shared[i](1))); + av_store(vertex, 2, newSVnv(THIS->stl.v_shared[i](2))); } RETVAL = newRV_noinc((SV*)vertices); @@ -155,9 +155,9 @@ TriangleMesh::normals() AV* facet = newAV(); av_store(normals, i, newRV_noinc((SV*)facet)); av_extend(facet, 2); - av_store(facet, 0, newSVnv(THIS->stl.facet_start[i].normal.x)); - av_store(facet, 1, newSVnv(THIS->stl.facet_start[i].normal.y)); - av_store(facet, 2, newSVnv(THIS->stl.facet_start[i].normal.z)); + av_store(facet, 0, newSVnv(THIS->stl.facet_start[i].normal(0))); + av_store(facet, 1, newSVnv(THIS->stl.facet_start[i].normal(1))); + av_store(facet, 2, newSVnv(THIS->stl.facet_start[i].normal(2))); } RETVAL = newRV_noinc((SV*)normals); @@ -169,9 +169,9 @@ TriangleMesh::size() CODE: AV* size = newAV(); av_extend(size, 2); - av_store(size, 0, newSVnv(THIS->stl.stats.size.x)); - av_store(size, 1, newSVnv(THIS->stl.stats.size.y)); - av_store(size, 2, newSVnv(THIS->stl.stats.size.z)); + av_store(size, 0, newSVnv(THIS->stl.stats.size(0))); + av_store(size, 1, newSVnv(THIS->stl.stats.size(1))); + av_store(size, 2, newSVnv(THIS->stl.stats.size(2))); RETVAL = newRV_noinc((SV*)size); OUTPUT: RETVAL @@ -181,11 +181,11 @@ TriangleMesh::slice(z) std::vector z CODE: // convert doubles to floats - std::vector z_f(z.begin(), z.end()); + std::vector z_f = cast(z); std::vector layers; TriangleMeshSlicer mslicer(THIS); - mslicer.slice(z_f, &layers); + mslicer.slice(z_f, &layers, [](){}); AV* layers_av = newAV(); size_t len = layers.size(); @@ -216,12 +216,12 @@ TriangleMesh::cut(z, upper, lower) std::vector TriangleMesh::bb3() CODE: - RETVAL.push_back(THIS->stl.stats.min.x); - RETVAL.push_back(THIS->stl.stats.min.y); - RETVAL.push_back(THIS->stl.stats.max.x); - RETVAL.push_back(THIS->stl.stats.max.y); - RETVAL.push_back(THIS->stl.stats.min.z); - RETVAL.push_back(THIS->stl.stats.max.z); + RETVAL.push_back(THIS->stl.stats.min(0)); + RETVAL.push_back(THIS->stl.stats.min(1)); + RETVAL.push_back(THIS->stl.stats.max(0)); + RETVAL.push_back(THIS->stl.stats.max(1)); + RETVAL.push_back(THIS->stl.stats.min(2)); + RETVAL.push_back(THIS->stl.stats.max(2)); OUTPUT: RETVAL diff --git a/xs/xsp/my.map b/xs/xsp/my.map index ba20ee2362..90a5feaf75 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -66,13 +66,13 @@ Point3* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T -Pointf* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T +Vec2d* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T -Pointf3* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T +Vec3d* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T Line* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T @@ -216,9 +216,9 @@ Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T AppConfig* O_OBJECT_SLIC3R -AppController* O_OBJECT_SLIC3R -PrintController* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T +BackgroundSlicingProcess* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T GLShader* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T @@ -233,8 +233,12 @@ PresetCollection* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T PresetBundle* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T -TabIface* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T +TabIface* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +PreviewIface* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +ProgressStatusBar* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T PresetUpdater* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index cee75fe261..0209ce92dd 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -22,12 +22,12 @@ %typemap{Point3*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; -%typemap{Pointf*}; -%typemap{Ref}{simple}; -%typemap{Clone}{simple}; -%typemap{Pointf3*}; -%typemap{Ref}{simple}; -%typemap{Clone}{simple}; +%typemap{Vec2d*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; +%typemap{Vec3d*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; %typemap{BoundingBox*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; @@ -196,6 +196,8 @@ %typemap{Clone}{simple}; %typemap{AppConfig*}; %typemap{Ref}{simple}; +%typemap{BackgroundSlicingProcess*}; +%typemap{Ref}{simple}; %typemap{GLShader*}; %typemap{Ref}{simple}; %typemap{GLVolume*}; @@ -213,6 +215,8 @@ %typemap{PresetHints*}; %typemap{Ref}{simple}; %typemap{TabIface*}; +%typemap{PreviewIface*}; +%typemap{ProgressStatusBar*}; %typemap{PrintRegionPtrs*}; %typemap{PrintObjectPtrs*};